@volter-ai-dev/supercode-ui 0.1.7 → 0.1.9

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/messenger.mjs CHANGED
@@ -64,6 +64,11 @@ var MODES = /* @__PURE__ */ new Set(["none", "control", "mirror"]);
64
64
  var STRATEGIES = /* @__PURE__ */ new Set(["start", "resume", "attach", "branch", "reduce"]);
65
65
  var STARTUP = /* @__PURE__ */ new Set(["connecting", "starting", "discovering", "ready"]);
66
66
  var FIDELITY = /* @__PURE__ */ new Set(["byte_lossless", "value_lossless", "semantic"]);
67
+ var TOOL_CATEGORIES = /* @__PURE__ */ new Set(["read", "search", "edit", "command", "test", "web", "agent", "plan", "other"]);
68
+ var TOOL_DETAILS = /* @__PURE__ */ new Set(["file", "matches", "diff", "terminal", "web", "agent", "plan", "fields"]);
69
+ var MAX_TOOL_FIELDS = 8;
70
+ var MAX_TOOL_FIELD_CHARS = 800;
71
+ var MAX_TOOL_PREVIEW_CHARS = 4e3;
67
72
  function record(value) {
68
73
  return value !== null && typeof value === "object" && !Array.isArray(value) ? value : null;
69
74
  }
