@volter-ai-dev/supercode-ui 0.1.18 → 0.1.21

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.18",
3
+ "version": "0.1.21",
4
4
  "type": "module",
5
5
  "description": "Composable default UI kit for Supercode-powered coding-agent experiences",
6
6
  "exports": {
package/sessions.mjs CHANGED
@@ -78,7 +78,7 @@ function sessionActivity(state, row) {
78
78
  function filterSessions(rows, query) {
79
79
  const needle = query.trim().toLocaleLowerCase();
80
80
  if (!needle) return [...rows];
81
- return rows.filter((row) => [row.name, row.title, row.cwd, row.harness, harnessDisplayName(row.harness)].some((value) => value.toLocaleLowerCase().includes(needle)));
81
+ return rows.filter((row) => [row.name, row.title, row.preview, row.cwd, row.harness, harnessDisplayName(row.harness)].some((value) => typeof value === "string" && value.toLocaleLowerCase().includes(needle)));
82
82
  }
83
83
 
84
84
  // src/conversation.jsx
@@ -146,6 +146,7 @@ function boundedSet(map, key, value) {
146
146
  // src/icon.jsx
147
147
  import { Fragment, jsx as jsx2, jsxs } from "preact/jsx-runtime";
148
148
  var ICONS = {
149
+ attach: () => /* @__PURE__ */ jsx2("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" }),
149
150
  back: () => /* @__PURE__ */ jsx2("path", { d: "m11.5 4.5-4.5 4.5 4.5 4.5" }),
150
151
  check: () => /* @__PURE__ */ jsx2("path", { d: "m4 9 3.25 3.25L14 5.5" }),
151
152
  chevron: () => /* @__PURE__ */ jsx2("path", { d: "m7 4.5 4.5 4.5L7 13.5" }),
@@ -161,6 +162,11 @@ var ICONS = {
161
162
  /* @__PURE__ */ jsx2("path", { d: "M9 3.5v10" }),
162
163
  /* @__PURE__ */ jsx2("path", { d: "m4.75 9.5 4.25 4 4.25-4" })
163
164
  ] }),
165
+ image: () => /* @__PURE__ */ jsxs(Fragment, { children: [
166
+ /* @__PURE__ */ jsx2("rect", { x: "3", y: "3.5", width: "12", height: "11", rx: "1.75" }),
167
+ /* @__PURE__ */ jsx2("circle", { cx: "6.5", cy: "7", r: "1.25" }),
168
+ /* @__PURE__ */ jsx2("path", { d: "m4.5 13 3.25-3 2.1 1.85 1.65-1.5L14 13" })
169
+ ] }),
164
170
  menu: () => /* @__PURE__ */ jsxs(Fragment, { children: [
165
171
  /* @__PURE__ */ jsx2("circle", { cx: "4", cy: "9", r: "1.25", fill: "currentColor", stroke: "none" }),
166
172
  /* @__PURE__ */ jsx2("circle", { cx: "9", cy: "9", r: "1.25", fill: "currentColor", stroke: "none" }),
@@ -186,8 +192,12 @@ function UiIcon({ name, size = 16, class: className = "" }) {
186
192
  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
193
  }
188
194
 
195
+ // src/context.jsx
196
+ import { jsx as jsx3, jsxs as jsxs2 } from "preact/jsx-runtime";
197
+ var MAX_IMAGE_BYTES = 5 * 1024 * 1024;
198
+
189
199
  // src/conversation.jsx
190
- import { Fragment as Fragment3, jsx as jsx3, jsxs as jsxs2 } from "preact/jsx-runtime";
200
+ import { Fragment as Fragment3, jsx as jsx4, jsxs as jsxs3 } from "preact/jsx-runtime";
191
201
  function LoadingStatus({ state, compact = false }) {
192
202
  const copy = {
193
203
  connecting: ["Connecting to coding agents", "Checking installed harnesses and capabilities.", 0],
@@ -195,19 +205,19 @@ function LoadingStatus({ state, compact = false }) {
195
205
  discovering: ["Loading recent sessions", "Scanning native session stores without loading full transcripts.", 2],
196
206
  ready: ["Ready", "Coding sessions are up to date.", 3]
197
207
  }[state.startup];
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] })
208
+ return /* @__PURE__ */ jsxs3("div", { class: `scui-loading${compact ? " scui-loading-compact" : ""}`, role: "status", "aria-busy": state.startup !== "ready", children: [
209
+ /* @__PURE__ */ jsx4("span", { class: "scui-orbit", "aria-hidden": "true", children: /* @__PURE__ */ jsx4("i", {}) }),
210
+ /* @__PURE__ */ jsxs3("span", { class: "scui-loading-copy", children: [
211
+ /* @__PURE__ */ jsx4("strong", { children: copy[0] }),
212
+ /* @__PURE__ */ jsx4("small", { children: copy[1] })
203
213
  ] }),
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)) })
214
+ /* @__PURE__ */ jsx4("span", { class: "scui-progress", "aria-hidden": "true", children: [1, 2, 3].map((step) => /* @__PURE__ */ jsx4("i", { "data-progress": step <= copy[2] ? "done" : step === copy[2] + 1 ? "current" : "waiting" }, step)) })
205
215
  ] });
