@volter-ai-dev/supercode-ui 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,375 @@
1
+ // src/components.jsx
2
+ import MarkdownIt from "markdown-it";
3
+ import { useEffect, useId, useLayoutEffect, useMemo, useRef, useState } from "preact/hooks";
4
+
5
+ // core.mjs
6
+ var HARNESS_NAMES = Object.freeze({
7
+ "claude-code": "Claude Code",
8
+ codex: "Codex",
9
+ opencode: "OpenCode",
10
+ pi: "Pi",
11
+ grok: "Grok"
12
+ });
13
+ var DEFAULT_LABELS = Object.freeze({
14
+ chats: "Chats",
15
+ newChat: "New chat",
16
+ searchChats: "Search chats",
17
+ askAgent: "Ask your agent\u2026",
18
+ continueHere: "Continue here",
19
+ joinLive: "Join live",
20
+ forkHere: "Fork here"
21
+ });
22
+ var EMPTY_UI_STATE = Object.freeze({
23
+ pill: Object.freeze({ tone: "off", label: "connecting\u2026" }),
24
+ startup: "connecting",
25
+ transcript: Object.freeze([]),
26
+ busy: false,
27
+ operation: null,
28
+ needsInput: false,
29
+ harness: "",
30
+ mode: "none",
31
+ strategy: null,
32
+ canSend: false,
33
+ canResume: false,
34
+ canBranch: false,
35
+ canAttach: false,
36
+ canDetach: false,
37
+ canOpenTerminal: false,
38
+ canExport: false,
39
+ canReduce: false,
40
+ canInterrupt: false,
41
+ canRespond: false,
42
+ messaging: null,
43
+ workspace: "",
44
+ taskPlan: Object.freeze({ source: "none", items: Object.freeze([]), residueCount: 0, observedAt: null }),
45
+ semantics: Object.freeze({ fidelity: null, residue: Object.freeze([]), residueCount: 0, parseErrors: 0, rawRecords: 0, subagents: Object.freeze([]) }),
46
+ terminalHandoff: null,
47
+ exportBackTarget: null,
48
+ exportReceipt: null,
49
+ reductionReceipt: null,
50
+ error: null,
51
+ recoverable: false,
52
+ harnesses: Object.freeze([]),
53
+ history: Object.freeze({ sessionLimit: 0, hasMoreSessions: false, transcriptLimit: 120, hasEarlier: false }),
54
+ savedDraft: "",
55
+ attention: Object.freeze([]),
56
+ sessions: Object.freeze([]),
57
+ attached: null,
58
+ owned: null,
59
+ attachError: null
60
+ });
61
+ function harnessDisplayName(id) {
62
+ return HARNESS_NAMES[id] ?? id;
63
+ }
64
+ function groupConversation(entries) {
65
+ const blocks = [];
66
+ for (const entry of entries) {
67
+ if (entry.role !== "tool") {
68
+ blocks.push({ kind: "entry", id: entry.id, entry });
69
+ continue;
70
+ }
71
+ const previous = blocks.at(-1);
72
+ if (previous?.kind === "activity") previous.entries.push(entry);
73
+ else blocks.push({ kind: "activity", id: `activity:${entry.id}`, entries: [entry] });
74
+ }
75
+ return blocks;
76
+ }
77
+ function toolCategory(entry) {
78
+ const name = entry.label?.toLocaleLowerCase() ?? "";
79
+ if (/read|view|open_file|list_dir/.test(name)) return "read";
80
+ if (/search|find|grep|glob/.test(name)) return "search";
81
+ if (/edit|write|patch|replace|create_file/.test(name)) return "edit";
82
+ if (/test|typecheck|lint|build/.test(name)) return "test";
83
+ if (/terminal|bash|shell|command|exec/.test(name)) return /\b(test|typecheck|lint|build)\b/i.test(entry.arguments ?? "") ? "test" : "command";
84
+ if (/browser|web|fetch|url/.test(name)) return "web";
85
+ if (/subagent|spawn|task/.test(name)) return "agent";
86
+ return "other";
87
+ }
88
+ function toolTarget(argumentsText) {
89
+ if (!argumentsText) return "";
90
+ try {
91
+ const args = JSON.parse(argumentsText);
92
+ for (const key of ["file_path", "target_file", "path", "command", "cmd", "query", "pattern", "url"]) {
93
+ if (typeof args?.[key] === "string") return args[key];
94
+ }
95
+ } catch {
96
+ return argumentsText.length > 120 ? `${argumentsText.slice(0, 117)}\u2026` : argumentsText;
97
+ }
98
+ return "";
99
+ }
100
+ function compactToolTarget(target, workspace) {
101
+ const prefix = workspace && !workspace.endsWith("/") ? `${workspace}/` : workspace;
102
+ return prefix && target.startsWith(prefix) ? target.slice(prefix.length) : target;
103
+ }
104
+ function activitySummary(entries) {
105
+ const counts = /* @__PURE__ */ new Map();
106
+ for (const entry of entries) counts.set(toolCategory(entry), (counts.get(toolCategory(entry)) ?? 0) + 1);
107
+ if ([...counts.keys()].every((key) => key === "read" || key === "search")) return `Explored ${entries.length} ${entries.length === 1 ? "item" : "items"}`;
108
+ const labels = { edit: "changed", test: "tests/builds", command: "commands", read: "reads", search: "searches", web: "web", agent: "agents", other: "other" };
109
+ return `Activity \xB7 ${Object.entries(labels).flatMap(([key, label]) => counts.has(key) ? [`${counts.get(key)} ${label}`] : []).join(" \xB7 ")}`;
110
+ }
111
+
112
+ // src/components.jsx
113
+ import { jsx, jsxs } from "preact/jsx-runtime";
114
+ var markdown = new MarkdownIt({ html: false, linkify: true, breaks: false });
115
+ var defaultLinkOpen = markdown.renderer.rules.link_open;
116
+ markdown.renderer.rules.link_open = (tokens, index, options, env, self) => {
117
+ tokens[index]?.attrSet("target", "_blank");
118
+ tokens[index]?.attrSet("rel", "noreferrer noopener");
119
+ return defaultLinkOpen ? defaultLinkOpen(tokens, index, options, env, self) : self.renderToken(tokens, index, options);
120
+ };
121
+ function Markdown({ value }) {
122
+ const html = useMemo(() => markdown.render(value), [value]);
123
+ return /* @__PURE__ */ jsx("div", { class: "scui-markdown", dangerouslySetInnerHTML: { __html: html } });
124
+ }
125
+ function LoadingStatus({ state, compact = false }) {
126
+ const copy = {
127
+ connecting: ["Connecting to coding agents", "Checking installed harnesses and capabilities.", 0],
128
+ starting: [`Starting ${harnessDisplayName(state.harness) || "coding agent"}`, "Opening a controlled session in this workspace.", 1],
129
+ discovering: ["Loading recent sessions", "Scanning native session stores without loading full transcripts.", 2],
130
+ ready: ["Ready", "Coding sessions are up to date.", 3]
131
+ }[state.startup];
132
+ return /* @__PURE__ */ jsxs("div", { class: `scui-loading${compact ? " scui-loading-compact" : ""}`, role: "status", "aria-busy": state.startup !== "ready", children: [
133
+ /* @__PURE__ */ jsx("span", { class: "scui-orbit", "aria-hidden": "true", children: /* @__PURE__ */ jsx("i", {}) }),
134
+ /* @__PURE__ */ jsxs("span", { class: "scui-loading-copy", children: [
135
+ /* @__PURE__ */ jsx("strong", { children: copy[0] }),
136
+ /* @__PURE__ */ jsx("small", { children: copy[1] })
137
+ ] }),
138
+ /* @__PURE__ */ jsx("span", { class: "scui-progress", "aria-hidden": "true", children: [1, 2, 3].map((step) => /* @__PURE__ */ jsx("i", { "data-progress": step <= copy[2] ? "done" : step === copy[2] + 1 ? "current" : "waiting" }, step)) })
139
+ ] });
140
+ }
141
+ function RequestCard({ entry, adapter, canRespond }) {
142
+ const request = entry.request;
143
+ if (!request) return null;
144
+ if (request.status === "responded") {
145
+ return /* @__PURE__ */ jsxs("div", { class: "scui-request-done", children: [
146
+ "\u2713 Request answered \xB7 ",
147
+ request.resolution?.name ?? request.requestKind
148
+ ] });
149
+ }
150
+ return /* @__PURE__ */ jsxs("section", { class: "scui-request", "aria-label": `${request.requestKind} needs input`, children: [
151
+ /* @__PURE__ */ jsx("strong", { children: "Agent needs input" }),
152
+ /* @__PURE__ */ jsx(Markdown, { value: request.payloadText || entry.text }),
153
+ /* @__PURE__ */ jsxs("div", { class: "scui-request-actions", children: [
154
+ request.options.map((option) => /* @__PURE__ */ jsx("button", { type: "button", disabled: !canRespond, onClick: () => adapter.onIntent({ action: "respond", requestId: request.requestId, optionId: option.optionId }), children: option.name }, option.optionId)),
155
+ request.cancellable ? /* @__PURE__ */ jsx("button", { type: "button", disabled: !canRespond, onClick: () => adapter.onIntent({ action: "respond", requestId: request.requestId, optionId: null }), children: "Cancel" }) : null
156
+ ] })
157
+ ] });
158
+ }
159
+ function ContextDisclosure({ context }) {
160
+ if (!context?.length) return null;
161
+ return /* @__PURE__ */ jsxs("details", { class: "scui-context", children: [
162
+ /* @__PURE__ */ jsxs("summary", { children: [
163
+ "Context \xB7 ",
164
+ context.length
165
+ ] }),
166
+ /* @__PURE__ */ jsx("div", { children: context.map((item, index) => /* @__PURE__ */ jsxs("p", { children: [
167
+ /* @__PURE__ */ jsx("strong", { children: item.label }),
168
+ /* @__PURE__ */ jsx("span", { children: item.detail })
169
+ ] }, item.id ?? index)) })
170
+ ] });
171
+ }
172
+ function TranscriptEntry({ entry, state, adapter }) {
173
+ if (entry.role === "request") return /* @__PURE__ */ jsx(RequestCard, { entry, adapter, canRespond: state.canRespond });
174
+ if (entry.role === "reasoning") {
175
+ return /* @__PURE__ */ jsxs("details", { class: "scui-reasoning", open: entry.streaming, children: [
176
+ /* @__PURE__ */ jsx("summary", { children: entry.streaming ? "Reasoning\u2026" : "Reasoning" }),
177
+ /* @__PURE__ */ jsx(Markdown, { value: entry.text })
178
+ ] });
179
+ }
180
+ if (entry.role === "notice" || entry.role === "system") return /* @__PURE__ */ jsx("div", { class: "scui-notice", "data-code": entry.code, children: entry.text });
181
+ return /* @__PURE__ */ jsxs("article", { class: "scui-message", "data-role": entry.role, children: [
182
+ /* @__PURE__ */ jsx(Markdown, { value: entry.text }),
183
+ /* @__PURE__ */ jsx(ContextDisclosure, { context: entry.context }),
184
+ entry.truncated ? /* @__PURE__ */ jsx("small", { class: "scui-truncated", children: "Entry truncated by host \xB7 load native session for the complete value" }) : null
185
+ ] });
186
+ }
187
+ function ToolRow({ entry, workspace }) {
188
+ const [open, setOpen] = useState(entry.status === "pending");
189
+ const detailId = useId();
190
+ const target = compactToolTarget(toolTarget(entry.arguments), workspace);
191
+ const hasDetail = Boolean(entry.arguments || entry.resultText);
192
+ const category = toolCategory(entry);
193
+ return /* @__PURE__ */ jsxs("div", { class: "scui-tool", "data-status": entry.status ?? "completed", "data-category": category, children: [
194
+ /* @__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: [
195
+ /* @__PURE__ */ jsx("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] }),
196
+ /* @__PURE__ */ jsx("strong", { children: entry.label?.split(/__|\//).at(-1)?.replaceAll("_", " ") || "Tool" }),
197
+ target ? /* @__PURE__ */ jsx("span", { class: "scui-tool-target", title: toolTarget(entry.arguments), children: target }) : null,
198
+ /* @__PURE__ */ jsx("span", { class: "scui-spacer" }),
199
+ /* @__PURE__ */ jsx("span", { "aria-label": entry.status ?? "completed", children: entry.status === "pending" ? "\u25CC" : entry.status === "error" ? "\xD7" : "\u2713" })
200
+ ] }),
201
+ hasDetail && open ? /* @__PURE__ */ jsxs("div", { id: detailId, class: "scui-tool-detail", children: [
202
+ entry.arguments ? /* @__PURE__ */ jsxs("pre", { children: [
203
+ /* @__PURE__ */ jsx("b", { children: "Input" }),
204
+ "\n",
205
+ entry.arguments
206
+ ] }) : null,
207
+ entry.resultText ? /* @__PURE__ */ jsxs("pre", { "data-error": entry.status === "error", children: [
208
+ /* @__PURE__ */ jsx("b", { children: "Output" }),
209
+ "\n",
210
+ entry.resultText,
211
+ entry.truncated ? "\n[truncated]" : ""
212
+ ] }) : null
213
+ ] }) : null
214
+ ] });
215
+ }
216
+ function ActivityGroup({ entries, state }) {
217
+ const active = entries.some((entry) => entry.status === "pending");
218
+ const [open, setOpen] = useState(active);
219
+ const id = useId();
220
+ useEffect(() => {
221
+ if (active) setOpen(true);
222
+ }, [active]);
223
+ return /* @__PURE__ */ jsxs("section", { class: "scui-activity", children: [
224
+ /* @__PURE__ */ jsxs("button", { class: "scui-activity-head", type: "button", "aria-expanded": open, "aria-controls": id, onClick: () => setOpen((value) => !value), children: [
225
+ /* @__PURE__ */ jsx("span", { class: "scui-fold", "data-open": open, children: "\u203A" }),
226
+ /* @__PURE__ */ jsx("strong", { children: activitySummary(entries) }),
227
+ /* @__PURE__ */ jsx("span", { class: "scui-spacer" }),
228
+ /* @__PURE__ */ jsxs("small", { children: [
229
+ entries.filter((entry) => entry.status === "completed").length,
230
+ "/",
231
+ entries.length
232
+ ] })
233
+ ] }),
234
+ open ? /* @__PURE__ */ jsx("div", { id, children: entries.map((entry) => /* @__PURE__ */ jsx(ToolRow, { entry, workspace: state.workspace }, entry.id)) }) : null
235
+ ] });
236
+ }
237
+ function TaskPlan({ plan }) {
238
+ if (!plan.items.length) return null;
239
+ const complete = plan.items.filter((item) => item.status === "completed" || item.status === "cancelled").length;
240
+ return /* @__PURE__ */ jsxs("details", { class: "scui-plan", children: [
241
+ /* @__PURE__ */ jsxs("summary", { children: [
242
+ /* @__PURE__ */ jsx("span", { children: "Plan" }),
243
+ /* @__PURE__ */ jsxs("small", { children: [
244
+ complete,
245
+ "/",
246
+ plan.items.length
247
+ ] })
248
+ ] }),
249
+ /* @__PURE__ */ jsx("ol", { children: plan.items.map((item) => /* @__PURE__ */ jsxs("li", { "data-status": item.status, children: [
250
+ /* @__PURE__ */ jsx("i", { "aria-hidden": "true" }),
251
+ " ",
252
+ /* @__PURE__ */ jsx("span", { children: item.title })
253
+ ] }, item.id)) })
254
+ ] });
255
+ }
256
+ function SessionDetails({ semantics }) {
257
+ if (!semantics.fidelity && !semantics.residueCount && !semantics.parseErrors && !semantics.subagents.length) return null;
258
+ return /* @__PURE__ */ jsxs("details", { class: "scui-details", children: [
259
+ /* @__PURE__ */ jsx("summary", { children: "Session details" }),
260
+ /* @__PURE__ */ jsxs("div", { children: [
261
+ semantics.fidelity ? /* @__PURE__ */ jsxs("p", { children: [
262
+ /* @__PURE__ */ jsx("strong", { children: "Fidelity" }),
263
+ /* @__PURE__ */ jsx("span", { children: semantics.fidelity.replaceAll("_", " ") })
264
+ ] }) : null,
265
+ /* @__PURE__ */ jsxs("p", { children: [
266
+ /* @__PURE__ */ jsx("strong", { children: "Native records" }),
267
+ /* @__PURE__ */ jsx("span", { children: semantics.rawRecords })
268
+ ] }),
269
+ semantics.residueCount ? /* @__PURE__ */ jsxs("p", { children: [
270
+ /* @__PURE__ */ jsx("strong", { children: "Residue" }),
271
+ /* @__PURE__ */ jsxs("span", { children: [
272
+ semantics.residueCount,
273
+ " retained"
274
+ ] })
275
+ ] }) : null,
276
+ semantics.parseErrors ? /* @__PURE__ */ jsxs("p", { children: [
277
+ /* @__PURE__ */ jsx("strong", { children: "Parse diagnostics" }),
278
+ /* @__PURE__ */ jsx("span", { children: semantics.parseErrors })
279
+ ] }) : null,
280
+ semantics.subagents.map((agent) => /* @__PURE__ */ jsxs("p", { children: [
281
+ /* @__PURE__ */ jsxs("strong", { children: [
282
+ agent.source,
283
+ " subagent"
284
+ ] }),
285
+ /* @__PURE__ */ jsxs("span", { children: [
286
+ agent.messages,
287
+ " messages \xB7 ",
288
+ agent.fidelity.replaceAll("_", " ")
289
+ ] })
290
+ ] }, agent.id))
291
+ ] })
292
+ ] });
293
+ }
294
+ var conversationMemory = /* @__PURE__ */ new Map();
295
+ function Conversation({ state, adapter, components = {}, slots = {}, memoryKey = state.attached?.key || `${state.harness}:${state.workspace}`, pending = null }) {
296
+ const scroller = useRef(null);
297
+ const remembered = conversationMemory.get(memoryKey) ?? { top: null, atBottom: true };
298
+ const [atBottom, setAtBottom] = useState(remembered.atBottom);
299
+ const earlierAnchor = useRef(null);
300
+ const restored = useRef(false);
301
+ const blocks = groupConversation(state.transcript);
302
+ const Entry = components.TranscriptEntry ?? TranscriptEntry;
303
+ const Group = components.ActivityGroup ?? ActivityGroup;
304
+ const Before = slots.beforeConversation;
305
+ const After = slots.afterConversation;
306
+ const Empty = slots.emptyConversation;
307
+ const pin = () => {
308
+ if (!scroller.current) return;
309
+ scroller.current.scrollTop = scroller.current.scrollHeight;
310
+ setAtBottom(true);
311
+ conversationMemory.set(memoryKey, { top: scroller.current.scrollTop, atBottom: true });
312
+ };
313
+ useLayoutEffect(() => {
314
+ const element = scroller.current;
315
+ if (!element) return;
316
+ const anchor = earlierAnchor.current;
317
+ if (anchor && state.transcript.length > anchor.entries) {
318
+ element.scrollTop = anchor.top + (element.scrollHeight - anchor.height);
319
+ earlierAnchor.current = null;
320
+ conversationMemory.set(memoryKey, { top: element.scrollTop, atBottom: false });
321
+ return;
322
+ }
323
+ if (!restored.current) {
324
+ restored.current = true;
325
+ if (remembered.top !== null && !remembered.atBottom) element.scrollTop = remembered.top;
326
+ else pin();
327
+ } else if (atBottom) pin();
328
+ }, [memoryKey, state.transcript.length, state.busy]);
329
+ return /* @__PURE__ */ jsxs("div", { class: "scui-conversation-wrap", children: [
330
+ /* @__PURE__ */ jsx("div", { class: "scui-conversation", ref: scroller, tabIndex: 0, "aria-label": "Conversation", onScroll: (event) => {
331
+ const element = event.currentTarget;
332
+ const bottom = element.scrollHeight - element.scrollTop - element.clientHeight <= 64;
333
+ setAtBottom(bottom);
334
+ conversationMemory.set(memoryKey, { top: element.scrollTop, atBottom: bottom });
335
+ }, children: /* @__PURE__ */ jsxs("div", { children: [
336
+ Before ? /* @__PURE__ */ jsx(Before, { state, adapter, value: null }) : null,
337
+ /* @__PURE__ */ jsx(TaskPlan, { plan: state.taskPlan }),
338
+ /* @__PURE__ */ jsx(SessionDetails, { semantics: state.semantics }),
339
+ state.history.hasEarlier ? /* @__PURE__ */ jsx("button", { class: "scui-load", type: "button", disabled: Boolean(state.operation), onClick: () => {
340
+ const element = scroller.current;
341
+ if (element) earlierAnchor.current = { height: element.scrollHeight, top: element.scrollTop, entries: state.transcript.length };
342
+ setAtBottom(false);
343
+ adapter.onIntent({ action: "loadEarlier" });
344
+ }, children: "Load earlier messages" }) : null,
345
+ !blocks.length && state.startup !== "ready" ? /* @__PURE__ */ jsx(LoadingStatus, { state }) : null,
346
+ !blocks.length && state.startup === "ready" ? Empty ? /* @__PURE__ */ jsx(Empty, { state, adapter, value: null }) : /* @__PURE__ */ jsx("div", { class: "scui-empty", children: state.error ?? (state.harness ? `${harnessDisplayName(state.harness)} is listening. Say something.` : "No transcript yet.") }) : null,
347
+ blocks.map((block) => block.kind === "activity" ? /* @__PURE__ */ jsx(Group, { value: block.entries, entries: block.entries, state, adapter }, block.id) : /* @__PURE__ */ jsx(Entry, { value: block.entry, entry: block.entry, state, adapter }, block.id)),
348
+ pending ? /* @__PURE__ */ jsxs("article", { class: "scui-message scui-pending", "data-role": "user", children: [
349
+ /* @__PURE__ */ jsx(Markdown, { value: pending }),
350
+ /* @__PURE__ */ jsx("small", { children: state.error && !state.busy ? "Not sent" : "Sending\u2026" })
351
+ ] }) : null,
352
+ state.busy ? /* @__PURE__ */ jsxs("div", { class: "scui-working", role: "status", children: [
353
+ /* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: "\u2726" }),
354
+ /* @__PURE__ */ jsx("i", {}),
355
+ /* @__PURE__ */ jsx("i", {}),
356
+ /* @__PURE__ */ jsx("i", {}),
357
+ /* @__PURE__ */ jsxs("small", { children: [
358
+ harnessDisplayName(state.harness),
359
+ " is working"
360
+ ] })
361
+ ] }) : null,
362
+ After ? /* @__PURE__ */ jsx(After, { state, adapter, value: null }) : null
363
+ ] }) }),
364
+ !atBottom ? /* @__PURE__ */ jsx("button", { class: "scui-latest", type: "button", onClick: pin, children: "\u2193 Latest" }) : null
365
+ ] });
366
+ }
367
+ export {
368
+ ActivityGroup,
369
+ Conversation,
370
+ LoadingStatus,
371
+ RequestCard,
372
+ SessionDetails,
373
+ TaskPlan,
374
+ TranscriptEntry
375
+ };