@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.
package/index.d.ts ADDED
@@ -0,0 +1,313 @@
1
+ import type { ComponentChildren, ComponentType, VNode } from 'preact';
2
+
3
+ export type HarnessId = 'claude-code' | 'codex' | 'opencode' | 'pi' | 'grok' | (string & {});
4
+ export type UiTone = 'live' | 'warn' | 'dead' | 'off';
5
+ export type StartupPhase = 'connecting' | 'starting' | 'discovering' | 'ready';
6
+ export type SessionMode = 'none' | 'control' | 'mirror';
7
+ export type ControlStrategy = 'start' | 'resume' | 'attach' | 'branch' | null;
8
+ export type SessionActivity = 'idle' | 'recent' | 'working' | 'needs-input' | 'finished' | 'failed' | 'unseen';
9
+
10
+ export interface HarnessOption {
11
+ id: HarnessId;
12
+ label: string;
13
+ installed: boolean;
14
+ startable: boolean;
15
+ reason: string | null;
16
+ }
17
+
18
+ export interface SessionRowModel {
19
+ key: string;
20
+ harness: HarnessId;
21
+ name: string;
22
+ cwd: string;
23
+ title: string;
24
+ age: string;
25
+ updatedAt: number | null;
26
+ messages: number | null;
27
+ active: boolean;
28
+ live: boolean;
29
+ runtimeStatus: 'busy' | 'idle' | null;
30
+ }
31
+
32
+ export interface AttachedSessionModel {
33
+ key: string;
34
+ harness: HarnessId;
35
+ name: string;
36
+ cwd: string;
37
+ title: string;
38
+ }
39
+
40
+ export interface TranscriptContext {
41
+ id?: string;
42
+ kind?: string;
43
+ label: string;
44
+ detail: string;
45
+ }
46
+
47
+ export interface RequestOption {
48
+ optionId: string;
49
+ name: string;
50
+ kind: 'allow_once' | 'allow_always' | 'reject_once' | 'reject_always' | 'other';
51
+ }
52
+
53
+ export interface TranscriptRequest {
54
+ requestId: unknown;
55
+ requestKind: string;
56
+ payloadText: string;
57
+ options: RequestOption[];
58
+ cancellable: boolean;
59
+ status: 'pending' | 'responded';
60
+ resolution: { optionId: string | null; name: string; kind: string } | null;
61
+ }
62
+
63
+ export interface TranscriptEntryModel {
64
+ id: string;
65
+ role: 'system' | 'user' | 'assistant' | 'tool' | 'reasoning' | 'request' | 'notice';
66
+ text: string;
67
+ ts: number | null;
68
+ truncated: boolean;
69
+ label?: string;
70
+ arguments?: string;
71
+ resultText?: string;
72
+ status?: 'pending' | 'completed' | 'error';
73
+ streaming?: boolean;
74
+ request?: TranscriptRequest;
75
+ code?: string;
76
+ context?: TranscriptContext[];
77
+ }
78
+
79
+ export interface TaskPlanItem {
80
+ id: string;
81
+ title: string;
82
+ status: 'pending' | 'in_progress' | 'completed' | 'cancelled' | 'unknown';
83
+ nativeStatus?: string;
84
+ blockedBy?: string[];
85
+ }
86
+
87
+ export interface TaskPlanModel {
88
+ source: 'codex-update-plan' | 'claude-tasks' | 'opencode-todos' | 'none';
89
+ items: TaskPlanItem[];
90
+ residueCount: number;
91
+ observedAt: number | null;
92
+ }
93
+
94
+ export interface SessionSemanticsModel {
95
+ fidelity: 'byte_lossless' | 'value_lossless' | 'semantic' | null;
96
+ residue: string[];
97
+ residueCount: number;
98
+ parseErrors: number;
99
+ rawRecords: number;
100
+ subagents: Array<{
101
+ id: string;
102
+ source: string;
103
+ model: string | null;
104
+ messages: number;
105
+ fidelity: 'byte_lossless' | 'value_lossless' | 'semantic';
106
+ }>;
107
+ }
108
+
109
+ export interface ReductionReceiptModel {
110
+ sourceTokens: number;
111
+ reducedTokens: number;
112
+ ratio: number;
113
+ sidecarId: string;
114
+ verified: boolean;
115
+ reversible: boolean;
116
+ targetHarness: HarnessId;
117
+ }
118
+
119
+ export interface SessionAttention {
120
+ key: string;
121
+ kind: 'unseen' | 'finished' | 'failed';
122
+ preview?: string;
123
+ }
124
+
125
+ export interface SupercodeUiState {
126
+ pill: { tone: UiTone; label: string };
127
+ startup: StartupPhase;
128
+ transcript: TranscriptEntryModel[];
129
+ busy: boolean;
130
+ operation: string | null;
131
+ needsInput: boolean;
132
+ harness: HarnessId | '';
133
+ mode: SessionMode;
134
+ strategy: ControlStrategy;
135
+ canSend: boolean;
136
+ canResume: boolean;
137
+ canBranch: boolean;
138
+ canAttach: boolean;
139
+ canDetach: boolean;
140
+ canOpenTerminal: boolean;
141
+ canExport: boolean;
142
+ canReduce: boolean;
143
+ canInterrupt: boolean;
144
+ canRespond: boolean;
145
+ messaging: 'live_peer' | null;
146
+ workspace: string;
147
+ taskPlan: TaskPlanModel;
148
+ semantics: SessionSemanticsModel;
149
+ terminalHandoff: { program: string; arguments: string[]; cwd: string } | null;
150
+ exportBackTarget: HarnessId | null;
151
+ exportReceipt: {
152
+ targetHarness: HarnessId;
153
+ fidelity: 'byte_lossless' | 'value_lossless';
154
+ path: string;
155
+ files: number;
156
+ residueCount: number;
157
+ } | null;
158
+ reductionReceipt: ReductionReceiptModel | null;
159
+ error: string | null;
160
+ recoverable: boolean;
161
+ harnesses: HarnessOption[];
162
+ history: {
163
+ sessionLimit: number;
164
+ hasMoreSessions: boolean;
165
+ transcriptLimit: number;
166
+ hasEarlier: boolean;
167
+ };
168
+ savedDraft: string;
169
+ attention: SessionAttention[];
170
+ sessions: SessionRowModel[];
171
+ attached: AttachedSessionModel | null;
172
+ owned: AttachedSessionModel | null;
173
+ attachError: { key: string; message: string } | null;
174
+ }
175
+
176
+ export type SupercodeUiIntent =
177
+ | { action: 'mounted' }
178
+ | { action: 'attach'; key: string }
179
+ | { action: 'ack'; key: string }
180
+ | { action: 'loadSessions' }
181
+ | { action: 'loadEarlier' }
182
+ | { action: 'draft'; text: string }
183
+ | { action: 'send'; text: string }
184
+ | { action: 'new'; harness: HarnessId; text: string }
185
+ | { action: 'resume' }
186
+ | { action: 'join' }
187
+ | { action: 'detach' }
188
+ | { action: 'branch'; targetHarness?: HarnessId }
189
+ | { action: 'reduce'; targetHarness?: HarnessId }
190
+ | { action: 'terminal' }
191
+ | { action: 'export'; targetHarness: HarnessId }
192
+ | { action: 'interrupt' }
193
+ | { action: 'respond'; requestId: unknown; optionId: string | null }
194
+ | { action: 'refresh' }
195
+ | { action: 'release' };
196
+
197
+ export interface UiAdapter {
198
+ onIntent(intent: SupercodeUiIntent): void;
199
+ onClose?(): void;
200
+ onOpen?(): void;
201
+ copyText?(value: string): void | Promise<void>;
202
+ now?(): number;
203
+ }
204
+
205
+ export interface MessengerLabels {
206
+ chats: string;
207
+ newChat: string;
208
+ searchChats: string;
209
+ askAgent: string;
210
+ continueHere: string;
211
+ joinLive: string;
212
+ forkHere: string;
213
+ }
214
+
215
+ export interface ComponentOverrideProps<T = unknown> {
216
+ state: SupercodeUiState;
217
+ adapter: UiAdapter;
218
+ value: T;
219
+ }
220
+
221
+ export interface SessionRowProps {
222
+ row: SessionRowModel;
223
+ value?: SessionRowModel;
224
+ state: SupercodeUiState;
225
+ adapter: UiAdapter;
226
+ onOpen(row: SessionRowModel): void;
227
+ }
228
+
229
+ export interface TranscriptEntryProps {
230
+ entry: TranscriptEntryModel;
231
+ value?: TranscriptEntryModel;
232
+ state: SupercodeUiState;
233
+ adapter: UiAdapter;
234
+ }
235
+
236
+ export interface ActivityGroupProps {
237
+ entries: TranscriptEntryModel[];
238
+ value?: TranscriptEntryModel[];
239
+ state: SupercodeUiState;
240
+ adapter: UiAdapter;
241
+ }
242
+
243
+ export interface MessengerComponents {
244
+ SessionRow?: ComponentType<SessionRowProps>;
245
+ TranscriptEntry?: ComponentType<TranscriptEntryProps>;
246
+ ActivityGroup?: ComponentType<ActivityGroupProps>;
247
+ HarnessLogo?: ComponentType<{ id: HarnessId; activity?: SessionActivity; size?: number }>;
248
+ }
249
+
250
+ export interface MessengerSlots {
251
+ header?: ComponentType<ComponentOverrideProps>;
252
+ emptyConversation?: ComponentType<ComponentOverrideProps>;
253
+ beforeConversation?: ComponentType<ComponentOverrideProps>;
254
+ afterConversation?: ComponentType<ComponentOverrideProps>;
255
+ footer?: ComponentType<ComponentOverrideProps>;
256
+ }
257
+
258
+ export interface MessengerProps {
259
+ state: SupercodeUiState | unknown;
260
+ adapter: UiAdapter;
261
+ class?: string;
262
+ initialView?: 'list' | 'chat' | 'new';
263
+ labels?: Partial<MessengerLabels>;
264
+ components?: MessengerComponents;
265
+ slots?: MessengerSlots;
266
+ children?: ComponentChildren;
267
+ }
268
+
269
+ export const EMPTY_UI_STATE: Readonly<SupercodeUiState>;
270
+ export const DEFAULT_LABELS: Readonly<MessengerLabels>;
271
+ export function normalizeUiState(value: unknown): SupercodeUiState;
272
+ export function harnessDisplayName(id: string): string;
273
+ export function sessionDisplayName(session: Pick<SessionRowModel, 'name' | 'title'>): string;
274
+ export function sessionActivity(state: SupercodeUiState, row: SessionRowModel): SessionActivity;
275
+ export function filterSessions(rows: readonly SessionRowModel[], query: string): SessionRowModel[];
276
+ export function groupConversation(entries: readonly TranscriptEntryModel[]): Array<
277
+ | { kind: 'entry'; id: string; entry: TranscriptEntryModel }
278
+ | { kind: 'activity'; id: string; entries: TranscriptEntryModel[] }
279
+ >;
280
+ export function toolCategory(entry: Pick<TranscriptEntryModel, 'label' | 'arguments'>): 'read' | 'search' | 'edit' | 'command' | 'test' | 'web' | 'agent' | 'other';
281
+ export function toolTarget(argumentsText?: string): string;
282
+ export function compactToolTarget(target: string, workspace: string): string;
283
+ export function activitySummary(entries: readonly TranscriptEntryModel[]): string;
284
+ export function canContinueHere(state: SupercodeUiState): boolean;
285
+ export function operationLabel(operation: string | null): string;
286
+ export function terminalCommand(handoff: NonNullable<SupercodeUiState['terminalHandoff']>): string;
287
+ export function isSendKey(event: Pick<KeyboardEvent, 'key' | 'shiftKey' | 'isComposing'>): boolean;
288
+
289
+ export function HarnessLogo(props: { id: HarnessId; activity?: SessionActivity; size?: number; onMissingLogo?(id: string): void }): VNode | null;
290
+ export function hasHarnessLogo(id: string): boolean;
291
+ export function LoadingStatus(props: { state: SupercodeUiState; compact?: boolean }): VNode;
292
+ export function RequestCard(props: { entry: TranscriptEntryModel; adapter: UiAdapter; canRespond: boolean }): VNode | null;
293
+ export function TranscriptEntry(props: { entry: TranscriptEntryModel; state: SupercodeUiState; adapter: UiAdapter }): VNode;
294
+ export function ActivityGroup(props: { entries: TranscriptEntryModel[]; state: SupercodeUiState; adapter: UiAdapter }): VNode;
295
+ export function TaskPlan(props: { plan: TaskPlanModel }): VNode | null;
296
+ export function SessionDetails(props: { semantics: SessionSemanticsModel }): VNode | null;
297
+ export function Conversation(props: { state: SupercodeUiState; adapter: UiAdapter; components?: MessengerComponents; slots?: MessengerSlots; memoryKey?: string; pending?: string | null }): VNode;
298
+ export function SessionRow(props: { row: SessionRowModel; state: SupercodeUiState; adapter: UiAdapter; onOpen(row: SessionRowModel): void }): VNode;
299
+ export function SessionList(props: { state: SupercodeUiState; adapter: UiAdapter; onOpen(row: SessionRowModel): void; onNew?(): void; onClose?(): void; components?: MessengerComponents; labels?: MessengerLabels }): VNode;
300
+ export function ContinuationBar(props: { state: SupercodeUiState; adapter: UiAdapter; labels?: MessengerLabels }): VNode | null;
301
+ export function Composer(props: { state: SupercodeUiState; adapter: UiAdapter; labels?: MessengerLabels; memoryKey?: string; onPending?(text: string): void }): VNode;
302
+ export function SupercodeMessenger(props: MessengerProps): VNode;
303
+
304
+ export interface MountedMessenger {
305
+ update(state: SupercodeUiState | unknown): void;
306
+ unmount(): void;
307
+ focus(): void;
308
+ }
309
+
310
+ export function mountSupercodeMessenger(
311
+ element: Element,
312
+ options: Omit<MessengerProps, 'state'> & { state: SupercodeUiState | unknown },
313
+ ): MountedMessenger;
package/index.mjs ADDED
@@ -0,0 +1,3 @@
1
+ export * from './core.mjs';
2
+ export * from './components.mjs';
3
+ export * from './embed.mjs';
package/logo.mjs ADDED
@@ -0,0 +1,114 @@
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
+
62
+ // src/components.jsx
63
+ import { jsx, jsxs } from "preact/jsx-runtime";
64
+ var markdown = new MarkdownIt({ html: false, linkify: true, breaks: false });
65
+ var defaultLinkOpen = markdown.renderer.rules.link_open;
66
+ markdown.renderer.rules.link_open = (tokens, index, options, env, self) => {
67
+ tokens[index]?.attrSet("target", "_blank");
68
+ tokens[index]?.attrSet("rel", "noreferrer noopener");
69
+ return defaultLinkOpen ? defaultLinkOpen(tokens, index, options, env, self) : self.renderToken(tokens, index, options);
70
+ };
71
+ var LOGOS = {
72
+ "claude-code": {
73
+ viewBox: "-1 3.5 26 18",
74
+ paths: [
75
+ ["path", { "fill-rule": "evenodd", "clip-rule": "evenodd", d: "M20.998 10.949H24v3.102h-3v3.028h-1.487V20H18v-2.921h-1.487V20H15v-2.921H9V20H7.488v-2.921H6V20H4.487v-2.921H3V14.05H0V10.95h3V5h17.998v5.949zM6 10.949h1.488V8.102H6v2.847zm10.51 0H18V8.102h-1.49v2.847z" }]
76
+ ]
77
+ },
78
+ codex: {
79
+ viewBox: "-1 -1 26 26",
80
+ paths: [
81
+ ["path", { "fill-rule": "evenodd", "clip-rule": "evenodd", d: "M8.086.457a6.105 6.105 0 013.046-.415c1.333.153 2.521.72 3.564 1.7a.117.117 0 00.107.029c1.408-.346 2.762-.224 4.061.366l.063.03.154.076c1.357.703 2.33 1.77 2.918 3.198.278.679.418 1.388.421 2.126a5.655 5.655 0 01-.18 1.631.167.167 0 00.04.155 5.982 5.982 0 011.578 2.891c.385 1.901-.01 3.615-1.183 5.14l-.182.22a6.063 6.063 0 01-2.934 1.851.162.162 0 00-.108.102c-.255.736-.511 1.364-.987 1.992-1.199 1.582-2.962 2.462-4.948 2.451-1.583-.008-2.986-.587-4.21-1.736a.145.145 0 00-.14-.032c-.518.167-1.04.191-1.604.185a5.924 5.924 0 01-2.595-.622 6.058 6.058 0 01-2.146-1.781c-.203-.269-.404-.522-.551-.821a7.74 7.74 0 01-.495-1.283 6.11 6.11 0 01-.017-3.064.166.166 0 00.008-.074.115.115 0 00-.037-.064 5.958 5.958 0 01-1.38-2.202 5.196 5.196 0 01-.333-1.589 6.915 6.915 0 01.188-2.132c.45-1.484 1.309-2.648 2.577-3.493.282-.188.55-.334.802-.438.286-.12.573-.22.861-.304a.129.129 0 00.087-.087A6.016 6.016 0 015.635 2.31C6.315 1.464 7.132.846 8.086.457zm-.804 7.85a.848.848 0 00-1.473.842l1.694 2.965-1.688 2.848a.849.849 0 001.46.864l1.94-3.272a.849.849 0 00.007-.854l-1.94-3.393zm5.446 6.24a.849.849 0 000 1.695h4.848a.849.849 0 000-1.696h-4.848z" }]
82
+ ]
83
+ },
84
+ grok: {
85
+ viewBox: "-1 -1 26 26",
86
+ paths: [["path", { d: "M9.27 15.29l7.978-5.897c.391-.29.95-.177 1.137.272.98 2.369.542 5.215-1.41 7.169-1.951 1.954-4.667 2.382-7.149 1.406l-2.711 1.257c3.889 2.661 8.611 2.003 11.562-.953 2.341-2.344 3.066-5.539 2.388-8.42l.006.007c-.983-4.232.242-5.924 2.75-9.383.06-.082.12-.164.179-.248l-3.301 3.305v-.01L9.267 15.292M7.623 16.723c-2.792-2.67-2.31-6.801.071-9.184 1.761-1.763 4.647-2.483 7.166-1.425l2.705-1.25a7.808 7.808 0 00-1.829-1A8.975 8.975 0 005.984 5.83c-2.533 2.536-3.33 6.436-1.962 9.764 1.022 2.487-.653 4.246-2.34 6.022-.599.63-1.199 1.259-1.682 1.925l7.62-6.815" }]]
87
+ },
88
+ opencode: { viewBox: "2.5 0 19 24", paths: [["path", { d: "M16 6H8v12h8V6zm4 16H4V2h16v20z" }]] },
89
+ pi: {
90
+ viewBox: "0 0 24 24",
91
+ paths: [
92
+ ["path", { "fill-rule": "evenodd", "clip-rule": "evenodd", d: "M1 1h16.5v11H12v5.5H6.5V23H1V1zm5.5 5.5V12H12V6.5H6.5z" }],
93
+ ["path", { d: "M17.5 12H23v11h-5.5V12z" }]
94
+ ]
95
+ }
96
+ };
97
+ function hasHarnessLogo(id) {
98
+ return Object.hasOwn(LOGOS, id);
99
+ }
100
+ function HarnessLogo({ id, activity, size = 28, onMissingLogo }) {
101
+ const logo = LOGOS[id];
102
+ useEffect(() => {
103
+ if (!logo) onMissingLogo?.(id);
104
+ }, [id, logo, onMissingLogo]);
105
+ if (!logo) return null;
106
+ return /* @__PURE__ */ jsxs("span", { class: "scui-logo", "data-harness": id, "data-activity": activity, style: `--scui-logo-size:${size}px`, "aria-hidden": "true", children: [
107
+ /* @__PURE__ */ jsx("svg", { viewBox: logo.viewBox, focusable: "false", children: logo.paths.map(([Tag, props], index) => /* @__PURE__ */ jsx(Tag, { ...props }, index)) }),
108
+ activity && activity !== "idle" ? /* @__PURE__ */ jsx("i", {}) : null
109
+ ] });
110
+ }
111
+ export {
112
+ HarnessLogo,
113
+ hasHarnessLogo
114
+ };