@volter-ai-dev/supercode-ui 0.1.0

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 ADDED
@@ -0,0 +1,106 @@
1
+ {
2
+ "name": "@volter-ai-dev/supercode-ui",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "description": "Composable default UI kit for Supercode-powered coding-agent experiences",
6
+ "exports": {
7
+ ".": {
8
+ "types": "./index.d.ts",
9
+ "import": "./index.mjs"
10
+ },
11
+ "./core": {
12
+ "types": "./index.d.ts",
13
+ "import": "./core.mjs"
14
+ },
15
+ "./controller": {
16
+ "types": "./controller.d.ts",
17
+ "import": "./controller.mjs"
18
+ },
19
+ "./preact": {
20
+ "types": "./index.d.ts",
21
+ "import": "./components.mjs"
22
+ },
23
+ "./preact/logo": {
24
+ "types": "./index.d.ts",
25
+ "import": "./logo.mjs"
26
+ },
27
+ "./preact/conversation": {
28
+ "types": "./index.d.ts",
29
+ "import": "./conversation.mjs"
30
+ },
31
+ "./preact/sessions": {
32
+ "types": "./index.d.ts",
33
+ "import": "./sessions.mjs"
34
+ },
35
+ "./preact/composer": {
36
+ "types": "./index.d.ts",
37
+ "import": "./composer.mjs"
38
+ },
39
+ "./preact/messenger": {
40
+ "types": "./index.d.ts",
41
+ "import": "./messenger.mjs"
42
+ },
43
+ "./embed": {
44
+ "types": "./index.d.ts",
45
+ "import": "./embed.mjs"
46
+ },
47
+ "./styles.css": "./styles.css"
48
+ },
49
+ "types": "./index.d.ts",
50
+ "files": [
51
+ "core.mjs",
52
+ "controller.mjs",
53
+ "controller.d.ts",
54
+ "components.mjs",
55
+ "logo.mjs",
56
+ "conversation.mjs",
57
+ "sessions.mjs",
58
+ "composer.mjs",
59
+ "messenger.mjs",
60
+ "embed.mjs",
61
+ "index.mjs",
62
+ "index.d.ts",
63
+ "styles.css",
64
+ "README.md"
65
+ ],
66
+ "scripts": {
67
+ "build": "esbuild src/components.jsx src/embed.jsx src/logo.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",
68
+ "build:fixture": "esbuild test/browser-entry.jsx --bundle --format=esm --platform=browser --jsx=automatic --jsx-import-source=preact --outfile=test/browser-bundle.mjs",
69
+ "storybook": "npm run build && storybook dev -p 6006",
70
+ "build:storybook": "npm run build && storybook build",
71
+ "test:storybook": "vitest --run",
72
+ "test": "node --test test/*.test.mjs"
73
+ },
74
+ "dependencies": {
75
+ "markdown-it": "^14.1.0"
76
+ },
77
+ "peerDependencies": {
78
+ "@volter-ai-dev/supercode-client": ">=0.3.7",
79
+ "preact": ">=10.19.0 <11"
80
+ },
81
+ "peerDependenciesMeta": {
82
+ "@volter-ai-dev/supercode-client": {
83
+ "optional": true
84
+ }
85
+ },
86
+ "devDependencies": {
87
+ "@storybook/addon-a11y": "10.5.9",
88
+ "@storybook/addon-docs": "10.5.9",
89
+ "@storybook/addon-themes": "10.5.9",
90
+ "@storybook/addon-vitest": "10.5.9",
91
+ "@storybook/preact-vite": "10.5.9",
92
+ "@types/react": "19.2.18",
93
+ "@vitest/browser-playwright": "4.1.10",
94
+ "esbuild": "^0.25.0",
95
+ "playwright": "1.62.1",
96
+ "preact": "^10.19.0",
97
+ "preact-render-to-string": "^6.5.0",
98
+ "storybook": "10.5.9",
99
+ "vite": "7.3.6",
100
+ "vitest": "4.1.10"
101
+ },
102
+ "engines": {
103
+ "node": ">=20"
104
+ },
105
+ "license": "MIT OR Apache-2.0"
106
+ }
package/sessions.mjs ADDED
@@ -0,0 +1,196 @@
1
+ // src/components.jsx
2
+ import MarkdownIt from "markdown-it";
3
+ import { useEffect, useId, useLayoutEffect, useMemo, useRef, useState } from "preact/hooks";
4
+
5
+ // core.mjs
6
+ var HARNESS_NAMES = Object.freeze({
7
+ "claude-code": "Claude Code",
8
+ codex: "Codex",
9
+ opencode: "OpenCode",
10
+ pi: "Pi",
11
+ grok: "Grok"
12
+ });
13
+ var DEFAULT_LABELS = Object.freeze({
14
+ chats: "Chats",
15
+ newChat: "New chat",
16
+ searchChats: "Search chats",
17
+ askAgent: "Ask your agent\u2026",
18
+ continueHere: "Continue here",
19
+ joinLive: "Join live",
20
+ forkHere: "Fork here"
21
+ });
22
+ var EMPTY_UI_STATE = Object.freeze({
23
+ pill: Object.freeze({ tone: "off", label: "connecting\u2026" }),
24
+ startup: "connecting",
25
+ transcript: Object.freeze([]),
26
+ busy: false,
27
+ operation: null,
28
+ needsInput: false,
29
+ harness: "",
30
+ mode: "none",
31
+ strategy: null,
32
+ canSend: false,
33
+ canResume: false,
34
+ canBranch: false,
35
+ canAttach: false,
36
+ canDetach: false,
37
+ canOpenTerminal: false,
38
+ canExport: false,
39
+ canReduce: false,
40
+ canInterrupt: false,
41
+ canRespond: false,
42
+ messaging: null,
43
+ workspace: "",
44
+ taskPlan: Object.freeze({ source: "none", items: Object.freeze([]), residueCount: 0, observedAt: null }),
45
+ semantics: Object.freeze({ fidelity: null, residue: Object.freeze([]), residueCount: 0, parseErrors: 0, rawRecords: 0, subagents: Object.freeze([]) }),
46
+ terminalHandoff: null,
47
+ exportBackTarget: null,
48
+ exportReceipt: null,
49
+ reductionReceipt: null,
50
+ error: null,
51
+ recoverable: false,
52
+ harnesses: Object.freeze([]),
53
+ history: Object.freeze({ sessionLimit: 0, hasMoreSessions: false, transcriptLimit: 120, hasEarlier: false }),
54
+ savedDraft: "",
55
+ attention: Object.freeze([]),
56
+ sessions: Object.freeze([]),
57
+ attached: null,
58
+ owned: null,
59
+ attachError: null
60
+ });
61
+ function harnessDisplayName(id) {
62
+ return HARNESS_NAMES[id] ?? id;
63
+ }
64
+ function sessionDisplayName(session) {
65
+ const title = session.title?.trim();
66
+ return title && title !== session.name ? title : session.name || "Untitled chat";
67
+ }
68
+ function sessionActivity(state, row) {
69
+ if (state.needsInput && row.active) return "needs-input";
70
+ if (state.busy && row.active) return "working";
71
+ const attention = state.attention.find((item) => item.key === row.key)?.kind;
72
+ if (attention) return attention;
73
+ if (row.runtimeStatus === "busy") return "working";
74
+ if (row.live || row.runtimeStatus === "idle") return "recent";
75
+ return "idle";
76
+ }
77
+ function filterSessions(rows, query) {
78
+ const needle = query.trim().toLocaleLowerCase();
79
+ if (!needle) return [...rows];
80
+ return rows.filter((row) => [row.name, row.title, row.cwd, row.harness, harnessDisplayName(row.harness)].some((value) => value.toLocaleLowerCase().includes(needle)));
81
+ }
82
+
83
+ // src/components.jsx
84
+ import { jsx, jsxs } from "preact/jsx-runtime";
85
+ var markdown = new MarkdownIt({ html: false, linkify: true, breaks: false });
86
+ var defaultLinkOpen = markdown.renderer.rules.link_open;
87
+ markdown.renderer.rules.link_open = (tokens, index, options, env, self) => {
88
+ tokens[index]?.attrSet("target", "_blank");
89
+ tokens[index]?.attrSet("rel", "noreferrer noopener");
90
+ return defaultLinkOpen ? defaultLinkOpen(tokens, index, options, env, self) : self.renderToken(tokens, index, options);
91
+ };
92
+ var LOGOS = {
93
+ "claude-code": {
94
+ viewBox: "-1 3.5 26 18",
95
+ paths: [
96
+ ["path", { "fill-rule": "evenodd", "clip-rule": "evenodd", d: "M20.998 10.949H24v3.102h-3v3.028h-1.487V20H18v-2.921h-1.487V20H15v-2.921H9V20H7.488v-2.921H6V20H4.487v-2.921H3V14.05H0V10.95h3V5h17.998v5.949zM6 10.949h1.488V8.102H6v2.847zm10.51 0H18V8.102h-1.49v2.847z" }]
97
+ ]
98
+ },
99
+ codex: {
100
+ viewBox: "-1 -1 26 26",
101
+ paths: [
102
+ ["path", { "fill-rule": "evenodd", "clip-rule": "evenodd", d: "M8.086.457a6.105 6.105 0 013.046-.415c1.333.153 2.521.72 3.564 1.7a.117.117 0 00.107.029c1.408-.346 2.762-.224 4.061.366l.063.03.154.076c1.357.703 2.33 1.77 2.918 3.198.278.679.418 1.388.421 2.126a5.655 5.655 0 01-.18 1.631.167.167 0 00.04.155 5.982 5.982 0 011.578 2.891c.385 1.901-.01 3.615-1.183 5.14l-.182.22a6.063 6.063 0 01-2.934 1.851.162.162 0 00-.108.102c-.255.736-.511 1.364-.987 1.992-1.199 1.582-2.962 2.462-4.948 2.451-1.583-.008-2.986-.587-4.21-1.736a.145.145 0 00-.14-.032c-.518.167-1.04.191-1.604.185a5.924 5.924 0 01-2.595-.622 6.058 6.058 0 01-2.146-1.781c-.203-.269-.404-.522-.551-.821a7.74 7.74 0 01-.495-1.283 6.11 6.11 0 01-.017-3.064.166.166 0 00.008-.074.115.115 0 00-.037-.064 5.958 5.958 0 01-1.38-2.202 5.196 5.196 0 01-.333-1.589 6.915 6.915 0 01.188-2.132c.45-1.484 1.309-2.648 2.577-3.493.282-.188.55-.334.802-.438.286-.12.573-.22.861-.304a.129.129 0 00.087-.087A6.016 6.016 0 015.635 2.31C6.315 1.464 7.132.846 8.086.457zm-.804 7.85a.848.848 0 00-1.473.842l1.694 2.965-1.688 2.848a.849.849 0 001.46.864l1.94-3.272a.849.849 0 00.007-.854l-1.94-3.393zm5.446 6.24a.849.849 0 000 1.695h4.848a.849.849 0 000-1.696h-4.848z" }]
103
+ ]
104
+ },
105
+ grok: {
106
+ viewBox: "-1 -1 26 26",
107
+ paths: [["path", { d: "M9.27 15.29l7.978-5.897c.391-.29.95-.177 1.137.272.98 2.369.542 5.215-1.41 7.169-1.951 1.954-4.667 2.382-7.149 1.406l-2.711 1.257c3.889 2.661 8.611 2.003 11.562-.953 2.341-2.344 3.066-5.539 2.388-8.42l.006.007c-.983-4.232.242-5.924 2.75-9.383.06-.082.12-.164.179-.248l-3.301 3.305v-.01L9.267 15.292M7.623 16.723c-2.792-2.67-2.31-6.801.071-9.184 1.761-1.763 4.647-2.483 7.166-1.425l2.705-1.25a7.808 7.808 0 00-1.829-1A8.975 8.975 0 005.984 5.83c-2.533 2.536-3.33 6.436-1.962 9.764 1.022 2.487-.653 4.246-2.34 6.022-.599.63-1.199 1.259-1.682 1.925l7.62-6.815" }]]
108
+ },
109
+ opencode: { viewBox: "2.5 0 19 24", paths: [["path", { d: "M16 6H8v12h8V6zm4 16H4V2h16v20z" }]] },
110
+ pi: {
111
+ viewBox: "0 0 24 24",
112
+ paths: [
113
+ ["path", { "fill-rule": "evenodd", "clip-rule": "evenodd", d: "M1 1h16.5v11H12v5.5H6.5V23H1V1zm5.5 5.5V12H12V6.5H6.5z" }],
114
+ ["path", { d: "M17.5 12H23v11h-5.5V12z" }]
115
+ ]
116
+ }
117
+ };
118
+ function HarnessLogo({ id, activity, size = 28, onMissingLogo }) {
119
+ const logo = LOGOS[id];
120
+ useEffect(() => {
121
+ if (!logo) onMissingLogo?.(id);
122
+ }, [id, logo, onMissingLogo]);
123
+ if (!logo) return null;
124
+ return /* @__PURE__ */ jsxs("span", { class: "scui-logo", "data-harness": id, "data-activity": activity, style: `--scui-logo-size:${size}px`, "aria-hidden": "true", children: [
125
+ /* @__PURE__ */ jsx("svg", { viewBox: logo.viewBox, focusable: "false", children: logo.paths.map(([Tag, props], index) => /* @__PURE__ */ jsx(Tag, { ...props }, index)) }),
126
+ activity && activity !== "idle" ? /* @__PURE__ */ jsx("i", {}) : null
127
+ ] });
128
+ }
129
+ function LoadingStatus({ state, compact = false }) {
130
+ const copy = {
131
+ connecting: ["Connecting to coding agents", "Checking installed harnesses and capabilities.", 0],
132
+ starting: [`Starting ${harnessDisplayName(state.harness) || "coding agent"}`, "Opening a controlled session in this workspace.", 1],
133
+ discovering: ["Loading recent sessions", "Scanning native session stores without loading full transcripts.", 2],
134
+ ready: ["Ready", "Coding sessions are up to date.", 3]
135
+ }[state.startup];
136
+ return /* @__PURE__ */ jsxs("div", { class: `scui-loading${compact ? " scui-loading-compact" : ""}`, role: "status", "aria-busy": state.startup !== "ready", children: [
137
+ /* @__PURE__ */ jsx("span", { class: "scui-orbit", "aria-hidden": "true", children: /* @__PURE__ */ jsx("i", {}) }),
138
+ /* @__PURE__ */ jsxs("span", { class: "scui-loading-copy", children: [
139
+ /* @__PURE__ */ jsx("strong", { children: copy[0] }),
140
+ /* @__PURE__ */ jsx("small", { children: copy[1] })
141
+ ] }),
142
+ /* @__PURE__ */ jsx("span", { class: "scui-progress", "aria-hidden": "true", children: [1, 2, 3].map((step) => /* @__PURE__ */ jsx("i", { "data-progress": step <= copy[2] ? "done" : step === copy[2] + 1 ? "current" : "waiting" }, step)) })
143
+ ] });
144
+ }
145
+ function SessionRow({ row, state, onOpen }) {
146
+ const activity = sessionActivity(state, row);
147
+ const preview = state.attention.find((item) => item.key === row.key)?.preview;
148
+ return /* @__PURE__ */ jsxs("button", { class: "scui-session", "data-active": row.active, "data-activity": activity, type: "button", onClick: () => onOpen(row), children: [
149
+ /* @__PURE__ */ jsx(HarnessLogo, { id: row.harness, activity, size: 34 }),
150
+ /* @__PURE__ */ jsxs("span", { class: "scui-session-copy", children: [
151
+ /* @__PURE__ */ jsxs("span", { children: [
152
+ /* @__PURE__ */ jsx("strong", { children: sessionDisplayName(row) }),
153
+ /* @__PURE__ */ jsx("small", { children: row.age })
154
+ ] }),
155
+ /* @__PURE__ */ jsxs("span", { children: [
156
+ /* @__PURE__ */ jsx("b", { children: harnessDisplayName(row.harness) }),
157
+ /* @__PURE__ */ jsx("small", { children: preview || row.cwd || row.name })
158
+ ] }),
159
+ state.attachError?.key === row.key ? /* @__PURE__ */ jsx("em", { children: state.attachError.message }) : null
160
+ ] })
161
+ ] });
162
+ }
163
+ function SessionList({ state, adapter, onOpen, onNew, onClose, components = {}, labels = DEFAULT_LABELS }) {
164
+ const [query, setQuery] = useState("");
165
+ const rows = filterSessions(state.sessions, query);
166
+ const Row = components.SessionRow ?? SessionRow;
167
+ return /* @__PURE__ */ jsxs("section", { class: "scui-list", children: [
168
+ /* @__PURE__ */ jsxs("header", { class: "scui-head", children: [
169
+ /* @__PURE__ */ jsxs("span", { children: [
170
+ /* @__PURE__ */ jsx("strong", { children: labels.chats }),
171
+ /* @__PURE__ */ jsxs("small", { children: [
172
+ state.sessions.length,
173
+ " recent conversations"
174
+ ] })
175
+ ] }),
176
+ /* @__PURE__ */ jsx("button", { type: "button", "aria-label": labels.newChat, disabled: !state.harnesses.some((item) => item.startable), onClick: onNew, children: "\uFF0B" }),
177
+ onClose ? /* @__PURE__ */ jsx("button", { type: "button", "aria-label": "Close", onClick: onClose, children: "\xD7" }) : null
178
+ ] }),
179
+ state.startup !== "ready" ? /* @__PURE__ */ jsx(LoadingStatus, { state, compact: rows.length > 0 }) : null,
180
+ state.sessions.length > 4 ? /* @__PURE__ */ jsxs("label", { class: "scui-search", children: [
181
+ /* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: "\u2315" }),
182
+ /* @__PURE__ */ jsx("input", { type: "search", "aria-label": labels.searchChats, placeholder: labels.searchChats, value: query, onInput: (event) => setQuery(event.currentTarget.value) }),
183
+ /* @__PURE__ */ jsx("small", { children: rows.length })
184
+ ] }) : null,
185
+ /* @__PURE__ */ jsxs("div", { class: "scui-session-rows", children: [
186
+ !rows.length && state.startup === "ready" ? /* @__PURE__ */ jsx("div", { class: "scui-empty", children: query ? "No chats match your search." : state.error ?? "No coding chats found." }) : null,
187
+ rows.map((row) => /* @__PURE__ */ jsx(Row, { value: row, row, state, adapter, onOpen }, row.key)),
188
+ state.history.hasMoreSessions ? /* @__PURE__ */ jsx("button", { class: "scui-load", type: "button", onClick: () => adapter.onIntent({ action: "loadSessions" }), children: "Load older chats" }) : null
189
+ ] })
190
+ ] });
191
+ }
192
+ export {
193
+ LoadingStatus,
194
+ SessionList,
195
+ SessionRow
196
+ };
package/styles.css ADDED
@@ -0,0 +1,154 @@
1
+ :where(.scui-root) {
2
+ --scui-width: 420px;
3
+ --scui-height: 480px;
4
+ --scui-bg: #fff;
5
+ --scui-bg-raised: #f7f7f6;
6
+ --scui-fill: #f1f1ef;
7
+ --scui-fg: #191918;
8
+ --scui-muted: #5f5f5a;
9
+ --scui-border: #deded9;
10
+ --scui-border-strong: #c7c7c0;
11
+ --scui-accent: #2458b8;
12
+ --scui-positive: #22863a;
13
+ --scui-warning: #b26a00;
14
+ --scui-danger: #b4232e;
15
+ --scui-radius: 14px;
16
+ --scui-font: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
17
+ box-sizing: border-box;
18
+ display: flex;
19
+ position: relative;
20
+ width: min(var(--scui-width), 100%);
21
+ height: min(var(--scui-height), 100%);
22
+ overflow: hidden;
23
+ border: 1px solid var(--scui-border);
24
+ border-radius: var(--scui-radius);
25
+ background: var(--scui-bg);
26
+ color: var(--scui-fg);
27
+ font: 13px/1.4 var(--scui-font);
28
+ color-scheme: light dark;
29
+ }
30
+
31
+ @media (prefers-color-scheme: dark) {
32
+ :where(.scui-root) {
33
+ --scui-bg: #151515;
34
+ --scui-bg-raised: #1c1c1b;
35
+ --scui-fill: #242422;
36
+ --scui-fg: #f3f3ef;
37
+ --scui-muted: #a3a39d;
38
+ --scui-border: #353532;
39
+ --scui-border-strong: #4a4a45;
40
+ --scui-accent: #72a7ff;
41
+ --scui-positive: #57c26e;
42
+ --scui-warning: #e8a23a;
43
+ --scui-danger: #ff747d;
44
+ }
45
+ }
46
+
47
+ :where(.scui-root *), :where(.scui-root *::before), :where(.scui-root *::after) { box-sizing: border-box }
48
+ :where(.scui-root button, .scui-root input, .scui-root textarea, .scui-root select) { font: inherit }
49
+ :where(.scui-root button) { color: inherit }
50
+ :where(.scui-list button, .scui-chat button, .scui-request button, .scui-activity button, .scui-compose button, .scui-continuation button) { color: inherit }
51
+ :where(.scui-root button:focus-visible, .scui-root input:focus-visible, .scui-root textarea:focus-visible, .scui-root select:focus-visible, .scui-root [tabindex]:focus-visible) { outline: 2px solid var(--scui-accent); outline-offset: -2px }
52
+
53
+ .scui-list,.scui-chat { display:flex; flex:1; min-width:0; min-height:0; flex-direction:column }
54
+ .scui-head { display:flex; flex:none; align-items:center; gap:8px; min-height:49px; padding:7px 8px 7px 12px; border-bottom:1px solid var(--scui-border); background:var(--scui-bg-raised) }
55
+ .scui-head > span { display:grid; flex:1; min-width:0 }
56
+ .scui-head strong { overflow:hidden; text-overflow:ellipsis; white-space:nowrap; font-size:13px }
57
+ .scui-head small { overflow:hidden; color:var(--scui-muted); font-size:10.5px; text-overflow:ellipsis; white-space:nowrap }
58
+ .scui-head > button,.scui-menu > summary { display:grid; place-items:center; width:30px; height:30px; padding:0; border:0; border-radius:8px; background:transparent; cursor:pointer; font-size:18px; list-style:none }
59
+ .scui-head > button:hover,.scui-menu > summary:hover { background:var(--scui-fill) }
60
+ .scui-menu { position:relative }
61
+ .scui-menu > summary::-webkit-details-marker { display:none }
62
+ .scui-menu > div { position:absolute; z-index:20; top:36px; right:0; display:grid; width:250px; max-height:300px; padding:5px; overflow:auto; border:1px solid var(--scui-border-strong); border-radius:10px; background:var(--scui-bg); box-shadow:0 10px 28px rgba(0,0,0,.22) }
63
+ .scui-menu button { padding:8px; border:0; border-radius:6px; background:transparent; text-align:left; cursor:pointer }
64
+ .scui-menu button:hover { background:var(--scui-fill) }
65
+
66
+ .scui-logo { position:relative; display:inline-grid; place-items:center; width:var(--scui-logo-size); height:var(--scui-logo-size); flex:none; border:1px solid var(--scui-border); border-radius:50%; background:#111; color:#f7f7f5 }
67
+ .scui-logo svg { width:70%; height:70%; fill:currentColor }
68
+ .scui-logo[data-harness="claude-code"] { color:#d97757 }
69
+ .scui-logo[data-harness="codex"] { color:#111; background:#f7f7f5 }
70
+ .scui-logo[data-harness="pi"] { color:#111; background:#efefeb }
71
+ .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) }
72
+ .scui-logo[data-activity="working"] > i { background:var(--scui-accent); animation:scui-breathe 1.4s ease-in-out infinite }
73
+ .scui-logo[data-activity="needs-input"] > i { background:var(--scui-warning) }
74
+ .scui-logo[data-activity="failed"] > i { background:var(--scui-danger) }
75
+ .scui-logo[data-activity="finished"] > i,.scui-logo[data-activity="unseen"] > i { background:var(--scui-positive) }
76
+
77
+ .scui-loading { display:grid; grid-template-columns:30px minmax(0,1fr); gap:4px 10px; align-items:center; margin:auto; padding:18px; color:var(--scui-muted) }
78
+ .scui-loading-compact { width:calc(100% - 16px); margin:8px; padding:8px; border:1px solid var(--scui-border); border-radius:9px; background:var(--scui-bg-raised) }
79
+ .scui-orbit { grid-row:1 / span 2; position:relative; width:27px; height:27px; border:1px solid var(--scui-border-strong); border-radius:50% }
80
+ .scui-orbit::before { content:""; position:absolute; inset:5px; border-radius:50%; background:var(--scui-fill); animation:scui-breathe 1.5s ease-in-out infinite }
81
+ .scui-orbit i { position:absolute; inset:-1px; border:2px solid transparent; border-top-color:var(--scui-accent); border-radius:50%; animation:scui-spin 1s linear infinite }
82
+ .scui-loading-copy { display:grid; gap:2px }.scui-loading-copy strong { color:var(--scui-fg); font-size:12px }.scui-loading-copy small { font-size:10.5px }
83
+ .scui-progress { grid-column:2; display:grid; grid-template-columns:repeat(3,minmax(18px,38px)); gap:4px }
84
+ .scui-progress i { height:2px; overflow:hidden; border-radius:2px; background:var(--scui-fill) }
85
+ .scui-progress i[data-progress="done"] { background:var(--scui-accent) }
86
+ .scui-progress i[data-progress="current"]::after { content:""; display:block; width:55%; height:100%; background:var(--scui-accent); animation:scui-progress 1s ease-in-out infinite alternate }
87
+
88
+ .scui-search { display:flex; flex:none; align-items:center; gap:7px; margin:8px; padding:6px 8px; border:1px solid var(--scui-border); border-radius:8px; background:var(--scui-bg-raised); color:var(--scui-muted) }
89
+ .scui-search input { flex:1; min-width:0; border:0; outline:0; background:transparent; color:var(--scui-fg) }
90
+ .scui-session-rows { flex:1; min-height:0; overflow:auto; padding:0 6px 8px }
91
+ .scui-session { display:flex; align-items:center; gap:9px; width:100%; padding:7px 8px; border:0; border-radius:9px; background:transparent; text-align:left; cursor:pointer }
92
+ .scui-session:hover,.scui-session[data-active="true"] { background:var(--scui-fill) }
93
+ .scui-session-copy { display:grid; flex:1; min-width:0; gap:2px }
94
+ .scui-session-copy > span { display:flex; align-items:baseline; gap:7px; min-width:0 }
95
+ .scui-session-copy strong,.scui-session-copy span > small { overflow:hidden; text-overflow:ellipsis; white-space:nowrap }
96
+ .scui-session-copy strong { flex:1; font-size:12.5px }.scui-session-copy span:first-child > small { flex:none; color:var(--scui-muted); font-size:10px }
97
+ .scui-session-copy b { flex:none; padding:1px 5px; border-radius:4px; background:var(--scui-bg-raised); color:var(--scui-muted); font-size:9px; text-transform:uppercase }
98
+ .scui-session-copy span:last-child > small { color:var(--scui-muted); font-size:10.5px }
99
+ .scui-session-copy em { overflow:hidden; color:var(--scui-danger); font-size:10px; font-style:normal; text-overflow:ellipsis; white-space:nowrap }
100
+ .scui-load { display:block; margin:7px auto; padding:5px 10px; border:1px solid var(--scui-border); border-radius:8px; background:var(--scui-bg-raised); color:var(--scui-muted); cursor:pointer }
101
+
102
+ .scui-operation,.scui-error,.scui-receipt { display:flex; flex:none; align-items:center; gap:8px; min-height:32px; padding:6px 10px; border-bottom:1px solid var(--scui-border); background:var(--scui-bg-raised); font-size:10.5px }
103
+ .scui-operation i { width:11px; height:11px; border:2px solid var(--scui-border); border-top-color:var(--scui-accent); border-radius:50%; animation:scui-spin .8s linear infinite }
104
+ .scui-error { color:var(--scui-danger) }.scui-error span,.scui-receipt span { flex:1; min-width:0 }
105
+ .scui-error button,.scui-receipt button { border:0; background:transparent; color:var(--scui-accent); cursor:pointer }
106
+ .scui-receipt span { display:grid }.scui-receipt small { overflow:hidden; color:var(--scui-muted); text-overflow:ellipsis; white-space:nowrap }
107
+
108
+ .scui-conversation-wrap { position:relative; display:flex; flex:1; min-height:0 }
109
+ .scui-conversation { flex:1; min-width:0; overflow:auto; overscroll-behavior:contain; padding:12px 16px 18px }
110
+ .scui-conversation > div { display:flex; flex-direction:column; gap:10px }
111
+ .scui-empty { margin:auto; padding:20px; color:var(--scui-muted); text-align:center }
112
+ .scui-latest { position:absolute; z-index:5; right:12px; bottom:8px; padding:5px 9px; border:1px solid var(--scui-border-strong); border-radius:999px; background:var(--scui-bg-raised); box-shadow:0 2px 8px rgba(0,0,0,.2); cursor:pointer }
113
+ .scui-message { position:relative; overflow-wrap:anywhere }
114
+ .scui-message[data-role="user"] { align-self:flex-end; max-width:90%; padding:8px 9px; border:1px solid var(--scui-border); border-radius:10px; background:var(--scui-fill) }
115
+ .scui-pending { opacity:.7 }.scui-pending > small { display:block; margin-top:3px; color:var(--scui-muted); font-size:9.5px }
116
+ .scui-markdown { font-size:12.5px; line-height:1.55 }.scui-markdown > :first-child { margin-top:0 }.scui-markdown > :last-child { margin-bottom:0 }
117
+ .scui-markdown p { margin:0 0 8px }.scui-markdown h1,.scui-markdown h2,.scui-markdown h3 { margin:12px 0 5px; line-height:1.25 }.scui-markdown h1 { font-size:17px }.scui-markdown h2 { font-size:15px }.scui-markdown h3 { font-size:13px }
118
+ .scui-markdown ul,.scui-markdown ol { margin:5px 0 9px; padding-left:20px }.scui-markdown blockquote { margin:8px 0; padding-left:9px; border-left:2px solid var(--scui-border-strong); color:var(--scui-muted) }
119
+ .scui-markdown a { color:var(--scui-accent) }.scui-markdown code { padding:1px 4px; border-radius:4px; background:var(--scui-fill); font:.92em ui-monospace,SFMono-Regular,Menlo,monospace }
120
+ .scui-markdown pre { padding:8px; overflow:auto; border:1px solid var(--scui-border); border-radius:7px; background:var(--scui-bg-raised) }.scui-markdown pre code { padding:0; background:transparent }
121
+ .scui-markdown table { display:block; overflow:auto; border-collapse:collapse }.scui-markdown th,.scui-markdown td { padding:4px; border:1px solid var(--scui-border) }
122
+ .scui-context,.scui-reasoning,.scui-details,.scui-plan { color:var(--scui-muted); font-size:10.5px }
123
+ .scui-context summary,.scui-reasoning summary,.scui-details summary,.scui-plan summary { cursor:pointer }
124
+ .scui-context > div,.scui-details > div { display:grid; gap:5px; max-height:150px; margin-top:5px; padding:7px; overflow:auto; border:1px solid var(--scui-border); border-radius:7px; background:var(--scui-bg-raised) }
125
+ .scui-context p,.scui-details p { display:grid; margin:0 }.scui-context strong,.scui-details strong { color:var(--scui-fg) }
126
+ .scui-truncated { display:block; margin-top:5px; color:var(--scui-warning) }
127
+ .scui-notice { align-self:center; max-width:90%; padding:5px 8px; border-radius:7px; background:var(--scui-bg-raised); color:var(--scui-muted); font-size:10.5px; text-align:center }
128
+
129
+ .scui-activity { overflow:hidden; border:1px solid var(--scui-border); border-radius:9px; background:var(--scui-bg-raised) }
130
+ .scui-activity-head,.scui-tool-head { display:flex; align-items:center; gap:6px; width:100%; min-height:32px; padding:0 8px; border:0; background:transparent; text-align:left; cursor:pointer }
131
+ .scui-activity-head strong { font-size:11px }.scui-activity-head small { color:var(--scui-muted) }.scui-spacer { flex:1 }
132
+ .scui-fold { font-size:16px; transition:transform 120ms }.scui-fold[data-open="true"] { transform:rotate(90deg) }
133
+ .scui-tool { border-top:1px solid var(--scui-border) }.scui-tool-head:disabled { cursor:default }.scui-tool-glyph { width:18px; color:var(--scui-accent); text-align:center }.scui-tool-head strong { font-size:10.5px; text-transform:capitalize }
134
+ .scui-tool-target { min-width:0; overflow:hidden; color:var(--scui-muted); font:10px ui-monospace,SFMono-Regular,Menlo,monospace; text-overflow:ellipsis; white-space:nowrap }
135
+ .scui-tool-detail { padding:0 7px 7px }.scui-tool-detail pre { max-height:220px; margin:0 0 5px; padding:7px; overflow:auto; border-radius:6px; background:var(--scui-bg); font:10px/1.45 ui-monospace,SFMono-Regular,Menlo,monospace; white-space:pre-wrap }.scui-tool-detail pre[data-error="true"] { color:var(--scui-danger) }
136
+ .scui-request { padding:9px; border:1px solid var(--scui-warning); border-radius:9px; background:color-mix(in srgb,var(--scui-warning) 8%,var(--scui-bg)) }.scui-request-actions { display:flex; flex-wrap:wrap; gap:5px }.scui-request-actions button { padding:5px 8px; border:1px solid var(--scui-border-strong); border-radius:6px; background:var(--scui-bg); cursor:pointer }
137
+ .scui-request-done { color:var(--scui-muted); font-size:10.5px }
138
+ .scui-plan > summary { display:flex; justify-content:space-between; padding:6px 8px; border:1px solid var(--scui-border); border-radius:7px; background:var(--scui-bg-raised) }.scui-plan ol { display:grid; gap:4px; max-height:140px; margin:5px 0 0; padding:7px 8px 7px 25px; overflow:auto; border:1px solid var(--scui-border); border-radius:7px }.scui-plan li[data-status="completed"] { text-decoration:line-through; opacity:.65 }
139
+ .scui-working { display:flex; align-items:center; gap:4px; color:var(--scui-muted) }.scui-working > span { color:var(--scui-accent) }.scui-working > i { width:4px; height:4px; border-radius:50%; background:currentColor; animation:scui-dots 1.2s infinite }.scui-working > i:nth-of-type(2) { animation-delay:.15s }.scui-working > i:nth-of-type(3) { animation-delay:.3s }
140
+
141
+ .scui-continuation { display:flex; align-items:center; gap:8px; padding:7px 9px; border-top:1px solid var(--scui-border); background:var(--scui-bg-raised) }.scui-continuation > span { display:grid; flex:1 }.scui-continuation small { color:var(--scui-muted); font-size:10px }.scui-continuation button { padding:5px 8px; border:1px solid var(--scui-accent); border-radius:7px; background:transparent; color:var(--scui-accent); cursor:pointer }
142
+ .scui-compose { flex:none; padding:8px; border-top:1px solid var(--scui-border); background:var(--scui-bg-raised) }
143
+ .scui-envelope { display:flex; align-items:flex-end; gap:7px; padding:7px; border:1px solid var(--scui-border-strong); border-radius:10px; background:var(--scui-bg) }.scui-envelope textarea { flex:1; min-width:0; min-height:34px; max-height:150px; resize:none; border:0; outline:0; background:transparent; color:var(--scui-fg) }.scui-envelope > span { display:flex; gap:4px }
144
+ .scui-send,.scui-stop { width:29px; height:29px; border:0; border-radius:8px; background:var(--scui-accent); color:#fff; cursor:pointer }.scui-stop { background:var(--scui-danger) }.scui-send:disabled,.scui-stop:disabled { opacity:.4; cursor:default }
145
+ .scui-queue { display:grid; gap:4px; max-height:85px; margin-bottom:6px; overflow:auto; font-size:10.5px }.scui-queue > span { display:flex; gap:6px; padding:4px 6px; border-radius:5px; background:var(--scui-fill) }.scui-queue > span button { margin-left:auto; border:0; background:transparent }
146
+ .scui-harness-picker { display:flex; align-items:center; gap:7px; margin-bottom:7px }.scui-harness-picker select { margin-left:auto; max-width:55%; padding:4px; border:1px solid var(--scui-border); border-radius:6px; background:var(--scui-bg); color:var(--scui-fg) }
147
+ .scui-new { display:grid; justify-items:center; gap:5px; margin:auto; padding:20px; color:var(--scui-muted); text-align:center }.scui-new > span { color:var(--scui-accent); font-size:28px }.scui-new strong { color:var(--scui-fg) }
148
+ .scui-opening { position:absolute; z-index:30; inset:49px 0 0; display:flex; align-items:center; justify-content:center; gap:10px; padding:20px; background:var(--scui-bg) }.scui-opening > span { display:grid }.scui-opening small { color:var(--scui-muted) }.scui-opening > i { width:16px; height:16px; border:2px solid var(--scui-border); border-top-color:var(--scui-accent); border-radius:50%; animation:scui-spin .8s linear infinite }
149
+
150
+ @keyframes scui-spin { to { transform:rotate(360deg) } }
151
+ @keyframes scui-breathe { 50% { transform:scale(.72); opacity:.5 } }
152
+ @keyframes scui-progress { from { transform:translateX(-100%) } to { transform:translateX(180%) } }
153
+ @keyframes scui-dots { 50% { transform:translateY(-3px); opacity:.45 } }
154
+ @media (prefers-reduced-motion:reduce) { .scui-root * { animation-duration:.001ms !important; animation-iteration-count:1 !important; transition:none !important } }