@tangle-network/sandbox-ui 0.105.3 → 0.107.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chat.d.ts +1 -279
- package/dist/chat.js +3 -14
- package/dist/{chunk-XIXYQNXD.js → chunk-EKYA6AFY.js} +120 -613
- package/dist/globals.css +0 -91
- package/dist/index.d.ts +1 -1
- package/dist/index.js +16 -26
- package/dist/styles.css +0 -91
- package/package.json +1 -3
- package/dist/chunk-WGOOQAW3.js +0 -12
- package/dist/mention-editor-Q3TKSZDL.js +0 -490
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
import {
|
|
2
|
-
formatBytes
|
|
3
|
-
} from "./chunk-TK46XFLM.js";
|
|
4
1
|
import {
|
|
5
2
|
cn
|
|
6
3
|
} from "./chunk-EI44GEQ5.js";
|
|
@@ -145,504 +142,17 @@ function ReasoningLevelPicker({
|
|
|
145
142
|
] });
|
|
146
143
|
}
|
|
147
144
|
|
|
148
|
-
// src/chat/agent-composer.tsx
|
|
149
|
-
import {
|
|
150
|
-
ArrowUp,
|
|
151
|
-
Folder,
|
|
152
|
-
Loader2,
|
|
153
|
-
Paperclip,
|
|
154
|
-
RotateCcw,
|
|
155
|
-
Square,
|
|
156
|
-
Upload,
|
|
157
|
-
X
|
|
158
|
-
} from "lucide-react";
|
|
159
|
-
import * as React from "react";
|
|
160
|
-
|
|
161
|
-
// src/chat/attachment-validation.ts
|
|
162
|
-
function isAcceptedType(file, accept) {
|
|
163
|
-
if (!accept || accept.trim().length === 0) return true;
|
|
164
|
-
const patterns = accept.split(",").map((pattern) => pattern.trim()).filter((pattern) => pattern.length > 0);
|
|
165
|
-
if (patterns.length === 0) return true;
|
|
166
|
-
const name = file.name.toLowerCase();
|
|
167
|
-
const type = (file.type || "").toLowerCase();
|
|
168
|
-
return patterns.some((pattern) => {
|
|
169
|
-
const lowerPattern = pattern.toLowerCase();
|
|
170
|
-
if (lowerPattern.startsWith(".")) {
|
|
171
|
-
return name.endsWith(lowerPattern);
|
|
172
|
-
}
|
|
173
|
-
if (lowerPattern.endsWith("/*")) {
|
|
174
|
-
const prefix = lowerPattern.slice(0, -1);
|
|
175
|
-
return type.startsWith(prefix);
|
|
176
|
-
}
|
|
177
|
-
return type === lowerPattern;
|
|
178
|
-
});
|
|
179
|
-
}
|
|
180
|
-
function validateComposerFiles(files, config) {
|
|
181
|
-
const { maxSizeBytes, maxCount, currentCount = 0, accept } = config;
|
|
182
|
-
const list = Array.isArray(files) ? files : Array.from(files);
|
|
183
|
-
const accepted = [];
|
|
184
|
-
const rejected = [];
|
|
185
|
-
for (const file of list) {
|
|
186
|
-
if (!isAcceptedType(file, accept)) {
|
|
187
|
-
rejected.push({
|
|
188
|
-
file,
|
|
189
|
-
reason: `"${file.name}" is not an accepted file type (${accept}).`
|
|
190
|
-
});
|
|
191
|
-
continue;
|
|
192
|
-
}
|
|
193
|
-
if (maxSizeBytes !== void 0 && file.size > maxSizeBytes) {
|
|
194
|
-
rejected.push({
|
|
195
|
-
file,
|
|
196
|
-
reason: `"${file.name}" (${formatBytes(file.size)}) exceeds the ${formatBytes(maxSizeBytes)} limit.`
|
|
197
|
-
});
|
|
198
|
-
continue;
|
|
199
|
-
}
|
|
200
|
-
if (maxCount !== void 0 && currentCount + accepted.length >= maxCount) {
|
|
201
|
-
rejected.push({
|
|
202
|
-
file,
|
|
203
|
-
reason: `"${file.name}" was not added \u2014 the ${maxCount}-file limit is already reached.`
|
|
204
|
-
});
|
|
205
|
-
continue;
|
|
206
|
-
}
|
|
207
|
-
accepted.push(file);
|
|
208
|
-
}
|
|
209
|
-
return { accepted, rejected };
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
// src/chat/agent-composer.tsx
|
|
213
|
-
import { Fragment, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
214
|
-
var IMAGE_EXTENSION_BY_MIME = {
|
|
215
|
-
"image/png": "png",
|
|
216
|
-
"image/jpeg": "jpg",
|
|
217
|
-
"image/jpg": "jpg",
|
|
218
|
-
"image/gif": "gif",
|
|
219
|
-
"image/webp": "webp",
|
|
220
|
-
"image/bmp": "bmp",
|
|
221
|
-
"image/svg+xml": "svg"
|
|
222
|
-
};
|
|
223
|
-
function isGenericImageName(name) {
|
|
224
|
-
return /^(image)?(\.[a-z0-9]+)?$/i.test(name.trim());
|
|
225
|
-
}
|
|
226
|
-
function imageExtension(file) {
|
|
227
|
-
const fromMime = IMAGE_EXTENSION_BY_MIME[file.type.toLowerCase()];
|
|
228
|
-
if (fromMime) return fromMime;
|
|
229
|
-
const fromName = /\.([a-z0-9]+)$/i.exec(file.name)?.[1];
|
|
230
|
-
return fromName?.toLowerCase() ?? "png";
|
|
231
|
-
}
|
|
232
|
-
var MentionEditor = React.lazy(() => import("./mention-editor-Q3TKSZDL.js"));
|
|
233
|
-
function AgentComposer({
|
|
234
|
-
value,
|
|
235
|
-
onChange,
|
|
236
|
-
onSubmit,
|
|
237
|
-
placeholder = "Send a message\u2026",
|
|
238
|
-
disabled,
|
|
239
|
-
busy,
|
|
240
|
-
onCancel,
|
|
241
|
-
controls,
|
|
242
|
-
trailing,
|
|
243
|
-
contextItems = [],
|
|
244
|
-
onAttach,
|
|
245
|
-
onAttachFolder,
|
|
246
|
-
attachments = [],
|
|
247
|
-
onRemoveFile,
|
|
248
|
-
onRetryFile,
|
|
249
|
-
accept,
|
|
250
|
-
onRejectFiles,
|
|
251
|
-
dropTitle = "Drop files to add context",
|
|
252
|
-
dropDescription = "They attach to your next message.",
|
|
253
|
-
canSubmitWhileBusy = false,
|
|
254
|
-
canSubmitAttachmentsOnly = false,
|
|
255
|
-
mention,
|
|
256
|
-
focusShortcut = false,
|
|
257
|
-
minRows = 2,
|
|
258
|
-
maxHeight = 200,
|
|
259
|
-
className,
|
|
260
|
-
sendLabel = "Send",
|
|
261
|
-
autoFocus
|
|
262
|
-
}) {
|
|
263
|
-
const textareaRef = React.useRef(null);
|
|
264
|
-
const richFocusRef = React.useRef(null);
|
|
265
|
-
const fileInputRef = React.useRef(null);
|
|
266
|
-
const folderInputRef = React.useRef(null);
|
|
267
|
-
const [dragOver, setDragOver] = React.useState(false);
|
|
268
|
-
const dragDepth = React.useRef(0);
|
|
269
|
-
const pastedImageCount = React.useRef(0);
|
|
270
|
-
const resize = React.useCallback(
|
|
271
|
-
(el) => {
|
|
272
|
-
el.style.height = "auto";
|
|
273
|
-
el.style.height = `${Math.min(el.scrollHeight, maxHeight)}px`;
|
|
274
|
-
},
|
|
275
|
-
[maxHeight]
|
|
276
|
-
);
|
|
277
|
-
React.useEffect(() => {
|
|
278
|
-
if (textareaRef.current) resize(textareaRef.current);
|
|
279
|
-
}, [value, resize]);
|
|
280
|
-
React.useEffect(() => {
|
|
281
|
-
if (!focusShortcut || disabled) return;
|
|
282
|
-
function onKeyDown(event) {
|
|
283
|
-
if ((event.metaKey || event.ctrlKey) && event.key.toLowerCase() === "l") {
|
|
284
|
-
event.preventDefault();
|
|
285
|
-
if (mention) richFocusRef.current?.();
|
|
286
|
-
else textareaRef.current?.focus();
|
|
287
|
-
}
|
|
288
|
-
}
|
|
289
|
-
document.addEventListener("keydown", onKeyDown);
|
|
290
|
-
return () => document.removeEventListener("keydown", onKeyDown);
|
|
291
|
-
}, [focusShortcut, disabled, mention]);
|
|
292
|
-
const canSend = !disabled && (!busy || canSubmitWhileBusy) && (value.trim().length > 0 || canSubmitAttachmentsOnly && attachments.length > 0);
|
|
293
|
-
const submit = () => {
|
|
294
|
-
if (!canSend) return;
|
|
295
|
-
onSubmit();
|
|
296
|
-
};
|
|
297
|
-
const registerRichFocus = React.useCallback((focus) => {
|
|
298
|
-
richFocusRef.current = focus;
|
|
299
|
-
}, []);
|
|
300
|
-
const dropEnabled = Boolean(onAttach);
|
|
301
|
-
const handleDragEnter = (event) => {
|
|
302
|
-
if (!dropEnabled) return;
|
|
303
|
-
event.preventDefault();
|
|
304
|
-
event.stopPropagation();
|
|
305
|
-
dragDepth.current++;
|
|
306
|
-
if (event.dataTransfer?.types.includes("Files")) setDragOver(true);
|
|
307
|
-
};
|
|
308
|
-
const handleDragLeave = (event) => {
|
|
309
|
-
if (!dropEnabled) return;
|
|
310
|
-
event.preventDefault();
|
|
311
|
-
event.stopPropagation();
|
|
312
|
-
dragDepth.current--;
|
|
313
|
-
if (dragDepth.current <= 0) {
|
|
314
|
-
dragDepth.current = 0;
|
|
315
|
-
setDragOver(false);
|
|
316
|
-
}
|
|
317
|
-
};
|
|
318
|
-
const handleDragOver = (event) => {
|
|
319
|
-
if (!dropEnabled) return;
|
|
320
|
-
event.preventDefault();
|
|
321
|
-
event.stopPropagation();
|
|
322
|
-
if (event.dataTransfer) event.dataTransfer.dropEffect = "copy";
|
|
323
|
-
};
|
|
324
|
-
const deliverFiles = (files) => {
|
|
325
|
-
if (!onAttach || files.length === 0) return;
|
|
326
|
-
const { accepted, rejected } = validateComposerFiles(files, { accept });
|
|
327
|
-
if (rejected.length > 0) onRejectFiles?.(rejected);
|
|
328
|
-
if (accepted.length === 0) return;
|
|
329
|
-
const dt = new DataTransfer();
|
|
330
|
-
for (const file of accepted) dt.items.add(file);
|
|
331
|
-
onAttach(dt.files);
|
|
332
|
-
};
|
|
333
|
-
const handleDrop = (event) => {
|
|
334
|
-
if (!dropEnabled) return;
|
|
335
|
-
event.preventDefault();
|
|
336
|
-
event.stopPropagation();
|
|
337
|
-
dragDepth.current = 0;
|
|
338
|
-
setDragOver(false);
|
|
339
|
-
const files = event.dataTransfer?.files;
|
|
340
|
-
if (files?.length) deliverFiles(Array.from(files));
|
|
341
|
-
};
|
|
342
|
-
const renamePastedFiles = (files) => files.map((file) => {
|
|
343
|
-
if (file.type.startsWith("image/") && isGenericImageName(file.name)) {
|
|
344
|
-
pastedImageCount.current += 1;
|
|
345
|
-
return new File(
|
|
346
|
-
[file],
|
|
347
|
-
`pasted-image-${pastedImageCount.current}.${imageExtension(file)}`,
|
|
348
|
-
{ type: file.type }
|
|
349
|
-
);
|
|
350
|
-
}
|
|
351
|
-
return file;
|
|
352
|
-
});
|
|
353
|
-
const handlePaste = (event) => {
|
|
354
|
-
if (!onAttach) return;
|
|
355
|
-
const clipboardFiles = event.clipboardData?.files;
|
|
356
|
-
if (!clipboardFiles || clipboardFiles.length === 0) return;
|
|
357
|
-
event.preventDefault();
|
|
358
|
-
deliverFiles(renamePastedFiles(Array.from(clipboardFiles)));
|
|
359
|
-
};
|
|
360
|
-
const handleEditorPasteFiles = (clipboardFiles) => {
|
|
361
|
-
if (!onAttach || clipboardFiles.length === 0) return false;
|
|
362
|
-
deliverFiles(renamePastedFiles(Array.from(clipboardFiles)));
|
|
363
|
-
return true;
|
|
364
|
-
};
|
|
365
|
-
const folderChips = attachments.filter((file) => file.kind === "folder");
|
|
366
|
-
const fileChips = attachments.filter((file) => file.kind !== "folder");
|
|
367
|
-
const attachButtonClass = cn(
|
|
368
|
-
"flex size-8 shrink-0 items-center justify-center rounded-lg text-muted-foreground transition-colors",
|
|
369
|
-
"hover:bg-surface-container-highest hover:text-foreground focus:outline-none focus-visible:ring-1 focus-visible:ring-ring",
|
|
370
|
-
"disabled:cursor-not-allowed disabled:opacity-50"
|
|
371
|
-
);
|
|
372
|
-
return /* @__PURE__ */ jsxs2("div", { className: cn("relative", className), children: [
|
|
373
|
-
dragOver && /* @__PURE__ */ jsx2("div", { className: "pointer-events-none absolute inset-0 z-10 flex items-center justify-center rounded-2xl border-2 border-dashed border-primary/50 bg-surface-container-high/95", children: /* @__PURE__ */ jsxs2("div", { className: "text-center", children: [
|
|
374
|
-
/* @__PURE__ */ jsx2("span", { className: "mx-auto mb-2 flex size-11 items-center justify-center rounded-xl bg-primary/10 text-primary", children: /* @__PURE__ */ jsx2(Upload, { className: "size-5" }) }),
|
|
375
|
-
/* @__PURE__ */ jsx2("p", { className: "font-semibold text-foreground text-sm", children: dropTitle }),
|
|
376
|
-
/* @__PURE__ */ jsx2("p", { className: "mt-0.5 text-muted-foreground text-xs", children: dropDescription })
|
|
377
|
-
] }) }),
|
|
378
|
-
/* @__PURE__ */ jsxs2(
|
|
379
|
-
"div",
|
|
380
|
-
{
|
|
381
|
-
"data-testid": "agent-composer",
|
|
382
|
-
onDragEnter: handleDragEnter,
|
|
383
|
-
onDragLeave: handleDragLeave,
|
|
384
|
-
onDragOver: handleDragOver,
|
|
385
|
-
onDrop: handleDrop,
|
|
386
|
-
className: cn(
|
|
387
|
-
"flex flex-col gap-2 rounded-2xl border border-[var(--md3-outline-variant)] bg-surface-container-high p-2.5 shadow-sm transition-colors",
|
|
388
|
-
"focus-within:border-primary/60",
|
|
389
|
-
disabled && "opacity-60"
|
|
390
|
-
),
|
|
391
|
-
children: [
|
|
392
|
-
contextItems.length > 0 && /* @__PURE__ */ jsx2(
|
|
393
|
-
"div",
|
|
394
|
-
{
|
|
395
|
-
"aria-label": "Message context",
|
|
396
|
-
className: "flex min-w-0 flex-wrap gap-1.5",
|
|
397
|
-
children: contextItems.map((item) => /* @__PURE__ */ jsxs2(
|
|
398
|
-
"span",
|
|
399
|
-
{
|
|
400
|
-
className: "inline-flex min-w-0 max-w-full items-center gap-1.5 rounded-md border border-primary/30 bg-primary/10 px-2.5 py-1 text-primary text-xs",
|
|
401
|
-
children: [
|
|
402
|
-
item.icon && /* @__PURE__ */ jsx2("span", { className: "shrink-0", "aria-hidden": "true", children: item.icon }),
|
|
403
|
-
/* @__PURE__ */ jsx2("span", { className: "min-w-0 truncate", children: item.label }),
|
|
404
|
-
item.onRemove && /* @__PURE__ */ jsx2(
|
|
405
|
-
"button",
|
|
406
|
-
{
|
|
407
|
-
type: "button",
|
|
408
|
-
"aria-label": `Remove context ${item.label}`,
|
|
409
|
-
onClick: item.onRemove,
|
|
410
|
-
className: "shrink-0 rounded p-0.5 text-primary/70 transition-colors hover:text-primary focus:outline-none focus-visible:ring-1 focus-visible:ring-ring",
|
|
411
|
-
children: /* @__PURE__ */ jsx2(X, { className: "size-3" })
|
|
412
|
-
}
|
|
413
|
-
)
|
|
414
|
-
]
|
|
415
|
-
},
|
|
416
|
-
item.id
|
|
417
|
-
))
|
|
418
|
-
}
|
|
419
|
-
),
|
|
420
|
-
attachments.length > 0 && /* @__PURE__ */ jsx2("div", { className: "flex flex-wrap gap-1.5", children: [...folderChips, ...fileChips].map((file) => {
|
|
421
|
-
const isError = file.status === "error";
|
|
422
|
-
const isPending = file.status === "pending";
|
|
423
|
-
return /* @__PURE__ */ jsxs2(
|
|
424
|
-
"span",
|
|
425
|
-
{
|
|
426
|
-
title: isError ? file.errorMessage : void 0,
|
|
427
|
-
className: cn(
|
|
428
|
-
"inline-flex items-center gap-1.5 rounded-full border px-2.5 py-1 text-xs",
|
|
429
|
-
isError ? "border-[var(--status-error,#ff4d6d)]/40 text-[var(--status-error,#ff4d6d)]" : "border-[var(--md3-outline-variant)] bg-surface-container text-foreground",
|
|
430
|
-
isPending && "opacity-60"
|
|
431
|
-
),
|
|
432
|
-
children: [
|
|
433
|
-
file.kind !== "folder" && file.previewUrl ? /* @__PURE__ */ jsx2(
|
|
434
|
-
"img",
|
|
435
|
-
{
|
|
436
|
-
src: file.previewUrl,
|
|
437
|
-
alt: "",
|
|
438
|
-
className: "size-8 rounded object-cover"
|
|
439
|
-
}
|
|
440
|
-
) : file.kind === "folder" ? /* @__PURE__ */ jsx2(Folder, { className: "size-3 shrink-0" }) : /* @__PURE__ */ jsx2(Paperclip, { className: "size-3 shrink-0" }),
|
|
441
|
-
/* @__PURE__ */ jsx2("span", { className: "max-w-[150px] truncate", children: file.name }),
|
|
442
|
-
file.fileCount !== void 0 && /* @__PURE__ */ jsxs2("span", { className: "text-muted-foreground", children: [
|
|
443
|
-
"(",
|
|
444
|
-
file.fileCount,
|
|
445
|
-
")"
|
|
446
|
-
] }),
|
|
447
|
-
file.status === "uploading" && /* @__PURE__ */ jsx2(Loader2, { className: "size-3 animate-spin", "data-motion": "essential" }),
|
|
448
|
-
isError && file.errorMessage && /* @__PURE__ */ jsx2("span", { className: "max-w-[150px] truncate text-[var(--status-error,#ff4d6d)]/80", children: file.errorMessage }),
|
|
449
|
-
isError && onRetryFile && /* @__PURE__ */ jsx2(
|
|
450
|
-
"button",
|
|
451
|
-
{
|
|
452
|
-
type: "button",
|
|
453
|
-
"aria-label": `Retry upload ${file.name}`,
|
|
454
|
-
onClick: () => onRetryFile(file.id),
|
|
455
|
-
className: "rounded p-0.5 text-muted-foreground transition-colors hover:text-foreground focus:outline-none focus-visible:ring-1 focus-visible:ring-ring",
|
|
456
|
-
children: /* @__PURE__ */ jsx2(RotateCcw, { className: "size-3" })
|
|
457
|
-
}
|
|
458
|
-
),
|
|
459
|
-
onRemoveFile && /* @__PURE__ */ jsx2(
|
|
460
|
-
"button",
|
|
461
|
-
{
|
|
462
|
-
type: "button",
|
|
463
|
-
"aria-label": `Remove ${file.name}`,
|
|
464
|
-
onClick: () => onRemoveFile(file.id),
|
|
465
|
-
className: "rounded p-0.5 text-muted-foreground transition-colors hover:text-foreground focus:outline-none focus-visible:ring-1 focus-visible:ring-ring",
|
|
466
|
-
children: /* @__PURE__ */ jsx2(X, { className: "size-3" })
|
|
467
|
-
}
|
|
468
|
-
)
|
|
469
|
-
]
|
|
470
|
-
},
|
|
471
|
-
file.id
|
|
472
|
-
);
|
|
473
|
-
}) }),
|
|
474
|
-
mention ? /* @__PURE__ */ jsx2(
|
|
475
|
-
React.Suspense,
|
|
476
|
-
{
|
|
477
|
-
fallback: /* @__PURE__ */ jsx2(
|
|
478
|
-
"textarea",
|
|
479
|
-
{
|
|
480
|
-
rows: minRows,
|
|
481
|
-
value,
|
|
482
|
-
disabled: true,
|
|
483
|
-
placeholder,
|
|
484
|
-
"aria-label": "Message input",
|
|
485
|
-
className: cn(
|
|
486
|
-
"w-full resize-none bg-transparent px-2 py-1 text-sm leading-relaxed text-foreground outline-none",
|
|
487
|
-
"placeholder:text-muted-foreground"
|
|
488
|
-
)
|
|
489
|
-
}
|
|
490
|
-
),
|
|
491
|
-
children: /* @__PURE__ */ jsx2(
|
|
492
|
-
MentionEditor,
|
|
493
|
-
{
|
|
494
|
-
value,
|
|
495
|
-
onChange,
|
|
496
|
-
onSubmit: submit,
|
|
497
|
-
placeholder,
|
|
498
|
-
disabled,
|
|
499
|
-
autoFocus,
|
|
500
|
-
minRows,
|
|
501
|
-
maxHeight,
|
|
502
|
-
mention,
|
|
503
|
-
registerFocus: registerRichFocus,
|
|
504
|
-
onPasteFiles: onAttach ? handleEditorPasteFiles : void 0
|
|
505
|
-
}
|
|
506
|
-
)
|
|
507
|
-
}
|
|
508
|
-
) : /* @__PURE__ */ jsx2(
|
|
509
|
-
"textarea",
|
|
510
|
-
{
|
|
511
|
-
ref: textareaRef,
|
|
512
|
-
rows: minRows,
|
|
513
|
-
value,
|
|
514
|
-
disabled,
|
|
515
|
-
autoFocus,
|
|
516
|
-
onChange: (event) => onChange(event.target.value),
|
|
517
|
-
onInput: (event) => resize(event.currentTarget),
|
|
518
|
-
onPaste: handlePaste,
|
|
519
|
-
onKeyDown: (event) => {
|
|
520
|
-
if (event.key === "Enter" && !event.shiftKey && !event.nativeEvent.isComposing && event.keyCode !== 229) {
|
|
521
|
-
event.preventDefault();
|
|
522
|
-
submit();
|
|
523
|
-
}
|
|
524
|
-
},
|
|
525
|
-
placeholder,
|
|
526
|
-
"aria-label": "Message input",
|
|
527
|
-
className: cn(
|
|
528
|
-
"w-full resize-none bg-transparent px-2 py-1 text-sm leading-relaxed text-foreground outline-none",
|
|
529
|
-
"placeholder:text-muted-foreground"
|
|
530
|
-
)
|
|
531
|
-
}
|
|
532
|
-
),
|
|
533
|
-
/* @__PURE__ */ jsxs2("div", { className: "flex items-end gap-2", children: [
|
|
534
|
-
onAttach && /* @__PURE__ */ jsxs2(Fragment, { children: [
|
|
535
|
-
/* @__PURE__ */ jsx2(
|
|
536
|
-
"button",
|
|
537
|
-
{
|
|
538
|
-
type: "button",
|
|
539
|
-
onClick: () => fileInputRef.current?.click(),
|
|
540
|
-
disabled,
|
|
541
|
-
"aria-label": "Attach files",
|
|
542
|
-
title: "Attach files",
|
|
543
|
-
className: attachButtonClass,
|
|
544
|
-
children: /* @__PURE__ */ jsx2(Paperclip, { className: "size-4" })
|
|
545
|
-
}
|
|
546
|
-
),
|
|
547
|
-
/* @__PURE__ */ jsx2(
|
|
548
|
-
"input",
|
|
549
|
-
{
|
|
550
|
-
ref: fileInputRef,
|
|
551
|
-
type: "file",
|
|
552
|
-
multiple: true,
|
|
553
|
-
className: "hidden",
|
|
554
|
-
accept,
|
|
555
|
-
onChange: (event) => {
|
|
556
|
-
if (event.target.files?.length)
|
|
557
|
-
deliverFiles(Array.from(event.target.files));
|
|
558
|
-
event.target.value = "";
|
|
559
|
-
}
|
|
560
|
-
}
|
|
561
|
-
)
|
|
562
|
-
] }),
|
|
563
|
-
onAttachFolder && /* @__PURE__ */ jsxs2(Fragment, { children: [
|
|
564
|
-
/* @__PURE__ */ jsx2(
|
|
565
|
-
"button",
|
|
566
|
-
{
|
|
567
|
-
type: "button",
|
|
568
|
-
onClick: () => folderInputRef.current?.click(),
|
|
569
|
-
disabled,
|
|
570
|
-
"aria-label": "Attach folder",
|
|
571
|
-
title: "Attach folder",
|
|
572
|
-
className: attachButtonClass,
|
|
573
|
-
children: /* @__PURE__ */ jsx2(Folder, { className: "size-4" })
|
|
574
|
-
}
|
|
575
|
-
),
|
|
576
|
-
/* @__PURE__ */ jsx2(
|
|
577
|
-
"input",
|
|
578
|
-
{
|
|
579
|
-
ref: folderInputRef,
|
|
580
|
-
type: "file",
|
|
581
|
-
multiple: true,
|
|
582
|
-
className: "hidden",
|
|
583
|
-
onChange: (event) => {
|
|
584
|
-
if (event.target.files?.length)
|
|
585
|
-
(onAttachFolder ?? onAttach)?.(event.target.files);
|
|
586
|
-
event.target.value = "";
|
|
587
|
-
},
|
|
588
|
-
...{ webkitdirectory: "" }
|
|
589
|
-
}
|
|
590
|
-
)
|
|
591
|
-
] }),
|
|
592
|
-
/* @__PURE__ */ jsxs2("div", { className: "flex min-w-0 flex-1 items-center gap-2 overflow-x-auto overscroll-x-contain [-ms-overflow-style:none] [scrollbar-width:none] [&::-webkit-scrollbar]:hidden", children: [
|
|
593
|
-
controls,
|
|
594
|
-
trailing && /* @__PURE__ */ jsx2("div", { className: "ml-auto flex shrink-0 items-center gap-2", children: trailing })
|
|
595
|
-
] }),
|
|
596
|
-
busy && onCancel ? /* @__PURE__ */ jsx2(
|
|
597
|
-
"button",
|
|
598
|
-
{
|
|
599
|
-
type: "button",
|
|
600
|
-
onClick: onCancel,
|
|
601
|
-
"aria-label": "Stop response",
|
|
602
|
-
className: cn(
|
|
603
|
-
"flex size-9 shrink-0 items-center justify-center rounded-full",
|
|
604
|
-
"bg-[var(--status-error,#ff4d6d)]/15 text-[var(--status-error,#ff4d6d)] transition-colors",
|
|
605
|
-
"hover:bg-[var(--status-error,#ff4d6d)]/25"
|
|
606
|
-
),
|
|
607
|
-
children: /* @__PURE__ */ jsx2(Square, { className: "size-3.5 fill-current" })
|
|
608
|
-
}
|
|
609
|
-
) : /* @__PURE__ */ jsx2(
|
|
610
|
-
"button",
|
|
611
|
-
{
|
|
612
|
-
type: "button",
|
|
613
|
-
onClick: submit,
|
|
614
|
-
disabled: !canSend,
|
|
615
|
-
"aria-label": sendLabel,
|
|
616
|
-
className: cn(
|
|
617
|
-
"flex size-9 shrink-0 items-center justify-center rounded-full bg-primary text-primary-foreground",
|
|
618
|
-
"transition-opacity hover:opacity-90 disabled:opacity-40"
|
|
619
|
-
),
|
|
620
|
-
children: busy ? /* @__PURE__ */ jsx2(Loader2, { className: "size-4 animate-spin", "data-motion": "essential" }) : /* @__PURE__ */ jsx2(ArrowUp, { className: "size-4" })
|
|
621
|
-
}
|
|
622
|
-
)
|
|
623
|
-
] })
|
|
624
|
-
]
|
|
625
|
-
}
|
|
626
|
-
),
|
|
627
|
-
focusShortcut && /* @__PURE__ */ jsx2("div", { className: "mt-1.5 flex justify-end px-1", children: /* @__PURE__ */ jsxs2("span", { className: "text-muted-foreground text-xs", children: [
|
|
628
|
-
/* @__PURE__ */ jsx2("kbd", { className: "rounded border border-[var(--md3-outline-variant)] bg-surface-container px-1 py-0.5 text-[10px]", children: "Cmd" }),
|
|
629
|
-
/* @__PURE__ */ jsx2("kbd", { className: "ml-0.5 rounded border border-[var(--md3-outline-variant)] bg-surface-container px-1 py-0.5 text-[10px]", children: "L" }),
|
|
630
|
-
/* @__PURE__ */ jsx2("span", { className: "ml-1", children: "to focus" })
|
|
631
|
-
] }) })
|
|
632
|
-
] });
|
|
633
|
-
}
|
|
634
|
-
|
|
635
145
|
// src/chat/agent-profile-picker.tsx
|
|
636
146
|
import { Bot, Check, ChevronDown as ChevronDown2, Pencil, Plus as Plus2, Trash2 } from "lucide-react";
|
|
637
|
-
import * as
|
|
147
|
+
import * as React4 from "react";
|
|
638
148
|
|
|
639
149
|
// src/lib/use-click-outside.ts
|
|
640
|
-
import * as
|
|
150
|
+
import * as React from "react";
|
|
641
151
|
function useClickOutside(onOutside, extras) {
|
|
642
|
-
const ref =
|
|
643
|
-
const extrasRef =
|
|
152
|
+
const ref = React.useRef(null);
|
|
153
|
+
const extrasRef = React.useRef(extras);
|
|
644
154
|
extrasRef.current = extras;
|
|
645
|
-
|
|
155
|
+
React.useEffect(() => {
|
|
646
156
|
function handler(event) {
|
|
647
157
|
const target = event.target;
|
|
648
158
|
if (!ref.current || ref.current.contains(target)) return;
|
|
@@ -656,19 +166,19 @@ function useClickOutside(onOutside, extras) {
|
|
|
656
166
|
}
|
|
657
167
|
|
|
658
168
|
// src/chat/anchored-popover.tsx
|
|
659
|
-
import * as
|
|
169
|
+
import * as React2 from "react";
|
|
660
170
|
import { createPortal } from "react-dom";
|
|
661
|
-
import { jsx as
|
|
662
|
-
var AnchoredPopover =
|
|
171
|
+
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
172
|
+
var AnchoredPopover = React2.forwardRef(
|
|
663
173
|
function AnchoredPopover2({ anchorRef, side, children, className, style, ...rest }, forwardedRef) {
|
|
664
|
-
const innerRef =
|
|
174
|
+
const innerRef = React2.useRef(null);
|
|
665
175
|
const setRefs = (node) => {
|
|
666
176
|
innerRef.current = node;
|
|
667
177
|
if (typeof forwardedRef === "function") forwardedRef(node);
|
|
668
178
|
else if (forwardedRef) forwardedRef.current = node;
|
|
669
179
|
};
|
|
670
|
-
const [position, setPosition] =
|
|
671
|
-
const place =
|
|
180
|
+
const [position, setPosition] = React2.useState(null);
|
|
181
|
+
const place = React2.useCallback(() => {
|
|
672
182
|
const anchor = anchorRef.current;
|
|
673
183
|
if (!anchor) return;
|
|
674
184
|
const rect = anchor.getBoundingClientRect();
|
|
@@ -682,7 +192,7 @@ var AnchoredPopover = React3.forwardRef(
|
|
|
682
192
|
side === "top" ? { left, bottom: window.innerHeight - rect.top + margin } : { left, top: rect.bottom + margin }
|
|
683
193
|
);
|
|
684
194
|
}, [anchorRef, side]);
|
|
685
|
-
|
|
195
|
+
React2.useLayoutEffect(() => {
|
|
686
196
|
place();
|
|
687
197
|
window.addEventListener("resize", place);
|
|
688
198
|
window.addEventListener("scroll", place, true);
|
|
@@ -692,7 +202,7 @@ var AnchoredPopover = React3.forwardRef(
|
|
|
692
202
|
};
|
|
693
203
|
}, [place]);
|
|
694
204
|
return createPortal(
|
|
695
|
-
/* @__PURE__ */
|
|
205
|
+
/* @__PURE__ */ jsx2(
|
|
696
206
|
"div",
|
|
697
207
|
{
|
|
698
208
|
...rest,
|
|
@@ -713,8 +223,8 @@ var AnchoredPopover = React3.forwardRef(
|
|
|
713
223
|
|
|
714
224
|
// src/chat/informative-lock.tsx
|
|
715
225
|
import { Lock, Plus } from "lucide-react";
|
|
716
|
-
import * as
|
|
717
|
-
import { jsx as
|
|
226
|
+
import * as React3 from "react";
|
|
227
|
+
import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
718
228
|
function InformativeLock({
|
|
719
229
|
children,
|
|
720
230
|
ariaLabel,
|
|
@@ -727,22 +237,22 @@ function InformativeLock({
|
|
|
727
237
|
popoverClassName,
|
|
728
238
|
side = "top"
|
|
729
239
|
}) {
|
|
730
|
-
const [open, setOpen] =
|
|
731
|
-
const closeTimer =
|
|
732
|
-
const popoverRef =
|
|
240
|
+
const [open, setOpen] = React3.useState(false);
|
|
241
|
+
const closeTimer = React3.useRef(null);
|
|
242
|
+
const popoverRef = React3.useRef(null);
|
|
733
243
|
const ref = useClickOutside(() => setOpen(false), [popoverRef]);
|
|
734
|
-
const cancelClose =
|
|
244
|
+
const cancelClose = React3.useCallback(() => {
|
|
735
245
|
if (closeTimer.current) {
|
|
736
246
|
clearTimeout(closeTimer.current);
|
|
737
247
|
closeTimer.current = null;
|
|
738
248
|
}
|
|
739
249
|
}, []);
|
|
740
|
-
const scheduleClose =
|
|
250
|
+
const scheduleClose = React3.useCallback(() => {
|
|
741
251
|
cancelClose();
|
|
742
252
|
closeTimer.current = setTimeout(() => setOpen(false), 150);
|
|
743
253
|
}, [cancelClose]);
|
|
744
|
-
|
|
745
|
-
return /* @__PURE__ */
|
|
254
|
+
React3.useEffect(() => cancelClose, [cancelClose]);
|
|
255
|
+
return /* @__PURE__ */ jsxs2(
|
|
746
256
|
"div",
|
|
747
257
|
{
|
|
748
258
|
ref,
|
|
@@ -756,7 +266,7 @@ function InformativeLock({
|
|
|
756
266
|
if (event.key === "Escape") setOpen(false);
|
|
757
267
|
},
|
|
758
268
|
children: [
|
|
759
|
-
/* @__PURE__ */
|
|
269
|
+
/* @__PURE__ */ jsx3(
|
|
760
270
|
"button",
|
|
761
271
|
{
|
|
762
272
|
type: "button",
|
|
@@ -775,7 +285,7 @@ function InformativeLock({
|
|
|
775
285
|
children
|
|
776
286
|
}
|
|
777
287
|
),
|
|
778
|
-
open && /* @__PURE__ */
|
|
288
|
+
open && /* @__PURE__ */ jsxs2(
|
|
779
289
|
AnchoredPopover,
|
|
780
290
|
{
|
|
781
291
|
ref: popoverRef,
|
|
@@ -793,12 +303,12 @@ function InformativeLock({
|
|
|
793
303
|
popoverClassName
|
|
794
304
|
),
|
|
795
305
|
children: [
|
|
796
|
-
/* @__PURE__ */
|
|
797
|
-
/* @__PURE__ */
|
|
798
|
-
/* @__PURE__ */
|
|
306
|
+
/* @__PURE__ */ jsxs2("p", { className: "flex items-start gap-2 text-sm font-medium text-foreground", children: [
|
|
307
|
+
/* @__PURE__ */ jsx3(Lock, { className: "mt-0.5 h-3.5 w-3.5 shrink-0 text-muted-foreground" }),
|
|
308
|
+
/* @__PURE__ */ jsx3("span", { children: lockTitle })
|
|
799
309
|
] }),
|
|
800
|
-
/* @__PURE__ */
|
|
801
|
-
/* @__PURE__ */
|
|
310
|
+
/* @__PURE__ */ jsx3("p", { className: "mt-1.5 text-xs text-muted-foreground", children: lockBody }),
|
|
311
|
+
/* @__PURE__ */ jsxs2(
|
|
802
312
|
"button",
|
|
803
313
|
{
|
|
804
314
|
type: "button",
|
|
@@ -812,7 +322,7 @@ function InformativeLock({
|
|
|
812
322
|
"hover:bg-primary/15 focus:outline-none focus-visible:ring-1 focus-visible:ring-ring"
|
|
813
323
|
),
|
|
814
324
|
children: [
|
|
815
|
-
/* @__PURE__ */
|
|
325
|
+
/* @__PURE__ */ jsx3(Plus, { className: "h-4 w-4" }),
|
|
816
326
|
newChatLabel
|
|
817
327
|
]
|
|
818
328
|
}
|
|
@@ -826,7 +336,7 @@ function InformativeLock({
|
|
|
826
336
|
}
|
|
827
337
|
|
|
828
338
|
// src/chat/agent-profile-picker.tsx
|
|
829
|
-
import { jsx as
|
|
339
|
+
import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
830
340
|
function profileSubtitle(profile) {
|
|
831
341
|
if (profile.description) return profile.description;
|
|
832
342
|
const count = profile.capabilities?.length ?? 0;
|
|
@@ -849,11 +359,11 @@ function AgentProfilePicker({
|
|
|
849
359
|
popoverClassName,
|
|
850
360
|
side = "top"
|
|
851
361
|
}) {
|
|
852
|
-
const [open, setOpen] =
|
|
853
|
-
const [composing, setComposing] =
|
|
362
|
+
const [open, setOpen] = React4.useState(false);
|
|
363
|
+
const [composing, setComposing] = React4.useState(
|
|
854
364
|
null
|
|
855
365
|
);
|
|
856
|
-
const popoverRef =
|
|
366
|
+
const popoverRef = React4.useRef(null);
|
|
857
367
|
const ref = useClickOutside(() => {
|
|
858
368
|
setOpen(false);
|
|
859
369
|
setComposing(null);
|
|
@@ -863,7 +373,7 @@ function AgentProfilePicker({
|
|
|
863
373
|
const custom = profiles.filter((profile) => !profile.builtin);
|
|
864
374
|
const selected = profiles.find((profile) => profile.id === value);
|
|
865
375
|
const selectedName = selected?.name ?? value;
|
|
866
|
-
|
|
376
|
+
React4.useEffect(() => {
|
|
867
377
|
if (!locked) return;
|
|
868
378
|
setOpen(false);
|
|
869
379
|
setComposing(null);
|
|
@@ -878,7 +388,7 @@ function AgentProfilePicker({
|
|
|
878
388
|
setComposing(null);
|
|
879
389
|
}
|
|
880
390
|
if (locked && onNewChat) {
|
|
881
|
-
return /* @__PURE__ */
|
|
391
|
+
return /* @__PURE__ */ jsxs3(
|
|
882
392
|
InformativeLock,
|
|
883
393
|
{
|
|
884
394
|
ariaLabel: "Agent profile (locked)",
|
|
@@ -891,14 +401,14 @@ function AgentProfilePicker({
|
|
|
891
401
|
popoverClassName,
|
|
892
402
|
side,
|
|
893
403
|
children: [
|
|
894
|
-
/* @__PURE__ */
|
|
895
|
-
/* @__PURE__ */
|
|
404
|
+
/* @__PURE__ */ jsx4(Bot, { className: "h-3.5 w-3.5 text-muted-foreground" }),
|
|
405
|
+
/* @__PURE__ */ jsx4("span", { className: "max-w-[140px] truncate", children: selectedName })
|
|
896
406
|
]
|
|
897
407
|
}
|
|
898
408
|
);
|
|
899
409
|
}
|
|
900
|
-
return /* @__PURE__ */
|
|
901
|
-
/* @__PURE__ */
|
|
410
|
+
return /* @__PURE__ */ jsxs3("div", { ref, className: cn("relative inline-flex", className), children: [
|
|
411
|
+
/* @__PURE__ */ jsxs3(
|
|
902
412
|
"button",
|
|
903
413
|
{
|
|
904
414
|
type: "button",
|
|
@@ -916,13 +426,13 @@ function AgentProfilePicker({
|
|
|
916
426
|
"data-state": open ? "open" : "closed",
|
|
917
427
|
"aria-label": "Agent profile",
|
|
918
428
|
children: [
|
|
919
|
-
/* @__PURE__ */
|
|
920
|
-
/* @__PURE__ */
|
|
921
|
-
!locked && /* @__PURE__ */
|
|
429
|
+
/* @__PURE__ */ jsx4(Bot, { className: "h-3.5 w-3.5 text-muted-foreground" }),
|
|
430
|
+
/* @__PURE__ */ jsx4("span", { className: "max-w-[140px] truncate", children: selectedName }),
|
|
431
|
+
!locked && /* @__PURE__ */ jsx4(ChevronDown2, { className: "ml-auto h-3.5 w-3.5 shrink-0 text-muted-foreground" })
|
|
922
432
|
]
|
|
923
433
|
}
|
|
924
434
|
),
|
|
925
|
-
open && !locked && /* @__PURE__ */
|
|
435
|
+
open && !locked && /* @__PURE__ */ jsx4(
|
|
926
436
|
AnchoredPopover,
|
|
927
437
|
{
|
|
928
438
|
ref: popoverRef,
|
|
@@ -934,7 +444,7 @@ function AgentProfilePicker({
|
|
|
934
444
|
popoverClassName
|
|
935
445
|
),
|
|
936
446
|
"data-testid": "agent-profile-popover",
|
|
937
|
-
children: composing ? /* @__PURE__ */
|
|
447
|
+
children: composing ? /* @__PURE__ */ jsx4(
|
|
938
448
|
ProfileForm,
|
|
939
449
|
{
|
|
940
450
|
capabilities: capabilities ?? [],
|
|
@@ -942,9 +452,9 @@ function AgentProfilePicker({
|
|
|
942
452
|
onCancel: () => setComposing(null),
|
|
943
453
|
onSave: save
|
|
944
454
|
}
|
|
945
|
-
) : /* @__PURE__ */
|
|
946
|
-
/* @__PURE__ */
|
|
947
|
-
builtins.map((profile) => /* @__PURE__ */
|
|
455
|
+
) : /* @__PURE__ */ jsxs3("div", { className: "max-h-[min(60vh,420px)] overflow-y-auto", children: [
|
|
456
|
+
/* @__PURE__ */ jsx4(SectionHeader, { children: "Modes" }),
|
|
457
|
+
builtins.map((profile) => /* @__PURE__ */ jsx4(
|
|
948
458
|
ProfileRow,
|
|
949
459
|
{
|
|
950
460
|
active: profile.id === value,
|
|
@@ -954,8 +464,8 @@ function AgentProfilePicker({
|
|
|
954
464
|
},
|
|
955
465
|
profile.id
|
|
956
466
|
)),
|
|
957
|
-
custom.length > 0 && /* @__PURE__ */
|
|
958
|
-
custom.map((profile) => /* @__PURE__ */
|
|
467
|
+
custom.length > 0 && /* @__PURE__ */ jsx4(SectionHeader, { children: "Your agents" }),
|
|
468
|
+
custom.map((profile) => /* @__PURE__ */ jsx4(
|
|
959
469
|
ProfileRow,
|
|
960
470
|
{
|
|
961
471
|
active: profile.id === value,
|
|
@@ -967,14 +477,14 @@ function AgentProfilePicker({
|
|
|
967
477
|
},
|
|
968
478
|
profile.id
|
|
969
479
|
)),
|
|
970
|
-
canAuthor && /* @__PURE__ */
|
|
480
|
+
canAuthor && /* @__PURE__ */ jsxs3(
|
|
971
481
|
"button",
|
|
972
482
|
{
|
|
973
483
|
type: "button",
|
|
974
484
|
onClick: () => setComposing("new"),
|
|
975
485
|
className: "mt-1 flex w-full items-center gap-2 rounded-md px-2.5 py-2 text-left text-sm text-[var(--accent-text)] transition-colors hover:bg-accent/40",
|
|
976
486
|
children: [
|
|
977
|
-
/* @__PURE__ */
|
|
487
|
+
/* @__PURE__ */ jsx4(Plus2, { className: "h-4 w-4" }),
|
|
978
488
|
" New agent\u2026"
|
|
979
489
|
]
|
|
980
490
|
}
|
|
@@ -985,7 +495,7 @@ function AgentProfilePicker({
|
|
|
985
495
|
] });
|
|
986
496
|
}
|
|
987
497
|
function SectionHeader({ children }) {
|
|
988
|
-
return /* @__PURE__ */
|
|
498
|
+
return /* @__PURE__ */ jsx4("div", { className: "px-2.5 pb-1 pt-2 text-[10px] font-semibold uppercase tracking-wide text-muted-foreground/70", children });
|
|
989
499
|
}
|
|
990
500
|
function ProfileRow({
|
|
991
501
|
active,
|
|
@@ -995,7 +505,7 @@ function ProfileRow({
|
|
|
995
505
|
onEdit,
|
|
996
506
|
onDelete
|
|
997
507
|
}) {
|
|
998
|
-
return /* @__PURE__ */
|
|
508
|
+
return /* @__PURE__ */ jsxs3(
|
|
999
509
|
"div",
|
|
1000
510
|
{
|
|
1001
511
|
className: cn(
|
|
@@ -1003,40 +513,40 @@ function ProfileRow({
|
|
|
1003
513
|
active ? "bg-[var(--accent-surface-soft)] text-[var(--accent-text)]" : "hover:bg-accent/40"
|
|
1004
514
|
),
|
|
1005
515
|
children: [
|
|
1006
|
-
/* @__PURE__ */
|
|
516
|
+
/* @__PURE__ */ jsxs3(
|
|
1007
517
|
"button",
|
|
1008
518
|
{
|
|
1009
519
|
type: "button",
|
|
1010
520
|
onClick: onSelect,
|
|
1011
521
|
className: "flex min-w-0 flex-1 items-center gap-2 text-left",
|
|
1012
522
|
children: [
|
|
1013
|
-
/* @__PURE__ */
|
|
1014
|
-
/* @__PURE__ */
|
|
1015
|
-
/* @__PURE__ */
|
|
1016
|
-
/* @__PURE__ */
|
|
523
|
+
/* @__PURE__ */ jsx4("span", { className: "flex h-4 w-4 shrink-0 items-center justify-center", children: active && /* @__PURE__ */ jsx4(Check, { className: "h-3.5 w-3.5" }) }),
|
|
524
|
+
/* @__PURE__ */ jsxs3("span", { className: "min-w-0", children: [
|
|
525
|
+
/* @__PURE__ */ jsx4("span", { className: "block truncate font-medium", children: title }),
|
|
526
|
+
/* @__PURE__ */ jsx4("span", { className: "block truncate text-xs text-muted-foreground", children: subtitle })
|
|
1017
527
|
] })
|
|
1018
528
|
]
|
|
1019
529
|
}
|
|
1020
530
|
),
|
|
1021
|
-
(onEdit || onDelete) && /* @__PURE__ */
|
|
1022
|
-
onEdit && /* @__PURE__ */
|
|
531
|
+
(onEdit || onDelete) && /* @__PURE__ */ jsxs3("span", { className: "flex shrink-0 items-center gap-1 opacity-0 transition-opacity group-hover:opacity-100", children: [
|
|
532
|
+
onEdit && /* @__PURE__ */ jsx4(
|
|
1023
533
|
"button",
|
|
1024
534
|
{
|
|
1025
535
|
type: "button",
|
|
1026
536
|
onClick: onEdit,
|
|
1027
537
|
title: "Edit",
|
|
1028
538
|
className: "rounded p-1 text-muted-foreground hover:text-foreground",
|
|
1029
|
-
children: /* @__PURE__ */
|
|
539
|
+
children: /* @__PURE__ */ jsx4(Pencil, { className: "h-3 w-3" })
|
|
1030
540
|
}
|
|
1031
541
|
),
|
|
1032
|
-
onDelete && /* @__PURE__ */
|
|
542
|
+
onDelete && /* @__PURE__ */ jsx4(
|
|
1033
543
|
"button",
|
|
1034
544
|
{
|
|
1035
545
|
type: "button",
|
|
1036
546
|
onClick: onDelete,
|
|
1037
547
|
title: "Delete",
|
|
1038
548
|
className: "rounded p-1 text-muted-foreground hover:text-[var(--status-error,#ff4d6d)]",
|
|
1039
|
-
children: /* @__PURE__ */
|
|
549
|
+
children: /* @__PURE__ */ jsx4(Trash2, { className: "h-3 w-3" })
|
|
1040
550
|
}
|
|
1041
551
|
)
|
|
1042
552
|
] })
|
|
@@ -1050,16 +560,16 @@ function ProfileForm({
|
|
|
1050
560
|
onCancel,
|
|
1051
561
|
onSave
|
|
1052
562
|
}) {
|
|
1053
|
-
const [name, setName] =
|
|
1054
|
-
const [caps, setCaps] =
|
|
563
|
+
const [name, setName] = React4.useState(initial?.name ?? "");
|
|
564
|
+
const [caps, setCaps] = React4.useState(
|
|
1055
565
|
initial?.capabilities ? [...initial.capabilities] : capabilities.map((c) => c.id)
|
|
1056
566
|
);
|
|
1057
|
-
const [instructions, setInstructions] =
|
|
567
|
+
const [instructions, setInstructions] = React4.useState(initial?.instructions ?? "");
|
|
1058
568
|
const toggle = (id) => setCaps(
|
|
1059
569
|
(prev) => prev.includes(id) ? prev.filter((c) => c !== id) : [...prev, id]
|
|
1060
570
|
);
|
|
1061
|
-
return /* @__PURE__ */
|
|
1062
|
-
/* @__PURE__ */
|
|
571
|
+
return /* @__PURE__ */ jsxs3("div", { className: "space-y-3 p-2.5", children: [
|
|
572
|
+
/* @__PURE__ */ jsx4(
|
|
1063
573
|
"input",
|
|
1064
574
|
{
|
|
1065
575
|
autoFocus: true,
|
|
@@ -1069,14 +579,14 @@ function ProfileForm({
|
|
|
1069
579
|
className: "w-full rounded-lg border border-[var(--md3-outline-variant)] bg-surface-container px-3 py-2 text-sm outline-none focus:border-primary"
|
|
1070
580
|
}
|
|
1071
581
|
),
|
|
1072
|
-
capabilities.length > 0 && /* @__PURE__ */
|
|
1073
|
-
/* @__PURE__ */
|
|
1074
|
-
/* @__PURE__ */
|
|
582
|
+
capabilities.length > 0 && /* @__PURE__ */ jsxs3("div", { children: [
|
|
583
|
+
/* @__PURE__ */ jsx4("p", { className: "mb-1.5 text-[10px] font-semibold uppercase tracking-wide text-muted-foreground/70", children: "Tools" }),
|
|
584
|
+
/* @__PURE__ */ jsx4("div", { className: "space-y-0.5", children: capabilities.map((cap) => /* @__PURE__ */ jsxs3(
|
|
1075
585
|
"label",
|
|
1076
586
|
{
|
|
1077
587
|
className: "flex cursor-pointer items-start gap-2 rounded-md px-2 py-1.5 hover:bg-accent/40",
|
|
1078
588
|
children: [
|
|
1079
|
-
/* @__PURE__ */
|
|
589
|
+
/* @__PURE__ */ jsx4(
|
|
1080
590
|
"input",
|
|
1081
591
|
{
|
|
1082
592
|
type: "checkbox",
|
|
@@ -1085,16 +595,16 @@ function ProfileForm({
|
|
|
1085
595
|
className: "mt-0.5"
|
|
1086
596
|
}
|
|
1087
597
|
),
|
|
1088
|
-
/* @__PURE__ */
|
|
1089
|
-
/* @__PURE__ */
|
|
1090
|
-
cap.description && /* @__PURE__ */
|
|
598
|
+
/* @__PURE__ */ jsxs3("span", { className: "min-w-0", children: [
|
|
599
|
+
/* @__PURE__ */ jsx4("span", { className: "block text-sm font-medium", children: cap.label }),
|
|
600
|
+
cap.description && /* @__PURE__ */ jsx4("span", { className: "block text-xs text-muted-foreground", children: cap.description })
|
|
1091
601
|
] })
|
|
1092
602
|
]
|
|
1093
603
|
},
|
|
1094
604
|
cap.id
|
|
1095
605
|
)) })
|
|
1096
606
|
] }),
|
|
1097
|
-
/* @__PURE__ */
|
|
607
|
+
/* @__PURE__ */ jsx4(
|
|
1098
608
|
"textarea",
|
|
1099
609
|
{
|
|
1100
610
|
value: instructions,
|
|
@@ -1104,8 +614,8 @@ function ProfileForm({
|
|
|
1104
614
|
className: "w-full resize-none rounded-lg border border-[var(--md3-outline-variant)] bg-surface-container px-3 py-2 text-sm outline-none focus:border-primary"
|
|
1105
615
|
}
|
|
1106
616
|
),
|
|
1107
|
-
/* @__PURE__ */
|
|
1108
|
-
/* @__PURE__ */
|
|
617
|
+
/* @__PURE__ */ jsxs3("div", { className: "flex justify-end gap-2", children: [
|
|
618
|
+
/* @__PURE__ */ jsx4(
|
|
1109
619
|
"button",
|
|
1110
620
|
{
|
|
1111
621
|
type: "button",
|
|
@@ -1114,7 +624,7 @@ function ProfileForm({
|
|
|
1114
624
|
children: "Cancel"
|
|
1115
625
|
}
|
|
1116
626
|
),
|
|
1117
|
-
/* @__PURE__ */
|
|
627
|
+
/* @__PURE__ */ jsx4(
|
|
1118
628
|
"button",
|
|
1119
629
|
{
|
|
1120
630
|
type: "button",
|
|
@@ -1137,10 +647,10 @@ function ProfileForm({
|
|
|
1137
647
|
import { modelProvider, snapHarnessToModel } from "@tangle-network/agent-interface";
|
|
1138
648
|
|
|
1139
649
|
// src/chat/artifact-agent-dock.tsx
|
|
1140
|
-
import * as
|
|
1141
|
-
import { Sparkles as Sparkles2, Send, Square
|
|
650
|
+
import * as React5 from "react";
|
|
651
|
+
import { Sparkles as Sparkles2, Send, Square, X } from "lucide-react";
|
|
1142
652
|
import { ChatMessage } from "@tangle-network/ui/chat";
|
|
1143
|
-
import { jsx as
|
|
653
|
+
import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
1144
654
|
function defaultScopeLabel(scope) {
|
|
1145
655
|
if (scope.label) return scope.label;
|
|
1146
656
|
switch (scope.kind) {
|
|
@@ -1186,23 +696,23 @@ function ArtifactAgentDock({
|
|
|
1186
696
|
onError,
|
|
1187
697
|
className
|
|
1188
698
|
}) {
|
|
1189
|
-
const [threadId, setThreadId] =
|
|
1190
|
-
const [messages, setMessages] =
|
|
1191
|
-
const [composer, setComposer] =
|
|
1192
|
-
const [streamContent, setStreamContent] =
|
|
1193
|
-
const [sending, setSending] =
|
|
1194
|
-
const [loading, setLoading] =
|
|
1195
|
-
const abortRef =
|
|
1196
|
-
const scrollRef =
|
|
1197
|
-
const scopeTokenRef =
|
|
1198
|
-
const reportError =
|
|
699
|
+
const [threadId, setThreadId] = React5.useState(null);
|
|
700
|
+
const [messages, setMessages] = React5.useState([]);
|
|
701
|
+
const [composer, setComposer] = React5.useState(defaultPrompt ?? "");
|
|
702
|
+
const [streamContent, setStreamContent] = React5.useState("");
|
|
703
|
+
const [sending, setSending] = React5.useState(false);
|
|
704
|
+
const [loading, setLoading] = React5.useState(false);
|
|
705
|
+
const abortRef = React5.useRef(null);
|
|
706
|
+
const scrollRef = React5.useRef(null);
|
|
707
|
+
const scopeTokenRef = React5.useRef(0);
|
|
708
|
+
const reportError = React5.useCallback(
|
|
1199
709
|
(message) => {
|
|
1200
710
|
if (onError) onError(message);
|
|
1201
711
|
else if (typeof console !== "undefined") console.error("[ArtifactAgentDock]", message);
|
|
1202
712
|
},
|
|
1203
713
|
[onError]
|
|
1204
714
|
);
|
|
1205
|
-
|
|
715
|
+
React5.useEffect(() => {
|
|
1206
716
|
if (!open) return;
|
|
1207
717
|
scopeTokenRef.current += 1;
|
|
1208
718
|
abortRef.current?.abort();
|
|
@@ -1238,11 +748,11 @@ function ArtifactAgentDock({
|
|
|
1238
748
|
cancelled = true;
|
|
1239
749
|
};
|
|
1240
750
|
}, [open, scope.kind, scope.key, transport, reportError]);
|
|
1241
|
-
|
|
751
|
+
React5.useEffect(() => {
|
|
1242
752
|
if (!scrollRef.current) return;
|
|
1243
753
|
scrollRef.current.scrollTop = scrollRef.current.scrollHeight;
|
|
1244
754
|
}, [messages.length, streamContent]);
|
|
1245
|
-
const send =
|
|
755
|
+
const send = React5.useCallback(async () => {
|
|
1246
756
|
const text = composer.trim();
|
|
1247
757
|
if (!text || !threadId || sending) return;
|
|
1248
758
|
const token = scopeTokenRef.current;
|
|
@@ -1292,7 +802,7 @@ function ArtifactAgentDock({
|
|
|
1292
802
|
if (abortRef.current === controller) abortRef.current = null;
|
|
1293
803
|
}
|
|
1294
804
|
}, [composer, threadId, sending, transport, reportError]);
|
|
1295
|
-
const cancel =
|
|
805
|
+
const cancel = React5.useCallback(() => {
|
|
1296
806
|
if (threadId && transport.cancel) {
|
|
1297
807
|
try {
|
|
1298
808
|
void Promise.resolve(transport.cancel({ threadId })).catch(() => {
|
|
@@ -1306,7 +816,7 @@ function ArtifactAgentDock({
|
|
|
1306
816
|
}, [threadId, transport]);
|
|
1307
817
|
if (!open) return null;
|
|
1308
818
|
const heading = defaultScopeLabel(scope);
|
|
1309
|
-
return /* @__PURE__ */
|
|
819
|
+
return /* @__PURE__ */ jsxs4(
|
|
1310
820
|
"aside",
|
|
1311
821
|
{
|
|
1312
822
|
className: cn(
|
|
@@ -1315,38 +825,38 @@ function ArtifactAgentDock({
|
|
|
1315
825
|
),
|
|
1316
826
|
"aria-label": `Agent chat about ${heading}`,
|
|
1317
827
|
children: [
|
|
1318
|
-
/* @__PURE__ */
|
|
1319
|
-
/* @__PURE__ */
|
|
1320
|
-
/* @__PURE__ */
|
|
1321
|
-
/* @__PURE__ */
|
|
1322
|
-
/* @__PURE__ */
|
|
1323
|
-
/* @__PURE__ */
|
|
828
|
+
/* @__PURE__ */ jsxs4("header", { className: "flex items-center justify-between border-b border-[var(--md3-outline-variant)] bg-surface-container-low px-4 py-3", children: [
|
|
829
|
+
/* @__PURE__ */ jsxs4("div", { className: "flex min-w-0 items-center gap-2", children: [
|
|
830
|
+
/* @__PURE__ */ jsx5(Sparkles2, { className: "h-4 w-4 shrink-0 text-muted-foreground" }),
|
|
831
|
+
/* @__PURE__ */ jsxs4("div", { className: "min-w-0", children: [
|
|
832
|
+
/* @__PURE__ */ jsx5("div", { className: "text-[10px] font-medium uppercase tracking-wider text-muted-foreground", children: "Discussing" }),
|
|
833
|
+
/* @__PURE__ */ jsx5("div", { className: "truncate text-sm font-semibold text-foreground", children: heading })
|
|
1324
834
|
] })
|
|
1325
835
|
] }),
|
|
1326
|
-
/* @__PURE__ */
|
|
836
|
+
/* @__PURE__ */ jsx5(
|
|
1327
837
|
"button",
|
|
1328
838
|
{
|
|
1329
839
|
type: "button",
|
|
1330
840
|
onClick: () => onOpenChange(false),
|
|
1331
841
|
"aria-label": "Close artifact chat",
|
|
1332
842
|
className: "rounded-md p-1 text-muted-foreground transition-colors hover:bg-surface-container-high hover:text-foreground",
|
|
1333
|
-
children: /* @__PURE__ */
|
|
843
|
+
children: /* @__PURE__ */ jsx5(X, { className: "h-4 w-4" })
|
|
1334
844
|
}
|
|
1335
845
|
)
|
|
1336
846
|
] }),
|
|
1337
|
-
/* @__PURE__ */
|
|
1338
|
-
/* @__PURE__ */
|
|
1339
|
-
/* @__PURE__ */
|
|
847
|
+
/* @__PURE__ */ jsx5("div", { ref: scrollRef, className: "flex-1 overflow-y-auto px-3 py-4", children: loading ? /* @__PURE__ */ jsx5("div", { className: "px-3 py-8 text-center text-xs text-muted-foreground", children: "Loading conversation\u2026" }) : messages.length === 0 && !streamContent ? /* @__PURE__ */ jsxs4("div", { className: "px-3 py-12 text-center", children: [
|
|
848
|
+
/* @__PURE__ */ jsx5(Sparkles2, { className: "mx-auto mb-3 h-5 w-5 text-muted-foreground/40" }),
|
|
849
|
+
/* @__PURE__ */ jsxs4("p", { className: "text-sm text-foreground", children: [
|
|
1340
850
|
"Ask the agent about this ",
|
|
1341
851
|
scope.kind === "vault-file" ? "document" : scope.kind,
|
|
1342
852
|
"."
|
|
1343
853
|
] }),
|
|
1344
|
-
/* @__PURE__ */
|
|
1345
|
-
] }) : /* @__PURE__ */
|
|
1346
|
-
messages.map((m) => /* @__PURE__ */
|
|
1347
|
-
streamContent && /* @__PURE__ */
|
|
854
|
+
/* @__PURE__ */ jsx5("p", { className: "mt-1 text-xs text-muted-foreground", children: "It can read the current contents and propose edits, rewrites, or follow-ups." })
|
|
855
|
+
] }) : /* @__PURE__ */ jsxs4("div", { className: "space-y-3", children: [
|
|
856
|
+
messages.map((m) => /* @__PURE__ */ jsx5(ChatMessage, { role: m.role, content: m.content, timestamp: m.createdAt }, m.id)),
|
|
857
|
+
streamContent && /* @__PURE__ */ jsx5(ChatMessage, { role: "assistant", content: streamContent, isStreaming: true })
|
|
1348
858
|
] }) }),
|
|
1349
|
-
/* @__PURE__ */
|
|
859
|
+
/* @__PURE__ */ jsx5(
|
|
1350
860
|
"form",
|
|
1351
861
|
{
|
|
1352
862
|
className: "border-t border-[var(--md3-outline-variant)] bg-surface-container-low p-3",
|
|
@@ -1354,8 +864,8 @@ function ArtifactAgentDock({
|
|
|
1354
864
|
e.preventDefault();
|
|
1355
865
|
void send();
|
|
1356
866
|
},
|
|
1357
|
-
children: /* @__PURE__ */
|
|
1358
|
-
/* @__PURE__ */
|
|
867
|
+
children: /* @__PURE__ */ jsxs4("div", { className: "flex items-end gap-2", children: [
|
|
868
|
+
/* @__PURE__ */ jsx5(
|
|
1359
869
|
"textarea",
|
|
1360
870
|
{
|
|
1361
871
|
value: composer,
|
|
@@ -1372,23 +882,23 @@ function ArtifactAgentDock({
|
|
|
1372
882
|
className: "min-h-[44px] flex-1 resize-none rounded-[20px] border border-[var(--md3-outline-variant)] bg-surface-dim px-3.5 py-2 text-sm text-foreground placeholder:text-muted-foreground transition-colors focus:outline-none focus:border-[var(--border-accent)] disabled:opacity-50"
|
|
1373
883
|
}
|
|
1374
884
|
),
|
|
1375
|
-
sending ? /* @__PURE__ */
|
|
885
|
+
sending ? /* @__PURE__ */ jsx5(
|
|
1376
886
|
"button",
|
|
1377
887
|
{
|
|
1378
888
|
type: "button",
|
|
1379
889
|
onClick: cancel,
|
|
1380
890
|
"aria-label": "Cancel",
|
|
1381
891
|
className: "inline-flex h-9 w-9 items-center justify-center rounded-md text-muted-foreground transition-colors hover:bg-surface-container-high hover:text-foreground",
|
|
1382
|
-
children: /* @__PURE__ */
|
|
892
|
+
children: /* @__PURE__ */ jsx5(Square, { className: "h-4 w-4" })
|
|
1383
893
|
}
|
|
1384
|
-
) : /* @__PURE__ */
|
|
894
|
+
) : /* @__PURE__ */ jsx5(
|
|
1385
895
|
"button",
|
|
1386
896
|
{
|
|
1387
897
|
type: "submit",
|
|
1388
898
|
disabled: !composer.trim() || !threadId,
|
|
1389
899
|
"aria-label": "Send",
|
|
1390
900
|
className: "inline-flex h-9 w-9 items-center justify-center rounded-full bg-primary text-primary-foreground transition-colors hover:bg-primary/90 disabled:opacity-50",
|
|
1391
|
-
children: /* @__PURE__ */
|
|
901
|
+
children: /* @__PURE__ */ jsx5(Send, { className: "h-4 w-4" })
|
|
1392
902
|
}
|
|
1393
903
|
)
|
|
1394
904
|
] })
|
|
@@ -1478,9 +988,6 @@ export {
|
|
|
1478
988
|
DEFAULT_REASONING_LEVEL_OPTIONS,
|
|
1479
989
|
HARNESS_REASONING_OPTIONS,
|
|
1480
990
|
ReasoningLevelPicker,
|
|
1481
|
-
isAcceptedType,
|
|
1482
|
-
validateComposerFiles,
|
|
1483
|
-
AgentComposer,
|
|
1484
991
|
AgentProfilePicker,
|
|
1485
992
|
modelProvider,
|
|
1486
993
|
snapHarnessToModel,
|