@oh-my-pi/pi-tui 18.13.2 → 18.14.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 (245) hide show
  1. package/CHANGELOG.md +40 -54
  2. package/README.md +34 -34
  3. package/dist/types/app-keybindings.d.ts +15 -0
  4. package/dist/types/apps/session-picker.d.ts +5 -0
  5. package/dist/types/charts/chart-plan.d.ts +9 -7
  6. package/dist/types/chat/assistant-message.d.ts +15 -3
  7. package/dist/types/chat/bash-execution.d.ts +2 -0
  8. package/dist/types/chat/display-preferences.d.ts +2 -0
  9. package/dist/types/chat/extension-types.d.ts +31 -0
  10. package/dist/types/chat/reply-write.d.ts +48 -0
  11. package/dist/types/chat/table-chart.d.ts +4 -3
  12. package/dist/types/chat/tool-execution.d.ts +1 -2
  13. package/dist/types/chat/transcript-outline.d.ts +7 -0
  14. package/dist/types/chat/transcript-render-helpers.d.ts +2 -0
  15. package/dist/types/chrome/index.d.ts +1 -0
  16. package/dist/types/chrome/transcript-notice.d.ts +35 -0
  17. package/dist/types/chrome/transcript-status.d.ts +20 -8
  18. package/dist/types/components/editor.d.ts +8 -0
  19. package/dist/types/components/markdown.d.ts +7 -0
  20. package/dist/types/native/blobs.d.ts +2 -1
  21. package/dist/types/native/picker.d.ts +3 -3
  22. package/dist/types/overlays/annotation-overlay.d.ts +7 -1
  23. package/dist/types/overlays/annotation-types.d.ts +28 -0
  24. package/dist/types/overlays/login-dialog.d.ts +5 -1
  25. package/dist/types/overlays/model-browser.d.ts +10 -1
  26. package/dist/types/overlays/oauth-selector.d.ts +23 -0
  27. package/dist/types/overlays/plan-review-overlay.d.ts +2 -0
  28. package/dist/types/overlays/rewind-selector.d.ts +1 -1
  29. package/dist/types/overlays/session-selector.d.ts +22 -2
  30. package/dist/types/prompt/composer.d.ts +41 -4
  31. package/dist/types/prompt/interactive-context-helpers.d.ts +10 -3
  32. package/dist/types/prompt/welcome.d.ts +6 -2
  33. package/dist/types/render/extension-links.d.ts +65 -0
  34. package/dist/types/render/hyperlink.d.ts +25 -7
  35. package/dist/types/render/index.d.ts +1 -0
  36. package/dist/types/setup/scenes/model.d.ts +1 -1
  37. package/dist/types/setup/scenes/outro.d.ts +5 -4
  38. package/dist/types/setup/scenes/sign-in.d.ts +13 -8
  39. package/dist/types/setup/scenes/splash.d.ts +4 -4
  40. package/dist/types/setup/scenes/types.d.ts +16 -1
  41. package/dist/types/setup/startup-splash.d.ts +1 -1
  42. package/dist/types/setup/wizard-overlay.d.ts +11 -7
  43. package/dist/types/setup/wizard.d.ts +5 -1
  44. package/dist/types/status-line/component.d.ts +1 -1
  45. package/dist/types/status-line/context-usage.d.ts +28 -6
  46. package/dist/types/status-line/host.d.ts +6 -2
  47. package/dist/types/theme/theme.d.ts +0 -23
  48. package/dist/types/tools/bash-interactive.d.ts +6 -0
  49. package/dist/types/tools/bash.d.ts +2 -0
  50. package/dist/types/tools/default-renderer.d.ts +6 -4
  51. package/dist/types/tools/irc.d.ts +6 -0
  52. package/dist/types/tools/json-tree.d.ts +23 -4
  53. package/dist/types/tools/lsp.d.ts +2 -0
  54. package/dist/types/tools/mcp.d.ts +6 -5
  55. package/dist/types/tools/native-view.d.ts +1 -1
  56. package/dist/types/tools/renderer.d.ts +2 -0
  57. package/dist/types/tools/streaming-output.d.ts +11 -0
  58. package/dist/types/tools/task.d.ts +9 -1
  59. package/dist/types/tools/url-scheme-host.d.ts +17 -0
  60. package/dist/types/tools/yield.d.ts +1 -1
  61. package/dist/types/tui.d.ts +7 -2
  62. package/package.json +10 -15
  63. package/src/app-keybindings.ts +15 -0
  64. package/src/apps/session-picker.ts +7 -0
  65. package/src/charts/chart-plan.ts +20 -50
  66. package/src/chat/advisor-message.ts +2 -0
  67. package/src/chat/assistant-message.ts +137 -19
  68. package/src/chat/background-tan-message.ts +4 -0
  69. package/src/chat/bash-execution.ts +6 -0
  70. package/src/chat/chat-transcript-builder.ts +58 -28
  71. package/src/chat/compaction-summary-message.ts +2 -1
  72. package/src/chat/display-preferences.ts +3 -0
  73. package/src/chat/extension-types.ts +34 -0
  74. package/src/chat/reply-write.ts +127 -0
  75. package/src/chat/table-chart.ts +10 -4
  76. package/src/chat/tool-execution.ts +20 -13
  77. package/src/chat/transcript-outline.ts +27 -0
  78. package/src/chat/transcript-render-helpers.ts +66 -24
  79. package/src/chrome/index.ts +1 -0
  80. package/src/chrome/transcript-notice.ts +83 -0
  81. package/src/chrome/transcript-status.ts +29 -29
  82. package/src/components/editor.ts +10 -1
  83. package/src/components/markdown.ts +116 -6
  84. package/src/hotkeys-markdown.ts +1 -0
  85. package/src/native/backend.ts +5 -66
  86. package/src/native/blobs.ts +3 -2
  87. package/src/native/picker.ts +3 -3
  88. package/src/overlays/advisor-config.ts +1 -10
  89. package/src/overlays/annotation-overlay.ts +125 -37
  90. package/src/overlays/annotation-types.ts +24 -0
  91. package/src/overlays/hook-selector.ts +2 -2
  92. package/src/overlays/login-dialog.ts +31 -5
  93. package/src/overlays/model-browser.ts +37 -0
  94. package/src/overlays/model-hub.ts +1 -7
  95. package/src/overlays/model-picker.ts +1 -10
  96. package/src/overlays/oauth-selector.ts +44 -6
  97. package/src/overlays/plan-review-overlay.ts +13 -2
  98. package/src/overlays/rewind-selector.ts +29 -12
  99. package/src/overlays/session-selector.ts +305 -39
  100. package/src/overlays/usage-dashboard.ts +5 -12
  101. package/src/prompt/composer.ts +213 -5
  102. package/src/prompt/custom-editor.ts +6 -1
  103. package/src/prompt/emoji-autocomplete.ts +0 -2
  104. package/src/prompt/interactive-context-helpers.ts +29 -4
  105. package/src/prompt/welcome.ts +31 -45
  106. package/src/render/extension-links.ts +214 -0
  107. package/src/render/hyperlink.ts +65 -9
  108. package/src/render/index.ts +1 -0
  109. package/src/setup/scenes/composer.ts +2 -0
  110. package/src/setup/scenes/glyph.ts +2 -0
  111. package/src/setup/scenes/model.ts +192 -29
  112. package/src/setup/scenes/outro.ts +30 -17
  113. package/src/setup/scenes/sign-in.ts +196 -400
  114. package/src/setup/scenes/splash.ts +25 -21
  115. package/src/setup/scenes/theme.ts +2 -0
  116. package/src/setup/scenes/types.ts +18 -1
  117. package/src/setup/startup-splash.ts +8 -7
  118. package/src/setup/wizard-overlay.ts +164 -33
  119. package/src/setup/wizard.ts +11 -2
  120. package/src/status-line/component.ts +4 -10
  121. package/src/status-line/context-usage.ts +72 -21
  122. package/src/status-line/host.ts +6 -2
  123. package/src/terminal.ts +24 -2
  124. package/src/theme/defaults.json +1 -0
  125. package/src/theme/loader.ts +1 -1
  126. package/src/theme/theme.ts +2 -75
  127. package/src/tools/bash-interactive.ts +6 -1
  128. package/src/tools/bash.ts +2 -0
  129. package/src/tools/default-renderer.ts +9 -6
  130. package/src/tools/eval.ts +14 -4
  131. package/src/tools/irc.ts +6 -0
  132. package/src/tools/job-widget.ts +13 -1
  133. package/src/tools/json-tree.ts +132 -42
  134. package/src/tools/lsp.ts +36 -15
  135. package/src/tools/mcp.ts +20 -12
  136. package/src/tools/native-view.ts +1 -1
  137. package/src/tools/renderer.ts +2 -0
  138. package/src/tools/streaming-output.ts +41 -0
  139. package/src/tools/task.ts +59 -17
  140. package/src/tools/url-scheme-host.ts +21 -0
  141. package/src/tools/yield.ts +18 -9
  142. package/src/tui.ts +43 -15
  143. package/THIRD-PARTY-NOTICES.txt +0 -22901
  144. package/dist/types/theme/defaults/index.d.ts +0 -10289
  145. package/src/theme/defaults/alabaster.json +0 -103
  146. package/src/theme/defaults/amethyst.json +0 -106
  147. package/src/theme/defaults/anthracite.json +0 -103
  148. package/src/theme/defaults/basalt.json +0 -101
  149. package/src/theme/defaults/birch.json +0 -105
  150. package/src/theme/defaults/dark-abyss.json +0 -101
  151. package/src/theme/defaults/dark-arctic.json +0 -114
  152. package/src/theme/defaults/dark-aurora.json +0 -105
  153. package/src/theme/defaults/dark-catppuccin.json +0 -117
  154. package/src/theme/defaults/dark-cavern.json +0 -101
  155. package/src/theme/defaults/dark-celestial.json +0 -116
  156. package/src/theme/defaults/dark-copper.json +0 -105
  157. package/src/theme/defaults/dark-cosmos.json +0 -100
  158. package/src/theme/defaults/dark-cyberpunk.json +0 -112
  159. package/src/theme/defaults/dark-dracula.json +0 -108
  160. package/src/theme/defaults/dark-eclipse.json +0 -101
  161. package/src/theme/defaults/dark-ember.json +0 -105
  162. package/src/theme/defaults/dark-equinox.json +0 -100
  163. package/src/theme/defaults/dark-forest.json +0 -106
  164. package/src/theme/defaults/dark-github.json +0 -115
  165. package/src/theme/defaults/dark-gruvbox.json +0 -122
  166. package/src/theme/defaults/dark-lavender.json +0 -105
  167. package/src/theme/defaults/dark-lunar.json +0 -99
  168. package/src/theme/defaults/dark-midnight.json +0 -105
  169. package/src/theme/defaults/dark-monochrome.json +0 -104
  170. package/src/theme/defaults/dark-monokai.json +0 -108
  171. package/src/theme/defaults/dark-nebula.json +0 -100
  172. package/src/theme/defaults/dark-neon-noir.json +0 -110
  173. package/src/theme/defaults/dark-nord.json +0 -108
  174. package/src/theme/defaults/dark-ocean.json +0 -111
  175. package/src/theme/defaults/dark-one.json +0 -110
  176. package/src/theme/defaults/dark-poimandres.json +0 -125
  177. package/src/theme/defaults/dark-rainforest.json +0 -101
  178. package/src/theme/defaults/dark-reef.json +0 -101
  179. package/src/theme/defaults/dark-retro.json +0 -102
  180. package/src/theme/defaults/dark-rose-pine.json +0 -106
  181. package/src/theme/defaults/dark-sakura.json +0 -105
  182. package/src/theme/defaults/dark-slate.json +0 -105
  183. package/src/theme/defaults/dark-solarized.json +0 -107
  184. package/src/theme/defaults/dark-solstice.json +0 -100
  185. package/src/theme/defaults/dark-starfall.json +0 -101
  186. package/src/theme/defaults/dark-sunset.json +0 -109
  187. package/src/theme/defaults/dark-swamp.json +0 -100
  188. package/src/theme/defaults/dark-synthwave.json +0 -113
  189. package/src/theme/defaults/dark-taiga.json +0 -101
  190. package/src/theme/defaults/dark-terminal.json +0 -105
  191. package/src/theme/defaults/dark-tokyo-night.json +0 -111
  192. package/src/theme/defaults/dark-tundra.json +0 -101
  193. package/src/theme/defaults/dark-twilight.json +0 -101
  194. package/src/theme/defaults/dark-volcanic.json +0 -101
  195. package/src/theme/defaults/graphite.json +0 -102
  196. package/src/theme/defaults/index.ts +0 -203
  197. package/src/theme/defaults/light-arctic.json +0 -117
  198. package/src/theme/defaults/light-aurora-day.json +0 -101
  199. package/src/theme/defaults/light-canyon.json +0 -101
  200. package/src/theme/defaults/light-catppuccin.json +0 -116
  201. package/src/theme/defaults/light-cirrus.json +0 -100
  202. package/src/theme/defaults/light-coral.json +0 -105
  203. package/src/theme/defaults/light-cyberpunk.json +0 -106
  204. package/src/theme/defaults/light-dawn.json +0 -100
  205. package/src/theme/defaults/light-dunes.json +0 -101
  206. package/src/theme/defaults/light-eucalyptus.json +0 -105
  207. package/src/theme/defaults/light-forest.json +0 -110
  208. package/src/theme/defaults/light-frost.json +0 -105
  209. package/src/theme/defaults/light-github.json +0 -125
  210. package/src/theme/defaults/light-glacier.json +0 -101
  211. package/src/theme/defaults/light-gruvbox.json +0 -118
  212. package/src/theme/defaults/light-haze.json +0 -100
  213. package/src/theme/defaults/light-honeycomb.json +0 -105
  214. package/src/theme/defaults/light-lagoon.json +0 -101
  215. package/src/theme/defaults/light-lavender.json +0 -105
  216. package/src/theme/defaults/light-meadow.json +0 -101
  217. package/src/theme/defaults/light-mint.json +0 -105
  218. package/src/theme/defaults/light-monochrome.json +0 -111
  219. package/src/theme/defaults/light-ocean.json +0 -109
  220. package/src/theme/defaults/light-one.json +0 -109
  221. package/src/theme/defaults/light-opal.json +0 -101
  222. package/src/theme/defaults/light-orchard.json +0 -101
  223. package/src/theme/defaults/light-paper.json +0 -105
  224. package/src/theme/defaults/light-poimandres.json +0 -125
  225. package/src/theme/defaults/light-prism.json +0 -100
  226. package/src/theme/defaults/light-retro.json +0 -108
  227. package/src/theme/defaults/light-sand.json +0 -105
  228. package/src/theme/defaults/light-savanna.json +0 -101
  229. package/src/theme/defaults/light-solarized.json +0 -112
  230. package/src/theme/defaults/light-soleil.json +0 -100
  231. package/src/theme/defaults/light-sunset.json +0 -109
  232. package/src/theme/defaults/light-synthwave.json +0 -108
  233. package/src/theme/defaults/light-tokyo-night.json +0 -121
  234. package/src/theme/defaults/light-wetland.json +0 -101
  235. package/src/theme/defaults/light-zenith.json +0 -99
  236. package/src/theme/defaults/limestone.json +0 -104
  237. package/src/theme/defaults/mahogany.json +0 -107
  238. package/src/theme/defaults/marble.json +0 -103
  239. package/src/theme/defaults/obsidian.json +0 -101
  240. package/src/theme/defaults/onyx.json +0 -101
  241. package/src/theme/defaults/pearl.json +0 -103
  242. package/src/theme/defaults/porcelain.json +0 -101
  243. package/src/theme/defaults/quartz.json +0 -106
  244. package/src/theme/defaults/sandstone.json +0 -105
  245. package/src/theme/defaults/titanium.json +0 -100
