@xp266/dshtui 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.
Files changed (159) hide show
  1. package/bin/dshtui.js +65 -0
  2. package/cordis.patch.yml +81 -0
  3. package/lib/contract/index.d.mts +2 -0
  4. package/lib/contract/index.mjs +5 -0
  5. package/lib/dialog.d.mts +156 -0
  6. package/lib/dialog.mjs +3 -0
  7. package/lib/index-DqnaSXqP.d.mts +823 -0
  8. package/lib/index.d.mts +17 -0
  9. package/lib/index.mjs +11404 -0
  10. package/lib/prod-react-Dzof4qJx.mjs +4 -0
  11. package/lib/surface-DdxzIgIY.mjs +3126 -0
  12. package/lib/vendor.d.mts +3 -0
  13. package/lib/vendor.mjs +4 -0
  14. package/package.json +128 -0
  15. package/src/apply-theme.ts +8 -0
  16. package/src/boot-log.ts +35 -0
  17. package/src/chat/blocks.ts +41 -0
  18. package/src/chat/bridge.ts +1076 -0
  19. package/src/chat/builtin-tool-views.ts +53 -0
  20. package/src/chat/chat-face.ts +16 -0
  21. package/src/chat/chat-nodes.ts +53 -0
  22. package/src/chat/efforts.ts +4 -0
  23. package/src/chat/interactions.ts +329 -0
  24. package/src/chat/models.ts +263 -0
  25. package/src/chat/partial-json.ts +125 -0
  26. package/src/chat/presets.ts +26 -0
  27. package/src/chat/question-view.ts +106 -0
  28. package/src/chat/retry-status.ts +33 -0
  29. package/src/chat/session-groups.ts +34 -0
  30. package/src/chat/session-list.ts +311 -0
  31. package/src/chat/store.ts +636 -0
  32. package/src/chat/todo-view.ts +66 -0
  33. package/src/chat/tool-view.ts +311 -0
  34. package/src/chat/tool-views.ts +114 -0
  35. package/src/contract/index.ts +704 -0
  36. package/src/contract/upstream.ts +165 -0
  37. package/src/core/caret-nonce.ts +19 -0
  38. package/src/core/composer-layout.ts +65 -0
  39. package/src/core/edit.ts +152 -0
  40. package/src/core/field-view.ts +52 -0
  41. package/src/core/fields.ts +185 -0
  42. package/src/core/metrics.ts +26 -0
  43. package/src/core/paste.ts +149 -0
  44. package/src/core/segments.ts +112 -0
  45. package/src/core/text.ts +290 -0
  46. package/src/dialog.ts +11 -0
  47. package/src/env.ts +38 -0
  48. package/src/harness-home.ts +22 -0
  49. package/src/hot-theme.ts +56 -0
  50. package/src/index.tsx +185 -0
  51. package/src/kernel/registry.ts +102 -0
  52. package/src/kernel/surface.ts +33 -0
  53. package/src/log.ts +249 -0
  54. package/src/model/message.ts +89 -0
  55. package/src/model/selection.ts +63 -0
  56. package/src/performance-guard.ts +28 -0
  57. package/src/runtime/prod-react.ts +10 -0
  58. package/src/terminal/background.ts +66 -0
  59. package/src/terminal/capabilities.ts +54 -0
  60. package/src/terminal/clipboard-backends.ts +69 -0
  61. package/src/terminal/clipboard.ts +161 -0
  62. package/src/terminal/cursor-shape.ts +13 -0
  63. package/src/terminal/glyphs.ts +44 -0
  64. package/src/terminal/mouse.ts +154 -0
  65. package/src/terminal/probe.ts +61 -0
  66. package/src/terminal/screen.ts +349 -0
  67. package/src/theme-settings.ts +31 -0
  68. package/src/theme.ts +319 -0
  69. package/src/ui/app.tsx +454 -0
  70. package/src/ui/chrome/caps.tsx +9 -0
  71. package/src/ui/chrome/hint-service.ts +87 -0
  72. package/src/ui/chrome/input-status.ts +27 -0
  73. package/src/ui/chrome/key-gate.tsx +29 -0
  74. package/src/ui/chrome/status-bar.tsx +147 -0
  75. package/src/ui/contributions.ts +47 -0
  76. package/src/ui/dialog/defaults-dialog.tsx +152 -0
  77. package/src/ui/dialog/dialog-item.tsx +38 -0
  78. package/src/ui/dialog/dialog.tsx +386 -0
  79. package/src/ui/dialog/effort-dialog.tsx +45 -0
  80. package/src/ui/dialog/geometry.ts +131 -0
  81. package/src/ui/dialog/items.ts +124 -0
  82. package/src/ui/dialog/list-dialog.tsx +63 -0
  83. package/src/ui/dialog/models-dialog.tsx +203 -0
  84. package/src/ui/dialog/presets-dialog.tsx +46 -0
  85. package/src/ui/dialog/providers-dialog.tsx +313 -0
  86. package/src/ui/dialog/sessions-dialog.tsx +163 -0
  87. package/src/ui/dialog/sizes.ts +7 -0
  88. package/src/ui/dialog/status-lines.ts +11 -0
  89. package/src/ui/dialog/todo-dialog.tsx +29 -0
  90. package/src/ui/dialog/use-dialog-input.ts +186 -0
  91. package/src/ui/extension-point.ts +188 -0
  92. package/src/ui/home-logo.ts +132 -0
  93. package/src/ui/hooks/use-async-action.ts +25 -0
  94. package/src/ui/hooks/use-async-list.ts +71 -0
  95. package/src/ui/hooks/use-caret.ts +24 -0
  96. package/src/ui/hooks/use-chat-events.ts +153 -0
  97. package/src/ui/hooks/use-mouse-selection.ts +352 -0
  98. package/src/ui/hooks/use-scroll.ts +49 -0
  99. package/src/ui/hooks/use-terminal-size.ts +20 -0
  100. package/src/ui/input/commands.ts +118 -0
  101. package/src/ui/input/composer-bus.ts +14 -0
  102. package/src/ui/input/composer-fields.ts +111 -0
  103. package/src/ui/input/composer-keys.ts +21 -0
  104. package/src/ui/input/composer-paste.ts +20 -0
  105. package/src/ui/input/input-bar.tsx +279 -0
  106. package/src/ui/input/use-composer.ts +495 -0
  107. package/src/ui/key-arbiter.ts +37 -0
  108. package/src/ui/keymap.ts +15 -0
  109. package/src/ui/layout-service.ts +77 -0
  110. package/src/ui/message/layout.ts +763 -0
  111. package/src/ui/message/md/block.ts +247 -0
  112. package/src/ui/message/md/engine.ts +430 -0
  113. package/src/ui/message/md/extensions.ts +58 -0
  114. package/src/ui/message/md/highlight.ts +626 -0
  115. package/src/ui/message/md/index.ts +14 -0
  116. package/src/ui/message/md/inline.ts +97 -0
  117. package/src/ui/message/md/palette.ts +87 -0
  118. package/src/ui/message/message-list.tsx +69 -0
  119. package/src/ui/message/message-row.tsx +137 -0
  120. package/src/ui/message/message-views.ts +16 -0
  121. package/src/ui/message/renderers.ts +39 -0
  122. package/src/ui/message/tool-diff.ts +188 -0
  123. package/src/ui/message/warmup.ts +41 -0
  124. package/src/ui/message/wave.ts +98 -0
  125. package/src/ui/overlay.ts +22 -0
  126. package/src/ui/panels/approval-panel.tsx +193 -0
  127. package/src/ui/panels/exports.ts +3 -0
  128. package/src/ui/panels/question-model.ts +424 -0
  129. package/src/ui/panels/question-panel.tsx +154 -0
  130. package/src/ui/panels/surface.tsx +106 -0
  131. package/src/ui/panels-builtin.tsx +86 -0
  132. package/src/ui/pointer/builtins.ts +281 -0
  133. package/src/ui/pointer/registry.ts +25 -0
  134. package/src/ui/region.tsx +31 -0
  135. package/src/ui/selection/service.ts +92 -0
  136. package/src/ui/selection-registry.ts +88 -0
  137. package/src/ui/selection.tsx +148 -0
  138. package/src/ui/spinner-tick.tsx +24 -0
  139. package/src/ui/use-available-windows.ts +31 -0
  140. package/src/ui/widgets/actions.tsx +84 -0
  141. package/src/ui/widgets/button.tsx +57 -0
  142. package/src/ui/widgets/checkbox.tsx +58 -0
  143. package/src/ui/widgets/header.tsx +36 -0
  144. package/src/ui/widgets/index.ts +8 -0
  145. package/src/ui/widgets/input.tsx +76 -0
  146. package/src/ui/widgets/registry.ts +32 -0
  147. package/src/ui/widgets/search.tsx +59 -0
  148. package/src/ui/widgets/select.tsx +97 -0
  149. package/src/ui/widgets/static.tsx +37 -0
  150. package/src/ui/widgets/types.ts +1 -0
  151. package/src/ui/window-services-bridge.ts +71 -0
  152. package/src/ui/window-services.ts +20 -0
  153. package/src/ui/windows/models-window.tsx +15 -0
  154. package/src/ui/windows/providers-window.tsx +14 -0
  155. package/src/ui/windows/service-window.tsx +18 -0
  156. package/src/ui/windows/sessions-window.tsx +25 -0
  157. package/src/ui/windows-builtin.tsx +94 -0
  158. package/src/ui/windows.ts +18 -0
  159. package/src/vendor.ts +12 -0
