@agents24/chat-react 0.5.5 → 0.5.7
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.
- package/dist/index.cjs +11 -6
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +11 -6
- package/dist/index.js.map +1 -1
- package/dist/latest-thread-scroller.d.ts +2 -1
- package/dist/runtime/index.cjs +17 -11
- package/dist/runtime/index.cjs.map +1 -1
- package/dist/runtime/index.js +17 -11
- package/dist/runtime/index.js.map +1 -1
- package/dist/runtime/use-agent-chat-runtime.d.ts +7 -2
- package/dist/scaffold/components/chat/chat-composer.d.ts +2 -1
- package/dist/scaffold/components/chat/chat-message.d.ts +3 -1
- package/dist/scaffold/components/chat/turn-outline.d.ts +1 -1
- package/dist/scaffold/index.cjs +195 -154
- package/dist/scaffold/index.cjs.map +1 -1
- package/dist/scaffold/index.js +215 -168
- package/dist/scaffold/index.js.map +1 -1
- package/dist/styles.css +25 -6
- package/dist/ui/adapters.d.ts +14 -2
- package/dist/ui/agent-chat-composer.d.ts +2 -9
- package/dist/ui/agent-chat-message.d.ts +7 -1
- package/dist/ui/composer-attachments.d.ts +11 -0
- package/dist/ui/index.cjs +781 -389
- package/dist/ui/index.cjs.map +1 -1
- package/dist/ui/index.d.ts +1 -0
- package/dist/ui/index.js +766 -375
- package/dist/ui/index.js.map +1 -1
- package/package.json +4 -3
- package/scaffold/src/components/chat/chat-composer.tsx +43 -19
- package/scaffold/src/components/chat/chat-message.tsx +19 -10
- package/scaffold/src/components/chat/chat-shell.tsx +65 -34
- package/scaffold/src/components/chat/turn-outline.tsx +17 -59
- package/scaffold/src/components/ui/message-scroller.tsx +1 -1
- package/scaffold/src/components/ui/message.tsx +1 -1
- package/scaffold/src/components/ui/sidebar.tsx +1 -1
package/dist/ui/index.cjs
CHANGED
|
@@ -44,6 +44,7 @@ __export(ui_exports, {
|
|
|
44
44
|
AgentChatToolPart: () => AgentChatToolPart,
|
|
45
45
|
AgentChatToolRow: () => AgentChatToolRow,
|
|
46
46
|
AgentChatToolSession: () => AgentChatToolSession,
|
|
47
|
+
AgentChatUserMessageActionRow: () => AgentChatUserMessageActionRow,
|
|
47
48
|
AgentChatUserMessageContent: () => AgentChatUserMessageContent,
|
|
48
49
|
AgentResponseTimeline: () => AgentResponseTimeline,
|
|
49
50
|
AskUserInteraction: () => AskUserInteraction,
|
|
@@ -61,6 +62,7 @@ __export(ui_exports, {
|
|
|
61
62
|
BubbleGroup: () => BubbleGroup,
|
|
62
63
|
BubbleReactions: () => BubbleReactions,
|
|
63
64
|
ChatAttachment: () => ChatAttachment,
|
|
65
|
+
ChatAttachmentRows: () => ChatAttachmentRows,
|
|
64
66
|
ChatAttachments: () => ChatAttachments,
|
|
65
67
|
ChatMessage: () => ChatMessage,
|
|
66
68
|
ChatMessageAction: () => ChatMessageAction,
|
|
@@ -74,6 +76,7 @@ __export(ui_exports, {
|
|
|
74
76
|
MessageAction: () => ChatMessageAction,
|
|
75
77
|
MessageActions: () => ChatMessageActions,
|
|
76
78
|
MessageAttachment: () => ChatAttachment,
|
|
79
|
+
MessageAttachmentRows: () => ChatAttachmentRows,
|
|
77
80
|
MessageAttachments: () => ChatAttachments,
|
|
78
81
|
MessageAvatar: () => MessageAvatar,
|
|
79
82
|
MessageContent: () => ChatMessageContent,
|
|
@@ -89,6 +92,7 @@ __export(ui_exports, {
|
|
|
89
92
|
bubbleVariants: () => bubbleVariants,
|
|
90
93
|
cn: () => cn,
|
|
91
94
|
contextStatusMetrics: () => contextStatusMetrics,
|
|
95
|
+
createAgentChatComposerFile: () => createAgentChatComposerFile,
|
|
92
96
|
hitlActionLabel: () => hitlActionLabel,
|
|
93
97
|
hitlTitle: () => hitlTitle,
|
|
94
98
|
isAgentChatSubagent: () => isAgentChatSubagent,
|
|
@@ -97,15 +101,25 @@ __export(ui_exports, {
|
|
|
97
101
|
markerVariants: () => markerVariants,
|
|
98
102
|
partitionAgentChatToolTimeline: () => partitionAgentChatToolTimeline,
|
|
99
103
|
resolvedHitlLabel: () => resolvedHitlLabel,
|
|
104
|
+
revokeAgentChatComposerFiles: () => revokeAgentChatComposerFiles,
|
|
100
105
|
shouldCollapseUserMessage: () => shouldCollapseUserMessage,
|
|
101
106
|
useAudioRecorder: () => useAudioRecorder
|
|
102
107
|
});
|
|
103
108
|
module.exports = __toCommonJS(ui_exports);
|
|
104
109
|
|
|
105
110
|
// src/ui/agent-chat-composer.tsx
|
|
111
|
+
var React2 = __toESM(require("react"), 1);
|
|
112
|
+
var import_lucide_react2 = require("lucide-react");
|
|
113
|
+
|
|
114
|
+
// src/ui/adapters.ts
|
|
106
115
|
var React = __toESM(require("react"), 1);
|
|
116
|
+
var import_file_icons = require("@untitledui/file-icons");
|
|
107
117
|
var import_lucide_react = require("lucide-react");
|
|
108
118
|
|
|
119
|
+
// src/ui/attachment.tsx
|
|
120
|
+
var import_class_variance_authority = require("class-variance-authority");
|
|
121
|
+
var import_react_slot = require("@radix-ui/react-slot");
|
|
122
|
+
|
|
109
123
|
// src/ui/utils.ts
|
|
110
124
|
var import_clsx = require("clsx");
|
|
111
125
|
var import_tailwind_merge = require("tailwind-merge");
|
|
@@ -113,266 +127,8 @@ function cn(...values) {
|
|
|
113
127
|
return (0, import_tailwind_merge.twMerge)((0, import_clsx.clsx)(values));
|
|
114
128
|
}
|
|
115
129
|
|
|
116
|
-
// src/ui/agent-chat-composer.tsx
|
|
117
|
-
var import_jsx_runtime = require("react/jsx-runtime");
|
|
118
|
-
var createId = () => typeof crypto !== "undefined" && "randomUUID" in crypto ? crypto.randomUUID() : `file-${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
119
|
-
function createComposerFile(file) {
|
|
120
|
-
return {
|
|
121
|
-
id: createId(),
|
|
122
|
-
type: "file",
|
|
123
|
-
url: URL.createObjectURL(file),
|
|
124
|
-
filename: file.name || "attachment",
|
|
125
|
-
mediaType: file.type || "application/octet-stream",
|
|
126
|
-
source: file
|
|
127
|
-
};
|
|
128
|
-
}
|
|
129
|
-
function revokeComposerFiles(files) {
|
|
130
|
-
files.forEach((file) => {
|
|
131
|
-
try {
|
|
132
|
-
URL.revokeObjectURL(file.url);
|
|
133
|
-
} catch {
|
|
134
|
-
}
|
|
135
|
-
});
|
|
136
|
-
}
|
|
137
|
-
function formatFileCount(files) {
|
|
138
|
-
if (files.length === 1) return files[0].filename;
|
|
139
|
-
return `${files.length} files`;
|
|
140
|
-
}
|
|
141
|
-
var pillButtonClass = "inline-flex size-8 shrink-0 items-center justify-center gap-2 rounded-lg border-0 bg-muted text-sm text-muted-foreground shadow-none transition-colors hover:bg-accent hover:text-accent-foreground focus-visible:outline-none focus-visible:ring-0 disabled:pointer-events-none disabled:opacity-50";
|
|
142
|
-
var submitButtonClass = "inline-flex size-9 shrink-0 items-center justify-center gap-2 rounded-lg border-0 bg-primary text-sm text-primary-foreground shadow-none transition-[background-color,color,opacity,transform] duration-200 hover:bg-primary/90 focus-visible:outline-none focus-visible:ring-0 disabled:pointer-events-none disabled:opacity-50";
|
|
143
|
-
function AgentChatComposer({
|
|
144
|
-
accept,
|
|
145
|
-
allowAttachments = true,
|
|
146
|
-
attachmentLayout = "flow",
|
|
147
|
-
className,
|
|
148
|
-
disabled = false,
|
|
149
|
-
forceExpanded = false,
|
|
150
|
-
inputToolbarContent,
|
|
151
|
-
isRunning = false,
|
|
152
|
-
onAttachmentCountChange,
|
|
153
|
-
onStop,
|
|
154
|
-
onSubmit,
|
|
155
|
-
placeholder = "Message the agent",
|
|
156
|
-
textareaRef
|
|
157
|
-
}) {
|
|
158
|
-
const fileInputRef = React.useRef(null);
|
|
159
|
-
const filesRef = React.useRef([]);
|
|
160
|
-
const localTextareaRef = React.useRef(null);
|
|
161
|
-
const [text, setText] = React.useState("");
|
|
162
|
-
const [files, setFiles] = React.useState([]);
|
|
163
|
-
const [isSubmitting, setIsSubmitting] = React.useState(false);
|
|
164
|
-
const isExpanded = forceExpanded || text.includes("\n") || text.length > 62;
|
|
165
|
-
const setTextareaRef = React.useCallback(
|
|
166
|
-
(node) => {
|
|
167
|
-
localTextareaRef.current = node;
|
|
168
|
-
if (typeof textareaRef === "function") {
|
|
169
|
-
textareaRef(node);
|
|
170
|
-
} else if (textareaRef) {
|
|
171
|
-
textareaRef.current = node;
|
|
172
|
-
}
|
|
173
|
-
},
|
|
174
|
-
[textareaRef]
|
|
175
|
-
);
|
|
176
|
-
React.useEffect(() => {
|
|
177
|
-
filesRef.current = files;
|
|
178
|
-
onAttachmentCountChange?.(files.length);
|
|
179
|
-
}, [files, onAttachmentCountChange]);
|
|
180
|
-
React.useEffect(() => () => revokeComposerFiles(filesRef.current), []);
|
|
181
|
-
React.useLayoutEffect(() => {
|
|
182
|
-
const textarea = localTextareaRef.current;
|
|
183
|
-
if (!textarea) return;
|
|
184
|
-
if (isExpanded) {
|
|
185
|
-
textarea.style.height = "auto";
|
|
186
|
-
const scrollHeight = textarea.scrollHeight;
|
|
187
|
-
textarea.style.height = `${Math.min(scrollHeight, 224)}px`;
|
|
188
|
-
textarea.style.overflowY = scrollHeight >= 224 ? "auto" : "hidden";
|
|
189
|
-
return;
|
|
190
|
-
}
|
|
191
|
-
textarea.style.height = "";
|
|
192
|
-
textarea.style.overflowY = "hidden";
|
|
193
|
-
}, [isExpanded, text]);
|
|
194
|
-
const canSubmit = !disabled && !isSubmitting && !isRunning && (text.trim().length > 0 || files.length > 0);
|
|
195
|
-
const handleFilesChange = (event) => {
|
|
196
|
-
const selected = Array.from(event.target.files || []).map(createComposerFile);
|
|
197
|
-
if (selected.length > 0) {
|
|
198
|
-
setFiles((current) => [...current, ...selected]);
|
|
199
|
-
}
|
|
200
|
-
event.currentTarget.value = "";
|
|
201
|
-
};
|
|
202
|
-
const removeFile = (fileId) => {
|
|
203
|
-
setFiles((current) => {
|
|
204
|
-
const target = current.find((file) => file.id === fileId);
|
|
205
|
-
if (target) revokeComposerFiles([target]);
|
|
206
|
-
return current.filter((file) => file.id !== fileId);
|
|
207
|
-
});
|
|
208
|
-
};
|
|
209
|
-
const submit = async () => {
|
|
210
|
-
if (!canSubmit) return;
|
|
211
|
-
const submittedText = text.trim();
|
|
212
|
-
const submittedFiles = files;
|
|
213
|
-
setIsSubmitting(true);
|
|
214
|
-
try {
|
|
215
|
-
await onSubmit({ text: submittedText, files: submittedFiles });
|
|
216
|
-
setText("");
|
|
217
|
-
setFiles([]);
|
|
218
|
-
revokeComposerFiles(submittedFiles);
|
|
219
|
-
} catch {
|
|
220
|
-
} finally {
|
|
221
|
-
setIsSubmitting(false);
|
|
222
|
-
}
|
|
223
|
-
};
|
|
224
|
-
const handleKeyDown = (event) => {
|
|
225
|
-
if (event.key === "Enter" && !event.shiftKey) {
|
|
226
|
-
event.preventDefault();
|
|
227
|
-
void submit();
|
|
228
|
-
}
|
|
229
|
-
};
|
|
230
|
-
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: cn("a24-composer relative mx-auto w-full max-w-3xl overflow-visible", className), "data-agents24-chat-composer": "", children: [
|
|
231
|
-
files.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
232
|
-
"div",
|
|
233
|
-
{
|
|
234
|
-
"data-testid": "agent-chat-composer-attachments",
|
|
235
|
-
"data-layout": attachmentLayout,
|
|
236
|
-
className: cn(
|
|
237
|
-
"a24-composer__attachments flex min-w-0 gap-2 py-1",
|
|
238
|
-
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"
|
|
239
|
-
),
|
|
240
|
-
children: files.map((file) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
241
|
-
"div",
|
|
242
|
-
{
|
|
243
|
-
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-border bg-card px-1.5 text-sm font-medium text-card-foreground transition-colors hover:bg-accent",
|
|
244
|
-
children: [
|
|
245
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "flex size-5 shrink-0 items-center justify-center rounded bg-background text-muted-foreground", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.Paperclip, { className: "size-3" }) }),
|
|
246
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "min-w-0 flex-1 truncate", children: file.filename }),
|
|
247
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
248
|
-
"button",
|
|
249
|
-
{
|
|
250
|
-
"aria-label": `Remove ${file.filename}`,
|
|
251
|
-
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",
|
|
252
|
-
onClick: () => removeFile(file.id),
|
|
253
|
-
type: "button",
|
|
254
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.X, { className: "size-3" })
|
|
255
|
-
}
|
|
256
|
-
)
|
|
257
|
-
]
|
|
258
|
-
},
|
|
259
|
-
file.id
|
|
260
|
-
))
|
|
261
|
-
}
|
|
262
|
-
) : null,
|
|
263
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
264
|
-
"div",
|
|
265
|
-
{
|
|
266
|
-
"data-expanded": isExpanded ? "" : void 0,
|
|
267
|
-
className: cn(
|
|
268
|
-
"a24-composer__surface relative w-full border border-input bg-background shadow-sm",
|
|
269
|
-
isExpanded ? "rounded-xl p-2 pb-1.5" : "rounded-xl px-2 py-[3px]"
|
|
270
|
-
),
|
|
271
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
272
|
-
"div",
|
|
273
|
-
{
|
|
274
|
-
className: "a24-composer__grid w-full",
|
|
275
|
-
style: {
|
|
276
|
-
alignItems: "center",
|
|
277
|
-
display: "grid",
|
|
278
|
-
gridTemplateAreas: isExpanded ? `"textarea textarea textarea" "plus toolbar submit"` : `"plus textarea composer mic submit"`,
|
|
279
|
-
gridTemplateColumns: isExpanded ? "auto 1fr auto" : "auto 1fr auto auto auto",
|
|
280
|
-
gap: isExpanded ? "3px 10px" : "8px"
|
|
281
|
-
},
|
|
282
|
-
children: [
|
|
283
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { gridArea: "plus" }, className: "a24-composer__attach-slot flex shrink-0 items-center justify-center", children: [
|
|
284
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
285
|
-
"input",
|
|
286
|
-
{
|
|
287
|
-
ref: fileInputRef,
|
|
288
|
-
accept,
|
|
289
|
-
"aria-label": "Attach files",
|
|
290
|
-
className: "hidden",
|
|
291
|
-
multiple: true,
|
|
292
|
-
name: "attachments",
|
|
293
|
-
onChange: handleFilesChange,
|
|
294
|
-
type: "file"
|
|
295
|
-
}
|
|
296
|
-
),
|
|
297
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
298
|
-
"button",
|
|
299
|
-
{
|
|
300
|
-
"aria-label": "Attach files",
|
|
301
|
-
className: cn("a24-composer__attach", pillButtonClass),
|
|
302
|
-
disabled: disabled || isSubmitting || isRunning || !allowAttachments,
|
|
303
|
-
onClick: () => fileInputRef.current?.click(),
|
|
304
|
-
title: "Attach files",
|
|
305
|
-
type: "button",
|
|
306
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.Plus, { className: "size-4" })
|
|
307
|
-
}
|
|
308
|
-
)
|
|
309
|
-
] }),
|
|
310
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { gridArea: "textarea" }, className: "a24-composer__textarea-slot min-w-0 w-full", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
311
|
-
"textarea",
|
|
312
|
-
{
|
|
313
|
-
ref: setTextareaRef,
|
|
314
|
-
"aria-label": "Message",
|
|
315
|
-
autoComplete: "off",
|
|
316
|
-
className: cn(
|
|
317
|
-
"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-muted-foreground focus-visible:outline-none focus-visible:ring-0",
|
|
318
|
-
isExpanded ? "min-h-9 max-h-[224px] py-1" : "min-h-9 h-9 max-h-9 py-2"
|
|
319
|
-
),
|
|
320
|
-
disabled: disabled || isSubmitting || isRunning,
|
|
321
|
-
name: "message",
|
|
322
|
-
onChange: (event) => setText(event.target.value),
|
|
323
|
-
onKeyDown: handleKeyDown,
|
|
324
|
-
placeholder,
|
|
325
|
-
rows: isExpanded ? 2 : 1,
|
|
326
|
-
value: text
|
|
327
|
-
}
|
|
328
|
-
) }),
|
|
329
|
-
isExpanded ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { gridArea: "toolbar" }, className: "a24-composer__toolbar flex min-w-0 items-center gap-1.5", children: inputToolbarContent }) : null,
|
|
330
|
-
!isExpanded ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { gridArea: "composer" }, className: "flex min-w-0 items-center gap-1.5" }) : null,
|
|
331
|
-
!isExpanded ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { gridArea: "mic" }, className: "flex shrink-0 items-center justify-end" }) : null,
|
|
332
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { gridArea: "submit" }, className: "a24-composer__submit-slot flex shrink-0 items-center justify-end", children: isRunning ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
333
|
-
"button",
|
|
334
|
-
{
|
|
335
|
-
"aria-label": "Stop generating",
|
|
336
|
-
className: cn("a24-composer__submit", submitButtonClass),
|
|
337
|
-
disabled: disabled || !onStop,
|
|
338
|
-
onClick: onStop,
|
|
339
|
-
title: "Stop",
|
|
340
|
-
type: "button",
|
|
341
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.Square, { className: "size-3.5 fill-current" })
|
|
342
|
-
}
|
|
343
|
-
) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
344
|
-
"button",
|
|
345
|
-
{
|
|
346
|
-
"aria-label": files.length > 0 ? `Send ${formatFileCount(files)}` : "Send message",
|
|
347
|
-
className: cn("a24-composer__submit", submitButtonClass, text.trim().length > 0 && "hover:scale-105"),
|
|
348
|
-
disabled: !canSubmit,
|
|
349
|
-
onClick: () => void submit(),
|
|
350
|
-
title: "Send",
|
|
351
|
-
type: "button",
|
|
352
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.ArrowUp, { className: "size-4", strokeWidth: 2.5 })
|
|
353
|
-
}
|
|
354
|
-
) })
|
|
355
|
-
]
|
|
356
|
-
}
|
|
357
|
-
)
|
|
358
|
-
}
|
|
359
|
-
),
|
|
360
|
-
!isExpanded && inputToolbarContent ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "a24-composer__toolbar a24-composer__toolbar--external mt-2 flex items-center gap-2 px-1", children: inputToolbarContent }) : null
|
|
361
|
-
] });
|
|
362
|
-
}
|
|
363
|
-
|
|
364
|
-
// src/ui/agent-chat-actions.tsx
|
|
365
|
-
var React3 = __toESM(require("react"), 1);
|
|
366
|
-
var import_lucide_react2 = require("lucide-react");
|
|
367
|
-
var import_radix_ui = require("radix-ui");
|
|
368
|
-
|
|
369
|
-
// src/ui/adapters.ts
|
|
370
|
-
var React2 = __toESM(require("react"), 1);
|
|
371
|
-
|
|
372
130
|
// src/ui/attachment.tsx
|
|
373
|
-
var
|
|
374
|
-
var import_react_slot = require("@radix-ui/react-slot");
|
|
375
|
-
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
131
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
376
132
|
var attachmentVariants = (0, import_class_variance_authority.cva)(
|
|
377
133
|
"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",
|
|
378
134
|
{
|
|
@@ -396,7 +152,7 @@ function Attachment({
|
|
|
396
152
|
orientation = "horizontal",
|
|
397
153
|
...props
|
|
398
154
|
}) {
|
|
399
|
-
return /* @__PURE__ */ (0,
|
|
155
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
400
156
|
"div",
|
|
401
157
|
{
|
|
402
158
|
"data-slot": "attachment",
|
|
@@ -409,12 +165,12 @@ function Attachment({
|
|
|
409
165
|
);
|
|
410
166
|
}
|
|
411
167
|
var attachmentMediaVariants = (0, import_class_variance_authority.cva)(
|
|
412
|
-
"relative flex aspect-square w-10 shrink-0 items-center justify-center overflow-hidden rounded-lg
|
|
168
|
+
"relative flex aspect-square w-10 shrink-0 items-center justify-center overflow-hidden rounded-lg 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: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",
|
|
413
169
|
{
|
|
414
170
|
variants: {
|
|
415
171
|
variant: {
|
|
416
172
|
icon: "",
|
|
417
|
-
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"
|
|
173
|
+
image: "bg-muted 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"
|
|
418
174
|
}
|
|
419
175
|
},
|
|
420
176
|
defaultVariants: {
|
|
@@ -427,7 +183,7 @@ function AttachmentMedia({
|
|
|
427
183
|
variant = "icon",
|
|
428
184
|
...props
|
|
429
185
|
}) {
|
|
430
|
-
return /* @__PURE__ */ (0,
|
|
186
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
431
187
|
"div",
|
|
432
188
|
{
|
|
433
189
|
"data-slot": "attachment-media",
|
|
@@ -441,7 +197,7 @@ function AttachmentContent({
|
|
|
441
197
|
className,
|
|
442
198
|
...props
|
|
443
199
|
}) {
|
|
444
|
-
return /* @__PURE__ */ (0,
|
|
200
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
445
201
|
"div",
|
|
446
202
|
{
|
|
447
203
|
"data-slot": "attachment-content",
|
|
@@ -457,7 +213,7 @@ function AttachmentTitle({
|
|
|
457
213
|
className,
|
|
458
214
|
...props
|
|
459
215
|
}) {
|
|
460
|
-
return /* @__PURE__ */ (0,
|
|
216
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
461
217
|
"span",
|
|
462
218
|
{
|
|
463
219
|
"data-slot": "attachment-title",
|
|
@@ -473,7 +229,7 @@ function AttachmentDescription({
|
|
|
473
229
|
className,
|
|
474
230
|
...props
|
|
475
231
|
}) {
|
|
476
|
-
return /* @__PURE__ */ (0,
|
|
232
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
477
233
|
"span",
|
|
478
234
|
{
|
|
479
235
|
"data-slot": "attachment-description",
|
|
@@ -490,7 +246,7 @@ function AttachmentActions({
|
|
|
490
246
|
className,
|
|
491
247
|
...props
|
|
492
248
|
}) {
|
|
493
|
-
return /* @__PURE__ */ (0,
|
|
249
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
494
250
|
"div",
|
|
495
251
|
{
|
|
496
252
|
"data-slot": "attachment-actions",
|
|
@@ -507,7 +263,7 @@ function AttachmentAction({
|
|
|
507
263
|
type = "button",
|
|
508
264
|
...props
|
|
509
265
|
}) {
|
|
510
|
-
return /* @__PURE__ */ (0,
|
|
266
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
511
267
|
"button",
|
|
512
268
|
{
|
|
513
269
|
"data-slot": "attachment-action",
|
|
@@ -527,7 +283,7 @@ function AttachmentTrigger({
|
|
|
527
283
|
...props
|
|
528
284
|
}) {
|
|
529
285
|
const Comp = asChild ? import_react_slot.Slot : "button";
|
|
530
|
-
return /* @__PURE__ */ (0,
|
|
286
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
531
287
|
Comp,
|
|
532
288
|
{
|
|
533
289
|
"data-slot": "attachment-trigger",
|
|
@@ -538,7 +294,7 @@ function AttachmentTrigger({
|
|
|
538
294
|
);
|
|
539
295
|
}
|
|
540
296
|
function AttachmentGroup({ className, ...props }) {
|
|
541
|
-
return /* @__PURE__ */ (0,
|
|
297
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
542
298
|
"div",
|
|
543
299
|
{
|
|
544
300
|
"data-slot": "attachment-group",
|
|
@@ -552,9 +308,9 @@ function AttachmentGroup({ className, ...props }) {
|
|
|
552
308
|
}
|
|
553
309
|
|
|
554
310
|
// src/ui/message.tsx
|
|
555
|
-
var
|
|
311
|
+
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
556
312
|
function MessageGroup({ className, ...props }) {
|
|
557
|
-
return /* @__PURE__ */ (0,
|
|
313
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
558
314
|
"div",
|
|
559
315
|
{
|
|
560
316
|
"data-slot": "message-group",
|
|
@@ -568,7 +324,7 @@ function Message({
|
|
|
568
324
|
align = "start",
|
|
569
325
|
...props
|
|
570
326
|
}) {
|
|
571
|
-
return /* @__PURE__ */ (0,
|
|
327
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
572
328
|
"div",
|
|
573
329
|
{
|
|
574
330
|
"data-slot": "message",
|
|
@@ -582,7 +338,7 @@ function Message({
|
|
|
582
338
|
);
|
|
583
339
|
}
|
|
584
340
|
function MessageAvatar({ className, ...props }) {
|
|
585
|
-
return /* @__PURE__ */ (0,
|
|
341
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
586
342
|
"div",
|
|
587
343
|
{
|
|
588
344
|
"data-slot": "message-avatar",
|
|
@@ -595,7 +351,7 @@ function MessageAvatar({ className, ...props }) {
|
|
|
595
351
|
);
|
|
596
352
|
}
|
|
597
353
|
function MessageContent({ className, ...props }) {
|
|
598
|
-
return /* @__PURE__ */ (0,
|
|
354
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
599
355
|
"div",
|
|
600
356
|
{
|
|
601
357
|
"data-slot": "message-content",
|
|
@@ -608,7 +364,7 @@ function MessageContent({ className, ...props }) {
|
|
|
608
364
|
);
|
|
609
365
|
}
|
|
610
366
|
function MessageHeader({ className, ...props }) {
|
|
611
|
-
return /* @__PURE__ */ (0,
|
|
367
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
612
368
|
"div",
|
|
613
369
|
{
|
|
614
370
|
"data-slot": "message-header",
|
|
@@ -621,7 +377,7 @@ function MessageHeader({ className, ...props }) {
|
|
|
621
377
|
);
|
|
622
378
|
}
|
|
623
379
|
function MessageFooter({ className, ...props }) {
|
|
624
|
-
return /* @__PURE__ */ (0,
|
|
380
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
625
381
|
"div",
|
|
626
382
|
{
|
|
627
383
|
"data-slot": "message-footer",
|
|
@@ -635,23 +391,22 @@ function MessageFooter({ className, ...props }) {
|
|
|
635
391
|
}
|
|
636
392
|
|
|
637
393
|
// src/ui/adapters.ts
|
|
638
|
-
var ChatMessageRoleContext =
|
|
394
|
+
var ChatMessageRoleContext = React.createContext("assistant");
|
|
639
395
|
function ChatMessage(input) {
|
|
640
396
|
const { className, from, align, ...props } = input;
|
|
641
397
|
const isUser = from === "user";
|
|
642
|
-
const isRtl = props.dir === "rtl";
|
|
643
398
|
const resolvedAlign = align || "start";
|
|
644
399
|
const messageProps = Object.assign({}, props);
|
|
645
400
|
messageProps.align = resolvedAlign;
|
|
646
401
|
messageProps.className = cn(
|
|
647
|
-
"a24-chat-message group flex w-full flex-col
|
|
648
|
-
isUser ?
|
|
402
|
+
"a24-chat-message group flex w-full flex-col",
|
|
403
|
+
isUser ? "a24-chat-message--user is-user max-w-full justify-end gap-0" : "a24-chat-message--assistant is-assistant max-w-full gap-2",
|
|
649
404
|
className
|
|
650
405
|
);
|
|
651
|
-
return
|
|
406
|
+
return React.createElement(
|
|
652
407
|
ChatMessageRoleContext.Provider,
|
|
653
408
|
{ value: from },
|
|
654
|
-
|
|
409
|
+
React.createElement(Message, messageProps)
|
|
655
410
|
);
|
|
656
411
|
}
|
|
657
412
|
function ChatMessageContent({
|
|
@@ -659,9 +414,9 @@ function ChatMessageContent({
|
|
|
659
414
|
from,
|
|
660
415
|
...props
|
|
661
416
|
}) {
|
|
662
|
-
const inheritedRole =
|
|
417
|
+
const inheritedRole = React.useContext(ChatMessageRoleContext);
|
|
663
418
|
const isUser = (from ?? inheritedRole) === "user";
|
|
664
|
-
return
|
|
419
|
+
return React.createElement("div", {
|
|
665
420
|
className: cn(
|
|
666
421
|
"a24-chat-message-content flex max-w-full min-w-0 flex-col gap-2 text-sm",
|
|
667
422
|
isUser ? "a24-chat-message-content--user is-user:dark w-fit overflow-visible rounded-lg bg-secondary px-4 py-3 text-foreground" : "a24-chat-message-content--assistant w-full overflow-visible text-foreground",
|
|
@@ -675,7 +430,7 @@ function ChatMessageActions({
|
|
|
675
430
|
className,
|
|
676
431
|
...props
|
|
677
432
|
}) {
|
|
678
|
-
return
|
|
433
|
+
return React.createElement("div", {
|
|
679
434
|
className: cn("flex items-center gap-1", className),
|
|
680
435
|
"data-slot": "message-actions",
|
|
681
436
|
...props
|
|
@@ -690,7 +445,7 @@ function ChatMessageAction({
|
|
|
690
445
|
...props
|
|
691
446
|
}) {
|
|
692
447
|
const tooltipLabel = typeof tooltip === "string" ? tooltip : void 0;
|
|
693
|
-
return
|
|
448
|
+
return React.createElement(
|
|
694
449
|
"button",
|
|
695
450
|
{
|
|
696
451
|
"aria-label": label || tooltipLabel,
|
|
@@ -703,83 +458,629 @@ function ChatMessageAction({
|
|
|
703
458
|
...props
|
|
704
459
|
},
|
|
705
460
|
children,
|
|
706
|
-
|
|
461
|
+
React.createElement(
|
|
707
462
|
"span",
|
|
708
463
|
{ className: "sr-only" },
|
|
709
464
|
label || tooltipLabel
|
|
710
465
|
)
|
|
711
466
|
);
|
|
712
467
|
}
|
|
468
|
+
var resolvedContentCache = /* @__PURE__ */ new WeakMap();
|
|
469
|
+
var CONTENT_CACHE_EXPIRY_BUFFER_MS = 15e3;
|
|
470
|
+
var CONTENT_CACHE_MAX_ENTRIES = 100;
|
|
471
|
+
function attachmentCacheKey(data) {
|
|
472
|
+
const id = typeof data.id === "string" ? data.id.trim() : "";
|
|
473
|
+
return id || null;
|
|
474
|
+
}
|
|
475
|
+
function cachedAttachmentContent(resolver, data) {
|
|
476
|
+
const key = attachmentCacheKey(data);
|
|
477
|
+
if (!key) return null;
|
|
478
|
+
const cached = resolvedContentCache.get(resolver)?.get(key);
|
|
479
|
+
if (!cached) return null;
|
|
480
|
+
if (!cached.validUntil) return cached;
|
|
481
|
+
const validUntil = Date.parse(cached.validUntil);
|
|
482
|
+
if (Number.isFinite(validUntil) && validUntil - CONTENT_CACHE_EXPIRY_BUFFER_MS > Date.now()) {
|
|
483
|
+
return cached;
|
|
484
|
+
}
|
|
485
|
+
resolvedContentCache.get(resolver)?.delete(key);
|
|
486
|
+
return null;
|
|
487
|
+
}
|
|
488
|
+
function cacheAttachmentContent(resolver, data, result) {
|
|
489
|
+
const key = attachmentCacheKey(data);
|
|
490
|
+
if (!key) return;
|
|
491
|
+
const cache = resolvedContentCache.get(resolver) || /* @__PURE__ */ new Map();
|
|
492
|
+
cache.delete(key);
|
|
493
|
+
cache.set(key, result);
|
|
494
|
+
while (cache.size > CONTENT_CACHE_MAX_ENTRIES) {
|
|
495
|
+
const oldestKey = cache.keys().next().value;
|
|
496
|
+
if (typeof oldestKey !== "string") break;
|
|
497
|
+
cache.delete(oldestKey);
|
|
498
|
+
}
|
|
499
|
+
resolvedContentCache.set(resolver, cache);
|
|
500
|
+
}
|
|
501
|
+
function invalidateAttachmentContent(resolver, data) {
|
|
502
|
+
const key = attachmentCacheKey(data);
|
|
503
|
+
if (key) resolvedContentCache.get(resolver)?.delete(key);
|
|
504
|
+
}
|
|
505
|
+
function attachmentMediaType(data) {
|
|
506
|
+
return data.mediaType || data.contentType || String(data.type || "application/octet-stream");
|
|
507
|
+
}
|
|
508
|
+
function attachmentFilename(data) {
|
|
509
|
+
return data.filename || data.name || "Attachment";
|
|
510
|
+
}
|
|
511
|
+
function attachmentExtension(filename) {
|
|
512
|
+
const extension = filename.split(".").pop();
|
|
513
|
+
return extension && extension !== filename ? extension.toLowerCase() : "";
|
|
514
|
+
}
|
|
515
|
+
function resolveAttachmentVisual(data) {
|
|
516
|
+
const mediaType = attachmentMediaType(data).toLowerCase();
|
|
517
|
+
const extension = attachmentExtension(attachmentFilename(data));
|
|
518
|
+
if (mediaType.startsWith("image/") || ["avif", "gif", "heic", "jpeg", "jpg", "png", "svg", "webp"].includes(extension)) {
|
|
519
|
+
return { fileIconType: extension || "image", kind: "image" };
|
|
520
|
+
}
|
|
521
|
+
if (mediaType === "application/pdf" || extension === "pdf") {
|
|
522
|
+
return { fileIconType: "pdf", kind: "pdf" };
|
|
523
|
+
}
|
|
524
|
+
if (mediaType.includes("spreadsheet") || mediaType.includes("csv") || ["csv", "numbers", "ods", "xls", "xlsm", "xlsx"].includes(extension)) {
|
|
525
|
+
if (["csv", "xls", "xlsx"].includes(extension)) {
|
|
526
|
+
return { fileIconType: extension, kind: "spreadsheet" };
|
|
527
|
+
}
|
|
528
|
+
return { fileIconType: "spreadsheets", kind: "spreadsheet" };
|
|
529
|
+
}
|
|
530
|
+
if (mediaType.includes("presentation") || ["key", "odp", "ppt", "pptx"].includes(extension)) {
|
|
531
|
+
return { fileIconType: extension === "ppt" ? "ppt" : "pptx", kind: "presentation" };
|
|
532
|
+
}
|
|
533
|
+
if (mediaType.includes("word") || mediaType.includes("rtf") || ["doc", "docx", "odt", "rtf"].includes(extension)) {
|
|
534
|
+
if (extension === "doc" || extension === "docx") {
|
|
535
|
+
return { fileIconType: extension, kind: "document" };
|
|
536
|
+
}
|
|
537
|
+
return { fileIconType: "document", kind: "document" };
|
|
538
|
+
}
|
|
539
|
+
if (mediaType.startsWith("audio/") || ["aac", "flac", "m4a", "mp3", "ogg", "wav"].includes(extension)) {
|
|
540
|
+
if (extension === "mp3" || extension === "wav") {
|
|
541
|
+
return { fileIconType: extension, kind: "audio" };
|
|
542
|
+
}
|
|
543
|
+
return { fileIconType: "audio", kind: "audio" };
|
|
544
|
+
}
|
|
545
|
+
if (mediaType.startsWith("video/") || ["avi", "m4v", "mkv", "mov", "mp4", "webm"].includes(extension)) {
|
|
546
|
+
if (["avi", "mkv", "mp4", "mpeg"].includes(extension)) {
|
|
547
|
+
return { fileIconType: extension, kind: "video" };
|
|
548
|
+
}
|
|
549
|
+
return { fileIconType: "video", kind: "video" };
|
|
550
|
+
}
|
|
551
|
+
if (mediaType.includes("zip") || mediaType.includes("compressed") || ["7z", "bz2", "gz", "rar", "tar", "tgz", "zip"].includes(extension)) {
|
|
552
|
+
return { fileIconType: extension === "rar" ? "rar" : "zip", kind: "archive" };
|
|
553
|
+
}
|
|
554
|
+
if (mediaType.includes("json") || mediaType.includes("javascript") || mediaType.includes("typescript") || mediaType.includes("xml") || ["css", "html", "js", "jsx", "json", "py", "sql", "ts", "tsx", "xml", "yaml", "yml"].includes(extension)) {
|
|
555
|
+
if (["css", "html", "java", "js", "json", "sql", "xml"].includes(extension)) {
|
|
556
|
+
return { fileIconType: extension, kind: "code" };
|
|
557
|
+
}
|
|
558
|
+
return { fileIconType: "code", kind: "code" };
|
|
559
|
+
}
|
|
560
|
+
if (mediaType.startsWith("text/") || ["md", "markdown", "txt"].includes(extension)) {
|
|
561
|
+
return { fileIconType: "txt", kind: "file" };
|
|
562
|
+
}
|
|
563
|
+
return { fileIconType: "empty", kind: "file" };
|
|
564
|
+
}
|
|
565
|
+
function solidFileIconBackground(fileIconType, kind) {
|
|
566
|
+
if (["txt", "zip", "rar"].includes(fileIconType)) return "#344054";
|
|
567
|
+
if (kind === "pdf") return "#D92D20";
|
|
568
|
+
if (kind === "spreadsheet") return "#079455";
|
|
569
|
+
if (kind === "document" || kind === "video") return "#155EEF";
|
|
570
|
+
if (kind === "presentation") return "#E62E05";
|
|
571
|
+
if (kind === "audio") return "#DD2590";
|
|
572
|
+
if (kind === "code") return "#444CE7";
|
|
573
|
+
return "#7F56D9";
|
|
574
|
+
}
|
|
575
|
+
function isImageAttachment(data) {
|
|
576
|
+
return resolveAttachmentVisual(data).kind === "image";
|
|
577
|
+
}
|
|
578
|
+
function attachmentRenderKey(data, index, rowKind) {
|
|
579
|
+
const id = typeof data.id === "string" ? data.id.trim() : "";
|
|
580
|
+
const identity = id || `${attachmentFilename(data)}:${attachmentMediaType(data)}`;
|
|
581
|
+
return `${rowKind}:${identity}:${index}`;
|
|
582
|
+
}
|
|
583
|
+
function ChatImageAttachment({
|
|
584
|
+
className,
|
|
585
|
+
data,
|
|
586
|
+
onRemove,
|
|
587
|
+
resolveContent,
|
|
588
|
+
...props
|
|
589
|
+
}) {
|
|
590
|
+
const filename = attachmentFilename(data);
|
|
591
|
+
const initialUrl = typeof data.url === "string" ? data.url.trim() : "";
|
|
592
|
+
const contentStatus = String(data.contentStatus || data.content_status || "available");
|
|
593
|
+
const attachmentKey = attachmentCacheKey(data);
|
|
594
|
+
const dataRef = React.useRef(data);
|
|
595
|
+
dataRef.current = data;
|
|
596
|
+
const rootRef = React.useRef(null);
|
|
597
|
+
const retryCountRef = React.useRef(0);
|
|
598
|
+
const [isNearViewport, setIsNearViewport] = React.useState(Boolean(initialUrl));
|
|
599
|
+
const [sourceUrl, setSourceUrl] = React.useState(initialUrl);
|
|
600
|
+
const [status, setStatus] = React.useState(
|
|
601
|
+
initialUrl ? "loading" : contentStatus === "expired" || !resolveContent ? "error" : "idle"
|
|
602
|
+
);
|
|
603
|
+
const [resolutionAttempt, setResolutionAttempt] = React.useState(0);
|
|
604
|
+
React.useEffect(() => {
|
|
605
|
+
if (initialUrl && resolutionAttempt === 0 || contentStatus === "expired" || !resolveContent) return;
|
|
606
|
+
const node = rootRef.current;
|
|
607
|
+
if (!node || typeof IntersectionObserver === "undefined") {
|
|
608
|
+
setIsNearViewport(true);
|
|
609
|
+
return;
|
|
610
|
+
}
|
|
611
|
+
const observer = new IntersectionObserver(
|
|
612
|
+
(entries) => {
|
|
613
|
+
if (entries.some((entry) => entry.isIntersecting)) {
|
|
614
|
+
setIsNearViewport(true);
|
|
615
|
+
observer.disconnect();
|
|
616
|
+
}
|
|
617
|
+
},
|
|
618
|
+
{ rootMargin: "240px" }
|
|
619
|
+
);
|
|
620
|
+
observer.observe(node);
|
|
621
|
+
return () => observer.disconnect();
|
|
622
|
+
}, [contentStatus, initialUrl, resolutionAttempt, resolveContent]);
|
|
623
|
+
React.useEffect(() => {
|
|
624
|
+
if (initialUrl && resolutionAttempt === 0) {
|
|
625
|
+
setSourceUrl(initialUrl);
|
|
626
|
+
setStatus("loading");
|
|
627
|
+
return;
|
|
628
|
+
}
|
|
629
|
+
if (!isNearViewport || !resolveContent || contentStatus === "expired") return;
|
|
630
|
+
const attachment = dataRef.current;
|
|
631
|
+
const cached = cachedAttachmentContent(resolveContent, attachment);
|
|
632
|
+
if (cached) {
|
|
633
|
+
setSourceUrl(cached.url);
|
|
634
|
+
setStatus("loading");
|
|
635
|
+
return;
|
|
636
|
+
}
|
|
637
|
+
const controller = new AbortController();
|
|
638
|
+
setStatus("resolving");
|
|
639
|
+
void resolveContent(attachment, controller.signal).then((result) => {
|
|
640
|
+
if (controller.signal.aborted) return;
|
|
641
|
+
if (!result.url?.trim()) throw new Error("Attachment content resolver returned an empty URL");
|
|
642
|
+
cacheAttachmentContent(resolveContent, attachment, result);
|
|
643
|
+
setSourceUrl(result.url);
|
|
644
|
+
setStatus("loading");
|
|
645
|
+
}).catch(() => {
|
|
646
|
+
if (!controller.signal.aborted) setStatus("error");
|
|
647
|
+
});
|
|
648
|
+
return () => controller.abort();
|
|
649
|
+
}, [attachmentKey, contentStatus, initialUrl, isNearViewport, resolutionAttempt, resolveContent]);
|
|
650
|
+
const handleImageError = () => {
|
|
651
|
+
if (resolveContent && retryCountRef.current === 0) {
|
|
652
|
+
retryCountRef.current = 1;
|
|
653
|
+
invalidateAttachmentContent(resolveContent, dataRef.current);
|
|
654
|
+
setSourceUrl("");
|
|
655
|
+
setStatus("resolving");
|
|
656
|
+
setResolutionAttempt((current) => current + 1);
|
|
657
|
+
return;
|
|
658
|
+
}
|
|
659
|
+
setStatus("error");
|
|
660
|
+
};
|
|
661
|
+
return React.createElement(
|
|
662
|
+
Attachment,
|
|
663
|
+
{
|
|
664
|
+
...props,
|
|
665
|
+
className: cn("a24-chat-image-attachment", className),
|
|
666
|
+
orientation: "vertical",
|
|
667
|
+
ref: rootRef,
|
|
668
|
+
state: status === "error" ? "error" : status === "done" ? "done" : "processing",
|
|
669
|
+
title: filename
|
|
670
|
+
},
|
|
671
|
+
React.createElement(
|
|
672
|
+
AttachmentMedia,
|
|
673
|
+
{ className: "a24-chat-image-media", variant: "image" },
|
|
674
|
+
sourceUrl ? React.createElement("img", {
|
|
675
|
+
alt: filename,
|
|
676
|
+
decoding: "async",
|
|
677
|
+
loading: "lazy",
|
|
678
|
+
onError: handleImageError,
|
|
679
|
+
onLoad: () => setStatus("done"),
|
|
680
|
+
src: sourceUrl
|
|
681
|
+
}) : status === "error" ? React.createElement(import_lucide_react.ImageOff, { "aria-hidden": "true", className: "size-5" }) : React.createElement(import_lucide_react.LoaderCircle, {
|
|
682
|
+
"aria-hidden": "true",
|
|
683
|
+
className: "a24-chat-image-spinner size-5 animate-spin"
|
|
684
|
+
}),
|
|
685
|
+
status !== "done" && status !== "error" ? React.createElement("span", { className: "sr-only", role: "status" }, `Loading ${filename}`) : null,
|
|
686
|
+
status === "error" ? React.createElement("span", { className: "sr-only" }, `${filename} preview unavailable`) : null
|
|
687
|
+
),
|
|
688
|
+
onRemove ? React.createElement(
|
|
689
|
+
"button",
|
|
690
|
+
{
|
|
691
|
+
"aria-label": `Remove ${filename}`,
|
|
692
|
+
className: "a24-chat-image-remove",
|
|
693
|
+
onClick: (event) => {
|
|
694
|
+
event.stopPropagation();
|
|
695
|
+
onRemove();
|
|
696
|
+
},
|
|
697
|
+
type: "button"
|
|
698
|
+
},
|
|
699
|
+
React.createElement(import_lucide_react.X, { "aria-hidden": "true", className: "size-3.5" })
|
|
700
|
+
) : null
|
|
701
|
+
);
|
|
702
|
+
}
|
|
713
703
|
function ChatAttachment({
|
|
704
|
+
className,
|
|
714
705
|
data,
|
|
715
706
|
onRemove,
|
|
716
707
|
orientation,
|
|
708
|
+
resolveContent,
|
|
717
709
|
...props
|
|
718
710
|
}) {
|
|
719
|
-
const
|
|
720
|
-
const
|
|
721
|
-
|
|
722
|
-
|
|
711
|
+
const filename = attachmentFilename(data);
|
|
712
|
+
const { fileIconType, kind } = resolveAttachmentVisual(data);
|
|
713
|
+
if (kind === "image") {
|
|
714
|
+
return React.createElement(ChatImageAttachment, {
|
|
715
|
+
...props,
|
|
716
|
+
className,
|
|
717
|
+
data,
|
|
718
|
+
onRemove,
|
|
719
|
+
resolveContent
|
|
720
|
+
});
|
|
721
|
+
}
|
|
722
|
+
return React.createElement(
|
|
723
723
|
Attachment,
|
|
724
724
|
{
|
|
725
|
-
|
|
725
|
+
className: cn("a24-chat-attachment", className),
|
|
726
|
+
orientation: orientation || "horizontal",
|
|
727
|
+
size: "sm",
|
|
726
728
|
...props
|
|
727
729
|
},
|
|
728
|
-
|
|
730
|
+
React.createElement(
|
|
729
731
|
AttachmentMedia,
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
"
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
732
|
+
Object.assign(
|
|
733
|
+
{
|
|
734
|
+
className: "a24-chat-attachment-icon",
|
|
735
|
+
style: { backgroundColor: solidFileIconBackground(fileIconType, kind) },
|
|
736
|
+
variant: "icon"
|
|
737
|
+
},
|
|
738
|
+
{ "data-file-kind": kind }
|
|
739
|
+
),
|
|
740
|
+
React.createElement(import_file_icons.FileIcon, {
|
|
741
|
+
"aria-hidden": "true",
|
|
742
|
+
size: 28,
|
|
743
|
+
type: fileIconType,
|
|
744
|
+
variant: "solid"
|
|
745
|
+
})
|
|
740
746
|
),
|
|
741
|
-
|
|
747
|
+
React.createElement(
|
|
742
748
|
AttachmentContent,
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
React2.createElement(AttachmentDescription, null, mediaType)
|
|
749
|
+
{ className: "a24-chat-attachment-content" },
|
|
750
|
+
React.createElement(AttachmentTitle, { title: filename }, filename)
|
|
746
751
|
),
|
|
747
|
-
onRemove ?
|
|
752
|
+
onRemove ? React.createElement(
|
|
748
753
|
"button",
|
|
749
754
|
{
|
|
750
755
|
"aria-label": "Remove attachment",
|
|
751
|
-
className: "relative z-20
|
|
756
|
+
className: "relative z-20 inline-flex size-6 shrink-0 items-center justify-center rounded-md text-muted-foreground hover:bg-muted hover:text-foreground",
|
|
752
757
|
onClick: (event) => {
|
|
753
758
|
event.stopPropagation();
|
|
754
759
|
onRemove();
|
|
755
760
|
},
|
|
756
761
|
type: "button"
|
|
757
762
|
},
|
|
758
|
-
|
|
763
|
+
React.createElement(import_lucide_react.X, { "aria-hidden": "true", className: "size-3.5" })
|
|
759
764
|
) : null
|
|
760
765
|
);
|
|
761
766
|
}
|
|
762
|
-
function ChatAttachments({
|
|
763
|
-
children,
|
|
767
|
+
function ChatAttachments({
|
|
768
|
+
children,
|
|
769
|
+
className,
|
|
770
|
+
...props
|
|
771
|
+
}) {
|
|
772
|
+
if (!children) return null;
|
|
773
|
+
return React.createElement(
|
|
774
|
+
AttachmentGroup,
|
|
775
|
+
{
|
|
776
|
+
className: cn(
|
|
777
|
+
"a24-chat-attachments w-full flex-nowrap",
|
|
778
|
+
className
|
|
779
|
+
),
|
|
780
|
+
...props
|
|
781
|
+
},
|
|
782
|
+
children
|
|
783
|
+
);
|
|
784
|
+
}
|
|
785
|
+
function ChatAttachmentRows({
|
|
786
|
+
attachments,
|
|
787
|
+
className,
|
|
788
|
+
dir,
|
|
789
|
+
onRemove,
|
|
790
|
+
resolveContent,
|
|
791
|
+
...props
|
|
792
|
+
}) {
|
|
793
|
+
if (attachments.length === 0) return null;
|
|
794
|
+
const imageAttachments = attachments.filter(isImageAttachment);
|
|
795
|
+
const fileAttachments = attachments.filter((attachment) => !isImageAttachment(attachment));
|
|
796
|
+
const renderRow = (items, kind) => items.length > 0 ? React.createElement(
|
|
797
|
+
ChatAttachments,
|
|
798
|
+
Object.assign({ dir, key: kind }, { "data-attachment-row": kind }),
|
|
799
|
+
items.map(
|
|
800
|
+
(attachment, index) => React.createElement(ChatAttachment, {
|
|
801
|
+
data: attachment,
|
|
802
|
+
dir: "ltr",
|
|
803
|
+
key: attachmentRenderKey(attachment, index, kind),
|
|
804
|
+
onRemove: onRemove ? () => onRemove(attachment) : void 0,
|
|
805
|
+
resolveContent
|
|
806
|
+
})
|
|
807
|
+
)
|
|
808
|
+
) : null;
|
|
809
|
+
return React.createElement(
|
|
810
|
+
"div",
|
|
811
|
+
{
|
|
812
|
+
className: cn("a24-chat-attachment-rows", className),
|
|
813
|
+
dir,
|
|
814
|
+
...props
|
|
815
|
+
},
|
|
816
|
+
renderRow(imageAttachments, "images"),
|
|
817
|
+
renderRow(fileAttachments, "files")
|
|
818
|
+
);
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
// src/ui/composer-attachments.ts
|
|
822
|
+
var createId = () => typeof crypto !== "undefined" && "randomUUID" in crypto ? crypto.randomUUID() : `file-${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
823
|
+
function createAgentChatComposerFile(file) {
|
|
824
|
+
return {
|
|
825
|
+
id: createId(),
|
|
826
|
+
type: "file",
|
|
827
|
+
url: URL.createObjectURL(file),
|
|
828
|
+
filename: file.name || "attachment",
|
|
829
|
+
mediaType: file.type || "application/octet-stream",
|
|
830
|
+
source: file
|
|
831
|
+
};
|
|
832
|
+
}
|
|
833
|
+
function revokeAgentChatComposerFiles(files) {
|
|
834
|
+
files.forEach((file) => {
|
|
835
|
+
try {
|
|
836
|
+
URL.revokeObjectURL(file.url);
|
|
837
|
+
} catch {
|
|
838
|
+
}
|
|
839
|
+
});
|
|
840
|
+
}
|
|
841
|
+
|
|
842
|
+
// src/ui/agent-chat-composer.tsx
|
|
843
|
+
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
844
|
+
function formatFileCount(files) {
|
|
845
|
+
if (files.length === 1) return files[0].filename;
|
|
846
|
+
return `${files.length} files`;
|
|
847
|
+
}
|
|
848
|
+
var pillButtonClass = "inline-flex size-8 shrink-0 items-center justify-center gap-2 rounded-lg border-0 bg-muted text-sm text-muted-foreground shadow-none transition-colors hover:bg-accent hover:text-accent-foreground focus-visible:outline-none focus-visible:ring-0 disabled:pointer-events-none disabled:opacity-50";
|
|
849
|
+
var submitButtonClass = "inline-flex size-9 shrink-0 items-center justify-center gap-2 rounded-lg border-0 bg-primary text-sm text-primary-foreground shadow-none transition-[background-color,color,opacity,transform] duration-200 hover:bg-primary/90 focus-visible:outline-none focus-visible:ring-0 disabled:pointer-events-none disabled:opacity-50";
|
|
850
|
+
function AgentChatComposer({
|
|
851
|
+
accept,
|
|
852
|
+
allowAttachments = true,
|
|
853
|
+
attachmentLayout = "flow",
|
|
764
854
|
className,
|
|
765
|
-
|
|
855
|
+
disabled = false,
|
|
856
|
+
forceExpanded = false,
|
|
857
|
+
inputToolbarContent,
|
|
858
|
+
isRunning = false,
|
|
859
|
+
onAttachmentCountChange,
|
|
860
|
+
onStop,
|
|
861
|
+
onSubmit,
|
|
862
|
+
placeholder = "Message the agent",
|
|
863
|
+
textareaRef
|
|
766
864
|
}) {
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
865
|
+
const fileInputRef = React2.useRef(null);
|
|
866
|
+
const filesRef = React2.useRef([]);
|
|
867
|
+
const inFlightFilesRef = React2.useRef([]);
|
|
868
|
+
const localTextareaRef = React2.useRef(null);
|
|
869
|
+
const draftRevisionRef = React2.useRef(0);
|
|
870
|
+
const [text, setText] = React2.useState("");
|
|
871
|
+
const [files, setFiles] = React2.useState([]);
|
|
872
|
+
const [isSubmitting, setIsSubmitting] = React2.useState(false);
|
|
873
|
+
const isExpanded = forceExpanded || text.includes("\n") || text.length > 62;
|
|
874
|
+
const setTextareaRef = React2.useCallback(
|
|
875
|
+
(node) => {
|
|
876
|
+
localTextareaRef.current = node;
|
|
877
|
+
if (typeof textareaRef === "function") {
|
|
878
|
+
textareaRef(node);
|
|
879
|
+
} else if (textareaRef) {
|
|
880
|
+
textareaRef.current = node;
|
|
881
|
+
}
|
|
777
882
|
},
|
|
778
|
-
|
|
883
|
+
[textareaRef]
|
|
779
884
|
);
|
|
885
|
+
React2.useEffect(() => {
|
|
886
|
+
filesRef.current = files;
|
|
887
|
+
onAttachmentCountChange?.(files.length);
|
|
888
|
+
}, [files, onAttachmentCountChange]);
|
|
889
|
+
React2.useEffect(() => () => {
|
|
890
|
+
revokeAgentChatComposerFiles(filesRef.current);
|
|
891
|
+
revokeAgentChatComposerFiles(inFlightFilesRef.current);
|
|
892
|
+
}, []);
|
|
893
|
+
React2.useLayoutEffect(() => {
|
|
894
|
+
const textarea = localTextareaRef.current;
|
|
895
|
+
if (!textarea) return;
|
|
896
|
+
if (isExpanded) {
|
|
897
|
+
textarea.style.height = "auto";
|
|
898
|
+
const scrollHeight = textarea.scrollHeight;
|
|
899
|
+
textarea.style.height = `${Math.min(scrollHeight, 224)}px`;
|
|
900
|
+
textarea.style.overflowY = scrollHeight >= 224 ? "auto" : "hidden";
|
|
901
|
+
return;
|
|
902
|
+
}
|
|
903
|
+
textarea.style.height = "";
|
|
904
|
+
textarea.style.overflowY = "hidden";
|
|
905
|
+
}, [isExpanded, text]);
|
|
906
|
+
const canSubmit = !disabled && !isSubmitting && !isRunning && (text.trim().length > 0 || files.length > 0);
|
|
907
|
+
const handleFilesChange = (event) => {
|
|
908
|
+
const selected = Array.from(event.target.files || []).map(createAgentChatComposerFile);
|
|
909
|
+
if (selected.length > 0) {
|
|
910
|
+
draftRevisionRef.current += 1;
|
|
911
|
+
setFiles((current) => [...current, ...selected]);
|
|
912
|
+
}
|
|
913
|
+
event.currentTarget.value = "";
|
|
914
|
+
};
|
|
915
|
+
const removeFile = (fileId) => {
|
|
916
|
+
draftRevisionRef.current += 1;
|
|
917
|
+
setFiles((current) => {
|
|
918
|
+
const target = current.find((file) => file.id === fileId);
|
|
919
|
+
if (target) revokeAgentChatComposerFiles([target]);
|
|
920
|
+
return current.filter((file) => file.id !== fileId);
|
|
921
|
+
});
|
|
922
|
+
};
|
|
923
|
+
const submit = async () => {
|
|
924
|
+
if (!canSubmit) return;
|
|
925
|
+
const submittedText = text.trim();
|
|
926
|
+
const submittedFiles = files;
|
|
927
|
+
const clearedRevision = draftRevisionRef.current + 1;
|
|
928
|
+
draftRevisionRef.current = clearedRevision;
|
|
929
|
+
inFlightFilesRef.current = submittedFiles;
|
|
930
|
+
setIsSubmitting(true);
|
|
931
|
+
setText("");
|
|
932
|
+
setFiles([]);
|
|
933
|
+
try {
|
|
934
|
+
await onSubmit({ text: submittedText, files: submittedFiles });
|
|
935
|
+
revokeAgentChatComposerFiles(submittedFiles);
|
|
936
|
+
inFlightFilesRef.current = [];
|
|
937
|
+
} catch {
|
|
938
|
+
if (draftRevisionRef.current === clearedRevision) {
|
|
939
|
+
draftRevisionRef.current += 1;
|
|
940
|
+
setText(submittedText);
|
|
941
|
+
setFiles(submittedFiles);
|
|
942
|
+
inFlightFilesRef.current = [];
|
|
943
|
+
} else {
|
|
944
|
+
revokeAgentChatComposerFiles(submittedFiles);
|
|
945
|
+
inFlightFilesRef.current = [];
|
|
946
|
+
}
|
|
947
|
+
} finally {
|
|
948
|
+
setIsSubmitting(false);
|
|
949
|
+
}
|
|
950
|
+
};
|
|
951
|
+
const handleKeyDown = (event) => {
|
|
952
|
+
if (event.key === "Enter" && !event.shiftKey) {
|
|
953
|
+
event.preventDefault();
|
|
954
|
+
void submit();
|
|
955
|
+
}
|
|
956
|
+
};
|
|
957
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: cn("a24-composer relative mx-auto w-full max-w-3xl overflow-visible", className), "data-agents24-chat-composer": "", children: [
|
|
958
|
+
files.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
959
|
+
"div",
|
|
960
|
+
{
|
|
961
|
+
"data-testid": "agent-chat-composer-attachments",
|
|
962
|
+
"data-layout": attachmentLayout,
|
|
963
|
+
className: cn(
|
|
964
|
+
"a24-composer__attachments min-w-0 py-1",
|
|
965
|
+
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"
|
|
966
|
+
),
|
|
967
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
968
|
+
ChatAttachmentRows,
|
|
969
|
+
{
|
|
970
|
+
attachments: files,
|
|
971
|
+
onRemove: (attachment) => attachment.id && removeFile(attachment.id)
|
|
972
|
+
}
|
|
973
|
+
)
|
|
974
|
+
}
|
|
975
|
+
) : null,
|
|
976
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
977
|
+
"div",
|
|
978
|
+
{
|
|
979
|
+
"data-expanded": isExpanded ? "" : void 0,
|
|
980
|
+
className: cn(
|
|
981
|
+
"a24-composer__surface relative w-full border border-input bg-background shadow-sm",
|
|
982
|
+
isExpanded ? "rounded-xl p-2 pb-1.5" : "rounded-xl px-2 py-[3px]"
|
|
983
|
+
),
|
|
984
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
985
|
+
"div",
|
|
986
|
+
{
|
|
987
|
+
className: "a24-composer__grid w-full",
|
|
988
|
+
style: {
|
|
989
|
+
alignItems: "center",
|
|
990
|
+
display: "grid",
|
|
991
|
+
gridTemplateAreas: isExpanded ? `"textarea textarea textarea" "plus toolbar submit"` : `"plus textarea composer mic submit"`,
|
|
992
|
+
gridTemplateColumns: isExpanded ? "auto 1fr auto" : "auto 1fr auto auto auto",
|
|
993
|
+
gap: isExpanded ? "3px 10px" : "8px"
|
|
994
|
+
},
|
|
995
|
+
children: [
|
|
996
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { style: { gridArea: "plus" }, className: "a24-composer__attach-slot flex shrink-0 items-center justify-center", children: [
|
|
997
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
998
|
+
"input",
|
|
999
|
+
{
|
|
1000
|
+
ref: fileInputRef,
|
|
1001
|
+
accept,
|
|
1002
|
+
"aria-label": "Attach files",
|
|
1003
|
+
className: "hidden",
|
|
1004
|
+
multiple: true,
|
|
1005
|
+
name: "attachments",
|
|
1006
|
+
onChange: handleFilesChange,
|
|
1007
|
+
type: "file"
|
|
1008
|
+
}
|
|
1009
|
+
),
|
|
1010
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
1011
|
+
"button",
|
|
1012
|
+
{
|
|
1013
|
+
"aria-label": "Attach files",
|
|
1014
|
+
className: cn("a24-composer__attach", pillButtonClass),
|
|
1015
|
+
disabled: disabled || isSubmitting || isRunning || !allowAttachments,
|
|
1016
|
+
onClick: () => fileInputRef.current?.click(),
|
|
1017
|
+
title: "Attach files",
|
|
1018
|
+
type: "button",
|
|
1019
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_lucide_react2.Plus, { className: "size-4" })
|
|
1020
|
+
}
|
|
1021
|
+
)
|
|
1022
|
+
] }),
|
|
1023
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: { gridArea: "textarea" }, className: "a24-composer__textarea-slot min-w-0 w-full", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
1024
|
+
"textarea",
|
|
1025
|
+
{
|
|
1026
|
+
ref: setTextareaRef,
|
|
1027
|
+
"aria-label": "Message",
|
|
1028
|
+
autoComplete: "off",
|
|
1029
|
+
className: cn(
|
|
1030
|
+
"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-muted-foreground focus-visible:outline-none focus-visible:ring-0",
|
|
1031
|
+
isExpanded ? "min-h-9 max-h-[224px] py-1" : "min-h-9 h-9 max-h-9 py-2"
|
|
1032
|
+
),
|
|
1033
|
+
disabled: disabled || isSubmitting || isRunning,
|
|
1034
|
+
name: "message",
|
|
1035
|
+
onChange: (event) => {
|
|
1036
|
+
draftRevisionRef.current += 1;
|
|
1037
|
+
setText(event.target.value);
|
|
1038
|
+
},
|
|
1039
|
+
onKeyDown: handleKeyDown,
|
|
1040
|
+
placeholder,
|
|
1041
|
+
rows: isExpanded ? 2 : 1,
|
|
1042
|
+
value: text
|
|
1043
|
+
}
|
|
1044
|
+
) }),
|
|
1045
|
+
isExpanded ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: { gridArea: "toolbar" }, className: "a24-composer__toolbar flex min-w-0 items-center gap-1.5", children: inputToolbarContent }) : null,
|
|
1046
|
+
!isExpanded ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: { gridArea: "composer" }, className: "flex min-w-0 items-center gap-1.5" }) : null,
|
|
1047
|
+
!isExpanded ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: { gridArea: "mic" }, className: "flex shrink-0 items-center justify-end" }) : null,
|
|
1048
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: { gridArea: "submit" }, className: "a24-composer__submit-slot flex shrink-0 items-center justify-end", children: isRunning ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
1049
|
+
"button",
|
|
1050
|
+
{
|
|
1051
|
+
"aria-label": "Stop generating",
|
|
1052
|
+
className: cn("a24-composer__submit", submitButtonClass),
|
|
1053
|
+
disabled: disabled || !onStop,
|
|
1054
|
+
onClick: onStop,
|
|
1055
|
+
title: "Stop",
|
|
1056
|
+
type: "button",
|
|
1057
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_lucide_react2.Square, { className: "size-3.5 fill-current" })
|
|
1058
|
+
}
|
|
1059
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
1060
|
+
"button",
|
|
1061
|
+
{
|
|
1062
|
+
"aria-label": files.length > 0 ? `Send ${formatFileCount(files)}` : "Send message",
|
|
1063
|
+
className: cn("a24-composer__submit", submitButtonClass, text.trim().length > 0 && "hover:scale-105"),
|
|
1064
|
+
disabled: !canSubmit,
|
|
1065
|
+
onClick: () => void submit(),
|
|
1066
|
+
title: "Send",
|
|
1067
|
+
type: "button",
|
|
1068
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_lucide_react2.ArrowUp, { className: "size-4", strokeWidth: 2.5 })
|
|
1069
|
+
}
|
|
1070
|
+
) })
|
|
1071
|
+
]
|
|
1072
|
+
}
|
|
1073
|
+
)
|
|
1074
|
+
}
|
|
1075
|
+
),
|
|
1076
|
+
!isExpanded && inputToolbarContent ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "a24-composer__toolbar a24-composer__toolbar--external mt-2 flex items-center gap-2 px-1", children: inputToolbarContent }) : null
|
|
1077
|
+
] });
|
|
780
1078
|
}
|
|
781
1079
|
|
|
782
1080
|
// src/ui/agent-chat-actions.tsx
|
|
1081
|
+
var React3 = __toESM(require("react"), 1);
|
|
1082
|
+
var import_lucide_react3 = require("lucide-react");
|
|
1083
|
+
var import_radix_ui = require("radix-ui");
|
|
783
1084
|
var import_jsx_runtime4 = require("react/jsx-runtime");
|
|
784
1085
|
var FEEDBACK_REASONS = [
|
|
785
1086
|
["incorrect", "Incorrect"],
|
|
@@ -789,6 +1090,15 @@ var FEEDBACK_REASONS = [
|
|
|
789
1090
|
["citation_issue", "Citation issue"],
|
|
790
1091
|
["other", "Other"]
|
|
791
1092
|
];
|
|
1093
|
+
function formatSentAt(value) {
|
|
1094
|
+
if (!value) return null;
|
|
1095
|
+
const date = value instanceof Date ? value : new Date(value);
|
|
1096
|
+
if (Number.isNaN(date.getTime())) return null;
|
|
1097
|
+
return {
|
|
1098
|
+
dateTime: date.toISOString(),
|
|
1099
|
+
label: new Intl.DateTimeFormat(void 0, { timeStyle: "short" }).format(date)
|
|
1100
|
+
};
|
|
1101
|
+
}
|
|
792
1102
|
function AgentChatDefaultActions({
|
|
793
1103
|
additionalActions,
|
|
794
1104
|
dir,
|
|
@@ -799,11 +1109,43 @@ function AgentChatDefaultActions({
|
|
|
799
1109
|
const [feedbackTrigger, setFeedbackTrigger] = React3.useState(null);
|
|
800
1110
|
const [reason, setReason] = React3.useState(handlers?.feedback?.reason ?? null);
|
|
801
1111
|
const [comment, setComment] = React3.useState(handlers?.feedback?.comment ?? "");
|
|
1112
|
+
const [optimisticCopied, setOptimisticCopied] = React3.useState(false);
|
|
1113
|
+
const copyResetTimer = React3.useRef(null);
|
|
802
1114
|
React3.useEffect(() => {
|
|
803
1115
|
setReason(handlers?.feedback?.reason ?? null);
|
|
804
1116
|
setComment(handlers?.feedback?.comment ?? "");
|
|
805
1117
|
}, [handlers?.feedback?.comment, handlers?.feedback?.reason]);
|
|
806
|
-
|
|
1118
|
+
React3.useEffect(() => () => {
|
|
1119
|
+
if (copyResetTimer.current !== null) window.clearTimeout(copyResetTimer.current);
|
|
1120
|
+
}, []);
|
|
1121
|
+
const copied = handlers?.copied ?? optimisticCopied;
|
|
1122
|
+
const copyMessage = () => {
|
|
1123
|
+
handlers?.onCopy?.(message.content || "", message.id);
|
|
1124
|
+
if (handlers?.copied !== void 0) return;
|
|
1125
|
+
setOptimisticCopied(true);
|
|
1126
|
+
if (copyResetTimer.current !== null) window.clearTimeout(copyResetTimer.current);
|
|
1127
|
+
copyResetTimer.current = window.setTimeout(() => {
|
|
1128
|
+
copyResetTimer.current = null;
|
|
1129
|
+
setOptimisticCopied(false);
|
|
1130
|
+
}, 1200);
|
|
1131
|
+
};
|
|
1132
|
+
if (!handlers || message.isFinal === false) return null;
|
|
1133
|
+
if (message.role === "user") {
|
|
1134
|
+
const sentAt = formatSentAt(message.createdAt);
|
|
1135
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(ChatMessageActions, { className: "a24-user-message-actions", children: [
|
|
1136
|
+
sentAt ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("time", { dateTime: sentAt.dateTime, children: sentAt.label }) : null,
|
|
1137
|
+
handlers.onCopy ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
1138
|
+
ChatMessageAction,
|
|
1139
|
+
{
|
|
1140
|
+
label: "Copy",
|
|
1141
|
+
onClick: copyMessage,
|
|
1142
|
+
tooltip: copied ? "Copied!" : "Copy to clipboard",
|
|
1143
|
+
className: "a24-user-message-copy",
|
|
1144
|
+
children: copied ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_lucide_react3.Check, { className: "a24-user-message-copy__check text-green-600" }) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_lucide_react3.Copy, { className: "a24-user-message-copy__icon" })
|
|
1145
|
+
}
|
|
1146
|
+
) : null
|
|
1147
|
+
] });
|
|
1148
|
+
}
|
|
807
1149
|
const canWriteFeedback = Boolean(message.runId && handlers.onSetFeedback);
|
|
808
1150
|
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(ChatMessageActions, { children: [
|
|
809
1151
|
handlers.onRetry ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
@@ -812,7 +1154,7 @@ function AgentChatDefaultActions({
|
|
|
812
1154
|
label: "Retry",
|
|
813
1155
|
onClick: () => void handlers.onRetry?.(message),
|
|
814
1156
|
tooltip: "Regenerate response",
|
|
815
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
1157
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_lucide_react3.RefreshCcw, { className: "size-4" })
|
|
816
1158
|
}
|
|
817
1159
|
) : null,
|
|
818
1160
|
canWriteFeedback ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
@@ -823,7 +1165,7 @@ function AgentChatDefaultActions({
|
|
|
823
1165
|
rating: handlers.feedback?.rating === "like" ? null : "like"
|
|
824
1166
|
}),
|
|
825
1167
|
tooltip: "Like this response",
|
|
826
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
1168
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_lucide_react3.ThumbsUp, { className: "size-4", fill: handlers.feedback?.rating === "like" ? "currentColor" : "none" })
|
|
827
1169
|
}
|
|
828
1170
|
) : null,
|
|
829
1171
|
canWriteFeedback ? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_radix_ui.Popover.Root, { onOpenChange: setFeedbackOpen, open: feedbackOpen, children: [
|
|
@@ -839,7 +1181,7 @@ function AgentChatDefaultActions({
|
|
|
839
1181
|
},
|
|
840
1182
|
ref: setFeedbackTrigger,
|
|
841
1183
|
tooltip: "Dislike this response",
|
|
842
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
1184
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_lucide_react3.ThumbsDown, { className: "size-4", fill: handlers.feedback?.rating === "dislike" ? "currentColor" : "none" })
|
|
843
1185
|
}
|
|
844
1186
|
) }),
|
|
845
1187
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
@@ -914,9 +1256,9 @@ function AgentChatDefaultActions({
|
|
|
914
1256
|
ChatMessageAction,
|
|
915
1257
|
{
|
|
916
1258
|
label: "Copy",
|
|
917
|
-
onClick:
|
|
918
|
-
tooltip:
|
|
919
|
-
children:
|
|
1259
|
+
onClick: copyMessage,
|
|
1260
|
+
tooltip: copied ? "Copied!" : "Copy to clipboard",
|
|
1261
|
+
children: copied ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_lucide_react3.Check, { className: "size-4 text-green-600" }) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_lucide_react3.Copy, { className: "size-4" })
|
|
920
1262
|
}
|
|
921
1263
|
) : null
|
|
922
1264
|
] });
|
|
@@ -924,7 +1266,7 @@ function AgentChatDefaultActions({
|
|
|
924
1266
|
|
|
925
1267
|
// src/ui/agent-chat-message.tsx
|
|
926
1268
|
var React8 = __toESM(require("react"), 1);
|
|
927
|
-
var
|
|
1269
|
+
var import_lucide_react6 = require("lucide-react");
|
|
928
1270
|
|
|
929
1271
|
// src/ui/agent-response-timeline.tsx
|
|
930
1272
|
var React7 = __toESM(require("react"), 1);
|
|
@@ -1113,7 +1455,7 @@ function useStreamingText({
|
|
|
1113
1455
|
|
|
1114
1456
|
// src/ui/ask-user-interaction.tsx
|
|
1115
1457
|
var React4 = __toESM(require("react"), 1);
|
|
1116
|
-
var
|
|
1458
|
+
var import_lucide_react4 = require("lucide-react");
|
|
1117
1459
|
|
|
1118
1460
|
// src/ui/ask-user-interaction-state.ts
|
|
1119
1461
|
function emptyAskUserDrafts(questions) {
|
|
@@ -1207,8 +1549,8 @@ function ResolvedQuestions({
|
|
|
1207
1549
|
" question",
|
|
1208
1550
|
count === 1 ? "" : "s"
|
|
1209
1551
|
] }),
|
|
1210
|
-
open ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1211
|
-
|
|
1552
|
+
open ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_lucide_react4.ChevronDown, { "aria-hidden": "true", className: "size-3.5" }) : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1553
|
+
import_lucide_react4.ChevronRight,
|
|
1212
1554
|
{
|
|
1213
1555
|
"aria-hidden": "true",
|
|
1214
1556
|
className: "size-3.5 opacity-70 rtl:rotate-180"
|
|
@@ -1323,7 +1665,7 @@ function AskUserInteraction({
|
|
|
1323
1665
|
onClick: () => setStep((current) => Math.max(0, current - 1)),
|
|
1324
1666
|
type: "button",
|
|
1325
1667
|
children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1326
|
-
|
|
1668
|
+
import_lucide_react4.ChevronLeft,
|
|
1327
1669
|
{
|
|
1328
1670
|
"aria-hidden": "true",
|
|
1329
1671
|
className: "size-4 rtl:rotate-180"
|
|
@@ -1352,7 +1694,7 @@ function AskUserInteraction({
|
|
|
1352
1694
|
onClick: () => completeStep(drafts),
|
|
1353
1695
|
type: "button",
|
|
1354
1696
|
children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1355
|
-
|
|
1697
|
+
import_lucide_react4.ChevronRight,
|
|
1356
1698
|
{
|
|
1357
1699
|
"aria-hidden": "true",
|
|
1358
1700
|
className: "size-4 rtl:rotate-180"
|
|
@@ -1389,7 +1731,7 @@ function AskUserInteraction({
|
|
|
1389
1731
|
),
|
|
1390
1732
|
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "min-w-0 flex-1 font-medium text-foreground", children: option.label }),
|
|
1391
1733
|
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1392
|
-
|
|
1734
|
+
import_lucide_react4.ChevronRight,
|
|
1393
1735
|
{
|
|
1394
1736
|
"aria-hidden": "true",
|
|
1395
1737
|
className: "size-4 shrink-0 text-muted-foreground opacity-0 transition-opacity group-hover:opacity-100 group-focus-visible:opacity-100 rtl:rotate-180"
|
|
@@ -1401,7 +1743,7 @@ function AskUserInteraction({
|
|
|
1401
1743
|
);
|
|
1402
1744
|
}),
|
|
1403
1745
|
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex min-h-11 items-center gap-2 rounded-xl bg-muted px-2.5 py-1.5 focus-within:ring-1 focus-within:ring-ring", children: [
|
|
1404
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "flex size-8 shrink-0 items-center justify-center rounded-full border border-border/70 bg-background/70 text-muted-foreground", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1746
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "flex size-8 shrink-0 items-center justify-center rounded-full border border-border/70 bg-background/70 text-muted-foreground", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_lucide_react4.Pencil, { "aria-hidden": "true", className: "size-4" }) }),
|
|
1405
1747
|
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1406
1748
|
"input",
|
|
1407
1749
|
{
|
|
@@ -1653,7 +1995,7 @@ function resolvedHitlLabel(part) {
|
|
|
1653
1995
|
|
|
1654
1996
|
// src/ui/tool-sessions.tsx
|
|
1655
1997
|
var React6 = __toESM(require("react"), 1);
|
|
1656
|
-
var
|
|
1998
|
+
var import_lucide_react5 = require("lucide-react");
|
|
1657
1999
|
var import_thinking_orbs = require("thinking-orbs");
|
|
1658
2000
|
var import_jsx_runtime8 = require("react/jsx-runtime");
|
|
1659
2001
|
function agentChatToolLabel(part) {
|
|
@@ -1821,7 +2163,7 @@ function AgentChatNodeActivityRow({
|
|
|
1821
2163
|
type: "button",
|
|
1822
2164
|
children: [
|
|
1823
2165
|
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "a24-tool-trigger__label", children: label }),
|
|
1824
|
-
open ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
2166
|
+
open ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_lucide_react5.ChevronDown, { "aria-hidden": "true", className: "a24-tool-trigger__chevron" }) : /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_lucide_react5.ChevronRight, { "aria-hidden": "true", className: "a24-tool-trigger__chevron" })
|
|
1825
2167
|
]
|
|
1826
2168
|
}
|
|
1827
2169
|
) }),
|
|
@@ -1849,10 +2191,9 @@ function ToolDisclosure({
|
|
|
1849
2191
|
const [open, setOpen] = React6.useState(false);
|
|
1850
2192
|
const contentId = React6.useId();
|
|
1851
2193
|
const expandable = React6.Children.toArray(children).length > 0;
|
|
1852
|
-
const textOnlyRoot = Boolean(root && !grouped && !showOrb);
|
|
1853
2194
|
if (!expandable) {
|
|
1854
2195
|
const triggerClassName = cn("a24-tool-trigger", root && "a24-tool-trigger--root", root && !showOrb && "a24-tool-trigger--without-orb", error && "a24-tool-trigger--error", loading && "a24-tool-trigger--active");
|
|
1855
|
-
return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className:
|
|
2196
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "a24-tool-trigger-row", children: [
|
|
1856
2197
|
onSelect && part ? /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("button", { className: triggerClassName, onClick: () => onSelect(part), type: "button", children: [
|
|
1857
2198
|
showOrb ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(ToolActivityOrb, { label: statusLabel }) : null,
|
|
1858
2199
|
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "a24-tool-trigger__label", children: label })
|
|
@@ -1864,7 +2205,7 @@ function ToolDisclosure({
|
|
|
1864
2205
|
] });
|
|
1865
2206
|
}
|
|
1866
2207
|
return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "a24-tool-disclosure", children: [
|
|
1867
|
-
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className:
|
|
2208
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "a24-tool-trigger-row", children: [
|
|
1868
2209
|
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
|
|
1869
2210
|
"button",
|
|
1870
2211
|
{
|
|
@@ -1881,7 +2222,7 @@ function ToolDisclosure({
|
|
|
1881
2222
|
children: [
|
|
1882
2223
|
showOrb ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(ToolActivityOrb, { label: statusLabel }) : null,
|
|
1883
2224
|
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "a24-tool-trigger__label", children: label }),
|
|
1884
|
-
open ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
2225
|
+
open ? /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_lucide_react5.ChevronDown, { "aria-hidden": "true", className: "a24-tool-trigger__chevron" }) : /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_lucide_react5.ChevronRight, { "aria-hidden": "true", className: "a24-tool-trigger__chevron" })
|
|
1885
2226
|
]
|
|
1886
2227
|
}
|
|
1887
2228
|
),
|
|
@@ -2342,7 +2683,7 @@ function AgentChatUserMessageContent({
|
|
|
2342
2683
|
const [isExpanded, setIsExpanded] = React8.useState(false);
|
|
2343
2684
|
const contentId = React8.useId();
|
|
2344
2685
|
const shouldCollapse = shouldCollapseUserMessage(content, collapseThreshold);
|
|
2345
|
-
const Icon = isExpanded ?
|
|
2686
|
+
const Icon = isExpanded ? import_lucide_react6.ChevronUp : import_lucide_react6.ChevronDown;
|
|
2346
2687
|
const clampedStyle = shouldCollapse && !isExpanded ? {
|
|
2347
2688
|
WebkitBoxOrient: "vertical",
|
|
2348
2689
|
WebkitLineClamp: Math.max(1, collapsedLines),
|
|
@@ -2370,11 +2711,47 @@ function AgentChatUserMessageContent({
|
|
|
2370
2711
|
) : null
|
|
2371
2712
|
] });
|
|
2372
2713
|
}
|
|
2714
|
+
function AgentChatUserMessageActionRow({
|
|
2715
|
+
children,
|
|
2716
|
+
className
|
|
2717
|
+
}) {
|
|
2718
|
+
const rowRef = React8.useRef(null);
|
|
2719
|
+
const [visible, setVisible] = React8.useState(false);
|
|
2720
|
+
React8.useEffect(() => {
|
|
2721
|
+
const message = rowRef.current?.closest('[data-slot="message"]');
|
|
2722
|
+
if (!message) return;
|
|
2723
|
+
const show = () => setVisible(true);
|
|
2724
|
+
const hide = () => setVisible(false);
|
|
2725
|
+
const hideWhenFocusLeaves = (event) => {
|
|
2726
|
+
if (!message.contains(event.relatedTarget)) hide();
|
|
2727
|
+
};
|
|
2728
|
+
message.addEventListener("mouseenter", show);
|
|
2729
|
+
message.addEventListener("mouseleave", hide);
|
|
2730
|
+
message.addEventListener("focusin", show);
|
|
2731
|
+
message.addEventListener("focusout", hideWhenFocusLeaves);
|
|
2732
|
+
return () => {
|
|
2733
|
+
message.removeEventListener("mouseenter", show);
|
|
2734
|
+
message.removeEventListener("mouseleave", hide);
|
|
2735
|
+
message.removeEventListener("focusin", show);
|
|
2736
|
+
message.removeEventListener("focusout", hideWhenFocusLeaves);
|
|
2737
|
+
};
|
|
2738
|
+
}, []);
|
|
2739
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
2740
|
+
"div",
|
|
2741
|
+
{
|
|
2742
|
+
ref: rowRef,
|
|
2743
|
+
className: cn("a24-chat-message-action-row--user", visible && "is-visible", className),
|
|
2744
|
+
style: { opacity: visible ? 1 : 0, pointerEvents: visible ? "auto" : "none" },
|
|
2745
|
+
children
|
|
2746
|
+
}
|
|
2747
|
+
);
|
|
2748
|
+
}
|
|
2373
2749
|
function AgentChatMessage({
|
|
2374
2750
|
actionHandlers,
|
|
2375
2751
|
additionalActions,
|
|
2376
2752
|
actions,
|
|
2377
2753
|
actionsClassName,
|
|
2754
|
+
attachmentContentResolver,
|
|
2378
2755
|
className,
|
|
2379
2756
|
contentClassName,
|
|
2380
2757
|
dir,
|
|
@@ -2407,48 +2784,58 @@ function AgentChatMessage({
|
|
|
2407
2784
|
message
|
|
2408
2785
|
}
|
|
2409
2786
|
) : null);
|
|
2410
|
-
|
|
2411
|
-
|
|
2412
|
-
|
|
2413
|
-
|
|
2414
|
-
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
|
|
2419
|
-
|
|
2420
|
-
|
|
2421
|
-
|
|
2422
|
-
|
|
2423
|
-
|
|
2424
|
-
|
|
2425
|
-
|
|
2426
|
-
|
|
2427
|
-
|
|
2428
|
-
|
|
2429
|
-
|
|
2430
|
-
|
|
2431
|
-
|
|
2432
|
-
|
|
2433
|
-
|
|
2434
|
-
|
|
2435
|
-
|
|
2436
|
-
|
|
2437
|
-
|
|
2438
|
-
|
|
2439
|
-
|
|
2440
|
-
|
|
2441
|
-
|
|
2442
|
-
|
|
2443
|
-
|
|
2444
|
-
|
|
2445
|
-
|
|
2446
|
-
|
|
2447
|
-
|
|
2448
|
-
|
|
2449
|
-
|
|
2450
|
-
|
|
2451
|
-
|
|
2787
|
+
const defaultUserActions = !actions && Boolean(actionHandlers) && !isAssistant;
|
|
2788
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
|
|
2789
|
+
ChatMessage,
|
|
2790
|
+
{
|
|
2791
|
+
className,
|
|
2792
|
+
dir,
|
|
2793
|
+
from: message.role,
|
|
2794
|
+
children: [
|
|
2795
|
+
showAttachments ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
2796
|
+
ChatAttachmentRows,
|
|
2797
|
+
{
|
|
2798
|
+
attachments: message.attachments || [],
|
|
2799
|
+
className: "mb-2",
|
|
2800
|
+
dir,
|
|
2801
|
+
resolveContent: attachmentContentResolver
|
|
2802
|
+
}
|
|
2803
|
+
) : null,
|
|
2804
|
+
shouldRenderContent ? /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
|
|
2805
|
+
ChatMessageContent,
|
|
2806
|
+
{
|
|
2807
|
+
className: cn(isAssistant ? "bg-transparent p-0" : void 0, contentClassName),
|
|
2808
|
+
dir,
|
|
2809
|
+
from: message.role,
|
|
2810
|
+
children: [
|
|
2811
|
+
isAssistant ? renderAssistantContent ? renderAssistantContent({ isStreaming, message }) : /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
2812
|
+
Timeline,
|
|
2813
|
+
{
|
|
2814
|
+
getHitlActionState,
|
|
2815
|
+
isLoading: isStreaming,
|
|
2816
|
+
message,
|
|
2817
|
+
nodeActivityMode,
|
|
2818
|
+
onHitlAction,
|
|
2819
|
+
parts: message.parts
|
|
2820
|
+
}
|
|
2821
|
+
) : null,
|
|
2822
|
+
!isAssistant ? renderUserContent ? renderUserContent({ message }) : resolvedUserContent ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
2823
|
+
AgentChatUserMessageContent,
|
|
2824
|
+
{
|
|
2825
|
+
collapsedLines: userMessageCollapsedLines,
|
|
2826
|
+
collapseThreshold: userMessageCollapseThreshold,
|
|
2827
|
+
content: resolvedUserContent,
|
|
2828
|
+
showLessLabel: userMessageShowLessLabel,
|
|
2829
|
+
showMoreLabel: userMessageShowMoreLabel
|
|
2830
|
+
}
|
|
2831
|
+
) : null : null
|
|
2832
|
+
]
|
|
2833
|
+
}
|
|
2834
|
+
) : null,
|
|
2835
|
+
renderedActions ? defaultUserActions ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(AgentChatUserMessageActionRow, { className: actionsClassName, children: renderedActions }) : /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: cn(dir === "rtl" ? "mt-1 h-9 w-full" : "mt-1 h-9 w-full -translate-x-2", actionsClassName), children: renderedActions }) : null
|
|
2836
|
+
]
|
|
2837
|
+
}
|
|
2838
|
+
);
|
|
2452
2839
|
}
|
|
2453
2840
|
|
|
2454
2841
|
// src/ui/agent-chat-context-status.tsx
|
|
@@ -2854,6 +3241,7 @@ function useAudioRecorder(onRecorded, options = {}) {
|
|
|
2854
3241
|
AgentChatToolPart,
|
|
2855
3242
|
AgentChatToolRow,
|
|
2856
3243
|
AgentChatToolSession,
|
|
3244
|
+
AgentChatUserMessageActionRow,
|
|
2857
3245
|
AgentChatUserMessageContent,
|
|
2858
3246
|
AgentResponseTimeline,
|
|
2859
3247
|
AskUserInteraction,
|
|
@@ -2871,6 +3259,7 @@ function useAudioRecorder(onRecorded, options = {}) {
|
|
|
2871
3259
|
BubbleGroup,
|
|
2872
3260
|
BubbleReactions,
|
|
2873
3261
|
ChatAttachment,
|
|
3262
|
+
ChatAttachmentRows,
|
|
2874
3263
|
ChatAttachments,
|
|
2875
3264
|
ChatMessage,
|
|
2876
3265
|
ChatMessageAction,
|
|
@@ -2884,6 +3273,7 @@ function useAudioRecorder(onRecorded, options = {}) {
|
|
|
2884
3273
|
MessageAction,
|
|
2885
3274
|
MessageActions,
|
|
2886
3275
|
MessageAttachment,
|
|
3276
|
+
MessageAttachmentRows,
|
|
2887
3277
|
MessageAttachments,
|
|
2888
3278
|
MessageAvatar,
|
|
2889
3279
|
MessageContent,
|
|
@@ -2899,6 +3289,7 @@ function useAudioRecorder(onRecorded, options = {}) {
|
|
|
2899
3289
|
bubbleVariants,
|
|
2900
3290
|
cn,
|
|
2901
3291
|
contextStatusMetrics,
|
|
3292
|
+
createAgentChatComposerFile,
|
|
2902
3293
|
hitlActionLabel,
|
|
2903
3294
|
hitlTitle,
|
|
2904
3295
|
isAgentChatSubagent,
|
|
@@ -2907,6 +3298,7 @@ function useAudioRecorder(onRecorded, options = {}) {
|
|
|
2907
3298
|
markerVariants,
|
|
2908
3299
|
partitionAgentChatToolTimeline,
|
|
2909
3300
|
resolvedHitlLabel,
|
|
3301
|
+
revokeAgentChatComposerFiles,
|
|
2910
3302
|
shouldCollapseUserMessage,
|
|
2911
3303
|
useAudioRecorder
|
|
2912
3304
|
});
|