@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/messenger.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// src/messenger.jsx
|
|
2
|
-
import { useEffect as
|
|
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({
|
|
@@ -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
|
}
|
|
@@ -758,7 +761,7 @@ function isSendKey(event) {
|
|
|
758
761
|
}
|
|
759
762
|
|
|
760
763
|
// src/composer.jsx
|
|
761
|
-
import { useEffect, useRef, useState } from "preact/hooks";
|
|
764
|
+
import { useEffect as useEffect2, useRef as useRef2, useState as useState2 } from "preact/hooks";
|
|
762
765
|
|
|
763
766
|
// src/memory.js
|
|
764
767
|
var MEMORY_LIMIT = 100;
|
|
@@ -818,10 +821,12 @@ function UiIcon({ name, size = 16, class: className = "" }) {
|
|
|
818
821
|
}
|
|
819
822
|
|
|
820
823
|
// src/context.jsx
|
|
821
|
-
import {
|
|
824
|
+
import { useEffect, useRef, useState } from "preact/hooks";
|
|
825
|
+
import { Fragment as Fragment2, jsx as jsx2, jsxs as jsxs2 } from "preact/jsx-runtime";
|
|
822
826
|
var MAX_CONTEXT_ITEMS = 32;
|
|
823
827
|
var MAX_IMAGE_ITEMS = 4;
|
|
824
828
|
var MAX_IMAGE_BYTES = 5 * 1024 * 1024;
|
|
829
|
+
var MAX_RESOLVED_IMAGE_BYTES = 16 * 1024 * 1024;
|
|
825
830
|
var IMAGE_TYPES = /* @__PURE__ */ new Set(["image/png", "image/jpeg", "image/gif", "image/webp"]);
|
|
826
831
|
function normalizeContext(value) {
|
|
827
832
|
return (Array.isArray(value) ? value : value ? [value] : []).flatMap((item) => {
|
|
@@ -917,12 +922,156 @@ function ImageTray({ items, onRemove }) {
|
|
|
917
922
|
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
923
|
] }, item.id ?? `${item.label}:${index}`)) });
|
|
919
924
|
}
|
|
920
|
-
function
|
|
925
|
+
function imageFilename(label) {
|
|
926
|
+
const value = label.trim().replace(/[\\/:*?"<>|]+/g, "-");
|
|
927
|
+
return value || "image";
|
|
928
|
+
}
|
|
929
|
+
function ImageViewer({ items, index, adapter, onChange, onClose }) {
|
|
930
|
+
const dialog = useRef(null);
|
|
931
|
+
const reset = useRef(null);
|
|
932
|
+
const resolutions = useRef(/* @__PURE__ */ new Map());
|
|
933
|
+
const alive = useRef(true);
|
|
934
|
+
const [, redraw] = useState(0);
|
|
935
|
+
const [copyState, setCopyState2] = useState("idle");
|
|
936
|
+
const item = items[index];
|
|
937
|
+
const key = item?.reference ?? item?.url ?? `${item?.id ?? ""}:${index}`;
|
|
938
|
+
const resolution = item?.url ? null : resolutions.current.get(key);
|
|
939
|
+
const imageUrl = item?.url ?? (resolution?.status === "ready" ? resolution.url : null);
|
|
940
|
+
const remote = imageUrl?.startsWith("http://") || imageUrl?.startsWith("https://");
|
|
941
|
+
const resolve = (candidate, force = false) => {
|
|
942
|
+
if (candidate?.url || !candidate?.reference || !adapter?.resolveImage) return;
|
|
943
|
+
const candidateKey = candidate.reference;
|
|
944
|
+
const current = resolutions.current.get(candidateKey);
|
|
945
|
+
if (!force && (current?.status === "loading" || current?.status === "ready")) return;
|
|
946
|
+
if (current?.url) URL.revokeObjectURL(current.url);
|
|
947
|
+
resolutions.current.set(candidateKey, { status: "loading" });
|
|
948
|
+
redraw((value) => value + 1);
|
|
949
|
+
Promise.resolve().then(() => adapter.resolveImage(candidate)).then((blob) => {
|
|
950
|
+
if (!(blob instanceof Blob) || !blob.type.startsWith("image/")) throw new Error("The host returned an invalid image.");
|
|
951
|
+
if (blob.size > MAX_RESOLVED_IMAGE_BYTES) throw new Error("This image is too large to preview safely.");
|
|
952
|
+
if (!alive.current) return;
|
|
953
|
+
const url = URL.createObjectURL(blob);
|
|
954
|
+
resolutions.current.set(candidateKey, { status: "ready", url });
|
|
955
|
+
redraw((value) => value + 1);
|
|
956
|
+
}).catch((error) => {
|
|
957
|
+
if (!alive.current) return;
|
|
958
|
+
resolutions.current.set(candidateKey, {
|
|
959
|
+
status: "error",
|
|
960
|
+
message: error instanceof Error && error.message ? error.message : "Could not load this image."
|
|
961
|
+
});
|
|
962
|
+
redraw((value) => value + 1);
|
|
963
|
+
});
|
|
964
|
+
};
|
|
965
|
+
useEffect(() => {
|
|
966
|
+
alive.current = true;
|
|
967
|
+
if (!dialog.current?.open) dialog.current?.showModal();
|
|
968
|
+
return () => {
|
|
969
|
+
alive.current = false;
|
|
970
|
+
clearTimeout(reset.current);
|
|
971
|
+
for (const value of resolutions.current.values()) if (value.url) URL.revokeObjectURL(value.url);
|
|
972
|
+
resolutions.current.clear();
|
|
973
|
+
};
|
|
974
|
+
}, []);
|
|
975
|
+
useEffect(() => {
|
|
976
|
+
clearTimeout(reset.current);
|
|
977
|
+
setCopyState2("idle");
|
|
978
|
+
resolve(item);
|
|
979
|
+
}, [index, item?.reference]);
|
|
980
|
+
if (!item || !item.url && (!item.reference || !adapter?.resolveImage)) return null;
|
|
981
|
+
const move = (amount) => onChange((index + amount + items.length) % items.length);
|
|
982
|
+
const copy = async () => {
|
|
983
|
+
try {
|
|
984
|
+
await adapter.copyText(imageUrl);
|
|
985
|
+
setCopyState2("copied");
|
|
986
|
+
} catch {
|
|
987
|
+
setCopyState2("failed");
|
|
988
|
+
}
|
|
989
|
+
clearTimeout(reset.current);
|
|
990
|
+
reset.current = setTimeout(() => setCopyState2("idle"), 1500);
|
|
991
|
+
};
|
|
992
|
+
const close = () => dialog.current?.close();
|
|
993
|
+
return /* @__PURE__ */ jsxs2(
|
|
994
|
+
"dialog",
|
|
995
|
+
{
|
|
996
|
+
ref: dialog,
|
|
997
|
+
class: "scui-image-viewer",
|
|
998
|
+
"aria-label": `Image preview: ${item.label}`,
|
|
999
|
+
onClose,
|
|
1000
|
+
onClick: (event) => {
|
|
1001
|
+
if (event.target === event.currentTarget) close();
|
|
1002
|
+
},
|
|
1003
|
+
onKeyDown: (event) => {
|
|
1004
|
+
if (items.length < 2 || !["ArrowLeft", "ArrowRight"].includes(event.key)) return;
|
|
1005
|
+
event.preventDefault();
|
|
1006
|
+
move(event.key === "ArrowLeft" ? -1 : 1);
|
|
1007
|
+
},
|
|
1008
|
+
children: [
|
|
1009
|
+
/* @__PURE__ */ jsxs2("header", { children: [
|
|
1010
|
+
/* @__PURE__ */ jsxs2("span", { children: [
|
|
1011
|
+
/* @__PURE__ */ jsx2("strong", { children: item.label }),
|
|
1012
|
+
items.length > 1 ? /* @__PURE__ */ jsxs2("small", { children: [
|
|
1013
|
+
index + 1,
|
|
1014
|
+
" of ",
|
|
1015
|
+
items.length
|
|
1016
|
+
] }) : null
|
|
1017
|
+
] }),
|
|
1018
|
+
/* @__PURE__ */ jsxs2("nav", { "aria-label": "Image actions", children: [
|
|
1019
|
+
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,
|
|
1020
|
+
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,
|
|
1021
|
+
/* @__PURE__ */ jsx2("button", { type: "button", "aria-label": "Close image preview", title: "Close", onClick: close, children: /* @__PURE__ */ jsx2(UiIcon, { name: "close", size: 17 }) })
|
|
1022
|
+
] })
|
|
1023
|
+
] }),
|
|
1024
|
+
/* @__PURE__ */ jsxs2("figure", { children: [
|
|
1025
|
+
imageUrl ? /* @__PURE__ */ jsx2("img", { src: imageUrl, alt: item.label }) : resolution?.status === "error" ? /* @__PURE__ */ jsxs2("div", { class: "scui-image-resolution", role: "alert", children: [
|
|
1026
|
+
/* @__PURE__ */ jsx2(UiIcon, { name: "image", size: 28 }),
|
|
1027
|
+
/* @__PURE__ */ jsx2("strong", { children: "Could not load image" }),
|
|
1028
|
+
/* @__PURE__ */ jsx2("small", { children: resolution.message }),
|
|
1029
|
+
/* @__PURE__ */ jsx2("button", { type: "button", onClick: () => resolve(item, true), children: "Retry" })
|
|
1030
|
+
] }) : /* @__PURE__ */ jsxs2("div", { class: "scui-image-resolution", role: "status", children: [
|
|
1031
|
+
/* @__PURE__ */ jsx2("i", { class: "scui-control-spinner" }),
|
|
1032
|
+
/* @__PURE__ */ jsx2("strong", { children: "Loading image\u2026" }),
|
|
1033
|
+
/* @__PURE__ */ jsx2("small", { children: "The original stays out of the transcript payload." })
|
|
1034
|
+
] }),
|
|
1035
|
+
items.length > 1 ? /* @__PURE__ */ jsxs2(Fragment2, { children: [
|
|
1036
|
+
/* @__PURE__ */ jsx2("button", { type: "button", class: "scui-image-previous", "aria-label": "Previous image", onClick: () => move(-1), children: /* @__PURE__ */ jsx2(UiIcon, { name: "chevron", size: 19 }) }),
|
|
1037
|
+
/* @__PURE__ */ jsx2("button", { type: "button", class: "scui-image-next", "aria-label": "Next image", onClick: () => move(1), children: /* @__PURE__ */ jsx2(UiIcon, { name: "chevron", size: 19 }) })
|
|
1038
|
+
] }) : null
|
|
1039
|
+
] })
|
|
1040
|
+
]
|
|
1041
|
+
}
|
|
1042
|
+
);
|
|
1043
|
+
}
|
|
1044
|
+
function MessageImages({ items, adapter }) {
|
|
1045
|
+
const [active, setActive] = useState(null);
|
|
1046
|
+
const opener = useRef(null);
|
|
921
1047
|
if (!items?.length) return null;
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
1048
|
+
const viewable = items.filter((item) => item.url || item.reference && adapter?.resolveImage);
|
|
1049
|
+
const close = () => {
|
|
1050
|
+
setActive(null);
|
|
1051
|
+
requestAnimationFrame(() => opener.current?.focus({ preventScroll: true }));
|
|
1052
|
+
};
|
|
1053
|
+
return /* @__PURE__ */ jsxs2(Fragment2, { children: [
|
|
1054
|
+
/* @__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) => {
|
|
1055
|
+
opener.current = event.currentTarget;
|
|
1056
|
+
setActive(viewable.indexOf(item));
|
|
1057
|
+
}, 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) => {
|
|
1058
|
+
opener.current = event.currentTarget;
|
|
1059
|
+
setActive(viewable.indexOf(item));
|
|
1060
|
+
}, children: [
|
|
1061
|
+
/* @__PURE__ */ jsx2(UiIcon, { name: "image", size: 16 }),
|
|
1062
|
+
/* @__PURE__ */ jsxs2("span", { children: [
|
|
1063
|
+
/* @__PURE__ */ jsx2("strong", { children: item.label }),
|
|
1064
|
+
/* @__PURE__ */ jsx2("small", { children: "Load preview" })
|
|
1065
|
+
] })
|
|
1066
|
+
] }, item.id ?? `${item.label}:${index}`) : /* @__PURE__ */ jsxs2("span", { "data-unavailable": "true", children: [
|
|
1067
|
+
/* @__PURE__ */ jsx2(UiIcon, { name: "image", size: 14 }),
|
|
1068
|
+
/* @__PURE__ */ jsxs2("span", { children: [
|
|
1069
|
+
/* @__PURE__ */ jsx2("strong", { children: item.label }),
|
|
1070
|
+
/* @__PURE__ */ jsx2("small", { children: "Preview unavailable" })
|
|
1071
|
+
] })
|
|
1072
|
+
] }, item.id ?? `${item.label}:${index}`)) }),
|
|
1073
|
+
active !== null && viewable[active] ? /* @__PURE__ */ jsx2(ImageViewer, { items: viewable, index: active, adapter, onChange: setActive, onClose: close }) : null
|
|
1074
|
+
] });
|
|
926
1075
|
}
|
|
927
1076
|
|
|
928
1077
|
// src/textarea.js
|
|
@@ -964,18 +1113,18 @@ function ContinuationBar({ state, adapter, labels = DEFAULT_LABELS }) {
|
|
|
964
1113
|
}
|
|
965
1114
|
function Composer({ state, adapter, labels = DEFAULT_LABELS, memoryKey = state.attached?.key || `${state.harness}:${state.workspace}`, pendingStatus = null, restoreDraft = null, onPending, onDraftRestored }) {
|
|
966
1115
|
const remembered = composerMemory.get(memoryKey) ?? { draft: state.savedDraft, context: [], images: [], queue: [] };
|
|
967
|
-
const [draft, setDraft] =
|
|
968
|
-
const [context, setContext] =
|
|
969
|
-
const [images, setImages] =
|
|
970
|
-
const [queue, setQueue] =
|
|
971
|
-
const [dispatching, setDispatching] =
|
|
972
|
-
const [picking, setPicking] =
|
|
973
|
-
const [dragging, setDragging] =
|
|
974
|
-
const [pickerError, setPickerError] =
|
|
975
|
-
const textarea =
|
|
1116
|
+
const [draft, setDraft] = useState2(remembered.draft);
|
|
1117
|
+
const [context, setContext] = useState2(remembered.context ?? []);
|
|
1118
|
+
const [images, setImages] = useState2(remembered.images ?? []);
|
|
1119
|
+
const [queue, setQueue] = useState2((remembered.queue ?? []).map((item) => ({ ...item, context: item.context ?? [], images: item.images ?? [] })));
|
|
1120
|
+
const [dispatching, setDispatching] = useState2(false);
|
|
1121
|
+
const [picking, setPicking] = useState2(false);
|
|
1122
|
+
const [dragging, setDragging] = useState2(false);
|
|
1123
|
+
const [pickerError, setPickerError] = useState2(null);
|
|
1124
|
+
const textarea = useRef2(null);
|
|
976
1125
|
useAutosizeTextarea(textarea, draft);
|
|
977
1126
|
const remember = (nextDraft, nextContext, nextImages, nextQueue) => boundedSet(composerMemory, memoryKey, { draft: nextDraft, context: nextContext, images: nextImages, queue: nextQueue });
|
|
978
|
-
|
|
1127
|
+
useEffect2(() => {
|
|
979
1128
|
remember(draft, context, images, queue);
|
|
980
1129
|
}, [draft, context, images, memoryKey, queue]);
|
|
981
1130
|
const updateQueue = (update) => setQueue((items) => {
|
|
@@ -985,7 +1134,7 @@ function Composer({ state, adapter, labels = DEFAULT_LABELS, memoryKey = state.a
|
|
|
985
1134
|
});
|
|
986
1135
|
const queueBlocked = state.busy || pendingStatus !== null || dispatching;
|
|
987
1136
|
const queuesNewMessage = state.busy || pendingStatus === "sending" || pendingStatus === "failed" || dispatching;
|
|
988
|
-
|
|
1137
|
+
useEffect2(() => {
|
|
989
1138
|
if (!queueBlocked && state.canSend && queue.length) {
|
|
990
1139
|
const [next, ...rest] = queue;
|
|
991
1140
|
setDispatching(true);
|
|
@@ -995,13 +1144,13 @@ function Composer({ state, adapter, labels = DEFAULT_LABELS, memoryKey = state.a
|
|
|
995
1144
|
adapter.onIntent({ action: "send", text: next.text, ...next.context.length ? { context: next.context } : {}, ...next.images.length ? { images: next.images } : {} });
|
|
996
1145
|
}
|
|
997
1146
|
}, [adapter, draft, memoryKey, onPending, queue, queueBlocked, state.canSend]);
|
|
998
|
-
|
|
1147
|
+
useEffect2(() => {
|
|
999
1148
|
if (pendingStatus !== null || state.busy) setDispatching(false);
|
|
1000
1149
|
}, [pendingStatus, state.busy]);
|
|
1001
|
-
|
|
1150
|
+
useEffect2(() => {
|
|
1002
1151
|
textarea.current?.focus({ preventScroll: true });
|
|
1003
1152
|
}, [memoryKey]);
|
|
1004
|
-
|
|
1153
|
+
useEffect2(() => {
|
|
1005
1154
|
if (!restoreDraft) return;
|
|
1006
1155
|
setDraft(restoreDraft.text);
|
|
1007
1156
|
const restoredContext = normalizeContext(restoreDraft.context);
|
|
@@ -1012,7 +1161,7 @@ function Composer({ state, adapter, labels = DEFAULT_LABELS, memoryKey = state.a
|
|
|
1012
1161
|
textarea.current?.focus({ preventScroll: true });
|
|
1013
1162
|
onDraftRestored?.(restoreDraft.id);
|
|
1014
1163
|
}, [onDraftRestored, restoreDraft?.id]);
|
|
1015
|
-
|
|
1164
|
+
useEffect2(() => {
|
|
1016
1165
|
const timer = setTimeout(() => adapter.onIntent({ action: "draft", text: draft }), 250);
|
|
1017
1166
|
return () => clearTimeout(timer);
|
|
1018
1167
|
}, [adapter, draft]);
|
|
@@ -1137,12 +1286,12 @@ function Composer({ state, adapter, labels = DEFAULT_LABELS, memoryKey = state.a
|
|
|
1137
1286
|
}
|
|
1138
1287
|
|
|
1139
1288
|
// src/conversation.jsx
|
|
1140
|
-
import { Fragment as
|
|
1141
|
-
import { useEffect as
|
|
1289
|
+
import { Fragment as Fragment3 } from "preact";
|
|
1290
|
+
import { useEffect as useEffect4, useId, useLayoutEffect as useLayoutEffect2, useRef as useRef4, useState as useState3 } from "preact/hooks";
|
|
1142
1291
|
|
|
1143
1292
|
// src/markdown.jsx
|
|
1144
1293
|
import MarkdownIt from "markdown-it";
|
|
1145
|
-
import { useEffect as
|
|
1294
|
+
import { useEffect as useEffect3, useMemo, useRef as useRef3 } from "preact/hooks";
|
|
1146
1295
|
import { jsx as jsx4 } from "preact/jsx-runtime";
|
|
1147
1296
|
var markdown = new MarkdownIt({ html: false, linkify: true, breaks: false });
|
|
1148
1297
|
var LANGUAGE_LABELS = {
|
|
@@ -1204,8 +1353,8 @@ function setCopyState(button, status) {
|
|
|
1204
1353
|
}
|
|
1205
1354
|
function Markdown({ value, copyText }) {
|
|
1206
1355
|
const html = useMemo(() => markdown.render(value), [value]);
|
|
1207
|
-
const resets =
|
|
1208
|
-
|
|
1356
|
+
const resets = useRef3(/* @__PURE__ */ new Map());
|
|
1357
|
+
useEffect3(() => () => {
|
|
1209
1358
|
for (const timer of resets.current.values()) clearTimeout(timer);
|
|
1210
1359
|
resets.current.clear();
|
|
1211
1360
|
}, []);
|
|
@@ -1234,7 +1383,7 @@ function Markdown({ value, copyText }) {
|
|
|
1234
1383
|
}
|
|
1235
1384
|
|
|
1236
1385
|
// src/conversation.jsx
|
|
1237
|
-
import { Fragment as
|
|
1386
|
+
import { Fragment as Fragment4, jsx as jsx5, jsxs as jsxs4 } from "preact/jsx-runtime";
|
|
1238
1387
|
function LoadingStatus({ state, compact = false }) {
|
|
1239
1388
|
const copy = {
|
|
1240
1389
|
connecting: ["Connecting to coding agents", "Checking installed harnesses and capabilities.", 0],
|
|
@@ -1283,9 +1432,9 @@ function ContextDisclosure({ context }) {
|
|
|
1283
1432
|
] });
|
|
1284
1433
|
}
|
|
1285
1434
|
function MessageMeta({ entry, adapter }) {
|
|
1286
|
-
const [copyState, setCopyState2] =
|
|
1287
|
-
const reset =
|
|
1288
|
-
|
|
1435
|
+
const [copyState, setCopyState2] = useState3("idle");
|
|
1436
|
+
const reset = useRef4(null);
|
|
1437
|
+
useEffect4(() => () => clearTimeout(reset.current), []);
|
|
1289
1438
|
const date = entry.ts === null ? null : new Date(entry.ts);
|
|
1290
1439
|
const validDate = date && Number.isFinite(date.valueOf()) ? date : null;
|
|
1291
1440
|
if (!validDate && (!adapter?.copyText || !entry.text)) return null;
|
|
@@ -1306,33 +1455,33 @@ function MessageMeta({ entry, adapter }) {
|
|
|
1306
1455
|
] });
|
|
1307
1456
|
}
|
|
1308
1457
|
var TOOL_ICONS = {
|
|
1309
|
-
read: () => /* @__PURE__ */ jsxs4(
|
|
1458
|
+
read: () => /* @__PURE__ */ jsxs4(Fragment4, { children: [
|
|
1310
1459
|
/* @__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
1460
|
/* @__PURE__ */ jsx5("path", { d: "M12 2.9v3h2.85M6 9h6M6 12h4" })
|
|
1312
1461
|
] }),
|
|
1313
|
-
search: () => /* @__PURE__ */ jsxs4(
|
|
1462
|
+
search: () => /* @__PURE__ */ jsxs4(Fragment4, { children: [
|
|
1314
1463
|
/* @__PURE__ */ jsx5("circle", { cx: "8", cy: "8", r: "4.5" }),
|
|
1315
1464
|
/* @__PURE__ */ jsx5("path", { d: "m11.5 11.5 3 3" })
|
|
1316
1465
|
] }),
|
|
1317
|
-
edit: () => /* @__PURE__ */ jsxs4(
|
|
1466
|
+
edit: () => /* @__PURE__ */ jsxs4(Fragment4, { children: [
|
|
1318
1467
|
/* @__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
1468
|
/* @__PURE__ */ jsx5("path", { d: "m10 5 3 3" })
|
|
1320
1469
|
] }),
|
|
1321
|
-
command: () => /* @__PURE__ */ jsx5(
|
|
1322
|
-
test: () => /* @__PURE__ */ jsxs4(
|
|
1470
|
+
command: () => /* @__PURE__ */ jsx5(Fragment4, { children: /* @__PURE__ */ jsx5("path", { d: "m3 5 3 3-3 3M8 12h6" }) }),
|
|
1471
|
+
test: () => /* @__PURE__ */ jsxs4(Fragment4, { children: [
|
|
1323
1472
|
/* @__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
1473
|
/* @__PURE__ */ jsx5("path", { d: "M5 2.5h8" })
|
|
1325
1474
|
] }),
|
|
1326
|
-
web: () => /* @__PURE__ */ jsxs4(
|
|
1475
|
+
web: () => /* @__PURE__ */ jsxs4(Fragment4, { children: [
|
|
1327
1476
|
/* @__PURE__ */ jsx5("circle", { cx: "9", cy: "9", r: "6.5" }),
|
|
1328
1477
|
/* @__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
1478
|
] }),
|
|
1330
|
-
agent: () => /* @__PURE__ */ jsxs4(
|
|
1479
|
+
agent: () => /* @__PURE__ */ jsxs4(Fragment4, { children: [
|
|
1331
1480
|
/* @__PURE__ */ jsx5("circle", { cx: "9", cy: "6", r: "2.5" }),
|
|
1332
1481
|
/* @__PURE__ */ jsx5("path", { d: "M4 15c.4-3 2-4.5 5-4.5s4.6 1.5 5 4.5" })
|
|
1333
1482
|
] }),
|
|
1334
|
-
plan: () => /* @__PURE__ */ jsx5(
|
|
1335
|
-
other: () => /* @__PURE__ */ jsxs4(
|
|
1483
|
+
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" }) }),
|
|
1484
|
+
other: () => /* @__PURE__ */ jsxs4(Fragment4, { children: [
|
|
1336
1485
|
/* @__PURE__ */ jsx5("path", { d: "M9 2.5v3M9 12.5v3M2.5 9h3M12.5 9h3" }),
|
|
1337
1486
|
/* @__PURE__ */ jsx5("circle", { cx: "9", cy: "9", r: "3.5" })
|
|
1338
1487
|
] })
|
|
@@ -1398,9 +1547,9 @@ function ToolMetrics({ presentation }) {
|
|
|
1398
1547
|
}
|
|
1399
1548
|
function PendingElapsed({ now }) {
|
|
1400
1549
|
const clock = now ?? Date.now;
|
|
1401
|
-
const started =
|
|
1402
|
-
const [elapsed, setElapsed] =
|
|
1403
|
-
|
|
1550
|
+
const started = useRef4(clock());
|
|
1551
|
+
const [elapsed, setElapsed] = useState3(0);
|
|
1552
|
+
useEffect4(() => {
|
|
1404
1553
|
const timer = setInterval(() => setElapsed(Math.max(0, clock() - started.current)), 1e3);
|
|
1405
1554
|
return () => clearInterval(timer);
|
|
1406
1555
|
}, [clock]);
|
|
@@ -1441,7 +1590,7 @@ function SearchPreview({ presentation }) {
|
|
|
1441
1590
|
] }) : null,
|
|
1442
1591
|
lines.length ? /* @__PURE__ */ jsx5("ol", { children: lines.map((line, index) => {
|
|
1443
1592
|
const match = /^(.*?):(\d+)(?::(\d+))?:(.*)$/.exec(line);
|
|
1444
|
-
return /* @__PURE__ */ jsx5("li", { children: match ? /* @__PURE__ */ jsxs4(
|
|
1593
|
+
return /* @__PURE__ */ jsx5("li", { children: match ? /* @__PURE__ */ jsxs4(Fragment4, { children: [
|
|
1445
1594
|
/* @__PURE__ */ jsx5("code", { children: match[1] }),
|
|
1446
1595
|
/* @__PURE__ */ jsxs4("small", { children: [
|
|
1447
1596
|
match[2],
|
|
@@ -1474,9 +1623,9 @@ function ToolPreview({ presentation, entry }) {
|
|
|
1474
1623
|
return presentation.preview ? /* @__PURE__ */ jsx5("pre", { class: "scui-tool-output", "data-error": failed, children: stripAnsi(presentation.preview) }) : null;
|
|
1475
1624
|
}
|
|
1476
1625
|
function ToolActions({ presentation, adapter }) {
|
|
1477
|
-
const [copied, setCopied] =
|
|
1478
|
-
const reset =
|
|
1479
|
-
|
|
1626
|
+
const [copied, setCopied] = useState3(false);
|
|
1627
|
+
const reset = useRef4(null);
|
|
1628
|
+
useEffect4(() => () => clearTimeout(reset.current), []);
|
|
1480
1629
|
if (!adapter?.copyText) return null;
|
|
1481
1630
|
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
1631
|
const copy = async () => {
|
|
@@ -1523,7 +1672,7 @@ function TranscriptEntry({ entry, state, adapter }) {
|
|
|
1523
1672
|
}
|
|
1524
1673
|
if (entry.role === "notice" || entry.role === "system") return /* @__PURE__ */ jsx5("div", { class: "scui-notice", "data-code": entry.code, children: entry.text });
|
|
1525
1674
|
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 }),
|
|
1675
|
+
/* @__PURE__ */ jsx5(MessageImages, { items: entry.images, adapter }),
|
|
1527
1676
|
/* @__PURE__ */ jsx5(Markdown, { value: entry.text, copyText: adapter?.copyText }),
|
|
1528
1677
|
/* @__PURE__ */ jsx5(ContextDisclosure, { context: entry.context }),
|
|
1529
1678
|
entry.truncated ? /* @__PURE__ */ jsx5("small", { class: "scui-truncated", children: "Entry truncated by host \xB7 load native session for the complete value" }) : null,
|
|
@@ -1532,14 +1681,14 @@ function TranscriptEntry({ entry, state, adapter }) {
|
|
|
1532
1681
|
}
|
|
1533
1682
|
function ToolRow({ entry, workspace, open = false, adapter }) {
|
|
1534
1683
|
const presentation = entry.presentation ?? createToolPresentation(entry);
|
|
1535
|
-
const [expanded, setExpanded] =
|
|
1536
|
-
|
|
1684
|
+
const [expanded, setExpanded] = useState3(open || entry.status === "pending");
|
|
1685
|
+
useEffect4(() => {
|
|
1537
1686
|
if (entry.status === "pending") setExpanded(true);
|
|
1538
1687
|
}, [entry.status]);
|
|
1539
1688
|
const target = compactToolTarget(presentation.target, workspace);
|
|
1540
1689
|
const hasDetail = Boolean(entry.arguments || entry.resultText || presentation.preview || presentation.fields.length);
|
|
1541
1690
|
const category = presentation.category ?? toolCategory(entry);
|
|
1542
|
-
const summary = /* @__PURE__ */ jsxs4(
|
|
1691
|
+
const summary = /* @__PURE__ */ jsxs4(Fragment4, { children: [
|
|
1543
1692
|
/* @__PURE__ */ jsx5(ToolIcon, { category }),
|
|
1544
1693
|
/* @__PURE__ */ jsx5("strong", { children: toolAction2(entry, category, presentation) }),
|
|
1545
1694
|
target ? /* @__PURE__ */ jsx5("code", { class: "scui-tool-target", title: presentation.target, children: target }) : null,
|
|
@@ -1567,9 +1716,9 @@ function ToolRow({ entry, workspace, open = false, adapter }) {
|
|
|
1567
1716
|
function ActivityGroup({ entries, state, adapter }) {
|
|
1568
1717
|
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
1718
|
const active = entries.some((entry) => entry.status === "pending");
|
|
1570
|
-
const [open, setOpen] =
|
|
1719
|
+
const [open, setOpen] = useState3(active);
|
|
1571
1720
|
const id = useId();
|
|
1572
|
-
|
|
1721
|
+
useEffect4(() => {
|
|
1573
1722
|
if (active) setOpen(true);
|
|
1574
1723
|
}, [active]);
|
|
1575
1724
|
return /* @__PURE__ */ jsxs4("section", { class: "scui-activity", children: [
|
|
@@ -1645,9 +1794,9 @@ function SessionDetails({ semantics }) {
|
|
|
1645
1794
|
}
|
|
1646
1795
|
var conversationMemory = /* @__PURE__ */ new Map();
|
|
1647
1796
|
function ConversationAnnouncements({ state }) {
|
|
1648
|
-
const previousBusy =
|
|
1649
|
-
const [announcement, setAnnouncement] =
|
|
1650
|
-
|
|
1797
|
+
const previousBusy = useRef4(state.busy);
|
|
1798
|
+
const [announcement, setAnnouncement] = useState3("");
|
|
1799
|
+
useEffect4(() => {
|
|
1651
1800
|
if (previousBusy.current && !state.busy && !state.error) {
|
|
1652
1801
|
setAnnouncement(`${harnessDisplayName(state.harness) || "Coding agent"} finished working`);
|
|
1653
1802
|
}
|
|
@@ -1656,13 +1805,13 @@ function ConversationAnnouncements({ state }) {
|
|
|
1656
1805
|
return /* @__PURE__ */ jsx5("span", { class: "scui-sr-only", role: "status", "aria-live": "polite", "aria-atomic": "true", children: announcement });
|
|
1657
1806
|
}
|
|
1658
1807
|
function Conversation({ state, adapter, components = {}, slots = {}, memoryKey = state.attached?.key || `${state.harness}:${state.workspace}`, pending = null, unreadAfterMessages = null }) {
|
|
1659
|
-
const scroller =
|
|
1808
|
+
const scroller = useRef4(null);
|
|
1660
1809
|
const remembered = conversationMemory.get(memoryKey) ?? { top: null, atBottom: true };
|
|
1661
|
-
const [atBottom, setAtBottom] =
|
|
1662
|
-
const earlierAnchor =
|
|
1663
|
-
const restored =
|
|
1810
|
+
const [atBottom, setAtBottom] = useState3(remembered.atBottom);
|
|
1811
|
+
const earlierAnchor = useRef4(null);
|
|
1812
|
+
const restored = useRef4(false);
|
|
1664
1813
|
const blocks = groupConversation(state.transcript);
|
|
1665
|
-
const unreadBoundary =
|
|
1814
|
+
const unreadBoundary = useRef4(Number.isSafeInteger(unreadAfterMessages) && unreadAfterMessages >= 0 ? unreadAfterMessages : null);
|
|
1666
1815
|
const unreadBlock = unreadBoundary.current === null ? -1 : blocks.findIndex((block) => {
|
|
1667
1816
|
const entries = block.kind === "activity" ? block.entries : [block.entry];
|
|
1668
1817
|
return entries.some((entry) => Number.isSafeInteger(entry.messageIndex) && entry.messageIndex > unreadBoundary.current);
|
|
@@ -1721,12 +1870,12 @@ function Conversation({ state, adapter, components = {}, slots = {}, memoryKey =
|
|
|
1721
1870
|
}, children: "Load earlier messages" }) : null,
|
|
1722
1871
|
!blocks.length && state.startup !== "ready" ? /* @__PURE__ */ jsx5(LoadingStatus, { state }) : null,
|
|
1723
1872
|
!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(
|
|
1873
|
+
blocks.map((block, index) => /* @__PURE__ */ jsxs4(Fragment3, { children: [
|
|
1725
1874
|
index === unreadBlock ? /* @__PURE__ */ jsx5("div", { class: "scui-unread-divider", role: "separator", "aria-label": "New messages", children: /* @__PURE__ */ jsx5("span", { children: "New" }) }) : null,
|
|
1726
1875
|
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
1876
|
] }, block.id)),
|
|
1728
1877
|
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 }),
|
|
1878
|
+
/* @__PURE__ */ jsx5(MessageImages, { items: pendingMessage.images, adapter }),
|
|
1730
1879
|
/* @__PURE__ */ jsx5(Markdown, { value: pendingMessage.text, copyText: adapter?.copyText }),
|
|
1731
1880
|
/* @__PURE__ */ jsx5(ContextDisclosure, { context: pendingMessage.context }),
|
|
1732
1881
|
/* @__PURE__ */ jsxs4("footer", { children: [
|
|
@@ -1757,7 +1906,7 @@ function Conversation({ state, adapter, components = {}, slots = {}, memoryKey =
|
|
|
1757
1906
|
}
|
|
1758
1907
|
|
|
1759
1908
|
// src/logo.jsx
|
|
1760
|
-
import { useEffect as
|
|
1909
|
+
import { useEffect as useEffect5 } from "preact/hooks";
|
|
1761
1910
|
import { jsx as jsx6, jsxs as jsxs5 } from "preact/jsx-runtime";
|
|
1762
1911
|
var LOGOS = {
|
|
1763
1912
|
"claude-code": {
|
|
@@ -1805,7 +1954,7 @@ var LOGOS = {
|
|
|
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,7 +1965,7 @@ function HarnessLogo({ id, activity, size = 28, onMissingLogo }) {
|
|
|
1816
1965
|
}
|
|
1817
1966
|
|
|
1818
1967
|
// src/sessions.jsx
|
|
1819
|
-
import { useEffect as
|
|
1968
|
+
import { useEffect as useEffect6, useLayoutEffect as useLayoutEffect3, useRef as useRef5, useState as useState4 } from "preact/hooks";
|
|
1820
1969
|
import { jsx as jsx7, jsxs as jsxs6 } from "preact/jsx-runtime";
|
|
1821
1970
|
var sessionListMemory = /* @__PURE__ */ new Map();
|
|
1822
1971
|
function SessionRow({ row, state, onOpen }) {
|
|
@@ -1838,21 +1987,21 @@ function SessionRow({ row, state, onOpen }) {
|
|
|
1838
1987
|
}
|
|
1839
1988
|
function SessionList({ state, adapter, onOpen, onNew, onClose, components = {}, labels = DEFAULT_LABELS, focusKey = null, memoryKey = state.workspace || "@default" }) {
|
|
1840
1989
|
const remembered = sessionListMemory.get(memoryKey) ?? { query: "", top: 0 };
|
|
1841
|
-
const [query, setQuery] =
|
|
1842
|
-
const [loadingMore, setLoadingMore] =
|
|
1843
|
-
const root =
|
|
1844
|
-
const rowScroller =
|
|
1990
|
+
const [query, setQuery] = useState4(remembered.query);
|
|
1991
|
+
const [loadingMore, setLoadingMore] = useState4(false);
|
|
1992
|
+
const root = useRef5(null);
|
|
1993
|
+
const rowScroller = useRef5(null);
|
|
1845
1994
|
const rows = filterSessions(state.sessions, query);
|
|
1846
1995
|
const Row = components.SessionRow ?? SessionRow;
|
|
1847
1996
|
useLayoutEffect3(() => {
|
|
1848
1997
|
if (rowScroller.current) rowScroller.current.scrollTop = remembered.top;
|
|
1849
1998
|
}, [memoryKey]);
|
|
1850
|
-
|
|
1999
|
+
useEffect6(() => {
|
|
1851
2000
|
if (!focusKey || !root.current) return;
|
|
1852
2001
|
const target = focusKey === "@new" ? root.current.querySelector('[data-list-focus="new"]') : [...root.current.querySelectorAll("[data-session-key]")].find((element) => element.dataset.sessionKey === focusKey);
|
|
1853
2002
|
(target ?? root.current.querySelector("input,button"))?.focus({ preventScroll: true });
|
|
1854
2003
|
}, [focusKey, rows.length]);
|
|
1855
|
-
|
|
2004
|
+
useEffect6(() => {
|
|
1856
2005
|
if (loadingMore) setLoadingMore(false);
|
|
1857
2006
|
}, [state.error, state.history.hasMoreSessions, state.sessions.length]);
|
|
1858
2007
|
const loadMore = () => {
|
|
@@ -1938,10 +2087,10 @@ function Receipt({ state, adapter }) {
|
|
|
1938
2087
|
return null;
|
|
1939
2088
|
}
|
|
1940
2089
|
function ConversationActions({ state, adapter, actionPending }) {
|
|
1941
|
-
const [open, setOpen] =
|
|
1942
|
-
const root =
|
|
1943
|
-
const panel =
|
|
1944
|
-
const trigger =
|
|
2090
|
+
const [open, setOpen] = useState5(false);
|
|
2091
|
+
const root = useRef6(null);
|
|
2092
|
+
const panel = useRef6(null);
|
|
2093
|
+
const trigger = useRef6(null);
|
|
1945
2094
|
const menuId = useId2();
|
|
1946
2095
|
const targets = state.harnesses.filter((item) => item.startable);
|
|
1947
2096
|
const groups = [
|
|
@@ -1962,7 +2111,7 @@ function ConversationActions({ state, adapter, actionPending }) {
|
|
|
1962
2111
|
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
2112
|
}
|
|
1964
2113
|
].filter((group) => group.items.length);
|
|
1965
|
-
|
|
2114
|
+
useEffect7(() => {
|
|
1966
2115
|
if (!open) return;
|
|
1967
2116
|
panel.current?.querySelector("button:not(:disabled)")?.focus({ preventScroll: true });
|
|
1968
2117
|
const dismiss = (event) => {
|
|
@@ -2012,12 +2161,12 @@ function ConversationActions({ state, adapter, actionPending }) {
|
|
|
2012
2161
|
] });
|
|
2013
2162
|
}
|
|
2014
2163
|
function ChatHeader({ state, adapter, pendingStatus, actionPending, onBack, onNew, onClose }) {
|
|
2015
|
-
const back =
|
|
2164
|
+
const back = useRef6(null);
|
|
2016
2165
|
const harness = state.attached?.harness ?? state.harness;
|
|
2017
2166
|
const title = state.attached ? sessionDisplayName(state.attached) : harnessDisplayName(harness) || "Agent chat";
|
|
2018
2167
|
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
2168
|
const menu = state.canDetach || state.canOpenTerminal || state.canBranch || state.canAttach || state.canExport || state.canReduce;
|
|
2020
|
-
|
|
2169
|
+
useEffect7(() => {
|
|
2021
2170
|
if (state.mode === "mirror" && !state.canSend) back.current?.focus({ preventScroll: true });
|
|
2022
2171
|
}, []);
|
|
2023
2172
|
return /* @__PURE__ */ jsxs7("header", { class: "scui-head scui-chat-head", children: [
|
|
@@ -2039,24 +2188,24 @@ function ChatHeader({ state, adapter, pendingStatus, actionPending, onBack, onNe
|
|
|
2039
2188
|
function Chat({ state, adapter, onBack, onNew, onClose, components, slots, labels }) {
|
|
2040
2189
|
const Header = slots.header;
|
|
2041
2190
|
const memoryKey = state.attached?.key || `${state.harness}:${state.workspace}`;
|
|
2042
|
-
const [pending, setPendingState] =
|
|
2043
|
-
const [pendingAction, setPendingAction] =
|
|
2044
|
-
const [restoreDraft, setRestoreDraft] =
|
|
2045
|
-
const restoreSequence =
|
|
2046
|
-
const actionSequence =
|
|
2047
|
-
const acknowledged =
|
|
2191
|
+
const [pending, setPendingState] = useState5(() => pendingMessageMemory.get(memoryKey) ?? null);
|
|
2192
|
+
const [pendingAction, setPendingAction] = useState5(null);
|
|
2193
|
+
const [restoreDraft, setRestoreDraft] = useState5(null);
|
|
2194
|
+
const restoreSequence = useRef6(0);
|
|
2195
|
+
const actionSequence = useRef6(0);
|
|
2196
|
+
const acknowledged = useRef6(/* @__PURE__ */ new Set());
|
|
2048
2197
|
const setPending = (update) => setPendingState((current) => {
|
|
2049
2198
|
const next = typeof update === "function" ? update(current) : update;
|
|
2050
2199
|
if (next) boundedSet(pendingMessageMemory, memoryKey, next);
|
|
2051
2200
|
else pendingMessageMemory.delete(memoryKey);
|
|
2052
2201
|
return next;
|
|
2053
2202
|
});
|
|
2054
|
-
|
|
2203
|
+
useEffect7(() => {
|
|
2055
2204
|
setPendingState(pendingMessageMemory.get(memoryKey) ?? null);
|
|
2056
2205
|
setPendingAction(null);
|
|
2057
2206
|
setRestoreDraft(null);
|
|
2058
2207
|
}, [memoryKey]);
|
|
2059
|
-
|
|
2208
|
+
useEffect7(() => {
|
|
2060
2209
|
if (!pending) return;
|
|
2061
2210
|
if (state.transcript.some((entry) => entry.role === "user" && entry.text.trim() === pending.text.trim() && !pending.baselineIds.includes(entry.id))) {
|
|
2062
2211
|
setPending(null);
|
|
@@ -2085,7 +2234,7 @@ function Chat({ state, adapter, onBack, onNew, onClose, components, slots, label
|
|
|
2085
2234
|
setRestoreDraft({ id: restoreSequence.current, text: pending.text, context: pending.context, images: pending.images });
|
|
2086
2235
|
setPending({ ...pending, status: "editing" });
|
|
2087
2236
|
};
|
|
2088
|
-
|
|
2237
|
+
useEffect7(() => {
|
|
2089
2238
|
const key = state.attached?.key;
|
|
2090
2239
|
if (!key || !state.attention.some((item) => item.key === key)) {
|
|
2091
2240
|
if (key) acknowledged.current.delete(key);
|
|
@@ -2120,7 +2269,7 @@ function Chat({ state, adapter, onBack, onNew, onClose, components, slots, label
|
|
|
2120
2269
|
return result;
|
|
2121
2270
|
}
|
|
2122
2271
|
}), [adapter, state.error]);
|
|
2123
|
-
|
|
2272
|
+
useEffect7(() => {
|
|
2124
2273
|
if (!pendingAction) return;
|
|
2125
2274
|
if (state.operation && !pendingAction.seenOperation) {
|
|
2126
2275
|
setPendingAction({ ...pendingAction, seenOperation: true });
|
|
@@ -2153,24 +2302,24 @@ function NewChat({ state, adapter, onBack, onClose, onStarted, labels, memoryKey
|
|
|
2153
2302
|
const startable = state.harnesses.filter((item) => item.startable);
|
|
2154
2303
|
const startableKey = startable.map((item) => item.id).join("\0");
|
|
2155
2304
|
const remembered = newChatMemory.get(memoryKey) ?? { harness: startable[0]?.id ?? "", draft: "", context: [], images: [] };
|
|
2156
|
-
const [harness, setHarness] =
|
|
2157
|
-
const [draft, setDraft] =
|
|
2158
|
-
const [context, setContext] =
|
|
2159
|
-
const [images, setImages] =
|
|
2160
|
-
const [starting, setStarting] =
|
|
2161
|
-
const [picking, setPicking] =
|
|
2162
|
-
const [dragging, setDragging] =
|
|
2163
|
-
const [pickerError, setPickerError] =
|
|
2164
|
-
const startSequence =
|
|
2165
|
-
const textarea =
|
|
2305
|
+
const [harness, setHarness] = useState5(remembered.harness);
|
|
2306
|
+
const [draft, setDraft] = useState5(remembered.draft);
|
|
2307
|
+
const [context, setContext] = useState5(remembered.context);
|
|
2308
|
+
const [images, setImages] = useState5(remembered.images ?? []);
|
|
2309
|
+
const [starting, setStarting] = useState5(null);
|
|
2310
|
+
const [picking, setPicking] = useState5(false);
|
|
2311
|
+
const [dragging, setDragging] = useState5(false);
|
|
2312
|
+
const [pickerError, setPickerError] = useState5(null);
|
|
2313
|
+
const startSequence = useRef6(0);
|
|
2314
|
+
const textarea = useRef6(null);
|
|
2166
2315
|
useAutosizeTextarea(textarea, draft);
|
|
2167
|
-
|
|
2316
|
+
useEffect7(() => {
|
|
2168
2317
|
if (startable.some((item) => item.id === harness)) return;
|
|
2169
2318
|
const next = startable[0]?.id ?? "";
|
|
2170
2319
|
setHarness(next);
|
|
2171
2320
|
boundedSet(newChatMemory, memoryKey, { harness: next, draft, context, images });
|
|
2172
2321
|
}, [harness, startableKey]);
|
|
2173
|
-
|
|
2322
|
+
useEffect7(() => {
|
|
2174
2323
|
if (!starting) return;
|
|
2175
2324
|
const sessionChanged = (state.attached?.key ?? null) !== starting.attachedKey;
|
|
2176
2325
|
const beganWorking = !starting.busy && state.busy;
|
|
@@ -2178,10 +2327,10 @@ function NewChat({ state, adapter, onBack, onClose, onStarted, labels, memoryKey
|
|
|
2178
2327
|
newChatMemory.delete(memoryKey);
|
|
2179
2328
|
onStarted();
|
|
2180
2329
|
}, [memoryKey, onStarted, starting, state.attached?.key, state.busy]);
|
|
2181
|
-
|
|
2330
|
+
useEffect7(() => {
|
|
2182
2331
|
if (starting && state.error !== starting.initialError && !state.busy && !state.operation) setStarting(null);
|
|
2183
2332
|
}, [starting, state.busy, state.error, state.operation]);
|
|
2184
|
-
|
|
2333
|
+
useEffect7(() => {
|
|
2185
2334
|
textarea.current?.focus({ preventScroll: true });
|
|
2186
2335
|
}, []);
|
|
2187
2336
|
const pickContext = () => {
|
|
@@ -2315,14 +2464,14 @@ function SupercodeMessenger({ state: stateInput, adapter, class: className = "",
|
|
|
2315
2464
|
const state = useMemo2(() => normalizeUiState(stateInput), [stateInput]);
|
|
2316
2465
|
const copy = { ...DEFAULT_LABELS, ...labels };
|
|
2317
2466
|
const memoryKey = state.workspace || "@default";
|
|
2318
|
-
const [view, setViewState] =
|
|
2319
|
-
const [opening, setOpening] =
|
|
2320
|
-
const [listFocus, setListFocus] =
|
|
2467
|
+
const [view, setViewState] = useState5(initialView ?? (state.attention.length ? "list" : messengerViewMemory.get(memoryKey) ?? (state.attached || state.transcript.length ? "chat" : "list")));
|
|
2468
|
+
const [opening, setOpening] = useState5(null);
|
|
2469
|
+
const [listFocus, setListFocus] = useState5(null);
|
|
2321
2470
|
const setView = (next) => {
|
|
2322
2471
|
boundedSet(messengerViewMemory, memoryKey, next);
|
|
2323
2472
|
setViewState(next);
|
|
2324
2473
|
};
|
|
2325
|
-
|
|
2474
|
+
useEffect7(() => {
|
|
2326
2475
|
if (!opening) return;
|
|
2327
2476
|
if (state.attached?.key === opening.key) {
|
|
2328
2477
|
setOpening(null);
|