@volter-ai-dev/supercode-ui 0.1.22 → 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/messenger.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  // src/messenger.jsx
2
- import { useEffect as useEffect6, useId as useId2, useMemo as useMemo2, useRef as useRef5, useState as useState4 } from "preact/hooks";
2
+ import { useEffect as useEffect7, useId as useId2, useMemo as useMemo2, useRef as useRef6, useState as useState5 } from "preact/hooks";
3
3
 
4
4
  // core.mjs
5
5
  var HARNESS_NAMES = Object.freeze({
@@ -758,7 +758,7 @@ function isSendKey(event) {
758
758
  }
759
759
 
760
760
  // src/composer.jsx
761
- import { useEffect, useRef, useState } from "preact/hooks";
761
+ import { useEffect as useEffect2, useRef as useRef2, useState as useState2 } from "preact/hooks";
762
762
 
763
763
  // src/memory.js
764
764
  var MEMORY_LIMIT = 100;
@@ -818,7 +818,8 @@ function UiIcon({ name, size = 16, class: className = "" }) {
818
818
  }
819
819
 
820
820
  // src/context.jsx
821
- import { jsx as jsx2, jsxs as jsxs2 } from "preact/jsx-runtime";
821
+ import { useEffect, useRef, useState } from "preact/hooks";
822
+ import { Fragment as Fragment2, jsx as jsx2, jsxs as jsxs2 } from "preact/jsx-runtime";
822
823
  var MAX_CONTEXT_ITEMS = 32;
823
824
  var MAX_IMAGE_ITEMS = 4;
824
825
  var MAX_IMAGE_BYTES = 5 * 1024 * 1024;
@@ -917,12 +918,101 @@ function ImageTray({ items, onRemove }) {
917
918
  onRemove ? /* @__PURE__ */ jsx2("button", { type: "button", "aria-label": `Remove image ${item.label}`, onClick: () => onRemove(index), children: /* @__PURE__ */ jsx2(UiIcon, { name: "close", size: 12 }) }) : null
918
919
  ] }, item.id ?? `${item.label}:${index}`)) });
919
920
  }
