@volter-ai-dev/supercode-ui 0.1.70 → 0.1.72

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.
Files changed (59) hide show
  1. package/README.md +61 -1
  2. package/activity.mjs +5 -1235
  3. package/chunks/chunk-2FG7VKIW.mjs +201 -0
  4. package/chunks/chunk-4XUMCWPC.mjs +29 -0
  5. package/chunks/chunk-E65I2I2L.mjs +120 -0
  6. package/chunks/chunk-IL5T2AST.mjs +842 -0
  7. package/chunks/chunk-LOP6XOON.mjs +671 -0
  8. package/chunks/chunk-LS6JBXNT.mjs +1237 -0
  9. package/chunks/chunk-SJ3YTA6Z.mjs +316 -0
  10. package/chunks/chunk-TKTOVDXB.mjs +139 -0
  11. package/chunks/chunk-V3UM7H7W.mjs +328 -0
  12. package/chunks/chunk-WRK5TBX3.mjs +224 -0
  13. package/chunks/chunk-ZLYHUYE2.mjs +62 -0
  14. package/components.d.ts +3 -0
  15. package/components.mjs +59 -3766
  16. package/composer.mjs +8 -580
  17. package/conversation.mjs +16 -1373
  18. package/embed.mjs +15 -3681
  19. package/icon.mjs +3 -58
  20. package/index.d.ts +25 -0
  21. package/logo.mjs +5 -87
  22. package/messenger.d.ts +3 -0
  23. package/messenger.mjs +21 -3677
  24. package/package.json +22 -3
  25. package/react/activity.mjs +5 -1235
  26. package/react/chunks/chunk-7ES5FSLG.mjs +316 -0
  27. package/react/chunks/chunk-LS6JBXNT.mjs +1237 -0
  28. package/react/chunks/chunk-LYRIQE5M.mjs +29 -0
  29. package/react/chunks/chunk-NJC6S2AW.mjs +201 -0
  30. package/react/chunks/chunk-OINC7LV7.mjs +62 -0
  31. package/react/chunks/chunk-QL6QEYPK.mjs +842 -0
  32. package/react/chunks/chunk-UGL6AB2K.mjs +120 -0
  33. package/react/chunks/chunk-UKHDCPME.mjs +328 -0
  34. package/react/chunks/chunk-UKMTRNJN.mjs +671 -0
  35. package/react/chunks/chunk-VFOVJ4S3.mjs +139 -0
  36. package/react/chunks/chunk-YK7SJ6EH.mjs +224 -0
  37. package/react/components.mjs +59 -3766
  38. package/react/composer.mjs +8 -580
  39. package/react/conversation.mjs +16 -1373
  40. package/react/icon.mjs +3 -58
  41. package/react/index.d.ts +25 -0
  42. package/react/logo.mjs +5 -87
  43. package/react/messenger.d.ts +3 -0
  44. package/react/messenger.mjs +21 -3677
  45. package/react/sessions.mjs +9 -519
  46. package/react/settings.mjs +9 -425
  47. package/react/subagents.mjs +8 -1546
  48. package/react/supervision-components.mjs +1354 -0
  49. package/react/supervision.d.ts +36 -0
  50. package/sessions.mjs +9 -519
  51. package/settings.mjs +9 -425
  52. package/source-inventory.d.ts +240 -0
  53. package/source-inventory.mjs +367 -0
  54. package/styles.css +104 -0
  55. package/subagents.mjs +8 -1546
  56. package/supervision-components.d.ts +36 -0
  57. package/supervision-components.mjs +1354 -0
  58. package/supervision.d.ts +242 -0
  59. package/supervision.mjs +349 -0