206
216
  }
207
217
 
208
218
  // src/logo.jsx
209
219
  import { useEffect as useEffect3 } from "preact/hooks";
210
- import { jsx as jsx4, jsxs as jsxs3 } from "preact/jsx-runtime";
220
+ import { jsx as jsx5, jsxs as jsxs4 } from "preact/jsx-runtime";
211
221
  var LOGOS = {
212
222
  "claude-code": {
213
223
  viewBox: "-1 3.5 26 18",
@@ -258,29 +268,29 @@ function HarnessLogo({ id, activity, size = 28, onMissingLogo }) {
258
268
  if (!logo) onMissingLogo?.(id);
259
269
  }, [id, logo, onMissingLogo]);
260
270
  if (!logo) return null;
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
271
+ return /* @__PURE__ */ jsxs4("span", { class: "scui-logo", "data-harness": id, "data-activity": activity, style: `--scui-logo-size:${size}px`, "aria-hidden": "true", children: [
272
+ /* @__PURE__ */ jsx5("svg", { viewBox: logo.viewBox, preserveAspectRatio: "xMidYMid meet", focusable: "false", children: logo.paths.map(([Tag, props], index) => /* @__PURE__ */ jsx5(Tag, { ...props }, index)) }),
273
+ activity && activity !== "idle" ? /* @__PURE__ */ jsx5("i", {}) : null
264
274
  ] });
265
275
  }
266
276
 
267
277
  // src/sessions.jsx
268
- import { jsx as jsx5, jsxs as jsxs4 } from "preact/jsx-runtime";
278
+ import { jsx as jsx6, jsxs as jsxs5 } from "preact/jsx-runtime";
269
279
  var sessionListMemory = /* @__PURE__ */ new Map();
270
280
  function SessionRow({ row, state, onOpen }) {
271
281
  const activity = sessionActivity(state, row);
272
282
  const attentionPreview = state.attention.find((item) => item.key === row.key)?.preview;
273
283
  const title = sessionDisplayName(row);
274
284
  const detail = attentionPreview || row.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 })
