@agent-native/core 0.154.5 → 0.157.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (263) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/templates/analytics/.agents/skills/account-health/SKILL.md +76 -0
  3. package/corpus/templates/analytics/.agents/skills/bigquery/SKILL.md +3 -0
  4. package/corpus/templates/analytics/.agents/skills/cross-source-analysis/SKILL.md +2 -1
  5. package/corpus/templates/analytics/.agents/skills/data-querying/SKILL.md +6 -5
  6. package/corpus/templates/analytics/AGENTS.md +3 -0
  7. package/corpus/templates/analytics/actions/account-deep-dive.ts +3 -2
  8. package/corpus/templates/analytics/actions/bigquery.ts +1 -1
  9. package/corpus/templates/analytics/app/components/dashboard/SqlChart.tsx +4 -1
  10. package/corpus/templates/analytics/app/components/layout/NewDashboardDialog.tsx +1 -0
  11. package/corpus/templates/analytics/app/pages/adhoc/explorer/components/PropertyCombobox.tsx +12 -4
  12. package/corpus/templates/analytics/app/pages/adhoc/explorer/components/PropertyValueCombobox.tsx +5 -2
  13. package/corpus/templates/analytics/app/pages/adhoc/explorer/property-label.ts +32 -0
  14. package/corpus/templates/analytics/changelog/2026-08-13-account-health-readouts-verify-customer-scope-and-contract-metrics.md +7 -0
  15. package/corpus/templates/analytics/server/jobs/analytics-bigquery-backfill.ts +43 -17
  16. package/corpus/templates/analytics/server/lib/analytics-query-catalog.ts +17 -0
  17. package/corpus/templates/analytics/server/plugins/agent-chat.ts +4 -0
  18. package/corpus/templates/assets/app/components/asset/AssetPreviewDialog.tsx +3 -1
  19. package/corpus/templates/assets/app/i18n-data.ts +21 -1
  20. package/corpus/templates/assets/app/routes/brand-kits.$id.tsx +17 -13
  21. package/corpus/templates/calendar/AGENTS.md +2 -0
  22. package/corpus/templates/calendar/DEVELOPING.md +4 -4
  23. package/corpus/templates/calendar/actions/get-settings.ts +4 -11
  24. package/corpus/templates/calendar/actions/update-availability.ts +15 -4
  25. package/corpus/templates/calendar/actions/update-settings.ts +15 -4
  26. package/corpus/templates/calendar/app/components/booking/DatePicker.tsx +9 -3
  27. package/corpus/templates/calendar/app/components/calendar/CreateEventDialog.tsx +21 -19
  28. package/corpus/templates/calendar/app/components/calendar/EventDetailPopover.tsx +9 -6
  29. package/corpus/templates/calendar/app/components/calendar/FindTimePanel.tsx +25 -3
  30. package/corpus/templates/calendar/app/components/calendar/InlineEventPickers.tsx +10 -3
  31. package/corpus/templates/calendar/app/components/calendar/MonthView.tsx +9 -9
  32. package/corpus/templates/calendar/app/components/calendar/WeekView.tsx +11 -3
  33. package/corpus/templates/calendar/app/components/layout/Sidebar.tsx +10 -4
  34. package/corpus/templates/calendar/app/i18n-data.ts +30 -0
  35. package/corpus/templates/calendar/app/lib/availability-schedule.ts +53 -0
  36. package/corpus/templates/calendar/app/lib/event-form-utils.ts +5 -0
  37. package/corpus/templates/calendar/app/pages/AvailabilitySettings.tsx +90 -28
  38. package/corpus/templates/calendar/app/pages/BookingLinksPage.tsx +135 -61
  39. package/corpus/templates/calendar/app/pages/BookingPage.tsx +2 -0
  40. package/corpus/templates/calendar/app/pages/CalendarView.tsx +30 -15
  41. package/corpus/templates/calendar/app/pages/Settings.tsx +60 -1
  42. package/corpus/templates/calendar/changelog/2026-08-13-find-a-time-now-closes-from-a-clear-header-action.md +6 -0
  43. package/corpus/templates/calendar/server/handlers/bookings.ts +2 -1
  44. package/corpus/templates/calendar/server/handlers/settings.ts +12 -16
  45. package/corpus/templates/calendar/shared/api.ts +1 -0
  46. package/corpus/templates/calendar/shared/availability-schedule.ts +64 -0
  47. package/corpus/templates/calendar/shared/calendar-week.ts +21 -0
  48. package/corpus/templates/calendar/shared/settings.ts +44 -0
  49. package/corpus/templates/clips/AGENTS.md +2 -0
  50. package/corpus/templates/clips/actions/add-comment.ts +5 -2
  51. package/corpus/templates/clips/actions/finalize-recording.ts +31 -1
  52. package/corpus/templates/clips/actions/get-recording-player-data.ts +9 -0
  53. package/corpus/templates/clips/actions/lib/ensure-seekable-video.ts +21 -1
  54. package/corpus/templates/clips/actions/reply-to-comment.ts +5 -2
  55. package/corpus/templates/clips/actions/update-comment.ts +9 -2
  56. package/corpus/templates/clips/app/components/library/search-bar.tsx +26 -13
  57. package/corpus/templates/clips/app/components/meetings/share-meeting-dialog.tsx +135 -3
  58. package/corpus/templates/clips/app/components/player/comments-panel.tsx +5 -99
  59. package/corpus/templates/clips/app/components/player/playback-comment-overlay.tsx +7 -3
  60. package/corpus/templates/clips/app/components/player/share-dialog.tsx +102 -59
  61. package/corpus/templates/clips/app/components/player/video-player.tsx +84 -24
  62. package/corpus/templates/clips/app/components/workspace/notifications-list.tsx +11 -3
  63. package/corpus/templates/clips/app/i18n/en-US.ts +12 -0
  64. package/corpus/templates/clips/app/lib/public-meeting.ts +11 -3
  65. package/corpus/templates/clips/app/lib/search-focus.ts +15 -0
  66. package/corpus/templates/clips/app/root.tsx +2 -1
  67. package/corpus/templates/clips/app/routes/_app.meetings._index.tsx +94 -26
  68. package/corpus/templates/clips/app/routes/_app.settings._index.tsx +24 -7
  69. package/corpus/templates/clips/app/routes/embed.$shareId.tsx +8 -4
  70. package/corpus/templates/clips/app/routes/r.$recordingId.tsx +29 -8
  71. package/corpus/templates/clips/app/routes/share.$shareId.tsx +24 -6
  72. package/corpus/templates/clips/app/routes/share.meeting.$meetingId.tsx +72 -26
  73. package/corpus/templates/clips/changelog/2026-08-13-inline-markdown-comments.md +6 -0
  74. package/corpus/templates/clips/changelog/2026-08-13-private-meeting-agent-links.md +6 -0
  75. package/corpus/templates/clips/desktop/src/app.tsx +116 -19
  76. package/corpus/templates/clips/desktop/src/lib/recorder.ts +13 -29
  77. package/corpus/templates/clips/desktop/src/lib/recording-request.ts +39 -0
  78. package/corpus/templates/clips/desktop/src/lib/settings-navigation.ts +11 -0
  79. package/corpus/templates/clips/desktop/src/lib/whisper-model-picker.ts +7 -0
  80. package/corpus/templates/clips/desktop/src/styles.css +117 -0
  81. package/corpus/templates/clips/server/db/index.ts +10 -0
  82. package/corpus/templates/clips/server/db/schema.ts +1 -0
  83. package/corpus/templates/clips/server/lib/recordings.ts +3 -1
  84. package/corpus/templates/clips/server/lib/seekable-media-state.ts +90 -0
  85. package/corpus/templates/clips/server/plugins/db.ts +5 -0
  86. package/corpus/templates/clips/server/routes/api/public-meeting.get.ts +37 -4
  87. package/corpus/templates/clips/server/routes/api/public-recording.get.ts +16 -5
  88. package/corpus/templates/clips/shared/clips-ai-prefs.ts +3 -0
  89. package/corpus/templates/clips/shared/meeting-agent-access.ts +5 -0
  90. package/corpus/templates/content/actions/add-comment.ts +2 -1
  91. package/corpus/templates/content/actions/update-comment.ts +1 -1
  92. package/corpus/templates/content/app/components/editor/CommentsSidebar.tsx +24 -15
  93. package/corpus/templates/design/actions/generate-design.ts +10 -7
  94. package/corpus/templates/design/actions/present-design-variants.ts +35 -6
  95. package/corpus/templates/design/actions/run-design-audit.ts +33 -0
  96. package/corpus/templates/design/changelog/2026-08-13-the-design-audit-now-catches-a-screen-that-renders-behind-an.md +6 -0
  97. package/corpus/templates/design/shared/design-review.ts +1 -0
  98. package/corpus/templates/design/shared/html-integrity.ts +66 -0
  99. package/corpus/templates/dispatch/.env.example +5 -0
  100. package/corpus/templates/dispatch/AGENTS.md +24 -19
  101. package/corpus/templates/dispatch/app/i18n-data.ts +20 -0
  102. package/corpus/templates/dispatch/server/lib/identity-sso.ts +21 -90
  103. package/corpus/templates/dispatch/server/plugins/identity-sso.ts +3 -12
  104. package/corpus/templates/dispatch/shared/feature-flags.ts +4 -0
  105. package/corpus/templates/factory/AGENTS.md +5 -1
  106. package/corpus/templates/factory/README.md +23 -0
  107. package/corpus/templates/factory/actions/view-screen.ts +17 -0
  108. package/corpus/templates/factory/app/components/factory/FactoryAgentsView.tsx +239 -0
  109. package/corpus/templates/factory/app/components/factory/FactoryCanvas.tsx +2 -0
  110. package/corpus/templates/factory/app/components/factory/FactoryInspector.tsx +130 -7
  111. package/corpus/templates/factory/app/i18n/en-US.ts +20 -0
  112. package/corpus/templates/factory/app/routes/factory.tsx +41 -0
  113. package/corpus/templates/factory/package.json +1 -0
  114. package/corpus/templates/factory/server/factory-graph/contracts.ts +2 -0
  115. package/corpus/templates/factory/server/plugins/agent-chat.ts +9 -0
  116. package/corpus/templates/factory/server/plugins/dispatch-db.ts +1 -0
  117. package/corpus/templates/factory/server/plugins/setup-dispatch.ts +3 -0
  118. package/corpus/templates/mail/app/lib/native-mcp-exclusions.ts +3 -0
  119. package/corpus/templates/mail/vite.config.ts +7 -0
  120. package/corpus/templates/plan/actions/reply-to-plan-comment.ts +7 -2
  121. package/corpus/templates/plan/actions/resolve-plan-comment.ts +2 -2
  122. package/corpus/templates/plan/actions/update-visual-plan.ts +1 -1
  123. package/corpus/templates/plan/app/pages/PlansPage.tsx +29 -20
  124. package/corpus/templates/slides/actions/add-slide-comment.ts +1 -1
  125. package/corpus/templates/slides/actions/update-slide-comment.ts +1 -1
  126. package/corpus/templates/slides/app/components/comments/SlideCommentsPanel.tsx +9 -6
  127. package/corpus/templates/slides/app/components/editor/AddSlidePopover.tsx +98 -29
  128. package/corpus/templates/slides/app/components/editor/EditorActionCluster.tsx +45 -91
  129. package/corpus/templates/slides/app/components/editor/EditorSidebar.tsx +94 -2
  130. package/corpus/templates/slides/app/components/editor/EditorToolbar.tsx +17 -29
  131. package/corpus/templates/slides/app/components/editor/SlideContextToolbar.tsx +1 -1
  132. package/corpus/templates/slides/app/context/DeckContext.tsx +42 -2
  133. package/corpus/templates/slides/app/global.css +7 -40
  134. package/corpus/templates/slides/app/i18n/en-US.ts +4 -0
  135. package/corpus/templates/slides/app/pages/DeckEditor.tsx +130 -45
  136. package/dist/agent/index.d.ts +1 -1
  137. package/dist/agent/production-agent.d.ts +30 -0
  138. package/dist/agent/production-agent.js +105 -9
  139. package/dist/agent/thread-data-builder.js +17 -1
  140. package/dist/agent/types.d.ts +9 -0
  141. package/dist/cli/skills-content/help.js +6 -4
  142. package/dist/cli/skills.js +17 -2
  143. package/dist/cli/workspacify.d.ts +5 -0
  144. package/dist/cli/workspacify.js +65 -8
  145. package/dist/client/AgentPanel.d.ts +5 -0
  146. package/dist/client/AgentPanel.js +32 -13
  147. package/dist/client/AssistantChat.d.ts +36 -1
  148. package/dist/client/AssistantChat.js +175 -110
  149. package/dist/client/MultiTabAssistantChat.js +44 -27
  150. package/dist/client/active-run-state.d.ts +14 -1
  151. package/dist/client/active-run-state.js +0 -0
  152. package/dist/client/agent-chat-adapter.js +107 -5
  153. package/dist/client/analytics.js +9 -2
  154. package/dist/client/chat/markdown-renderer.js +4 -2
  155. package/dist/client/chat/message-components.d.ts +2 -2
  156. package/dist/client/chat/message-components.js +69 -35
  157. package/dist/client/chat/run-recovery.d.ts +4 -1
  158. package/dist/client/chat/run-recovery.js +62 -30
  159. package/dist/client/chat/tool-call-display.d.ts +12 -3
  160. package/dist/client/chat/tool-call-display.js +79 -40
  161. package/dist/client/chat/widgets/DataChartRenderer.js +3 -1
  162. package/dist/client/chat/widgets/DataChartWidget.js +8 -2
  163. package/dist/client/chat/widgets/DataInsightsWidget.js +3 -1
  164. package/dist/client/chat/widgets/DataTableWidget.js +23 -9
  165. package/dist/client/chat/widgets/builtin-tool-renderers.js +3 -1
  166. package/dist/client/chat-first/apps-rail.js +9 -4
  167. package/dist/client/chat-first/primary-nav.d.ts +2 -1
  168. package/dist/client/chat-first/primary-nav.js +3 -3
  169. package/dist/client/chat-first/types.d.ts +5 -1
  170. package/dist/client/components/ui/message-scroller.d.ts +1 -1
  171. package/dist/client/components/ui/message-scroller.js +7 -3
  172. package/dist/client/composer/runtime-adapters.js +3 -2
  173. package/dist/client/context-xray/ContextMeter.js +5 -1
  174. package/dist/client/context-xray/ContextXRayPanel.js +5 -5
  175. package/dist/client/error-format.d.ts +4 -0
  176. package/dist/client/error-format.js +93 -5
  177. package/dist/client/i18n.js +83 -17
  178. package/dist/client/markdown/InlineMarkdown.d.ts +23 -0
  179. package/dist/client/markdown/InlineMarkdown.js +65 -0
  180. package/dist/client/markdown/index.d.ts +1 -0
  181. package/dist/client/markdown/index.js +1 -0
  182. package/dist/client/observability/ThumbsFeedback.js +10 -8
  183. package/dist/client/review/ReviewThreadPanel.js +2 -1
  184. package/dist/client/run-stream-ownership.d.ts +39 -0
  185. package/dist/client/run-stream-ownership.js +65 -0
  186. package/dist/client/sharing/ShareButton.js +160 -54
  187. package/dist/coding-tools/run-code.js +2 -1
  188. package/dist/coding-tools/sandbox/background.d.ts +1 -0
  189. package/dist/coding-tools/sandbox/background.js +8 -1
  190. package/dist/coding-tools/sandbox/executions-store.d.ts +2 -0
  191. package/dist/coding-tools/sandbox/executions-store.js +41 -3
  192. package/dist/collab/awareness.d.ts +2 -2
  193. package/dist/collab/routes.d.ts +1 -1
  194. package/dist/collab/struct-routes.d.ts +1 -1
  195. package/dist/feature-flags/index.d.ts +1 -1
  196. package/dist/feature-flags/index.js +1 -1
  197. package/dist/feature-flags/store.d.ts +10 -0
  198. package/dist/feature-flags/store.js +112 -1
  199. package/dist/guards/no-env-credentials.js +4 -0
  200. package/dist/index.d.ts +2 -2
  201. package/dist/index.js +1 -1
  202. package/dist/integrations/adapters/slack.js +5 -1
  203. package/dist/localization/core-messages/ar-SA.d.ts +3 -0
  204. package/dist/localization/core-messages/ar-SA.js +541 -0
  205. package/dist/localization/core-messages/de-DE.d.ts +3 -0
  206. package/dist/localization/core-messages/de-DE.js +513 -0
  207. package/dist/localization/core-messages/en-US.d.ts +513 -0
  208. package/dist/localization/core-messages/en-US.js +513 -0
  209. package/dist/localization/core-messages/es-ES.d.ts +3 -0
  210. package/dist/localization/core-messages/es-ES.js +520 -0
  211. package/dist/localization/core-messages/fr-FR.d.ts +3 -0
  212. package/dist/localization/core-messages/fr-FR.js +520 -0
  213. package/dist/localization/core-messages/hi-IN.d.ts +3 -0
  214. package/dist/localization/core-messages/hi-IN.js +513 -0
  215. package/dist/localization/core-messages/ja-JP.d.ts +3 -0
  216. package/dist/localization/core-messages/ja-JP.js +506 -0
  217. package/dist/localization/core-messages/ko-KR.d.ts +3 -0
  218. package/dist/localization/core-messages/ko-KR.js +506 -0
  219. package/dist/localization/core-messages/pt-BR.d.ts +3 -0
  220. package/dist/localization/core-messages/pt-BR.js +520 -0
  221. package/dist/localization/core-messages/zh-CN.d.ts +3 -0
  222. package/dist/localization/core-messages/zh-CN.js +506 -0
  223. package/dist/localization/core-messages/zh-TW.d.ts +3 -0
  224. package/dist/localization/core-messages/zh-TW.js +506 -0
  225. package/dist/localization/core-messages.d.ts +524 -0
  226. package/dist/localization/core-messages.js +124 -0
  227. package/dist/localization/default-messages.d.ts +3 -0
  228. package/dist/localization/default-messages.js +3 -0
  229. package/dist/notifications/routes.d.ts +1 -1
  230. package/dist/observability/routes.d.ts +2 -2
  231. package/dist/progress/routes.d.ts +1 -1
  232. package/dist/provider-api/actions/custom-provider-registration.d.ts +11 -11
  233. package/dist/provider-api/actions/provider-api.d.ts +15 -15
  234. package/dist/resources/agents.js +40 -5
  235. package/dist/resources/metadata.d.ts +10 -0
  236. package/dist/resources/metadata.js +7 -3
  237. package/dist/review/actions/create-review-comment.js +1 -1
  238. package/dist/review/actions/reply-review-comment.js +1 -1
  239. package/dist/review/actions/resolve-review-thread.js +1 -1
  240. package/dist/secrets/routes.d.ts +6 -6
  241. package/dist/server/action-routes.js +32 -6
  242. package/dist/server/agent-chat/plugin-options.d.ts +7 -2
  243. package/dist/server/agent-chat-plugin.d.ts +10 -0
  244. package/dist/server/agent-chat-plugin.js +144 -41
  245. package/dist/server/agent-discovery.js +14 -3
  246. package/dist/server/agent-run-context.d.ts +7 -1
  247. package/dist/server/agent-run-context.js +9 -2
  248. package/dist/server/agent-teams-run-queue.d.ts +3 -0
  249. package/dist/server/agent-teams.js +38 -9
  250. package/dist/server/core-routes-plugin.js +1 -1
  251. package/dist/server/identity-sso-migrations.d.ts +7 -0
  252. package/dist/server/identity-sso-migrations.js +36 -0
  253. package/dist/server/identity-sso-store.d.ts +3 -2
  254. package/dist/server/identity-sso-store.js +8 -3
  255. package/dist/server/index.d.ts +1 -1
  256. package/dist/server/realtime-token.d.ts +1 -1
  257. package/dist/server/release-migrations.js +4 -0
  258. package/dist/server/request-context.d.ts +7 -0
  259. package/dist/server/request-context.js +15 -0
  260. package/dist/tracking/providers.js +13 -3
  261. package/dist/workspace-files/tool.js +1 -1
  262. package/docs/content/agent-surfaces.mdx +30 -0
  263. package/package.json +3 -2
