@agents24/chat-react 0.4.0 → 0.5.1

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 (132) hide show
  1. package/README.md +92 -18
  2. package/compatibility.json +10 -10
  3. package/dist/chunk-SSUO4EPD.js +189 -0
  4. package/dist/chunk-SSUO4EPD.js.map +1 -0
  5. package/dist/chunk-ZCNHLFRB.js +108 -0
  6. package/dist/chunk-ZCNHLFRB.js.map +1 -0
  7. package/dist/index.cjs +112 -95
  8. package/dist/index.cjs.map +1 -1
  9. package/dist/index.d.ts +0 -1
  10. package/dist/index.js +362 -68
  11. package/dist/index.js.map +1 -1
  12. package/dist/mcp-oauth.d.ts +13 -0
  13. package/dist/runtime/index.cjs +594 -0
  14. package/dist/runtime/index.cjs.map +1 -0
  15. package/dist/runtime/index.d.ts +2 -0
  16. package/dist/runtime/index.js +458 -0
  17. package/dist/runtime/index.js.map +1 -0
  18. package/dist/runtime/input-contract.d.ts +20 -0
  19. package/dist/runtime/use-agent-chat-runtime.d.ts +83 -0
  20. package/dist/scaffold/app.d.ts +30 -0
  21. package/dist/scaffold/components/chat/chat-composer.d.ts +21 -0
  22. package/dist/scaffold/components/chat/chat-message.d.ts +21 -0
  23. package/dist/scaffold/components/chat/chat-shell.d.ts +8 -0
  24. package/dist/scaffold/components/chat/collapsible-user-message.d.ts +3 -0
  25. package/dist/scaffold/components/chat/external-link-dialog.d.ts +2 -0
  26. package/dist/scaffold/components/chat/identity-control.d.ts +4 -0
  27. package/dist/scaffold/components/chat/message-parts.d.ts +12 -0
  28. package/dist/scaffold/components/chat/theme-menu.d.ts +1 -0
  29. package/dist/scaffold/components/chat/thread-sidebar.d.ts +16 -0
  30. package/dist/scaffold/components/chat/timeline-loading.d.ts +7 -0
  31. package/dist/scaffold/components/chat/turn-outline.d.ts +4 -0
  32. package/dist/scaffold/components/theme-provider.d.ts +20 -0
  33. package/dist/scaffold/components/ui/attachment.d.ts +24 -0
  34. package/dist/scaffold/components/ui/bubble.d.ts +17 -0
  35. package/dist/scaffold/components/ui/button-group.d.ts +11 -0
  36. package/dist/scaffold/components/ui/button.d.ts +10 -0
  37. package/dist/scaffold/components/ui/dialog.d.ts +17 -0
  38. package/dist/scaffold/components/ui/dropdown-menu.d.ts +29 -0
  39. package/dist/scaffold/components/ui/input.d.ts +3 -0
  40. package/dist/scaffold/components/ui/marker.d.ts +11 -0
  41. package/dist/scaffold/components/ui/message-scroller.d.ts +10 -0
  42. package/dist/scaffold/components/ui/message.d.ts +10 -0
  43. package/dist/scaffold/components/ui/popover.d.ts +6 -0
  44. package/dist/scaffold/components/ui/select.d.ts +8 -0
  45. package/dist/scaffold/components/ui/separator.d.ts +4 -0
  46. package/dist/scaffold/components/ui/sheet.d.ts +14 -0
  47. package/dist/scaffold/components/ui/sidebar.d.ts +69 -0
  48. package/dist/scaffold/components/ui/skeleton.d.ts +2 -0
  49. package/dist/scaffold/components/ui/textarea.d.ts +3 -0
  50. package/dist/scaffold/components/ui/tooltip.d.ts +15 -0
  51. package/dist/scaffold/hooks/use-mobile.d.ts +1 -0
  52. package/dist/scaffold/identity.d.ts +10 -0
  53. package/dist/scaffold/index.cjs +2270 -0
  54. package/dist/scaffold/index.cjs.map +1 -0
  55. package/dist/scaffold/index.d.ts +14 -0
  56. package/dist/scaffold/index.js +2244 -0
  57. package/dist/scaffold/index.js.map +1 -0
  58. package/dist/scaffold/lib/utils.d.ts +2 -0
  59. package/dist/styles.css +143 -298
  60. package/dist/types.d.ts +3 -13
  61. package/dist/ui/adapters.d.ts +1 -1
  62. package/dist/ui/agent-chat-actions.d.ts +12 -5
  63. package/dist/ui/agent-chat-composer.d.ts +2 -1
  64. package/dist/ui/agent-chat-context-compression-row.d.ts +5 -0
  65. package/dist/ui/agent-chat-context-status.d.ts +13 -0
  66. package/dist/ui/agent-chat-message.d.ts +7 -5
  67. package/dist/ui/agent-response-timeline.d.ts +29 -7
  68. package/dist/ui/ask-user-interaction-state.d.ts +20 -0
  69. package/dist/ui/ask-user-interaction.d.ts +8 -0
  70. package/dist/ui/index.cjs +1392 -272
  71. package/dist/ui/index.cjs.map +1 -1
  72. package/dist/ui/index.d.ts +5 -0
  73. package/dist/ui/index.js +2380 -65
  74. package/dist/ui/index.js.map +1 -1
  75. package/dist/ui/tool-sessions.d.ts +70 -0
  76. package/dist/ui/use-audio-recorder.d.ts +8 -0
  77. package/package.json +27 -12
  78. package/scaffold/components.json +25 -0
  79. package/scaffold/manifest.json +28 -0
  80. package/scaffold/src/app.tsx +71 -0
  81. package/scaffold/src/chat.css +54 -0
  82. package/scaffold/src/components/chat/chat-composer.tsx +237 -0
  83. package/scaffold/src/components/chat/chat-message.tsx +76 -0
  84. package/scaffold/src/components/chat/chat-shell.tsx +257 -0
  85. package/scaffold/src/components/chat/collapsible-user-message.tsx +37 -0
  86. package/scaffold/src/components/chat/external-link-dialog.tsx +48 -0
  87. package/scaffold/src/components/chat/identity-control.tsx +69 -0
  88. package/scaffold/src/components/chat/message-parts.tsx +83 -0
  89. package/scaffold/src/components/chat/theme-menu.tsx +79 -0
  90. package/scaffold/src/components/chat/thread-sidebar.tsx +114 -0
  91. package/scaffold/src/components/chat/timeline-loading.ts +17 -0
  92. package/scaffold/src/components/chat/turn-outline.tsx +73 -0
  93. package/scaffold/src/components/theme-provider.tsx +91 -0
  94. package/scaffold/src/components/ui/attachment.tsx +204 -0
  95. package/scaffold/src/components/ui/bubble.tsx +125 -0
  96. package/scaffold/src/components/ui/button-group.tsx +83 -0
  97. package/scaffold/src/components/ui/button.tsx +65 -0
  98. package/scaffold/src/components/ui/dialog.tsx +171 -0
  99. package/scaffold/src/components/ui/dropdown-menu.tsx +271 -0
  100. package/scaffold/src/components/ui/input.tsx +19 -0
  101. package/scaffold/src/components/ui/marker.tsx +69 -0
  102. package/scaffold/src/components/ui/message-scroller.tsx +129 -0
  103. package/scaffold/src/components/ui/message.tsx +92 -0
  104. package/scaffold/src/components/ui/popover.tsx +40 -0
  105. package/scaffold/src/components/ui/select.tsx +92 -0
  106. package/scaffold/src/components/ui/separator.tsx +28 -0
  107. package/scaffold/src/components/ui/sheet.tsx +150 -0
  108. package/scaffold/src/components/ui/sidebar.tsx +677 -0
  109. package/scaffold/src/components/ui/skeleton.tsx +13 -0
  110. package/scaffold/src/components/ui/textarea.tsx +18 -0
  111. package/scaffold/src/components/ui/tooltip.tsx +56 -0
  112. package/scaffold/src/hooks/use-mobile.ts +24 -0
  113. package/scaffold/src/identity.ts +17 -0
  114. package/scaffold/src/index.css +222 -0
  115. package/scaffold/src/index.tsx +39 -0
  116. package/scaffold/src/lib/utils.ts +6 -0
  117. package/scaffold/styles.css +3 -0
  118. package/dist/chunk-CHAU7FNW.js +0 -140
  119. package/dist/chunk-CHAU7FNW.js.map +0 -1
  120. package/dist/chunk-LI67M74F.js +0 -445
  121. package/dist/chunk-LI67M74F.js.map +0 -1
  122. package/dist/chunk-NLEOWTZG.js +0 -1327
  123. package/dist/chunk-NLEOWTZG.js.map +0 -1
  124. package/dist/renderers.d.ts +0 -10
  125. package/dist/templates/agent-chat-app.d.ts +0 -18
  126. package/dist/templates/agent-chat-layout.d.ts +0 -69
  127. package/dist/templates/agent-chat-shell.d.ts +0 -37
  128. package/dist/templates/index.cjs +0 -2476
  129. package/dist/templates/index.cjs.map +0 -1
  130. package/dist/templates/index.d.ts +0 -3
  131. package/dist/templates/index.js +0 -723
  132. package/dist/templates/index.js.map +0 -1
