@volter-ai-dev/supercode-ui 0.1.69 → 0.1.71

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 (51) hide show
  1. package/README.md +37 -1
  2. package/activity.mjs +5 -1235
  3. package/chunks/chunk-ATCOWFRV.mjs +105 -0
  4. package/chunks/chunk-LOP6XOON.mjs +671 -0
  5. package/chunks/chunk-LQMYNJPU.mjs +120 -0
  6. package/chunks/chunk-LS6JBXNT.mjs +1237 -0
  7. package/chunks/chunk-O7Q2PELK.mjs +201 -0
  8. package/chunks/chunk-OW42DS6P.mjs +842 -0
  9. package/chunks/chunk-SJ3YTA6Z.mjs +316 -0
  10. package/chunks/chunk-SSYNT434.mjs +224 -0
  11. package/chunks/chunk-V3UM7H7W.mjs +328 -0
  12. package/chunks/chunk-XD2WJYSL.mjs +29 -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 +3 -2
  25. package/react/activity.mjs +5 -1235
  26. package/react/chunks/chunk-2RIHDJT6.mjs +842 -0
  27. package/react/chunks/chunk-7ES5FSLG.mjs +316 -0
  28. package/react/chunks/chunk-GV5KV5UY.mjs +105 -0
  29. package/react/chunks/chunk-GYQOTTZ5.mjs +224 -0
  30. package/react/chunks/chunk-LS6JBXNT.mjs +1237 -0
  31. package/react/chunks/chunk-OINC7LV7.mjs +62 -0
  32. package/react/chunks/chunk-TQHX2DQG.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-ZM5X5LOF.mjs +29 -0
  36. package/react/chunks/chunk-ZXD7NZXI.mjs +201 -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/sessions.mjs +9 -519
  49. package/settings.mjs +9 -425
  50. package/styles.css +12 -0
  51. package/subagents.mjs +8 -1546