@@ -0,0 +1,120 @@
1
+ import {
2
+ HarnessLogo
3
+ } from "./chunk-VFOVJ4S3.mjs";
4
+ import {
5
+ Conversation
6
+ } from "./chunk-UKMTRNJN.mjs";
7
+ import {
8
+ harnessDisplayName,
9
+ sessionDisplayName
10
+ } from "./chunk-LS6JBXNT.mjs";
11
+ import {
12
+ UiIcon
13
+ } from "./chunk-OINC7LV7.mjs";
14
+
15
+ // src/subagents.jsx
16
+ import { useEffect, useRef, useState } from "react";
17
+ import { jsx, jsxs } from "react/jsx-runtime";
18
+ var ACTIVE_ACTIVITY = /* @__PURE__ */ new Set(["working", "running", "recent"]);
19
+ function activityLabel(activity, age) {
20
+ if (activity === "needs-input") return "Needs input";
21
+ if (activity === "failed") return "Failed";
22
+ if (activity === "unseen") return "New update";
23
+ if (activity === "working") return "Working";
24
+ if (activity === "running") return "Running";
25
+ if (activity === "recent") return "Recent";
26
+ if (activity === "finished") return "Finished";
27
+ return age;
28
+ }
29
+ function SubagentRow({ row, inspector, adapter }) {
30
+ return /* @__PURE__ */ jsxs("button", { className: "scui-subagent-row", type: "button", "data-activity": row.activity ?? "idle", onClick: () => adapter.onIntent({ action: "openSubagent", parentKey: inspector.parentKey, key: row.key }), children: [
31
+ /* @__PURE__ */ jsx(HarnessLogo, { id: row.harness, activity: row.activity, size: 30 }),
32
+ /* @__PURE__ */ jsxs("span", { children: [
33
+ /* @__PURE__ */ jsx("strong", { children: sessionDisplayName(row) }),
34
+ /* @__PURE__ */ jsx("small", { children: row.preview || (row.activity === "working" ? "Working\u2026" : row.cwd) })
35
+ ] }),
36
+ /* @__PURE__ */ jsxs("span", { children: [
37
+ /* @__PURE__ */ jsx("small", { children: activityLabel(row.activity, row.age) }),
38
+ /* @__PURE__ */ jsx(UiIcon, { name: "chevron", size: 14 })
39
+ ] })
40
+ ] });
41
+ }
42
+ function SubagentSection({ label, rows, inspector, adapter }) {
43
+ if (!rows.length) return null;
44
+ return /* @__PURE__ */ jsxs("section", { className: "scui-subagent-section", "aria-labelledby": `scui-subagents-${label.toLowerCase().replaceAll(" ", "-")}`, children: [
45
+ /* @__PURE__ */ jsx("h2", { id: `scui-subagents-${label.toLowerCase().replaceAll(" ", "-")}`, children: label }),
46
+ rows.map((row) => /* @__PURE__ */ jsx(SubagentRow, { row, inspector, adapter }, row.key))
47
+ ] });
48
+ }
49
+ function SubagentInspectorContent({ state, adapter, onClose, inspector }) {
50
+ const selected = inspector.items.find((row) => row.key === inspector.selectedKey) ?? null;
51
+ const active = inspector.items.filter((row) => ACTIVE_ACTIVITY.has(row.activity));
52
+ const history = inspector.items.filter((row) => !ACTIVE_ACTIVITY.has(row.activity));
53
+ const [historyOpen, setHistoryOpen] = useState(false);
54
+ const backButton = useRef(null);
55
+ useEffect(() => {
56
+ if (inspector.status === "ready" && !active.length) setHistoryOpen(true);
57
+ }, [inspector.parentKey, inspector.status]);
58
+ useEffect(() => {
59
+ backButton.current?.focus({ preventScroll: true });
60
+ }, [selected?.key]);
61
+ const detailState = selected ? {
62
+ ...state,
63
+ transcript: inspector.transcript,
64
+ busy: selected.activity === "working",
65
+ needsInput: selected.activity === "needs-input",
66
+ harness: selected.harness,
67
+ mode: "mirror",
68
+ canSend: false,
69
+ canSteer: false,
70
+ canInterrupt: false,
71
+ canRespond: false,
72
+ taskPlan: { source: "none", items: [], residueCount: 0, observedAt: null },
73
+ semantics: { fidelity: null, residue: [], residueCount: 0, parseErrors: 0, rawRecords: 0, subagents: [] },
74
+ history: { ...state.history, hasEarlier: false }
75
+ } : null;
76
+ const leave = () => {
77
+ adapter.onIntent({ action: "closeSubagents" });
78
+ onClose();
79
+ };
80
+ const back = () => selected ? adapter.onIntent({ action: "openSubagents", key: inspector.parentKey }) : leave();
81
+ return /* @__PURE__ */ jsxs("section", { className: "scui-subagents", "aria-label": `Agents for ${inspector.parentTitle}`, children: [
82
+ /* @__PURE__ */ jsxs("header", { className: "scui-head", children: [
83
+ /* @__PURE__ */ jsx("button", { ref: backButton, type: "button", "aria-label": selected ? "Back to agents" : "Back to chats", onClick: back, children: /* @__PURE__ */ jsx(UiIcon, { name: "back", size: 17 }) }),
84
+ selected ? /* @__PURE__ */ jsx(HarnessLogo, { id: selected.harness, activity: selected.activity, size: 24 }) : /* @__PURE__ */ jsx(UiIcon, { name: "agents", size: 20 }),
85
+ /* @__PURE__ */ jsxs("span", { className: "scui-head-copy", children: [
86
+ /* @__PURE__ */ jsx("strong", { children: selected ? sessionDisplayName(selected) : "Agents" }),
87
+ /* @__PURE__ */ jsx("small", { children: selected ? `${harnessDisplayName(selected.harness)} \xB7 Read-only` : `${inspector.items.length} ${inspector.items.length === 1 ? "session" : "sessions"} \xB7 ${inspector.parentTitle}` })
88
+ ] })
89
+ ] }),
90
+ inspector.status === "loading" ? /* @__PURE__ */ jsxs("div", { className: "scui-subagents-loading", role: "status", children: [
91
+ /* @__PURE__ */ jsx("i", {}),
92
+ /* @__PURE__ */ jsxs("span", { children: [
93
+ /* @__PURE__ */ jsx("strong", { children: selected ? "Loading subagent" : "Finding subagents" }),
94
+ /* @__PURE__ */ jsx("small", { children: selected ? "Reading a bounded recent transcript\u2026" : "Reading native session relationships\u2026" })
95
+ ] })
96
+ ] }) : null,
97
+ inspector.error ? /* @__PURE__ */ jsx("div", { className: "scui-error", role: "alert", children: inspector.error }) : null,
98
+ !selected && inspector.status === "ready" && inspector.items.length ? /* @__PURE__ */ jsxs("div", { className: "scui-subagent-rows", children: [
99
+ /* @__PURE__ */ jsx(SubagentSection, { label: "Active", rows: active, inspector, adapter }),
100
+ history.length ? /* @__PURE__ */ jsxs("details", { className: "scui-subagent-history", open: historyOpen, onToggle: (event) => setHistoryOpen(event.currentTarget.open), children: [
101
+ /* @__PURE__ */ jsxs("summary", { children: [
102
+ /* @__PURE__ */ jsx("span", { children: "History" }),
103
+ /* @__PURE__ */ jsx("small", { children: history.length }),
104
+ /* @__PURE__ */ jsx(UiIcon, { name: "chevron", size: 14 })
105
+ ] }),
106
+ /* @__PURE__ */ jsx("div", { children: history.map((row) => /* @__PURE__ */ jsx(SubagentRow, { row, inspector, adapter }, row.key)) })
107
+ ] }) : null
108
+ ] }) : null,
109
+ !selected && inspector.status === "ready" && !inspector.items.length ? /* @__PURE__ */ jsx("div", { className: "scui-empty", children: "No native child sessions were found." }) : null,
110
+ selected && inspector.status === "ready" && detailState ? /* @__PURE__ */ jsx(Conversation, { state: detailState, adapter, memoryKey: `subagent:${selected.key}` }) : null
111
+ ] });
112
+ }
113
+ function SubagentInspector({ state, adapter, onClose }) {
114
+ if (!state.subagentInspector) return null;
115
+ return /* @__PURE__ */ jsx(SubagentInspectorContent, { state, adapter, onClose, inspector: state.subagentInspector });
116
+ }
117
+
118
+ export {
119
+ SubagentInspector
120
+ };
@@ -0,0 +1,328 @@
1
+ import {
2
+ UiIcon
3
+ } from "./chunk-OINC7LV7.mjs";
4
+
5
+ // src/memory.js
6
+ var MEMORY_LIMIT = 100;
7
+ var registry = /* @__PURE__ */ new Set();
8
+ function uiMemory() {
9
+ const map = /* @__PURE__ */ new Map();
10
+ registry.add(map);
11
+ return map;
12
+ }
13
+ function resetSupercodeUiMemory() {
14
+ for (const map of registry) map.clear();
15
+ }
16
+ function boundedSet(map, key, value) {
17
+ map.delete(key);
18
+ map.set(key, value);
19
+ while (map.size > MEMORY_LIMIT) map.delete(map.keys().next().value);
20
+ }
21
+
22
+ // src/context.jsx
23
+ import { useEffect, useRef, useState } from "react";
24
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
25
+ var MAX_CONTEXT_ITEMS = 32;
26
+ var MAX_IMAGE_ITEMS = 4;
27
+ var MAX_IMAGE_BYTES = 5 * 1024 * 1024;
28
+ var MAX_RESOLVED_IMAGE_BYTES = 16 * 1024 * 1024;
29
+ var IMAGE_TYPES = /* @__PURE__ */ new Set(["image/png", "image/jpeg", "image/gif", "image/webp"]);
30
+ function normalizeContext(value) {
31
+ return (Array.isArray(value) ? value : value ? [value] : []).flatMap((item) => {
32
+ if (!item || typeof item.label !== "string" || typeof item.detail !== "string") return [];
33
+ const label = item.label.trim().slice(0, 200);
34
+ const detail = item.detail.slice(0, 2e4);
35
+ if (!label || !detail) return [];
36
+ return [{
37
+ ...typeof item.id === "string" && item.id ? { id: item.id.slice(0, 2e3) } : {},
38
+ ...typeof item.kind === "string" && item.kind ? { kind: item.kind.slice(0, 100) } : {},
39
+ label,
40
+ detail
41
+ }];
42
+ }).slice(0, MAX_CONTEXT_ITEMS);
43
+ }
44
+ function mergeContext(current, picked) {
45
+ const next = [...current];
46
+ const seen = new Set(current.map((item) => item.id ? `id:${item.id}` : `value:${item.kind ?? ""}\0${item.label}\0${item.detail}`));
47
+ for (const item of normalizeContext(picked)) {
48
+ const key = item.id ? `id:${item.id}` : `value:${item.kind ?? ""}\0${item.label}\0${item.detail}`;
49
+ if (seen.has(key)) continue;
50
+ seen.add(key);
51
+ next.push(item);
52
+ if (next.length === MAX_CONTEXT_ITEMS) break;
53
+ }
54
+ return next;
55
+ }
56
+ function normalizeImages(value) {
57
+ const seen = /* @__PURE__ */ new Set();
58
+ return (Array.isArray(value) ? value : value ? [value] : []).flatMap((item) => {
59
+ if (!item || typeof item.label !== "string" || typeof item.url !== "string") return [];
60
+ const label = item.label.trim().slice(0, 200);
61
+ const url = item.url;
62
+ if (!label || seen.has(url) || !(url.startsWith("data:image/") || url.startsWith("https://") || url.startsWith("http://"))) return [];
63
+ seen.add(url);
64
+ return [{
65
+ ...typeof item.id === "string" && item.id ? { id: item.id.slice(0, 2e3) } : {},
66
+ label,
67
+ url
68
+ }];
69
+ }).slice(0, MAX_IMAGE_ITEMS);
70
+ }
71
+ function mergeImages(current, picked) {
72
+ const next = [...current];
73
+ const seen = new Set(current.map((item) => item.url));
74
+ for (const item of normalizeImages(picked)) {
75
+ if (seen.has(item.url)) continue;
76
+ seen.add(item.url);
77
+ next.push(item);
78
+ if (next.length === MAX_IMAGE_ITEMS) break;
79
+ }
80
+ return next;
81
+ }
82
+ function partitionAttachments(value) {
83
+ const values = Array.isArray(value) ? value : value ? [value] : [];
84
+ const context = [];
85
+ const images = [];
86
+ for (const item of values) {
87
+ if (item && typeof item.label === "string" && item.label.trim() && typeof item.detail === "string" && item.detail) context.push(item);
88
+ else if (item && typeof item.label === "string" && item.label.trim() && typeof item.url === "string" && (item.url.startsWith("data:image/") || item.url.startsWith("https://") || item.url.startsWith("http://"))) images.push(item);
89
+ else throw new Error("The attachment picker returned an invalid item.");
90
+ }
91
+ return { context, images };
92
+ }
93
+ function normalizeAttachmentCandidates(value) {
94
+ const candidates = [];
95
+ for (const item of Array.isArray(value) ? value : []) {
96
+ const context = normalizeContext(item)[0];
97
+ if (context) {
98
+ candidates.push(context);
99
+ continue;
100
+ }
101
+ const image = normalizeImages(item)[0];
102
+ if (image) candidates.push(image);
103
+ }
104
+ return candidates.slice(0, MAX_CONTEXT_ITEMS + MAX_IMAGE_ITEMS);
105
+ }
106
+ function attachmentKey(item) {
107
+ if (item.id) return `id:${item.id}`;
108
+ return "detail" in item ? `context:${item.kind ?? ""}\0${item.label}\0${item.detail}` : `image:${item.url}`;
109
+ }
110
+ function ContextCandidate({ attachment, attached, onAttach }) {
111
+ const image = "url" in attachment;
112
+ return /* @__PURE__ */ jsxs("button", { type: "button", disabled: attached, "aria-label": `${attached ? "Attached" : "Attach"} ${attachment.label}`, onClick: () => onAttach(attachment), children: [
113
+ image ? /* @__PURE__ */ jsx("img", { src: attachment.url, alt: "" }) : /* @__PURE__ */ jsx(UiIcon, { name: "attach", size: 13 }),
114
+ /* @__PURE__ */ jsxs("span", { children: [
115
+ /* @__PURE__ */ jsx("strong", { children: attachment.label }),
116
+ /* @__PURE__ */ jsx("small", { children: image ? "Image" : attachment.kind || "Context" })
117
+ ] }),
118
+ attached ? /* @__PURE__ */ jsx(UiIcon, { name: "check", size: 13 }) : /* @__PURE__ */ jsx(UiIcon, { name: "plus", size: 13 })
119
+ ] });
120
+ }
121
+ function ContextCandidates({ items, context, images, state, adapter, onAttach, component: Candidate = ContextCandidate }) {
122
+ if (!items.length) return null;
123
+ const attached = new Set([...context, ...images].map(attachmentKey));
124
+ return /* @__PURE__ */ jsx("div", { className: "scui-context-candidates", "aria-label": "Available context", children: items.map((attachment, index) => /* @__PURE__ */ jsx(Candidate, { value: attachment, attachment, attached: attached.has(attachmentKey(attachment)), state, adapter, onAttach, index }, attachmentKey(attachment))) });
125
+ }
126
+ async function imageAttachmentsFromFiles(value) {
127
+ const files = Array.from(value ?? []).filter((file) => file?.type?.startsWith("image/"));
128
+ if (files.length > MAX_IMAGE_ITEMS) throw new Error(`Attach at most ${MAX_IMAGE_ITEMS} images at a time.`);
129
+ return Promise.all(files.map(async (file) => {
130
+ if (!IMAGE_TYPES.has(file.type)) throw new Error(`${file.name || "That image"} is not PNG, JPEG, GIF, or WebP.`);
131
+ if (file.size > MAX_IMAGE_BYTES) throw new Error(`${file.name || "That image"} is larger than 5 MB.`);
132
+ const url = await new Promise((resolve, reject) => {
133
+ const reader = new FileReader();
134
+ reader.onload = () => resolve(reader.result);
135
+ reader.onerror = () => reject(reader.error ?? new Error(`Could not read ${file.name || "image"}.`));
136
+ reader.readAsDataURL(file);
137
+ });
138
+ return { id: `${file.name}:${file.size}:${file.lastModified}`, label: file.name || "Pasted image", url };
139
+ }));
140
+ }
141
+ function ContextTray({ items, onRemove }) {
142
+ if (!items.length) return null;
143
+ return /* @__PURE__ */ jsx("div", { className: "scui-compose-context", "aria-label": "Attached context", children: items.map((item, index) => /* @__PURE__ */ jsxs("span", { children: [
144
+ /* @__PURE__ */ jsx(UiIcon, { name: "attach", size: 12 }),
145
+ /* @__PURE__ */ jsx("strong", { children: item.label }),
146
+ /* @__PURE__ */ jsx("button", { type: "button", "aria-label": `Remove context ${item.label}`, onClick: () => onRemove(index), children: /* @__PURE__ */ jsx(UiIcon, { name: "close", size: 12 }) })
147
+ ] }, item.id ?? `${item.label}:${index}`)) });
148
+ }
149
+ function ImageTray({ items, onRemove }) {
150
+ if (!items.length) return null;
151
+ return /* @__PURE__ */ jsx("div", { className: "scui-compose-images", "aria-label": "Attached images", children: items.map((item, index) => /* @__PURE__ */ jsxs("span", { children: [
152
+ /* @__PURE__ */ jsx("img", { src: item.url, alt: "" }),
153
+ /* @__PURE__ */ jsx("strong", { children: item.label }),
154
+ onRemove ? /* @__PURE__ */ jsx("button", { type: "button", "aria-label": `Remove image ${item.label}`, onClick: () => onRemove(index), children: /* @__PURE__ */ jsx(UiIcon, { name: "close", size: 12 }) }) : null
155
+ ] }, item.id ?? `${item.label}:${index}`)) });
156
+ }
157
+ function imageFilename(label) {
158
+ const value = label.trim().replace(/[\\/:*?"<>|]+/g, "-");
159
+ return value || "image";
160
+ }
161
+ function ImageViewer({ items, index, adapter, onChange, onClose }) {
162
+ const dialog = useRef(null);
163
+ const reset = useRef(null);
164
+ const resolutions = useRef(/* @__PURE__ */ new Map());
165
+ const alive = useRef(true);
166
+ const [, redraw] = useState(0);
167
+ const [copyState, setCopyState] = useState("idle");
168
+ const item = items[index];
169
+ const key = item?.reference ?? item?.url ?? `${item?.id ?? ""}:${index}`;
170
+ const resolution = item?.url ? null : resolutions.current.get(key);
171
+ const imageUrl = item?.url ?? (resolution?.status === "ready" ? resolution.url : null);
172
+ const remote = imageUrl?.startsWith("http://") || imageUrl?.startsWith("https://");
173
+ const resolve = (candidate, force = false) => {
174
+ if (candidate?.url || !candidate?.reference || !adapter?.resolveImage) return;
175
+ const candidateKey = candidate.reference;
176
+ const current = resolutions.current.get(candidateKey);
177
+ if (!force && (current?.status === "loading" || current?.status === "ready")) return;
178
+ if (current?.url) URL.revokeObjectURL(current.url);
179
+ resolutions.current.set(candidateKey, { status: "loading" });
180
+ redraw((value) => value + 1);
181
+ Promise.resolve().then(() => adapter.resolveImage(candidate)).then((blob) => {
182
+ if (!(blob instanceof Blob) || !blob.type.startsWith("image/")) throw new Error("The host returned an invalid image.");
183
+ if (blob.size > MAX_RESOLVED_IMAGE_BYTES) throw new Error("This image is too large to preview safely.");
184
+ if (!alive.current) return;
185
+ const url = URL.createObjectURL(blob);
186
+ resolutions.current.set(candidateKey, { status: "ready", url });
187
+ redraw((value) => value + 1);
188
+ }).catch((error) => {
189
+ if (!alive.current) return;
190
+ resolutions.current.set(candidateKey, {
191
+ status: "error",
192
+ message: error instanceof Error && error.message ? error.message : "Could not load this image."
193
+ });
194
+ redraw((value) => value + 1);
195
+ });
196
+ };
197
+ useEffect(() => {
198
+ alive.current = true;
199
+ if (!dialog.current?.open) dialog.current?.showModal();
200
+ return () => {
201
+ alive.current = false;
202
+ clearTimeout(reset.current);
203
+ for (const value of resolutions.current.values()) if (value.url) URL.revokeObjectURL(value.url);
204
+ resolutions.current.clear();
205
+ };
206
+ }, []);
207
+ useEffect(() => {
208
+ clearTimeout(reset.current);
209
+ setCopyState("idle");
210
+ resolve(item);
211
+ }, [index, item?.reference]);
212
+ if (!item || !item.url && (!item.reference || !adapter?.resolveImage)) return null;
213
+ const move = (amount) => onChange((index + amount + items.length) % items.length);
214
+ const copy = async () => {
215
+ try {
216
+ await adapter.copyText(imageUrl);
217
+ setCopyState("copied");
218
+ } catch {
219
+ setCopyState("failed");
220
+ }
221
+ clearTimeout(reset.current);
222
+ reset.current = setTimeout(() => setCopyState("idle"), 1500);
223
+ };
224
+ const close = () => dialog.current?.close();
225
+ return /* @__PURE__ */ jsxs(
226
+ "dialog",
227
+ {
228
+ ref: dialog,
229
+ className: "scui-image-viewer",
230
+ "aria-label": `Image preview: ${item.label}`,
231
+ onClose,
232
+ onClick: (event) => {
233
+ if (event.target === event.currentTarget) close();
234
+ },
235
+ onKeyDown: (event) => {
236
+ if (items.length < 2 || !["ArrowLeft", "ArrowRight"].includes(event.key)) return;
237
+ event.preventDefault();
238
+ move(event.key === "ArrowLeft" ? -1 : 1);
239
+ },
240
+ children: [
241
+ /* @__PURE__ */ jsxs("header", { children: [
242
+ /* @__PURE__ */ jsxs("span", { children: [
243
+ /* @__PURE__ */ jsx("strong", { children: item.label }),
244
+ items.length > 1 ? /* @__PURE__ */ jsxs("small", { children: [
245
+ index + 1,
246
+ " of ",
247
+ items.length
248
+ ] }) : null
249
+ ] }),
250
+ /* @__PURE__ */ jsxs("nav", { "aria-label": "Image actions", children: [
251
+ remote && adapter?.copyText ? /* @__PURE__ */ jsx("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__ */ jsx(UiIcon, { name: copyState === "copied" ? "check" : "copy", size: 16 }) }) : null,
252
+ imageUrl ? remote ? /* @__PURE__ */ jsx("a", { href: imageUrl, target: "_blank", rel: "noreferrer", "aria-label": "Open original image", title: "Open original image", children: /* @__PURE__ */ jsx(UiIcon, { name: "image", size: 16 }) }) : /* @__PURE__ */ jsx("a", { href: imageUrl, download: imageFilename(item.label), "aria-label": "Download image", title: "Download image", children: /* @__PURE__ */ jsx(UiIcon, { name: "down", size: 16 }) }) : null,
253
+ /* @__PURE__ */ jsx("button", { type: "button", "aria-label": "Close image preview", title: "Close", onClick: close, children: /* @__PURE__ */ jsx(UiIcon, { name: "close", size: 17 }) })
254
+ ] })
255
+ ] }),
256
+ /* @__PURE__ */ jsxs("figure", { children: [
257
+ imageUrl ? /* @__PURE__ */ jsx("img", { src: imageUrl, alt: item.label }) : resolution?.status === "error" ? /* @__PURE__ */ jsxs("div", { className: "scui-image-resolution", role: "alert", children: [
258
+ /* @__PURE__ */ jsx(UiIcon, { name: "image", size: 28 }),
259
+ /* @__PURE__ */ jsx("strong", { children: "Could not load image" }),
260
+ /* @__PURE__ */ jsx("small", { children: resolution.message }),
261
+ /* @__PURE__ */ jsx("button", { type: "button", onClick: () => resolve(item, true), children: "Retry" })
262
+ ] }) : /* @__PURE__ */ jsxs("div", { className: "scui-image-resolution", role: "status", children: [
263
+ /* @__PURE__ */ jsx("i", { className: "scui-control-spinner" }),
264
+ /* @__PURE__ */ jsx("strong", { children: "Loading image\u2026" }),
265
+ /* @__PURE__ */ jsx("small", { children: "The original stays out of the transcript payload." })
266
+ ] }),
267
+ items.length > 1 ? /* @__PURE__ */ jsxs(Fragment, { children: [
268
+ /* @__PURE__ */ jsx("button", { type: "button", className: "scui-image-previous", "aria-label": "Previous image", onClick: () => move(-1), children: /* @__PURE__ */ jsx(UiIcon, { name: "chevron", size: 19 }) }),
269
+ /* @__PURE__ */ jsx("button", { type: "button", className: "scui-image-next", "aria-label": "Next image", onClick: () => move(1), children: /* @__PURE__ */ jsx(UiIcon, { name: "chevron", size: 19 }) })
270
+ ] }) : null
271
+ ] })
272
+ ]
273
+ }
274
+ );
275
+ }
276
+ function MessageImages({ items, adapter }) {
277
+ const [active, setActive] = useState(null);
278
+ const opener = useRef(null);
279
+ if (!items?.length) return null;
280
+ const viewable = items.filter((item) => item.url || item.reference && adapter?.resolveImage);
281
+ const close = () => {
282
+ setActive(null);
283
+ requestAnimationFrame(() => opener.current?.focus({ preventScroll: true }));
284
+ };
285
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
286
+ /* @__PURE__ */ jsx("div", { className: "scui-message-images", "aria-label": "Message images", children: items.map((item, index) => item.url ? /* @__PURE__ */ jsx("button", { type: "button", "aria-label": `View image ${item.label}`, onClick: (event) => {
287
+ opener.current = event.currentTarget;
288
+ setActive(viewable.indexOf(item));
289
+ }, children: /* @__PURE__ */ jsx("img", { src: item.url, alt: "" }) }, item.id ?? `${item.label}:${index}`) : item.reference && adapter?.resolveImage ? /* @__PURE__ */ jsxs("button", { type: "button", "data-lazy": "true", "aria-label": `Load image ${item.label}`, onClick: (event) => {
290
+ opener.current = event.currentTarget;
291
+ setActive(viewable.indexOf(item));
292
+ }, children: [
293
+ /* @__PURE__ */ jsx(UiIcon, { name: "image", size: 16 }),
294
+ /* @__PURE__ */ jsxs("span", { children: [
295
+ /* @__PURE__ */ jsx("strong", { children: item.label }),
296
+ /* @__PURE__ */ jsx("small", { children: "Load preview" })
297
+ ] })
298
+ ] }, item.id ?? `${item.label}:${index}`) : /* @__PURE__ */ jsxs("span", { "data-unavailable": "true", children: [
299
+ /* @__PURE__ */ jsx(UiIcon, { name: "image", size: 14 }),
300
+ /* @__PURE__ */ jsxs("span", { children: [
301
+ /* @__PURE__ */ jsx("strong", { children: item.label }),
302
+ /* @__PURE__ */ jsx("small", { children: "Preview unavailable" })
303
+ ] })
304
+ ] }, item.id ?? `${item.label}:${index}`)) }),
305
+ active !== null && viewable[active] ? /* @__PURE__ */ jsx(ImageViewer, { items: viewable, index: active, adapter, onChange: setActive, onClose: close }) : null
306
+ ] });
307
+ }
308
+
309
+ export {
310
+ uiMemory,
311
+ resetSupercodeUiMemory,
312
+ boundedSet,
313
+ MAX_CONTEXT_ITEMS,
314
+ MAX_IMAGE_ITEMS,
315
+ normalizeContext,
316
+ mergeContext,
317
+ normalizeImages,
318
+ mergeImages,
319
+ partitionAttachments,
320
+ normalizeAttachmentCandidates,
321
+ ContextCandidate,
322
+ ContextCandidates,
323
+ imageAttachmentsFromFiles,
324
+ ContextTray,
325
+ ImageTray,
326
+ ImageViewer,
327
+ MessageImages
328
+ };