@linxiraos/pi-tui 1.1.26 → 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 (423) hide show
  1. package/CHANGELOG.md +51 -491
  2. package/README.md +13 -7
  3. package/dist/types/active-terminal.d.ts +30 -0
  4. package/dist/types/app-keybindings.d.ts +7 -2
  5. package/dist/types/apps/cleanse-board.d.ts +1 -1
  6. package/dist/types/apps/debug/log-viewer.d.ts +4 -0
  7. package/dist/types/apps/debug/raw-sse-buffer.d.ts +2 -0
  8. package/dist/types/apps/git/git-tui.d.ts +5 -4
  9. package/dist/types/apps/git/help.d.ts +3 -1
  10. package/dist/types/apps/if-bench-board.d.ts +2 -2
  11. package/dist/types/apps/setup-model-picker.d.ts +1 -1
  12. package/dist/types/autocomplete.d.ts +5 -2
  13. package/dist/types/charts/chart-plan.d.ts +674 -0
  14. package/dist/types/charts/chart-svg.d.ts +59 -0
  15. package/dist/types/charts/table-data.d.ts +175 -0
  16. package/dist/types/chat/assistant-message.d.ts +11 -1
  17. package/dist/types/chat/chat-transcript-builder.d.ts +3 -0
  18. package/dist/types/chat/compaction-summary-message.d.ts +1 -0
  19. package/dist/types/chat/execution-shared.d.ts +6 -5
  20. package/dist/types/chat/fence-figure.d.ts +42 -0
  21. package/dist/types/chat/figure-markdown.d.ts +42 -0
  22. package/dist/types/chat/messages.d.ts +1 -1
  23. package/dist/types/chat/svg-figure.d.ts +33 -0
  24. package/dist/types/chat/svg-source.d.ts +49 -0
  25. package/dist/types/chat/table-chart.d.ts +89 -0
  26. package/dist/types/chat/tool-execution.d.ts +2 -0
  27. package/dist/types/chat/user-message.d.ts +17 -4
  28. package/dist/types/chrome/shared.d.ts +9 -0
  29. package/dist/types/chrome/status-notice.d.ts +15 -3
  30. package/dist/types/chrome/transcript-container.d.ts +6 -0
  31. package/dist/types/components/box.d.ts +1 -0
  32. package/dist/types/components/disclosure.d.ts +1 -0
  33. package/dist/types/components/editor.d.ts +23 -5
  34. package/dist/types/components/form.d.ts +1 -0
  35. package/dist/types/components/image.d.ts +26 -44
  36. package/dist/types/components/input.d.ts +2 -1
  37. package/dist/types/components/key-value-list.d.ts +1 -0
  38. package/dist/types/components/layout/row.d.ts +1 -0
  39. package/dist/types/components/layout/stack.d.ts +1 -0
  40. package/dist/types/components/loader.d.ts +5 -6
  41. package/dist/types/components/markdown.d.ts +47 -5
  42. package/dist/types/components/scroll-view.d.ts +1 -0
  43. package/dist/types/components/section.d.ts +1 -0
  44. package/dist/types/components/table.d.ts +1 -0
  45. package/dist/types/components/text.d.ts +1 -0
  46. package/dist/types/components/tree-view.d.ts +7 -0
  47. package/dist/types/components/truncated-text.d.ts +1 -0
  48. package/dist/types/fuzzy.d.ts +29 -1
  49. package/dist/types/key-hint-format.d.ts +9 -1
  50. package/dist/types/keybinding-matchers.d.ts +3 -0
  51. package/dist/types/keybindings.d.ts +1 -1
  52. package/dist/types/kitty-graphics.d.ts +2 -2
  53. package/dist/types/loop-watchdog.d.ts +8 -7
  54. package/dist/types/multiplexers/cmux.d.ts +16 -0
  55. package/dist/types/multiplexers/herdr.d.ts +26 -0
  56. package/dist/types/multiplexers/notify.d.ts +13 -0
  57. package/dist/types/multiplexers/orca.d.ts +12 -0
  58. package/dist/types/multiplexers/screen.d.ts +9 -0
  59. package/dist/types/multiplexers/tmux.d.ts +29 -0
  60. package/dist/types/multiplexers/types.d.ts +100 -0
  61. package/dist/types/multiplexers/wmux.d.ts +12 -0
  62. package/dist/types/multiplexers/zellij.d.ts +12 -0
  63. package/dist/types/native/apply.d.ts +4 -3
  64. package/dist/types/native/backend.d.ts +5 -4
  65. package/dist/types/native/blobs.d.ts +7 -3
  66. package/dist/types/native/encode.d.ts +8 -6
  67. package/dist/types/native/node.d.ts +36 -5
  68. package/dist/types/native/overlay.d.ts +8 -3
  69. package/dist/types/native/settle.d.ts +4 -3
  70. package/dist/types/overlays/agent-hub-projection.d.ts +6 -5
  71. package/dist/types/overlays/agent-hub-types.d.ts +9 -0
  72. package/dist/types/overlays/agent-hub.d.ts +3 -0
  73. package/dist/types/overlays/agent-transcript-viewer.d.ts +12 -2
  74. package/dist/types/overlays/agents-hub.d.ts +9 -0
  75. package/dist/types/overlays/annotation-overlay.d.ts +19 -2
  76. package/dist/types/overlays/annotation-types.d.ts +2 -0
  77. package/dist/types/overlays/ask-dialog.d.ts +5 -2
  78. package/dist/types/overlays/btw-history-panel.d.ts +13 -1
  79. package/dist/types/overlays/copy-selector.d.ts +1 -0
  80. package/dist/types/overlays/history-search.d.ts +10 -0
  81. package/dist/types/overlays/hook-selector.d.ts +9 -4
  82. package/dist/types/overlays/hub-frame.d.ts +4 -1
  83. package/dist/types/overlays/jobs-panel.d.ts +1 -7
  84. package/dist/types/overlays/model-browser.d.ts +54 -7
  85. package/dist/types/overlays/model-hub.d.ts +37 -0
  86. package/dist/types/overlays/oauth-selector.d.ts +0 -1
  87. package/dist/types/overlays/plan-review-overlay.d.ts +3 -2
  88. package/dist/types/overlays/report-panel.d.ts +68 -0
  89. package/dist/types/overlays/rewind-selector.d.ts +1 -0
  90. package/dist/types/overlays/session-observer-registry.d.ts +2 -0
  91. package/dist/types/overlays/session-selector.d.ts +6 -2
  92. package/dist/types/overlays/settings-defs.d.ts +1 -0
  93. package/dist/types/overlays/thinking-selector.d.ts +2 -2
  94. package/dist/types/overlays/usage-dashboard.d.ts +12 -8
  95. package/dist/types/overlays/usage-display.d.ts +3 -1
  96. package/dist/types/prompt/composer-cache.d.ts +0 -16
  97. package/dist/types/prompt/composer.d.ts +18 -3
  98. package/dist/types/prompt/custom-editor.d.ts +25 -13
  99. package/dist/types/prompt/image-source.d.ts +2 -1
  100. package/dist/types/prompt/interactive-context-helpers.d.ts +5 -1
  101. package/dist/types/prompt/model-mention-autocomplete.d.ts +1 -1
  102. package/dist/types/prompt/welcome.d.ts +7 -2
  103. package/dist/types/render/composite.d.ts +2 -0
  104. package/dist/types/render/output-pane.d.ts +1 -0
  105. package/dist/types/render/render-utils.d.ts +1 -0
  106. package/dist/types/render/tool-card.d.ts +6 -0
  107. package/dist/types/render/width-aware-text.d.ts +2 -0
  108. package/dist/types/setup/scenes/types.d.ts +1 -0
  109. package/dist/types/space-hold.d.ts +40 -25
  110. package/dist/types/status-line/component.d.ts +3 -0
  111. package/dist/types/status-line/context-usage.d.ts +7 -5
  112. package/dist/types/status-line/host.d.ts +4 -1
  113. package/dist/types/status-line/segments.d.ts +2 -0
  114. package/dist/types/status-line/types.d.ts +14 -3
  115. package/dist/types/terminal-capabilities.d.ts +41 -36
  116. package/dist/types/terminal-multiplexer.d.ts +127 -14
  117. package/dist/types/terminal.d.ts +23 -16
  118. package/dist/types/theme/active-symbols.d.ts +1 -1
  119. package/dist/types/theme/schema.d.ts +11 -2
  120. package/dist/types/theme/symbols.d.ts +1 -1
  121. package/dist/types/theme/theme-class.d.ts +7 -0
  122. package/dist/types/theme/theme.d.ts +1 -1
  123. package/dist/types/theme/tui-adapters.d.ts +3 -0
  124. package/dist/types/thinking.d.ts +1 -1
  125. package/dist/types/tmux.d.ts +11 -4
  126. package/dist/types/tools/bash.d.ts +7 -2
  127. package/dist/types/tools/daemon.d.ts +1 -1
  128. package/dist/types/tools/eval.d.ts +22 -3
  129. package/dist/types/tools/glob.d.ts +4 -0
  130. package/dist/types/tools/native-view.d.ts +47 -0
  131. package/dist/types/tools/output-meta.d.ts +11 -1
  132. package/dist/types/tools/renderer.d.ts +37 -2
  133. package/dist/types/tools/streaming-output.d.ts +20 -1
  134. package/dist/types/tools/task.d.ts +2 -3
  135. package/dist/types/tools/write.d.ts +9 -2
  136. package/dist/types/tools/yield.d.ts +22 -0
  137. package/dist/types/tui.d.ts +36 -0
  138. package/dist/types/utils.d.ts +6 -0
  139. package/dist/types/vim.d.ts +19 -5
  140. package/dist/types/windows-input-mode.d.ts +33 -1
  141. package/package.json +13 -9
  142. package/src/active-terminal.d.ts +30 -0
  143. package/src/active-terminal.ts +80 -0
  144. package/src/app-keybindings.d.ts +7 -2
  145. package/src/app-keybindings.ts +7 -2
  146. package/src/apps/autoresearch-dashboard.ts +7 -3
  147. package/src/apps/cleanse-board.ts +2 -2
  148. package/src/apps/cleanse-picker.ts +1 -1
  149. package/src/apps/debug/log-viewer.ts +188 -72
  150. package/src/apps/debug/raw-sse-buffer.ts +8 -0
  151. package/src/apps/debug/raw-sse.ts +39 -5
  152. package/src/apps/git/diff-pane.ts +96 -23
  153. package/src/apps/git/git-tui.ts +23 -16
  154. package/src/apps/git/help.ts +29 -20
  155. package/src/apps/git/sidebar.ts +8 -3
  156. package/src/apps/if-bench-board.ts +2 -2
  157. package/src/apps/ps-top.ts +2 -2
  158. package/src/apps/setup-model-picker.ts +1 -1
  159. package/src/autocomplete.d.ts +5 -2
  160. package/src/autocomplete.ts +67 -43
  161. package/src/bracketed-paste.ts +10 -3
  162. package/src/charts/chart-plan.d.ts +674 -0
  163. package/src/charts/chart-plan.ts +5081 -0
  164. package/src/charts/chart-svg.d.ts +59 -0
  165. package/src/charts/chart-svg.ts +4657 -0
  166. package/src/charts/table-data.d.ts +175 -0
  167. package/src/charts/table-data.ts +1478 -0
  168. package/src/chat/advisor-message.ts +11 -0
  169. package/src/chat/assistant-message.d.ts +11 -1
  170. package/src/chat/assistant-message.ts +245 -54
  171. package/src/chat/chat-transcript-builder.d.ts +3 -0
  172. package/src/chat/chat-transcript-builder.ts +9 -3
  173. package/src/chat/compaction-summary-message.d.ts +1 -0
  174. package/src/chat/compaction-summary-message.ts +8 -0
  175. package/src/chat/execution-shared.d.ts +6 -5
  176. package/src/chat/execution-shared.ts +42 -25
  177. package/src/chat/fence-figure.d.ts +42 -0
  178. package/src/chat/fence-figure.ts +80 -0
  179. package/src/chat/figure-markdown.d.ts +42 -0
  180. package/src/chat/figure-markdown.ts +174 -0
  181. package/src/chat/messages.d.ts +1 -1
  182. package/src/chat/messages.ts +1 -1
  183. package/src/chat/read-tool-group.ts +6 -4
  184. package/src/chat/skill-message.ts +6 -0
  185. package/src/chat/svg-figure.d.ts +33 -0
  186. package/src/chat/svg-figure.ts +399 -0
  187. package/src/chat/svg-source.d.ts +49 -0
  188. package/src/chat/svg-source.ts +188 -0
  189. package/src/chat/table-chart.d.ts +89 -0
  190. package/src/chat/table-chart.ts +294 -0
  191. package/src/chat/tool-execution.d.ts +2 -0
  192. package/src/chat/tool-execution.ts +86 -7
  193. package/src/chat/user-message.d.ts +17 -4
  194. package/src/chat/user-message.ts +71 -8
  195. package/src/chrome/shared.ts +53 -0
  196. package/src/chrome/status-notice.ts +25 -7
  197. package/src/chrome/transcript-container.d.ts +6 -0
  198. package/src/chrome/transcript-container.ts +162 -19
  199. package/src/components/box.d.ts +1 -0
  200. package/src/components/box.ts +5 -0
  201. package/src/components/disclosure.d.ts +1 -0
  202. package/src/components/disclosure.ts +8 -0
  203. package/src/components/editor.d.ts +23 -5
  204. package/src/components/editor.ts +316 -47
  205. package/src/components/form.d.ts +1 -0
  206. package/src/components/form.ts +8 -0
  207. package/src/components/image.d.ts +26 -44
  208. package/src/components/image.ts +115 -152
  209. package/src/components/input.d.ts +2 -1
  210. package/src/components/input.ts +17 -7
  211. package/src/components/key-value-list.d.ts +1 -0
  212. package/src/components/key-value-list.ts +4 -0
  213. package/src/components/layout/row.d.ts +1 -0
  214. package/src/components/layout/row.ts +7 -0
  215. package/src/components/layout/stack.d.ts +1 -0
  216. package/src/components/layout/stack.ts +7 -0
  217. package/src/components/loader.d.ts +5 -6
  218. package/src/components/loader.ts +10 -17
  219. package/src/components/markdown.d.ts +47 -5
  220. package/src/components/markdown.ts +786 -303
  221. package/src/components/scroll-view.d.ts +1 -0
  222. package/src/components/scroll-view.ts +5 -0
  223. package/src/components/section.d.ts +1 -0
  224. package/src/components/section.ts +5 -0
  225. package/src/components/settings-list.ts +2 -0
  226. package/src/components/table.d.ts +1 -0
  227. package/src/components/table.ts +4 -0
  228. package/src/components/text.d.ts +1 -0
  229. package/src/components/text.ts +7 -0
  230. package/src/components/tree-view.d.ts +7 -0
  231. package/src/components/tree-view.ts +38 -8
  232. package/src/components/truncated-text.d.ts +1 -0
  233. package/src/components/truncated-text.ts +5 -0
  234. package/src/desktop-notify.ts +3 -2
  235. package/src/fuzzy.d.ts +29 -1
  236. package/src/fuzzy.ts +115 -30
  237. package/src/hotkeys-markdown.ts +9 -2
  238. package/src/key-hint-format.d.ts +9 -1
  239. package/src/key-hint-format.ts +26 -11
  240. package/src/keybinding-matchers.d.ts +3 -0
  241. package/src/keybinding-matchers.ts +6 -2
  242. package/src/keybindings.d.ts +1 -1
  243. package/src/keybindings.ts +1 -1
  244. package/src/kitty-graphics.d.ts +2 -2
  245. package/src/kitty-graphics.ts +11 -5
  246. package/src/loop-watchdog.d.ts +8 -7
  247. package/src/loop-watchdog.ts +18 -13
  248. package/src/multiplexers/cmux.d.ts +16 -0
  249. package/src/multiplexers/cmux.ts +29 -0
  250. package/src/multiplexers/herdr.d.ts +26 -0
  251. package/src/multiplexers/herdr.ts +64 -0
  252. package/src/multiplexers/notify.d.ts +13 -0
  253. package/src/multiplexers/notify.ts +24 -0
  254. package/src/multiplexers/orca.d.ts +12 -0
  255. package/src/multiplexers/orca.ts +16 -0
  256. package/src/multiplexers/screen.d.ts +9 -0
  257. package/src/multiplexers/screen.ts +15 -0
  258. package/src/multiplexers/tmux.d.ts +29 -0
  259. package/src/multiplexers/tmux.ts +40 -0
  260. package/src/multiplexers/types.d.ts +100 -0
  261. package/src/multiplexers/types.ts +110 -0
  262. package/src/multiplexers/wmux.d.ts +12 -0
  263. package/src/multiplexers/wmux.ts +16 -0
  264. package/src/multiplexers/zellij.d.ts +12 -0
  265. package/src/multiplexers/zellij.ts +24 -0
  266. package/src/native/apply.d.ts +4 -3
  267. package/src/native/apply.ts +4 -3
  268. package/src/native/backend.d.ts +5 -4
  269. package/src/native/backend.ts +199 -22
  270. package/src/native/blobs.d.ts +7 -3
  271. package/src/native/blobs.ts +47 -12
  272. package/src/native/encode.d.ts +8 -6
  273. package/src/native/encode.ts +76 -34
  274. package/src/native/node.d.ts +36 -5
  275. package/src/native/node.ts +31 -5
  276. package/src/native/overlay.d.ts +8 -3
  277. package/src/native/overlay.ts +17 -4
  278. package/src/native/reconcile.ts +16 -4
  279. package/src/native/settle.d.ts +4 -3
  280. package/src/native/settle.ts +4 -3
  281. package/src/overlays/agent-hub-projection.d.ts +6 -5
  282. package/src/overlays/agent-hub-projection.ts +124 -13
  283. package/src/overlays/agent-hub-types.d.ts +9 -0
  284. package/src/overlays/agent-hub-types.ts +6 -0
  285. package/src/overlays/agent-hub.ts +48 -29
  286. package/src/overlays/agent-transcript-viewer.ts +93 -23
  287. package/src/overlays/agents-hub.ts +28 -8
  288. package/src/overlays/annotation-overlay.ts +293 -90
  289. package/src/overlays/annotation-types.ts +2 -0
  290. package/src/overlays/ask-dialog.ts +10 -17
  291. package/src/overlays/btw-history-panel.ts +136 -39
  292. package/src/overlays/copy-selector.d.ts +1 -0
  293. package/src/overlays/copy-selector.ts +32 -8
  294. package/src/overlays/extensions/extension-dashboard.ts +4 -4
  295. package/src/overlays/extensions/state-manager.ts +52 -12
  296. package/src/overlays/history-search.ts +100 -7
  297. package/src/overlays/hook-selector.ts +12 -5
  298. package/src/overlays/hub-frame.ts +19 -1
  299. package/src/overlays/jobs-panel.ts +33 -52
  300. package/src/overlays/model-browser.ts +367 -122
  301. package/src/overlays/model-hub.ts +435 -59
  302. package/src/overlays/move-overlay.ts +28 -14
  303. package/src/overlays/oauth-selector.ts +54 -70
  304. package/src/overlays/pause-screen.ts +2 -2
  305. package/src/overlays/plan-review-overlay.ts +208 -70
  306. package/src/overlays/plugin-settings.ts +3 -3
  307. package/src/overlays/report-panel.ts +191 -0
  308. package/src/overlays/rewind-selector.ts +88 -17
  309. package/src/overlays/session-observer-registry.d.ts +2 -0
  310. package/src/overlays/session-observer-registry.ts +10 -2
  311. package/src/overlays/session-selector.ts +64 -26
  312. package/src/overlays/settings-defs.ts +5 -4
  313. package/src/overlays/settings-selector.ts +33 -8
  314. package/src/overlays/snapcompact-shape-preview.ts +2 -3
  315. package/src/overlays/stats-notice.ts +1 -1
  316. package/src/overlays/thinking-selector.ts +11 -10
  317. package/src/overlays/tree-selector.ts +41 -20
  318. package/src/overlays/usage-dashboard.ts +117 -58
  319. package/src/overlays/usage-display.ts +10 -4
  320. package/src/overlays/usage-row.ts +22 -8
  321. package/src/prompt/attachment-chips.ts +15 -11
  322. package/src/prompt/composer-attachments.ts +6 -4
  323. package/src/prompt/composer-cache.ts +7 -86
  324. package/src/prompt/composer.ts +37 -26
  325. package/src/prompt/custom-editor.ts +282 -105
  326. package/src/prompt/image-source.ts +2 -1
  327. package/src/prompt/interactive-context-helpers.ts +32 -18
  328. package/src/prompt/macos-spelling.ts +7 -0
  329. package/src/prompt/magic-keywords.ts +11 -2
  330. package/src/prompt/model-mention-autocomplete.ts +14 -14
  331. package/src/prompt/queued-messages.ts +6 -4
  332. package/src/prompt/tips.txt +13 -9
  333. package/src/prompt/welcome.ts +64 -46
  334. package/src/prompt/word-completion.ts +38 -5
  335. package/src/render/composite.d.ts +2 -0
  336. package/src/render/composite.ts +64 -0
  337. package/src/render/output-pane.d.ts +1 -0
  338. package/src/render/output-pane.ts +6 -0
  339. package/src/render/render-utils.d.ts +1 -0
  340. package/src/render/render-utils.ts +16 -1
  341. package/src/render/tool-card.d.ts +6 -0
  342. package/src/render/tool-card.ts +15 -0
  343. package/src/render/width-aware-text.d.ts +2 -0
  344. package/src/render/width-aware-text.ts +5 -0
  345. package/src/setup/scenes/model.ts +2 -1
  346. package/src/setup/scenes/types.ts +1 -0
  347. package/src/space-hold.d.ts +40 -25
  348. package/src/space-hold.ts +156 -52
  349. package/src/status-line/component.ts +292 -78
  350. package/src/status-line/context-usage.ts +21 -46
  351. package/src/status-line/host.ts +4 -1
  352. package/src/status-line/segments.ts +40 -22
  353. package/src/status-line/types.ts +15 -3
  354. package/src/terminal-capabilities.d.ts +41 -36
  355. package/src/terminal-capabilities.ts +137 -213
  356. package/src/terminal-multiplexer.d.ts +127 -14
  357. package/src/terminal-multiplexer.ts +112 -33
  358. package/src/terminal.d.ts +23 -16
  359. package/src/terminal.ts +203 -99
  360. package/src/theme/active-symbols.d.ts +1 -1
  361. package/src/theme/active-symbols.ts +1 -1
  362. package/src/theme/schema-validation.ts +1 -0
  363. package/src/theme/schema.d.ts +11 -2
  364. package/src/theme/schema.ts +14 -1
  365. package/src/theme/session-color.ts +1 -7
  366. package/src/theme/symbols.d.ts +1 -1
  367. package/src/theme/symbols.ts +11 -4
  368. package/src/theme/theme-class.d.ts +7 -0
  369. package/src/theme/theme-class.ts +26 -1
  370. package/src/theme/theme-schema.json +5 -1
  371. package/src/theme/theme.d.ts +1 -1
  372. package/src/theme/theme.ts +8 -4
  373. package/src/theme/tui-adapters.d.ts +3 -0
  374. package/src/theme/tui-adapters.ts +68 -0
  375. package/src/thinking.d.ts +1 -1
  376. package/src/thinking.ts +1 -1
  377. package/src/tmux.d.ts +11 -4
  378. package/src/tmux.ts +47 -11
  379. package/src/tools/ast-edit.ts +59 -35
  380. package/src/tools/ast-grep.ts +35 -16
  381. package/src/tools/bash.d.ts +7 -2
  382. package/src/tools/bash.ts +116 -47
  383. package/src/tools/daemon.d.ts +1 -1
  384. package/src/tools/daemon.ts +1 -1
  385. package/src/tools/default-renderer.ts +35 -19
  386. package/src/tools/edit.ts +142 -120
  387. package/src/tools/eval-format/index.ts +28 -2
  388. package/src/tools/eval-format/javascript.ts +7 -5
  389. package/src/tools/eval.d.ts +22 -3
  390. package/src/tools/eval.ts +364 -208
  391. package/src/tools/fetch.ts +5 -6
  392. package/src/tools/glob.d.ts +4 -0
  393. package/src/tools/glob.ts +33 -65
  394. package/src/tools/grep.ts +81 -48
  395. package/src/tools/index.ts +2 -0
  396. package/src/tools/lsp.ts +81 -59
  397. package/src/tools/mcp.ts +111 -68
  398. package/src/tools/native-view.d.ts +47 -0
  399. package/src/tools/native-view.ts +102 -1
  400. package/src/tools/output-meta.d.ts +11 -1
  401. package/src/tools/output-meta.ts +23 -3
  402. package/src/tools/read.ts +7 -5
  403. package/src/tools/renderer.d.ts +37 -2
  404. package/src/tools/renderer.ts +42 -2
  405. package/src/tools/streaming-output.d.ts +20 -1
  406. package/src/tools/streaming-output.ts +154 -50
  407. package/src/tools/task.d.ts +2 -3
  408. package/src/tools/task.ts +115 -62
  409. package/src/tools/todo.ts +119 -30
  410. package/src/tools/wait.ts +34 -24
  411. package/src/tools/web-search.ts +1 -1
  412. package/src/tools/write.d.ts +9 -2
  413. package/src/tools/write.ts +147 -95
  414. package/src/tools/yield.d.ts +22 -0
  415. package/src/tools/yield.ts +225 -0
  416. package/src/tui.d.ts +36 -0
  417. package/src/tui.ts +666 -217
  418. package/src/utils.d.ts +6 -0
  419. package/src/utils.ts +19 -0
  420. package/src/vim.d.ts +19 -5
  421. package/src/vim.ts +333 -18
  422. package/src/windows-input-mode.d.ts +33 -1
  423. package/src/windows-input-mode.ts +212 -6
