@volter-ai-dev/supercode-ui 0.1.6 → 0.1.8

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/components.mjs CHANGED
@@ -329,9 +329,12 @@ function compactToolTarget(target, workspace) {
329
329
  function activitySummary(entries) {
330
330
  const counts = /* @__PURE__ */ new Map();
331
331
  for (const entry of entries) counts.set(toolCategory(entry), (counts.get(toolCategory(entry)) ?? 0) + 1);
332
+ const failed = entries.filter((entry) => entry.status === "error").length;
333
+ const pending = entries.filter((entry) => entry.status === "pending").length;
334
+ if (pending) return `${entries.length} actions in progress`;
335
+ if (failed) return `${entries.length} actions \xB7 ${failed} failed`;
332
336
  if ([...counts.keys()].every((key) => key === "read" || key === "search")) return `Explored ${entries.length} ${entries.length === 1 ? "item" : "items"}`;
333
- const labels = { edit: "changed", test: "tests/builds", command: "commands", read: "reads", search: "searches", web: "web", agent: "agents", other: "other" };
334
- return `Activity \xB7 ${Object.entries(labels).flatMap(([key, label]) => counts.has(key) ? [`${counts.get(key)} ${label}`] : []).join(" \xB7 ")}`;
337
+ return `${entries.length} actions`;
335
338
  }
336
339
  function canContinueHere(state) {
337
340
  if (state.mode !== "mirror" || state.canSend) return false;
@@ -467,7 +470,7 @@ function Markdown({ value }) {
467
470
  }
468
471
 
469
472
  // src/conversation.jsx
470
- import { jsx as jsx3, jsxs as jsxs2 } from "preact/jsx-runtime";
473
+ import { Fragment, jsx as jsx3, jsxs as jsxs2 } from "preact/jsx-runtime";
471
474
  function LoadingStatus({ state, compact = false }) {
472
475
  const copy = {
473
476
  connecting: ["Connecting to coding agents", "Checking installed harnesses and capabilities.", 0],
@@ -515,6 +518,76 @@ function ContextDisclosure({ context }) {
515
518
  ] }, item.id ?? index)) })
516
519
  ] });
517
520
  }
521
+ var TOOL_ICONS = {
522
+ read: () => /* @__PURE__ */ jsxs2(Fragment, { children: [
523
+ /* @__PURE__ */ jsx3("path", { d: "M5 2.75h7.25L15 5.5v7.75A1.75 1.75 0 0 1 13.25 15h-8.5A1.75 1.75 0 0 1 3 13.25v-8.5A2 2 0 0 1 5 2.75Z" }),
524
+ /* @__PURE__ */ jsx3("path", { d: "M12 2.9v3h2.85M6 9h6M6 12h4" })
525
+ ] }),
526
+ search: () => /* @__PURE__ */ jsxs2(Fragment, { children: [
527
+ /* @__PURE__ */ jsx3("circle", { cx: "8", cy: "8", r: "4.5" }),
528
+ /* @__PURE__ */ jsx3("path", { d: "m11.5 11.5 3 3" })
529
+ ] }),
530
+ edit: () => /* @__PURE__ */ jsxs2(Fragment, { children: [
531
+ /* @__PURE__ */ jsx3("path", { d: "m11.75 3.25 3 3-8.5 8.5-3.75.75.75-3.75 8.5-8.5Z" }),
532
+ /* @__PURE__ */ jsx3("path", { d: "m10 5 3 3" })
533
+ ] }),
534
+ command: () => /* @__PURE__ */ jsx3(Fragment, { children: /* @__PURE__ */ jsx3("path", { d: "m3 5 3 3-3 3M8 12h6" }) }),
535
+ test: () => /* @__PURE__ */ jsxs2(Fragment, { children: [
536
+ /* @__PURE__ */ jsx3("path", { d: "M6 2.5v3L3 12a2 2 0 0 0 1.8 3h8.4a2 2 0 0 0 1.8-3l-3-6.5v-3M5 9h8" }),
537
+ /* @__PURE__ */ jsx3("path", { d: "M5 2.5h8" })
538
+ ] }),
539
+ web: () => /* @__PURE__ */ jsxs2(Fragment, { children: [
540
+ /* @__PURE__ */ jsx3("circle", { cx: "9", cy: "9", r: "6.5" }),
541
+ /* @__PURE__ */ jsx3("path", { d: "M2.75 9h12.5M9 2.5c2 1.8 3 4 3 6.5s-1 4.7-3 6.5c-2-1.8-3-4-3-6.5s1-4.7 3-6.5Z" })
542
+ ] }),
543
+ agent: () => /* @__PURE__ */ jsxs2(Fragment, { children: [
544
+ /* @__PURE__ */ jsx3("circle", { cx: "9", cy: "6", r: "2.5" }),
545
+ /* @__PURE__ */ jsx3("path", { d: "M4 15c.4-3 2-4.5 5-4.5s4.6 1.5 5 4.5" })
546
+ ] }),
547
+ other: () => /* @__PURE__ */ jsxs2(Fragment, { children: [
548
+ /* @__PURE__ */ jsx3("path", { d: "M9 2.5v3M9 12.5v3M2.5 9h3M12.5 9h3" }),
549
+ /* @__PURE__ */ jsx3("circle", { cx: "9", cy: "9", r: "3.5" })
550
+ ] })
551
+ };
552
+ function ToolIcon({ category }) {
553
+ const Glyph = TOOL_ICONS[category] ?? TOOL_ICONS.other;
554
+ return /* @__PURE__ */ jsx3("svg", { class: "scui-tool-icon", viewBox: "0 0 18 18", fill: "none", stroke: "currentColor", "stroke-width": "1.35", "stroke-linecap": "round", "stroke-linejoin": "round", "aria-hidden": "true", children: /* @__PURE__ */ jsx3(Glyph, {}) });
555
+ }
556
+ function toolAction(entry, category) {
557
+ const status = entry.status ?? "completed";
558
+ const actions = {
559
+ read: ["Reading", "Read", "Read failed"],
560
+ search: ["Searching", "Searched", "Search failed"],
561
+ edit: ["Editing", "Edited", "Edit failed"],
562
+ command: ["Running command", "Ran command", "Command failed"],
563
+ test: ["Running tests", "Ran tests", "Tests failed"],
564
+ web: ["Browsing", "Browsed", "Browser action failed"],
565
+ agent: ["Starting agent", "Started agent", "Agent failed"]
566
+ };
567
+ const position = status === "pending" ? 0 : status === "error" ? 2 : 1;
568
+ if (actions[category]) return actions[category][position];
569
+ const label = entry.label?.split(/__|\//).at(-1)?.replaceAll(/[_-]+/g, " ") || "Tool";
570
+ return status === "error" ? `${label} failed` : label;
571
+ }
572
+ function argumentLabel(key) {
573
+ const labels = { cmd: "Command", command: "Command", cwd: "Working directory", file_path: "File", target_file: "File", path: "Path", query: "Query", pattern: "Pattern", url: "URL" };
574
+ return labels[key] ?? key.replaceAll(/[_-]+/g, " ").replace(/^./, (letter) => letter.toLocaleUpperCase());
575
+ }
576
+ function argumentRows(argumentsText) {
577
+ if (!argumentsText) return [];
578
+ try {
579
+ const value = JSON.parse(argumentsText);
580
+ if (value && typeof value === "object" && !Array.isArray(value)) {
581
+ return Object.entries(value).map(([key, item]) => ({
582
+ key,
583
+ label: argumentLabel(key),
584
+ value: typeof item === "string" ? item : JSON.stringify(item, null, 2)
585
+ }));
586
+ }
587
+ } catch {
588
+ }
589
+ return [{ key: "arguments", label: "Details", value: argumentsText }];
590
+ }
518
591
  function TranscriptEntry({ entry, state, adapter }) {
519
592
  if (entry.role === "request") return /* @__PURE__ */ jsx3(RequestCard, { entry, adapter, canRespond: state.canRespond });
520
593
  if (entry.role === "reasoning") {
@@ -531,35 +604,37 @@ function TranscriptEntry({ entry, state, adapter }) {
531
604
  ] });
532
605
  }
