@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/README.md +5 -0
- package/components.d.ts +2 -0
- package/components.mjs +200 -108
- package/composer.mjs +18 -17
- package/conversation.d.ts +2 -0
- package/conversation.mjs +136 -44
- package/embed.mjs +198 -108
- package/index.d.ts +2 -0
- package/messenger.mjs +198 -108
- package/package.json +1 -1
- package/sessions.mjs +14 -13
- package/styles.css +2 -1
package/embed.mjs
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { render } from "preact";
|
|
3
3
|
|
|
4
4
|
// src/messenger.jsx
|
|
5
|
-
import { useEffect as
|
|
5
|
+
import { useEffect as useEffect7, useId as useId2, useMemo as useMemo2, useRef as useRef6, useState as useState5 } from "preact/hooks";
|
|
6
6
|
|
|
7
7
|
// core.mjs
|
|
8
8
|
var HARNESS_NAMES = Object.freeze({
|
|
@@ -761,7 +761,7 @@ function isSendKey(event) {
|
|
|
761
761
|
}
|
|
762
762
|
|
|
763
763
|
// src/composer.jsx
|
|
764
|
-
import { useEffect, useRef, useState } from "preact/hooks";
|
|
764
|
+
import { useEffect as useEffect2, useRef as useRef2, useState as useState2 } from "preact/hooks";
|
|
765
765
|
|
|
766
766
|
// src/memory.js
|
|
767
767
|
var MEMORY_LIMIT = 100;
|
|
@@ -821,7 +821,8 @@ function UiIcon({ name, size = 16, class: className = "" }) {
|
|
|
821
821
|
}
|
|
822
822
|
|
|
823
823
|
// src/context.jsx
|
|
824
|
-
import {
|
|
824
|
+
import { useEffect, useRef, useState } from "preact/hooks";
|
|
825
|
+
import { Fragment as Fragment2, jsx as jsx2, jsxs as jsxs2 } from "preact/jsx-runtime";
|
|
825
826
|
var MAX_CONTEXT_ITEMS = 32;
|
|
826
827
|
var MAX_IMAGE_ITEMS = 4;
|
|
827
828
|
var MAX_IMAGE_BYTES = 5 * 1024 * 1024;
|
|
@@ -920,12 +921,101 @@ function ImageTray({ items, onRemove }) {
|
|
|
920
921
|
onRemove ? /* @__PURE__ */ jsx2("button", { type: "button", "aria-label": `Remove image ${item.label}`, onClick: () => onRemove(index), children: /* @__PURE__ */ jsx2(UiIcon, { name: "close", size: 12 }) }) : null
|
|
921
922
|
] }, item.id ?? `${item.label}:${index}`)) });
|
|
922
923
|
}
|
|
923
|
-
function
|
|
924
|
+
function imageFilename(label) {
|
|
925
|
+
const value = label.trim().replace(/[\\/:*?"<>|]+/g, "-");
|
|
926
|
+
return value || "image";
|
|
927
|
+
}
|
|
928
|
+
function ImageViewer({ items, index, adapter, onChange, onClose }) {
|
|
929
|
+
const dialog = useRef(null);
|
|
930
|
+
const reset = useRef(null);
|
|
931
|
+
const [copyState, setCopyState2] = useState("idle");
|
|
932
|
+
const item = items[index];
|
|
933
|
+
const remote = item?.url?.startsWith("http://") || item?.url?.startsWith("https://");
|
|
934
|
+
useEffect(() => {
|
|
935
|
+
if (!dialog.current?.open) dialog.current?.showModal();
|
|
936
|
+
return () => clearTimeout(reset.current);
|
|
937
|
+
}, []);
|
|
938
|
+
useEffect(() => {
|
|
939
|
+
clearTimeout(reset.current);
|
|
940
|
+
setCopyState2("idle");
|
|
941
|
+
}, [index]);
|
|
942
|
+
if (!item?.url) return null;
|
|
943
|
+
const move = (amount) => onChange((index + amount + items.length) % items.length);
|
|
944
|
+
const copy = async () => {
|
|
945
|
+
try {
|
|
946
|
+
await adapter.copyText(item.url);
|
|
947
|
+
setCopyState2("copied");
|
|
948
|
+
} catch {
|
|
949
|
+
setCopyState2("failed");
|
|
950
|
+
}
|
|
951
|
+
clearTimeout(reset.current);
|
|
952
|
+
reset.current = setTimeout(() => setCopyState2("idle"), 1500);
|
|
953
|
+
};
|
|
954
|
+
const close = () => dialog.current?.close();
|
|
955
|
+
return /* @__PURE__ */ jsxs2(
|
|
956
|
+
"dialog",
|
|
957
|
+
{
|
|
958
|
+
ref: dialog,
|
|
959
|
+
class: "scui-image-viewer",
|
|
960
|
+
"aria-label": `Image preview: ${item.label}`,
|
|
961
|
+
onClose,
|
|
962
|
+
onClick: (event) => {
|
|
963
|
+
if (event.target === event.currentTarget) close();
|
|
964
|
+
},
|
|
965
|
+
onKeyDown: (event) => {
|
|
966
|
+
if (items.length < 2 || !["ArrowLeft", "ArrowRight"].includes(event.key)) return;
|
|
967
|
+
event.preventDefault();
|
|
968
|
+
move(event.key === "ArrowLeft" ? -1 : 1);
|
|
969
|
+
},
|
|
970
|
+
children: [
|
|
971
|
+
/* @__PURE__ */ jsxs2("header", { children: [
|
|
972
|
+
/* @__PURE__ */ jsxs2("span", { children: [
|
|
973
|
+
/* @__PURE__ */ jsx2("strong", { children: item.label }),
|
|
974
|
+
items.length > 1 ? /* @__PURE__ */ jsxs2("small", { children: [
|
|
975
|
+
index + 1,
|
|
976
|
+
" of ",
|
|
977
|
+
items.length
|
|
978
|
+
] }) : null
|
|
979
|
+
] }),
|
|
980
|
+
/* @__PURE__ */ jsxs2("nav", { "aria-label": "Image actions", children: [
|
|
981
|
+
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,
|
|
982
|
+
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 }) }),
|
|
983
|
+
/* @__PURE__ */ jsx2("button", { type: "button", "aria-label": "Close image preview", title: "Close", onClick: close, children: /* @__PURE__ */ jsx2(UiIcon, { name: "close", size: 17 }) })
|
|
984
|
+
] })
|
|
985
|
+
] }),
|
|
986
|
+
/* @__PURE__ */ jsxs2("figure", { children: [
|
|
987
|
+
/* @__PURE__ */ jsx2("img", { src: item.url, alt: item.label }),
|
|
988
|
+
items.length > 1 ? /* @__PURE__ */ jsxs2(Fragment2, { children: [
|
|
989
|
+
/* @__PURE__ */ jsx2("button", { type: "button", class: "scui-image-previous", "aria-label": "Previous image", onClick: () => move(-1), children: /* @__PURE__ */ jsx2(UiIcon, { name: "chevron", size: 19 }) }),
|
|
990
|
+
/* @__PURE__ */ jsx2("button", { type: "button", class: "scui-image-next", "aria-label": "Next image", onClick: () => move(1), children: /* @__PURE__ */ jsx2(UiIcon, { name: "chevron", size: 19 }) })
|
|
991
|
+
] }) : null
|
|
992
|
+
] })
|
|
993
|
+
]
|
|
994
|
+
}
|
|
995
|
+
);
|
|
996
|
+
}
|
|
997
|
+
function MessageImages({ items, adapter }) {
|
|
998
|
+
const [active, setActive] = useState(null);
|
|
999
|
+
const opener = useRef(null);
|
|
924
1000
|
if (!items?.length) return null;
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
1001
|
+
const viewable = items.filter((item) => item.url);
|
|
1002
|
+
const close = () => {
|
|
1003
|
+
setActive(null);
|
|
1004
|
+
requestAnimationFrame(() => opener.current?.focus({ preventScroll: true }));
|
|
1005
|
+
};
|
|
1006
|
+
return /* @__PURE__ */ jsxs2(Fragment2, { children: [
|
|
1007
|
+
/* @__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) => {
|
|
1008
|
+
opener.current = event.currentTarget;
|
|
1009
|
+
setActive(viewable.indexOf(item));
|
|
1010
|
+
}, children: /* @__PURE__ */ jsx2("img", { src: item.url, alt: "" }) }, item.id ?? `${item.label}:${index}`) : /* @__PURE__ */ jsxs2("span", { "data-unavailable": "true", children: [
|
|
1011
|
+
/* @__PURE__ */ jsx2(UiIcon, { name: "image", size: 14 }),
|
|
1012
|
+
/* @__PURE__ */ jsxs2("span", { children: [
|
|
1013
|
+
/* @__PURE__ */ jsx2("strong", { children: item.label }),
|
|
1014
|
+
/* @__PURE__ */ jsx2("small", { children: "Preview unavailable" })
|
|
1015
|
+
] })
|
|
1016
|
+
] }, item.id ?? `${item.label}:${index}`)) }),
|
|
1017
|
+
active !== null && viewable[active] ? /* @__PURE__ */ jsx2(ImageViewer, { items: viewable, index: active, adapter, onChange: setActive, onClose: close }) : null
|
|
1018
|
+
] });
|
|
929
1019
|
}
|
|
930
1020
|
|
|
931
1021
|
// src/textarea.js
|
|
@@ -967,18 +1057,18 @@ function ContinuationBar({ state, adapter, labels = DEFAULT_LABELS }) {
|
|
|
967
1057
|
}
|
|
968
1058
|
function Composer({ state, adapter, labels = DEFAULT_LABELS, memoryKey = state.attached?.key || `${state.harness}:${state.workspace}`, pendingStatus = null, restoreDraft = null, onPending, onDraftRestored }) {
|
|
969
1059
|
const remembered = composerMemory.get(memoryKey) ?? { draft: state.savedDraft, context: [], images: [], queue: [] };
|
|
970
|
-
const [draft, setDraft] =
|
|
971
|
-
const [context, setContext] =
|
|
972
|
-
const [images, setImages] =
|
|
973
|
-
const [queue, setQueue] =
|
|
974
|
-
const [dispatching, setDispatching] =
|
|
975
|
-
const [picking, setPicking] =
|
|
976
|
-
const [dragging, setDragging] =
|
|
977
|
-
const [pickerError, setPickerError] =
|
|
978
|
-
const textarea =
|
|
1060
|
+
const [draft, setDraft] = useState2(remembered.draft);
|
|
1061
|
+
const [context, setContext] = useState2(remembered.context ?? []);
|
|
1062
|
+
const [images, setImages] = useState2(remembered.images ?? []);
|
|
1063
|
+
const [queue, setQueue] = useState2((remembered.queue ?? []).map((item) => ({ ...item, context: item.context ?? [], images: item.images ?? [] })));
|
|
1064
|
+
const [dispatching, setDispatching] = useState2(false);
|
|
1065
|
+
const [picking, setPicking] = useState2(false);
|
|
1066
|
+
const [dragging, setDragging] = useState2(false);
|
|
1067
|
+
const [pickerError, setPickerError] = useState2(null);
|
|
1068
|
+
const textarea = useRef2(null);
|
|
979
1069
|
useAutosizeTextarea(textarea, draft);
|
|
980
1070
|
const remember = (nextDraft, nextContext, nextImages, nextQueue) => boundedSet(composerMemory, memoryKey, { draft: nextDraft, context: nextContext, images: nextImages, queue: nextQueue });
|
|
981
|
-
|
|
1071
|
+
useEffect2(() => {
|
|
982
1072
|
remember(draft, context, images, queue);
|
|
983
1073
|
}, [draft, context, images, memoryKey, queue]);
|
|
984
1074
|
const updateQueue = (update) => setQueue((items) => {
|
|
@@ -988,7 +1078,7 @@ function Composer({ state, adapter, labels = DEFAULT_LABELS, memoryKey = state.a
|
|
|
988
1078
|
});
|
|
989
1079
|
const queueBlocked = state.busy || pendingStatus !== null || dispatching;
|
|
990
1080
|
const queuesNewMessage = state.busy || pendingStatus === "sending" || pendingStatus === "failed" || dispatching;
|
|
991
|
-
|
|
1081
|
+
useEffect2(() => {
|
|
992
1082
|
if (!queueBlocked && state.canSend && queue.length) {
|
|
993
1083
|
const [next, ...rest] = queue;
|
|
994
1084
|
setDispatching(true);
|
|
@@ -998,13 +1088,13 @@ function Composer({ state, adapter, labels = DEFAULT_LABELS, memoryKey = state.a
|
|
|
998
1088
|
adapter.onIntent({ action: "send", text: next.text, ...next.context.length ? { context: next.context } : {}, ...next.images.length ? { images: next.images } : {} });
|
|
999
1089
|
}
|
|
1000
1090
|
}, [adapter, draft, memoryKey, onPending, queue, queueBlocked, state.canSend]);
|
|
1001
|
-
|
|
1091
|
+
useEffect2(() => {
|
|
1002
1092
|
if (pendingStatus !== null || state.busy) setDispatching(false);
|
|
1003
1093
|
}, [pendingStatus, state.busy]);
|
|
1004
|
-
|
|
1094
|
+
useEffect2(() => {
|
|
1005
1095
|
textarea.current?.focus({ preventScroll: true });
|
|
1006
1096
|
}, [memoryKey]);
|
|
1007
|
-
|
|
1097
|
+
useEffect2(() => {
|
|
1008
1098
|
if (!restoreDraft) return;
|
|
1009
1099
|
setDraft(restoreDraft.text);
|
|
1010
1100
|
const restoredContext = normalizeContext(restoreDraft.context);
|
|
@@ -1015,7 +1105,7 @@ function Composer({ state, adapter, labels = DEFAULT_LABELS, memoryKey = state.a
|
|
|
1015
1105
|
textarea.current?.focus({ preventScroll: true });
|
|
1016
1106
|
onDraftRestored?.(restoreDraft.id);
|
|
1017
1107
|
}, [onDraftRestored, restoreDraft?.id]);
|
|
1018
|
-
|
|
1108
|
+
useEffect2(() => {
|
|
1019
1109
|
const timer = setTimeout(() => adapter.onIntent({ action: "draft", text: draft }), 250);
|
|
1020
1110
|
return () => clearTimeout(timer);
|
|
1021
1111
|
}, [adapter, draft]);
|
|
@@ -1140,12 +1230,12 @@ function Composer({ state, adapter, labels = DEFAULT_LABELS, memoryKey = state.a
|
|
|
1140
1230
|
}
|
|
1141
1231
|
|
|
1142
1232
|
// src/conversation.jsx
|
|
1143
|
-
import { Fragment as
|
|
1144
|
-
import { useEffect as
|
|
1233
|
+
import { Fragment as Fragment3 } from "preact";
|
|
1234
|
+
import { useEffect as useEffect4, useId, useLayoutEffect as useLayoutEffect2, useRef as useRef4, useState as useState3 } from "preact/hooks";
|
|
1145
1235
|
|
|
1146
1236
|
// src/markdown.jsx
|
|
1147
1237
|
import MarkdownIt from "markdown-it";
|
|
1148
|
-
import { useEffect as
|
|
1238
|
+
import { useEffect as useEffect3, useMemo, useRef as useRef3 } from "preact/hooks";
|
|
1149
1239
|
import { jsx as jsx4 } from "preact/jsx-runtime";
|
|
1150
1240
|
var markdown = new MarkdownIt({ html: false, linkify: true, breaks: false });
|
|
1151
1241
|
var LANGUAGE_LABELS = {
|
|
@@ -1207,8 +1297,8 @@ function setCopyState(button, status) {
|
|
|
1207
1297
|
}
|
|
1208
1298
|
function Markdown({ value, copyText }) {
|
|
1209
1299
|
const html = useMemo(() => markdown.render(value), [value]);
|
|
1210
|
-
const resets =
|
|
1211
|
-
|
|
1300
|
+
const resets = useRef3(/* @__PURE__ */ new Map());
|
|
1301
|
+
useEffect3(() => () => {
|
|
1212
1302
|
for (const timer of resets.current.values()) clearTimeout(timer);
|
|
1213
1303
|
resets.current.clear();
|
|
1214
1304
|
}, []);
|
|
@@ -1237,7 +1327,7 @@ function Markdown({ value, copyText }) {
|
|
|
1237
1327
|
}
|
|
1238
1328
|
|
|
1239
1329
|
// src/conversation.jsx
|
|
1240
|
-
import { Fragment as
|
|
1330
|
+
import { Fragment as Fragment4, jsx as jsx5, jsxs as jsxs4 } from "preact/jsx-runtime";
|
|
1241
1331
|
function LoadingStatus({ state, compact = false }) {
|
|
1242
1332
|
const copy = {
|
|
1243
1333
|
connecting: ["Connecting to coding agents", "Checking installed harnesses and capabilities.", 0],
|
|
@@ -1286,9 +1376,9 @@ function ContextDisclosure({ context }) {
|
|
|
1286
1376
|
] });
|
|
1287
1377
|
}
|
|
1288
1378
|
function MessageMeta({ entry, adapter }) {
|
|
1289
|
-
const [copyState, setCopyState2] =
|
|
1290
|
-
const reset =
|
|
1291
|
-
|
|
1379
|
+
const [copyState, setCopyState2] = useState3("idle");
|
|
1380
|
+
const reset = useRef4(null);
|
|
1381
|
+
useEffect4(() => () => clearTimeout(reset.current), []);
|
|
1292
1382
|
const date = entry.ts === null ? null : new Date(entry.ts);
|
|
1293
1383
|
const validDate = date && Number.isFinite(date.valueOf()) ? date : null;
|
|
1294
1384
|
if (!validDate && (!adapter?.copyText || !entry.text)) return null;
|
|
@@ -1309,33 +1399,33 @@ function MessageMeta({ entry, adapter }) {
|
|
|
1309
1399
|
] });
|
|
1310
1400
|
}
|
|
1311
1401
|
var TOOL_ICONS = {
|
|
1312
|
-
read: () => /* @__PURE__ */ jsxs4(
|
|
1402
|
+
read: () => /* @__PURE__ */ jsxs4(Fragment4, { children: [
|
|
1313
1403
|
/* @__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" }),
|
|
1314
1404
|
/* @__PURE__ */ jsx5("path", { d: "M12 2.9v3h2.85M6 9h6M6 12h4" })
|
|
1315
1405
|
] }),
|
|
1316
|
-
search: () => /* @__PURE__ */ jsxs4(
|
|
1406
|
+
search: () => /* @__PURE__ */ jsxs4(Fragment4, { children: [
|
|
1317
1407
|
/* @__PURE__ */ jsx5("circle", { cx: "8", cy: "8", r: "4.5" }),
|
|
1318
1408
|
/* @__PURE__ */ jsx5("path", { d: "m11.5 11.5 3 3" })
|
|
1319
1409
|
] }),
|
|
1320
|
-
edit: () => /* @__PURE__ */ jsxs4(
|
|
1410
|
+
edit: () => /* @__PURE__ */ jsxs4(Fragment4, { children: [
|
|
1321
1411
|
/* @__PURE__ */ jsx5("path", { d: "m11.75 3.25 3 3-8.5 8.5-3.75.75.75-3.75 8.5-8.5Z" }),
|
|
1322
1412
|
/* @__PURE__ */ jsx5("path", { d: "m10 5 3 3" })
|
|
1323
1413
|
] }),
|
|
1324
|
-
command: () => /* @__PURE__ */ jsx5(
|
|
1325
|
-
test: () => /* @__PURE__ */ jsxs4(
|
|
1414
|
+
command: () => /* @__PURE__ */ jsx5(Fragment4, { children: /* @__PURE__ */ jsx5("path", { d: "m3 5 3 3-3 3M8 12h6" }) }),
|
|
1415
|
+
test: () => /* @__PURE__ */ jsxs4(Fragment4, { children: [
|
|
1326
1416
|
/* @__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" }),
|
|
1327
1417
|
/* @__PURE__ */ jsx5("path", { d: "M5 2.5h8" })
|
|
1328
1418
|
] }),
|
|
1329
|
-
web: () => /* @__PURE__ */ jsxs4(
|
|
1419
|
+
web: () => /* @__PURE__ */ jsxs4(Fragment4, { children: [
|
|
1330
1420
|
/* @__PURE__ */ jsx5("circle", { cx: "9", cy: "9", r: "6.5" }),
|
|
1331
1421
|
/* @__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" })
|
|
1332
1422
|
] }),
|
|
1333
|
-
agent: () => /* @__PURE__ */ jsxs4(
|
|
1423
|
+
agent: () => /* @__PURE__ */ jsxs4(Fragment4, { children: [
|
|
1334
1424
|
/* @__PURE__ */ jsx5("circle", { cx: "9", cy: "6", r: "2.5" }),
|
|
1335
1425
|
/* @__PURE__ */ jsx5("path", { d: "M4 15c.4-3 2-4.5 5-4.5s4.6 1.5 5 4.5" })
|
|
1336
1426
|
] }),
|
|
1337
|
-
plan: () => /* @__PURE__ */ jsx5(
|
|
1338
|
-
other: () => /* @__PURE__ */ jsxs4(
|
|
1427
|
+
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" }) }),
|
|
1428
|
+
other: () => /* @__PURE__ */ jsxs4(Fragment4, { children: [
|
|
1339
1429
|
/* @__PURE__ */ jsx5("path", { d: "M9 2.5v3M9 12.5v3M2.5 9h3M12.5 9h3" }),
|
|
1340
1430
|
/* @__PURE__ */ jsx5("circle", { cx: "9", cy: "9", r: "3.5" })
|
|
1341
1431
|
] })
|
|
@@ -1401,9 +1491,9 @@ function ToolMetrics({ presentation }) {
|
|
|
1401
1491
|
}
|
|
1402
1492
|
function PendingElapsed({ now }) {
|
|
1403
1493
|
const clock = now ?? Date.now;
|
|
1404
|
-
const started =
|
|
1405
|
-
const [elapsed, setElapsed] =
|
|
1406
|
-
|
|
1494
|
+
const started = useRef4(clock());
|
|
1495
|
+
const [elapsed, setElapsed] = useState3(0);
|
|
1496
|
+
useEffect4(() => {
|
|
1407
1497
|
const timer = setInterval(() => setElapsed(Math.max(0, clock() - started.current)), 1e3);
|
|
1408
1498
|
return () => clearInterval(timer);
|
|
1409
1499
|
}, [clock]);
|
|
@@ -1444,7 +1534,7 @@ function SearchPreview({ presentation }) {
|
|
|
1444
1534
|
] }) : null,
|
|
1445
1535
|
lines.length ? /* @__PURE__ */ jsx5("ol", { children: lines.map((line, index) => {
|
|
1446
1536
|
const match = /^(.*?):(\d+)(?::(\d+))?:(.*)$/.exec(line);
|
|
1447
|
-
return /* @__PURE__ */ jsx5("li", { children: match ? /* @__PURE__ */ jsxs4(
|
|
1537
|
+
return /* @__PURE__ */ jsx5("li", { children: match ? /* @__PURE__ */ jsxs4(Fragment4, { children: [
|
|
1448
1538
|
/* @__PURE__ */ jsx5("code", { children: match[1] }),
|
|
1449
1539
|
/* @__PURE__ */ jsxs4("small", { children: [
|
|
1450
1540
|
match[2],
|
|
@@ -1477,9 +1567,9 @@ function ToolPreview({ presentation, entry }) {
|
|
|
1477
1567
|
return presentation.preview ? /* @__PURE__ */ jsx5("pre", { class: "scui-tool-output", "data-error": failed, children: stripAnsi(presentation.preview) }) : null;
|
|
1478
1568
|
}
|
|
1479
1569
|
function ToolActions({ presentation, adapter }) {
|
|
1480
|
-
const [copied, setCopied] =
|
|
1481
|
-
const reset =
|
|
1482
|
-
|
|
1570
|
+
const [copied, setCopied] = useState3(false);
|
|
1571
|
+
const reset = useRef4(null);
|
|
1572
|
+
useEffect4(() => () => clearTimeout(reset.current), []);
|
|
1483
1573
|
if (!adapter?.copyText) return null;
|
|
1484
1574
|
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;
|
|
1485
1575
|
const copy = async () => {
|
|
@@ -1526,7 +1616,7 @@ function TranscriptEntry({ entry, state, adapter }) {
|
|
|
1526
1616
|
}
|
|
1527
1617
|
if (entry.role === "notice" || entry.role === "system") return /* @__PURE__ */ jsx5("div", { class: "scui-notice", "data-code": entry.code, children: entry.text });
|
|
1528
1618
|
return /* @__PURE__ */ jsxs4("article", { class: "scui-message", "data-role": entry.role, "aria-label": `${entry.role === "user" ? "Your" : "Assistant"} message`, children: [
|
|
1529
|
-
/* @__PURE__ */ jsx5(MessageImages, { items: entry.images }),
|
|
1619
|
+
/* @__PURE__ */ jsx5(MessageImages, { items: entry.images, adapter }),
|
|
1530
1620
|
/* @__PURE__ */ jsx5(Markdown, { value: entry.text, copyText: adapter?.copyText }),
|
|
1531
1621
|
/* @__PURE__ */ jsx5(ContextDisclosure, { context: entry.context }),
|
|
1532
1622
|
entry.truncated ? /* @__PURE__ */ jsx5("small", { class: "scui-truncated", children: "Entry truncated by host \xB7 load native session for the complete value" }) : null,
|
|
@@ -1535,14 +1625,14 @@ function TranscriptEntry({ entry, state, adapter }) {
|
|
|
1535
1625
|
}
|
|
1536
1626
|
function ToolRow({ entry, workspace, open = false, adapter }) {
|
|
1537
1627
|
const presentation = entry.presentation ?? createToolPresentation(entry);
|
|
1538
|
-
const [expanded, setExpanded] =
|
|
1539
|
-
|
|
1628
|
+
const [expanded, setExpanded] = useState3(open || entry.status === "pending");
|
|
1629
|
+
useEffect4(() => {
|
|
1540
1630
|
if (entry.status === "pending") setExpanded(true);
|
|
1541
1631
|
}, [entry.status]);
|
|
1542
1632
|
const target = compactToolTarget(presentation.target, workspace);
|
|
1543
1633
|
const hasDetail = Boolean(entry.arguments || entry.resultText || presentation.preview || presentation.fields.length);
|
|
1544
1634
|
const category = presentation.category ?? toolCategory(entry);
|
|
1545
|
-
const summary = /* @__PURE__ */ jsxs4(
|
|
1635
|
+
const summary = /* @__PURE__ */ jsxs4(Fragment4, { children: [
|
|
1546
1636
|
/* @__PURE__ */ jsx5(ToolIcon, { category }),
|
|
1547
1637
|
/* @__PURE__ */ jsx5("strong", { children: toolAction2(entry, category, presentation) }),
|
|
1548
1638
|
target ? /* @__PURE__ */ jsx5("code", { class: "scui-tool-target", title: presentation.target, children: target }) : null,
|
|
@@ -1570,9 +1660,9 @@ function ToolRow({ entry, workspace, open = false, adapter }) {
|
|
|
1570
1660
|
function ActivityGroup({ entries, state, adapter }) {
|
|
1571
1661
|
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 }) });
|
|
1572
1662
|
const active = entries.some((entry) => entry.status === "pending");
|
|
1573
|
-
const [open, setOpen] =
|
|
1663
|
+
const [open, setOpen] = useState3(active);
|
|
1574
1664
|
const id = useId();
|
|
1575
|
-
|
|
1665
|
+
useEffect4(() => {
|
|
1576
1666
|
if (active) setOpen(true);
|
|
1577
1667
|
}, [active]);
|
|
1578
1668
|
return /* @__PURE__ */ jsxs4("section", { class: "scui-activity", children: [
|
|
@@ -1648,9 +1738,9 @@ function SessionDetails({ semantics }) {
|
|
|
1648
1738
|
}
|
|
1649
1739
|
var conversationMemory = /* @__PURE__ */ new Map();
|
|
1650
1740
|
function ConversationAnnouncements({ state }) {
|
|
1651
|
-
const previousBusy =
|
|
1652
|
-
const [announcement, setAnnouncement] =
|
|
1653
|
-
|
|
1741
|
+
const previousBusy = useRef4(state.busy);
|
|
1742
|
+
const [announcement, setAnnouncement] = useState3("");
|
|
1743
|
+
useEffect4(() => {
|
|
1654
1744
|
if (previousBusy.current && !state.busy && !state.error) {
|
|
1655
1745
|
setAnnouncement(`${harnessDisplayName(state.harness) || "Coding agent"} finished working`);
|
|
1656
1746
|
}
|
|
@@ -1659,13 +1749,13 @@ function ConversationAnnouncements({ state }) {
|
|
|
1659
1749
|
return /* @__PURE__ */ jsx5("span", { class: "scui-sr-only", role: "status", "aria-live": "polite", "aria-atomic": "true", children: announcement });
|
|
1660
1750
|
}
|
|
1661
1751
|
function Conversation({ state, adapter, components = {}, slots = {}, memoryKey = state.attached?.key || `${state.harness}:${state.workspace}`, pending = null, unreadAfterMessages = null }) {
|
|
1662
|
-
const scroller =
|
|
1752
|
+
const scroller = useRef4(null);
|
|
1663
1753
|
const remembered = conversationMemory.get(memoryKey) ?? { top: null, atBottom: true };
|
|
1664
|
-
const [atBottom, setAtBottom] =
|
|
1665
|
-
const earlierAnchor =
|
|
1666
|
-
const restored =
|
|
1754
|
+
const [atBottom, setAtBottom] = useState3(remembered.atBottom);
|
|
1755
|
+
const earlierAnchor = useRef4(null);
|
|
1756
|
+
const restored = useRef4(false);
|
|
1667
1757
|
const blocks = groupConversation(state.transcript);
|
|
1668
|
-
const unreadBoundary =
|
|
1758
|
+
const unreadBoundary = useRef4(Number.isSafeInteger(unreadAfterMessages) && unreadAfterMessages >= 0 ? unreadAfterMessages : null);
|
|
1669
1759
|
const unreadBlock = unreadBoundary.current === null ? -1 : blocks.findIndex((block) => {
|
|
1670
1760
|
const entries = block.kind === "activity" ? block.entries : [block.entry];
|
|
1671
1761
|
return entries.some((entry) => Number.isSafeInteger(entry.messageIndex) && entry.messageIndex > unreadBoundary.current);
|
|
@@ -1724,12 +1814,12 @@ function Conversation({ state, adapter, components = {}, slots = {}, memoryKey =
|
|
|
1724
1814
|
}, children: "Load earlier messages" }) : null,
|
|
1725
1815
|
!blocks.length && state.startup !== "ready" ? /* @__PURE__ */ jsx5(LoadingStatus, { state }) : null,
|
|
1726
1816
|
!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,
|
|
1727
|
-
blocks.map((block, index) => /* @__PURE__ */ jsxs4(
|
|
1817
|
+
blocks.map((block, index) => /* @__PURE__ */ jsxs4(Fragment3, { children: [
|
|
1728
1818
|
index === unreadBlock ? /* @__PURE__ */ jsx5("div", { class: "scui-unread-divider", role: "separator", "aria-label": "New messages", children: /* @__PURE__ */ jsx5("span", { children: "New" }) }) : null,
|
|
1729
1819
|
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 })
|
|
1730
1820
|
] }, block.id)),
|
|
1731
1821
|
pendingMessage ? /* @__PURE__ */ jsxs4("article", { class: "scui-message scui-pending", "data-role": "user", "data-status": pendingMessage.status, "aria-label": "Your pending message", children: [
|
|
1732
|
-
/* @__PURE__ */ jsx5(MessageImages, { items: pendingMessage.images }),
|
|
1822
|
+
/* @__PURE__ */ jsx5(MessageImages, { items: pendingMessage.images, adapter }),
|
|
1733
1823
|
/* @__PURE__ */ jsx5(Markdown, { value: pendingMessage.text, copyText: adapter?.copyText }),
|
|
1734
1824
|
/* @__PURE__ */ jsx5(ContextDisclosure, { context: pendingMessage.context }),
|
|
1735
1825
|
/* @__PURE__ */ jsxs4("footer", { children: [
|
|
@@ -1760,7 +1850,7 @@ function Conversation({ state, adapter, components = {}, slots = {}, memoryKey =
|
|
|
1760
1850
|
}
|
|
1761
1851
|
|
|
1762
1852
|
// src/logo.jsx
|
|
1763
|
-
import { useEffect as
|
|
1853
|
+
import { useEffect as useEffect5 } from "preact/hooks";
|
|
1764
1854
|
import { jsx as jsx6, jsxs as jsxs5 } from "preact/jsx-runtime";
|
|
1765
1855
|
var LOGOS = {
|
|
1766
1856
|
"claude-code": {
|
|
@@ -1808,7 +1898,7 @@ var LOGOS = {
|
|
|
1808
1898
|
};
|
|
1809
1899
|
function HarnessLogo({ id, activity, size = 28, onMissingLogo }) {
|
|
1810
1900
|
const logo = LOGOS[id];
|
|
1811
|
-
|
|
1901
|
+
useEffect5(() => {
|
|
1812
1902
|
if (!logo) onMissingLogo?.(id);
|
|
1813
1903
|
}, [id, logo, onMissingLogo]);
|
|
1814
1904
|
if (!logo) return null;
|
|
@@ -1819,7 +1909,7 @@ function HarnessLogo({ id, activity, size = 28, onMissingLogo }) {
|
|
|
1819
1909
|
}
|
|
1820
1910
|
|
|
1821
1911
|
// src/sessions.jsx
|
|
1822
|
-
import { useEffect as
|
|
1912
|
+
import { useEffect as useEffect6, useLayoutEffect as useLayoutEffect3, useRef as useRef5, useState as useState4 } from "preact/hooks";
|
|
1823
1913
|
import { jsx as jsx7, jsxs as jsxs6 } from "preact/jsx-runtime";
|
|
1824
1914
|
var sessionListMemory = /* @__PURE__ */ new Map();
|
|
1825
1915
|
function SessionRow({ row, state, onOpen }) {
|
|
@@ -1841,21 +1931,21 @@ function SessionRow({ row, state, onOpen }) {
|
|
|
1841
1931
|
}
|
|
1842
1932
|
function SessionList({ state, adapter, onOpen, onNew, onClose, components = {}, labels = DEFAULT_LABELS, focusKey = null, memoryKey = state.workspace || "@default" }) {
|
|
1843
1933
|
const remembered = sessionListMemory.get(memoryKey) ?? { query: "", top: 0 };
|
|
1844
|
-
const [query, setQuery] =
|
|
1845
|
-
const [loadingMore, setLoadingMore] =
|
|
1846
|
-
const root =
|
|
1847
|
-
const rowScroller =
|
|
1934
|
+
const [query, setQuery] = useState4(remembered.query);
|
|
1935
|
+
const [loadingMore, setLoadingMore] = useState4(false);
|
|
1936
|
+
const root = useRef5(null);
|
|
1937
|
+
const rowScroller = useRef5(null);
|
|
1848
1938
|
const rows = filterSessions(state.sessions, query);
|
|
1849
1939
|
const Row = components.SessionRow ?? SessionRow;
|
|
1850
1940
|
useLayoutEffect3(() => {
|
|
1851
1941
|
if (rowScroller.current) rowScroller.current.scrollTop = remembered.top;
|
|
1852
1942
|
}, [memoryKey]);
|
|
1853
|
-
|
|
1943
|
+
useEffect6(() => {
|
|
1854
1944
|
if (!focusKey || !root.current) return;
|
|
1855
1945
|
const target = focusKey === "@new" ? root.current.querySelector('[data-list-focus="new"]') : [...root.current.querySelectorAll("[data-session-key]")].find((element) => element.dataset.sessionKey === focusKey);
|
|
1856
1946
|
(target ?? root.current.querySelector("input,button"))?.focus({ preventScroll: true });
|
|
1857
1947
|
}, [focusKey, rows.length]);
|
|
1858
|
-
|
|
1948
|
+
useEffect6(() => {
|
|
1859
1949
|
if (loadingMore) setLoadingMore(false);
|
|
1860
1950
|
}, [state.error, state.history.hasMoreSessions, state.sessions.length]);
|
|
1861
1951
|
const loadMore = () => {
|
|
@@ -1941,10 +2031,10 @@ function Receipt({ state, adapter }) {
|
|
|
1941
2031
|
return null;
|
|
1942
2032
|
}
|
|
1943
2033
|
function ConversationActions({ state, adapter, actionPending }) {
|
|
1944
|
-
const [open, setOpen] =
|
|
1945
|
-
const root =
|
|
1946
|
-
const panel =
|
|
1947
|
-
const trigger =
|
|
2034
|
+
const [open, setOpen] = useState5(false);
|
|
2035
|
+
const root = useRef6(null);
|
|
2036
|
+
const panel = useRef6(null);
|
|
2037
|
+
const trigger = useRef6(null);
|
|
1948
2038
|
const menuId = useId2();
|
|
1949
2039
|
const targets = state.harnesses.filter((item) => item.startable);
|
|
1950
2040
|
const groups = [
|
|
@@ -1965,7 +2055,7 @@ function ConversationActions({ state, adapter, actionPending }) {
|
|
|
1965
2055
|
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 } })) : []
|
|
1966
2056
|
}
|
|
1967
2057
|
].filter((group) => group.items.length);
|
|
1968
|
-
|
|
2058
|
+
useEffect7(() => {
|
|
1969
2059
|
if (!open) return;
|
|
1970
2060
|
panel.current?.querySelector("button:not(:disabled)")?.focus({ preventScroll: true });
|
|
1971
2061
|
const dismiss = (event) => {
|
|
@@ -2015,12 +2105,12 @@ function ConversationActions({ state, adapter, actionPending }) {
|
|
|
2015
2105
|
] });
|
|
2016
2106
|
}
|
|
2017
2107
|
function ChatHeader({ state, adapter, pendingStatus, actionPending, onBack, onNew, onClose }) {
|
|
2018
|
-
const back =
|
|
2108
|
+
const back = useRef6(null);
|
|
2019
2109
|
const harness = state.attached?.harness ?? state.harness;
|
|
2020
2110
|
const title = state.attached ? sessionDisplayName(state.attached) : harnessDisplayName(harness) || "Agent chat";
|
|
2021
2111
|
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";
|
|
2022
2112
|
const menu = state.canDetach || state.canOpenTerminal || state.canBranch || state.canAttach || state.canExport || state.canReduce;
|
|
2023
|
-
|
|
2113
|
+
useEffect7(() => {
|
|
2024
2114
|
if (state.mode === "mirror" && !state.canSend) back.current?.focus({ preventScroll: true });
|
|
2025
2115
|
}, []);
|
|
2026
2116
|
return /* @__PURE__ */ jsxs7("header", { class: "scui-head scui-chat-head", children: [
|
|
@@ -2042,24 +2132,24 @@ function ChatHeader({ state, adapter, pendingStatus, actionPending, onBack, onNe
|
|
|
2042
2132
|
function Chat({ state, adapter, onBack, onNew, onClose, components, slots, labels }) {
|
|
2043
2133
|
const Header = slots.header;
|
|
2044
2134
|
const memoryKey = state.attached?.key || `${state.harness}:${state.workspace}`;
|
|
2045
|
-
const [pending, setPendingState] =
|
|
2046
|
-
const [pendingAction, setPendingAction] =
|
|
2047
|
-
const [restoreDraft, setRestoreDraft] =
|
|
2048
|
-
const restoreSequence =
|
|
2049
|
-
const actionSequence =
|
|
2050
|
-
const acknowledged =
|
|
2135
|
+
const [pending, setPendingState] = useState5(() => pendingMessageMemory.get(memoryKey) ?? null);
|
|
2136
|
+
const [pendingAction, setPendingAction] = useState5(null);
|
|
2137
|
+
const [restoreDraft, setRestoreDraft] = useState5(null);
|
|
2138
|
+
const restoreSequence = useRef6(0);
|
|
2139
|
+
const actionSequence = useRef6(0);
|
|
2140
|
+
const acknowledged = useRef6(/* @__PURE__ */ new Set());
|
|
2051
2141
|
const setPending = (update) => setPendingState((current) => {
|
|
2052
2142
|
const next = typeof update === "function" ? update(current) : update;
|
|
2053
2143
|
if (next) boundedSet(pendingMessageMemory, memoryKey, next);
|
|
2054
2144
|
else pendingMessageMemory.delete(memoryKey);
|
|
2055
2145
|
return next;
|
|
2056
2146
|
});
|
|
2057
|
-
|
|
2147
|
+
useEffect7(() => {
|
|
2058
2148
|
setPendingState(pendingMessageMemory.get(memoryKey) ?? null);
|
|
2059
2149
|
setPendingAction(null);
|
|
2060
2150
|
setRestoreDraft(null);
|
|
2061
2151
|
}, [memoryKey]);
|
|
2062
|
-
|
|
2152
|
+
useEffect7(() => {
|
|
2063
2153
|
if (!pending) return;
|
|
2064
2154
|
if (state.transcript.some((entry) => entry.role === "user" && entry.text.trim() === pending.text.trim() && !pending.baselineIds.includes(entry.id))) {
|
|
2065
2155
|
setPending(null);
|
|
@@ -2088,7 +2178,7 @@ function Chat({ state, adapter, onBack, onNew, onClose, components, slots, label
|
|
|
2088
2178
|
setRestoreDraft({ id: restoreSequence.current, text: pending.text, context: pending.context, images: pending.images });
|
|
2089
2179
|
setPending({ ...pending, status: "editing" });
|
|
2090
2180
|
};
|
|
2091
|
-
|
|
2181
|
+
useEffect7(() => {
|
|
2092
2182
|
const key = state.attached?.key;
|
|
2093
2183
|
if (!key || !state.attention.some((item) => item.key === key)) {
|
|
2094
2184
|
if (key) acknowledged.current.delete(key);
|
|
@@ -2123,7 +2213,7 @@ function Chat({ state, adapter, onBack, onNew, onClose, components, slots, label
|
|
|
2123
2213
|
return result;
|
|
2124
2214
|
}
|
|
2125
2215
|
}), [adapter, state.error]);
|
|
2126
|
-
|
|
2216
|
+
useEffect7(() => {
|
|
2127
2217
|
if (!pendingAction) return;
|
|
2128
2218
|
if (state.operation && !pendingAction.seenOperation) {
|
|
2129
2219
|
setPendingAction({ ...pendingAction, seenOperation: true });
|
|
@@ -2156,24 +2246,24 @@ function NewChat({ state, adapter, onBack, onClose, onStarted, labels, memoryKey
|
|
|
2156
2246
|
const startable = state.harnesses.filter((item) => item.startable);
|
|
2157
2247
|
const startableKey = startable.map((item) => item.id).join("\0");
|
|
2158
2248
|
const remembered = newChatMemory.get(memoryKey) ?? { harness: startable[0]?.id ?? "", draft: "", context: [], images: [] };
|
|
2159
|
-
const [harness, setHarness] =
|
|
2160
|
-
const [draft, setDraft] =
|
|
2161
|
-
const [context, setContext] =
|
|
2162
|
-
const [images, setImages] =
|
|
2163
|
-
const [starting, setStarting] =
|
|
2164
|
-
const [picking, setPicking] =
|
|
2165
|
-
const [dragging, setDragging] =
|
|
2166
|
-
const [pickerError, setPickerError] =
|
|
2167
|
-
const startSequence =
|
|
2168
|
-
const textarea =
|
|
2249
|
+
const [harness, setHarness] = useState5(remembered.harness);
|
|
2250
|
+
const [draft, setDraft] = useState5(remembered.draft);
|
|
2251
|
+
const [context, setContext] = useState5(remembered.context);
|
|
2252
|
+
const [images, setImages] = useState5(remembered.images ?? []);
|
|
2253
|
+
const [starting, setStarting] = useState5(null);
|
|
2254
|
+
const [picking, setPicking] = useState5(false);
|
|
2255
|
+
const [dragging, setDragging] = useState5(false);
|
|
2256
|
+
const [pickerError, setPickerError] = useState5(null);
|
|
2257
|
+
const startSequence = useRef6(0);
|
|
2258
|
+
const textarea = useRef6(null);
|
|
2169
2259
|
useAutosizeTextarea(textarea, draft);
|
|
2170
|
-
|
|
2260
|
+
useEffect7(() => {
|
|
2171
2261
|
if (startable.some((item) => item.id === harness)) return;
|
|
2172
2262
|
const next = startable[0]?.id ?? "";
|
|
2173
2263
|
setHarness(next);
|
|
2174
2264
|
boundedSet(newChatMemory, memoryKey, { harness: next, draft, context, images });
|
|
2175
2265
|
}, [harness, startableKey]);
|
|
2176
|
-
|
|
2266
|
+
useEffect7(() => {
|
|
2177
2267
|
if (!starting) return;
|
|
2178
2268
|
const sessionChanged = (state.attached?.key ?? null) !== starting.attachedKey;
|
|
2179
2269
|
const beganWorking = !starting.busy && state.busy;
|
|
@@ -2181,10 +2271,10 @@ function NewChat({ state, adapter, onBack, onClose, onStarted, labels, memoryKey
|
|
|
2181
2271
|
newChatMemory.delete(memoryKey);
|
|
2182
2272
|
onStarted();
|
|
2183
2273
|
}, [memoryKey, onStarted, starting, state.attached?.key, state.busy]);
|
|
2184
|
-
|
|
2274
|
+
useEffect7(() => {
|
|
2185
2275
|
if (starting && state.error !== starting.initialError && !state.busy && !state.operation) setStarting(null);
|
|
2186
2276
|
}, [starting, state.busy, state.error, state.operation]);
|
|
2187
|
-
|
|
2277
|
+
useEffect7(() => {
|
|
2188
2278
|
textarea.current?.focus({ preventScroll: true });
|
|
2189
2279
|
}, []);
|
|
2190
2280
|
const pickContext = () => {
|
|
@@ -2318,14 +2408,14 @@ function SupercodeMessenger({ state: stateInput, adapter, class: className = "",
|
|
|
2318
2408
|
const state = useMemo2(() => normalizeUiState(stateInput), [stateInput]);
|
|
2319
2409
|
const copy = { ...DEFAULT_LABELS, ...labels };
|
|
2320
2410
|
const memoryKey = state.workspace || "@default";
|
|
2321
|
-
const [view, setViewState] =
|
|
2322
|
-
const [opening, setOpening] =
|
|
2323
|
-
const [listFocus, setListFocus] =
|
|
2411
|
+
const [view, setViewState] = useState5(initialView ?? (state.attention.length ? "list" : messengerViewMemory.get(memoryKey) ?? (state.attached || state.transcript.length ? "chat" : "list")));
|
|
2412
|
+
const [opening, setOpening] = useState5(null);
|
|
2413
|
+
const [listFocus, setListFocus] = useState5(null);
|
|
2324
2414
|
const setView = (next) => {
|
|
2325
2415
|
boundedSet(messengerViewMemory, memoryKey, next);
|
|
2326
2416
|
setViewState(next);
|
|
2327
2417
|
};
|
|
2328
|
-
|
|
2418
|
+
useEffect7(() => {
|
|
2329
2419
|
if (!opening) return;
|
|
2330
2420
|
if (state.attached?.key === opening.key) {
|
|
2331
2421
|
setOpening(null);
|
package/index.d.ts
CHANGED
|
@@ -357,6 +357,8 @@ export function HarnessLogo(props: { id: HarnessId; activity?: SessionActivity;
|
|
|
357
357
|
export type UiIconName = 'attach' | 'back' | 'check' | 'chevron' | 'close' | 'copy' | 'down' | 'menu' | 'plus' | 'search' | 'send' | 'stop';
|
|
358
358
|
export function UiIcon(props: { name: UiIconName; size?: number; class?: string }): VNode | null;
|
|
359
359
|
export function hasHarnessLogo(id: string): boolean;
|
|
360
|
+
export function ImageViewer(props: { items: Array<TranscriptImage & { url: string }>; index: number; adapter?: Pick<UiAdapter, 'copyText'>; onChange(index: number): void; onClose(): void }): VNode | null;
|
|
361
|
+
export function MessageImages(props: { items?: TranscriptImage[]; adapter?: Pick<UiAdapter, 'copyText'> }): VNode | null;
|
|
360
362
|
export function LoadingStatus(props: { state: SupercodeUiState; compact?: boolean }): VNode;
|
|
361
363
|
export function RequestCard(props: { entry: TranscriptEntryModel; adapter: UiAdapter; canRespond: boolean }): VNode | null;
|
|
362
364
|
export function TranscriptEntry(props: { entry: TranscriptEntryModel; state: SupercodeUiState; adapter: UiAdapter }): VNode;
|