@@ -0,0 +1,316 @@
1
+ import {
2
+ ContextCandidates,
3
+ ContextTray,
4
+ ImageTray,
5
+ MAX_CONTEXT_ITEMS,
6
+ MAX_IMAGE_ITEMS,
7
+ boundedSet,
8
+ imageAttachmentsFromFiles,
9
+ mergeContext,
10
+ mergeImages,
11
+ normalizeContext,
12
+ normalizeImages,
13
+ partitionAttachments,
14
+ uiMemory
15
+ } from "./chunk-V3UM7H7W.mjs";
16
+ import {
17
+ DEFAULT_LABELS,
18
+ canContinueHere,
19
+ harnessDisplayName,
20
+ isSendKey
21
+ } from "./chunk-LS6JBXNT.mjs";
22
+ import {
23
+ UiIcon
24
+ } from "./chunk-ZLYHUYE2.mjs";
25
+
26
+ // src/composer.jsx
27
+ import { useEffect, useRef, useState } from "preact/hooks";
28
+
29
+ // src/intent.js
30
+ var CONFIRMABLE_ACTIONS = /* @__PURE__ */ new Set(["resume", "join", "branch", "reduce", "export", "take_over"]);
31
+ async function dispatchConfirmedIntent(adapter, intent) {
32
+ if (CONFIRMABLE_ACTIONS.has(intent.action) && adapter.confirmIntent) {
33
+ const confirmed = await adapter.confirmIntent(intent);
34
+ if (!confirmed) return;
35
+ }
36
+ return adapter.onIntent(intent);
37
+ }
38
+
39
+ // src/textarea.js
40
+ import { useLayoutEffect } from "preact/hooks";
41
+ function useAutosizeTextarea(ref, value) {
42
+ useLayoutEffect(() => {
43
+ const element = ref.current;
44
+ if (!element) return;
45
+ element.style.height = "auto";
46
+ const maxHeight = Number.parseFloat(getComputedStyle(element).maxHeight) || 150;
47
+ const height = Math.min(element.scrollHeight, maxHeight);
48
+ element.style.height = `${height}px`;
49
+ element.style.overflowY = element.scrollHeight > maxHeight ? "auto" : "hidden";
50
+ }, [ref, value]);
51
+ }
52
+
53
+ // src/composer.jsx
54
+ import { jsx, jsxs } from "preact/jsx-runtime";
55
+ var composerMemory = uiMemory();
56
+ function ExecutionModeSelect({ modes = [], value, onChange, disabled = false, label = "Run in" }) {
57
+ if (modes.length < 2) return null;
58
+ return /* @__PURE__ */ jsxs("label", { className: "scui-execution-mode", title: disabled ? void 0 : `${label}: ${value}`, children: [
59
+ /* @__PURE__ */ jsx("span", { children: label }),
60
+ /* @__PURE__ */ jsx("select", { "aria-label": label, value, disabled, onChange: (event) => onChange?.(event.currentTarget.value), children: modes.map((mode) => /* @__PURE__ */ jsx("option", { value: mode, children: mode === "terminal" ? "Terminal" : "Headless" }, mode)) })
61
+ ] });
62
+ }
63
+ function ContinuationBar({ state, adapter, labels = DEFAULT_LABELS }) {
64
+ const continuationModes = state.continuationModes?.length ? state.continuationModes : ["headless"];
65
+ const [selection, setSelection] = useState({ key: null, mode: null });
66
+ const requestedMode = selection.key === state.attached?.key ? selection.mode : null;
67
+ const executionMode = continuationModes.includes(requestedMode) ? requestedMode : continuationModes.includes("terminal") ? "terminal" : continuationModes[0];
68
+ if (state.mode !== "mirror" || state.canSend) return null;
69
+ const attached = state.attached;
70
+ const row = attached?.key ? state.sessions.find((item) => item.key === attached.key) : null;
71
+ const activeElsewhere = row?.runtimeStatus === "running" || row?.runtimeStatus === "busy" || row?.runtimeStatus === "idle";
72
+ const available = {
73
+ resume: canContinueHere(state),
74
+ join: state.canAttach,
75
+ branch: state.canBranch
76
+ };
77
+ const continuation = state.supportsAttach ? "join" : state.supportsResume ? "resume" : state.supportsBranch ? "branch" : available.join ? "join" : available.resume ? "resume" : available.branch ? "branch" : null;
78
+ if (!continuation) return /* @__PURE__ */ jsx("div", { className: "scui-continuation", children: /* @__PURE__ */ jsxs("span", { children: [
79
+ /* @__PURE__ */ jsx("strong", { children: activeElsewhere ? "Active elsewhere" : "Read-only" }),
80
+ /* @__PURE__ */ jsx("small", { children: "This session cannot be continued by an available harness." })
81
+ ] }) });
82
+ const enabled = available[continuation];
83
+ return /* @__PURE__ */ jsxs("div", { className: "scui-continuation", children: [
84
+ /* @__PURE__ */ jsxs("span", { children: [
85
+ /* @__PURE__ */ jsx("strong", { children: activeElsewhere ? "Active elsewhere" : "Read-only" }),
86
+ /* @__PURE__ */ jsx("small", { children: continuation === "join" ? "Join the proven live runtime without taking it over." : continuation === "resume" ? `Resume this ${harnessDisplayName(state.harness)} session here.` : "Start an independent continuation." })
87
+ ] }),
88
+ /* @__PURE__ */ jsxs("span", { className: "scui-continuation-actions", children: [
89
+ continuation === "resume" ? /* @__PURE__ */ jsx(ExecutionModeSelect, { modes: continuationModes, value: executionMode, disabled: Boolean(state.operation) || !enabled, onChange: (mode) => setSelection({ key: state.attached?.key ?? null, mode }) }) : null,
90
+ /* @__PURE__ */ jsx("button", { type: "button", disabled: Boolean(state.operation) || !enabled, onClick: () => dispatchConfirmedIntent(adapter, continuation === "join" ? { action: "join" } : continuation === "resume" ? { action: "resume", mode: executionMode } : { action: "branch" }), children: continuation === "join" ? labels.joinLive : continuation === "resume" ? labels.continueHere : labels.forkHere })
91
+ ] })
92
+ ] });
93
+ }
94
+ function Composer({ state, adapter, labels = DEFAULT_LABELS, memoryKey = state.attached?.key || `${state.harness}:${state.workspace}`, pendingStatus = null, restoreDraft = null, command = null, contextCandidates = [], components = {}, onPending, onDraftRestored }) {
95
+ const remembered = composerMemory.get(memoryKey) ?? { draft: state.savedDraft, context: [], images: [], queue: [] };
96
+ const [draft, setDraft] = useState(remembered.draft);
97
+ const [context, setContext] = useState(remembered.context ?? []);
98
+ const [images, setImages] = useState(remembered.images ?? []);
99
+ const [queue, setQueue] = useState((remembered.queue ?? []).map((item) => ({ ...item, context: item.context ?? [], images: item.images ?? [] })));
100
+ const [dispatching, setDispatching] = useState(false);
101
+ const [steering, setSteering] = useState(false);
102
+ const [picking, setPicking] = useState(false);
103
+ const [dragging, setDragging] = useState(false);
104
+ const [pickerError, setPickerError] = useState(null);
105
+ const textarea = useRef(null);
106
+ const lastCommand = useRef(null);
107
+ useAutosizeTextarea(textarea, draft);
108
+ const remember = (nextDraft, nextContext, nextImages, nextQueue) => boundedSet(composerMemory, memoryKey, { draft: nextDraft, context: nextContext, images: nextImages, queue: nextQueue });
109
+ useEffect(() => {
110
+ remember(draft, context, images, queue);
111
+ }, [draft, context, images, memoryKey, queue]);
112
+ const updateQueue = (update) => setQueue((items) => {
113
+ const next = update(items);
114
+ remember(draft, context, images, next);
115
+ return next;
116
+ });
117
+ const queueBlocked = state.busy || pendingStatus !== null || dispatching || steering;
118
+ const queuesNewMessage = state.busy || pendingStatus === "sending" || pendingStatus === "failed" || dispatching || steering;
119
+ const steerAvailable = state.busy && state.canSteer && !steering && pendingStatus === null;
120
+ const canSteerDraft = steerAvailable && Boolean(draft.trim()) && context.length === 0 && images.length === 0;
121
+ useEffect(() => {
122
+ if (!queueBlocked && state.canSend && queue.length) {
123
+ const [next, ...rest] = queue;
124
+ setDispatching(true);
125
+ setQueue(rest);
126
+ remember(draft, context, images, rest);
127
+ onPending?.(next.text, next.context, next.images);
128
+ adapter.onIntent({ action: "send", text: next.text, ...next.context.length ? { context: next.context } : {}, ...next.images.length ? { images: next.images } : {} });
129
+ }
130
+ }, [adapter, draft, memoryKey, onPending, queue, queueBlocked, state.canSend]);
131
+ useEffect(() => {
132
+ if (pendingStatus !== null || state.busy) setDispatching(false);
133
+ }, [pendingStatus, state.busy]);
134
+ useEffect(() => {
135
+ textarea.current?.focus({ preventScroll: true });
136
+ }, [memoryKey]);
137
+ useEffect(() => {
138
+ if (!restoreDraft) return;
139
+ setDraft(restoreDraft.text);
140
+ const restoredContext = normalizeContext(restoreDraft.context);
141
+ const restoredImages = normalizeImages(restoreDraft.images);
142
+ setContext(restoredContext);
143
+ setImages(restoredImages);
144
+ remember(restoreDraft.text, restoredContext, restoredImages, queue);
145
+ textarea.current?.focus({ preventScroll: true });
146
+ onDraftRestored?.(restoreDraft.id);
147
+ }, [onDraftRestored, restoreDraft?.id]);
148
+ useEffect(() => {
149
+ if (!command || command.id === lastCommand.current) return;
150
+ lastCommand.current = command.id;
151
+ if (command.action === "attach") {
152
+ try {
153
+ const attachments = partitionAttachments(command.attachments);
154
+ if (context.length + attachments.context.length > MAX_CONTEXT_ITEMS) throw new Error(`Attach at most ${MAX_CONTEXT_ITEMS} text items.`);
155
+ if (images.length + attachments.images.length > MAX_IMAGE_ITEMS) throw new Error(`Attach at most ${MAX_IMAGE_ITEMS} images.`);
156
+ const nextContext = mergeContext(context, attachments.context);
157
+ const nextImages = mergeImages(images, attachments.images);
158
+ setContext(nextContext);
159
+ setImages(nextImages);
160
+ setPickerError(null);
161
+ remember(draft, nextContext, nextImages, queue);
162
+ } catch (error) {
163
+ setPickerError(error instanceof Error ? error.message : "Could not attach context.");
164
+ }
165
+ }
166
+ textarea.current?.focus({ preventScroll: true });
167
+ }, [command?.id]);
168
+ useEffect(() => {
169
+ const timer = setTimeout(() => adapter.onIntent({ action: "draft", text: draft }), 250);
170
+ return () => clearTimeout(timer);
171
+ }, [adapter, draft]);
172
+ const pickContext = () => {
173
+ if (!adapter.pickContext || state.mode !== "control" || picking || context.length >= MAX_CONTEXT_ITEMS && images.length >= MAX_IMAGE_ITEMS) return;
174
+ setPicking(true);
175
+ setPickerError(null);
176
+ Promise.resolve().then(() => adapter.pickContext()).then((picked) => {
177
+ const attachments = partitionAttachments(picked);
178
+ if (context.length + attachments.context.length > MAX_CONTEXT_ITEMS) throw new Error(`Attach at most ${MAX_CONTEXT_ITEMS} text items.`);
179
+ if (images.length + attachments.images.length > MAX_IMAGE_ITEMS) throw new Error(`Attach at most ${MAX_IMAGE_ITEMS} images.`);
180
+ setContext((current) => {
181
+ const next = mergeContext(current, attachments.context);
182
+ remember(draft, next, images, queue);
183
+ return next;
184
+ });
185
+ setImages((current) => {
186
+ const next = mergeImages(current, attachments.images);
187
+ remember(draft, context, next, queue);
188
+ return next;
189
+ });
190
+ }).catch((error) => setPickerError(error instanceof Error ? error.message : "Could not attach context.")).finally(() => setPicking(false));
191
+ };
192
+ const attachCandidate = (picked) => {
193
+ const attachments = partitionAttachments(picked);
194
+ const nextContext = mergeContext(context, attachments.context);
195
+ const nextImages = mergeImages(images, attachments.images);
196
+ setContext(nextContext);
197
+ setImages(nextImages);
198
+ remember(draft, nextContext, nextImages, queue);
199
+ };
200
+ const addImageFiles = (value, source) => {
201
+ const allFiles = Array.from(value ?? []);
202
+ if (!allFiles.length) return false;
203
+ const files = allFiles.filter((file) => file.type.startsWith("image/"));
204
+ if (files.length !== allFiles.length) {
205
+ setPickerError("Drop or paste PNG, JPEG, GIF, or WebP images.");
206
+ return true;
207
+ }
208
+ if (images.length + files.length > MAX_IMAGE_ITEMS) {
209
+ setPickerError(`Attach at most ${MAX_IMAGE_ITEMS} images.`);
210
+ return true;
211
+ }
212
+ setPicking(true);
213
+ setPickerError(null);
214
+ imageAttachmentsFromFiles(files).then((picked) => setImages((current) => {
215
+ const next = mergeImages(current, picked);
216
+ remember(draft, context, next, queue);
217
+ return next;
218
+ }), (error) => setPickerError(error instanceof Error ? error.message : `Could not ${source} image.`)).finally(() => setPicking(false));
219
+ return true;
220
+ };
221
+ const pasteImages = (event) => {
222
+ if (addImageFiles(event.clipboardData?.files, "paste")) event.preventDefault();
223
+ };
224
+ const dropImages = (event) => {
225
+ setDragging(false);
226
+ if (addImageFiles(event.dataTransfer?.files, "drop")) event.preventDefault();
227
+ };
228
+ const send = (forceQueue = false) => {
229
+ const text = draft.trim();
230
+ if (!text && !images.length) return;
231
+ if (canSteerDraft && !forceQueue) {
232
+ setSteering(true);
233
+ Promise.resolve(adapter.onIntent({ action: "steer", text })).then(() => {
234
+ setDraft("");
235
+ remember("", context, images, queue);
236
+ }, (error) => setPickerError(error instanceof Error ? error.message : "Could not steer the active turn.")).finally(() => setSteering(false));
237
+ return;
238
+ }
239
+ const message = { text, context, images };
240
+ if (queuesNewMessage) updateQueue((items) => [...items, message]);
241
+ else if (state.canSend) {
242
+ if (onPending) setDispatching(true);
243
+ onPending?.(text, context, images);
244
+ adapter.onIntent({ action: "send", text, ...context.length ? { context } : {}, ...images.length ? { images } : {} });
245
+ } else return;
246
+ setDraft("");
247
+ setContext([]);
248
+ setImages([]);
249
+ remember("", [], [], queuesNewMessage ? [...queue, message] : queue);
250
+ };
251
+ return /* @__PURE__ */ jsxs("div", { className: "scui-compose", children: [
252
+ queue.length ? /* @__PURE__ */ jsxs("div", { className: "scui-queue", children: [
253
+ /* @__PURE__ */ jsxs("strong", { children: [
254
+ queue.length,
255
+ " queued"
256
+ ] }),
257
+ queue.map((item, index) => /* @__PURE__ */ jsxs("span", { children: [
258
+ /* @__PURE__ */ jsxs("span", { children: [
259
+ item.text || "Image attachment",
260
+ item.context.length + item.images.length ? /* @__PURE__ */ jsxs("small", { children: [
261
+ item.context.length + item.images.length,
262
+ " attached"
263
+ ] }) : null
264
+ ] }),
265
+ /* @__PURE__ */ jsx("button", { type: "button", "aria-label": `Remove queued message ${index + 1}`, onClick: () => updateQueue((items) => items.filter((_, itemIndex) => itemIndex !== index)), children: /* @__PURE__ */ jsx(UiIcon, { name: "close", size: 13 }) })
266
+ ] }, `${index}:${item.text}`))
267
+ ] }) : null,
268
+ /* @__PURE__ */ jsx(ContextCandidates, { items: contextCandidates, context, images, state, adapter, onAttach: attachCandidate, component: components.ContextCandidate }),
269
+ /* @__PURE__ */ jsx(ImageTray, { items: images, onRemove: (index) => setImages((items) => {
270
+ const next = items.filter((_, itemIndex) => itemIndex !== index);
271
+ remember(draft, context, next, queue);
272
+ return next;
273
+ }) }),
274
+ /* @__PURE__ */ jsx(ContextTray, { items: context, onRemove: (index) => setContext((items) => {
275
+ const next = items.filter((_, itemIndex) => itemIndex !== index);
276
+ remember(draft, next, images, queue);
277
+ return next;
278
+ }) }),
279
+ pickerError ? /* @__PURE__ */ jsx("small", { className: "scui-context-error", role: "alert", children: pickerError }) : null,
280
+ /* @__PURE__ */ jsxs("div", { className: `scui-envelope${dragging ? " scui-drop-target" : ""}`, onDragEnter: (event) => {
281
+ if (Array.from(event.dataTransfer?.types ?? []).includes("Files")) {
282
+ event.preventDefault();
283
+ setDragging(true);
284
+ }
285
+ }, onDragOver: (event) => {
286
+ if (Array.from(event.dataTransfer?.types ?? []).includes("Files")) event.preventDefault();
287
+ }, onDragLeave: (event) => {
288
+ if (!event.currentTarget.contains(event.relatedTarget)) setDragging(false);
289
+ }, onDrop: dropImages, children: [
290
+ adapter.pickContext && state.mode === "control" ? /* @__PURE__ */ jsx("button", { className: "scui-attach", type: "button", "aria-label": labels.attachContext ?? DEFAULT_LABELS.attachContext, disabled: picking || context.length >= MAX_CONTEXT_ITEMS && images.length >= MAX_IMAGE_ITEMS, onClick: pickContext, children: picking ? /* @__PURE__ */ jsx("i", { className: "scui-control-spinner" }) : /* @__PURE__ */ jsx(UiIcon, { name: "attach", size: 17 }) }) : null,
291
+ /* @__PURE__ */ jsx("textarea", { ref: textarea, rows: 1, "aria-label": `Message ${harnessDisplayName(state.harness) || "agent"}`, placeholder: state.startup !== "ready" ? "Connecting\u2026" : pendingStatus === "failed" ? "Retry or edit the unsent message\u2026" : pendingStatus === "editing" ? "Edit and resend\u2026" : steerAvailable && context.length === 0 && images.length === 0 ? "Redirect the current turn\u2026" : queueBlocked ? "Queue a follow-up\u2026" : labels.askAgent, value: draft, disabled: state.mode !== "control" && !state.canSend, onPaste: pasteImages, onInput: (event) => {
292
+ const value = event.currentTarget.value;
293
+ setDraft(value);
294
+ remember(value, context, images, queue);
295
+ }, onKeyDown: (event) => {
296
+ if (isSendKey(event)) {
297
+ event.preventDefault();
298
+ send();
299
+ }
300
+ } }),
301
+ /* @__PURE__ */ jsxs("span", { children: [
302
+ state.busy ? /* @__PURE__ */ jsx("button", { className: "scui-stop", type: "button", "aria-label": "Stop agent", disabled: !state.canInterrupt, onClick: () => adapter.onIntent({ action: "interrupt" }), children: /* @__PURE__ */ jsx(UiIcon, { name: "stop", size: 15 }) }) : null,
303
+ canSteerDraft ? /* @__PURE__ */ jsx("button", { className: "scui-queue-send", type: "button", "aria-label": "Queue follow-up instead", onClick: () => send(true), children: /* @__PURE__ */ jsx(UiIcon, { name: "plus", size: 15 }) }) : null,
304
+ /* @__PURE__ */ jsx("button", { className: "scui-send", type: "button", "aria-label": canSteerDraft ? "Steer current turn" : queuesNewMessage ? "Queue message" : "Send message", disabled: steering || !draft.trim() && !images.length || !queuesNewMessage && !state.canSend, onClick: () => send(), children: steering ? /* @__PURE__ */ jsx("i", { className: "scui-control-spinner" }) : /* @__PURE__ */ jsx(UiIcon, { name: canSteerDraft ? "send" : queuesNewMessage ? "plus" : "send", size: 17 }) })
305
+ ] })
306
+ ] })
307
+ ] });
308
+ }
309
+
310
+ export {
311
+ dispatchConfirmedIntent,
312
+ useAutosizeTextarea,
313
+ ExecutionModeSelect,
314
+ ContinuationBar,
315
+ Composer
316
+ };
@@ -0,0 +1,224 @@
1
+ import {
2
+ HarnessLogo
3
+ } from "./chunk-ATCOWFRV.mjs";
4
+ import {
5
+ harnessDisplayName
6
+ } from "./chunk-LS6JBXNT.mjs";
7
+ import {
8
+ UiIcon
9
+ } from "./chunk-ZLYHUYE2.mjs";
10
+
11
+ // src/settings.jsx
12
+ import { useEffect, useId, useMemo, useRef, useState } from "preact/hooks";
13
+ import { jsx, jsxs } from "preact/jsx-runtime";
14
+ function HarnessAdvisory({ state, onReview }) {
15
+ const advisory = state.interopSettings?.advisories[0];
16
+ if (!advisory && !state.interopSettingsError) return null;
17
+ return /* @__PURE__ */ jsxs("div", { className: "scui-advisory", "data-severity": advisory?.severity ?? "error", role: advisory?.severity === "error" || !advisory ? "alert" : "status", children: [
18
+ /* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: "!" }),
19
+ /* @__PURE__ */ jsxs("span", { children: [
20
+ /* @__PURE__ */ jsx("strong", { children: advisory?.title ?? "Could not inspect harness settings" }),
21
+ /* @__PURE__ */ jsx("small", { children: advisory?.message ?? state.interopSettingsError })
22
+ ] }),
23
+ advisory && state.canConfigureSettings ? /* @__PURE__ */ jsx("button", { type: "button", onClick: () => onReview(advisory.recommendation.change), children: "Review" }) : null
24
+ ] });
25
+ }
26
+ function initialValues(report, recommendedChange) {
27
+ return Object.fromEntries(report.controls.map((control) => [
28
+ control.key,
29
+ recommendedChange?.key === control.key ? recommendedChange.value : control.configuredValue ?? control.effectiveValue ?? control.choices[0]?.value ?? null
30
+ ]));
31
+ }
32
+ function HarnessSettingsPanel({ state, adapter, onClose, recommendedChange = null }) {
33
+ const report = state.interopSettings;
34
+ const titleId = useId();
35
+ const panel = useRef(null);
36
+ const valuesKey = `${report?.revision ?? ""}:${recommendedChange?.key ?? ""}:${recommendedChange?.value ?? ""}`;
37
+ const defaults = useMemo(() => report ? initialValues(report, recommendedChange) : {}, [valuesKey]);
38
+ const [values, setValues] = useState(defaults);
39
+ useEffect(() => setValues(defaults), [defaults]);
40
+ useEffect(() => {
41
+ panel.current?.querySelector("select, button")?.focus({ preventScroll: true });
42
+ const dismiss = (event) => {
43
+ if (event.key === "Escape") {
44
+ event.preventDefault();
45
+ onClose();
46
+ }
47
+ };
48
+ document.addEventListener("keydown", dismiss);
49
+ return () => document.removeEventListener("keydown", dismiss);
50
+ }, [onClose]);
51
+ if (!report) return /* @__PURE__ */ jsx("section", { ref: panel, className: "scui-settings", role: "dialog", "aria-modal": "true", "aria-labelledby": titleId, children: /* @__PURE__ */ jsxs("header", { children: [
52
+ /* @__PURE__ */ jsx("button", { type: "button", "aria-label": "Close settings", onClick: onClose, children: /* @__PURE__ */ jsx(UiIcon, { name: "close", size: 18 }) }),
53
+ /* @__PURE__ */ jsxs("span", { children: [
54
+ /* @__PURE__ */ jsx("strong", { id: titleId, children: "Harness settings" }),
55
+ /* @__PURE__ */ jsx("small", { children: state.interopSettingsError ?? "No interoperability controls are available." })
56
+ ] })
57
+ ] }) });
58
+ const changed = report.controls.flatMap((control) => values[control.key] !== (control.configuredValue ?? control.effectiveValue ?? control.choices[0]?.value ?? null) ? [{ key: control.key, value: values[control.key] ?? null }] : []);
59
+ const submit = (event) => {
60
+ event.preventDefault();
61
+ if (!changed.length || !state.canConfigureSettings) return;
62
+ adapter.onIntent({ action: "configureHarness", harness: report.harness, changes: changed, expectedRevision: report.revision });
63
+ };
64
+ return /* @__PURE__ */ jsxs("section", { ref: panel, className: "scui-settings", role: "dialog", "aria-modal": "true", "aria-labelledby": titleId, children: [
65
+ /* @__PURE__ */ jsxs("header", { children: [
66
+ /* @__PURE__ */ jsx("button", { type: "button", "aria-label": "Close settings", onClick: onClose, children: /* @__PURE__ */ jsx(UiIcon, { name: "close", size: 18 }) }),
67
+ /* @__PURE__ */ jsxs("span", { children: [
68
+ /* @__PURE__ */ jsxs("strong", { id: titleId, children: [
69
+ harnessDisplayName(report.harness),
70
+ " interoperability"
71
+ ] }),
72
+ /* @__PURE__ */ jsx("small", { children: "Native harness settings used by Supercode" })
73
+ ] })
74
+ ] }),
75
+ /* @__PURE__ */ jsxs("form", { onSubmit: submit, children: [
76
+ report.controls.map((control) => {
77
+ const choice = control.choices.find((item) => item.value === values[control.key]);
78
+ const recommendation = report.advisories.map((advisory) => advisory.recommendation).find((item) => item.change.key === control.key && item.change.value === values[control.key]);
79
+ const consequence = choice?.risk ?? recommendation?.consequence;
80
+ return /* @__PURE__ */ jsxs("fieldset", { disabled: !control.writable || Boolean(state.operation), children: [
81
+ /* @__PURE__ */ jsxs("label", { htmlFor: `scui-setting-${control.key}`, children: [
82
+ /* @__PURE__ */ jsx("strong", { children: control.label }),
83
+ /* @__PURE__ */ jsx("small", { children: control.description })
84
+ ] }),
85
+ /* @__PURE__ */ jsxs("select", { id: `scui-setting-${control.key}`, value: values[control.key] ?? "@default", onChange: (event) => setValues((current) => ({ ...current, [control.key]: event.currentTarget.value === "@default" ? null : event.currentTarget.value })), children: [
86
+ control.resettable ? /* @__PURE__ */ jsx("option", { value: "@default", children: "Use harness default" }) : null,
87
+ control.choices.map((item) => /* @__PURE__ */ jsx("option", { value: item.value, children: item.label }, item.value))
88
+ ] }),
89
+ choice ? /* @__PURE__ */ jsx("p", { children: choice.description }) : null,
90
+ consequence ? /* @__PURE__ */ jsxs("p", { className: "scui-setting-risk", children: [
91
+ /* @__PURE__ */ jsx("strong", { children: "Security consequence" }),
92
+ consequence
93
+ ] }) : null,
94
+ /* @__PURE__ */ jsxs("small", { className: "scui-setting-source", children: [
95
+ control.effectiveNote,
96
+ control.sourcePath ? ` Source: ${control.sourcePath}` : ""
97
+ ] })
98
+ ] }, control.key);
99
+ }),
100
+ /* @__PURE__ */ jsxs("footer", { children: [
101
+ /* @__PURE__ */ jsx("button", { type: "button", onClick: onClose, children: "Cancel" }),
102
+ /* @__PURE__ */ jsx("button", { type: "submit", disabled: !changed.length || !state.canConfigureSettings || Boolean(state.operation), children: state.operation === "configureHarness" ? "Applying\u2026" : "Apply changes" })
103
+ ] })
104
+ ] })
105
+ ] });
106
+ }
107
+ var ACTIVE_AUTHENTICATION_PHASES = /* @__PURE__ */ new Set(["checking", "launching", "awaiting_user", "verifying"]);
108
+ function HarnessAuthenticationButton({ item, state, adapter }) {
109
+ if (!item.installed || !["claude-code", "codex"].includes(item.id) || ["ready", "configured"].includes(item.auth)) return null;
110
+ const flow = state?.harnessAuthentication?.harness === item.id ? state.harnessAuthentication : null;
111
+ const busy = flow && ACTIVE_AUTHENTICATION_PHASES.has(flow.phase);
112
+ const completed = flow?.phase === "authenticated";
113
+ const label = flow?.phase === "checking" ? "Checking\u2026" : flow?.phase === "launching" ? "Opening\u2026" : flow?.phase === "awaiting_user" ? "Finish sign-in" : flow?.phase === "verifying" ? "Verifying\u2026" : flow?.phase === "authenticated" ? "Signed in" : flow?.phase === "failed" ? "Try again" : "Sign in";
114
+ return /* @__PURE__ */ jsx("button", { type: "button", disabled: Boolean(busy || completed), "aria-busy": Boolean(busy), onClick: () => adapter?.onIntent({ action: "authenticateHarness", harness: item.id }), children: label });
115
+ }
116
+ function canAuthenticateHarness(item) {
117
+ return item.installed && ["claude-code", "codex"].includes(item.id) && !["ready", "configured"].includes(item.auth);
118
+ }
119
+ function harnessAuthenticationReason(item, state) {
120
+ const flow = state?.harnessAuthentication?.harness === item.id ? state.harnessAuthentication : null;
121
+ return flow?.error?.message || flow?.plan?.instructions || item.reason || (item.auth === "required" ? "Authentication required" : "Unavailable");
122
+ }
123
+ function HarnessReadiness({ harnesses, state, adapter }) {
124
+ const blocked = harnesses.filter((item) => !item.startable);
125
+ if (!blocked.length || harnesses.some((item) => item.startable)) return null;
126
+ return /* @__PURE__ */ jsxs("details", { className: "scui-readiness", open: true, children: [
127
+ /* @__PURE__ */ jsx("summary", { children: "No coding harness is ready" }),
128
+ /* @__PURE__ */ jsx("div", { children: blocked.map((item) => /* @__PURE__ */ jsxs("section", { children: [
129
+ /* @__PURE__ */ jsx(HarnessLogo, { id: item.id, size: 25 }),
130
+ /* @__PURE__ */ jsxs("span", { children: [
131
+ /* @__PURE__ */ jsx("strong", { children: item.label }),
132
+ /* @__PURE__ */ jsx("small", { children: harnessAuthenticationReason(item, state) }),
133
+ item.protocol ? /* @__PURE__ */ jsxs("em", { children: [
134
+ item.protocol,
135
+ " \xB7 ",
136
+ item.runtime
137
+ ] }) : null
138
+ ] }),
139
+ /* @__PURE__ */ jsx(HarnessAuthenticationButton, { item, state, adapter }),
140
+ !canAuthenticateHarness(item) && item.repair ? /* @__PURE__ */ jsx("button", { type: "button", onClick: () => adapter.copyText?.(item.repair), children: "Copy fix" }) : null
141
+ ] }, item.id)) })
142
+ ] });
143
+ }
144
+ function HarnessPicker({ harnesses, state, value, disabled = false, adapter, onChange, logo: Logo = HarnessLogo }) {
145
+ const [open, setOpen] = useState(false);
146
+ const menuId = useId();
147
+ const root = useRef(null);
148
+ const trigger = useRef(null);
149
+ const panel = useRef(null);
150
+ const available = harnesses.filter((item) => item.startable);
151
+ const unavailable = harnesses.filter((item) => !item.startable);
152
+ const selected = available.find((item) => item.id === value) ?? available[0] ?? null;
153
+ const close = () => {
154
+ setOpen(false);
155
+ trigger.current?.focus({ preventScroll: true });
156
+ };
157
+ useEffect(() => {
158
+ if (!open) return;
159
+ panel.current?.querySelector('[aria-selected="true"], [role="option"]')?.focus({ preventScroll: true });
160
+ const dismiss = (event) => {
161
+ if (event.type === "keydown" && event.key === "Escape") {
162
+ event.preventDefault();
163
+ close();
164
+ } else if (event.type === "pointerdown" && !root.current?.contains(event.target)) {
165
+ setOpen(false);
166
+ }
167
+ };
168
+ document.addEventListener("keydown", dismiss);
169
+ document.addEventListener("pointerdown", dismiss, true);
170
+ return () => {
171
+ document.removeEventListener("keydown", dismiss);
172
+ document.removeEventListener("pointerdown", dismiss, true);
173
+ };
174
+ }, [open]);
175
+ const navigate = (event) => {
176
+ if (!["ArrowDown", "ArrowUp", "Home", "End"].includes(event.key)) return;
177
+ const items = [...panel.current.querySelectorAll('[role="option"]')];
178
+ if (!items.length) return;
179
+ event.preventDefault();
180
+ const current = items.indexOf(document.activeElement);
181
+ const index = event.key === "Home" ? 0 : event.key === "End" ? items.length - 1 : event.key === "ArrowDown" ? (current + 1) % items.length : (current <= 0 ? items.length : current) - 1;
182
+ items[index].focus({ preventScroll: true });
183
+ };
184
+ const select = (id) => {
185
+ onChange(id);
186
+ close();
187
+ };
188
+ return /* @__PURE__ */ jsxs("div", { className: "scui-harness-menu", ref: root, onBlur: (event) => {
189
+ if (event.relatedTarget && !event.currentTarget.contains(event.relatedTarget)) setOpen(false);
190
+ }, children: [
191
+ /* @__PURE__ */ jsxs("button", { ref: trigger, className: "scui-harness-trigger", type: "button", disabled: disabled || !selected, "aria-label": selected ? `Coding harness: ${selected.label}` : "No coding harness available", "aria-haspopup": "dialog", "aria-expanded": open, "aria-controls": open ? menuId : void 0, onClick: () => setOpen((current) => !current), children: [
192
+ selected ? /* @__PURE__ */ jsx(Logo, { id: selected.id, size: 20 }) : null,
193
+ /* @__PURE__ */ jsx("strong", { children: selected?.label ?? "No harness available" }),
194
+ /* @__PURE__ */ jsx(UiIcon, { name: "chevron", size: 13 })
195
+ ] }),
196
+ open ? /* @__PURE__ */ jsxs("div", { ref: panel, id: menuId, className: "scui-harness-popover", role: "dialog", "aria-label": "Choose coding harness", onKeyDown: navigate, children: [
197
+ /* @__PURE__ */ jsx("strong", { className: "scui-harness-popover-title", children: "Choose coding harness" }),
198
+ /* @__PURE__ */ jsx("div", { className: "scui-harness-options", role: "listbox", "aria-label": "Available coding harnesses", children: available.map((item) => /* @__PURE__ */ jsxs("button", { type: "button", role: "option", "aria-selected": item.id === selected?.id, onClick: () => select(item.id), children: [
199
+ /* @__PURE__ */ jsx(Logo, { id: item.id, size: 24 }),
200
+ /* @__PURE__ */ jsx("strong", { children: item.label }),
201
+ /* @__PURE__ */ jsx("span", { children: item.id === selected?.id ? /* @__PURE__ */ jsx(UiIcon, { name: "check", size: 15 }) : null })
202
+ ] }, item.id)) }),
203
+ unavailable.length ? /* @__PURE__ */ jsxs("details", { className: "scui-harness-manage", children: [
204
+ /* @__PURE__ */ jsx("summary", { children: "Manage additional harnesses" }),
205
+ /* @__PURE__ */ jsx("div", { children: unavailable.map((item) => /* @__PURE__ */ jsxs("section", { children: [
206
+ /* @__PURE__ */ jsx(Logo, { id: item.id, size: 24 }),
207
+ /* @__PURE__ */ jsxs("span", { children: [
208
+ /* @__PURE__ */ jsx("strong", { children: item.label }),
209
+ /* @__PURE__ */ jsx("small", { children: harnessAuthenticationReason(item, state) })
210
+ ] }),
211
+ /* @__PURE__ */ jsx(HarnessAuthenticationButton, { item, state, adapter }),
212
+ !canAuthenticateHarness(item) && item.repair ? /* @__PURE__ */ jsx("button", { type: "button", onClick: () => adapter?.copyText?.(item.repair), children: "Copy fix" }) : null
213
+ ] }, item.id)) })
214
+ ] }) : null
215
+ ] }) : null
216
+ ] });
217
+ }
218
+
219
+ export {
220
+ HarnessAdvisory,
221
+ HarnessSettingsPanel,
222
+ HarnessReadiness,
223
+ HarnessPicker
224
+ };