@volter-ai-dev/supercode-ui 0.1.22 → 0.1.24
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 +9 -0
- package/components.d.ts +2 -0
- package/components.mjs +260 -109
- package/composer.mjs +19 -17
- package/controller.d.ts +20 -0
- package/controller.mjs +69 -12
- package/conversation.d.ts +2 -0
- package/conversation.mjs +192 -44
- package/core.mjs +3 -0
- package/embed.mjs +258 -109
- package/index.d.ts +8 -0
- package/messenger.mjs +258 -109
- package/package.json +1 -1
- package/sessions.mjs +15 -13
- package/styles.css +2 -1
package/components.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({
|
|
@@ -507,7 +507,10 @@ function readTranscript(value) {
|
|
|
507
507
|
return image && typeof image.label === "string" ? [{
|
|
508
508
|
...typeof image.id === "string" ? { id: image.id } : {},
|
|
509
509
|
label: image.label,
|
|
510
|
-
...typeof image.url === "string" ? { url: image.url } : {}
|
|
510
|
+
...typeof image.url === "string" ? { url: image.url } : {},
|
|
511
|
+
...typeof image.reference === "string" && image.reference.length <= 4e3 ? { reference: image.reference } : {},
|
|
512
|
+
...typeof image.mediaType === "string" && image.mediaType.startsWith("image/") && image.mediaType.length <= 100 ? { mediaType: image.mediaType } : {},
|
|
513
|
+
...Number.isSafeInteger(image.byteSize) && image.byteSize >= 0 ? { byteSize: image.byteSize } : {}
|
|
511
514
|
}] : [];
|
|
512
515
|
}).slice(0, 4);
|
|
513
516
|
}
|
|
@@ -815,10 +818,12 @@ function UiIcon({ name, size = 16, class: className = "" }) {
|
|
|
815
818
|
}
|
|
816
819
|
|
|
817
820
|
// src/context.jsx
|
|
818
|
-
import {
|
|
821
|
+
import { useEffect, useRef, useState } from "preact/hooks";
|
|
822
|
+
import { Fragment as Fragment2, jsx as jsx2, jsxs as jsxs2 } from "preact/jsx-runtime";
|
|
819
823
|
var MAX_CONTEXT_ITEMS = 32;
|
|
820
824
|
var MAX_IMAGE_ITEMS = 4;
|
|
821
825
|
var MAX_IMAGE_BYTES = 5 * 1024 * 1024;
|
|
826
|
+
var MAX_RESOLVED_IMAGE_BYTES = 16 * 1024 * 1024;
|
|
822
827
|
var IMAGE_TYPES = /* @__PURE__ */ new Set(["image/png", "image/jpeg", "image/gif", "image/webp"]);
|
|
823
828
|
function normalizeContext(value) {
|
|
824
829
|
return (Array.isArray(value) ? value : value ? [value] : []).flatMap((item) => {
|
|
@@ -914,12 +919,156 @@ function ImageTray({ items, onRemove }) {
|
|
|
914
919
|
onRemove ? /* @__PURE__ */ jsx2("button", { type: "button", "aria-label": `Remove image ${item.label}`, onClick: () => onRemove(index), children: /* @__PURE__ */ jsx2(UiIcon, { name: "close", size: 12 }) }) : null
|
|
915
920
|
] }, item.id ?? `${item.label}:${index}`)) });
|
|
916
921
|
}
|
|
917
|
-
function
|
|
922
|
+
function imageFilename(label) {
|
|
923
|
+
const value = label.trim().replace(/[\\/:*?"<>|]+/g, "-");
|
|
924
|
+
return value || "image";
|
|
925
|
+
}
|
|
926
|
+
function ImageViewer({ items, index, adapter, onChange, onClose }) {
|
|
927
|
+
const dialog = useRef(null);
|
|
928
|
+
const reset = useRef(null);
|
|
929
|
+
const resolutions = useRef(/* @__PURE__ */ new Map());
|
|
930
|
+
const alive = useRef(true);
|
|
931
|
+
const [, redraw] = useState(0);
|
|
932
|
+
const [copyState, setCopyState2] = useState("idle");
|
|
933
|
+
const item = items[index];
|
|
934
|
+
const key = item?.reference ?? item?.url ?? `${item?.id ?? ""}:${index}`;
|
|
935
|
+
const resolution = item?.url ? null : resolutions.current.get(key);
|
|
936
|
+
const imageUrl = item?.url ?? (resolution?.status === "ready" ? resolution.url : null);
|
|
937
|
+
const remote = imageUrl?.startsWith("http://") || imageUrl?.startsWith("https://");
|
|
938
|
+
const resolve = (candidate, force = false) => {
|
|
939
|
+
if (candidate?.url || !candidate?.reference || !adapter?.resolveImage) return;
|
|
940
|
+
const candidateKey = candidate.reference;
|
|
941
|
+
const current = resolutions.current.get(candidateKey);
|
|
942
|
+
if (!force && (current?.status === "loading" || current?.status === "ready")) return;
|
|
943
|
+
if (current?.url) URL.revokeObjectURL(current.url);
|
|
944
|
+
resolutions.current.set(candidateKey, { status: "loading" });
|
|
945
|
+
redraw((value) => value + 1);
|
|
946
|
+
Promise.resolve().then(() => adapter.resolveImage(candidate)).then((blob) => {
|
|
947
|
+
if (!(blob instanceof Blob) || !blob.type.startsWith("image/")) throw new Error("The host returned an invalid image.");
|
|
948
|
+
if (blob.size > MAX_RESOLVED_IMAGE_BYTES) throw new Error("This image is too large to preview safely.");
|
|
949
|
+
if (!alive.current) return;
|
|
950
|
+
const url = URL.createObjectURL(blob);
|
|
951
|
+
resolutions.current.set(candidateKey, { status: "ready", url });
|
|
952
|
+
redraw((value) => value + 1);
|
|
953
|
+
}).catch((error) => {
|
|
954
|
+
if (!alive.current) return;
|
|
955
|
+
resolutions.current.set(candidateKey, {
|
|
956
|
+
status: "error",
|
|
957
|
+
message: error instanceof Error && error.message ? error.message : "Could not load this image."
|
|
958
|
+
});
|
|
959
|
+
redraw((value) => value + 1);
|
|
960
|
+
});
|
|
961
|
+
};
|
|
962
|
+
useEffect(() => {
|
|
963
|
+
alive.current = true;
|
|
964
|
+
if (!dialog.current?.open) dialog.current?.showModal();
|
|
965
|
+
return () => {
|
|
966
|
+
alive.current = false;
|
|
967
|
+
clearTimeout(reset.current);
|
|
968
|
+
for (const value of resolutions.current.values()) if (value.url) URL.revokeObjectURL(value.url);
|
|
969
|
+
resolutions.current.clear();
|
|
970
|
+
};
|
|
971
|
+
}, []);
|
|
972
|
+
useEffect(() => {
|
|
973
|
+
clearTimeout(reset.current);
|
|
974
|
+
setCopyState2("idle");
|
|
975
|
+
resolve(item);
|
|
976
|
+
}, [index, item?.reference]);
|
|
977
|
+
if (!item || !item.url && (!item.reference || !adapter?.resolveImage)) return null;
|
|
978
|
+
const move = (amount) => onChange((index + amount + items.length) % items.length);
|
|
979
|
+
const copy = async () => {
|
|
980
|
+
try {
|
|
981
|
+
await adapter.copyText(imageUrl);
|
|
982
|
+
setCopyState2("copied");
|
|
983
|
+
} catch {
|
|
984
|
+
setCopyState2("failed");
|
|
985
|
+
}
|
|
986
|
+
clearTimeout(reset.current);
|
|
987
|
+
reset.current = setTimeout(() => setCopyState2("idle"), 1500);
|
|
988
|
+
};
|
|
989
|
+
const close = () => dialog.current?.close();
|
|
990
|
+
return /* @__PURE__ */ jsxs2(
|
|
991
|
+
"dialog",
|
|
992
|
+
{
|
|
993
|
+
ref: dialog,
|
|
994
|
+
class: "scui-image-viewer",
|
|
995
|
+
"aria-label": `Image preview: ${item.label}`,
|
|
996
|
+
onClose,
|
|
997
|
+
onClick: (event) => {
|
|
998
|
+
if (event.target === event.currentTarget) close();
|
|
999
|
+
},
|
|
1000
|
+
onKeyDown: (event) => {
|
|
1001
|
+
if (items.length < 2 || !["ArrowLeft", "ArrowRight"].includes(event.key)) return;
|
|
1002
|
+
event.preventDefault();
|
|
1003
|
+
move(event.key === "ArrowLeft" ? -1 : 1);
|
|
1004
|
+
},
|
|
1005
|
+
children: [
|
|
1006
|
+
/* @__PURE__ */ jsxs2("header", { children: [
|
|
1007
|
+
/* @__PURE__ */ jsxs2("span", { children: [
|
|
1008
|
+
/* @__PURE__ */ jsx2("strong", { children: item.label }),
|
|
1009
|
+
items.length > 1 ? /* @__PURE__ */ jsxs2("small", { children: [
|
|
1010
|
+
index + 1,
|
|
1011
|
+
" of ",
|
|
1012
|
+
items.length
|
|
1013
|
+
] }) : null
|
|
1014
|
+
] }),
|
|
1015
|
+
/* @__PURE__ */ jsxs2("nav", { "aria-label": "Image actions", children: [
|
|
1016
|
+
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,
|
|
1017
|
+
imageUrl ? remote ? /* @__PURE__ */ jsx2("a", { href: imageUrl, 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: imageUrl, download: imageFilename(item.label), "aria-label": "Download image", title: "Download image", children: /* @__PURE__ */ jsx2(UiIcon, { name: "down", size: 16 }) }) : null,
|
|
1018
|
+
/* @__PURE__ */ jsx2("button", { type: "button", "aria-label": "Close image preview", title: "Close", onClick: close, children: /* @__PURE__ */ jsx2(UiIcon, { name: "close", size: 17 }) })
|
|
1019
|
+
] })
|
|
1020
|
+
] }),
|
|
1021
|
+
/* @__PURE__ */ jsxs2("figure", { children: [
|
|
1022
|
+
imageUrl ? /* @__PURE__ */ jsx2("img", { src: imageUrl, alt: item.label }) : resolution?.status === "error" ? /* @__PURE__ */ jsxs2("div", { class: "scui-image-resolution", role: "alert", children: [
|
|
1023
|
+
/* @__PURE__ */ jsx2(UiIcon, { name: "image", size: 28 }),
|
|
1024
|
+
/* @__PURE__ */ jsx2("strong", { children: "Could not load image" }),
|
|
1025
|
+
/* @__PURE__ */ jsx2("small", { children: resolution.message }),
|
|
1026
|
+
/* @__PURE__ */ jsx2("button", { type: "button", onClick: () => resolve(item, true), children: "Retry" })
|
|
1027
|
+
] }) : /* @__PURE__ */ jsxs2("div", { class: "scui-image-resolution", role: "status", children: [
|
|
1028
|
+
/* @__PURE__ */ jsx2("i", { class: "scui-control-spinner" }),
|
|
1029
|
+
/* @__PURE__ */ jsx2("strong", { children: "Loading image\u2026" }),
|
|
1030
|
+
/* @__PURE__ */ jsx2("small", { children: "The original stays out of the transcript payload." })
|
|
1031
|
+
] }),
|
|
1032
|
+
items.length > 1 ? /* @__PURE__ */ jsxs2(Fragment2, { children: [
|
|
1033
|
+
/* @__PURE__ */ jsx2("button", { type: "button", class: "scui-image-previous", "aria-label": "Previous image", onClick: () => move(-1), children: /* @__PURE__ */ jsx2(UiIcon, { name: "chevron", size: 19 }) }),
|
|
1034
|
+
/* @__PURE__ */ jsx2("button", { type: "button", class: "scui-image-next", "aria-label": "Next image", onClick: () => move(1), children: /* @__PURE__ */ jsx2(UiIcon, { name: "chevron", size: 19 }) })
|
|
1035
|
+
] }) : null
|
|
1036
|
+
] })
|
|
1037
|
+
]
|
|
1038
|
+
}
|
|
1039
|
+
);
|
|
1040
|
+
}
|
|
1041
|
+
function MessageImages({ items, adapter }) {
|
|
1042
|
+
const [active, setActive] = useState(null);
|
|
1043
|
+
const opener = useRef(null);
|
|
918
1044
|
if (!items?.length) return null;
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
1045
|
+
const viewable = items.filter((item) => item.url || item.reference && adapter?.resolveImage);
|
|
1046
|
+
const close = () => {
|
|
1047
|
+
setActive(null);
|
|
1048
|
+
requestAnimationFrame(() => opener.current?.focus({ preventScroll: true }));
|
|
1049
|
+
};
|
|
1050
|
+
return /* @__PURE__ */ jsxs2(Fragment2, { children: [
|
|
1051
|
+
/* @__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) => {
|
|
1052
|
+
opener.current = event.currentTarget;
|
|
1053
|
+
setActive(viewable.indexOf(item));
|
|
1054
|
+
}, children: /* @__PURE__ */ jsx2("img", { src: item.url, alt: "" }) }, item.id ?? `${item.label}:${index}`) : item.reference && adapter?.resolveImage ? /* @__PURE__ */ jsxs2("button", { type: "button", "data-lazy": "true", "aria-label": `Load image ${item.label}`, onClick: (event) => {
|
|
1055
|
+
opener.current = event.currentTarget;
|
|
1056
|
+
setActive(viewable.indexOf(item));
|
|
1057
|
+
}, children: [
|
|
1058
|
+
/* @__PURE__ */ jsx2(UiIcon, { name: "image", size: 16 }),
|
|
1059
|
+
/* @__PURE__ */ jsxs2("span", { children: [
|
|
1060
|
+
/* @__PURE__ */ jsx2("strong", { children: item.label }),
|
|
1061
|
+
/* @__PURE__ */ jsx2("small", { children: "Load preview" })
|
|
1062
|
+
] })
|
|
1063
|
+
] }, item.id ?? `${item.label}:${index}`) : /* @__PURE__ */ jsxs2("span", { "data-unavailable": "true", children: [
|
|
1064
|
+
/* @__PURE__ */ jsx2(UiIcon, { name: "image", size: 14 }),
|
|
1065
|
+
/* @__PURE__ */ jsxs2("span", { children: [
|
|
1066
|
+
/* @__PURE__ */ jsx2("strong", { children: item.label }),
|
|
1067
|
+
/* @__PURE__ */ jsx2("small", { children: "Preview unavailable" })
|
|
1068
|
+
] })
|
|
1069
|
+
] }, item.id ?? `${item.label}:${index}`)) }),
|
|
1070
|
+
active !== null && viewable[active] ? /* @__PURE__ */ jsx2(ImageViewer, { items: viewable, index: active, adapter, onChange: setActive, onClose: close }) : null
|
|
1071
|
+
] });
|
|
923
1072
|
}
|
|
924
1073
|
|
|
925
1074
|
// src/textarea.js
|
|
@@ -961,18 +1110,18 @@ function ContinuationBar({ state, adapter, labels = DEFAULT_LABELS }) {
|
|
|
961
1110
|
}
|
|
962
1111
|
function Composer({ state, adapter, labels = DEFAULT_LABELS, memoryKey = state.attached?.key || `${state.harness}:${state.workspace}`, pendingStatus = null, restoreDraft = null, onPending, onDraftRestored }) {
|
|
963
1112
|
const remembered = composerMemory.get(memoryKey) ?? { draft: state.savedDraft, context: [], images: [], queue: [] };
|
|
964
|
-
const [draft, setDraft] =
|
|
965
|
-
const [context, setContext] =
|
|
966
|
-
const [images, setImages] =
|
|
967
|
-
const [queue, setQueue] =
|
|
968
|
-
const [dispatching, setDispatching] =
|
|
969
|
-
const [picking, setPicking] =
|
|
970
|
-
const [dragging, setDragging] =
|
|
971
|
-
const [pickerError, setPickerError] =
|
|
972
|
-
const textarea =
|
|
1113
|
+
const [draft, setDraft] = useState2(remembered.draft);
|
|
1114
|
+
const [context, setContext] = useState2(remembered.context ?? []);
|
|
1115
|
+
const [images, setImages] = useState2(remembered.images ?? []);
|
|
1116
|
+
const [queue, setQueue] = useState2((remembered.queue ?? []).map((item) => ({ ...item, context: item.context ?? [], images: item.images ?? [] })));
|
|
1117
|
+
const [dispatching, setDispatching] = useState2(false);
|
|
1118
|
+
const [picking, setPicking] = useState2(false);
|
|
1119
|
+
const [dragging, setDragging] = useState2(false);
|
|
1120
|
+
const [pickerError, setPickerError] = useState2(null);
|
|
1121
|
+
const textarea = useRef2(null);
|
|
973
1122
|
useAutosizeTextarea(textarea, draft);
|
|
974
1123
|
const remember = (nextDraft, nextContext, nextImages, nextQueue) => boundedSet(composerMemory, memoryKey, { draft: nextDraft, context: nextContext, images: nextImages, queue: nextQueue });
|
|
975
|
-
|
|
1124
|
+
useEffect2(() => {
|
|
976
1125
|
remember(draft, context, images, queue);
|
|
977
1126
|
}, [draft, context, images, memoryKey, queue]);
|
|
978
1127
|
const updateQueue = (update) => setQueue((items) => {
|
|
@@ -982,7 +1131,7 @@ function Composer({ state, adapter, labels = DEFAULT_LABELS, memoryKey = state.a
|
|
|
982
1131
|
});
|
|
983
1132
|
const queueBlocked = state.busy || pendingStatus !== null || dispatching;
|
|
984
1133
|
const queuesNewMessage = state.busy || pendingStatus === "sending" || pendingStatus === "failed" || dispatching;
|
|
985
|
-
|
|
1134
|
+
useEffect2(() => {
|
|
986
1135
|
if (!queueBlocked && state.canSend && queue.length) {
|
|
987
1136
|
const [next, ...rest] = queue;
|
|
988
1137
|
setDispatching(true);
|
|
@@ -992,13 +1141,13 @@ function Composer({ state, adapter, labels = DEFAULT_LABELS, memoryKey = state.a
|
|
|
992
1141
|
adapter.onIntent({ action: "send", text: next.text, ...next.context.length ? { context: next.context } : {}, ...next.images.length ? { images: next.images } : {} });
|
|
993
1142
|
}
|
|
994
1143
|
}, [adapter, draft, memoryKey, onPending, queue, queueBlocked, state.canSend]);
|
|
995
|
-
|
|
1144
|
+
useEffect2(() => {
|
|
996
1145
|
if (pendingStatus !== null || state.busy) setDispatching(false);
|
|
997
1146
|
}, [pendingStatus, state.busy]);
|
|
998
|
-
|
|
1147
|
+
useEffect2(() => {
|
|
999
1148
|
textarea.current?.focus({ preventScroll: true });
|
|
1000
1149
|
}, [memoryKey]);
|
|
1001
|
-
|
|
1150
|
+
useEffect2(() => {
|
|
1002
1151
|
if (!restoreDraft) return;
|
|
1003
1152
|
setDraft(restoreDraft.text);
|
|
1004
1153
|
const restoredContext = normalizeContext(restoreDraft.context);
|
|
@@ -1009,7 +1158,7 @@ function Composer({ state, adapter, labels = DEFAULT_LABELS, memoryKey = state.a
|
|
|
1009
1158
|
textarea.current?.focus({ preventScroll: true });
|
|
1010
1159
|
onDraftRestored?.(restoreDraft.id);
|
|
1011
1160
|
}, [onDraftRestored, restoreDraft?.id]);
|
|
1012
|
-
|
|
1161
|
+
useEffect2(() => {
|
|
1013
1162
|
const timer = setTimeout(() => adapter.onIntent({ action: "draft", text: draft }), 250);
|
|
1014
1163
|
return () => clearTimeout(timer);
|
|
1015
1164
|
}, [adapter, draft]);
|
|
@@ -1134,12 +1283,12 @@ function Composer({ state, adapter, labels = DEFAULT_LABELS, memoryKey = state.a
|
|
|
1134
1283
|
}
|
|
1135
1284
|
|
|
1136
1285
|
// src/conversation.jsx
|
|
1137
|
-
import { Fragment as
|
|
1138
|
-
import { useEffect as
|
|
1286
|
+
import { Fragment as Fragment3 } from "preact";
|
|
1287
|
+
import { useEffect as useEffect4, useId, useLayoutEffect as useLayoutEffect2, useRef as useRef4, useState as useState3 } from "preact/hooks";
|
|
1139
1288
|
|
|
1140
1289
|
// src/markdown.jsx
|
|
1141
1290
|
import MarkdownIt from "markdown-it";
|
|
1142
|
-
import { useEffect as
|
|
1291
|
+
import { useEffect as useEffect3, useMemo, useRef as useRef3 } from "preact/hooks";
|
|
1143
1292
|
import { jsx as jsx4 } from "preact/jsx-runtime";
|
|
1144
1293
|
var markdown = new MarkdownIt({ html: false, linkify: true, breaks: false });
|
|
1145
1294
|
var LANGUAGE_LABELS = {
|
|
@@ -1201,8 +1350,8 @@ function setCopyState(button, status) {
|
|
|
1201
1350
|
}
|
|
1202
1351
|
function Markdown({ value, copyText }) {
|
|
1203
1352
|
const html = useMemo(() => markdown.render(value), [value]);
|
|
1204
|
-
const resets =
|
|
1205
|
-
|
|
1353
|
+
const resets = useRef3(/* @__PURE__ */ new Map());
|
|
1354
|
+
useEffect3(() => () => {
|
|
1206
1355
|
for (const timer of resets.current.values()) clearTimeout(timer);
|
|
1207
1356
|
resets.current.clear();
|
|
1208
1357
|
}, []);
|
|
@@ -1231,7 +1380,7 @@ function Markdown({ value, copyText }) {
|
|
|
1231
1380
|
}
|
|
1232
1381
|
|
|
1233
1382
|
// src/conversation.jsx
|
|
1234
|
-
import { Fragment as
|
|
1383
|
+
import { Fragment as Fragment4, jsx as jsx5, jsxs as jsxs4 } from "preact/jsx-runtime";
|
|
1235
1384
|
function LoadingStatus({ state, compact = false }) {
|
|
1236
1385
|
const copy = {
|
|
1237
1386
|
connecting: ["Connecting to coding agents", "Checking installed harnesses and capabilities.", 0],
|
|
@@ -1280,9 +1429,9 @@ function ContextDisclosure({ context }) {
|
|
|
1280
1429
|
] });
|
|
1281
1430
|
}
|
|
1282
1431
|
function MessageMeta({ entry, adapter }) {
|
|
1283
|
-
const [copyState, setCopyState2] =
|
|
1284
|
-
const reset =
|
|
1285
|
-
|
|
1432
|
+
const [copyState, setCopyState2] = useState3("idle");
|
|
1433
|
+
const reset = useRef4(null);
|
|
1434
|
+
useEffect4(() => () => clearTimeout(reset.current), []);
|
|
1286
1435
|
const date = entry.ts === null ? null : new Date(entry.ts);
|
|
1287
1436
|
const validDate = date && Number.isFinite(date.valueOf()) ? date : null;
|
|
1288
1437
|
if (!validDate && (!adapter?.copyText || !entry.text)) return null;
|
|
@@ -1303,33 +1452,33 @@ function MessageMeta({ entry, adapter }) {
|
|
|
1303
1452
|
] });
|
|
1304
1453
|
}
|
|
1305
1454
|
var TOOL_ICONS = {
|
|
1306
|
-
read: () => /* @__PURE__ */ jsxs4(
|
|
1455
|
+
read: () => /* @__PURE__ */ jsxs4(Fragment4, { children: [
|
|
1307
1456
|
/* @__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" }),
|
|
1308
1457
|
/* @__PURE__ */ jsx5("path", { d: "M12 2.9v3h2.85M6 9h6M6 12h4" })
|
|
1309
1458
|
] }),
|
|
1310
|
-
search: () => /* @__PURE__ */ jsxs4(
|
|
1459
|
+
search: () => /* @__PURE__ */ jsxs4(Fragment4, { children: [
|
|
1311
1460
|
/* @__PURE__ */ jsx5("circle", { cx: "8", cy: "8", r: "4.5" }),
|
|
1312
1461
|
/* @__PURE__ */ jsx5("path", { d: "m11.5 11.5 3 3" })
|
|
1313
1462
|
] }),
|
|
1314
|
-
edit: () => /* @__PURE__ */ jsxs4(
|
|
1463
|
+
edit: () => /* @__PURE__ */ jsxs4(Fragment4, { children: [
|
|
1315
1464
|
/* @__PURE__ */ jsx5("path", { d: "m11.75 3.25 3 3-8.5 8.5-3.75.75.75-3.75 8.5-8.5Z" }),
|
|
1316
1465
|
/* @__PURE__ */ jsx5("path", { d: "m10 5 3 3" })
|
|
1317
1466
|
] }),
|
|
1318
|
-
command: () => /* @__PURE__ */ jsx5(
|
|
1319
|
-
test: () => /* @__PURE__ */ jsxs4(
|
|
1467
|
+
command: () => /* @__PURE__ */ jsx5(Fragment4, { children: /* @__PURE__ */ jsx5("path", { d: "m3 5 3 3-3 3M8 12h6" }) }),
|
|
1468
|
+
test: () => /* @__PURE__ */ jsxs4(Fragment4, { children: [
|
|
1320
1469
|
/* @__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" }),
|
|
1321
1470
|
/* @__PURE__ */ jsx5("path", { d: "M5 2.5h8" })
|
|
1322
1471
|
] }),
|
|
1323
|
-
web: () => /* @__PURE__ */ jsxs4(
|
|
1472
|
+
web: () => /* @__PURE__ */ jsxs4(Fragment4, { children: [
|
|
1324
1473
|
/* @__PURE__ */ jsx5("circle", { cx: "9", cy: "9", r: "6.5" }),
|
|
1325
1474
|
/* @__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" })
|
|
1326
1475
|
] }),
|
|
1327
|
-
agent: () => /* @__PURE__ */ jsxs4(
|
|
1476
|
+
agent: () => /* @__PURE__ */ jsxs4(Fragment4, { children: [
|
|
1328
1477
|
/* @__PURE__ */ jsx5("circle", { cx: "9", cy: "6", r: "2.5" }),
|
|
1329
1478
|
/* @__PURE__ */ jsx5("path", { d: "M4 15c.4-3 2-4.5 5-4.5s4.6 1.5 5 4.5" })
|
|
1330
1479
|
] }),
|
|
1331
|
-
plan: () => /* @__PURE__ */ jsx5(
|
|
1332
|
-
other: () => /* @__PURE__ */ jsxs4(
|
|
1480
|
+
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" }) }),
|
|
1481
|
+
other: () => /* @__PURE__ */ jsxs4(Fragment4, { children: [
|
|
1333
1482
|
/* @__PURE__ */ jsx5("path", { d: "M9 2.5v3M9 12.5v3M2.5 9h3M12.5 9h3" }),
|
|
1334
1483
|
/* @__PURE__ */ jsx5("circle", { cx: "9", cy: "9", r: "3.5" })
|
|
1335
1484
|
] })
|
|
@@ -1395,9 +1544,9 @@ function ToolMetrics({ presentation }) {
|
|
|
1395
1544
|
}
|
|
1396
1545
|
function PendingElapsed({ now }) {
|
|
1397
1546
|
const clock = now ?? Date.now;
|
|
1398
|
-
const started =
|
|
1399
|
-
const [elapsed, setElapsed] =
|
|
1400
|
-
|
|
1547
|
+
const started = useRef4(clock());
|
|
1548
|
+
const [elapsed, setElapsed] = useState3(0);
|
|
1549
|
+
useEffect4(() => {
|
|
1401
1550
|
const timer = setInterval(() => setElapsed(Math.max(0, clock() - started.current)), 1e3);
|
|
1402
1551
|
return () => clearInterval(timer);
|
|
1403
1552
|
}, [clock]);
|
|
@@ -1438,7 +1587,7 @@ function SearchPreview({ presentation }) {
|
|
|
1438
1587
|
] }) : null,
|
|
1439
1588
|
lines.length ? /* @__PURE__ */ jsx5("ol", { children: lines.map((line, index) => {
|
|
1440
1589
|
const match = /^(.*?):(\d+)(?::(\d+))?:(.*)$/.exec(line);
|
|
1441
|
-
return /* @__PURE__ */ jsx5("li", { children: match ? /* @__PURE__ */ jsxs4(
|
|
1590
|
+
return /* @__PURE__ */ jsx5("li", { children: match ? /* @__PURE__ */ jsxs4(Fragment4, { children: [
|
|
1442
1591
|
/* @__PURE__ */ jsx5("code", { children: match[1] }),
|
|
1443
1592
|
/* @__PURE__ */ jsxs4("small", { children: [
|
|
1444
1593
|
match[2],
|
|
@@ -1471,9 +1620,9 @@ function ToolPreview({ presentation, entry }) {
|
|
|
1471
1620
|
return presentation.preview ? /* @__PURE__ */ jsx5("pre", { class: "scui-tool-output", "data-error": failed, children: stripAnsi(presentation.preview) }) : null;
|
|
1472
1621
|
}
|
|
1473
1622
|
function ToolActions({ presentation, adapter }) {
|
|
1474
|
-
const [copied, setCopied] =
|
|
1475
|
-
const reset =
|
|
1476
|
-
|
|
1623
|
+
const [copied, setCopied] = useState3(false);
|
|
1624
|
+
const reset = useRef4(null);
|
|
1625
|
+
useEffect4(() => () => clearTimeout(reset.current), []);
|
|
1477
1626
|
if (!adapter?.copyText) return null;
|
|
1478
1627
|
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;
|
|
1479
1628
|
const copy = async () => {
|
|
@@ -1520,7 +1669,7 @@ function TranscriptEntry({ entry, state, adapter }) {
|
|
|
1520
1669
|
}
|
|
1521
1670
|
if (entry.role === "notice" || entry.role === "system") return /* @__PURE__ */ jsx5("div", { class: "scui-notice", "data-code": entry.code, children: entry.text });
|
|
1522
1671
|
return /* @__PURE__ */ jsxs4("article", { class: "scui-message", "data-role": entry.role, "aria-label": `${entry.role === "user" ? "Your" : "Assistant"} message`, children: [
|
|
1523
|
-
/* @__PURE__ */ jsx5(MessageImages, { items: entry.images }),
|
|
1672
|
+
/* @__PURE__ */ jsx5(MessageImages, { items: entry.images, adapter }),
|
|
1524
1673
|
/* @__PURE__ */ jsx5(Markdown, { value: entry.text, copyText: adapter?.copyText }),
|
|
1525
1674
|
/* @__PURE__ */ jsx5(ContextDisclosure, { context: entry.context }),
|
|
1526
1675
|
entry.truncated ? /* @__PURE__ */ jsx5("small", { class: "scui-truncated", children: "Entry truncated by host \xB7 load native session for the complete value" }) : null,
|
|
@@ -1529,14 +1678,14 @@ function TranscriptEntry({ entry, state, adapter }) {
|
|
|
1529
1678
|
}
|
|
1530
1679
|
function ToolRow({ entry, workspace, open = false, adapter }) {
|
|
1531
1680
|
const presentation = entry.presentation ?? createToolPresentation(entry);
|
|
1532
|
-
const [expanded, setExpanded] =
|
|
1533
|
-
|
|
1681
|
+
const [expanded, setExpanded] = useState3(open || entry.status === "pending");
|
|
1682
|
+
useEffect4(() => {
|
|
1534
1683
|
if (entry.status === "pending") setExpanded(true);
|
|
1535
1684
|
}, [entry.status]);
|
|
1536
1685
|
const target = compactToolTarget(presentation.target, workspace);
|
|
1537
1686
|
const hasDetail = Boolean(entry.arguments || entry.resultText || presentation.preview || presentation.fields.length);
|
|
1538
1687
|
const category = presentation.category ?? toolCategory(entry);
|
|
1539
|
-
const summary = /* @__PURE__ */ jsxs4(
|
|
1688
|
+
const summary = /* @__PURE__ */ jsxs4(Fragment4, { children: [
|
|
1540
1689
|
/* @__PURE__ */ jsx5(ToolIcon, { category }),
|
|
1541
1690
|
/* @__PURE__ */ jsx5("strong", { children: toolAction2(entry, category, presentation) }),
|
|
1542
1691
|
target ? /* @__PURE__ */ jsx5("code", { class: "scui-tool-target", title: presentation.target, children: target }) : null,
|
|
@@ -1564,9 +1713,9 @@ function ToolRow({ entry, workspace, open = false, adapter }) {
|
|
|
1564
1713
|
function ActivityGroup({ entries, state, adapter }) {
|
|
1565
1714
|
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 }) });
|
|
1566
1715
|
const active = entries.some((entry) => entry.status === "pending");
|
|
1567
|
-
const [open, setOpen] =
|
|
1716
|
+
const [open, setOpen] = useState3(active);
|
|
1568
1717
|
const id = useId();
|
|
1569
|
-
|
|
1718
|
+
useEffect4(() => {
|
|
1570
1719
|
if (active) setOpen(true);
|
|
1571
1720
|
}, [active]);
|
|
1572
1721
|
return /* @__PURE__ */ jsxs4("section", { class: "scui-activity", children: [
|
|
@@ -1642,9 +1791,9 @@ function SessionDetails({ semantics }) {
|
|
|
1642
1791
|
}
|
|
1643
1792
|
var conversationMemory = /* @__PURE__ */ new Map();
|
|
1644
1793
|
function ConversationAnnouncements({ state }) {
|
|
1645
|
-
const previousBusy =
|
|
1646
|
-
const [announcement, setAnnouncement] =
|
|
1647
|
-
|
|
1794
|
+
const previousBusy = useRef4(state.busy);
|
|
1795
|
+
const [announcement, setAnnouncement] = useState3("");
|
|
1796
|
+
useEffect4(() => {
|
|
1648
1797
|
if (previousBusy.current && !state.busy && !state.error) {
|
|
1649
1798
|
setAnnouncement(`${harnessDisplayName(state.harness) || "Coding agent"} finished working`);
|
|
1650
1799
|
}
|
|
@@ -1653,13 +1802,13 @@ function ConversationAnnouncements({ state }) {
|
|
|
1653
1802
|
return /* @__PURE__ */ jsx5("span", { class: "scui-sr-only", role: "status", "aria-live": "polite", "aria-atomic": "true", children: announcement });
|
|
1654
1803
|
}
|
|
1655
1804
|
function Conversation({ state, adapter, components = {}, slots = {}, memoryKey = state.attached?.key || `${state.harness}:${state.workspace}`, pending = null, unreadAfterMessages = null }) {
|
|
1656
|
-
const scroller =
|
|
1805
|
+
const scroller = useRef4(null);
|
|
1657
1806
|
const remembered = conversationMemory.get(memoryKey) ?? { top: null, atBottom: true };
|
|
1658
|
-
const [atBottom, setAtBottom] =
|
|
1659
|
-
const earlierAnchor =
|
|
1660
|
-
const restored =
|
|
1807
|
+
const [atBottom, setAtBottom] = useState3(remembered.atBottom);
|
|
1808
|
+
const earlierAnchor = useRef4(null);
|
|
1809
|
+
const restored = useRef4(false);
|
|
1661
1810
|
const blocks = groupConversation(state.transcript);
|
|
1662
|
-
const unreadBoundary =
|
|
1811
|
+
const unreadBoundary = useRef4(Number.isSafeInteger(unreadAfterMessages) && unreadAfterMessages >= 0 ? unreadAfterMessages : null);
|
|
1663
1812
|
const unreadBlock = unreadBoundary.current === null ? -1 : blocks.findIndex((block) => {
|
|
1664
1813
|
const entries = block.kind === "activity" ? block.entries : [block.entry];
|
|
1665
1814
|
return entries.some((entry) => Number.isSafeInteger(entry.messageIndex) && entry.messageIndex > unreadBoundary.current);
|
|
@@ -1718,12 +1867,12 @@ function Conversation({ state, adapter, components = {}, slots = {}, memoryKey =
|
|
|
1718
1867
|
}, children: "Load earlier messages" }) : null,
|
|
1719
1868
|
!blocks.length && state.startup !== "ready" ? /* @__PURE__ */ jsx5(LoadingStatus, { state }) : null,
|
|
1720
1869
|
!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,
|
|
1721
|
-
blocks.map((block, index) => /* @__PURE__ */ jsxs4(
|
|
1870
|
+
blocks.map((block, index) => /* @__PURE__ */ jsxs4(Fragment3, { children: [
|
|
1722
1871
|
index === unreadBlock ? /* @__PURE__ */ jsx5("div", { class: "scui-unread-divider", role: "separator", "aria-label": "New messages", children: /* @__PURE__ */ jsx5("span", { children: "New" }) }) : null,
|
|
1723
1872
|
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 })
|
|
1724
1873
|
] }, block.id)),
|
|
1725
1874
|
pendingMessage ? /* @__PURE__ */ jsxs4("article", { class: "scui-message scui-pending", "data-role": "user", "data-status": pendingMessage.status, "aria-label": "Your pending message", children: [
|
|
1726
|
-
/* @__PURE__ */ jsx5(MessageImages, { items: pendingMessage.images }),
|
|
1875
|
+
/* @__PURE__ */ jsx5(MessageImages, { items: pendingMessage.images, adapter }),
|
|
1727
1876
|
/* @__PURE__ */ jsx5(Markdown, { value: pendingMessage.text, copyText: adapter?.copyText }),
|
|
1728
1877
|
/* @__PURE__ */ jsx5(ContextDisclosure, { context: pendingMessage.context }),
|
|
1729
1878
|
/* @__PURE__ */ jsxs4("footer", { children: [
|
|
@@ -1754,7 +1903,7 @@ function Conversation({ state, adapter, components = {}, slots = {}, memoryKey =
|
|
|
1754
1903
|
}
|
|
1755
1904
|
|
|
1756
1905
|
// src/logo.jsx
|
|
1757
|
-
import { useEffect as
|
|
1906
|
+
import { useEffect as useEffect5 } from "preact/hooks";
|
|
1758
1907
|
import { jsx as jsx6, jsxs as jsxs5 } from "preact/jsx-runtime";
|
|
1759
1908
|
var LOGOS = {
|
|
1760
1909
|
"claude-code": {
|
|
@@ -1805,7 +1954,7 @@ function hasHarnessLogo(id) {
|
|
|
1805
1954
|
}
|
|
1806
1955
|
function HarnessLogo({ id, activity, size = 28, onMissingLogo }) {
|
|
1807
1956
|
const logo = LOGOS[id];
|
|
1808
|
-
|
|
1957
|
+
useEffect5(() => {
|
|
1809
1958
|
if (!logo) onMissingLogo?.(id);
|
|
1810
1959
|
}, [id, logo, onMissingLogo]);
|
|
1811
1960
|
if (!logo) return null;
|
|
@@ -1816,10 +1965,10 @@ function HarnessLogo({ id, activity, size = 28, onMissingLogo }) {
|
|
|
1816
1965
|
}
|
|
1817
1966
|
|
|
1818
1967
|
// src/messenger.jsx
|
|
1819
|
-
import { useEffect as
|
|
1968
|
+
import { useEffect as useEffect7, useId as useId2, useMemo as useMemo2, useRef as useRef6, useState as useState5 } from "preact/hooks";
|
|
1820
1969
|
|
|
1821
1970
|
// src/sessions.jsx
|
|
1822
|
-
import { useEffect as
|
|
1971
|
+
import { useEffect as useEffect6, useLayoutEffect as useLayoutEffect3, useRef as useRef5, useState as useState4 } from "preact/hooks";
|
|
1823
1972
|
import { jsx as jsx7, jsxs as jsxs6 } from "preact/jsx-runtime";
|
|
1824
1973
|
var sessionListMemory = /* @__PURE__ */ new Map();
|
|
1825
1974
|
function SessionRow({ row, state, onOpen }) {
|
|
@@ -1841,21 +1990,21 @@ function SessionRow({ row, state, onOpen }) {
|
|
|
1841
1990
|
}
|
|
1842
1991
|
function SessionList({ state, adapter, onOpen, onNew, onClose, components = {}, labels = DEFAULT_LABELS, focusKey = null, memoryKey = state.workspace || "@default" }) {
|
|
1843
1992
|
const remembered = sessionListMemory.get(memoryKey) ?? { query: "", top: 0 };
|
|
1844
|
-
const [query, setQuery] =
|
|
1845
|
-
const [loadingMore, setLoadingMore] =
|
|
1846
|
-
const root =
|
|
1847
|
-
const rowScroller =
|
|
1993
|
+
const [query, setQuery] = useState4(remembered.query);
|
|
1994
|
+
const [loadingMore, setLoadingMore] = useState4(false);
|
|
1995
|
+
const root = useRef5(null);
|
|
1996
|
+
const rowScroller = useRef5(null);
|
|
1848
1997
|
const rows = filterSessions(state.sessions, query);
|
|
1849
1998
|
const Row = components.SessionRow ?? SessionRow;
|
|
1850
1999
|
useLayoutEffect3(() => {
|
|
1851
2000
|
if (rowScroller.current) rowScroller.current.scrollTop = remembered.top;
|
|
1852
2001
|
}, [memoryKey]);
|
|
1853
|
-
|
|
2002
|
+
useEffect6(() => {
|
|
1854
2003
|
if (!focusKey || !root.current) return;
|
|
1855
2004
|
const target = focusKey === "@new" ? root.current.querySelector('[data-list-focus="new"]') : [...root.current.querySelectorAll("[data-session-key]")].find((element) => element.dataset.sessionKey === focusKey);
|
|
1856
2005
|
(target ?? root.current.querySelector("input,button"))?.focus({ preventScroll: true });
|
|
1857
2006
|
}, [focusKey, rows.length]);
|
|
1858
|
-
|
|
2007
|
+
useEffect6(() => {
|
|
1859
2008
|
if (loadingMore) setLoadingMore(false);
|
|
1860
2009
|
}, [state.error, state.history.hasMoreSessions, state.sessions.length]);
|
|
1861
2010
|
const loadMore = () => {
|
|
@@ -1941,10 +2090,10 @@ function Receipt({ state, adapter }) {
|
|
|
1941
2090
|
return null;
|
|
1942
2091
|
}
|
|
1943
2092
|
function ConversationActions({ state, adapter, actionPending }) {
|
|
1944
|
-
const [open, setOpen] =
|
|
1945
|
-
const root =
|
|
1946
|
-
const panel =
|
|
1947
|
-
const trigger =
|
|
2093
|
+
const [open, setOpen] = useState5(false);
|
|
2094
|
+
const root = useRef6(null);
|
|
2095
|
+
const panel = useRef6(null);
|
|
2096
|
+
const trigger = useRef6(null);
|
|
1948
2097
|
const menuId = useId2();
|
|
1949
2098
|
const targets = state.harnesses.filter((item) => item.startable);
|
|
1950
2099
|
const groups = [
|
|
@@ -1965,7 +2114,7 @@ function ConversationActions({ state, adapter, actionPending }) {
|
|
|
1965
2114
|
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
2115
|
}
|
|
1967
2116
|
].filter((group) => group.items.length);
|
|
1968
|
-
|
|
2117
|
+
useEffect7(() => {
|
|
1969
2118
|
if (!open) return;
|
|
1970
2119
|
panel.current?.querySelector("button:not(:disabled)")?.focus({ preventScroll: true });
|
|
1971
2120
|
const dismiss = (event) => {
|
|
@@ -2015,12 +2164,12 @@ function ConversationActions({ state, adapter, actionPending }) {
|
|
|
2015
2164
|
] });
|
|
2016
2165
|
}
|
|
2017
2166
|
function ChatHeader({ state, adapter, pendingStatus, actionPending, onBack, onNew, onClose }) {
|
|
2018
|
-
const back =
|
|
2167
|
+
const back = useRef6(null);
|
|
2019
2168
|
const harness = state.attached?.harness ?? state.harness;
|
|
2020
2169
|
const title = state.attached ? sessionDisplayName(state.attached) : harnessDisplayName(harness) || "Agent chat";
|
|
2021
2170
|
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
2171
|
const menu = state.canDetach || state.canOpenTerminal || state.canBranch || state.canAttach || state.canExport || state.canReduce;
|
|
2023
|
-
|
|
2172
|
+
useEffect7(() => {
|
|
2024
2173
|
if (state.mode === "mirror" && !state.canSend) back.current?.focus({ preventScroll: true });
|
|
2025
2174
|
}, []);
|
|
2026
2175
|
return /* @__PURE__ */ jsxs7("header", { class: "scui-head scui-chat-head", children: [
|
|
@@ -2042,24 +2191,24 @@ function ChatHeader({ state, adapter, pendingStatus, actionPending, onBack, onNe
|
|
|
2042
2191
|
function Chat({ state, adapter, onBack, onNew, onClose, components, slots, labels }) {
|
|
2043
2192
|
const Header = slots.header;
|
|
2044
2193
|
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 =
|
|
2194
|
+
const [pending, setPendingState] = useState5(() => pendingMessageMemory.get(memoryKey) ?? null);
|
|
2195
|
+
const [pendingAction, setPendingAction] = useState5(null);
|
|
2196
|
+
const [restoreDraft, setRestoreDraft] = useState5(null);
|
|
2197
|
+
const restoreSequence = useRef6(0);
|
|
2198
|
+
const actionSequence = useRef6(0);
|
|
2199
|
+
const acknowledged = useRef6(/* @__PURE__ */ new Set());
|
|
2051
2200
|
const setPending = (update) => setPendingState((current) => {
|
|
2052
2201
|
const next = typeof update === "function" ? update(current) : update;
|
|
2053
2202
|
if (next) boundedSet(pendingMessageMemory, memoryKey, next);
|
|
2054
2203
|
else pendingMessageMemory.delete(memoryKey);
|
|
2055
2204
|
return next;
|
|
2056
2205
|
});
|
|
2057
|
-
|
|
2206
|
+
useEffect7(() => {
|
|
2058
2207
|
setPendingState(pendingMessageMemory.get(memoryKey) ?? null);
|
|
2059
2208
|
setPendingAction(null);
|
|
2060
2209
|
setRestoreDraft(null);
|
|
2061
2210
|
}, [memoryKey]);
|
|
2062
|
-
|
|
2211
|
+
useEffect7(() => {
|
|
2063
2212
|
if (!pending) return;
|
|
2064
2213
|
if (state.transcript.some((entry) => entry.role === "user" && entry.text.trim() === pending.text.trim() && !pending.baselineIds.includes(entry.id))) {
|
|
2065
2214
|
setPending(null);
|
|
@@ -2088,7 +2237,7 @@ function Chat({ state, adapter, onBack, onNew, onClose, components, slots, label
|
|
|
2088
2237
|
setRestoreDraft({ id: restoreSequence.current, text: pending.text, context: pending.context, images: pending.images });
|
|
2089
2238
|
setPending({ ...pending, status: "editing" });
|
|
2090
2239
|
};
|
|
2091
|
-
|
|
2240
|
+
useEffect7(() => {
|
|
2092
2241
|
const key = state.attached?.key;
|
|
2093
2242
|
if (!key || !state.attention.some((item) => item.key === key)) {
|
|
2094
2243
|
if (key) acknowledged.current.delete(key);
|
|
@@ -2123,7 +2272,7 @@ function Chat({ state, adapter, onBack, onNew, onClose, components, slots, label
|
|
|
2123
2272
|
return result;
|
|
2124
2273
|
}
|
|
2125
2274
|
}), [adapter, state.error]);
|
|
2126
|
-
|
|
2275
|
+
useEffect7(() => {
|
|
2127
2276
|
if (!pendingAction) return;
|
|
2128
2277
|
if (state.operation && !pendingAction.seenOperation) {
|
|
2129
2278
|
setPendingAction({ ...pendingAction, seenOperation: true });
|
|
@@ -2156,24 +2305,24 @@ function NewChat({ state, adapter, onBack, onClose, onStarted, labels, memoryKey
|
|
|
2156
2305
|
const startable = state.harnesses.filter((item) => item.startable);
|
|
2157
2306
|
const startableKey = startable.map((item) => item.id).join("\0");
|
|
2158
2307
|
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 =
|
|
2308
|
+
const [harness, setHarness] = useState5(remembered.harness);
|
|
2309
|
+
const [draft, setDraft] = useState5(remembered.draft);
|
|
2310
|
+
const [context, setContext] = useState5(remembered.context);
|
|
2311
|
+
const [images, setImages] = useState5(remembered.images ?? []);
|
|
2312
|
+
const [starting, setStarting] = useState5(null);
|
|
2313
|
+
const [picking, setPicking] = useState5(false);
|
|
2314
|
+
const [dragging, setDragging] = useState5(false);
|
|
2315
|
+
const [pickerError, setPickerError] = useState5(null);
|
|
2316
|
+
const startSequence = useRef6(0);
|
|
2317
|
+
const textarea = useRef6(null);
|
|
2169
2318
|
useAutosizeTextarea(textarea, draft);
|
|
2170
|
-
|
|
2319
|
+
useEffect7(() => {
|
|
2171
2320
|
if (startable.some((item) => item.id === harness)) return;
|
|
2172
2321
|
const next = startable[0]?.id ?? "";
|
|
2173
2322
|
setHarness(next);
|
|
2174
2323
|
boundedSet(newChatMemory, memoryKey, { harness: next, draft, context, images });
|
|
2175
2324
|
}, [harness, startableKey]);
|
|
2176
|
-
|
|
2325
|
+
useEffect7(() => {
|
|
2177
2326
|
if (!starting) return;
|
|
2178
2327
|
const sessionChanged = (state.attached?.key ?? null) !== starting.attachedKey;
|
|
2179
2328
|
const beganWorking = !starting.busy && state.busy;
|
|
@@ -2181,10 +2330,10 @@ function NewChat({ state, adapter, onBack, onClose, onStarted, labels, memoryKey
|
|
|
2181
2330
|
newChatMemory.delete(memoryKey);
|
|
2182
2331
|
onStarted();
|
|
2183
2332
|
}, [memoryKey, onStarted, starting, state.attached?.key, state.busy]);
|
|
2184
|
-
|
|
2333
|
+
useEffect7(() => {
|
|
2185
2334
|
if (starting && state.error !== starting.initialError && !state.busy && !state.operation) setStarting(null);
|
|
2186
2335
|
}, [starting, state.busy, state.error, state.operation]);
|
|
2187
|
-
|
|
2336
|
+
useEffect7(() => {
|
|
2188
2337
|
textarea.current?.focus({ preventScroll: true });
|
|
2189
2338
|
}, []);
|
|
2190
2339
|
const pickContext = () => {
|
|
@@ -2318,14 +2467,14 @@ function SupercodeMessenger({ state: stateInput, adapter, class: className = "",
|
|
|
2318
2467
|
const state = useMemo2(() => normalizeUiState(stateInput), [stateInput]);
|
|
2319
2468
|
const copy = { ...DEFAULT_LABELS, ...labels };
|
|
2320
2469
|
const memoryKey = state.workspace || "@default";
|
|
2321
|
-
const [view, setViewState] =
|
|
2322
|
-
const [opening, setOpening] =
|
|
2323
|
-
const [listFocus, setListFocus] =
|
|
2470
|
+
const [view, setViewState] = useState5(initialView ?? (state.attention.length ? "list" : messengerViewMemory.get(memoryKey) ?? (state.attached || state.transcript.length ? "chat" : "list")));
|
|
2471
|
+
const [opening, setOpening] = useState5(null);
|
|
2472
|
+
const [listFocus, setListFocus] = useState5(null);
|
|
2324
2473
|
const setView = (next) => {
|
|
2325
2474
|
boundedSet(messengerViewMemory, memoryKey, next);
|
|
2326
2475
|
setViewState(next);
|
|
2327
2476
|
};
|
|
2328
|
-
|
|
2477
|
+
useEffect7(() => {
|
|
2329
2478
|
if (!opening) return;
|
|
2330
2479
|
if (state.attached?.key === opening.key) {
|
|
2331
2480
|
setOpening(null);
|
|
@@ -2374,7 +2523,9 @@ export {
|
|
|
2374
2523
|
ContinuationBar,
|
|
2375
2524
|
Conversation,
|
|
2376
2525
|
HarnessLogo,
|
|
2526
|
+
ImageViewer,
|
|
2377
2527
|
LoadingStatus,
|
|
2528
|
+
MessageImages,
|
|
2378
2529
|
RequestCard,
|
|
2379
2530
|
SessionDetails,
|
|
2380
2531
|
SessionList,
|