@@ -1,1327 +0,0 @@
1
- import {
2
- useStreamingText
3
- } from "./chunk-CHAU7FNW.js";
4
-
5
- // src/ui/utils.ts
6
- import { clsx } from "clsx";
7
- import { twMerge } from "tailwind-merge";
8
- function cn(...values) {
9
- return twMerge(clsx(values));
10
- }
11
-
12
- // src/ui/agent-chat-composer.tsx
13
- import * as React from "react";
14
- import { ArrowUp, Paperclip, Plus, Square, X } from "lucide-react";
15
- import { jsx, jsxs } from "react/jsx-runtime";
16
- var createId = () => typeof crypto !== "undefined" && "randomUUID" in crypto ? crypto.randomUUID() : `file-${Date.now()}-${Math.random().toString(36).slice(2)}`;
17
- function createComposerFile(file) {
18
- return {
19
- id: createId(),
20
- type: "file",
21
- url: URL.createObjectURL(file),
22
- filename: file.name || "attachment",
23
- mediaType: file.type || "application/octet-stream",
24
- source: file
25
- };
26
- }
27
- function revokeComposerFiles(files) {
28
- files.forEach((file) => {
29
- try {
30
- URL.revokeObjectURL(file.url);
31
- } catch {
32
- }
33
- });
34
- }
35
- function formatFileCount(files) {
36
- if (files.length === 1) return files[0].filename;
37
- return `${files.length} files`;
38
- }
39
- var pillButtonClass = "inline-flex size-8 shrink-0 items-center justify-center gap-2 rounded-full border-0 bg-neutral-100 text-sm text-neutral-600 shadow-none transition-colors hover:bg-neutral-200 hover:text-neutral-800 focus-visible:border-neutral-300 focus-visible:outline-none focus-visible:ring-0 disabled:pointer-events-none disabled:opacity-50 dark:bg-neutral-800 dark:text-neutral-300 dark:hover:bg-neutral-700";
40
- var submitButtonClass = "inline-flex size-9 shrink-0 items-center justify-center gap-2 rounded-full border-0 bg-neutral-900 text-sm text-white shadow-none transition-[background-color,color,opacity,transform] duration-200 hover:bg-neutral-800 hover:text-white focus-visible:border-neutral-300 focus-visible:outline-none focus-visible:ring-0 disabled:pointer-events-none disabled:opacity-50 dark:bg-neutral-100 dark:text-neutral-900 dark:hover:bg-white";
41
- function AgentChatComposer({
42
- accept,
43
- allowAttachments = true,
44
- attachmentLayout = "flow",
45
- className,
46
- disabled = false,
47
- inputToolbarContent,
48
- isRunning = false,
49
- onAttachmentCountChange,
50
- onStop,
51
- onSubmit,
52
- placeholder = "Send follow-up\u2026",
53
- textareaRef
54
- }) {
55
- const fileInputRef = React.useRef(null);
56
- const filesRef = React.useRef([]);
57
- const localTextareaRef = React.useRef(null);
58
- const [text, setText] = React.useState("");
59
- const [files, setFiles] = React.useState([]);
60
- const [isSubmitting, setIsSubmitting] = React.useState(false);
61
- const isExpanded = text.includes("\n") || text.length > 62;
62
- const setTextareaRef = React.useCallback(
63
- (node) => {
64
- localTextareaRef.current = node;
65
- if (typeof textareaRef === "function") {
66
- textareaRef(node);
67
- } else if (textareaRef) {
68
- textareaRef.current = node;
69
- }
70
- },
71
- [textareaRef]
72
- );
73
- React.useEffect(() => {
74
- filesRef.current = files;
75
- onAttachmentCountChange?.(files.length);
76
- }, [files, onAttachmentCountChange]);
77
- React.useEffect(() => () => revokeComposerFiles(filesRef.current), []);
78
- React.useLayoutEffect(() => {
79
- const textarea = localTextareaRef.current;
80
- if (!textarea) return;
81
- if (isExpanded) {
82
- textarea.style.height = "auto";
83
- const scrollHeight = textarea.scrollHeight;
84
- textarea.style.height = `${Math.min(scrollHeight, 224)}px`;
85
- textarea.style.overflowY = scrollHeight >= 224 ? "auto" : "hidden";
86
- return;
87
- }
88
- textarea.style.height = "";
89
- textarea.style.overflowY = "hidden";
90
- }, [isExpanded, text]);
91
- const canSubmit = !disabled && !isSubmitting && !isRunning && (text.trim().length > 0 || files.length > 0);
92
- const handleFilesChange = (event) => {
93
- const selected = Array.from(event.target.files || []).map(createComposerFile);
94
- if (selected.length > 0) {
95
- setFiles((current) => [...current, ...selected]);
96
- }
97
- event.currentTarget.value = "";
98
- };
99
- const removeFile = (fileId) => {
100
- setFiles((current) => {
101
- const target = current.find((file) => file.id === fileId);
102
- if (target) revokeComposerFiles([target]);
103
- return current.filter((file) => file.id !== fileId);
104
- });
105
- };
106
- const submit = async () => {
107
- if (!canSubmit) return;
108
- const submittedText = text.trim();
109
- const submittedFiles = files;
110
- setIsSubmitting(true);
111
- try {
112
- await onSubmit({ text: submittedText, files: submittedFiles });
113
- setText("");
114
- setFiles([]);
115
- revokeComposerFiles(submittedFiles);
116
- } finally {
117
- setIsSubmitting(false);
118
- }
119
- };
120
- const handleKeyDown = (event) => {
121
- if (event.key === "Enter" && !event.shiftKey) {
122
- event.preventDefault();
123
- void submit();
124
- }
125
- };
126
- return /* @__PURE__ */ jsxs("div", { className: cn("a24-composer relative mx-auto w-full max-w-3xl overflow-visible", className), children: [
127
- files.length > 0 ? /* @__PURE__ */ jsx(
128
- "div",
129
- {
130
- "data-testid": "agent-chat-composer-attachments",
131
- "data-layout": attachmentLayout,
132
- className: cn(
133
- "a24-composer__attachments flex min-w-0 gap-2 py-1",
134
- attachmentLayout === "overlay" ? "absolute bottom-full left-0 right-0 mb-1.5 flex-nowrap overflow-x-auto overflow-y-hidden overscroll-x-contain px-2" : "mb-2 flex-wrap px-1"
135
- ),
136
- children: files.map((file) => /* @__PURE__ */ jsxs(
137
- "div",
138
- {
139
- className: "a24-composer__attachment group relative flex h-8 max-w-[min(20rem,calc(100vw-2rem))] cursor-default select-none items-center gap-1.5 rounded-md border border-neutral-200 bg-white px-1.5 text-sm font-medium text-foreground transition-colors hover:bg-neutral-50",
140
- children: [
141
- /* @__PURE__ */ jsx("span", { className: "flex size-5 shrink-0 items-center justify-center rounded bg-background text-muted-foreground", children: /* @__PURE__ */ jsx(Paperclip, { className: "size-3" }) }),
142
- /* @__PURE__ */ jsx("span", { className: "min-w-0 flex-1 truncate", children: file.filename }),
143
- /* @__PURE__ */ jsx(
144
- "button",
145
- {
146
- "aria-label": `Remove ${file.filename}`,
147
- className: "a24-composer__attachment-remove inline-flex size-5 shrink-0 items-center justify-center rounded text-muted-foreground opacity-70 transition-colors hover:bg-muted hover:text-foreground hover:opacity-100 focus-visible:outline-none",
148
- onClick: () => removeFile(file.id),
149
- type: "button",
150
- children: /* @__PURE__ */ jsx(X, { className: "size-3" })
151
- }
152
- )
153
- ]
154
- },
155
- file.id
156
- ))
157
- }
158
- ) : null,
159
- /* @__PURE__ */ jsx(
160
- "div",
161
- {
162
- "data-expanded": isExpanded ? "" : void 0,
163
- className: cn(
164
- "a24-composer__surface relative w-full border border-neutral-200 bg-white shadow-sm transition-colors duration-200 focus-within:border-neutral-300 dark:border-neutral-700 dark:bg-card dark:focus-within:border-neutral-600",
165
- isExpanded ? "rounded-[18px] p-2 pb-1.5" : "rounded-full px-2 py-[3px]"
166
- ),
167
- children: /* @__PURE__ */ jsxs(
168
- "div",
169
- {
170
- className: "a24-composer__grid w-full",
171
- style: {
172
- alignItems: "center",
173
- display: "grid",
174
- gridTemplateAreas: isExpanded ? `"textarea textarea textarea" "plus toolbar submit"` : `"plus textarea composer mic submit"`,
175
- gridTemplateColumns: isExpanded ? "auto 1fr auto" : "auto 1fr auto auto auto",
176
- gap: isExpanded ? "3px 10px" : "8px"
177
- },
178
- children: [
179
- /* @__PURE__ */ jsxs("div", { style: { gridArea: "plus" }, className: "a24-composer__attach-slot flex shrink-0 items-center justify-center", children: [
180
- /* @__PURE__ */ jsx(
181
- "input",
182
- {
183
- ref: fileInputRef,
184
- accept,
185
- "aria-label": "Attach files",
186
- className: "hidden",
187
- multiple: true,
188
- name: "attachments",
189
- onChange: handleFilesChange,
190
- type: "file"
191
- }
192
- ),
193
- /* @__PURE__ */ jsx(
194
- "button",
195
- {
196
- "aria-label": "Attach files",
197
- className: cn("a24-composer__attach", pillButtonClass),
198
- disabled: disabled || isSubmitting || isRunning || !allowAttachments,
199
- onClick: () => fileInputRef.current?.click(),
200
- title: "Attach files",
201
- type: "button",
202
- children: /* @__PURE__ */ jsx(Plus, { className: "size-4" })
203
- }
204
- )
205
- ] }),
206
- /* @__PURE__ */ jsx("div", { style: { gridArea: "textarea" }, className: "a24-composer__textarea-slot min-w-0 w-full", children: /* @__PURE__ */ jsx(
207
- "textarea",
208
- {
209
- ref: setTextareaRef,
210
- "aria-label": "Message",
211
- autoComplete: "off",
212
- className: cn(
213
- "a24-composer__textarea w-full resize-none border-0 bg-transparent px-1 text-[15px] leading-relaxed text-foreground shadow-none outline-none placeholder:text-neutral-400 focus-visible:ring-0 scrollbar-thin",
214
- isExpanded ? "min-h-9 max-h-[224px] py-1" : "min-h-9 h-9 max-h-9 py-2"
215
- ),
216
- disabled: disabled || isSubmitting || isRunning,
217
- name: "message",
218
- onChange: (event) => setText(event.target.value),
219
- onKeyDown: handleKeyDown,
220
- placeholder,
221
- rows: isExpanded ? 2 : 1,
222
- value: text
223
- }
224
- ) }),
225
- isExpanded ? /* @__PURE__ */ jsx("div", { style: { gridArea: "toolbar" }, className: "a24-composer__toolbar flex min-w-0 items-center gap-1.5", children: inputToolbarContent }) : null,
226
- !isExpanded ? /* @__PURE__ */ jsx("div", { style: { gridArea: "composer" }, className: "flex min-w-0 items-center gap-1.5" }) : null,
227
- !isExpanded ? /* @__PURE__ */ jsx("div", { style: { gridArea: "mic" }, className: "flex shrink-0 items-center justify-end" }) : null,
228
- /* @__PURE__ */ jsx("div", { style: { gridArea: "submit" }, className: "a24-composer__submit-slot flex shrink-0 items-center justify-end", children: isRunning ? /* @__PURE__ */ jsx(
229
- "button",
230
- {
231
- "aria-label": "Stop generating",
232
- className: cn("a24-composer__submit", submitButtonClass),
233
- disabled: disabled || !onStop,
234
- onClick: onStop,
235
- title: "Stop",
236
- type: "button",
237
- children: /* @__PURE__ */ jsx(Square, { className: "size-3.5 fill-current" })
238
- }
239
- ) : /* @__PURE__ */ jsx(
240
- "button",
241
- {
242
- "aria-label": files.length > 0 ? `Send ${formatFileCount(files)}` : "Send message",
243
- className: cn("a24-composer__submit", submitButtonClass, text.trim().length > 0 && "hover:scale-105"),
244
- disabled: !canSubmit,
245
- onClick: () => void submit(),
246
- title: "Send",
247
- type: "button",
248
- children: /* @__PURE__ */ jsx(ArrowUp, { className: "size-4", strokeWidth: 2.5 })
249
- }
250
- ) })
251
- ]
252
- }
253
- )
254
- }
255
- ),
256
- !isExpanded && inputToolbarContent ? /* @__PURE__ */ jsx("div", { className: "a24-composer__toolbar a24-composer__toolbar--external mt-2 flex items-center gap-2 px-1", children: inputToolbarContent }) : null
257
- ] });
258
- }
259
-
260
- // src/ui/attachment.tsx
261
- import { cva } from "class-variance-authority";
262
- import { Slot } from "@radix-ui/react-slot";
263
- import { jsx as jsx2 } from "react/jsx-runtime";
264
- var attachmentVariants = cva(
265
- "group/attachment relative flex w-fit max-w-full min-w-0 shrink-0 flex-wrap rounded-xl border bg-card text-card-foreground transition-colors focus-within:border-neutral-300 focus-within:ring-0 has-[>a,>button]:hover:bg-muted/50 data-[state=error]:border-destructive/30 data-[state=idle]:border-dashed",
266
- {
267
- variants: {
268
- size: {
269
- default: "gap-2 text-sm has-data-[slot=attachment-content]:px-2.5 has-data-[slot=attachment-content]:py-2 has-data-[slot=attachment-media]:p-2",
270
- sm: "gap-2.5 text-xs has-data-[slot=attachment-content]:px-2 has-data-[slot=attachment-content]:py-1.5 has-data-[slot=attachment-media]:p-1.5",
271
- xs: "gap-1.5 rounded-lg text-xs has-data-[slot=attachment-content]:px-1.5 has-data-[slot=attachment-content]:py-1 has-data-[slot=attachment-media]:p-1"
272
- },
273
- orientation: {
274
- horizontal: "min-w-40 items-center",
275
- vertical: "w-24 flex-col has-data-[slot=attachment-content]:w-30"
276
- }
277
- }
278
- }
279
- );
280
- function Attachment({
281
- className,
282
- state = "done",
283
- size = "default",
284
- orientation = "horizontal",
285
- ...props
286
- }) {
287
- return /* @__PURE__ */ jsx2(
288
- "div",
289
- {
290
- "data-slot": "attachment",
291
- "data-state": state,
292
- "data-size": size,
293
- "data-orientation": orientation,
294
- className: cn(attachmentVariants({ size, orientation }), className),
295
- ...props
296
- }
297
- );
298
- }
299
- var attachmentMediaVariants = cva(
300
- "relative flex aspect-square w-10 shrink-0 items-center justify-center overflow-hidden rounded-lg bg-muted text-foreground group-data-[orientation=vertical]/attachment:w-full group-data-[size=sm]/attachment:w-8 group-data-[size=xs]/attachment:w-7 group-data-[size=xs]/attachment:rounded-md group-data-[state=error]/attachment:bg-destructive/10 group-data-[state=error]/attachment:text-destructive group-data-[orientation=vertical]/attachment:*:data-[slot=spinner]:size-6! [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 group-data-[orientation=vertical]/attachment:[&_svg:not([class*='size-'])]:size-6 group-data-[size=xs]/attachment:[&_svg:not([class*='size-'])]:size-3.5",
301
- {
302
- variants: {
303
- variant: {
304
- icon: "",
305
- image: "opacity-60 group-data-[state=done]/attachment:opacity-100 group-data-[state=idle]/attachment:opacity-100 *:[img]:aspect-square *:[img]:w-full *:[img]:object-cover"
306
- }
307
- },
308
- defaultVariants: {
309
- variant: "icon"
310
- }
311
- }
312
- );
313
- function AttachmentMedia({
314
- className,
315
- variant = "icon",
316
- ...props
317
- }) {
318
- return /* @__PURE__ */ jsx2(
319
- "div",
320
- {
321
- "data-slot": "attachment-media",
322
- "data-variant": variant,
323
- className: cn(attachmentMediaVariants({ variant }), className),
324
- ...props
325
- }
326
- );
327
- }
328
- function AttachmentContent({
329
- className,
330
- ...props
331
- }) {
332
- return /* @__PURE__ */ jsx2(
333
- "div",
334
- {
335
- "data-slot": "attachment-content",
336
- className: cn(
337
- "max-w-full min-w-0 flex-1 leading-tight group-data-[orientation=vertical]/attachment:px-1",
338
- className
339
- ),
340
- ...props
341
- }
342
- );
343
- }
344
- function AttachmentTitle({
345
- className,
346
- ...props
347
- }) {
348
- return /* @__PURE__ */ jsx2(
349
- "span",
350
- {
351
- "data-slot": "attachment-title",
352
- className: cn(
353
- "block max-w-full min-w-0 truncate font-medium group-data-[state=processing]/attachment:shimmer group-data-[state=uploading]/attachment:shimmer",
354
- className
355
- ),
356
- ...props
357
- }
358
- );
359
- }
360
- function AttachmentDescription({
361
- className,
362
- ...props
363
- }) {
364
- return /* @__PURE__ */ jsx2(
365
- "span",
366
- {
367
- "data-slot": "attachment-description",
368
- className: cn(
369
- "mt-0.5 block min-w-0 truncate text-xs text-muted-foreground group-data-[state=error]/attachment:text-destructive/80",
370
- "max-w-full",
371
- className
372
- ),
373
- ...props
374
- }
375
- );
376
- }
377
- function AttachmentActions({
378
- className,
379
- ...props
380
- }) {
381
- return /* @__PURE__ */ jsx2(
382
- "div",
383
- {
384
- "data-slot": "attachment-actions",
385
- className: cn(
386
- "relative z-20 flex shrink-0 items-center group-data-[orientation=vertical]/attachment:absolute group-data-[orientation=vertical]/attachment:top-3 group-data-[orientation=vertical]/attachment:right-3 group-data-[orientation=vertical]/attachment:gap-1",
387
- className
388
- ),
389
- ...props
390
- }
391
- );
392
- }
393
- function AttachmentAction({
394
- className,
395
- type = "button",
396
- ...props
397
- }) {
398
- return /* @__PURE__ */ jsx2(
399
- "button",
400
- {
401
- "data-slot": "attachment-action",
402
- type,
403
- className: cn(
404
- "inline-flex size-6 shrink-0 items-center justify-center rounded-md text-muted-foreground transition-colors hover:bg-muted hover:text-foreground focus-visible:border-neutral-300 focus-visible:outline-none focus-visible:ring-0 disabled:pointer-events-none disabled:opacity-50",
405
- className
406
- ),
407
- ...props
408
- }
409
- );
410
- }
411
- function AttachmentTrigger({
412
- className,
413
- asChild = false,
414
- type,
415
- ...props
416
- }) {
417
- const Comp = asChild ? Slot : "button";
418
- return /* @__PURE__ */ jsx2(
419
- Comp,
420
- {
421
- "data-slot": "attachment-trigger",
422
- type: asChild ? void 0 : type ?? "button",
423
- className: cn("absolute inset-0 z-10 outline-none", className),
424
- ...props
425
- }
426
- );
427
- }
428
- function AttachmentGroup({ className, ...props }) {
429
- return /* @__PURE__ */ jsx2(
430
- "div",
431
- {
432
- "data-slot": "attachment-group",
433
- className: cn(
434
- "flex min-w-0 scroll-fade-x snap-x snap-mandatory scroll-px-1 scrollbar-none gap-3 overflow-x-auto overscroll-x-contain py-1 *:data-[slot=attachment]:flex-none *:data-[slot=attachment]:snap-start",
435
- className
436
- ),
437
- ...props
438
- }
439
- );
440
- }
441
-
442
- // src/ui/message.tsx
443
- import { jsx as jsx3 } from "react/jsx-runtime";
444
- function MessageGroup({ className, ...props }) {
445
- return /* @__PURE__ */ jsx3(
446
- "div",
447
- {
448
- "data-slot": "message-group",
449
- className: cn("flex min-w-0 flex-col gap-2", className),
450
- ...props
451
- }
452
- );
453
- }
454
- function Message({
455
- className,
456
- align = "start",
457
- ...props
458
- }) {
459
- return /* @__PURE__ */ jsx3(
460
- "div",
461
- {
462
- "data-slot": "message",
463
- "data-align": align,
464
- className: cn(
465
- "group/message relative flex w-full min-w-0 gap-2 text-sm data-[align=end]:flex-row-reverse",
466
- className
467
- ),
468
- ...props
469
- }
470
- );
471
- }
472
- function MessageAvatar({ className, ...props }) {
473
- return /* @__PURE__ */ jsx3(
474
- "div",
475
- {
476
- "data-slot": "message-avatar",
477
- className: cn(
478
- "flex w-fit min-w-8 shrink-0 items-center justify-center self-end overflow-hidden rounded-full bg-muted group-has-data-[slot=message-footer]/message:-translate-y-8",
479
- className
480
- ),
481
- ...props
482
- }
483
- );
484
- }
485
- function MessageContent({ className, ...props }) {
486
- return /* @__PURE__ */ jsx3(
487
- "div",
488
- {
489
- "data-slot": "message-content",
490
- className: cn(
491
- "flex w-full min-w-0 flex-col gap-2.5 wrap-break-word group-data-[align=end]/message:*:data-slot:self-end",
492
- className
493
- ),
494
- ...props
495
- }
496
- );
497
- }
498
- function MessageHeader({ className, ...props }) {
499
- return /* @__PURE__ */ jsx3(
500
- "div",
501
- {
502
- "data-slot": "message-header",
503
- className: cn(
504
- "flex max-w-full min-w-0 items-center px-3 text-xs font-medium text-muted-foreground group-has-data-[variant=ghost]/message:px-0",
505
- className
506
- ),
507
- ...props
508
- }
509
- );
510
- }
511
- function MessageFooter({ className, ...props }) {
512
- return /* @__PURE__ */ jsx3(
513
- "div",
514
- {
515
- "data-slot": "message-footer",
516
- className: cn(
517
- "flex max-w-full min-w-0 items-center px-3 text-xs font-medium text-muted-foreground group-has-data-[variant=ghost]/message:px-0 group-data-[align=end]/message:justify-end",
518
- className
519
- ),
520
- ...props
521
- }
522
- );
523
- }
524
-
525
- // src/ui/adapters.ts
526
- import * as React2 from "react";
527
- function ChatMessage(input) {
528
- const { className, from, align, ...props } = input;
529
- const isUser = from === "user";
530
- const isRtl = props.dir === "rtl";
531
- const resolvedAlign = align || "start";
532
- const messageProps = Object.assign({}, props);
533
- messageProps.align = resolvedAlign;
534
- messageProps.className = cn(
535
- "group flex w-full flex-col gap-2",
536
- isUser ? cn("is-user max-w-[80%] justify-end", isRtl ? "mr-auto" : "ml-auto") : "is-assistant max-w-full",
537
- className
538
- );
539
- return React2.createElement(Message, messageProps);
540
- }
541
- function ChatMessageContent({
542
- className,
543
- ...props
544
- }) {
545
- return React2.createElement("div", {
546
- className: cn(
547
- "is-user:dark flex w-full max-w-full min-w-0 flex-col gap-2 overflow-hidden text-sm group-[.is-user]:w-fit group-[.is-user]:overflow-visible",
548
- "group-[.is-user]:rounded-lg group-[.is-user]:bg-secondary group-[.is-user]:px-4 group-[.is-user]:py-3 group-[.is-user]:text-foreground",
549
- "group-[.is-assistant]:w-full group-[.is-assistant]:max-w-full group-[.is-assistant]:text-foreground",
550
- props.dir === "rtl" ? "group-[.is-user]:mr-auto" : "group-[.is-user]:ml-auto",
551
- className
552
- ),
553
- "data-slot": "bubble-content",
554
- ...props
555
- });
556
- }
557
- function ChatMessageActions({
558
- className,
559
- ...props
560
- }) {
561
- return React2.createElement("div", {
562
- className: cn("flex items-center gap-1", className),
563
- "data-slot": "message-actions",
564
- ...props
565
- });
566
- }
567
- function ChatMessageAction({
568
- children,
569
- className,
570
- label,
571
- tooltip,
572
- type = "button",
573
- ...props
574
- }) {
575
- const tooltipLabel = typeof tooltip === "string" ? tooltip : void 0;
576
- return React2.createElement(
577
- "button",
578
- {
579
- "aria-label": label || tooltipLabel,
580
- className: cn(
581
- "inline-flex size-8 items-center justify-center rounded-md text-muted-foreground transition-colors hover:bg-muted hover:text-foreground focus-visible:border-neutral-300 focus-visible:outline-none focus-visible:ring-0 disabled:pointer-events-none disabled:opacity-50",
582
- className
583
- ),
584
- title: tooltipLabel,
585
- type,
586
- ...props
587
- },
588
- children,
589
- React2.createElement(
590
- "span",
591
- { className: "sr-only" },
592
- label || tooltipLabel
593
- )
594
- );
595
- }
596
- function ChatAttachment({
597
- data,
598
- onRemove,
599
- orientation,
600
- ...props
601
- }) {
602
- const mediaType = data.mediaType || data.contentType || String(data.type || "application/octet-stream");
603
- const filename = data.filename || data.name || "Attachment";
604
- const isImage = mediaType.startsWith("image/") && Boolean(data.url);
605
- return React2.createElement(
606
- Attachment,
607
- {
608
- orientation: orientation || (isImage ? "vertical" : "horizontal"),
609
- ...props
610
- },
611
- React2.createElement(
612
- AttachmentMedia,
613
- { variant: isImage ? "image" : "icon" },
614
- isImage ? React2.createElement("img", {
615
- alt: filename,
616
- className: "size-full object-cover",
617
- src: data.url
618
- }) : React2.createElement(
619
- "span",
620
- { "aria-hidden": "true", className: "text-sm" },
621
- filename.slice(0, 1).toUpperCase()
622
- )
623
- ),
624
- React2.createElement(
625
- AttachmentContent,
626
- null,
627
- React2.createElement(AttachmentTitle, null, filename),
628
- React2.createElement(AttachmentDescription, null, mediaType)
629
- ),
630
- onRemove ? React2.createElement(
631
- "button",
632
- {
633
- "aria-label": "Remove attachment",
634
- className: "relative z-20 mr-1 inline-flex size-6 shrink-0 items-center justify-center rounded-md text-muted-foreground hover:bg-muted hover:text-foreground",
635
- onClick: (event) => {
636
- event.stopPropagation();
637
- onRemove();
638
- },
639
- type: "button"
640
- },
641
- React2.createElement("span", { "aria-hidden": "true" }, "x")
642
- ) : null
643
- );
644
- }
645
- function ChatAttachments({
646
- children,
647
- className,
648
- ...props
649
- }) {
650
- if (!children) return null;
651
- return React2.createElement(
652
- AttachmentGroup,
653
- {
654
- className: cn(
655
- props.dir === "rtl" ? "mr-auto" : "ml-auto",
656
- "w-fit flex-wrap overflow-visible",
657
- className
658
- ),
659
- ...props
660
- },
661
- children
662
- );
663
- }
664
-
665
- // src/ui/agent-chat-actions.tsx
666
- import { Check, Copy, RefreshCcw, ThumbsDown, ThumbsUp } from "lucide-react";
667
- import { jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
668
- function AgentChatDefaultActions({
669
- handlers,
670
- message
671
- }) {
672
- if (!handlers || message.role !== "assistant" || message.isFinal === false) return null;
673
- return /* @__PURE__ */ jsxs2(ChatMessageActions, { children: [
674
- handlers.onRetry ? /* @__PURE__ */ jsx4(
675
- ChatMessageAction,
676
- {
677
- label: "Retry",
678
- onClick: () => void handlers.onRetry?.(message),
679
- tooltip: "Regenerate response",
680
- children: /* @__PURE__ */ jsx4(RefreshCcw, { className: "size-4" })
681
- }
682
- ) : null,
683
- handlers.onLike ? /* @__PURE__ */ jsx4(
684
- ChatMessageAction,
685
- {
686
- label: "Like",
687
- onClick: () => void handlers.onLike?.(message),
688
- tooltip: "Like this response",
689
- children: /* @__PURE__ */ jsx4(ThumbsUp, { className: "size-4", fill: handlers.liked ? "currentColor" : "none" })
690
- }
691
- ) : null,
692
- handlers.onDislike ? /* @__PURE__ */ jsx4(
693
- ChatMessageAction,
694
- {
695
- label: "Dislike",
696
- onClick: () => void handlers.onDislike?.(message),
697
- tooltip: "Dislike this response",
698
- children: /* @__PURE__ */ jsx4(ThumbsDown, { className: "size-4", fill: handlers.disliked ? "currentColor" : "none" })
699
- }
700
- ) : null,
701
- handlers.onCopy ? /* @__PURE__ */ jsx4(
702
- ChatMessageAction,
703
- {
704
- label: "Copy",
705
- onClick: () => handlers.onCopy?.(message.content || "", message.id),
706
- tooltip: handlers.copied ? "Copied!" : "Copy to clipboard",
707
- children: handlers.copied ? /* @__PURE__ */ jsx4(Check, { className: "size-4 text-green-600" }) : /* @__PURE__ */ jsx4(Copy, { className: "size-4" })
708
- }
709
- ) : null
710
- ] });
711
- }
712
-
713
- // src/ui/message-response.tsx
714
- import * as React3 from "react";
715
- import { defaultRehypePlugins, Streamdown } from "streamdown";
716
-
717
- // src/active-content.ts
718
- var CHAT_MARKDOWN_HARDEN_OPTIONS = Object.freeze({
719
- allowDataImages: false,
720
- allowedImagePrefixes: [],
721
- allowedLinkPrefixes: ["*"],
722
- allowedProtocols: [],
723
- defaultOrigin: void 0,
724
- imageBlockPolicy: "text-only",
725
- linkBlockPolicy: "text-only"
726
- });
727
- function safeChatMarkdownHref(value) {
728
- if (!value) return void 0;
729
- if (value.startsWith("#")) return value;
730
- try {
731
- const url = new URL(value);
732
- return url.protocol === "https:" || url.protocol === "http:" ? url.href : void 0;
733
- } catch {
734
- return void 0;
735
- }
736
- }
737
-
738
- // src/ui/message-response.tsx
739
- import { Fragment, jsx as jsx5 } from "react/jsx-runtime";
740
- function SafeMarkdownLink({ children, href, ...props }) {
741
- const domProps = { ...props };
742
- delete domProps.node;
743
- const safeHref = safeChatMarkdownHref(href);
744
- if (!safeHref) return /* @__PURE__ */ jsx5("span", { children });
745
- if (safeHref.startsWith("#")) return /* @__PURE__ */ jsx5("a", { ...domProps, href: safeHref, children });
746
- return /* @__PURE__ */ jsx5("a", { ...domProps, href: safeHref, rel: "noopener noreferrer", target: "_blank", children });
747
- }
748
- function BlockedMarkdownImage({ alt }) {
749
- return alt ? /* @__PURE__ */ jsx5("span", { "aria-label": `Image blocked: ${alt}`, role: "img", children: alt }) : null;
750
- }
751
- var BLOCK_MARKDOWN_TAGS = /* @__PURE__ */ new Set([
752
- "blockquote",
753
- "div",
754
- "h1",
755
- "h2",
756
- "h3",
757
- "h4",
758
- "h5",
759
- "h6",
760
- "hr",
761
- "ol",
762
- "pre",
763
- "table",
764
- "ul"
765
- ]);
766
- function isMultilineCodeNode(node) {
767
- return node?.tagName === "code" && node.position?.start?.line !== void 0 && node.position?.end?.line !== void 0 && node.position.start.line !== node.position.end.line;
768
- }
769
- function hasBlockMarkdownChild(child) {
770
- if (!React3.isValidElement(child)) {
771
- return false;
772
- }
773
- if (typeof child.type === "string" && BLOCK_MARKDOWN_TAGS.has(child.type)) {
774
- return true;
775
- }
776
- const props = child.props;
777
- if (BLOCK_MARKDOWN_TAGS.has(props.node?.tagName || "") || isMultilineCodeNode(props.node)) {
778
- return true;
779
- }
780
- return React3.Children.toArray(props.children).some(hasBlockMarkdownChild);
781
- }
782
- function MessageMarkdownParagraph({
783
- children,
784
- ...props
785
- }) {
786
- const domProps = { ...props };
787
- delete domProps.node;
788
- const childArray = React3.Children.toArray(children).filter(
789
- (child) => child !== null && child !== ""
790
- );
791
- const isImageOnly = childArray.length === 1 && React3.isValidElement(childArray[0]) && childArray[0].props.node?.tagName === "img";
792
- if (isImageOnly) {
793
- return /* @__PURE__ */ jsx5(Fragment, { children });
794
- }
795
- if (childArray.some(hasBlockMarkdownChild)) {
796
- return /* @__PURE__ */ jsx5("div", { ...domProps, children });
797
- }
798
- return /* @__PURE__ */ jsx5("p", { ...domProps, children });
799
- }
800
- var MESSAGE_RESPONSE_COMPONENTS = {
801
- a: SafeMarkdownLink,
802
- img: BlockedMarkdownImage,
803
- p: MessageMarkdownParagraph
804
- };
805
- var streamdownDefaultRehypePlugins = defaultRehypePlugins || {};
806
- var streamdownHardenPlugin = Array.isArray(
807
- streamdownDefaultRehypePlugins.harden
808
- ) ? streamdownDefaultRehypePlugins.harden[0] : streamdownDefaultRehypePlugins.harden;
809
- var MESSAGE_RESPONSE_REHYPE_PLUGINS = [
810
- ...Object.entries(streamdownDefaultRehypePlugins).filter(([key]) => key !== "harden").map(([, plugin]) => plugin),
811
- ...streamdownHardenPlugin ? [
812
- [
813
- streamdownHardenPlugin,
814
- CHAT_MARKDOWN_HARDEN_OPTIONS
815
- ]
816
- ] : []
817
- ];
818
- var MessageResponse = React3.memo(
819
- ({
820
- children,
821
- className,
822
- components,
823
- rehypePlugins,
824
- streaming = false,
825
- streamingId,
826
- ...props
827
- }) => {
828
- const text = typeof children === "string" || typeof children === "number" ? String(children) : "";
829
- const streamingText = useStreamingText({
830
- charsPerSecond: 120,
831
- id: streamingId || text,
832
- isStreaming: streaming && text.length > 0,
833
- maxCatchupChars: 32,
834
- text
835
- });
836
- const streamdownComponents = React3.useMemo(
837
- () => ({ ...MESSAGE_RESPONSE_COMPONENTS, ...components }),
838
- [components]
839
- );
840
- return /* @__PURE__ */ jsx5(
841
- Streamdown,
842
- {
843
- className: cn("size-full [&>*:first-child]:mt-0 [&>*:last-child]:mb-0", className),
844
- components: streamdownComponents,
845
- rehypePlugins: rehypePlugins || MESSAGE_RESPONSE_REHYPE_PLUGINS,
846
- ...props,
847
- children: text ? streamingText.displayedText : children
848
- }
849
- );
850
- },
851
- (prevProps, nextProps) => prevProps.children === nextProps.children && prevProps.className === nextProps.className && prevProps.streaming === nextProps.streaming && prevProps.streamingId === nextProps.streamingId && prevProps.components === nextProps.components
852
- );
853
- MessageResponse.displayName = "MessageResponse";
854
-
855
- // src/ui/hitl-presentation.ts
856
- function optionalString(value) {
857
- return typeof value === "string" && value.trim() ? value.trim() : null;
858
- }
859
- function hitlActionLabel(part, action) {
860
- if (action === "approve") return ["tool_review", "app_data_permission"].includes(part.hitlKind) ? "Allow once" : "Approve";
861
- if (action === "reject") return ["tool_review", "app_data_permission"].includes(part.hitlKind) ? "Don\u2019t allow" : "Reject";
862
- if (action === "connect") return "Connect";
863
- return "Skip";
864
- }
865
- function hitlTitle(part) {
866
- const presentation = part.presentation || {};
867
- if (part.hitlKind === "tool_review") return `Run ${optionalString(presentation.tool_name) || "this tool"}?`;
868
- if (part.hitlKind === "mcp_auth") {
869
- const serverName = optionalString(presentation.server_name);
870
- return serverName ? `Connect ${serverName} to continue` : part.message;
871
- }
872
- if (part.hitlKind === "app_data_permission") return "Allow this data change?";
873
- return part.message;
874
- }
875
- function resolvedHitlLabel(part) {
876
- const outcome = String(part.resolution?.outcome || part.status).trim().toLowerCase();
877
- return {
878
- approved: part.hitlKind === "user_approval" ? "Approved" : "Allowed",
879
- rejected: part.hitlKind === "user_approval" ? "Rejected" : "Not allowed",
880
- connected: "connected",
881
- skipped: "Skipped",
882
- timeout: "Timed out",
883
- expired: "Timed out",
884
- cancelled: "Cancelled",
885
- invalidated: "No longer available",
886
- policy_denied: "Not allowed"
887
- }[outcome] || outcome.replace(/_/g, " ");
888
- }
889
-
890
- // src/ui/agent-response-timeline.tsx
891
- import * as React4 from "react";
892
- import { ChevronDown, ChevronRight } from "lucide-react";
893
- import { jsx as jsx6, jsxs as jsxs3 } from "react/jsx-runtime";
894
- function optionalString2(value) {
895
- return typeof value === "string" && value.trim() ? value.trim() : null;
896
- }
897
- function presentationValue(part, key) {
898
- const presentation = part.presentation || {};
899
- const snakeKey = key.replace(/[A-Z]/g, (letter) => `_${letter.toLowerCase()}`);
900
- return optionalString2(presentation[key]) || optionalString2(presentation[snakeKey]);
901
- }
902
- function groupKey(part) {
903
- return presentationValue(part, "groupKey");
904
- }
905
- function groupLabel(part) {
906
- return presentationValue(part, "groupLabel") || toolLabel(part);
907
- }
908
- function toolLabel(part) {
909
- return presentationValue(part, "activity") || presentationValue(part, "title") || part.toolName || "Tool activity";
910
- }
911
- function toolDetail(part) {
912
- return presentationValue(part, "detail") || part.errorText || null;
913
- }
914
- function isActiveTool(part, activeToolId) {
915
- return part.id === activeToolId || part.toolCallId === activeToolId || part.state === "input-available" || part.state === "input-streaming";
916
- }
917
- function Shimmer({ children, className }) {
918
- return /* @__PURE__ */ jsx6("span", { className: cn("inline-block animate-pulse", className), children });
919
- }
920
- function CompactTask({
921
- children,
922
- defaultOpen = false,
923
- detail,
924
- error,
925
- label,
926
- loading
927
- }) {
928
- const [open, setOpen] = React4.useState(defaultOpen);
929
- const expandable = Boolean(children || detail);
930
- const content = loading ? /* @__PURE__ */ jsx6(Shimmer, { children: label }) : /* @__PURE__ */ jsx6("span", { children: label });
931
- if (!expandable) {
932
- return /* @__PURE__ */ jsx6("div", { className: cn("flex min-w-0 items-center gap-1.5 text-sm", error ? "text-destructive" : "text-muted-foreground"), children: content });
933
- }
934
- return /* @__PURE__ */ jsxs3("div", { className: "w-full", children: [
935
- /* @__PURE__ */ jsxs3(
936
- "button",
937
- {
938
- "aria-expanded": open,
939
- className: cn(
940
- "group flex min-w-0 items-center gap-1.5 rounded-md px-0 py-0.5 text-left text-sm transition-colors hover:text-foreground",
941
- error ? "text-destructive" : "text-muted-foreground"
942
- ),
943
- onClick: () => setOpen((value) => !value),
944
- type: "button",
945
- children: [
946
- content,
947
- open ? /* @__PURE__ */ jsx6(ChevronDown, { className: "size-3" }) : /* @__PURE__ */ jsx6(ChevronRight, { className: "size-3 opacity-70" })
948
- ]
949
- }
950
- ),
951
- open ? /* @__PURE__ */ jsxs3("div", { className: "mt-1 pl-4 text-sm text-muted-foreground", children: [
952
- detail ? /* @__PURE__ */ jsx6("div", { children: detail }) : null,
953
- children
954
- ] }) : null
955
- ] });
956
- }
957
- function ToolPartRow({
958
- activeToolId,
959
- part
960
- }) {
961
- return /* @__PURE__ */ jsx6("div", { "data-agents24-tool-part": part.type, "data-state": part.state, children: /* @__PURE__ */ jsx6(
962
- CompactTask,
963
- {
964
- defaultOpen: part.state === "output-error",
965
- detail: toolDetail(part),
966
- error: part.state === "output-error",
967
- label: toolLabel(part),
968
- loading: isActiveTool(part, activeToolId)
969
- }
970
- ) });
971
- }
972
- function ToolGroup({
973
- activeToolId,
974
- parts
975
- }) {
976
- const loading = parts.some((part) => isActiveTool(part, activeToolId));
977
- return /* @__PURE__ */ jsx6(CompactTask, { label: groupLabel(parts[0]), loading, children: /* @__PURE__ */ jsx6("div", { className: "space-y-1", children: parts.map((part) => /* @__PURE__ */ jsxs3(
978
- "div",
979
- {
980
- className: cn("text-sm", part.state === "output-error" ? "text-destructive" : "text-muted-foreground"),
981
- children: [
982
- isActiveTool(part, activeToolId) ? /* @__PURE__ */ jsx6(Shimmer, { children: toolLabel(part) }) : toolLabel(part),
983
- toolDetail(part) ? /* @__PURE__ */ jsx6("span", { className: "ml-1 text-xs text-muted-foreground/75", children: toolDetail(part) }) : null
984
- ]
985
- },
986
- part.id
987
- )) }) });
988
- }
989
- function HitlPartRow({
990
- getHitlActionState,
991
- isLoading,
992
- onHitlAction,
993
- part
994
- }) {
995
- const state = getHitlActionState?.(part);
996
- const [comment, setComment] = React4.useState("");
997
- const presentation = part.presentation || {};
998
- const requireComment = presentation.require_comment === true;
999
- const isTerminal = part.status !== "pending";
1000
- const resolvedRef = React4.useRef(null);
1001
- const wasTerminal = React4.useRef(isTerminal);
1002
- React4.useEffect(() => {
1003
- if (isTerminal && !wasTerminal.current) resolvedRef.current?.focus();
1004
- wasTerminal.current = isTerminal;
1005
- }, [isTerminal]);
1006
- if (isTerminal) {
1007
- return /* @__PURE__ */ jsx6("div", { className: "a24-hitl a24-hitl--resolved px-1 py-1 text-sm text-muted-foreground", ref: resolvedRef, role: "status", tabIndex: -1, children: resolvedHitlLabel(part) });
1008
- }
1009
- return /* @__PURE__ */ jsxs3("div", { className: "a24-hitl space-y-2.5 px-1 py-2", children: [
1010
- /* @__PURE__ */ jsx6("div", { className: "a24-hitl__title text-sm font-medium text-foreground text-pretty", children: hitlTitle(part) }),
1011
- requireComment ? /* @__PURE__ */ jsx6(
1012
- "textarea",
1013
- {
1014
- "aria-label": "Approval comment",
1015
- autoComplete: "off",
1016
- className: "a24-hitl__comment min-h-20 w-full resize-y rounded-md border border-border/60 bg-background px-3 py-2 text-sm outline-none focus-visible:border-neutral-300 focus-visible:ring-0 dark:focus-visible:border-neutral-600",
1017
- name: "hitl-comment",
1018
- onChange: (event) => setComment(event.target.value),
1019
- placeholder: "Comment required\u2026",
1020
- value: comment
1021
- }
1022
- ) : null,
1023
- state?.error ? /* @__PURE__ */ jsx6("div", { className: "a24-hitl__error text-sm text-destructive", children: state.error }) : null,
1024
- /* @__PURE__ */ jsx6("div", { className: "a24-hitl__actions flex flex-wrap items-center gap-1.5", children: part.allowedActions.map((action) => /* @__PURE__ */ jsx6(
1025
- "button",
1026
- {
1027
- "aria-label": hitlActionLabel(part, action),
1028
- className: cn(
1029
- "a24-hitl__action min-h-10 rounded-md px-3 text-sm font-medium transition-[background-color,color,opacity,transform] duration-150 ease-out active:scale-[0.96] disabled:pointer-events-none disabled:opacity-60",
1030
- action === "approve" || action === "connect" ? "bg-neutral-900 text-white hover:bg-neutral-800 dark:bg-neutral-100 dark:text-neutral-900 dark:hover:bg-white" : "bg-transparent text-muted-foreground hover:bg-background/80 hover:text-foreground"
1031
- ),
1032
- "data-primary": action === "approve" || action === "connect" ? "" : void 0,
1033
- disabled: !onHitlAction || isLoading || state?.status === "connecting" || state?.status === "resuming" || requireComment && !comment.trim(),
1034
- onClick: () => onHitlAction?.(part, action, comment.trim() || void 0),
1035
- type: "button",
1036
- children: hitlActionLabel(part, action)
1037
- },
1038
- action
1039
- )) })
1040
- ] });
1041
- }
1042
- function DefaultPartRow({
1043
- activeToolId,
1044
- getHitlActionState,
1045
- isLoading,
1046
- message,
1047
- onHitlAction,
1048
- part
1049
- }) {
1050
- if (part.kind === "text") return /* @__PURE__ */ jsx6(MessageResponse, { children: part.text });
1051
- if (part.kind === "tool") return /* @__PURE__ */ jsx6(ToolPartRow, { activeToolId, part });
1052
- if (part.kind === "reasoning") {
1053
- const label = part.text || part.label || "Thinking";
1054
- return part.status === "running" || part.status === "streaming" ? /* @__PURE__ */ jsx6(Shimmer, { className: "text-sm text-muted-foreground", children: label }) : /* @__PURE__ */ jsx6("div", { className: "text-sm text-muted-foreground", children: label });
1055
- }
1056
- if (part.kind === "ui-blocks") {
1057
- if (part.state === "output-error") {
1058
- return /* @__PURE__ */ jsx6("div", { className: "text-sm text-destructive", children: part.errorText || "Failed to render UI content." });
1059
- }
1060
- return part.bundle ? /* @__PURE__ */ jsx6(SafeUiBlocks, { bundle: part.bundle }) : /* @__PURE__ */ jsx6(CompactTask, { label: part.state === "input-streaming" ? "Rendering UI block" : "Rendered UI block", loading: part.state === "input-streaming" });
1061
- }
1062
- if (part.kind === "hitl") {
1063
- return /* @__PURE__ */ jsx6(
1064
- HitlPartRow,
1065
- {
1066
- getHitlActionState,
1067
- isLoading,
1068
- onHitlAction,
1069
- part
1070
- }
1071
- );
1072
- }
1073
- if (part.kind === "error") return /* @__PURE__ */ jsx6("div", { className: "text-sm text-destructive", role: "alert", children: part.errorText });
1074
- if (part.kind === "source") {
1075
- const href = safeExternalUrl(part.url);
1076
- return href ? /* @__PURE__ */ jsx6("a", { className: "a24-source-link", href, rel: "noopener noreferrer", target: "_blank", children: part.title }) : /* @__PURE__ */ jsx6("span", { className: "a24-source-link", children: part.title });
1077
- }
1078
- if (part.kind === "citation") {
1079
- const href = safeExternalUrl(part.url);
1080
- const label = part.label || "Citation";
1081
- return href ? /* @__PURE__ */ jsx6("a", { className: "a24-citation", href, rel: "noopener noreferrer", target: "_blank", children: label }) : /* @__PURE__ */ jsx6("span", { className: "a24-citation", children: label });
1082
- }
1083
- if (part.kind === "attachment") return /* @__PURE__ */ jsx6("div", { className: "a24-output-attachment", children: part.attachment.filename || "Attachment" });
1084
- if (part.kind === "data") return /* @__PURE__ */ jsx6("div", { "data-agents24-data-part": part.name, className: "hidden", children: message.id });
1085
- return null;
1086
- }
1087
- function safeExternalUrl(value) {
1088
- if (!value) return null;
1089
- try {
1090
- const url = new URL(value);
1091
- return url.protocol === "https:" || url.protocol === "http:" ? url.href : null;
1092
- } catch {
1093
- return null;
1094
- }
1095
- }
1096
- function SafeUiBlocks({ bundle }) {
1097
- const rows = Array.isArray(bundle.rows) ? bundle.rows : [];
1098
- return /* @__PURE__ */ jsxs3("section", { "aria-label": typeof bundle.title === "string" ? bundle.title : "Agent result", className: "a24-ui-blocks", children: [
1099
- typeof bundle.title === "string" ? /* @__PURE__ */ jsx6("h3", { children: bundle.title }) : null,
1100
- rows.map((rawRow, rowIndex) => {
1101
- const row = rawRow && typeof rawRow === "object" && !Array.isArray(rawRow) ? rawRow : {};
1102
- const blocks = Array.isArray(row.blocks) ? row.blocks : [];
1103
- return /* @__PURE__ */ jsx6("div", { className: "a24-ui-blocks__row", children: blocks.map((rawBlock, blockIndex) => {
1104
- const block = rawBlock && typeof rawBlock === "object" && !Array.isArray(rawBlock) ? rawBlock : {};
1105
- const title = typeof block.title === "string" ? block.title : "Result";
1106
- const kind = String(block.kind || "note");
1107
- if (kind === "table" && Array.isArray(block.columns) && Array.isArray(block.rows)) {
1108
- return /* @__PURE__ */ jsxs3("div", { className: "a24-ui-block", children: [
1109
- /* @__PURE__ */ jsx6("h4", { children: title }),
1110
- /* @__PURE__ */ jsx6("div", { className: "a24-ui-table-scroll", children: /* @__PURE__ */ jsxs3("table", { children: [
1111
- /* @__PURE__ */ jsx6("thead", { children: /* @__PURE__ */ jsx6("tr", { children: block.columns.map((column, index) => /* @__PURE__ */ jsx6("th", { children: String(column) }, index)) }) }),
1112
- /* @__PURE__ */ jsx6("tbody", { children: block.rows.map((rawCells, index) => /* @__PURE__ */ jsx6("tr", { children: (Array.isArray(rawCells) ? rawCells : []).map((cell, cellIndex) => /* @__PURE__ */ jsx6("td", { children: String(cell) }, cellIndex)) }, index)) })
1113
- ] }) })
1114
- ] }, String(block.id || blockIndex));
1115
- }
1116
- const value = block.value ?? block.text ?? "";
1117
- return /* @__PURE__ */ jsxs3("div", { className: "a24-ui-block", children: [
1118
- /* @__PURE__ */ jsx6("h4", { children: title }),
1119
- /* @__PURE__ */ jsx6("p", { children: String(value) })
1120
- ] }, String(block.id || blockIndex));
1121
- }) }, `row-${rowIndex}`);
1122
- })
1123
- ] });
1124
- }
1125
- function AgentResponseTimeline({
1126
- getHitlActionState,
1127
- isLoading = false,
1128
- message,
1129
- onHitlAction,
1130
- renderPart
1131
- }) {
1132
- const parts = message.parts || [];
1133
- const activeToolId = [...parts].reverse().find((part) => part.kind === "tool" && (part.state === "input-available" || part.state === "input-streaming"))?.id;
1134
- const rendered = [];
1135
- let index = 0;
1136
- while (index < parts.length) {
1137
- const part = parts[index];
1138
- if (part.kind === "tool" && groupKey(part)) {
1139
- const key = groupKey(part);
1140
- const group = [];
1141
- while (index < parts.length) {
1142
- const candidate = parts[index];
1143
- if (candidate.kind !== "tool" || groupKey(candidate) !== key) break;
1144
- group.push(candidate);
1145
- index += 1;
1146
- }
1147
- rendered.push(group.length === 1 ? /* @__PURE__ */ jsx6(ToolPartRow, { activeToolId, part: group[0] }, group[0].id) : /* @__PURE__ */ jsx6(ToolGroup, { activeToolId, parts: group }, `tool-group-${key}-${group[0].id}`));
1148
- continue;
1149
- }
1150
- rendered.push(
1151
- /* @__PURE__ */ jsx6(React4.Fragment, { children: renderPart ? renderPart(part, message) : /* @__PURE__ */ jsx6(
1152
- DefaultPartRow,
1153
- {
1154
- activeToolId,
1155
- getHitlActionState,
1156
- isLoading,
1157
- message,
1158
- onHitlAction,
1159
- part
1160
- }
1161
- ) }, part.id)
1162
- );
1163
- index += 1;
1164
- }
1165
- if (rendered.length === 0 && message.content) {
1166
- rendered.push(/* @__PURE__ */ jsx6(MessageResponse, { children: message.content }, `${message.id}-fallback`));
1167
- }
1168
- if (rendered.length === 0 && isLoading) {
1169
- rendered.push(/* @__PURE__ */ jsx6("div", { className: "text-sm text-muted-foreground", children: "Thinking" }, `${message.id}-thinking`));
1170
- }
1171
- return /* @__PURE__ */ jsx6("div", { className: "space-y-3", children: rendered });
1172
- }
1173
-
1174
- // src/ui/agent-chat-message.tsx
1175
- import * as React5 from "react";
1176
- import { ChevronDown as ChevronDown2, ChevronUp } from "lucide-react";
1177
- import { jsx as jsx7, jsxs as jsxs4 } from "react/jsx-runtime";
1178
- var DEFAULT_USER_MESSAGE_COLLAPSE_THRESHOLD = 360;
1179
- var DEFAULT_USER_MESSAGE_COLLAPSED_LINES = 4;
1180
- function shouldCollapseUserMessage(content, threshold = DEFAULT_USER_MESSAGE_COLLAPSE_THRESHOLD) {
1181
- return typeof threshold === "number" && threshold > 0 && content.length > threshold;
1182
- }
1183
- function AgentChatUserMessageContent({
1184
- collapsedLines = DEFAULT_USER_MESSAGE_COLLAPSED_LINES,
1185
- collapseThreshold = DEFAULT_USER_MESSAGE_COLLAPSE_THRESHOLD,
1186
- content,
1187
- showLessLabel = "Show less",
1188
- showMoreLabel = "Show more"
1189
- }) {
1190
- const [isExpanded, setIsExpanded] = React5.useState(false);
1191
- const contentId = React5.useId();
1192
- const shouldCollapse = shouldCollapseUserMessage(content, collapseThreshold);
1193
- const Icon = isExpanded ? ChevronUp : ChevronDown2;
1194
- const clampedStyle = shouldCollapse && !isExpanded ? {
1195
- WebkitBoxOrient: "vertical",
1196
- WebkitLineClamp: Math.max(1, collapsedLines),
1197
- display: "-webkit-box",
1198
- overflow: "hidden"
1199
- } : void 0;
1200
- return /* @__PURE__ */ jsxs4("div", { className: "flex min-w-0 flex-col gap-3", children: [
1201
- /* @__PURE__ */ jsx7("div", { id: contentId, className: "min-w-0", style: clampedStyle, children: content }),
1202
- shouldCollapse ? /* @__PURE__ */ jsxs4(
1203
- "button",
1204
- {
1205
- "aria-controls": contentId,
1206
- "aria-expanded": isExpanded,
1207
- className: cn(
1208
- "inline-flex w-fit items-center gap-1 rounded-md text-left text-sm font-medium text-muted-foreground transition-colors",
1209
- "hover:text-foreground focus-visible:border-neutral-300 focus-visible:outline-none focus-visible:ring-0"
1210
- ),
1211
- onClick: () => setIsExpanded((current) => !current),
1212
- type: "button",
1213
- children: [
1214
- isExpanded ? showLessLabel : showMoreLabel,
1215
- /* @__PURE__ */ jsx7(Icon, { "aria-hidden": "true", className: "size-4", strokeWidth: 2 })
1216
- ]
1217
- }
1218
- ) : null
1219
- ] });
1220
- }
1221
- function AgentChatMessage({
1222
- actionHandlers,
1223
- actions,
1224
- actionsClassName,
1225
- className,
1226
- contentClassName,
1227
- dir,
1228
- getHitlActionState,
1229
- message,
1230
- onHitlAction,
1231
- renderAssistantContent,
1232
- renderPart,
1233
- renderUserContent,
1234
- showAssistantAttachments = false,
1235
- streamingMessageId,
1236
- userMessageCollapsedLines,
1237
- userMessageContent,
1238
- userMessageCollapseThreshold,
1239
- userMessageShowLessLabel,
1240
- userMessageShowMoreLabel
1241
- }) {
1242
- const isAssistant = message.role === "assistant";
1243
- const isStreaming = isAssistant && message.id === streamingMessageId;
1244
- const resolvedUserContent = userMessageContent ?? message.content;
1245
- const shouldRenderContent = isAssistant || Boolean(renderUserContent || resolvedUserContent);
1246
- const showAttachments = Boolean(message.attachments?.length) && (message.role === "user" || showAssistantAttachments);
1247
- const renderedActions = actions ?? (actionHandlers ? /* @__PURE__ */ jsx7(AgentChatDefaultActions, { handlers: actionHandlers, message }) : null);
1248
- return /* @__PURE__ */ jsxs4(ChatMessage, { className, dir, from: message.role, children: [
1249
- showAttachments ? /* @__PURE__ */ jsx7(ChatAttachments, { className: "mb-2", dir, children: message.attachments?.map((attachment, index) => /* @__PURE__ */ jsx7(
1250
- ChatAttachment,
1251
- {
1252
- data: attachment,
1253
- dir: "ltr"
1254
- },
1255
- String(attachment.url || attachment.filename || index)
1256
- )) }) : null,
1257
- shouldRenderContent ? /* @__PURE__ */ jsxs4(
1258
- ChatMessageContent,
1259
- {
1260
- className: cn(isAssistant ? "bg-transparent p-0" : void 0, contentClassName),
1261
- dir,
1262
- children: [
1263
- isAssistant ? renderAssistantContent ? renderAssistantContent({ isStreaming, message }) : /* @__PURE__ */ jsx7(
1264
- AgentResponseTimeline,
1265
- {
1266
- getHitlActionState,
1267
- isLoading: isStreaming,
1268
- message,
1269
- onHitlAction,
1270
- renderPart
1271
- }
1272
- ) : null,
1273
- !isAssistant ? renderUserContent ? renderUserContent({ message }) : resolvedUserContent ? /* @__PURE__ */ jsx7(
1274
- AgentChatUserMessageContent,
1275
- {
1276
- collapsedLines: userMessageCollapsedLines,
1277
- collapseThreshold: userMessageCollapseThreshold,
1278
- content: resolvedUserContent,
1279
- showLessLabel: userMessageShowLessLabel,
1280
- showMoreLabel: userMessageShowMoreLabel
1281
- }
1282
- ) : null : null
1283
- ]
1284
- }
1285
- ) : null,
1286
- renderedActions ? /* @__PURE__ */ jsx7("div", { className: cn(dir === "rtl" ? "mt-1 h-9 w-full translate-x-2" : "mt-1 h-9 w-full -translate-x-2", actionsClassName), children: renderedActions }) : null
1287
- ] });
1288
- }
1289
-
1290
- export {
1291
- cn,
1292
- AgentChatComposer,
1293
- attachmentVariants,
1294
- Attachment,
1295
- attachmentMediaVariants,
1296
- AttachmentMedia,
1297
- AttachmentContent,
1298
- AttachmentTitle,
1299
- AttachmentDescription,
1300
- AttachmentActions,
1301
- AttachmentAction,
1302
- AttachmentTrigger,
1303
- AttachmentGroup,
1304
- MessageGroup,
1305
- Message,
1306
- MessageAvatar,
1307
- MessageContent,
1308
- MessageHeader,
1309
- MessageFooter,
1310
- ChatMessage,
1311
- ChatMessageContent,
1312
- ChatMessageActions,
1313
- ChatMessageAction,
1314
- ChatAttachment,
1315
- ChatAttachments,
1316
- AgentChatDefaultActions,
1317
- MessageResponse,
1318
- hitlActionLabel,
1319
- hitlTitle,
1320
- resolvedHitlLabel,
1321
- DefaultPartRow,
1322
- AgentResponseTimeline,
1323
- shouldCollapseUserMessage,
1324
- AgentChatUserMessageContent,
1325
- AgentChatMessage
1326
- };
1327
- //# sourceMappingURL=chunk-NLEOWTZG.js.map