@arbidocs/blocks 0.4.2 → 0.4.4

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 (156) hide show
  1. package/README.md +42 -0
  2. package/dist/agents/definitions/AgentCreator.d.ts +11 -2
  3. package/dist/agents/definitions/index.d.ts +1 -0
  4. package/dist/agents/definitions/persona.d.ts +16 -0
  5. package/dist/agents/index.cjs +199 -146
  6. package/dist/agents/index.js +3 -2
  7. package/dist/agents/tasks/TaskRunsDialog.d.ts +15 -3
  8. package/dist/agents/tasks/index.d.ts +1 -0
  9. package/dist/agents/tasks/taskRequest.d.ts +61 -0
  10. package/dist/chat/AssistantDock.d.ts +39 -0
  11. package/dist/chat/ChatDocuments.d.ts +17 -0
  12. package/dist/chat/ChatPage.d.ts +16 -0
  13. package/dist/chat/ChatThreads.d.ts +28 -0
  14. package/dist/chat/chat-page-types.d.ts +121 -0
  15. package/dist/chat/chat-surface.d.ts +9 -0
  16. package/dist/chat/index.cjs +247 -205
  17. package/dist/chat/index.d.ts +6 -0
  18. package/dist/chat/index.js +4 -2
  19. package/dist/{chunk-OBLSURHV.js → chunk-2NNTQLBY.js} +3 -3
  20. package/dist/{chunk-OBLSURHV.js.map → chunk-2NNTQLBY.js.map} +1 -1
  21. package/dist/chunk-2VBJ763Y.js +296 -0
  22. package/dist/chunk-2VBJ763Y.js.map +1 -0
  23. package/dist/{chunk-2ORA62OF.js → chunk-67MKU5U7.js} +3 -3
  24. package/dist/{chunk-2ORA62OF.js.map → chunk-67MKU5U7.js.map} +1 -1
  25. package/dist/{chunk-AE5FHFZ5.cjs → chunk-6CHMNKXC.cjs} +6 -6
  26. package/dist/{chunk-AE5FHFZ5.cjs.map → chunk-6CHMNKXC.cjs.map} +1 -1
  27. package/dist/{chunk-TYZUCAT7.js → chunk-7YADKWZT.js} +43 -6
  28. package/dist/chunk-7YADKWZT.js.map +1 -0
  29. package/dist/chunk-ATXPAD6S.cjs +1952 -0
  30. package/dist/chunk-ATXPAD6S.cjs.map +1 -0
  31. package/dist/{chunk-YBXCZEWS.js → chunk-B673CCW5.js} +29 -6
  32. package/dist/chunk-B673CCW5.js.map +1 -0
  33. package/dist/{chunk-ITGHMJOM.js → chunk-BBEMPS6C.js} +169 -26
  34. package/dist/chunk-BBEMPS6C.js.map +1 -0
  35. package/dist/{chunk-XNBAXRY7.cjs → chunk-DGVKBOY5.cjs} +188 -32
  36. package/dist/chunk-DGVKBOY5.cjs.map +1 -0
  37. package/dist/{chunk-2K33YGM6.cjs → chunk-DN3NFNIA.cjs} +63 -294
  38. package/dist/chunk-DN3NFNIA.cjs.map +1 -0
  39. package/dist/{chunk-XQ7PKKDY.cjs → chunk-G4YOH5DU.cjs} +3 -3
  40. package/dist/{chunk-XQ7PKKDY.cjs.map → chunk-G4YOH5DU.cjs.map} +1 -1
  41. package/dist/{chunk-AXIZ2MDV.js → chunk-HER7MTRR.js} +3 -3
  42. package/dist/{chunk-AXIZ2MDV.js.map → chunk-HER7MTRR.js.map} +1 -1
  43. package/dist/{chunk-SYDHKKRZ.cjs → chunk-IZHWP6WQ.cjs} +47 -5
  44. package/dist/chunk-IZHWP6WQ.cjs.map +1 -0
  45. package/dist/chunk-JPO3F4J7.js +1899 -0
  46. package/dist/chunk-JPO3F4J7.js.map +1 -0
  47. package/dist/{chunk-WJMEXTVD.cjs → chunk-KDG6FER6.cjs} +28 -2
  48. package/dist/chunk-KDG6FER6.cjs.map +1 -0
  49. package/dist/chunk-KHX2BX3S.cjs +307 -0
  50. package/dist/chunk-KHX2BX3S.cjs.map +1 -0
  51. package/dist/{chunk-WTNMWEBC.js → chunk-NJL4LIXX.js} +4 -3
  52. package/dist/chunk-NJL4LIXX.js.map +1 -0
  53. package/dist/{chunk-J2BGSWXM.cjs → chunk-O637DUKS.cjs} +27 -27
  54. package/dist/{chunk-J2BGSWXM.cjs.map → chunk-O637DUKS.cjs.map} +1 -1
  55. package/dist/{chunk-WW6EOVZA.js → chunk-OXF2IFXG.js} +26 -3
  56. package/dist/chunk-OXF2IFXG.js.map +1 -0
  57. package/dist/{chunk-6HIPFEID.cjs → chunk-PBS5MFIY.cjs} +4 -4
  58. package/dist/{chunk-6HIPFEID.cjs.map → chunk-PBS5MFIY.cjs.map} +1 -1
  59. package/dist/{chunk-AZQDNYH4.js → chunk-RCP6THBS.js} +9 -1249
  60. package/dist/chunk-RCP6THBS.js.map +1 -0
  61. package/dist/{chunk-GDFWJ73C.cjs → chunk-RIL7G7AS.cjs} +4 -1286
  62. package/dist/chunk-RIL7G7AS.cjs.map +1 -0
  63. package/dist/{chunk-DREI7EQ2.cjs → chunk-SS23F63U.cjs} +9 -9
  64. package/dist/{chunk-DREI7EQ2.cjs.map → chunk-SS23F63U.cjs.map} +1 -1
  65. package/dist/{chunk-7PEELPMA.js → chunk-TI4UTYRL.js} +62 -285
  66. package/dist/chunk-TI4UTYRL.js.map +1 -0
  67. package/dist/{chunk-6P6AEQJB.js → chunk-WJZBJUPX.js} +4 -4
  68. package/dist/{chunk-6P6AEQJB.js.map → chunk-WJZBJUPX.js.map} +1 -1
  69. package/dist/{chunk-UFHMLHIR.cjs → chunk-XAJIKT6Y.cjs} +32 -11
  70. package/dist/chunk-XAJIKT6Y.cjs.map +1 -0
  71. package/dist/{chunk-G3G7LZPM.cjs → chunk-XKKGGUU2.cjs} +29 -3
  72. package/dist/chunk-XKKGGUU2.cjs.map +1 -0
  73. package/dist/{chunk-UWGIB3VQ.js → chunk-XNOASBGW.js} +33 -12
  74. package/dist/chunk-XNOASBGW.js.map +1 -0
  75. package/dist/{chunk-CTZ5745I.js → chunk-XQKGYGCJ.js} +3 -3
  76. package/dist/{chunk-CTZ5745I.js.map → chunk-XQKGYGCJ.js.map} +1 -1
  77. package/dist/{chunk-54Q3RNJU.cjs → chunk-ZVPTFTGV.cjs} +4 -3
  78. package/dist/chunk-ZVPTFTGV.cjs.map +1 -0
  79. package/dist/citations/index.cjs +11 -11
  80. package/dist/citations/index.js +2 -2
  81. package/dist/collaboration/contacts/AddContactsParts.d.ts +9 -4
  82. package/dist/collaboration/contacts/index.d.ts +1 -0
  83. package/dist/collaboration/contacts/parseEmails.d.ts +26 -0
  84. package/dist/collaboration/index.cjs +51 -31
  85. package/dist/collaboration/index.js +1 -1
  86. package/dist/components/MemberAvatarStack.d.ts +7 -2
  87. package/dist/connect/index.cjs +41 -12
  88. package/dist/connect/index.cjs.map +1 -1
  89. package/dist/connect/index.d.ts +3 -2
  90. package/dist/connect/index.js +20 -4
  91. package/dist/connect/index.js.map +1 -1
  92. package/dist/connection/ConnectionProvider.d.ts +2 -0
  93. package/dist/connection/ai.d.ts +13 -1
  94. package/dist/connection/useConnectedShellWorkspaces.d.ts +15 -0
  95. package/dist/facts/index.cjs +3 -3
  96. package/dist/facts/index.cjs.map +1 -1
  97. package/dist/facts/index.js +2 -2
  98. package/dist/facts/index.js.map +1 -1
  99. package/dist/files/index.cjs +4 -3
  100. package/dist/files/index.cjs.map +1 -1
  101. package/dist/files/index.js +4 -3
  102. package/dist/files/index.js.map +1 -1
  103. package/dist/home/index.cjs +18 -17
  104. package/dist/home/index.cjs.map +1 -1
  105. package/dist/home/index.js +3 -2
  106. package/dist/home/index.js.map +1 -1
  107. package/dist/index.cjs +824 -695
  108. package/dist/index.cjs.map +1 -1
  109. package/dist/index.d.ts +1 -1
  110. package/dist/index.js +16 -15
  111. package/dist/index.js.map +1 -1
  112. package/dist/lib/format.d.ts +31 -0
  113. package/dist/retrieval/index.cjs +36 -36
  114. package/dist/retrieval/index.js +1 -1
  115. package/dist/settings/index.cjs +13 -11
  116. package/dist/settings/index.cjs.map +1 -1
  117. package/dist/settings/index.js +13 -11
  118. package/dist/settings/index.js.map +1 -1
  119. package/dist/shell/SplitLayout.d.ts +32 -0
  120. package/dist/shell/index.cjs +72 -68
  121. package/dist/shell/index.d.ts +1 -0
  122. package/dist/shell/index.js +2 -2
  123. package/dist/site/index.cjs +71 -71
  124. package/dist/site/index.js +3 -3
  125. package/dist/studio/index.cjs +79 -76
  126. package/dist/studio/index.cjs.map +1 -1
  127. package/dist/studio/index.js +9 -6
  128. package/dist/studio/index.js.map +1 -1
  129. package/dist/workspaces/WorkspaceGallery.d.ts +42 -0
  130. package/dist/workspaces/index.cjs +74 -7
  131. package/dist/workspaces/index.cjs.map +1 -1
  132. package/dist/workspaces/index.d.ts +2 -0
  133. package/dist/workspaces/index.js +73 -7
  134. package/dist/workspaces/index.js.map +1 -1
  135. package/package.json +3 -3
  136. package/src/blocks.css +10 -13
  137. package/dist/chunk-2K33YGM6.cjs.map +0 -1
  138. package/dist/chunk-54Q3RNJU.cjs.map +0 -1
  139. package/dist/chunk-7PEELPMA.js.map +0 -1
  140. package/dist/chunk-AZQDNYH4.js.map +0 -1
  141. package/dist/chunk-G3G7LZPM.cjs.map +0 -1
  142. package/dist/chunk-GDFWJ73C.cjs.map +0 -1
  143. package/dist/chunk-ITGHMJOM.js.map +0 -1
  144. package/dist/chunk-SCFYPAG6.cjs +0 -18
  145. package/dist/chunk-SCFYPAG6.cjs.map +0 -1
  146. package/dist/chunk-SYDHKKRZ.cjs.map +0 -1
  147. package/dist/chunk-TYZUCAT7.js.map +0 -1
  148. package/dist/chunk-UFHMLHIR.cjs.map +0 -1
  149. package/dist/chunk-UWGIB3VQ.js.map +0 -1
  150. package/dist/chunk-WJMEXTVD.cjs.map +0 -1
  151. package/dist/chunk-WTNMWEBC.js.map +0 -1
  152. package/dist/chunk-WW6EOVZA.js.map +0 -1
  153. package/dist/chunk-XNBAXRY7.cjs.map +0 -1
  154. package/dist/chunk-YBXCZEWS.js.map +0 -1
  155. package/dist/chunk-YDHO4AVL.js +0 -16
  156. package/dist/chunk-YDHO4AVL.js.map +0 -1
