@volter-ai-dev/supercode-ui 0.1.12 → 0.1.14
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 +1 -1
- package/components.d.ts +1 -0
- package/components.mjs +791 -364
- package/composer.d.ts +1 -1
- package/composer.mjs +100 -24
- package/controller.mjs +5 -2
- package/conversation.d.ts +1 -0
- package/conversation.mjs +368 -193
- package/core.mjs +3 -2
- package/embed.mjs +792 -366
- package/icon.d.ts +2 -0
- package/icon.mjs +45 -0
- package/index.d.ts +17 -4
- package/messenger.mjs +790 -364
- package/package.json +8 -2
- package/sessions.mjs +164 -46
- package/styles.css +38 -18
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.14",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Composable default UI kit for Supercode-powered coding-agent experiences",
|
|
6
6
|
"exports": {
|
|
@@ -24,6 +24,10 @@
|
|
|
24
24
|
"types": "./logo.d.ts",
|
|
25
25
|
"import": "./logo.mjs"
|
|
26
26
|
},
|
|
27
|
+
"./preact/icon": {
|
|
28
|
+
"types": "./icon.d.ts",
|
|
29
|
+
"import": "./icon.mjs"
|
|
30
|
+
},
|
|
27
31
|
"./preact/conversation": {
|
|
28
32
|
"types": "./conversation.d.ts",
|
|
29
33
|
"import": "./conversation.mjs"
|
|
@@ -56,6 +60,8 @@
|
|
|
56
60
|
"components.d.ts",
|
|
57
61
|
"logo.mjs",
|
|
58
62
|
"logo.d.ts",
|
|
63
|
+
"icon.mjs",
|
|
64
|
+
"icon.d.ts",
|
|
59
65
|
"conversation.mjs",
|
|
60
66
|
"conversation.d.ts",
|
|
61
67
|
"sessions.mjs",
|
|
@@ -72,7 +78,7 @@
|
|
|
72
78
|
"README.md"
|
|
73
79
|
],
|
|
74
80
|
"scripts": {
|
|
75
|
-
"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",
|
|
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",
|
|
76
82
|
"build:fixture": "esbuild test/browser-entry.jsx --bundle --format=esm --platform=browser --jsx=automatic --jsx-import-source=preact --outfile=test/browser-bundle.mjs",
|
|
77
83
|
"storybook": "npm run build && storybook dev -p 6006",
|
|
78
84
|
"build:storybook": "npm run build && storybook build",
|
package/sessions.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// src/sessions.jsx
|
|
2
|
-
import { useState as useState2 } from "preact/hooks";
|
|
2
|
+
import { useEffect as useEffect4, useLayoutEffect as useLayoutEffect2, useRef as useRef3, useState as useState2 } from "preact/hooks";
|
|
3
3
|
|
|
4
4
|
// core.mjs
|
|
5
5
|
var HARNESS_NAMES = Object.freeze({
|
|
@@ -82,13 +82,52 @@ function filterSessions(rows, query) {
|
|
|
82
82
|
}
|
|
83
83
|
|
|
84
84
|
// src/conversation.jsx
|
|
85
|
-
import {
|
|
85
|
+
import { Fragment as Fragment2 } from "preact";
|
|
86
|
+
import { useEffect as useEffect2, useId, useLayoutEffect, useRef as useRef2, useState } from "preact/hooks";
|
|
86
87
|
|
|
87
88
|
// src/markdown.jsx
|
|
88
89
|
import MarkdownIt from "markdown-it";
|
|
89
|
-
import { useMemo } from "preact/hooks";
|
|
90
|
+
import { useEffect, useMemo, useRef } from "preact/hooks";
|
|
90
91
|
import { jsx } from "preact/jsx-runtime";
|
|
91
92
|
var markdown = new MarkdownIt({ html: false, linkify: true, breaks: false });
|
|
93
|
+
var LANGUAGE_LABELS = {
|
|
94
|
+
bash: "Shell",
|
|
95
|
+
css: "CSS",
|
|
96
|
+
html: "HTML",
|
|
97
|
+
js: "JavaScript",
|
|
98
|
+
javascript: "JavaScript",
|
|
99
|
+
jsx: "JSX",
|
|
100
|
+
md: "Markdown",
|
|
101
|
+
markdown: "Markdown",
|
|
102
|
+
py: "Python",
|
|
103
|
+
python: "Python",
|
|
104
|
+
rs: "Rust",
|
|
105
|
+
rust: "Rust",
|
|
106
|
+
sh: "Shell",
|
|
107
|
+
shell: "Shell",
|
|
108
|
+
ts: "TypeScript",
|
|
109
|
+
tsx: "TSX",
|
|
110
|
+
yaml: "YAML",
|
|
111
|
+
yml: "YAML",
|
|
112
|
+
json: "JSON",
|
|
113
|
+
jsonc: "JSONC",
|
|
114
|
+
sql: "SQL",
|
|
115
|
+
xml: "XML"
|
|
116
|
+
};
|
|
117
|
+
var COPY_ICON = '<svg viewBox="0 0 18 18" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="5" y="5" width="8" height="8" rx="1.5"></rect><path d="M3 10.5V4.25C3 3.56 3.56 3 4.25 3h6.25"></path></svg>';
|
|
118
|
+
function languageLabel(info) {
|
|
119
|
+
const language = info.trim().split(/\s+/, 1)[0]?.toLocaleLowerCase() ?? "";
|
|
120
|
+
if (!language) return "Code";
|
|
121
|
+
return LANGUAGE_LABELS[language] ?? language.toLocaleUpperCase();
|
|
122
|
+
}
|
|
123
|
+
function frameCode(render, tokens, index, options, env, self) {
|
|
124
|
+
const label = markdown.utils.escapeHtml(languageLabel(tokens[index]?.info ?? ""));
|
|
125
|
+
return `<div class="scui-code-block"><div class="scui-code-head"><span>${label}</span><button class="scui-code-copy" type="button" aria-label="Copy code" title="Copy code">${COPY_ICON}<span>Copy</span></button></div>${render(tokens, index, options, env, self)}</div>`;
|
|
126
|
+
}
|
|
127
|
+
for (const kind of ["fence", "code_block"]) {
|
|
128
|
+
const render = markdown.renderer.rules[kind];
|
|
129
|
+
markdown.renderer.rules[kind] = (tokens, index, options, env, self) => frameCode(render, tokens, index, options, env, self);
|
|
130
|
+
}
|
|
92
131
|
var defaultLinkOpen = markdown.renderer.rules.link_open;
|
|
93
132
|
markdown.renderer.rules.link_open = (tokens, index, options, env, self) => {
|
|
94
133
|
tokens[index]?.attrSet("target", "_blank");
|
|
@@ -96,8 +135,59 @@ markdown.renderer.rules.link_open = (tokens, index, options, env, self) => {
|
|
|
96
135
|
return defaultLinkOpen ? defaultLinkOpen(tokens, index, options, env, self) : self.renderToken(tokens, index, options);
|
|
97
136
|
};
|
|
98
137
|
|
|
99
|
-
// src/
|
|
138
|
+
// src/memory.js
|
|
139
|
+
var MEMORY_LIMIT = 100;
|
|
140
|
+
function boundedSet(map, key, value) {
|
|
141
|
+
map.delete(key);
|
|
142
|
+
map.set(key, value);
|
|
143
|
+
while (map.size > MEMORY_LIMIT) map.delete(map.keys().next().value);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// src/icon.jsx
|
|
100
147
|
import { Fragment, jsx as jsx2, jsxs } from "preact/jsx-runtime";
|
|
148
|
+
var ICONS = {
|
|
149
|
+
back: () => /* @__PURE__ */ jsx2("path", { d: "m11.5 4.5-4.5 4.5 4.5 4.5" }),
|
|
150
|
+
check: () => /* @__PURE__ */ jsx2("path", { d: "m4 9 3.25 3.25L14 5.5" }),
|
|
151
|
+
chevron: () => /* @__PURE__ */ jsx2("path", { d: "m7 4.5 4.5 4.5L7 13.5" }),
|
|
152
|
+
close: () => /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
153
|
+
/* @__PURE__ */ jsx2("path", { d: "m4.75 4.75 8.5 8.5" }),
|
|
154
|
+
/* @__PURE__ */ jsx2("path", { d: "m13.25 4.75-8.5 8.5" })
|
|
155
|
+
] }),
|
|
156
|
+
copy: () => /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
157
|
+
/* @__PURE__ */ jsx2("rect", { x: "5", y: "5", width: "8", height: "8", rx: "1.5" }),
|
|
158
|
+
/* @__PURE__ */ jsx2("path", { d: "M3 10.5V4.25C3 3.56 3.56 3 4.25 3h6.25" })
|
|
159
|
+
] }),
|
|
160
|
+
down: () => /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
161
|
+
/* @__PURE__ */ jsx2("path", { d: "M9 3.5v10" }),
|
|
162
|
+
/* @__PURE__ */ jsx2("path", { d: "m4.75 9.5 4.25 4 4.25-4" })
|
|
163
|
+
] }),
|
|
164
|
+
menu: () => /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
165
|
+
/* @__PURE__ */ jsx2("circle", { cx: "4", cy: "9", r: "1.25", fill: "currentColor", stroke: "none" }),
|
|
166
|
+
/* @__PURE__ */ jsx2("circle", { cx: "9", cy: "9", r: "1.25", fill: "currentColor", stroke: "none" }),
|
|
167
|
+
/* @__PURE__ */ jsx2("circle", { cx: "14", cy: "9", r: "1.25", fill: "currentColor", stroke: "none" })
|
|
168
|
+
] }),
|
|
169
|
+
plus: () => /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
170
|
+
/* @__PURE__ */ jsx2("path", { d: "M9 3.5v11" }),
|
|
171
|
+
/* @__PURE__ */ jsx2("path", { d: "M3.5 9h11" })
|
|
172
|
+
] }),
|
|
173
|
+
search: () => /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
174
|
+
/* @__PURE__ */ jsx2("circle", { cx: "7.75", cy: "7.75", r: "4.25" }),
|
|
175
|
+
/* @__PURE__ */ jsx2("path", { d: "m11 11 3.5 3.5" })
|
|
176
|
+
] }),
|
|
177
|
+
send: () => /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
178
|
+
/* @__PURE__ */ jsx2("path", { d: "M9 14.5v-11" }),
|
|
179
|
+
/* @__PURE__ */ jsx2("path", { d: "m4.75 7.75 4.25-4.25 4.25 4.25" })
|
|
180
|
+
] }),
|
|
181
|
+
stop: () => /* @__PURE__ */ jsx2("rect", { x: "4.5", y: "4.5", width: "9", height: "9", rx: "1.5", fill: "currentColor", stroke: "none" })
|
|
182
|
+
};
|
|
183
|
+
function UiIcon({ name, size = 16, class: className = "" }) {
|
|
184
|
+
const Glyph = ICONS[name];
|
|
185
|
+
if (!Glyph) return null;
|
|
186
|
+
return /* @__PURE__ */ jsx2("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__ */ jsx2(Glyph, {}) });
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// src/conversation.jsx
|
|
190
|
+
import { Fragment as Fragment3, jsx as jsx3, jsxs as jsxs2 } from "preact/jsx-runtime";
|
|
101
191
|
function LoadingStatus({ state, compact = false }) {
|
|
102
192
|
const copy = {
|
|
103
193
|
connecting: ["Connecting to coding agents", "Checking installed harnesses and capabilities.", 0],
|
|
@@ -105,19 +195,19 @@ function LoadingStatus({ state, compact = false }) {
|
|
|
105
195
|
discovering: ["Loading recent sessions", "Scanning native session stores without loading full transcripts.", 2],
|
|
106
196
|
ready: ["Ready", "Coding sessions are up to date.", 3]
|
|
107
197
|
}[state.startup];
|
|
108
|
-
return /* @__PURE__ */
|
|
109
|
-
/* @__PURE__ */
|
|
110
|
-
/* @__PURE__ */
|
|
111
|
-
/* @__PURE__ */
|
|
112
|
-
/* @__PURE__ */
|
|
198
|
+
return /* @__PURE__ */ jsxs2("div", { class: `scui-loading${compact ? " scui-loading-compact" : ""}`, role: "status", "aria-busy": state.startup !== "ready", children: [
|
|
199
|
+
/* @__PURE__ */ jsx3("span", { class: "scui-orbit", "aria-hidden": "true", children: /* @__PURE__ */ jsx3("i", {}) }),
|
|
200
|
+
/* @__PURE__ */ jsxs2("span", { class: "scui-loading-copy", children: [
|
|
201
|
+
/* @__PURE__ */ jsx3("strong", { children: copy[0] }),
|
|
202
|
+
/* @__PURE__ */ jsx3("small", { children: copy[1] })
|
|
113
203
|
] }),
|
|
114
|
-
/* @__PURE__ */
|
|
204
|
+
/* @__PURE__ */ jsx3("span", { class: "scui-progress", "aria-hidden": "true", children: [1, 2, 3].map((step) => /* @__PURE__ */ jsx3("i", { "data-progress": step <= copy[2] ? "done" : step === copy[2] + 1 ? "current" : "waiting" }, step)) })
|
|
115
205
|
] });
|
|
116
206
|
}
|
|
117
207
|
|
|
118
208
|
// src/logo.jsx
|
|
119
|
-
import { useEffect as
|
|
120
|
-
import { jsx as
|
|
209
|
+
import { useEffect as useEffect3 } from "preact/hooks";
|
|
210
|
+
import { jsx as jsx4, jsxs as jsxs3 } from "preact/jsx-runtime";
|
|
121
211
|
var LOGOS = {
|
|
122
212
|
"claude-code": {
|
|
123
213
|
viewBox: "-1 3.5 26 18",
|
|
@@ -164,62 +254,90 @@ var LOGOS = {
|
|
|
164
254
|
};
|
|
165
255
|
function HarnessLogo({ id, activity, size = 28, onMissingLogo }) {
|
|
166
256
|
const logo = LOGOS[id];
|
|
167
|
-
|
|
257
|
+
useEffect3(() => {
|
|
168
258
|
if (!logo) onMissingLogo?.(id);
|
|
169
259
|
}, [id, logo, onMissingLogo]);
|
|
170
260
|
if (!logo) return null;
|
|
171
|
-
return /* @__PURE__ */
|
|
172
|
-
/* @__PURE__ */
|
|
173
|
-
activity && activity !== "idle" ? /* @__PURE__ */
|
|
261
|
+
return /* @__PURE__ */ jsxs3("span", { class: "scui-logo", "data-harness": id, "data-activity": activity, style: `--scui-logo-size:${size}px`, "aria-hidden": "true", children: [
|
|
262
|
+
/* @__PURE__ */ jsx4("svg", { viewBox: logo.viewBox, preserveAspectRatio: "xMidYMid meet", focusable: "false", children: logo.paths.map(([Tag, props], index) => /* @__PURE__ */ jsx4(Tag, { ...props }, index)) }),
|
|
263
|
+
activity && activity !== "idle" ? /* @__PURE__ */ jsx4("i", {}) : null
|
|
174
264
|
] });
|
|
175
265
|
}
|
|
176
266
|
|
|
177
267
|
// src/sessions.jsx
|
|
178
|
-
import { jsx as
|
|
268
|
+
import { jsx as jsx5, jsxs as jsxs4 } from "preact/jsx-runtime";
|
|
269
|
+
var sessionListMemory = /* @__PURE__ */ new Map();
|
|
179
270
|
function SessionRow({ row, state, onOpen }) {
|
|
180
271
|
const activity = sessionActivity(state, row);
|
|
181
272
|
const preview = state.attention.find((item) => item.key === row.key)?.preview;
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
/* @__PURE__ */ jsx4("b", { children: harnessDisplayName(row.harness) }),
|
|
191
|
-
/* @__PURE__ */ jsx4("small", { children: preview || row.cwd || row.name })
|
|
273
|
+
const title = sessionDisplayName(row);
|
|
274
|
+
const detail = preview || (row.name && row.name !== title ? row.name : row.cwd);
|
|
275
|
+
return /* @__PURE__ */ jsxs4("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)}${row.age ? ` \xB7 ${row.age}` : ""}`, "aria-current": row.active ? "true" : void 0, onClick: () => onOpen(row), children: [
|
|
276
|
+
/* @__PURE__ */ jsx5(HarnessLogo, { id: row.harness, activity, size: 34 }),
|
|
277
|
+
/* @__PURE__ */ jsxs4("span", { class: "scui-session-copy", children: [
|
|
278
|
+
/* @__PURE__ */ jsxs4("span", { children: [
|
|
279
|
+
/* @__PURE__ */ jsx5("strong", { children: title }),
|
|
280
|
+
/* @__PURE__ */ jsx5("small", { children: row.age })
|
|
192
281
|
] }),
|
|
193
|
-
|
|
282
|
+
detail ? /* @__PURE__ */ jsx5("span", { children: /* @__PURE__ */ jsx5("small", { title: row.cwd, children: detail }) }) : null,
|
|
283
|
+
state.attachError?.key === row.key ? /* @__PURE__ */ jsx5("em", { children: state.attachError.message }) : null
|
|
194
284
|
] })
|
|
195
285
|
] });
|
|
196
286
|
}
|
|
197
|
-
function SessionList({ state, adapter, onOpen, onNew, onClose, components = {}, labels = DEFAULT_LABELS }) {
|
|
198
|
-
const
|
|
287
|
+
function SessionList({ state, adapter, onOpen, onNew, onClose, components = {}, labels = DEFAULT_LABELS, focusKey = null, memoryKey = state.workspace || "@default" }) {
|
|
288
|
+
const remembered = sessionListMemory.get(memoryKey) ?? { query: "", top: 0 };
|
|
289
|
+
const [query, setQuery] = useState2(remembered.query);
|
|
290
|
+
const [loadingMore, setLoadingMore] = useState2(false);
|
|
291
|
+
const root = useRef3(null);
|
|
292
|
+
const rowScroller = useRef3(null);
|
|
199
293
|
const rows = filterSessions(state.sessions, query);
|
|
200
294
|
const Row = components.SessionRow ?? SessionRow;
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
295
|
+
useLayoutEffect2(() => {
|
|
296
|
+
if (rowScroller.current) rowScroller.current.scrollTop = remembered.top;
|
|
297
|
+
}, [memoryKey]);
|
|
298
|
+
useEffect4(() => {
|
|
299
|
+
if (!focusKey || !root.current) return;
|
|
300
|
+
const target = focusKey === "@new" ? root.current.querySelector('[data-list-focus="new"]') : [...root.current.querySelectorAll("[data-session-key]")].find((element) => element.dataset.sessionKey === focusKey);
|
|
301
|
+
(target ?? root.current.querySelector("input,button"))?.focus({ preventScroll: true });
|
|
302
|
+
}, [focusKey, rows.length]);
|
|
303
|
+
useEffect4(() => {
|
|
304
|
+
if (loadingMore) setLoadingMore(false);
|
|
305
|
+
}, [state.error, state.history.hasMoreSessions, state.sessions.length]);
|
|
306
|
+
const loadMore = () => {
|
|
307
|
+
if (loadingMore) return;
|
|
308
|
+
setLoadingMore(true);
|
|
309
|
+
const result = adapter.onIntent({ action: "loadSessions" });
|
|
310
|
+
if (result && typeof result.then === "function") {
|
|
311
|
+
Promise.resolve(result).then(() => setLoadingMore(false), () => setLoadingMore(false));
|
|
312
|
+
}
|
|
313
|
+
};
|
|
314
|
+
return /* @__PURE__ */ jsxs4("section", { class: "scui-list", ref: root, children: [
|
|
315
|
+
/* @__PURE__ */ jsxs4("header", { class: "scui-head", children: [
|
|
316
|
+
/* @__PURE__ */ jsxs4("span", { class: "scui-head-copy", children: [
|
|
317
|
+
/* @__PURE__ */ jsx5("strong", { children: labels.chats }),
|
|
318
|
+
/* @__PURE__ */ jsxs4("small", { children: [
|
|
206
319
|
state.sessions.length,
|
|
207
320
|
" recent conversations"
|
|
208
321
|
] })
|
|
209
322
|
] }),
|
|
210
|
-
/* @__PURE__ */
|
|
211
|
-
onClose ? /* @__PURE__ */
|
|
323
|
+
/* @__PURE__ */ jsx5("button", { type: "button", "data-list-focus": "new", "aria-label": labels.newChat, disabled: !state.harnesses.some((item) => item.startable), onClick: onNew, children: /* @__PURE__ */ jsx5(UiIcon, { name: "plus", size: 18 }) }),
|
|
324
|
+
onClose ? /* @__PURE__ */ jsx5("button", { type: "button", "aria-label": "Close", onClick: onClose, children: /* @__PURE__ */ jsx5(UiIcon, { name: "close", size: 18 }) }) : null
|
|
212
325
|
] }),
|
|
213
|
-
state.startup !== "ready" ? /* @__PURE__ */
|
|
214
|
-
state.sessions.length > 4 ? /* @__PURE__ */
|
|
215
|
-
/* @__PURE__ */
|
|
216
|
-
/* @__PURE__ */
|
|
217
|
-
|
|
326
|
+
state.startup !== "ready" ? /* @__PURE__ */ jsx5(LoadingStatus, { state, compact: rows.length > 0 }) : null,
|
|
327
|
+
state.sessions.length > 4 ? /* @__PURE__ */ jsxs4("label", { class: "scui-search", children: [
|
|
328
|
+
/* @__PURE__ */ jsx5(UiIcon, { name: "search", size: 15 }),
|
|
329
|
+
/* @__PURE__ */ jsx5("input", { type: "search", "aria-label": labels.searchChats, placeholder: labels.searchChats, value: query, onInput: (event) => {
|
|
330
|
+
const value = event.currentTarget.value;
|
|
331
|
+
setQuery(value);
|
|
332
|
+
boundedSet(sessionListMemory, memoryKey, { query: value, top: 0 });
|
|
333
|
+
if (rowScroller.current) rowScroller.current.scrollTop = 0;
|
|
334
|
+
} }),
|
|
335
|
+
/* @__PURE__ */ jsx5("small", { children: rows.length })
|
|
218
336
|
] }) : null,
|
|
219
|
-
/* @__PURE__ */
|
|
220
|
-
!rows.length && state.startup === "ready" ? /* @__PURE__ */
|
|
221
|
-
rows.map((row) => /* @__PURE__ */
|
|
222
|
-
state.history.hasMoreSessions ? /* @__PURE__ */
|
|
337
|
+
/* @__PURE__ */ jsxs4("div", { class: "scui-session-rows", ref: rowScroller, onScroll: (event) => boundedSet(sessionListMemory, memoryKey, { query, top: event.currentTarget.scrollTop }), children: [
|
|
338
|
+
!rows.length && state.startup === "ready" ? /* @__PURE__ */ jsx5("div", { class: "scui-empty", children: query ? "No chats match your search." : state.error ?? "No coding chats found." }) : null,
|
|
339
|
+
rows.map((row) => /* @__PURE__ */ jsx5(Row, { value: row, row, state, adapter, onOpen }, row.key)),
|
|
340
|
+
state.history.hasMoreSessions ? /* @__PURE__ */ jsx5("button", { class: "scui-load", type: "button", disabled: loadingMore, onClick: loadMore, children: loadingMore ? "Loading older chats\u2026" : "Load older chats" }) : null
|
|
223
341
|
] })
|
|
224
342
|
] });
|
|
225
343
|
}
|
package/styles.css
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
--scui-positive: #22863a;
|
|
13
13
|
--scui-warning: #b26a00;
|
|
14
14
|
--scui-danger: #b4232e;
|
|
15
|
-
--scui-radius:
|
|
15
|
+
--scui-radius: 10px;
|
|
16
16
|
--scui-font: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
|
17
17
|
box-sizing: border-box;
|
|
18
18
|
display: flex;
|
|
@@ -46,22 +46,23 @@
|
|
|
46
46
|
|
|
47
47
|
:where(.scui-root *), :where(.scui-root *::before), :where(.scui-root *::after) { box-sizing: border-box }
|
|
48
48
|
:where(.scui-root button, .scui-root input, .scui-root textarea, .scui-root select) { font: inherit }
|
|
49
|
+
.scui-icon { display:block; flex:none; width:var(--scui-icon-size); height:var(--scui-icon-size) }
|
|
49
50
|
:where(.scui-root button) { color: inherit }
|
|
50
51
|
:where(.scui-list button, .scui-chat button, .scui-request button, .scui-activity button, .scui-compose button, .scui-continuation button) { color: inherit }
|
|
51
52
|
: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 }
|
|
53
|
+
.scui-sr-only { position:absolute; width:1px; height:1px; padding:0; overflow:hidden; clip:rect(0,0,0,0); white-space:nowrap; border:0 }
|
|
52
54
|
|
|
53
55
|
.scui-list,.scui-chat { display:flex; flex:1; min-width:0; min-height:0; flex-direction:column }
|
|
54
56
|
.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
57
|
.scui-head > .scui-head-copy { display:grid; flex:1; min-width:0 }
|
|
56
58
|
.scui-head strong { overflow:hidden; text-overflow:ellipsis; white-space:nowrap; font-size:13px }
|
|
57
59
|
.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
|
|
59
|
-
.scui-head > button:hover,.scui-menu
|
|
60
|
+
.scui-head > button,.scui-menu-trigger { display:grid; place-items:center; width:30px; height:30px; padding:0; border:0; border-radius:8px; background:transparent; color:inherit; cursor:pointer; font-size:18px }
|
|
61
|
+
.scui-head > button:hover,.scui-menu-trigger:hover,.scui-menu-trigger[aria-expanded="true"] { background:var(--scui-fill) }
|
|
60
62
|
.scui-menu { position:relative }
|
|
61
|
-
.scui-menu
|
|
62
|
-
.scui-menu
|
|
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) }
|
|
63
|
+
.scui-menu-panel { 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) }
|
|
64
|
+
.scui-menu-panel section { display:grid; gap:1px }.scui-menu-panel section + section { margin-top:4px; padding-top:4px; border-top:1px solid var(--scui-border) }.scui-menu-panel section > strong { padding:4px 8px 2px; color:var(--scui-muted); font-size:9px; font-weight:650; letter-spacing:.04em; text-transform:uppercase }
|
|
65
|
+
.scui-menu-panel button { padding:7px 8px; border:0; border-radius:6px; background:transparent; color:inherit; text-align:left; cursor:pointer }.scui-menu-panel button:hover,.scui-menu-panel button:focus-visible { outline:none; background:var(--scui-fill) }.scui-menu-panel button:disabled { opacity:.45; cursor:default }
|
|
65
66
|
|
|
66
67
|
.scui-logo { position:relative; display:inline-grid; place-items:center; box-sizing:border-box; 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
68
|
.scui-logo svg { display:block; width:70%; height:70%; overflow:visible; fill:currentColor }
|
|
@@ -91,16 +92,15 @@
|
|
|
91
92
|
.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) }
|
|
92
93
|
.scui-search input { flex:1; min-width:0; border:0; outline:0; background:transparent; color:var(--scui-fg) }
|
|
93
94
|
.scui-session-rows { flex:1; min-height:0; overflow:auto; padding:0 6px 8px }
|
|
94
|
-
.scui-session { display:flex; align-items:center; gap:9px; width:100%; padding:7px 8px; border:0; border-radius:
|
|
95
|
+
.scui-session { display:flex; align-items:center; gap:9px; width:100%; padding:7px 8px; border:0; border-radius:7px; background:transparent; text-align:left; cursor:pointer }
|
|
95
96
|
.scui-session:hover,.scui-session[data-active="true"] { background:var(--scui-fill) }
|
|
96
97
|
.scui-session-copy { display:grid; flex:1; min-width:0; gap:2px }
|
|
97
98
|
.scui-session-copy > span { display:flex; align-items:baseline; gap:7px; min-width:0 }
|
|
98
99
|
.scui-session-copy strong,.scui-session-copy span > small { overflow:hidden; text-overflow:ellipsis; white-space:nowrap }
|
|
99
100
|
.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 }
|
|
100
|
-
.scui-session-copy
|
|
101
|
-
.scui-session-copy span:last-child > small { color:var(--scui-muted); font-size:10.5px }
|
|
101
|
+
.scui-session-copy span:last-child > small { min-width:0; overflow:hidden; color:var(--scui-muted); font-size:10.5px; text-overflow:ellipsis; white-space:nowrap }
|
|
102
102
|
.scui-session-copy em { overflow:hidden; color:var(--scui-danger); font-size:10px; font-style:normal; text-overflow:ellipsis; white-space:nowrap }
|
|
103
|
-
.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 }
|
|
103
|
+
.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 }.scui-load:disabled { opacity:.65; cursor:default }
|
|
104
104
|
|
|
105
105
|
.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 }
|
|
106
106
|
.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 }
|
|
@@ -112,15 +112,26 @@
|
|
|
112
112
|
.scui-conversation { flex:1; min-width:0; overflow:auto; overscroll-behavior:contain; padding:12px 16px 18px }
|
|
113
113
|
.scui-conversation > div { display:flex; flex-direction:column; gap:10px }
|
|
114
114
|
.scui-empty { margin:auto; padding:20px; color:var(--scui-muted); text-align:center }
|
|
115
|
-
.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 }
|
|
116
|
-
.scui-message { position:relative; overflow-wrap:anywhere }
|
|
115
|
+
.scui-latest { position:absolute; z-index:5; right:12px; bottom:8px; display:flex; align-items:center; gap:3px; 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 }
|
|
116
|
+
.scui-message { position:relative; min-width:0; max-width:100%; overflow-wrap:anywhere }
|
|
117
117
|
.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) }
|
|
118
|
+
.scui-message-meta { position:absolute; z-index:2; top:-10px; right:0; display:flex; align-items:center; gap:3px; min-height:20px; padding:1px 3px 1px 6px; border:1px solid var(--scui-border); border-radius:7px; background:var(--scui-bg-raised); box-shadow:0 2px 7px rgba(0,0,0,.14); color:var(--scui-muted); opacity:0; pointer-events:none; transition:opacity 100ms }
|
|
119
|
+
.scui-message:hover > .scui-message-meta,.scui-message:focus-within > .scui-message-meta { opacity:1; pointer-events:auto }.scui-message-meta time { font:9px ui-monospace,SFMono-Regular,Menlo,monospace; white-space:nowrap }.scui-message-meta button { display:grid; width:20px; height:20px; padding:3px; place-items:center; border:0; border-radius:5px; background:transparent; color:inherit; cursor:pointer }.scui-message-meta button:hover,.scui-message-meta button:focus-visible { background:var(--scui-fill); color:var(--scui-fg) }.scui-message-meta button[data-status="copied"] { color:var(--scui-success) }.scui-message-meta button[data-status="failed"] { color:var(--scui-danger) }.scui-message-meta svg { width:13px; height:13px; fill:none; stroke:currentColor; stroke-width:1.25; stroke-linecap:round; stroke-linejoin:round }
|
|
120
|
+
@media (hover:none) { .scui-message-meta { opacity:.82; pointer-events:auto } }
|
|
121
|
+
.scui-pending[data-status="failed"] { border-color:color-mix(in srgb,var(--scui-danger) 45%,var(--scui-border)); background:color-mix(in srgb,var(--scui-danger) 7%,var(--scui-fill)); opacity:1 }
|
|
122
|
+
.scui-pending > footer { display:flex; align-items:center; gap:8px; margin-top:5px }.scui-pending > footer small { color:var(--scui-muted) }.scui-pending[data-status="failed"] > footer small { color:var(--scui-danger) }
|
|
123
|
+
.scui-pending > footer > span { display:flex; gap:4px; margin-left:auto }.scui-pending > footer button { padding:2px 6px; border:1px solid var(--scui-border-strong); border-radius:5px; background:transparent; color:var(--scui-fg); cursor:pointer; font:inherit; font-size:9.5px }
|
|
118
124
|
.scui-pending { opacity:.7 }.scui-pending > small { display:block; margin-top:3px; color:var(--scui-muted); font-size:9.5px }
|
|
119
|
-
.scui-markdown { font-size:12.5px; line-height:1.55 }.scui-markdown > :first-child { margin-top:0 }.scui-markdown > :last-child { margin-bottom:0 }
|
|
125
|
+
.scui-markdown { min-width:0; font-size:12.5px; line-height:1.55 }.scui-markdown > :first-child { margin-top:0 }.scui-markdown > :last-child { margin-bottom:0 }
|
|
120
126
|
.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 }
|
|
121
127
|
.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) }
|
|
122
128
|
.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 }
|
|
123
|
-
.scui-
|
|
129
|
+
.scui-code-block { min-width:0; margin:8px 0; overflow:hidden; border:1px solid var(--scui-border); border-radius:8px; background:var(--scui-bg-raised) }
|
|
130
|
+
.scui-code-head { display:flex; align-items:center; min-height:28px; padding:0 4px 0 9px; border-bottom:1px solid var(--scui-border); color:var(--scui-muted); font-size:9.5px; font-weight:600; letter-spacing:.025em }
|
|
131
|
+
.scui-code-head > span { flex:1; min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap }
|
|
132
|
+
.scui-code-copy { display:flex; align-items:center; gap:4px; min-width:28px; min-height:24px; padding:0 5px; border:0; border-radius:5px; background:transparent; color:var(--scui-muted); cursor:pointer; font-size:9.5px }.scui-code-copy:hover,.scui-code-copy:focus-visible { background:var(--scui-fill); color:var(--scui-fg) }.scui-code-copy:disabled { cursor:wait }.scui-code-copy[data-status="copied"] { color:var(--scui-positive) }.scui-code-copy[data-status="failed"] { color:var(--scui-danger) }.scui-code-copy svg { width:13px; height:13px; flex:none }
|
|
133
|
+
.scui-markdown[data-copyable="false"] .scui-code-copy { display:none }
|
|
134
|
+
.scui-markdown pre { max-width:100%; margin:0; padding:9px 10px; overflow:auto; overscroll-behavior:contain; scrollbar-gutter:stable; background:transparent }.scui-markdown pre code { padding:0; background:transparent; overflow-wrap:normal; word-break:normal; white-space:pre }
|
|
124
135
|
.scui-markdown table { display:block; overflow:auto; border-collapse:collapse }.scui-markdown th,.scui-markdown td { padding:4px; border:1px solid var(--scui-border) }
|
|
125
136
|
.scui-context,.scui-reasoning,.scui-details,.scui-plan { color:var(--scui-muted); font-size:10.5px }
|
|
126
137
|
.scui-context summary,.scui-reasoning summary,.scui-details summary,.scui-plan summary { cursor:pointer }
|
|
@@ -128,11 +139,12 @@
|
|
|
128
139
|
.scui-context p,.scui-details p { display:grid; margin:0 }.scui-context strong,.scui-details strong { color:var(--scui-fg) }
|
|
129
140
|
.scui-truncated { display:block; margin-top:5px; color:var(--scui-warning) }
|
|
130
141
|
.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 }
|
|
142
|
+
.scui-unread-divider { display:flex; align-items:center; gap:7px; color:var(--scui-accent); font-size:9.5px; font-weight:650 }.scui-unread-divider::before,.scui-unread-divider::after { content:""; flex:1; height:1px; background:color-mix(in srgb,var(--scui-accent) 45%,var(--scui-border)) }.scui-unread-divider span { flex:none }
|
|
131
143
|
|
|
132
144
|
.scui-activity { min-width:0; color:var(--scui-muted) }
|
|
133
145
|
.scui-activity-head,.scui-tool-head { display:flex; align-items:center; gap:6px; box-sizing:border-box; width:100%; min-height:27px; padding:0 3px; border:0; background:transparent; color:inherit; text-align:left }
|
|
134
146
|
.scui-activity-head { cursor:pointer }.scui-activity-head strong { color:var(--scui-muted); font-size:10.5px; font-weight:600 }.scui-activity-head small { color:var(--scui-muted) }.scui-spacer { flex:1 }
|
|
135
|
-
.scui-fold {
|
|
147
|
+
.scui-fold { display:grid; flex:none; place-items:center; transition:transform 120ms }.scui-fold[data-open="true"] { transform:rotate(90deg) }
|
|
136
148
|
.scui-activity:not([data-single="true"]) > div { margin-left:9px; padding-left:7px; border-left:1px solid var(--scui-border) }
|
|
137
149
|
.scui-tool { min-width:0 }.scui-tool summary { list-style:none }.scui-tool summary::-webkit-details-marker { display:none }.scui-tool-head { cursor:default }.scui-tool > summary.scui-tool-head { cursor:pointer }.scui-tool-icon { flex:0 0 14px; width:14px; height:14px; color:var(--scui-muted) }.scui-tool-head strong { flex:none; color:var(--scui-fg); font-size:10.5px; font-weight:600; white-space:nowrap }
|
|
138
150
|
.scui-tool-target { min-width:0; overflow:hidden; padding:0; background:transparent; color:var(--scui-muted); font:10px ui-monospace,SFMono-Regular,Menlo,monospace; text-overflow:ellipsis; white-space:nowrap }
|
|
@@ -157,8 +169,8 @@
|
|
|
157
169
|
|
|
158
170
|
.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 }
|
|
159
171
|
.scui-compose { flex:none; padding:8px; border-top:1px solid var(--scui-border); background:var(--scui-bg-raised) }
|
|
160
|
-
.scui-envelope { display:flex; align-items:flex-end; gap:7px; padding:7px; border:1px solid var(--scui-border-strong); border-radius:
|
|
161
|
-
.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 }
|
|
172
|
+
.scui-envelope { display:flex; align-items:flex-end; gap:7px; padding:7px; border:1px solid var(--scui-border-strong); border-radius:16px; background:var(--scui-bg) }.scui-envelope textarea { flex:1; min-width:0; min-height:34px; max-height:150px; resize:none; overflow-y:hidden; border:0; outline:0; background:transparent; color:var(--scui-fg) }.scui-envelope > span { display:flex; gap:4px }
|
|
173
|
+
.scui-send,.scui-stop { display:grid; width:29px; height:29px; padding:0; place-items:center; 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 }.scui-control-spinner { box-sizing:border-box; width:13px; height:13px; border:1.5px solid currentColor; border-right-color:transparent; border-radius:50%; animation:scui-spin .75s linear infinite }
|
|
162
174
|
.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 }
|
|
163
175
|
.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) }
|
|
164
176
|
.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) }
|
|
@@ -169,3 +181,11 @@
|
|
|
169
181
|
@keyframes scui-progress { from { transform:translateX(-100%) } to { transform:translateX(180%) } }
|
|
170
182
|
@keyframes scui-dots { 50% { transform:translateY(-3px); opacity:.45 } }
|
|
171
183
|
@media (prefers-reduced-motion:reduce) { .scui-root * { animation-duration:.001ms !important; animation-iteration-count:1 !important; transition:none !important } }
|
|
184
|
+
@media (pointer:coarse) {
|
|
185
|
+
.scui-head > button,.scui-menu-trigger { width:40px; height:40px }
|
|
186
|
+
.scui-head { min-height:52px }
|
|
187
|
+
.scui-menu-panel button,.scui-request-actions button,.scui-load { min-height:40px }
|
|
188
|
+
.scui-send,.scui-stop { width:40px; height:40px }
|
|
189
|
+
.scui-message-meta button,.scui-code-copy { min-width:36px; min-height:36px }
|
|
190
|
+
.scui-latest { min-height:36px; padding-inline:12px }
|
|
191
|
+
}
|