@oh-my-pi/pi-tui 18.8.6 → 18.8.8

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 (103) hide show
  1. package/CHANGELOG.md +45 -79
  2. package/README.md +7 -5
  3. package/dist/types/chat/assistant-message.d.ts +1 -0
  4. package/dist/types/chat/compaction-summary-message.d.ts +1 -0
  5. package/dist/types/chat/execution-shared.d.ts +6 -5
  6. package/dist/types/chat/user-message.d.ts +2 -0
  7. package/dist/types/components/box.d.ts +1 -0
  8. package/dist/types/components/disclosure.d.ts +1 -0
  9. package/dist/types/components/image.d.ts +1 -0
  10. package/dist/types/components/key-value-list.d.ts +1 -0
  11. package/dist/types/components/layout/row.d.ts +1 -0
  12. package/dist/types/components/layout/stack.d.ts +1 -0
  13. package/dist/types/components/markdown.d.ts +1 -0
  14. package/dist/types/components/scroll-view.d.ts +1 -0
  15. package/dist/types/components/section.d.ts +1 -0
  16. package/dist/types/components/table.d.ts +1 -0
  17. package/dist/types/components/text.d.ts +1 -0
  18. package/dist/types/components/truncated-text.d.ts +1 -0
  19. package/dist/types/keybinding-matchers.d.ts +3 -0
  20. package/dist/types/native/blobs.d.ts +3 -1
  21. package/dist/types/overlays/annotation-overlay.d.ts +12 -0
  22. package/dist/types/overlays/annotation-types.d.ts +2 -0
  23. package/dist/types/overlays/hook-selector.d.ts +9 -4
  24. package/dist/types/overlays/model-browser.d.ts +2 -2
  25. package/dist/types/overlays/model-hub.d.ts +21 -4
  26. package/dist/types/overlays/session-observer-registry.d.ts +2 -0
  27. package/dist/types/overlays/session-selector.d.ts +1 -1
  28. package/dist/types/overlays/usage-dashboard.d.ts +3 -1
  29. package/dist/types/render/output-pane.d.ts +1 -0
  30. package/dist/types/render/render-utils.d.ts +1 -0
  31. package/dist/types/render/tool-card.d.ts +6 -0
  32. package/dist/types/render/width-aware-text.d.ts +2 -0
  33. package/dist/types/status-line/types.d.ts +1 -1
  34. package/dist/types/terminal.d.ts +10 -2
  35. package/dist/types/theme/symbols.d.ts +1 -1
  36. package/dist/types/tools/eval.d.ts +1 -1
  37. package/dist/types/tools/native-view.d.ts +47 -0
  38. package/dist/types/tools/renderer.d.ts +15 -2
  39. package/dist/types/tools/write.d.ts +1 -1
  40. package/dist/types/tools/yield.d.ts +22 -0
  41. package/dist/types/tui.d.ts +31 -0
  42. package/dist/types/vim.d.ts +19 -5
  43. package/package.json +9 -9
  44. package/src/chat/advisor-message.ts +11 -0
  45. package/src/chat/assistant-message.ts +6 -0
  46. package/src/chat/compaction-summary-message.ts +8 -0
  47. package/src/chat/execution-shared.ts +42 -25
  48. package/src/chat/read-tool-group.ts +6 -4
  49. package/src/chat/skill-message.ts +6 -0
  50. package/src/chat/table-chart.ts +17 -20
  51. package/src/chat/tool-execution.ts +27 -2
  52. package/src/chat/user-message.ts +14 -0
  53. package/src/chrome/transcript-container.ts +32 -1
  54. package/src/components/box.ts +5 -0
  55. package/src/components/disclosure.ts +8 -0
  56. package/src/components/editor.ts +182 -5
  57. package/src/components/image.ts +7 -5
  58. package/src/components/key-value-list.ts +4 -0
  59. package/src/components/layout/row.ts +7 -0
  60. package/src/components/layout/stack.ts +7 -0
  61. package/src/components/markdown.ts +8 -0
  62. package/src/components/scroll-view.ts +5 -0
  63. package/src/components/section.ts +5 -0
  64. package/src/components/table.ts +4 -0
  65. package/src/components/text.ts +7 -0
  66. package/src/components/truncated-text.ts +5 -0
  67. package/src/keybinding-matchers.ts +6 -2
  68. package/src/native/backend.ts +16 -15
  69. package/src/native/blobs.ts +43 -10
  70. package/src/native/reconcile.ts +4 -0
  71. package/src/overlays/agent-hub.ts +7 -3
  72. package/src/overlays/annotation-overlay.ts +87 -6
  73. package/src/overlays/annotation-types.ts +2 -0
  74. package/src/overlays/hook-selector.ts +12 -5
  75. package/src/overlays/model-browser.ts +3 -3
  76. package/src/overlays/model-hub.ts +107 -27
  77. package/src/overlays/session-observer-registry.ts +10 -2
  78. package/src/overlays/session-selector.ts +29 -19
  79. package/src/overlays/snapcompact-shape-preview.ts +2 -3
  80. package/src/overlays/usage-dashboard.ts +70 -23
  81. package/src/prompt/attachment-chips.ts +13 -10
  82. package/src/prompt/composer.ts +1 -0
  83. package/src/render/output-pane.ts +6 -0
  84. package/src/render/render-utils.ts +11 -0
  85. package/src/render/tool-card.ts +15 -0
  86. package/src/render/width-aware-text.ts +5 -0
  87. package/src/status-line/segments.ts +3 -0
  88. package/src/status-line/types.ts +1 -1
  89. package/src/terminal.ts +24 -3
  90. package/src/theme/symbols.ts +5 -0
  91. package/src/tools/bash.ts +92 -42
  92. package/src/tools/edit.ts +142 -120
  93. package/src/tools/eval.ts +149 -123
  94. package/src/tools/fetch.ts +5 -6
  95. package/src/tools/index.ts +2 -0
  96. package/src/tools/mcp.ts +15 -2
  97. package/src/tools/native-view.ts +102 -1
  98. package/src/tools/read.ts +7 -5
  99. package/src/tools/renderer.ts +15 -2
  100. package/src/tools/write.ts +70 -56
  101. package/src/tools/yield.ts +225 -0
  102. package/src/tui.ts +93 -0
  103. package/src/vim.ts +333 -18
