@linxiraos/pi-tui 1.1.27 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (373) hide show
  1. package/CHANGELOG.md +47 -463
  2. package/README.md +7 -5
  3. package/dist/types/app-keybindings.d.ts +1 -1
  4. package/dist/types/apps/cleanse-board.d.ts +1 -1
  5. package/dist/types/apps/debug/log-viewer.d.ts +4 -0
  6. package/dist/types/apps/debug/raw-sse-buffer.d.ts +2 -0
  7. package/dist/types/apps/git/git-tui.d.ts +2 -2
  8. package/dist/types/apps/git/help.d.ts +3 -1
  9. package/dist/types/apps/if-bench-board.d.ts +2 -2
  10. package/dist/types/apps/setup-model-picker.d.ts +1 -1
  11. package/dist/types/autocomplete.d.ts +5 -2
  12. package/dist/types/charts/chart-plan.d.ts +674 -0
  13. package/dist/types/charts/chart-svg.d.ts +59 -0
  14. package/dist/types/charts/table-data.d.ts +175 -0
  15. package/dist/types/chat/assistant-message.d.ts +5 -0
  16. package/dist/types/chat/chat-transcript-builder.d.ts +2 -0
  17. package/dist/types/chat/compaction-summary-message.d.ts +1 -0
  18. package/dist/types/chat/execution-shared.d.ts +6 -5
  19. package/dist/types/chat/fence-figure.d.ts +42 -0
  20. package/dist/types/chat/figure-markdown.d.ts +42 -0
  21. package/dist/types/chat/messages.d.ts +1 -1
  22. package/dist/types/chat/svg-figure.d.ts +33 -0
  23. package/dist/types/chat/svg-source.d.ts +49 -0
  24. package/dist/types/chat/table-chart.d.ts +89 -0
  25. package/dist/types/chat/tool-execution.d.ts +2 -0
  26. package/dist/types/chat/user-message.d.ts +15 -2
  27. package/dist/types/chrome/shared.d.ts +9 -0
  28. package/dist/types/chrome/transcript-container.d.ts +6 -0
  29. package/dist/types/components/box.d.ts +1 -0
  30. package/dist/types/components/disclosure.d.ts +1 -0
  31. package/dist/types/components/editor.d.ts +15 -4
  32. package/dist/types/components/form.d.ts +1 -0
  33. package/dist/types/components/image.d.ts +12 -44
  34. package/dist/types/components/key-value-list.d.ts +1 -0
  35. package/dist/types/components/layout/row.d.ts +1 -0
  36. package/dist/types/components/layout/stack.d.ts +1 -0
  37. package/dist/types/components/markdown.d.ts +36 -3
  38. package/dist/types/components/scroll-view.d.ts +1 -0
  39. package/dist/types/components/section.d.ts +1 -0
  40. package/dist/types/components/table.d.ts +1 -0
  41. package/dist/types/components/text.d.ts +1 -0
  42. package/dist/types/components/tree-view.d.ts +7 -0
  43. package/dist/types/components/truncated-text.d.ts +1 -0
  44. package/dist/types/fuzzy.d.ts +29 -1
  45. package/dist/types/key-hint-format.d.ts +1 -1
  46. package/dist/types/keybinding-matchers.d.ts +3 -0
  47. package/dist/types/keybindings.d.ts +1 -1
  48. package/dist/types/kitty-graphics.d.ts +2 -2
  49. package/dist/types/loop-watchdog.d.ts +8 -7
  50. package/dist/types/multiplexers/cmux.d.ts +16 -0
  51. package/dist/types/multiplexers/herdr.d.ts +26 -0
  52. package/dist/types/multiplexers/notify.d.ts +13 -0
  53. package/dist/types/multiplexers/orca.d.ts +12 -0
  54. package/dist/types/multiplexers/screen.d.ts +9 -0
  55. package/dist/types/multiplexers/tmux.d.ts +29 -0
  56. package/dist/types/multiplexers/types.d.ts +100 -0
  57. package/dist/types/multiplexers/wmux.d.ts +12 -0
  58. package/dist/types/multiplexers/zellij.d.ts +12 -0
  59. package/dist/types/native/backend.d.ts +3 -2
  60. package/dist/types/native/blobs.d.ts +7 -3
  61. package/dist/types/native/encode.d.ts +3 -4
  62. package/dist/types/native/overlay.d.ts +5 -0
  63. package/dist/types/overlays/agent-hub-projection.d.ts +6 -5
  64. package/dist/types/overlays/agent-hub-types.d.ts +9 -0
  65. package/dist/types/overlays/agent-hub.d.ts +2 -0
  66. package/dist/types/overlays/agent-transcript-viewer.d.ts +11 -2
  67. package/dist/types/overlays/agents-hub.d.ts +9 -0
  68. package/dist/types/overlays/annotation-overlay.d.ts +19 -2
  69. package/dist/types/overlays/annotation-types.d.ts +2 -0
  70. package/dist/types/overlays/ask-dialog.d.ts +5 -2
  71. package/dist/types/overlays/history-search.d.ts +10 -0
  72. package/dist/types/overlays/hook-selector.d.ts +9 -4
  73. package/dist/types/overlays/jobs-panel.d.ts +1 -7
  74. package/dist/types/overlays/model-browser.d.ts +54 -7
  75. package/dist/types/overlays/model-hub.d.ts +23 -0
  76. package/dist/types/overlays/oauth-selector.d.ts +0 -1
  77. package/dist/types/overlays/plan-review-overlay.d.ts +3 -2
  78. package/dist/types/overlays/session-observer-registry.d.ts +2 -0
  79. package/dist/types/overlays/session-selector.d.ts +6 -2
  80. package/dist/types/overlays/settings-defs.d.ts +1 -0
  81. package/dist/types/overlays/usage-dashboard.d.ts +8 -4
  82. package/dist/types/overlays/usage-display.d.ts +3 -1
  83. package/dist/types/prompt/composer.d.ts +1 -1
  84. package/dist/types/prompt/custom-editor.d.ts +11 -8
  85. package/dist/types/prompt/image-source.d.ts +2 -1
  86. package/dist/types/prompt/interactive-context-helpers.d.ts +4 -1
  87. package/dist/types/prompt/model-mention-autocomplete.d.ts +1 -1
  88. package/dist/types/prompt/welcome.d.ts +6 -1
  89. package/dist/types/render/composite.d.ts +2 -0
  90. package/dist/types/render/output-pane.d.ts +1 -0
  91. package/dist/types/render/render-utils.d.ts +1 -0
  92. package/dist/types/render/tool-card.d.ts +6 -0
  93. package/dist/types/render/width-aware-text.d.ts +2 -0
  94. package/dist/types/setup/scenes/types.d.ts +1 -0
  95. package/dist/types/status-line/component.d.ts +3 -0
  96. package/dist/types/status-line/host.d.ts +4 -1
  97. package/dist/types/status-line/segments.d.ts +2 -0
  98. package/dist/types/status-line/types.d.ts +14 -3
  99. package/dist/types/terminal-capabilities.d.ts +27 -35
  100. package/dist/types/terminal-multiplexer.d.ts +127 -14
  101. package/dist/types/terminal.d.ts +22 -6
  102. package/dist/types/theme/active-symbols.d.ts +1 -1
  103. package/dist/types/theme/schema.d.ts +11 -2
  104. package/dist/types/theme/symbols.d.ts +1 -1
  105. package/dist/types/theme/theme-class.d.ts +7 -0
  106. package/dist/types/theme/theme.d.ts +1 -1
  107. package/dist/types/theme/tui-adapters.d.ts +3 -0
  108. package/dist/types/tmux.d.ts +11 -4
  109. package/dist/types/tools/daemon.d.ts +1 -1
  110. package/dist/types/tools/eval.d.ts +22 -3
  111. package/dist/types/tools/glob.d.ts +4 -0
  112. package/dist/types/tools/native-view.d.ts +47 -0
  113. package/dist/types/tools/output-meta.d.ts +11 -1
  114. package/dist/types/tools/renderer.d.ts +37 -2
  115. package/dist/types/tools/streaming-output.d.ts +20 -1
  116. package/dist/types/tools/task.d.ts +0 -4
  117. package/dist/types/tools/write.d.ts +9 -2
  118. package/dist/types/tools/yield.d.ts +22 -0
  119. package/dist/types/tui.d.ts +31 -0
  120. package/dist/types/utils.d.ts +6 -0
  121. package/dist/types/vim.d.ts +19 -5
  122. package/package.json +13 -9
  123. package/src/app-keybindings.d.ts +1 -1
  124. package/src/app-keybindings.ts +1 -1
  125. package/src/apps/autoresearch-dashboard.ts +7 -3
  126. package/src/apps/cleanse-board.ts +2 -2
  127. package/src/apps/cleanse-picker.ts +1 -1
  128. package/src/apps/debug/log-viewer.ts +188 -72
  129. package/src/apps/debug/raw-sse-buffer.ts +8 -0
  130. package/src/apps/debug/raw-sse.ts +39 -5
  131. package/src/apps/git/diff-pane.ts +26 -5
  132. package/src/apps/git/git-tui.ts +2 -2
  133. package/src/apps/git/help.ts +29 -20
  134. package/src/apps/if-bench-board.ts +2 -2
  135. package/src/apps/ps-top.ts +2 -2
  136. package/src/apps/setup-model-picker.ts +1 -1
  137. package/src/autocomplete.d.ts +5 -2
  138. package/src/autocomplete.ts +67 -43
  139. package/src/charts/chart-plan.d.ts +674 -0
  140. package/src/charts/chart-plan.ts +5081 -0
  141. package/src/charts/chart-svg.d.ts +59 -0
  142. package/src/charts/chart-svg.ts +4657 -0
  143. package/src/charts/table-data.d.ts +175 -0
  144. package/src/charts/table-data.ts +1478 -0
  145. package/src/chat/advisor-message.ts +11 -0
  146. package/src/chat/assistant-message.d.ts +5 -0
  147. package/src/chat/assistant-message.ts +219 -50
  148. package/src/chat/chat-transcript-builder.d.ts +2 -0
  149. package/src/chat/chat-transcript-builder.ts +5 -3
  150. package/src/chat/compaction-summary-message.d.ts +1 -0
  151. package/src/chat/compaction-summary-message.ts +8 -0
  152. package/src/chat/execution-shared.d.ts +6 -5
  153. package/src/chat/execution-shared.ts +42 -25
  154. package/src/chat/fence-figure.d.ts +42 -0
  155. package/src/chat/fence-figure.ts +80 -0
  156. package/src/chat/figure-markdown.d.ts +42 -0
  157. package/src/chat/figure-markdown.ts +174 -0
  158. package/src/chat/messages.d.ts +1 -1
  159. package/src/chat/messages.ts +1 -1
  160. package/src/chat/read-tool-group.ts +6 -4
  161. package/src/chat/skill-message.ts +6 -0
  162. package/src/chat/svg-figure.d.ts +33 -0
  163. package/src/chat/svg-figure.ts +399 -0
  164. package/src/chat/svg-source.d.ts +49 -0
  165. package/src/chat/svg-source.ts +188 -0
  166. package/src/chat/table-chart.d.ts +89 -0
  167. package/src/chat/table-chart.ts +294 -0
  168. package/src/chat/tool-execution.d.ts +2 -0
  169. package/src/chat/tool-execution.ts +80 -6
  170. package/src/chat/user-message.d.ts +15 -2
  171. package/src/chat/user-message.ts +55 -3
  172. package/src/chrome/shared.ts +53 -0
  173. package/src/chrome/transcript-container.d.ts +6 -0
  174. package/src/chrome/transcript-container.ts +93 -13
  175. package/src/components/box.d.ts +1 -0
  176. package/src/components/box.ts +5 -0
  177. package/src/components/disclosure.d.ts +1 -0
  178. package/src/components/disclosure.ts +8 -0
  179. package/src/components/editor.d.ts +15 -4
  180. package/src/components/editor.ts +294 -46
  181. package/src/components/form.d.ts +1 -0
  182. package/src/components/form.ts +8 -0
  183. package/src/components/image.d.ts +12 -44
  184. package/src/components/image.ts +47 -151
  185. package/src/components/key-value-list.d.ts +1 -0
  186. package/src/components/key-value-list.ts +4 -0
  187. package/src/components/layout/row.d.ts +1 -0
  188. package/src/components/layout/row.ts +7 -0
  189. package/src/components/layout/stack.d.ts +1 -0
  190. package/src/components/layout/stack.ts +7 -0
  191. package/src/components/markdown.d.ts +36 -3
  192. package/src/components/markdown.ts +433 -318
  193. package/src/components/scroll-view.d.ts +1 -0
  194. package/src/components/scroll-view.ts +5 -0
  195. package/src/components/section.d.ts +1 -0
  196. package/src/components/section.ts +5 -0
  197. package/src/components/settings-list.ts +2 -0
  198. package/src/components/table.d.ts +1 -0
  199. package/src/components/table.ts +4 -0
  200. package/src/components/text.d.ts +1 -0
  201. package/src/components/text.ts +7 -0
  202. package/src/components/tree-view.d.ts +7 -0
  203. package/src/components/tree-view.ts +38 -8
  204. package/src/components/truncated-text.d.ts +1 -0
  205. package/src/components/truncated-text.ts +5 -0
  206. package/src/desktop-notify.ts +1 -1
  207. package/src/fuzzy.d.ts +29 -1
  208. package/src/fuzzy.ts +115 -30
  209. package/src/key-hint-format.d.ts +1 -1
  210. package/src/key-hint-format.ts +1 -1
  211. package/src/keybinding-matchers.d.ts +3 -0
  212. package/src/keybinding-matchers.ts +6 -2
  213. package/src/keybindings.d.ts +1 -1
  214. package/src/keybindings.ts +1 -1
  215. package/src/kitty-graphics.d.ts +2 -2
  216. package/src/kitty-graphics.ts +4 -4
  217. package/src/loop-watchdog.d.ts +8 -7
  218. package/src/loop-watchdog.ts +18 -13
  219. package/src/multiplexers/cmux.d.ts +16 -0
  220. package/src/multiplexers/cmux.ts +29 -0
  221. package/src/multiplexers/herdr.d.ts +26 -0
  222. package/src/multiplexers/herdr.ts +64 -0
  223. package/src/multiplexers/notify.d.ts +13 -0
  224. package/src/multiplexers/notify.ts +24 -0
  225. package/src/multiplexers/orca.d.ts +12 -0
  226. package/src/multiplexers/orca.ts +16 -0
  227. package/src/multiplexers/screen.d.ts +9 -0
  228. package/src/multiplexers/screen.ts +15 -0
  229. package/src/multiplexers/tmux.d.ts +29 -0
  230. package/src/multiplexers/tmux.ts +40 -0
  231. package/src/multiplexers/types.d.ts +100 -0
  232. package/src/multiplexers/types.ts +110 -0
  233. package/src/multiplexers/wmux.d.ts +12 -0
  234. package/src/multiplexers/wmux.ts +16 -0
  235. package/src/multiplexers/zellij.d.ts +12 -0
  236. package/src/multiplexers/zellij.ts +24 -0
  237. package/src/native/backend.d.ts +3 -2
  238. package/src/native/backend.ts +174 -18
  239. package/src/native/blobs.d.ts +7 -3
  240. package/src/native/blobs.ts +47 -12
  241. package/src/native/encode.d.ts +3 -4
  242. package/src/native/encode.ts +58 -31
  243. package/src/native/overlay.d.ts +5 -0
  244. package/src/native/overlay.ts +13 -0
  245. package/src/native/reconcile.ts +4 -0
  246. package/src/overlays/agent-hub-projection.d.ts +6 -5
  247. package/src/overlays/agent-hub-projection.ts +124 -13
  248. package/src/overlays/agent-hub-types.d.ts +9 -0
  249. package/src/overlays/agent-hub-types.ts +6 -0
  250. package/src/overlays/agent-hub.ts +44 -29
  251. package/src/overlays/agent-transcript-viewer.ts +91 -23
  252. package/src/overlays/agents-hub.ts +28 -8
  253. package/src/overlays/annotation-overlay.ts +293 -90
  254. package/src/overlays/annotation-types.ts +2 -0
  255. package/src/overlays/ask-dialog.ts +7 -10
  256. package/src/overlays/btw-history-panel.ts +12 -1
  257. package/src/overlays/copy-selector.ts +30 -8
  258. package/src/overlays/extensions/state-manager.ts +52 -12
  259. package/src/overlays/history-search.ts +100 -7
  260. package/src/overlays/hook-selector.ts +12 -5
  261. package/src/overlays/jobs-panel.ts +28 -46
  262. package/src/overlays/model-browser.ts +341 -110
  263. package/src/overlays/model-hub.ts +300 -49
  264. package/src/overlays/move-overlay.ts +28 -14
  265. package/src/overlays/oauth-selector.ts +54 -70
  266. package/src/overlays/plan-review-overlay.ts +194 -70
  267. package/src/overlays/plugin-settings.ts +3 -3
  268. package/src/overlays/report-panel.ts +2 -2
  269. package/src/overlays/rewind-selector.ts +86 -17
  270. package/src/overlays/session-observer-registry.d.ts +2 -0
  271. package/src/overlays/session-observer-registry.ts +10 -2
  272. package/src/overlays/session-selector.ts +64 -26
  273. package/src/overlays/settings-defs.ts +4 -3
  274. package/src/overlays/settings-selector.ts +33 -8
  275. package/src/overlays/snapcompact-shape-preview.ts +2 -3
  276. package/src/overlays/stats-notice.ts +1 -1
  277. package/src/overlays/tree-selector.ts +41 -20
  278. package/src/overlays/usage-dashboard.ts +100 -50
  279. package/src/overlays/usage-display.ts +10 -4
  280. package/src/prompt/attachment-chips.ts +15 -11
  281. package/src/prompt/composer-attachments.ts +6 -4
  282. package/src/prompt/composer.ts +4 -15
  283. package/src/prompt/custom-editor.ts +54 -26
  284. package/src/prompt/image-source.ts +2 -1
  285. package/src/prompt/interactive-context-helpers.ts +30 -18
  286. package/src/prompt/macos-spelling.ts +7 -0
  287. package/src/prompt/magic-keywords.ts +11 -2
  288. package/src/prompt/model-mention-autocomplete.ts +14 -14
  289. package/src/prompt/tips.txt +6 -5
  290. package/src/prompt/welcome.ts +55 -12
  291. package/src/prompt/word-completion.ts +38 -5
  292. package/src/render/composite.d.ts +2 -0
  293. package/src/render/composite.ts +64 -0
  294. package/src/render/output-pane.d.ts +1 -0
  295. package/src/render/output-pane.ts +6 -0
  296. package/src/render/render-utils.d.ts +1 -0
  297. package/src/render/render-utils.ts +11 -0
  298. package/src/render/tool-card.d.ts +6 -0
  299. package/src/render/tool-card.ts +15 -0
  300. package/src/render/width-aware-text.d.ts +2 -0
  301. package/src/render/width-aware-text.ts +5 -0
  302. package/src/setup/scenes/model.ts +2 -1
  303. package/src/setup/scenes/types.ts +1 -0
  304. package/src/status-line/component.ts +292 -78
  305. package/src/status-line/host.ts +4 -1
  306. package/src/status-line/segments.ts +40 -22
  307. package/src/status-line/types.ts +15 -3
  308. package/src/terminal-capabilities.d.ts +27 -35
  309. package/src/terminal-capabilities.ts +75 -189
  310. package/src/terminal-multiplexer.d.ts +127 -14
  311. package/src/terminal-multiplexer.ts +112 -33
  312. package/src/terminal.d.ts +22 -6
  313. package/src/terminal.ts +139 -37
  314. package/src/theme/active-symbols.d.ts +1 -1
  315. package/src/theme/active-symbols.ts +1 -1
  316. package/src/theme/schema-validation.ts +1 -0
  317. package/src/theme/schema.d.ts +11 -2
  318. package/src/theme/schema.ts +14 -1
  319. package/src/theme/session-color.ts +1 -7
  320. package/src/theme/symbols.d.ts +1 -1
  321. package/src/theme/symbols.ts +6 -1
  322. package/src/theme/theme-class.d.ts +7 -0
  323. package/src/theme/theme-class.ts +26 -1
  324. package/src/theme/theme-schema.json +5 -1
  325. package/src/theme/theme.d.ts +1 -1
  326. package/src/theme/theme.ts +8 -4
  327. package/src/theme/tui-adapters.d.ts +3 -0
  328. package/src/theme/tui-adapters.ts +68 -0
  329. package/src/tmux.d.ts +11 -4
  330. package/src/tmux.ts +47 -11
  331. package/src/tools/ast-edit.ts +59 -35
  332. package/src/tools/ast-grep.ts +35 -16
  333. package/src/tools/bash.ts +92 -42
  334. package/src/tools/daemon.d.ts +1 -1
  335. package/src/tools/daemon.ts +1 -1
  336. package/src/tools/default-renderer.ts +35 -19
  337. package/src/tools/edit.ts +142 -120
  338. package/src/tools/eval-format/index.ts +28 -2
  339. package/src/tools/eval-format/javascript.ts +7 -5
  340. package/src/tools/eval.d.ts +22 -3
  341. package/src/tools/eval.ts +364 -208
  342. package/src/tools/fetch.ts +5 -6
  343. package/src/tools/glob.d.ts +4 -0
  344. package/src/tools/glob.ts +33 -65
  345. package/src/tools/grep.ts +81 -48
  346. package/src/tools/index.ts +2 -0
  347. package/src/tools/lsp.ts +81 -59
  348. package/src/tools/mcp.ts +111 -68
  349. package/src/tools/native-view.d.ts +47 -0
  350. package/src/tools/native-view.ts +102 -1
  351. package/src/tools/output-meta.d.ts +11 -1
  352. package/src/tools/output-meta.ts +23 -3
  353. package/src/tools/read.ts +7 -5
  354. package/src/tools/renderer.d.ts +37 -2
  355. package/src/tools/renderer.ts +42 -2
  356. package/src/tools/streaming-output.d.ts +20 -1
  357. package/src/tools/streaming-output.ts +149 -50
  358. package/src/tools/task.d.ts +0 -4
  359. package/src/tools/task.ts +112 -59
  360. package/src/tools/todo.ts +119 -30
  361. package/src/tools/wait.ts +34 -24
  362. package/src/tools/web-search.ts +1 -1
  363. package/src/tools/write.d.ts +9 -2
  364. package/src/tools/write.ts +147 -95
  365. package/src/tools/yield.d.ts +22 -0
  366. package/src/tools/yield.ts +225 -0
  367. package/src/tui.d.ts +31 -0
  368. package/src/tui.ts +309 -160
  369. package/src/utils.d.ts +6 -0
  370. package/src/utils.ts +19 -0
  371. package/src/vim.d.ts +19 -5
  372. package/src/vim.ts +333 -18
  373. package/src/windows-input-mode.ts +8 -0
