@linxiraos/pi-tui 1.1.26 → 1.1.27

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 (137) hide show
  1. package/CHANGELOG.md +9 -33
  2. package/README.md +6 -2
  3. package/dist/types/active-terminal.d.ts +30 -0
  4. package/dist/types/app-keybindings.d.ts +6 -1
  5. package/dist/types/apps/git/git-tui.d.ts +3 -2
  6. package/dist/types/chat/assistant-message.d.ts +6 -1
  7. package/dist/types/chat/chat-transcript-builder.d.ts +1 -0
  8. package/dist/types/chat/user-message.d.ts +2 -2
  9. package/dist/types/chrome/status-notice.d.ts +15 -3
  10. package/dist/types/components/editor.d.ts +8 -1
  11. package/dist/types/components/image.d.ts +14 -0
  12. package/dist/types/components/input.d.ts +2 -1
  13. package/dist/types/components/loader.d.ts +5 -6
  14. package/dist/types/components/markdown.d.ts +13 -4
  15. package/dist/types/key-hint-format.d.ts +8 -0
  16. package/dist/types/native/apply.d.ts +4 -3
  17. package/dist/types/native/backend.d.ts +2 -2
  18. package/dist/types/native/encode.d.ts +5 -2
  19. package/dist/types/native/node.d.ts +36 -5
  20. package/dist/types/native/overlay.d.ts +3 -3
  21. package/dist/types/native/settle.d.ts +4 -3
  22. package/dist/types/overlays/agent-hub.d.ts +1 -0
  23. package/dist/types/overlays/agent-transcript-viewer.d.ts +1 -0
  24. package/dist/types/overlays/btw-history-panel.d.ts +13 -1
  25. package/dist/types/overlays/copy-selector.d.ts +1 -0
  26. package/dist/types/overlays/hub-frame.d.ts +4 -1
  27. package/dist/types/overlays/model-hub.d.ts +14 -0
  28. package/dist/types/overlays/report-panel.d.ts +68 -0
  29. package/dist/types/overlays/rewind-selector.d.ts +1 -0
  30. package/dist/types/overlays/thinking-selector.d.ts +2 -2
  31. package/dist/types/overlays/usage-dashboard.d.ts +4 -4
  32. package/dist/types/prompt/composer-cache.d.ts +0 -16
  33. package/dist/types/prompt/composer.d.ts +17 -2
  34. package/dist/types/prompt/custom-editor.d.ts +14 -5
  35. package/dist/types/prompt/interactive-context-helpers.d.ts +1 -0
  36. package/dist/types/prompt/welcome.d.ts +1 -1
  37. package/dist/types/space-hold.d.ts +40 -25
  38. package/dist/types/status-line/context-usage.d.ts +7 -5
  39. package/dist/types/terminal-capabilities.d.ts +14 -1
  40. package/dist/types/terminal.d.ts +1 -10
  41. package/dist/types/thinking.d.ts +1 -1
  42. package/dist/types/tools/bash.d.ts +7 -2
  43. package/dist/types/tools/task.d.ts +6 -3
  44. package/dist/types/tui.d.ts +5 -0
  45. package/dist/types/windows-input-mode.d.ts +33 -1
  46. package/package.json +9 -9
  47. package/src/active-terminal.d.ts +30 -0
  48. package/src/active-terminal.ts +80 -0
  49. package/src/app-keybindings.d.ts +6 -1
  50. package/src/app-keybindings.ts +6 -1
  51. package/src/apps/git/diff-pane.ts +70 -18
  52. package/src/apps/git/git-tui.ts +21 -14
  53. package/src/apps/git/sidebar.ts +8 -3
  54. package/src/bracketed-paste.ts +10 -3
  55. package/src/chat/assistant-message.d.ts +6 -1
  56. package/src/chat/assistant-message.ts +26 -4
  57. package/src/chat/chat-transcript-builder.d.ts +1 -0
  58. package/src/chat/chat-transcript-builder.ts +4 -0
  59. package/src/chat/tool-execution.ts +6 -1
  60. package/src/chat/user-message.d.ts +2 -2
  61. package/src/chat/user-message.ts +16 -5
  62. package/src/chrome/status-notice.ts +25 -7
  63. package/src/chrome/transcript-container.ts +69 -6
  64. package/src/components/editor.d.ts +8 -1
  65. package/src/components/editor.ts +22 -1
  66. package/src/components/image.d.ts +14 -0
  67. package/src/components/image.ts +68 -1
  68. package/src/components/input.d.ts +2 -1
  69. package/src/components/input.ts +17 -7
  70. package/src/components/loader.d.ts +5 -6
  71. package/src/components/loader.ts +10 -17
  72. package/src/components/markdown.d.ts +13 -4
  73. package/src/components/markdown.ts +473 -105
  74. package/src/desktop-notify.ts +2 -1
  75. package/src/hotkeys-markdown.ts +9 -2
  76. package/src/key-hint-format.d.ts +8 -0
  77. package/src/key-hint-format.ts +25 -10
  78. package/src/kitty-graphics.ts +7 -1
  79. package/src/native/apply.d.ts +4 -3
  80. package/src/native/apply.ts +4 -3
  81. package/src/native/backend.d.ts +2 -2
  82. package/src/native/backend.ts +25 -4
  83. package/src/native/encode.d.ts +5 -2
  84. package/src/native/encode.ts +18 -3
  85. package/src/native/node.d.ts +36 -5
  86. package/src/native/node.ts +31 -5
  87. package/src/native/overlay.d.ts +3 -3
  88. package/src/native/overlay.ts +4 -4
  89. package/src/native/reconcile.ts +12 -4
  90. package/src/native/settle.d.ts +4 -3
  91. package/src/native/settle.ts +4 -3
  92. package/src/overlays/agent-hub.ts +4 -0
  93. package/src/overlays/agent-transcript-viewer.ts +2 -0
  94. package/src/overlays/ask-dialog.ts +3 -7
  95. package/src/overlays/btw-history-panel.ts +124 -38
  96. package/src/overlays/copy-selector.d.ts +1 -0
  97. package/src/overlays/copy-selector.ts +2 -0
  98. package/src/overlays/extensions/extension-dashboard.ts +4 -4
  99. package/src/overlays/hub-frame.ts +19 -1
  100. package/src/overlays/jobs-panel.ts +5 -6
  101. package/src/overlays/model-browser.ts +30 -16
  102. package/src/overlays/model-hub.ts +137 -12
  103. package/src/overlays/pause-screen.ts +2 -2
  104. package/src/overlays/plan-review-overlay.ts +14 -0
  105. package/src/overlays/report-panel.ts +191 -0
  106. package/src/overlays/rewind-selector.ts +2 -0
  107. package/src/overlays/settings-defs.ts +1 -1
  108. package/src/overlays/thinking-selector.ts +11 -10
  109. package/src/overlays/usage-dashboard.ts +17 -8
  110. package/src/overlays/usage-row.ts +22 -8
  111. package/src/prompt/composer-cache.ts +7 -86
  112. package/src/prompt/composer.ts +33 -11
  113. package/src/prompt/custom-editor.ts +228 -79
  114. package/src/prompt/interactive-context-helpers.ts +2 -0
  115. package/src/prompt/queued-messages.ts +6 -4
  116. package/src/prompt/tips.txt +12 -9
  117. package/src/prompt/welcome.ts +10 -35
  118. package/src/render/render-utils.ts +5 -1
  119. package/src/space-hold.d.ts +40 -25
  120. package/src/space-hold.ts +156 -52
  121. package/src/status-line/context-usage.ts +21 -46
  122. package/src/terminal-capabilities.d.ts +14 -1
  123. package/src/terminal-capabilities.ts +64 -26
  124. package/src/terminal.d.ts +1 -10
  125. package/src/terminal.ts +64 -62
  126. package/src/theme/symbols.ts +5 -3
  127. package/src/thinking.d.ts +1 -1
  128. package/src/thinking.ts +1 -1
  129. package/src/tools/bash.d.ts +7 -2
  130. package/src/tools/bash.ts +24 -5
  131. package/src/tools/streaming-output.ts +5 -0
  132. package/src/tools/task.d.ts +6 -3
  133. package/src/tools/task.ts +9 -9
  134. package/src/tui.d.ts +5 -0
  135. package/src/tui.ts +370 -70
  136. package/src/windows-input-mode.d.ts +33 -1
  137. package/src/windows-input-mode.ts +204 -6
