@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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@volter-ai-dev/supercode-ui",
3
- "version": "0.1.28",
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
- 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)}${path.complete ? ` \xB7 ${path.complete}` : ""}${preview ? ` \xB7 ${preview}` : ""}${unreadCount ? ` \xB7 ${unreadCount} unread` : ""}${row.age ? ` \xB7 ${row.age}` : ""}`, "aria-current": row.active ? "true" : void 0, onClick: () => onOpen(row), children: [
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
- row.age ? /* @__PURE__ */ jsx6("time", { children: row.age }) : null
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
- preview ? /* @__PURE__ */ jsx6("small", { children: preview }) : null,
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
@@ -0,0 +1,2 @@
1
+ export { HarnessAdvisory, HarnessSettingsPanel } from './index.js';
2
+ export type { HarnessAdvisoryProps, HarnessSettingsPanelProps } from './index.js';
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-accent); animation:scui-breathe 1.4s ease-in-out infinite }
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-title time { flex:none; color:var(--scui-muted); font-size:10px }
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 }