@volter-ai-dev/supercode-ui 0.1.29 → 0.1.31

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.29",
3
+ "version": "0.1.31",
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
  }
@@ -291,28 +303,35 @@ function sessionPathParts(value) {
291
303
  trailing: complete.slice(boundary + 1)
292
304
  };
293
305
  }
294
- function SessionRow({ row, state, onOpen }) {
306
+ function SessionRow({ row, state, onOpen, now = Date.now() }) {
295
307
  const activity = sessionActivity(state, row);
308
+ const working = activity === "working";
296
309
  const attention = state.attention.find((item) => item.key === row.key);
297
310
  const title = sessionDisplayName(row);
298
311
  const path = sessionPathParts(row.cwd);
299
312
  const preview = row.preview || attention?.preview || "";
300
313
  const unreadCount = attention?.unreadCount ?? 0;
301
314
  const unreadLabel = unreadCount > 99 ? "99+" : String(unreadCount);
302
- 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: [
303
317
  /* @__PURE__ */ jsx6(HarnessLogo, { id: row.harness, activity, size: 34 }),
304
318
  /* @__PURE__ */ jsxs5("span", { class: "scui-session-copy", children: [
305
319
  /* @__PURE__ */ jsxs5("span", { class: "scui-session-title", children: [
306
320
  /* @__PURE__ */ jsx6("strong", { children: title }),
307
- 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 })
308
322
  ] }),
309
323
  path.complete ? /* @__PURE__ */ jsxs5("small", { class: "scui-session-path", title: row.cwd, children: [
310
324
  /* @__PURE__ */ jsx6("span", { class: "scui-session-path-leading", children: path.leading }),
311
325
  path.separator ? /* @__PURE__ */ jsx6("span", { class: "scui-session-path-separator", children: path.separator }) : null,
312
326
  path.trailing ? /* @__PURE__ */ jsx6("span", { class: "scui-session-path-trailing", children: path.trailing }) : null
313
327
  ] }) : null,
314
- preview || unreadCount ? /* @__PURE__ */ jsxs5("span", { class: "scui-session-preview", children: [
315
- 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,
316
335
  unreadCount ? /* @__PURE__ */ jsx6("b", { "aria-label": `${unreadCount} unread messages`, children: unreadLabel }) : null
317
336
  ] }) : null,
318
337
  state.attachError?.key === row.key ? /* @__PURE__ */ jsx6("em", { children: state.attachError.message }) : null
@@ -323,6 +342,7 @@ function SessionList({ state, adapter, onOpen, onNew, onClose, components = {},
323
342
  const remembered = sessionListMemory.get(memoryKey) ?? { query: "", top: 0 };
324
343
  const [query, setQuery] = useState3(remembered.query);
325
344
  const [loadingMore, setLoadingMore] = useState3(false);
345
+ const [now, setNow] = useState3(() => Date.now());
326
346
  const root = useRef4(null);
327
347
  const rowScroller = useRef4(null);
328
348
  const rows = filterSessions(state.sessions, query);
@@ -338,6 +358,10 @@ function SessionList({ state, adapter, onOpen, onNew, onClose, components = {},
338
358
  useEffect5(() => {
339
359
  if (loadingMore) setLoadingMore(false);
340
360
  }, [state.error, state.history.hasMoreSessions, state.sessions.length]);
361
+ useEffect5(() => {
362
+ const timer = setInterval(() => setNow(Date.now()), 1e4);
363
+ return () => clearInterval(timer);
364
+ }, []);
341
365
  const loadMore = () => {
342
366
  if (loadingMore) return;
343
367
  setLoadingMore(true);
@@ -371,7 +395,7 @@ function SessionList({ state, adapter, onOpen, onNew, onClose, components = {},
371
395
  ] }) : null,
372
396
  /* @__PURE__ */ jsxs5("div", { class: "scui-session-rows", ref: rowScroller, onScroll: (event) => boundedSet(sessionListMemory, memoryKey, { query, top: event.currentTarget.scrollTop }), children: [
373
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,
374
- 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)),
375
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
376
400
  ] })
377
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("div", { class: "scui-advisory", "data-severity": advisory?.severity ?? "error", role: advisory?.severity === "error" || !advisory ? "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,8 +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 }
77
- .scui-logo[data-activity="running"] > i { background:var(--scui-positive) }
76
+ .scui-logo[data-activity="working"] > i,.scui-logo[data-activity="running"] > i { background:var(--scui-positive) }
78
77
  .scui-logo[data-activity="needs-input"] > i { background:var(--scui-warning) }
79
78
  .scui-logo[data-activity="failed"] > i { background:var(--scui-danger) }
80
79
  .scui-logo[data-activity="finished"] > i,.scui-logo[data-activity="unseen"] > i { background:var(--scui-positive) }
@@ -98,7 +97,7 @@
98
97
  .scui-session-copy { display:grid; flex:1; min-width:0; gap:1px }
99
98
  .scui-session-copy > span { display:flex; align-items:baseline; gap:7px; min-width:0 }
100
99
  .scui-session-copy strong,.scui-session-copy span > small { overflow:hidden; text-overflow:ellipsis; white-space:nowrap }
101
- .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 }
102
101
  .scui-session-path { display:flex; min-width:0; overflow:hidden; color:var(--scui-muted); font-size:9.5px; white-space:nowrap }
103
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 }
104
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 }
@@ -110,6 +109,15 @@
110
109
  .scui-error { color:var(--scui-danger) }.scui-error span,.scui-receipt span { flex:1; min-width:0 }
111
110
  .scui-error button,.scui-receipt button { border:0; background:transparent; color:var(--scui-accent); cursor:pointer }
112
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 }
113
121
 
114
122
  .scui-conversation-wrap { position:relative; display:flex; flex:1; min-height:0 }
115
123
  .scui-conversation { flex:1; min-width:0; overflow:auto; overscroll-behavior:contain; padding:12px 16px 18px }
@@ -190,6 +198,7 @@
190
198
  @keyframes scui-breathe { 50% { transform:scale(.72); opacity:.5 } }
191
199
  @keyframes scui-progress { from { transform:translateX(-100%) } to { transform:translateX(180%) } }
192
200
  @keyframes scui-dots { 50% { transform:translateY(-3px); opacity:.45 } }
201
+ @keyframes scui-session-dots { 50% { transform:translateY(-1px); opacity:.55 } }
193
202
  @media (prefers-reduced-motion:reduce) { .scui-root * { animation-duration:.001ms !important; animation-iteration-count:1 !important; transition:none !important } }
194
203
  @media (pointer:coarse) {
195
204
  .scui-head > button,.scui-menu-trigger { width:40px; height:40px }