package/CHANGELOG.md CHANGED
@@ -2,9 +2,11 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
- ## [1.1.26] - 2026-10-03
5
+ ## [1.1.27] - 2026-10-07
6
6
 
7
- - 版本线推进至 1.1.26;本版无独立用户可见变化。
7
+ - 版本线推进至 1.1.27;本版无独立用户可见变化。
8
+
9
+ - Inline images now retire to native scrollback as soon as their transcript block finalizes instead of waiting for row pressure or shutdown, so terminals whose live-region sixel does not stick (Windows Terminal) show images in real time; the exit-time history flush remains as the fallback.
8
10
 
9
11
  ## [14.9.8] - 2026-05-12
10
12
 
@@ -775,6 +777,10 @@ Initial release under @oh-my-pi scope. See previous releases at [badlogic/pi-mon
775
777
 
776
778
  - Added `getText()` method to Text component for retrieving current text content
777
779
 
780
+ ## [1.1.26] - 2026-10-03
781
+
782
+ - 版本线推进至 1.1.26;本版无独立用户可见变化。
783
+
778
784
  ## [1.1.25] - 2026-10-03
779
785
 
780
786
  - Hyperlinks (OSC 8) are now emitted when running inside Zetawork workbench panes: the `ZETA_WORKBENCH=1` probe unlocks `fileHyperlink` output, so file links stay clickable and titles update live in the workbench's embedded terminals.
@@ -1026,34 +1032,4 @@ Initial release under @oh-my-pi scope. See previous releases at [badlogic/pi-mon
1026
1032
 
1027
1033
  - `visibleWidth()` now strips OSC 8 hyperlink sequences, fixing text wrapping for clickable links ([#396](https://github.com/badlogic/pi-mono/pull/396) by [@Cursivez](https://github.com/Cursivez))
1028
1034
 
1029
- ## [0.31.0] - 2026-01-02
1030
-
1031
- ### Added
1032
-
1033
- - `isShiftCtrlO()` key detection function for Shift+Ctrl+O (Kitty protocol)
1034
- - `isShiftCtrlD()` key detection function for Shift+Ctrl+D (Kitty protocol)
1035
- - `TUI.onDebug` callback for global debug key handling (Shift+Ctrl+D)
1036
- - `wrapTextWithAnsi()` utility now exported (wraps text to width, preserving ANSI codes)
1037
-
1038
- ### Changed
1039
-
1040
- - README.md completely rewritten with accurate component documentation, theme interfaces, and examples
1041
- - `visibleWidth()` reimplemented with grapheme-based width calculation, 10x faster on Bun and ~15% faster on Node ([#369](https://github.com/badlogic/pi-mono/pull/369) by [@nathyong](https://github.com/nathyong))
1042
-
1043
- ### Fixed
1044
-
1045
- - Markdown component now renders HTML tags as plain text instead of silently dropping them ([#359](https://github.com/badlogic/pi-mono/issues/359))
1046
- - Crash in `visibleWidth()` and grapheme iteration when encountering undefined code points ([#372](https://github.com/badlogic/pi-mono/pull/372) by [@HACKE-RC](https://github.com/HACKE-RC))
1047
- - ZWJ emoji sequences (rainbow flag, family, etc.) now render with correct width instead of being split into multiple characters ([#369](https://github.com/badlogic/pi-mono/pull/369) by [@nathyong](https://github.com/nathyong))
1048
-
1049
- ## [0.29.0] - 2025-12-25
1050
-
1051
- ### Added
1052
-
1053
- - **Auto-space before pasted file paths**: When pasting a file path (starting with `/`, `~`, or `.`) and the cursor is after a word character, a space is automatically prepended for better readability. Useful when dragging screenshots from macOS. ([#307](https://github.com/badlogic/pi-mono/pull/307) by [@mitsuhiko](https://github.com/mitsuhiko))
1054
- - **Word navigation for Input component**: Added Ctrl+Left/Right and Alt+Left/Right support for word-by-word cursor movement. ([#306](https://github.com/badlogic/pi-mono/pull/306) by [@kim0](https://github.com/kim0))
1055
- - **Full Unicode input**: Input component now accepts Unicode characters beyond ASCII. ([#306](https://github.com/badlogic/pi-mono/pull/306) by [@kim0](https://github.com/kim0))
1056
-
1057
- ### Fixed
1058
-
1059
- - **Readline-style Ctrl+W**: Now skips trailing whitespace before deleting the preceding word, matching standard readline behavior. ([#306](https://github.com/badlogic/pi-mono/pull/306) by [@kim0](https://github.com/kim0))
1035
+ Older entries are archived in [packages/tui/CHANGELOG.md@9caccab691ce](https://github.com/can1357/oh-my-pi/blob/9caccab691ce575007f4b6bcbaf8f944723d5457/packages/tui/CHANGELOG.md).
package/README.md CHANGED
@@ -455,7 +455,7 @@ const spacer = new Spacer(2); // 2 empty lines (default: 1)
455
455
 
456
456
  ### Image
457
457
 
458
- Renders images inline for terminals that support the Kitty graphics protocol (Kitty, Ghostty, WezTerm, and Warp on macOS/Linux) or iTerm2 inline images. Falls back to a text placeholder on unsupported terminals.
458
+ Renders images inline for terminals that support the Kitty graphics protocol (Kitty, Ghostty, WezTerm, and Warp on macOS/Linux), iTerm2 inline images or SIXEL. Falls back to a text placeholder on unsupported terminals.
459
459
 
460
460
  ```typescript
461
461
  interface ImageTheme {
@@ -466,17 +466,21 @@ interface ImageOptions {
466
466
  maxWidthCells?: number;
467
467
  maxHeightCells?: number;
468
468
  filename?: string;
469
+ budget?: ImageBudget; // usually tui.imageBudget
470
+ requestRender?: () => void; // repaint once a SIXEL encode lands; defaults to the budget's
469
471
  }
470
472
 
471
473
  const image = new Image(
472
474
  base64Data, // base64-encoded image data
473
475
  "image/png", // MIME type
474
476
  theme, // ImageTheme
475
- options, // optional ImageOptions
477
+ { ...options, budget: tui.imageBudget }, // optional ImageOptions
476
478
  );
477
479
  tui.addChild(image);
478
480
  ```
479
481
 
482
+ SIXEL images encode off the JavaScript thread: the first render reserves the image's rows and the image appears on the repaint `budget` or `requestRender` triggers once the encode lands. Without either, it waits for an unrelated repaint.
483
+
480
484
  Supported formats: PNG, JPEG, GIF, WebP. Dimensions are parsed from the image headers automatically.
481
485
 
482
486
  ## Autocomplete
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Ownership of stdout by a started {@link ProcessTerminal}, kept in a leaf
3
+ * module so writers that `terminal.ts` itself imports (terminal capabilities,
4
+ * notifications) can route through the active terminal without an import cycle.
5
+ */
6
+ import type { ProcessTerminal } from "./terminal.js";
7
+ /** The started terminal that owns stdout in this thread, if any. */
8
+ export declare function getActiveTerminal(): ProcessTerminal | null;
9
+ /** Record the terminal that owns stdout (`ProcessTerminal.start`), or release it with `null` (`stop`). */
10
+ export declare function setActiveTerminal(terminal: ProcessTerminal | null): void;
11
+ /** Observe stdout errors through the shared listener. Returns the unregister callback. */
12
+ export declare function registerStdoutErrorHandler(handler: (err: Error) => void): () => void;
13
+ /**
14
+ * Hand `data` to the active terminal's ordered output path. Returns false when
15
+ * no terminal in this thread has started. On the main thread that means the
16
+ * caller owns stdout; a worker thread never sees the main thread's terminal, so
17
+ * there `false` is not permission to write. Prefer {@link writeTerminalSequence}.
18
+ */
19
+ export declare function writeThroughActiveTerminal(data: string): boolean;
20
+ /**
21
+ * Write an out-of-band escape sequence (window title, OSC 52 clipboard,
22
+ * notification). While a TUI owns stdout, its frames go through an off-thread
23
+ * pump in chunks that ignore escape boundaries; a direct `process.stdout.write`
24
+ * can land between two chunks and split a frame's escape sequence, and the host
25
+ * terminal then prints its tail as text. So the sequence takes the active
26
+ * terminal's ordered output path, and goes straight to stdout, best-effort, only
27
+ * when no terminal owns it. A worker thread cannot reach the main thread's
28
+ * terminal and would write around its pump, so it writes nothing.
29
+ */
30
+ export declare function writeTerminalSequence(data: string): void;
@@ -40,6 +40,7 @@ interface AppKeybindings {
40
40
  "app.tree.unfoldOrDown": true;
41
41
  "app.plan.toggle": true;
42
42
  "app.history.search": true;
43
+ "app.stt.pushToTalk": true;
43
44
  "app.stt.toggle": true;
44
45
  "app.live.toggle": true;
45
46
  }
@@ -329,9 +330,13 @@ export declare const KEYBINDINGS: {
329
330
  readonly defaultKeys: "ctrl+r";
330
331
  readonly description: "Search history";
331
332
  };
333
+ readonly "app.stt.pushToTalk": {
334
+ readonly defaultKeys: "space";
335
+ readonly description: "Hold to record speech-to-text";
336
+ };
332
337
  readonly "app.stt.toggle": {
333
338
  readonly defaultKeys: [];
334
- readonly description: "Toggle speech-to-text (default gesture: hold Space)";
339
+ readonly description: "Toggle speech-to-text recording";
335
340
  };
336
341
  readonly "app.live.toggle": {
337
342
  readonly defaultKeys: "ctrl+l";
@@ -13,8 +13,9 @@
13
13
  * arrows/PgUp/PgDn, vim motions (`j`/`k`/`h`/`l`/`g`/`G`), and mouse
14
14
  * clicks/wheel. All toolbar buttons are clickable and mirrored by keys:
15
15
  * `v` cycles the view (`1`–`4` pick one), `alt+↓`/`alt+↑` jump hunks and roll
16
- * into the adjacent file at the edges, `]`/`[` switch files, `s`/`u`
17
- * stage/unstage (hunk-aware), `x` discards a hunk, `delete` discards the
16
+ * into the adjacent file at the edges, `space` stages/unstages the focused
17
+ * diff row (the focused hunk in hunk view), `s`/`u` stage/unstage
18
+ * (hunk-aware), `x` discards a hunk, `delete` discards the
18
19
  * whole file (press twice to confirm), `w` wraps, `b` cycles
19
20
  * whitespace handling (exact → ignore whitespace → ignore
20
21
  * formatting/import-only changes), `c` jumps to the commit form, `r`
@@ -38,7 +38,7 @@ export declare class AssistantMessageComponent extends Container {
38
38
  * for lack of a target is re-rendered with its reaction stripped.
39
39
  */
40
40
  pickReactionTarget(transcript: readonly Component[]): void;
41
- constructor(message?: AssistantMessage, hideThinkingBlock?: boolean, onImageUpdate?: () => void, thinkingRenderers?: readonly AssistantThinkingRenderer[], imageBudget?: ImageBudget, proseOnlyThinking?: boolean, linkTargets?: ReadonlyMap<string, string>);
41
+ constructor(message?: AssistantMessage, hideThinkingBlock?: boolean, onImageUpdate?: () => void, thinkingRenderers?: readonly AssistantThinkingRenderer[], imageBudget?: ImageBudget, proseOnlyThinking?: boolean, linkTargets?: ReadonlyMap<string, string>, expandThinkingBlocks?: boolean);
42
42
  /**
43
43
  * Show or clear the trailing cache-invalidation divider. Set at `message_end`
44
44
  * (live) or during rebuild, once the turn's usage is known and compared
@@ -65,6 +65,11 @@ export declare class AssistantMessageComponent extends Container {
65
65
  */
66
66
  setMidStreamPublication(allowed: boolean): void;
67
67
  setProseOnlyThinking(proseOnly: boolean): void;
68
+ /**
69
+ * Keep finished thinking sections expanded instead of folding them to "Thought for 12s".
70
+ * Sections the user folded or unfolded by hand keep that choice.
71
+ */
72
+ setExpandThinkingBlocks(expand: boolean): void;
68
73
  dispose(): void;
69
74
  /**
70
75
  * Toggle suppression of the inline `Error: …` line while the same error is
@@ -25,6 +25,7 @@ export interface ChatTranscriptBuilderDeps {
25
25
  cwd: string;
26
26
  hideThinkingBlock?: () => boolean;
27
27
  proseOnlyThinking?: () => boolean;
28
+ expandThinkingBlocks?: () => boolean;
28
29
  /** Session-scoped resolved destinations for model-authored Markdown links. */
29
30
  linkTargets?: ReadonlyMap<string, string>;
30
31
  requestRender: () => void;
@@ -1,7 +1,7 @@
1
1
  import { type Component, Container } from "../tui.js";
2
2
  import type { ImageContent } from "@linxiraos/pi-ai";
3
3
  import type { ReactionTarget } from "./reaction.js";
4
- import { type NativeNode, type NativeUiEvent } from "../native/node.js";
4
+ import { type DescribeContext, type NativeNode, type NativeUiEvent } from "../native/node.js";
5
5
  /** How a user bubble styles its prose and chips (see {@link userBubbleColor}). */
6
6
  export interface UserBubbleOptions {
7
7
  /** Materialized `file://` targets per attached image, indexed by chip number. */
@@ -42,7 +42,7 @@ export declare class UserMessageComponent extends Container implements ReactionT
42
42
  * agent's reaction are chips at the bottom-right, so a reaction landing
43
43
  * later updates the frame in place, even deep in scrollback.
44
44
  */
45
- describe(): NativeNode;
45
+ describe(cx?: DescribeContext): NativeNode;
46
46
  /** Hover toolbar clicks: zeta's own copy and rewind commands. */
47
47
  handleNativeEvent(event: NativeUiEvent): void;
48
48
  render(width: number): readonly string[];
@@ -1,14 +1,26 @@
1
1
  import type { NativeNode } from "../native/node.js";
2
2
  import { Container } from "../tui.js";
3
+ /** How a {@link StatusNotice} shows. */
4
+ export interface StatusNoticeOptions {
5
+ /** Styles the ANSI line; resolved at render so a theme change re-shapes it. */
6
+ styleFn?: (text: string) => string;
7
+ /**
8
+ * Whether a native terminal toasts it (default). False keeps the notice to
9
+ * the ANSI transcript: background chatter such as MCP connection progress,
10
+ * which would otherwise re-toast on every server that connects or fails.
11
+ */
12
+ toast?: boolean;
13
+ }
3
14
  /**
4
15
  * A transient status notice (`showStatus`: "Thinking blocks: hidden", "Copied
5
16
  * to clipboard"). The ANSI render appends a blank row and the dim line to the
6
17
  * transcript; a native terminal shows it as a toast instead, so notices never
7
- * pile up in the history. Updating the text re-shows the toast.
18
+ * pile up in the history. Updating the text re-shows the toast; a notice
19
+ * with `toast: false` describes nothing natively.
8
20
  */
9
21
  export declare class StatusNotice extends Container {
10
22
  #private;
11
- constructor(message: string, styleFn?: (text: string) => string);
12
- setMessage(message: string, styleFn?: (text: string) => string): void;
23
+ constructor(message: string, options?: StatusNoticeOptions);
24
+ setMessage(message: string, options?: StatusNoticeOptions): void;
13
25
  describe(): NativeNode;
14
26
  }
@@ -138,6 +138,9 @@ export declare class Editor implements Component, Focusable {
138
138
  onLargePaste?: (text: string, lineCount: number, options: PasteOptions) => boolean;
139
139
  onAutocompleteCancel?: () => void;
140
140
  disableSubmit: boolean;
141
+ /** Base editors accept native edits, not atomic sends. Implementations that
142
+ * handle `send` override this readiness check for their submission path. */
143
+ protected get nativeSendable(): boolean;
141
144
  /** Placeholder painted right-aligned on the cursor row while the editor is empty and no
142
145
  * autocomplete is open; hidden when it can't keep {@link PLACEHOLDER_MIN_GAP} cells from the
143
146
  * cursor. The host styles it (ANSI allowed). Re-evaluated on every render, so hosts can derive
@@ -296,7 +299,7 @@ export declare class Editor implements Component, Focusable {
296
299
  submit(): void;
297
300
  /** Insert text at the current cursor position */
298
301
  insertText(text: string): void;
299
- /** Terminal-side selection edits on the `editor` node (see {@link applyHostEdit}). */
302
+ /** Terminal-side selection edits and undo on the `editor` node (see {@link applyHostEdit}). */
300
303
  handleNativeEvent(event: NativeUiEvent): void;
301
304
  /**
302
305
  * Apply an edit the terminal made over its own selection (TSP `edit`):
@@ -327,6 +330,10 @@ export declare class Editor implements Component, Focusable {
327
330
  * {@link getExpandedText}). Hosts that intercept large pastes through {@link onLargePaste} use
328
331
  * this to re-insert a (possibly transformed) paste without re-triggering the interception hook. */
329
332
  insertPaste(content: string): void;
333
+ /** Whether a character-jump hotkey is waiting for its target character. */
334
+ get isJumpPending(): boolean;
335
+ /** Type literal text through the character-editing pipeline without keybinding dispatch. */
336
+ typeCharacter(text: string): void;
330
337
  /**
331
338
  * Whether an autocomplete popup is on screen. An `@` list narrowed to no match
332
339
  * while its refresh is pending stays open internally but is hidden, so it does
@@ -16,7 +16,19 @@ export interface ImageOptions {
16
16
  * repaint replaces the placement instead of stacking a duplicate.
17
17
  */
18
18
  imageKey?: string;
19
+ /**
20
+ * Schedules a repaint once this image's off-thread SIXEL encode lands.
21
+ * Defaults to the budget's repaint; a host without a budget must pass one,
22
+ * or the image's reserved rows stay blank until an unrelated repaint.
23
+ */
24
+ requestRender?: () => void;
19
25
  }
26
+ /**
27
+ * Run `render` for rows bound for native scrollback. Those rows are never
28
+ * repainted, so an image whose off-thread SIXEL encode has not landed encodes
29
+ * synchronously instead of committing its reserved blank rows.
30
+ */
31
+ export declare function renderForScrollback<T>(render: () => T): T;
20
32
  /** Default count of inline images kept as live graphics before older ones fall back to text. */
21
33
  export declare const DEFAULT_MAX_INLINE_IMAGES = 8;
22
34
  /**
@@ -52,6 +64,8 @@ export declare class ImageBudget {
52
64
  get cap(): number;
53
65
  get enabled(): boolean;
54
66
  setRequestRender(requestRender: () => void): void;
67
+ /** Ask for a repaint, e.g. once an image's off-thread encode settles. */
68
+ requestRender(): void;
55
69
  setCap(cap: number): void;
56
70
  /**
57
71
  * Stable graphics id for a logical image. A non-empty `key` maps to the same
@@ -28,6 +28,7 @@ export declare class Input implements Component, Focusable {
28
28
  setValue(value: string): void;
29
29
  setUseTerminalCursor(useTerminalCursor: boolean): void;
30
30
  getUseTerminalCursor(): boolean;
31
+ capturesInput(data: string): boolean;
31
32
  /**
32
33
  * Apply one key: the editor's text bindings (motion, deletion, kill ring,
33
34
  * undo), pastes and printable text. Returns whether the key was the
@@ -39,7 +40,7 @@ export declare class Input implements Component, Focusable {
39
40
  /** Apply terminal paste semantics to text from non-bracketed paste transports
40
41
  * (e.g. kitty's OSC 5522 enhanced clipboard read). Mirrors `Editor.pasteText`. */
41
42
  pasteText(text: string): void;
42
- /** Terminal-side selection edits on the `input` node (see {@link applyHostEdit}). */
43
+ /** Terminal-side selection edits and undo on the `input` node (see {@link applyHostEdit}). */
43
44
  handleNativeEvent(event: NativeUiEvent): void;
44
45
  /**
45
46
  * Apply an edit the terminal made over its own selection (TSP `edit`):
@@ -1,3 +1,4 @@
1
+ import type { KeyId } from "../keys.js";
1
2
  import type { DescribeContext, NativeNode, NativeUiEvent } from "../native/node.js";
2
3
  import { type ShimmerPalette } from "../theme/shimmer.js";
3
4
  import type { TUI } from "../tui.js";
@@ -23,16 +24,14 @@ export interface WorkingRowSpec {
23
24
  readonly variant?: WorkingRowVariant;
24
25
  /** Shimmer palette of the label (a session-accented intent). */
25
26
  readonly palette?: ShimmerPalette;
26
- /** Live tok/s, docked before the stop control. */
27
- readonly rate?: number;
28
27
  /** Key id that interrupts (`escape`); undefined hides the stop control (Esc would not cancel). */
29
- readonly interruptKey?: string;
28
+ readonly interruptKey?: KeyId;
30
29
  }
31
30
  /**
32
31
  * The dock's working row: `starburst` spinner (or a retry countdown ring),
33
- * the intent shimmering, `·`, the elapsed time, a grow spacer, then the stop
34
- * control whose click sends `interrupt`. Compaction adds an indeterminate
35
- * `progress` before the stop control; a retry says "Cancel".
32
+ * the elapsed time, the `·` divider, the intent shimmering, a grow spacer,
33
+ * then the stop control whose click sends `interrupt`. Compaction adds an
34
+ * indeterminate `progress` before the stop control; a retry says "Cancel".
36
35
  */
37
36
  export declare function describeWorkingRow(spec: WorkingRowSpec, cx: DescribeContext, now?: number): NativeNode;
38
37
  type ColorFn = (str: string) => string;
@@ -1,8 +1,10 @@
1
1
  import type { DescribeContext, NativeNode } from "../native/node.js";
2
2
  import type { SymbolTheme } from "../symbols.js";
3
3
  import type { Component } from "../tui.js";
4
- /** @internal exported for tests — the grown-line-start block-kind gate. */
4
+ /** @internal exported for tests — the grown-line block-kind gate. */
5
5
  export declare function fastLineStartHazard(grownLine: string): boolean;
6
+ /** @internal exported for tests — the windowed lexer's first-probe window size. */
7
+ export declare const LEX_WINDOW_BYTES: number;
6
8
  /** A hyperlink as the renderer sees it: inline `[text](href)`, `<autolink>`, bare GFM URL, or reference link. */
7
9
  export interface MarkdownLink {
8
10
  /** Visible link text (equals `href` for autolinks and bare URLs). */
@@ -82,7 +84,12 @@ export interface MarkdownTheme {
82
84
  * Return null to fall back to fenced code rendering.
83
85
  */
84
86
  resolveMermaidAscii?: (source: string, maxWidth?: number) => string | null;
85
- symbols: SymbolTheme;
87
+ /**
88
+ * Glyphs for quote borders, rules, tables and color swatches. Optional so themes
89
+ * built to the upstream pi-tui `MarkdownTheme` shape (which has no `symbols`)
90
+ * still render; omitted symbols fall back to the active theme's set.
91
+ */
92
+ symbols?: SymbolTheme;
86
93
  }
87
94
  export declare class Markdown implements Component {
88
95
  #private;
@@ -101,8 +108,10 @@ export declare class Markdown implements Component {
101
108
  describe(_cx: DescribeContext): NativeNode;
102
109
  /**
103
110
  * Width-independent source prefix of the last render ending at a frozen
104
- * Markdown block boundary. Only meaningful while streaming (transient
105
- * render cache on); grows monotonically under append-only `setText`.
111
+ * Markdown block boundary that no append can move: it stops in front of a
112
+ * display-math opener whose block an append could still close. Only
113
+ * meaningful while streaming (transient render cache on); grows
114
+ * monotonically under append-only `setText`.
106
115
  */
107
116
  getLastRenderStableText(): string;
108
117
  get transientRenderCache(): boolean;
@@ -23,6 +23,14 @@ export type KeyName = KeyId | ModifierName;
23
23
  * is capitalized. Other keys are capitalized (`f5` → `F5`).
24
24
  */
25
25
  export declare function formatKeyHint(key: KeyName): string;
26
+ /**
27
+ * Format one key for a native tooltip (a TSP `title`): unicode keycap glyphs
28
+ * whatever the theme's symbol preset (`shift+tab` → `⇧⇥`), which Tern's
29
+ * tooltip splits into keycaps, and `esc` as Tern's own keycaps read. The nerd
30
+ * preset's icons are missing from Tern's UI font, and its spaced chords would
31
+ * not parse as keys.
32
+ */
33
+ export declare function formatTooltipKey(key: KeyName): string;
26
34
  /** Format alternative keys as a slash-separated hint (`["f5", "alt+r"]` → `F5/⌥R`). */
27
35
  export declare function formatKeyHints(keys: KeyName | readonly KeyName[]): string;
28
36
  /** Format a double-tap gesture: `←←` with glyphs, `Left Left` with words. */
@@ -1,8 +1,9 @@
1
1
  /**
2
2
  * Reference document applier: applies TSP frames to a node tree the way the
3
- * spec says the terminal does (§5.1). Ops are validated and applied one by
4
- * one; a rejected op is reported and skipped without affecting the rest of
5
- * the frame. `settle` is recorded as a hint and never restricts later ops.
3
+ * spec says the terminal does (Tern SDK, `protocol/documents.md`, frame ops).
4
+ * Ops are validated and applied one by one; a rejected op is reported and
5
+ * skipped without affecting the rest of the frame. `settle` is recorded as a
6
+ * hint and never restricts later ops.
6
7
  *
7
8
  * Used by tests, the debug server's `doc` op, and the TSP test harness.
8
9
  */
@@ -83,8 +83,8 @@ export declare class NativeBackend {
83
83
  * The terminal's real `hello` reply after an optimistic start: adopt its
84
84
  * APC limit, credits, cell size, kinds, appearance and motion preference.
85
85
  * A width the terminal already reported in a `resize` event wins over the
86
- * reply's. A different vocabulary or motion preference re-describes every
87
- * component, so kinds the terminal lacks fall back.
86
+ * reply's. A different vocabulary, motion preference or clock re-describes
87
+ * every component, so kinds the terminal lacks fall back.
88
88
  */
89
89
  confirm(hello: TspHello): void;
90
90
  /** SIGWINCH: refresh the width used for `rows` fallback until the terminal reports one. */
@@ -26,8 +26,11 @@ export declare function encodeTspMessage(verb: TspVerb, body: string, params?: T
26
26
  export declare function encodeTspJson(verb: TspVerb, value: unknown, params?: TspParams, limit?: number): string;
27
27
  /**
28
28
  * The `hello` query; callers follow it with a DA1 sentinel. `features: ["edit"]`
29
- * tells the terminal that zeta applies its `edit` events (TSP §8.5), so it may keep a
30
- * native selection in zeta's editors; without it, every key stays zeta's.
29
+ #1: * tells the terminal that zeta applies its `edit` events (Tern SDK,
30
+ * `protocol/input.md`), so it may keep a native selection in zeta's editors;
31
+ * without it, every key stays zeta's. `"undo"` says zeta applies `undo` events,
32
+ * so the terminal may turn ⌃Z in a field into one.
33
+ * `"send"` accepts an explicit prompt for a live composer without simulating keys.
31
34
  */
32
35
  export declare function encodeTspHelloQuery(version?: string): string;
33
36
  /** One decoded APC message: verb, parameters and raw body. */
@@ -11,7 +11,8 @@
11
11
  * `rows` fallback node rendered through `render(cx.cols)`.
12
12
  *
13
13
  * See `packages/wire/src/tsp.ts` for the wire vocabulary and
14
- * `crates/tern/SURFACE_PROTOCOL.md` (Stencil repository) for the spec.
14
+ * the Tern SDK's Surface Protocol reference (`docs/sdk/src/protocol` in the
15
+ * Stencil repository, https://docs.stencil.so/tern/protocol/) for the spec.
15
16
  */
16
17
  import type { TspEvent, TspKind, TspProps, TspScrollBy, TspSpan } from "@linxiraos/pi-wire";
17
18
  import type { Component } from "../tui.js";
@@ -28,10 +29,12 @@ export type NativeNode = {
28
29
  */
29
30
  readonly key?: string;
30
31
  /**
31
- * Scroll the node into view, placed like the `reveal` op, when it is
32
- * added. Key a node by what it points at to reveal it again on a move.
32
+ * Scroll the node into view, placed like the `reveal` op. A placement
33
+ * reveals it when it is added: key a node by what it points at to
34
+ * reveal it again on a move. A {@link NativeReveal} reveals it whenever
35
+ * its `n` differs from the previous description of the same node.
33
36
  */
34
- readonly reveal?: "start" | "end" | "nearest";
37
+ readonly reveal?: NativeRevealAt | NativeReveal;
35
38
  /**
36
39
  * Keyboard scrolling forwarded to the terminal (PgUp/PgDn/End reach the
37
40
  * program): moves the scroller at or above the node by `by` whenever
@@ -42,6 +45,17 @@ export type NativeNode = {
42
45
  readonly scroll?: NativeScroll;
43
46
  };
44
47
  }[TspKind];
48
+ /** Where a revealed node lands in its scroller. */
49
+ export type NativeRevealAt = "start" | "end" | "nearest";
50
+ /**
51
+ * A repeatable {@link NativeNode.reveal}: bump `n` to bring an existing node
52
+ * into view again (a Contents entry jumping to its section). A freshly added
53
+ * node is not revealed.
54
+ */
55
+ export interface NativeReveal {
56
+ readonly at: NativeRevealAt;
57
+ readonly n: number;
58
+ }
45
59
  /** A {@link NativeNode.scroll} request. */
46
60
  export interface NativeScroll {
47
61
  readonly by: TspScrollBy;
@@ -57,6 +71,12 @@ export interface DescribeContext {
57
71
  readonly reduceMotion: boolean;
58
72
  /** The terminal's appearance is dark. */
59
73
  readonly dark: boolean;
74
+ /**
75
+ * The user's system reads a 12-hour clock (`false`: 24-hour); `undefined`
76
+ * when the terminal doesn't say. Pass it as `hour12` when formatting times:
77
+ * the process's own default locale may not know.
78
+ */
79
+ readonly hour12?: boolean;
60
80
  /** Whether the terminal renders `kind` natively (else describe something simpler or let it fall back). */
61
81
  supports(kind: TspKind): boolean;
62
82
  /**
@@ -117,7 +137,18 @@ export type NativeUiEvent = {
117
137
  | ({
118
138
  readonly type: "edit";
119
139
  readonly key: string;
120
- } & NativeTextEdit);
140
+ } & NativeTextEdit)
141
+ /** Undo the last change to an `editor`/`input` node's text (the terminal's ⌃Z); a no-op with no history. */
142
+ | {
143
+ readonly type: "undo";
144
+ readonly key: string;
145
+ }
146
+ /** Submit an explicit prompt through the composer's normal path, preserving the previous draft for recall. */
147
+ | {
148
+ readonly type: "send";
149
+ readonly key: string;
150
+ readonly text: string;
151
+ };
121
152
  /**
122
153
  * A primitive edit the terminal made over its own text selection (cut,
123
154
  * typing, Backspace/Delete or paste over it, or collapsing it). Offsets are
@@ -7,7 +7,7 @@
7
7
  * component's root is a `card` whose `head` carries the title.
8
8
  */
9
9
  import type { TspProps, TspSpan, TspText } from "@linxiraos/pi-wire";
10
- import type { KeyName } from "../key-hint-format.js";
10
+ import { type KeyName } from "../key-hint-format.js";
11
11
  import { type Keybinding } from "../keybindings.js";
12
12
  import type { NativeChild, NativeNode } from "./node.js";
13
13
  /** One footer hint: the keys that trigger it and what it does. */
@@ -44,10 +44,10 @@ export interface ActionButtonOptions {
44
44
  /** Sibling identity key (defaults to the action id); `leafKey(event.key)` reports it back. */
45
45
  readonly key?: string;
46
46
  /** Key id of the keyboard path the button mirrors, drawn as a keycap inside it (`"r"`, `"ctrl+g"`). */
47
- readonly keys?: string;
47
+ readonly keys?: KeyName;
48
48
  /** `accent` = the view's hero action, `error` = destructive. */
49
49
  readonly tone?: "accent" | "error";
50
- /** Tooltip; defaults to the label plus the keycap (`"Refresh r"`). */
50
+ /** Tooltip; defaults to the label plus the keycap (`"Refresh r"`, see {@link formatTooltipKey}). */
51
51
  readonly title?: string;
52
52
  /** Target of an `open` action (URL or `file://` path). */
53
53
  readonly href?: string;
@@ -1,8 +1,9 @@
1
1
  /**
2
2
  * Settling: the frame provider marks finalized transcript blocks so the
3
- * native reconciler stops keeping their description (spec §12 "Settling and
4
- * program memory"). A settled block stays editable: a later change is sent as
5
- * targeted ops by id, followed by a fresh `settle` hint.
3
+ * native reconciler stops keeping their description (Tern SDK,
4
+ * `protocol/operations.md`, "Settling"). A settled block stays editable: a
5
+ * later change is sent as targeted ops by id, followed by a fresh `settle`
6
+ * hint.
6
7
  */
7
8
  import type { Component } from "../tui.js";
8
9
  /** Mark `component` (and everything it describes) as finalized. Idempotent; a no-op outside a TSP terminal. */
@@ -73,6 +73,7 @@ export interface AgentHubDeps<TRecord extends AgentRecordLike = AgentRecordLike>
73
73
  /** Mirrors the main transcript's thinking-block visibility. */
74
74
  hideThinkingBlock?: () => boolean;
75
75
  proseOnlyThinking?: () => boolean;
76
+ expandThinkingBlocks?: () => boolean;
76
77
  /** Keys toggling tool output expansion (app.tools.expand). */
77
78
  expandKeys?: KeyId[];
78
79
  /** Focus the main view on this agent's live session (ctx.focusAgentSession). When absent (collab guest, tests), Enter opens the in-hub chat view instead. */
@@ -54,6 +54,7 @@ export interface AgentTranscriptViewerDeps {
54
54
  cwd: string;
55
55
  hideThinkingBlock?: () => boolean;
56
56
  proseOnlyThinking?: () => boolean;
57
+ expandThinkingBlocks?: () => boolean;
57
58
  expandKeys: KeyId[];
58
59
  /** Keys that toggle the whole hub closed (app.agents.hub + app.session.observe). */
59
60
  hubKeys: KeyId[];
@@ -1,6 +1,7 @@
1
1
  import { type Component, type Focusable, Input } from "../index.js";
2
2
  import { type BtwHistoryRecord } from "./btw-history.js";
3
3
  import type { SpaceHoldHandler } from "../space-hold.js";
4
+ import { type KeyId } from "../app-keybindings.js";
4
5
  import type { NativeNode, NativeUiEvent } from "../native/node.js";
5
6
  interface BtwHistoryPanelOptions {
6
7
  records: readonly BtwHistoryRecord[];
@@ -9,10 +10,17 @@ interface BtwHistoryPanelOptions {
9
10
  onCancel: (record: BtwHistoryRecord) => void;
10
11
  canFollowUp?: (record: BtwHistoryRecord) => boolean;
11
12
  onFollowUp?: (record: BtwHistoryRecord, question: string, signal: AbortSignal) => Promise<boolean>;
12
- /** Space-bar push-to-talk for a follow-up composer: dictates into `input` while Space is held. */
13
+ /** Configured push-to-talk keys for a follow-up composer. */
14
+ spaceHoldKeys?: readonly KeyId[];
13
15
  spaceHold?: (input: Input) => SpaceHoldHandler;
14
16
  requestRender: () => void;
15
17
  getHeight: () => number;
18
+ /**
19
+ * Esc closes the panel even while the selected answer runs (it keeps
20
+ * streaming in the background) and `x` cancels it, instead of Esc cancelling
21
+ * first. For a host with no inline panel to fall back to (Tern).
22
+ */
23
+ escapeHides?: boolean;
16
24
  }
17
25
  /** Session-local side questions. Selecting or copying never promotes them into chat. */
18
26
  export declare class BtwHistoryPanel implements Component, Focusable {
@@ -25,6 +33,10 @@ export declare class BtwHistoryPanel implements Component, Focusable {
25
33
  markCopied(recordId: string, text: string): void;
26
34
  invalidate(): void;
27
35
  openFollowUp(recordId: string): boolean;
36
+ /** Select a record with its answer pane focused, read from the top (a just-asked /btw). */
37
+ showRecord(recordId: string): void;
38
+ /** Only an active follow-up composer can claim its configured push-to-talk keys. */
39
+ capturesInput(data: string): boolean;
28
40
  /** Enhanced clipboard pastes belong only to the active composer. */
29
41
  pasteText(text: string): void;
30
42
  handleInput(data: string): void;
@@ -31,6 +31,7 @@ export interface CopySelectorDeps {
31
31
  cwd: string;
32
32
  hideThinkingBlock?: () => boolean;
33
33
  proseOnlyThinking?: () => boolean;
34
+ expandThinkingBlocks?: () => boolean;
34
35
  linkTargets?: ReadonlyMap<string, string>;
35
36
  requestRender: () => void;
36
37
  /** Replaces the "Copy" header when the picker is reused for another purpose. */
@@ -61,7 +61,10 @@ export declare class HubFrame {
61
61
  constructor(title: string, sidebarBounds: {
62
62
  min: number;
63
63
  max: number;
64
- }, renderSidebar: (width: number, rows: number) => string[], renderBody: (width: number, height: number | undefined) => readonly string[]);
64
+ }, renderSidebar: (width: number, rows: number) => string[], renderBody: (width: number, height: number | undefined) => readonly string[], options?: {
65
+ bodyMinWidth?: number;
66
+ preserveSidebar?: boolean;
67
+ });
65
68
  /** Invalidate the composed layout. */
66
69
  invalidate(): void;
67
70
  /** Measure labels and annotations within this hub's sidebar width bounds. */