@@ -1,5 +1,7 @@
1
+ import * as path from "node:path";
1
2
  import type { HighlightStream } from "@linxiraos/pi-natives";
2
3
  import type { Component } from "../tui";
4
+ import { fencedCode } from "../components/markdown";
3
5
  import { Text } from "../components/text";
4
6
  import { getLanguageFromPath } from "../lang-from-path";
5
7
  import { createHighlightStream, highlightCode, type Theme } from "../theme/theme";
@@ -31,8 +33,7 @@ import {
31
33
  type ProcWriteDetails,
32
34
  } from "./proc-render";
33
35
  import type { TspTone } from "@linxiraos/pi-wire";
34
- import { code, compact, node, span } from "../native/describe";
35
- import { registerNativeBlob } from "../native/blobs";
36
+ import { code, compact, md, node, span } from "../native/describe";
36
37
  import type { NativeChild, NativeNode } from "../native/node";
37
38
  import { diagnosticsBadge, diagnosticsSection, displayPath, errorText, fileHref, resultText } from "./native-view";
38
39
  import { describeCfgWrite, renderCfgWrite, type CfgWriteDetails } from "./cfg-render";
@@ -45,6 +46,7 @@ import type {
45
46
  RenderResultOptions,
46
47
  ToolActivityContext,
47
48
  ToolActivitySummary,
49
+ ToolFigure,
48
50
  ToolRenderer,
49
51
  } from "./renderer";