533
606
  function ToolRow({ entry, workspace }) {
534
- const [open, setOpen] = useState2(entry.status === "pending");
535
- const detailId = useId();
536
607
  const target = compactToolTarget(toolTarget(entry.arguments), workspace);
537
608
  const hasDetail = Boolean(entry.arguments || entry.resultText);
538
609
  const category = toolCategory(entry);
539
- return /* @__PURE__ */ jsxs2("div", { class: "scui-tool", "data-status": entry.status ?? "completed", "data-category": category, children: [
540
- /* @__PURE__ */ jsxs2("button", { type: "button", class: "scui-tool-head", disabled: !hasDetail, "aria-expanded": hasDetail ? open : void 0, "aria-controls": hasDetail ? detailId : void 0, onClick: () => hasDetail && setOpen((value) => !value), children: [
541
- /* @__PURE__ */ jsx3("span", { class: "scui-tool-glyph", "aria-hidden": "true", children: { read: "\u25A4", search: "\u2315", edit: "\u270E", command: ">_", test: "\u25C7", web: "\u25CE", agent: "\u2659", other: "\u25C6" }[category] }),
542
- /* @__PURE__ */ jsx3("strong", { children: entry.label?.split(/__|\//).at(-1)?.replaceAll("_", " ") || "Tool" }),
543
- target ? /* @__PURE__ */ jsx3("span", { class: "scui-tool-target", title: toolTarget(entry.arguments), children: target }) : null,
544
- /* @__PURE__ */ jsx3("span", { class: "scui-spacer" }),
545
- /* @__PURE__ */ jsx3("span", { "aria-label": entry.status ?? "completed", children: entry.status === "pending" ? "\u25CC" : entry.status === "error" ? "\xD7" : "\u2713" })
546
- ] }),
547
- hasDetail && open ? /* @__PURE__ */ jsxs2("div", { id: detailId, class: "scui-tool-detail", children: [
548
- entry.arguments ? /* @__PURE__ */ jsxs2("pre", { children: [
549
- /* @__PURE__ */ jsx3("b", { children: "Input" }),
550
- "\n",
551
- entry.arguments
552
- ] }) : null,
553
- entry.resultText ? /* @__PURE__ */ jsxs2("pre", { "data-error": entry.status === "error", children: [
554
- /* @__PURE__ */ jsx3("b", { children: "Output" }),
555
- "\n",
556
- entry.resultText,
557
- entry.truncated ? "\n[truncated]" : ""
610
+ const summary = /* @__PURE__ */ jsxs2(Fragment, { children: [
611
+ /* @__PURE__ */ jsx3(ToolIcon, { category }),
612
+ /* @__PURE__ */ jsx3("strong", { children: toolAction(entry, category) }),
613
+ target ? /* @__PURE__ */ jsx3("code", { class: "scui-tool-target", title: toolTarget(entry.arguments), children: target }) : null,
614
+ /* @__PURE__ */ jsx3("span", { class: "scui-spacer" }),
615
+ /* @__PURE__ */ jsx3("span", { class: "scui-tool-status", role: "status", "data-status": entry.status ?? "completed", "aria-label": entry.status ?? "completed", children: entry.status === "pending" ? /* @__PURE__ */ jsx3("i", {}) : entry.status === "error" ? "\xD7" : "\u2713" }),
616
+ hasDetail ? /* @__PURE__ */ jsx3("span", { class: "scui-tool-chevron", "aria-hidden": "true", children: "\u203A" }) : null
617
+ ] });
618
+ if (!hasDetail) return /* @__PURE__ */ jsx3("div", { class: "scui-tool", "data-status": entry.status ?? "completed", "data-category": category, children: /* @__PURE__ */ jsx3("div", { class: "scui-tool-head", children: summary }) });
619
+ return /* @__PURE__ */ jsxs2("details", { class: "scui-tool", "data-status": entry.status ?? "completed", "data-category": category, children: [
620
+ /* @__PURE__ */ jsx3("summary", { class: "scui-tool-head", children: summary }),
621
+ /* @__PURE__ */ jsxs2("div", { class: "scui-tool-detail", children: [
622
+ entry.arguments ? /* @__PURE__ */ jsx3("dl", { children: argumentRows(entry.arguments).map((row) => /* @__PURE__ */ jsxs2("div", { children: [
623
+ /* @__PURE__ */ jsx3("dt", { children: row.label }),
624
+ /* @__PURE__ */ jsx3("dd", { children: /* @__PURE__ */ jsx3("pre", { children: row.value }) })
625
+ ] }, row.key)) }) : null,
626
+ entry.resultText ? /* @__PURE__ */ jsxs2("section", { children: [
627
+ /* @__PURE__ */ jsx3("strong", { children: entry.status === "error" ? "Error" : "Result" }),
628
+ /* @__PURE__ */ jsxs2("pre", { "data-error": entry.status === "error", children: [
629
+ entry.resultText,
630
+ entry.truncated ? "\n[truncated]" : ""
631
+ ] })
558
632
  ] }) : null
559
- ] }) : null
633
+ ] })
560
634
  ] });
561
635
  }
562
636
  function ActivityGroup({ entries, state }) {
637
+ if (entries.length === 1) return /* @__PURE__ */ jsx3("section", { class: "scui-activity", "data-single": "true", children: /* @__PURE__ */ jsx3(ToolRow, { entry: entries[0], workspace: state.workspace }) });
563
638
  const active = entries.some((entry) => entry.status === "pending");
564
639
  const [open, setOpen] = useState2(active);
565
640
  const id = useId();
@@ -572,7 +647,7 @@ function ActivityGroup({ entries, state }) {
572
647
  /* @__PURE__ */ jsx3("strong", { children: activitySummary(entries) }),
573
648
  /* @__PURE__ */ jsx3("span", { class: "scui-spacer" }),
574
649
  /* @__PURE__ */ jsxs2("small", { children: [
575
- entries.filter((entry) => entry.status === "completed").length,
650
+ entries.filter((entry) => entry.status !== "pending").length,
576
651
  "/",
577
652
  entries.length
578
653
  ] })
@@ -749,6 +824,15 @@ var LOGOS = {
749
824
  ["path", { "fill-rule": "evenodd", "clip-rule": "evenodd", d: "M1 1h16.5v11H12v5.5H6.5V23H1V1zm5.5 5.5V12H12V6.5H6.5z" }],
750
825
  ["path", { d: "M17.5 12H23v11h-5.5V12z" }]
751
826
  ]
827
+ },
828
+ // Product mark shared with the first-party browser frontend favicon.
829
+ supercode: {
830
+ viewBox: "0 0 64 64",
831
+ paths: [
832
+ ["rect", { width: "64", height: "64", rx: "14", fill: "#111923" }],
833
+ ["path", { d: "M18 21h28v6H18zm0 13h18v6H18z", fill: "#5f8fff" }],
834
+ ["circle", { cx: "46", cy: "37", r: "7", fill: "none", stroke: "#8ce0aa", "stroke-width": "4" }]
835
+ ]
752
836
  }
753
837
  };
754
838
  function hasHarnessLogo(id) {
@@ -933,9 +1017,13 @@ function Chat({ state, adapter, onBack, onNew, onClose, components, slots, label
933
1017
  }
934
1018
  function NewChat({ state, adapter, onBack, onClose, onStarted, labels }) {
935
1019
  const startable = state.harnesses.filter((item) => item.startable);
1020
+ const startableKey = startable.map((item) => item.id).join("\0");
936
1021
  const [harness, setHarness] = useState4(startable[0]?.id ?? "");
937
1022
  const [draft, setDraft] = useState4("");
938
1023
  const [starting, setStarting] = useState4(false);
1024
+ useEffect4(() => {
1025
+ if (!startable.some((item) => item.id === harness)) setHarness(startable[0]?.id ?? "");
1026
+ }, [harness, startableKey]);
939
1027
  useEffect4(() => {
940
1028
  if (starting && (state.busy || state.transcript.length)) onStarted();
941
1029
  }, [onStarted, starting, state.busy, state.transcript.length]);
package/conversation.mjs CHANGED
@@ -105,9 +105,12 @@ function compactToolTarget(target, workspace) {
105
105
  function activitySummary(entries) {
106
106
  const counts = /* @__PURE__ */ new Map();
107
107
  for (const entry of entries) counts.set(toolCategory(entry), (counts.get(toolCategory(entry)) ?? 0) + 1);
108
+ const failed = entries.filter((entry) => entry.status === "error").length;
109
+ const pending = entries.filter((entry) => entry.status === "pending").length;
110
+ if (pending) return `${entries.length} actions in progress`;
111
+ if (failed) return `${entries.length} actions \xB7 ${failed} failed`;
108
112
  if ([...counts.keys()].every((key) => key === "read" || key === "search")) return `Explored ${entries.length} ${entries.length === 1 ? "item" : "items"}`;
109
- const labels = { edit: "changed", test: "tests/builds", command: "commands", read: "reads", search: "searches", web: "web", agent: "agents", other: "other" };
110
- return `Activity \xB7 ${Object.entries(labels).flatMap(([key, label]) => counts.has(key) ? [`${counts.get(key)} ${label}`] : []).join(" \xB7 ")}`;
113
+ return `${entries.length} actions`;
111
114
  }
112
115
 
113
116
  // src/markdown.jsx
@@ -135,7 +138,7 @@ function boundedSet(map, key, value) {
135
138
  }
136
139
 
137
140
  // src/conversation.jsx
138
- import { jsx as jsx2, jsxs } from "preact/jsx-runtime";
141
+ import { Fragment, jsx as jsx2, jsxs } from "preact/jsx-runtime";
139
142
  function LoadingStatus({ state, compact = false }) {
140
143
  const copy = {
141
144
  connecting: ["Connecting to coding agents", "Checking installed harnesses and capabilities.", 0],
@@ -183,6 +186,76 @@ function ContextDisclosure({ context }) {
183
186
  ] }, item.id ?? index)) })
184
187
  ] });
185
188
  }
189
+ var TOOL_ICONS = {
190
+ read: () => /* @__PURE__ */ jsxs(Fragment, { children: [
191
+ /* @__PURE__ */ jsx2("path", { d: "M5 2.75h7.25L15 5.5v7.75A1.75 1.75 0 0 1 13.25 15h-8.5A1.75 1.75 0 0 1 3 13.25v-8.5A2 2 0 0 1 5 2.75Z" }),
192
+ /* @__PURE__ */ jsx2("path", { d: "M12 2.9v3h2.85M6 9h6M6 12h4" })
193
+ ] }),
194
+ search: () => /* @__PURE__ */ jsxs(Fragment, { children: [
195
+ /* @__PURE__ */ jsx2("circle", { cx: "8", cy: "8", r: "4.5" }),
196
+ /* @__PURE__ */ jsx2("path", { d: "m11.5 11.5 3 3" })
197
+ ] }),
198
+ edit: () => /* @__PURE__ */ jsxs(Fragment, { children: [
199
+ /* @__PURE__ */ jsx2("path", { d: "m11.75 3.25 3 3-8.5 8.5-3.75.75.75-3.75 8.5-8.5Z" }),
200
+ /* @__PURE__ */ jsx2("path", { d: "m10 5 3 3" })
201
+ ] }),
202
+ command: () => /* @__PURE__ */ jsx2(Fragment, { children: /* @__PURE__ */ jsx2("path", { d: "m3 5 3 3-3 3M8 12h6" }) }),
203
+ test: () => /* @__PURE__ */ jsxs(Fragment, { children: [
204
+ /* @__PURE__ */ jsx2("path", { d: "M6 2.5v3L3 12a2 2 0 0 0 1.8 3h8.4a2 2 0 0 0 1.8-3l-3-6.5v-3M5 9h8" }),
205
+ /* @__PURE__ */ jsx2("path", { d: "M5 2.5h8" })
206
+ ] }),
207
+ web: () => /* @__PURE__ */ jsxs(Fragment, { children: [
208
+ /* @__PURE__ */ jsx2("circle", { cx: "9", cy: "9", r: "6.5" }),
209
+ /* @__PURE__ */ jsx2("path", { d: "M2.75 9h12.5M9 2.5c2 1.8 3 4 3 6.5s-1 4.7-3 6.5c-2-1.8-3-4-3-6.5s1-4.7 3-6.5Z" })
210
+ ] }),
211
+ agent: () => /* @__PURE__ */ jsxs(Fragment, { children: [
212
+ /* @__PURE__ */ jsx2("circle", { cx: "9", cy: "6", r: "2.5" }),
213
+ /* @__PURE__ */ jsx2("path", { d: "M4 15c.4-3 2-4.5 5-4.5s4.6 1.5 5 4.5" })
214
+ ] }),
215
+ other: () => /* @__PURE__ */ jsxs(Fragment, { children: [
216
+ /* @__PURE__ */ jsx2("path", { d: "M9 2.5v3M9 12.5v3M2.5 9h3M12.5 9h3" }),
217
+ /* @__PURE__ */ jsx2("circle", { cx: "9", cy: "9", r: "3.5" })
218
+ ] })
219
+ };
220
+ function ToolIcon({ category }) {
221
+ const Glyph = TOOL_ICONS[category] ?? TOOL_ICONS.other;
222
+ return /* @__PURE__ */ jsx2("svg", { class: "scui-tool-icon", viewBox: "0 0 18 18", fill: "none", stroke: "currentColor", "stroke-width": "1.35", "stroke-linecap": "round", "stroke-linejoin": "round", "aria-hidden": "true", children: /* @__PURE__ */ jsx2(Glyph, {}) });
223
+ }
224
+ function toolAction(entry, category) {
225
+ const status = entry.status ?? "completed";
226
+ const actions = {
227
+ read: ["Reading", "Read", "Read failed"],
228
+ search: ["Searching", "Searched", "Search failed"],
229
+ edit: ["Editing", "Edited", "Edit failed"],
230
+ command: ["Running command", "Ran command", "Command failed"],
231
+ test: ["Running tests", "Ran tests", "Tests failed"],
232
+ web: ["Browsing", "Browsed", "Browser action failed"],
233
+ agent: ["Starting agent", "Started agent", "Agent failed"]
234
+ };
235
+ const position = status === "pending" ? 0 : status === "error" ? 2 : 1;
236
+ if (actions[category]) return actions[category][position];
237
+ const label = entry.label?.split(/__|\//).at(-1)?.replaceAll(/[_-]+/g, " ") || "Tool";
238
+ return status === "error" ? `${label} failed` : label;
239
+ }
240
+ function argumentLabel(key) {
241
+ const labels = { cmd: "Command", command: "Command", cwd: "Working directory", file_path: "File", target_file: "File", path: "Path", query: "Query", pattern: "Pattern", url: "URL" };
242
+ return labels[key] ?? key.replaceAll(/[_-]+/g, " ").replace(/^./, (letter) => letter.toLocaleUpperCase());
243
+ }
244
+ function argumentRows(argumentsText) {
245
+ if (!argumentsText) return [];
246
+ try {
247
+ const value = JSON.parse(argumentsText);
248
+ if (value && typeof value === "object" && !Array.isArray(value)) {
249
+ return Object.entries(value).map(([key, item]) => ({
250
+ key,
251
+ label: argumentLabel(key),
252
+ value: typeof item === "string" ? item : JSON.stringify(item, null, 2)
253
+ }));
254
+ }
255
+ } catch {
256
+ }
257
+ return [{ key: "arguments", label: "Details", value: argumentsText }];
258
+ }
186
259
  function TranscriptEntry({ entry, state, adapter }) {
187
260
  if (entry.role === "request") return /* @__PURE__ */ jsx2(RequestCard, { entry, adapter, canRespond: state.canRespond });
188
261
  if (entry.role === "reasoning") {
@@ -199,35 +272,37 @@ function TranscriptEntry({ entry, state, adapter }) {
199
272
  ] });
200
273
  }
201
274
  function ToolRow({ entry, workspace }) {
202
- const [open, setOpen] = useState(entry.status === "pending");
203
- const detailId = useId();
204
275
  const target = compactToolTarget(toolTarget(entry.arguments), workspace);
205
276
  const hasDetail = Boolean(entry.arguments || entry.resultText);
206
277
  const category = toolCategory(entry);
207
- return /* @__PURE__ */ jsxs("div", { class: "scui-tool", "data-status": entry.status ?? "completed", "data-category": category, children: [
208
- /* @__PURE__ */ jsxs("button", { type: "button", class: "scui-tool-head", disabled: !hasDetail, "aria-expanded": hasDetail ? open : void 0, "aria-controls": hasDetail ? detailId : void 0, onClick: () => hasDetail && setOpen((value) => !value), children: [
209
- /* @__PURE__ */ jsx2("span", { class: "scui-tool-glyph", "aria-hidden": "true", children: { read: "\u25A4", search: "\u2315", edit: "\u270E", command: ">_", test: "\u25C7", web: "\u25CE", agent: "\u2659", other: "\u25C6" }[category] }),
210
- /* @__PURE__ */ jsx2("strong", { children: entry.label?.split(/__|\//).at(-1)?.replaceAll("_", " ") || "Tool" }),
211
- target ? /* @__PURE__ */ jsx2("span", { class: "scui-tool-target", title: toolTarget(entry.arguments), children: target }) : null,
212
- /* @__PURE__ */ jsx2("span", { class: "scui-spacer" }),
213
- /* @__PURE__ */ jsx2("span", { "aria-label": entry.status ?? "completed", children: entry.status === "pending" ? "\u25CC" : entry.status === "error" ? "\xD7" : "\u2713" })
214
- ] }),
215
- hasDetail && open ? /* @__PURE__ */ jsxs("div", { id: detailId, class: "scui-tool-detail", children: [
216
- entry.arguments ? /* @__PURE__ */ jsxs("pre", { children: [
217
- /* @__PURE__ */ jsx2("b", { children: "Input" }),
218
- "\n",
219
- entry.arguments
220
- ] }) : null,
221
- entry.resultText ? /* @__PURE__ */ jsxs("pre", { "data-error": entry.status === "error", children: [
222
- /* @__PURE__ */ jsx2("b", { children: "Output" }),
223
- "\n",
224
- entry.resultText,
225
- entry.truncated ? "\n[truncated]" : ""
278
+ const summary = /* @__PURE__ */ jsxs(Fragment, { children: [
279
+ /* @__PURE__ */ jsx2(ToolIcon, { category }),
280
+ /* @__PURE__ */ jsx2("strong", { children: toolAction(entry, category) }),
281
+ target ? /* @__PURE__ */ jsx2("code", { class: "scui-tool-target", title: toolTarget(entry.arguments), children: target }) : null,
282
+ /* @__PURE__ */ jsx2("span", { class: "scui-spacer" }),
283
+ /* @__PURE__ */ jsx2("span", { class: "scui-tool-status", role: "status", "data-status": entry.status ?? "completed", "aria-label": entry.status ?? "completed", children: entry.status === "pending" ? /* @__PURE__ */ jsx2("i", {}) : entry.status === "error" ? "\xD7" : "\u2713" }),
284
+ hasDetail ? /* @__PURE__ */ jsx2("span", { class: "scui-tool-chevron", "aria-hidden": "true", children: "\u203A" }) : null
285
+ ] });
286
+ if (!hasDetail) return /* @__PURE__ */ jsx2("div", { class: "scui-tool", "data-status": entry.status ?? "completed", "data-category": category, children: /* @__PURE__ */ jsx2("div", { class: "scui-tool-head", children: summary }) });
287
+ return /* @__PURE__ */ jsxs("details", { class: "scui-tool", "data-status": entry.status ?? "completed", "data-category": category, children: [
288
+ /* @__PURE__ */ jsx2("summary", { class: "scui-tool-head", children: summary }),
289
+ /* @__PURE__ */ jsxs("div", { class: "scui-tool-detail", children: [
290
+ entry.arguments ? /* @__PURE__ */ jsx2("dl", { children: argumentRows(entry.arguments).map((row) => /* @__PURE__ */ jsxs("div", { children: [
291
+ /* @__PURE__ */ jsx2("dt", { children: row.label }),
292
+ /* @__PURE__ */ jsx2("dd", { children: /* @__PURE__ */ jsx2("pre", { children: row.value }) })
293
+ ] }, row.key)) }) : null,
294
+ entry.resultText ? /* @__PURE__ */ jsxs("section", { children: [
295
+ /* @__PURE__ */ jsx2("strong", { children: entry.status === "error" ? "Error" : "Result" }),
296
+ /* @__PURE__ */ jsxs("pre", { "data-error": entry.status === "error", children: [
297
+ entry.resultText,
298
+ entry.truncated ? "\n[truncated]" : ""
299
+ ] })
226
300
  ] }) : null
227
- ] }) : null
301
+ ] })
228
302
  ] });
229
303
  }
230
304
  function ActivityGroup({ entries, state }) {
305
+ if (entries.length === 1) return /* @__PURE__ */ jsx2("section", { class: "scui-activity", "data-single": "true", children: /* @__PURE__ */ jsx2(ToolRow, { entry: entries[0], workspace: state.workspace }) });
231
306
  const active = entries.some((entry) => entry.status === "pending");
232
307
  const [open, setOpen] = useState(active);
233
308
  const id = useId();
@@ -240,7 +315,7 @@ function ActivityGroup({ entries, state }) {
240
315
  /* @__PURE__ */ jsx2("strong", { children: activitySummary(entries) }),
241
316
  /* @__PURE__ */ jsx2("span", { class: "scui-spacer" }),
242
317
  /* @__PURE__ */ jsxs("small", { children: [
243
- entries.filter((entry) => entry.status === "completed").length,
318
+ entries.filter((entry) => entry.status !== "pending").length,
244
319
  "/",
245
320
  entries.length
246
321
  ] })
package/core.mjs CHANGED
@@ -390,9 +390,12 @@ export function compactToolTarget(target, workspace) {
390
390
  export function activitySummary(entries) {
391
391
  const counts = new Map();
392
392
  for (const entry of entries) counts.set(toolCategory(entry), (counts.get(toolCategory(entry)) ?? 0) + 1);
393
+ const failed = entries.filter((entry) => entry.status === 'error').length;
394
+ const pending = entries.filter((entry) => entry.status === 'pending').length;
395
+ if (pending) return `${entries.length} actions in progress`;
396
+ if (failed) return `${entries.length} actions · ${failed} failed`;
393
397
  if ([...counts.keys()].every((key) => key === 'read' || key === 'search')) return `Explored ${entries.length} ${entries.length === 1 ? 'item' : 'items'}`;
394
- const labels = { edit: 'changed', test: 'tests/builds', command: 'commands', read: 'reads', search: 'searches', web: 'web', agent: 'agents', other: 'other' };
395
- return `Activity · ${Object.entries(labels).flatMap(([key, label]) => counts.has(key) ? [`${counts.get(key)} ${label}`] : []).join(' · ')}`;
398
+ return `${entries.length} actions`;
396
399
  }
397
400
 
398
401
  export function canContinueHere(state) {
package/embed.mjs CHANGED
@@ -332,9 +332,12 @@ function compactToolTarget(target, workspace) {
332
332
  function activitySummary(entries) {
333
333
  const counts = /* @__PURE__ */ new Map();
334
334
  for (const entry of entries) counts.set(toolCategory(entry), (counts.get(toolCategory(entry)) ?? 0) + 1);
335
+ const failed = entries.filter((entry) => entry.status === "error").length;
336
+ const pending = entries.filter((entry) => entry.status === "pending").length;
337
+ if (pending) return `${entries.length} actions in progress`;
338
+ if (failed) return `${entries.length} actions \xB7 ${failed} failed`;
335
339
  if ([...counts.keys()].every((key) => key === "read" || key === "search")) return `Explored ${entries.length} ${entries.length === 1 ? "item" : "items"}`;
336
- const labels = { edit: "changed", test: "tests/builds", command: "commands", read: "reads", search: "searches", web: "web", agent: "agents", other: "other" };
337
- return `Activity \xB7 ${Object.entries(labels).flatMap(([key, label]) => counts.has(key) ? [`${counts.get(key)} ${label}`] : []).join(" \xB7 ")}`;
340
+ return `${entries.length} actions`;
338
341
  }
339
342
  function canContinueHere(state) {
340
343
  if (state.mode !== "mirror" || state.canSend) return false;
@@ -473,7 +476,7 @@ function Markdown({ value }) {
473
476
  }
474
477
 
475
478
  // src/conversation.jsx
476
- import { jsx as jsx3, jsxs as jsxs2 } from "preact/jsx-runtime";
479
+ import { Fragment, jsx as jsx3, jsxs as jsxs2 } from "preact/jsx-runtime";
477
480
  function LoadingStatus({ state, compact = false }) {
478
481
  const copy = {
479
482
  connecting: ["Connecting to coding agents", "Checking installed harnesses and capabilities.", 0],
@@ -521,6 +524,76 @@ function ContextDisclosure({ context }) {
521
524
  ] }, item.id ?? index)) })
522
525
  ] });
523
526
  }
527
+ var TOOL_ICONS = {
528
+ read: () => /* @__PURE__ */ jsxs2(Fragment, { children: [
529
+ /* @__PURE__ */ jsx3("path", { d: "M5 2.75h7.25L15 5.5v7.75A1.75 1.75 0 0 1 13.25 15h-8.5A1.75 1.75 0 0 1 3 13.25v-8.5A2 2 0 0 1 5 2.75Z" }),
530
+ /* @__PURE__ */ jsx3("path", { d: "M12 2.9v3h2.85M6 9h6M6 12h4" })
531
+ ] }),
532
+ search: () => /* @__PURE__ */ jsxs2(Fragment, { children: [
533
+ /* @__PURE__ */ jsx3("circle", { cx: "8", cy: "8", r: "4.5" }),
534
+ /* @__PURE__ */ jsx3("path", { d: "m11.5 11.5 3 3" })
535
+ ] }),
536
+ edit: () => /* @__PURE__ */ jsxs2(Fragment, { children: [
537
+ /* @__PURE__ */ jsx3("path", { d: "m11.75 3.25 3 3-8.5 8.5-3.75.75.75-3.75 8.5-8.5Z" }),
538
+ /* @__PURE__ */ jsx3("path", { d: "m10 5 3 3" })
539
+ ] }),
540
+ command: () => /* @__PURE__ */ jsx3(Fragment, { children: /* @__PURE__ */ jsx3("path", { d: "m3 5 3 3-3 3M8 12h6" }) }),
541
+ test: () => /* @__PURE__ */ jsxs2(Fragment, { children: [
542
+ /* @__PURE__ */ jsx3("path", { d: "M6 2.5v3L3 12a2 2 0 0 0 1.8 3h8.4a2 2 0 0 0 1.8-3l-3-6.5v-3M5 9h8" }),
543
+ /* @__PURE__ */ jsx3("path", { d: "M5 2.5h8" })
544
+ ] }),
545
+ web: () => /* @__PURE__ */ jsxs2(Fragment, { children: [
546
+ /* @__PURE__ */ jsx3("circle", { cx: "9", cy: "9", r: "6.5" }),
547
+ /* @__PURE__ */ jsx3("path", { d: "M2.75 9h12.5M9 2.5c2 1.8 3 4 3 6.5s-1 4.7-3 6.5c-2-1.8-3-4-3-6.5s1-4.7 3-6.5Z" })
548
+ ] }),
549
+ agent: () => /* @__PURE__ */ jsxs2(Fragment, { children: [
550
+ /* @__PURE__ */ jsx3("circle", { cx: "9", cy: "6", r: "2.5" }),
551
+ /* @__PURE__ */ jsx3("path", { d: "M4 15c.4-3 2-4.5 5-4.5s4.6 1.5 5 4.5" })
552
+ ] }),
553
+ other: () => /* @__PURE__ */ jsxs2(Fragment, { children: [
554
+ /* @__PURE__ */ jsx3("path", { d: "M9 2.5v3M9 12.5v3M2.5 9h3M12.5 9h3" }),
555
+ /* @__PURE__ */ jsx3("circle", { cx: "9", cy: "9", r: "3.5" })
556
+ ] })
557
+ };
558
+ function ToolIcon({ category }) {
559
+ const Glyph = TOOL_ICONS[category] ?? TOOL_ICONS.other;
560
+ return /* @__PURE__ */ jsx3("svg", { class: "scui-tool-icon", viewBox: "0 0 18 18", fill: "none", stroke: "currentColor", "stroke-width": "1.35", "stroke-linecap": "round", "stroke-linejoin": "round", "aria-hidden": "true", children: /* @__PURE__ */ jsx3(Glyph, {}) });
561
+ }
562
+ function toolAction(entry, category) {
563
+ const status = entry.status ?? "completed";
564
+ const actions = {
565
+ read: ["Reading", "Read", "Read failed"],
566
+ search: ["Searching", "Searched", "Search failed"],
567
+ edit: ["Editing", "Edited", "Edit failed"],
568
+ command: ["Running command", "Ran command", "Command failed"],
569
+ test: ["Running tests", "Ran tests", "Tests failed"],
570
+ web: ["Browsing", "Browsed", "Browser action failed"],
571
+ agent: ["Starting agent", "Started agent", "Agent failed"]
572
+ };
573
+ const position = status === "pending" ? 0 : status === "error" ? 2 : 1;
574
+ if (actions[category]) return actions[category][position];
575
+ const label = entry.label?.split(/__|\//).at(-1)?.replaceAll(/[_-]+/g, " ") || "Tool";
576
+ return status === "error" ? `${label} failed` : label;
577
+ }
578
+ function argumentLabel(key) {
579
+ const labels = { cmd: "Command", command: "Command", cwd: "Working directory", file_path: "File", target_file: "File", path: "Path", query: "Query", pattern: "Pattern", url: "URL" };
580
+ return labels[key] ?? key.replaceAll(/[_-]+/g, " ").replace(/^./, (letter) => letter.toLocaleUpperCase());
581
+ }
582
+ function argumentRows(argumentsText) {
583
+ if (!argumentsText) return [];
584
+ try {
585
+ const value = JSON.parse(argumentsText);
586
+ if (value && typeof value === "object" && !Array.isArray(value)) {
587
+ return Object.entries(value).map(([key, item]) => ({
588
+ key,
589
+ label: argumentLabel(key),
590
+ value: typeof item === "string" ? item : JSON.stringify(item, null, 2)
591
+ }));
592
+ }
593
+ } catch {
594
+ }
595
+ return [{ key: "arguments", label: "Details", value: argumentsText }];
596
+ }
524
597
  function TranscriptEntry({ entry, state, adapter }) {
525
598
  if (entry.role === "request") return /* @__PURE__ */ jsx3(RequestCard, { entry, adapter, canRespond: state.canRespond });
526
599
  if (entry.role === "reasoning") {
@@ -537,35 +610,37 @@ function TranscriptEntry({ entry, state, adapter }) {
537
610
  ] });
538
611
  }
539
612
  function ToolRow({ entry, workspace }) {
540
- const [open, setOpen] = useState2(entry.status === "pending");
541
- const detailId = useId();
542
613
  const target = compactToolTarget(toolTarget(entry.arguments), workspace);
543
614
  const hasDetail = Boolean(entry.arguments || entry.resultText);
544
615
  const category = toolCategory(entry);
545
- return /* @__PURE__ */ jsxs2("div", { class: "scui-tool", "data-status": entry.status ?? "completed", "data-category": category, children: [
546
- /* @__PURE__ */ jsxs2("button", { type: "button", class: "scui-tool-head", disabled: !hasDetail, "aria-expanded": hasDetail ? open : void 0, "aria-controls": hasDetail ? detailId : void 0, onClick: () => hasDetail && setOpen((value) => !value), children: [
547
- /* @__PURE__ */ jsx3("span", { class: "scui-tool-glyph", "aria-hidden": "true", children: { read: "\u25A4", search: "\u2315", edit: "\u270E", command: ">_", test: "\u25C7", web: "\u25CE", agent: "\u2659", other: "\u25C6" }[category] }),
548
- /* @__PURE__ */ jsx3("strong", { children: entry.label?.split(/__|\//).at(-1)?.replaceAll("_", " ") || "Tool" }),
549
- target ? /* @__PURE__ */ jsx3("span", { class: "scui-tool-target", title: toolTarget(entry.arguments), children: target }) : null,
550
- /* @__PURE__ */ jsx3("span", { class: "scui-spacer" }),
551
- /* @__PURE__ */ jsx3("span", { "aria-label": entry.status ?? "completed", children: entry.status === "pending" ? "\u25CC" : entry.status === "error" ? "\xD7" : "\u2713" })
552
- ] }),
553
- hasDetail && open ? /* @__PURE__ */ jsxs2("div", { id: detailId, class: "scui-tool-detail", children: [
554
- entry.arguments ? /* @__PURE__ */ jsxs2("pre", { children: [
555
- /* @__PURE__ */ jsx3("b", { children: "Input" }),
556
- "\n",
557
- entry.arguments
558
- ] }) : null,
559
- entry.resultText ? /* @__PURE__ */ jsxs2("pre", { "data-error": entry.status === "error", children: [
560
- /* @__PURE__ */ jsx3("b", { children: "Output" }),
561
- "\n",
562
- entry.resultText,
563
- entry.truncated ? "\n[truncated]" : ""
616
+ const summary = /* @__PURE__ */ jsxs2(Fragment, { children: [
617
+ /* @__PURE__ */ jsx3(ToolIcon, { category }),
618
+ /* @__PURE__ */ jsx3("strong", { children: toolAction(entry, category) }),
619
+ target ? /* @__PURE__ */ jsx3("code", { class: "scui-tool-target", title: toolTarget(entry.arguments), children: target }) : null,
620
+ /* @__PURE__ */ jsx3("span", { class: "scui-spacer" }),
621
+ /* @__PURE__ */ jsx3("span", { class: "scui-tool-status", role: "status", "data-status": entry.status ?? "completed", "aria-label": entry.status ?? "completed", children: entry.status === "pending" ? /* @__PURE__ */ jsx3("i", {}) : entry.status === "error" ? "\xD7" : "\u2713" }),
622
+ hasDetail ? /* @__PURE__ */ jsx3("span", { class: "scui-tool-chevron", "aria-hidden": "true", children: "\u203A" }) : null
623
+ ] });
624
+ if (!hasDetail) return /* @__PURE__ */ jsx3("div", { class: "scui-tool", "data-status": entry.status ?? "completed", "data-category": category, children: /* @__PURE__ */ jsx3("div", { class: "scui-tool-head", children: summary }) });
625
+ return /* @__PURE__ */ jsxs2("details", { class: "scui-tool", "data-status": entry.status ?? "completed", "data-category": category, children: [
626
+ /* @__PURE__ */ jsx3("summary", { class: "scui-tool-head", children: summary }),
627
+ /* @__PURE__ */ jsxs2("div", { class: "scui-tool-detail", children: [
628
+ entry.arguments ? /* @__PURE__ */ jsx3("dl", { children: argumentRows(entry.arguments).map((row) => /* @__PURE__ */ jsxs2("div", { children: [
629
+ /* @__PURE__ */ jsx3("dt", { children: row.label }),
630
+ /* @__PURE__ */ jsx3("dd", { children: /* @__PURE__ */ jsx3("pre", { children: row.value }) })
631
+ ] }, row.key)) }) : null,
632
+ entry.resultText ? /* @__PURE__ */ jsxs2("section", { children: [
633
+ /* @__PURE__ */ jsx3("strong", { children: entry.status === "error" ? "Error" : "Result" }),
634
+ /* @__PURE__ */ jsxs2("pre", { "data-error": entry.status === "error", children: [
635
+ entry.resultText,
636
+ entry.truncated ? "\n[truncated]" : ""
637
+ ] })
564
638
  ] }) : null
565
- ] }) : null
639
+ ] })
566
640
  ] });
567
641
  }
568
642
  function ActivityGroup({ entries, state }) {
643
+ if (entries.length === 1) return /* @__PURE__ */ jsx3("section", { class: "scui-activity", "data-single": "true", children: /* @__PURE__ */ jsx3(ToolRow, { entry: entries[0], workspace: state.workspace }) });
569
644
  const active = entries.some((entry) => entry.status === "pending");
570
645
  const [open, setOpen] = useState2(active);
571
646
  const id = useId();
@@ -578,7 +653,7 @@ function ActivityGroup({ entries, state }) {
578
653
  /* @__PURE__ */ jsx3("strong", { children: activitySummary(entries) }),
579
654
  /* @__PURE__ */ jsx3("span", { class: "scui-spacer" }),
580
655
  /* @__PURE__ */ jsxs2("small", { children: [
581
- entries.filter((entry) => entry.status === "completed").length,
656
+ entries.filter((entry) => entry.status !== "pending").length,
582
657
  "/",
583
658
  entries.length
584
659
  ] })
@@ -755,6 +830,15 @@ var LOGOS = {
755
830
  ["path", { "fill-rule": "evenodd", "clip-rule": "evenodd", d: "M1 1h16.5v11H12v5.5H6.5V23H1V1zm5.5 5.5V12H12V6.5H6.5z" }],
756
831
  ["path", { d: "M17.5 12H23v11h-5.5V12z" }]
757
832
  ]
833
+ },
834
+ // Product mark shared with the first-party browser frontend favicon.
835
+ supercode: {
836
+ viewBox: "0 0 64 64",
837
+ paths: [
838
+ ["rect", { width: "64", height: "64", rx: "14", fill: "#111923" }],
839
+ ["path", { d: "M18 21h28v6H18zm0 13h18v6H18z", fill: "#5f8fff" }],
840
+ ["circle", { cx: "46", cy: "37", r: "7", fill: "none", stroke: "#8ce0aa", "stroke-width": "4" }]
841
+ ]
758
842
  }
759
843
  };
760
844
  function HarnessLogo({ id, activity, size = 28, onMissingLogo }) {
@@ -933,9 +1017,13 @@ function Chat({ state, adapter, onBack, onNew, onClose, components, slots, label
933
1017
  }
934
1018
  function NewChat({ state, adapter, onBack, onClose, onStarted, labels }) {
935
1019
  const startable = state.harnesses.filter((item) => item.startable);
1020
+ const startableKey = startable.map((item) => item.id).join("\0");
936
1021
  const [harness, setHarness] = useState4(startable[0]?.id ?? "");
937
1022
  const [draft, setDraft] = useState4("");
938
1023
  const [starting, setStarting] = useState4(false);
1024
+ useEffect4(() => {
1025
+ if (!startable.some((item) => item.id === harness)) setHarness(startable[0]?.id ?? "");
1026
+ }, [harness, startableKey]);
939
1027
  useEffect4(() => {
940
1028
  if (starting && (state.busy || state.transcript.length)) onStarted();
941
1029
  }, [onStarted, starting, state.busy, state.transcript.length]);
package/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import type { ComponentChildren, ComponentType, VNode } from 'preact';
2
2
 
3
- export type HarnessId = 'claude-code' | 'codex' | 'opencode' | 'pi' | 'grok' | (string & {});
3
+ export type HarnessId = 'claude-code' | 'codex' | 'gemini' | 'goose' | 'grok' | 'opencode' | 'pi' | 'supercode' | (string & {});
4
4
  export type UiTone = 'live' | 'warn' | 'dead' | 'off';
5
5
  export type StartupPhase = 'connecting' | 'starting' | 'discovering' | 'ready';
6
6
  export type SessionMode = 'none' | 'control' | 'mirror';
package/logo.mjs CHANGED
@@ -34,6 +34,15 @@ var LOGOS = {
34
34
  ["path", { "fill-rule": "evenodd", "clip-rule": "evenodd", d: "M1 1h16.5v11H12v5.5H6.5V23H1V1zm5.5 5.5V12H12V6.5H6.5z" }],
35
35
  ["path", { d: "M17.5 12H23v11h-5.5V12z" }]
36
36
  ]
37
+ },
38
+ // Product mark shared with the first-party browser frontend favicon.
39
+ supercode: {
40
+ viewBox: "0 0 64 64",
41
+ paths: [
42
+ ["rect", { width: "64", height: "64", rx: "14", fill: "#111923" }],
43
+ ["path", { d: "M18 21h28v6H18zm0 13h18v6H18z", fill: "#5f8fff" }],
44
+ ["circle", { cx: "46", cy: "37", r: "7", fill: "none", stroke: "#8ce0aa", "stroke-width": "4" }]
45
+ ]
37
46
  }
38
47
  };
39
48
  function hasHarnessLogo(id) {
package/messenger.mjs CHANGED
@@ -329,9 +329,12 @@ function compactToolTarget(target, workspace) {
329
329
  function activitySummary(entries) {
330
330
  const counts = /* @__PURE__ */ new Map();
331
331
  for (const entry of entries) counts.set(toolCategory(entry), (counts.get(toolCategory(entry)) ?? 0) + 1);
332
+ const failed = entries.filter((entry) => entry.status === "error").length;
333
+ const pending = entries.filter((entry) => entry.status === "pending").length;
334
+ if (pending) return `${entries.length} actions in progress`;
335
+ if (failed) return `${entries.length} actions \xB7 ${failed} failed`;
332
336
  if ([...counts.keys()].every((key) => key === "read" || key === "search")) return `Explored ${entries.length} ${entries.length === 1 ? "item" : "items"}`;
333
- const labels = { edit: "changed", test: "tests/builds", command: "commands", read: "reads", search: "searches", web: "web", agent: "agents", other: "other" };
334
- return `Activity \xB7 ${Object.entries(labels).flatMap(([key, label]) => counts.has(key) ? [`${counts.get(key)} ${label}`] : []).join(" \xB7 ")}`;
337
+ return `${entries.length} actions`;
335
338
  }
336
339
  function canContinueHere(state) {
337
340
  if (state.mode !== "mirror" || state.canSend) return false;
@@ -470,7 +473,7 @@ function Markdown({ value }) {
470
473
  }
471
474
 
472
475
  // src/conversation.jsx
473
- import { jsx as jsx3, jsxs as jsxs2 } from "preact/jsx-runtime";
476
+ import { Fragment, jsx as jsx3, jsxs as jsxs2 } from "preact/jsx-runtime";
474
477
  function LoadingStatus({ state, compact = false }) {
475
478
  const copy = {
476
479
  connecting: ["Connecting to coding agents", "Checking installed harnesses and capabilities.", 0],
@@ -518,6 +521,76 @@ function ContextDisclosure({ context }) {
518
521
  ] }, item.id ?? index)) })
519
522
  ] });
520
523
  }
524
+ var TOOL_ICONS = {
525
+ read: () => /* @__PURE__ */ jsxs2(Fragment, { children: [
526
+ /* @__PURE__ */ jsx3("path", { d: "M5 2.75h7.25L15 5.5v7.75A1.75 1.75 0 0 1 13.25 15h-8.5A1.75 1.75 0 0 1 3 13.25v-8.5A2 2 0 0 1 5 2.75Z" }),
527
+ /* @__PURE__ */ jsx3("path", { d: "M12 2.9v3h2.85M6 9h6M6 12h4" })
528
+ ] }),
529
+ search: () => /* @__PURE__ */ jsxs2(Fragment, { children: [
530
+ /* @__PURE__ */ jsx3("circle", { cx: "8", cy: "8", r: "4.5" }),
531
+ /* @__PURE__ */ jsx3("path", { d: "m11.5 11.5 3 3" })
532
+ ] }),
533
+ edit: () => /* @__PURE__ */ jsxs2(Fragment, { children: [
534
+ /* @__PURE__ */ jsx3("path", { d: "m11.75 3.25 3 3-8.5 8.5-3.75.75.75-3.75 8.5-8.5Z" }),
535
+ /* @__PURE__ */ jsx3("path", { d: "m10 5 3 3" })
536
+ ] }),
537
+ command: () => /* @__PURE__ */ jsx3(Fragment, { children: /* @__PURE__ */ jsx3("path", { d: "m3 5 3 3-3 3M8 12h6" }) }),
538
+ test: () => /* @__PURE__ */ jsxs2(Fragment, { children: [
539
+ /* @__PURE__ */ jsx3("path", { d: "M6 2.5v3L3 12a2 2 0 0 0 1.8 3h8.4a2 2 0 0 0 1.8-3l-3-6.5v-3M5 9h8" }),
540
+ /* @__PURE__ */ jsx3("path", { d: "M5 2.5h8" })
541
+ ] }),
542
+ web: () => /* @__PURE__ */ jsxs2(Fragment, { children: [
543
+ /* @__PURE__ */ jsx3("circle", { cx: "9", cy: "9", r: "6.5" }),
544
+ /* @__PURE__ */ jsx3("path", { d: "M2.75 9h12.5M9 2.5c2 1.8 3 4 3 6.5s-1 4.7-3 6.5c-2-1.8-3-4-3-6.5s1-4.7 3-6.5Z" })
545
+ ] }),
546
+ agent: () => /* @__PURE__ */ jsxs2(Fragment, { children: [
547
+ /* @__PURE__ */ jsx3("circle", { cx: "9", cy: "6", r: "2.5" }),
548
+ /* @__PURE__ */ jsx3("path", { d: "M4 15c.4-3 2-4.5 5-4.5s4.6 1.5 5 4.5" })
549
+ ] }),
550
+ other: () => /* @__PURE__ */ jsxs2(Fragment, { children: [
551
+ /* @__PURE__ */ jsx3("path", { d: "M9 2.5v3M9 12.5v3M2.5 9h3M12.5 9h3" }),
552
+ /* @__PURE__ */ jsx3("circle", { cx: "9", cy: "9", r: "3.5" })
553
+ ] })
554
+ };
555
+ function ToolIcon({ category }) {
556
+ const Glyph = TOOL_ICONS[category] ?? TOOL_ICONS.other;
557
+ return /* @__PURE__ */ jsx3("svg", { class: "scui-tool-icon", viewBox: "0 0 18 18", fill: "none", stroke: "currentColor", "stroke-width": "1.35", "stroke-linecap": "round", "stroke-linejoin": "round", "aria-hidden": "true", children: /* @__PURE__ */ jsx3(Glyph, {}) });
558
+ }
559
+ function toolAction(entry, category) {
560
+ const status = entry.status ?? "completed";
561
+ const actions = {
562
+ read: ["Reading", "Read", "Read failed"],
563
+ search: ["Searching", "Searched", "Search failed"],
564
+ edit: ["Editing", "Edited", "Edit failed"],
565
+ command: ["Running command", "Ran command", "Command failed"],
566
+ test: ["Running tests", "Ran tests", "Tests failed"],
567
+ web: ["Browsing", "Browsed", "Browser action failed"],
568
+ agent: ["Starting agent", "Started agent", "Agent failed"]
569
+ };
570
+ const position = status === "pending" ? 0 : status === "error" ? 2 : 1;
571
+ if (actions[category]) return actions[category][position];
572
+ const label = entry.label?.split(/__|\//).at(-1)?.replaceAll(/[_-]+/g, " ") || "Tool";
573
+ return status === "error" ? `${label} failed` : label;
574
+ }
575
+ function argumentLabel(key) {
576
+ const labels = { cmd: "Command", command: "Command", cwd: "Working directory", file_path: "File", target_file: "File", path: "Path", query: "Query", pattern: "Pattern", url: "URL" };
577
+ return labels[key] ?? key.replaceAll(/[_-]+/g, " ").replace(/^./, (letter) => letter.toLocaleUpperCase());
578
+ }
579
+ function argumentRows(argumentsText) {
580
+ if (!argumentsText) return [];
581
+ try {
582
+ const value = JSON.parse(argumentsText);
583
+ if (value && typeof value === "object" && !Array.isArray(value)) {
584
+ return Object.entries(value).map(([key, item]) => ({
585
+ key,
586
+ label: argumentLabel(key),
587
+ value: typeof item === "string" ? item : JSON.stringify(item, null, 2)
588
+ }));
589
+ }
590
+ } catch {
591
+ }
592
+ return [{ key: "arguments", label: "Details", value: argumentsText }];
593
+ }
521
594
  function TranscriptEntry({ entry, state, adapter }) {
522
595
  if (entry.role === "request") return /* @__PURE__ */ jsx3(RequestCard, { entry, adapter, canRespond: state.canRespond });
523
596
  if (entry.role === "reasoning") {
@@ -534,35 +607,37 @@ function TranscriptEntry({ entry, state, adapter }) {
534
607
  ] });
535
608
  }
536
609
  function ToolRow({ entry, workspace }) {
537
- const [open, setOpen] = useState2(entry.status === "pending");
538
- const detailId = useId();
539
610
  const target = compactToolTarget(toolTarget(entry.arguments), workspace);
540
611
  const hasDetail = Boolean(entry.arguments || entry.resultText);
541
612
  const category = toolCategory(entry);
542
- return /* @__PURE__ */ jsxs2("div", { class: "scui-tool", "data-status": entry.status ?? "completed", "data-category": category, children: [
543
- /* @__PURE__ */ jsxs2("button", { type: "button", class: "scui-tool-head", disabled: !hasDetail, "aria-expanded": hasDetail ? open : void 0, "aria-controls": hasDetail ? detailId : void 0, onClick: () => hasDetail && setOpen((value) => !value), children: [
544
- /* @__PURE__ */ jsx3("span", { class: "scui-tool-glyph", "aria-hidden": "true", children: { read: "\u25A4", search: "\u2315", edit: "\u270E", command: ">_", test: "\u25C7", web: "\u25CE", agent: "\u2659", other: "\u25C6" }[category] }),
545
- /* @__PURE__ */ jsx3("strong", { children: entry.label?.split(/__|\//).at(-1)?.replaceAll("_", " ") || "Tool" }),
546
- target ? /* @__PURE__ */ jsx3("span", { class: "scui-tool-target", title: toolTarget(entry.arguments), children: target }) : null,
547
- /* @__PURE__ */ jsx3("span", { class: "scui-spacer" }),
548
- /* @__PURE__ */ jsx3("span", { "aria-label": entry.status ?? "completed", children: entry.status === "pending" ? "\u25CC" : entry.status === "error" ? "\xD7" : "\u2713" })
549
- ] }),
550
- hasDetail && open ? /* @__PURE__ */ jsxs2("div", { id: detailId, class: "scui-tool-detail", children: [
551
- entry.arguments ? /* @__PURE__ */ jsxs2("pre", { children: [
552
- /* @__PURE__ */ jsx3("b", { children: "Input" }),
553
- "\n",
554
- entry.arguments
555
- ] }) : null,
556
- entry.resultText ? /* @__PURE__ */ jsxs2("pre", { "data-error": entry.status === "error", children: [
557
- /* @__PURE__ */ jsx3("b", { children: "Output" }),
558
- "\n",
559
- entry.resultText,
560
- entry.truncated ? "\n[truncated]" : ""
613
+ const summary = /* @__PURE__ */ jsxs2(Fragment, { children: [
614
+ /* @__PURE__ */ jsx3(ToolIcon, { category }),
615
+ /* @__PURE__ */ jsx3("strong", { children: toolAction(entry, category) }),
616
+ target ? /* @__PURE__ */ jsx3("code", { class: "scui-tool-target", title: toolTarget(entry.arguments), children: target }) : null,
617
+ /* @__PURE__ */ jsx3("span", { class: "scui-spacer" }),
618
+ /* @__PURE__ */ jsx3("span", { class: "scui-tool-status", role: "status", "data-status": entry.status ?? "completed", "aria-label": entry.status ?? "completed", children: entry.status === "pending" ? /* @__PURE__ */ jsx3("i", {}) : entry.status === "error" ? "\xD7" : "\u2713" }),
619
+ hasDetail ? /* @__PURE__ */ jsx3("span", { class: "scui-tool-chevron", "aria-hidden": "true", children: "\u203A" }) : null
620
+ ] });
621
+ if (!hasDetail) return /* @__PURE__ */ jsx3("div", { class: "scui-tool", "data-status": entry.status ?? "completed", "data-category": category, children: /* @__PURE__ */ jsx3("div", { class: "scui-tool-head", children: summary }) });
622
+ return /* @__PURE__ */ jsxs2("details", { class: "scui-tool", "data-status": entry.status ?? "completed", "data-category": category, children: [
623
+ /* @__PURE__ */ jsx3("summary", { class: "scui-tool-head", children: summary }),
624
+ /* @__PURE__ */ jsxs2("div", { class: "scui-tool-detail", children: [
625
+ entry.arguments ? /* @__PURE__ */ jsx3("dl", { children: argumentRows(entry.arguments).map((row) => /* @__PURE__ */ jsxs2("div", { children: [
626
+ /* @__PURE__ */ jsx3("dt", { children: row.label }),
627
+ /* @__PURE__ */ jsx3("dd", { children: /* @__PURE__ */ jsx3("pre", { children: row.value }) })
628
+ ] }, row.key)) }) : null,
629
+ entry.resultText ? /* @__PURE__ */ jsxs2("section", { children: [
630
+ /* @__PURE__ */ jsx3("strong", { children: entry.status === "error" ? "Error" : "Result" }),
631
+ /* @__PURE__ */ jsxs2("pre", { "data-error": entry.status === "error", children: [
632
+ entry.resultText,
633
+ entry.truncated ? "\n[truncated]" : ""
634
+ ] })
561
635
  ] }) : null
562
- ] }) : null
636
+ ] })
563
637
  ] });
564
638
  }
565
639
  function ActivityGroup({ entries, state }) {
640
+ if (entries.length === 1) return /* @__PURE__ */ jsx3("section", { class: "scui-activity", "data-single": "true", children: /* @__PURE__ */ jsx3(ToolRow, { entry: entries[0], workspace: state.workspace }) });
566
641
  const active = entries.some((entry) => entry.status === "pending");
567
642
  const [open, setOpen] = useState2(active);
568
643
  const id = useId();
@@ -575,7 +650,7 @@ function ActivityGroup({ entries, state }) {
575
650
  /* @__PURE__ */ jsx3("strong", { children: activitySummary(entries) }),
576
651
  /* @__PURE__ */ jsx3("span", { class: "scui-spacer" }),
577
652
  /* @__PURE__ */ jsxs2("small", { children: [
578
- entries.filter((entry) => entry.status === "completed").length,
653
+ entries.filter((entry) => entry.status !== "pending").length,
579
654
  "/",
580
655
  entries.length
581
656
  ] })
@@ -752,6 +827,15 @@ var LOGOS = {
752
827
  ["path", { "fill-rule": "evenodd", "clip-rule": "evenodd", d: "M1 1h16.5v11H12v5.5H6.5V23H1V1zm5.5 5.5V12H12V6.5H6.5z" }],
753
828
  ["path", { d: "M17.5 12H23v11h-5.5V12z" }]
754
829
  ]
830
+ },
831
+ // Product mark shared with the first-party browser frontend favicon.
832
+ supercode: {
833
+ viewBox: "0 0 64 64",
834
+ paths: [
835
+ ["rect", { width: "64", height: "64", rx: "14", fill: "#111923" }],
836
+ ["path", { d: "M18 21h28v6H18zm0 13h18v6H18z", fill: "#5f8fff" }],
837
+ ["circle", { cx: "46", cy: "37", r: "7", fill: "none", stroke: "#8ce0aa", "stroke-width": "4" }]
838
+ ]
755
839
  }
756
840
  };
757
841
  function HarnessLogo({ id, activity, size = 28, onMissingLogo }) {
@@ -930,9 +1014,13 @@ function Chat({ state, adapter, onBack, onNew, onClose, components, slots, label
930
1014
  }
931
1015
  function NewChat({ state, adapter, onBack, onClose, onStarted, labels }) {
932
1016
  const startable = state.harnesses.filter((item) => item.startable);
1017
+ const startableKey = startable.map((item) => item.id).join("\0");
933
1018
  const [harness, setHarness] = useState4(startable[0]?.id ?? "");
934
1019
  const [draft, setDraft] = useState4("");
935
1020
  const [starting, setStarting] = useState4(false);
1021
+ useEffect4(() => {
1022
+ if (!startable.some((item) => item.id === harness)) setHarness(startable[0]?.id ?? "");
1023
+ }, [harness, startableKey]);
936
1024
  useEffect4(() => {
937
1025
  if (starting && (state.busy || state.transcript.length)) onStarted();
938
1026
  }, [onStarted, starting, state.busy, state.transcript.length]);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@volter-ai-dev/supercode-ui",
3
- "version": "0.1.6",
3
+ "version": "0.1.8",
4
4
  "type": "module",
5
5
  "description": "Composable default UI kit for Supercode-powered coding-agent experiences",
6
6
  "exports": {
package/sessions.mjs CHANGED
@@ -97,7 +97,7 @@ markdown.renderer.rules.link_open = (tokens, index, options, env, self) => {
97
97
  };
98
98
 
99
99
  // src/conversation.jsx
100
- import { jsx as jsx2, jsxs } from "preact/jsx-runtime";
100
+ import { Fragment, jsx as jsx2, jsxs } from "preact/jsx-runtime";
101
101
  function LoadingStatus({ state, compact = false }) {
102
102
  const copy = {
103
103
  connecting: ["Connecting to coding agents", "Checking installed harnesses and capabilities.", 0],
@@ -151,6 +151,15 @@ var LOGOS = {
151
151
  ["path", { "fill-rule": "evenodd", "clip-rule": "evenodd", d: "M1 1h16.5v11H12v5.5H6.5V23H1V1zm5.5 5.5V12H12V6.5H6.5z" }],
152
152
  ["path", { d: "M17.5 12H23v11h-5.5V12z" }]
153
153
  ]
154
+ },
155
+ // Product mark shared with the first-party browser frontend favicon.
156
+ supercode: {
157
+ viewBox: "0 0 64 64",
158
+ paths: [
159
+ ["rect", { width: "64", height: "64", rx: "14", fill: "#111923" }],
160
+ ["path", { d: "M18 21h28v6H18zm0 13h18v6H18z", fill: "#5f8fff" }],
161
+ ["circle", { cx: "46", cy: "37", r: "7", fill: "none", stroke: "#8ce0aa", "stroke-width": "4" }]
162
+ ]
154
163
  }
155
164
  };
156
165
  function HarnessLogo({ id, activity, size = 28, onMissingLogo }) {
package/styles.css CHANGED
@@ -70,6 +70,7 @@
70
70
  .scui-logo[data-harness="gemini"] { color:#6e79dc; background:#f7f7f5 }
71
71
  .scui-logo[data-harness="goose"] { color:#101010; background:#f7f7f5 }
72
72
  .scui-logo[data-harness="pi"] { color:#111; background:#efefeb }
73
+ .scui-logo[data-harness="supercode"] { background:#111923 }
73
74
  .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) }
74
75
  .scui-logo[data-activity="working"] > i { background:var(--scui-accent); animation:scui-breathe 1.4s ease-in-out infinite }
75
76
  .scui-logo[data-activity="needs-input"] > i { background:var(--scui-warning) }
@@ -128,13 +129,16 @@
128
129
  .scui-truncated { display:block; margin-top:5px; color:var(--scui-warning) }
129
130
  .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 }
130
131
 
131
- .scui-activity { overflow:hidden; border:1px solid var(--scui-border); border-radius:9px; background:var(--scui-bg-raised) }
132
- .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 }
133
- .scui-activity-head strong { font-size:11px }.scui-activity-head small { color:var(--scui-muted) }.scui-spacer { flex:1 }
132
+ .scui-activity { min-width:0; color:var(--scui-muted) }
133
+ .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
+ .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 }
134
135
  .scui-fold { font-size:16px; transition:transform 120ms }.scui-fold[data-open="true"] { transform:rotate(90deg) }
135
- .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 }
136
- .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 }
137
- .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-activity:not([data-single="true"]) > div { margin-left:9px; padding-left:7px; border-left:1px solid var(--scui-border) }
137
+ .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
+ .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 }
139
+ .scui-tool-status { display:grid; flex:0 0 13px; width:13px; height:13px; place-items:center; color:var(--scui-success); font-size:10px }.scui-tool-status[data-status="pending"] { color:var(--scui-accent) }.scui-tool-status[data-status="error"] { color:var(--scui-danger); font-size:13px }.scui-tool-status i { box-sizing:border-box; width:11px; height:11px; border:1.5px solid currentColor; border-right-color:transparent; border-radius:50%; animation:scui-spin .75s linear infinite }
140
+ .scui-tool-chevron { flex:none; font-size:15px; line-height:1; transition:transform 120ms }.scui-tool[open] .scui-tool-chevron { transform:rotate(90deg) }
141
+ .scui-tool-detail { display:grid; gap:7px; max-height:220px; margin:2px 0 6px 20px; padding:8px; overflow:auto; border-left:1px solid var(--scui-border); background:var(--scui-bg-raised); font-size:10px }.scui-tool-detail dl,.scui-tool-detail section,.scui-tool-detail dl > div { display:grid; gap:3px; margin:0 }.scui-tool-detail dt,.scui-tool-detail section > strong { color:var(--scui-muted); font-weight:600 }.scui-tool-detail dd { min-width:0; margin:0 }.scui-tool-detail pre { margin:0; overflow-wrap:anywhere; color:var(--scui-fg); 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) }
138
142
  .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 }
139
143
  .scui-request-done { color:var(--scui-muted); font-size:10.5px }
140
144
  .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; box-sizing:border-box; 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"] { color:var(--scui-fg); text-decoration:line-through }