@xmoon76/dsh-pi-tui 0.1.7 → 0.2.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,740 @@
1
+ //#region src/extension/public-types.d.ts
2
+ /**
3
+ * Public extension contracts for the dsh-pi-tui extension platform.
4
+ *
5
+ * These types are the ONLY surface a third-party plugin may import (through
6
+ * `@xmoon76/dsh-pi-tui/extensions`). Everything here must stay free of
7
+ * private pi-tui types, `TuiApp`, terminal types, and repository paths — the
8
+ * packed `.d.mts` leak gate enforces that.
9
+ *
10
+ * M1–M4 scope: registry primitives, the chrome slots, the immutable
11
+ * surface/session/activity snapshots, and the structured component kit
12
+ * (`ExtensionView`). Contributions are plain data (rendered by the host);
13
+ * plugins never touch components, terminals, or the TuiApp.
14
+ * @module @xmoon76/dsh-pi-tui/extensions
15
+ */
16
+ /** The semantic color palette shape a theme contribution may supply. The
17
+ * host maps it onto its own palette at apply time; a plugin supplies hex
18
+ * colours only (never ANSI, never terminal escapes). */
19
+ interface TuiColorPalette {
20
+ primary: string;
21
+ accent: string;
22
+ text: string;
23
+ textStrong: string;
24
+ textDim: string;
25
+ textMuted: string;
26
+ border: string;
27
+ borderFocus: string;
28
+ success: string;
29
+ warning: string;
30
+ error: string;
31
+ diffAdded: string;
32
+ diffRemoved: string;
33
+ diffAddedStrong: string;
34
+ diffRemovedStrong: string;
35
+ diffGutter: string;
36
+ diffMeta: string;
37
+ roleUser: string;
38
+ shellMode: string;
39
+ }
40
+ /** One autocomplete suggestion item (structural — the host maps it onto
41
+ * its editor's item shape). */
42
+ interface TuiAutocompleteItem {
43
+ value: string;
44
+ label: string;
45
+ description?: string;
46
+ }
47
+ /** One suggestion batch from a plugin provider. */
48
+ interface TuiAutocompleteSuggestions {
49
+ items: readonly TuiAutocompleteItem[];
50
+ prefix: string;
51
+ }
52
+ /** The editor's suggestion query a plugin provider answers. */
53
+ interface TuiAutocompleteQuery {
54
+ lines: readonly string[];
55
+ cursorLine: number;
56
+ cursorCol: number;
57
+ signal: AbortSignal;
58
+ force?: boolean;
59
+ }
60
+ /**
61
+ * A plugin autocomplete provider (structural — NEVER the fork's interface,
62
+ * so the public declarations stay free of private pi-tui types).
63
+ */
64
+ interface TuiAutocompleteProvider {
65
+ getSuggestions(query: TuiAutocompleteQuery): Promise<TuiAutocompleteSuggestions | null>;
66
+ }
67
+ /** API version of the extension surface (bumped only on breaking changes). */
68
+ declare const API_VERSION: 1;
69
+ /** Capability identifiers, feature-detected via {@link PiTuiApiInfo}. */
70
+ type PiTuiCapability = 'slot.chrome.header.badge' | 'slot.input.dock.item' | 'slot.chrome.footer.status' | 'slot.input.widget' | 'surface.snapshot' | 'advanced.input.capture' | 'advanced.ui.interactive' | 'advanced.editor.control' | 'unstable.input.raw' | 'unstable.surface.handle';
71
+ /** Slot identities this bundle knows; unknown names are rejected at registration. */
72
+ type PiTuiSlotName = 'chrome.header.badge' | 'input.dock.item' | 'chrome.footer.status' | 'input.widget.above' | 'input.widget.below';
73
+ /** Slot semantics: how competing contributions resolve. */
74
+ type PiTuiSlotSemantic = 'list' | 'single';
75
+ /** Extension tier metadata shared by every tier entry (plan §4/§5). */
76
+ type ExtensionTier = 'stable' | 'advanced' | 'unstable';
77
+ /** What a plugin may know about the host (M1: version + capabilities only). */
78
+ interface PiTuiApiInfo {
79
+ /** The extension API version; 1 for the M0–M3 foundation. */
80
+ readonly apiVersion: typeof API_VERSION;
81
+ /** The `@xmoon76/dsh-pi-tui` bundle version (semver string). */
82
+ readonly hostVersion: string;
83
+ /** Capabilities the host currently supports; feature-detect, never parse the version. */
84
+ readonly capabilities: ReadonlySet<PiTuiCapability>;
85
+ /**
86
+ * M11: the deprecation policy (plan §16). A capability or API marked
87
+ * deprecated stays functional but is REMOVED in the next API version —
88
+ * a plugin should migrate before then. An entry's presence means the
89
+ * deprecated surface is still active; absence means it is gone.
90
+ * Keys: capability ids (or API method names) → the deprecation note.
91
+ */
92
+ readonly deprecations: ReadonlyMap<string, string>;
93
+ }
94
+ /** Common registration metadata every contribution carries. */
95
+ interface RegistrationSpec {
96
+ /** Stable diagnostic identity, unique per (slot, owner). */
97
+ readonly id: string;
98
+ /**
99
+ * Deterministic ordering for `list` slots: `order` ASC, then `id` ASC.
100
+ * Load order NEVER decides conflicts.
101
+ */
102
+ readonly order?: number;
103
+ /** Winner selection for `single` slots: lowest `priority` wins. */
104
+ readonly priority?: number;
105
+ /** Human-readable description for diagnostics and future /status output. */
106
+ readonly description?: string;
107
+ }
108
+ /** Live control of one registered contribution. */
109
+ interface RegistrationHandle<T> {
110
+ /** The registration id (stable diagnostic identity). */
111
+ readonly id: string;
112
+ /**
113
+ * Request a re-render of this contribution. Batched: N invalidates in one
114
+ * tick produce ONE render request on the active screen.
115
+ */
116
+ invalidate(): void;
117
+ /**
118
+ * Replace the contribution value in place. The handle keeps its identity:
119
+ * owner, id and lifetime are unchanged by a replacement.
120
+ */
121
+ replace(next: T): void;
122
+ /** Remove the contribution. Idempotent; a disposed handle is inert. */
123
+ dispose(): void;
124
+ }
125
+ /** One contribution registered under a slot, with its owner identity. */
126
+ interface ContributionRecord<T> {
127
+ readonly slot: PiTuiSlotName;
128
+ readonly id: string;
129
+ readonly order: number;
130
+ readonly priority: number;
131
+ readonly description: string | undefined;
132
+ /** The Cordis fiber name that owns this registration (diagnostics). */
133
+ readonly owner: string;
134
+ /** The live contribution value. */
135
+ readonly value: T;
136
+ }
137
+ /** Health state of one contribution (future /status extension listing). */
138
+ type ContributionState = 'active' | 'shadowed' | 'failed' | 'disposed';
139
+ /** Diagnostic health record for one contribution (M1: recorded, surfaced later). */
140
+ interface ContributionHealth {
141
+ readonly id: string;
142
+ readonly owner: string;
143
+ readonly extensionPoint: string;
144
+ readonly state: ContributionState;
145
+ /** The error generation this record was produced under (0 = none yet). */
146
+ readonly errorGeneration?: number;
147
+ /** The last error message, when the contribution failed (no stack traces). */
148
+ readonly lastError?: string;
149
+ }
150
+ /** The live surface's geometry and mode (plan §7.1). */
151
+ interface SurfaceSnapshot {
152
+ readonly surfaceId: string;
153
+ /** The surface generation; stable across start/stop/fullscreen/editor
154
+ * round-trips, bumped only by a final dispose (M0 contract). */
155
+ readonly generation: number;
156
+ readonly width: number;
157
+ readonly height: number;
158
+ readonly fullscreen: boolean;
159
+ /** Which seat currently owns keyboard focus. */
160
+ readonly focusedSeat: 'editor' | 'overlay' | 'editor-panel' | 'none';
161
+ readonly themeId: string;
162
+ readonly themeRevision: number;
163
+ }
164
+ /** The live session's identity and mode (plan §7.2). Secrets, credentials,
165
+ * raw Context and live Agent objects are NEVER included. */
166
+ interface SessionSnapshot {
167
+ readonly sessionId?: string;
168
+ readonly title?: string;
169
+ readonly workspaceRoot: string;
170
+ readonly cwd: string;
171
+ readonly branch?: string;
172
+ readonly model?: string;
173
+ readonly permission?: string;
174
+ readonly planMode: boolean;
175
+ readonly busy: boolean;
176
+ readonly viewerMode: boolean;
177
+ /** Completed turns and steps (footer t/s counters). */
178
+ readonly turns: number;
179
+ readonly steps: number;
180
+ }
181
+ /** Background activity counts (plan §7.3). */
182
+ interface ActivitySnapshot {
183
+ readonly working: boolean;
184
+ readonly workingMessage?: string;
185
+ readonly queuedCount: number;
186
+ readonly taskCount: number;
187
+ readonly childAgentCount: number;
188
+ readonly todoCount: number;
189
+ /** The rendered todo summary line (`☑ N active · first`), when the host
190
+ * provides one (the first-party builtin dock item renders it). */
191
+ readonly todoSummary?: string;
192
+ }
193
+ /** The store's three named slices (state subscriptions deliver these). */
194
+ interface SurfaceStateValues {
195
+ readonly surface: SurfaceSnapshot;
196
+ readonly session: SessionSnapshot;
197
+ readonly activity: ActivitySnapshot;
198
+ }
199
+ /** A header badge: short text with a semantic tone (plan §8.1). */
200
+ interface HeaderBadge {
201
+ readonly text: string;
202
+ readonly tone?: 'info' | 'warning' | 'error' | 'success';
203
+ }
204
+ /** One dock item: a label with optional detail lines (plan §8.2). */
205
+ interface DockItem {
206
+ readonly label: readonly StyledSpan[];
207
+ readonly detail?: readonly StyledSpan[];
208
+ readonly importance?: number;
209
+ }
210
+ /** One footer segment: styled spans with layout hints (plan §8.3). */
211
+ interface FooterSegment {
212
+ readonly spans: readonly StyledSpan[];
213
+ readonly minWidth?: number;
214
+ readonly importance?: number;
215
+ }
216
+ /** One styled run of text. The host owns ANSI compilation; plugins supply
217
+ * semantic tokens only (no raw escapes, ever). */
218
+ interface StyledSpan {
219
+ readonly text: string;
220
+ readonly tone?: 'primary' | 'accent' | 'text' | 'textStrong' | 'textDim' | 'textMuted' | 'border' | 'success' | 'warning' | 'error' | 'roleUser' | 'shellMode';
221
+ readonly emphasis?: 'normal' | 'strong' | 'dim' | 'italic';
222
+ }
223
+ /**
224
+ * A structured view tree a plugin may contribute to widget slots. Plugins
225
+ * describe WHAT to render with semantic tokens; the host compiles it into
226
+ * private components and owns layout, ANSI compilation, wrapping, width
227
+ * measurement and error isolation. Raw ANSI and terminal escapes are never
228
+ * accepted (the host's render contract, plan §19).
229
+ *
230
+ * The tree is immutable and rebuilt on every contribution change; the host
231
+ * keeps the compiled component LIVE across resizes (the fork's per-frame
232
+ * processed-line reuse, AGENTS.md), so a width change re-wraps instead of
233
+ * freezing the baked lines.
234
+ */
235
+ type ExtensionView = TextView | MarkdownView | SpacerView | StackView | FrameView | RowsView;
236
+ /** One line of text: styled spans, optionally wrapped at the view width. */
237
+ interface TextView {
238
+ readonly kind: 'text';
239
+ readonly spans: readonly StyledSpan[];
240
+ /** Whether long content wraps onto further rows (default true). */
241
+ readonly wrap?: boolean;
242
+ }
243
+ /** One block of Markdown, rendered with the host's markdown palette. */
244
+ interface MarkdownView {
245
+ readonly kind: 'markdown';
246
+ readonly markdown: string;
247
+ }
248
+ /** A run of empty rows (vertical spacing). */
249
+ interface SpacerView {
250
+ readonly kind: 'spacer';
251
+ readonly rows: number;
252
+ }
253
+ /**
254
+ * A vertical or horizontal stack of views. Vertical stacks distribute the
255
+ * available height between children (basis/grow/shrink semantics like the
256
+ * fork's VStack); horizontal stacks place children side by side. Empty
257
+ * children render nothing.
258
+ */
259
+ interface StackView {
260
+ readonly kind: 'stack';
261
+ readonly direction: 'vertical' | 'horizontal';
262
+ readonly children: readonly ExtensionView[];
263
+ readonly gap?: number;
264
+ /** Stack-layout hints (vertical direction): basis/grow/shrink. */
265
+ readonly basis?: number;
266
+ readonly grow?: number;
267
+ readonly shrink?: number;
268
+ }
269
+ /**
270
+ * A bordered frame around one child view (padding 1, the host's border
271
+ * token). An absent child renders nothing (abdication).
272
+ */
273
+ interface FrameView {
274
+ readonly kind: 'frame';
275
+ readonly child?: ExtensionView;
276
+ /** The child's content width budget (cells); defaults to the host budget. */
277
+ readonly width?: number;
278
+ }
279
+ /** A fixed set of rows, each rendered at the current view width. */
280
+ interface RowsView {
281
+ readonly kind: 'rows';
282
+ readonly rows: readonly ExtensionView[];
283
+ /** Max rows to render (budget); excess rows are dropped. */
284
+ readonly maxRows?: number;
285
+ }
286
+ /**
287
+ * A widget contribution for the `input.widget.above` / `input.widget.below`
288
+ * slots: one structured view with layout hints. The host owns the row
289
+ * budget (plan §19 — minimum editor usability always wins) and truncates or
290
+ * collapses low-importance widgets first; a widget can never push the
291
+ * editor off-screen.
292
+ */
293
+ interface InputWidget {
294
+ /** The view tree to render. */
295
+ readonly view: ExtensionView;
296
+ /** Layout weight: lower importance widgets collapse first under pressure. */
297
+ readonly importance?: number;
298
+ /** Preferred max rows (budget); the host may grant fewer. */
299
+ readonly maxHeight?: number;
300
+ }
301
+ /** One command contribution (plan §10): ownership metadata over an
302
+ * existing command. The bridge does NOT execute — the commands service
303
+ * does. `/name args...` ALWAYS keeps `invocation.rawInput` verbatim. */
304
+ interface TuiCommandContribution {
305
+ readonly id: string;
306
+ /** The slash-command name WITHOUT the leading slash. */
307
+ readonly name: string;
308
+ readonly description: string;
309
+ /** Execution ownership: local (never steered) vs submission (session
310
+ * policy). Busy Enter classifies by the EFFECTIVE ownership. */
311
+ readonly execution: 'local' | 'submission';
312
+ /** Whether the command may run without a live session. */
313
+ readonly sessionless?: boolean;
314
+ /** Optional autocomplete provider for this command's arguments
315
+ * (the structural {@link TuiAutocompleteProvider}). */
316
+ readonly argumentProvider?: TuiAutocompleteProvider;
317
+ /** Optional local handler; absent = metadata-only ownership (the
318
+ * commands service handler runs). */
319
+ readonly handler?: TuiLocalCommandHandler;
320
+ }
321
+ /** The local command handler signature (invocation carries the VERBATIM
322
+ * raw input — never re-parsed or rewritten). Returns the commands
323
+ * service's result shape (`{ kind: 'success' }` / `{ kind: 'error',
324
+ * text }`) so the runner's notify path is shared. */
325
+ interface TuiLocalCommandHandler {
326
+ (invocation: {
327
+ commandId: string;
328
+ rawInput: string;
329
+ signal: AbortSignal;
330
+ }): {
331
+ readonly kind: 'success';
332
+ readonly text?: string;
333
+ readonly sourceEventSeq?: number;
334
+ } | {
335
+ readonly kind: 'error';
336
+ readonly text: string;
337
+ } | Promise<{
338
+ readonly kind: 'success';
339
+ readonly text?: string;
340
+ readonly sourceEventSeq?: number;
341
+ } | {
342
+ readonly kind: 'error';
343
+ readonly text: string;
344
+ }>;
345
+ }
346
+ /** A live handle on one command contribution. */
347
+ interface TuiCommandHandle {
348
+ readonly id: string;
349
+ dispose(): void;
350
+ }
351
+ /** The command bridge's observable snapshot. */
352
+ interface TuiCommandBridgeSnapshot {
353
+ readonly entries: readonly {
354
+ readonly id: string;
355
+ readonly name: string;
356
+ readonly description: string;
357
+ readonly execution: 'local' | 'submission';
358
+ readonly sessionless: boolean;
359
+ readonly owner: string;
360
+ }[];
361
+ readonly revision: number;
362
+ }
363
+ /** One registered theme contribution (M5). */
364
+ interface TuiThemeContribution {
365
+ readonly id: string;
366
+ /** The SELECTABLE name (shown in the theme picker). */
367
+ readonly name: string;
368
+ /** The semantic palette (hex colours; the host maps it at apply time). */
369
+ readonly palette: TuiColorPalette;
370
+ readonly description?: string;
371
+ }
372
+ /** A live handle on one theme contribution. */
373
+ interface TuiThemeHandle {
374
+ readonly id: string;
375
+ dispose(): void;
376
+ }
377
+ /** The theme registry's observable snapshot. */
378
+ interface TuiThemeRegistrySnapshot {
379
+ readonly themes: readonly {
380
+ readonly id: string;
381
+ readonly name: string;
382
+ readonly description: string | undefined;
383
+ readonly owner: string;
384
+ }[];
385
+ readonly revision: number;
386
+ }
387
+ /** One registered autocomplete provider (M5). `owner` is stamped by the
388
+ * service (the calling fiber) — a plugin never supplies it. */
389
+ interface AutocompleteProviderContribution {
390
+ readonly id: string;
391
+ /** The provider (the structural {@link TuiAutocompleteProvider} — never
392
+ * the fork's interface, so the public declarations stay free of private
393
+ * types). */
394
+ readonly provider: TuiAutocompleteProvider;
395
+ readonly description?: string;
396
+ readonly owner?: string;
397
+ }
398
+ /** A live handle on one autocomplete provider. */
399
+ interface AutocompleteHandle {
400
+ readonly id: string;
401
+ dispose(): void;
402
+ }
403
+ /** One registered settings row (M5). */
404
+ interface TuiSettingContribution {
405
+ readonly id: string;
406
+ readonly label: string;
407
+ readonly description?: string;
408
+ readonly currentValue: string;
409
+ readonly values?: readonly string[];
410
+ /** Return false to keep the old value (rejection). */
411
+ readonly onChange?: (value: string) => boolean | void | Promise<boolean | void>;
412
+ readonly order?: number;
413
+ }
414
+ /** A live handle on one settings row. */
415
+ interface TuiSettingHandle {
416
+ readonly id: string;
417
+ setValue(value: string): void;
418
+ dispose(): void;
419
+ }
420
+ /** The settings registry's observable snapshot. */
421
+ interface TuiSettingsRegistrySnapshot {
422
+ readonly rows: readonly {
423
+ readonly id: string;
424
+ readonly label: string;
425
+ readonly description: string | undefined;
426
+ readonly currentValue: string;
427
+ readonly values: readonly string[];
428
+ readonly order: number;
429
+ readonly owner: string;
430
+ }[];
431
+ readonly revision: number;
432
+ }
433
+ /** The read-side of the command bridge a plugin may consult (M5). The
434
+ * concrete bridge is host-internal; this narrow surface keeps the public
435
+ * declarations free of internal modules. */
436
+ interface TuiCommandBridgeView {
437
+ isLocal(name: string, staticLocal: ReadonlySet<string>): boolean;
438
+ snapshot(): TuiCommandBridgeSnapshot;
439
+ }
440
+ /** The read-side of the theme registry (M5). */
441
+ interface TuiThemeRegistryView {
442
+ names(): string[];
443
+ paletteFor(name: string): TuiColorPalette | undefined;
444
+ snapshot(): TuiThemeRegistrySnapshot;
445
+ }
446
+ /** The read-side of the settings registry (M5). */
447
+ interface TuiSettingsRegistryView {
448
+ rows(): readonly {
449
+ readonly id: string;
450
+ readonly label: string;
451
+ readonly description: string | undefined;
452
+ readonly currentValue: string;
453
+ readonly values: readonly string[];
454
+ readonly order: number;
455
+ readonly owner: string;
456
+ }[];
457
+ apply(id: string, value: string): Promise<boolean>;
458
+ snapshot(): TuiSettingsRegistrySnapshot;
459
+ }
460
+ /** The read-side of the autocomplete registry (M5). */
461
+ interface TuiAutocompleteRegistryView {
462
+ suggest(query: TuiAutocompleteQuery, onError?: (id: string, error: unknown) => void, onSuccess?: (id: string) => void): Promise<TuiAutocompleteSuggestions | null>;
463
+ snapshot(): {
464
+ providers: readonly AutocompleteProviderContribution[];
465
+ revision: number;
466
+ };
467
+ }
468
+ /** The read-side of the keybinding registry (M5). */
469
+ interface TuiKeybindingRegistryView {
470
+ actionFor(key: NormalizedKey): TuiAction | undefined;
471
+ snapshot(): TuiKeybindingRegistrySnapshot;
472
+ }
473
+ /** A normalized key identity (the host's normalization, plan §11.2). */
474
+ interface NormalizedKey {
475
+ readonly key: string;
476
+ readonly ctrl: boolean;
477
+ readonly alt: boolean;
478
+ readonly shift: boolean;
479
+ readonly super: boolean;
480
+ }
481
+ /** The semantic actions a plugin may bind (plan §2.2 / §11). */
482
+ type TuiAction = 'submit-draft' | 'queue-draft' | 'steer-draft' | 'cancel-activity' | 'open-search' | 'toggle-fullscreen' | 'cycle-permission';
483
+ /** One keybinding contribution (M5 metadata only; routing in M6). */
484
+ interface TuiKeybindingContribution {
485
+ readonly id: string;
486
+ readonly key: NormalizedKey;
487
+ readonly action: TuiAction;
488
+ readonly description?: string;
489
+ }
490
+ /** A live handle on one keybinding. */
491
+ interface TuiKeybindingHandle {
492
+ readonly id: string;
493
+ dispose(): void;
494
+ }
495
+ /** Human-readable key description (diagnostics). */
496
+ declare function describeKey(key: NormalizedKey): string;
497
+ /** The keybinding registry's observable snapshot. */
498
+ interface TuiKeybindingRegistrySnapshot {
499
+ readonly bindings: readonly {
500
+ readonly id: string;
501
+ readonly key: NormalizedKey;
502
+ readonly action: TuiAction;
503
+ readonly description: string | undefined;
504
+ readonly owner: string;
505
+ }[];
506
+ readonly revision: number;
507
+ }
508
+ /** The semantic snapshot of one TOOL call a renderer may present (plan
509
+ * §12). Immutable, readonly; secrets/credentials/raw Context/live Agent
510
+ * are NEVER included. */
511
+ interface ToolPresentationSnapshot {
512
+ readonly callId: string;
513
+ readonly toolName: string;
514
+ readonly status: 'ok' | 'error' | 'running';
515
+ readonly arguments?: unknown;
516
+ readonly result?: unknown;
517
+ /** Whether the card is currently expanded (fold boundary + click
518
+ * override). A renderer may present differently per expansion. */
519
+ readonly expanded: boolean;
520
+ }
521
+ /** The semantic snapshot of one TRANSCRIPT message a renderer may present
522
+ * (plan §12). Kind-specific fields are present only for their kind. */
523
+ interface MessagePresentationSnapshot {
524
+ readonly kind: 'user' | 'assistant' | 'thinking' | 'system' | 'tool' | 'summary';
525
+ readonly turn: number;
526
+ /** Present for text-bearing kinds. */
527
+ readonly text?: string;
528
+ /** Present for thinking (still streaming). */
529
+ readonly running?: boolean;
530
+ /** Present for system (producer label/summary). */
531
+ readonly label?: string;
532
+ readonly summary?: string;
533
+ /** Present for tool. */
534
+ readonly tool?: ToolPresentationSnapshot;
535
+ }
536
+ /** One registered message renderer (chain slot): may present ANY message;
537
+ * returning undefined abdicates (the chain continues to the next renderer,
538
+ * then the host fallback). */
539
+ interface TuiMessageRendererContribution {
540
+ readonly id: string;
541
+ /** Optional scope: only render this message kind (undefined = all). */
542
+ readonly kind?: MessagePresentationSnapshot['kind'];
543
+ readonly render: (snapshot: MessagePresentationSnapshot) => ExtensionView | undefined;
544
+ readonly description?: string;
545
+ /** Chain ordering (ASC); ties break by id ASC. */
546
+ readonly order?: number;
547
+ }
548
+ /** One registered tool renderer (keyed slot): presents the tool card for
549
+ * ONE tool name; undefined abdicates to the next renderer / host fallback.
550
+ * Registered via `transcript.tool.renderer.<toolName>` keys. */
551
+ interface TuiToolRendererContribution {
552
+ readonly id: string;
553
+ /** The tool name this renderer presents (the keyed slot's domain key). */
554
+ readonly toolName: string;
555
+ readonly render: (snapshot: ToolPresentationSnapshot) => ExtensionView | undefined;
556
+ readonly description?: string;
557
+ /** Winner selection for the tool name: lowest priority wins (a tie is
558
+ * an error). */
559
+ readonly priority?: number;
560
+ }
561
+ /** A live handle on one renderer contribution. */
562
+ interface TuiRendererHandle {
563
+ readonly id: string;
564
+ /** Remove the renderer (idempotent; owner unload also disposes). */
565
+ dispose(): void;
566
+ }
567
+ /** The read-side of the renderer registry (M7): the host's message cache
568
+ * asks it to present a message/tool through the plugin chain. The
569
+ * concrete registry is host-internal. */
570
+ interface TuiRendererRegistryView {
571
+ renderMessage(snapshot: MessagePresentationSnapshot, onError: (id: string, error: unknown) => void, canUse?: (id: string, view: ExtensionView) => boolean): {
572
+ view: ExtensionView;
573
+ rendererId: string;
574
+ } | undefined;
575
+ renderTool(snapshot: ToolPresentationSnapshot, onError: (id: string, error: unknown) => void, canUse?: (id: string, view: ExtensionView) => boolean): {
576
+ view: ExtensionView;
577
+ rendererId: string;
578
+ } | undefined;
579
+ snapshot(): TuiRendererRegistrySnapshot;
580
+ }
581
+ /** The renderer registry's observable snapshot. */
582
+ interface TuiRendererRegistrySnapshot {
583
+ readonly messageRenderers: readonly {
584
+ readonly id: string;
585
+ readonly kind: MessagePresentationSnapshot['kind'] | undefined;
586
+ readonly description: string | undefined;
587
+ readonly owner: string;
588
+ }[];
589
+ readonly toolRenderers: readonly {
590
+ readonly id: string;
591
+ readonly toolName: string;
592
+ readonly description: string | undefined;
593
+ readonly owner: string;
594
+ }[];
595
+ readonly revision: number;
596
+ }
597
+ /** A size hint: an absolute column/row count or a percentage string. */
598
+ type TuiSizeValue = number | `${number}%`;
599
+ /** The sizing/positioning hints for a managed overlay (structural — the
600
+ * host maps them onto its overlay options at mount time). */
601
+ interface TuiOverlayOptions {
602
+ readonly width?: TuiSizeValue;
603
+ readonly minWidth?: number;
604
+ readonly maxHeight?: TuiSizeValue;
605
+ readonly anchor?: 'center' | 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right' | 'top-center' | 'bottom-center';
606
+ readonly offsetX?: number;
607
+ readonly offsetY?: number;
608
+ readonly row?: TuiSizeValue;
609
+ readonly col?: TuiSizeValue;
610
+ readonly margin?: number | {
611
+ top?: number;
612
+ right?: number;
613
+ bottom?: number;
614
+ left?: number;
615
+ };
616
+ /** Non-capturing overlays never steal keyboard focus. */
617
+ readonly nonCapturing?: boolean;
618
+ }
619
+ /** A managed overlay lease: the plugin controls the overlay through this
620
+ * handle; the host owns mounting, stacking, focus and teardown. */
621
+ interface TuiOverlayHandle {
622
+ /** Close the overlay (idempotent; the surface's final dispose also
623
+ * closes every still-owned lease). */
624
+ close(): void;
625
+ /** Temporarily hide (the plugin keeps the lease). */
626
+ hide(): void;
627
+ /** Show again after hide(). */
628
+ show(): void;
629
+ }
630
+ /** The editor's runtime state a plugin editor may read (immutable
631
+ * snapshot — never the live component, never the terminal). */
632
+ interface EditorSnapshot {
633
+ readonly text: string;
634
+ readonly cursor: number;
635
+ readonly focused: boolean;
636
+ /** The active replacement id, when a plugin editor occupies the seat. */
637
+ readonly replacementId?: string;
638
+ /** Whether an editor-seat flow (question) currently covers the editor. */
639
+ readonly composing: boolean;
640
+ }
641
+ /** A semantic action a plugin editor may dispatch through the host (the
642
+ * host executes — submission/session safety is never bypassed). */
643
+ type EditorHostAction = 'submit' | 'queue-submit' | 'steer' | 'open-external-editor';
644
+ /** The result of a host-action dispatch. */
645
+ type EditorHostActionResult = {
646
+ kind: 'accepted';
647
+ } | {
648
+ kind: 'ignored';
649
+ };
650
+ /** The editor host a plugin editor receives at creation (plan §14.1). */
651
+ interface EditorHost {
652
+ readonly surfaceId: string;
653
+ readonly generation: number;
654
+ /** The current immutable editor snapshot. */
655
+ getSnapshot(): EditorSnapshot;
656
+ /** Replace the draft (and optionally move the cursor). */
657
+ replaceText(text: string, cursor?: number): void;
658
+ /** Dispatch a semantic action through the host's own paths. */
659
+ dispatch(action: EditorHostAction): EditorHostActionResult;
660
+ /** Subscribe to snapshot changes; returns a disposer. */
661
+ subscribe(listener: (snapshot: EditorSnapshot) => void): () => void;
662
+ /** Request a repaint of the active screen. */
663
+ invalidate(): void;
664
+ }
665
+ /** A live handle on one editor contribution. */
666
+ interface EditorHandle {
667
+ readonly id: string;
668
+ dispose(): void;
669
+ }
670
+ /** One editor contribution (single-winner: lowest priority wins; a tie is
671
+ * an error — plan §14.1). */
672
+ interface EditorContribution {
673
+ readonly id: string;
674
+ /** Winner selection: lowest priority wins; a tie is an explicit error. */
675
+ readonly priority?: number;
676
+ readonly description?: string;
677
+ /** Create the plugin editor component. A throw here keeps the CURRENT
678
+ * editor working (plan §14.2 — creation failure falls back). */
679
+ create(host: EditorHost): ExtensionEditor;
680
+ }
681
+ /** A semantic editor input event (P1-5). Third-party editors NEVER receive
682
+ * raw terminal bytes: the Host normalizes terminal protocol decoding
683
+ * (legacy + Kitty CSI-u + modifyOtherKeys encodings, paste protocols, key
684
+ * release/repeat filtering) into one of these shapes, so a plugin editor
685
+ * behaves identically on every terminal. */
686
+ type EditorInputEvent =
687
+ /** One key press, normalized to the semantic identity (same shape as
688
+ * the keybinding {@link NormalizedKey}). */
689
+ {
690
+ readonly kind: 'key';
691
+ readonly key: NormalizedKey;
692
+ } /** One plain printable character run (ordinary typing). */ | {
693
+ readonly kind: 'text';
694
+ readonly text: string;
695
+ } /** A paste burst (bracketed-paste or the host's paste heuristic). */ | {
696
+ readonly kind: 'paste';
697
+ readonly text: string;
698
+ };
699
+ /** The plugin editor surface (plan §14.1): the component to mount in the
700
+ * seat + the state hooks the host reads/writes. */
701
+ interface ExtensionEditor {
702
+ /** The component mounted in the editor seat (the M4 component kit or a
703
+ * host-compiled view — NEVER a raw pi-tui component). */
704
+ readonly component: ExtensionView;
705
+ /** Read the current draft. */
706
+ getText(): string;
707
+ /** Replace the draft (the host's transfer/fallback path). */
708
+ setText(text: string): void;
709
+ /** Read the cursor offset. */
710
+ getCursor?(): number;
711
+ /** Move the cursor. */
712
+ setCursor?(offset: number): void;
713
+ /** Whether this editor owns focus. */
714
+ readonly focused?: boolean;
715
+ /** The editor's own border color hook (optional; the host theme
716
+ * default applies otherwise). */
717
+ borderColor?: (text: string) => string;
718
+ /**
719
+ * P1-5: the plugin editor's INPUT channel. While the plugin editor
720
+ * occupies the seat, every key the host's precedence ladder routes to
721
+ * the editor is delivered here as a SEMANTIC event ({@link
722
+ * EditorInputEvent}) — the host has already decoded the terminal
723
+ * protocol (legacy/CSI-u/modifyOtherKeys encodings, paste bursts, key
724
+ * release/repeat filtering), so a plugin editor NEVER parses raw
725
+ * terminal escape bytes. Return true to CONSUME the event (the host
726
+ * does nothing further); return false/undefined to hand the event back
727
+ * to the HOST editing semantics. The host synchronizes the replacement's
728
+ * current text/cursor into its hidden Editor, forwards the event once,
729
+ * and copies the resulting text/cursor back into the visible
730
+ * replacement. Enter remains host-owned and submits through the normal
731
+ * host path.
732
+ * Without this hook the seat is display-only: ordinary typing is not
733
+ * silently routed into the hidden host editor.
734
+ */
735
+ handleInput?(event: EditorInputEvent): boolean;
736
+ /** Dispose the editor (the host calls it after the handoff). */
737
+ dispose(): void;
738
+ }
739
+ //#endregion
740
+ export { TuiKeybindingHandle as $, RegistrationHandle as A, ToolPresentationSnapshot as B, MarkdownView as C, PiTuiCapability as D, PiTuiApiInfo as E, StackView as F, TuiAutocompleteRegistryView as G, TuiAutocompleteItem as H, StyledSpan as I, TuiCommandBridgeSnapshot as J, TuiAutocompleteSuggestions as K, SurfaceSnapshot as L, RowsView as M, SessionSnapshot as N, PiTuiSlotName as O, SpacerView as P, TuiKeybindingContribution as Q, SurfaceStateValues as R, InputWidget as S, NormalizedKey as T, TuiAutocompleteProvider as U, TuiAction as V, TuiAutocompleteQuery as W, TuiCommandContribution as X, TuiCommandBridgeView as Y, TuiCommandHandle as Z, ExtensionTier as _, TuiThemeRegistryView as _t, ContributionHealth as a, TuiOverlayOptions as at, FrameView as b, DockItem as c, TuiRendererRegistryView as ct, EditorHost as d, TuiSettingsRegistrySnapshot as dt, TuiKeybindingRegistrySnapshot as et, EditorHostAction as f, TuiSettingsRegistryView as ft, ExtensionEditor as g, TuiThemeRegistrySnapshot as gt, EditorSnapshot as h, TuiThemeHandle as ht, AutocompleteProviderContribution as i, TuiOverlayHandle as it, RegistrationSpec as j, PiTuiSlotSemantic as k, EditorContribution as l, TuiSettingContribution as lt, EditorInputEvent as m, TuiThemeContribution as mt, ActivitySnapshot as n, TuiLocalCommandHandler as nt, ContributionRecord as o, TuiRendererHandle as ot, EditorHostActionResult as p, TuiSizeValue as pt, TuiColorPalette as q, AutocompleteHandle as r, TuiMessageRendererContribution as rt, ContributionState as s, TuiRendererRegistrySnapshot as st, API_VERSION as t, TuiKeybindingRegistryView as tt, EditorHandle as u, TuiSettingHandle as ut, ExtensionView as v, TuiToolRendererContribution as vt, MessagePresentationSnapshot as w, HeaderBadge as x, FooterSegment as y, describeKey as yt, TextView as z };