@@ -0,0 +1,704 @@
1
+ import type { ComponentType, ReactNode, Ref } from 'react'
2
+ import type { Context } from '@deepseek-ai/cordis'
3
+ import type { ContentBlock } from '@deepseek-ai/dsh-llm'
4
+ import type { SessionEvent } from '@deepseek-ai/dsh-session'
5
+ import type { Token } from 'marked'
6
+ import type { MarkStyle, Segment } from '../core/segments.ts'
7
+ import type { MdPalette } from '../ui/message/md/palette.ts'
8
+ import type { PendingImage } from '../core/paste.ts'
9
+ import type { LineSelection } from '../model/selection.ts'
10
+ import type { Message, MessageKind } from '../model/message.ts'
11
+ export type { MessageKind } from '../model/message.ts'
12
+ import type { MouseEventData } from '../terminal/mouse.ts'
13
+ import type { ScrollSnapshot } from '../ui/hooks/use-scroll.ts'
14
+
15
+ export type { PendingImage } from '../core/paste.ts'
16
+ export type { LineSelection } from '../model/selection.ts'
17
+ export type { MouseEventData, MouseEventType } from '../terminal/mouse.ts'
18
+ export type { ScrollSnapshot } from '../ui/hooks/use-scroll.ts'
19
+
20
+ export interface WindowHandle {
21
+ clickAt(y: number, x: number): void
22
+ wheelAt(y: number, dir: -1 | 1): boolean
23
+ }
24
+
25
+ export interface WindowProps {
26
+ open: boolean
27
+ onClose(): void
28
+ handleRef?: Ref<WindowHandle>
29
+ }
30
+
31
+ export interface WindowCommandSpec {
32
+ name: string
33
+ description: string
34
+ }
35
+
36
+ export interface WindowContribution {
37
+ id: string
38
+ title: string
39
+ component: ComponentType<WindowProps>
40
+ order?: number
41
+ command?: WindowCommandSpec
42
+ required?: readonly string[]
43
+ }
44
+
45
+ export interface TuiWindowsFace {
46
+ register(contribution: WindowContribution): () => void
47
+ }
48
+
49
+ export interface StatusLineContribution {
50
+ id: string
51
+ order?: number
52
+ render(props: { columns: number }): string
53
+ }
54
+
55
+ export interface OverlayContribution {
56
+ id: string
57
+ render(props: { onClose(): void }): ReactNode
58
+ }
59
+
60
+ export interface TuiServicesFace {
61
+ register(name: string, service: unknown): () => void
62
+ }
63
+
64
+ export interface PaintArgs<I> {
65
+ item: I
66
+ focused: boolean
67
+ subCol: number
68
+ cursor?: number
69
+ width: number
70
+ y: number
71
+ x: number
72
+ pressed: 'left' | 'right' | null
73
+ clip: number
74
+ }
75
+
76
+ export interface ClickHit {
77
+ localX: number
78
+ localY: number
79
+ width: number
80
+ }
81
+
82
+ export interface ClickActions {
83
+ focus(subCol: number): void
84
+ flash(side: 'left' | 'right'): void
85
+ }
86
+
87
+ export interface WidgetKeyApi {
88
+ cursor: number
89
+ subCol: number
90
+ setCursor(next: number): void
91
+ navigate(direction: 'up' | 'down' | 'left' | 'right'): void
92
+ }
93
+
94
+ export interface WidgetDef<I extends { type: string }> {
95
+ height(item: I, width: number): number
96
+ paintWidth(item: I): number
97
+ render(paint: PaintArgs<I>): ReactNode
98
+ searchTexts?(item: I, searchRight: boolean): string[]
99
+ stops?(item: I): number
100
+ fullRowFocus?: boolean
101
+ selectable?: boolean
102
+ editable?: boolean
103
+ caret?(item: I, cursor: number, width: number): { dy: number; dx: number }
104
+ onLeftRight?(item: I, direction: -1 | 1, api: WidgetKeyApi): boolean
105
+ onEnter?(item: I, api: WidgetKeyApi): boolean
106
+ onSpace?(item: I): void
107
+ onClick?(item: I, hit: ClickHit, actions: ClickActions): boolean
108
+ activate?(item: I): void
109
+ }
110
+
111
+ export interface TuiWidgetsFace {
112
+ register<I extends { type: string }>(type: I['type'], def: WidgetDef<I>): () => void
113
+ }
114
+
115
+ /**
116
+ * Open registry for plugin dialog item kinds. Declaration-merge a property
117
+ * named after your item kind (any key) whose value is the item object type
118
+ * carrying a `type` discriminant; the merged types join the `DialogItem`
119
+ * union so your widget def stays fully typed.
120
+ */
121
+ export interface DialogItemKinds {}
122
+
123
+ export interface ThemePaletteContribution {
124
+ id: string
125
+ order?: number
126
+ /** Which theme mode(s) this palette patch applies to; defaults to 'both'. */
127
+ mode?: 'dark' | 'light' | 'both'
128
+ /** Color name (a `Theme` palette key) to replacement hex/text value. */
129
+ colors: Readonly<Record<string, string>>
130
+ }
131
+
132
+ export interface TuiPaletteFace {
133
+ register(contribution: ThemePaletteContribution): () => void
134
+ /** Read a palette color by key, including plugin-contributed keys. */
135
+ color(key: string): string | undefined
136
+ }
137
+
138
+ export interface ClipboardBackendContribution {
139
+ id: string
140
+ order?: number
141
+ readText?(): string | undefined | Promise<string | undefined>
142
+ readImage?(): { data: Uint8Array; mediaType: string } | undefined | Promise<{ data: Uint8Array; mediaType: string } | undefined>
143
+ writeText?(text: string): boolean
144
+ }
145
+
146
+ export interface TuiClipboardFace {
147
+ register(contribution: ClipboardBackendContribution): () => void
148
+ }
149
+
150
+ export interface TuiKey {
151
+ upArrow: boolean
152
+ downArrow: boolean
153
+ leftArrow: boolean
154
+ rightArrow: boolean
155
+ pageDown: boolean
156
+ pageUp: boolean
157
+ home?: boolean
158
+ end?: boolean
159
+ return: boolean
160
+ escape: boolean
161
+ ctrl: boolean
162
+ shift: boolean
163
+ tab: boolean
164
+ backspace: boolean
165
+ delete: boolean
166
+ meta: boolean
167
+ }
168
+
169
+ export interface KeyBindingContribution {
170
+ id: string
171
+ order?: number
172
+ /**
173
+ * Invoked for every terminal key event before builtin shell handling.
174
+ * Return true to consume the key; later bindings and builtin handling
175
+ * are skipped for that event.
176
+ */
177
+ handle(input: string, key: TuiKey): boolean
178
+ }
179
+
180
+ export interface TuiKeymapFace {
181
+ register(contribution: KeyBindingContribution): () => void
182
+ }
183
+
184
+ export interface PointerUiContext {
185
+ dialogOpen: boolean
186
+ panelActive: boolean
187
+ hintRegion: { top: number; bottom: number } | null
188
+ rows: number
189
+ columns: number
190
+ messageHeight: number
191
+ inputHeight: number
192
+ scrollTop: number
193
+ getScroll(): ScrollSnapshot
194
+ }
195
+
196
+ export interface PointerSession {
197
+ getSelection(): LineSelection | null
198
+ setSelection(next: LineSelection | null): void
199
+ scroll(next: number): void
200
+ /** Start (-1/1) or stop (0) drag-follow auto scrolling of the message area. */
201
+ autoScroll(direction: -1 | 1 | 0): void
202
+ }
203
+
204
+ export interface PointerEventFrame {
205
+ event: MouseEventData
206
+ session: PointerSession
207
+ ui: PointerUiContext
208
+ }
209
+
210
+ export interface PointerHandlerContribution {
211
+ id: string
212
+ order?: number
213
+ /** Return true to claim the gesture; drag and up are then routed here exclusively. */
214
+ onDown(frame: PointerEventFrame): boolean
215
+ onDrag?(frame: PointerEventFrame): void
216
+ onUp?(frame: PointerEventFrame): void
217
+ onWheel?(frame: PointerEventFrame): boolean
218
+ }
219
+
220
+ /** Builtin gesture handlers live at order 100..190; see registerPointerHandler. */
221
+ export const POINTER_PLUGIN_ORDER = 300
222
+
223
+ export interface HintMatcherContribution {
224
+ id: string
225
+ order?: number
226
+ match(entries: readonly HintEntry[], context: { value: string; query: string }): HintEntry[] | null
227
+ }
228
+
229
+ export interface HintEntry {
230
+ command: string
231
+ description: string
232
+ hint?: string
233
+ }
234
+
235
+ export interface HintArgsContribution {
236
+ id: string
237
+ order?: number
238
+ args(name: string): string[] | null | Promise<string[] | null>
239
+ }
240
+
241
+ export interface TuiHintFace {
242
+ matchers: { register(contribution: HintMatcherContribution): () => void }
243
+ args: { register(contribution: HintArgsContribution): () => void }
244
+ }
245
+
246
+ export interface TuiPointerFace {
247
+ register(contribution: PointerHandlerContribution): () => void
248
+ }
249
+
250
+ export interface SelectionExtractContext {
251
+ messageText(sel: LineSelection): string
252
+ chromeText(sel: LineSelection): string
253
+ }
254
+
255
+ export interface SelectionDomainContribution {
256
+ id: string
257
+ order?: number
258
+ hit(sel: LineSelection): boolean
259
+ extract(sel: LineSelection, context: SelectionExtractContext): string | null
260
+ }
261
+
262
+ export interface SelectionTransformerContribution {
263
+ id: string
264
+ order?: number
265
+ transform(text: string): string
266
+ }
267
+
268
+ export interface SelectionClipboardContribution {
269
+ id: string
270
+ order?: number
271
+ copy(text: string): boolean
272
+ }
273
+
274
+ export interface TuiSelectionFace {
275
+ domains: { register(contribution: SelectionDomainContribution): () => void }
276
+ transformers: { register(contribution: SelectionTransformerContribution): () => void }
277
+ clipboard: { register(contribution: SelectionClipboardContribution): () => void }
278
+ }
279
+
280
+ export interface InputStatusContext {
281
+ columns: number
282
+ rows: number
283
+ busy: boolean
284
+ running: boolean
285
+ modelName: string
286
+ permissionMode: string
287
+ effortName: string | undefined
288
+ presetName: string | undefined
289
+ }
290
+
291
+ export interface InputStatusPart {
292
+ text: string
293
+ color?: string
294
+ bold?: boolean
295
+ }
296
+
297
+ export interface InputStatusContribution {
298
+ id: string
299
+ order?: number
300
+ /** Return one part, an array of parts, or null to contribute nothing this render. */
301
+ render(context: InputStatusContext): InputStatusPart | InputStatusPart[] | null
302
+ }
303
+
304
+ export interface HomeLogoContribution {
305
+ id: string
306
+ order?: number
307
+ /** Logo artwork rendered as the first chat-styled bubble on the home page, one string per terminal row. */
308
+ lines: readonly string[]
309
+ }
310
+
311
+ export interface TuiHomeLogoFace {
312
+ register(contribution: HomeLogoContribution): () => void
313
+ }
314
+
315
+ export interface TuiChromeFace {
316
+ statusLine: { register(contribution: StatusLineContribution): () => void }
317
+ overlays: { register(contribution: OverlayContribution): () => void }
318
+ widgets: TuiWidgetsFace
319
+ palette: TuiPaletteFace
320
+ keys: TuiKeymapFace
321
+ inputStatus: { register(contribution: InputStatusContribution): () => void }
322
+ logo: TuiHomeLogoFace
323
+ }
324
+
325
+ export interface CommandDef {
326
+ id: string
327
+ command: string
328
+ description: string
329
+ hint?: string
330
+ args?: readonly string[]
331
+ run?: () => void
332
+ order?: number
333
+ }
334
+
335
+ export interface TuiCommandsFace {
336
+ register(def: CommandDef): () => void
337
+ }
338
+
339
+ export type DiffLineKind = 'ctx' | 'del' | 'add'
340
+
341
+ export interface DiffLine {
342
+ kind: DiffLineKind
343
+ text: string
344
+ }
345
+
346
+ export type DiffHunk = readonly DiffLine[]
347
+
348
+ export interface ToolViewDiff {
349
+ path: string
350
+ hunks: readonly DiffHunk[]
351
+ /**
352
+ * Paint whole-line red/green backgrounds behind removed and added lines.
353
+ * Defaults to true when any line is a removal, false for pure additions.
354
+ * Set it explicitly to override that heuristic.
355
+ */
356
+ backgrounds?: boolean
357
+ }
358
+
359
+ export interface ToolResultView {
360
+ content: readonly ContentBlock[]
361
+ isError: boolean
362
+ meta?: unknown
363
+ }
364
+
365
+ export interface ToolViewCallContext {
366
+ tool: string
367
+ callId: string
368
+ args: unknown
369
+ argumentsRaw: string
370
+ cwd: string
371
+ }
372
+
373
+ export interface ToolViewResultContext {
374
+ tool: string
375
+ callId: string
376
+ args: unknown
377
+ result: ToolResultView
378
+ cwd: string
379
+ }
380
+
381
+ export interface ToolCallPresentation {
382
+ label?: string
383
+ body?: string
384
+ bodyCol?: number
385
+ diff?: ToolViewDiff
386
+ }
387
+
388
+ export interface ToolResultPresentation {
389
+ kind: 'replace' | 'append'
390
+ text: string
391
+ /** Header replacement applied when kind is 'replace' ('' clears the suffix). */
392
+ label?: string
393
+ /** Call-side body replacement ('' clears it), for settled views that supersede the args. */
394
+ argsBody?: string
395
+ exitCode?: number
396
+ signal?: string
397
+ bodyCol?: number
398
+ diff?: ToolViewDiff
399
+ /** Structured read window; rendered as a numbered code card. */
400
+ read?: ToolReadView
401
+ }
402
+
403
+ /** One numbered line of a structured read result. */
404
+ export interface ToolReadLine {
405
+ number: number
406
+ text: string
407
+ }
408
+
409
+ /** The `card: 'read'` projection: a numbered file window plus its totals. */
410
+ export interface ToolReadView {
411
+ path?: string
412
+ lines: readonly ToolReadLine[]
413
+ /** 1-based first line of the window when it carries no lines. */
414
+ offset?: number
415
+ /** Total lines in the file, for a "lines N of M" footer. */
416
+ totalLines?: number
417
+ /** Syntax highlighting hint (e.g. `ts`). */
418
+ lang?: string
419
+ }
420
+
421
+ export interface ToolViewContribution {
422
+ tool: string
423
+ order?: number
424
+ /**
425
+ * When true, the builtin presenter never runs for this tool: a call or
426
+ * result handler returning undefined renders as a bare card instead of
427
+ * falling through to the builtin presentation.
428
+ */
429
+ takeover?: boolean
430
+ call?(context: ToolViewCallContext): ToolCallPresentation | undefined
431
+ result?(context: ToolViewResultContext): ToolResultPresentation | undefined
432
+ }
433
+
434
+ export interface TuiToolsFace {
435
+ register(contribution: ToolViewContribution): () => void
436
+ }
437
+
438
+ export interface CustomMessage {
439
+ kind: 'custom'
440
+ id: string
441
+ view: string
442
+ data: unknown
443
+ running?: boolean
444
+ streaming?: boolean
445
+ }
446
+
447
+ export type CustomMessageSeed = Omit<CustomMessage, 'kind' | 'id'>
448
+
449
+ export interface ChatNodeDefinition {
450
+ id: string
451
+ order?: number
452
+ match(event: SessionEvent): boolean
453
+ start(event: SessionEvent): CustomMessageSeed
454
+ update?(event: SessionEvent, message: CustomMessage): CustomMessageSeed | null
455
+ }
456
+
457
+ export interface MessageViewContext {
458
+ message: CustomMessage
459
+ width: number
460
+ }
461
+
462
+ export interface MessageViewRender {
463
+ label?: string
464
+ lines: readonly string[]
465
+ wrap?: boolean
466
+ muted?: boolean
467
+ }
468
+
469
+ export interface MessageViewContribution {
470
+ view: string
471
+ order?: number
472
+ render(context: MessageViewContext): MessageViewRender
473
+ }
474
+
475
+ export interface MessageRendererResult {
476
+ lines: string[]
477
+ rows?: Segment[][] | null
478
+ bgs?: (string | undefined)[] | null
479
+ customLabel?: string
480
+ customMuted?: boolean
481
+ }
482
+
483
+ export interface MessageRendererContribution {
484
+ /** Message kind to take over: 'bubble' | 'collapsible' | 'tool-card' | 'compaction' | 'custom'. */
485
+ kind: string
486
+ order?: number
487
+ /** Return undefined to fall through to the builtin rendering. */
488
+ render(message: Message, width: number): MessageRendererResult | undefined
489
+ }
490
+
491
+ export interface TuiContentFace {
492
+ nodes: { register(definition: ChatNodeDefinition): () => void }
493
+ views: { register(contribution: MessageViewContribution): () => void }
494
+ renderers: { register(contribution: MessageRendererContribution): () => void }
495
+ }
496
+
497
+ export interface TuiStartupSink {
498
+ id: string
499
+ /** Invoked for each boot progress line while the shell is starting. */
500
+ write(line: string): void
501
+ }
502
+
503
+ export interface TuiStartupFace {
504
+ registerSink(sink: TuiStartupSink): () => void
505
+ }
506
+
507
+ export interface SpecialFieldStyle {
508
+ color: string
509
+ background: string
510
+ bold?: boolean
511
+ }
512
+
513
+ export interface SpecialFieldContribution {
514
+ kind: string
515
+ style(): SpecialFieldStyle
516
+ /** Text injected into the sent message for a field of this kind; defaults to the label. */
517
+ expand?(data: unknown): string
518
+ }
519
+
520
+ export interface TuiFieldsFace {
521
+ register(contribution: SpecialFieldContribution): () => void
522
+ }
523
+
524
+ /**
525
+ * Create a special field instance rendered as an inline chip with its own
526
+ * background. The returned sentinel char behaves like a single grapheme:
527
+ * cursor movement and deletion treat it atomically and line wrapping never
528
+ * splits it. `kind` resolves the style from registered field kinds.
529
+ */
530
+ export interface SpecialFieldCreateInput {
531
+ kind: string
532
+ label: string
533
+ /** Which reclaim scan owns the slot: 'composer' chips die with composer text, 'message' chips with the message list. */
534
+ owner?: 'composer' | 'message'
535
+ }
536
+
537
+ export interface SpecialFieldFactory {
538
+ create(input: SpecialFieldCreateInput): string | null
539
+ release(char: string): void
540
+ /** Protect a chip from the automatic reclaim scans until the disposer runs. */
541
+ pin(char: string): (() => void) | undefined
542
+ isFieldChar(char: string): boolean
543
+ labelOf(char: string): string | undefined
544
+ }
545
+
546
+ export interface PasteContext {
547
+ /** Raw pasted text with normalized newlines. */
548
+ text: string
549
+ /** Insert at this char index in the composer value. */
550
+ cursor: number
551
+ }
552
+
553
+ export interface PasteResult {
554
+ /** Text to insert (may contain field sentinel chars created via the field factory). */
555
+ insert: string
556
+ }
557
+
558
+ export interface PasteHandlerContribution {
559
+ id: string
560
+ order?: number
561
+ /** Return a result to claim the paste; return undefined to fall through. */
562
+ handle(context: PasteContext): PasteResult | undefined
563
+ }
564
+
565
+ export interface ComposerInsertSpec {
566
+ /** Plain text to insert at the cursor. */
567
+ text?: string
568
+ /** A special field chip to insert at the cursor; `data` is passed to the kind's expand on send. */
569
+ field?: { kind: string; label: string; data?: unknown }
570
+ }
571
+
572
+ export interface TuiComposerFace {
573
+ paste: { register(handler: PasteHandlerContribution): () => void }
574
+ /**
575
+ * Intercept a key before the builtin composer handling consumes it.
576
+ * Runs for every key routed to the composer (only while it is interactive);
577
+ * return true to consume the event.
578
+ */
579
+ keys: { register(binding: { id: string; order?: number; handle(input: string, key: TuiKey): boolean }): () => void }
580
+ /** Insert text or a special field into the live composer at the cursor. False when the composer is not mounted. */
581
+ insert(spec: ComposerInsertSpec): boolean
582
+ }
583
+
584
+ export interface MarkdownInlineContext {
585
+ palette: MdPalette
586
+ base?: MarkStyle
587
+ }
588
+
589
+ export interface MarkdownBlockContext {
590
+ palette: MdPalette
591
+ thinking: boolean
592
+ width: number
593
+ streamId: string
594
+ }
595
+
596
+ /**
597
+ * A custom markdown block renderer. Keyed by the marked token `type` it
598
+ * claims (`code`, `heading`, `table`, ...). Registering over an existing
599
+ * type replaces that layer; disposing restores it (layered registry
600
+ * semantics).
601
+ */
602
+ export interface MarkdownBlockContribution {
603
+ /** marked token type this renderer claims. */
604
+ type: string
605
+ order?: number
606
+ render(token: Token, context: MarkdownBlockContext): Segment[][]
607
+ }
608
+
609
+ /** A custom inline (span-level) markdown token renderer, keyed by token `type`. */
610
+ export interface MarkdownInlineContribution {
611
+ type: string
612
+ order?: number
613
+ render(token: Token, context: MarkdownInlineContext): Segment
614
+ }
615
+
616
+ export interface PrismGrammarContribution {
617
+ /** Code fence language id (lowercase). */
618
+ id: string
619
+ /** Prism grammar object (from a loaded prism component or hand-built). */
620
+ grammar: unknown
621
+ /** Extra aliases accepted for the same grammar. */
622
+ aliases?: readonly string[]
623
+ }
624
+
625
+ export interface TuiMarkdownFace {
626
+ blocks: { register(contribution: MarkdownBlockContribution): () => void }
627
+ inline: { register(contribution: MarkdownInlineContribution): () => void }
628
+ languages: { register(contribution: PrismGrammarContribution): () => void }
629
+ }
630
+
631
+ export interface InteractionPanelComponentProps {
632
+ request: unknown
633
+ resolve(value: unknown): void
634
+ reject(cause: unknown): void
635
+ active: boolean
636
+ columns: number
637
+ rows: number
638
+ innerWidth: number
639
+ blockWidth: number
640
+ background: string
641
+ handleRef?: { current: unknown }
642
+ onResize(height: number): void
643
+ }
644
+
645
+ export interface InteractionPanelContribution {
646
+ kind: string
647
+ order?: number
648
+ component: ComponentType<InteractionPanelComponentProps>
649
+ }
650
+
651
+ export interface TuiInteractionPanelsFace {
652
+ register(contribution: InteractionPanelContribution): () => void
653
+ }
654
+
655
+ export interface TuiInteractionsFace {
656
+ panels: TuiInteractionPanelsFace
657
+ /** Queue a plugin interaction; resolves when the panel for `kind` settles it. */
658
+ push(kind: string, request: unknown, signal?: AbortSignal): Promise<unknown>
659
+ }
660
+
661
+ export interface TuiSessionSummary {
662
+ id: string
663
+ name: string
664
+ directory: string
665
+ ungrouped: boolean
666
+ updatedAt: number
667
+ modifiedAt?: number
668
+ }
669
+
670
+ export interface TuiChatFace {
671
+ cwd(): string
672
+ activeSessionId(): string
673
+ onEvent(listener: (event: SessionEvent) => void): () => void
674
+ send(text: string, images?: ReadonlyArray<readonly PendingImage[]>): void
675
+ interrupt(): void
676
+ newSession(): Promise<void>
677
+ openSession(id: string): Promise<void>
678
+ listSessions(): Promise<TuiSessionSummary[]>
679
+ }
680
+
681
+ export interface TuiExtensionPoint {
682
+ windows: TuiWindowsFace
683
+ services: TuiServicesFace
684
+ chrome: TuiChromeFace
685
+ commands: TuiCommandsFace
686
+ tools: TuiToolsFace
687
+ content: TuiContentFace
688
+ startup: TuiStartupFace
689
+ fields: TuiFieldsFace & { factory: SpecialFieldFactory }
690
+ composer: TuiComposerFace
691
+ markdown: TuiMarkdownFace
692
+ hint: TuiHintFace
693
+ pointer: TuiPointerFace
694
+ selection: TuiSelectionFace
695
+ clipboard: TuiClipboardFace
696
+ interactions?: TuiInteractionsFace
697
+ chat?: TuiChatFace
698
+ }
699
+
700
+ declare module '@deepseek-ai/cordis' {
701
+ interface Context {
702
+ tui: TuiExtensionPoint
703
+ }
704
+ }