@@ -29,6 +29,8 @@ export interface ImageOptions {
29
29
  * synchronously instead of committing its reserved blank rows.
30
30
  */
31
31
  export declare function renderForScrollback<T>(render: () => T): T;
32
+ /** Widest an {@link Image} rendered at `width` columns draws; callers sizing a raster for it (`SvgFigure`) match it. */
33
+ export declare function imageMaxColumns(width: number): number;
32
34
  /** Default count of inline images kept as live graphics before older ones fall back to text. */
33
35
  export declare const DEFAULT_MAX_INLINE_IMAGES = 8;
34
36
  /**
@@ -73,6 +75,13 @@ export declare class ImageBudget {
73
75
  * gets a fresh id every call.
74
76
  */
75
77
  acquireId(key?: string): number;
78
+ /**
79
+ * Retire the graphic held under `key` as soon as no frame shows it, instead
80
+ * of when the residency sweep reaches it. For an owner that replaced the
81
+ * image for good — a streaming SVG figure's earlier raster — so superseded
82
+ * revisions never push older images (scrollback included) out of the store.
83
+ */
84
+ release(key: string): void;
76
85
  /**
77
86
  * Start an alternate-buffer lifecycle. Call once per `?1049h`, before the
78
87
  * first pass of the fullscreen overlay or resize borrow that owns the buffer.
@@ -140,50 +149,8 @@ export declare class ImageBudget {
140
149
  * the image is purged from the terminal store.
141
150
  */
142
151
  registerPlacementGeometry(imageId: number, widthPx: number, heightPx: number): void;
143
- /**
144
- * Record this frame's native-scrollback commit target (the frame-row count
145
- * that is committed once the frame's writes land). Called once per rendered
146
- * frame — including frames that emit no placements — so an epoch whose rows
147
- * commit while its line is never rewritten is still flagged before the next
148
- * re-emission.
149
- */
150
- observeCommitWatermark(committedTo: number): void;
151
- /**
152
- * End the physical-row coordinate epoch after observing its final commit
153
- * watermark. Placement ids and latched archive state survive, but attachment
154
- * rows do not: the next placement emit records them in the new-width frame.
155
- */
156
- beginPlacementCoordinateEpoch(): void;
157
- /**
158
- * Resolve the placement id and geometry for a direct-placement emit whose
159
- * topmost attached cell sits at `attachTopFrameRow` — the first frame row
160
- * the placement covers, i.e. the block's first *visible* row, not its
161
- * origin (-1 when the writer has no frame-space position: alt-screen,
162
- * resize, ConPTY-truncated replays). `committedTo` is this frame's commit
163
- * target in the same frame-row space (-1 when unknown).
164
- *
165
- * Invariant: a placement id may be re-used (Kitty replace strips that id's
166
- * cells everywhere, scrollback included) only while none of the cells it
167
- * attached have entered native scrollback. The epoch — the `p=` id —
168
- * advances exactly when the archived flag says otherwise; rewrites with no
169
- * commit progression keep replacing the same id in place.
170
- */
171
- resolvePlacementEmit(imageId: number, attachTopFrameRow: number, committedTo: number): {
172
- placementId: number;
173
- widthPx: number;
174
- heightPx: number;
175
- } | null;
176
- /**
177
- * Restart every placement epoch after a destructive history clear (`CSI 3 J`
178
- * full paint). The clear destroys all placement cells — scrollback rows are
179
- * gone and the replay rewrites the viewport — so no archive remains to
180
- * protect. Reverting to epoch 1 lets the replay recreate every visible
181
- * placement after the terminal-wide cleanup.
182
- */
183
- resetPlacementEpochs(): ReadonlyArray<{
184
- imageId: number;
185
- lastEpoch: number;
186
- }>;
152
+ /** Source dimensions used by the frame writer to clip a direct placement. */
153
+ getPlacementGeometry(imageId: number): ImageDimensions | undefined;
187
154
  /**
188
155
  * Queue a one-time transmit for `imageId` on the surface the in-flight pass
189
156
  * paints. No-op if that surface already holds the data, so a repeated call
@@ -218,6 +185,7 @@ export declare class Image implements Component {
218
185
  /** Return source metadata without exposing the encoded image buffer. */
219
186
  debugState(): Record<string, unknown>;
220
187
  invalidate(): void;
188
+ releaseRenderCaches(): void;
221
189
  /**
222
190
  * A native `image` backed by a content-addressed blob; the terminal fits
223
191
  * it. Cell caps become `ch`/`lines` bounds. The inline-image budget and
@@ -10,8 +10,7 @@ import {
10
10
  renderImage,
11
11
  TERMINAL,
12
12
  } from "../terminal-capabilities";
13
- import { registerNativeBlob } from "../native/blobs";
14
- import { node } from "../native/describe";
13
+ import { nativeImageNode } from "../native/blobs";
15
14
  import type { DescribeContext, NativeNode } from "../native/node";
16
15
  import type { Component } from "../tui";
17
16
 
@@ -58,35 +57,20 @@ export function renderForScrollback<T>(render: () => T): T {
58
57
 
59
58
  const EMPTY_IDS: readonly number[] = [];
60
59
  const EMPTY_TRANSMITS: readonly string[] = [];
61
- const EMPTY_STALE_EPOCHS: ReadonlyArray<{ imageId: number; lastEpoch: number }> = [];
62
60
  const SAVE_CURSOR = "\x1b7";
63
61
  const RESTORE_CURSOR = "\x1b8";
64
62
  // Direct placements reserve height with leading zero-width rows. Keep them
65
63
  // non-plain so transcript blank-edge trimming does not collapse image-only blocks.
66
64
  const RESERVED_IMAGE_ROW = "\x1b[0m";
67
65
 
66
+ /** Widest an {@link Image} rendered at `width` columns draws; callers sizing a raster for it (`SvgFigure`) match it. */
67
+ export function imageMaxColumns(width: number): number {
68
+ return Math.max(1, width - 2);
69
+ }
70
+
68
71
  /** Default count of inline images kept as live graphics before older ones fall back to text. */
69
72
  export const DEFAULT_MAX_INLINE_IMAGES = 8;
70
73
 
71
- /** Per-image direct-placement emit state tracked by {@link ImageBudget}. */
72
- interface PlacementEmitState {
73
- widthPx: number;
74
- heightPx: number;
75
- /** Current placement-id (`p=`) generation. */
76
- epoch: number;
77
- /** First frame row the current epoch's last emit attached cells to. */
78
- lastAttachTopFrameRow: number | undefined;
79
- /**
80
- * Whether any cell attached by the current epoch's last emit has entered
81
- * native scrollback. Set by {@link ImageBudget.observeCommitWatermark}
82
- * comparing each frame's raw commit target against the attach top —
83
- * era-local comparisons, so a divergence recommit that rewinds and
84
- * re-advances the ledger is detected the moment it re-crosses the attach
85
- * top, and a stale pre-rewind peak can never re-trigger.
86
- */
87
- cellsArchived: boolean;
88
- }
89
-
90
74
  /** A surface the renderer paints frames on. */
91
75
  type Surface = "screen" | "alt";
92
76
  const SURFACES: readonly Surface[] = ["screen", "alt"];
@@ -218,22 +202,10 @@ export class ImageBudget {
218
202
  * fullscreen overlay keeps its placements and is restored from cache on exit.
219
203
  */
220
204
  #liveIds: Record<Surface, Set<number>> = { screen: new Set(), alt: new Set() };
221
- /**
222
- * Per-image direct-placement emit state: source pixel geometry for the
223
- * renderer's clipped source rectangle, plus the placement-id epoch (see
224
- * {@link resolvePlacementEmit}). Entries deliberately live as long as the
225
- * terminal's own placement registry for the image — they are the ledger the
226
- * destructive-clear sweep uses to delete every registry entry an image ever
227
- * placed — and die with it on demotion purge (`d=I`) or full cleanup.
228
- */
229
- #placementState = new Map<number, PlacementEmitState>();
230
- /**
231
- * States with an un-archived live attach top — the only ones a frame's
232
- * commit watermark can affect. {@link observeCommitWatermark} runs every
233
- * rendered frame, so it scans this set (bounded by concurrently live
234
- * placements) instead of every image ever registered.
235
- */
236
- #watchedPlacements = new Set<PlacementEmitState>();
205
+ /** Source pixel dimensions for clipping direct placements at the viewport edge. */
206
+ #placementGeometry = new Map<number, ImageDimensions>();
207
+ /** Ids whose owner replaced them for good ({@link release}); retired once no frame shows them. */
208
+ #released = new Set<number>();
237
209
 
238
210
  constructor(cap: number = DEFAULT_MAX_INLINE_IMAGES, requestRender: () => void = () => {}) {
239
211
  this.#cap = normalizeCap(cap);
@@ -284,6 +256,17 @@ export class ImageBudget {
284
256
  return id;
285
257
  }
286
258
 
259
+ /**
260
+ * Retire the graphic held under `key` as soon as no frame shows it, instead
261
+ * of when the residency sweep reaches it. For an owner that replaced the
262
+ * image for good — a streaming SVG figure's earlier raster — so superseded
263
+ * revisions never push older images (scrollback included) out of the store.
264
+ */
265
+ release(key: string): void {
266
+ const id = this.#keyToId.get(key);
267
+ if (id !== undefined) this.#released.add(id);
268
+ }
269
+
287
270
  /**
288
271
  * Start an alternate-buffer lifecycle. Call once per `?1049h`, before the
289
272
  * first pass of the fullscreen overlay or resize borrow that owns the buffer.
@@ -405,6 +388,13 @@ export class ImageBudget {
405
388
  if (this.#passShowsLive(id)) liveIds.add(id);
406
389
  }
407
390
  const transmitted = this.#transmitted[this.#surface];
391
+ for (const id of this.#released) {
392
+ // #retire refuses while this pass or the other surface still shows it.
393
+ if (transmitted.has(id)) this.#retire(id);
394
+ if (this.#isTransmitted(id)) continue;
395
+ this.#forgetKeyForId(id);
396
+ this.#released.delete(id);
397
+ }
408
398
  if (this.#cap <= 0 || transmitted.size <= this.#cap) return;
409
399
  for (const id of transmitted) {
410
400
  if (transmitted.size <= this.#cap) break;
@@ -456,7 +446,7 @@ export class ImageBudget {
456
446
  // terminal, so cancel it instead of transmitting then purging.
457
447
  if (!this.#pendingTransmits[this.#surface].delete(imageId)) this.#purgeIds.push(imageId);
458
448
  this.#transmitted[this.#surface].delete(imageId);
459
- if (!this.#isTransmitted(imageId)) this.#deletePlacementState(imageId);
449
+ if (!this.#isTransmitted(imageId)) this.#placementGeometry.delete(imageId);
460
450
  this.#forgetKeyForId(imageId);
461
451
  return true;
462
452
  }
@@ -499,8 +489,8 @@ export class ImageBudget {
499
489
  this.#resetPurgeIds = [];
500
490
  this.#keyToId.clear();
501
491
  this.#idToKey.clear();
502
- this.#placementState.clear();
503
- this.#watchedPlacements.clear();
492
+ this.#placementGeometry.clear();
493
+ this.#released.clear();
504
494
  for (const surface of SURFACES) this.#liveIds[surface].clear();
505
495
  return [...ids];
506
496
  }
@@ -516,115 +506,18 @@ export class ImageBudget {
516
506
  * the image is purged from the terminal store.
517
507
  */
518
508
  registerPlacementGeometry(imageId: number, widthPx: number, heightPx: number): void {
519
- const state = this.#placementState.get(imageId);
520
- if (state) {
521
- state.widthPx = widthPx;
522
- state.heightPx = heightPx;
523
- return;
524
- }
525
- this.#placementState.set(imageId, {
526
- widthPx,
527
- heightPx,
528
- epoch: 1,
529
- lastAttachTopFrameRow: undefined,
530
- cellsArchived: false,
531
- });
532
- }
533
-
534
- /**
535
- * Record this frame's native-scrollback commit target (the frame-row count
536
- * that is committed once the frame's writes land). Called once per rendered
537
- * frame — including frames that emit no placements — so an epoch whose rows
538
- * commit while its line is never rewritten is still flagged before the next
539
- * re-emission.
540
- */
541
- observeCommitWatermark(committedTo: number): void {
542
- if (committedTo < 0 || this.#watchedPlacements.size === 0) return;
543
- for (const state of this.#watchedPlacements) {
544
- if (state.lastAttachTopFrameRow !== undefined && committedTo > state.lastAttachTopFrameRow) {
545
- // Latched: the flag only clears when the next emit consumes it,
546
- // so the state needs no further per-frame scans until then.
547
- state.cellsArchived = true;
548
- this.#watchedPlacements.delete(state);
549
- }
550
- }
551
- }
552
-
553
- /**
554
- * End the physical-row coordinate epoch after observing its final commit
555
- * watermark. Placement ids and latched archive state survive, but attachment
556
- * rows do not: the next placement emit records them in the new-width frame.
557
- */
558
- beginPlacementCoordinateEpoch(): void {
559
- for (const state of this.#placementState.values()) state.lastAttachTopFrameRow = undefined;
560
- this.#watchedPlacements.clear();
561
- }
562
-
563
- /**
564
- * Resolve the placement id and geometry for a direct-placement emit whose
565
- * topmost attached cell sits at `attachTopFrameRow` — the first frame row
566
- * the placement covers, i.e. the block's first *visible* row, not its
567
- * origin (-1 when the writer has no frame-space position: alt-screen,
568
- * resize, ConPTY-truncated replays). `committedTo` is this frame's commit
569
- * target in the same frame-row space (-1 when unknown).
570
- *
571
- * Invariant: a placement id may be re-used (Kitty replace strips that id's
572
- * cells everywhere, scrollback included) only while none of the cells it
573
- * attached have entered native scrollback. The epoch — the `p=` id —
574
- * advances exactly when the archived flag says otherwise; rewrites with no
575
- * commit progression keep replacing the same id in place.
576
- */
577
- resolvePlacementEmit(
578
- imageId: number,
579
- attachTopFrameRow: number,
580
- committedTo: number,
581
- ): { placementId: number; widthPx: number; heightPx: number } | null {
582
- const state = this.#placementState.get(imageId);
583
- if (!state) return null;
584
- // Frames that commit as they write (seam/full-paint chunk passes) pass
585
- // their own commit target; fold it in before deciding, so a commit that
586
- // lands in the same frame as the re-emission still advances the epoch.
587
- if (committedTo >= 0 && state.lastAttachTopFrameRow !== undefined && committedTo > state.lastAttachTopFrameRow) {
588
- state.cellsArchived = true;
589
- this.#watchedPlacements.delete(state);
590
- }
591
- if (state.cellsArchived) {
592
- state.epoch += 1;
593
- state.cellsArchived = false;
594
- state.lastAttachTopFrameRow = undefined;
595
- }
596
- if (attachTopFrameRow >= 0) {
597
- state.lastAttachTopFrameRow = attachTopFrameRow;
598
- this.#watchedPlacements.add(state);
599
- }
600
- return { placementId: state.epoch, widthPx: state.widthPx, heightPx: state.heightPx };
601
- }
602
-
603
- /**
604
- * Restart every placement epoch after a destructive history clear (`CSI 3 J`
605
- * full paint). The clear destroys all placement cells — scrollback rows are
606
- * gone and the replay rewrites the viewport — so no archive remains to
607
- * protect. Reverting to epoch 1 lets the replay recreate every visible
608
- * placement after the terminal-wide cleanup.
609
- */
610
- resetPlacementEpochs(): ReadonlyArray<{ imageId: number; lastEpoch: number }> {
611
- let stale: Array<{ imageId: number; lastEpoch: number }> | undefined;
612
- for (const [imageId, state] of this.#placementState) {
613
- stale ??= [];
614
- stale.push({ imageId, lastEpoch: state.epoch });
615
- state.epoch = 1;
616
- state.lastAttachTopFrameRow = undefined;
617
- state.cellsArchived = false;
509
+ const geometry = this.#placementGeometry.get(imageId);
510
+ if (geometry) {
511
+ geometry.widthPx = widthPx;
512
+ geometry.heightPx = heightPx;
513
+ } else {
514
+ this.#placementGeometry.set(imageId, { widthPx, heightPx });
618
515
  }
619
- this.#watchedPlacements.clear();
620
- return stale ?? EMPTY_STALE_EPOCHS;
621
516
  }
622
517
 
623
- #deletePlacementState(imageId: number): void {
624
- const state = this.#placementState.get(imageId);
625
- if (!state) return;
626
- this.#watchedPlacements.delete(state);
627
- this.#placementState.delete(imageId);
518
+ /** Source dimensions used by the frame writer to clip a direct placement. */
519
+ getPlacementGeometry(imageId: number): ImageDimensions | undefined {
520
+ return this.#placementGeometry.get(imageId);
628
521
  }
629
522
 
630
523
  /**
@@ -800,6 +693,11 @@ export class Image implements Component {
800
693
  this.#cachedWidth = undefined;
801
694
  }
802
695
 
696
+ releaseRenderCaches(): void {
697
+ this.#cachedLines = undefined;
698
+ this.#cachedWidth = undefined;
699
+ }
700
+
803
701
  /**
804
702
  * SIXEL sequence for a target size. A new size starts the encode off the JS
805
703
  * thread and answers `undefined` until it settles; the settled encode
@@ -839,11 +737,9 @@ export class Image implements Component {
839
737
  */
840
738
  describe(_cx: DescribeContext): NativeNode {
841
739
  if (this.#native) return this.#native;
842
- const blob = registerNativeBlob(Buffer.from(this.#base64Data, "base64"), this.#mimeType);
843
740
  const maxW = this.#options.maxWidthCells;
844
741
  const maxH = this.#options.maxHeightCells;
845
- this.#native = node("image", {
846
- blob,
742
+ this.#native = nativeImageNode(Buffer.from(this.#base64Data, "base64"), this.#mimeType, {
847
743
  alt: imageFallback(this.#mimeType, this.#dimensions, this.#options.filename),
848
744
  w: this.#dimensions.widthPx,
849
745
  h: this.#dimensions.heightPx,
@@ -892,7 +788,7 @@ export class Image implements Component {
892
788
  }
893
789
 
894
790
  const cap = this.#options.maxWidthCells;
895
- const maxWidth = cap != null && cap > 0 ? Math.min(width - 2, cap) : width - 2;
791
+ const maxWidth = cap != null && cap > 0 ? Math.min(imageMaxColumns(width), cap) : imageMaxColumns(width);
896
792
 
897
793
  let lines: string[];
898
794
 
@@ -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[];
@@ -54,6 +54,10 @@ export class KeyValueList implements Component {
54
54
  this.#native = undefined;
55
55
  }
56
56
 
57
+ releaseRenderCaches(): void {
58
+ this.#cache = undefined;
59
+ }
60
+
57
61
  /** An aligned `kv` grid; label width and value wrapping are the terminal's. */
58
62
  describe(_cx: DescribeContext): NativeNode {
59
63
  this.#native ??= node("kv", {
@@ -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
@@ -168,6 +168,13 @@ export class Row implements Component, MouseRoutable {
168
168
  }
169
169
  }
170
170
 
171
+ releaseRenderCaches(): void {
172
+ this.#memo = undefined;
173
+ for (const child of uniqueLayoutComponents(this.#children.map(item => item.content))) {
174
+ child.releaseRenderCaches?.();
175
+ }
176
+ }
177
+
171
178
  dispose(): void {
172
179
  for (const child of uniqueLayoutComponents(this.#children.map(item => item.content))) {
173
180
  child.dispose?.();
@@ -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
@@ -140,6 +140,13 @@ export class Stack implements Component, MouseRoutable {
140
140
  }
141
141
  }
142
142
 
143
+ releaseRenderCaches(): void {
144
+ this.#memo = undefined;
145
+ for (const child of uniqueLayoutComponents(this.#children.map(item => item.content))) {
146
+ child.releaseRenderCaches?.();
147
+ }
148
+ }
149
+
143
150
  dispose(): void {
144
151
  for (const child of uniqueLayoutComponents(this.#children.map(item => item.content))) {
145
152
  child.dispose?.();
@@ -1,13 +1,27 @@
1
+ import { type TokensList } from "@linxiraos/pi-utils/marked";
1
2
  import type { DescribeContext, NativeNode } from "../native/node";
2
3
  import type { SymbolTheme } from "../symbols";
3
4
  import type { Component } from "../tui";
4
5
  /** @internal exported for tests — the grown-line block-kind gate. */
5
6
  export declare function fastLineStartHazard(grownLine: string): boolean;
6
- /** @internal exported for tests — the windowed lexer's first-probe window size. */
7
- export declare const LEX_WINDOW_BYTES: number;
7
+ /**
8
+ * Lex a whole document with the renderer's rules (math, custom rules,
9
+ * strikethrough). Callers that split a document along its blocks (table
10
+ * charts) lex through this so they see the blocks Markdown renders.
11
+ */
12
+ export declare function lexDocument(text: string): TokensList;
13
+ /**
14
+ * `source` as a fenced code block in `lang`: the fence is one backtick longer
15
+ * than any backtick run in the source (at least three), so the block always
16
+ * closes where intended. `open` leaves it unclosed, the source verbatim, for
17
+ * a body that is still arriving.
18
+ */
19
+ export declare function fencedCode(lang: string, source: string, options?: {
20
+ open?: boolean;
21
+ }): string;
8
22
  /** A hyperlink as the renderer sees it: inline `[text](href)`, `<autolink>`, bare GFM URL, or reference link. */
9
23
  export interface MarkdownLink {
10
- /** Visible link text (equals `href` for autolinks and bare URLs). */
24
+ /** Flattened visible label with whitespace collapsed to one row; falls back to `href` when empty. */
11
25
  text: string;
12
26
  /** Destination exactly as marked resolved it (references resolved, no normalization). */
13
27
  href: string;
@@ -19,6 +33,17 @@ export interface MarkdownLink {
19
33
  * Duplicate hrefs are kept; callers decide how to fold them.
20
34
  */
21
35
  export declare function extractMarkdownLinks(text: string): MarkdownLink[];
36
+ /**
37
+ * Rewrite the destinations of inline links and link reference definitions in
38
+ * `text` to `resolve(href)`, leaving everything else byte-for-byte intact.
39
+ * Hosts that render the Markdown source themselves (a native terminal's `md`
40
+ * node) resolve relative destinations against their own idea of the working
41
+ * directory; handing them the session-resolved target keeps links pointing at
42
+ * what the author meant. Links inside code, images, autolinks and reference
43
+ * uses (`[x][ref]`, rewritten through their definition) are untouched; a link
44
+ * whose source cannot be located exactly is left as written.
45
+ */
46
+ export declare function rewriteMarkdownLinkDestinations(text: string, resolve: (href: string) => string | undefined): string;
22
47
  /** Drop all L2 cache entries. Call on theme change to prevent stale styled output. */
23
48
  export declare function clearRenderCache(): void;
24
49
  /**
@@ -28,6 +53,13 @@ export declare function clearRenderCache(): void;
28
53
  export interface DefaultTextStyle {
29
54
  /** Foreground color function */
30
55
  color?: (text: string) => string;
56
+ /**
57
+ * The `color` foreground is prose-level: semantically-themed markdown spans
58
+ * (headings, links) keep their own `md*` theme colors instead of being
59
+ * painted over by it, while plain text runs still use `color`. Unset keeps
60
+ * the historical behavior where `color` paints every run.
61
+ */
62
+ proseFg?: boolean;
31
63
  /** Background color function */
32
64
  bgColor?: (text: string) => string;
33
65
  /** Bold text */
@@ -99,6 +131,7 @@ export declare class Markdown implements Component {
99
131
  debugState(): Record<string, unknown>;
100
132
  setText(text: string): boolean;
101
133
  invalidate(): void;
134
+ releaseRenderCaches(): void;
102
135
  /**
103
136
  * The unrendered source as an `md` node, `stream` while the streaming
104
137
  * (transient) cache is on. Append-only growth yields a fresh node of the