285
+ 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)}${detail ? ` \xB7 ${detail}` : ""}${row.age ? ` \xB7 ${row.age}` : ""}`, "aria-current": row.active ? "true" : void 0, onClick: () => onOpen(row), children: [
286
+ /* @__PURE__ */ jsx6(HarnessLogo, { id: row.harness, activity, size: 34 }),
287
+ /* @__PURE__ */ jsxs5("span", { class: "scui-session-copy", children: [
288
+ /* @__PURE__ */ jsxs5("span", { children: [
289
+ /* @__PURE__ */ jsx6("strong", { children: title }),
290
+ /* @__PURE__ */ jsx6("small", { children: row.age })
281
291
  ] }),
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
292
+ detail ? /* @__PURE__ */ jsx6("span", { children: /* @__PURE__ */ jsx6("small", { title: row.cwd, children: detail }) }) : null,
293
+ state.attachError?.key === row.key ? /* @__PURE__ */ jsx6("em", { children: state.attachError.message }) : null
284
294
  ] })
285
295
  ] });
286
296
  }
@@ -311,33 +321,33 @@ function SessionList({ state, adapter, onOpen, onNew, onClose, components = {},
311
321
  Promise.resolve(result).then(() => setLoadingMore(false), () => setLoadingMore(false));
312
322
  }
313
323
  };
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: [
324
+ return /* @__PURE__ */ jsxs5("section", { class: "scui-list", ref: root, children: [
325
+ /* @__PURE__ */ jsxs5("header", { class: "scui-head", children: [
326
+ /* @__PURE__ */ jsxs5("span", { class: "scui-head-copy", children: [
327
+ /* @__PURE__ */ jsx6("strong", { children: labels.chats }),
328
+ /* @__PURE__ */ jsxs5("small", { children: [
319
329
  state.sessions.length,
320
330
  " recent conversations"
321
331
  ] })
322
332
  ] }),
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
333
+ /* @__PURE__ */ jsx6("button", { type: "button", "data-list-focus": "new", "aria-label": labels.newChat, disabled: !state.harnesses.some((item) => item.startable), onClick: onNew, children: /* @__PURE__ */ jsx6(UiIcon, { name: "plus", size: 18 }) }),
334
+ onClose ? /* @__PURE__ */ jsx6("button", { type: "button", "aria-label": "Close", onClick: onClose, children: /* @__PURE__ */ jsx6(UiIcon, { name: "close", size: 18 }) }) : null
325
335
  ] }),
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) => {
336
+ state.startup !== "ready" ? /* @__PURE__ */ jsx6(LoadingStatus, { state, compact: rows.length > 0 }) : null,
337
+ state.sessions.length > 4 ? /* @__PURE__ */ jsxs5("label", { class: "scui-search", children: [
338
+ /* @__PURE__ */ jsx6(UiIcon, { name: "search", size: 15 }),
339
+ /* @__PURE__ */ jsx6("input", { type: "search", "aria-label": labels.searchChats, placeholder: labels.searchChats, value: query, onInput: (event) => {
330
340
  const value = event.currentTarget.value;
331
341
  setQuery(value);
332
342
  boundedSet(sessionListMemory, memoryKey, { query: value, top: 0 });
333
343
  if (rowScroller.current) rowScroller.current.scrollTop = 0;
334
344
  } }),
335
- /* @__PURE__ */ jsx5("small", { children: rows.length })
345
+ /* @__PURE__ */ jsx6("small", { children: rows.length })
336
346
  ] }) : null,
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
347
+ /* @__PURE__ */ jsxs5("div", { class: "scui-session-rows", ref: rowScroller, onScroll: (event) => boundedSet(sessionListMemory, memoryKey, { query, top: event.currentTarget.scrollTop }), children: [
348
+ !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,
349
+ rows.map((row) => /* @__PURE__ */ jsx6(Row, { value: row, row, state, adapter, onOpen }, row.key)),
350
+ 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
341
351
  ] })
342
352
  ] });
343
353
  }
package/styles.css CHANGED
@@ -171,8 +171,12 @@
171
171
  .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 }
172
172
  .scui-compose { flex:none; padding:8px; border-top:1px solid var(--scui-border); background:var(--scui-bg-raised) }
173
173
  .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 }
174
+ .scui-attach { display:grid; flex:0 0 34px; width:34px; height:34px; place-items:center; border:0; border-radius:50%; background:transparent; color:var(--scui-muted) }.scui-attach:hover:not(:disabled) { background:var(--scui-fill); color:var(--scui-fg) }
175
+ .scui-compose-context { display:flex; gap:5px; margin-bottom:6px; overflow-x:auto; scrollbar-width:thin }.scui-compose-context > span { display:flex; flex:0 0 auto; max-width:210px; align-items:center; gap:5px; padding:4px 5px 4px 7px; border:1px solid var(--scui-border); border-radius:999px; background:var(--scui-fill); font-size:10px }.scui-compose-context strong { overflow:hidden; text-overflow:ellipsis; white-space:nowrap }.scui-compose-context button { display:grid; width:18px; height:18px; padding:0; place-items:center; border:0; border-radius:50%; background:transparent }.scui-compose-context button:hover { background:color-mix(in srgb,var(--scui-fg) 9%,transparent) }.scui-context-error { display:block; margin:0 2px 6px; color:var(--scui-danger) }
176
+ .scui-compose-images { display:flex; gap:6px; margin-bottom:6px; overflow-x:auto; scrollbar-width:thin }.scui-compose-images > span { position:relative; display:grid; flex:0 0 58px; width:58px; height:58px; overflow:hidden; border:1px solid var(--scui-border); border-radius:8px; background:var(--scui-fill); place-items:center }.scui-compose-images img { width:100%; height:100%; object-fit:cover }.scui-compose-images strong { position:absolute; right:2px; bottom:2px; left:2px; overflow:hidden; padding:2px 3px; border-radius:4px; background:rgba(0,0,0,.65); color:#fff; font-size:8px; font-weight:500; text-overflow:ellipsis; white-space:nowrap }.scui-compose-images button { position:absolute; z-index:1; top:2px; right:2px; display:grid; width:19px; height:19px; padding:0; border:1px solid rgba(255,255,255,.32); border-radius:50%; background:rgba(0,0,0,.68); color:#fff; place-items:center; cursor:pointer }
177
+ .scui-message-images { display:flex; max-width:280px; gap:5px; margin-bottom:6px; flex-wrap:wrap }.scui-message-images img { display:block; width:auto; min-width:72px; max-width:100%; height:auto; max-height:210px; border:1px solid var(--scui-border); border-radius:8px; object-fit:contain; background:var(--scui-fill) }.scui-message-images > span { display:flex; align-items:center; gap:5px; padding:5px 7px; border:1px solid var(--scui-border); border-radius:7px; color:var(--scui-muted); font-size:10px }
174
178
  .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 }
175
- .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 }
179
+ .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 > span { display:grid; min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap }.scui-queue small { color:var(--scui-muted) }.scui-queue > span button { margin-left:auto; border:0; background:transparent }
176
180
  .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) }
177
181
  .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) }
178
182
  .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 }