@volter-ai-dev/supercode-ui 0.1.28 → 0.1.30
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 +10 -2
- package/components.mjs +330 -133
- package/composer.mjs +5 -2
- package/controller.mjs +10 -11
- package/conversation.mjs +4 -1
- package/core.d.ts +1 -0
- package/core.mjs +97 -4
- package/embed.mjs +330 -135
- package/index.d.ts +82 -3
- package/messenger.mjs +328 -133
- package/package.json +8 -2
- package/sessions.mjs +31 -6
- package/settings.d.ts +2 -0
- package/settings.mjs +216 -0
- package/styles.css +12 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@volter-ai-dev/supercode-ui",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.30",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Composable default UI kit for Supercode-powered coding-agent experiences",
|
|
6
6
|
"exports": {
|
|
@@ -44,6 +44,10 @@
|
|
|
44
44
|
"types": "./messenger.d.ts",
|
|
45
45
|
"import": "./messenger.mjs"
|
|
46
46
|
},
|
|
47
|
+
"./preact/settings": {
|
|
48
|
+
"types": "./settings.d.ts",
|
|
49
|
+
"import": "./settings.mjs"
|
|
50
|
+
},
|
|
47
51
|
"./embed": {
|
|
48
52
|
"types": "./embed.d.ts",
|
|
49
53
|
"import": "./embed.mjs"
|
|
@@ -70,6 +74,8 @@
|
|
|
70
74
|
"composer.d.ts",
|
|
71
75
|
"messenger.mjs",
|
|
72
76
|
"messenger.d.ts",
|
|
77
|
+
"settings.mjs",
|
|
78
|
+
"settings.d.ts",
|
|
73
79
|
"embed.mjs",
|
|
74
80
|
"embed.d.ts",
|
|
75
81
|
"index.mjs",
|
|
@@ -78,7 +84,7 @@
|
|
|
78
84
|
"README.md"
|
|
79
85
|
],
|
|
80
86
|
"scripts": {
|
|
81
|
-
"build": "esbuild src/components.jsx src/embed.jsx src/logo.jsx src/icon.jsx src/conversation.jsx src/sessions.jsx src/composer.jsx src/messenger.jsx --bundle --format=esm --platform=browser --jsx=automatic --jsx-import-source=preact --external:preact --external:preact/* --external:markdown-it --outdir=. --out-extension:.js=.mjs",
|
|
87
|
+
"build": "esbuild src/components.jsx src/embed.jsx src/logo.jsx src/icon.jsx src/conversation.jsx src/sessions.jsx src/composer.jsx src/messenger.jsx src/settings.jsx --bundle --format=esm --platform=browser --jsx=automatic --jsx-import-source=preact --external:preact --external:preact/* --external:markdown-it --outdir=. --out-extension:.js=.mjs",
|
|
82
88
|
"build:fixture": "esbuild test/browser-entry.jsx --bundle --format=esm --platform=browser --jsx=automatic --jsx-import-source=preact --outfile=test/browser-bundle.mjs",
|
|
83
89
|
"storybook": "npm run build && storybook dev -p 6006",
|
|
84
90
|
"build:storybook": "npm run build && storybook build",
|
package/sessions.mjs
CHANGED
|
@@ -40,6 +40,7 @@ var EMPTY_UI_STATE = Object.freeze({
|
|
|
40
40
|
canReduce: false,
|
|
41
41
|
canInterrupt: false,
|
|
42
42
|
canRespond: false,
|
|
43
|
+
canConfigureSettings: false,
|
|
43
44
|
messaging: null,
|
|
44
45
|
workspace: "",
|
|
45
46
|
taskPlan: Object.freeze({ source: "none", items: Object.freeze([]), residueCount: 0, observedAt: null }),
|
|
@@ -48,6 +49,8 @@ var EMPTY_UI_STATE = Object.freeze({
|
|
|
48
49
|
exportBackTarget: null,
|
|
49
50
|
exportReceipt: null,
|
|
50
51
|
reductionReceipt: null,
|
|
52
|
+
interopSettings: null,
|
|
53
|
+
interopSettingsError: null,
|
|
51
54
|
error: null,
|
|
52
55
|
recoverable: false,
|
|
53
56
|
harnesses: Object.freeze([]),
|
|
@@ -59,6 +62,15 @@ var EMPTY_UI_STATE = Object.freeze({
|
|
|
59
62
|
owned: null,
|
|
60
63
|
attachError: null
|
|
61
64
|
});
|
|
65
|
+
function relativeAge(updatedAt, now = Date.now()) {
|
|
66
|
+
if (typeof updatedAt !== "number" || !Number.isFinite(updatedAt) || updatedAt <= 0) return "";
|
|
67
|
+
const delta = Math.max(0, now - updatedAt);
|
|
68
|
+
if (delta < 6e4) return "now";
|
|
69
|
+
if (delta < 36e5) return `${Math.floor(delta / 6e4)}m ago`;
|
|
70
|
+
if (delta < 864e5) return `${Math.floor(delta / 36e5)}h ago`;
|
|
71
|
+
if (delta < 6048e5) return `${Math.floor(delta / 864e5)}d ago`;
|
|
72
|
+
return `${Math.floor(delta / 6048e5)}w ago`;
|
|
73
|
+
}
|
|
62
74
|
function harnessDisplayName(id) {
|
|
63
75
|
return HARNESS_NAMES[id] ?? id;
|
|
64
76
|
}
|
|
@@ -72,6 +84,7 @@ function sessionActivity(state, row) {
|
|
|
72
84
|
const attention = state.attention.find((item) => item.key === row.key)?.kind;
|
|
73
85
|
if (attention) return attention;
|
|
74
86
|
if (row.runtimeStatus === "busy") return "working";
|
|
87
|
+
if (row.runtimeStatus === "running") return "running";
|
|
75
88
|
if (row.live || row.runtimeStatus === "idle") return "recent";
|
|
76
89
|
return "idle";
|
|
77
90
|
}
|
|
@@ -290,28 +303,35 @@ function sessionPathParts(value) {
|
|
|
290
303
|
trailing: complete.slice(boundary + 1)
|
|
291
304
|
};
|
|
292
305
|
}
|
|
293
|
-
function SessionRow({ row, state, onOpen }) {
|
|
306
|
+
function SessionRow({ row, state, onOpen, now = Date.now() }) {
|
|
294
307
|
const activity = sessionActivity(state, row);
|
|
308
|
+
const working = activity === "working";
|
|
295
309
|
const attention = state.attention.find((item) => item.key === row.key);
|
|
296
310
|
const title = sessionDisplayName(row);
|
|
297
311
|
const path = sessionPathParts(row.cwd);
|
|
298
312
|
const preview = row.preview || attention?.preview || "";
|
|
299
313
|
const unreadCount = attention?.unreadCount ?? 0;
|
|
300
314
|
const unreadLabel = unreadCount > 99 ? "99+" : String(unreadCount);
|
|
301
|
-
|
|
315
|
+
const age = relativeAge(row.previewUpdatedAt ?? row.updatedAt, now) || row.age;
|
|
316
|
+
return /* @__PURE__ */ jsxs5("button", { class: "scui-session", "data-active": row.active, "data-activity": activity, "data-session-key": row.key, type: "button", "aria-label": `${title} \xB7 ${harnessDisplayName(row.harness)}${working ? " \xB7 Working" : ""}${path.complete ? ` \xB7 ${path.complete}` : ""}${preview ? ` \xB7 ${preview}` : ""}${unreadCount ? ` \xB7 ${unreadCount} unread` : ""}${age ? ` \xB7 ${age}` : ""}`, "aria-current": row.active ? "true" : void 0, onClick: () => onOpen(row), children: [
|
|
302
317
|
/* @__PURE__ */ jsx6(HarnessLogo, { id: row.harness, activity, size: 34 }),
|
|
303
318
|
/* @__PURE__ */ jsxs5("span", { class: "scui-session-copy", children: [
|
|
304
319
|
/* @__PURE__ */ jsxs5("span", { class: "scui-session-title", children: [
|
|
305
320
|
/* @__PURE__ */ jsx6("strong", { children: title }),
|
|
306
|
-
|
|
321
|
+
/* @__PURE__ */ jsx6("span", { class: "scui-session-meta", children: age ? /* @__PURE__ */ jsx6("time", { children: age }) : null })
|
|
307
322
|
] }),
|
|
308
323
|
path.complete ? /* @__PURE__ */ jsxs5("small", { class: "scui-session-path", title: row.cwd, children: [
|
|
309
324
|
/* @__PURE__ */ jsx6("span", { class: "scui-session-path-leading", children: path.leading }),
|
|
310
325
|
path.separator ? /* @__PURE__ */ jsx6("span", { class: "scui-session-path-separator", children: path.separator }) : null,
|
|
311
326
|
path.trailing ? /* @__PURE__ */ jsx6("span", { class: "scui-session-path-trailing", children: path.trailing }) : null
|
|
312
327
|
] }) : null,
|
|
313
|
-
preview || unreadCount ? /* @__PURE__ */ jsxs5("span", { class: "scui-session-preview", children: [
|
|
314
|
-
|
|
328
|
+
working || preview || unreadCount ? /* @__PURE__ */ jsxs5("span", { class: "scui-session-preview", children: [
|
|
329
|
+
working ? /* @__PURE__ */ jsxs5("span", { class: "scui-session-working", "aria-hidden": "true", children: [
|
|
330
|
+
/* @__PURE__ */ jsx6("i", {}),
|
|
331
|
+
/* @__PURE__ */ jsx6("i", {}),
|
|
332
|
+
/* @__PURE__ */ jsx6("i", {})
|
|
333
|
+
] }) : null,
|
|
334
|
+
preview || working ? /* @__PURE__ */ jsx6("small", { children: preview || "Working\u2026" }) : null,
|
|
315
335
|
unreadCount ? /* @__PURE__ */ jsx6("b", { "aria-label": `${unreadCount} unread messages`, children: unreadLabel }) : null
|
|
316
336
|
] }) : null,
|
|
317
337
|
state.attachError?.key === row.key ? /* @__PURE__ */ jsx6("em", { children: state.attachError.message }) : null
|
|
@@ -322,6 +342,7 @@ function SessionList({ state, adapter, onOpen, onNew, onClose, components = {},
|
|
|
322
342
|
const remembered = sessionListMemory.get(memoryKey) ?? { query: "", top: 0 };
|
|
323
343
|
const [query, setQuery] = useState3(remembered.query);
|
|
324
344
|
const [loadingMore, setLoadingMore] = useState3(false);
|
|
345
|
+
const [now, setNow] = useState3(() => Date.now());
|
|
325
346
|
const root = useRef4(null);
|
|
326
347
|
const rowScroller = useRef4(null);
|
|
327
348
|
const rows = filterSessions(state.sessions, query);
|
|
@@ -337,6 +358,10 @@ function SessionList({ state, adapter, onOpen, onNew, onClose, components = {},
|
|
|
337
358
|
useEffect5(() => {
|
|
338
359
|
if (loadingMore) setLoadingMore(false);
|
|
339
360
|
}, [state.error, state.history.hasMoreSessions, state.sessions.length]);
|
|
361
|
+
useEffect5(() => {
|
|
362
|
+
const timer = setInterval(() => setNow(Date.now()), 1e4);
|
|
363
|
+
return () => clearInterval(timer);
|
|
364
|
+
}, []);
|
|
340
365
|
const loadMore = () => {
|
|
341
366
|
if (loadingMore) return;
|
|
342
367
|
setLoadingMore(true);
|
|
@@ -370,7 +395,7 @@ function SessionList({ state, adapter, onOpen, onNew, onClose, components = {},
|
|
|
370
395
|
] }) : null,
|
|
371
396
|
/* @__PURE__ */ jsxs5("div", { class: "scui-session-rows", ref: rowScroller, onScroll: (event) => boundedSet(sessionListMemory, memoryKey, { query, top: event.currentTarget.scrollTop }), children: [
|
|
372
397
|
!rows.length && state.startup === "ready" ? /* @__PURE__ */ jsx6("div", { class: "scui-empty", children: query ? "No chats match your search." : state.error ?? "No coding chats found." }) : null,
|
|
373
|
-
rows.map((row) => /* @__PURE__ */ jsx6(Row, { value: row, row, state, adapter, onOpen }, row.key)),
|
|
398
|
+
rows.map((row) => /* @__PURE__ */ jsx6(Row, { value: row, row, state, adapter, onOpen, now }, row.key)),
|
|
374
399
|
state.history.hasMoreSessions ? /* @__PURE__ */ jsx6("button", { class: "scui-load", type: "button", disabled: loadingMore, onClick: loadMore, children: loadingMore ? "Loading older chats\u2026" : "Load older chats" }) : null
|
|
375
400
|
] })
|
|
376
401
|
] });
|
package/settings.d.ts
ADDED
package/settings.mjs
ADDED
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
// src/settings.jsx
|
|
2
|
+
import { useEffect, useId, useMemo, useRef, useState } from "preact/hooks";
|
|
3
|
+
|
|
4
|
+
// core.mjs
|
|
5
|
+
var HARNESS_NAMES = Object.freeze({
|
|
6
|
+
"claude-code": "Claude Code",
|
|
7
|
+
codex: "Codex",
|
|
8
|
+
gemini: "Gemini CLI",
|
|
9
|
+
goose: "Goose",
|
|
10
|
+
opencode: "OpenCode",
|
|
11
|
+
pi: "Pi",
|
|
12
|
+
grok: "Grok"
|
|
13
|
+
});
|
|
14
|
+
var DEFAULT_LABELS = Object.freeze({
|
|
15
|
+
chats: "Chats",
|
|
16
|
+
newChat: "New chat",
|
|
17
|
+
searchChats: "Search chats",
|
|
18
|
+
askAgent: "Ask your agent\u2026",
|
|
19
|
+
continueHere: "Continue here",
|
|
20
|
+
joinLive: "Join live",
|
|
21
|
+
forkHere: "Fork here"
|
|
22
|
+
});
|
|
23
|
+
var EMPTY_UI_STATE = Object.freeze({
|
|
24
|
+
pill: Object.freeze({ tone: "off", label: "connecting\u2026" }),
|
|
25
|
+
startup: "connecting",
|
|
26
|
+
transcript: Object.freeze([]),
|
|
27
|
+
busy: false,
|
|
28
|
+
operation: null,
|
|
29
|
+
needsInput: false,
|
|
30
|
+
harness: "",
|
|
31
|
+
mode: "none",
|
|
32
|
+
strategy: null,
|
|
33
|
+
canSend: false,
|
|
34
|
+
canResume: false,
|
|
35
|
+
canBranch: false,
|
|
36
|
+
canAttach: false,
|
|
37
|
+
canDetach: false,
|
|
38
|
+
canOpenTerminal: false,
|
|
39
|
+
canExport: false,
|
|
40
|
+
canReduce: false,
|
|
41
|
+
canInterrupt: false,
|
|
42
|
+
canRespond: false,
|
|
43
|
+
canConfigureSettings: false,
|
|
44
|
+
messaging: null,
|
|
45
|
+
workspace: "",
|
|
46
|
+
taskPlan: Object.freeze({ source: "none", items: Object.freeze([]), residueCount: 0, observedAt: null }),
|
|
47
|
+
semantics: Object.freeze({ fidelity: null, residue: Object.freeze([]), residueCount: 0, parseErrors: 0, rawRecords: 0, subagents: Object.freeze([]) }),
|
|
48
|
+
terminalHandoff: null,
|
|
49
|
+
exportBackTarget: null,
|
|
50
|
+
exportReceipt: null,
|
|
51
|
+
reductionReceipt: null,
|
|
52
|
+
interopSettings: null,
|
|
53
|
+
interopSettingsError: null,
|
|
54
|
+
error: null,
|
|
55
|
+
recoverable: false,
|
|
56
|
+
harnesses: Object.freeze([]),
|
|
57
|
+
history: Object.freeze({ sessionLimit: 0, hasMoreSessions: false, transcriptLimit: 120, hasEarlier: false }),
|
|
58
|
+
savedDraft: "",
|
|
59
|
+
attention: Object.freeze([]),
|
|
60
|
+
sessions: Object.freeze([]),
|
|
61
|
+
attached: null,
|
|
62
|
+
owned: null,
|
|
63
|
+
attachError: null
|
|
64
|
+
});
|
|
65
|
+
function harnessDisplayName(id) {
|
|
66
|
+
return HARNESS_NAMES[id] ?? id;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// src/icon.jsx
|
|
70
|
+
import { Fragment, jsx, jsxs } from "preact/jsx-runtime";
|
|
71
|
+
var ICONS = {
|
|
72
|
+
attach: () => /* @__PURE__ */ jsx("path", { d: "M6.25 9.75 10.6 5.4a2.1 2.1 0 0 1 2.97 2.97l-5.4 5.4a3.3 3.3 0 0 1-4.67-4.66l5.52-5.52" }),
|
|
73
|
+
back: () => /* @__PURE__ */ jsx("path", { d: "m11.5 4.5-4.5 4.5 4.5 4.5" }),
|
|
74
|
+
check: () => /* @__PURE__ */ jsx("path", { d: "m4 9 3.25 3.25L14 5.5" }),
|
|
75
|
+
chevron: () => /* @__PURE__ */ jsx("path", { d: "m7 4.5 4.5 4.5L7 13.5" }),
|
|
76
|
+
close: () => /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
77
|
+
/* @__PURE__ */ jsx("path", { d: "m4.75 4.75 8.5 8.5" }),
|
|
78
|
+
/* @__PURE__ */ jsx("path", { d: "m13.25 4.75-8.5 8.5" })
|
|
79
|
+
] }),
|
|
80
|
+
copy: () => /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
81
|
+
/* @__PURE__ */ jsx("rect", { x: "5", y: "5", width: "8", height: "8", rx: "1.5" }),
|
|
82
|
+
/* @__PURE__ */ jsx("path", { d: "M3 10.5V4.25C3 3.56 3.56 3 4.25 3h6.25" })
|
|
83
|
+
] }),
|
|
84
|
+
down: () => /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
85
|
+
/* @__PURE__ */ jsx("path", { d: "M9 3.5v10" }),
|
|
86
|
+
/* @__PURE__ */ jsx("path", { d: "m4.75 9.5 4.25 4 4.25-4" })
|
|
87
|
+
] }),
|
|
88
|
+
image: () => /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
89
|
+
/* @__PURE__ */ jsx("rect", { x: "3", y: "3.5", width: "12", height: "11", rx: "1.75" }),
|
|
90
|
+
/* @__PURE__ */ jsx("circle", { cx: "6.5", cy: "7", r: "1.25" }),
|
|
91
|
+
/* @__PURE__ */ jsx("path", { d: "m4.5 13 3.25-3 2.1 1.85 1.65-1.5L14 13" })
|
|
92
|
+
] }),
|
|
93
|
+
menu: () => /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
94
|
+
/* @__PURE__ */ jsx("circle", { cx: "4", cy: "9", r: "1.25", fill: "currentColor", stroke: "none" }),
|
|
95
|
+
/* @__PURE__ */ jsx("circle", { cx: "9", cy: "9", r: "1.25", fill: "currentColor", stroke: "none" }),
|
|
96
|
+
/* @__PURE__ */ jsx("circle", { cx: "14", cy: "9", r: "1.25", fill: "currentColor", stroke: "none" })
|
|
97
|
+
] }),
|
|
98
|
+
plus: () => /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
99
|
+
/* @__PURE__ */ jsx("path", { d: "M9 3.5v11" }),
|
|
100
|
+
/* @__PURE__ */ jsx("path", { d: "M3.5 9h11" })
|
|
101
|
+
] }),
|
|
102
|
+
search: () => /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
103
|
+
/* @__PURE__ */ jsx("circle", { cx: "7.75", cy: "7.75", r: "4.25" }),
|
|
104
|
+
/* @__PURE__ */ jsx("path", { d: "m11 11 3.5 3.5" })
|
|
105
|
+
] }),
|
|
106
|
+
send: () => /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
107
|
+
/* @__PURE__ */ jsx("path", { d: "M9 14.5v-11" }),
|
|
108
|
+
/* @__PURE__ */ jsx("path", { d: "m4.75 7.75 4.25-4.25 4.25 4.25" })
|
|
109
|
+
] }),
|
|
110
|
+
stop: () => /* @__PURE__ */ jsx("rect", { x: "4.5", y: "4.5", width: "9", height: "9", rx: "1.5", fill: "currentColor", stroke: "none" })
|
|
111
|
+
};
|
|
112
|
+
function UiIcon({ name, size = 16, class: className = "" }) {
|
|
113
|
+
const Glyph = ICONS[name];
|
|
114
|
+
if (!Glyph) return null;
|
|
115
|
+
return /* @__PURE__ */ jsx("svg", { class: `scui-icon ${className}`, style: { "--scui-icon-size": `${size}px` }, viewBox: "0 0 18 18", fill: "none", stroke: "currentColor", "stroke-width": "1.5", "stroke-linecap": "round", "stroke-linejoin": "round", "aria-hidden": "true", children: /* @__PURE__ */ jsx(Glyph, {}) });
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// src/settings.jsx
|
|
119
|
+
import { jsx as jsx2, jsxs as jsxs2 } from "preact/jsx-runtime";
|
|
120
|
+
function HarnessAdvisory({ state, onReview }) {
|
|
121
|
+
const advisory = state.interopSettings?.advisories[0];
|
|
122
|
+
if (!advisory && !state.interopSettingsError) return null;
|
|
123
|
+
return /* @__PURE__ */ jsxs2("aside", { class: "scui-advisory", "data-severity": advisory?.severity ?? "error", role: advisory?.severity === "error" ? "alert" : "status", children: [
|
|
124
|
+
/* @__PURE__ */ jsx2("span", { "aria-hidden": "true", children: "!" }),
|
|
125
|
+
/* @__PURE__ */ jsxs2("span", { children: [
|
|
126
|
+
/* @__PURE__ */ jsx2("strong", { children: advisory?.title ?? "Could not inspect harness settings" }),
|
|
127
|
+
/* @__PURE__ */ jsx2("small", { children: advisory?.message ?? state.interopSettingsError })
|
|
128
|
+
] }),
|
|
129
|
+
advisory && state.canConfigureSettings ? /* @__PURE__ */ jsx2("button", { type: "button", onClick: () => onReview(advisory.recommendation.change), children: "Review" }) : null
|
|
130
|
+
] });
|
|
131
|
+
}
|
|
132
|
+
function initialValues(report, recommendedChange) {
|
|
133
|
+
return Object.fromEntries(report.controls.map((control) => [
|
|
134
|
+
control.key,
|
|
135
|
+
recommendedChange?.key === control.key ? recommendedChange.value : control.configuredValue ?? control.effectiveValue ?? control.choices[0]?.value ?? null
|
|
136
|
+
]));
|
|
137
|
+
}
|
|
138
|
+
function HarnessSettingsPanel({ state, adapter, onClose, recommendedChange = null }) {
|
|
139
|
+
const report = state.interopSettings;
|
|
140
|
+
const titleId = useId();
|
|
141
|
+
const panel = useRef(null);
|
|
142
|
+
const valuesKey = `${report?.revision ?? ""}:${recommendedChange?.key ?? ""}:${recommendedChange?.value ?? ""}`;
|
|
143
|
+
const defaults = useMemo(() => report ? initialValues(report, recommendedChange) : {}, [valuesKey]);
|
|
144
|
+
const [values, setValues] = useState(defaults);
|
|
145
|
+
useEffect(() => setValues(defaults), [defaults]);
|
|
146
|
+
useEffect(() => {
|
|
147
|
+
panel.current?.querySelector("select, button")?.focus({ preventScroll: true });
|
|
148
|
+
const dismiss = (event) => {
|
|
149
|
+
if (event.key === "Escape") {
|
|
150
|
+
event.preventDefault();
|
|
151
|
+
onClose();
|
|
152
|
+
}
|
|
153
|
+
};
|
|
154
|
+
document.addEventListener("keydown", dismiss);
|
|
155
|
+
return () => document.removeEventListener("keydown", dismiss);
|
|
156
|
+
}, [onClose]);
|
|
157
|
+
if (!report) return /* @__PURE__ */ jsx2("section", { ref: panel, class: "scui-settings", role: "dialog", "aria-modal": "true", "aria-labelledby": titleId, children: /* @__PURE__ */ jsxs2("header", { children: [
|
|
158
|
+
/* @__PURE__ */ jsx2("button", { type: "button", "aria-label": "Close settings", onClick: onClose, children: /* @__PURE__ */ jsx2(UiIcon, { name: "close", size: 18 }) }),
|
|
159
|
+
/* @__PURE__ */ jsxs2("span", { children: [
|
|
160
|
+
/* @__PURE__ */ jsx2("strong", { id: titleId, children: "Harness settings" }),
|
|
161
|
+
/* @__PURE__ */ jsx2("small", { children: state.interopSettingsError ?? "No interoperability controls are available." })
|
|
162
|
+
] })
|
|
163
|
+
] }) });
|
|
164
|
+
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 }] : []);
|
|
165
|
+
const submit = (event) => {
|
|
166
|
+
event.preventDefault();
|
|
167
|
+
if (!changed.length || !state.canConfigureSettings) return;
|
|
168
|
+
adapter.onIntent({ action: "configureHarness", harness: report.harness, changes: changed, expectedRevision: report.revision });
|
|
169
|
+
};
|
|
170
|
+
return /* @__PURE__ */ jsxs2("section", { ref: panel, class: "scui-settings", role: "dialog", "aria-modal": "true", "aria-labelledby": titleId, children: [
|
|
171
|
+
/* @__PURE__ */ jsxs2("header", { children: [
|
|
172
|
+
/* @__PURE__ */ jsx2("button", { type: "button", "aria-label": "Close settings", onClick: onClose, children: /* @__PURE__ */ jsx2(UiIcon, { name: "close", size: 18 }) }),
|
|
173
|
+
/* @__PURE__ */ jsxs2("span", { children: [
|
|
174
|
+
/* @__PURE__ */ jsxs2("strong", { id: titleId, children: [
|
|
175
|
+
harnessDisplayName(report.harness),
|
|
176
|
+
" interoperability"
|
|
177
|
+
] }),
|
|
178
|
+
/* @__PURE__ */ jsx2("small", { children: "Native harness settings used by Supercode" })
|
|
179
|
+
] })
|
|
180
|
+
] }),
|
|
181
|
+
/* @__PURE__ */ jsxs2("form", { onSubmit: submit, children: [
|
|
182
|
+
report.controls.map((control) => {
|
|
183
|
+
const choice = control.choices.find((item) => item.value === values[control.key]);
|
|
184
|
+
const recommendation = report.advisories.map((advisory) => advisory.recommendation).find((item) => item.change.key === control.key && item.change.value === values[control.key]);
|
|
185
|
+
const consequence = choice?.risk ?? recommendation?.consequence;
|
|
186
|
+
return /* @__PURE__ */ jsxs2("fieldset", { disabled: !control.writable || Boolean(state.operation), children: [
|
|
187
|
+
/* @__PURE__ */ jsxs2("label", { for: `scui-setting-${control.key}`, children: [
|
|
188
|
+
/* @__PURE__ */ jsx2("strong", { children: control.label }),
|
|
189
|
+
/* @__PURE__ */ jsx2("small", { children: control.description })
|
|
190
|
+
] }),
|
|
191
|
+
/* @__PURE__ */ jsxs2("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: [
|
|
192
|
+
control.resettable ? /* @__PURE__ */ jsx2("option", { value: "@default", children: "Use harness default" }) : null,
|
|
193
|
+
control.choices.map((item) => /* @__PURE__ */ jsx2("option", { value: item.value, children: item.label }, item.value))
|
|
194
|
+
] }),
|
|
195
|
+
choice ? /* @__PURE__ */ jsx2("p", { children: choice.description }) : null,
|
|
196
|
+
consequence ? /* @__PURE__ */ jsxs2("p", { class: "scui-setting-risk", children: [
|
|
197
|
+
/* @__PURE__ */ jsx2("strong", { children: "Security consequence" }),
|
|
198
|
+
consequence
|
|
199
|
+
] }) : null,
|
|
200
|
+
/* @__PURE__ */ jsxs2("small", { class: "scui-setting-source", children: [
|
|
201
|
+
control.effectiveNote,
|
|
202
|
+
control.sourcePath ? ` Source: ${control.sourcePath}` : ""
|
|
203
|
+
] })
|
|
204
|
+
] }, control.key);
|
|
205
|
+
}),
|
|
206
|
+
/* @__PURE__ */ jsxs2("footer", { children: [
|
|
207
|
+
/* @__PURE__ */ jsx2("button", { type: "button", onClick: onClose, children: "Cancel" }),
|
|
208
|
+
/* @__PURE__ */ jsx2("button", { type: "submit", disabled: !changed.length || !state.canConfigureSettings || Boolean(state.operation), children: state.operation === "configureHarness" ? "Applying\u2026" : "Apply changes" })
|
|
209
|
+
] })
|
|
210
|
+
] })
|
|
211
|
+
] });
|
|
212
|
+
}
|
|
213
|
+
export {
|
|
214
|
+
HarnessAdvisory,
|
|
215
|
+
HarnessSettingsPanel
|
|
216
|
+
};
|
package/styles.css
CHANGED
|
@@ -73,7 +73,7 @@
|
|
|
73
73
|
.scui-logo[data-harness="pi"] { color:#111; background:#efefeb }
|
|
74
74
|
.scui-logo[data-harness="supercode"] { background:#111923 }
|
|
75
75
|
.scui-logo > i { position:absolute; right:-5%; bottom:-5%; width:32%; height:32%; border:2px solid var(--scui-bg); border-radius:50%; background:var(--scui-muted) }
|
|
76
|
-
.scui-logo[data-activity="working"] > i { background:var(--scui-
|
|
76
|
+
.scui-logo[data-activity="working"] > i,.scui-logo[data-activity="running"] > i { background:var(--scui-positive) }
|
|
77
77
|
.scui-logo[data-activity="needs-input"] > i { background:var(--scui-warning) }
|
|
78
78
|
.scui-logo[data-activity="failed"] > i { background:var(--scui-danger) }
|
|
79
79
|
.scui-logo[data-activity="finished"] > i,.scui-logo[data-activity="unseen"] > i { background:var(--scui-positive) }
|
|
@@ -97,7 +97,7 @@
|
|
|
97
97
|
.scui-session-copy { display:grid; flex:1; min-width:0; gap:1px }
|
|
98
98
|
.scui-session-copy > span { display:flex; align-items:baseline; gap:7px; min-width:0 }
|
|
99
99
|
.scui-session-copy strong,.scui-session-copy span > small { overflow:hidden; text-overflow:ellipsis; white-space:nowrap }
|
|
100
|
-
.scui-session-copy strong { flex:1; font-size:12.5px }.scui-session-
|
|
100
|
+
.scui-session-copy strong { flex:1; font-size:12.5px }.scui-session-meta { display:flex; flex:none; align-items:center }.scui-session-title time { color:var(--scui-muted); font-size:10px }.scui-session-working { display:flex; flex:none; width:14px; height:8px; align-self:center; align-items:center; justify-content:space-between; color:var(--scui-muted) }.scui-session-working > i { width:3px; height:3px; border-radius:50%; background:currentColor; animation:scui-session-dots 1.2s ease-in-out infinite }.scui-session-working > i:nth-child(2) { animation-delay:.15s }.scui-session-working > i:nth-child(3) { animation-delay:.3s }
|
|
101
101
|
.scui-session-path { display:flex; min-width:0; overflow:hidden; color:var(--scui-muted); font-size:9.5px; white-space:nowrap }
|
|
102
102
|
.scui-session-path-leading { flex:0 1 auto; overflow:hidden; text-overflow:ellipsis }.scui-session-path-separator { flex:none }.scui-session-path-trailing { flex:none; max-width:58%; overflow:hidden; direction:rtl; text-align:left; text-overflow:ellipsis; unicode-bidi:plaintext }
|
|
103
103
|
.scui-session-preview small { flex:1; min-width:0; color:var(--scui-muted); font-size:10.5px }.scui-session-preview b { display:grid; flex:none; min-width:16px; height:16px; padding:0 4px; place-items:center; border-radius:8px; background:var(--scui-accent); color:var(--scui-bg); font-size:9px; line-height:16px }
|
|
@@ -109,6 +109,15 @@
|
|
|
109
109
|
.scui-error { color:var(--scui-danger) }.scui-error span,.scui-receipt span { flex:1; min-width:0 }
|
|
110
110
|
.scui-error button,.scui-receipt button { border:0; background:transparent; color:var(--scui-accent); cursor:pointer }
|
|
111
111
|
.scui-receipt span { display:grid }.scui-receipt small { overflow:hidden; color:var(--scui-muted); text-overflow:ellipsis; white-space:nowrap }
|
|
112
|
+
.scui-advisory { display:flex; flex:none; align-items:center; gap:7px; padding:6px 8px; border-top:1px solid var(--scui-border); background:var(--scui-bg-raised); font-size:10px }
|
|
113
|
+
.scui-advisory > span:first-child { display:grid; width:17px; height:17px; flex:none; place-items:center; border:1px solid color-mix(in srgb,var(--scui-warning) 48%,var(--scui-border)); border-radius:50%; color:var(--scui-warning); font-size:10px; font-weight:700 }
|
|
114
|
+
.scui-advisory[data-severity="error"] > span:first-child { border-color:color-mix(in srgb,var(--scui-danger) 48%,var(--scui-border)); color:var(--scui-danger) }
|
|
115
|
+
.scui-advisory > span:nth-child(2) { display:grid; min-width:0; flex:1 }.scui-advisory strong,.scui-advisory small { overflow:hidden; text-overflow:ellipsis; white-space:nowrap }.scui-advisory small { color:var(--scui-muted) }
|
|
116
|
+
.scui-advisory button { flex:none; padding:4px 7px; border:1px solid var(--scui-border-strong); border-radius:6px; background:var(--scui-bg); cursor:pointer }
|
|
117
|
+
.scui-settings { position:absolute; z-index:30; inset:0; display:flex; min-height:0; flex-direction:column; background:var(--scui-bg); color:var(--scui-fg) }
|
|
118
|
+
.scui-settings > header { display:flex; min-height:49px; flex:none; align-items:center; gap:8px; padding:7px 10px; border-bottom:1px solid var(--scui-border); background:var(--scui-bg-raised) }.scui-settings > header button { display:grid; width:30px; height:30px; padding:0; place-items:center; border:0; border-radius:8px; background:transparent; cursor:pointer }.scui-settings > header button:hover { background:var(--scui-fill) }.scui-settings > header span { display:grid; min-width:0 }.scui-settings > header small { color:var(--scui-muted); font-size:10px }
|
|
119
|
+
.scui-settings form { display:flex; min-height:0; flex:1; flex-direction:column; overflow:auto }.scui-settings fieldset { display:grid; gap:7px; margin:0; padding:12px; border:0 }.scui-settings fieldset + fieldset { border-top:1px solid var(--scui-border) }.scui-settings fieldset > label { display:grid; gap:2px }.scui-settings fieldset > label small,.scui-settings fieldset > p,.scui-setting-source { color:var(--scui-muted); font-size:10.5px }.scui-settings select { width:100%; padding:7px 8px; border:1px solid var(--scui-border-strong); border-radius:7px; background:var(--scui-bg); color:var(--scui-fg) }.scui-settings fieldset > p { margin:0 }.scui-setting-risk { display:grid; gap:2px; padding:7px; border-left:2px solid var(--scui-warning); background:color-mix(in srgb,var(--scui-warning) 7%,transparent) }.scui-setting-risk strong { color:var(--scui-fg); font-size:10px }.scui-setting-source { overflow-wrap:anywhere }
|
|
120
|
+
.scui-settings form > footer { display:flex; gap:6px; justify-content:flex-end; margin-top:auto; padding:8px; border-top:1px solid var(--scui-border); background:var(--scui-bg-raised) }.scui-settings form > footer button { padding:6px 9px; border:1px solid var(--scui-border-strong); border-radius:7px; background:var(--scui-bg); cursor:pointer }.scui-settings form > footer button[type="submit"] { border-color:var(--scui-fg); background:var(--scui-fg); color:var(--scui-bg) }.scui-settings button:disabled { opacity:.5; cursor:default }
|
|
112
121
|
|
|
113
122
|
.scui-conversation-wrap { position:relative; display:flex; flex:1; min-height:0 }
|
|
114
123
|
.scui-conversation { flex:1; min-width:0; overflow:auto; overscroll-behavior:contain; padding:12px 16px 18px }
|
|
@@ -189,6 +198,7 @@
|
|
|
189
198
|
@keyframes scui-breathe { 50% { transform:scale(.72); opacity:.5 } }
|
|
190
199
|
@keyframes scui-progress { from { transform:translateX(-100%) } to { transform:translateX(180%) } }
|
|
191
200
|
@keyframes scui-dots { 50% { transform:translateY(-3px); opacity:.45 } }
|
|
201
|
+
@keyframes scui-session-dots { 50% { transform:translateY(-1px); opacity:.55 } }
|
|
192
202
|
@media (prefers-reduced-motion:reduce) { .scui-root * { animation-duration:.001ms !important; animation-iteration-count:1 !important; transition:none !important } }
|
|
193
203
|
@media (pointer:coarse) {
|
|
194
204
|
.scui-head > button,.scui-menu-trigger { width:40px; height:40px }
|