@@ -76,6 +81,132 @@ function number(value, fallback = 0) {
76
81
  function nullableNumber(value) {
77
82
  return typeof value === "number" && Number.isFinite(value) ? value : null;
78
83
  }
84
+ function boundedString(value, max) {
85
+ if (typeof value !== "string") return "";
86
+ return value.length <= max ? value : `${value.slice(0, max)}\u2026`;
87
+ }
88
+ function argumentObject(argumentsText) {
89
+ if (!argumentsText) return null;
90
+ try {
91
+ return record(JSON.parse(argumentsText));
92
+ } catch {
93
+ return null;
94
+ }
95
+ }
96
+ function firstString(source, keys) {
97
+ for (const key of keys) {
98
+ if (typeof source?.[key] === "string" && source[key].trim()) return source[key].trim();
99
+ }
100
+ return "";
101
+ }
102
+ function explicitNumber(sources, keys) {
103
+ for (const source of sources) {
104
+ for (const key of keys) {
105
+ const value = source?.[key];
106
+ const parsed = typeof value === "string" && value.trim() !== "" ? Number(value) : value;
107
+ if (typeof parsed === "number" && Number.isFinite(parsed)) return parsed;
108
+ }
109
+ }
110
+ return null;
111
+ }
112
+ function toolDetail(category) {
113
+ return { read: "file", search: "matches", edit: "diff", command: "terminal", test: "terminal", web: "web", agent: "agent", plan: "plan" }[category] ?? "fields";
114
+ }
115
+ function usefulToolFields(args) {
116
+ if (!args) return [];
117
+ const hidden = /* @__PURE__ */ new Set(["command", "cmd", "file_path", "target_file", "path", "query", "pattern", "url", "patch", "diff", "old_string", "new_string", "content", "prompt", "description", "task", "plan", "todos"]);
118
+ return Object.entries(args).flatMap(([key, value]) => {
119
+ if (hidden.has(key) || value === null || value === void 0) return [];
120
+ const rendered = typeof value === "string" ? value : JSON.stringify(value);
121
+ if (!rendered) return [];
122
+ return [{ label: key.replaceAll(/[_-]+/g, " ").replace(/^./, (letter) => letter.toLocaleUpperCase()), value: boundedString(rendered, MAX_TOOL_FIELD_CHARS) }];
123
+ }).slice(0, MAX_TOOL_FIELDS);
124
+ }
125
+ function planItems(args) {
126
+ const source = Array.isArray(args?.plan) ? args.plan : Array.isArray(args?.todos) ? args.todos : [];
127
+ return source.flatMap((item) => {
128
+ if (typeof item === "string" && item.trim()) return [{ label: boundedString(item.trim(), 300), status: "" }];
129
+ const value = record(item);
130
+ const label = firstString(value, ["step", "title", "content", "text"]);
131
+ if (!label) return [];
132
+ return [{ label: boundedString(label, 300), status: firstString(value, ["status"]) }];
133
+ }).slice(0, 12);
134
+ }
135
+ function editPreview(args, resultText) {
136
+ const direct = firstString(args, ["patch", "diff"]);
137
+ if (direct) return direct;
138
+ const oldText = firstString(args, ["old_string"]);
139
+ const newText = firstString(args, ["new_string"]);
140
+ if (oldText || newText) {
141
+ return [
142
+ ...oldText.split("\n").map((line) => `- ${line}`),
143
+ ...newText.split("\n").map((line) => `+ ${line}`)
144
+ ].join("\n");
145
+ }
146
+ return /^(?:diff --git|@@ |--- |\+\+\+ )/m.test(resultText ?? "") ? resultText : "";
147
+ }
148
+ function createToolPresentation(entry) {
149
+ const args = argumentObject(entry.arguments);
150
+ const category = toolCategory({ label: entry.label, arguments: entry.arguments });
151
+ const command = firstString(args, ["command", "cmd"]);
152
+ const path = firstString(args, ["file_path", "target_file", "path"]);
153
+ const query = firstString(args, ["query", "pattern"]);
154
+ const url = firstString(args, ["url"]);
155
+ const subject = firstString(args, ["description", "task", "prompt"]);
156
+ const target = path || command || query || url || subject || toolTarget(entry.arguments);
157
+ const previewSource = category === "edit" ? editPreview(args, entry.resultText) : entry.resultText ?? "";
158
+ const result = record(entry.resultContent);
159
+ const metadata = record(entry.metadata);
160
+ return {
161
+ category,
162
+ detail: toolDetail(category),
163
+ target: boundedString(target, 300),
164
+ command: boundedString(command, MAX_TOOL_FIELD_CHARS),
165
+ path: boundedString(path, MAX_TOOL_FIELD_CHARS),
166
+ query: boundedString(query, MAX_TOOL_FIELD_CHARS),
167
+ url: boundedString(url, MAX_TOOL_FIELD_CHARS),
168
+ subject: boundedString(subject, MAX_TOOL_FIELD_CHARS),
169
+ preview: boundedString(previewSource, MAX_TOOL_PREVIEW_CHARS),
170
+ fields: usefulToolFields(args),
171
+ items: planItems(args),
172
+ exitCode: explicitNumber([result, metadata], ["exit_code", "exitCode"]),
173
+ durationMs: explicitNumber([result, metadata], ["duration_ms", "durationMs", "elapsed_ms", "elapsedMs"]),
174
+ additions: explicitNumber([result, metadata], ["additions", "lines_added"]),
175
+ deletions: explicitNumber([result, metadata], ["deletions", "lines_removed"]),
176
+ matches: explicitNumber([result, metadata], ["matches", "match_count", "result_count"])
177
+ };
178
+ }
179
+ function readToolPresentation(value, entry) {
180
+ const generated = createToolPresentation(entry);
181
+ const item = record(value);
182
+ if (!item) return generated;
183
+ const fields = Array.isArray(item.fields) ? item.fields.flatMap((raw) => {
184
+ const field = record(raw);
185
+ return field && typeof field.label === "string" && typeof field.value === "string" ? [{ label: boundedString(field.label, 80), value: boundedString(field.value, MAX_TOOL_FIELD_CHARS) }] : [];
186
+ }).slice(0, MAX_TOOL_FIELDS) : generated.fields;
187
+ const items = Array.isArray(item.items) ? item.items.flatMap((raw) => {
188
+ const planItem = record(raw);
189
+ return planItem && typeof planItem.label === "string" ? [{ label: boundedString(planItem.label, 300), status: boundedString(planItem.status, 40) }] : [];
190
+ }).slice(0, 12) : generated.items;
191
+ return {
192
+ category: TOOL_CATEGORIES.has(item.category) ? item.category : generated.category,
193
+ detail: TOOL_DETAILS.has(item.detail) ? item.detail : generated.detail,
194
+ target: boundedString(item.target, 300) || generated.target,
195
+ command: boundedString(item.command, MAX_TOOL_FIELD_CHARS) || generated.command,
196
+ path: boundedString(item.path, MAX_TOOL_FIELD_CHARS) || generated.path,
197
+ query: boundedString(item.query, MAX_TOOL_FIELD_CHARS) || generated.query,
198
+ url: boundedString(item.url, MAX_TOOL_FIELD_CHARS) || generated.url,
199
+ subject: boundedString(item.subject, MAX_TOOL_FIELD_CHARS) || generated.subject,
200
+ preview: boundedString(item.preview, MAX_TOOL_PREVIEW_CHARS) || generated.preview,
201
+ fields,
202
+ items,
203
+ exitCode: nullableNumber(item.exitCode) ?? generated.exitCode,
204
+ durationMs: nullableNumber(item.durationMs) ?? generated.durationMs,
205
+ additions: nullableNumber(item.additions) ?? generated.additions,
206
+ deletions: nullableNumber(item.deletions) ?? generated.deletions,
207
+ matches: nullableNumber(item.matches) ?? generated.matches
208
+ };
209
+ }
79
210
  function readTranscript(value) {
80
211
  if (!Array.isArray(value)) return [];
81
212
  const result = [];
@@ -93,6 +224,7 @@ function readTranscript(value) {
93
224
  if (typeof item[key] === "string") entry[key] = item[key];
94
225
  }
95
226
  if (["pending", "completed", "error"].includes(item.status)) entry.status = item.status;
227
+ if (item.role === "tool") entry.presentation = readToolPresentation(item.presentation, entry);
96
228
  if (typeof item.streaming === "boolean") entry.streaming = item.streaming;
97
229
  if (Array.isArray(item.context)) {
98
230
  entry.context = item.context.flatMap((raw) => {
@@ -300,6 +432,7 @@ function groupConversation(entries) {
300
432
  return blocks;
301
433
  }
302
434
  function toolCategory(entry) {
435
+ if (TOOL_CATEGORIES.has(entry.presentation?.category)) return entry.presentation.category;
303
436
  const name = entry.label?.toLocaleLowerCase() ?? "";
304
437
  if (/read|view|open_file|list_dir/.test(name)) return "read";
305
438
  if (/search|find|grep|glob/.test(name)) return "search";
@@ -307,7 +440,8 @@ function toolCategory(entry) {
307
440
  if (/test|typecheck|lint|build/.test(name)) return "test";
308
441
  if (/terminal|bash|shell|command|exec/.test(name)) return /\b(test|typecheck|lint|build)\b/i.test(entry.arguments ?? "") ? "test" : "command";
309
442
  if (/browser|web|fetch|url/.test(name)) return "web";
310
- if (/subagent|spawn|task/.test(name)) return "agent";
443
+ if (/update.?plan|todo|checklist/.test(name)) return "plan";
444
+ if (/subagent|spawn.?agent|delegate|^task$/.test(name)) return "agent";
311
445
  return "other";
312
446
  }
313
447
  function toolTarget(argumentsText) {
@@ -329,9 +463,12 @@ function compactToolTarget(target, workspace) {
329
463
  function activitySummary(entries) {
330
464
  const counts = /* @__PURE__ */ new Map();
331
465
  for (const entry of entries) counts.set(toolCategory(entry), (counts.get(toolCategory(entry)) ?? 0) + 1);
466
+ const failed = entries.filter((entry) => entry.status === "error").length;
467
+ const pending = entries.filter((entry) => entry.status === "pending").length;
468
+ if (pending) return `${entries.length} actions in progress`;
469
+ if (failed) return `${entries.length} actions \xB7 ${failed} failed`;
332
470
  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 ")}`;
471
+ return `${entries.length} actions`;
335
472
  }
336
473
  function canContinueHere(state) {
337
474
  if (state.mode !== "mirror" || state.canSend) return false;
@@ -470,7 +607,7 @@ function Markdown({ value }) {
470
607
  }
471
608
 
472
609
  // src/conversation.jsx
473
- import { jsx as jsx3, jsxs as jsxs2 } from "preact/jsx-runtime";
610
+ import { Fragment, jsx as jsx3, jsxs as jsxs2 } from "preact/jsx-runtime";
474
611
  function LoadingStatus({ state, compact = false }) {
475
612
  const copy = {
476
613
  connecting: ["Connecting to coding agents", "Checking installed harnesses and capabilities.", 0],
@@ -518,6 +655,185 @@ function ContextDisclosure({ context }) {
518
655
  ] }, item.id ?? index)) })
519
656
  ] });
520
657
  }
658
+ var TOOL_ICONS = {
659
+ read: () => /* @__PURE__ */ jsxs2(Fragment, { children: [
660
+ /* @__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" }),
661
+ /* @__PURE__ */ jsx3("path", { d: "M12 2.9v3h2.85M6 9h6M6 12h4" })
662
+ ] }),
663
+ search: () => /* @__PURE__ */ jsxs2(Fragment, { children: [
664
+ /* @__PURE__ */ jsx3("circle", { cx: "8", cy: "8", r: "4.5" }),
665
+ /* @__PURE__ */ jsx3("path", { d: "m11.5 11.5 3 3" })
666
+ ] }),
667
+ edit: () => /* @__PURE__ */ jsxs2(Fragment, { children: [
668
+ /* @__PURE__ */ jsx3("path", { d: "m11.75 3.25 3 3-8.5 8.5-3.75.75.75-3.75 8.5-8.5Z" }),
669
+ /* @__PURE__ */ jsx3("path", { d: "m10 5 3 3" })
670
+ ] }),
671
+ command: () => /* @__PURE__ */ jsx3(Fragment, { children: /* @__PURE__ */ jsx3("path", { d: "m3 5 3 3-3 3M8 12h6" }) }),
672
+ test: () => /* @__PURE__ */ jsxs2(Fragment, { children: [
673
+ /* @__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" }),
674
+ /* @__PURE__ */ jsx3("path", { d: "M5 2.5h8" })
675
+ ] }),
676
+ web: () => /* @__PURE__ */ jsxs2(Fragment, { children: [
677
+ /* @__PURE__ */ jsx3("circle", { cx: "9", cy: "9", r: "6.5" }),
678
+ /* @__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" })
679
+ ] }),
680
+ agent: () => /* @__PURE__ */ jsxs2(Fragment, { children: [
681
+ /* @__PURE__ */ jsx3("circle", { cx: "9", cy: "6", r: "2.5" }),
682
+ /* @__PURE__ */ jsx3("path", { d: "M4 15c.4-3 2-4.5 5-4.5s4.6 1.5 5 4.5" })
683
+ ] }),
684
+ plan: () => /* @__PURE__ */ jsx3(Fragment, { children: /* @__PURE__ */ jsx3("path", { d: "m3 5 1 1 2-2M3 9l1 1 2-2M3 13l1 1 2-2M8 5h7M8 9h7M8 13h7" }) }),
685
+ other: () => /* @__PURE__ */ jsxs2(Fragment, { children: [
686
+ /* @__PURE__ */ jsx3("path", { d: "M9 2.5v3M9 12.5v3M2.5 9h3M12.5 9h3" }),
687
+ /* @__PURE__ */ jsx3("circle", { cx: "9", cy: "9", r: "3.5" })
688
+ ] })
689
+ };
690
+ function ToolIcon({ category }) {
691
+ const Glyph = TOOL_ICONS[category] ?? TOOL_ICONS.other;
692
+ 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, {}) });
693
+ }
694
+ function toolAction(entry, category) {
695
+ const status = entry.status ?? "completed";
696
+ const actions = {
697
+ read: ["Reading", "Read", "Read failed"],
698
+ search: ["Searching", "Searched", "Search failed"],
699
+ edit: ["Editing", "Edited", "Edit failed"],
700
+ command: ["Running command", "Ran command", "Command failed"],
701
+ test: ["Running tests", "Ran tests", "Tests failed"],
702
+ web: ["Browsing", "Browsed", "Browser action failed"],
703
+ agent: ["Starting agent", "Started agent", "Agent failed"],
704
+ plan: ["Updating plan", "Updated plan", "Plan update failed"]
705
+ };
706
+ const position = status === "pending" ? 0 : status === "error" ? 2 : 1;
707
+ if (actions[category]) return actions[category][position];
708
+ const label = entry.label?.split(/__|\//).at(-1)?.replaceAll(/[_-]+/g, " ") || "Tool";
709
+ return status === "error" ? `${label} failed` : label;
710
+ }
711
+ function argumentLabel(key) {
712
+ const labels = { cmd: "Command", command: "Command", cwd: "Working directory", file_path: "File", target_file: "File", path: "Path", query: "Query", pattern: "Pattern", url: "URL" };
713
+ return labels[key] ?? key.replaceAll(/[_-]+/g, " ").replace(/^./, (letter) => letter.toLocaleUpperCase());
714
+ }
715
+ function argumentRows(argumentsText) {
716
+ if (!argumentsText) return [];
717
+ try {
718
+ const value = JSON.parse(argumentsText);
719
+ if (value && typeof value === "object" && !Array.isArray(value)) {
720
+ return Object.entries(value).map(([key, item]) => ({
721
+ key,
722
+ label: argumentLabel(key),
723
+ value: typeof item === "string" ? item : JSON.stringify(item, null, 2)
724
+ }));
725
+ }
726
+ } catch {
727
+ }
728
+ return [{ key: "arguments", label: "Details", value: argumentsText }];
729
+ }
730
+ function stripAnsi(value) {
731
+ return value?.replaceAll(/[\u001B\u009B][[\]()#;?]*(?:(?:(?:[a-zA-Z\d]*(?:;[-a-zA-Z\d\/#&.:=?%@~_]+)*)?\u0007)|(?:(?:\d{1,4}(?:[;:]\d{0,4})*)?[\dA-PR-TZcf-nq-uy=><~]))/g, "") ?? "";
732
+ }
733
+ function formatDuration(value) {
734
+ if (value === null) return "";
735
+ if (value < 1e3) return `${Math.round(value)}ms`;
736
+ return `${(value / 1e3).toFixed(value < 1e4 ? 1 : 0)}s`;
737
+ }
738
+ function ToolMetrics({ presentation }) {
739
+ const metrics = [
740
+ presentation.matches === null ? "" : `${presentation.matches} ${presentation.matches === 1 ? "match" : "matches"}`,
741
+ presentation.additions === null ? "" : `+${presentation.additions}`,
742
+ presentation.deletions === null ? "" : `\u2212${presentation.deletions}`,
743
+ presentation.exitCode === null ? "" : `exit ${presentation.exitCode}`,
744
+ formatDuration(presentation.durationMs)
745
+ ].filter(Boolean);
746
+ return metrics.length ? /* @__PURE__ */ jsx3("div", { class: "scui-tool-metrics", children: metrics.map((metric) => /* @__PURE__ */ jsx3("span", { children: metric }, metric)) }) : null;
747
+ }
748
+ function LinePreview({ value, kind }) {
749
+ if (!value) return null;
750
+ return /* @__PURE__ */ jsx3("ol", { class: "scui-code-preview", "data-kind": kind, children: stripAnsi(value).split("\n").map((line, index) => {
751
+ const tone = kind === "diff" ? line.startsWith("+") && !line.startsWith("+++") ? "add" : line.startsWith("-") && !line.startsWith("---") ? "remove" : line.startsWith("@@") ? "hunk" : "plain" : "plain";
752
+ return /* @__PURE__ */ jsxs2("li", { "data-tone": tone, children: [
753
+ /* @__PURE__ */ jsx3("span", { children: index + 1 }),
754
+ /* @__PURE__ */ jsx3("code", { children: line || " " })
755
+ ] }, index);
756
+ }) });
757
+ }
758
+ function TerminalPreview({ presentation, pending, failed }) {
759
+ return /* @__PURE__ */ jsxs2("section", { class: "scui-terminal", children: [
760
+ /* @__PURE__ */ jsxs2("header", { children: [
761
+ /* @__PURE__ */ jsxs2("span", { "aria-hidden": "true", children: [
762
+ /* @__PURE__ */ jsx3("i", {}),
763
+ /* @__PURE__ */ jsx3("i", {}),
764
+ /* @__PURE__ */ jsx3("i", {})
765
+ ] }),
766
+ /* @__PURE__ */ jsx3("code", { children: presentation.command ? `$ ${presentation.command}` : "Terminal" })
767
+ ] }),
768
+ presentation.preview ? /* @__PURE__ */ jsx3("pre", { "data-error": failed, children: stripAnsi(presentation.preview) }) : pending ? /* @__PURE__ */ jsxs2("div", { class: "scui-terminal-wait", children: [
769
+ /* @__PURE__ */ jsx3("i", {}),
770
+ " Waiting for output"
771
+ ] }) : /* @__PURE__ */ jsx3("div", { class: "scui-terminal-empty", children: "No output" })
772
+ ] });
773
+ }
774
+ function SearchPreview({ presentation }) {
775
+ const lines = stripAnsi(presentation.preview).split("\n").filter(Boolean);
776
+ return /* @__PURE__ */ jsxs2("section", { class: "scui-search-preview", children: [
777
+ presentation.query ? /* @__PURE__ */ jsxs2("header", { children: [
778
+ /* @__PURE__ */ jsx3("span", { children: "Search" }),
779
+ /* @__PURE__ */ jsx3("code", { children: presentation.query })
780
+ ] }) : null,
781
+ lines.length ? /* @__PURE__ */ jsx3("ol", { children: lines.map((line, index) => {
782
+ const match = /^(.*?):(\d+)(?::(\d+))?:(.*)$/.exec(line);
783
+ return /* @__PURE__ */ jsx3("li", { children: match ? /* @__PURE__ */ jsxs2(Fragment, { children: [
784
+ /* @__PURE__ */ jsx3("code", { children: match[1] }),
785
+ /* @__PURE__ */ jsxs2("small", { children: [
786
+ match[2],
787
+ match[3] ? `:${match[3]}` : ""
788
+ ] }),
789
+ /* @__PURE__ */ jsx3("span", { children: match[4] })
790
+ ] }) : /* @__PURE__ */ jsx3("span", { children: line }) }, index);
791
+ }) }) : /* @__PURE__ */ jsx3("p", { children: "No textual results" })
792
+ ] });
793
+ }
794
+ function ToolPreview({ presentation, entry }) {
795
+ const pending = entry.status === "pending";
796
+ const failed = entry.status === "error";
797
+ if (presentation.detail === "terminal") return /* @__PURE__ */ jsx3(TerminalPreview, { presentation, pending, failed });
798
+ if (presentation.detail === "diff") return presentation.preview ? /* @__PURE__ */ jsx3(LinePreview, { value: presentation.preview, kind: "diff" }) : /* @__PURE__ */ jsx3("div", { class: "scui-tool-empty", children: "Edit completed without a textual diff" });
799
+ if (presentation.detail === "file") return presentation.preview ? /* @__PURE__ */ jsx3(LinePreview, { value: presentation.preview, kind: "file" }) : /* @__PURE__ */ jsx3("div", { class: "scui-tool-empty", children: "File contents were not included in this event" });
800
+ if (presentation.detail === "matches") return /* @__PURE__ */ jsx3(SearchPreview, { presentation });
801
+ if (presentation.detail === "web") return /* @__PURE__ */ jsxs2("section", { class: "scui-web-preview", children: [
802
+ presentation.url ? /* @__PURE__ */ jsx3("code", { children: presentation.url }) : null,
803
+ presentation.preview ? /* @__PURE__ */ jsx3("p", { children: stripAnsi(presentation.preview) }) : /* @__PURE__ */ jsx3("small", { children: pending ? "Waiting for the page" : "No page summary returned" })
804
+ ] });
805
+ if (presentation.detail === "agent") return /* @__PURE__ */ jsxs2("section", { class: "scui-agent-preview", children: [
806
+ presentation.subject ? /* @__PURE__ */ jsx3("p", { children: presentation.subject }) : null,
807
+ presentation.preview ? /* @__PURE__ */ jsx3("pre", { children: stripAnsi(presentation.preview) }) : /* @__PURE__ */ jsx3("small", { children: pending ? "Agent is working" : "No textual handoff returned" })
808
+ ] });
809
+ if (presentation.detail === "plan") return /* @__PURE__ */ jsx3("ol", { class: "scui-plan-preview", children: presentation.items?.map((item, index) => /* @__PURE__ */ jsxs2("li", { "data-status": item.status, children: [
810
+ /* @__PURE__ */ jsx3("i", { "aria-hidden": "true" }),
811
+ /* @__PURE__ */ jsx3("span", { children: item.label })
812
+ ] }, `${item.label}:${index}`)) });
813
+ return presentation.preview ? /* @__PURE__ */ jsx3("pre", { class: "scui-tool-output", "data-error": failed, children: stripAnsi(presentation.preview) }) : null;
814
+ }
815
+ function TechnicalDetails({ entry }) {
816
+ if (!entry.arguments && !entry.resultText) return null;
817
+ return /* @__PURE__ */ jsxs2("details", { class: "scui-tool-technical", children: [
818
+ /* @__PURE__ */ jsx3("summary", { children: "Technical details" }),
819
+ /* @__PURE__ */ jsxs2("div", { children: [
820
+ entry.arguments ? /* @__PURE__ */ jsxs2("section", { children: [
821
+ /* @__PURE__ */ jsx3("strong", { children: "Native arguments" }),
822
+ /* @__PURE__ */ jsx3("dl", { children: argumentRows(entry.arguments).map((row) => /* @__PURE__ */ jsxs2("div", { children: [
823
+ /* @__PURE__ */ jsx3("dt", { children: row.label }),
824
+ /* @__PURE__ */ jsx3("dd", { children: /* @__PURE__ */ jsx3("pre", { children: row.value }) })
825
+ ] }, row.key)) })
826
+ ] }) : null,
827
+ entry.resultText ? /* @__PURE__ */ jsxs2("section", { children: [
828
+ /* @__PURE__ */ jsx3("strong", { children: "Native result" }),
829
+ /* @__PURE__ */ jsxs2("pre", { "data-error": entry.status === "error", children: [
830
+ entry.resultText,
831
+ entry.truncated ? "\n[truncated]" : ""
832
+ ] })
833
+ ] }) : null
834
+ ] })
835
+ ] });
836
+ }
521
837
  function TranscriptEntry({ entry, state, adapter }) {
522
838
  if (entry.role === "request") return /* @__PURE__ */ jsx3(RequestCard, { entry, adapter, canRespond: state.canRespond });
523
839
  if (entry.role === "reasoning") {
@@ -533,36 +849,35 @@ function TranscriptEntry({ entry, state, adapter }) {
533
849
  entry.truncated ? /* @__PURE__ */ jsx3("small", { class: "scui-truncated", children: "Entry truncated by host \xB7 load native session for the complete value" }) : null
534
850
  ] });
535
851
  }
536
- function ToolRow({ entry, workspace }) {
537
- const [open, setOpen] = useState2(entry.status === "pending");
538
- const detailId = useId();
539
- const target = compactToolTarget(toolTarget(entry.arguments), workspace);
540
- const hasDetail = Boolean(entry.arguments || entry.resultText);
541
- 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]" : ""
561
- ] }) : null
562
- ] }) : null
852
+ function ToolRow({ entry, workspace, open = false }) {
853
+ const presentation = entry.presentation ?? createToolPresentation(entry);
854
+ const target = compactToolTarget(presentation.target, workspace);
855
+ const hasDetail = Boolean(entry.arguments || entry.resultText || presentation.preview || presentation.fields.length);
856
+ const category = presentation.category ?? toolCategory(entry);
857
+ const summary = /* @__PURE__ */ jsxs2(Fragment, { children: [
858
+ /* @__PURE__ */ jsx3(ToolIcon, { category }),
859
+ /* @__PURE__ */ jsx3("strong", { children: toolAction(entry, category) }),
860
+ target ? /* @__PURE__ */ jsx3("code", { class: "scui-tool-target", title: presentation.target, children: target }) : null,
861
+ /* @__PURE__ */ jsx3("span", { class: "scui-spacer" }),
862
+ /* @__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" }),
863
+ hasDetail ? /* @__PURE__ */ jsx3("span", { class: "scui-tool-chevron", "aria-hidden": "true", children: "\u203A" }) : null
864
+ ] });
865
+ 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 }) });
866
+ return /* @__PURE__ */ jsxs2("details", { class: "scui-tool", "data-status": entry.status ?? "completed", "data-category": category, open: open || entry.status === "pending", children: [
867
+ /* @__PURE__ */ jsx3("summary", { class: "scui-tool-head", children: summary }),
868
+ /* @__PURE__ */ jsxs2("div", { class: "scui-tool-detail", children: [
869
+ /* @__PURE__ */ jsx3(ToolMetrics, { presentation }),
870
+ /* @__PURE__ */ jsx3(ToolPreview, { presentation, entry }),
871
+ presentation.fields.length ? /* @__PURE__ */ jsx3("dl", { class: "scui-tool-fields", children: presentation.fields.map((field) => /* @__PURE__ */ jsxs2("div", { children: [
872
+ /* @__PURE__ */ jsx3("dt", { children: field.label }),
873
+ /* @__PURE__ */ jsx3("dd", { children: field.value })
874
+ ] }, field.label)) }) : null,
875
+ /* @__PURE__ */ jsx3(TechnicalDetails, { entry })
876
+ ] })
563
877
  ] });