package/CHANGELOG.md CHANGED
@@ -2,6 +2,50 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [18.8.8] - 2026-10-10
6
+
7
+ ### Breaking Changes
8
+
9
+ - `ModelHubCallbacks.onCompactionPointChange` now receives a `confirmed` flag and returns a `CompactionPointChangeResult` (`{ kind: "error" }` or `{ kind: "confirm" }`) instead of an error string; a `confirm` result shows a warning that a second Enter on the same input accepts ([#15048](https://github.com/can1357/oh-my-pi/pull/15048) by [@H4vC](https://github.com/H4vC))
10
+ - `ModelCompactionPoint.percent` is replaced by `basis`, a short explanation of why the model compacts there (`fixed`, `85% of 400K base`, `80% of window`) that the model hub preview shows next to the trigger ([#15065](https://github.com/can1357/oh-my-pi/pull/15065) by [@H4vC](https://github.com/H4vC))
11
+
12
+ ### Added
13
+
14
+ - Added an `inline` option to `HookSelectorComponent` that keeps a selector in the composer slot in Tern instead of opening it as a sheet over the screen ([#15058](https://github.com/can1357/oh-my-pi/pull/15058) by [@H4vC](https://github.com/H4vC))
15
+ - Added `ModelHubCallbacks.previewCompactionPoint`, whose line the compaction limit field shows while you type instead of the input syntax ([#15065](https://github.com/can1357/oh-my-pi/pull/15065) by [@H4vC](https://github.com/H4vC))
16
+ - Added Vim find and till motions (`f`, `F`, `t`, `T`) in the prompt editor, with `;` and `,` to repeat them ([#15100](https://github.com/can1357/oh-my-pi/pull/15100) by [@Shadorain](https://github.com/Shadorain))
17
+ - Added Vim replace (`r`, `R`) in the prompt editor ([#15100](https://github.com/can1357/oh-my-pi/pull/15100) by [@Shadorain](https://github.com/Shadorain))
18
+ - Added Vim bracket and quote matching (`%`), paragraph motions (`{`, `}`), line join (`J`), and indent (`>>`, `<<`) in the prompt editor ([#15100](https://github.com/can1357/oh-my-pi/pull/15100) by [@Shadorain](https://github.com/Shadorain))
19
+ - Added an external-editor key (Ctrl+G by default) to the annotation overlay for the note draft and, when the host provides `onExternalEditor`, the source being annotated; an edited text source is returned as `editedText` on the paste result ([#15151](https://github.com/can1357/oh-my-pi/pull/15151) by [@Shadorain](https://github.com/Shadorain))
20
+ - Added `TUI.releaseHeldInput()`: on a terminal expected to speak TSP, a `deferInput` start now holds keystrokes (TSP events and the cell-size reply still apply; Ctrl+C/Ctrl+D release early) until the app calls it once its session is wired; such terminals also skip the sixel probe ([#15120](https://github.com/can1357/oh-my-pi/pull/15120) by [@H4vC](https://github.com/H4vC))
21
+ - Added `TUI.replaceHeldFocus(previous, next)`, which `Composer.setEditor()` calls so a swapped-in editor inherits held startup keys; only keys for the start-time focus owner are now held, and a dialog that takes focus gets its input live ([#15122](https://github.com/can1357/oh-my-pi/pull/15122) by [@H4vC](https://github.com/H4vC))
22
+ - Added `RenderResultOptions.elapsedMs` and `cancelled`, `NativeToolHead.command` and `NativeToolView.preview: "children"` for native describe hooks
23
+
24
+ ### Changed
25
+
26
+ - Tern draws each bash run, eval cell and `!`/`$` run as one box: the command or code, its output, status lines and a foot with state, time and facts; images sit below the box and the bash head shows the intent instead of the command
27
+
28
+ ### Fixed
29
+
30
+ - Fixed the Agents hub Time column rewinding and freezing for running subagents between progress updates ([#15140](https://github.com/can1357/oh-my-pi/pull/15140) by [@H4vC](https://github.com/H4vC))
31
+
32
+ ## [18.8.7] - 2026-10-09
33
+
34
+ ### Added
35
+
36
+ - Added cache-release hooks for TUI components and tool cards, allowing extensions to discard derived render data without rebuilding content ([#13632](https://github.com/can1357/oh-my-pi/pull/13632) by [@iliaal](https://github.com/iliaal)).
37
+
38
+ ### Changed
39
+
40
+ - Subagent "Submit Result" cards now show the submitted result (its fields as a tree, or the report text), the section it fills, and why a submission was rejected, instead of only "Result submitted.".
41
+ - Reduced memory held by retired transcript history in long sessions ([#13632](https://github.com/can1357/oh-my-pi/pull/13632) by [@iliaal](https://github.com/iliaal)).
42
+
43
+ ### Fixed
44
+
45
+ - Reduced memory retained by discarded TSP images and previews ([#14336](https://github.com/can1357/oh-my-pi/pull/14336) by [@iliaal](https://github.com/iliaal)).
46
+ - Fixed startup capability probes printing as text in the prompt (e.g. `25a1;stsp;q;{…}pppppp`) on terminals that cannot parse them, such as macOS Terminal.app.
47
+ - Fixed the `/resume` picker flashing while a search runs over a large session history: background fuzzy matches now land in one update instead of reordering the list dozens of times per keystroke.
48
+
5
49
  ## [18.8.6] - 2026-10-08
6
50
 
7
51
  ### Changed
@@ -2960,82 +3004,4 @@ Initial release under @oh-my-pi scope. See previous releases at [badlogic/pi-mon
2960
3004
 
2961
3005
  - Decode Kitty CSI-u printable sequences in the editor so shifted symbol keys (e.g., `@`, `?`) work in terminals that enable Kitty keyboard protocol ([#779](https://github.com/badlogic/pi-mono/pull/779) by [@iamd3vil](https://github.com/iamd3vil))
2962
3006
 
2963
- ## [0.47.0] - 2026-01-16
2964
-
2965
- ### Breaking Changes
2966
-
2967
- - `Editor` constructor now requires `TUI` as first parameter: `new Editor(tui, theme)`. This enables automatic vertical scrolling when content exceeds terminal height. ([#732](https://github.com/badlogic/pi-mono/issues/732))
2968
-
2969
- ### Added
2970
-
2971
- - Hardware cursor positioning for IME support in `Editor` and `Input` components. The terminal cursor now follows the text cursor position, enabling proper IME candidate window placement for CJK input. ([#719](https://github.com/badlogic/pi-mono/pull/719))
2972
- - `Focusable` interface for components that need hardware cursor positioning. Implement `focused: boolean` and emit `CURSOR_MARKER` in render output when focused.
2973
- - `CURSOR_MARKER` constant and `isFocusable()` type guard exported from the package
2974
- - Editor now supports Page Up/Down keys (Fn+Up/Down on MacBook) for scrolling through large content ([#732](https://github.com/badlogic/pi-mono/issues/732))
2975
- - Expanded keymap coverage for terminal compatibility: added support for Home/End keys in tmux, additional modifier combinations, and improved key sequence parsing ([#752](https://github.com/badlogic/pi-mono/pull/752) by [@richardgill](https://github.com/richardgill))
2976
-
2977
- ### Fixed
2978
-
2979
- - Editor no longer corrupts terminal display when text exceeds screen height. Content now scrolls vertically with indicators showing lines above/below the viewport. Max height is 30% of terminal (minimum 5 lines). ([#732](https://github.com/badlogic/pi-mono/issues/732))
2980
- - `visibleWidth()` and `extractAnsiCode()` now handle APC escape sequences (`ESC _ ... BEL`), fixing width calculation and string slicing for strings containing cursor markers
2981
- - SelectList now handles multi-line descriptions by replacing newlines with spaces ([#728](https://github.com/badlogic/pi-mono/pull/728) by [@richardgill](https://github.com/richardgill))
2982
-
2983
- ## [0.46.0] - 2026-01-15
2984
-
2985
- ### Fixed
2986
-
2987
- - Keyboard shortcuts (Ctrl+C, Ctrl+D, etc.) now work on non-Latin keyboard layouts (Russian, Ukrainian, Bulgarian, etc.) in terminals supporting Kitty keyboard protocol with alternate key reporting ([#718](https://github.com/badlogic/pi-mono/pull/718) by [@dannote](https://github.com/dannote))
2988
-
2989
- ## [0.45.6] - 2026-01-13
2990
-
2991
- ### Added
2992
-
2993
- - `OverlayOptions` API for overlay positioning and sizing with CSS-like values: `width`, `maxHeight`, `row`, `col` accept numbers (absolute) or percentage strings (e.g., `"50%"`). Also supports `minWidth`, `anchor`, `offsetX`, `offsetY`, `margin`. ([#667](https://github.com/badlogic/pi-mono/pull/667) by [@nicobailon](https://github.com/nicobailon))
2994
- - `OverlayOptions.visible` callback for responsive overlays - receives terminal dimensions, return false to hide ([#667](https://github.com/badlogic/pi-mono/pull/667) by [@nicobailon](https://github.com/nicobailon))
2995
- - `showOverlay()` now returns `OverlayHandle` with `hide()`, `setHidden(boolean)`, `isHidden()` for programmatic visibility control ([#667](https://github.com/badlogic/pi-mono/pull/667) by [@nicobailon](https://github.com/nicobailon))
2996
- - New exported types: `OverlayAnchor`, `OverlayHandle`, `OverlayMargin`, `OverlayOptions`, `SizeValue` ([#667](https://github.com/badlogic/pi-mono/pull/667) by [@nicobailon](https://github.com/nicobailon))
2997
- - `truncateToWidth()` now accepts optional `pad` parameter to pad result with spaces to exactly `maxWidth` ([#667](https://github.com/badlogic/pi-mono/pull/667) by [@nicobailon](https://github.com/nicobailon))
2998
-
2999
- ### Fixed
3000
-
3001
- - Overlay compositing crash when rendered lines exceed terminal width due to complex ANSI/OSC sequences (e.g., hyperlinks in subagent output) ([#667](https://github.com/badlogic/pi-mono/pull/667) by [@nicobailon](https://github.com/nicobailon))
3002
-
3003
- ## [0.44.0] - 2026-01-12
3004
-
3005
- ### Added
3006
-
3007
- - `SettingsListOptions` with `enableSearch` for fuzzy filtering in `SettingsList` ([#643](https://github.com/badlogic/pi-mono/pull/643) by [@ninlds](https://github.com/ninlds))
3008
- - `pageUp` and `pageDown` key support with `selectPageUp`/`selectPageDown` editor actions ([#662](https://github.com/badlogic/pi-mono/pull/662) by [@aliou](https://github.com/aliou))
3009
-
3010
- ### Fixed
3011
-
3012
- - Numbered list items showing "1." for all items when code blocks break list continuity ([#660](https://github.com/badlogic/pi-mono/pull/660) by [@ogulcancelik](https://github.com/ogulcancelik))
3013
-
3014
- ## [0.43.0] - 2026-01-11
3015
-
3016
- ### Added
3017
-
3018
- - `fuzzyFilter()` and `fuzzyMatch()` utilities for fuzzy text matching
3019
- - Slash command autocomplete now uses fuzzy matching instead of prefix matching
3020
-
3021
- ### Fixed
3022
-
3023
- - Cursor now moves to end of content on exit, preventing status line from being overwritten ([#629](https://github.com/badlogic/pi-mono/pull/629) by [@tallshort](https://github.com/tallshort))
3024
- - Reset ANSI styles after each rendered line to prevent style leakage
3025
-
3026
- ## [0.42.5] - 2026-01-11
3027
-
3028
- ### Fixed
3029
-
3030
- - Reduced flicker by only re-rendering changed lines ([#617](https://github.com/badlogic/pi-mono/pull/617) by [@ogulcancelik](https://github.com/ogulcancelik))
3031
- - Cursor position tracking when content shrinks with unchanged remaining lines
3032
- - TUI renders with wrong dimensions after suspend/resume if terminal was resized while suspended ([#599](https://github.com/badlogic/pi-mono/issues/599))
3033
- - Pasted content containing Kitty key release patterns (e.g., `:3F` in MAC addresses) was incorrectly filtered out ([#623](https://github.com/badlogic/pi-mono/pull/623) by [@ogulcancelik](https://github.com/ogulcancelik))
3034
-
3035
- ## [0.39.0] - 2026-01-08
3036
-
3037
- ### Added
3038
-
3039
- - **Experimental:** Overlay compositing for `ctx.ui.custom()` with `{ overlay: true }` option ([#558](https://github.com/badlogic/pi-mono/pull/558) by [@nicobailon](https://github.com/nicobailon))
3040
-
3041
- Older entries are archived in [packages/tui/CHANGELOG.md@fa14205f838f](https://github.com/can1357/oh-my-pi/blob/fa14205f838f282fcea048c64fca74026789a492/packages/tui/CHANGELOG.md).
3007
+ Older entries are archived in [packages/tui/CHANGELOG.md@01e0b5c26e26](https://github.com/can1357/oh-my-pi/blob/01e0b5c26e267dff08c305f51d1ab56ec5a2f05d/packages/tui/CHANGELOG.md).
package/README.md CHANGED
@@ -66,14 +66,16 @@ interface Component {
66
66
  render(width: number): readonly string[];
67
67
  handleInput?(data: string): void;
68
68
  invalidate?(): void;
69
+ releaseRenderCaches?(): void;
69
70
  }
70
71
  ```
71
72
 
72
- | Method | Description |
73
- | -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
74
- | `render(width)` | Returns an array of strings, one per line. Each line **must not exceed `width`** or the TUI will error. Use `truncateToWidth()` or manual wrapping to ensure this. The result is component-owned and immutable to callers; return the same array reference when unchanged (enables renderer memoization) and a new array when content changed. |
75
- | `handleInput?(data)` | Called when the component has focus and receives keyboard input. The `data` string contains raw terminal input (may include ANSI escape sequences). |
76
- | `invalidate?()` | Called to clear any cached render state. Components should re-render from scratch on the next `render()` call. |
73
+ | Method | Description |
74
+ | ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
75
+ | `render(width)` | Returns an array of strings, one per line. Each line **must not exceed `width`** or the TUI will error. Use `truncateToWidth()` or manual wrapping to ensure this. The result is component-owned and immutable to callers; return the same array reference when unchanged (enables renderer memoization) and a new array when content changed. |
76
+ | `handleInput?(data)` | Called when the component has focus and receives keyboard input. The `data` string contains raw terminal input (may include ANSI escape sequences). |
77
+ | `invalidate?()` | Called to clear any cached render state. Components should re-render from scratch on the next `render()` call. |
78
+ | `releaseRenderCaches?()` | Drops only derived render caches. The next `render()` must return the same rows without eager rebuilds, renderer callbacks, image conversions, or child replacement. |
77
79
 
78
80
  ## Built-in Components
79
81
 
@@ -54,6 +54,7 @@ export declare class AssistantMessageComponent extends Container {
54
54
  */
55
55
  setServedModelMismatch(info: ServedModelMismatch | undefined): void;
56
56
  invalidate(): void;
57
+ releaseRenderCaches(): void;
57
58
  setHideThinkingBlock(hide: boolean): void;
58
59
  /**
59
60
  * Allow or withhold retiring finished lines into native scrollback while the
@@ -31,6 +31,7 @@ declare class SummaryMessageComponent implements Component {
31
31
  handleNativeEvent(event: NativeUiEvent): void;
32
32
  setIgnoreTight(ignore: boolean): this;
33
33
  invalidate(): void;
34
+ releaseRenderCaches(): void;
34
35
  dispose(): void;
35
36
  render(width: number): readonly string[];
36
37
  }
@@ -78,7 +78,7 @@ export interface ExecutionToolInput {
78
78
  /** Tool name for the head icon (`bash`, `eval`). */
79
79
  readonly name: string;
80
80
  readonly title: string;
81
- /** The command / cell source, shown once in the head. */
81
+ /** The command / cell source: the run box's input and the head's Copy command. */
82
82
  readonly command: string;
83
83
  readonly lang: string;
84
84
  /** Excluded from context (`!!`/`$$`). */
@@ -96,10 +96,11 @@ export interface ExecutionToolInput {
96
96
  readonly artifactError?: OutputArtifactError;
97
97
  }
98
98
  /**
99
- * A user `!`/`$` run as the agent's `tool` frame (§6.6): the command in the
100
- * head with a `you` badge (plus `not sent` when excluded from context), the
101
- * timer, exit chip and cancelled note; the body is the output as an `ansi`
102
- * mini terminal following its tail, then one quiet truncation line.
99
+ * A user `!`/`$` run as the agent's `tool` frame (§6.6): a head with a `you`
100
+ * badge (plus `not sent` when excluded from context) and the command for Copy
101
+ * command; the body is one run box — the source, the output as an `ansi` mini
102
+ * terminal following its tail, and the foot (state, time, truncation) — with
103
+ * any images below it. Timer, exit chip and cancelled note stay head data.
103
104
  */
104
105
  export declare function describeExecutionTool(input: ExecutionToolInput): NativeNode;
105
106
  /**
@@ -33,6 +33,7 @@ export declare function userBubbleColor(options?: UserBubbleOptions, tokenRegex?
33
33
  export declare class UserMessageComponent extends Container implements ReactionTarget {
34
34
  #private;
35
35
  constructor(text: string, options?: UserBubbleOptions);
36
+ releaseRenderCaches(): void;
36
37
  setReaction(emoji: string): void;
37
38
  /**
38
39
  * A head-less user-toned frame with the prompt as markdown under its
@@ -75,6 +76,7 @@ export declare class CollapsedSyntheticMessageComponent implements Component {
75
76
  handleNativeEvent(event: NativeUiEvent): void;
76
77
  setIgnoreTight(ignore: boolean): this;
77
78
  invalidate(): void;
79
+ releaseRenderCaches(): void;
78
80
  dispose(): void;
79
81
  render(width: number): readonly string[];
80
82
  }
@@ -30,6 +30,7 @@ export declare class Box implements Component {
30
30
  setBgFn(bgFn?: (text: string) => string): void;
31
31
  setBorder(border?: BoxBorder): void;
32
32
  invalidate(): void;
33
+ releaseRenderCaches(): void;
33
34
  /**
34
35
  * A `card` when the box has a border or background: the background token
35
36
  * picks tone/role (user message, tool state, selection), a border colour
@@ -36,6 +36,7 @@ export declare class Disclosure implements Component {
36
36
  setExpanded(expanded: boolean): void;
37
37
  setIgnoreTight(ignore: boolean): this;
38
38
  invalidate(): void;
39
+ releaseRenderCaches(): void;
39
40
  dispose(): void;
40
41
  debugState(): Record<string, unknown>;
41
42
  get debugChildren(): readonly Component[];
@@ -227,6 +227,7 @@ export declare class Image implements Component {
227
227
  /** Return source metadata without exposing the encoded image buffer. */
228
228
  debugState(): Record<string, unknown>;
229
229
  invalidate(): void;
230
+ releaseRenderCaches(): void;
230
231
  /**
231
232
  * A native `image` backed by a content-addressed blob; the terminal fits
232
233
  * it. Cell caps become `ch`/`lines` bounds. The inline-image budget and
@@ -21,6 +21,7 @@ export declare class KeyValueList implements Component {
21
21
  constructor(rows: readonly KeyValueRow[], options: KeyValueListOptions);
22
22
  setRows(rows: readonly KeyValueRow[]): boolean;
23
23
  invalidate(): void;
24
+ releaseRenderCaches(): void;
24
25
  /** An aligned `kv` grid; label width and value wrapping are the terminal's. */
25
26
  describe(_cx: DescribeContext): NativeNode;
26
27
  render(width: number): readonly string[];
@@ -48,6 +48,7 @@ export declare class Row implements Component, MouseRoutable {
48
48
  setDecorations(prefix: LayoutDecoration | undefined, gap: LayoutDecoration | undefined, suffix: LayoutDecoration | undefined): void;
49
49
  setIgnoreTight(ignore: boolean): this;
50
50
  invalidate(): void;
51
+ releaseRenderCaches(): void;
51
52
  dispose(): void;
52
53
  /**
53
54
  * A flex `row`: each child sits in a slot carrying its width bounds
@@ -43,6 +43,7 @@ export declare class Stack implements Component, MouseRoutable {
43
43
  setHeight(height: number | undefined): void;
44
44
  setIgnoreTight(ignore: boolean): this;
45
45
  invalidate(): void;
46
+ releaseRenderCaches(): void;
46
47
  dispose(): void;
47
48
  /**
48
49
  * A `col` of slots carrying each child's height bounds (fixed → min = max
@@ -124,6 +124,7 @@ export declare class Markdown implements Component {
124
124
  debugState(): Record<string, unknown>;
125
125
  setText(text: string): boolean;
126
126
  invalidate(): void;
127
+ releaseRenderCaches(): void;
127
128
  /**
128
129
  * The unrendered source as an `md` node, `stream` while the streaming
129
130
  * (transient) cache is on. Append-only growth yields a fresh node of the
@@ -92,6 +92,7 @@ export declare class ScrollView implements Component {
92
92
  /** Apply Arrow, Shift+Arrow, Page, Home, and End navigation keys. */
93
93
  handleScrollKey(data: string): boolean;
94
94
  invalidate(): void;
95
+ releaseRenderCaches(): void;
95
96
  /** Permanently release a wrapped child's timers, subscriptions, and other resources. */
96
97
  dispose(): void;
97
98
  /**
@@ -15,6 +15,7 @@ export declare class Section implements Component {
15
15
  #private;
16
16
  constructor(options: SectionOptions);
17
17
  invalidate(): void;
18
+ releaseRenderCaches(): void;
18
19
  dispose(): void;
19
20
  /** A `section` headed by the styled title; the rule is the terminal's. */
20
21
  describe(_cx: DescribeContext): NativeNode;
@@ -29,6 +29,7 @@ export declare class Table implements Component {
29
29
  constructor(rows: readonly (readonly TableCell[])[], columns: readonly TableColumn[], options?: TableOptions);
30
30
  setRows(rows: readonly (readonly TableCell[])[]): boolean;
31
31
  invalidate(): void;
32
+ releaseRenderCaches(): void;
32
33
  /**
33
34
  * A native `table`. Column priorities carry over unchanged (lower gives
34
35
  * way first); truncating columns truncate at the end, the rest keep
@@ -26,6 +26,7 @@ export declare class Text implements Component {
26
26
  */
27
27
  setStyleFn(styleFn?: (text: string) => string): this;
28
28
  invalidate(): void;
29
+ releaseRenderCaches(): void;
29
30
  /**
30
31
  * Wrapped text; styling re-derived from the styled source so a theme swap
31
32
  * re-resolves tokens. A background fill becomes the node's tone.
@@ -9,6 +9,7 @@ export declare class TruncatedText implements Component {
9
9
  /** Return bounded source text and the last-known truncation state. */
10
10
  debugState(): Record<string, unknown>;
11
11
  invalidate(): void;
12
+ releaseRenderCaches(): void;
12
13
  /** First line only, clamped to one visual line with an end ellipsis. */
13
14
  describe(_cx: DescribeContext): NativeNode;
14
15
  render(width: number): readonly string[];
@@ -1,3 +1,4 @@
1
+ import type { KeyId } from "./app-keybindings.js";
1
2
  /**
2
3
  * Match the coding-agent interrupt key.
3
4
  *
@@ -18,6 +19,8 @@ export declare function matchesSelectDown(data: string): boolean;
18
19
  export declare function matchesSelectPageUp(data: string): boolean;
19
20
  /** Match the generic selector page-down keybinding. */
20
21
  export declare function matchesSelectPageDown(data: string): boolean;
22
+ /** Effective external-editor key: the first bound key, or Ctrl+G when unbound. */
23
+ export declare function appExternalEditorKey(): KeyId;
21
24
  /** Match the external-editor keybinding, falling back to Ctrl+G when unbound. */
22
25
  export declare function matchesAppExternalEditor(data: string): boolean;
23
26
  /**
@@ -14,10 +14,12 @@ export interface NativeBlob {
14
14
  readonly mime: string;
15
15
  readonly bytes: Uint8Array;
16
16
  }
17
- /** Register `bytes` and return their content address (sha256 hex) for `image.p.blob`. */
17
+ /** Register bytes for process-lifetime, id-only use. Prefer {@link nativeImageNode} for owned images. */
18
18
  export declare function registerNativeBlob(bytes: Uint8Array, mime: string): string;
19
19
  /** A registered blob by id. */
20
20
  export declare function getNativeBlob(id: string): NativeBlob | undefined;
21
+ /** An image whose props own its bytes, including while hidden, unmounted, or awaiting upload. */
22
+ export declare function nativeImageNode(bytes: Uint8Array, mime: string, p?: Omit<TspProps<"image">, "blob" | "builtin">, key?: string): NativeNode;
21
23
  /**
22
24
  * An `image` node for a base64 payload: the bytes are registered as a blob and
23
25
  * the pixel size probed from the header. Decoding and hashing cost real time,
@@ -10,6 +10,8 @@ export interface AnnotationOverlayCallbacks {
10
10
  onWarning?(message: string): void;
11
11
  /** Open the draft in the host's editor and invoke `commit` with its result. */
12
12
  onAnnotationExternalEditor?(draft: string, commit: (text: string | null) => void): void | Promise<void>;
13
+ /** Open the current source in the host's editor. The overlay stays open. */
14
+ onExternalEditor?(): void | Promise<void>;
13
15
  }
14
16
  export interface CodeReviewOverlayCallbacks extends AnnotationOverlayCallbacks {
15
17
  onComplete(result: CodeReviewOverlayResult | undefined): void;
@@ -29,6 +31,16 @@ export declare class AnnotationOverlay implements Focusable {
29
31
  dispose(): void;
30
32
  getAnnotations(): CodeReviewAnnotation[];
31
33
  getTextAnnotations(): TextReviewAnnotation[];
34
+ /** Repo-relative path of the diff file under the cursor, if this is a code review. */
35
+ reviewFilePath(): string | undefined;
36
+ /** Current text-source bytes, including edits applied by {@link replaceTextSource}. */
37
+ textSourceText(): string | undefined;
38
+ /**
39
+ * Replace the text under review. Line notes whose quote still sits on the
40
+ * same row stay; a quote that moved to exactly one row is retargeted;
41
+ * every other line note is dropped. Returns how many were dropped.
42
+ */
43
+ replaceTextSource(text: string): number;
32
44
  handleInput(data: string): void;
33
45
  describe(): NativeNode;
34
46
  handleNativeEvent(event: NativeUiEvent): void;
@@ -81,4 +81,6 @@ export type TextReviewAnnotation = {
81
81
  export interface TextReviewOverlayResult {
82
82
  action: "paste";
83
83
  annotations: TextReviewAnnotation[];
84
+ /** Source text after an external-editor edit; absent when the source was not edited. */
85
+ editedText?: string;
84
86
  }
@@ -57,6 +57,10 @@ export interface HookSelectorOptions {
57
57
  /** Number of leading options (original order) that receive a selection
58
58
  * marker. Defaults to every option when {@link selectionMarker} is set. */
59
59
  markableCount?: number;
60
+ /** Describe as a card in the dock (the composer slot) instead of a picker
61
+ * sheet, so dock content above it (e.g. a `/omfg` candidate rule) stays
62
+ * visible and the transcript stays scrollable on native surfaces. */
63
+ inline?: boolean;
60
64
  }
61
65
  export interface HookSelectorOption {
62
66
  label: string;
@@ -70,10 +74,11 @@ export declare class HookSelectorComponent extends OverlayPanel {
70
74
  render(width: number): readonly string[];
71
75
  dispose(): void;
72
76
  /**
73
- * A plain option list is a picker sheet. Otherwise a card headed by the
74
- * title (plus the countdown `elapsed` when a timeout runs), the extra title
75
- * lines, the slider as `tabs`, the options as a `list` keyed by original
76
- * option index, the search query while typing, and the key hints.
77
+ * A plain option list is a picker sheet. Otherwise (inline, slider, or
78
+ * countdown) a card headed by the title (plus the countdown `elapsed` when a
79
+ * timeout runs), the extra title lines, the slider as `tabs`, the options as
80
+ * a `list` keyed by original option index, the search query while typing,
81
+ * and the key hints.
77
82
  */
78
83
  describe(cx: DescribeContext): NativeNode;
79
84
  /** Pointer pick of an option does what highlighting it and pressing Enter does; a tab pick moves the slider. */
@@ -41,8 +41,8 @@ export interface ResolvedModelRoleValue {
41
41
  export interface ModelCompactionPoint {
42
42
  /** Context tokens that trigger auto-compaction; undefined when auto-compaction is off or the window is unknown. */
43
43
  tokens: number | undefined;
44
- /** The configured percentage of the window, when the trigger is percent-based. */
45
- percent?: number;
44
+ /** Why it triggers there, in a few words: `fixed`, `85% of 400K base`, `80% of window`. */
45
+ basis?: string;
46
46
  /** What sets it: the matching `compaction.modelThresholds` key, `global`, or `default`. */
47
47
  source: string;
48
48
  /** The model's own exact entry as editable text (`90k`, `80%`); absent when it has none. */
@@ -65,13 +65,30 @@ export interface ModelHubCallbacks {
65
65
  /** Persist a new quick-switch cycle order (the ctrl+p role cycle). */
66
66
  onCycleOrderChange?: (order: string[]) => void;
67
67
  /**
68
- * Persist typed text as `model`'s own compaction point: a token count (`90000`,
69
- * `90k`, `1M`), a percentage (`80%`), or empty to reset. Returns an error
70
- * message for input it rejects, which keeps the field open.
68
+ * Persist typed text as `model`'s own compaction limit: a token base the
69
+ * policy scales (`400k`), a fixed trigger (`f400k`), a percentage (`80%`), or
70
+ * empty to reset. Rejected input returns `{ kind: "error" }`; input that
71
+ * needs acknowledgement (one that opens the extended window) returns
72
+ * `{ kind: "confirm" }` until called again with the same text and
73
+ * `confirmed`. Either keeps the field open.
71
74
  */
72
- onCompactionPointChange?: (model: Model, input: string) => string | undefined;
75
+ onCompactionPointChange?: (model: Model, input: string, confirmed: boolean) => CompactionPointChangeResult | undefined;
76
+ /**
77
+ * One short line on where `model` would compact with the typed limit
78
+ * (`compacts at 340K · 85% of 400K base`), shown beside the field while
79
+ * typing; undefined for input that does not parse or fit.
80
+ */
81
+ previewCompactionPoint?: (model: Model, input: string) => string | undefined;
73
82
  onCancel: () => void;
74
83
  }
84
+ /** Why {@link ModelHubCallbacks.onCompactionPointChange} kept the field open. */
85
+ export type CompactionPointChangeResult = {
86
+ kind: "error";
87
+ message: string;
88
+ } | {
89
+ kind: "confirm";
90
+ message: string;
91
+ };
75
92
  export interface ModelHubOptions {
76
93
  /** Preselect this provider's sidebar entry (e.g. when reopening after /login). */
77
94
  initialProviderId?: string;
@@ -58,6 +58,8 @@ export interface ObservableSession {
58
58
  lastUpdate: number;
59
59
  /** Latest progress snapshot from the subagent executor */
60
60
  progress?: AgentProgress;
61
+ /** `Date.now()` when {@link progress} arrived; its `durationMs` was current then. */
62
+ progressAt?: number;
61
63
  }
62
64
  /** Coarse source of an observer change; callers use it to separate lifecycle work from high-frequency progress. */
63
65
  export type SessionObserverChangeKind = "main" | "reset" | "lifecycle" | "progress";
@@ -71,7 +71,7 @@ declare class SessionList<T extends SessionSelectorEntry> implements Component {
71
71
  onExit: () => void;
72
72
  onToggleScope?: () => void;
73
73
  onDeleteRequest?: (session: T) => void;
74
- /** Re-render hook for async list updates (fuzzy scan chunks, history merge). */
74
+ /** Re-render hook for async list updates (fuzzy scan completion, history merge). */
75
75
  onRequestRender?: () => void;
76
76
  constructor(sessions: T[], showCwd?: boolean, historyMatcher?: SessionHistoryMatcher, getTerminalRows?: () => number, pinnedIds?: ReadonlySet<string>, currentSessionPath?: string | (() => string | undefined));
77
77
  /** Replace the visible dataset, e.g. when toggling folder/all-projects scope. */
@@ -35,8 +35,10 @@ export interface ProviderCard {
35
35
  unavailableAccounts: string[];
36
36
  /** Window rows in provider-declared order (e.g. 5h → weekly → monthly); the fullest CARD_MAX_WINDOWS lead. */
37
37
  windows: CardWindowRow[];
38
- /** True when every account reports no limits (e.g. enterprise plans). */
38
+ /** True when every account reports no limits (e.g. enterprise plans, providers without a quota API). */
39
39
  unlimited: boolean;
40
+ /** Distinct report notes across accounts (e.g. why a provider exposes no quota windows). */
41
+ notes: string[];
40
42
  /** True when nothing is used anywhere (or there are no limits): collapses to a tick. */
41
43
  idle: boolean;
42
44
  resetCredits?: {
@@ -92,6 +92,7 @@ export declare class OutputPane implements Component {
92
92
  describe(_cx: DescribeContext): NativeNode;
93
93
  render(width: number): readonly string[];
94
94
  invalidate(): void;
95
+ releaseRenderCaches(): void;
95
96
  }
96
97
  /** Default styling for plain tool output while preserving existing ANSI. */
97
98
  export declare function styleToolOutputLine(line: string, theme: Theme): string;
@@ -298,6 +298,7 @@ export interface RenderedStringCache {
298
298
  export declare function createRenderedStringCache(): RenderedStringCache;
299
299
  /** Drop the memo so the next lookup re-renders (e.g. the render function identity changed). */
300
300
  export declare function invalidateRenderedStringCache(cache: RenderedStringCache): void;
301
+ export declare function releaseRenderedStringCache(cache: RenderedStringCache): void;
301
302
  /** Reuse a rendered string while its theme, expansion, content, and salt match. */
302
303
  export declare function cachedRenderedString(cache: RenderedStringCache | undefined, theme: Theme, expanded: boolean, salt: string, content: string, render: () => string): string;
303
304
  /**
@@ -46,6 +46,11 @@ export interface ToolCardOptions {
46
46
  paddingY?: number;
47
47
  ignoreTight?: boolean;
48
48
  onInvalidate?: () => void;
49
+ /**
50
+ * Drop builder-owned memos the next build recomputes. Called by
51
+ * {@link ToolCard.releaseRenderCaches}, which never calls `onInvalidate`.
52
+ */
53
+ onReleaseRenderCaches?: () => void;
49
54
  onDispose?: () => void;
50
55
  }
51
56
  /** Map transcript lifecycle terminology onto the output frame's visual states. */
@@ -71,6 +76,7 @@ export declare class ToolCard implements Component {
71
76
  handleInput(data: string): void;
72
77
  setIgnoreTight(ignore: boolean): void;
73
78
  invalidate(): void;
79
+ releaseRenderCaches(): void;
74
80
  dispose(): void;
75
81
  }
76
82
  /** Construct a lazy framed tool card. */
@@ -20,6 +20,8 @@ export declare class WidthAwareText implements Component {
20
20
  setCustomBgFn(customBgFn?: (text: string) => string): void;
21
21
  setIgnoreTight(ignore: boolean): this;
22
22
  invalidate(): void;
23
+ /** The formatted text stays: the inner `Text` renders from it, so dropping it would free nothing. */
24
+ releaseRenderCaches(): void;
23
25
  /**
24
26
  * Re-run the formatter on the next render because its inputs changed.
25
27
  * Unlike {@link invalidate}, the inner `Text` keeps its wrap cache, so a
@@ -112,7 +112,7 @@ export interface SegmentContext {
112
112
  } | null;
113
113
  /** Modal editing state, or null when `tui.vimMode` is off. */
114
114
  vim: {
115
- mode: "insert" | "normal" | "visual" | "visual-line";
115
+ mode: "insert" | "normal" | "visual" | "visual-line" | "replace";
116
116
  /** Half-typed operator/count (`"2d"`), empty when nothing is pending. */
117
117
  pending: string;
118
118
  /** Lines spanned by the active Visual selection; 0 outside Visual modes. */
@@ -128,13 +128,21 @@ export type TspHelloHandler = (hello: TspHello | null) => void;
128
128
  export type CursorShape = "default" | "block" | "underline" | "bar";
129
129
  export declare const CURSOR_SHAPE_CODES: Record<CursorShape, number>;
130
130
  export interface Terminal {
131
+ /**
132
+ * Start the terminal with input, resize, and host-disconnect handlers.
133
+ * Unless input is deferred, this sends capability probes; a terminal that
134
+ * cannot parse one may leave concealed bytes on the cursor row, so the
135
+ * caller repaints after starting.
136
+ */
131
137
  start(onInput: (data: string) => void, onResize: () => void, onDisconnect?: () => void, options?: TerminalStartOptions): void;
132
138
  /**
133
139
  * Take ownership of stdin after a `deferInput` start: enable raw mode,
134
140
  * attach input handlers, and run the capability probes start() skipped.
135
141
  * Bytes the user typed in cooked mode meanwhile are replayed through
136
- * `onInput`. No-op when input was never deferred. Optional so custom
137
- * Terminals built against older pi-tui versions keep working.
142
+ * `onInput`. No-op when input was never deferred. Like {@link start}, the
143
+ * probes may leave concealed bytes on the cursor row, so the caller
144
+ * repaints afterwards. Optional so custom Terminals built against older
145
+ * pi-tui versions keep working.
138
146
  */
139
147
  enableInput?(): void;
140
148
  stop(): void;