@@ -72,8 +72,11 @@ export type ToolDetailPayload = {
72
72
  copyText: string;
73
73
  lang: string;
74
74
  };
75
- export declare function toolInputPayload(toolName: string, args: Record<string, unknown>): ToolDetailPayload | null;
76
- export declare function toolResultPayload(result: string | undefined): ToolDetailPayload | null;
75
+ export declare function toolInputPayload(toolName: string, args: Record<string, unknown>, labels?: {
76
+ input: string;
77
+ inputWithLabel: (label: string) => string;
78
+ }): ToolDetailPayload | null;
79
+ export declare function toolResultPayload(result: string | undefined, title?: string): ToolDetailPayload | null;
77
80
  export declare function AnimatedCollapse({ open, children, }: {
78
81
  open: boolean;
79
82
  children: React.ReactNode;
@@ -138,7 +141,13 @@ export declare function ReasoningCell({ text, isStreaming, defaultOpen, autoColl
138
141
  */
139
142
  durationMs?: number | null;
140
143
  }): React.JSX.Element | null;
141
- export declare function formatWorkedDuration(ms: number): string;
144
+ export declare function formatWorkedDuration(ms: number, options?: {
145
+ locale?: string;
146
+ hour?: string;
147
+ minute?: string;
148
+ second?: string;
149
+ }): string;
150
+ export declare function useLocalizedWorkedDuration(): (ms: number) => string;
142
151
  export declare function WorkedForSummary({ durationMs, defaultOpen, autoCollapse, children, }: {
143
152
  durationMs?: number | null;
144
153
  /** Keep completed work visible when the turn contains interactive UI. */
@@ -6,7 +6,7 @@ import { writeClipboardText } from "../clipboard.js";
6
6
  import { Popover, PopoverContent, PopoverTrigger, } from "../components/ui/popover.js";
7
7
  import { ConnectBuilderCard } from "../ConnectBuilderCard.js";
8
8
  import { FileStorageSetupCard } from "../FileStorageSetupCard.js";
9
- import { useT } from "../i18n.js";
9
+ import { useOptionalLocale, useT } from "../i18n.js";
10
10
  import { McpAppRenderer } from "../mcp-apps/McpAppRenderer.js";
11
11
  import { findMcpIntegrationForToolName } from "../resources/mcp-integration-catalog.js";
12
12
  import { McpIntegrationLogo } from "../resources/McpIntegrationLogo.js";
@@ -41,6 +41,7 @@ export function toolCallHasPendingApproval(part) {
41
41
  }
42
42
  export const TOOL_LONG_RUNNING_HINT_DELAY_MS = 45_000;
43
43
  export function ToolActivityPresentation({ toolName, isRunning, toolCallId, suppressLongRunningHint = false, children, }) {
44
+ const t = useT();
44
45
  const [showLongRunningHint, setShowLongRunningHint] = useState(false);
45
46
  useEffect(() => {
46
47
  if (!isRunning || suppressLongRunningHint) {
@@ -53,7 +54,7 @@ export function ToolActivityPresentation({ toolName, isRunning, toolCallId, supp
53
54
  }, TOOL_LONG_RUNNING_HINT_DELAY_MS);
54
55
  return () => window.clearTimeout(timeout);
55
56
  }, [isRunning, suppressLongRunningHint, toolName]);
56
- return (_jsx("div", { className: "agent-tool-call", "data-agent-tool-call-id": toolCallId, "data-running": isRunning ? "true" : undefined, children: _jsxs("div", { className: "agent-tool-call__content", children: [children, isRunning && showLongRunningHint && (_jsx("div", { className: "mt-0.5 px-2.5 pb-2 text-[11px] leading-snug text-muted-foreground/80", children: "Still working. Large updates can take a minute or two." }))] }) }));
57
+ return (_jsx("div", { className: "agent-tool-call", "data-agent-tool-call-id": toolCallId, "data-running": isRunning ? "true" : undefined, children: _jsxs("div", { className: "agent-tool-call__content", children: [children, isRunning && showLongRunningHint && (_jsx("div", { className: "mt-0.5 px-2.5 pb-2 text-[11px] leading-snug text-muted-foreground/80", children: t("agentChat.tool.longRunning") }))] }) }));
57
58
  }
58
59
  function stringifyToolValue(value, pretty = false) {
59
60
  if (typeof value === "string")
@@ -109,7 +110,10 @@ function formatToolTextValue(value, key, toolName) {
109
110
  }
110
111
  return { text: stringifyToolValue(value, true), lang: "json" };
111
112
  }
112
- export function toolInputPayload(toolName, args) {
113
+ export function toolInputPayload(toolName, args, labels = {
114
+ input: "Input",
115
+ inputWithLabel: (label) => `Input - ${label}`,
116
+ }) {
113
117
  const entries = Object.entries(args);
114
118
  if (entries.length === 0)
115
119
  return null;
@@ -120,7 +124,7 @@ export function toolInputPayload(toolName, args) {
120
124
  const keyLabel = normalizedKey === "sql" || normalizedKey.endsWith("sql") ? "SQL" : key;
121
125
  return {
122
126
  section: "input",
123
- title: `Input - ${keyLabel}`,
127
+ title: labels.inputWithLabel(keyLabel),
124
128
  text: formatted.text,
125
129
  copyText: typeof value === "string" ? value : stringifyToolValue(value, true),
126
130
  lang: formatted.lang,
@@ -128,19 +132,19 @@ export function toolInputPayload(toolName, args) {
128
132
  }
129
133
  return {
130
134
  section: "input",
131
- title: "Input",
135
+ title: labels.input,
132
136
  text: JSON.stringify(args, null, 2),
133
137
  copyText: JSON.stringify(args, null, 2),
134
138
  lang: "json",
135
139
  };
136
140
  }
137
- export function toolResultPayload(result) {
141
+ export function toolResultPayload(result, title = "Result") {
138
142
  if (result === undefined)
139
143
  return null;
140
144
  const formatted = formatToolTextValue(result);
141
145
  return {
142
146
  section: "result",
143
- title: "Result",
147
+ title,
144
148
  text: formatted.text,
145
149
  copyText: result,
146
150
  lang: formatted.lang,
@@ -195,6 +199,7 @@ function SimpleCodeViewer({ text, lang, className, maxHeightClass = "max-h-56",
195
199
  return (_jsxs("div", { className: cn("agent-tool-code overflow-auto rounded-md bg-muted/70 font-mono text-[11px] leading-relaxed text-foreground", maxHeightClass, className), children: [lang !== "text" && (_jsx("div", { className: "sticky top-0 z-[1] flex items-center justify-between border-b border-border/40 bg-muted/90 px-2.5 py-1", children: _jsx("span", { className: "font-mono text-[10px] uppercase tracking-wide text-muted-foreground/80", children: lang }) })), _jsx(HighlightedCodeBlock, { code: text, lang: lang })] }));
196
200
  }
197
201
  function ToolOutputPopover({ open, onOpenChange, title, payload, children, }) {
202
+ const t = useT();
198
203
  const [copied, setCopied] = useState(false);
199
204
  const copyResetRef = useRef(null);
200
205
  useEffect(() => {
@@ -216,7 +221,7 @@ function ToolOutputPopover({ open, onOpenChange, title, payload, children, }) {
216
221
  // Clipboard failures should not interrupt chat rendering.
217
222
  }
218
223
  }, [payload.copyText]);
219
- return (_jsxs(Popover, { open: open, onOpenChange: onOpenChange, children: [_jsx(PopoverTrigger, { asChild: true, children: children }), _jsxs(PopoverContent, { align: "start", side: "bottom", sideOffset: 6, collisionPadding: 12, className: "flex max-h-[min(calc(100vh-2rem),var(--radix-popover-content-available-height,75vh))] w-[min(calc(100vw-2rem),var(--radix-popover-content-available-width,760px),760px)] flex-col gap-0 overflow-hidden p-0", children: [_jsxs("div", { className: "flex shrink-0 items-center justify-between gap-3 border-b border-border px-4 py-3", children: [_jsx("div", { className: "truncate text-sm font-medium", children: title }), _jsxs("button", { type: "button", onClick: copyValue, className: "inline-flex h-7 items-center gap-1.5 rounded-md px-2 text-xs text-muted-foreground hover:bg-accent hover:text-foreground", children: [copied ? _jsx(IconCheck, { size: 13 }) : _jsx(IconCopy, { size: 13 }), copied ? "Copied" : "Copy"] })] }), _jsx("div", { className: "min-h-0 flex-1 overflow-hidden p-3", children: _jsx(SimpleCodeViewer, { text: payload.text, lang: payload.lang, maxHeightClass: "max-h-[min(70vh,calc(var(--radix-popover-content-available-height,75vh)-4.5rem))]" }) })] })] }));
224
+ return (_jsxs(Popover, { open: open, onOpenChange: onOpenChange, children: [_jsx(PopoverTrigger, { asChild: true, children: children }), _jsxs(PopoverContent, { align: "start", side: "bottom", sideOffset: 6, collisionPadding: 12, className: "flex max-h-[min(calc(100vh-2rem),var(--radix-popover-content-available-height,75vh))] w-[min(calc(100vw-2rem),var(--radix-popover-content-available-width,760px),760px)] flex-col gap-0 overflow-hidden p-0", children: [_jsxs("div", { className: "flex shrink-0 items-center justify-between gap-3 border-b border-border px-4 py-3", children: [_jsx("div", { className: "truncate text-sm font-medium", children: title }), _jsxs("button", { type: "button", onClick: copyValue, className: "inline-flex h-7 items-center gap-1.5 rounded-md px-2 text-xs text-muted-foreground hover:bg-accent hover:text-foreground", children: [copied ? _jsx(IconCheck, { size: 13 }) : _jsx(IconCopy, { size: 13 }), copied ? t("agentChat.common.copied") : t("agentChat.common.copy")] })] }), _jsx("div", { className: "min-h-0 flex-1 overflow-hidden p-3", children: _jsx(SimpleCodeViewer, { text: payload.text, lang: payload.lang, maxHeightClass: "max-h-[min(70vh,calc(var(--radix-popover-content-available-height,75vh)-4.5rem))]" }) })] })] }));
220
225
  }
221
226
  // ─── Collapsible height animation ─────────────────────────────────────────────
222
227
  export function AnimatedCollapse({ open, children, }) {
@@ -241,6 +246,7 @@ export function AnimatedCollapse({ open, children, }) {
241
246
  * the prompt locally (the action stays un-run).
242
247
  */
243
248
  function ApprovalAffordance({ toolName, toolCallId, approval, }) {
249
+ const t = useT();
244
250
  const ctx = React.useContext(ApprovalContext);
245
251
  const [localResolution, setLocalResolution] = useState(null);
246
252
  const retainedResolution = ctx?.getApprovalResolution?.(approval.approvalKey, toolCallId) ?? null;
@@ -250,27 +256,27 @@ function ApprovalAffordance({ toolName, toolCallId, approval, }) {
250
256
  // Once resolved, collapse to a quiet note so a repository refresh cannot
251
257
  // restore the action buttons while the continuation is running.
252
258
  if (resolution === "approved") {
253
- return (_jsxs("div", { className: "mt-1.5 text-xs text-muted-foreground", children: ["Approved. Re-running ", toolName, "..."] }));
259
+ return (_jsx("div", { className: "mt-1.5 text-xs text-muted-foreground", children: t("agentChat.approval.approved", { tool: toolName }) }));
254
260
  }
255
261
  // Deny defaults to local-only (the action simply stays un-run). When the
256
262
  // host also provided `onDeny` (e.g. a Code session resolving its own
257
263
  // pending approval), it fires alongside the local state.
258
264
  if (resolution === "denied") {
259
- return (_jsxs("div", { className: "mt-1.5 text-xs text-muted-foreground", children: ["Denied. ", toolName, " did not run."] }));
265
+ return (_jsx("div", { className: "mt-1.5 text-xs text-muted-foreground", children: t("agentChat.approval.denied", { tool: toolName }) }));
260
266
  }
261
- return (_jsxs("div", { className: "mt-1.5 flex items-center gap-2 rounded-md border border-border bg-muted/40 px-2.5 py-1.5", children: [_jsx(IconShieldCheck, { className: "h-3.5 w-3.5 shrink-0 text-muted-foreground" }), _jsxs("span", { className: "mr-auto text-xs text-muted-foreground", children: ["Approve to run ", toolName, "?"] }), ctx && (_jsxs("button", { type: "button", onClick: () => {
267
+ return (_jsxs("div", { className: "mt-1.5 flex min-w-0 flex-wrap items-center gap-2 rounded-md border border-border bg-muted/40 px-2.5 py-1.5", children: [_jsx(IconShieldCheck, { className: "h-3.5 w-3.5 shrink-0 text-muted-foreground" }), _jsx("span", { className: "min-w-0 flex-1 text-xs text-muted-foreground", children: t("agentChat.approval.question", { tool: toolName }) }), ctx && (_jsxs("button", { type: "button", onClick: () => {
262
268
  setLocalResolution("approved");
263
269
  ctx.onApprovalResolved?.(approval.approvalKey, "approved", toolCallId);
264
270
  ctx.onApprove(approval.approvalKey);
265
- }, className: cn("inline-flex items-center gap-1 rounded-md px-2.5 py-1 text-xs font-medium transition-colors", "bg-foreground text-background hover:bg-foreground/90"), children: [_jsx(IconCheck, { className: "h-3.5 w-3.5" }), "Approve"] })), ctx?.onAlwaysAllow && (_jsxs("button", { type: "button", onClick: () => {
271
+ }, className: cn("inline-flex shrink-0 items-center gap-1 rounded-md px-2.5 py-1 text-xs font-medium transition-colors", "bg-foreground text-background hover:bg-foreground/90"), children: [_jsx(IconCheck, { className: "h-3.5 w-3.5" }), t("agentChat.approval.approve")] })), ctx?.onAlwaysAllow && (_jsxs("button", { type: "button", onClick: () => {
266
272
  setLocalResolution("approved");
267
273
  ctx.onApprovalResolved?.(approval.approvalKey, "approved", toolCallId);
268
274
  ctx.onAlwaysAllow?.(approval.approvalKey);
269
- }, title: "Approve and always allow this exact command", className: cn("inline-flex items-center gap-1 rounded-md border border-border px-2.5 py-1 text-xs font-medium transition-colors", "text-foreground hover:bg-muted"), children: [_jsx(IconShieldCheck, { className: "h-3.5 w-3.5" }), "Always allow"] })), _jsxs("button", { type: "button", onClick: () => {
275
+ }, title: t("agentChat.approval.alwaysAllowHint"), className: cn("inline-flex shrink-0 items-center gap-1 rounded-md border border-border px-2.5 py-1 text-xs font-medium transition-colors", "text-foreground hover:bg-muted"), children: [_jsx(IconShieldCheck, { className: "h-3.5 w-3.5" }), t("agentChat.approval.alwaysAllow")] })), _jsxs("button", { type: "button", onClick: () => {
270
276
  setLocalResolution("denied");
271
277
  ctx?.onApprovalResolved?.(approval.approvalKey, "denied", toolCallId);
272
278
  ctx?.onDeny?.(approval.approvalKey);
273
- }, className: cn("inline-flex items-center gap-1 rounded-md border border-border px-2.5 py-1 text-xs font-medium transition-colors", "text-foreground hover:bg-muted"), children: [_jsx(IconX, { className: "h-3.5 w-3.5" }), "Deny"] })] }));
279
+ }, className: cn("inline-flex shrink-0 items-center gap-1 rounded-md border border-border px-2.5 py-1 text-xs font-medium transition-colors", "text-foreground hover:bg-muted"), children: [_jsx(IconX, { className: "h-3.5 w-3.5" }), t("agentChat.approval.deny")] })] }));
274
280
  }
275
281
  // ─── ToolCallDisplay ──────────────────────────────────────────────────────────
276
282
  export function ToolCallDisplay({ toolName, toolCallId, argsText, args, result, mcpApp, chatUI, isRunning, outcome, structuredMeta, activity, approval, repeatCount, isLatestRunning = isRunning, isActiveTail, }) {
@@ -303,6 +309,7 @@ export function ToolCallDisplay({ toolName, toolCallId, argsText, args, result,
303
309
  return wrapToolDisplay(_jsx(ToolCallDisplayGeneric, { toolName: toolName, toolCallId: toolCallId, argsText: argsText, args: args, result: result, mcpApp: mcpApp, chatUI: chatUI, isRunning: effectiveIsRunning, outcome: outcome, isActiveTail: showActiveTail, structuredMeta: structuredMeta, approval: approval, repeatCount: repeatCount }));
304
310
  }
305
311
  function ToolCallDisplayGeneric({ toolName, toolCallId, argsText, args, result, mcpApp, chatUI, isRunning, outcome, isActiveTail, structuredMeta, approval, repeatCount, }) {
312
+ const t = useT();
306
313
  const suppressInlineOpenApp = React.useContext(SuppressInlineOpenAppContext);
307
314
  const isRawCallAgent = toolName === "call-agent";
308
315
  const isAgentCall = toolName.startsWith("agent:") || isRawCallAgent;
@@ -362,7 +369,7 @@ function ToolCallDisplayGeneric({ toolName, toolCallId, argsText, args, result,
362
369
  if (parsed.taskId && parsed.threadId) {
363
370
  return (_jsx(AgentTaskCard, { taskId: parsed.taskId, threadId: parsed.threadId, description: parsed.description ||
364
371
  args?.task ||
365
- "Sub-agent task", onOpen: (tid) => {
372
+ t("agentChat.tool.subAgentTask"), onOpen: (tid) => {
366
373
  window.dispatchEvent(new CustomEvent("agent-task-open", {
367
374
  detail: {
368
375
  threadId: tid,
@@ -398,30 +405,38 @@ function ToolCallDisplayGeneric({ toolName, toolCallId, argsText, args, result,
398
405
  if (NativeToolRenderer) {
399
406
  return (_jsx(ActionChatUiSurface, { context: nativeToolContext, isBuiltinDataWidget: isBuiltinDataWidgetActionRenderer(nativeToolContext), children: _jsx(NativeToolRenderer, { context: nativeToolContext }) }));
400
407
  }
401
- const inputPayload = hasArgs ? toolInputPayload(toolName, args) : null;
402
- const resultPayload = toolResultPayload(result);
408
+ const inputPayload = hasArgs
409
+ ? toolInputPayload(toolName, args, {
410
+ input: t("agentChat.tool.input"),
411
+ inputWithLabel: (label) => t("agentChat.tool.inputWithLabel", { label }),
412
+ })
413
+ : null;
414
+ const resultPayload = toolResultPayload(result, t("agentChat.tool.result"));
403
415
  const displayName = isAgentCall
404
416
  ? isRunning
405
- ? `Asking ${agentName}...`
417
+ ? t("agentChat.tool.askingAgent", { agent: agentName })
406
418
  : isAgentError
407
- ? `Error asking ${agentName}`
408
- : `Asked ${agentName}`
419
+ ? t("agentChat.tool.askingAgentFailed", { agent: agentName })
420
+ : t("agentChat.tool.askedAgent", { agent: agentName })
409
421
  : humanizeToolName(toolName);
410
422
  const canExpand = isAgentCall
411
423
  ? hasStreamText
412
424
  : hasArgs || result !== undefined;
413
425
  const isExpanded = isAgentCall ? hasStreamText && expanded : expanded;
414
426
  const ToolIcon = resolveToolIcon(toolName);
415
- const outputTitle = `Raw ${toolName} tool call output`;
427
+ const outputTitle = t("agentChat.tool.rawOutput", { tool: toolName });
416
428
  if (isAgentCall) {
417
- return (_jsx(AgentCallCell, { agentName: agentName ?? "agent", activity: agentActivity, progress: agentProgress, responseText: agentStreamText, isRunning: isRunning, isError: isAgentError, durationMs: typeof structuredMeta?.agentDurationMs === "number"
429
+ return (_jsx(AgentCallCell, { agentName: agentName ?? t("agentChat.common.agent"), activity: agentActivity, progress: agentProgress, responseText: agentStreamText, isRunning: isRunning, isError: isAgentError, durationMs: typeof structuredMeta?.agentDurationMs === "number"
418
430
  ? structuredMeta.agentDurationMs
419
431
  : agentActivity?.durationMs }));
420
432
  }
421
- return (_jsxs("div", { className: "group/tool my-0.5 w-full overflow-hidden", children: [mcpApp && !(suppressInlineOpenApp && toolName === "open_app") && (_jsx(McpAppRenderer, { app: mcpApp, className: "mb-1.5" })), _jsxs("button", { type: "button", onClick: () => canExpand && setExpanded(!isExpanded), "aria-expanded": canExpand ? isExpanded : undefined, className: cn("flex w-full items-center gap-1.5 rounded-md py-0.5 text-left text-[13px] text-muted-foreground transition-colors", canExpand && "hover:text-foreground", isRunning && "text-muted-foreground"), children: [_jsx("span", { className: "relative flex size-4 shrink-0 items-center justify-center", children: isRunning ? (_jsx(IconLoader2, { className: "size-3.5 animate-spin" })) : isAgentError ? (_jsx(IconCircleX, { className: "size-3.5 text-destructive" })) : isUnknownOutcome ? (_jsx(IconAlertTriangle, { className: "size-3.5 text-muted-foreground" })) : (_jsxs(_Fragment, { children: [_jsx(ToolIcon, { className: cn("size-3.5 transition-opacity", canExpand && "group-hover/tool:opacity-0") }), canExpand && (_jsx(IconChevronRight, { className: cn("absolute size-3.5 opacity-0 transition-[opacity,transform] group-hover/tool:opacity-100", isExpanded && "rotate-90") }))] })) }), _jsx("span", { className: cn("min-w-0 truncate font-normal", isActiveTail && "agent-running-shimmer"), children: displayName }), repeatCount && repeatCount > 1 && (_jsxs("span", { className: "shrink-0 rounded border border-border/60 px-1.5 py-0.5 text-[10px] leading-none text-muted-foreground", title: `Repeated ${repeatCount} times`, children: [repeatCount, "x"] }))] }), _jsx(AnimatedCollapse, { open: isExpanded && !isAgentCall && (hasArgs || result !== undefined), children: _jsxs("div", { className: "mt-1 space-y-2 pl-5", children: [inputPayload && (_jsx(SimpleCodeViewer, { text: inputPayload.text, lang: inputPayload.lang })), resultPayload && (_jsx(ToolOutputPopover, { open: outputOpen, onOpenChange: setOutputOpen, title: outputTitle, payload: resultPayload, children: _jsx("button", { type: "button", "aria-label": `View ${toolName} output`, className: "inline-flex size-6 items-center justify-center rounded-md text-muted-foreground/70 transition-colors hover:bg-accent hover:text-foreground", children: _jsx(IconCode, { className: "size-3.5" }) }) }))] }) }), isUnknownOutcome && (_jsx("p", { role: "status", className: "ps-5 text-xs text-muted-foreground", children: "Interrupted before this finished reporting \u2014 it may or may not have completed. Check before retrying." })), approval && (_jsx(ApprovalAffordance, { toolName: toolName, toolCallId: toolCallId, approval: approval }))] }));
433
+ return (_jsxs("div", { className: "group/tool my-0.5 w-full overflow-hidden", children: [mcpApp && !(suppressInlineOpenApp && toolName === "open_app") && (_jsx(McpAppRenderer, { app: mcpApp, className: "mb-1.5" })), _jsxs("button", { type: "button", onClick: () => canExpand && setExpanded(!isExpanded), "aria-expanded": canExpand ? isExpanded : undefined, className: cn("flex w-full items-center gap-1.5 rounded-md py-0.5 text-left text-[13px] text-muted-foreground transition-colors", canExpand && "hover:text-foreground", isRunning && "text-muted-foreground"), children: [_jsx("span", { className: "relative flex size-4 shrink-0 items-center justify-center", children: isRunning ? (_jsx(IconLoader2, { className: "size-3.5 animate-spin" })) : isAgentError ? (_jsx(IconCircleX, { className: "size-3.5 text-destructive" })) : isUnknownOutcome ? (_jsx(IconAlertTriangle, { className: "size-3.5 text-muted-foreground" })) : (_jsxs(_Fragment, { children: [_jsx(ToolIcon, { className: cn("size-3.5 transition-opacity", canExpand && "group-hover/tool:opacity-0") }), canExpand && (_jsx(IconChevronRight, { className: cn("absolute size-3.5 opacity-0 transition-[opacity,transform] group-hover/tool:opacity-100", isExpanded && "rotate-90") }))] })) }), _jsx("span", { className: cn("min-w-0 truncate font-normal", isActiveTail && "agent-running-shimmer"), children: displayName }), repeatCount && repeatCount > 1 && (_jsxs("span", { className: "shrink-0 rounded border border-border/60 px-1.5 py-0.5 text-[10px] leading-none text-muted-foreground", title: t("agentChat.tool.repeated", { count: repeatCount }), children: [repeatCount, "x"] }))] }), _jsx(AnimatedCollapse, { open: isExpanded && !isAgentCall && (hasArgs || result !== undefined), children: _jsxs("div", { className: "mt-1 space-y-2 pl-5", children: [inputPayload && (_jsx(SimpleCodeViewer, { text: inputPayload.text, lang: inputPayload.lang })), resultPayload && (_jsx(ToolOutputPopover, { open: outputOpen, onOpenChange: setOutputOpen, title: outputTitle, payload: resultPayload, children: _jsx("button", { type: "button", "aria-label": t("agentChat.tool.viewOutput", {
434
+ tool: toolName,
435
+ }), className: "inline-flex size-6 items-center justify-center rounded-md text-muted-foreground/70 transition-colors hover:bg-accent hover:text-foreground", children: _jsx(IconCode, { className: "size-3.5" }) }) }))] }) }), isUnknownOutcome && (_jsx("p", { role: "status", className: "ps-5 text-xs text-muted-foreground", children: t("agentChat.tool.interrupted") })), approval && (_jsx(ApprovalAffordance, { toolName: toolName, toolCallId: toolCallId, approval: approval }))] }));
422
436
  }
423
437
  function AgentCallCell({ agentName, activity, progress, responseText, isRunning, isError, durationMs, }) {
424
438
  const t = useT();
439
+ const formatDuration = useLocalizedWorkedDuration();
425
440
  const [open, setOpen] = useState(true);
426
441
  const toolCount = activity?.toolCalls?.length ?? 0;
427
442
  // Response segments are ordered against the tool calls that preceded them, so
@@ -434,10 +449,10 @@ function AgentCallCell({ agentName, activity, progress, responseText, isRunning,
434
449
  const work = activity?.reasoning?.length || toolCount || inlineSegments.length;
435
450
  const workItemCount = Math.max(activity?.reasoning?.length ?? 0, toolCount, inlineSegments.length);
436
451
  const label = isRunning
437
- ? t("agentPanel.delegatedAgent.asking", { name: agentName })
452
+ ? t("agentChat.tool.askingAgent", { agent: agentName })
438
453
  : isError
439
- ? t("agentPanel.delegatedAgent.error", { name: agentName })
440
- : t("agentPanel.delegatedAgent.asked", { name: agentName });
454
+ ? t("agentChat.tool.askingAgentFailed", { agent: agentName })
455
+ : t("agentChat.tool.askedAgent", { agent: agentName });
441
456
  const workContent = work ? (_jsx("div", { className: "space-y-1 ps-5", children: Array.from({ length: workItemCount }, (_, index) => {
442
457
  const reasoningText = activity?.reasoning?.[index];
443
458
  const segment = inlineSegments[index];
@@ -452,8 +467,8 @@ function AgentCallCell({ agentName, activity, progress, responseText, isRunning,
452
467
  const progressText = isRunning && !activity && progress && progressState
453
468
  ? [
454
469
  progressState.charAt(0).toUpperCase() + progressState.slice(1),
455
- t("agentPanel.delegatedAgent.elapsed", {
456
- duration: formatWorkedDuration(progress.elapsedSeconds * 1000),
470
+ t("agentChat.tool.elapsed", {
471
+ duration: formatDuration(progress.elapsedSeconds * 1000),
457
472
  }),
458
473
  progress.detail,
459
474
  ]
@@ -603,6 +618,8 @@ function isReconnectToolSummaryPart(content, index, startIndex) {
603
618
  */
604
619
  const WorkSummaryContentContext = React.createContext(false);
605
620
  export function ReasoningCell({ text, isStreaming = false, defaultOpen, autoCollapse = false, collapseWhenReplaced = false, durationMs, }) {
621
+ const t = useT();
622
+ const formatDuration = useLocalizedWorkedDuration();
606
623
  const embeddedInWorkSummary = React.useContext(WorkSummaryContentContext);
607
624
  const [open, setOpen] = useState(defaultOpen ?? true);
608
625
  const wasStreamingRef = useRef(isStreaming);
@@ -630,35 +647,54 @@ export function ReasoningCell({ text, isStreaming = false, defaultOpen, autoColl
630
647
  return (_jsx("div", { className: "pb-1 pl-5 text-[13px] leading-relaxed text-muted-foreground whitespace-pre-wrap", children: visibleText || (isStreaming ? "…" : "") }));
631
648
  }
632
649
  const label = isStreaming
633
- ? "Thinking"
650
+ ? t("agentChat.status.thinking")
634
651
  : durationMs != null
635
- ? `Thought for ${formatWorkedDuration(durationMs)}`
636
- : "Thought";
652
+ ? t("agentChat.tool.thoughtFor", {
653
+ duration: formatDuration(durationMs),
654
+ })
655
+ : t("agentChat.tool.thought");
637
656
  // Only clamp to a scroll-free "tail" view while actively streaming and
638
657
  // expanded — once the run finishes the full text is shown, unclamped.
639
658
  const showTail = isStreaming && open;
640
659
  return (_jsxs("div", { className: "my-0.5 w-full", children: [_jsxs("button", { type: "button", onClick: () => setOpen((v) => !v), "aria-expanded": open, className: "flex items-center gap-1.5 py-0.5 text-[13px] text-muted-foreground transition-colors hover:text-foreground", children: [_jsx(IconChevronRight, { className: cn("size-3.5 shrink-0 transition-transform", open && "rotate-90") }), isStreaming ? (_jsx("span", { className: "agent-thinking-indicator__text", children: label })) : (_jsx("span", { children: label }))] }), _jsx(AnimatedCollapse, { open: open, children: _jsx("div", { className: cn("pl-5 pb-1", showTail && "reasoning-cell-tail"), children: _jsx("div", { className: "text-[13px] leading-relaxed text-muted-foreground whitespace-pre-wrap", children: visibleText || (isStreaming ? "…" : "") }) }) })] }));
641
660
  }
642
661
  // ─── Worked-for duration helpers ──────────────────────────────────────────────
643
- export function formatWorkedDuration(ms) {
662
+ export function formatWorkedDuration(ms, options = {}) {
663
+ const number = new Intl.NumberFormat(options.locale ?? "en-US");
664
+ const hour = options.hour ?? "h";
665
+ const minute = options.minute ?? "m";
666
+ const second = options.second ?? "s";
667
+ const part = (value, unit) => `${number.format(value)}${unit}`;
644
668
  const totalSeconds = Math.max(0, Math.round(ms / 1000));
645
669
  if (totalSeconds < 60) {
646
- return totalSeconds <= 1 ? "1s" : `${totalSeconds}s`;
670
+ return part(Math.max(1, totalSeconds), second);
647
671
  }
648
672
  const minutes = Math.floor(totalSeconds / 60);
649
673
  const seconds = totalSeconds % 60;
650
674
  if (minutes < 60) {
651
675
  if (seconds === 0)
652
- return `${minutes}m`;
653
- return `${minutes}m ${seconds}s`;
676
+ return part(minutes, minute);
677
+ return `${part(minutes, minute)} ${part(seconds, second)}`;
654
678
  }
655
679
  const hours = Math.floor(minutes / 60);
656
680
  const remMinutes = minutes % 60;
657
681
  if (remMinutes === 0)
658
- return `${hours}h`;
659
- return `${hours}h ${remMinutes}m`;
682
+ return part(hours, hour);
683
+ return `${part(hours, hour)} ${part(remMinutes, minute)}`;
684
+ }
685
+ export function useLocalizedWorkedDuration() {
686
+ const t = useT();
687
+ const locale = useOptionalLocale()?.locale ?? "en-US";
688
+ return useCallback((ms) => formatWorkedDuration(ms, {
689
+ locale,
690
+ hour: t("agentChat.duration.hourShort"),
691
+ minute: t("agentChat.duration.minuteShort"),
692
+ second: t("agentChat.duration.secondShort"),
693
+ }), [locale, t]);
660
694
  }
661
695
  export function WorkedForSummary({ durationMs, defaultOpen = false, autoCollapse = false, children, }) {
696
+ const t = useT();
697
+ const formatDuration = useLocalizedWorkedDuration();
662
698
  // Ordinary completed work starts closed so a remount never flashes details
663
699
  // while auto-collapse settles. Interactive UI opts into an open summary.
664
700
  const [open, setOpen] = useState(defaultOpen);
@@ -671,13 +707,16 @@ export function WorkedForSummary({ durationMs, defaultOpen = false, autoCollapse
671
707
  }
672
708
  }, [autoCollapse, defaultOpen]);
673
709
  const label = durationMs != null && durationMs >= 1000
674
- ? `Worked for ${formatWorkedDuration(durationMs)}`
675
- : "Worked";
710
+ ? t("agentChat.tool.workedFor", {
711
+ duration: formatDuration(durationMs),
712
+ })
713
+ : t("agentChat.tool.worked");
676
714
  return (_jsxs("div", { className: "my-1 w-full", children: [_jsxs("button", { type: "button", onClick: () => setOpen((v) => !v), "aria-expanded": open, className: "flex items-center gap-1.5 py-0.5 text-[13px] text-muted-foreground transition-colors hover:text-foreground", children: [_jsx("span", { children: label }), _jsx(IconChevronRight, { className: cn("size-3.5 shrink-0 transition-transform", open && "rotate-90") })] }), _jsx(AnimatedCollapse, { open: open, children: _jsx(WorkSummaryContentContext.Provider, { value: true, children: _jsx("div", { className: "pt-1", children: children }) }) })] }));
677
715
  }
678
716
  export function RanToolsSummary({ toolCount, motionKey = "summary", children, }) {
717
+ const t = useT();
679
718
  const [open, setOpen] = useState(false);
680
- const label = `Ran ${toolCount} ${toolCount === 1 ? "tool" : "tools"}`;
719
+ const label = t("agentChat.tool.ranTools", { count: toolCount });
681
720
  return (_jsxs("div", { className: "agent-tool-summary my-1 w-full", "data-agent-tool-summary": motionKey, children: [_jsxs("button", { type: "button", onClick: () => setOpen((v) => !v), "aria-expanded": open, className: "flex items-center gap-1.5 py-0.5 text-[13px] text-muted-foreground transition-colors hover:text-foreground", children: [_jsx("span", { className: "agent-tool-summary__label", children: label }), _jsx(IconChevronRight, { className: cn("size-3.5 shrink-0 transition-transform", open && "rotate-90") })] }), _jsx(AnimatedCollapse, { open: open, children: _jsx("div", { className: "pt-1", children: children }) })] }));
682
721
  }
683
722
  // ─── Re-export for AssistantMessage ───────────────────────────────────────────
@@ -1,4 +1,5 @@
1
1
  import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useT } from "../../i18n.js";
2
3
  const DEFAULT_COLORS = [
3
4
  "hsl(var(--primary))",
4
5
  "hsl(199 89% 48%)",
@@ -105,5 +106,6 @@ function ChartBody({ chart }) {
105
106
  return (_jsxs(_Fragment, { children: [axes, paths.map(({ series, color, areaPath }) => chart.type === "area" && areaPath ? (_jsx("path", { d: areaPath, fill: color, opacity: "0.18", stroke: "none" }, series.key)) : null), paths.map(({ series, color, linePath, points }) => (_jsxs("g", { children: [_jsx("path", { d: linePath, fill: "none", stroke: color, strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round" }), points.map((point, index) => (_jsx("circle", { cx: point.x, cy: point.y, r: "3", fill: "hsl(var(--background))", stroke: color, strokeWidth: "2" }, index)))] }, series.key)))] }));
106
107
  }
107
108
  export function DataChartRenderer({ chart }) {
108
- return (_jsx("div", { className: "h-60 w-full min-w-0 overflow-hidden", children: _jsx("svg", { className: "agent-data-chart-renderer h-full w-full", viewBox: `0 0 ${WIDTH} ${HEIGHT}`, role: "img", "aria-label": chart.title ?? "Data chart", preserveAspectRatio: "none", children: _jsx(ChartBody, { chart: chart }) }) }));
109
+ const t = useT();
110
+ return (_jsx("div", { className: "h-60 w-full min-w-0 overflow-hidden", children: _jsx("svg", { className: "agent-data-chart-renderer h-full w-full", viewBox: `0 0 ${WIDTH} ${HEIGHT}`, role: "img", "aria-label": chart.title ?? t("agentChat.widget.dataChart"), preserveAspectRatio: "none", children: _jsx(ChartBody, { chart: chart }) }) }));
109
111
  }
@@ -1,14 +1,20 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { IconChartBar } from "@tabler/icons-react";
3
3
  import { lazy, Suspense, useEffect, useState } from "react";
4
+ import { DEFAULT_LOCALE, useOptionalLocale, useT } from "../../i18n.js";
4
5
  const LazyDataChartRenderer = lazy(() => import("./DataChartRenderer.js").then((module) => ({
5
6
  default: module.DataChartRenderer,
6
7
  })));
7
8
  export function DataChartWidget({ chart }) {
9
+ const t = useT();
10
+ const locale = useOptionalLocale()?.locale ?? DEFAULT_LOCALE;
8
11
  const [mounted, setMounted] = useState(false);
9
12
  useEffect(() => {
10
13
  setMounted(true);
11
14
  }, []);
12
- const fallback = (_jsx("div", { className: "flex h-60 items-center justify-center rounded-md bg-muted/30 text-xs text-muted-foreground", children: "Chart" }));
13
- return (_jsxs("div", { className: "my-1.5 overflow-hidden rounded-lg border border-border bg-background text-foreground shadow-sm", children: [_jsxs("div", { className: "flex items-center gap-2 border-b border-border px-3 py-2", children: [_jsx(IconChartBar, { className: "h-4 w-4 shrink-0 text-muted-foreground" }), _jsxs("div", { className: "min-w-0 flex-1", children: [_jsx("div", { className: "truncate text-sm font-medium", children: chart.title ?? "Data chart" }), _jsxs("div", { className: "text-[11px] text-muted-foreground", children: [chart.data.length.toLocaleString(), " point", chart.data.length === 1 ? "" : "s", chart.sampled ? " sampled" : ""] })] })] }), _jsx("div", { className: "p-3", children: !mounted ? (fallback) : (_jsx(Suspense, { fallback: fallback, children: _jsx(LazyDataChartRenderer, { chart: chart }) })) })] }));
15
+ const fallback = (_jsx("div", { className: "flex h-60 items-center justify-center rounded-md bg-muted/30 text-xs text-muted-foreground", children: t("agentChat.widget.chart") }));
16
+ return (_jsxs("div", { className: "my-1.5 overflow-hidden rounded-lg border border-border bg-background text-foreground shadow-sm", children: [_jsxs("div", { className: "flex items-center gap-2 border-b border-border px-3 py-2", children: [_jsx(IconChartBar, { className: "h-4 w-4 shrink-0 text-muted-foreground" }), _jsxs("div", { className: "min-w-0 flex-1", children: [_jsx("div", { className: "truncate text-sm font-medium", children: chart.title ?? t("agentChat.widget.dataChart") }), _jsxs("div", { className: "text-[11px] text-muted-foreground", children: [t("agentChat.widget.points", {
17
+ count: chart.data.length,
18
+ formattedCount: chart.data.length.toLocaleString(locale),
19
+ }), chart.sampled ? ` ${t("agentChat.widget.sampled")}` : ""] })] })] }), _jsx("div", { className: "p-3", children: !mounted ? (fallback) : (_jsx(Suspense, { fallback: fallback, children: _jsx(LazyDataChartRenderer, { chart: chart }) })) })] }));
14
20
  }
@@ -1,5 +1,6 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { IconDatabase } from "@tabler/icons-react";
3
+ import { useT } from "../../i18n.js";
3
4
  import { DataChartWidget } from "./DataChartWidget.js";
4
5
  import { DataTableWidget } from "./DataTableWidget.js";
5
6
  function SummaryPill({ label, value }) {
@@ -8,7 +9,8 @@ function SummaryPill({ label, value }) {
8
9
  return (_jsxs("div", { className: "rounded-md border border-border bg-muted/30 px-2 py-1", children: [_jsx("div", { className: "text-[10px] uppercase tracking-wide text-muted-foreground", children: label }), _jsx("div", { className: "text-xs font-medium text-foreground", children: typeof value === "number" ? value.toLocaleString() : String(value) })] }));
9
10
  }
10
11
  export function DataInsightsWidget({ result }) {
11
- const title = result.display?.title ?? result.title ?? "Data insights";
12
+ const t = useT();
13
+ const title = result.display?.title ?? result.title ?? t("agentChat.widget.dataInsights");
12
14
  const summary = result.summary ?? {};
13
15
  return (_jsxs("div", { className: "my-1.5 space-y-2", children: [_jsxs("div", { className: "rounded-lg border border-border bg-background p-3 text-foreground shadow-sm", children: [_jsxs("div", { className: "mb-2 flex items-center gap-2", children: [_jsx(IconDatabase, { className: "h-4 w-4 shrink-0 text-muted-foreground" }), _jsxs("div", { className: "min-w-0", children: [_jsx("div", { className: "truncate text-sm font-medium", children: title }), result.display?.description ? (_jsx("div", { className: "text-xs text-muted-foreground", children: result.display.description })) : null] })] }), _jsx("div", { className: "grid grid-cols-2 gap-2 sm:grid-cols-4", children: Object.entries(summary)
14
16
  .filter(([, value]) => typeof value !== "object")
@@ -4,23 +4,26 @@ import { requestAgentSidebarOpen } from "../../agent-sidebar-state.js";
4
4
  import { appPath } from "../../api-path.js";
5
5
  import { startAgentChatViewTransition } from "../../chat-view-transition.js";
6
6
  import { downloadFile, toCSVTable } from "../../db-admin/export-utils.js";
7
+ import { DEFAULT_LOCALE, useOptionalLocale, useT } from "../../i18n.js";
7
8
  import { cn } from "../../utils.js";
8
9
  const SAFE_ACTION_PROTOCOLS = new Set(["http:", "https:", "mailto:"]);
9
- function formatCell(value) {
10
+ function formatCell(value, locale, booleanLabels = { yes: "Yes", no: "No" }) {
10
11
  if (value === undefined || value === null || value === "")
11
12
  return "—";
12
13
  if (typeof value === "boolean")
13
- return value ? "Yes" : "No";
14
+ return value ? booleanLabels.yes : booleanLabels.no;
14
15
  if (typeof value === "number")
15
- return value.toLocaleString();
16
+ return value.toLocaleString(locale);
16
17
  if (Array.isArray(value))
17
- return value.map(formatCell).join(", ");
18
+ return value
19
+ .map((item) => formatCell(item, locale, booleanLabels))
20
+ .join(", ");
18
21
  if (typeof value === "object")
19
22
  return JSON.stringify(value);
20
23
  const text = String(value);
21
24
  const timestamp = Date.parse(text);
22
25
  if (Number.isFinite(timestamp) && /^\d{4}-\d{2}-\d{2}T/.test(text)) {
23
- return new Intl.DateTimeFormat(undefined, {
26
+ return new Intl.DateTimeFormat(locale, {
24
27
  month: "short",
25
28
  day: "numeric",
26
29
  hour: "numeric",
@@ -83,6 +86,8 @@ function slugifyFilenamePart(value) {
83
86
  return slug || "data-table";
84
87
  }
85
88
  export function DataTableWidget({ table, action, }) {
89
+ const t = useT();
90
+ const locale = useOptionalLocale()?.locale ?? DEFAULT_LOCALE;
86
91
  const rows = table.rows ?? [];
87
92
  const actionHref = action ? normalizeActionHref(action.href) : null;
88
93
  const canExportCsv = table.columns.length > 0;
@@ -92,9 +97,15 @@ export function DataTableWidget({ table, action, }) {
92
97
  const stamp = new Date().toISOString().slice(0, 10);
93
98
  downloadFile(`${slugifyFilenamePart(table.title)}-${stamp}.csv`, "text/csv;charset=utf-8", toCSVTable(table.columns.map((column) => column.label), rows.map((row) => table.columns.map((column) => row[column.key]))));
94
99
  };
95
- return (_jsxs("div", { className: "my-1.5 overflow-hidden rounded-lg border border-border bg-background text-foreground shadow-sm", children: [_jsxs("div", { className: "flex items-center gap-2 border-b border-border px-3 py-2", children: [_jsx(IconTable, { className: "h-4 w-4 shrink-0 text-muted-foreground" }), _jsxs("div", { className: "min-w-0 flex-1", children: [_jsx("div", { className: "truncate text-sm font-medium", children: table.title ?? "Data table" }), _jsxs("div", { className: "text-[11px] text-muted-foreground", children: [typeof table.totalRows === "number"
96
- ? `${table.totalRows.toLocaleString()} row${table.totalRows === 1 ? "" : "s"}`
97
- : `${rows.length.toLocaleString()} row${rows.length === 1 ? "" : "s"}`, table.truncated ? " sampled" : ""] })] }), action && actionHref ? (_jsxs("a", { href: actionHref, onClick: (event) => {
100
+ return (_jsxs("div", { className: "my-1.5 overflow-hidden rounded-lg border border-border bg-background text-foreground shadow-sm", children: [_jsxs("div", { className: "flex items-center gap-2 border-b border-border px-3 py-2", children: [_jsx(IconTable, { className: "h-4 w-4 shrink-0 text-muted-foreground" }), _jsxs("div", { className: "min-w-0 flex-1", children: [_jsx("div", { className: "truncate text-sm font-medium", children: table.title ?? t("agentChat.widget.dataTable") }), _jsxs("div", { className: "text-[11px] text-muted-foreground", children: [typeof table.totalRows === "number"
101
+ ? t("agentChat.widget.rows", {
102
+ count: table.totalRows,
103
+ formattedCount: table.totalRows.toLocaleString(locale),
104
+ })
105
+ : t("agentChat.widget.rows", {
106
+ count: rows.length,
107
+ formattedCount: rows.length.toLocaleString(locale),
108
+ }), table.truncated ? ` ${t("agentChat.widget.sampled")}` : ""] })] }), action && actionHref ? (_jsxs("a", { href: actionHref, onClick: (event) => {
98
109
  if (event.defaultPrevented ||
99
110
  event.button !== 0 ||
100
111
  event.metaKey ||
@@ -106,5 +117,8 @@ export function DataTableWidget({ table, action, }) {
106
117
  }
107
118
  event.preventDefault();
108
119
  navigateSameAppHref(actionHref);
109
- }, className: "inline-flex shrink-0 items-center gap-1 rounded-md border border-border px-2 py-1 text-[11px] font-medium text-foreground no-underline hover:bg-muted hover:no-underline", children: [action.label, _jsx(IconExternalLink, { className: "h-3 w-3" })] })) : null, _jsxs("button", { type: "button", onClick: exportCsv, disabled: !canExportCsv, className: "inline-flex shrink-0 items-center gap-1 rounded-md bg-primary px-2 py-1 text-[11px] font-medium text-primary-foreground transition-colors hover:bg-primary/90 disabled:pointer-events-none disabled:opacity-50", children: [_jsx(IconDownload, { className: "h-3 w-3" }), "Download CSV"] })] }), _jsx("div", { className: "max-h-[360px] overflow-auto", children: _jsxs("table", { className: "w-full min-w-[420px] border-collapse text-xs", children: [_jsx("thead", { className: "sticky top-0 z-10 bg-muted/70 backdrop-blur", children: _jsx("tr", { children: table.columns.map((column) => (_jsx("th", { className: cn("border-b border-border px-3 py-2 font-medium text-muted-foreground", column.align === "right" ? "text-right" : "text-left"), children: column.label }, column.key))) }) }), _jsx("tbody", { children: rows.length === 0 ? (_jsx("tr", { children: _jsx("td", { colSpan: Math.max(1, table.columns.length), className: "px-3 py-8 text-center text-muted-foreground", children: "No rows" }) })) : (rows.map((row, rowIndex) => (_jsx("tr", { className: "border-b border-border/70 last:border-0", children: table.columns.map((column) => (_jsx("td", { className: cn("max-w-[260px] px-3 py-2 align-top text-foreground", column.align === "right" ? "text-right" : "text-left"), children: _jsx("span", { className: "line-clamp-3 break-words", children: formatCell(row[column.key]) }) }, `${rowIndex}-${column.key}`))) }, String(row.id ?? rowIndex))))) })] }) })] }));
120
+ }, className: "inline-flex shrink-0 items-center gap-1 rounded-md border border-border px-2 py-1 text-[11px] font-medium text-foreground no-underline hover:bg-muted hover:no-underline", children: [action.label, _jsx(IconExternalLink, { className: "h-3 w-3" })] })) : null, _jsxs("button", { type: "button", onClick: exportCsv, disabled: !canExportCsv, className: "inline-flex shrink-0 items-center gap-1 rounded-md bg-primary px-2 py-1 text-[11px] font-medium text-primary-foreground transition-colors hover:bg-primary/90 disabled:pointer-events-none disabled:opacity-50", children: [_jsx(IconDownload, { className: "h-3 w-3" }), t("agentChat.widget.downloadCsv")] })] }), _jsx("div", { className: "max-h-[360px] overflow-auto", children: _jsxs("table", { className: "w-full min-w-[420px] border-collapse text-xs", children: [_jsx("thead", { className: "sticky top-0 z-10 bg-muted/70 backdrop-blur", children: _jsx("tr", { children: table.columns.map((column) => (_jsx("th", { className: cn("border-b border-border px-3 py-2 font-medium text-muted-foreground", column.align === "right" ? "text-right" : "text-left"), children: column.label }, column.key))) }) }), _jsx("tbody", { children: rows.length === 0 ? (_jsx("tr", { children: _jsx("td", { colSpan: Math.max(1, table.columns.length), className: "px-3 py-8 text-center text-muted-foreground", children: t("agentChat.widget.noRows") }) })) : (rows.map((row, rowIndex) => (_jsx("tr", { className: "border-b border-border/70 last:border-0", children: table.columns.map((column) => (_jsx("td", { className: cn("max-w-[260px] px-3 py-2 align-top text-foreground", column.align === "right" ? "text-right" : "text-left"), children: _jsx("span", { className: "line-clamp-3 break-words", children: formatCell(row[column.key], locale, {
121
+ yes: t("agentChat.common.yes"),
122
+ no: t("agentChat.common.no"),
123
+ }) }) }, `${rowIndex}-${column.key}`))) }, String(row.id ?? rowIndex))))) })] }) })] }));
110
124
  }
@@ -1,6 +1,7 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { lazy, Suspense } from "react";
3
3
  import { ACTION_CHAT_UI_DATA_CHART_RENDERER, ACTION_CHAT_UI_DATA_INSIGHTS_RENDERER, ACTION_CHAT_UI_DATA_TABLE_RENDERER, ACTION_CHAT_UI_DATA_WIDGET_RENDERER, ACTION_CHAT_UI_INLINE_EXTENSION_RENDERER, ACTION_CHAT_UI_WORKSPACE_FILE_RENDERER, } from "../../../action-ui.js";
4
+ import { useT } from "../../i18n.js";
4
5
  import { registerReservedActionChatRenderer, registerReservedFallbackToolRenderer, } from "../tool-render-registry.js";
5
6
  import { DATA_CHART_WIDGET, DATA_INSIGHTS_WIDGET, DATA_TABLE_WIDGET, normalizeDataWidgetKind, normalizeDataWidgetResult, } from "./data-widget-types.js";
6
7
  import { normalizeInlineExtensionToolResult } from "./inline-extension-result.js";
@@ -80,7 +81,8 @@ function renderDataWidget(context) {
80
81
  return null;
81
82
  }
82
83
  function BuiltinToolRendererSkeleton({ framed = true }) {
83
- return (_jsx("div", { "aria-label": "Loading tool result", className: framed
84
+ const t = useT();
85
+ return (_jsx("div", { "aria-label": t("agentChat.widget.loadingToolResult"), className: framed
84
86
  ? "my-1.5 h-24 animate-pulse rounded-lg border border-border bg-muted/30"
85
87
  : "h-24 animate-pulse bg-muted/30" }));
86
88
  }
@@ -5,6 +5,11 @@ import { memo, useMemo, useState } from "react";
5
5
  import { orderChatFirstAppIds, readChatFirstAppLayout, writeChatFirstAppLayout, } from "../chat-first.js";
6
6
  import { cn } from "../utils.js";
7
7
  import { defaultChatFirstCopy } from "./copy.js";
8
+ function ChatFirstRailAppIcon({ app, activeAppId, renderIcon, }) {
9
+ const isActive = activeAppId !== undefined && activeAppId === app.id;
10
+ const isInactive = activeAppId !== undefined && !isActive;
11
+ return (_jsx("span", { "data-chat-first-app-icon": true, className: cn("transition-[filter]", isInactive && "grayscale"), children: renderIcon(app, { isActive, isInactive }) }));
12
+ }
8
13
  function AppRows({ apps, activeAppId, layout, onDragStart, onDrop, onDragEnd, onOpenApp, onRemoveApp, onTogglePinned, onMove, renderIcon, copy, }) {
9
14
  const orderedIds = orderChatFirstAppIds(apps.map((app) => app.id), layout);
10
15
  const appsById = new Map(apps.map((app) => [app.id, app]));
@@ -16,7 +21,7 @@ function AppRows({ apps, activeAppId, layout, onDragStart, onDrop, onDragEnd, on
16
21
  const pinned = layout.pinnedIds.includes(app.id);
17
22
  const index = orderedApps.indexOf(app);
18
23
  return (_jsxs(ContextMenu, { children: [_jsx(ContextMenuTrigger, { asChild: true, children: _jsxs("li", { draggable: true, "data-chat-first-app": true, "data-app-id": app.id, className: cn("group flex h-8 w-full min-w-0 items-center gap-1 rounded-md px-0 text-sm", active
19
- ? "bg-sidebar-accent font-medium text-sidebar-accent-foreground"
24
+ ? "font-medium text-sidebar-foreground"
20
25
  : "text-sidebar-foreground/75 hover:bg-sidebar-accent hover:text-sidebar-accent-foreground"), onDragStart: (event) => {
21
26
  event.dataTransfer.effectAllowed = "move";
22
27
  onDragStart(app.id);
@@ -31,7 +36,7 @@ function AppRows({ apps, activeAppId, layout, onDragStart, onDrop, onDragEnd, on
31
36
  event.preventDefault();
32
37
  onMove(app.id, 1);
33
38
  }
34
- }, "aria-keyshortcuts": "Alt+ArrowUp Alt+ArrowDown", "aria-label": copy("openApp", { name: app.name }), children: [renderIcon(app), _jsx("span", { className: "truncate", children: app.name })] }), _jsx("button", { type: "button", className: cn("flex size-6 shrink-0 items-center justify-center rounded text-sidebar-foreground/45 opacity-0 hover:bg-sidebar-accent hover:text-sidebar-foreground group-hover:opacity-100 focus-visible:opacity-100", pinned && "text-sidebar-foreground/70 opacity-100"), "aria-label": copy(pinned ? "unpinApp" : "pinApp", {
39
+ }, "aria-keyshortcuts": "Alt+ArrowUp Alt+ArrowDown", "aria-label": copy("openApp", { name: app.name }), children: [_jsx(ChatFirstRailAppIcon, { app: app, activeAppId: activeAppId, renderIcon: renderIcon }), _jsx("span", { className: "truncate", children: app.name })] }), _jsx("button", { type: "button", className: cn("flex size-6 shrink-0 items-center justify-center rounded text-sidebar-foreground/45 opacity-0 hover:bg-sidebar-accent hover:text-sidebar-foreground group-hover:opacity-100 focus-visible:opacity-100", pinned && "text-sidebar-foreground/70 opacity-100"), "aria-label": copy(pinned ? "unpinApp" : "pinApp", {
35
40
  name: app.name,
36
41
  }), "aria-pressed": pinned, onClick: () => onTogglePinned(app.id), children: _jsx(IconPin, { size: 13, strokeWidth: pinned ? 2.2 : 1.6, "aria-hidden": "true" }) }), _jsx("span", { className: "sr-only", children: `${index + 1} of ${orderedApps.length}` })] }) }), _jsxs(ContextMenuContent, { children: [_jsxs(ContextMenuItem, { onSelect: () => onTogglePinned(app.id), children: [_jsx(IconPin, { size: 14, "aria-hidden": "true" }), pinned ? copy("removePinned") : copy("pinTop")] }), _jsx(ContextMenuSeparator, {}), _jsxs(ContextMenuItem, { disabled: index === 0, onSelect: () => onMove(app.id, -1), children: [_jsx(IconChevronUp, { size: 14, "aria-hidden": "true" }), copy("moveUp")] }), _jsxs(ContextMenuItem, { disabled: index === orderedApps.length - 1, onSelect: () => onMove(app.id, 1), children: [_jsx(IconChevronDown, { size: 14, "aria-hidden": "true" }), copy("moveDown")] }), onRemoveApp ? (_jsxs(_Fragment, { children: [_jsx(ContextMenuSeparator, {}), _jsxs(ContextMenuItem, { onSelect: () => onRemoveApp(app), className: "text-destructive focus:text-destructive", children: [_jsx(IconTrash, { size: 14, "aria-hidden": "true" }), copy("removeApp")] })] })) : null] })] }, app.id));
37
42
  }) }));
@@ -96,8 +101,8 @@ export const ChatFirstAppsRail = memo(function ChatFirstAppsRail({ apps, activeA
96
101
  return (_jsxs("section", { "data-chat-first-apps-rail": true, className: "flex flex-col items-center gap-1 px-1.5 pt-2", "aria-label": copy("workspaceApps"), children: [createTrigger, loading && apps.length === 0
97
102
  ? [0, 1, 2].map((index) => (_jsx(Skeleton, { className: "size-9 rounded-md" }, index)))
98
103
  : visibleApps.map((app) => (_jsx("button", { type: "button", "data-chat-first-app": true, "data-app-id": app.id, className: cn("flex size-9 items-center justify-center rounded-md", activeAppId === app.id
99
- ? "bg-sidebar-accent text-sidebar-accent-foreground"
100
- : "text-sidebar-foreground/70 hover:bg-sidebar-accent hover:text-sidebar-accent-foreground"), onClick: () => onOpenApp(app), "aria-label": copy("openApp", { name: app.name }), title: app.name, children: renderIcon(app) }, app.id))), onOpenAllApps ? (_jsx("button", { type: "button", "data-chat-first-all-apps": true, className: "flex size-9 items-center justify-center rounded-md text-sidebar-foreground/70 hover:bg-sidebar-accent hover:text-sidebar-accent-foreground", onClick: onOpenAllApps, "aria-label": copy("allApps"), title: copy("allApps"), children: _jsx(IconApps, { size: 16, "aria-hidden": "true" }) })) : null, error ? (_jsx("span", { className: "size-1.5 rounded-full bg-destructive", title: error, "aria-label": error })) : null] }));
104
+ ? "text-sidebar-foreground"
105
+ : "text-sidebar-foreground/70 hover:bg-sidebar-accent hover:text-sidebar-accent-foreground"), onClick: () => onOpenApp(app), "aria-label": copy("openApp", { name: app.name }), title: app.name, children: _jsx(ChatFirstRailAppIcon, { app: app, activeAppId: activeAppId, renderIcon: renderIcon }) }, app.id))), onOpenAllApps ? (_jsx("button", { type: "button", "data-chat-first-all-apps": true, className: "flex size-9 items-center justify-center rounded-md text-sidebar-foreground/70 hover:bg-sidebar-accent hover:text-sidebar-accent-foreground", onClick: onOpenAllApps, "aria-label": copy("allApps"), title: copy("allApps"), children: _jsx(IconApps, { size: 16, "aria-hidden": "true" }) })) : null, error ? (_jsx("span", { className: "size-1.5 rounded-full bg-destructive", title: error, "aria-label": error })) : null] }));
101
106
  }
102
107
  return (_jsxs("section", { "data-chat-first-apps-rail": true, className: "mt-3 px-2 pb-2 pt-2", "aria-label": copy("workspaceApps"), children: [_jsxs("div", { className: "mb-1 flex items-center gap-1.5 px-2 text-[11px] font-medium text-sidebar-foreground/50", children: [_jsx("span", { children: copy("workspaceApps") }), _jsx("span", { className: "ml-auto", children: createTrigger })] }), loading && apps.length === 0 ? (_jsx("div", { className: "space-y-0.5 px-2", children: [0, 1, 2].map((index) => (_jsx(Skeleton, { className: "h-8 w-full rounded-md" }, index))) })) : apps.length === 0 ? (_jsxs("div", { className: "px-2", children: [_jsx("p", { className: "text-xs text-sidebar-foreground/55", children: copy("noWorkspaceApps") }), createAppTrigger ? (createTrigger) : onCreateApp ? (_jsxs("button", { type: "button", className: "mt-1 inline-flex h-6 items-center gap-1.5 rounded-md border border-sidebar-border px-2 text-xs text-sidebar-foreground/75 hover:bg-sidebar-accent hover:text-sidebar-foreground", onClick: onCreateApp, children: [_jsx(IconPlus, { size: 13, "aria-hidden": "true" }), copy("createApp")] })) : (createTrigger)] })) : (_jsx(AppRows, { apps: visibleApps, activeAppId: activeAppId, layout: layout, onDragStart: setDraggedAppId, onDrop: reorderApps, onDragEnd: () => setDraggedAppId(null), onOpenApp: onOpenApp, onRemoveApp: onRemoveApp, onTogglePinned: togglePinned, onMove: moveApp, renderIcon: renderIcon, copy: copy })), hasMoreApps || showAllApps ? (_jsxs("button", { type: "button", className: "mt-0.5 flex h-8 w-full items-center gap-2 rounded-md px-2 text-xs text-sidebar-foreground/50 hover:bg-sidebar-accent hover:text-sidebar-foreground", onClick: () => setShowAllApps((value) => !value), children: [showAllApps ? (_jsx(IconChevronUp, { size: 14, "aria-hidden": "true" })) : (_jsx(IconChevronDown, { size: 14, "aria-hidden": "true" })), showAllApps ? copy("showLess") : copy("showMore")] })) : null, onOpenAllApps ? (_jsx("div", { className: "mt-1 border-t border-sidebar-border/60 pt-1", children: _jsxs("button", { type: "button", "data-chat-first-all-apps": true, className: "flex h-8 w-full items-center gap-2 rounded-md px-2 text-xs text-sidebar-foreground/55 hover:bg-sidebar-accent hover:text-sidebar-foreground", onClick: onOpenAllApps, children: [_jsx(IconApps, { size: 14, "aria-hidden": "true" }), _jsx("span", { children: copy("allApps") })] }) })) : null, error ? (_jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsxs("button", { type: "button", className: "mt-1 flex items-center gap-1 px-2 text-[10px] text-destructive/80", onClick: onRetry, "aria-label": error, children: [_jsx("span", { className: "size-1.5 rounded-full bg-destructive" }), onRetry ? copy("retry") : copy("appsLoadError")] }) }), _jsx(TooltipContent, { side: "right", children: error })] })) : null] }));
103
108
  });
@@ -1,11 +1,12 @@
1
1
  import type { ChatFirstCopy } from "./types.js";
2
2
  export type ChatFirstPrimaryTab = "new-chat" | "integrations" | "scheduled";
3
- export declare function ChatFirstPrimaryNavigation({ onNewChat, onOpenIntegrations, onOpenScheduled, onSearch, activeTab, stickyNewChat, copy, }: {
3
+ export declare function ChatFirstPrimaryNavigation({ onNewChat, onOpenIntegrations, onOpenScheduled, onSearch, activeTab, collapsed, stickyNewChat, copy, }: {
4
4
  onNewChat?: () => void;
5
5
  onOpenIntegrations: () => void;
6
6
  onOpenScheduled: () => void;
7
7
  onSearch?: () => void;
8
8
  activeTab?: ChatFirstPrimaryTab;
9
+ collapsed?: boolean;
9
10
  stickyNewChat?: boolean;
10
11
  copy?: ChatFirstCopy;
11
12
  }): import("react").JSX.Element;