package/CHANGELOG.md CHANGED
@@ -2,6 +2,45 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [18.14.0] - 2026-10-29
6
+
7
+ ### Breaking Changes
8
+
9
+ - Moved `PINNED_HUD_TOGGLE_ID` to the coding agent's task output manager so reserving subagent IDs no longer loads the interactive editor.
10
+ - Updated `StatusLineHost.resetExpiryNotice` to receive the session and manage warning deduplication; custom status-line components must use the new signature.
11
+ - Removed `ToolExecutionHandle.setToolActivityVisible`; tool-activity visibility is now controlled by transcript components, while reply blocks remain visible by default.
12
+ - Removed the `@oh-my-pi/pi-tui/theme/defaults` export. Bundled palettes are now available through `getBuiltinThemes()` from `theme/defaults.json`.
13
+
14
+ ### Fixed
15
+
16
+ - Fixed table-chart direction detection so measures are not incorrectly marked as better based on header words; single-measure tables are now sent for judging when local charting is appropriate.
17
+ - Fixed LSP diagnostics that incorrectly showed “No issues” before a language server had reported; such files are now shown as unverified. ([#17293](https://github.com/can1357/oh-my-pi/pull/17293) by [@JustasMasiulis](https://github.com/JustasMasiulis))
18
+ - Fixed Esc-Esc rewind in Tern selecting interrupted turns with no visible output instead of the last displayed turn.
19
+ - Fixed streamed code blocks losing lines that scrolled off screen before the closing fence arrived; completed lines now remain available in scrollback. ([#17333](https://github.com/can1357/oh-my-pi/pull/17333) by [@gzkGit777](https://github.com/gzkGit777))
20
+
21
+ ## [18.13.3] - 2026-10-28
22
+
23
+ ### Added
24
+
25
+ - The resume picker renames the selected session in place: F2 opens a dialog prefilled with the current title, Enter commits, Esc cancels ([#17262](https://github.com/can1357/oh-my-pi/pull/17262) by [@Twilight0](https://github.com/Twilight0))
26
+
27
+ ### Changed
28
+
29
+ - Updated Bun engine requirement to ≥1.4.4
30
+
31
+ ### Fixed
32
+
33
+ - Restored WebP and JPEG images stay visible in Kitty scrollback after resuming a session ([#17220](https://github.com/can1357/oh-my-pi/issues/17220)).
34
+ - Escape in an annotation note no longer discards it: the overlay closes and `getDraft()`/`restoreDraft()` let the host keep and reopen the note; **Discard notes and close** (confirmed by a second press) clears them instead, and code review's actions start on Paste so a stray Enter never sends the LLM review ([#17271](https://github.com/can1357/oh-my-pi/pull/17271) by [@H4vC](https://github.com/H4vC), based on [#17230](https://github.com/can1357/oh-my-pi/pull/17230) by [@Shadorain](https://github.com/Shadorain)).
35
+ - Fixed terminal replies such as Tern's `tsp;e;{"ev":"ack",…}` showing up in the shell prompt after quitting omp; the exit drain now waits for the terminal to answer a final DA1 sentinel
36
+ - Fixed the plan review sheet in Tern squeezing the "continue with" model tiers under a long plan, so the model name overlapped the tier buttons
37
+ - `/usage` in Tern shows when each quota resets as a countdown (`resets in 2h30m`) instead of a clock time; hovering shows the date and time
38
+ - Fixed extension `ui.select` dialogs treating unconfigured `j`/`k` keys as navigation, which could turn a default No into Yes when typing `ok` then Enter ([#17255](https://github.com/can1357/oh-my-pi/pull/17255) by [@SAKANA-MOONLIGHT](https://github.com/SAKANA-MOONLIGHT)).
39
+
40
+ ### Removed
41
+
42
+ - Removed support for the TSP `t` (theme) verb, as surface theme synchronization is no longer required
43
+
5
44
  ## [18.13.2] - 2026-10-27
6
45
 
7
46
  ### Added
@@ -2435,57 +2474,4 @@
2435
2474
  - Fixed a notification chip (or any injected block) rendering on top of an actively streaming tool render on ED3-risk terminals (Ghostty/kitty/Alacritty/iTerm2). While a foreground tool streams, its header's elapsed-time counter ticks every frame; once output scrolls the header above the viewport top, each tick is an offscreen edit that — because the eager scrollback-rebuild opt-in is gated off on these terminals — repaints the viewport in place and advances the rendered line count without committing the new overflow to native history. `#scrollbackHighWater` then lagged the logical viewport top, so a later content shrink whose changes landed in the visible region slipped past the shrink-across-boundary guard and reached the differential emitter, which is anchored to `#maxLinesRendered - height`: it rewrote only the suffix, dropped the newly exposed top row, and left a blank at the bottom, drifting every row below the edit one line up so it painted over the rows above. Such shrinks now re-anchor the bottom of the viewport with a non-destructive repaint, and the foreground-streaming shrink-across-boundary case repaints the live tail instead of padding and pinning the pre-shrink viewport.
2436
2475
  - Fixed a terminal resize during foreground-tool streaming on an unknown-viewport / ED3-risk host (Ghostty/kitty/Alacritty/iTerm2/WSL) leaving native scrollback permanently out of sync, so scrolling back after the turn showed missing rows. A pure geometry resize (no content change) takes the in-place viewport-repaint path, which — unlike a content-bearing resize that rebuilds via the geometry branch — never flagged native history. Because the prompt-submit checkpoint (`refreshNativeScrollbackIfDirty`) only rebuilds when scrollback is marked dirty on these hosts, the discrepancy was never reconciled. Overflowing geometry repaints whose viewport is not known to be at the bottom now mark scrollback dirty so the next checkpoint rebuilds an exact copy of the transcript.
2437
2476
 
2438
- ## [15.8.2] - 2026-06-03
2439
-
2440
- ### Added
2441
-
2442
- - Added `PI_NO_SYNC_OUTPUT=1` to disable DEC 2026 synchronized-output wrappers for terminals whose implementation is buggy or visually worse, while keeping the renderer's autowrap guards active during paints ([#1765](https://github.com/can1357/oh-my-pi/issues/1765)).
2443
-
2444
- ### Fixed
2445
-
2446
- - Fixed terminal resizes that land in the same render frame as streamed output splicing a phantom blank row into native scrollback and offsetting every later row by one. A height shrink (or width change carrying an append) with content overflowing the viewport fell through to the differential emitter, whose scroll math is anchored to the pre-resize viewport top and hardware-cursor row — both invalidated by the terminal's own resize reflow. Geometry-changed frames now rebuild native history when the viewport is at (or possibly at) the bottom, and defer non-destructively for a reader confirmed scrolled into history.
2447
- - Fixed Ghostty/kitty/Alacritty-style ED3-risk terminals freezing the prompt after a deferred shrink; focused keyboard input now uses the same explicit user-input viewport opt-in as autocomplete and can repaint immediately instead of waiting for a resize.
2448
- - Deferred eager live scrollback rebuilds under WSL fronted by Windows Terminal (`WT_SESSION` present in a Linux environment) so foreground streaming no longer emits ED3 (`CSI 3 J`) and yanks a reader scrolled into Windows Terminal's host scrollback; deferred rewrites still reconcile at the next prompt-submit checkpoint ([#1610](https://github.com/can1357/oh-my-pi/issues/1610)).
2449
- - Fixed tmux (and screen/zellij) pane history gaining a complete duplicate copy of the transcript every time a deferred offscreen edit was followed by another render. Multiplexer panes never receive a destructive scrollback clear, so the dirty-scrollback rebuild path only appended the full transcript on top of preserved pane history — repeatedly. Live frames inside multiplexers now keep repainting the viewport and leave history reconciliation to explicit checkpoints, which also removes the O(transcript) write amplification per frame.
2450
- - Fixed tmux pane viewports corrupting and pane history duplicating when a resize coincides with rendering: a resize racing a streamed append reached the stale-anchor diff emitters (phantom rows in the pane), a forced render racing a resize replayed the whole transcript into preserved pane history, and the prompt-submit checkpoint did the same after any deferred offscreen edit. Geometry-changed frames inside multiplexers now repaint the viewport in place, and forced-render geometry replays plus checkpoint replays are disabled there — tmux reflows its own pane grid and its history cannot be cleared, only duplicated.
2451
- - Fixed terminal resize events whose dimensions net out unchanged by render time (rapid SIGWINCH round trips during a window drag, coalesced into one 16ms frame) being invisible to the renderer. The terminal reflows its buffer on every resize event — rows move between the viewport and scrollback and can be evicted at the scrollback cap — so diffing against the pre-resize screen splices blank phantom rows into the viewport. The renderer now tracks the resize event itself, not just the dimension delta, and routes such frames through the geometry-change repaint/rebuild paths.
2452
- - Fixed Termux terminal resizes (screen rotation or software-keyboard toggles) displacing or hiding output after the viewport height changed. Content-bearing resizes were routed to the differential emitter, whose scroll math is anchored to the pre-resize viewport, so appended rows landed too low; pure height changes were treated as no-ops, exposing blank rows that later appends could fill without growing native scrollback. Termux resizes now repaint or rebuild at the new geometry like every other non-multiplexer terminal.
2453
- - Fixed the turn-end teardown frame freezing on ED3-risk terminals (Ghostty/kitty/Alacritty/iTerm2): disabling eager scrollback rebuild now takes effect only after the in-flight frame is classified, so the loader/status removal still paints instead of deferring and leaving a stale spinner until the next keystroke.
2454
- - Fixed non-WT ConPTY terminals on Windows (Tabby, Hyper, VS Code, conhost) clearing scrollback and yanking the viewport to the top whenever streaming output or a prompt-submit rebuild arrived while the user was scrolled up. The kernel32 viewport probe describes the ConPTY pseudo-console buffer — which is pinned to the visible grid, invisible to host-UI scrollback — so it reported "at bottom" no matter where the user had scrolled, and the [#1635](https://github.com/can1357/oh-my-pi/issues/1635) fix only distrusted it under `WT_SESSION`, which Tabby and other ConPTY hosts never set. The probe is now removed entirely: every Windows host is treated as viewport-unobservable, live mutations defer destructive rebuilds (no `\x1b[3J`, no viewport movement), and native scrollback reconciles at the prompt-submit checkpoint where the Enter keystroke has already pinned the host viewport to the bottom ([#1746](https://github.com/can1357/oh-my-pi/issues/1746)).
2455
- - Fixed emoji-presentation symbols (a default-text symbol followed by variation-selector-16 `U+FE0F`, e.g. `⚠️`, `ℹ️`, `❤️`, keycaps) measuring as 1 cell instead of 2 in the native width engine on macOS. The native scanner now keeps `UnicodeWidthStr` as the source of truth for multi-codepoint graphemes and applies only the local macOS Hangul Compatibility Jamo character-width delta, preserving VS16/keycap sequence widths without reintroducing jamo cursor drift.
2456
- - Deferred eager live scrollback rebuilds on macOS Terminal.app and iTerm2 so assistant/tool streaming no longer emits ED3 (`CSI 3 J`) while their native viewport position is unobservable, preserving readers scrolled into terminal history ([#1300](https://github.com/can1357/oh-my-pi/issues/1300)).
2457
- - Fixed width-shrink reflow leaving old-width rows in native history so later appends no longer undercount scrollback growth or duplicate wrapped content.
2458
- - Fixed hiding overlays after terminal reflow so stale dialog rows are scrubbed from native scrollback on non-multiplexer terminals.
2459
-
2460
- ### Removed
2461
-
2462
- - Removed `shouldTrustNativeViewportProbe` and `ProcessTerminal`'s kernel32 `GetConsoleScreenBufferInfo` viewport probe. No Windows environment can answer "is the user's viewport at the bottom" truthfully — under ConPTY (every modern host) the pseudo-console buffer is pinned to the visible grid so the probe always read "at bottom", and under legacy conhost the window tracks the output cursor rather than the buffer tail so it always read "scrolled up" — so the probe and its trust gate are gone; `ProcessTerminal` no longer implements the optional `Terminal.isNativeViewportAtBottom`.
2463
-
2464
- ## [15.8.1] - 2026-06-02
2465
-
2466
- ### Fixed
2467
-
2468
- - Deferred eager live scrollback rebuilds on VTE terminals so GNOME-style Linux terminals do not flash or erase readable scrollback during streaming ([#1719](https://github.com/can1357/oh-my-pi/issues/1719)).
2469
-
2470
- ## [15.8.0] - 2026-06-02
2471
-
2472
- ### Fixed
2473
-
2474
- - Deferred eager live scrollback rebuilds on POSIX terminals where xterm ED3 (`CSI 3 J`, erase saved lines) can disturb scrolled-up readers during streaming, while keeping direct user-input and checkpoint rebuilds explicit ([#1682](https://github.com/can1357/oh-my-pi/issues/1682)).
2475
- - Fixed TUI shutdown placing the parent shell prompt one row below short rendered content instead of directly on the next line ([#1620](https://github.com/can1357/oh-my-pi/issues/1620)).
2476
- - Stopped painting inline color swatches for 4-digit hex runs in Markdown rendering. The `#RGBA` CSS form collides with hashline `#TAG` snapshot tags (4 hex digits, e.g. `#6C5E`), which were sprouting spurious RGB swatches in prose and codespans. Only `#RGB`, `#RRGGBB`, and `#RRGGBBAA` qualify now.
2477
-
2478
- ## [15.7.6] - 2026-06-01
2479
-
2480
- ### Fixed
2481
-
2482
- - Fixed native Windows + Windows Terminal freezing the editor on the wrap keystroke, on `/plan`/`/resume`/model-switch/status-line toggles, and on any other offscreen structural mutation until the next prompt submit. The `15.7.5` `#1635` fix routed every viewport-saturating pure-append and structural mutation through `deferredMutation` (a literal no-op) whenever `isNativeViewportAtBottom()` returned `undefined` — which it always does under `WT_SESSION` because the kernel32 probe can't see WT host scrollback. The deferral was only ever meant for the *confirmed-scrolled* case; an unknown viewport now falls back to a non-destructive `viewportRepaint` instead, so the live UI keeps updating without emitting `\x1b[3J` and without yanking a possibly-scrolled reader. Confirmed-scrolled frames (probe returns `false`) still defer.
2483
- - Removed the hard-coded 20-result cap on `@`-prefixed fuzzy file completion in `CombinedAutocompleteProvider.#getFuzzyFileSuggestions`. The dropdown now honors the existing `maxResults: 100` ceiling already configured for `fuzzyFind`, so projects with many files sharing a common stem (e.g. `@controller`, `@test`) surface all relevant matches instead of being silently truncated. ([#1652](https://github.com/can1357/oh-my-pi/issues/1652))
2484
-
2485
- ## [15.7.5] - 2026-06-01
2486
-
2487
- ### Fixed
2488
-
2489
- - Fixed native Windows + Windows Terminal scrollback being yanked to the top when a streaming response triggered a TUI full redraw. Under ConPTY the `kernel32` `GetConsoleScreenBufferInfo` probe answers about the pseudo-console (always at the buffer tail) and not about WT's host scrollback, so `isNativeViewportAtBottom()` falsely returned `true` while the user was scrolled up and the shrink-across-viewport branch issued a destructive `historyRebuild` (`\x1b[2J\x1b[H\x1b[3J`). The probe now short-circuits to `undefined` whenever `WT_SESSION` is set, letting the existing deferred-rebuild path keep streaming-time mutations non-destructive and reconcile native history at the next prompt-submit checkpoint. ([#1635](https://github.com/can1357/oh-my-pi/issues/1635))
2490
-
2491
- Older entries are archived in [packages/tui/CHANGELOG.md@3d45d8557cd7](https://github.com/can1357/oh-my-pi/blob/3d45d8557cd7493221b69c7ed3c6b6a2307f46e7/packages/tui/CHANGELOG.md).
2477
+ Older entries are archived in [packages/tui/CHANGELOG.md@6693ddbfd58c](https://github.com/can1357/oh-my-pi/blob/6693ddbfd58ce9edcdc0dc3fe98a8882c9bae834/packages/tui/CHANGELOG.md).
package/README.md CHANGED
@@ -28,7 +28,7 @@ const tui = new TUI(terminal);
28
28
  tui.addChild(new Text("Welcome to my app!"));
29
29
 
30
30
  const editor = new Editor(editorTheme);
31
- editor.onSubmit = (text) => {
31
+ editor.onSubmit = text => {
32
32
  console.log("Submitted:", text);
33
33
  tui.addChild(new Text(`You said: ${text}`));
34
34
  };
@@ -106,12 +106,12 @@ interface Component {
106
106
  }
107
107
  ```
108
108
 
109
- | Method | Description |
110
- | ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
109
+ | Method | Description |
110
+ | ------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
111
111
  | `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. |
112
- | `handleInput?(data)` | Called when the component has focus and receives keyboard input. The `data` string contains raw terminal input (may include ANSI escape sequences). |
113
- | `invalidate?()` | Called to clear any cached render state. Components should re-render from scratch on the next `render()` call. |
114
- | `releaseRenderCaches?()` | Drops only derived render caches. The next `render()` must return the same rows without eager rebuilds, renderer callbacks, image conversions, or child replacement. |
112
+ | `handleInput?(data)` | Called when the component has focus and receives keyboard input. The `data` string contains raw terminal input (may include ANSI escape sequences). |
113
+ | `invalidate?()` | Called to clear any cached render state. Components should re-render from scratch on the next `render()` call. |
114
+ | `releaseRenderCaches?()` | Drops only derived render caches. The next `render()` must return the same rows without eager rebuilds, renderer callbacks, image conversions, or child replacement. |
115
115
 
116
116
  ## Built-in Components
117
117
 
@@ -119,20 +119,20 @@ interface Component {
119
119
 
120
120
  Build screens from persistent components and update their data, selection, expansion, or size through setters. Layouts own child bounds and mouse-coordinate translation; controllers retain domain workflows and asynchronous operations. Propagate `invalidate()` after theme changes and `dispose()` when removing an owned component tree.
121
121
 
122
- | Family | Components | Import |
123
- | --- | --- | --- |
124
- | Layout | `Stack`, `Row`, `SplitPane` | package root |
125
- | Panels | `OverlayPanel`, `PanelRows`, `PanelDivider` | `/chrome` |
126
- | Menus | `SelectList`, `MenuSelection` | package root |
127
- | Forms | `Form`, `FormField`, `TextFormField`, `SelectFormField`, `SettingsFormField` | package root |
128
- | Wizard steps | `WizardStep` | package root |
129
- | Viewports | `ScrollView`, including child rendering, follow-tail, and keyed range anchoring | package root |
130
- | Trees | `TreeView` | package root |
131
- | Disclosure | `Disclosure`, with lazy summary/detail children | package root |
132
- | Messages | `FramedMessageComponent`, `MessageNoticeComponent`, `MessageDividerComponent` | `/chrome` |
133
- | Tool output | `ToolCard`, `framedToolCard`, `plainToolCard`, `OutputPane` | `/render` |
134
- | Transcripts | `TranscriptBrowser` | package root |
135
- | Data | `MetricRow`, `ProgressBar`, `Table`, `KeyValueList`, `Section` | package root |
122
+ | Family | Components | Import |
123
+ | ------------ | ------------------------------------------------------------------------------- | ------------ |
124
+ | Layout | `Stack`, `Row`, `SplitPane` | package root |
125
+ | Panels | `OverlayPanel`, `PanelRows`, `PanelDivider` | `/chrome` |
126
+ | Menus | `SelectList`, `MenuSelection` | package root |
127
+ | Forms | `Form`, `FormField`, `TextFormField`, `SelectFormField`, `SettingsFormField` | package root |
128
+ | Wizard steps | `WizardStep` | package root |
129
+ | Viewports | `ScrollView`, including child rendering, follow-tail, and keyed range anchoring | package root |
130
+ | Trees | `TreeView` | package root |
131
+ | Disclosure | `Disclosure`, with lazy summary/detail children | package root |
132
+ | Messages | `FramedMessageComponent`, `MessageNoticeComponent`, `MessageDividerComponent` | `/chrome` |
133
+ | Tool output | `ToolCard`, `framedToolCard`, `plainToolCard`, `OutputPane` | `/render` |
134
+ | Transcripts | `TranscriptBrowser` | package root |
135
+ | Data | `MetricRow`, `ProgressBar`, `Table`, `KeyValueList`, `Section` | package root |
136
136
 
137
137
  ```typescript
138
138
  import { Disclosure, SplitPane, Text } from "@oh-my-pi/pi-tui";
@@ -176,10 +176,10 @@ Container that applies padding and background color to all children.
176
176
  const box = new Box(
177
177
  1, // paddingX (default: 1)
178
178
  1, // paddingY (default: 1)
179
- (text) => chalk.bgGray(text), // optional background function
179
+ text => chalk.bgGray(text), // optional background function
180
180
  );
181
181
  box.addChild(new Text("Content"));
182
- box.setBgFn((text) => chalk.bgBlue(text)); // Change background dynamically
182
+ box.setBgFn(text => chalk.bgBlue(text)); // Change background dynamically
183
183
  ```
184
184
 
185
185
  ### Text
@@ -191,10 +191,10 @@ const text = new Text(
191
191
  "Hello World", // text content
192
192
  1, // paddingX (default: 1)
193
193
  1, // paddingY (default: 1)
194
- (text) => chalk.bgGray(text), // optional background function
194
+ text => chalk.bgGray(text), // optional background function
195
195
  );
196
196
  text.setText("Updated text");
197
- text.setCustomBgFn((text) => chalk.bgBlue(text));
197
+ text.setCustomBgFn(text => chalk.bgBlue(text));
198
198
  ```
199
199
 
200
200
  ### TruncatedText
@@ -215,7 +215,7 @@ Single-line text input with horizontal scrolling.
215
215
 
216
216
  ```typescript
217
217
  const input = new Input();
218
- input.onSubmit = (value) => console.log(value);
218
+ input.onSubmit = value => console.log(value);
219
219
  input.setValue("initial");
220
220
  input.getValue();
221
221
  ```
@@ -285,11 +285,11 @@ interface EditorTheme {
285
285
  }
286
286
 
287
287
  const editor = new Editor(theme);
288
- editor.onSubmit = (text) => console.log(text);
289
- editor.onChange = (text) => console.log("Changed:", text);
288
+ editor.onSubmit = text => console.log(text);
289
+ editor.onChange = text => console.log("Changed:", text);
290
290
  editor.disableSubmit = true; // Disable submit temporarily
291
291
  editor.setAutocompleteProvider(provider);
292
- editor.borderColor = (s) => chalk.blue(s); // Change border dynamically
292
+ editor.borderColor = s => chalk.blue(s); // Change border dynamically
293
293
  ```
294
294
 
295
295
  **Features:**
@@ -371,8 +371,8 @@ Animated loading spinner.
371
371
  ```typescript
372
372
  const loader = new Loader(
373
373
  tui, // TUI instance for render updates
374
- (s) => chalk.cyan(s), // spinner color function
375
- (s) => chalk.gray(s), // message color function
374
+ s => chalk.cyan(s), // spinner color function
375
+ s => chalk.gray(s), // message color function
376
376
  "Loading...", // message (default: "Loading...")
377
377
  );
378
378
  loader.start();
@@ -387,8 +387,8 @@ Extends Loader with Escape key handling and an AbortSignal for cancelling async
387
387
  ```typescript
388
388
  const loader = new CancellableLoader(
389
389
  tui, // TUI instance for render updates
390
- (s) => chalk.cyan(s), // spinner color function
391
- (s) => chalk.gray(s), // message color function
390
+ s => chalk.cyan(s), // spinner color function
391
+ s => chalk.gray(s), // message color function
392
392
  "Working...", // message
393
393
  );
394
394
  loader.onAbort = () => done(null); // Called when user presses Escape
@@ -430,9 +430,9 @@ const list = new SelectList(
430
430
  theme, // SelectListTheme
431
431
  );
432
432
 
433
- list.onSelect = (item) => console.log("Selected:", item);
433
+ list.onSelect = item => console.log("Selected:", item);
434
434
  list.onCancel = () => console.log("Cancelled");
435
- list.onSelectionChange = (item) => console.log("Highlighted:", item);
435
+ list.onSelectionChange = item => console.log("Highlighted:", item);
436
436
  list.setFilter("opt"); // Filter items
437
437
  ```
438
438
 
@@ -39,10 +39,13 @@ interface AppKeybindings {
39
39
  "app.session.rename": true;
40
40
  "app.session.delete": true;
41
41
  "app.session.deleteNoninvasive": true;
42
+ "app.session.pin": true;
43
+ "app.session.unpin": true;
42
44
  "app.tree.previousBranch": true;
43
45
  "app.tree.nextBranch": true;
44
46
  "app.plan.toggle": true;
45
47
  "app.history.search": true;
48
+ "app.links.hint": true;
46
49
  "app.stt.pushToTalk": true;
47
50
  "app.stt.toggle": true;
48
51
  "app.live.toggle": true;
@@ -329,6 +332,14 @@ export declare const KEYBINDINGS: {
329
332
  readonly defaultKeys: "ctrl+backspace";
330
333
  readonly description: "Delete session (non-invasive)";
331
334
  };
335
+ readonly "app.session.pin": {
336
+ readonly defaultKeys: "alt+k";
337
+ readonly description: "Pin the selected session in the resume picker";
338
+ };
339
+ readonly "app.session.unpin": {
340
+ readonly defaultKeys: "alt+shift+k";
341
+ readonly description: "Unpin the selected session in the resume picker";
342
+ };
332
343
  readonly "app.tree.previousBranch": {
333
344
  readonly defaultKeys: [];
334
345
  readonly description: "Jump to the previous branch in /tree";
@@ -345,6 +356,10 @@ export declare const KEYBINDINGS: {
345
356
  readonly defaultKeys: "ctrl+r";
346
357
  readonly description: "Search history";
347
358
  };
359
+ readonly "app.links.hint": {
360
+ readonly defaultKeys: "alt+o";
361
+ readonly description: "Pick an extension link on screen by its hint tag";
362
+ };
348
363
  readonly "app.stt.pushToTalk": {
349
364
  readonly defaultKeys: "space";
350
365
  readonly description: "Hold to record speech-to-text";
@@ -4,6 +4,9 @@ export interface SessionPickerHost<T extends SessionSelectorEntry = SessionSelec
4
4
  loadPinnedIds?(): Promise<ReadonlySet<string>>;
5
5
  loadHistoryMatcher?(): SessionHistoryMatcher;
6
6
  deleteSession?(session: T): Promise<boolean>;
7
+ /** Rename the session and resolve with the title as stored, or null when it did not happen. */
8
+ renameSession?(session: T, title: string): Promise<string | null>;
9
+ setSessionPinned?(session: T, pinned: boolean): Promise<void>;
7
10
  loadAllSessions?(): Promise<T[]>;
8
11
  }
9
12
  /** Presentation and capability controls for the standalone session picker. */
@@ -13,6 +16,8 @@ export interface SessionPickerOptions<T extends SessionSelectorEntry = SessionSe
13
16
  scopeLabel?: string | false;
14
17
  showCwd?: boolean;
15
18
  allowDelete?: boolean;
19
+ /** Rename the selected session; resolve with the title as stored, or null when it did not happen. */
20
+ onRename?: (session: T, title: string) => Promise<string | null>;
16
21
  allowGlobalScope?: boolean;
17
22
  historySearch?: boolean;
18
23
  pinnedIds?: ReadonlySet<string>;
@@ -419,9 +419,10 @@ export type ChartAnnotation = {
419
419
  export declare function planChart(table: TableAnalysis): ChartPlan | undefined;
420
420
  /**
421
421
  * Whether a model should pick `table`'s chart (`tui.autoGraph: smart`):
422
- * multi-series tables, and tables whose rows may be named by a number the
423
- * `guess` plots (`Thread | State`, `Finding | Current`), where only meaning
424
- * tells a name from a measure.
422
+ * multi-series tables, tables whose rows may be named by a number the
423
+ * `guess` plots (`Thread | State`, `Finding | Current`), and any other chart
424
+ * the guess would draw — only meaning tells a measure from page references,
425
+ * step numbers or figures quoted in prose, which the judge charts as none.
425
426
  */
426
427
  export declare function shouldJudge(table: TableAnalysis, guess: ChartPlan | undefined): boolean;
427
428
  /** Measure columns before `guess`'s text label column: numbers that may only name their rows. */
@@ -446,10 +447,11 @@ export declare function tableTakeaways(table: TableAnalysis, plan: ChartPlan, co
446
447
  */
447
448
  export declare function drawnNames(spec: ChartSpec): Set<string>;
448
449
  /**
449
- * The local guess at what `plan`'s rows and measures mean: header keywords
450
- * for each measure's {@link Polarity} (resources like tokens or memory only
451
- * beside a baseline), rivals from the label's header, focus from bolded or
452
- * `(current)` row names, and a status column whose cells vary.
450
+ * The local guess at what `plan`'s rows and measures mean: each measure's
451
+ * {@link Polarity} as far as its unit says ({@link dimPolarity}; what a name
452
+ * means — `Memory` as a capacity or a cost — is the judge's call), rivals from
453
+ * the label's header, focus from bolded or `(current)` row names, and a status
454
+ * column whose cells vary.
453
455
  */
454
456
  export declare function guessMeaning(table: TableAnalysis, plan: ChartPlan): Pick<ChartPlan, "polarity" | "rowPolarity" | "rivals" | "focus" | "status">;
455
457
  /** Whether `plan`'s rows may each be a metric of their own: a `change`, a transposed table, or mixed-unit columns. */
@@ -1,7 +1,7 @@
1
1
  import type { AssistantMessage, ImageContent } from "@oh-my-pi/pi-ai";
2
2
  import { type Component, Container } from "../tui.js";
3
3
  import { type ImageBudget } from "../components/image.js";
4
- import type { AssistantThinkingRenderer } from "./extension-types.js";
4
+ import type { AssistantTextRenderer, AssistantTextRendering, AssistantThinkingRenderer } from "./extension-types.js";
5
5
  import type { DescribeContext, NativeNode, NativeUiEvent } from "../native/node.js";
6
6
  import { type CacheInvalidation } from "./cache-invalidation-marker.js";
7
7
  import { type ServedModelMismatch } from "./served-model-marker.js";
@@ -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>, expandThinkingBlocks?: boolean);
41
+ constructor(message?: AssistantMessage, hideThinkingBlock?: boolean, onImageUpdate?: () => void, thinkingRenderers?: readonly AssistantThinkingRenderer[], imageBudget?: ImageBudget, proseOnlyThinking?: boolean, linkTargets?: ReadonlyMap<string, string>, expandThinkingBlocks?: boolean, textRendering?: AssistantTextRendering);
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
@@ -87,7 +87,7 @@ export declare class AssistantMessageComponent extends Container {
87
87
  */
88
88
  setExpanded(expanded: boolean): void;
89
89
  isTranscriptBlockFinalized(): boolean;
90
- /** Whether a figure still waits for the raster of its final source; the transcript holds retirement meanwhile. */
90
+ /** Hold image and figure rendering until the completed block can enter immutable scrollback. */
91
91
  isTranscriptBlockPending(): boolean;
92
92
  render(width: number): readonly string[];
93
93
  /**
@@ -122,7 +122,19 @@ export declare class AssistantMessageComponent extends Container {
122
122
  renderTranscriptBlockEmergencyRow(width: number): string | undefined;
123
123
  getTranscriptBlockVersion(): number;
124
124
  markTranscriptBlockFinalized(): void;
125
+ /**
126
+ * Swap the extension text renderers (the setting enabling them changed) and redraw with them.
127
+ * Enabling them also drops rows already published as stable, since a renderer may rewrite them;
128
+ * callers MUST pair this with a scrollback-clearing display reset, as for
129
+ * {@link resetTranscriptStableRows}.
130
+ */
131
+ setTextRenderers(renderers: readonly AssistantTextRenderer[]): void;
125
132
  applyRetryRecovery(retryRecovery: AssistantMessage["retryRecovery"]): void;
133
+ /**
134
+ * Draw the current message again when it is the reply sent at `timestamp`, re-running the
135
+ * extension text renderers; returns whether it was. The caller schedules the frame.
136
+ */
137
+ rerenderMessage(timestamp: number): boolean;
126
138
  messagePersistenceKey(): string | undefined;
127
139
  /** Toggle rendering for assistant-native and tool-result images. */
128
140
  setImagesVisible(visible: boolean): void;
@@ -37,6 +37,8 @@ export declare class BashExecutionComponent extends Container {
37
37
  appendOutput(chunk: string): void;
38
38
  /** Switch to PTY rendering and feed raw terminal bytes through the vterm replay. */
39
39
  appendPtyChunk(chunk: string): void;
40
+ /** Bytes a terminal would send for a key event, honoring the command's cursor-key mode; "" for key releases. */
41
+ encodePtyInput(data: string): string;
40
42
  setComplete(exitCode: number | undefined, cancelled: boolean, options?: {
41
43
  output?: string;
42
44
  truncation?: TruncationMeta;
@@ -6,6 +6,8 @@ export interface ChatTranscriptDisplayPreferences {
6
6
  cacheMissMarker: boolean;
7
7
  showTokenUsage: boolean;
8
8
  showTurnTime: boolean;
9
+ /** Max rows a collapsed advisor card shows below its header; 0 = no cap. */
10
+ advisorCollapsedLines: number;
9
11
  }
10
12
  /** Current transcript display preferences. */
11
13
  export declare const chatTranscriptDisplayPreferences: ChatTranscriptDisplayPreferences;
@@ -1,3 +1,4 @@
1
+ import type { AssistantMessage } from "@oh-my-pi/pi-ai";
1
2
  import type { Component, TUI } from "../tui.js";
2
3
  import type { Theme } from "../theme/theme.js";
3
4
  import type { CustomMessage, HookMessage } from "./messages.js";
@@ -17,6 +18,36 @@ export interface AssistantThinkingRenderContext {
17
18
  requestRender(): void;
18
19
  }
19
20
  export type AssistantThinkingRenderer = (context: AssistantThinkingRenderContext, theme: Theme) => Component | undefined;
21
+ export interface AssistantTextRenderContext {
22
+ /**
23
+ * The message being drawn; `message.timestamp` identifies the reply for `ctx.ui.rerenderMessage()`.
24
+ * After a tool call this is only the segment drawn there, so its `content` does not hold the whole reply.
25
+ * A deep copy: editing it, at any depth, cannot change the stored reply.
26
+ */
27
+ message: AssistantMessage;
28
+ /**
29
+ * The text block's index in the full reply's `content`, also when the reply is drawn in segments
30
+ * around tool calls; not an index into `message.content` for a segment. It counts the content as
31
+ * currently stored: a transcript rebuild that drops tool calls left without a result shifts later
32
+ * blocks down, so do not persist it as a permanent key.
33
+ */
34
+ contentIndex: number;
35
+ /** The block's markdown as drawn so far, after any earlier renderer. */
36
+ text: string;
37
+ /** True while the block may still grow; false for its final render, a block sealed at a tool call, and later redraws. */
38
+ streaming: boolean;
39
+ }
40
+ /**
41
+ * Display-only transform of one assistant text block: returns the markdown to draw, or `undefined`
42
+ * to draw `context.text` unchanged. The session, exports and model context keep the original text.
43
+ */
44
+ export type AssistantTextRenderer = (context: AssistantTextRenderContext) => string | undefined;
45
+ /** Extension text renderers for one assistant component, and where its content starts in the full reply. */
46
+ export interface AssistantTextRendering {
47
+ renderers: readonly AssistantTextRenderer[];
48
+ /** Index in the full reply's `content` of this component's first block; a segment after a tool call starts past it. */
49
+ contentOffset: number;
50
+ }
20
51
  export interface HookMessageRenderOptions {
21
52
  /** Whether the view is expanded */
22
53
  expanded: boolean;
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Replies: text the agent sends to a scheme whose writes are `reply`-scoped (`out://`)
3
+ * is its mid-turn message to the user, drawn as assistant prose so it can answer
4
+ * without ending its turn.
5
+ * - A `write` call to a reply URL draws as {@link ReplyWriteComponent}, streaming as the
6
+ * call's arguments arrive, instead of a tool card.
7
+ * - Text a tool's commands write there (`printf … > out://` in bash) arrives in the
8
+ * tool's result `details.replies` ({@link toolResultReplies}); transcripts draw each
9
+ * one as a prose block after the call ({@link replyDisplayMessage}).
10
+ */
11
+ import type { AssistantMessage } from "@oh-my-pi/pi-ai";
12
+ import type { ImageBudget } from "../components/image.js";
13
+ import { AssistantMessageComponent } from "./assistant-message.js";
14
+ import type { ToolExecutionHandle, ToolResultFrame } from "./tool-execution.js";
15
+ /**
16
+ * Whether a `toolName` call writes to a reply URL. `undefined` while streamed args cannot tell yet:
17
+ * no path so far, or a path that could still grow into a reply URL (`ou`, `out:/`). Callers holding
18
+ * final args treat `undefined` as `false`.
19
+ */
20
+ export declare function isReplyWrite(toolName: string, args: unknown): boolean | undefined;
21
+ /** Display options of a reply block; the rest of its presentation follows the assistant prose defaults. */
22
+ export interface ReplyWriteOptions {
23
+ /** Repaint request for figures and images that finish loading after a render. */
24
+ readonly requestRender?: () => void;
25
+ readonly imageBudget?: ImageBudget;
26
+ /** Resolved local destinations of Markdown links, as for assistant text. */
27
+ readonly linkTargets?: ReadonlyMap<string, string>;
28
+ }
29
+ /** Replies a tool result carries in `details.replies` (text its commands wrote to a reply URL), oldest first. */
30
+ export declare function toolResultReplies(details: unknown): readonly string[];
31
+ /** Assistant message drawing reply text. No provider turn backs it, so its identity fields stay empty. */
32
+ export declare function replyDisplayMessage(text: string, errorMessage?: string): AssistantMessage;
33
+ /**
34
+ * A reply write ({@link isReplyWrite}) drawn as assistant prose. The call's lifecycle arrives
35
+ * through {@link ToolExecutionHandle}: streamed args grow the text, final args settle it, and
36
+ * the result seals the block, a failure showing its error under the text. It is a reply, not
37
+ * tool activity, so hiding tool activity leaves it on screen.
38
+ */
39
+ export declare class ReplyWriteComponent extends AssistantMessageComponent implements ToolExecutionHandle {
40
+ #private;
41
+ constructor(options?: ReplyWriteOptions);
42
+ updateArgs(args: unknown): void;
43
+ setArgsComplete(): void;
44
+ setExecutionStarted(): void;
45
+ updateResult(result: ToolResultFrame, isPartial?: boolean): void;
46
+ parkAsBackground(): void;
47
+ seal(): void;
48
+ }
@@ -19,10 +19,11 @@ export interface TableChartRequest {
19
19
  readonly guess: ChartPlan;
20
20
  }
21
21
  /**
22
- * Picks the chart for a multi-series table under `smart`: a plan, or `null`
23
- * for no chart. A rejection falls back to the local guess.
22
+ * Picks the chart for a table under `smart`: a plan, or `null` for no chart.
23
+ * A rejection falls back to the local guess; `undefined` (no judge to ask
24
+ * right now) keeps the guess at once, exactly as `always` would draw it.
24
25
  */
25
- export type TableChartPlanner = (request: TableChartRequest) => Promise<ChartPlan | null>;
26
+ export type TableChartPlanner = (request: TableChartRequest) => Promise<ChartPlan | null> | undefined;
26
27
  /**
27
28
  * The chart under a table: its SVG, colored with figure tokens (`var(--accent)`, …)
28
29
  * still to resolve; none; or a smart pick on its way (the promise settles once the cache holds it).
@@ -35,7 +35,6 @@ export interface ToolExecutionHandle extends Component {
35
35
  setArgsComplete(toolCallId?: string): void;
36
36
  setExecutionStarted(toolCallId?: string): void;
37
37
  setExpanded(expanded: boolean): void;
38
- setToolActivityVisible(visible: boolean): void;
39
38
  /** Mark the call parked: it returned, but stays tracked for async job frames. */
40
39
  parkAsBackground(): void;
41
40
  /** Seal the block as final history and stop its animations. */
@@ -102,7 +101,7 @@ export declare class ToolExecutionComponent extends Container {
102
101
  * component-scoped so the TUI reuses every other root subtree (issue #4377).
103
102
  */
104
103
  tickSpinner(frame: number): void;
105
- /** Whether the result's figure still waits for its raster; the transcript holds retirement meanwhile. */
104
+ /** Hold visible results until their figures and Kitty images are ready for immutable scrollback. */
106
105
  isTranscriptBlockPending(): boolean;
107
106
  /**
108
107
  * Whether this block is ready to retire as immutable history. Partial
@@ -9,6 +9,7 @@ import { type TranscriptEntryLike as TranscriptEntry } from "./transcript-entry.
9
9
  import type { SessionMessageEntryLike as SessionMessageEntry } from "./transcript-entry.js";
10
10
  import { type ThemeColor } from "../theme/index.js";
11
11
  import type { ChatTranscriptBuilder } from "./chat-transcript-builder.js";
12
+ import type { DescribeContext } from "../native/node.js";
12
13
  /** One selectable transcript item: a message entry plus its rendered block range. */
13
14
  export interface OutlineTarget {
14
15
  /** Entry the selection resolves to; extended over trailing componentless tool results. */
@@ -63,6 +64,12 @@ export declare class OutlineRowCache {
63
64
  export declare function appendOutlineEntries(builder: ChatTranscriptBuilder, entries: TranscriptEntry[]): OutlineTarget[];
64
65
  /** Per-target "renders at least one non-blank row" flags at the given rows. */
65
66
  export declare function outlineVisibility(childRows: readonly (readonly string[])[], targets: readonly OutlineTarget[]): boolean[];
67
+ /**
68
+ * Per-target "describes something that shows" flags: {@link outlineVisibility}
69
+ * for a TSP page, where blocks describe nodes instead of rendering rows. An
70
+ * empty `col`/`row` (an interrupted turn with no output) shows nothing.
71
+ */
72
+ export declare function describedVisibility(blocks: readonly Component[], targets: readonly OutlineTarget[], cx: DescribeContext): boolean[];
66
73
  /** Dotted horizontal rule with rounded corners, spanning the outline width. */
67
74
  export declare function outlineRule(left: string, right: string, innerWidth: number, color?: ThemeColor, caption?: string): string;
68
75
  /** Wrap pre-rendered rows in the dotted outline (rules above/below, `┆` sides). */
@@ -55,6 +55,8 @@ export declare function assistantHasVisibleContent(message: AssistantAgentMessag
55
55
  export declare function splitAssistantMessageToolTimeline(message: AssistantAgentMessage): {
56
56
  beforeTools: AssistantAgentMessage;
57
57
  afterToolCalls: ReadonlyMap<string, AssistantAgentMessage>;
58
+ /** Index in `message.content` of each after-tool segment's first block, by tool-call id. */
59
+ contentOffsets: ReadonlyMap<string, number>;
58
60
  hasToolCalls: boolean;
59
61
  lastToolCallId?: string;
60
62
  };
@@ -16,5 +16,6 @@ export * from "./shared.js";
16
16
  export * from "./streaming-panel.js";
17
17
  export * from "./tool-activity.js";
18
18
  export * from "./transcript-container.js";
19
+ export * from "./transcript-notice.js";
19
20
  export * from "./transcript-status.js";
20
21
  export * from "./visual-truncate.js";
@@ -0,0 +1,35 @@
1
+ import type { TspText } from "@oh-my-pi/pi-wire";
2
+ import type { NativeNode } from "../native/node.js";
3
+ import type { Component } from "../tui.js";
4
+ import { TranscriptBlock } from "./transcript-container.js";
5
+ /** How loud an inline transcript notice is; a native terminal styles it as `omp.notice.<tone>`. */
6
+ export type NoticeTone = "info" | "success" | "warning" | "error";
7
+ /** Optional parts of a {@link noticeRow}. */
8
+ export interface NoticeRowOptions {
9
+ /** Icon name in place of the tone's own (`read` for a file read, `download` for an update). */
10
+ readonly icon?: string;
11
+ readonly key?: string;
12
+ }
13
+ /**
14
+ * One inline transcript notice for a native terminal (`omp.notice.<tone>`): a
15
+ * leading icon beside the wrapped message. Warnings, errors, background-job
16
+ * completions and prompt file reads all draw as this row, so they read alike.
17
+ * The tone colors the icon (an info notice keeps it quiet); the message spans
18
+ * carry emphasis only (`path`, `mono`, `dim`), never the tone itself.
19
+ */
20
+ export declare function noticeRow(tone: NoticeTone, message: TspText, options?: NoticeRowOptions): NativeNode;
21
+ /**
22
+ * A transcript block that a native terminal draws as one {@link noticeRow}
23
+ * while a text terminal draws `ansi`.
24
+ */
25
+ export declare class TranscriptNotice extends TranscriptBlock {
26
+ #private;
27
+ constructor(native: NativeNode, ansi: readonly Component[]);
28
+ /**
29
+ * `showWarning` / `showError`: a blank row and the `Warning: …` / `Error: …`
30
+ * line in its color in a text terminal; natively the tone's icon and the
31
+ * message, the label left to the icon.
32
+ */
33
+ static line(tone: "warning" | "error", message: string): TranscriptNotice;
34
+ describe(): NativeNode;
35
+ }