@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/README.md +116 -0
- package/components.mjs +963 -0
- package/composer.mjs +168 -0
- package/controller.d.ts +53 -0
- package/controller.mjs +262 -0
- package/conversation.mjs +375 -0
- package/core.mjs +349 -0
- package/embed.mjs +972 -0
- package/index.d.ts +313 -0
- package/index.mjs +3 -0
- package/logo.mjs +114 -0
- package/messenger.mjs +947 -0
- package/package.json +106 -0
- package/sessions.mjs +196 -0
- package/styles.css +154 -0
package/core.mjs
ADDED
|
@@ -0,0 +1,349 @@
|
|
|
1
|
+
const HARNESS_NAMES = Object.freeze({
|
|
2
|
+
'claude-code': 'Claude Code',
|
|
3
|
+
codex: 'Codex',
|
|
4
|
+
opencode: 'OpenCode',
|
|
5
|
+
pi: 'Pi',
|
|
6
|
+
grok: 'Grok',
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
export const DEFAULT_LABELS = Object.freeze({
|
|
10
|
+
chats: 'Chats',
|
|
11
|
+
newChat: 'New chat',
|
|
12
|
+
searchChats: 'Search chats',
|
|
13
|
+
askAgent: 'Ask your agent…',
|
|
14
|
+
continueHere: 'Continue here',
|
|
15
|
+
joinLive: 'Join live',
|
|
16
|
+
forkHere: 'Fork here',
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
export const EMPTY_UI_STATE = Object.freeze({
|
|
20
|
+
pill: Object.freeze({ tone: 'off', label: 'connecting…' }),
|
|
21
|
+
startup: 'connecting',
|
|
22
|
+
transcript: Object.freeze([]),
|
|
23
|
+
busy: false,
|
|
24
|
+
operation: null,
|
|
25
|
+
needsInput: false,
|
|
26
|
+
harness: '',
|
|
27
|
+
mode: 'none',
|
|
28
|
+
strategy: null,
|
|
29
|
+
canSend: false,
|
|
30
|
+
canResume: false,
|
|
31
|
+
canBranch: false,
|
|
32
|
+
canAttach: false,
|
|
33
|
+
canDetach: false,
|
|
34
|
+
canOpenTerminal: false,
|
|
35
|
+
canExport: false,
|
|
36
|
+
canReduce: false,
|
|
37
|
+
canInterrupt: false,
|
|
38
|
+
canRespond: false,
|
|
39
|
+
messaging: null,
|
|
40
|
+
workspace: '',
|
|
41
|
+
taskPlan: Object.freeze({ source: 'none', items: Object.freeze([]), residueCount: 0, observedAt: null }),
|
|
42
|
+
semantics: Object.freeze({ fidelity: null, residue: Object.freeze([]), residueCount: 0, parseErrors: 0, rawRecords: 0, subagents: Object.freeze([]) }),
|
|
43
|
+
terminalHandoff: null,
|
|
44
|
+
exportBackTarget: null,
|
|
45
|
+
exportReceipt: null,
|
|
46
|
+
reductionReceipt: null,
|
|
47
|
+
error: null,
|
|
48
|
+
recoverable: false,
|
|
49
|
+
harnesses: Object.freeze([]),
|
|
50
|
+
history: Object.freeze({ sessionLimit: 0, hasMoreSessions: false, transcriptLimit: 120, hasEarlier: false }),
|
|
51
|
+
savedDraft: '',
|
|
52
|
+
attention: Object.freeze([]),
|
|
53
|
+
sessions: Object.freeze([]),
|
|
54
|
+
attached: null,
|
|
55
|
+
owned: null,
|
|
56
|
+
attachError: null,
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
const ROLES = new Set(['system', 'user', 'assistant', 'tool', 'reasoning', 'request', 'notice']);
|
|
60
|
+
const MODES = new Set(['none', 'control', 'mirror']);
|
|
61
|
+
const STRATEGIES = new Set(['start', 'resume', 'attach', 'branch']);
|
|
62
|
+
const STARTUP = new Set(['connecting', 'starting', 'discovering', 'ready']);
|
|
63
|
+
const FIDELITY = new Set(['byte_lossless', 'value_lossless', 'semantic']);
|
|
64
|
+
|
|
65
|
+
function record(value) {
|
|
66
|
+
return value !== null && typeof value === 'object' && !Array.isArray(value) ? value : null;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function string(value, fallback = '') {
|
|
70
|
+
return typeof value === 'string' ? value : fallback;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function number(value, fallback = 0) {
|
|
74
|
+
return typeof value === 'number' && Number.isFinite(value) ? value : fallback;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function nullableNumber(value) {
|
|
78
|
+
return typeof value === 'number' && Number.isFinite(value) ? value : null;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function readTranscript(value) {
|
|
82
|
+
if (!Array.isArray(value)) return [];
|
|
83
|
+
const result = [];
|
|
84
|
+
for (const candidate of value) {
|
|
85
|
+
const item = record(candidate);
|
|
86
|
+
if (!item || typeof item.id !== 'string' || typeof item.text !== 'string' || !ROLES.has(item.role)) continue;
|
|
87
|
+
const entry = {
|
|
88
|
+
id: item.id,
|
|
89
|
+
role: item.role,
|
|
90
|
+
text: item.text,
|
|
91
|
+
ts: nullableNumber(item.ts),
|
|
92
|
+
truncated: item.truncated === true,
|
|
93
|
+
};
|
|
94
|
+
for (const key of ['label', 'arguments', 'resultText', 'code']) {
|
|
95
|
+
if (typeof item[key] === 'string') entry[key] = item[key];
|
|
96
|
+
}
|
|
97
|
+
if (['pending', 'completed', 'error'].includes(item.status)) entry.status = item.status;
|
|
98
|
+
if (typeof item.streaming === 'boolean') entry.streaming = item.streaming;
|
|
99
|
+
if (Array.isArray(item.context)) {
|
|
100
|
+
entry.context = item.context.flatMap((raw) => {
|
|
101
|
+
const context = record(raw);
|
|
102
|
+
return context && typeof context.label === 'string' && typeof context.detail === 'string'
|
|
103
|
+
? [{
|
|
104
|
+
...(typeof context.id === 'string' ? { id: context.id } : {}),
|
|
105
|
+
...(typeof context.kind === 'string' ? { kind: context.kind } : {}),
|
|
106
|
+
label: context.label,
|
|
107
|
+
detail: context.detail,
|
|
108
|
+
}]
|
|
109
|
+
: [];
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
const request = record(item.request);
|
|
113
|
+
if (request && typeof request.requestKind === 'string' && typeof request.payloadText === 'string') {
|
|
114
|
+
entry.request = {
|
|
115
|
+
requestId: request.requestId,
|
|
116
|
+
requestKind: request.requestKind,
|
|
117
|
+
payloadText: request.payloadText,
|
|
118
|
+
options: Array.isArray(request.options) ? request.options.flatMap((raw) => {
|
|
119
|
+
const option = record(raw);
|
|
120
|
+
return option && typeof option.optionId === 'string' && typeof option.name === 'string'
|
|
121
|
+
? [{ optionId: option.optionId, name: option.name, kind: string(option.kind, 'other') }]
|
|
122
|
+
: [];
|
|
123
|
+
}) : [],
|
|
124
|
+
cancellable: request.cancellable === true,
|
|
125
|
+
status: request.status === 'responded' ? 'responded' : 'pending',
|
|
126
|
+
resolution: record(request.resolution),
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
result.push(entry);
|
|
130
|
+
}
|
|
131
|
+
return result;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function readSessions(value) {
|
|
135
|
+
if (!Array.isArray(value)) return [];
|
|
136
|
+
return value.flatMap((raw) => {
|
|
137
|
+
const row = record(raw);
|
|
138
|
+
if (!row || typeof row.key !== 'string' || typeof row.harness !== 'string') return [];
|
|
139
|
+
return [{
|
|
140
|
+
key: row.key,
|
|
141
|
+
harness: row.harness,
|
|
142
|
+
name: string(row.name),
|
|
143
|
+
cwd: string(row.cwd),
|
|
144
|
+
title: string(row.title),
|
|
145
|
+
age: string(row.age),
|
|
146
|
+
updatedAt: nullableNumber(row.updatedAt),
|
|
147
|
+
messages: nullableNumber(row.messages),
|
|
148
|
+
active: row.active === true,
|
|
149
|
+
live: row.live === true,
|
|
150
|
+
runtimeStatus: row.runtimeStatus === 'busy' || row.runtimeStatus === 'idle' ? row.runtimeStatus : null,
|
|
151
|
+
}];
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function readAttached(value) {
|
|
156
|
+
const item = record(value);
|
|
157
|
+
if (!item || typeof item.harness !== 'string') return null;
|
|
158
|
+
return { key: string(item.key), harness: item.harness, name: string(item.name), cwd: string(item.cwd), title: string(item.title) };
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function readTaskPlan(value) {
|
|
162
|
+
const plan = record(value);
|
|
163
|
+
if (!plan) return { ...EMPTY_UI_STATE.taskPlan, items: [] };
|
|
164
|
+
return {
|
|
165
|
+
source: ['codex-update-plan', 'claude-tasks', 'opencode-todos'].includes(plan.source) ? plan.source : 'none',
|
|
166
|
+
items: Array.isArray(plan.items) ? plan.items.flatMap((raw) => {
|
|
167
|
+
const item = record(raw);
|
|
168
|
+
if (!item || typeof item.id !== 'string' || typeof item.title !== 'string') return [];
|
|
169
|
+
return [{
|
|
170
|
+
id: item.id,
|
|
171
|
+
title: item.title,
|
|
172
|
+
status: ['pending', 'in_progress', 'completed', 'cancelled', 'unknown'].includes(item.status) ? item.status : 'unknown',
|
|
173
|
+
...(typeof item.nativeStatus === 'string' ? { nativeStatus: item.nativeStatus } : {}),
|
|
174
|
+
...(Array.isArray(item.blockedBy) ? { blockedBy: item.blockedBy.filter((value) => typeof value === 'string') } : {}),
|
|
175
|
+
}];
|
|
176
|
+
}) : [],
|
|
177
|
+
residueCount: number(plan.residueCount),
|
|
178
|
+
observedAt: nullableNumber(plan.observedAt),
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
function readSemantics(value) {
|
|
183
|
+
const semantics = record(value);
|
|
184
|
+
if (!semantics) return { ...EMPTY_UI_STATE.semantics, residue: [], subagents: [] };
|
|
185
|
+
return {
|
|
186
|
+
fidelity: FIDELITY.has(semantics.fidelity) ? semantics.fidelity : null,
|
|
187
|
+
residue: Array.isArray(semantics.residue) ? semantics.residue.filter((item) => typeof item === 'string') : [],
|
|
188
|
+
residueCount: number(semantics.residueCount),
|
|
189
|
+
parseErrors: number(semantics.parseErrors),
|
|
190
|
+
rawRecords: number(semantics.rawRecords),
|
|
191
|
+
subagents: Array.isArray(semantics.subagents) ? semantics.subagents.flatMap((raw) => {
|
|
192
|
+
const child = record(raw);
|
|
193
|
+
if (!child || typeof child.id !== 'string' || typeof child.source !== 'string') return [];
|
|
194
|
+
return [{ id: child.id, source: child.source, model: typeof child.model === 'string' ? child.model : null, messages: number(child.messages), fidelity: FIDELITY.has(child.fidelity) ? child.fidelity : 'semantic' }];
|
|
195
|
+
}) : [],
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
export function normalizeUiState(value) {
|
|
200
|
+
const raw = record(value) ?? {};
|
|
201
|
+
const pill = record(raw.pill);
|
|
202
|
+
const history = record(raw.history);
|
|
203
|
+
const attachError = record(raw.attachError);
|
|
204
|
+
return {
|
|
205
|
+
pill: { tone: ['live', 'warn', 'dead'].includes(pill?.tone) ? pill.tone : 'off', label: string(pill?.label, 'connecting…') },
|
|
206
|
+
startup: STARTUP.has(raw.startup) ? raw.startup : 'connecting',
|
|
207
|
+
transcript: readTranscript(raw.transcript),
|
|
208
|
+
busy: raw.busy === true,
|
|
209
|
+
operation: typeof raw.operation === 'string' ? raw.operation : null,
|
|
210
|
+
needsInput: raw.needsInput === true,
|
|
211
|
+
harness: string(raw.harness),
|
|
212
|
+
mode: MODES.has(raw.mode) ? raw.mode : 'none',
|
|
213
|
+
strategy: STRATEGIES.has(raw.strategy) ? raw.strategy : null,
|
|
214
|
+
canSend: raw.canSend === true,
|
|
215
|
+
canResume: raw.canResume === true,
|
|
216
|
+
canBranch: raw.canBranch === true,
|
|
217
|
+
canAttach: raw.canAttach === true,
|
|
218
|
+
canDetach: raw.canDetach === true,
|
|
219
|
+
canOpenTerminal: raw.canOpenTerminal === true,
|
|
220
|
+
canExport: raw.canExport === true,
|
|
221
|
+
canReduce: raw.canReduce === true,
|
|
222
|
+
canInterrupt: raw.canInterrupt === true,
|
|
223
|
+
canRespond: raw.canRespond === true,
|
|
224
|
+
messaging: raw.messaging === 'live_peer' ? 'live_peer' : null,
|
|
225
|
+
workspace: string(raw.workspace),
|
|
226
|
+
taskPlan: readTaskPlan(raw.taskPlan),
|
|
227
|
+
semantics: readSemantics(raw.semantics),
|
|
228
|
+
terminalHandoff: record(raw.terminalHandoff),
|
|
229
|
+
exportBackTarget: typeof raw.exportBackTarget === 'string' ? raw.exportBackTarget : null,
|
|
230
|
+
exportReceipt: record(raw.exportReceipt),
|
|
231
|
+
reductionReceipt: record(raw.reductionReceipt),
|
|
232
|
+
error: typeof raw.error === 'string' ? raw.error : null,
|
|
233
|
+
recoverable: raw.recoverable === true,
|
|
234
|
+
harnesses: Array.isArray(raw.harnesses) ? raw.harnesses.flatMap((candidate) => {
|
|
235
|
+
const item = record(candidate);
|
|
236
|
+
return item && typeof item.id === 'string' ? [{ id: item.id, label: string(item.label, harnessDisplayName(item.id)), installed: item.installed === true, startable: item.startable === true, reason: typeof item.reason === 'string' ? item.reason : null }] : [];
|
|
237
|
+
}) : [],
|
|
238
|
+
history: { sessionLimit: number(history?.sessionLimit), hasMoreSessions: history?.hasMoreSessions === true, transcriptLimit: number(history?.transcriptLimit, 120), hasEarlier: history?.hasEarlier === true },
|
|
239
|
+
savedDraft: string(raw.savedDraft),
|
|
240
|
+
attention: Array.isArray(raw.attention) ? raw.attention.flatMap((candidate) => {
|
|
241
|
+
const item = record(candidate);
|
|
242
|
+
return item && typeof item.key === 'string' && ['unseen', 'finished', 'failed'].includes(item.kind)
|
|
243
|
+
? [{ key: item.key, kind: item.kind, ...(typeof item.preview === 'string' ? { preview: item.preview } : {}) }]
|
|
244
|
+
: [];
|
|
245
|
+
}) : [],
|
|
246
|
+
sessions: readSessions(raw.sessions),
|
|
247
|
+
attached: readAttached(raw.attached),
|
|
248
|
+
owned: readAttached(raw.owned),
|
|
249
|
+
attachError: attachError && typeof attachError.key === 'string' && typeof attachError.message === 'string' ? { key: attachError.key, message: attachError.message } : null,
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
export function harnessDisplayName(id) {
|
|
254
|
+
return HARNESS_NAMES[id] ?? id;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
export function sessionDisplayName(session) {
|
|
258
|
+
const title = session.title?.trim();
|
|
259
|
+
return title && title !== session.name ? title : session.name || 'Untitled chat';
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
export function sessionActivity(state, row) {
|
|
263
|
+
if (state.needsInput && row.active) return 'needs-input';
|
|
264
|
+
if (state.busy && row.active) return 'working';
|
|
265
|
+
const attention = state.attention.find((item) => item.key === row.key)?.kind;
|
|
266
|
+
if (attention) return attention;
|
|
267
|
+
if (row.runtimeStatus === 'busy') return 'working';
|
|
268
|
+
if (row.live || row.runtimeStatus === 'idle') return 'recent';
|
|
269
|
+
return 'idle';
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
export function filterSessions(rows, query) {
|
|
273
|
+
const needle = query.trim().toLocaleLowerCase();
|
|
274
|
+
if (!needle) return [...rows];
|
|
275
|
+
return rows.filter((row) => [row.name, row.title, row.cwd, row.harness, harnessDisplayName(row.harness)].some((value) => value.toLocaleLowerCase().includes(needle)));
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
export function groupConversation(entries) {
|
|
279
|
+
const blocks = [];
|
|
280
|
+
for (const entry of entries) {
|
|
281
|
+
if (entry.role !== 'tool') {
|
|
282
|
+
blocks.push({ kind: 'entry', id: entry.id, entry });
|
|
283
|
+
continue;
|
|
284
|
+
}
|
|
285
|
+
const previous = blocks.at(-1);
|
|
286
|
+
if (previous?.kind === 'activity') previous.entries.push(entry);
|
|
287
|
+
else blocks.push({ kind: 'activity', id: `activity:${entry.id}`, entries: [entry] });
|
|
288
|
+
}
|
|
289
|
+
return blocks;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
export function toolCategory(entry) {
|
|
293
|
+
const name = entry.label?.toLocaleLowerCase() ?? '';
|
|
294
|
+
if (/read|view|open_file|list_dir/.test(name)) return 'read';
|
|
295
|
+
if (/search|find|grep|glob/.test(name)) return 'search';
|
|
296
|
+
if (/edit|write|patch|replace|create_file/.test(name)) return 'edit';
|
|
297
|
+
if (/test|typecheck|lint|build/.test(name)) return 'test';
|
|
298
|
+
if (/terminal|bash|shell|command|exec/.test(name)) return /\b(test|typecheck|lint|build)\b/i.test(entry.arguments ?? '') ? 'test' : 'command';
|
|
299
|
+
if (/browser|web|fetch|url/.test(name)) return 'web';
|
|
300
|
+
if (/subagent|spawn|task/.test(name)) return 'agent';
|
|
301
|
+
return 'other';
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
export function toolTarget(argumentsText) {
|
|
305
|
+
if (!argumentsText) return '';
|
|
306
|
+
try {
|
|
307
|
+
const args = JSON.parse(argumentsText);
|
|
308
|
+
for (const key of ['file_path', 'target_file', 'path', 'command', 'cmd', 'query', 'pattern', 'url']) {
|
|
309
|
+
if (typeof args?.[key] === 'string') return args[key];
|
|
310
|
+
}
|
|
311
|
+
} catch {
|
|
312
|
+
return argumentsText.length > 120 ? `${argumentsText.slice(0, 117)}…` : argumentsText;
|
|
313
|
+
}
|
|
314
|
+
return '';
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
export function compactToolTarget(target, workspace) {
|
|
318
|
+
const prefix = workspace && !workspace.endsWith('/') ? `${workspace}/` : workspace;
|
|
319
|
+
return prefix && target.startsWith(prefix) ? target.slice(prefix.length) : target;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
export function activitySummary(entries) {
|
|
323
|
+
const counts = new Map();
|
|
324
|
+
for (const entry of entries) counts.set(toolCategory(entry), (counts.get(toolCategory(entry)) ?? 0) + 1);
|
|
325
|
+
if ([...counts.keys()].every((key) => key === 'read' || key === 'search')) return `Explored ${entries.length} ${entries.length === 1 ? 'item' : 'items'}`;
|
|
326
|
+
const labels = { edit: 'changed', test: 'tests/builds', command: 'commands', read: 'reads', search: 'searches', web: 'web', agent: 'agents', other: 'other' };
|
|
327
|
+
return `Activity · ${Object.entries(labels).flatMap(([key, label]) => counts.has(key) ? [`${counts.get(key)} ${label}`] : []).join(' · ')}`;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
export function canContinueHere(state) {
|
|
331
|
+
if (state.mode !== 'mirror' || state.canSend) return false;
|
|
332
|
+
const row = state.attached?.key ? state.sessions.find((item) => item.key === state.attached.key) : null;
|
|
333
|
+
return state.canResume && row?.runtimeStatus !== 'busy' && row?.runtimeStatus !== 'idle';
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
export function operationLabel(operation) {
|
|
337
|
+
if (!operation) return '';
|
|
338
|
+
const labels = { discover: 'Refreshing chats…', attach: 'Opening chat…', resume: 'Continuing here…', branch: 'Starting continuation…', reduce: 'Reducing context and verifying reversibility…', terminal: 'Preparing terminal handoff…', export: 'Exporting losslessly…', refresh: 'Retrying…' };
|
|
339
|
+
return labels[operation] ?? `${operation.replaceAll('_', ' ')}…`;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
export function terminalCommand(handoff) {
|
|
343
|
+
const quote = (value) => /^[A-Za-z0-9_./:@%+=,-]+$/.test(value) ? value : `'${value.replaceAll("'", "'\\''")}'`;
|
|
344
|
+
return [handoff.program, ...handoff.arguments].map(quote).join(' ');
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
export function isSendKey(event) {
|
|
348
|
+
return event.key === 'Enter' && !event.shiftKey && !event.isComposing;
|
|
349
|
+
}
|