50
52
  import { splitUrlScheme } from "./url-scheme-host";
@@ -91,8 +93,31 @@ interface WriteRenderArgs {
91
93
  const WRITE_PREVIEW_LINES = 6;
92
94
  /** Collapsed native write body: the first lines of the file (§7.3). */
93
95
  const NATIVE_WRITE_PREVIEW = { lines: 8 } as const;
94
- /** Tallest rendered SVG preview, in lines; collapsed, the card shows just the picture. */
95
- const NATIVE_SVG_PREVIEW_LINES = 16;
96
+ /**
97
+ * Collapsed clamp of a write drawn as a figure: Tern's model figure (its head
98
+ * over a 320px stage) clears the clamp's fade at 16px lines.
99
+ */
100
+ const NATIVE_FIGURE_PREVIEW_LINES = 26;
101
+
102
+ /**
103
+ * Fence language per extension of a written file that transcripts draw as a
104
+ * figure, as they draw that fence in assistant text: svg and mermaid, plus
105
+ * the 3D formats only Tern draws (stencil-markdown's model fences).
106
+ */
107
+ const FIGURE_FENCES: Readonly<Record<string, string>> = {
108
+ svg: "svg",
109
+ mmd: "mermaid",
110
+ mermaid: "mermaid",
111
+ obj: "obj",
112
+ ply: "ply",
113
+ wrl: "wrl",
114
+ vrml: "vrml",
115
+ x3dv: "x3dv",
116
+ stl: "stl",
117
+ gltf: "gltf",
118
+ usda: "usda",
119
+ usd: "usd",
120
+ };
96
121
 
97
122
  /** A write tool result as the renderer receives it. */
98
123
  interface WriteResult {
@@ -101,26 +126,46 @@ interface WriteResult {
101
126
  isError?: boolean;
102
127
  }
103
128
 
104
- /** Cached SVG preview node on its result, so the content is encoded and hashed once per write. */
105
- const kSvgPreview = Symbol("write.svgPreview");
129
+ /**
130
+ * The fence a file write draws as while its content streams and after: its
131
+ * extension names a figure language and it has content, closed once the args
132
+ * are final. None for URL-card writes, or after an error.
133
+ */
134
+ function writeFigure(
135
+ args: WriteRenderArgs | undefined,
136
+ result: WriteResult | undefined,
137
+ options: RenderResultOptions,
138
+ ): ToolFigure | undefined {
139
+ if (result?.isError || result?.details?.xdev) return undefined;
140
+ const rawPath =
141
+ typeof args?.file_path === "string" ? args.file_path : typeof args?.path === "string" ? args.path : "";
142
+ const lang = FIGURE_FENCES[path.extname(rawPath).slice(1).toLowerCase()];
143
+ const content = args?.content;
144
+ if (!lang || typeof content !== "string" || !/\S/.test(content) || writeUrlCard(rawPath, result?.details)) {
145
+ return undefined;
146
+ }
147
+ return {
148
+ lang,
149
+ source: content.includes("\r") ? content.replace(/\r/g, "") : content,
150
+ closed: result !== undefined || options.argsComplete === true,
151
+ };
152
+ }
106
153
 
107
- interface TaggedWriteResult extends WriteResult {
108
- [kSvgPreview]?: { content: string; node: NativeNode };
154
+ /**
155
+ * The `md` node drawing a figure write as its fence draws in a reply: open
156
+ * and streaming while the content arrives. A mermaid figure waits for the
157
+ * fence to close, as Tern shows an open one as code, which the source is.
158
+ */
159
+ function describeFigure(figure: ToolFigure | undefined): NativeNode | undefined {
160
+ if (!figure || (figure.lang === "mermaid" && !figure.closed)) return undefined;
161
+ const text = fencedCode(figure.lang, figure.source, { open: !figure.closed });
162
+ return { ...md(text, { role: "zeta.tool.write.figure", stream: !figure.closed }), key: "figure" };
109
163
  }
110
164
 
111
- /** Native `image` node drawing written SVG `content`, cached on `result`. */
112
- function describeSvgPreview(result: TaggedWriteResult, content: string, alt: string): NativeNode {
113
- const cached = result[kSvgPreview];
114
- if (cached?.content === content) return cached.node;
115
- const blob = registerNativeBlob(new TextEncoder().encode(content), "image/svg+xml");
116
- const preview = node("image", {
117
- blob,
118
- alt,
119
- role: "zeta.tool.write.image",
120
- max: { h: `${NATIVE_SVG_PREVIEW_LINES}lines` },
121
- });
122
- result[kSvgPreview] = { content, node: preview };
123
- return preview;
165
+ /** The written content as numbered code, keyed so it keeps its node as the figure above it comes and goes. */
166
+ function describeSource(rawPath: string, content: string): NativeNode {
167
+ const lang = rawPath ? getLanguageFromPath(rawPath) : undefined;
168
+ return { ...code(content, { lang, numbers: true }), key: "source" };
124
169
  }
125
170
 
126
171
  function countLines(text: string): number {
@@ -592,40 +637,49 @@ export const writeToolRenderer = {
592
637
  // back to the normalizing stringify.
593
638
  const content = typeof args.content === "string" ? args.content : normalizeDisplayText(args.content);
594
639
  const streamingCache = createRenderedStringCache();
595
- return framedToolCard(uiTheme, () => {
596
- const body = content
597
- ? formatStreamingContent(
598
- content,
599
- Boolean(options?.expanded),
600
- lang,
601
- uiTheme,
602
- options?.spinnerFrame,
603
- streamingCache,
604
- // `options` is the ToolExecutionComponent's persistent
605
- // render-state object — a stable identity across reveal ticks
606
- // that keys the incremental preview state. `argsComplete`
607
- // flushes the trailing line through the highlighter once.
608
- options,
609
- options?.argsComplete,
610
- )
611
- : "";
612
- const bodyLines = body ? body.split("\n") : [];
613
- while (bodyLines.length > 0 && bodyLines[0].trim() === "") bodyLines.shift();
614
- return {
615
- header,
616
- sections: bodyLines.length > 0 ? [{ content: bodyLines }] : [],
617
- phase: "pending",
618
- borderColor: "borderMuted",
619
- };
620
- });
640
+ return framedToolCard(
641
+ uiTheme,
642
+ () => {
643
+ const body = content
644
+ ? formatStreamingContent(
645
+ content,
646
+ Boolean(options?.expanded),
647
+ lang,
648
+ uiTheme,
649
+ options?.spinnerFrame,
650
+ streamingCache,
651
+ // `options` is the ToolExecutionComponent's persistent
652
+ // render-state object — a stable identity across reveal ticks
653
+ // that keys the incremental preview state. `argsComplete`
654
+ // flushes the trailing line through the highlighter once.
655
+ options,
656
+ options?.argsComplete,
657
+ )
658
+ : "";
659
+ const bodyLines = body ? body.split("\n") : [];
660
+ while (bodyLines.length > 0 && bodyLines[0].trim() === "") bodyLines.shift();
661
+ return {
662
+ header,
663
+ sections: bodyLines.length > 0 ? [{ content: bodyLines }] : [],
664
+ phase: "pending",
665
+ borderColor: "borderMuted",
666
+ };
667
+ },
668
+ // The highlighted body in `streamingCache` does not depend on width, and re-highlighting every committed
669
+ // card on each resize replay costs seconds; only the incremental highlighter state goes.
670
+ { onReleaseRenderCaches: () => delete options?.[writeStreamingPreviewStateKey] },
671
+ );
621
672
  },
622
673
 
623
674
  renderResult(
624
675
  result: WriteResult,
625
- options: RenderResultOptions & { renderContext?: WriteRenderContext },
676
+ options: RenderResultOptions & WriteStreamingPreviewStateCarrier & { renderContext?: WriteRenderContext },
626
677
  uiTheme: Theme,
627
678
  args?: WriteRenderArgs,
628
679
  ): Component {
680
+ // Write merges call and result, so this builder runs once per display rebuild after the result arrives and the
681
+ // call card never renders again: its incremental highlighter state is dead from here on.
682
+ delete options[writeStreamingPreviewStateKey];
629
683
  const cardPath =
630
684
  typeof args?.path === "string" ? args.path : typeof args?.file_path === "string" ? args.file_path : "";
631
685
  const routed = writeUrlCard(cardPath, result.details);
@@ -687,36 +741,41 @@ export const writeToolRenderer = {
687
741
  const diagnostics = result.details?.diagnostics;
688
742
 
689
743
  const previewCache = createRenderedStringCache();
690
- return framedToolCard(uiTheme, () => {
691
- const { expanded } = options;
692
- let body = renderContentPreview(fileContent, expanded, lang, uiTheme, previewCache);
693
- if (isPartial && progressText) {
694
- const safeProgressText = truncateToWidth(
695
- replaceTabs(progressText),
696
- TRUNCATE_LENGTHS.LINE,
697
- Ellipsis.Unicode,
698
- );
699
- body = `${uiTheme.fg("muted", safeProgressText)}${body ? `\n${body}` : ""}`;
700
- }
701
- if (!isPartial && diagnostics) {
702
- const diagText = formatDiagnostics(diagnostics, expanded, uiTheme, fp =>
703
- uiTheme.getLangIcon(getLanguageFromPath(fp)),
704
- );
705
- if (diagText.trim()) {
706
- const diagLines = diagText.split("\n");
707
- const firstNonEmpty = diagLines.findIndex(line => line.trim());
708
- if (firstNonEmpty >= 0) body += `\n${diagLines.slice(firstNonEmpty).join("\n")}`;
744
+ return framedToolCard(
745
+ uiTheme,
746
+ () => {
747
+ const { expanded } = options;
748
+ let body = renderContentPreview(fileContent, expanded, lang, uiTheme, previewCache);
749
+ if (isPartial && progressText) {
750
+ const safeProgressText = truncateToWidth(
751
+ replaceTabs(progressText),
752
+ TRUNCATE_LENGTHS.LINE,
753
+ Ellipsis.Unicode,
754
+ );
755
+ body = `${uiTheme.fg("muted", safeProgressText)}${body ? `\n${body}` : ""}`;
709
756
  }
710
- }
711
- const bodyLines = body.split("\n");
712
- while (bodyLines.length > 0 && bodyLines[0].trim() === "") bodyLines.shift();
713
- return {
714
- header,
715
- sections: bodyLines.length > 0 ? [{ content: bodyLines }] : [],
716
- phase: isPartial ? "partial" : "success",
717
- borderColor: "borderMuted",
718
- };
719
- });
757
+ if (!isPartial && diagnostics) {
758
+ const diagText = formatDiagnostics(diagnostics, expanded, uiTheme, fp =>
759
+ uiTheme.getLangIcon(getLanguageFromPath(fp)),
760
+ );
761
+ if (diagText.trim()) {
762
+ const diagLines = diagText.split("\n");
763
+ const firstNonEmpty = diagLines.findIndex(line => line.trim());
764
+ if (firstNonEmpty >= 0) body += `\n${diagLines.slice(firstNonEmpty).join("\n")}`;
765
+ }
766
+ }
767
+ const bodyLines = body.split("\n");
768
+ while (bodyLines.length > 0 && bodyLines[0].trim() === "") bodyLines.shift();
769
+ return {
770
+ header,
771
+ sections: bodyLines.length > 0 ? [{ content: bodyLines }] : [],
772
+ phase: isPartial ? "partial" : "success",
773
+ borderColor: "borderMuted",
774
+ };
775
+ },
776
+ // No release hook: `previewCache` holds the width-independent highlighted preview, which resize replay
777
+ // would otherwise re-highlight for every committed card.
778
+ );
720
779
  },
721
780
  describeCall(
722
781
  args: WriteRenderArgs,
@@ -739,17 +798,12 @@ export const writeToolRenderer = {
739
798
  return describeXdevCall(xdev.name, args.content, options, options.renderContext?.resolveXdevMounted);
740
799
  }
741
800
  const content = normalizeDisplayText(args.content);
801
+ // A figure write draws as it arrives, over its streaming source.
802
+ const figure = writeFigure(args, undefined, options);
742
803
  return {
743
804
  tool: writeToolHead(rawPath, content),
744
- body: content
745
- ? [
746
- code(content, {
747
- lang: rawPath ? getLanguageFromPath(rawPath) : undefined,
748
- numbers: true,
749
- }),
750
- ]
751
- : [],
752
- preview: NATIVE_WRITE_PREVIEW,
805
+ body: content ? compact([describeFigure(figure), describeSource(rawPath, content)]) : [],
806
+ preview: figure ? { lines: NATIVE_FIGURE_PREVIEW_LINES } : NATIVE_WRITE_PREVIEW,
753
807
  };
754
808
  },
755
809
 
@@ -776,22 +830,20 @@ export const writeToolRenderer = {
776
830
  });
777
831
  if (result.isError) return { tool, tone: "error", body: [errorText(resultText(result))] };
778
832
  const progressText = resultText(result);
779
- // A finished SVG write leads with the drawing; its source follows below the collapsed clamp.
780
- const svg = !isPartial && fileContent.trim().length > 0 && rawPath.toLowerCase().endsWith(".svg");
833
+ // A figure write leads with the drawing, drawn as its fence in a reply;
834
+ // its source follows below the collapsed clamp.
835
+ const figure = writeFigure(args, result, options);
781
836
  const body = compact<NativeChild>([
782
837
  isPartial &&
783
838
  progressText.length > 0 &&
784
839
  node("text", { spans: [span(progressText, "muted")], truncate: "end" }),
785
- svg && describeSvgPreview(result, fileContent, displayPath(rawPath)),
786
- fileContent.length > 0 &&
787
- code(fileContent, {
788
- lang: rawPath ? getLanguageFromPath(rawPath) : undefined,
789
- numbers: true,
790
- }),
840
+ describeFigure(figure),
841
+ fileContent.length > 0 && describeSource(rawPath, fileContent),
791
842
  diagnosticsSection(diagnostics),
792
843
  ]);
793
- return { tool, body, preview: svg ? { lines: NATIVE_SVG_PREVIEW_LINES } : NATIVE_WRITE_PREVIEW };
844
+ return { tool, body, preview: figure ? { lines: NATIVE_FIGURE_PREVIEW_LINES } : NATIVE_WRITE_PREVIEW };
794
845
  },
846
+ figure: writeFigure,
795
847
  mergeCallAndResult: true,
796
848
  // The collapsed pending preview follows the streaming edge with a tail
797
849
  // window once the content outgrows it (`… (N earlier lines)` + last rows);
@@ -0,0 +1,22 @@
1
+ import type { Theme } from "../theme/theme";
2
+ import type { Component } from "../tui";
3
+ import type { NativeToolView, RenderResultContextOptions, RenderResultOptions, ToolRenderResult } from "./renderer";
4
+ import type { YieldItem } from "./task";
5
+ /** `yield` arguments as sent; strict-mode providers send omitted optionals as `null`. */
6
+ interface YieldRenderArgs {
7
+ type?: string | string[] | null;
8
+ data?: unknown;
9
+ error?: string | null;
10
+ /** 1-based workpool item number. */
11
+ key?: number;
12
+ }
13
+ /** Shows a `yield` call's submitted data instead of its model-facing acknowledgement. */
14
+ export declare const yieldToolRenderer: {
15
+ renderCall(args: YieldRenderArgs, options: RenderResultOptions, theme: Theme): Component;
16
+ renderResult(result: ToolRenderResult<YieldItem>, options: RenderResultContextOptions, theme: Theme, args?: YieldRenderArgs): Component;
17
+ describeCall(args: YieldRenderArgs, options: RenderResultOptions): NativeToolView;
18
+ describeResult(result: ToolRenderResult<YieldItem>, _options: RenderResultContextOptions, args?: YieldRenderArgs): NativeToolView;
19
+ mergeCallAndResult: true;
20
+ animatedPendingPreview: true;
21
+ };
22
+ export {};
@@ -0,0 +1,225 @@
1
+ /**
2
+ * Transcript renderer for the subagent `yield` tool ("Submit Result"). Its
3
+ * reply ("Result submitted.") is for the model; the card shows what was
4
+ * submitted: the section labels or workpool item in the head, the data as a
5
+ * JSON tree (Markdown when it is a string), and the reported or rejected error.
6
+ */
7
+ import { formatCount, isRecord, sanitizeText } from "@linxiraos/pi-utils";
8
+ import { compact, md } from "../native/describe";
9
+ import { OwnerMemo } from "../native/memo";
10
+ import type { NativeChild } from "../native/node";
11
+ import { formatOutputPaneLines, styleToolOutputLine } from "../render/output-pane";
12
+ import { formatExpandHint, sanitizeDisplayLines, truncateToWidth } from "../render/render-utils";
13
+ import type { StatusLineOptions } from "../render/status-line";
14
+ import { plainToolCard, type ToolCardPhase } from "../render/tool-card";
15
+ import type { Theme } from "../theme/theme";
16
+ import type { Component } from "../tui";
17
+ import { wrapTextWithAnsi } from "../utils";
18
+ import {
19
+ describeJsonTree,
20
+ JSON_TREE_MAX_DEPTH_COLLAPSED,
21
+ JSON_TREE_MAX_DEPTH_EXPANDED,
22
+ JSON_TREE_MAX_LINES_COLLAPSED,
23
+ JSON_TREE_MAX_LINES_EXPANDED,
24
+ JSON_TREE_SCALAR_LEN_COLLAPSED,
25
+ JSON_TREE_SCALAR_LEN_EXPANDED,
26
+ renderJsonTreeLines,
27
+ } from "./json-tree";
28
+ import { errorText, noteText, resultText } from "./native-view";
29
+ import type {
30
+ NativeToolView,
31
+ RenderResultContextOptions,
32
+ RenderResultOptions,
33
+ ToolRenderer,
34
+ ToolRenderResult,
35
+ } from "./renderer";
36
+ import type { YieldItem } from "./task";
37
+
38
+ const TITLE = "Submit Result";
39
+
40
+ /** Collapsed rows of string data (a report) in the ANSI card. */
41
+ const TEXT_ROWS_COLLAPSED = 4;
42
+
43
+ /** `yield` arguments as sent; strict-mode providers send omitted optionals as `null`. */
44
+ interface YieldRenderArgs {
45
+ type?: string | string[] | null;
46
+ data?: unknown;
47
+ error?: string | null;
48
+ /** 1-based workpool item number. */
49
+ key?: number;
50
+ }
51
+
52
+ /** One `yield` call as its card shows it, read from the args and, once settled, the result. */
53
+ interface Submission {
54
+ /** Section labels or workpool item the call fills; absent for a whole result. */
55
+ target?: string;
56
+ /** The accepted (normalized) data once settled, else the data as sent. */
57
+ data: unknown;
58
+ /** The failure the call reports, or why the tool rejected it. */
59
+ error?: string;
60
+ settled: boolean;
61
+ /** Accepted although it failed the output schema. */
62
+ overridden: boolean;
63
+ /** Where a settled call without data takes its result from. */
64
+ note?: string;
65
+ }
66
+
67
+ function readSubmission(
68
+ args: YieldRenderArgs | undefined,
69
+ result: ToolRenderResult<YieldItem> | undefined,
70
+ ): Submission {
71
+ const details = result?.isError ? undefined : result?.details;
72
+ const type = details?.type ?? args?.type ?? undefined;
73
+ const incremental = Array.isArray(type) && type.length > 0;
74
+ const data = details?.data !== undefined ? details.data : (args?.data ?? undefined);
75
+ let error = args?.error || undefined;
76
+ if (result?.isError) error = resultText(result) || `${TITLE} failed`;
77
+ else if (result && details?.status === "aborted") error = details.error || resultText(result) || "Aborted";
78
+ const settled = result !== undefined;
79
+ return {
80
+ target: typeof args?.key === "number" ? `item ${args.key}` : incremental ? type.join(", ") : undefined,
81
+ data,
82
+ error,
83
+ settled,
84
+ overridden: details?.schemaOverridden === true,
85
+ note:
86
+ settled && error === undefined && data === undefined
87
+ ? incremental
88
+ ? "Takes the last message as this section"
89
+ : "Ends with the sections submitted earlier, or the last message"
90
+ : undefined,
91
+ };
92
+ }
93
+
94
+ /** Head fact for container data (`13 fields`, `4 items`). */
95
+ function sizeMeta(data: unknown): string | undefined {
96
+ if (Array.isArray(data)) return formatCount("item", data.length);
97
+ if (isRecord(data)) return formatCount("field", Object.keys(data).length);
98
+ return undefined;
99
+ }
100
+
101
+ function describeData(data: unknown, streaming: boolean): NativeChild | undefined {
102
+ if (data === undefined) return undefined;
103
+ if (typeof data === "string") return data.trim() ? md(data, { stream: streaming }) : noteText("(empty)");
104
+ // Open to the depth cap: the data is the card's whole point, and the
105
+ // collapsed card's preview clamp already bounds what shows.
106
+ return describeJsonTree(data, { hiddenRootKeys: [], openDepth: JSON_TREE_MAX_DEPTH_EXPANDED });
107
+ }
108
+
109
+ function describeSubmission(s: Submission, streaming: boolean): NativeToolView {
110
+ const size = sizeMeta(s.data);
111
+ return {
112
+ tool: {
113
+ title: TITLE,
114
+ target: s.target,
115
+ targetKind: s.target ? "text" : undefined,
116
+ meta: size ? [size] : undefined,
117
+ badges: s.overridden ? [{ text: "schema overridden", tone: "warning" }] : undefined,
118
+ },
119
+ tone: s.settled && s.error !== undefined ? "error" : undefined,
120
+ body: compact([
121
+ s.error !== undefined ? errorText(s.error) : undefined,
122
+ describeData(s.data, streaming),
123
+ s.note ? noteText(s.note, "dim") : undefined,
124
+ ]),
125
+ };
126
+ }
127
+
128
+ function dataLines(data: unknown, expanded: boolean, width: number, theme: Theme): string[] {
129
+ if (data === undefined) return [];
130
+ if (typeof data === "string") {
131
+ return [
132
+ ...formatOutputPaneLines(
133
+ {
134
+ lines: sanitizeText(data).trimEnd().split("\n"),
135
+ expanded,
136
+ collapsedMaxLines: TEXT_ROWS_COLLAPSED,
137
+ styleLine: line => truncateToWidth(styleToolOutputLine(line, theme), width),
138
+ },
139
+ theme,
140
+ ).lines,
141
+ ];
142
+ }
143
+ const tree = renderJsonTreeLines(data, theme, {
144
+ maxDepth: expanded ? JSON_TREE_MAX_DEPTH_EXPANDED : JSON_TREE_MAX_DEPTH_COLLAPSED,
145
+ maxLines: expanded ? JSON_TREE_MAX_LINES_EXPANDED : JSON_TREE_MAX_LINES_COLLAPSED,
146
+ maxScalarLen: expanded ? JSON_TREE_SCALAR_LEN_EXPANDED : JSON_TREE_SCALAR_LEN_COLLAPSED,
147
+ sanitizeText,
148
+ hiddenRootKeys: [],
149
+ });
150
+ if (tree.truncated) tree.lines.push(expanded ? theme.fg("dim", "…") : formatExpandHint(theme, false, true));
151
+ return tree.lines;
152
+ }
153
+
154
+ function renderSubmission(s: Submission, options: RenderResultOptions, theme: Theme): Component {
155
+ // The body is spinner-invariant; rebuild it only on expansion or width change.
156
+ let memo: { expanded: boolean; width: number; lines: readonly string[] } | undefined;
157
+ return plainToolCard(
158
+ theme,
159
+ ({ contentWidth }) => {
160
+ const running = options.spinnerFrame !== undefined;
161
+ const failed = s.settled && s.error !== undefined;
162
+ const size = sizeMeta(s.data);
163
+ const status: StatusLineOptions = {
164
+ icon: !s.settled ? (running ? "running" : "pending") : failed ? "error" : "done",
165
+ spinnerFrame: options.spinnerFrame,
166
+ title: TITLE,
167
+ description: s.target,
168
+ badge: s.overridden ? { label: "schema overridden", color: "warning" } : undefined,
169
+ meta: size ? [size] : undefined,
170
+ };
171
+ const phase: ToolCardPhase = !s.settled ? (running ? "running" : "partial") : failed ? "error" : "success";
172
+ if (memo === undefined || memo.expanded !== options.expanded || memo.width !== contentWidth) {
173
+ const lines: string[] = [];
174
+ // Wrapped in full: a schema rejection names every offending field.
175
+ if (s.error !== undefined) {
176
+ for (const line of sanitizeDisplayLines(s.error)) {
177
+ lines.push(...wrapTextWithAnsi(theme.fg("error", line), Math.max(1, contentWidth)));
178
+ }
179
+ }
180
+ lines.push(...dataLines(s.data, options.expanded, contentWidth, theme));
181
+ if (s.note) lines.push(theme.fg("dim", s.note));
182
+ memo = { expanded: options.expanded, width: contentWidth, lines };
183
+ }
184
+ return { status, phase, body: memo.lines };
185
+ },
186
+ { paddingX: 1, paddingY: 1, ignoreTight: true, onInvalidate: () => (memo = undefined) },
187
+ );
188
+ }
189
+
190
+ const callMemo = new OwnerMemo<NativeToolView>();
191
+ const resultMemo = new OwnerMemo<NativeToolView>();
192
+
193
+ /** Shows a `yield` call's submitted data instead of its model-facing acknowledgement. */
194
+ export const yieldToolRenderer = {
195
+ renderCall(args: YieldRenderArgs, options: RenderResultOptions, theme: Theme): Component {
196
+ return renderSubmission(readSubmission(args, undefined), options, theme);
197
+ },
198
+
199
+ renderResult(
200
+ result: ToolRenderResult<YieldItem>,
201
+ options: RenderResultContextOptions,
202
+ theme: Theme,
203
+ args?: YieldRenderArgs,
204
+ ): Component {
205
+ return renderSubmission(readSubmission(args, result), options, theme);
206
+ },
207
+
208
+ describeCall(args: YieldRenderArgs, options: RenderResultOptions): NativeToolView {
209
+ const streaming = options.argsComplete === false;
210
+ return callMemo.get(args, [streaming], () => describeSubmission(readSubmission(args, undefined), streaming));
211
+ },
212
+
213
+ describeResult(
214
+ result: ToolRenderResult<YieldItem>,
215
+ _options: RenderResultContextOptions,
216
+ args?: YieldRenderArgs,
217
+ ): NativeToolView {
218
+ return resultMemo.get(result.content, [args, result.details, result.isError], () =>
219
+ describeSubmission(readSubmission(args, result), false),
220
+ );
221
+ },
222
+
223
+ mergeCallAndResult: true,
224
+ animatedPendingPreview: true,
225
+ } satisfies ToolRenderer<YieldRenderArgs, YieldItem>;
package/src/tui.d.ts CHANGED
@@ -78,6 +78,10 @@ export interface TUIStartOptions {
78
78
  * Paint without owning stdin: the terminal stays in cooked mode (kernel
79
79
  * echo + line editing at the hardware cursor) until {@link TUI.enableInput}
80
80
  * switches to raw input and replays the kernel-buffered keystrokes.
81
+ *
82
+ * A terminal expected to speak TSP needs raw input from the start, so it
83
+ * gets it; its keystrokes are held instead (TSP events and the cell-size
84
+ * reply still apply) until {@link TUI.releaseHeldInput} replays them.
81
85
  */
82
86
  deferInput?: boolean;
83
87
  }
@@ -150,6 +154,11 @@ export interface Component {
150
154
  describeScreen?(cx: DescribeContext): NativeScreen;
151
155
  /** User actions on nodes this component described (toggle, select, activate, custom). */
152
156
  handleNativeEvent?(event: NativeUiEvent): void;
157
+ /**
158
+ * Pure preflight for focused input that must precede TUI-wide input listeners
159
+ * and debug shortcuts. A true result routes the event to this component first.
160
+ */
161
+ capturesInput?(data: string): boolean;
153
162
  /**
154
163
  * Optional handler for keyboard input when component has focus
155
164
  */
@@ -164,6 +173,16 @@ export interface Component {
164
173
  * Called when theme changes or when component needs to re-render from scratch.
165
174
  */
166
175
  invalidate?(): void;
176
+ /**
177
+ * Optional hook to drop memoized render output (rows, parse and wrap state)
178
+ * that the next `render()` can rebuild from state the component keeps.
179
+ * Unlike {@link invalidate}, it must not rebuild anything eagerly: no
180
+ * renderer or extension callbacks, no image conversions, and no child
181
+ * replacement or disposal. The next render must return the same rows it
182
+ * would have returned without the release. Components without it are
183
+ * simply not released.
184
+ */
185
+ releaseRenderCaches?(): void;
167
186
  /**
168
187
  * Optional hook to set whether this component ignores tight layout mode.
169
188
  */
@@ -303,6 +322,7 @@ export declare class Container implements Component {
303
322
  /** Dispose every child, then detach it from this container. */
304
323
  disposeChildren(): void;
305
324
  invalidate(): void;
325
+ releaseRenderCaches(): void;
306
326
  /**
307
327
  * Propagate teardown to children. Call when the container's children are
308
328
  * being permanently discarded (not when they are detached for reuse — use
@@ -471,6 +491,22 @@ export declare class TUI extends Container {
471
491
  * path. Idempotent; no-op when input was never deferred.
472
492
  */
473
493
  enableInput(): void;
494
+ /**
495
+ * Replay the keystrokes held since a TSP `deferInput` start through the
496
+ * normal input path, then deliver input live. Call once the app's key
497
+ * handlers are installed so a hotkey pressed during startup still fires.
498
+ * Only keys typed while the start-time focus owner had focus are held. The
499
+ * hold survives a stop/start until released. Idempotent; no-op when nothing
500
+ * is held.
501
+ */
502
+ releaseHeldInput(): void;
503
+ /**
504
+ * Hand held-key ownership from `previous` to `next` when the app replaces
505
+ * the component focused at start (a swapped-in custom editor), so keys
506
+ * typed into the replacement stay queued behind the held ones instead of
507
+ * overtaking them. No-op unless `previous` owns the held keys.
508
+ */
509
+ replaceHeldFocus(previous: Component, next: Component): void;
474
510
  addStartListener(listener: StartListener): () => void;
475
511
  addInputListener(listener: InputListener): () => void;
476
512
  removeInputListener(listener: InputListener): void;