@@ -0,0 +1,1899 @@
1
+ "use client";
2
+ import { ToolButton, Shell } from './chunk-2VBJ763Y.js';
3
+ import { formatCount } from './chunk-OXF2IFXG.js';
4
+ import { testIdOr } from './chunk-7F6CBP3Z.js';
5
+ import { forwardRef, useState, useRef, useEffect, useSyncExternalStore, useCallback } from 'react';
6
+ import { FileText, ChevronDown, Pencil, Sparkles, ChevronUp, MoreHorizontal, RotateCw, AlertCircle, ArrowLeft, Edit3, Send, Loader2, Square, Mic, ArrowDown, GanttChartSquare, ArrowUpRight, Upload, MessageSquarePlus, Trash2, PanelRight, MessageSquare, X, FileUp } from 'lucide-react';
7
+ import { cn, Textarea, Button, PopoverTrigger, Popover, PopoverAnchor, PopoverContent, DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuItem, Tooltip, TooltipTrigger, TooltipContent, MarkdownRenderer } from '@arbidocs/react/ui';
8
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
9
+
10
+ var DEFAULT_MESSAGE_LABELS = {
11
+ subagentBadge: "sub-agent",
12
+ hideSteps: "Hide steps",
13
+ showSteps: "Show steps",
14
+ stepCount: (count) => formatCount(count, { one: "step", other: "steps" }),
15
+ openSource: (num, title) => `Open source ${num}: ${title}`,
16
+ openSourceAria: (num, title, page) => `Open source ${num}: ${title}${page != null ? `, page ${page}` : ""}`,
17
+ page: (page) => `p. ${page}`,
18
+ pages: (pages) => `pp. ${pages.join(", ")}`,
19
+ moreActionsTitle: "More actions",
20
+ moreActionsAria: "More message actions"
21
+ };
22
+ function MessageRoot({
23
+ role,
24
+ messageId,
25
+ tone = "default",
26
+ testId = `message-${messageId}`,
27
+ className,
28
+ children,
29
+ ...rest
30
+ }) {
31
+ return /* @__PURE__ */ jsx(
32
+ "div",
33
+ {
34
+ "data-slot": "message",
35
+ "data-testid": testId,
36
+ "data-message-role": role,
37
+ className: cn(
38
+ "group mx-auto mb-1 flex w-full max-w-[46rem] justify-center",
39
+ tone === "error" ? "rounded-lg bg-red-bg" : tone === "subagent" ? "rounded-lg bg-muted/40" : "bg-background",
40
+ className
41
+ ),
42
+ ...rest,
43
+ children
44
+ }
45
+ );
46
+ }
47
+ function MessageContent({
48
+ role,
49
+ messageId,
50
+ testId = `message-content-${messageId}`,
51
+ className,
52
+ children,
53
+ ...rest
54
+ }) {
55
+ return /* @__PURE__ */ jsx(
56
+ "div",
57
+ {
58
+ "data-slot": "message-content",
59
+ "data-testid": testId,
60
+ className: cn(
61
+ "w-full max-w-full overflow-hidden text-chat",
62
+ role === "user" && "flex flex-col items-end",
63
+ className
64
+ ),
65
+ ...rest,
66
+ children
67
+ }
68
+ );
69
+ }
70
+ var MessageBubble = forwardRef(function MessageBubble2({ role, messageId, isEditing, isQueued, testId, className, children, ...rest }, ref) {
71
+ const isUser = role === "user";
72
+ return /* @__PURE__ */ jsx(
73
+ "div",
74
+ {
75
+ ref,
76
+ "data-slot": "message-bubble",
77
+ "data-testid": testId ?? (isUser ? `user-message-${messageId}` : `assistant-message-${messageId}`),
78
+ className: cn(
79
+ "overflow-hidden transition-colors",
80
+ isUser && "max-w-[80%] rounded-lg bg-muted",
81
+ isEditing && "ring-2 ring-inset ring-primary/30",
82
+ className
83
+ ),
84
+ ...rest,
85
+ children: /* @__PURE__ */ jsx(
86
+ "div",
87
+ {
88
+ className: cn(
89
+ isUser ? "px-4 py-2.5" : "px-4 py-3 sm:pl-9 sm:pr-0",
90
+ isQueued && "opacity-70"
91
+ ),
92
+ children
93
+ }
94
+ )
95
+ }
96
+ );
97
+ });
98
+ function MessageByline({
99
+ messageId,
100
+ identity,
101
+ timeLabel,
102
+ isSettled,
103
+ isFailed,
104
+ statusLabel,
105
+ className,
106
+ testId = `assistant-byline-${messageId}`,
107
+ statusTestId
108
+ }) {
109
+ return /* @__PURE__ */ jsxs(
110
+ "div",
111
+ {
112
+ "data-slot": "message-byline",
113
+ "data-testid": testId,
114
+ className: cn("mb-2 flex min-w-0 items-center gap-2 text-sm sm:-ml-9", className),
115
+ children: [
116
+ /* @__PURE__ */ jsx(
117
+ "span",
118
+ {
119
+ className: "flex h-7 w-7 shrink-0 items-center justify-center rounded-full bg-ai-soft text-ai-ink",
120
+ "aria-hidden": "true",
121
+ children: /* @__PURE__ */ jsx(Sparkles, { size: 14 })
122
+ }
123
+ ),
124
+ identity,
125
+ /* @__PURE__ */ jsx("span", { className: "truncate text-xs text-muted-foreground", children: timeLabel }),
126
+ isSettled && /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", "aria-hidden": "true", children: "\xB7" }),
127
+ isSettled && /* @__PURE__ */ jsx(
128
+ "span",
129
+ {
130
+ className: cn(
131
+ "shrink-0 text-xs",
132
+ isFailed ? "font-medium text-red-text" : "text-muted-foreground"
133
+ ),
134
+ "data-testid": testIdOr(statusTestId, { testId: "assistant-status" }),
135
+ children: statusLabel
136
+ }
137
+ )
138
+ ]
139
+ }
140
+ );
141
+ }
142
+ function MessageSubagentBadge({
143
+ messageId,
144
+ children,
145
+ labels,
146
+ className,
147
+ testId = `subagent-task-badge-${messageId}`
148
+ }) {
149
+ const l = { ...DEFAULT_MESSAGE_LABELS, ...labels };
150
+ return /* @__PURE__ */ jsx(
151
+ "span",
152
+ {
153
+ "data-slot": "message-subagent-badge",
154
+ className: cn(
155
+ "mb-2 inline-block rounded bg-muted px-1 py-0.5 text-xs font-semibold uppercase leading-none tracking-wide text-muted-foreground",
156
+ className
157
+ ),
158
+ "data-testid": testId,
159
+ children: children ?? l.subagentBadge
160
+ }
161
+ );
162
+ }
163
+ function MessageBody({
164
+ messageId,
165
+ className,
166
+ children,
167
+ testId = `message-body-${messageId}`
168
+ }) {
169
+ return /* @__PURE__ */ jsx(
170
+ "div",
171
+ {
172
+ "data-slot": "message-body",
173
+ className: cn("markdown-content m-0 p-0 text-base leading-relaxed", className),
174
+ "data-testid": testId,
175
+ children
176
+ }
177
+ );
178
+ }
179
+ function MessageNarration({
180
+ messageId,
181
+ children,
182
+ className,
183
+ testId = `message-narration-${messageId}`
184
+ }) {
185
+ return /* @__PURE__ */ jsx(
186
+ "div",
187
+ {
188
+ "data-slot": "message-narration",
189
+ className: cn("opacity-50 transition-opacity duration-300", className),
190
+ "data-provisional": "true",
191
+ "data-testid": testId,
192
+ children
193
+ }
194
+ );
195
+ }
196
+ function SourceChip({
197
+ num,
198
+ title,
199
+ page,
200
+ pages,
201
+ onOpen,
202
+ className,
203
+ labels,
204
+ testId = `source-chip-${num}`
205
+ }) {
206
+ const l = { ...DEFAULT_MESSAGE_LABELS, ...labels };
207
+ const pageText = pages && pages.length > 1 ? l.pages(pages) : page != null ? l.page(page) : null;
208
+ return /* @__PURE__ */ jsxs(
209
+ "button",
210
+ {
211
+ type: "button",
212
+ "data-slot": "source-chip",
213
+ onClick: onOpen,
214
+ className: cn(
215
+ "inline-flex min-h-8 max-w-full items-center gap-2 rounded-md bg-muted px-2 py-1 text-sm text-foreground transition-colors hover:bg-accent focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring pointer-coarse:min-h-11",
216
+ className
217
+ ),
218
+ title: l.openSource(num, title),
219
+ "aria-label": l.openSourceAria(num, title, page),
220
+ "data-testid": testId,
221
+ children: [
222
+ /* @__PURE__ */ jsx("span", { className: "inline-flex h-5 min-w-5 items-center justify-center rounded-sm bg-ai-soft px-1 text-xs font-semibold text-ai-ink", children: num }),
223
+ /* @__PURE__ */ jsx("span", { className: "min-w-0 truncate", children: title }),
224
+ pageText && /* @__PURE__ */ jsx("span", { className: "shrink-0 text-xs text-muted-foreground", children: pageText })
225
+ ]
226
+ }
227
+ );
228
+ }
229
+ function mergeSourcesByFile(sources) {
230
+ const byTitle = /* @__PURE__ */ new Map();
231
+ for (const s of sources) {
232
+ const group = byTitle.get(s.title) ?? { nums: [], title: s.title, pages: [] };
233
+ group.nums.push(s.num);
234
+ if (s.page != null && !group.pages.includes(s.page)) group.pages.push(s.page);
235
+ byTitle.set(s.title, group);
236
+ }
237
+ return [...byTitle.values()];
238
+ }
239
+ function MessageSources({
240
+ messageId,
241
+ sources,
242
+ onOpenSource,
243
+ className,
244
+ labels,
245
+ testId = `source-chips-${messageId}`
246
+ }) {
247
+ if (sources.length === 0) return null;
248
+ return /* @__PURE__ */ jsx(
249
+ "div",
250
+ {
251
+ "data-slot": "message-sources",
252
+ className: cn("mt-3 flex flex-wrap gap-2", className),
253
+ "data-testid": testId,
254
+ children: mergeSourcesByFile(sources).map((g) => /* @__PURE__ */ jsx(
255
+ SourceChip,
256
+ {
257
+ num: g.nums.join(", "),
258
+ testId: `source-chip-${g.nums[0]}`,
259
+ title: g.title,
260
+ page: g.pages[0],
261
+ pages: g.pages,
262
+ onOpen: () => onOpenSource(g.nums[0]),
263
+ labels
264
+ },
265
+ g.nums[0]
266
+ ))
267
+ }
268
+ );
269
+ }
270
+ function MessageFooter({
271
+ role,
272
+ leading,
273
+ actions,
274
+ steps,
275
+ className,
276
+ labels,
277
+ stepsToggleTestId
278
+ }) {
279
+ const l = { ...DEFAULT_MESSAGE_LABELS, ...labels };
280
+ const isUser = role === "user";
281
+ const showSteps = !isUser && !!steps && steps.count > 0;
282
+ const row = /* @__PURE__ */ jsxs(
283
+ "div",
284
+ {
285
+ className: cn(
286
+ "flex items-center text-sm",
287
+ isUser ? "justify-end gap-2 text-xs" : "justify-between"
288
+ ),
289
+ children: [
290
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
291
+ leading,
292
+ showSteps && /* @__PURE__ */ jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxs(
293
+ "button",
294
+ {
295
+ type: "button",
296
+ className: "flex items-center gap-1 text-muted-foreground transition-colors hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring pointer-coarse:min-h-11",
297
+ "aria-expanded": steps.open,
298
+ "aria-label": steps.open ? l.hideSteps : l.showSteps,
299
+ "data-testid": testIdOr(stepsToggleTestId, { testId: "steps-toggle" }),
300
+ children: [
301
+ steps.open ? /* @__PURE__ */ jsx(ChevronDown, { size: 14 }) : /* @__PURE__ */ jsx(ChevronUp, { size: 14 }),
302
+ /* @__PURE__ */ jsx("span", { className: "tabular-nums", children: l.stepCount(steps.count) })
303
+ ]
304
+ }
305
+ ) })
306
+ ] }),
307
+ /* @__PURE__ */ jsx("div", { className: "flex items-center gap-1", children: actions })
308
+ ]
309
+ }
310
+ );
311
+ return /* @__PURE__ */ jsx(
312
+ "div",
313
+ {
314
+ "data-slot": "message-footer",
315
+ className: cn(
316
+ "text-muted-foreground",
317
+ isUser ? "mt-1 max-w-full self-end" : "mt-3",
318
+ className
319
+ ),
320
+ children: steps ? /* @__PURE__ */ jsxs(Popover, { open: steps.open, onOpenChange: steps.onOpenChange, children: [
321
+ /* @__PURE__ */ jsx(PopoverAnchor, { asChild: true, children: row }),
322
+ !isUser && steps.count > 0 && /* @__PURE__ */ jsx(
323
+ PopoverContent,
324
+ {
325
+ side: "top",
326
+ align: "start",
327
+ sideOffset: 8,
328
+ className: "max-h-[min(60vh,32rem)] w-[var(--radix-popover-trigger-width)] overflow-y-auto rounded-lg border border-border bg-popover p-0 shadow-lg",
329
+ children: steps.panel
330
+ }
331
+ )
332
+ ] }) : row
333
+ }
334
+ );
335
+ }
336
+ var MessageActionButton = forwardRef(
337
+ function MessageActionButton2({ isDone, className, type = "button", ...rest }, ref) {
338
+ return /* @__PURE__ */ jsx(
339
+ "button",
340
+ {
341
+ ref,
342
+ type,
343
+ "data-slot": "message-action",
344
+ className: cn(
345
+ "inline-flex h-8 w-8 items-center justify-center rounded-md text-muted-foreground transition-colors hover:bg-muted hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:opacity-50 pointer-coarse:h-11 pointer-coarse:w-11",
346
+ isDone && "text-green-text",
347
+ className
348
+ ),
349
+ ...rest
350
+ }
351
+ );
352
+ }
353
+ );
354
+ function MessageActionMenu({
355
+ items,
356
+ testId,
357
+ label,
358
+ labels,
359
+ className
360
+ }) {
361
+ const l = { ...DEFAULT_MESSAGE_LABELS, ...labels };
362
+ return /* @__PURE__ */ jsxs(DropdownMenu, { children: [
363
+ /* @__PURE__ */ jsx(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
364
+ MessageActionButton,
365
+ {
366
+ onClick: (e) => e.stopPropagation(),
367
+ className,
368
+ title: l.moreActionsTitle,
369
+ "aria-label": label ?? l.moreActionsAria,
370
+ "data-testid": testId,
371
+ children: /* @__PURE__ */ jsx(MoreHorizontal, { size: 16 })
372
+ }
373
+ ) }),
374
+ /* @__PURE__ */ jsx(DropdownMenuContent, { align: "end", children: items.map((item) => /* @__PURE__ */ jsxs(
375
+ DropdownMenuItem,
376
+ {
377
+ onClick: (e) => {
378
+ e.stopPropagation();
379
+ item.onSelect();
380
+ },
381
+ className: item.tone === "danger" ? "text-red-text focus:text-red-text" : void 0,
382
+ "data-testid": item.testId,
383
+ children: [
384
+ item.icon,
385
+ item.label
386
+ ]
387
+ },
388
+ item.id
389
+ )) })
390
+ ] });
391
+ }
392
+ var Message = {
393
+ Root: MessageRoot,
394
+ Content: MessageContent,
395
+ Bubble: MessageBubble,
396
+ Byline: MessageByline,
397
+ SubagentBadge: MessageSubagentBadge,
398
+ Body: MessageBody,
399
+ Narration: MessageNarration,
400
+ Sources: MessageSources,
401
+ Footer: MessageFooter,
402
+ ActionButton: MessageActionButton,
403
+ ActionMenu: MessageActionMenu
404
+ };
405
+ var DEFAULT_MESSAGE_ALERT_LABELS = {
406
+ retrying: "Retrying...",
407
+ retry: "Retry"
408
+ };
409
+ function MessageAlert({
410
+ title,
411
+ description,
412
+ isBusy = false,
413
+ busyTitle,
414
+ labels,
415
+ onActivate,
416
+ actions,
417
+ className,
418
+ testId = "message-alert"
419
+ }) {
420
+ const l = { ...DEFAULT_MESSAGE_ALERT_LABELS, ...labels };
421
+ return /* @__PURE__ */ jsx(
422
+ "div",
423
+ {
424
+ "data-slot": "message-alert",
425
+ role: isBusy ? "status" : "alert",
426
+ "aria-busy": isBusy,
427
+ className: cn(
428
+ "mb-3 rounded-lg border border-red-border bg-red-bg px-4 py-2 transition-colors",
429
+ className
430
+ ),
431
+ "data-testid": testId,
432
+ children: /* @__PURE__ */ jsxs("div", { className: "flex items-start gap-3", children: [
433
+ isBusy ? /* @__PURE__ */ jsx(
434
+ RotateCw,
435
+ {
436
+ "aria-hidden": "true",
437
+ className: "mt-0.5 h-5 w-5 shrink-0 animate-spin text-red-text motion-reduce:animate-none"
438
+ }
439
+ ) : /* @__PURE__ */ jsx(AlertCircle, { "aria-hidden": "true", className: "mt-0.5 h-5 w-5 shrink-0 text-red-text" }),
440
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
441
+ /* @__PURE__ */ jsx("p", { className: "mb-1 text-base font-medium text-red-text", children: isBusy ? busyTitle ?? l.retrying : title }),
442
+ /* @__PURE__ */ jsx("p", { className: "mb-1 text-sm text-muted-foreground", children: description }),
443
+ (actions || onActivate) && /* @__PURE__ */ jsxs("div", { className: "mt-2 flex flex-wrap gap-2", children: [
444
+ actions,
445
+ onActivate && /* @__PURE__ */ jsxs(
446
+ Button,
447
+ {
448
+ type: "button",
449
+ size: "sm",
450
+ variant: "outline",
451
+ className: "pointer-coarse:min-h-11",
452
+ disabled: isBusy,
453
+ onClick: onActivate,
454
+ "data-testid": `${testId}-retry`,
455
+ children: [
456
+ /* @__PURE__ */ jsx(RotateCw, { "aria-hidden": "true", className: "mr-2 h-4 w-4" }),
457
+ l.retry
458
+ ]
459
+ }
460
+ )
461
+ ] })
462
+ ] })
463
+ ] })
464
+ }
465
+ );
466
+ }
467
+ var DEFAULT_MESSAGE_LIST_LABELS = {
468
+ messageList: "Message list",
469
+ startConversation: "Start a new conversation",
470
+ backToConversation: "Back to conversation \u2014 this is a sub-agent's delegated thread",
471
+ conversationMessages: "Conversation messages",
472
+ responding: "Assistant is responding",
473
+ responseComplete: (text) => text ? `Response complete: ${text}` : "Response complete"
474
+ };
475
+ function MessageListRoot({
476
+ className,
477
+ children,
478
+ isBusy = false,
479
+ labels,
480
+ testId = "message-list",
481
+ ...rest
482
+ }) {
483
+ const l = { ...DEFAULT_MESSAGE_LIST_LABELS, ...labels };
484
+ return /* @__PURE__ */ jsx(
485
+ "div",
486
+ {
487
+ "data-slot": "message-list",
488
+ style: { containerType: "inline-size" },
489
+ "data-testid": testId,
490
+ role: "log",
491
+ "aria-live": "off",
492
+ "aria-busy": isBusy,
493
+ "aria-label": l.messageList,
494
+ className,
495
+ ...rest,
496
+ children
497
+ }
498
+ );
499
+ }
500
+ function MessageListStatus({
501
+ message,
502
+ testId = "message-list-status"
503
+ }) {
504
+ return /* @__PURE__ */ jsx(
505
+ "div",
506
+ {
507
+ "data-slot": "message-list-status",
508
+ role: "status",
509
+ "aria-live": "polite",
510
+ "aria-atomic": "true",
511
+ className: "sr-only",
512
+ "data-testid": testId,
513
+ children: message
514
+ }
515
+ );
516
+ }
517
+ function MessageListEmpty({
518
+ children,
519
+ labels,
520
+ className,
521
+ testId = "message-list-empty"
522
+ }) {
523
+ const l = { ...DEFAULT_MESSAGE_LIST_LABELS, ...labels };
524
+ return /* @__PURE__ */ jsx("div", { className: cn("flex h-64 items-center justify-center", className), "data-testid": testId, children: /* @__PURE__ */ jsx("p", { className: "text-muted-foreground", children: children ?? l.startConversation }) });
525
+ }
526
+ function MessageListBanner({
527
+ onBack,
528
+ children,
529
+ labels,
530
+ className,
531
+ testId = "back-to-conversation-button"
532
+ }) {
533
+ const l = { ...DEFAULT_MESSAGE_LIST_LABELS, ...labels };
534
+ return /* @__PURE__ */ jsx(
535
+ "div",
536
+ {
537
+ "data-slot": "message-list-banner",
538
+ className: cn(
539
+ "sticky top-0 z-10 border-b border-border/40 bg-background px-3 py-2 sm:px-6",
540
+ className
541
+ ),
542
+ children: /* @__PURE__ */ jsxs(
543
+ "button",
544
+ {
545
+ type: "button",
546
+ onClick: onBack,
547
+ className: "flex items-center gap-1.5 rounded-md px-2 py-1 text-sm font-medium text-primary transition-colors hover:bg-muted/60 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring pointer-coarse:min-h-11",
548
+ "data-testid": testId,
549
+ children: [
550
+ /* @__PURE__ */ jsx(ArrowLeft, { size: 14 }),
551
+ children ?? l.backToConversation
552
+ ]
553
+ }
554
+ )
555
+ }
556
+ );
557
+ }
558
+ function MessageListRows({
559
+ totalSize,
560
+ className,
561
+ children,
562
+ labels,
563
+ ...rest
564
+ }) {
565
+ const l = { ...DEFAULT_MESSAGE_LIST_LABELS, ...labels };
566
+ return /* @__PURE__ */ jsx(
567
+ "div",
568
+ {
569
+ "data-slot": "message-list-rows",
570
+ style: { height: totalSize, position: "relative" },
571
+ role: "list",
572
+ "aria-label": l.conversationMessages,
573
+ className,
574
+ ...rest,
575
+ children
576
+ }
577
+ );
578
+ }
579
+ var MessageListRow = forwardRef(
580
+ function MessageListRow2({ index, start, messageId, setSize, className, children, ...rest }, ref) {
581
+ const style = {
582
+ position: "absolute",
583
+ top: 0,
584
+ left: 0,
585
+ width: "100%",
586
+ transform: `translateY(${start}px)`
587
+ };
588
+ return /* @__PURE__ */ jsx(
589
+ "div",
590
+ {
591
+ ref,
592
+ "data-slot": "message-list-row",
593
+ "data-index": index,
594
+ "data-message-id": messageId,
595
+ role: "listitem",
596
+ "aria-posinset": setSize === void 0 ? void 0 : index + 1,
597
+ "aria-setsize": setSize,
598
+ style,
599
+ className,
600
+ ...rest,
601
+ children: /* @__PURE__ */ jsx("div", { className: cn("w-full px-3 sm:px-6"), children })
602
+ }
603
+ );
604
+ }
605
+ );
606
+ function MessageListTail() {
607
+ return /* @__PURE__ */ jsx("div", { className: "h-12 sm:h-32", "aria-hidden": "true", children: /* @__PURE__ */ jsx("div", { className: "pointer-events-none h-8 bg-gradient-to-b from-background to-transparent sm:h-20" }) });
608
+ }
609
+ var MessageList = {
610
+ Root: MessageListRoot,
611
+ Empty: MessageListEmpty,
612
+ Banner: MessageListBanner,
613
+ Status: MessageListStatus,
614
+ Rows: MessageListRows,
615
+ Row: MessageListRow,
616
+ Tail: MessageListTail
617
+ };
618
+ function toSpeakableText(markdown, maxLength = 600) {
619
+ const flat = markdown.replace(/```[\s\S]*?```/g, " ").replace(/<[^>]+>/g, " ").replace(/!?\[([^\]]*)\]\([^)]*\)/g, "$1").replace(/[*_`#>~|]/g, "").replace(/\s+/g, " ").trim();
620
+ if (flat.length <= maxLength) return flat;
621
+ const cut = flat.slice(0, maxLength);
622
+ const lastSpace = cut.lastIndexOf(" ");
623
+ return `${(lastSpace > maxLength / 2 ? cut.slice(0, lastSpace) : cut).trimEnd()}\u2026`;
624
+ }
625
+ function useStreamAnnouncement({
626
+ isStreaming,
627
+ text,
628
+ resetKey,
629
+ maxLength,
630
+ labels
631
+ }) {
632
+ const l = { ...DEFAULT_MESSAGE_LIST_LABELS, ...labels };
633
+ const [message, setMessage] = useState("");
634
+ const wasStreaming = useRef(isStreaming);
635
+ const textRef = useRef(text);
636
+ textRef.current = text;
637
+ const lastKey = useRef(resetKey);
638
+ useEffect(() => {
639
+ if (lastKey.current !== resetKey) {
640
+ lastKey.current = resetKey;
641
+ wasStreaming.current = isStreaming;
642
+ setMessage("");
643
+ return;
644
+ }
645
+ if (isStreaming === wasStreaming.current) return;
646
+ wasStreaming.current = isStreaming;
647
+ setMessage(
648
+ isStreaming ? l.responding : l.responseComplete(toSpeakableText(textRef.current, maxLength))
649
+ );
650
+ }, [isStreaming, resetKey]);
651
+ return message;
652
+ }
653
+ var DEFAULT_COMPOSER_LABELS = {
654
+ branchingNotice: "Editing message. Submit to send, or cancel to go back.",
655
+ cancel: "Cancel",
656
+ inputAriaLabel: "Write your prompt here",
657
+ pastedText: "Pasted text",
658
+ pasteSummary: (lines, characters) => `${lines} lines \xB7 ${characters} characters`,
659
+ expand: "Expand",
660
+ retryMessage: "Retry message",
661
+ sendMessage: "Send message",
662
+ voiceButton: "Press and hold to speak",
663
+ jumpToBottom: "Jump to bottom"
664
+ };
665
+ function ComposerRoot({
666
+ onSubmit,
667
+ notice,
668
+ isBranching,
669
+ formRef,
670
+ testId = "message-input",
671
+ formTestId,
672
+ className,
673
+ children,
674
+ ...rest
675
+ }) {
676
+ return /* @__PURE__ */ jsxs(
677
+ "div",
678
+ {
679
+ "data-slot": "composer",
680
+ className: cn("relative z-[20] shrink-0", isBranching && "bg-muted/30"),
681
+ "data-testid": testId,
682
+ children: [
683
+ notice,
684
+ /* @__PURE__ */ jsx(
685
+ "form",
686
+ {
687
+ ref: formRef,
688
+ onSubmit,
689
+ ...rest,
690
+ className: cn("relative w-full", className),
691
+ "data-testid": testIdOr(formTestId, { testId: "message-input-form" }),
692
+ children
693
+ }
694
+ )
695
+ ]
696
+ }
697
+ );
698
+ }
699
+ function ComposerBranchingNotice({
700
+ onCancel,
701
+ children,
702
+ labels,
703
+ testId = "message-input-branching-indicator",
704
+ cancelTestId,
705
+ className,
706
+ ...rest
707
+ }) {
708
+ const l = { ...DEFAULT_COMPOSER_LABELS, ...labels };
709
+ return /* @__PURE__ */ jsxs(
710
+ "div",
711
+ {
712
+ ...rest,
713
+ "data-slot": "composer-notice",
714
+ className: cn("mb-2 flex items-center rounded-md bg-muted px-3 py-1.5 text-sm", className),
715
+ "data-testid": testId,
716
+ children: [
717
+ /* @__PURE__ */ jsx(Edit3, { size: 16, className: "mr-2 text-muted-foreground" }),
718
+ /* @__PURE__ */ jsx("span", { className: "flex-1 text-muted-foreground", children: children ?? l.branchingNotice }),
719
+ onCancel && /* @__PURE__ */ jsx(
720
+ Button,
721
+ {
722
+ type: "button",
723
+ variant: "outline",
724
+ size: "sm",
725
+ className: "ml-2",
726
+ onClick: onCancel,
727
+ "data-testid": testIdOr(cancelTestId, { testId: "cancel-branching-button" }),
728
+ children: l.cancel
729
+ }
730
+ )
731
+ ]
732
+ }
733
+ );
734
+ }
735
+ var INTERACTIVE = 'button, a, input, textarea, select, [role="button"], [contenteditable="true"]';
736
+ function ComposerFrame({ className, children, ...rest }) {
737
+ return /* @__PURE__ */ jsx(
738
+ "div",
739
+ {
740
+ "data-slot": "composer-frame",
741
+ className: cn(
742
+ "relative flex w-full flex-col rounded-lg border border-border bg-background px-4 py-3 shadow-lg transition-shadow focus-within:shadow-xl",
743
+ className
744
+ ),
745
+ onMouseDown: (e) => {
746
+ const target = e.target;
747
+ if (target.closest(INTERACTIVE)) return;
748
+ e.preventDefault();
749
+ e.currentTarget.querySelector("textarea")?.focus();
750
+ },
751
+ ...rest,
752
+ children
753
+ }
754
+ );
755
+ }
756
+ function scrollToLastContentLine(textarea) {
757
+ if (!textarea) return;
758
+ const text = textarea.value;
759
+ const lines = text.split("\n");
760
+ const trimmedLines = text.trimEnd().split("\n");
761
+ if (lines.length - trimmedLines.length > 0 && trimmedLines.length > 0) {
762
+ const lineHeight = parseFloat(getComputedStyle(textarea).lineHeight) || 24;
763
+ textarea.scrollTop = (trimmedLines.length - 1) * lineHeight;
764
+ } else {
765
+ textarea.scrollTop = textarea.scrollHeight;
766
+ }
767
+ }
768
+ var ComposerInput = forwardRef(
769
+ function ComposerInput2({
770
+ value,
771
+ onValueChange,
772
+ onKeyDown,
773
+ submitOnEnter = true,
774
+ onPaste,
775
+ placeholder,
776
+ textareaRef,
777
+ isSuggestion,
778
+ isRoomy,
779
+ isPasteCollapsed,
780
+ onExpandPaste,
781
+ ariaLabel,
782
+ textareaProps,
783
+ className,
784
+ labels,
785
+ testId = "message-input-textarea",
786
+ pasteChipTestId
787
+ }, forwardedRef) {
788
+ const l = { ...DEFAULT_COMPOSER_LABELS, ...labels };
789
+ const [isFocused, setIsFocused] = useState(false);
790
+ const [isHovered, setIsHovered] = useState(false);
791
+ const innerRef = useRef(null);
792
+ const setRefs = (node) => {
793
+ innerRef.current = node;
794
+ for (const ref of [forwardedRef, textareaRef]) {
795
+ if (typeof ref === "function") ref(node);
796
+ else if (ref) ref.current = node;
797
+ }
798
+ };
799
+ const handleKeyDown = (event) => {
800
+ onKeyDown?.(event);
801
+ if (event.defaultPrevented || !submitOnEnter) return;
802
+ if (event.key !== "Enter" || event.shiftKey || event.altKey) return;
803
+ if (event.nativeEvent.isComposing || event.keyCode === 229) return;
804
+ event.preventDefault();
805
+ event.currentTarget.form?.requestSubmit();
806
+ };
807
+ if (isPasteCollapsed) {
808
+ const lineCount = value.split("\n").length;
809
+ return /* @__PURE__ */ jsxs(
810
+ "button",
811
+ {
812
+ type: "button",
813
+ "data-slot": "composer-paste-chip",
814
+ onClick: onExpandPaste,
815
+ className: "flex w-full items-center gap-2 rounded-md bg-muted/50 px-3 py-2 text-left transition-colors hover:bg-muted focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/40",
816
+ "data-testid": testIdOr(pasteChipTestId, { testId: "long-paste-placeholder" }),
817
+ "data-full-length": value.length,
818
+ children: [
819
+ /* @__PURE__ */ jsx(FileText, { className: "h-4 w-4 shrink-0 text-muted-foreground", "aria-hidden": "true" }),
820
+ /* @__PURE__ */ jsxs("span", { className: "min-w-0 flex-1", children: [
821
+ /* @__PURE__ */ jsx("span", { className: "block text-sm font-medium text-foreground", children: l.pastedText }),
822
+ /* @__PURE__ */ jsx("span", { className: "block text-xs text-muted-foreground", children: l.pasteSummary(lineCount, value.length.toLocaleString()) })
823
+ ] }),
824
+ /* @__PURE__ */ jsxs("span", { className: "flex shrink-0 items-center gap-1 text-xs font-medium text-muted-foreground", children: [
825
+ l.expand,
826
+ /* @__PURE__ */ jsx(ChevronDown, { className: "h-3.5 w-3.5", "aria-hidden": "true" })
827
+ ] })
828
+ ]
829
+ }
830
+ );
831
+ }
832
+ return /* @__PURE__ */ jsx(
833
+ Textarea,
834
+ {
835
+ ...textareaProps,
836
+ "aria-label": ariaLabel ?? l.inputAriaLabel,
837
+ placeholder,
838
+ "data-suggested-query": isSuggestion ? "true" : void 0,
839
+ rows: 1,
840
+ ref: setRefs,
841
+ value,
842
+ onChange: (event) => onValueChange(event.target.value),
843
+ onPaste,
844
+ onKeyDown: handleKeyDown,
845
+ disabled: false,
846
+ className: cn(
847
+ "scrollbar-subtle !min-h-0 pointer-coarse:!min-h-11 pointer-coarse:py-2.5 w-full resize-none border-0 bg-transparent p-0 !text-base leading-relaxed text-foreground shadow-none placeholder:text-muted-foreground/50 focus-visible:ring-0 focus-visible:ring-offset-0 md:!text-base",
848
+ className
849
+ ),
850
+ style: {
851
+ fieldSizing: "content",
852
+ maxHeight: isFocused || isHovered ? "40vh" : value ? "6.5em" : isRoomy ? "4em" : "1.7em",
853
+ overflowY: value ? "auto" : "hidden",
854
+ scrollbarGutter: "stable"
855
+ },
856
+ onFocus: () => {
857
+ setIsFocused(true);
858
+ requestAnimationFrame(() => {
859
+ const ta = innerRef.current;
860
+ if (ta) {
861
+ ta.setSelectionRange(ta.value.length, ta.value.length);
862
+ ta.scrollTop = ta.scrollHeight;
863
+ }
864
+ });
865
+ },
866
+ onBlur: () => {
867
+ setIsFocused(false);
868
+ if (!isHovered) requestAnimationFrame(() => scrollToLastContentLine(innerRef.current));
869
+ },
870
+ onMouseEnter: () => setIsHovered(true),
871
+ onMouseLeave: () => {
872
+ setIsHovered(false);
873
+ if (!isFocused) requestAnimationFrame(() => scrollToLastContentLine(innerRef.current));
874
+ },
875
+ "data-testid": testId
876
+ }
877
+ );
878
+ }
879
+ );
880
+ function ComposerToolbar({ leading, trailing, className }) {
881
+ return /* @__PURE__ */ jsxs(
882
+ "div",
883
+ {
884
+ "data-slot": "composer-toolbar",
885
+ className: cn(
886
+ "mt-2 flex flex-wrap items-center justify-between gap-x-1 gap-y-0.5",
887
+ className
888
+ ),
889
+ children: [
890
+ /* @__PURE__ */ jsx("div", { className: "flex shrink-0 items-center gap-1", children: leading }),
891
+ /* @__PURE__ */ jsx("div", { className: "ml-auto flex min-w-0 items-center justify-end gap-1", children: trailing })
892
+ ]
893
+ }
894
+ );
895
+ }
896
+ function ComposerSend({
897
+ onSend,
898
+ disabled,
899
+ isRetry,
900
+ labels,
901
+ className,
902
+ testIds
903
+ }) {
904
+ const l = { ...DEFAULT_COMPOSER_LABELS, ...labels };
905
+ const sendTestId = testIdOr(testIds?.send, { testId: "send-message-button" });
906
+ const retryTestId = testIdOr(testIds?.retry, { testId: "retry-button" });
907
+ return /* @__PURE__ */ jsx(
908
+ Button,
909
+ {
910
+ type: "button",
911
+ size: "icon",
912
+ className: cn(
913
+ "h-9 w-9 shrink-0 pointer-coarse:h-11 pointer-coarse:w-11",
914
+ // A faded primary is unreadable; a flat muted chip reads as "not available yet" at 4.5:1.
915
+ "disabled:bg-muted disabled:text-muted-foreground disabled:opacity-100",
916
+ className
917
+ ),
918
+ "aria-label": isRetry ? l.retryMessage : l.sendMessage,
919
+ disabled,
920
+ onClick: onSend,
921
+ "data-testid": isRetry ? retryTestId : sendTestId,
922
+ children: isRetry ? /* @__PURE__ */ jsx(RotateCw, { className: "h-3.5 w-3.5" }) : /* @__PURE__ */ jsx(Send, { className: "h-3.5 w-3.5" })
923
+ }
924
+ );
925
+ }
926
+ function ComposerVoiceButton({
927
+ isRecording,
928
+ isBusy,
929
+ onPressStart,
930
+ onPressEnd,
931
+ label,
932
+ labels,
933
+ className,
934
+ testIds
935
+ }) {
936
+ const text = label ?? { ...DEFAULT_COMPOSER_LABELS, ...labels }.voiceButton;
937
+ const startTestId = testIdOr(testIds?.start, { testId: "start-recording-button" });
938
+ const recordingTestId = testIdOr(testIds?.recording, { testId: "recording-mic-button" });
939
+ return /* @__PURE__ */ jsxs(Tooltip, { children: [
940
+ /* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
941
+ Button,
942
+ {
943
+ type: "button",
944
+ variant: "ghost",
945
+ size: "sm",
946
+ "aria-label": text,
947
+ onPointerDown: (e) => {
948
+ e.preventDefault();
949
+ onPressStart();
950
+ },
951
+ onPointerUp: onPressEnd,
952
+ onPointerLeave: onPressEnd,
953
+ onPointerCancel: onPressEnd,
954
+ disabled: isBusy,
955
+ className: cn(
956
+ "h-8 w-8 shrink-0 p-0 hover:bg-muted pointer-coarse:h-11 pointer-coarse:w-11",
957
+ isRecording && "bg-red-bg",
958
+ className
959
+ ),
960
+ "data-testid": isRecording ? recordingTestId : startTestId,
961
+ children: isBusy ? /* @__PURE__ */ jsx(Loader2, { className: "h-3.5 w-3.5 animate-spin text-muted-foreground" }) : isRecording ? /* @__PURE__ */ jsx(Square, { className: "h-3 w-3 fill-red-text text-red-text" }) : /* @__PURE__ */ jsx(Mic, { className: "h-4 w-4 text-muted-foreground" })
962
+ }
963
+ ) }),
964
+ /* @__PURE__ */ jsx(TooltipContent, { side: "top", children: text })
965
+ ] });
966
+ }
967
+ function ComposerContainer({
968
+ className,
969
+ children,
970
+ testId = "message-input-container",
971
+ ...rest
972
+ }) {
973
+ return /* @__PURE__ */ jsx(
974
+ "div",
975
+ {
976
+ ...rest,
977
+ "data-slot": "composer-container",
978
+ className: cn("w-full bg-background py-1", className),
979
+ "data-testid": testId,
980
+ children
981
+ }
982
+ );
983
+ }
984
+ function ComposerDock({ children, floating }) {
985
+ return /* @__PURE__ */ jsxs(
986
+ "div",
987
+ {
988
+ "data-slot": "composer-dock",
989
+ className: "sticky bottom-0 z-40 w-full pb-4 sm:pb-[max(1rem,env(safe-area-inset-bottom))]",
990
+ children: [
991
+ /* @__PURE__ */ jsx(
992
+ "div",
993
+ {
994
+ className: "pointer-events-none absolute bottom-full left-0 right-0 h-16 bg-gradient-to-b from-transparent to-background/70",
995
+ "aria-hidden": "true"
996
+ }
997
+ ),
998
+ /* @__PURE__ */ jsx(
999
+ "div",
1000
+ {
1001
+ className: "pointer-events-none absolute bottom-0 left-0 right-0 h-4 bg-gradient-to-b from-transparent to-background/60 backdrop-blur-sm",
1002
+ "aria-hidden": "true"
1003
+ }
1004
+ ),
1005
+ floating,
1006
+ children
1007
+ ]
1008
+ }
1009
+ );
1010
+ }
1011
+ function ScrollToBottom({
1012
+ show,
1013
+ onClick,
1014
+ label,
1015
+ labels,
1016
+ className,
1017
+ testId = "scroll-to-bottom-button"
1018
+ }) {
1019
+ if (!show) return null;
1020
+ const text = label ?? { ...DEFAULT_COMPOSER_LABELS, ...labels }.jumpToBottom;
1021
+ return /* @__PURE__ */ jsx(
1022
+ Button,
1023
+ {
1024
+ onClick,
1025
+ variant: "outline",
1026
+ size: "icon",
1027
+ className: cn(
1028
+ "absolute -top-12 left-1/2 z-[15] h-10 w-10 -translate-x-1/2 shadow-lg pointer-coarse:h-11 pointer-coarse:w-11",
1029
+ className
1030
+ ),
1031
+ title: text,
1032
+ "aria-label": text,
1033
+ "data-testid": testId,
1034
+ children: /* @__PURE__ */ jsx(ArrowDown, { size: 20 })
1035
+ }
1036
+ );
1037
+ }
1038
+ var Composer = {
1039
+ Root: ComposerRoot,
1040
+ Notice: ComposerBranchingNotice,
1041
+ Frame: ComposerFrame,
1042
+ Input: ComposerInput,
1043
+ Toolbar: ComposerToolbar,
1044
+ Send: ComposerSend,
1045
+ VoiceButton: ComposerVoiceButton,
1046
+ Container: ComposerContainer,
1047
+ Dock: ComposerDock
1048
+ };
1049
+ var DEFAULT_CONVERSATION_EMPTY_LABELS = {
1050
+ title: "Ask ARBI about your documents",
1051
+ description: "Ask a question and verify every answer against the source.",
1052
+ dropDocuments: "Drop documents here to add them"
1053
+ };
1054
+ function AddDocumentsArea({
1055
+ onAddDocuments,
1056
+ label,
1057
+ testId
1058
+ }) {
1059
+ return /* @__PURE__ */ jsxs(
1060
+ "button",
1061
+ {
1062
+ type: "button",
1063
+ onClick: () => onAddDocuments(),
1064
+ onDragOver: (e) => {
1065
+ if (e.dataTransfer.types.includes("Files")) {
1066
+ e.preventDefault();
1067
+ e.dataTransfer.dropEffect = "copy";
1068
+ }
1069
+ },
1070
+ onDrop: (e) => {
1071
+ const files = Array.from(e.dataTransfer.files ?? []);
1072
+ if (files.length > 0) {
1073
+ e.preventDefault();
1074
+ onAddDocuments(files);
1075
+ }
1076
+ },
1077
+ "data-testid": testId,
1078
+ className: "mx-auto flex w-full max-w-sm items-center justify-center gap-2 rounded-lg bg-muted px-3 py-2.5 text-sm text-muted-foreground transition-colors hover:bg-accent focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring pointer-coarse:min-h-11",
1079
+ children: [
1080
+ /* @__PURE__ */ jsx(FileUp, { className: "h-4 w-4 shrink-0" }),
1081
+ /* @__PURE__ */ jsx("span", { children: label })
1082
+ ]
1083
+ }
1084
+ );
1085
+ }
1086
+ var BELOW = "mx-auto w-full max-w-4xl text-left";
1087
+ var CHIP = "group inline-flex items-center rounded-full bg-muted px-3.5 py-2 text-left text-sm text-foreground transition-colors hover:bg-accent focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring pointer-coarse:min-h-11";
1088
+ function ConversationEmpty({
1089
+ hasDocuments,
1090
+ prompts,
1091
+ shortcut,
1092
+ onUsePrompt,
1093
+ onAddDocuments,
1094
+ noDocuments,
1095
+ below,
1096
+ title,
1097
+ description,
1098
+ className,
1099
+ labels,
1100
+ testIds
1101
+ }) {
1102
+ const l = { ...DEFAULT_CONVERSATION_EMPTY_LABELS, ...labels };
1103
+ const rootTestId = testIdOr(testIds?.root, { testId: "conversation-empty-state" });
1104
+ const uploadTestId = testIdOr(testIds?.upload, { testId: "conversation-empty-upload" });
1105
+ const shortcutTestId = testIdOr(testIds?.shortcut, {
1106
+ testId: "conversation-shortcut-main-events"
1107
+ });
1108
+ const promptTestId = testIdOr(testIds?.prompt, { testId: "conversation-example-prompt" });
1109
+ if (!hasDocuments) {
1110
+ return /* @__PURE__ */ jsx(
1111
+ "div",
1112
+ {
1113
+ "data-slot": "conversation-empty",
1114
+ className: cn(
1115
+ "absolute inset-0 flex flex-col items-center overflow-y-auto px-6 py-8 pb-28",
1116
+ className
1117
+ ),
1118
+ "data-testid": rootTestId,
1119
+ children: /* @__PURE__ */ jsxs("div", { className: "my-auto flex w-full flex-col items-center gap-8", children: [
1120
+ /* @__PURE__ */ jsx("div", { className: "flex w-full max-w-xl flex-col items-center gap-3", children: noDocuments }),
1121
+ below && /* @__PURE__ */ jsx("div", { className: BELOW, children: below })
1122
+ ] })
1123
+ }
1124
+ );
1125
+ }
1126
+ return /* @__PURE__ */ jsx(
1127
+ "div",
1128
+ {
1129
+ "data-slot": "conversation-empty",
1130
+ className: cn("absolute inset-0 flex overflow-y-auto p-6 pb-28", className),
1131
+ "data-testid": rootTestId,
1132
+ children: /* @__PURE__ */ jsxs("div", { className: "m-auto flex w-full flex-col", children: [
1133
+ /* @__PURE__ */ jsxs("div", { className: "mx-auto w-full max-w-xl text-center", children: [
1134
+ /* @__PURE__ */ jsx("div", { className: "mx-auto mb-4 flex h-12 w-12 items-center justify-center rounded-lg bg-ai-soft text-ai-ink", children: /* @__PURE__ */ jsx(Sparkles, { className: "h-6 w-6" }) }),
1135
+ /* @__PURE__ */ jsx("h2", { className: "text-lg font-semibold text-foreground", children: title ?? l.title }),
1136
+ /* @__PURE__ */ jsx("p", { className: "mt-1.5 text-sm text-muted-foreground", children: description ?? l.description })
1137
+ ] }),
1138
+ below && /* @__PURE__ */ jsx("div", { className: cn(BELOW, "mt-6"), children: below }),
1139
+ /* @__PURE__ */ jsxs("div", { className: "mx-auto w-full max-w-xl text-center", children: [
1140
+ /* @__PURE__ */ jsxs("div", { className: cn("flex flex-wrap justify-center gap-2", below ? "mt-8" : "mt-5"), children: [
1141
+ shortcut && /* @__PURE__ */ jsxs(
1142
+ "button",
1143
+ {
1144
+ type: "button",
1145
+ onClick: () => onUsePrompt(shortcut.prompt),
1146
+ title: shortcut.prompt,
1147
+ className: cn(CHIP, "gap-2 font-medium"),
1148
+ "data-testid": shortcutTestId,
1149
+ children: [
1150
+ /* @__PURE__ */ jsx(GanttChartSquare, { className: "h-4 w-4 shrink-0 text-ai-ink" }),
1151
+ /* @__PURE__ */ jsx("span", { children: shortcut.label }),
1152
+ /* @__PURE__ */ jsx(ArrowUpRight, { className: "h-3.5 w-3.5 shrink-0 text-muted-foreground" })
1153
+ ]
1154
+ }
1155
+ ),
1156
+ prompts.map((prompt) => /* @__PURE__ */ jsxs(
1157
+ "button",
1158
+ {
1159
+ type: "button",
1160
+ onClick: () => onUsePrompt(prompt),
1161
+ className: cn(CHIP, "gap-1.5"),
1162
+ "data-testid": promptTestId,
1163
+ children: [
1164
+ prompt,
1165
+ /* @__PURE__ */ jsx(ArrowUpRight, { className: "h-3.5 w-3.5 text-muted-foreground" })
1166
+ ]
1167
+ },
1168
+ prompt
1169
+ ))
1170
+ ] }),
1171
+ /* @__PURE__ */ jsx("div", { className: "mt-5", children: /* @__PURE__ */ jsx(
1172
+ AddDocumentsArea,
1173
+ {
1174
+ onAddDocuments,
1175
+ label: l.dropDocuments,
1176
+ testId: uploadTestId
1177
+ }
1178
+ ) })
1179
+ ] })
1180
+ ] })
1181
+ }
1182
+ );
1183
+ }
1184
+ function canHover() {
1185
+ return typeof window !== "undefined" && !!window.matchMedia?.("(hover: hover)").matches;
1186
+ }
1187
+ function isFocusVisible(el) {
1188
+ try {
1189
+ return el.matches(":focus-visible");
1190
+ } catch {
1191
+ return true;
1192
+ }
1193
+ }
1194
+ var ContextChip = forwardRef(function ContextChip2({
1195
+ count,
1196
+ label,
1197
+ isOpen: isOpen2 = false,
1198
+ children,
1199
+ className,
1200
+ onMouseEnter,
1201
+ onMouseLeave,
1202
+ onFocus,
1203
+ onBlur,
1204
+ ...props
1205
+ }, ref) {
1206
+ const [isHovered, setIsHovered] = useState(false);
1207
+ const [isFocused, setIsFocused] = useState(false);
1208
+ const isEditing = isHovered || isFocused || isOpen2;
1209
+ const Icon = isEditing ? Pencil : FileText;
1210
+ return /* @__PURE__ */ jsxs(
1211
+ Button,
1212
+ {
1213
+ ref,
1214
+ type: "button",
1215
+ variant: "outline",
1216
+ "aria-label": label,
1217
+ "aria-haspopup": "dialog",
1218
+ "aria-expanded": isOpen2,
1219
+ "data-slot": "context-chip",
1220
+ "data-icon": isEditing ? "edit" : "document",
1221
+ className: cn(
1222
+ "flex h-8 min-w-0 max-w-[280px] flex-shrink-0 items-center gap-1.5 rounded-md px-2.5",
1223
+ className
1224
+ ),
1225
+ ...props,
1226
+ onMouseEnter: (e) => {
1227
+ onMouseEnter?.(e);
1228
+ if (canHover()) setIsHovered(true);
1229
+ },
1230
+ onMouseLeave: (e) => {
1231
+ onMouseLeave?.(e);
1232
+ setIsHovered(false);
1233
+ },
1234
+ onFocus: (e) => {
1235
+ onFocus?.(e);
1236
+ setIsFocused(isFocusVisible(e.currentTarget));
1237
+ },
1238
+ onBlur: (e) => {
1239
+ onBlur?.(e);
1240
+ setIsFocused(false);
1241
+ },
1242
+ children: [
1243
+ /* @__PURE__ */ jsx(Icon, { className: "h-3.5 w-3.5 flex-shrink-0", "aria-hidden": "true" }),
1244
+ /* @__PURE__ */ jsx("span", { className: "flex-shrink-0 text-sm font-medium", "data-testid": "context-chip-count", children: count >= 1e3 ? "999+" : count }),
1245
+ children
1246
+ ]
1247
+ }
1248
+ );
1249
+ });
1250
+ function ChatDocuments({ chat, labels, onPickFiles, testIds }) {
1251
+ const [isOpen2, setIsOpen] = useState(false);
1252
+ const selected = chat.documents.filter((d) => d.isSelected).length;
1253
+ const total = chat.documents.length;
1254
+ return /* @__PURE__ */ jsxs(Popover, { open: isOpen2, onOpenChange: setIsOpen, children: [
1255
+ /* @__PURE__ */ jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
1256
+ ContextChip,
1257
+ {
1258
+ count: selected,
1259
+ label: labels.documentsInChat(selected, total),
1260
+ isOpen: isOpen2,
1261
+ "data-testid": testIdOr(testIds?.documents, { testId: "chat-documents" }),
1262
+ className: "pointer-coarse:h-11"
1263
+ }
1264
+ ) }),
1265
+ /* @__PURE__ */ jsxs(
1266
+ PopoverContent,
1267
+ {
1268
+ align: "start",
1269
+ className: "w-80 p-3",
1270
+ "aria-label": labels.documentsHeading,
1271
+ "data-testid": "chat-documents-panel",
1272
+ children: [
1273
+ /* @__PURE__ */ jsxs("div", { className: "mb-2 flex items-center justify-between gap-2", children: [
1274
+ /* @__PURE__ */ jsx("h3", { className: "text-sm font-medium text-foreground", children: labels.documentsHeading }),
1275
+ total > 0 && selected < total && /* @__PURE__ */ jsx(
1276
+ Button,
1277
+ {
1278
+ type: "button",
1279
+ variant: "ghost",
1280
+ size: "sm",
1281
+ className: "pointer-coarse:min-h-11",
1282
+ onClick: chat.selectAllDocuments,
1283
+ children: labels.selectAllDocuments
1284
+ }
1285
+ )
1286
+ ] }),
1287
+ total === 0 ? /* @__PURE__ */ jsx("p", { className: "py-2 text-sm text-muted-foreground", children: labels.noDocuments }) : /* @__PURE__ */ jsx("ul", { className: "max-h-60 overflow-y-auto", children: chat.documents.map((doc) => /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsxs("label", { className: "flex cursor-pointer items-center gap-2 rounded-md px-1 py-1.5 text-sm hover:bg-muted pointer-coarse:min-h-11", children: [
1288
+ /* @__PURE__ */ jsx(
1289
+ "input",
1290
+ {
1291
+ type: "checkbox",
1292
+ checked: doc.isSelected,
1293
+ onChange: () => chat.toggleDocument(doc.id),
1294
+ className: "size-4 shrink-0 accent-[hsl(var(--primary))]"
1295
+ }
1296
+ ),
1297
+ /* @__PURE__ */ jsx("span", { className: "min-w-0 truncate", title: doc.name, children: doc.name })
1298
+ ] }) }, doc.id)) }),
1299
+ /* @__PURE__ */ jsxs(
1300
+ Button,
1301
+ {
1302
+ type: "button",
1303
+ variant: "outline",
1304
+ size: "sm",
1305
+ className: "mt-2 w-full gap-2 pointer-coarse:min-h-11",
1306
+ disabled: chat.isUploading,
1307
+ onClick: onPickFiles,
1308
+ "data-testid": testIdOr(testIds?.upload, { testId: "chat-upload" }),
1309
+ children: [
1310
+ chat.isUploading ? /* @__PURE__ */ jsx(Loader2, { className: "size-4 animate-spin", "aria-hidden": "true" }) : /* @__PURE__ */ jsx(Upload, { className: "size-4", "aria-hidden": "true" }),
1311
+ chat.isUploading ? labels.uploading : labels.uploadDocuments
1312
+ ]
1313
+ }
1314
+ )
1315
+ ]
1316
+ }
1317
+ )
1318
+ ] });
1319
+ }
1320
+ function ChatThreadList({
1321
+ threads,
1322
+ activeId,
1323
+ labels,
1324
+ onSelect,
1325
+ onNew,
1326
+ onDelete,
1327
+ testIds
1328
+ }) {
1329
+ return /* @__PURE__ */ jsxs(
1330
+ "nav",
1331
+ {
1332
+ "aria-label": labels.conversations,
1333
+ "data-slot": "chat-thread-list",
1334
+ "data-testid": testIdOr(testIds?.list, { testId: "chat-thread-list" }),
1335
+ className: "flex min-h-0 flex-1 flex-col gap-2 p-3",
1336
+ children: [
1337
+ /* @__PURE__ */ jsxs(
1338
+ Button,
1339
+ {
1340
+ type: "button",
1341
+ variant: "outline",
1342
+ className: "w-full justify-start gap-2 pointer-coarse:min-h-11",
1343
+ onClick: onNew,
1344
+ "data-testid": testIdOr(testIds?.new, { testId: "chat-new" }),
1345
+ children: [
1346
+ /* @__PURE__ */ jsx(MessageSquarePlus, { className: "size-4", "aria-hidden": "true" }),
1347
+ labels.newChat
1348
+ ]
1349
+ }
1350
+ ),
1351
+ /* @__PURE__ */ jsx("ul", { className: "flex min-h-0 flex-1 flex-col gap-0.5 overflow-y-auto", children: threads.map((thread) => {
1352
+ const isActive = thread.id === activeId;
1353
+ const title = thread.title || labels.untitled;
1354
+ return /* @__PURE__ */ jsxs("li", { className: "group relative", children: [
1355
+ /* @__PURE__ */ jsx(
1356
+ "button",
1357
+ {
1358
+ type: "button",
1359
+ onClick: () => onSelect(thread.id),
1360
+ "aria-current": isActive ? "true" : void 0,
1361
+ title,
1362
+ "data-testid": `${testIdOr(testIds?.row, { testId: "chat-conversation" })}-${thread.id}`,
1363
+ className: cn(
1364
+ "flex w-full items-center rounded-md px-3 py-2 pr-10 text-left text-sm transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring pointer-coarse:min-h-11",
1365
+ isActive ? "bg-muted font-medium text-foreground" : "text-muted-foreground hover:bg-muted/60 hover:text-foreground"
1366
+ ),
1367
+ children: /* @__PURE__ */ jsx("span", { className: "truncate", children: title })
1368
+ }
1369
+ ),
1370
+ onDelete && /* @__PURE__ */ jsx(
1371
+ "button",
1372
+ {
1373
+ type: "button",
1374
+ onClick: () => onDelete(thread.id),
1375
+ "aria-label": labels.deleteConversation(title),
1376
+ "data-testid": `${testIdOr(testIds?.delete, { testId: "chat-delete" })}-${thread.id}`,
1377
+ className: "absolute right-1 top-1/2 flex size-8 -translate-y-1/2 items-center justify-center rounded-md text-muted-foreground opacity-100 hover:bg-background hover:text-foreground focus-visible:opacity-100 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring pointer-fine:opacity-0 pointer-fine:group-hover:opacity-100 pointer-coarse:size-11",
1378
+ children: /* @__PURE__ */ jsx(Trash2, { className: "size-4", "aria-hidden": "true" })
1379
+ }
1380
+ )
1381
+ ] }, thread.id);
1382
+ }) })
1383
+ ]
1384
+ }
1385
+ );
1386
+ }
1387
+ function ChatThreadSelect({
1388
+ threads,
1389
+ activeId,
1390
+ labels,
1391
+ onSelect,
1392
+ testIds
1393
+ }) {
1394
+ return /* @__PURE__ */ jsx(
1395
+ "select",
1396
+ {
1397
+ "aria-label": labels.conversations,
1398
+ value: activeId,
1399
+ onChange: (e) => onSelect(e.target.value),
1400
+ "data-testid": testIdOr(testIds?.select, { testId: "chat-conversation-select" }),
1401
+ className: "h-9 min-w-0 flex-1 truncate rounded-md border border-input bg-background px-2 text-sm font-medium text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring pointer-coarse:h-11 pointer-coarse:text-base",
1402
+ children: threads.map((thread) => /* @__PURE__ */ jsx("option", { value: thread.id, children: thread.title || labels.untitled }, thread.id))
1403
+ }
1404
+ );
1405
+ }
1406
+ var embeddedCount = 0;
1407
+ var listeners = /* @__PURE__ */ new Set();
1408
+ function subscribe(listener) {
1409
+ listeners.add(listener);
1410
+ return () => listeners.delete(listener);
1411
+ }
1412
+ function emit() {
1413
+ listeners.forEach((l) => l());
1414
+ }
1415
+ function useRegisterEmbeddedChat(enabled = true) {
1416
+ useEffect(() => {
1417
+ if (!enabled) return;
1418
+ embeddedCount += 1;
1419
+ emit();
1420
+ return () => {
1421
+ embeddedCount -= 1;
1422
+ emit();
1423
+ };
1424
+ }, [enabled]);
1425
+ }
1426
+ function useChatSurface() {
1427
+ const count = useSyncExternalStore(
1428
+ subscribe,
1429
+ () => embeddedCount,
1430
+ () => 0
1431
+ );
1432
+ return { hasEmbeddedChat: count > 0, isDockAvailable: count === 0 };
1433
+ }
1434
+
1435
+ // src/chat/chat-page-types.ts
1436
+ var DEFAULT_CHAT_PAGE_LABELS = {
1437
+ chat: "Assistant chat",
1438
+ conversations: "Conversations",
1439
+ newChat: "New chat",
1440
+ untitled: "New chat",
1441
+ deleteConversation: (title) => `Delete conversation ${title}`,
1442
+ assistant: "Assistant",
1443
+ thinking: "Thinking\u2026",
1444
+ steps: (count) => count === 1 ? "1 step" : `${count} steps`,
1445
+ responseFailed: "Response failed",
1446
+ answerFailedTitle: "The answer could not be completed",
1447
+ uploadFailedTitle: "The upload failed",
1448
+ placeholder: "Ask about your documents",
1449
+ composerAriaLabel: "Message the assistant",
1450
+ documentsInChat: (selected, total) => `${selected} of ${total} documents in this chat`,
1451
+ documentsHeading: "Documents in this chat",
1452
+ selectAllDocuments: "Select all",
1453
+ noDocuments: "No documents yet",
1454
+ uploadDocuments: "Upload documents",
1455
+ uploading: "Uploading\u2026",
1456
+ emptyTitle: "Ask about your documents",
1457
+ emptyDescription: "Every answer is grounded in the selected documents and cites its sources.",
1458
+ emptyUploadTitle: "Add a document to start",
1459
+ emptyUploadDescription: "Upload the documents you want to ask about.",
1460
+ workflows: "Workflows",
1461
+ loadingDocuments: "Loading documents\u2026"
1462
+ };
1463
+ var USER_BUBBLE = "bg-[color:hsl(var(--chat-user-bubble,var(--muted)))] text-[color:hsl(var(--chat-user-bubble-foreground,var(--foreground)))]";
1464
+ var NEAR_BOTTOM_PX = 96;
1465
+ function MessageRow({ message, identity, renderContent, labels }) {
1466
+ if (message.role === "user") {
1467
+ return /* @__PURE__ */ jsx(Message.Root, { role: "user", messageId: message.id, children: /* @__PURE__ */ jsx(Message.Content, { role: "user", messageId: message.id, children: /* @__PURE__ */ jsx(
1468
+ Message.Bubble,
1469
+ {
1470
+ role: "user",
1471
+ messageId: message.id,
1472
+ className: USER_BUBBLE,
1473
+ testId: "chat-message-user",
1474
+ children: /* @__PURE__ */ jsx("p", { className: "m-0 whitespace-pre-wrap break-words text-base leading-relaxed", children: message.content })
1475
+ }
1476
+ ) }) });
1477
+ }
1478
+ const isStreaming = message.status === "streaming";
1479
+ const isFailed = message.status === "error";
1480
+ const hasText = message.content.trim() !== "";
1481
+ return /* @__PURE__ */ jsx(Message.Root, { role: "assistant", messageId: message.id, tone: isFailed ? "error" : void 0, children: /* @__PURE__ */ jsx(Message.Content, { role: "assistant", messageId: message.id, children: /* @__PURE__ */ jsxs(Message.Bubble, { role: "assistant", messageId: message.id, testId: "chat-message-assistant", children: [
1482
+ /* @__PURE__ */ jsx(
1483
+ Message.Byline,
1484
+ {
1485
+ messageId: message.id,
1486
+ identity: identity ?? /* @__PURE__ */ jsx("span", { className: "font-medium", children: labels.assistant }),
1487
+ timeLabel: message.stepCount ? labels.steps(message.stepCount) : "",
1488
+ isSettled: !isStreaming,
1489
+ isFailed,
1490
+ statusLabel: isFailed ? labels.responseFailed : ""
1491
+ }
1492
+ ),
1493
+ hasText && (renderContent?.(message) ?? /* @__PURE__ */ jsx(MarkdownRenderer, { isStreaming, children: message.content })),
1494
+ isStreaming && !hasText && /* @__PURE__ */ jsxs(
1495
+ "span",
1496
+ {
1497
+ className: "flex items-center gap-2 text-muted-foreground",
1498
+ "data-testid": "chat-streaming",
1499
+ children: [
1500
+ /* @__PURE__ */ jsx(Loader2, { className: "size-4 animate-spin motion-reduce:animate-none", "aria-hidden": true }),
1501
+ labels.thinking
1502
+ ]
1503
+ }
1504
+ )
1505
+ ] }) }) });
1506
+ }
1507
+ function ChatPage({
1508
+ chat,
1509
+ surface = "page",
1510
+ header,
1511
+ headerActions,
1512
+ aside,
1513
+ prompts = [],
1514
+ emptyTitle,
1515
+ emptyDescription,
1516
+ identity,
1517
+ renderContent,
1518
+ accept,
1519
+ labels,
1520
+ className,
1521
+ testIds
1522
+ }) {
1523
+ const l = { ...DEFAULT_CHAT_PAGE_LABELS, ...labels };
1524
+ useRegisterEmbeddedChat(surface === "page");
1525
+ const [draft, setDraft] = useState("");
1526
+ const [isAsideOpen, setIsAsideOpen] = useState(false);
1527
+ const [isAway, setIsAway] = useState(false);
1528
+ const scrollRef = useRef(null);
1529
+ const fileRef = useRef(null);
1530
+ const textareaRef = useRef(null);
1531
+ const last = chat.messages[chat.messages.length - 1];
1532
+ const announcement = useStreamAnnouncement({
1533
+ isStreaming: chat.isRunning,
1534
+ text: last?.role === "assistant" ? last.content : "",
1535
+ resetKey: chat.activeThreadId
1536
+ });
1537
+ const scrollToEnd = useCallback(() => {
1538
+ const el = scrollRef.current;
1539
+ if (el) el.scrollTop = el.scrollHeight;
1540
+ }, []);
1541
+ useEffect(() => {
1542
+ if (!isAway) scrollToEnd();
1543
+ }, [chat.messages, isAway, scrollToEnd]);
1544
+ useEffect(() => {
1545
+ setIsAway(false);
1546
+ }, [chat.activeThreadId]);
1547
+ const onScroll = () => {
1548
+ const el = scrollRef.current;
1549
+ if (el) setIsAway(el.scrollHeight - el.scrollTop - el.clientHeight > NEAR_BOTTOM_PX);
1550
+ };
1551
+ const submit = (e) => {
1552
+ e?.preventDefault();
1553
+ const text = draft.trim();
1554
+ if (!text || chat.isRunning) return;
1555
+ setDraft("");
1556
+ setIsAway(false);
1557
+ void chat.send(text);
1558
+ };
1559
+ const pickFiles = () => fileRef.current?.click();
1560
+ const onFiles = (files) => {
1561
+ if (files.length > 0) void chat.upload(files);
1562
+ };
1563
+ const activeThread = chat.threads.find((t) => t.id === chat.activeThreadId);
1564
+ const hasDocuments = chat.documents.length > 0;
1565
+ const isEmpty = chat.messages.length === 0;
1566
+ const heading = header ?? /* @__PURE__ */ jsx("h2", { className: "min-w-0 truncate text-sm font-semibold text-foreground", children: activeThread?.title || l.untitled });
1567
+ return /* @__PURE__ */ jsxs(
1568
+ "section",
1569
+ {
1570
+ "aria-label": l.chat,
1571
+ "data-slot": "chat-page",
1572
+ "data-surface": surface,
1573
+ "data-testid": testIdOr(testIds?.root, { testId: "chat-page" }),
1574
+ className: cn(
1575
+ "@container/chat flex h-full min-h-0 w-full overflow-hidden bg-background text-foreground",
1576
+ className
1577
+ ),
1578
+ children: [
1579
+ /* @__PURE__ */ jsx("div", { className: "hidden w-64 shrink-0 flex-col border-r border-border @3xl/chat:flex", children: /* @__PURE__ */ jsx(
1580
+ ChatThreadList,
1581
+ {
1582
+ threads: chat.threads,
1583
+ activeId: chat.activeThreadId,
1584
+ labels: l,
1585
+ onSelect: chat.selectThread,
1586
+ onNew: chat.newThread,
1587
+ onDelete: chat.deleteThread
1588
+ }
1589
+ ) }),
1590
+ /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 flex-1 flex-col", children: [
1591
+ /* @__PURE__ */ jsxs(
1592
+ "div",
1593
+ {
1594
+ "data-slot": "chat-page-header",
1595
+ className: "flex min-h-12 shrink-0 items-center gap-2 border-b border-border px-3 py-1.5",
1596
+ children: [
1597
+ /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 flex-1 items-center gap-2", children: [
1598
+ /* @__PURE__ */ jsx("div", { className: "hidden min-w-0 flex-1 @3xl/chat:block", children: heading }),
1599
+ /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 flex-1 items-center gap-2 @3xl/chat:hidden", children: [
1600
+ header ? /* @__PURE__ */ jsx("div", { className: "min-w-0 shrink", children: header }) : null,
1601
+ /* @__PURE__ */ jsx(
1602
+ ChatThreadSelect,
1603
+ {
1604
+ threads: chat.threads,
1605
+ activeId: chat.activeThreadId,
1606
+ labels: l,
1607
+ onSelect: chat.selectThread
1608
+ }
1609
+ ),
1610
+ /* @__PURE__ */ jsx(
1611
+ Button,
1612
+ {
1613
+ type: "button",
1614
+ variant: "ghost",
1615
+ size: "icon",
1616
+ className: "size-9 shrink-0 pointer-coarse:size-11",
1617
+ "aria-label": l.newChat,
1618
+ onClick: chat.newThread,
1619
+ children: /* @__PURE__ */ jsx(MessageSquarePlus, { className: "size-4", "aria-hidden": "true" })
1620
+ }
1621
+ )
1622
+ ] })
1623
+ ] }),
1624
+ aside && /* @__PURE__ */ jsxs(
1625
+ Button,
1626
+ {
1627
+ type: "button",
1628
+ variant: "outline",
1629
+ size: "sm",
1630
+ className: "shrink-0 gap-1.5 pointer-coarse:min-h-11 @6xl/chat:hidden",
1631
+ "aria-expanded": isAsideOpen,
1632
+ "aria-controls": "chat-page-aside-panel",
1633
+ onClick: () => setIsAsideOpen((v) => !v),
1634
+ "data-testid": testIdOr(testIds?.asideToggle, { testId: "chat-aside-toggle" }),
1635
+ children: [
1636
+ /* @__PURE__ */ jsx(PanelRight, { className: "size-4", "aria-hidden": "true" }),
1637
+ l.workflows
1638
+ ]
1639
+ }
1640
+ ),
1641
+ headerActions
1642
+ ]
1643
+ }
1644
+ ),
1645
+ aside && isAsideOpen && /* @__PURE__ */ jsx(
1646
+ "div",
1647
+ {
1648
+ id: "chat-page-aside-panel",
1649
+ "data-slot": "chat-page-aside-panel",
1650
+ className: "max-h-[45%] shrink-0 overflow-y-auto border-b border-border p-3 @6xl/chat:hidden",
1651
+ children: aside
1652
+ }
1653
+ ),
1654
+ /* @__PURE__ */ jsxs(
1655
+ MessageList.Root,
1656
+ {
1657
+ isBusy: chat.isRunning,
1658
+ testId: testIdOr(testIds?.transcript, { testId: "chat-messages" }),
1659
+ className: "relative flex min-h-0 flex-1 flex-col",
1660
+ children: [
1661
+ /* @__PURE__ */ jsx(MessageList.Status, { message: announcement }),
1662
+ /* @__PURE__ */ jsx(
1663
+ "div",
1664
+ {
1665
+ ref: scrollRef,
1666
+ onScroll,
1667
+ "data-slot": "chat-page-scroll",
1668
+ className: "relative min-h-0 flex-1 overflow-y-auto overscroll-contain",
1669
+ children: isEmpty ? chat.isLoadingDocuments ? /* @__PURE__ */ jsx("div", { className: "flex h-full items-center justify-center text-sm text-muted-foreground", children: l.loadingDocuments }) : /* @__PURE__ */ jsx(
1670
+ ConversationEmpty,
1671
+ {
1672
+ hasDocuments,
1673
+ prompts,
1674
+ onUsePrompt: (text) => {
1675
+ setDraft(text);
1676
+ textareaRef.current?.focus();
1677
+ },
1678
+ onAddDocuments: (files) => files ? onFiles(files) : pickFiles(),
1679
+ title: emptyTitle ?? l.emptyTitle,
1680
+ description: emptyDescription ?? l.emptyDescription,
1681
+ className: "pb-6",
1682
+ noDocuments: /* @__PURE__ */ jsxs(Fragment, { children: [
1683
+ /* @__PURE__ */ jsxs("div", { className: "text-center", children: [
1684
+ /* @__PURE__ */ jsx("h2", { className: "text-lg font-semibold", children: l.emptyUploadTitle }),
1685
+ /* @__PURE__ */ jsx("p", { className: "mt-1 text-sm text-muted-foreground", children: l.emptyUploadDescription })
1686
+ ] }),
1687
+ /* @__PURE__ */ jsxs(
1688
+ Button,
1689
+ {
1690
+ type: "button",
1691
+ className: "gap-2 pointer-coarse:min-h-11",
1692
+ onClick: pickFiles,
1693
+ disabled: chat.isUploading,
1694
+ children: [
1695
+ /* @__PURE__ */ jsx(Upload, { className: "size-4", "aria-hidden": "true" }),
1696
+ chat.isUploading ? l.uploading : l.uploadDocuments
1697
+ ]
1698
+ }
1699
+ )
1700
+ ] })
1701
+ }
1702
+ ) : /* @__PURE__ */ jsx(
1703
+ "div",
1704
+ {
1705
+ role: "list",
1706
+ "aria-label": "Conversation messages",
1707
+ className: "mx-auto flex w-full max-w-3xl flex-col gap-5 px-3 py-5 @lg/chat:px-6",
1708
+ children: chat.messages.map((message) => /* @__PURE__ */ jsx("div", { role: "listitem", "data-message-id": message.id, children: /* @__PURE__ */ jsx(
1709
+ MessageRow,
1710
+ {
1711
+ message,
1712
+ identity,
1713
+ renderContent,
1714
+ labels: l
1715
+ }
1716
+ ) }, message.id))
1717
+ }
1718
+ )
1719
+ }
1720
+ ),
1721
+ /* @__PURE__ */ jsx(
1722
+ ScrollToBottom,
1723
+ {
1724
+ className: "bottom-3 top-auto",
1725
+ show: isAway && !isEmpty,
1726
+ onClick: () => {
1727
+ setIsAway(false);
1728
+ scrollToEnd();
1729
+ }
1730
+ }
1731
+ )
1732
+ ]
1733
+ }
1734
+ ),
1735
+ /* @__PURE__ */ jsx("div", { className: "shrink-0 border-t border-border/60 bg-background px-3 pb-3 pt-2 @lg/chat:px-6", children: /* @__PURE__ */ jsxs("div", { className: "mx-auto w-full max-w-3xl", children: [
1736
+ chat.error && /* @__PURE__ */ jsx(
1737
+ MessageAlert,
1738
+ {
1739
+ title: l.answerFailedTitle,
1740
+ description: chat.error,
1741
+ isBusy: chat.isRunning,
1742
+ onActivate: () => void chat.retry(),
1743
+ testId: testIdOr(testIds?.alert, { testId: "chat-alert" })
1744
+ }
1745
+ ),
1746
+ chat.uploadError && /* @__PURE__ */ jsx(
1747
+ MessageAlert,
1748
+ {
1749
+ title: l.uploadFailedTitle,
1750
+ description: chat.uploadError,
1751
+ testId: "chat-upload-alert"
1752
+ }
1753
+ ),
1754
+ /* @__PURE__ */ jsx(Composer.Root, { onSubmit: submit, testId: "chat-composer", children: /* @__PURE__ */ jsxs(Composer.Frame, { className: "px-3 py-2 shadow-sm", children: [
1755
+ /* @__PURE__ */ jsx(
1756
+ Composer.Input,
1757
+ {
1758
+ value: draft,
1759
+ onValueChange: setDraft,
1760
+ placeholder: l.placeholder,
1761
+ ariaLabel: l.composerAriaLabel,
1762
+ textareaRef,
1763
+ testId: testIdOr(testIds?.input, { testId: "chat-input" })
1764
+ }
1765
+ ),
1766
+ /* @__PURE__ */ jsx(
1767
+ Composer.Toolbar,
1768
+ {
1769
+ leading: /* @__PURE__ */ jsx(
1770
+ ChatDocuments,
1771
+ {
1772
+ chat,
1773
+ labels: l,
1774
+ onPickFiles: pickFiles,
1775
+ testIds: { documents: testIds?.documents, upload: testIds?.upload }
1776
+ }
1777
+ ),
1778
+ trailing: /* @__PURE__ */ jsx(
1779
+ Composer.Send,
1780
+ {
1781
+ onSend: () => submit(),
1782
+ disabled: chat.isRunning || draft.trim() === "",
1783
+ testIds: { send: testIdOr(testIds?.send, { testId: "chat-send" }) }
1784
+ }
1785
+ )
1786
+ }
1787
+ )
1788
+ ] }) })
1789
+ ] }) })
1790
+ ] }),
1791
+ aside && /* @__PURE__ */ jsx(
1792
+ "aside",
1793
+ {
1794
+ "aria-label": l.workflows,
1795
+ "data-slot": "chat-page-aside",
1796
+ "data-testid": testIdOr(testIds?.aside, { testId: "chat-aside" }),
1797
+ className: "hidden w-80 shrink-0 overflow-y-auto border-l border-border p-4 @6xl/chat:block",
1798
+ children: aside
1799
+ }
1800
+ ),
1801
+ /* @__PURE__ */ jsx(
1802
+ "input",
1803
+ {
1804
+ ref: fileRef,
1805
+ type: "file",
1806
+ multiple: true,
1807
+ accept,
1808
+ className: "sr-only",
1809
+ tabIndex: -1,
1810
+ "aria-hidden": "true",
1811
+ "data-testid": "chat-file-input",
1812
+ onChange: (e) => {
1813
+ onFiles(Array.from(e.target.files ?? []));
1814
+ e.target.value = "";
1815
+ }
1816
+ }
1817
+ )
1818
+ ]
1819
+ }
1820
+ );
1821
+ }
1822
+ var isOpen = false;
1823
+ var listeners2 = /* @__PURE__ */ new Set();
1824
+ var subscribe2 = (l) => {
1825
+ listeners2.add(l);
1826
+ return () => listeners2.delete(l);
1827
+ };
1828
+ function set(next) {
1829
+ if (next === isOpen) return;
1830
+ isOpen = next;
1831
+ listeners2.forEach((l) => l());
1832
+ }
1833
+ function useAssistantDock() {
1834
+ const open = useSyncExternalStore(
1835
+ subscribe2,
1836
+ () => isOpen,
1837
+ () => false
1838
+ );
1839
+ return { isOpen: open, setOpen: set, toggle: () => set(!isOpen) };
1840
+ }
1841
+ function AssistantDockToggle({
1842
+ label = "Assistant chat",
1843
+ testId = "chat-dock-toggle"
1844
+ }) {
1845
+ const { isDockAvailable } = useChatSurface();
1846
+ const dock = useAssistantDock();
1847
+ if (!isDockAvailable) return null;
1848
+ return /* @__PURE__ */ jsx(ToolButton, { onClick: dock.toggle, label, active: dock.isOpen, "data-testid": testId, children: /* @__PURE__ */ jsx(MessageSquare, { className: "size-4", "aria-hidden": "true" }) });
1849
+ }
1850
+ function AssistantDock({
1851
+ chat,
1852
+ isMobile = false,
1853
+ width = 380,
1854
+ label = "Assistant chat",
1855
+ closeLabel = "Close chat",
1856
+ headerActions,
1857
+ ...pageProps
1858
+ }) {
1859
+ const { isDockAvailable } = useChatSurface();
1860
+ const dock = useAssistantDock();
1861
+ if (!isDockAvailable) return null;
1862
+ return /* @__PURE__ */ jsx(
1863
+ Shell.Dock,
1864
+ {
1865
+ isOpen: dock.isOpen,
1866
+ onOpenChange: dock.setOpen,
1867
+ isMobile,
1868
+ width,
1869
+ label,
1870
+ closeLabel,
1871
+ "data-testid": "chat-dock",
1872
+ children: /* @__PURE__ */ jsx(
1873
+ ChatPage,
1874
+ {
1875
+ ...pageProps,
1876
+ chat,
1877
+ surface: "dock",
1878
+ className: "min-h-0 flex-1",
1879
+ headerActions: /* @__PURE__ */ jsxs(Fragment, { children: [
1880
+ headerActions,
1881
+ !isMobile && /* @__PURE__ */ jsx(
1882
+ ToolButton,
1883
+ {
1884
+ onClick: () => dock.setOpen(false),
1885
+ label: closeLabel,
1886
+ "data-testid": "chat-close",
1887
+ children: /* @__PURE__ */ jsx(X, { className: "size-4", "aria-hidden": "true" })
1888
+ }
1889
+ )
1890
+ ] })
1891
+ }
1892
+ )
1893
+ }
1894
+ );
1895
+ }
1896
+
1897
+ export { AssistantDock, AssistantDockToggle, ChatDocuments, ChatPage, ChatThreadList, ChatThreadSelect, Composer, ComposerBranchingNotice, ComposerContainer, ComposerDock, ComposerFrame, ComposerInput, ComposerRoot, ComposerSend, ComposerToolbar, ComposerVoiceButton, ContextChip, ConversationEmpty, DEFAULT_CHAT_PAGE_LABELS, DEFAULT_COMPOSER_LABELS, DEFAULT_CONVERSATION_EMPTY_LABELS, DEFAULT_MESSAGE_ALERT_LABELS, DEFAULT_MESSAGE_LABELS, DEFAULT_MESSAGE_LIST_LABELS, Message, MessageActionButton, MessageActionMenu, MessageAlert, MessageBody, MessageBubble, MessageByline, MessageContent, MessageFooter, MessageList, MessageListBanner, MessageListEmpty, MessageListRoot, MessageListRow, MessageListRows, MessageListStatus, MessageListTail, MessageNarration, MessageRoot, MessageSources, MessageSubagentBadge, ScrollToBottom, SourceChip, toSpeakableText, useAssistantDock, useChatSurface, useRegisterEmbeddedChat, useStreamAnnouncement };
1898
+ //# sourceMappingURL=chunk-JPO3F4J7.js.map
1899
+ //# sourceMappingURL=chunk-JPO3F4J7.js.map