@volter-ai-dev/supercode-ui 0.1.21 → 0.1.23
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/README.md +8 -2
- package/components.d.ts +2 -0
- package/components.mjs +261 -125
- package/composer.mjs +49 -26
- package/conversation.d.ts +2 -0
- package/conversation.mjs +136 -44
- package/embed.mjs +259 -125
- package/index.d.ts +2 -0
- package/messenger.mjs +259 -125
- package/package.json +1 -1
- package/sessions.mjs +14 -13
- package/styles.css +3 -1
package/composer.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// src/composer.jsx
|
|
2
|
-
import { useEffect, useRef, useState } from "preact/hooks";
|
|
2
|
+
import { useEffect as useEffect2, useRef as useRef2, useState as useState2 } from "preact/hooks";
|
|
3
3
|
|
|
4
4
|
// core.mjs
|
|
5
5
|
var HARNESS_NAMES = Object.freeze({
|
|
@@ -129,7 +129,8 @@ function UiIcon({ name, size = 16, class: className = "" }) {
|
|
|
129
129
|
}
|
|
130
130
|
|
|
131
131
|
// src/context.jsx
|
|
132
|
-
import {
|
|
132
|
+
import { useEffect, useRef, useState } from "preact/hooks";
|
|
133
|
+
import { Fragment as Fragment2, jsx as jsx2, jsxs as jsxs2 } from "preact/jsx-runtime";
|
|
133
134
|
var MAX_CONTEXT_ITEMS = 32;
|
|
134
135
|
var MAX_IMAGE_ITEMS = 4;
|
|
135
136
|
var MAX_IMAGE_BYTES = 5 * 1024 * 1024;
|
|
@@ -268,17 +269,18 @@ function ContinuationBar({ state, adapter, labels = DEFAULT_LABELS }) {
|
|
|
268
269
|
}
|
|
269
270
|
function Composer({ state, adapter, labels = DEFAULT_LABELS, memoryKey = state.attached?.key || `${state.harness}:${state.workspace}`, pendingStatus = null, restoreDraft = null, onPending, onDraftRestored }) {
|
|
270
271
|
const remembered = composerMemory.get(memoryKey) ?? { draft: state.savedDraft, context: [], images: [], queue: [] };
|
|
271
|
-
const [draft, setDraft] =
|
|
272
|
-
const [context, setContext] =
|
|
273
|
-
const [images, setImages] =
|
|
274
|
-
const [queue, setQueue] =
|
|
275
|
-
const [dispatching, setDispatching] =
|
|
276
|
-
const [picking, setPicking] =
|
|
277
|
-
const [
|
|
278
|
-
const
|
|
272
|
+
const [draft, setDraft] = useState2(remembered.draft);
|
|
273
|
+
const [context, setContext] = useState2(remembered.context ?? []);
|
|
274
|
+
const [images, setImages] = useState2(remembered.images ?? []);
|
|
275
|
+
const [queue, setQueue] = useState2((remembered.queue ?? []).map((item) => ({ ...item, context: item.context ?? [], images: item.images ?? [] })));
|
|
276
|
+
const [dispatching, setDispatching] = useState2(false);
|
|
277
|
+
const [picking, setPicking] = useState2(false);
|
|
278
|
+
const [dragging, setDragging] = useState2(false);
|
|
279
|
+
const [pickerError, setPickerError] = useState2(null);
|
|
280
|
+
const textarea = useRef2(null);
|
|
279
281
|
useAutosizeTextarea(textarea, draft);
|
|
280
282
|
const remember = (nextDraft, nextContext, nextImages, nextQueue) => boundedSet(composerMemory, memoryKey, { draft: nextDraft, context: nextContext, images: nextImages, queue: nextQueue });
|
|
281
|
-
|
|
283
|
+
useEffect2(() => {
|
|
282
284
|
remember(draft, context, images, queue);
|
|
283
285
|
}, [draft, context, images, memoryKey, queue]);
|
|
284
286
|
const updateQueue = (update) => setQueue((items) => {
|
|
@@ -288,7 +290,7 @@ function Composer({ state, adapter, labels = DEFAULT_LABELS, memoryKey = state.a
|
|
|
288
290
|
});
|
|
289
291
|
const queueBlocked = state.busy || pendingStatus !== null || dispatching;
|
|
290
292
|
const queuesNewMessage = state.busy || pendingStatus === "sending" || pendingStatus === "failed" || dispatching;
|
|
291
|
-
|
|
293
|
+
useEffect2(() => {
|
|
292
294
|
if (!queueBlocked && state.canSend && queue.length) {
|
|
293
295
|
const [next, ...rest] = queue;
|
|
294
296
|
setDispatching(true);
|
|
@@ -298,13 +300,13 @@ function Composer({ state, adapter, labels = DEFAULT_LABELS, memoryKey = state.a
|
|
|
298
300
|
adapter.onIntent({ action: "send", text: next.text, ...next.context.length ? { context: next.context } : {}, ...next.images.length ? { images: next.images } : {} });
|
|
299
301
|
}
|
|
300
302
|
}, [adapter, draft, memoryKey, onPending, queue, queueBlocked, state.canSend]);
|
|
301
|
-
|
|
303
|
+
useEffect2(() => {
|
|
302
304
|
if (pendingStatus !== null || state.busy) setDispatching(false);
|
|
303
305
|
}, [pendingStatus, state.busy]);
|
|
304
|
-
|
|
306
|
+
useEffect2(() => {
|
|
305
307
|
textarea.current?.focus({ preventScroll: true });
|
|
306
308
|
}, [memoryKey]);
|
|
307
|
-
|
|
309
|
+
useEffect2(() => {
|
|
308
310
|
if (!restoreDraft) return;
|
|
309
311
|
setDraft(restoreDraft.text);
|
|
310
312
|
const restoredContext = normalizeContext(restoreDraft.context);
|
|
@@ -315,7 +317,7 @@ function Composer({ state, adapter, labels = DEFAULT_LABELS, memoryKey = state.a
|
|
|
315
317
|
textarea.current?.focus({ preventScroll: true });
|
|
316
318
|
onDraftRestored?.(restoreDraft.id);
|
|
317
319
|
}, [onDraftRestored, restoreDraft?.id]);
|
|
318
|
-
|
|
320
|
+
useEffect2(() => {
|
|
319
321
|
const timer = setTimeout(() => adapter.onIntent({ action: "draft", text: draft }), 250);
|
|
320
322
|
return () => clearTimeout(timer);
|
|
321
323
|
}, [adapter, draft]);
|
|
@@ -339,13 +341,17 @@ function Composer({ state, adapter, labels = DEFAULT_LABELS, memoryKey = state.a
|
|
|
339
341
|
});
|
|
340
342
|
}).catch((error) => setPickerError(error instanceof Error ? error.message : "Could not attach context.")).finally(() => setPicking(false));
|
|
341
343
|
};
|
|
342
|
-
const
|
|
343
|
-
const
|
|
344
|
-
if (!
|
|
345
|
-
|
|
344
|
+
const addImageFiles = (value, source) => {
|
|
345
|
+
const allFiles = Array.from(value ?? []);
|
|
346
|
+
if (!allFiles.length) return false;
|
|
347
|
+
const files = allFiles.filter((file) => file.type.startsWith("image/"));
|
|
348
|
+
if (files.length !== allFiles.length) {
|
|
349
|
+
setPickerError("Drop or paste PNG, JPEG, GIF, or WebP images.");
|
|
350
|
+
return true;
|
|
351
|
+
}
|
|
346
352
|
if (images.length + files.length > MAX_IMAGE_ITEMS) {
|
|
347
353
|
setPickerError(`Attach at most ${MAX_IMAGE_ITEMS} images.`);
|
|
348
|
-
return;
|
|
354
|
+
return true;
|
|
349
355
|
}
|
|
350
356
|
setPicking(true);
|
|
351
357
|
setPickerError(null);
|
|
@@ -353,11 +359,19 @@ function Composer({ state, adapter, labels = DEFAULT_LABELS, memoryKey = state.a
|
|
|
353
359
|
const next = mergeImages(current, picked);
|
|
354
360
|
remember(draft, context, next, queue);
|
|
355
361
|
return next;
|
|
356
|
-
}), (error) => setPickerError(error instanceof Error ? error.message :
|
|
362
|
+
}), (error) => setPickerError(error instanceof Error ? error.message : `Could not ${source} image.`)).finally(() => setPicking(false));
|
|
363
|
+
return true;
|
|
364
|
+
};
|
|
365
|
+
const pasteImages = (event) => {
|
|
366
|
+
if (addImageFiles(event.clipboardData?.files, "paste")) event.preventDefault();
|
|
367
|
+
};
|
|
368
|
+
const dropImages = (event) => {
|
|
369
|
+
setDragging(false);
|
|
370
|
+
if (addImageFiles(event.dataTransfer?.files, "drop")) event.preventDefault();
|
|
357
371
|
};
|
|
358
372
|
const send = () => {
|
|
359
373
|
const text = draft.trim();
|
|
360
|
-
if (!text) return;
|
|
374
|
+
if (!text && !images.length) return;
|
|
361
375
|
const message = { text, context, images };
|
|
362
376
|
if (queuesNewMessage) updateQueue((items) => [...items, message]);
|
|
363
377
|
else if (state.canSend) {
|
|
@@ -378,7 +392,7 @@ function Composer({ state, adapter, labels = DEFAULT_LABELS, memoryKey = state.a
|
|
|
378
392
|
] }),
|
|
379
393
|
queue.map((item, index) => /* @__PURE__ */ jsxs3("span", { children: [
|
|
380
394
|
/* @__PURE__ */ jsxs3("span", { children: [
|
|
381
|
-
item.text,
|
|
395
|
+
item.text || "Image attachment",
|
|
382
396
|
item.context.length + item.images.length ? /* @__PURE__ */ jsxs3("small", { children: [
|
|
383
397
|
item.context.length + item.images.length,
|
|
384
398
|
" attached"
|
|
@@ -398,7 +412,16 @@ function Composer({ state, adapter, labels = DEFAULT_LABELS, memoryKey = state.a
|
|
|
398
412
|
return next;
|
|
399
413
|
}) }),
|
|
400
414
|
pickerError ? /* @__PURE__ */ jsx3("small", { class: "scui-context-error", role: "alert", children: pickerError }) : null,
|
|
401
|
-
/* @__PURE__ */ jsxs3("div", { class:
|
|
415
|
+
/* @__PURE__ */ jsxs3("div", { class: `scui-envelope${dragging ? " scui-drop-target" : ""}`, onDragEnter: (event) => {
|
|
416
|
+
if (Array.from(event.dataTransfer?.types ?? []).includes("Files")) {
|
|
417
|
+
event.preventDefault();
|
|
418
|
+
setDragging(true);
|
|
419
|
+
}
|
|
420
|
+
}, onDragOver: (event) => {
|
|
421
|
+
if (Array.from(event.dataTransfer?.types ?? []).includes("Files")) event.preventDefault();
|
|
422
|
+
}, onDragLeave: (event) => {
|
|
423
|
+
if (!event.currentTarget.contains(event.relatedTarget)) setDragging(false);
|
|
424
|
+
}, onDrop: dropImages, children: [
|
|
402
425
|
adapter.pickContext && state.mode === "control" ? /* @__PURE__ */ jsx3("button", { class: "scui-attach", type: "button", "aria-label": "Attach files or images", disabled: picking || context.length >= MAX_CONTEXT_ITEMS && images.length >= MAX_IMAGE_ITEMS, onClick: pickContext, children: picking ? /* @__PURE__ */ jsx3("i", { class: "scui-control-spinner" }) : /* @__PURE__ */ jsx3(UiIcon, { name: "attach", size: 17 }) }) : null,
|
|
403
426
|
/* @__PURE__ */ jsx3("textarea", { ref: textarea, rows: 1, "aria-label": `Message ${harnessDisplayName(state.harness) || "agent"}`, placeholder: state.startup !== "ready" ? "Connecting\u2026" : pendingStatus === "failed" ? "Retry or edit the unsent message\u2026" : pendingStatus === "editing" ? "Edit and resend\u2026" : queueBlocked ? "Queue a follow-up\u2026" : labels.askAgent, value: draft, disabled: state.mode !== "control" && !state.canSend, onPaste: pasteImages, onInput: (event) => {
|
|
404
427
|
const value = event.currentTarget.value;
|
|
@@ -412,7 +435,7 @@ function Composer({ state, adapter, labels = DEFAULT_LABELS, memoryKey = state.a
|
|
|
412
435
|
} }),
|
|
413
436
|
/* @__PURE__ */ jsxs3("span", { children: [
|
|
414
437
|
state.busy ? /* @__PURE__ */ jsx3("button", { class: "scui-stop", type: "button", "aria-label": "Stop agent", disabled: !state.canInterrupt, onClick: () => adapter.onIntent({ action: "interrupt" }), children: /* @__PURE__ */ jsx3(UiIcon, { name: "stop", size: 15 }) }) : null,
|
|
415
|
-
/* @__PURE__ */ jsx3("button", { class: "scui-send", type: "button", "aria-label": queuesNewMessage ? "Queue message" : "Send message", disabled: !draft.trim() || !queuesNewMessage && !state.canSend, onClick: send, children: /* @__PURE__ */ jsx3(UiIcon, { name: queuesNewMessage ? "plus" : "send", size: 17 }) })
|
|
438
|
+
/* @__PURE__ */ jsx3("button", { class: "scui-send", type: "button", "aria-label": queuesNewMessage ? "Queue message" : "Send message", disabled: !draft.trim() && !images.length || !queuesNewMessage && !state.canSend, onClick: send, children: /* @__PURE__ */ jsx3(UiIcon, { name: queuesNewMessage ? "plus" : "send", size: 17 }) })
|
|
416
439
|
] })
|
|
417
440
|
] })
|
|
418
441
|
] });
|
package/conversation.d.ts
CHANGED
package/conversation.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// src/conversation.jsx
|
|
2
|
-
import { Fragment as
|
|
3
|
-
import { useEffect as
|
|
2
|
+
import { Fragment as Fragment3 } from "preact";
|
|
3
|
+
import { useEffect as useEffect3, useId, useLayoutEffect, useRef as useRef3, useState as useState2 } from "preact/hooks";
|
|
4
4
|
|
|
5
5
|
// core.mjs
|
|
6
6
|
var HARNESS_NAMES = Object.freeze({
|
|
@@ -622,18 +622,108 @@ function UiIcon({ name, size = 16, class: className = "" }) {
|
|
|
622
622
|
}
|
|
623
623
|
|
|
624
624
|
// src/context.jsx
|
|
625
|
-
import {
|
|
625
|
+
import { useEffect as useEffect2, useRef as useRef2, useState } from "preact/hooks";
|
|
626
|
+
import { Fragment as Fragment2, jsx as jsx3, jsxs as jsxs2 } from "preact/jsx-runtime";
|
|
626
627
|
var MAX_IMAGE_BYTES = 5 * 1024 * 1024;
|
|
627
|
-
function
|
|
628
|
+
function imageFilename(label) {
|
|
629
|
+
const value = label.trim().replace(/[\\/:*?"<>|]+/g, "-");
|
|
630
|
+
return value || "image";
|
|
631
|
+
}
|
|
632
|
+
function ImageViewer({ items, index, adapter, onChange, onClose }) {
|
|
633
|
+
const dialog = useRef2(null);
|
|
634
|
+
const reset = useRef2(null);
|
|
635
|
+
const [copyState, setCopyState2] = useState("idle");
|
|
636
|
+
const item = items[index];
|
|
637
|
+
const remote = item?.url?.startsWith("http://") || item?.url?.startsWith("https://");
|
|
638
|
+
useEffect2(() => {
|
|
639
|
+
if (!dialog.current?.open) dialog.current?.showModal();
|
|
640
|
+
return () => clearTimeout(reset.current);
|
|
641
|
+
}, []);
|
|
642
|
+
useEffect2(() => {
|
|
643
|
+
clearTimeout(reset.current);
|
|
644
|
+
setCopyState2("idle");
|
|
645
|
+
}, [index]);
|
|
646
|
+
if (!item?.url) return null;
|
|
647
|
+
const move = (amount) => onChange((index + amount + items.length) % items.length);
|
|
648
|
+
const copy = async () => {
|
|
649
|
+
try {
|
|
650
|
+
await adapter.copyText(item.url);
|
|
651
|
+
setCopyState2("copied");
|
|
652
|
+
} catch {
|
|
653
|
+
setCopyState2("failed");
|
|
654
|
+
}
|
|
655
|
+
clearTimeout(reset.current);
|
|
656
|
+
reset.current = setTimeout(() => setCopyState2("idle"), 1500);
|
|
657
|
+
};
|
|
658
|
+
const close = () => dialog.current?.close();
|
|
659
|
+
return /* @__PURE__ */ jsxs2(
|
|
660
|
+
"dialog",
|
|
661
|
+
{
|
|
662
|
+
ref: dialog,
|
|
663
|
+
class: "scui-image-viewer",
|
|
664
|
+
"aria-label": `Image preview: ${item.label}`,
|
|
665
|
+
onClose,
|
|
666
|
+
onClick: (event) => {
|
|
667
|
+
if (event.target === event.currentTarget) close();
|
|
668
|
+
},
|
|
669
|
+
onKeyDown: (event) => {
|
|
670
|
+
if (items.length < 2 || !["ArrowLeft", "ArrowRight"].includes(event.key)) return;
|
|
671
|
+
event.preventDefault();
|
|
672
|
+
move(event.key === "ArrowLeft" ? -1 : 1);
|
|
673
|
+
},
|
|
674
|
+
children: [
|
|
675
|
+
/* @__PURE__ */ jsxs2("header", { children: [
|
|
676
|
+
/* @__PURE__ */ jsxs2("span", { children: [
|
|
677
|
+
/* @__PURE__ */ jsx3("strong", { children: item.label }),
|
|
678
|
+
items.length > 1 ? /* @__PURE__ */ jsxs2("small", { children: [
|
|
679
|
+
index + 1,
|
|
680
|
+
" of ",
|
|
681
|
+
items.length
|
|
682
|
+
] }) : null
|
|
683
|
+
] }),
|
|
684
|
+
/* @__PURE__ */ jsxs2("nav", { "aria-label": "Image actions", children: [
|
|
685
|
+
remote && adapter?.copyText ? /* @__PURE__ */ jsx3("button", { type: "button", "aria-label": copyState === "copied" ? "Image link copied" : copyState === "failed" ? "Could not copy image link" : "Copy image link", title: "Copy image link", "data-status": copyState, onClick: copy, children: /* @__PURE__ */ jsx3(UiIcon, { name: copyState === "copied" ? "check" : "copy", size: 16 }) }) : null,
|
|
686
|
+
remote ? /* @__PURE__ */ jsx3("a", { href: item.url, target: "_blank", rel: "noreferrer", "aria-label": "Open original image", title: "Open original image", children: /* @__PURE__ */ jsx3(UiIcon, { name: "image", size: 16 }) }) : /* @__PURE__ */ jsx3("a", { href: item.url, download: imageFilename(item.label), "aria-label": "Download image", title: "Download image", children: /* @__PURE__ */ jsx3(UiIcon, { name: "down", size: 16 }) }),
|
|
687
|
+
/* @__PURE__ */ jsx3("button", { type: "button", "aria-label": "Close image preview", title: "Close", onClick: close, children: /* @__PURE__ */ jsx3(UiIcon, { name: "close", size: 17 }) })
|
|
688
|
+
] })
|
|
689
|
+
] }),
|
|
690
|
+
/* @__PURE__ */ jsxs2("figure", { children: [
|
|
691
|
+
/* @__PURE__ */ jsx3("img", { src: item.url, alt: item.label }),
|
|
692
|
+
items.length > 1 ? /* @__PURE__ */ jsxs2(Fragment2, { children: [
|
|
693
|
+
/* @__PURE__ */ jsx3("button", { type: "button", class: "scui-image-previous", "aria-label": "Previous image", onClick: () => move(-1), children: /* @__PURE__ */ jsx3(UiIcon, { name: "chevron", size: 19 }) }),
|
|
694
|
+
/* @__PURE__ */ jsx3("button", { type: "button", class: "scui-image-next", "aria-label": "Next image", onClick: () => move(1), children: /* @__PURE__ */ jsx3(UiIcon, { name: "chevron", size: 19 }) })
|
|
695
|
+
] }) : null
|
|
696
|
+
] })
|
|
697
|
+
]
|
|
698
|
+
}
|
|
699
|
+
);
|
|
700
|
+
}
|
|
701
|
+
function MessageImages({ items, adapter }) {
|
|
702
|
+
const [active, setActive] = useState(null);
|
|
703
|
+
const opener = useRef2(null);
|
|
628
704
|
if (!items?.length) return null;
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
705
|
+
const viewable = items.filter((item) => item.url);
|
|
706
|
+
const close = () => {
|
|
707
|
+
setActive(null);
|
|
708
|
+
requestAnimationFrame(() => opener.current?.focus({ preventScroll: true }));
|
|
709
|
+
};
|
|
710
|
+
return /* @__PURE__ */ jsxs2(Fragment2, { children: [
|
|
711
|
+
/* @__PURE__ */ jsx3("div", { class: "scui-message-images", "aria-label": "Message images", children: items.map((item, index) => item.url ? /* @__PURE__ */ jsx3("button", { type: "button", "aria-label": `View image ${item.label}`, onClick: (event) => {
|
|
712
|
+
opener.current = event.currentTarget;
|
|
713
|
+
setActive(viewable.indexOf(item));
|
|
714
|
+
}, children: /* @__PURE__ */ jsx3("img", { src: item.url, alt: "" }) }, item.id ?? `${item.label}:${index}`) : /* @__PURE__ */ jsxs2("span", { "data-unavailable": "true", children: [
|
|
715
|
+
/* @__PURE__ */ jsx3(UiIcon, { name: "image", size: 14 }),
|
|
716
|
+
/* @__PURE__ */ jsxs2("span", { children: [
|
|
717
|
+
/* @__PURE__ */ jsx3("strong", { children: item.label }),
|
|
718
|
+
/* @__PURE__ */ jsx3("small", { children: "Preview unavailable" })
|
|
719
|
+
] })
|
|
720
|
+
] }, item.id ?? `${item.label}:${index}`)) }),
|
|
721
|
+
active !== null && viewable[active] ? /* @__PURE__ */ jsx3(ImageViewer, { items: viewable, index: active, adapter, onChange: setActive, onClose: close }) : null
|
|
722
|
+
] });
|
|
633
723
|
}
|
|
634
724
|
|
|
635
725
|
// src/conversation.jsx
|
|
636
|
-
import { Fragment as
|
|
726
|
+
import { Fragment as Fragment4, jsx as jsx4, jsxs as jsxs3 } from "preact/jsx-runtime";
|
|
637
727
|
function LoadingStatus({ state, compact = false }) {
|
|
638
728
|
const copy = {
|
|
639
729
|
connecting: ["Connecting to coding agents", "Checking installed harnesses and capabilities.", 0],
|
|
@@ -682,9 +772,9 @@ function ContextDisclosure({ context }) {
|
|
|
682
772
|
] });
|
|
683
773
|
}
|
|
684
774
|
function MessageMeta({ entry, adapter }) {
|
|
685
|
-
const [copyState, setCopyState2] =
|
|
686
|
-
const reset =
|
|
687
|
-
|
|
775
|
+
const [copyState, setCopyState2] = useState2("idle");
|
|
776
|
+
const reset = useRef3(null);
|
|
777
|
+
useEffect3(() => () => clearTimeout(reset.current), []);
|
|
688
778
|
const date = entry.ts === null ? null : new Date(entry.ts);
|
|
689
779
|
const validDate = date && Number.isFinite(date.valueOf()) ? date : null;
|
|
690
780
|
if (!validDate && (!adapter?.copyText || !entry.text)) return null;
|
|
@@ -705,33 +795,33 @@ function MessageMeta({ entry, adapter }) {
|
|
|
705
795
|
] });
|
|
706
796
|
}
|
|
707
797
|
var TOOL_ICONS = {
|
|
708
|
-
read: () => /* @__PURE__ */ jsxs3(
|
|
798
|
+
read: () => /* @__PURE__ */ jsxs3(Fragment4, { children: [
|
|
709
799
|
/* @__PURE__ */ jsx4("path", { d: "M5 2.75h7.25L15 5.5v7.75A1.75 1.75 0 0 1 13.25 15h-8.5A1.75 1.75 0 0 1 3 13.25v-8.5A2 2 0 0 1 5 2.75Z" }),
|
|
710
800
|
/* @__PURE__ */ jsx4("path", { d: "M12 2.9v3h2.85M6 9h6M6 12h4" })
|
|
711
801
|
] }),
|
|
712
|
-
search: () => /* @__PURE__ */ jsxs3(
|
|
802
|
+
search: () => /* @__PURE__ */ jsxs3(Fragment4, { children: [
|
|
713
803
|
/* @__PURE__ */ jsx4("circle", { cx: "8", cy: "8", r: "4.5" }),
|
|
714
804
|
/* @__PURE__ */ jsx4("path", { d: "m11.5 11.5 3 3" })
|
|
715
805
|
] }),
|
|
716
|
-
edit: () => /* @__PURE__ */ jsxs3(
|
|
806
|
+
edit: () => /* @__PURE__ */ jsxs3(Fragment4, { children: [
|
|
717
807
|
/* @__PURE__ */ jsx4("path", { d: "m11.75 3.25 3 3-8.5 8.5-3.75.75.75-3.75 8.5-8.5Z" }),
|
|
718
808
|
/* @__PURE__ */ jsx4("path", { d: "m10 5 3 3" })
|
|
719
809
|
] }),
|
|
720
|
-
command: () => /* @__PURE__ */ jsx4(
|
|
721
|
-
test: () => /* @__PURE__ */ jsxs3(
|
|
810
|
+
command: () => /* @__PURE__ */ jsx4(Fragment4, { children: /* @__PURE__ */ jsx4("path", { d: "m3 5 3 3-3 3M8 12h6" }) }),
|
|
811
|
+
test: () => /* @__PURE__ */ jsxs3(Fragment4, { children: [
|
|
722
812
|
/* @__PURE__ */ jsx4("path", { d: "M6 2.5v3L3 12a2 2 0 0 0 1.8 3h8.4a2 2 0 0 0 1.8-3l-3-6.5v-3M5 9h8" }),
|
|
723
813
|
/* @__PURE__ */ jsx4("path", { d: "M5 2.5h8" })
|
|
724
814
|
] }),
|
|
725
|
-
web: () => /* @__PURE__ */ jsxs3(
|
|
815
|
+
web: () => /* @__PURE__ */ jsxs3(Fragment4, { children: [
|
|
726
816
|
/* @__PURE__ */ jsx4("circle", { cx: "9", cy: "9", r: "6.5" }),
|
|
727
817
|
/* @__PURE__ */ jsx4("path", { d: "M2.75 9h12.5M9 2.5c2 1.8 3 4 3 6.5s-1 4.7-3 6.5c-2-1.8-3-4-3-6.5s1-4.7 3-6.5Z" })
|
|
728
818
|
] }),
|
|
729
|
-
agent: () => /* @__PURE__ */ jsxs3(
|
|
819
|
+
agent: () => /* @__PURE__ */ jsxs3(Fragment4, { children: [
|
|
730
820
|
/* @__PURE__ */ jsx4("circle", { cx: "9", cy: "6", r: "2.5" }),
|
|
731
821
|
/* @__PURE__ */ jsx4("path", { d: "M4 15c.4-3 2-4.5 5-4.5s4.6 1.5 5 4.5" })
|
|
732
822
|
] }),
|
|
733
|
-
plan: () => /* @__PURE__ */ jsx4(
|
|
734
|
-
other: () => /* @__PURE__ */ jsxs3(
|
|
823
|
+
plan: () => /* @__PURE__ */ jsx4(Fragment4, { children: /* @__PURE__ */ jsx4("path", { d: "m3 5 1 1 2-2M3 9l1 1 2-2M3 13l1 1 2-2M8 5h7M8 9h7M8 13h7" }) }),
|
|
824
|
+
other: () => /* @__PURE__ */ jsxs3(Fragment4, { children: [
|
|
735
825
|
/* @__PURE__ */ jsx4("path", { d: "M9 2.5v3M9 12.5v3M2.5 9h3M12.5 9h3" }),
|
|
736
826
|
/* @__PURE__ */ jsx4("circle", { cx: "9", cy: "9", r: "3.5" })
|
|
737
827
|
] })
|
|
@@ -797,9 +887,9 @@ function ToolMetrics({ presentation }) {
|
|
|
797
887
|
}
|
|
798
888
|
function PendingElapsed({ now }) {
|
|
799
889
|
const clock = now ?? Date.now;
|
|
800
|
-
const started =
|
|
801
|
-
const [elapsed, setElapsed] =
|
|
802
|
-
|
|
890
|
+
const started = useRef3(clock());
|
|
891
|
+
const [elapsed, setElapsed] = useState2(0);
|
|
892
|
+
useEffect3(() => {
|
|
803
893
|
const timer = setInterval(() => setElapsed(Math.max(0, clock() - started.current)), 1e3);
|
|
804
894
|
return () => clearInterval(timer);
|
|
805
895
|
}, [clock]);
|
|
@@ -840,7 +930,7 @@ function SearchPreview({ presentation }) {
|
|
|
840
930
|
] }) : null,
|
|
841
931
|
lines.length ? /* @__PURE__ */ jsx4("ol", { children: lines.map((line, index) => {
|
|
842
932
|
const match = /^(.*?):(\d+)(?::(\d+))?:(.*)$/.exec(line);
|
|
843
|
-
return /* @__PURE__ */ jsx4("li", { children: match ? /* @__PURE__ */ jsxs3(
|
|
933
|
+
return /* @__PURE__ */ jsx4("li", { children: match ? /* @__PURE__ */ jsxs3(Fragment4, { children: [
|
|
844
934
|
/* @__PURE__ */ jsx4("code", { children: match[1] }),
|
|
845
935
|
/* @__PURE__ */ jsxs3("small", { children: [
|
|
846
936
|
match[2],
|
|
@@ -873,9 +963,9 @@ function ToolPreview({ presentation, entry }) {
|
|
|
873
963
|
return presentation.preview ? /* @__PURE__ */ jsx4("pre", { class: "scui-tool-output", "data-error": failed, children: stripAnsi(presentation.preview) }) : null;
|
|
874
964
|
}
|
|
875
965
|
function ToolActions({ presentation, adapter }) {
|
|
876
|
-
const [copied, setCopied] =
|
|
877
|
-
const reset =
|
|
878
|
-
|
|
966
|
+
const [copied, setCopied] = useState2(false);
|
|
967
|
+
const reset = useRef3(null);
|
|
968
|
+
useEffect3(() => () => clearTimeout(reset.current), []);
|
|
879
969
|
if (!adapter?.copyText) return null;
|
|
880
970
|
const action = presentation.command ? ["Copy command", presentation.command] : presentation.path ? ["Copy path", presentation.path] : presentation.url ? ["Copy URL", presentation.url] : presentation.query ? ["Copy query", presentation.query] : null;
|
|
881
971
|
const copy = async () => {
|
|
@@ -922,7 +1012,7 @@ function TranscriptEntry({ entry, state, adapter }) {
|
|
|
922
1012
|
}
|
|
923
1013
|
if (entry.role === "notice" || entry.role === "system") return /* @__PURE__ */ jsx4("div", { class: "scui-notice", "data-code": entry.code, children: entry.text });
|
|
924
1014
|
return /* @__PURE__ */ jsxs3("article", { class: "scui-message", "data-role": entry.role, "aria-label": `${entry.role === "user" ? "Your" : "Assistant"} message`, children: [
|
|
925
|
-
/* @__PURE__ */ jsx4(MessageImages, { items: entry.images }),
|
|
1015
|
+
/* @__PURE__ */ jsx4(MessageImages, { items: entry.images, adapter }),
|
|
926
1016
|
/* @__PURE__ */ jsx4(Markdown, { value: entry.text, copyText: adapter?.copyText }),
|
|
927
1017
|
/* @__PURE__ */ jsx4(ContextDisclosure, { context: entry.context }),
|
|
928
1018
|
entry.truncated ? /* @__PURE__ */ jsx4("small", { class: "scui-truncated", children: "Entry truncated by host \xB7 load native session for the complete value" }) : null,
|
|
@@ -931,14 +1021,14 @@ function TranscriptEntry({ entry, state, adapter }) {
|
|
|
931
1021
|
}
|
|
932
1022
|
function ToolRow({ entry, workspace, open = false, adapter }) {
|
|
933
1023
|
const presentation = entry.presentation ?? createToolPresentation(entry);
|
|
934
|
-
const [expanded, setExpanded] =
|
|
935
|
-
|
|
1024
|
+
const [expanded, setExpanded] = useState2(open || entry.status === "pending");
|
|
1025
|
+
useEffect3(() => {
|
|
936
1026
|
if (entry.status === "pending") setExpanded(true);
|
|
937
1027
|
}, [entry.status]);
|
|
938
1028
|
const target = compactToolTarget(presentation.target, workspace);
|
|
939
1029
|
const hasDetail = Boolean(entry.arguments || entry.resultText || presentation.preview || presentation.fields.length);
|
|
940
1030
|
const category = presentation.category ?? toolCategory(entry);
|
|
941
|
-
const summary = /* @__PURE__ */ jsxs3(
|
|
1031
|
+
const summary = /* @__PURE__ */ jsxs3(Fragment4, { children: [
|
|
942
1032
|
/* @__PURE__ */ jsx4(ToolIcon, { category }),
|
|
943
1033
|
/* @__PURE__ */ jsx4("strong", { children: toolAction2(entry, category, presentation) }),
|
|
944
1034
|
target ? /* @__PURE__ */ jsx4("code", { class: "scui-tool-target", title: presentation.target, children: target }) : null,
|
|
@@ -966,9 +1056,9 @@ function ToolRow({ entry, workspace, open = false, adapter }) {
|
|
|
966
1056
|
function ActivityGroup({ entries, state, adapter }) {
|
|
967
1057
|
if (entries.length === 1) return /* @__PURE__ */ jsx4("section", { class: "scui-activity", "data-single": "true", children: /* @__PURE__ */ jsx4(ToolRow, { entry: entries[0], workspace: state.workspace, adapter }) });
|
|
968
1058
|
const active = entries.some((entry) => entry.status === "pending");
|
|
969
|
-
const [open, setOpen] =
|
|
1059
|
+
const [open, setOpen] = useState2(active);
|
|
970
1060
|
const id = useId();
|
|
971
|
-
|
|
1061
|
+
useEffect3(() => {
|
|
972
1062
|
if (active) setOpen(true);
|
|
973
1063
|
}, [active]);
|
|
974
1064
|
return /* @__PURE__ */ jsxs3("section", { class: "scui-activity", children: [
|
|
@@ -1044,9 +1134,9 @@ function SessionDetails({ semantics }) {
|
|
|
1044
1134
|
}
|
|
1045
1135
|
var conversationMemory = /* @__PURE__ */ new Map();
|
|
1046
1136
|
function ConversationAnnouncements({ state }) {
|
|
1047
|
-
const previousBusy =
|
|
1048
|
-
const [announcement, setAnnouncement] =
|
|
1049
|
-
|
|
1137
|
+
const previousBusy = useRef3(state.busy);
|
|
1138
|
+
const [announcement, setAnnouncement] = useState2("");
|
|
1139
|
+
useEffect3(() => {
|
|
1050
1140
|
if (previousBusy.current && !state.busy && !state.error) {
|
|
1051
1141
|
setAnnouncement(`${harnessDisplayName(state.harness) || "Coding agent"} finished working`);
|
|
1052
1142
|
}
|
|
@@ -1055,13 +1145,13 @@ function ConversationAnnouncements({ state }) {
|
|
|
1055
1145
|
return /* @__PURE__ */ jsx4("span", { class: "scui-sr-only", role: "status", "aria-live": "polite", "aria-atomic": "true", children: announcement });
|
|
1056
1146
|
}
|
|
1057
1147
|
function Conversation({ state, adapter, components = {}, slots = {}, memoryKey = state.attached?.key || `${state.harness}:${state.workspace}`, pending = null, unreadAfterMessages = null }) {
|
|
1058
|
-
const scroller =
|
|
1148
|
+
const scroller = useRef3(null);
|
|
1059
1149
|
const remembered = conversationMemory.get(memoryKey) ?? { top: null, atBottom: true };
|
|
1060
|
-
const [atBottom, setAtBottom] =
|
|
1061
|
-
const earlierAnchor =
|
|
1062
|
-
const restored =
|
|
1150
|
+
const [atBottom, setAtBottom] = useState2(remembered.atBottom);
|
|
1151
|
+
const earlierAnchor = useRef3(null);
|
|
1152
|
+
const restored = useRef3(false);
|
|
1063
1153
|
const blocks = groupConversation(state.transcript);
|
|
1064
|
-
const unreadBoundary =
|
|
1154
|
+
const unreadBoundary = useRef3(Number.isSafeInteger(unreadAfterMessages) && unreadAfterMessages >= 0 ? unreadAfterMessages : null);
|
|
1065
1155
|
const unreadBlock = unreadBoundary.current === null ? -1 : blocks.findIndex((block) => {
|
|
1066
1156
|
const entries = block.kind === "activity" ? block.entries : [block.entry];
|
|
1067
1157
|
return entries.some((entry) => Number.isSafeInteger(entry.messageIndex) && entry.messageIndex > unreadBoundary.current);
|
|
@@ -1120,12 +1210,12 @@ function Conversation({ state, adapter, components = {}, slots = {}, memoryKey =
|
|
|
1120
1210
|
}, children: "Load earlier messages" }) : null,
|
|
1121
1211
|
!blocks.length && state.startup !== "ready" ? /* @__PURE__ */ jsx4(LoadingStatus, { state }) : null,
|
|
1122
1212
|
!blocks.length && state.startup === "ready" ? Empty ? /* @__PURE__ */ jsx4(Empty, { state, adapter, value: null }) : /* @__PURE__ */ jsx4("div", { class: "scui-empty", children: state.error ?? (state.harness ? `${harnessDisplayName(state.harness)} is listening. Say something.` : "No transcript yet.") }) : null,
|
|
1123
|
-
blocks.map((block, index) => /* @__PURE__ */ jsxs3(
|
|
1213
|
+
blocks.map((block, index) => /* @__PURE__ */ jsxs3(Fragment3, { children: [
|
|
1124
1214
|
index === unreadBlock ? /* @__PURE__ */ jsx4("div", { class: "scui-unread-divider", role: "separator", "aria-label": "New messages", children: /* @__PURE__ */ jsx4("span", { children: "New" }) }) : null,
|
|
1125
1215
|
block.kind === "activity" ? /* @__PURE__ */ jsx4(Group, { value: block.entries, entries: block.entries, state, adapter }) : /* @__PURE__ */ jsx4(Entry, { value: block.entry, entry: block.entry, state, adapter })
|
|
1126
1216
|
] }, block.id)),
|
|
1127
1217
|
pendingMessage ? /* @__PURE__ */ jsxs3("article", { class: "scui-message scui-pending", "data-role": "user", "data-status": pendingMessage.status, "aria-label": "Your pending message", children: [
|
|
1128
|
-
/* @__PURE__ */ jsx4(MessageImages, { items: pendingMessage.images }),
|
|
1218
|
+
/* @__PURE__ */ jsx4(MessageImages, { items: pendingMessage.images, adapter }),
|
|
1129
1219
|
/* @__PURE__ */ jsx4(Markdown, { value: pendingMessage.text, copyText: adapter?.copyText }),
|
|
1130
1220
|
/* @__PURE__ */ jsx4(ContextDisclosure, { context: pendingMessage.context }),
|
|
1131
1221
|
/* @__PURE__ */ jsxs3("footer", { children: [
|
|
@@ -1157,7 +1247,9 @@ function Conversation({ state, adapter, components = {}, slots = {}, memoryKey =
|
|
|
1157
1247
|
export {
|
|
1158
1248
|
ActivityGroup,
|
|
1159
1249
|
Conversation,
|
|
1250
|
+
ImageViewer,
|
|
1160
1251
|
LoadingStatus,
|
|
1252
|
+
MessageImages,
|
|
1161
1253
|
RequestCard,
|
|
1162
1254
|
SessionDetails,
|
|
1163
1255
|
TaskPlan,
|