@buckeyestudio/pi-wire 18.4.13
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/CHANGELOG.md +82 -0
- package/LICENSE +22 -0
- package/README.md +31 -0
- package/package.json +54 -0
- package/src/index.ts +451 -0
- package/src/skillshare.ts +323 -0
- package/src/stream.ts +169 -0
- package/src/tsp.ts +958 -0
package/src/tsp.ts
ADDED
|
@@ -0,0 +1,958 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tern Surface Protocol (TSP) wire shapes.
|
|
3
|
+
*
|
|
4
|
+
* A TSP program describes its UI as a tree of semantic components and the
|
|
5
|
+
* terminal lays it out, draws and animates it natively. Messages travel
|
|
6
|
+
* in-band as APC strings on the pty:
|
|
7
|
+
*
|
|
8
|
+
* ESC _ tsp ; <verb> [; <key>=<value>]* ; <body> ESC \
|
|
9
|
+
*
|
|
10
|
+
* `body` is UTF-8 JSON (base64 for blob chunks). Program → terminal verbs:
|
|
11
|
+
* `q` query, `o` open, `f` frame, `b` blob, `t` palette, `x` close. Terminal →
|
|
12
|
+
* program: `r` reply, `e` event (on the pty's input side).
|
|
13
|
+
*
|
|
14
|
+
* The normative spec is `crates/tern/SURFACE_PROTOCOL.md` in the Stencil
|
|
15
|
+
* repository; these types mirror it. Unknown fields and verbs are ignored in
|
|
16
|
+
* both directions, so every addition here is optional.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
/** Protocol version this build speaks. */
|
|
20
|
+
export const TSP_VERSION = 1;
|
|
21
|
+
/** APC identifier: every TSP message body starts with `tsp;`. */
|
|
22
|
+
export const TSP_APC_ID = "tsp";
|
|
23
|
+
/** Largest APC body a sender emits before chunking unless the hello reply says otherwise. */
|
|
24
|
+
export const TSP_DEFAULT_APC_LIMIT = 65_536;
|
|
25
|
+
/** Unacknowledged frames a sender may have in flight unless the hello reply says otherwise. */
|
|
26
|
+
export const TSP_DEFAULT_CREDITS = 2;
|
|
27
|
+
|
|
28
|
+
/** One-letter message verb. */
|
|
29
|
+
export type TspVerb = "q" | "o" | "f" | "b" | "t" | "x" | "r" | "e";
|
|
30
|
+
|
|
31
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
32
|
+
// Vocabulary
|
|
33
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
34
|
+
|
|
35
|
+
/** Every component kind in the v1 vocabulary. */
|
|
36
|
+
export const TSP_KINDS = [
|
|
37
|
+
"col",
|
|
38
|
+
"row",
|
|
39
|
+
"card",
|
|
40
|
+
"section",
|
|
41
|
+
"rule",
|
|
42
|
+
"spacer",
|
|
43
|
+
"text",
|
|
44
|
+
"md",
|
|
45
|
+
"code",
|
|
46
|
+
"diff",
|
|
47
|
+
"ansi",
|
|
48
|
+
"math",
|
|
49
|
+
"image",
|
|
50
|
+
"kv",
|
|
51
|
+
"table",
|
|
52
|
+
"tree",
|
|
53
|
+
"badge",
|
|
54
|
+
"kbd",
|
|
55
|
+
"icon",
|
|
56
|
+
"spinner",
|
|
57
|
+
"shimmer",
|
|
58
|
+
"elapsed",
|
|
59
|
+
"progress",
|
|
60
|
+
"rate",
|
|
61
|
+
"list",
|
|
62
|
+
"item",
|
|
63
|
+
"tabs",
|
|
64
|
+
"editor",
|
|
65
|
+
"input",
|
|
66
|
+
"status",
|
|
67
|
+
"seg",
|
|
68
|
+
"overlay",
|
|
69
|
+
"toast",
|
|
70
|
+
"rows",
|
|
71
|
+
"picker",
|
|
72
|
+
"prefs",
|
|
73
|
+
"tool",
|
|
74
|
+
"checklist",
|
|
75
|
+
"agent",
|
|
76
|
+
"chart",
|
|
77
|
+
"meter",
|
|
78
|
+
"effort",
|
|
79
|
+
] as const;
|
|
80
|
+
|
|
81
|
+
export type TspKind = (typeof TSP_KINDS)[number];
|
|
82
|
+
|
|
83
|
+
/** Semantic colour of a node's chrome. */
|
|
84
|
+
export type TspTone = "neutral" | "accent" | "info" | "success" | "warning" | "error" | "pending" | "muted" | "user";
|
|
85
|
+
|
|
86
|
+
/** Spacing step for `gap`/`size`. */
|
|
87
|
+
export type TspSpace = "none" | "xs" | "sm" | "md" | "lg";
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Size bound: `"40ch"`, `"10lines"`, or a fraction of the available extent
|
|
91
|
+
* (`0.4`). Never pixels or cells.
|
|
92
|
+
*/
|
|
93
|
+
export type TspExtent = `${number}ch` | `${number}lines` | number;
|
|
94
|
+
|
|
95
|
+
/** Per-span visual effect, clocked by the terminal. */
|
|
96
|
+
export type TspEffect = "shimmer" | "pulse" | "none";
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* One styled run of text. `s` holds space-separated semantic tokens
|
|
100
|
+
* (`muted`, `dim`, `strong`, `em`, `accent`, `success`, `warning`, `error`,
|
|
101
|
+
* `info`, `code`, `mono`, `path`, `key`, `link`, `num`, `ins`, `del`, `mark`,
|
|
102
|
+
* `typo`, `icon`, `hide`) or omp theme token names (`thinkingText`,
|
|
103
|
+
* `toolTitle`, …). `mark` highlights (a match, the selected row); `typo` is a
|
|
104
|
+
* misspelled word, which the terminal underlines as its own spell checker does.
|
|
105
|
+
* `icon` marks a run of icon glyphs (Nerd Font / Private Use Area codepoints):
|
|
106
|
+
* the terminal draws it in its icon face and spaces it from neighbouring text
|
|
107
|
+
* itself, so senders omit padding spaces around icons. `hide` takes the run
|
|
108
|
+
* out of the drawing (an editor's mode sigil a chip stands in for).
|
|
109
|
+
*/
|
|
110
|
+
export interface TspSpan {
|
|
111
|
+
t: string;
|
|
112
|
+
s?: string;
|
|
113
|
+
fx?: TspEffect;
|
|
114
|
+
href?: string;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/** Text given either as one plain string or as styled spans. */
|
|
118
|
+
export type TspText = string | readonly TspSpan[];
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* What a pointer gesture on a node does. `zoom` shows an `image` (and the images
|
|
122
|
+
* beside it) large in the terminal's viewer; it is an image's click by default.
|
|
123
|
+
*/
|
|
124
|
+
export type TspAction = "toggle" | "copy" | "open" | "zoom" | "select" | "activate" | (string & {});
|
|
125
|
+
|
|
126
|
+
/** Props every node accepts. */
|
|
127
|
+
export interface TspCommonProps {
|
|
128
|
+
role?: string;
|
|
129
|
+
key?: string;
|
|
130
|
+
tone?: TspTone;
|
|
131
|
+
hidden?: boolean;
|
|
132
|
+
grow?: number;
|
|
133
|
+
shrink?: number;
|
|
134
|
+
basis?: "auto" | "content" | number;
|
|
135
|
+
min?: { w?: TspExtent; h?: TspExtent };
|
|
136
|
+
max?: { w?: TspExtent; h?: TspExtent };
|
|
137
|
+
actions?: { click?: TspAction; dblclick?: TspAction; menu?: readonly TspAction[] };
|
|
138
|
+
title?: string;
|
|
139
|
+
/** Accessible name for icon-only or visual-only nodes. */
|
|
140
|
+
aria?: string;
|
|
141
|
+
/** Target of an `open` action on this node (a URL or `file://` path). */
|
|
142
|
+
href?: string;
|
|
143
|
+
/**
|
|
144
|
+
* Transient selection state drawn over the node without restyling it (the
|
|
145
|
+
* rewind page): `pick` marks the chosen point (adjacent picks read as one
|
|
146
|
+
* run), `drop` dims what the choice discards.
|
|
147
|
+
*/
|
|
148
|
+
mark?: TspMark;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/** A {@link TspCommonProps.mark}. */
|
|
152
|
+
export type TspMark = "pick" | "drop";
|
|
153
|
+
|
|
154
|
+
export type TspWrap = "word" | "char" | "none";
|
|
155
|
+
export type TspTruncate = "end" | "start" | "middle";
|
|
156
|
+
export type TspCardStatus = "pending" | "running" | "done" | "error" | "cancelled";
|
|
157
|
+
export type TspPreview = { lines: number } | "auto";
|
|
158
|
+
|
|
159
|
+
export interface TspColProps {
|
|
160
|
+
gap?: TspSpace;
|
|
161
|
+
align?: "start" | "center" | "end" | "stretch";
|
|
162
|
+
}
|
|
163
|
+
export interface TspRowProps {
|
|
164
|
+
gap?: TspSpace;
|
|
165
|
+
align?: "start" | "center" | "baseline" | "end";
|
|
166
|
+
justify?: "start" | "between" | "end";
|
|
167
|
+
wrap?: boolean;
|
|
168
|
+
}
|
|
169
|
+
export interface TspCardProps {
|
|
170
|
+
/** Header content, inline. */
|
|
171
|
+
head?: TspText;
|
|
172
|
+
status?: TspCardStatus;
|
|
173
|
+
collapsible?: boolean;
|
|
174
|
+
collapsed?: boolean;
|
|
175
|
+
/** Body clamp while collapsed. */
|
|
176
|
+
preview?: TspPreview;
|
|
177
|
+
selected?: boolean;
|
|
178
|
+
inset?: boolean;
|
|
179
|
+
/** `bare`: no ring, fill or insets — a plain head row over the body, for grouped/compact presentations inside another frame. */
|
|
180
|
+
variant?: "bare";
|
|
181
|
+
}
|
|
182
|
+
export interface TspSectionProps {
|
|
183
|
+
head?: TspText;
|
|
184
|
+
collapsible?: boolean;
|
|
185
|
+
collapsed?: boolean;
|
|
186
|
+
/** A finished thinking section (`omp.thinking*`): how long it thought, in ms, like a tool card's `took`. */
|
|
187
|
+
took?: number;
|
|
188
|
+
}
|
|
189
|
+
export interface TspRuleProps {
|
|
190
|
+
label?: TspText;
|
|
191
|
+
}
|
|
192
|
+
export interface TspSpacerProps {
|
|
193
|
+
size?: TspSpace;
|
|
194
|
+
}
|
|
195
|
+
export interface TspTextProps {
|
|
196
|
+
text?: string;
|
|
197
|
+
spans?: readonly TspSpan[];
|
|
198
|
+
wrap?: TspWrap;
|
|
199
|
+
truncate?: TspTruncate;
|
|
200
|
+
lines?: number;
|
|
201
|
+
measure?: "prose" | "fill";
|
|
202
|
+
}
|
|
203
|
+
export interface TspMarkdownProps {
|
|
204
|
+
text?: string;
|
|
205
|
+
/** The tail is still arriving. */
|
|
206
|
+
stream?: boolean;
|
|
207
|
+
/** Literal runs drawn as these spans wherever they occur in prose (a sent prompt's chip tokens). */
|
|
208
|
+
marks?: readonly TspSpan[];
|
|
209
|
+
}
|
|
210
|
+
export interface TspCodeProps {
|
|
211
|
+
text?: string;
|
|
212
|
+
lang?: string;
|
|
213
|
+
path?: string;
|
|
214
|
+
start?: number;
|
|
215
|
+
numbers?: boolean;
|
|
216
|
+
marks?: readonly { line: number; tone: TspTone }[];
|
|
217
|
+
wrap?: boolean;
|
|
218
|
+
}
|
|
219
|
+
export interface TspDiffHunk {
|
|
220
|
+
oldStart: number;
|
|
221
|
+
newStart: number;
|
|
222
|
+
lines: readonly string[];
|
|
223
|
+
}
|
|
224
|
+
export interface TspDiffProps {
|
|
225
|
+
/** Unified diff text (or give `hunks`). */
|
|
226
|
+
text?: string;
|
|
227
|
+
hunks?: readonly TspDiffHunk[];
|
|
228
|
+
path?: string;
|
|
229
|
+
lang?: string;
|
|
230
|
+
mode?: "unified" | "split" | "auto";
|
|
231
|
+
}
|
|
232
|
+
export interface TspAnsiProps {
|
|
233
|
+
/** Raw terminal output (SGR, OSC 8, `\r`, `\b` allowed). */
|
|
234
|
+
text?: string;
|
|
235
|
+
/** Keep the tail in view as text arrives. */
|
|
236
|
+
follow?: boolean;
|
|
237
|
+
preview?: TspPreview;
|
|
238
|
+
/** Wrap hint in cells. */
|
|
239
|
+
cols?: number;
|
|
240
|
+
}
|
|
241
|
+
export interface TspMathProps {
|
|
242
|
+
text?: string;
|
|
243
|
+
display?: boolean;
|
|
244
|
+
}
|
|
245
|
+
/** Images the terminal ships (`image.p.builtin`): `omp` is omp's gradient mark. */
|
|
246
|
+
export type TspBuiltinImage = "omp";
|
|
247
|
+
export interface TspImageProps {
|
|
248
|
+
/** Content address (sha256 hex) of a blob sent with verb `b`. */
|
|
249
|
+
blob?: string;
|
|
250
|
+
/** An image the terminal ships, drawn instead of any blob. */
|
|
251
|
+
builtin?: TspBuiltinImage;
|
|
252
|
+
alt?: string;
|
|
253
|
+
w?: number;
|
|
254
|
+
h?: number;
|
|
255
|
+
max?: { w?: TspExtent; h?: TspExtent };
|
|
256
|
+
}
|
|
257
|
+
export interface TspKvProps {
|
|
258
|
+
items: readonly { k: TspText; v: TspText }[];
|
|
259
|
+
layout?: "grid" | "inline";
|
|
260
|
+
}
|
|
261
|
+
export interface TspTableColumn {
|
|
262
|
+
id: string;
|
|
263
|
+
head?: TspText;
|
|
264
|
+
align?: "start" | "center" | "end";
|
|
265
|
+
truncate?: TspTruncate;
|
|
266
|
+
/** Lower priorities hide first when narrow. */
|
|
267
|
+
priority?: number;
|
|
268
|
+
grow?: number;
|
|
269
|
+
}
|
|
270
|
+
export interface TspTableProps {
|
|
271
|
+
cols: readonly TspTableColumn[];
|
|
272
|
+
rows: readonly { id: string; cells: Readonly<Record<string, TspText>> }[];
|
|
273
|
+
}
|
|
274
|
+
export interface TspTreeNode {
|
|
275
|
+
id: string;
|
|
276
|
+
label: TspText;
|
|
277
|
+
icon?: string;
|
|
278
|
+
open?: boolean;
|
|
279
|
+
children?: readonly TspTreeNode[];
|
|
280
|
+
}
|
|
281
|
+
export interface TspTreeProps {
|
|
282
|
+
nodes: readonly TspTreeNode[];
|
|
283
|
+
}
|
|
284
|
+
export interface TspBadgeProps {
|
|
285
|
+
text: string;
|
|
286
|
+
}
|
|
287
|
+
export interface TspKbdProps {
|
|
288
|
+
keys: readonly string[];
|
|
289
|
+
}
|
|
290
|
+
export interface TspIconProps {
|
|
291
|
+
name: string;
|
|
292
|
+
}
|
|
293
|
+
export interface TspSpinnerProps {
|
|
294
|
+
style?: "dots" | "braille" | "starburst" | "orbit";
|
|
295
|
+
label?: TspText;
|
|
296
|
+
}
|
|
297
|
+
export interface TspShimmerProps {
|
|
298
|
+
text?: string;
|
|
299
|
+
spans?: readonly TspSpan[];
|
|
300
|
+
mode?: "classic" | "kitt";
|
|
301
|
+
palette?: { low?: string; mid?: string; high?: string };
|
|
302
|
+
}
|
|
303
|
+
export interface TspElapsedProps {
|
|
304
|
+
/** Milliseconds already elapsed when the frame was written. */
|
|
305
|
+
age: number;
|
|
306
|
+
/** Freeze at this many milliseconds. */
|
|
307
|
+
stopped?: number;
|
|
308
|
+
format?: "short" | "clock";
|
|
309
|
+
}
|
|
310
|
+
export interface TspProgressProps {
|
|
311
|
+
/** 0–1, or null for indeterminate. */
|
|
312
|
+
value: number | null;
|
|
313
|
+
label?: TspText;
|
|
314
|
+
}
|
|
315
|
+
export interface TspRateProps {
|
|
316
|
+
value: number;
|
|
317
|
+
unit?: string;
|
|
318
|
+
}
|
|
319
|
+
export interface TspListProps {
|
|
320
|
+
selected?: string | null;
|
|
321
|
+
filter?: string;
|
|
322
|
+
empty?: TspText;
|
|
323
|
+
max?: { lines: number } | number;
|
|
324
|
+
virtual?: boolean;
|
|
325
|
+
}
|
|
326
|
+
export interface TspItemProps {
|
|
327
|
+
label: TspText;
|
|
328
|
+
detail?: TspText;
|
|
329
|
+
icon?: string;
|
|
330
|
+
hint?: readonly string[];
|
|
331
|
+
disabled?: boolean;
|
|
332
|
+
value?: TspText;
|
|
333
|
+
}
|
|
334
|
+
export interface TspTabsProps {
|
|
335
|
+
items: readonly { id: string; label: TspText }[];
|
|
336
|
+
active?: string;
|
|
337
|
+
}
|
|
338
|
+
export interface TspEditorDecoration {
|
|
339
|
+
from: number;
|
|
340
|
+
to: number;
|
|
341
|
+
s: string;
|
|
342
|
+
fx?: TspEffect;
|
|
343
|
+
}
|
|
344
|
+
export interface TspEditorProps {
|
|
345
|
+
text?: string;
|
|
346
|
+
/** Caret as a UTF-16 offset into `text`. */
|
|
347
|
+
cursor?: number;
|
|
348
|
+
/** Selection anchor, or null for none. */
|
|
349
|
+
anchor?: number | null;
|
|
350
|
+
decor?: readonly TspEditorDecoration[];
|
|
351
|
+
/** Inline completion suffix drawn after the caret. */
|
|
352
|
+
ghost?: string;
|
|
353
|
+
placeholder?: string;
|
|
354
|
+
prompt?: TspText;
|
|
355
|
+
/** Mode label (vim). */
|
|
356
|
+
mode?: string;
|
|
357
|
+
/** The text is code in this language (`python`, `bash`): highlighted, in the mono face. */
|
|
358
|
+
lang?: string;
|
|
359
|
+
readonly?: boolean;
|
|
360
|
+
maxLines?: number;
|
|
361
|
+
}
|
|
362
|
+
export type TspInputProps = Omit<TspEditorProps, "maxLines">;
|
|
363
|
+
export interface TspStatusProps {
|
|
364
|
+
transparent?: boolean;
|
|
365
|
+
}
|
|
366
|
+
export interface TspSegProps {
|
|
367
|
+
spans?: readonly TspSpan[];
|
|
368
|
+
icon?: string;
|
|
369
|
+
/** Higher priorities stay longer when space runs out. */
|
|
370
|
+
priority?: number;
|
|
371
|
+
min?: { w?: TspExtent; h?: TspExtent };
|
|
372
|
+
/** Which side of the status bar the segment sits on. */
|
|
373
|
+
side?: "left" | "right";
|
|
374
|
+
}
|
|
375
|
+
export type TspOverlayAnchor =
|
|
376
|
+
| "center"
|
|
377
|
+
| "top"
|
|
378
|
+
| "bottom"
|
|
379
|
+
| { node: string; side: "above" | "below" }
|
|
380
|
+
| { caret: string };
|
|
381
|
+
export interface TspOverlayProps {
|
|
382
|
+
anchor?: TspOverlayAnchor;
|
|
383
|
+
size?: "sm" | "md" | "lg" | "full";
|
|
384
|
+
modal?: boolean;
|
|
385
|
+
head?: TspText;
|
|
386
|
+
}
|
|
387
|
+
export interface TspToastProps {
|
|
388
|
+
text: string;
|
|
389
|
+
sub?: string;
|
|
390
|
+
ttl?: number;
|
|
391
|
+
}
|
|
392
|
+
export interface TspRowsProps {
|
|
393
|
+
/** Width the rows were rendered at. */
|
|
394
|
+
cols: number;
|
|
395
|
+
/** Pre-rendered ANSI rows (migration fallback only). */
|
|
396
|
+
lines: readonly string[];
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
// ─── Data-first kinds (crates/tern/NATIVE_REDESIGN.md §3–§5) ─────────────
|
|
400
|
+
|
|
401
|
+
/** One selectable picker row. Every field but `id` and `label` is optional. */
|
|
402
|
+
export interface TspPickerItem {
|
|
403
|
+
id: string;
|
|
404
|
+
label: TspText;
|
|
405
|
+
/** Second line (`cards`) or trailing dim text (`rows`). */
|
|
406
|
+
detail?: TspText;
|
|
407
|
+
/** Leading visual: a named icon, a provider/avatar mark, or a status dot. */
|
|
408
|
+
icon?: string;
|
|
409
|
+
mark?: { text: string; seed?: string };
|
|
410
|
+
dot?: TspTone;
|
|
411
|
+
/** Label is a machine string (model id, path): mono, dim prefix and strong tail by the `/`. */
|
|
412
|
+
mono?: boolean;
|
|
413
|
+
/** Values for `columns`, by column id. `bar` columns take a number 0–1. */
|
|
414
|
+
facts?: Readonly<Record<string, TspText | number>>;
|
|
415
|
+
badges?: readonly { text: string; tone?: TspTone; title?: string }[];
|
|
416
|
+
/** Role-style chips after the label (`dot` = a thinking-level or state colour token). */
|
|
417
|
+
chips?: readonly { text: string; on?: boolean; auto?: boolean; dot?: string }[];
|
|
418
|
+
tone?: TspTone;
|
|
419
|
+
/** Disabled, with the reason as tooltip. */
|
|
420
|
+
disabled?: string | true;
|
|
421
|
+
/** Search hits in the label as UTF-16 [from, to) ranges. */
|
|
422
|
+
hits?: readonly (readonly [number, number])[];
|
|
423
|
+
/** `timeline`: node style; `tree`: nesting. */
|
|
424
|
+
node?: "user" | "assistant" | "tool" | "marker";
|
|
425
|
+
depth?: number;
|
|
426
|
+
open?: boolean;
|
|
427
|
+
/** Leading glyph slot for tree/timeline rows (omp role → icon, e.g. `omp.tool.grep`). */
|
|
428
|
+
role?: string;
|
|
429
|
+
title?: string;
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
/** A right-aligned fact column in a picker. */
|
|
433
|
+
export interface TspPickerColumn {
|
|
434
|
+
id: string;
|
|
435
|
+
head?: string;
|
|
436
|
+
/** `elapsed`: the value is an age in ms at send; Tern clocks it (spec §9). */
|
|
437
|
+
format?: "text" | "num" | "price" | "bar" | "time" | "elapsed" | "dim";
|
|
438
|
+
/** Lower priorities hide first when narrow. */
|
|
439
|
+
priority?: number;
|
|
440
|
+
/** Minimum width in ch. */
|
|
441
|
+
min?: number;
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
/** A row of a picker's scope column. */
|
|
445
|
+
export interface TspPickerScope {
|
|
446
|
+
id: string;
|
|
447
|
+
label: TspText;
|
|
448
|
+
icon?: string;
|
|
449
|
+
mark?: { text: string; seed?: string };
|
|
450
|
+
count?: number;
|
|
451
|
+
/** Group heading this scope sits under (consecutive scopes with the same group share a head). */
|
|
452
|
+
group?: string;
|
|
453
|
+
disabled?: string | true;
|
|
454
|
+
/** Status dot: `success` ok, `warning` cached/empty, `error` unavailable, `muted` signed out. */
|
|
455
|
+
dot?: TspTone;
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
/** A button of a picker's action bar. */
|
|
459
|
+
export interface TspPickerAction {
|
|
460
|
+
/** Sent back as `action.act`. */
|
|
461
|
+
id: string;
|
|
462
|
+
label: string;
|
|
463
|
+
/** Keycap(s) the program binds to it, shown in the button (`["enter"]`, `["alt","enter"]`). */
|
|
464
|
+
keys?: readonly string[];
|
|
465
|
+
primary?: boolean;
|
|
466
|
+
danger?: boolean;
|
|
467
|
+
/** Right-aligned group (close, help). */
|
|
468
|
+
end?: boolean;
|
|
469
|
+
/** A toggle's state (`Task model ⌥P`): drawn pressed when true. */
|
|
470
|
+
on?: boolean;
|
|
471
|
+
disabled?: string | true;
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
/** A group header inside a picker's `order`. */
|
|
475
|
+
export interface TspPickerGroup {
|
|
476
|
+
group: string;
|
|
477
|
+
label: TspText;
|
|
478
|
+
count?: number;
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
/**
|
|
482
|
+
* A data-first picker sheet (models, sessions, rewind, …). Children are the
|
|
483
|
+
* selected item's preview. A picker under `layer` is itself the modal sheet.
|
|
484
|
+
*/
|
|
485
|
+
export interface TspPickerProps {
|
|
486
|
+
/**
|
|
487
|
+
* What is being picked, e.g. "Models" (plain: it is also the common `title` prop, which a picker does not use as a
|
|
488
|
+
* tooltip). Absent: the head is the icon and the search, and the placeholder names the sheet.
|
|
489
|
+
*/
|
|
490
|
+
title?: string;
|
|
491
|
+
subtitle?: TspText;
|
|
492
|
+
icon?: string;
|
|
493
|
+
/** Plural noun for counts and empty copy ("models", "sessions"). */
|
|
494
|
+
noun?: string;
|
|
495
|
+
size?: "md" | "lg" | "screen";
|
|
496
|
+
layout?: "rows" | "cards" | "timeline" | "tree";
|
|
497
|
+
/** The program's search text (keys stay the program's); `null` hides the field. */
|
|
498
|
+
query?: string | null;
|
|
499
|
+
/** Caret as a UTF-16 offset into `query`; absent puts it at the end. */
|
|
500
|
+
cursor?: number;
|
|
501
|
+
placeholder?: string;
|
|
502
|
+
scopes?: readonly TspPickerScope[];
|
|
503
|
+
scope?: string;
|
|
504
|
+
tabs?: readonly { id: string; label: TspText; count?: number }[];
|
|
505
|
+
tab?: string;
|
|
506
|
+
columns?: readonly TspPickerColumn[];
|
|
507
|
+
/** The catalogue, sent once and patched by id through `itemsAdd`/`itemsDel`. */
|
|
508
|
+
items?: readonly TspPickerItem[];
|
|
509
|
+
/** Upserts applied to `items`. */
|
|
510
|
+
itemsAdd?: readonly TspPickerItem[];
|
|
511
|
+
/** Removals applied to `items`. */
|
|
512
|
+
itemsDel?: readonly string[];
|
|
513
|
+
/** What to show, in order, after the program's filtering: item ids and group headers. Absent = `items` order. */
|
|
514
|
+
order?: readonly (string | TspPickerGroup)[];
|
|
515
|
+
/** Hit ranges per item id for the current query (kept apart so `items` never changes while typing). */
|
|
516
|
+
hits?: Readonly<Record<string, readonly (readonly [number, number])[]>>;
|
|
517
|
+
selected?: string | null;
|
|
518
|
+
/** Items in use now (current model, current session). */
|
|
519
|
+
current?: readonly string[];
|
|
520
|
+
/** Total before filtering, for the head count ("12 of 1,604"). */
|
|
521
|
+
total?: number;
|
|
522
|
+
preview?: "side" | "below" | "none";
|
|
523
|
+
actions?: readonly TspPickerAction[];
|
|
524
|
+
/** A chip strip docked above the action bar (the model hub's role assignment). */
|
|
525
|
+
strip?: {
|
|
526
|
+
label?: TspText;
|
|
527
|
+
items: readonly { id: string; label: TspText; on?: boolean; dot?: string }[];
|
|
528
|
+
selected?: string;
|
|
529
|
+
} | null;
|
|
530
|
+
state?: "ready" | "loading" | "error";
|
|
531
|
+
message?: TspText;
|
|
532
|
+
empty?: TspText;
|
|
533
|
+
confirm?: { text: TspText; act: string; label?: string } | null;
|
|
534
|
+
/** Which region owns the keyboard now: the terminal draws the focus ring there. */
|
|
535
|
+
focus?: "list" | "scopes" | "tabs" | "strip" | "preview";
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
/** A settings row's typed control. */
|
|
539
|
+
export type TspPrefsControl =
|
|
540
|
+
| { k: "switch"; on: boolean }
|
|
541
|
+
| {
|
|
542
|
+
k: "choice";
|
|
543
|
+
value: string;
|
|
544
|
+
options: readonly { value: string; label: string; detail?: string }[];
|
|
545
|
+
style?: "auto" | "segmented" | "menu";
|
|
546
|
+
mono?: boolean;
|
|
547
|
+
}
|
|
548
|
+
| {
|
|
549
|
+
k: "number";
|
|
550
|
+
value: number;
|
|
551
|
+
min?: number;
|
|
552
|
+
max?: number;
|
|
553
|
+
step?: number;
|
|
554
|
+
unit?: string;
|
|
555
|
+
labels?: Readonly<Record<string, string>>;
|
|
556
|
+
}
|
|
557
|
+
| { k: "text"; value: string; placeholder?: string; secret?: boolean; mono?: boolean }
|
|
558
|
+
| { k: "keys"; keys: readonly (readonly string[])[] }
|
|
559
|
+
| {
|
|
560
|
+
k: "multi";
|
|
561
|
+
values: readonly string[];
|
|
562
|
+
options: readonly { value: string; label: string; detail?: string }[];
|
|
563
|
+
ordered?: boolean;
|
|
564
|
+
}
|
|
565
|
+
| { k: "action"; label: string; act: string };
|
|
566
|
+
|
|
567
|
+
/** One settings row. */
|
|
568
|
+
export interface TspPrefsRow {
|
|
569
|
+
/** The setting path (`theme.dark`); sent back as `item`. */
|
|
570
|
+
id: string;
|
|
571
|
+
label: string;
|
|
572
|
+
hint?: string;
|
|
573
|
+
warning?: string;
|
|
574
|
+
/** Differs from the default. */
|
|
575
|
+
changed?: boolean;
|
|
576
|
+
/** Default shown in the changed dot's title. */
|
|
577
|
+
defaultLabel?: string;
|
|
578
|
+
disabled?: string;
|
|
579
|
+
control: TspPrefsControl;
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
/** A titled group of settings rows. */
|
|
583
|
+
export interface TspPrefsSection {
|
|
584
|
+
id: string;
|
|
585
|
+
title: string;
|
|
586
|
+
/** Search results: the page this section belongs to. */
|
|
587
|
+
page?: string;
|
|
588
|
+
rows: readonly TspPrefsRow[];
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
/** A data-first settings page set (the terminal draws a native settings window). Children are previews placed by `role`. */
|
|
592
|
+
export interface TspPrefsProps {
|
|
593
|
+
title: string;
|
|
594
|
+
pages: readonly {
|
|
595
|
+
id: string;
|
|
596
|
+
label: string;
|
|
597
|
+
icon?: string;
|
|
598
|
+
changed?: number;
|
|
599
|
+
group?: string;
|
|
600
|
+
disabled?: string;
|
|
601
|
+
}[];
|
|
602
|
+
page: string;
|
|
603
|
+
/** Lead text of the current page. */
|
|
604
|
+
lead?: string;
|
|
605
|
+
/** The program's search text; non-empty switches the page to search results. */
|
|
606
|
+
query?: string;
|
|
607
|
+
/** Caret as a UTF-16 offset into `query` while the program's search field has it; absent: no caret. */
|
|
608
|
+
cursor?: number;
|
|
609
|
+
sections: readonly TspPrefsSection[];
|
|
610
|
+
/** Row with keyboard focus (the program's selection). */
|
|
611
|
+
focus?: string | null;
|
|
612
|
+
/** Row whose control the program has open for editing, with its draft (and its caret, a UTF-16 offset) for text rows. */
|
|
613
|
+
editing?: { row: string; draft?: string; cursor?: number; option?: string } | null;
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
/**
|
|
617
|
+
* One tool call (NATIVE_REDESIGN §7.2): data for the head plus generic
|
|
618
|
+
* children for the body. The terminal draws the head, the single frame (or
|
|
619
|
+
* none) and the status motion, and flattens nested `card`/`tool` children
|
|
620
|
+
* into borderless sections.
|
|
621
|
+
*/
|
|
622
|
+
export interface TspToolProps {
|
|
623
|
+
/** Tool name (`bash`, `edit`, `mcp__github__search`): icon, role styles. */
|
|
624
|
+
name: string;
|
|
625
|
+
/** The verb as users read it ("Bash", "Edit", "Read"; eval uses the model's title). */
|
|
626
|
+
title: TspText;
|
|
627
|
+
/** The primary argument, shown once: command, path, pattern, query. Mono. */
|
|
628
|
+
target?: TspText;
|
|
629
|
+
/** How to draw `target`: a shell command (highlighted), a path (dim dir, strong name), a pattern/query (quoted accent-ink). */
|
|
630
|
+
targetKind?: "command" | "path" | "pattern" | "query" | "text";
|
|
631
|
+
/** Language for `command` highlighting (`bash`, `python`, `js`). */
|
|
632
|
+
lang?: string;
|
|
633
|
+
/** `file://` link for path targets (⌘-click opens). */
|
|
634
|
+
href?: string;
|
|
635
|
+
/** Short facts after the target: `+8 −1`, `5 matches · 2 files`, `22 lines`. */
|
|
636
|
+
meta?: readonly TspText[];
|
|
637
|
+
/** Badges in the head (`you`, `background`, `new file`, `python`). */
|
|
638
|
+
badges?: readonly { text: string; tone?: TspTone; title?: string }[];
|
|
639
|
+
status: TspCardStatus;
|
|
640
|
+
/** Running: ms already elapsed when sent. Done: total duration. */
|
|
641
|
+
age?: number;
|
|
642
|
+
took?: number;
|
|
643
|
+
/** Non-zero exit renders as an error chip `exit 1`. */
|
|
644
|
+
exit?: number | null;
|
|
645
|
+
/** A one-word state note: "timed out", "cancelled", "partial". */
|
|
646
|
+
note?: TspText;
|
|
647
|
+
/** The model's intent line (`i` arg): shown in the working row while running, as the head's tooltip after. */
|
|
648
|
+
intent?: string;
|
|
649
|
+
/** `card`: one frame. `inline`: no ring, a head line plus a disclosed body. */
|
|
650
|
+
frame?: "card" | "inline";
|
|
651
|
+
collapsible?: boolean;
|
|
652
|
+
collapsed?: boolean;
|
|
653
|
+
/** Body clamp while collapsed: `{lines}` shows the head of the body, `{tail}` the end (bash output). */
|
|
654
|
+
preview?: { lines: number } | { tail: number } | "none";
|
|
655
|
+
/** Actions offered in the head on hover (`copy`, `rerun`, `open`). */
|
|
656
|
+
tools?: readonly TspPickerAction[];
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
/** One checklist item (§7.5). */
|
|
660
|
+
export interface TspChecklistItem {
|
|
661
|
+
id: string;
|
|
662
|
+
text: TspText;
|
|
663
|
+
status: "pending" | "active" | "done" | "dropped" | "blocked";
|
|
664
|
+
/** Blocker or note, shown under the item in `--t3` (blocked: `--warn`). */
|
|
665
|
+
note?: TspText;
|
|
666
|
+
}
|
|
667
|
+
/** A checklist phase (§7.5). */
|
|
668
|
+
export interface TspChecklistPhase {
|
|
669
|
+
id: string;
|
|
670
|
+
title: TspText;
|
|
671
|
+
items: readonly TspChecklistItem[];
|
|
672
|
+
/** Completed phases fold to one line unless expanded. */
|
|
673
|
+
collapsed?: boolean;
|
|
674
|
+
}
|
|
675
|
+
/** A todo list: the todo tool body, the dock HUD, or the reminder notice (§7.5). */
|
|
676
|
+
export interface TspChecklistProps {
|
|
677
|
+
phases: readonly TspChecklistPhase[];
|
|
678
|
+
/** `full` (the todo tool body), `hud` (dock pill + popover), `reminder` (inline notice listing open items). */
|
|
679
|
+
mode?: "full" | "hud" | "reminder";
|
|
680
|
+
/** Reminder count text ("reminder 1/3"). */
|
|
681
|
+
note?: TspText;
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
/** One subagent as live data (§7.6). Children are the expanded content. */
|
|
685
|
+
export interface TspAgentProps {
|
|
686
|
+
/** Display id ("SeqAudit"). */
|
|
687
|
+
name: string;
|
|
688
|
+
/** Agent type ("task", "explore") as a badge. */
|
|
689
|
+
agent?: string;
|
|
690
|
+
/** One-line task description (the assignment summary, never the raw "Complete assignment thoroughly:" prefix). */
|
|
691
|
+
task?: TspText;
|
|
692
|
+
status: "pending" | "running" | "done" | "failed" | "aborted" | "idle" | "parked";
|
|
693
|
+
model?: string;
|
|
694
|
+
/** Thinking level token (`thinkingLow`) for the model chip's dot. */
|
|
695
|
+
thinking?: string;
|
|
696
|
+
/** The tool running now, with its intent and age. */
|
|
697
|
+
tool?: { name: string; intent?: TspText; age?: number } | null;
|
|
698
|
+
stats?: {
|
|
699
|
+
tools?: number;
|
|
700
|
+
requests?: number;
|
|
701
|
+
tokens?: number;
|
|
702
|
+
context?: number;
|
|
703
|
+
contextLabel?: string;
|
|
704
|
+
/** The agent's own completion estimate, 0–1; drawn while running. */
|
|
705
|
+
done?: number;
|
|
706
|
+
cost?: number;
|
|
707
|
+
age?: number;
|
|
708
|
+
took?: number;
|
|
709
|
+
};
|
|
710
|
+
retry?: { attempt: number; max: number; age: number; delay: number; error?: string } | null;
|
|
711
|
+
/** `background`, `read-only`, `isolated`. */
|
|
712
|
+
badges?: readonly { text: string; tone?: TspTone }[];
|
|
713
|
+
/** Nesting depth in a tree of agents. */
|
|
714
|
+
depth?: number;
|
|
715
|
+
collapsible?: boolean;
|
|
716
|
+
collapsed?: boolean;
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
/**
|
|
720
|
+
* A thinking-effort glyph: a small ring that fills rung by rung with the level and
|
|
721
|
+
* turns into a flickering fireball at `max`. Leaf; the terminal draws everything.
|
|
722
|
+
*/
|
|
723
|
+
export interface TspEffortProps {
|
|
724
|
+
/** `off`, `minimal`, `low`, `medium`, `high`, `xhigh` or `max`; anything else (e.g. `auto`) draws an empty dashed ring. */
|
|
725
|
+
level: string;
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
/**
|
|
729
|
+
* A tick on a meter's track (compaction threshold, speculation point). A bar mark's `icon`
|
|
730
|
+
* (a symbol name, e.g. `context.compaction`) sits on the track, which breaks for it.
|
|
731
|
+
*/
|
|
732
|
+
export interface TspMeterMark {
|
|
733
|
+
/** Position, 0–1. */
|
|
734
|
+
at: number;
|
|
735
|
+
tone?: TspTone;
|
|
736
|
+
title?: string;
|
|
737
|
+
icon?: string;
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
/** A value drawn as a bar, ring or block grid (§8.2): context %, usage windows, agent context. */
|
|
741
|
+
export interface TspMeterProps {
|
|
742
|
+
/** 0–1, or null for unknown. */
|
|
743
|
+
value: number | null;
|
|
744
|
+
style?: "bar" | "ring" | "blocks";
|
|
745
|
+
/** `blocks` only: exactly this many cells in one row, `round(value × steps)` of them filled (e.g. the effort chip's fallback meter). */
|
|
746
|
+
steps?: number;
|
|
747
|
+
/** Stacked parts instead of one fill (context breakdown); values sum to ≤ 1. */
|
|
748
|
+
parts?: readonly { value: number; token?: string; label?: string; hatch?: boolean }[];
|
|
749
|
+
marks?: readonly TspMeterMark[];
|
|
750
|
+
/** Tone switches: at or above `warn` → warning, `bad` → error. */
|
|
751
|
+
thresholds?: { warn?: number; bad?: number };
|
|
752
|
+
/** The value as text (`74%`). */
|
|
753
|
+
label?: TspText;
|
|
754
|
+
/** The whole the track spans as text (a context window's `200K`). */
|
|
755
|
+
total?: TspText;
|
|
756
|
+
size?: "sm" | "md" | "lg";
|
|
757
|
+
}
|
|
758
|
+
/** Series data drawn natively: the usage heatmap, app dashboards (§9.2). */
|
|
759
|
+
export interface TspChartProps {
|
|
760
|
+
kind: "heatmap" | "bars" | "spark";
|
|
761
|
+
/** heatmap: rows × columns of 0–1 intensities (null = no data). */
|
|
762
|
+
cells?: readonly (readonly (number | null)[])[];
|
|
763
|
+
/** Column labels placed at column indexes (months). */
|
|
764
|
+
cols?: readonly { at: number; label: string }[];
|
|
765
|
+
/** Row labels (weekdays); empty strings skip a label. */
|
|
766
|
+
rows?: readonly string[];
|
|
767
|
+
/** Per-cell tooltips, same shape as `cells` (omit for none). */
|
|
768
|
+
tips?: readonly (readonly (string | null)[])[];
|
|
769
|
+
/** bars/spark: one series. */
|
|
770
|
+
series?: readonly { label?: string; value: number; title?: string }[];
|
|
771
|
+
/** Colour token of the fill (`accent`, `statusLineCost`). */
|
|
772
|
+
token?: string;
|
|
773
|
+
summary?: TspText;
|
|
774
|
+
/** Pixel-free size hint: cell size step for heatmaps, height in lines for bars/spark. */
|
|
775
|
+
size?: "sm" | "md" | "lg";
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
/** Kind-specific props, indexed by kind. */
|
|
779
|
+
export interface TspPropsByKind {
|
|
780
|
+
col: TspColProps;
|
|
781
|
+
row: TspRowProps;
|
|
782
|
+
card: TspCardProps;
|
|
783
|
+
section: TspSectionProps;
|
|
784
|
+
rule: TspRuleProps;
|
|
785
|
+
spacer: TspSpacerProps;
|
|
786
|
+
text: TspTextProps;
|
|
787
|
+
md: TspMarkdownProps;
|
|
788
|
+
code: TspCodeProps;
|
|
789
|
+
diff: TspDiffProps;
|
|
790
|
+
ansi: TspAnsiProps;
|
|
791
|
+
math: TspMathProps;
|
|
792
|
+
image: TspImageProps;
|
|
793
|
+
kv: TspKvProps;
|
|
794
|
+
table: TspTableProps;
|
|
795
|
+
tree: TspTreeProps;
|
|
796
|
+
badge: TspBadgeProps;
|
|
797
|
+
kbd: TspKbdProps;
|
|
798
|
+
icon: TspIconProps;
|
|
799
|
+
spinner: TspSpinnerProps;
|
|
800
|
+
shimmer: TspShimmerProps;
|
|
801
|
+
elapsed: TspElapsedProps;
|
|
802
|
+
progress: TspProgressProps;
|
|
803
|
+
rate: TspRateProps;
|
|
804
|
+
list: TspListProps;
|
|
805
|
+
item: TspItemProps;
|
|
806
|
+
tabs: TspTabsProps;
|
|
807
|
+
editor: TspEditorProps;
|
|
808
|
+
input: TspInputProps;
|
|
809
|
+
status: TspStatusProps;
|
|
810
|
+
seg: TspSegProps;
|
|
811
|
+
overlay: TspOverlayProps;
|
|
812
|
+
toast: TspToastProps;
|
|
813
|
+
rows: TspRowsProps;
|
|
814
|
+
picker: TspPickerProps;
|
|
815
|
+
prefs: TspPrefsProps;
|
|
816
|
+
tool: TspToolProps;
|
|
817
|
+
checklist: TspChecklistProps;
|
|
818
|
+
agent: TspAgentProps;
|
|
819
|
+
chart: TspChartProps;
|
|
820
|
+
meter: TspMeterProps;
|
|
821
|
+
effort: TspEffortProps;
|
|
822
|
+
}
|
|
823
|
+
|
|
824
|
+
/** Props of a node of kind `K`: its kind-specific props plus the common ones. */
|
|
825
|
+
export type TspProps<K extends TspKind = TspKind> = TspPropsByKind[K] & TspCommonProps;
|
|
826
|
+
|
|
827
|
+
/** Kinds whose primary text the `text` and `splice` ops address (the `text` prop). */
|
|
828
|
+
export const TSP_TEXT_KINDS = ["text", "md", "code", "ansi", "math", "editor", "input", "shimmer"] as const;
|
|
829
|
+
|
|
830
|
+
/** A node on the wire. */
|
|
831
|
+
export type TspNode = {
|
|
832
|
+
[K in TspKind]: { id: string; k: K; p?: TspProps<K>; c?: readonly TspNode[] };
|
|
833
|
+
}[TspKind];
|
|
834
|
+
|
|
835
|
+
/** Fixed ids of a surface's three regions. */
|
|
836
|
+
export const TSP_REGION_IDS = { main: "main", dock: "dock", layer: "layer" } as const;
|
|
837
|
+
|
|
838
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
839
|
+
// Ops and messages
|
|
840
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
841
|
+
|
|
842
|
+
export type TspOp =
|
|
843
|
+
| readonly [op: "add", id: string, parent: string, before: string | null, node: TspNode]
|
|
844
|
+
| readonly [op: "set", id: string, props: Readonly<Record<string, unknown>>]
|
|
845
|
+
| readonly [op: "text", id: string, mode: "append" | "replace", text: string]
|
|
846
|
+
| readonly [op: "splice", id: string, at: number, del: number, text: string]
|
|
847
|
+
| readonly [op: "move", id: string, parent: string, before: string | null]
|
|
848
|
+
| readonly [op: "del", id: string]
|
|
849
|
+
| readonly [op: "settle", id: string]
|
|
850
|
+
| readonly [op: "focus", id: string | null]
|
|
851
|
+
| readonly [op: "reveal", id: string, where: "start" | "end" | "nearest"]
|
|
852
|
+
| readonly [op: "scroll", id: string, by: TspScrollBy]
|
|
853
|
+
| readonly [op: "suspend"]
|
|
854
|
+
| readonly [op: "resume"];
|
|
855
|
+
|
|
856
|
+
/**
|
|
857
|
+
* How far a `scroll` op moves the scroller holding a node: a line, a
|
|
858
|
+
* viewport less a line, or to an end (`end` makes a following `ansi` block
|
|
859
|
+
* follow again). Sent only when `hello.features` lists `scroll`.
|
|
860
|
+
*/
|
|
861
|
+
export type TspScrollBy = "line-up" | "line-down" | "page-up" | "page-down" | "start" | "end";
|
|
862
|
+
|
|
863
|
+
/** Verb `f`: an atomic batch of ops for one surface. */
|
|
864
|
+
export interface TspFrame {
|
|
865
|
+
/** Surface id. */
|
|
866
|
+
sf: string;
|
|
867
|
+
/** Monotonic per-surface sequence number. */
|
|
868
|
+
s: number;
|
|
869
|
+
ops: readonly TspOp[];
|
|
870
|
+
}
|
|
871
|
+
|
|
872
|
+
/** Verb `o`: open (or adopt) a surface. */
|
|
873
|
+
export interface TspOpen {
|
|
874
|
+
id: string;
|
|
875
|
+
mode: "inline" | "screen";
|
|
876
|
+
title?: string;
|
|
877
|
+
role?: string;
|
|
878
|
+
adopt?: boolean;
|
|
879
|
+
}
|
|
880
|
+
|
|
881
|
+
/** Verb `x`: close a surface. */
|
|
882
|
+
export interface TspClose {
|
|
883
|
+
id: string;
|
|
884
|
+
/** Keep `main` in scrollback (true) or remove the surface (false). */
|
|
885
|
+
keep: boolean;
|
|
886
|
+
}
|
|
887
|
+
|
|
888
|
+
/**
|
|
889
|
+
* Verb `t`: the program's resolved theme for a surface, sent after `o` and
|
|
890
|
+
* before its first `f`, and again whenever the theme or variant changes.
|
|
891
|
+
* Each variant maps token names to `#rrggbb`; tokens left at the terminal
|
|
892
|
+
* default are omitted. A program with one variant sends only that one.
|
|
893
|
+
*/
|
|
894
|
+
export interface TspPalette {
|
|
895
|
+
/** Surface id. */
|
|
896
|
+
sf: string;
|
|
897
|
+
dark?: Readonly<Record<string, string>>;
|
|
898
|
+
light?: Readonly<Record<string, string>>;
|
|
899
|
+
/** Theme names behind each variant. */
|
|
900
|
+
name?: { dark?: string; light?: string };
|
|
901
|
+
}
|
|
902
|
+
|
|
903
|
+
/** Verb `q`. */
|
|
904
|
+
export type TspQuery =
|
|
905
|
+
| { q: "hello"; v: readonly number[]; app: string; ver?: string }
|
|
906
|
+
| { q: "blobs"; ids: readonly string[] };
|
|
907
|
+
|
|
908
|
+
/** Verb `r`. */
|
|
909
|
+
export type TspReply =
|
|
910
|
+
| {
|
|
911
|
+
r: "hello";
|
|
912
|
+
v: number;
|
|
913
|
+
term: string;
|
|
914
|
+
ver?: string;
|
|
915
|
+
kinds: readonly string[];
|
|
916
|
+
features?: readonly string[];
|
|
917
|
+
apc?: number;
|
|
918
|
+
credits?: number;
|
|
919
|
+
cols?: number;
|
|
920
|
+
cell?: { w: number; h: number };
|
|
921
|
+
dark?: boolean;
|
|
922
|
+
reduceMotion?: boolean;
|
|
923
|
+
}
|
|
924
|
+
| { r: "blobs"; have: readonly string[] };
|
|
925
|
+
|
|
926
|
+
/** Verb `e`: terminal → program events. */
|
|
927
|
+
export type TspEvent =
|
|
928
|
+
| { ev: "ack"; sf: string; s: number }
|
|
929
|
+
| { ev: "resize"; sf?: string; cols: number; cell?: { w: number; h: number }; visible?: boolean }
|
|
930
|
+
| { ev: "theme"; dark: boolean }
|
|
931
|
+
| { ev: "motion"; reduce: boolean }
|
|
932
|
+
| { ev: "visible"; sf?: string; visible: boolean }
|
|
933
|
+
| { ev: "toggle"; sf: string; id: string; key?: string; collapsed: boolean }
|
|
934
|
+
| { ev: "select"; sf: string; id: string; item: string }
|
|
935
|
+
| { ev: "activate"; sf: string; id: string; item: string }
|
|
936
|
+
| { ev: "action"; sf: string; id: string; act: string; value?: string; mods?: readonly string[] }
|
|
937
|
+
/** A typed value changed by pointer (prefs rows, picker toggles); `null` resets to the default. */
|
|
938
|
+
| {
|
|
939
|
+
ev: "change";
|
|
940
|
+
sf: string;
|
|
941
|
+
id: string;
|
|
942
|
+
item: string;
|
|
943
|
+
value: boolean | number | string | readonly string[] | null;
|
|
944
|
+
}
|
|
945
|
+
/**
|
|
946
|
+
* An edit over the terminal's selection in an `editor`/`input` node: replace
|
|
947
|
+
* `[from, to)` with `text`, caret to `cursor` (UTF-16 offsets; `len` is the
|
|
948
|
+
* text length the terminal saw, a mismatch makes the edit stale).
|
|
949
|
+
*/
|
|
950
|
+
| { ev: "edit"; sf: string; id: string; from: number; to: number; text: string; cursor: number; len: number }
|
|
951
|
+
/**
|
|
952
|
+
* The user clicked into node `id` (an `editor`/`input` without the focus, or
|
|
953
|
+
* a `prefs` sheet while the focus is outside it): the program moves its
|
|
954
|
+
* keyboard focus there, or ignores it (a modal overlay keeps the keys).
|
|
955
|
+
*/
|
|
956
|
+
| { ev: "focus"; sf: string; id: string }
|
|
957
|
+
| { ev: "error"; sf?: string; s?: number; op?: number; msg: string }
|
|
958
|
+
| { ev: "gone"; sf?: string; ids: readonly string[] };
|