920
- function MessageImages({ items }) {
921
+ function imageFilename(label) {
922
+ const value = label.trim().replace(/[\\/:*?"<>|]+/g, "-");
923
+ return value || "image";
924
+ }
925
+ function ImageViewer({ items, index, adapter, onChange, onClose }) {
926
+ const dialog = useRef(null);
927
+ const reset = useRef(null);
928
+ const [copyState, setCopyState2] = useState("idle");
929
+ const item = items[index];
930
+ const remote = item?.url?.startsWith("http://") || item?.url?.startsWith("https://");
931
+ useEffect(() => {
932
+ if (!dialog.current?.open) dialog.current?.showModal();
933
+ return () => clearTimeout(reset.current);
934
+ }, []);
935
+ useEffect(() => {
936
+ clearTimeout(reset.current);
937
+ setCopyState2("idle");
938
+ }, [index]);
939
+ if (!item?.url) return null;
940
+ const move = (amount) => onChange((index + amount + items.length) % items.length);
941
+ const copy = async () => {
942
+ try {
943
+ await adapter.copyText(item.url);
944
+ setCopyState2("copied");
945
+ } catch {
946
+ setCopyState2("failed");
947
+ }
948
+ clearTimeout(reset.current);
949
+ reset.current = setTimeout(() => setCopyState2("idle"), 1500);
950
+ };
951
+ const close = () => dialog.current?.close();
952
+ return /* @__PURE__ */ jsxs2(
953
+ "dialog",
954
+ {
955
+ ref: dialog,
956
+ class: "scui-image-viewer",
957
+ "aria-label": `Image preview: ${item.label}`,
958
+ onClose,
959
+ onClick: (event) => {
960
+ if (event.target === event.currentTarget) close();
961
+ },
962
+ onKeyDown: (event) => {
963
+ if (items.length < 2 || !["ArrowLeft", "ArrowRight"].includes(event.key)) return;
964
+ event.preventDefault();
965
+ move(event.key === "ArrowLeft" ? -1 : 1);
966
+ },
967
+ children: [
968
+ /* @__PURE__ */ jsxs2("header", { children: [
969
+ /* @__PURE__ */ jsxs2("span", { children: [
970
+ /* @__PURE__ */ jsx2("strong", { children: item.label }),
971
+ items.length > 1 ? /* @__PURE__ */ jsxs2("small", { children: [
972
+ index + 1,
973
+ " of ",
974
+ items.length
975
+ ] }) : null
976
+ ] }),
977
+ /* @__PURE__ */ jsxs2("nav", { "aria-label": "Image actions", children: [
978
+ remote && adapter?.copyText ? /* @__PURE__ */ jsx2("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__ */ jsx2(UiIcon, { name: copyState === "copied" ? "check" : "copy", size: 16 }) }) : null,
979
+ remote ? /* @__PURE__ */ jsx2("a", { href: item.url, target: "_blank", rel: "noreferrer", "aria-label": "Open original image", title: "Open original image", children: /* @__PURE__ */ jsx2(UiIcon, { name: "image", size: 16 }) }) : /* @__PURE__ */ jsx2("a", { href: item.url, download: imageFilename(item.label), "aria-label": "Download image", title: "Download image", children: /* @__PURE__ */ jsx2(UiIcon, { name: "down", size: 16 }) }),
980
+ /* @__PURE__ */ jsx2("button", { type: "button", "aria-label": "Close image preview", title: "Close", onClick: close, children: /* @__PURE__ */ jsx2(UiIcon, { name: "close", size: 17 }) })
981
+ ] })
982
+ ] }),
983
+ /* @__PURE__ */ jsxs2("figure", { children: [
984
+ /* @__PURE__ */ jsx2("img", { src: item.url, alt: item.label }),
985
+ items.length > 1 ? /* @__PURE__ */ jsxs2(Fragment2, { children: [
986
+ /* @__PURE__ */ jsx2("button", { type: "button", class: "scui-image-previous", "aria-label": "Previous image", onClick: () => move(-1), children: /* @__PURE__ */ jsx2(UiIcon, { name: "chevron", size: 19 }) }),
987
+ /* @__PURE__ */ jsx2("button", { type: "button", class: "scui-image-next", "aria-label": "Next image", onClick: () => move(1), children: /* @__PURE__ */ jsx2(UiIcon, { name: "chevron", size: 19 }) })
988
+ ] }) : null
989
+ ] })
990
+ ]
991
+ }
992
+ );
993
+ }
994
+ function MessageImages({ items, adapter }) {
995
+ const [active, setActive] = useState(null);
996
+ const opener = useRef(null);
921
997
  if (!items?.length) return null;
922
- return /* @__PURE__ */ jsx2("div", { class: "scui-message-images", children: items.map((item, index) => item.url ? /* @__PURE__ */ jsx2("img", { src: item.url, alt: item.label }, item.id ?? `${item.label}:${index}`) : /* @__PURE__ */ jsxs2("span", { children: [
923
- /* @__PURE__ */ jsx2(UiIcon, { name: "image", size: 14 }),
924
- item.label
925
- ] }, item.id ?? `${item.label}:${index}`)) });
998
+ const viewable = items.filter((item) => item.url);
999
+ const close = () => {
1000
+ setActive(null);
1001
+ requestAnimationFrame(() => opener.current?.focus({ preventScroll: true }));
1002
+ };
1003
+ return /* @__PURE__ */ jsxs2(Fragment2, { children: [
1004
+ /* @__PURE__ */ jsx2("div", { class: "scui-message-images", "aria-label": "Message images", children: items.map((item, index) => item.url ? /* @__PURE__ */ jsx2("button", { type: "button", "aria-label": `View image ${item.label}`, onClick: (event) => {
1005
+ opener.current = event.currentTarget;
1006
+ setActive(viewable.indexOf(item));
1007
+ }, children: /* @__PURE__ */ jsx2("img", { src: item.url, alt: "" }) }, item.id ?? `${item.label}:${index}`) : /* @__PURE__ */ jsxs2("span", { "data-unavailable": "true", children: [
1008
+ /* @__PURE__ */ jsx2(UiIcon, { name: "image", size: 14 }),
1009
+ /* @__PURE__ */ jsxs2("span", { children: [
1010
+ /* @__PURE__ */ jsx2("strong", { children: item.label }),
1011
+ /* @__PURE__ */ jsx2("small", { children: "Preview unavailable" })
1012
+ ] })
1013
+ ] }, item.id ?? `${item.label}:${index}`)) }),
1014
+ active !== null && viewable[active] ? /* @__PURE__ */ jsx2(ImageViewer, { items: viewable, index: active, adapter, onChange: setActive, onClose: close }) : null
1015
+ ] });
926
1016
  }
927
1017
 
928
1018
  // src/textarea.js
@@ -964,18 +1054,18 @@ function ContinuationBar({ state, adapter, labels = DEFAULT_LABELS }) {
964
1054
  }
965
1055
  function Composer({ state, adapter, labels = DEFAULT_LABELS, memoryKey = state.attached?.key || `${state.harness}:${state.workspace}`, pendingStatus = null, restoreDraft = null, onPending, onDraftRestored }) {
966
1056
  const remembered = composerMemory.get(memoryKey) ?? { draft: state.savedDraft, context: [], images: [], queue: [] };
967
- const [draft, setDraft] = useState(remembered.draft);
968
- const [context, setContext] = useState(remembered.context ?? []);
969
- const [images, setImages] = useState(remembered.images ?? []);
970
- const [queue, setQueue] = useState((remembered.queue ?? []).map((item) => ({ ...item, context: item.context ?? [], images: item.images ?? [] })));
971
- const [dispatching, setDispatching] = useState(false);
972
- const [picking, setPicking] = useState(false);
973
- const [dragging, setDragging] = useState(false);
974
- const [pickerError, setPickerError] = useState(null);
975
- const textarea = useRef(null);
1057
+ const [draft, setDraft] = useState2(remembered.draft);
1058
+ const [context, setContext] = useState2(remembered.context ?? []);
1059
+ const [images, setImages] = useState2(remembered.images ?? []);
1060
+ const [queue, setQueue] = useState2((remembered.queue ?? []).map((item) => ({ ...item, context: item.context ?? [], images: item.images ?? [] })));
1061
+ const [dispatching, setDispatching] = useState2(false);
1062
+ const [picking, setPicking] = useState2(false);
1063
+ const [dragging, setDragging] = useState2(false);
1064
+ const [pickerError, setPickerError] = useState2(null);
1065
+ const textarea = useRef2(null);
976
1066
  useAutosizeTextarea(textarea, draft);
977
1067
  const remember = (nextDraft, nextContext, nextImages, nextQueue) => boundedSet(composerMemory, memoryKey, { draft: nextDraft, context: nextContext, images: nextImages, queue: nextQueue });
978
- useEffect(() => {
1068
+ useEffect2(() => {
979
1069
  remember(draft, context, images, queue);
980
1070
  }, [draft, context, images, memoryKey, queue]);
981
1071
  const updateQueue = (update) => setQueue((items) => {
@@ -985,7 +1075,7 @@ function Composer({ state, adapter, labels = DEFAULT_LABELS, memoryKey = state.a
985
1075
  });
986
1076
  const queueBlocked = state.busy || pendingStatus !== null || dispatching;
987
1077
  const queuesNewMessage = state.busy || pendingStatus === "sending" || pendingStatus === "failed" || dispatching;
988
- useEffect(() => {
1078
+ useEffect2(() => {
989
1079
  if (!queueBlocked && state.canSend && queue.length) {
990
1080
  const [next, ...rest] = queue;
991
1081
  setDispatching(true);
@@ -995,13 +1085,13 @@ function Composer({ state, adapter, labels = DEFAULT_LABELS, memoryKey = state.a
995
1085
  adapter.onIntent({ action: "send", text: next.text, ...next.context.length ? { context: next.context } : {}, ...next.images.length ? { images: next.images } : {} });
996
1086
  }
997
1087
  }, [adapter, draft, memoryKey, onPending, queue, queueBlocked, state.canSend]);
998
- useEffect(() => {
1088
+ useEffect2(() => {
999
1089
  if (pendingStatus !== null || state.busy) setDispatching(false);
1000
1090
  }, [pendingStatus, state.busy]);
1001
- useEffect(() => {
1091
+ useEffect2(() => {
1002
1092
  textarea.current?.focus({ preventScroll: true });
1003
1093
  }, [memoryKey]);
1004
- useEffect(() => {
1094
+ useEffect2(() => {
1005
1095
  if (!restoreDraft) return;
1006
1096
  setDraft(restoreDraft.text);
1007
1097
  const restoredContext = normalizeContext(restoreDraft.context);
@@ -1012,7 +1102,7 @@ function Composer({ state, adapter, labels = DEFAULT_LABELS, memoryKey = state.a
1012
1102
  textarea.current?.focus({ preventScroll: true });
1013
1103
  onDraftRestored?.(restoreDraft.id);
1014
1104
  }, [onDraftRestored, restoreDraft?.id]);
1015
- useEffect(() => {
1105
+ useEffect2(() => {
1016
1106
  const timer = setTimeout(() => adapter.onIntent({ action: "draft", text: draft }), 250);
1017
1107
  return () => clearTimeout(timer);
1018
1108
  }, [adapter, draft]);
@@ -1137,12 +1227,12 @@ function Composer({ state, adapter, labels = DEFAULT_LABELS, memoryKey = state.a
1137
1227
  }
1138
1228
 
1139
1229
  // src/conversation.jsx
1140
- import { Fragment as Fragment2 } from "preact";
1141
- import { useEffect as useEffect3, useId, useLayoutEffect as useLayoutEffect2, useRef as useRef3, useState as useState2 } from "preact/hooks";
1230
+ import { Fragment as Fragment3 } from "preact";
1231
+ import { useEffect as useEffect4, useId, useLayoutEffect as useLayoutEffect2, useRef as useRef4, useState as useState3 } from "preact/hooks";
1142
1232
 
1143
1233
  // src/markdown.jsx
1144
1234
  import MarkdownIt from "markdown-it";
1145
- import { useEffect as useEffect2, useMemo, useRef as useRef2 } from "preact/hooks";
1235
+ import { useEffect as useEffect3, useMemo, useRef as useRef3 } from "preact/hooks";
1146
1236
  import { jsx as jsx4 } from "preact/jsx-runtime";
1147
1237
  var markdown = new MarkdownIt({ html: false, linkify: true, breaks: false });
1148
1238
  var LANGUAGE_LABELS = {
@@ -1204,8 +1294,8 @@ function setCopyState(button, status) {
1204
1294
  }
1205
1295
  function Markdown({ value, copyText }) {
1206
1296
  const html = useMemo(() => markdown.render(value), [value]);
1207
- const resets = useRef2(/* @__PURE__ */ new Map());
1208
- useEffect2(() => () => {
1297
+ const resets = useRef3(/* @__PURE__ */ new Map());
1298
+ useEffect3(() => () => {
1209
1299
  for (const timer of resets.current.values()) clearTimeout(timer);
1210
1300
  resets.current.clear();
1211
1301
  }, []);
@@ -1234,7 +1324,7 @@ function Markdown({ value, copyText }) {
1234
1324
  }
1235
1325
 
1236
1326
  // src/conversation.jsx
1237
- import { Fragment as Fragment3, jsx as jsx5, jsxs as jsxs4 } from "preact/jsx-runtime";
1327
+ import { Fragment as Fragment4, jsx as jsx5, jsxs as jsxs4 } from "preact/jsx-runtime";
1238
1328
  function LoadingStatus({ state, compact = false }) {
1239
1329
  const copy = {
1240
1330
  connecting: ["Connecting to coding agents", "Checking installed harnesses and capabilities.", 0],
@@ -1283,9 +1373,9 @@ function ContextDisclosure({ context }) {
1283
1373
  ] });
1284
1374
  }
1285
1375
  function MessageMeta({ entry, adapter }) {
1286
- const [copyState, setCopyState2] = useState2("idle");
1287
- const reset = useRef3(null);
1288
- useEffect3(() => () => clearTimeout(reset.current), []);
1376
+ const [copyState, setCopyState2] = useState3("idle");
1377
+ const reset = useRef4(null);
1378
+ useEffect4(() => () => clearTimeout(reset.current), []);
1289
1379
  const date = entry.ts === null ? null : new Date(entry.ts);
1290
1380
  const validDate = date && Number.isFinite(date.valueOf()) ? date : null;
1291
1381
  if (!validDate && (!adapter?.copyText || !entry.text)) return null;
@@ -1306,33 +1396,33 @@ function MessageMeta({ entry, adapter }) {
1306
1396
  ] });
1307
1397
  }
1308
1398
  var TOOL_ICONS = {
1309
- read: () => /* @__PURE__ */ jsxs4(Fragment3, { children: [
1399
+ read: () => /* @__PURE__ */ jsxs4(Fragment4, { children: [
1310
1400
  /* @__PURE__ */ jsx5("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" }),
1311
1401
  /* @__PURE__ */ jsx5("path", { d: "M12 2.9v3h2.85M6 9h6M6 12h4" })
1312
1402
  ] }),
1313
- search: () => /* @__PURE__ */ jsxs4(Fragment3, { children: [
1403
+ search: () => /* @__PURE__ */ jsxs4(Fragment4, { children: [
1314
1404
  /* @__PURE__ */ jsx5("circle", { cx: "8", cy: "8", r: "4.5" }),
1315
1405
  /* @__PURE__ */ jsx5("path", { d: "m11.5 11.5 3 3" })
1316
1406
  ] }),
1317
- edit: () => /* @__PURE__ */ jsxs4(Fragment3, { children: [
1407
+ edit: () => /* @__PURE__ */ jsxs4(Fragment4, { children: [
1318
1408
  /* @__PURE__ */ jsx5("path", { d: "m11.75 3.25 3 3-8.5 8.5-3.75.75.75-3.75 8.5-8.5Z" }),
1319
1409
  /* @__PURE__ */ jsx5("path", { d: "m10 5 3 3" })
1320
1410
  ] }),
1321
- command: () => /* @__PURE__ */ jsx5(Fragment3, { children: /* @__PURE__ */ jsx5("path", { d: "m3 5 3 3-3 3M8 12h6" }) }),
1322
- test: () => /* @__PURE__ */ jsxs4(Fragment3, { children: [
1411
+ command: () => /* @__PURE__ */ jsx5(Fragment4, { children: /* @__PURE__ */ jsx5("path", { d: "m3 5 3 3-3 3M8 12h6" }) }),
1412
+ test: () => /* @__PURE__ */ jsxs4(Fragment4, { children: [
1323
1413
  /* @__PURE__ */ jsx5("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" }),
1324
1414
  /* @__PURE__ */ jsx5("path", { d: "M5 2.5h8" })
1325
1415
  ] }),
1326
- web: () => /* @__PURE__ */ jsxs4(Fragment3, { children: [
1416
+ web: () => /* @__PURE__ */ jsxs4(Fragment4, { children: [
1327
1417
  /* @__PURE__ */ jsx5("circle", { cx: "9", cy: "9", r: "6.5" }),
1328
1418
  /* @__PURE__ */ jsx5("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" })
1329
1419
  ] }),
1330
- agent: () => /* @__PURE__ */ jsxs4(Fragment3, { children: [
1420
+ agent: () => /* @__PURE__ */ jsxs4(Fragment4, { children: [
1331
1421
  /* @__PURE__ */ jsx5("circle", { cx: "9", cy: "6", r: "2.5" }),
1332
1422
  /* @__PURE__ */ jsx5("path", { d: "M4 15c.4-3 2-4.5 5-4.5s4.6 1.5 5 4.5" })
1333
1423
  ] }),
1334
- plan: () => /* @__PURE__ */ jsx5(Fragment3, { children: /* @__PURE__ */ jsx5("path", { d: "m3 5 1 1 2-2M3 9l1 1 2-2M3 13l1 1 2-2M8 5h7M8 9h7M8 13h7" }) }),
1335
- other: () => /* @__PURE__ */ jsxs4(Fragment3, { children: [
1424
+ plan: () => /* @__PURE__ */ jsx5(Fragment4, { children: /* @__PURE__ */ jsx5("path", { d: "m3 5 1 1 2-2M3 9l1 1 2-2M3 13l1 1 2-2M8 5h7M8 9h7M8 13h7" }) }),
1425
+ other: () => /* @__PURE__ */ jsxs4(Fragment4, { children: [
1336
1426
  /* @__PURE__ */ jsx5("path", { d: "M9 2.5v3M9 12.5v3M2.5 9h3M12.5 9h3" }),
1337
1427
  /* @__PURE__ */ jsx5("circle", { cx: "9", cy: "9", r: "3.5" })
1338
1428
  ] })
@@ -1398,9 +1488,9 @@ function ToolMetrics({ presentation }) {
1398
1488
  }
1399
1489
  function PendingElapsed({ now }) {
1400
1490
  const clock = now ?? Date.now;
1401
- const started = useRef3(clock());
1402
- const [elapsed, setElapsed] = useState2(0);
1403
- useEffect3(() => {
1491
+ const started = useRef4(clock());
1492
+ const [elapsed, setElapsed] = useState3(0);
1493
+ useEffect4(() => {
1404
1494
  const timer = setInterval(() => setElapsed(Math.max(0, clock() - started.current)), 1e3);
1405
1495
  return () => clearInterval(timer);
1406
1496
  }, [clock]);
@@ -1441,7 +1531,7 @@ function SearchPreview({ presentation }) {
1441
1531
  ] }) : null,
1442
1532
  lines.length ? /* @__PURE__ */ jsx5("ol", { children: lines.map((line, index) => {
1443
1533
  const match = /^(.*?):(\d+)(?::(\d+))?:(.*)$/.exec(line);
1444
- return /* @__PURE__ */ jsx5("li", { children: match ? /* @__PURE__ */ jsxs4(Fragment3, { children: [
1534
+ return /* @__PURE__ */ jsx5("li", { children: match ? /* @__PURE__ */ jsxs4(Fragment4, { children: [
1445
1535
  /* @__PURE__ */ jsx5("code", { children: match[1] }),
1446
1536
  /* @__PURE__ */ jsxs4("small", { children: [
1447
1537
  match[2],
@@ -1474,9 +1564,9 @@ function ToolPreview({ presentation, entry }) {
1474
1564
  return presentation.preview ? /* @__PURE__ */ jsx5("pre", { class: "scui-tool-output", "data-error": failed, children: stripAnsi(presentation.preview) }) : null;
1475
1565
  }
1476
1566
  function ToolActions({ presentation, adapter }) {
1477
- const [copied, setCopied] = useState2(false);
1478
- const reset = useRef3(null);
1479
- useEffect3(() => () => clearTimeout(reset.current), []);
1567
+ const [copied, setCopied] = useState3(false);
1568
+ const reset = useRef4(null);
1569
+ useEffect4(() => () => clearTimeout(reset.current), []);
1480
1570
  if (!adapter?.copyText) return null;
1481
1571
  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;
1482
1572
  const copy = async () => {
@@ -1523,7 +1613,7 @@ function TranscriptEntry({ entry, state, adapter }) {
1523
1613
  }
1524
1614
  if (entry.role === "notice" || entry.role === "system") return /* @__PURE__ */ jsx5("div", { class: "scui-notice", "data-code": entry.code, children: entry.text });
1525
1615
  return /* @__PURE__ */ jsxs4("article", { class: "scui-message", "data-role": entry.role, "aria-label": `${entry.role === "user" ? "Your" : "Assistant"} message`, children: [
1526
- /* @__PURE__ */ jsx5(MessageImages, { items: entry.images }),
1616
+ /* @__PURE__ */ jsx5(MessageImages, { items: entry.images, adapter }),
1527
1617
  /* @__PURE__ */ jsx5(Markdown, { value: entry.text, copyText: adapter?.copyText }),
1528
1618
  /* @__PURE__ */ jsx5(ContextDisclosure, { context: entry.context }),
1529
1619
  entry.truncated ? /* @__PURE__ */ jsx5("small", { class: "scui-truncated", children: "Entry truncated by host \xB7 load native session for the complete value" }) : null,
@@ -1532,14 +1622,14 @@ function TranscriptEntry({ entry, state, adapter }) {
1532
1622
  }
1533
1623
  function ToolRow({ entry, workspace, open = false, adapter }) {
1534
1624
  const presentation = entry.presentation ?? createToolPresentation(entry);
1535
- const [expanded, setExpanded] = useState2(open || entry.status === "pending");
1536
- useEffect3(() => {
1625
+ const [expanded, setExpanded] = useState3(open || entry.status === "pending");
1626
+ useEffect4(() => {
1537
1627
  if (entry.status === "pending") setExpanded(true);
1538
1628
  }, [entry.status]);
1539
1629
  const target = compactToolTarget(presentation.target, workspace);
1540
1630
  const hasDetail = Boolean(entry.arguments || entry.resultText || presentation.preview || presentation.fields.length);
1541
1631
  const category = presentation.category ?? toolCategory(entry);
1542
- const summary = /* @__PURE__ */ jsxs4(Fragment3, { children: [
1632
+ const summary = /* @__PURE__ */ jsxs4(Fragment4, { children: [
1543
1633
  /* @__PURE__ */ jsx5(ToolIcon, { category }),
1544
1634
  /* @__PURE__ */ jsx5("strong", { children: toolAction2(entry, category, presentation) }),
1545
1635
  target ? /* @__PURE__ */ jsx5("code", { class: "scui-tool-target", title: presentation.target, children: target }) : null,
@@ -1567,9 +1657,9 @@ function ToolRow({ entry, workspace, open = false, adapter }) {
1567
1657
  function ActivityGroup({ entries, state, adapter }) {
1568
1658
  if (entries.length === 1) return /* @__PURE__ */ jsx5("section", { class: "scui-activity", "data-single": "true", children: /* @__PURE__ */ jsx5(ToolRow, { entry: entries[0], workspace: state.workspace, adapter }) });
1569
1659
  const active = entries.some((entry) => entry.status === "pending");
1570
- const [open, setOpen] = useState2(active);
1660
+ const [open, setOpen] = useState3(active);
1571
1661
  const id = useId();
1572
- useEffect3(() => {
1662
+ useEffect4(() => {
1573
1663
  if (active) setOpen(true);
1574
1664
  }, [active]);
1575
1665
  return /* @__PURE__ */ jsxs4("section", { class: "scui-activity", children: [
@@ -1645,9 +1735,9 @@ function SessionDetails({ semantics }) {
1645
1735
  }
1646
1736
  var conversationMemory = /* @__PURE__ */ new Map();
1647
1737
  function ConversationAnnouncements({ state }) {
1648
- const previousBusy = useRef3(state.busy);
1649
- const [announcement, setAnnouncement] = useState2("");
1650
- useEffect3(() => {
1738
+ const previousBusy = useRef4(state.busy);
1739
+ const [announcement, setAnnouncement] = useState3("");
1740
+ useEffect4(() => {
1651
1741
  if (previousBusy.current && !state.busy && !state.error) {
1652
1742
  setAnnouncement(`${harnessDisplayName(state.harness) || "Coding agent"} finished working`);
1653
1743
  }
@@ -1656,13 +1746,13 @@ function ConversationAnnouncements({ state }) {
1656
1746
  return /* @__PURE__ */ jsx5("span", { class: "scui-sr-only", role: "status", "aria-live": "polite", "aria-atomic": "true", children: announcement });
1657
1747
  }
1658
1748
  function Conversation({ state, adapter, components = {}, slots = {}, memoryKey = state.attached?.key || `${state.harness}:${state.workspace}`, pending = null, unreadAfterMessages = null }) {
1659
- const scroller = useRef3(null);
1749
+ const scroller = useRef4(null);
1660
1750
  const remembered = conversationMemory.get(memoryKey) ?? { top: null, atBottom: true };
1661
- const [atBottom, setAtBottom] = useState2(remembered.atBottom);
1662
- const earlierAnchor = useRef3(null);
1663
- const restored = useRef3(false);
1751
+ const [atBottom, setAtBottom] = useState3(remembered.atBottom);
1752
+ const earlierAnchor = useRef4(null);
1753
+ const restored = useRef4(false);
1664
1754
  const blocks = groupConversation(state.transcript);
1665
- const unreadBoundary = useRef3(Number.isSafeInteger(unreadAfterMessages) && unreadAfterMessages >= 0 ? unreadAfterMessages : null);
1755
+ const unreadBoundary = useRef4(Number.isSafeInteger(unreadAfterMessages) && unreadAfterMessages >= 0 ? unreadAfterMessages : null);
1666
1756
  const unreadBlock = unreadBoundary.current === null ? -1 : blocks.findIndex((block) => {
1667
1757
  const entries = block.kind === "activity" ? block.entries : [block.entry];
1668
1758
  return entries.some((entry) => Number.isSafeInteger(entry.messageIndex) && entry.messageIndex > unreadBoundary.current);
@@ -1721,12 +1811,12 @@ function Conversation({ state, adapter, components = {}, slots = {}, memoryKey =
1721
1811
  }, children: "Load earlier messages" }) : null,
1722
1812
  !blocks.length && state.startup !== "ready" ? /* @__PURE__ */ jsx5(LoadingStatus, { state }) : null,
1723
1813
  !blocks.length && state.startup === "ready" ? Empty ? /* @__PURE__ */ jsx5(Empty, { state, adapter, value: null }) : /* @__PURE__ */ jsx5("div", { class: "scui-empty", children: state.error ?? (state.harness ? `${harnessDisplayName(state.harness)} is listening. Say something.` : "No transcript yet.") }) : null,
1724
- blocks.map((block, index) => /* @__PURE__ */ jsxs4(Fragment2, { children: [
1814
+ blocks.map((block, index) => /* @__PURE__ */ jsxs4(Fragment3, { children: [
1725
1815
  index === unreadBlock ? /* @__PURE__ */ jsx5("div", { class: "scui-unread-divider", role: "separator", "aria-label": "New messages", children: /* @__PURE__ */ jsx5("span", { children: "New" }) }) : null,
1726
1816
  block.kind === "activity" ? /* @__PURE__ */ jsx5(Group, { value: block.entries, entries: block.entries, state, adapter }) : /* @__PURE__ */ jsx5(Entry, { value: block.entry, entry: block.entry, state, adapter })
1727
1817
  ] }, block.id)),
1728
1818
  pendingMessage ? /* @__PURE__ */ jsxs4("article", { class: "scui-message scui-pending", "data-role": "user", "data-status": pendingMessage.status, "aria-label": "Your pending message", children: [
1729
- /* @__PURE__ */ jsx5(MessageImages, { items: pendingMessage.images }),
1819
+ /* @__PURE__ */ jsx5(MessageImages, { items: pendingMessage.images, adapter }),
1730
1820
  /* @__PURE__ */ jsx5(Markdown, { value: pendingMessage.text, copyText: adapter?.copyText }),
1731
1821
  /* @__PURE__ */ jsx5(ContextDisclosure, { context: pendingMessage.context }),
1732
1822
  /* @__PURE__ */ jsxs4("footer", { children: [
@@ -1757,7 +1847,7 @@ function Conversation({ state, adapter, components = {}, slots = {}, memoryKey =
1757
1847
  }
1758
1848
 
1759
1849
  // src/logo.jsx
1760
- import { useEffect as useEffect4 } from "preact/hooks";
1850
+ import { useEffect as useEffect5 } from "preact/hooks";
1761
1851
  import { jsx as jsx6, jsxs as jsxs5 } from "preact/jsx-runtime";
1762
1852
  var LOGOS = {
1763
1853
  "claude-code": {
@@ -1805,7 +1895,7 @@ var LOGOS = {
1805
1895
  };
1806
1896
  function HarnessLogo({ id, activity, size = 28, onMissingLogo }) {
1807
1897
  const logo = LOGOS[id];
1808
- useEffect4(() => {
1898
+ useEffect5(() => {
1809
1899
  if (!logo) onMissingLogo?.(id);
1810
1900
  }, [id, logo, onMissingLogo]);
1811
1901
  if (!logo) return null;
@@ -1816,7 +1906,7 @@ function HarnessLogo({ id, activity, size = 28, onMissingLogo }) {
1816
1906
  }
1817
1907
 
1818
1908
  // src/sessions.jsx
1819
- import { useEffect as useEffect5, useLayoutEffect as useLayoutEffect3, useRef as useRef4, useState as useState3 } from "preact/hooks";
1909
+ import { useEffect as useEffect6, useLayoutEffect as useLayoutEffect3, useRef as useRef5, useState as useState4 } from "preact/hooks";
1820
1910
  import { jsx as jsx7, jsxs as jsxs6 } from "preact/jsx-runtime";
1821
1911
  var sessionListMemory = /* @__PURE__ */ new Map();
1822
1912
  function SessionRow({ row, state, onOpen }) {
@@ -1838,21 +1928,21 @@ function SessionRow({ row, state, onOpen }) {
1838
1928
  }
1839
1929
  function SessionList({ state, adapter, onOpen, onNew, onClose, components = {}, labels = DEFAULT_LABELS, focusKey = null, memoryKey = state.workspace || "@default" }) {
1840
1930
  const remembered = sessionListMemory.get(memoryKey) ?? { query: "", top: 0 };
1841
- const [query, setQuery] = useState3(remembered.query);
1842
- const [loadingMore, setLoadingMore] = useState3(false);
1843
- const root = useRef4(null);
1844
- const rowScroller = useRef4(null);
1931
+ const [query, setQuery] = useState4(remembered.query);
1932
+ const [loadingMore, setLoadingMore] = useState4(false);
1933
+ const root = useRef5(null);
1934
+ const rowScroller = useRef5(null);
1845
1935
  const rows = filterSessions(state.sessions, query);
1846
1936
  const Row = components.SessionRow ?? SessionRow;
1847
1937
  useLayoutEffect3(() => {
1848
1938
  if (rowScroller.current) rowScroller.current.scrollTop = remembered.top;
1849
1939
  }, [memoryKey]);
1850
- useEffect5(() => {
1940
+ useEffect6(() => {
1851
1941
  if (!focusKey || !root.current) return;
1852
1942
  const target = focusKey === "@new" ? root.current.querySelector('[data-list-focus="new"]') : [...root.current.querySelectorAll("[data-session-key]")].find((element) => element.dataset.sessionKey === focusKey);
1853
1943
  (target ?? root.current.querySelector("input,button"))?.focus({ preventScroll: true });
1854
1944
  }, [focusKey, rows.length]);
1855
- useEffect5(() => {
1945
+ useEffect6(() => {
1856
1946
  if (loadingMore) setLoadingMore(false);
1857
1947
  }, [state.error, state.history.hasMoreSessions, state.sessions.length]);
1858
1948
  const loadMore = () => {
@@ -1938,10 +2028,10 @@ function Receipt({ state, adapter }) {
1938
2028
  return null;
1939
2029
  }
1940
2030
  function ConversationActions({ state, adapter, actionPending }) {
1941
- const [open, setOpen] = useState4(false);
1942
- const root = useRef5(null);
1943
- const panel = useRef5(null);
1944
- const trigger = useRef5(null);
2031
+ const [open, setOpen] = useState5(false);
2032
+ const root = useRef6(null);
2033
+ const panel = useRef6(null);
2034
+ const trigger = useRef6(null);
1945
2035
  const menuId = useId2();
1946
2036
  const targets = state.harnesses.filter((item) => item.startable);
1947
2037
  const groups = [
@@ -1962,7 +2052,7 @@ function ConversationActions({ state, adapter, actionPending }) {
1962
2052
  items: state.canBranch ? targets.map((target) => ({ key: `branch:${target.id}`, label: target.id === state.harness ? `Fork in ${target.label}` : `Continue with ${target.label}`, intent: { action: "branch", targetHarness: target.id } })) : []
1963
2053
  }
1964
2054
  ].filter((group) => group.items.length);
1965
- useEffect6(() => {
2055
+ useEffect7(() => {
1966
2056
  if (!open) return;
1967
2057
  panel.current?.querySelector("button:not(:disabled)")?.focus({ preventScroll: true });
1968
2058
  const dismiss = (event) => {
@@ -2012,12 +2102,12 @@ function ConversationActions({ state, adapter, actionPending }) {
2012
2102
  ] });
2013
2103
  }
2014
2104
  function ChatHeader({ state, adapter, pendingStatus, actionPending, onBack, onNew, onClose }) {
2015
- const back = useRef5(null);
2105
+ const back = useRef6(null);
2016
2106
  const harness = state.attached?.harness ?? state.harness;
2017
2107
  const title = state.attached ? sessionDisplayName(state.attached) : harnessDisplayName(harness) || "Agent chat";
2018
2108
  const status = state.needsInput ? "Needs input" : pendingStatus === "failed" ? "Send failed" : state.busy ? "Working" : pendingStatus === "sending" ? "Sending" : pendingStatus === "editing" ? "Editing message" : state.mode === "mirror" ? "Read-only" : "Ready";
2019
2109
  const menu = state.canDetach || state.canOpenTerminal || state.canBranch || state.canAttach || state.canExport || state.canReduce;
2020
- useEffect6(() => {
2110
+ useEffect7(() => {
2021
2111
  if (state.mode === "mirror" && !state.canSend) back.current?.focus({ preventScroll: true });
2022
2112
  }, []);
2023
2113
  return /* @__PURE__ */ jsxs7("header", { class: "scui-head scui-chat-head", children: [
@@ -2039,24 +2129,24 @@ function ChatHeader({ state, adapter, pendingStatus, actionPending, onBack, onNe
2039
2129
  function Chat({ state, adapter, onBack, onNew, onClose, components, slots, labels }) {
2040
2130
  const Header = slots.header;
2041
2131
  const memoryKey = state.attached?.key || `${state.harness}:${state.workspace}`;
2042
- const [pending, setPendingState] = useState4(() => pendingMessageMemory.get(memoryKey) ?? null);
2043
- const [pendingAction, setPendingAction] = useState4(null);
2044
- const [restoreDraft, setRestoreDraft] = useState4(null);
2045
- const restoreSequence = useRef5(0);
2046
- const actionSequence = useRef5(0);
2047
- const acknowledged = useRef5(/* @__PURE__ */ new Set());
2132
+ const [pending, setPendingState] = useState5(() => pendingMessageMemory.get(memoryKey) ?? null);
2133
+ const [pendingAction, setPendingAction] = useState5(null);
2134
+ const [restoreDraft, setRestoreDraft] = useState5(null);
2135
+ const restoreSequence = useRef6(0);
2136
+ const actionSequence = useRef6(0);
2137
+ const acknowledged = useRef6(/* @__PURE__ */ new Set());
2048
2138
  const setPending = (update) => setPendingState((current) => {
2049
2139
  const next = typeof update === "function" ? update(current) : update;
2050
2140
  if (next) boundedSet(pendingMessageMemory, memoryKey, next);
2051
2141
  else pendingMessageMemory.delete(memoryKey);
2052
2142
  return next;
2053
2143
  });
2054
- useEffect6(() => {
2144
+ useEffect7(() => {
2055
2145
  setPendingState(pendingMessageMemory.get(memoryKey) ?? null);
2056
2146
  setPendingAction(null);
2057
2147
  setRestoreDraft(null);
2058
2148
  }, [memoryKey]);
2059
- useEffect6(() => {
2149
+ useEffect7(() => {
2060
2150
  if (!pending) return;
2061
2151
  if (state.transcript.some((entry) => entry.role === "user" && entry.text.trim() === pending.text.trim() && !pending.baselineIds.includes(entry.id))) {
2062
2152
  setPending(null);
@@ -2085,7 +2175,7 @@ function Chat({ state, adapter, onBack, onNew, onClose, components, slots, label
2085
2175
  setRestoreDraft({ id: restoreSequence.current, text: pending.text, context: pending.context, images: pending.images });
2086
2176
  setPending({ ...pending, status: "editing" });
2087
2177
  };
2088
- useEffect6(() => {
2178
+ useEffect7(() => {
2089
2179
  const key = state.attached?.key;
2090
2180
  if (!key || !state.attention.some((item) => item.key === key)) {
2091
2181
  if (key) acknowledged.current.delete(key);
@@ -2120,7 +2210,7 @@ function Chat({ state, adapter, onBack, onNew, onClose, components, slots, label
2120
2210
  return result;
2121
2211
  }
2122
2212
  }), [adapter, state.error]);
2123
- useEffect6(() => {
2213
+ useEffect7(() => {
2124
2214
  if (!pendingAction) return;
2125
2215
  if (state.operation && !pendingAction.seenOperation) {
2126
2216
  setPendingAction({ ...pendingAction, seenOperation: true });
@@ -2153,24 +2243,24 @@ function NewChat({ state, adapter, onBack, onClose, onStarted, labels, memoryKey
2153
2243
  const startable = state.harnesses.filter((item) => item.startable);
2154
2244
  const startableKey = startable.map((item) => item.id).join("\0");
2155
2245
  const remembered = newChatMemory.get(memoryKey) ?? { harness: startable[0]?.id ?? "", draft: "", context: [], images: [] };
2156
- const [harness, setHarness] = useState4(remembered.harness);
2157
- const [draft, setDraft] = useState4(remembered.draft);
2158
- const [context, setContext] = useState4(remembered.context);
2159
- const [images, setImages] = useState4(remembered.images ?? []);
2160
- const [starting, setStarting] = useState4(null);
2161
- const [picking, setPicking] = useState4(false);
2162
- const [dragging, setDragging] = useState4(false);
2163
- const [pickerError, setPickerError] = useState4(null);
2164
- const startSequence = useRef5(0);
2165
- const textarea = useRef5(null);
2246
+ const [harness, setHarness] = useState5(remembered.harness);
2247
+ const [draft, setDraft] = useState5(remembered.draft);
2248
+ const [context, setContext] = useState5(remembered.context);
2249
+ const [images, setImages] = useState5(remembered.images ?? []);
2250
+ const [starting, setStarting] = useState5(null);
2251
+ const [picking, setPicking] = useState5(false);
2252
+ const [dragging, setDragging] = useState5(false);
2253
+ const [pickerError, setPickerError] = useState5(null);
2254
+ const startSequence = useRef6(0);
2255
+ const textarea = useRef6(null);
2166
2256
  useAutosizeTextarea(textarea, draft);
2167
- useEffect6(() => {
2257
+ useEffect7(() => {
2168
2258
  if (startable.some((item) => item.id === harness)) return;
2169
2259
  const next = startable[0]?.id ?? "";
2170
2260
  setHarness(next);
2171
2261
  boundedSet(newChatMemory, memoryKey, { harness: next, draft, context, images });
2172
2262
  }, [harness, startableKey]);
2173
- useEffect6(() => {
2263
+ useEffect7(() => {
2174
2264
  if (!starting) return;
2175
2265
  const sessionChanged = (state.attached?.key ?? null) !== starting.attachedKey;
2176
2266
  const beganWorking = !starting.busy && state.busy;
@@ -2178,10 +2268,10 @@ function NewChat({ state, adapter, onBack, onClose, onStarted, labels, memoryKey
2178
2268
  newChatMemory.delete(memoryKey);
2179
2269
  onStarted();
2180
2270
  }, [memoryKey, onStarted, starting, state.attached?.key, state.busy]);
2181
- useEffect6(() => {
2271
+ useEffect7(() => {
2182
2272
  if (starting && state.error !== starting.initialError && !state.busy && !state.operation) setStarting(null);
2183
2273
  }, [starting, state.busy, state.error, state.operation]);
2184
- useEffect6(() => {
2274
+ useEffect7(() => {
2185
2275
  textarea.current?.focus({ preventScroll: true });
2186
2276
  }, []);
2187
2277
  const pickContext = () => {
@@ -2315,14 +2405,14 @@ function SupercodeMessenger({ state: stateInput, adapter, class: className = "",
2315
2405
  const state = useMemo2(() => normalizeUiState(stateInput), [stateInput]);
2316
2406
  const copy = { ...DEFAULT_LABELS, ...labels };
2317
2407
  const memoryKey = state.workspace || "@default";
2318
- const [view, setViewState] = useState4(initialView ?? (state.attention.length ? "list" : messengerViewMemory.get(memoryKey) ?? (state.attached || state.transcript.length ? "chat" : "list")));
2319
- const [opening, setOpening] = useState4(null);
2320
- const [listFocus, setListFocus] = useState4(null);
2408
+ const [view, setViewState] = useState5(initialView ?? (state.attention.length ? "list" : messengerViewMemory.get(memoryKey) ?? (state.attached || state.transcript.length ? "chat" : "list")));
2409
+ const [opening, setOpening] = useState5(null);
2410
+ const [listFocus, setListFocus] = useState5(null);
2321
2411
  const setView = (next) => {
2322
2412
  boundedSet(messengerViewMemory, memoryKey, next);
2323
2413
  setViewState(next);
2324
2414
  };
2325
- useEffect6(() => {
2415
+ useEffect7(() => {
2326
2416
  if (!opening) return;
2327
2417
  if (state.attached?.key === opening.key) {
2328
2418
  setOpening(null);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@volter-ai-dev/supercode-ui",
3
- "version": "0.1.22",
3
+ "version": "0.1.23",
4
4
  "type": "module",
5
5
  "description": "Composable default UI kit for Supercode-powered coding-agent experiences",
6
6
  "exports": {