564
878
  }
565
879
  function ActivityGroup({ entries, state }) {
880
+ 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
881
  const active = entries.some((entry) => entry.status === "pending");
567
882
  const [open, setOpen] = useState2(active);
568
883
  const id = useId();
@@ -575,7 +890,7 @@ function ActivityGroup({ entries, state }) {
575
890
  /* @__PURE__ */ jsx3("strong", { children: activitySummary(entries) }),
576
891
  /* @__PURE__ */ jsx3("span", { class: "scui-spacer" }),
577
892
  /* @__PURE__ */ jsxs2("small", { children: [
578
- entries.filter((entry) => entry.status === "completed").length,
893
+ entries.filter((entry) => entry.status !== "pending").length,
579
894
  "/",
580
895
  entries.length
581
896
  ] })
@@ -939,9 +1254,13 @@ function Chat({ state, adapter, onBack, onNew, onClose, components, slots, label
939
1254
  }
940
1255
  function NewChat({ state, adapter, onBack, onClose, onStarted, labels }) {
941
1256
  const startable = state.harnesses.filter((item) => item.startable);
1257
+ const startableKey = startable.map((item) => item.id).join("\0");
942
1258
  const [harness, setHarness] = useState4(startable[0]?.id ?? "");
943
1259
  const [draft, setDraft] = useState4("");
944
1260
  const [starting, setStarting] = useState4(false);
1261
+ useEffect4(() => {
1262
+ if (!startable.some((item) => item.id === harness)) setHarness(startable[0]?.id ?? "");
1263
+ }, [harness, startableKey]);
945
1264
  useEffect4(() => {
946
1265
  if (starting && (state.busy || state.transcript.length)) onStarted();
947
1266
  }, [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.7",
3
+ "version": "0.1.9",
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],
package/styles.css CHANGED
@@ -129,13 +129,24 @@
129
129
  .scui-truncated { display:block; margin-top:5px; color:var(--scui-warning) }
130
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 }
131
131
 
132
- .scui-activity { overflow:hidden; border:1px solid var(--scui-border); border-radius:9px; background:var(--scui-bg-raised) }
133
- .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 }
134
- .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 }
135
135
  .scui-fold { font-size:16px; transition:transform 120ms }.scui-fold[data-open="true"] { transform:rotate(90deg) }
136
- .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 }
137
- .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 }
138
- .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:240px; margin:2px 0 7px 20px; overflow:auto; border:1px solid var(--scui-border); border-radius:7px; background:var(--scui-bg-raised); font-size:10px }
142
+ .scui-tool-metrics { display:flex; flex-wrap:wrap; gap:4px; padding:7px 7px 0 }.scui-tool-metrics span { padding:2px 5px; border:1px solid var(--scui-border); border-radius:9px; color:var(--scui-muted); font:9px ui-monospace,SFMono-Regular,Menlo,monospace }.scui-tool[data-status="error"] .scui-tool-metrics span { border-color:color-mix(in srgb,var(--scui-danger) 30%,var(--scui-border)) }
143
+ .scui-terminal { min-width:0; overflow:hidden; background:color-mix(in srgb,var(--scui-fg) 7%,var(--scui-bg)); color:var(--scui-fg) }.scui-terminal header { display:flex; align-items:center; gap:7px; min-height:29px; padding:0 8px; border-bottom:1px solid var(--scui-border) }.scui-terminal header > span { display:flex; gap:3px }.scui-terminal header i { width:5px; height:5px; border-radius:50%; background:var(--scui-border-strong) }.scui-terminal header code { min-width:0; overflow:hidden; color:var(--scui-muted); font:9.5px ui-monospace,SFMono-Regular,Menlo,monospace; text-overflow:ellipsis; white-space:nowrap }.scui-terminal pre,.scui-tool-output,.scui-agent-preview pre { max-height:150px; margin:0; padding:8px; overflow:auto; overflow-wrap:anywhere; color:var(--scui-fg); font:10px/1.5 ui-monospace,SFMono-Regular,Menlo,monospace; white-space:pre-wrap }.scui-terminal pre[data-error="true"],.scui-tool-output[data-error="true"] { color:var(--scui-danger) }.scui-terminal-wait,.scui-terminal-empty { display:flex; align-items:center; gap:6px; padding:9px; color:var(--scui-muted) }.scui-terminal-wait i { width:5px; height:5px; border-radius:50%; background:var(--scui-accent); animation:scui-breathe 1.2s ease-in-out infinite }
144
+ .scui-code-preview { margin:0; padding:5px 0; overflow:auto; counter-reset:line; color:var(--scui-fg); font:9.5px/1.5 ui-monospace,SFMono-Regular,Menlo,monospace; list-style:none }.scui-code-preview li { display:grid; grid-template-columns:31px minmax(max-content,1fr); min-height:15px }.scui-code-preview li > span { padding-right:7px; color:var(--scui-muted); text-align:right; user-select:none }.scui-code-preview code { padding:0 8px; white-space:pre }.scui-code-preview li[data-tone="add"] { background:color-mix(in srgb,var(--scui-success) 11%,transparent) }.scui-code-preview li[data-tone="remove"] { background:color-mix(in srgb,var(--scui-danger) 10%,transparent) }.scui-code-preview li[data-tone="hunk"] { color:var(--scui-accent) }
145
+ .scui-search-preview header { display:flex; align-items:center; gap:6px; padding:7px 8px; border-bottom:1px solid var(--scui-border) }.scui-search-preview header span { color:var(--scui-muted) }.scui-search-preview header code { min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap }.scui-search-preview ol { display:grid; margin:0; padding:3px 0; list-style:none }.scui-search-preview li { display:grid; grid-template-columns:minmax(0,auto) auto minmax(70px,1fr); align-items:baseline; gap:5px; padding:3px 8px; border-bottom:1px solid color-mix(in srgb,var(--scui-border) 60%,transparent) }.scui-search-preview li:last-child { border:0 }.scui-search-preview li code { overflow:hidden; color:var(--scui-accent); text-overflow:ellipsis; white-space:nowrap }.scui-search-preview li small { color:var(--scui-muted) }.scui-search-preview li span { min-width:0; overflow:hidden; color:var(--scui-fg); text-overflow:ellipsis; white-space:nowrap }.scui-search-preview p { margin:0; padding:8px; color:var(--scui-muted) }
146
+ .scui-web-preview,.scui-agent-preview { display:grid; gap:6px; padding:8px }.scui-web-preview > code { overflow:hidden; color:var(--scui-accent); text-overflow:ellipsis; white-space:nowrap }.scui-web-preview p,.scui-agent-preview p { margin:0; color:var(--scui-fg); line-height:1.45 }.scui-web-preview small,.scui-agent-preview small,.scui-tool-empty { padding:8px; color:var(--scui-muted) }
147
+ .scui-plan-preview { display:grid; gap:5px; margin:0; padding:8px; list-style:none }.scui-plan-preview li { display:flex; align-items:flex-start; gap:6px; color:var(--scui-fg) }.scui-plan-preview li i { flex:0 0 8px; width:8px; height:8px; margin-top:3px; border:1px solid var(--scui-border-strong); border-radius:50% }.scui-plan-preview li[data-status="completed"] { color:var(--scui-muted); text-decoration:line-through }.scui-plan-preview li[data-status="completed"] i { border-color:var(--scui-success); background:var(--scui-success) }.scui-plan-preview li[data-status="in_progress"] i { border-color:var(--scui-accent); box-shadow:inset 0 0 0 2px var(--scui-bg-raised); background:var(--scui-accent) }
148
+ .scui-tool-fields { display:grid; gap:5px; margin:0; padding:0 8px 8px }.scui-tool-fields > div { display:grid; grid-template-columns:minmax(65px,auto) 1fr; gap:8px }.scui-tool-fields dt { color:var(--scui-muted) }.scui-tool-fields dd { min-width:0; margin:0; overflow-wrap:anywhere; color:var(--scui-fg) }
149
+ .scui-tool-technical { margin:0 8px 8px; color:var(--scui-muted) }.scui-tool-technical > summary { cursor:pointer; font-size:9.5px }.scui-tool-technical > div { display:grid; gap:8px; margin-top:5px; padding:7px; border-left:1px solid var(--scui-border) }.scui-tool-technical section,.scui-tool-technical dl,.scui-tool-technical dl > div { display:grid; gap:3px; margin:0 }.scui-tool-technical strong,.scui-tool-technical dt { color:var(--scui-muted); font-weight:600 }.scui-tool-technical dd { min-width:0; margin:0 }.scui-tool-technical pre { max-height:110px; margin:0; overflow:auto; overflow-wrap:anywhere; color:var(--scui-fg); font:9.5px/1.45 ui-monospace,SFMono-Regular,Menlo,monospace; white-space:pre-wrap }.scui-tool-technical pre[data-error="true"] { color:var(--scui-danger) }
139
150
  .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 }
140
151
  .scui-request-done { color:var(--scui-muted); font-size:10.5px }
141
152
  .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 }