@oh-my-pi/pi-tui 18.4.3 → 18.4.4

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 (447) hide show
  1. package/CHANGELOG.md +36 -0
  2. package/dist/types/apps/cleanse-board.d.ts +17 -0
  3. package/dist/types/apps/git/help.d.ts +16 -0
  4. package/dist/types/apps/git/sidebar.d.ts +18 -0
  5. package/dist/types/apps/live-visualizer.d.ts +10 -0
  6. package/dist/types/apps/ps-top.d.ts +15 -0
  7. package/dist/types/apps/standalone-picker.d.ts +3 -0
  8. package/dist/types/autocomplete.d.ts +8 -0
  9. package/dist/types/chat/advisor-message.d.ts +12 -2
  10. package/dist/types/chat/assistant-message.d.ts +20 -0
  11. package/dist/types/chat/bash-execution.d.ts +9 -0
  12. package/dist/types/chat/chat-transcript-builder.d.ts +6 -1
  13. package/dist/types/chat/collab-prompt-message.d.ts +4 -0
  14. package/dist/types/chat/compaction-summary-message.d.ts +15 -0
  15. package/dist/types/chat/eval-execution.d.ts +8 -0
  16. package/dist/types/chat/execution-shared.d.ts +58 -0
  17. package/dist/types/chat/late-diagnostics-message.d.ts +15 -1
  18. package/dist/types/chat/read-tool-group.d.ts +15 -0
  19. package/dist/types/chat/skill-message.d.ts +9 -0
  20. package/dist/types/chat/stripped-tool-calls-placeholder.d.ts +3 -0
  21. package/dist/types/chat/todo-reminder.d.ts +4 -1
  22. package/dist/types/chat/tool-execution.d.ts +25 -1
  23. package/dist/types/chat/transcript-actions.d.ts +37 -0
  24. package/dist/types/chat/transcript-entry.d.ts +3 -1
  25. package/dist/types/chat/user-message.d.ts +25 -0
  26. package/dist/types/chrome/collab-qrcode.d.ts +7 -0
  27. package/dist/types/chrome/context-thresholds.d.ts +11 -0
  28. package/dist/types/chrome/countdown-timer.d.ts +9 -3
  29. package/dist/types/chrome/diff.d.ts +12 -0
  30. package/dist/types/chrome/dynamic-border.d.ts +3 -0
  31. package/dist/types/chrome/format.d.ts +6 -0
  32. package/dist/types/chrome/message-divider.d.ts +13 -0
  33. package/dist/types/chrome/message-frame.d.ts +10 -0
  34. package/dist/types/chrome/message-notice.d.ts +23 -0
  35. package/dist/types/chrome/overlay-box.d.ts +11 -2
  36. package/dist/types/chrome/qrcode.d.ts +7 -0
  37. package/dist/types/chrome/segment-track.d.ts +7 -0
  38. package/dist/types/chrome/selector-helpers.d.ts +0 -6
  39. package/dist/types/chrome/status-notice.d.ts +14 -0
  40. package/dist/types/chrome/streaming-panel.d.ts +3 -0
  41. package/dist/types/chrome/tool-activity.d.ts +3 -0
  42. package/dist/types/chrome/transcript-container.d.ts +13 -0
  43. package/dist/types/chrome/transcript-status.d.ts +15 -3
  44. package/dist/types/components/box.d.ts +7 -0
  45. package/dist/types/components/disclosure.d.ts +9 -0
  46. package/dist/types/components/editor.d.ts +37 -0
  47. package/dist/types/components/form.d.ts +14 -0
  48. package/dist/types/components/image.d.ts +7 -0
  49. package/dist/types/components/input.d.ts +20 -1
  50. package/dist/types/components/key-value-list.d.ts +3 -0
  51. package/dist/types/components/layout/geometry.d.ts +16 -0
  52. package/dist/types/components/layout/row.d.ts +7 -0
  53. package/dist/types/components/layout/split-pane.d.ts +8 -0
  54. package/dist/types/components/layout/stack.d.ts +6 -0
  55. package/dist/types/components/loader.d.ts +48 -0
  56. package/dist/types/components/markdown.d.ts +8 -0
  57. package/dist/types/components/metric.d.ts +7 -0
  58. package/dist/types/components/progress-bar.d.ts +3 -0
  59. package/dist/types/components/scroll-view.d.ts +6 -0
  60. package/dist/types/components/section.d.ts +3 -0
  61. package/dist/types/components/select-list.d.ts +33 -0
  62. package/dist/types/components/settings-list.d.ts +68 -0
  63. package/dist/types/components/spacer.d.ts +4 -0
  64. package/dist/types/components/tab-bar.d.ts +9 -0
  65. package/dist/types/components/table.d.ts +7 -0
  66. package/dist/types/components/text.d.ts +6 -0
  67. package/dist/types/components/tree-view.d.ts +9 -0
  68. package/dist/types/components/truncated-text.d.ts +3 -0
  69. package/dist/types/components/wizard-step.d.ts +6 -0
  70. package/dist/types/hotkeys-markdown.d.ts +24 -0
  71. package/dist/types/index.d.ts +5 -0
  72. package/dist/types/latex-block.d.ts +7 -0
  73. package/dist/types/native/apply.d.ts +39 -0
  74. package/dist/types/native/backend.d.ts +94 -0
  75. package/dist/types/native/blobs.d.ts +29 -0
  76. package/dist/types/native/describe.d.ts +56 -0
  77. package/dist/types/native/encode.d.ts +50 -0
  78. package/dist/types/native/icons.d.ts +18 -0
  79. package/dist/types/native/memo.d.ts +27 -0
  80. package/dist/types/native/node.d.ts +107 -0
  81. package/dist/types/native/overlay.d.ts +64 -0
  82. package/dist/types/native/picker.d.ts +124 -0
  83. package/dist/types/native/reconcile.d.ts +41 -0
  84. package/dist/types/native/settle.d.ts +11 -0
  85. package/dist/types/native/spans.d.ts +21 -0
  86. package/dist/types/native/state.d.ts +6 -0
  87. package/dist/types/native/tone.d.ts +24 -0
  88. package/dist/types/overlays/advisor-config.d.ts +20 -0
  89. package/dist/types/overlays/agent-hub-renderer.d.ts +27 -11
  90. package/dist/types/overlays/agent-hub.d.ts +10 -0
  91. package/dist/types/overlays/agent-transcript-viewer.d.ts +7 -0
  92. package/dist/types/overlays/agents-hub.d.ts +8 -0
  93. package/dist/types/overlays/annotation-overlay.d.ts +3 -0
  94. package/dist/types/overlays/ask-dialog.d.ts +23 -0
  95. package/dist/types/overlays/bordered-loader.d.ts +3 -0
  96. package/dist/types/overlays/btw-history-panel.d.ts +9 -0
  97. package/dist/types/overlays/btw-panel.d.ts +3 -0
  98. package/dist/types/overlays/cleanse-panel.d.ts +5 -2
  99. package/dist/types/overlays/composer-shape-preview.d.ts +7 -1
  100. package/dist/types/overlays/copy-selector.d.ts +38 -0
  101. package/dist/types/overlays/error-banner.d.ts +9 -0
  102. package/dist/types/overlays/extensions/extension-list.d.ts +1 -0
  103. package/dist/types/overlays/history-search.d.ts +18 -0
  104. package/dist/types/overlays/hook-editor.d.ts +28 -1
  105. package/dist/types/overlays/hook-input.d.ts +3 -0
  106. package/dist/types/overlays/hook-selector.d.ts +10 -0
  107. package/dist/types/overlays/hub-frame.d.ts +18 -0
  108. package/dist/types/overlays/jobs-panel.d.ts +24 -0
  109. package/dist/types/overlays/login-dialog.d.ts +10 -0
  110. package/dist/types/overlays/logout-account-selector.d.ts +4 -0
  111. package/dist/types/overlays/mcp-add-wizard.d.ts +4 -0
  112. package/dist/types/overlays/model-browser.d.ts +69 -0
  113. package/dist/types/overlays/model-hub.d.ts +7 -0
  114. package/dist/types/overlays/model-picker.d.ts +7 -0
  115. package/dist/types/overlays/move-overlay.d.ts +3 -0
  116. package/dist/types/overlays/oauth-selector.d.ts +4 -0
  117. package/dist/types/overlays/omfg-panel.d.ts +3 -0
  118. package/dist/types/overlays/pause-screen.d.ts +12 -0
  119. package/dist/types/overlays/plan-review-overlay.d.ts +5 -0
  120. package/dist/types/overlays/plan-save-overlay.d.ts +10 -0
  121. package/dist/types/overlays/plugin-selector.d.ts +4 -0
  122. package/dist/types/overlays/plugin-settings.d.ts +30 -0
  123. package/dist/types/overlays/queue-mode-selector.d.ts +4 -0
  124. package/dist/types/overlays/reset-usage-selector.d.ts +8 -0
  125. package/dist/types/overlays/rewind-selector.d.ts +5 -0
  126. package/dist/types/overlays/session-info-overlay.d.ts +19 -1
  127. package/dist/types/overlays/session-selector.d.ts +64 -1
  128. package/dist/types/overlays/settings-defs.d.ts +14 -0
  129. package/dist/types/overlays/settings-selector.d.ts +21 -0
  130. package/dist/types/overlays/show-images-selector.d.ts +4 -0
  131. package/dist/types/overlays/snapcompact-shape-preview.d.ts +2 -0
  132. package/dist/types/overlays/stats-notice.d.ts +8 -0
  133. package/dist/types/overlays/theme-selector.d.ts +11 -0
  134. package/dist/types/overlays/thinking-selector.d.ts +4 -0
  135. package/dist/types/overlays/tree-selector.d.ts +60 -2
  136. package/dist/types/overlays/usage-dashboard.d.ts +26 -3
  137. package/dist/types/overlays/usage-row.d.ts +36 -2
  138. package/dist/types/prompt/attachment-chips.d.ts +8 -0
  139. package/dist/types/prompt/composer-attachments.d.ts +15 -3
  140. package/dist/types/prompt/composer.d.ts +18 -1
  141. package/dist/types/prompt/custom-editor.d.ts +48 -2
  142. package/dist/types/prompt/editor-top-gap.d.ts +2 -0
  143. package/dist/types/prompt/macos-spelling.d.ts +6 -0
  144. package/dist/types/prompt/magic-keywords.d.ts +9 -0
  145. package/dist/types/prompt/queued-messages.d.ts +23 -0
  146. package/dist/types/prompt/skill-tokens.d.ts +4 -2
  147. package/dist/types/prompt/welcome.d.ts +16 -0
  148. package/dist/types/render/code-cell.d.ts +10 -0
  149. package/dist/types/render/file-list.d.ts +8 -0
  150. package/dist/types/render/hyperlink.d.ts +14 -0
  151. package/dist/types/render/output-block.d.ts +43 -0
  152. package/dist/types/render/output-pane.d.ts +17 -0
  153. package/dist/types/render/tool-card.d.ts +8 -0
  154. package/dist/types/render/tree-list.d.ts +8 -0
  155. package/dist/types/render/width-aware-text.d.ts +7 -0
  156. package/dist/types/setup/scenes/outro.d.ts +7 -0
  157. package/dist/types/setup/scenes/sign-in.d.ts +7 -0
  158. package/dist/types/setup/scenes/splash.d.ts +8 -0
  159. package/dist/types/setup/scenes/types.d.ts +6 -1
  160. package/dist/types/setup/wizard-overlay.d.ts +9 -0
  161. package/dist/types/status-line/component.d.ts +49 -5
  162. package/dist/types/status-line/context-usage.d.ts +20 -0
  163. package/dist/types/status-line/footer.d.ts +8 -0
  164. package/dist/types/status-line/metrics.d.ts +1 -2
  165. package/dist/types/status-line/segments.d.ts +10 -1
  166. package/dist/types/status-line/types.d.ts +40 -1
  167. package/dist/types/terminal-capabilities.d.ts +1 -1
  168. package/dist/types/terminal.d.ts +27 -0
  169. package/dist/types/theme/defaults/index.d.ts +100 -0
  170. package/dist/types/theme/schema.d.ts +2 -0
  171. package/dist/types/theme/shimmer.d.ts +8 -0
  172. package/dist/types/theme/theme-class.d.ts +2 -0
  173. package/dist/types/theme/theme.d.ts +33 -1
  174. package/dist/types/tools/ask.d.ts +3 -0
  175. package/dist/types/tools/ast-edit.d.ts +10 -1
  176. package/dist/types/tools/ast-grep.d.ts +10 -1
  177. package/dist/types/tools/autoresearch.d.ts +21 -4
  178. package/dist/types/tools/bash.d.ts +30 -1
  179. package/dist/types/tools/cfg-render.d.ts +5 -1
  180. package/dist/types/tools/debug.d.ts +3 -1
  181. package/dist/types/tools/default-renderer.d.ts +6 -1
  182. package/dist/types/tools/edit.d.ts +14 -1
  183. package/dist/types/tools/eval.d.ts +12 -1
  184. package/dist/types/tools/fetch.d.ts +16 -1
  185. package/dist/types/tools/find.d.ts +15 -1
  186. package/dist/types/tools/github.d.ts +3 -0
  187. package/dist/types/tools/glob.d.ts +14 -1
  188. package/dist/types/tools/goal.d.ts +3 -1
  189. package/dist/types/tools/grep.d.ts +14 -1
  190. package/dist/types/tools/grouped-file-output.d.ts +58 -0
  191. package/dist/types/tools/json-tree.d.ts +20 -0
  192. package/dist/types/tools/lsp.d.ts +3 -10
  193. package/dist/types/tools/mcp.d.ts +16 -1
  194. package/dist/types/tools/memory.d.ts +26 -0
  195. package/dist/types/tools/native-view.d.ts +90 -0
  196. package/dist/types/tools/proc-render.d.ts +7 -1
  197. package/dist/types/tools/read.d.ts +23 -1
  198. package/dist/types/tools/renderer.d.ts +57 -0
  199. package/dist/types/tools/report-tool-issue.d.ts +3 -0
  200. package/dist/types/tools/resolve.d.ts +14 -1
  201. package/dist/types/tools/task.d.ts +3 -0
  202. package/dist/types/tools/think.d.ts +3 -0
  203. package/dist/types/tools/todo.d.ts +12 -0
  204. package/dist/types/tools/vibe.d.ts +3 -1
  205. package/dist/types/tools/wait.d.ts +3 -0
  206. package/dist/types/tools/web-search.d.ts +8 -0
  207. package/dist/types/tools/write.d.ts +14 -1
  208. package/dist/types/tools/xdev.d.ts +13 -0
  209. package/dist/types/tui.d.ts +49 -0
  210. package/package.json +9 -9
  211. package/src/apps/autoresearch-dashboard.ts +362 -4
  212. package/src/apps/cleanse-board.ts +219 -1
  213. package/src/apps/cleanse-picker.ts +9 -1
  214. package/src/apps/debug/log-viewer.ts +15 -15
  215. package/src/apps/debug/protocol-probe.ts +2 -20
  216. package/src/apps/git/avatar.ts +2 -22
  217. package/src/apps/git/git-tui.ts +406 -59
  218. package/src/apps/git/help.ts +130 -0
  219. package/src/apps/git/sidebar.ts +491 -0
  220. package/src/apps/live-visualizer.ts +65 -0
  221. package/src/apps/ps-top.ts +448 -17
  222. package/src/apps/session-picker.ts +1 -0
  223. package/src/apps/setup-model-picker.ts +5 -1
  224. package/src/apps/standalone-picker.ts +55 -1
  225. package/src/autocomplete.ts +44 -1
  226. package/src/chat/advisor-message.ts +72 -3
  227. package/src/chat/assistant-message.ts +346 -11
  228. package/src/chat/background-tan-message.ts +4 -4
  229. package/src/chat/bash-execution.ts +81 -0
  230. package/src/chat/cache-invalidation-marker.ts +8 -0
  231. package/src/chat/chat-transcript-builder.ts +37 -6
  232. package/src/chat/collab-prompt-message.ts +15 -3
  233. package/src/chat/compaction-summary-message.ts +74 -1
  234. package/src/chat/custom-message.ts +3 -1
  235. package/src/chat/eval-execution.ts +57 -0
  236. package/src/chat/execution-shared.ts +153 -1
  237. package/src/chat/hook-message.ts +1 -0
  238. package/src/chat/late-diagnostics-message.ts +85 -2
  239. package/src/chat/read-tool-group.ts +341 -1
  240. package/src/chat/served-model-marker.ts +8 -0
  241. package/src/chat/skill-message.ts +63 -0
  242. package/src/chat/stripped-tool-calls-placeholder.ts +18 -10
  243. package/src/chat/todo-reminder.ts +34 -4
  244. package/src/chat/tool-execution.ts +385 -10
  245. package/src/chat/transcript-actions.ts +38 -0
  246. package/src/chat/transcript-browser.ts +2 -9
  247. package/src/chat/transcript-entry.ts +10 -1
  248. package/src/chat/transcript-render-helpers.ts +16 -15
  249. package/src/chat/ttsr-notification.ts +38 -1
  250. package/src/chat/user-message.ts +167 -4
  251. package/src/chrome/collab-qrcode.ts +27 -2
  252. package/src/chrome/context-thresholds.ts +34 -0
  253. package/src/chrome/countdown-timer.ts +24 -1
  254. package/src/chrome/diff.ts +60 -1
  255. package/src/chrome/dynamic-border.ts +7 -0
  256. package/src/chrome/format.ts +12 -0
  257. package/src/chrome/message-divider.ts +33 -1
  258. package/src/chrome/message-frame.ts +53 -10
  259. package/src/chrome/message-notice.ts +71 -0
  260. package/src/chrome/overlay-box.ts +45 -2
  261. package/src/chrome/qrcode.ts +28 -0
  262. package/src/chrome/segment-track.ts +26 -0
  263. package/src/chrome/selector-helpers.ts +1 -17
  264. package/src/chrome/status-notice.ts +46 -0
  265. package/src/chrome/streaming-panel.ts +25 -1
  266. package/src/chrome/tool-activity.ts +20 -0
  267. package/src/chrome/transcript-container.ts +37 -0
  268. package/src/chrome/transcript-status.ts +40 -11
  269. package/src/components/box.ts +51 -0
  270. package/src/components/disclosure.ts +33 -0
  271. package/src/components/editor.ts +216 -27
  272. package/src/components/form.ts +93 -9
  273. package/src/components/image.ts +30 -0
  274. package/src/components/input.ts +77 -26
  275. package/src/components/key-value-list.ts +18 -0
  276. package/src/components/layout/geometry.ts +44 -0
  277. package/src/components/layout/row.ts +30 -0
  278. package/src/components/layout/split-pane.ts +65 -0
  279. package/src/components/layout/stack.ts +27 -0
  280. package/src/components/loader.ts +183 -1
  281. package/src/components/markdown.ts +21 -0
  282. package/src/components/metric.ts +39 -0
  283. package/src/components/progress-bar.ts +28 -0
  284. package/src/components/scroll-view.ts +23 -0
  285. package/src/components/section.ts +16 -0
  286. package/src/components/select-list.ts +174 -25
  287. package/src/components/settings-list.ts +264 -7
  288. package/src/components/spacer.ts +17 -0
  289. package/src/components/tab-bar.ts +42 -0
  290. package/src/components/table.ts +34 -0
  291. package/src/components/text.ts +23 -0
  292. package/src/components/tree-view.ts +65 -0
  293. package/src/components/truncated-text.ts +14 -0
  294. package/src/components/wizard-step.ts +18 -9
  295. package/src/debug-server.ts +14 -7
  296. package/src/hotkeys-markdown.ts +251 -69
  297. package/src/index.ts +6 -0
  298. package/src/latex-block.ts +11 -0
  299. package/src/native/apply.ts +275 -0
  300. package/src/native/backend.ts +682 -0
  301. package/src/native/blobs.ts +70 -0
  302. package/src/native/describe.ts +124 -0
  303. package/src/native/encode.ts +233 -0
  304. package/src/native/icons.ts +184 -0
  305. package/src/native/memo.ts +64 -0
  306. package/src/native/node.ts +112 -0
  307. package/src/native/overlay.ts +131 -0
  308. package/src/native/picker.ts +333 -0
  309. package/src/native/reconcile.ts +1063 -0
  310. package/src/native/settle.ts +23 -0
  311. package/src/native/spans.ts +308 -0
  312. package/src/native/state.ts +28 -0
  313. package/src/native/tone.ts +79 -0
  314. package/src/overlays/advisor-config.ts +420 -0
  315. package/src/overlays/agent-hub-renderer.ts +155 -18
  316. package/src/overlays/agent-hub.ts +1037 -44
  317. package/src/overlays/agent-transcript-viewer.ts +129 -0
  318. package/src/overlays/agents-hub.ts +688 -53
  319. package/src/overlays/annotation-overlay.ts +391 -52
  320. package/src/overlays/ask-dialog.ts +435 -24
  321. package/src/overlays/bordered-loader.ts +17 -0
  322. package/src/overlays/btw-history-panel.ts +182 -0
  323. package/src/overlays/btw-panel.ts +68 -2
  324. package/src/overlays/cleanse-panel.ts +105 -15
  325. package/src/overlays/codex-reset-fireworks.ts +65 -5
  326. package/src/overlays/composer-shape-preview.ts +26 -1
  327. package/src/overlays/copy-selector.ts +545 -23
  328. package/src/overlays/error-banner.ts +44 -0
  329. package/src/overlays/extensions/extension-list.ts +23 -17
  330. package/src/overlays/history-search.ts +175 -33
  331. package/src/overlays/hook-editor.ts +113 -6
  332. package/src/overlays/hook-input.ts +22 -1
  333. package/src/overlays/hook-selector.ts +311 -22
  334. package/src/overlays/hub-frame.ts +80 -0
  335. package/src/overlays/jobs-panel.ts +112 -0
  336. package/src/overlays/login-dialog.ts +183 -1
  337. package/src/overlays/logout-account-selector.ts +102 -4
  338. package/src/overlays/mcp-add-wizard.ts +232 -53
  339. package/src/overlays/model-browser.ts +681 -22
  340. package/src/overlays/model-hub.ts +1402 -78
  341. package/src/overlays/model-picker.ts +175 -1
  342. package/src/overlays/move-overlay.ts +45 -0
  343. package/src/overlays/oauth-selector.ts +227 -23
  344. package/src/overlays/omfg-panel.ts +86 -2
  345. package/src/overlays/pause-screen.ts +55 -4
  346. package/src/overlays/plan-review-overlay.ts +352 -0
  347. package/src/overlays/plan-save-overlay.ts +45 -0
  348. package/src/overlays/plugin-selector.ts +21 -1
  349. package/src/overlays/plugin-settings.ts +195 -5
  350. package/src/overlays/queue-mode-selector.ts +19 -1
  351. package/src/overlays/reset-usage-selector.ts +110 -40
  352. package/src/overlays/rewind-selector.ts +458 -44
  353. package/src/overlays/session-account-selector.ts +1 -1
  354. package/src/overlays/session-info-overlay.ts +137 -1
  355. package/src/overlays/session-selector.ts +807 -46
  356. package/src/overlays/settings-defs.ts +41 -0
  357. package/src/overlays/settings-selector.ts +583 -23
  358. package/src/overlays/show-images-selector.ts +18 -1
  359. package/src/overlays/snapcompact-shape-preview.ts +48 -3
  360. package/src/overlays/stats-notice.ts +31 -0
  361. package/src/overlays/theme-selector.ts +31 -1
  362. package/src/overlays/thinking-selector.ts +22 -1
  363. package/src/overlays/tree-selector.ts +570 -110
  364. package/src/overlays/usage-dashboard.ts +587 -12
  365. package/src/overlays/usage-row.ts +161 -25
  366. package/src/prompt/attachment-chips.ts +60 -0
  367. package/src/prompt/composer-attachments.ts +31 -9
  368. package/src/prompt/composer.ts +55 -2
  369. package/src/prompt/custom-editor.ts +302 -4
  370. package/src/prompt/editor-top-gap.ts +8 -0
  371. package/src/prompt/macos-spelling.ts +24 -9
  372. package/src/prompt/magic-keywords.ts +22 -1
  373. package/src/prompt/model-mention-autocomplete.ts +1 -0
  374. package/src/prompt/queued-messages.ts +101 -0
  375. package/src/prompt/skill-tokens.ts +5 -3
  376. package/src/prompt/welcome.ts +213 -6
  377. package/src/render/code-cell.ts +103 -2
  378. package/src/render/file-list.ts +29 -0
  379. package/src/render/hyperlink.ts +32 -0
  380. package/src/render/output-block.ts +110 -0
  381. package/src/render/output-pane.ts +37 -0
  382. package/src/render/tool-card.ts +137 -0
  383. package/src/render/tree-list.ts +39 -0
  384. package/src/render/width-aware-text.ts +20 -0
  385. package/src/setup/scenes/composer.ts +29 -0
  386. package/src/setup/scenes/glyph.ts +15 -0
  387. package/src/setup/scenes/model.ts +36 -8
  388. package/src/setup/scenes/outro.ts +28 -1
  389. package/src/setup/scenes/sign-in.ts +122 -13
  390. package/src/setup/scenes/splash.ts +28 -1
  391. package/src/setup/scenes/theme.ts +88 -3
  392. package/src/setup/scenes/types.ts +7 -1
  393. package/src/setup/startup-splash.ts +46 -9
  394. package/src/setup/wizard-overlay.ts +140 -23
  395. package/src/status-line/component.ts +398 -16
  396. package/src/status-line/context-usage.ts +279 -50
  397. package/src/status-line/footer.ts +94 -1
  398. package/src/status-line/metrics.ts +3 -6
  399. package/src/status-line/segments.ts +463 -97
  400. package/src/status-line/separators.ts +3 -16
  401. package/src/status-line/types.ts +43 -1
  402. package/src/terminal-capabilities.ts +7 -0
  403. package/src/terminal.ts +130 -2
  404. package/src/theme/defaults/dark-neon-noir.json +100 -0
  405. package/src/theme/defaults/index.ts +2 -0
  406. package/src/theme/defaults/light-prism.json +5 -5
  407. package/src/theme/defaults/onyx.json +62 -62
  408. package/src/theme/schema.ts +15 -0
  409. package/src/theme/shimmer.ts +33 -0
  410. package/src/theme/theme-class.ts +24 -19
  411. package/src/theme/theme.ts +111 -9
  412. package/src/tools/ask.ts +157 -1
  413. package/src/tools/ast-edit.ts +174 -1
  414. package/src/tools/ast-grep.ts +82 -2
  415. package/src/tools/autoresearch.ts +149 -1
  416. package/src/tools/bash.ts +180 -49
  417. package/src/tools/cfg-render.ts +66 -1
  418. package/src/tools/debug.ts +78 -1
  419. package/src/tools/default-renderer.ts +35 -1
  420. package/src/tools/edit.ts +247 -2
  421. package/src/tools/eval.ts +257 -1
  422. package/src/tools/fetch.ts +102 -3
  423. package/src/tools/find.ts +127 -1
  424. package/src/tools/github.ts +119 -10
  425. package/src/tools/glob.ts +116 -1
  426. package/src/tools/goal.ts +56 -1
  427. package/src/tools/grep.ts +83 -2
  428. package/src/tools/grouped-file-output.ts +205 -0
  429. package/src/tools/json-tree.ts +116 -1
  430. package/src/tools/lsp.ts +318 -1
  431. package/src/tools/mcp.ts +80 -1
  432. package/src/tools/memory.ts +93 -11
  433. package/src/tools/native-view.ts +292 -0
  434. package/src/tools/proc-render.ts +221 -1
  435. package/src/tools/read.ts +199 -4
  436. package/src/tools/renderer.ts +61 -0
  437. package/src/tools/report-tool-issue.ts +7 -1
  438. package/src/tools/resolve.ts +63 -1
  439. package/src/tools/task.ts +406 -4
  440. package/src/tools/think.ts +19 -1
  441. package/src/tools/todo.ts +89 -0
  442. package/src/tools/vibe.ts +230 -2
  443. package/src/tools/wait.ts +181 -1
  444. package/src/tools/web-search.ts +113 -1
  445. package/src/tools/write.ts +144 -5
  446. package/src/tools/xdev.ts +64 -1
  447. package/src/tui.ts +294 -3
@@ -22,23 +22,47 @@ import {
22
22
  truncateToWidth,
23
23
  } from "../render/render-utils";
24
24
  import type { CoordinationDetails } from "./wait";
25
- import { renderAgentWrite, renderProcWrite, type ProcWriteAction, type ProcWriteDetails } from "./proc-render";
26
- import { renderCfgWrite, type CfgWriteDetails } from "./cfg-render";
25
+ import {
26
+ describeAgentWrite,
27
+ describeProcWrite,
28
+ renderAgentWrite,
29
+ renderProcWrite,
30
+ type ProcWriteAction,
31
+ type ProcWriteDetails,
32
+ } from "./proc-render";
33
+ import type { TspTone } from "@oh-my-pi/pi-wire";
34
+ import { code, compact, node, span } from "../native/describe";
35
+ import type { NativeChild } from "../native/node";
36
+ import { diagnosticsBadge, diagnosticsSection, displayPath, errorText, fileHref, resultText } from "./native-view";
37
+ import { describeCfgWrite, renderCfgWrite, type CfgWriteDetails } from "./cfg-render";
27
38
  import type { FileDiagnosticsResult } from "./lsp";
28
39
  import type { OutputMeta } from "./output-meta";
29
40
  import type { CardToolResult } from "./result-card";
30
- import type { RenderResultOptions, ToolActivityContext, ToolActivitySummary, ToolRenderer } from "./renderer";
41
+ import type {
42
+ NativeToolHead,
43
+ NativeToolView,
44
+ RenderResultOptions,
45
+ ToolActivityContext,
46
+ ToolActivitySummary,
47
+ ToolRenderer,
48
+ } from "./renderer";
31
49
  import { splitUrlScheme } from "./url-scheme-host";
32
50
  import { couldBecomeXdUrl, parseXdUrl } from "./xd-url";
33
51
  import {
52
+ describeXdevCall,
53
+ describeXdevResult,
34
54
  renderXdevCall,
35
55
  renderXdevResult,
36
56
  xdevActivitySummary,
37
57
  type XdevRenderDispatch,
38
58
  type XdevMountedRenderer,
39
59
  } from "./xdev";
40
- import { isResolutionDeviceName, renderResolutionDeviceCall } from "./resolve";
41
- import { REPORT_ISSUE_DEVICE_NAME, renderReportIssueDeviceCall } from "./report-tool-issue";
60
+ import { describeResolutionDeviceCall, isResolutionDeviceName, renderResolutionDeviceCall } from "./resolve";
61
+ import {
62
+ describeReportIssueDeviceCall,
63
+ REPORT_ISSUE_DEVICE_NAME,
64
+ renderReportIssueDeviceCall,
65
+ } from "./report-tool-issue";
42
66
  import { pendingFileLinkPath } from "./read";
43
67
 
44
68
  /** Details returned by the write tool for transcript rendering. */
@@ -64,6 +88,8 @@ interface WriteRenderArgs {
64
88
  }
65
89
 
66
90
  const WRITE_PREVIEW_LINES = 6;
91
+ /** Collapsed native write body: the first lines of the file (§7.3). */
92
+ const NATIVE_WRITE_PREVIEW = { lines: 8 } as const;
67
93
 
68
94
  function countLines(text: string): number {
69
95
  if (!text) return 0;
@@ -416,6 +442,48 @@ function couldBecomeWriteCardUrl(lowerPath: string): boolean {
416
442
  return false;
417
443
  }
418
444
 
445
+ /** Native view of a `proc://`, `agent://` or `cfg://` write. */
446
+ function describeWriteUrl(
447
+ routed: { card: WriteUrlCard; target: string },
448
+ url: string,
449
+ content: unknown,
450
+ result: CardToolResult | undefined,
451
+ details: WriteToolDetails | undefined,
452
+ ): NativeToolView {
453
+ const body = typeof content === "string" ? content : undefined;
454
+ switch (routed.card.detailsKey) {
455
+ case "message":
456
+ return describeAgentWrite(routed.target, body ?? "", result, details?.message);
457
+ case "proc": {
458
+ const { id, action } = procWriteTarget(routed.target);
459
+ return describeProcWrite(id, action, body, result, details?.proc);
460
+ }
461
+ case "cfg":
462
+ return describeCfgWrite(url, body, result, details?.cfg);
463
+ }
464
+ }
465
+
466
+ /** Native write head shared by the call and result: `Write · path · 22 lines`, executable/diagnostics chips. */
467
+ function writeToolHead(
468
+ rawPath: string,
469
+ content: string,
470
+ details?: Pick<WriteToolDetails, "madeExecutable" | "resolvedPath" | "diagnostics">,
471
+ ): NativeToolHead {
472
+ const lineCount = countLines(content);
473
+ const badges: { text: string; tone?: TspTone }[] = [];
474
+ if (details?.madeExecutable) badges.push({ text: "executable", tone: "success" });
475
+ const diagnostics = diagnosticsBadge([details?.diagnostics]);
476
+ if (diagnostics) badges.push(diagnostics);
477
+ return {
478
+ title: "Write",
479
+ target: rawPath ? displayPath(rawPath) : undefined,
480
+ targetKind: "path",
481
+ href: fileHref(details?.resolvedPath),
482
+ meta: lineCount > 0 ? [`${lineCount} line${lineCount === 1 ? "" : "s"}`] : undefined,
483
+ badges: badges.length > 0 ? badges : undefined,
484
+ };
485
+ }
486
+
419
487
  /** Render file writes and delegated tool-device calls. */
420
488
  export const writeToolRenderer = {
421
489
  /** Compact one-line activity: device writes read as the mounted tool (`LSP · references foo`), file writes as `Write · <path>`. */
@@ -618,6 +686,77 @@ export const writeToolRenderer = {
618
686
  };
619
687
  });
620
688
  },
689
+ describeCall(
690
+ args: WriteRenderArgs,
691
+ options: RenderResultOptions & { renderContext?: WriteRenderContext },
692
+ ): NativeToolView | undefined {
693
+ const rawPath =
694
+ typeof args.file_path === "string" ? args.file_path : typeof args.path === "string" ? args.path : "";
695
+ // Same gating as renderCall: nothing until the path provably is not a device/card URL.
696
+ if (args.path === undefined && args.file_path === undefined) return undefined;
697
+ const pathSettled = args.content !== undefined || options.argsComplete === true;
698
+ const hasStringPath = typeof args.file_path === "string" || typeof args.path === "string";
699
+ if (hasStringPath && !pathSettled && couldBecomeWriteCardUrl(rawPath.toLowerCase())) return undefined;
700
+ const routed = writeUrlCard(rawPath);
701
+ if (routed) return describeWriteUrl(routed, rawPath, args.content, undefined, undefined);
702
+ if (rawPath && couldBecomeXdUrl(rawPath)) {
703
+ const xdev = parseXdUrl(rawPath);
704
+ if (!xdev?.name || args.content === undefined) return undefined;
705
+ if (isResolutionDeviceName(xdev.name)) return describeResolutionDeviceCall(xdev.name, args.content);
706
+ if (xdev.name === REPORT_ISSUE_DEVICE_NAME) return describeReportIssueDeviceCall(args.content);
707
+ return describeXdevCall(xdev.name, args.content, options, options.renderContext?.resolveXdevMounted);
708
+ }
709
+ const content = normalizeDisplayText(args.content);
710
+ return {
711
+ tool: writeToolHead(rawPath, content),
712
+ body: content
713
+ ? [
714
+ code(content, {
715
+ lang: rawPath ? getLanguageFromPath(rawPath) : undefined,
716
+ numbers: true,
717
+ }),
718
+ ]
719
+ : [],
720
+ preview: NATIVE_WRITE_PREVIEW,
721
+ };
722
+ },
723
+
724
+ describeResult(
725
+ result: { content: Array<{ type: string; text?: string }>; details?: WriteToolDetails; isError?: boolean },
726
+ options: RenderResultOptions & { renderContext?: WriteRenderContext },
727
+ args?: WriteRenderArgs,
728
+ ): NativeToolView | undefined {
729
+ const cardPath =
730
+ typeof args?.path === "string" ? args.path : typeof args?.file_path === "string" ? args.file_path : "";
731
+ const routed = writeUrlCard(cardPath, result.details);
732
+ if (routed) return describeWriteUrl(routed, cardPath, args?.content, result, result.details);
733
+ const xdev = result.details?.xdev;
734
+ if (xdev) return describeXdevResult(xdev, result, options, options.renderContext?.resolveXdevMounted);
735
+ const rawPath =
736
+ typeof args?.file_path === "string" ? args.file_path : typeof args?.path === "string" ? args.path : "";
737
+ const fileContent = normalizeDisplayText(args?.content);
738
+ const isPartial = options.isPartial === true;
739
+ const diagnostics = isPartial ? undefined : result.details?.diagnostics;
740
+ const tool = writeToolHead(rawPath, fileContent, {
741
+ madeExecutable: !isPartial && result.details?.madeExecutable === true,
742
+ resolvedPath: result.details?.resolvedPath,
743
+ diagnostics,
744
+ });
745
+ if (result.isError) return { tool, tone: "error", body: [errorText(resultText(result))] };
746
+ const progressText = resultText(result);
747
+ const body = compact<NativeChild>([
748
+ isPartial &&
749
+ progressText.length > 0 &&
750
+ node("text", { spans: [span(progressText, "muted")], truncate: "end" }),
751
+ fileContent.length > 0 &&
752
+ code(fileContent, {
753
+ lang: rawPath ? getLanguageFromPath(rawPath) : undefined,
754
+ numbers: true,
755
+ }),
756
+ diagnosticsSection(diagnostics),
757
+ ]);
758
+ return { tool, body, preview: NATIVE_WRITE_PREVIEW };
759
+ },
621
760
  mergeCallAndResult: true,
622
761
  // The collapsed pending preview follows the streaming edge with a tail
623
762
  // window once the content outgrows it (`… (N earlier lines)` + last rows);
package/src/tools/xdev.ts CHANGED
@@ -4,12 +4,16 @@ import { type Component, Container, Text } from "../index";
4
4
  import type { Theme } from "../theme/theme";
5
5
  import { replaceTabs } from "../render/render-utils";
6
6
  import type { RenderResultOptions, ToolActivitySummary, ToolRenderer } from "./renderer";
7
- import { renderDefaultToolExecution } from "./default-renderer";
7
+ import { describeDefaultToolExecution, renderDefaultToolExecution } from "./default-renderer";
8
+ import type { NativeToolView } from "./renderer";
9
+ import { text } from "../native/describe";
8
10
  import { parseMCPToolName } from "./mcp";
9
11
 
10
12
  /** Mounted device display callbacks, independent of tool execution state. */
11
13
  export interface XdevMountedRenderer {
12
14
  label?: string;
15
+ describeCall?: ToolRenderer["describeCall"];
16
+ describeResult?: ToolRenderer["describeResult"];
13
17
  renderCall?(...args: Parameters<ToolRenderer["renderCall"]>): unknown;
14
18
  renderResult?(...args: Parameters<ToolRenderer["renderResult"]>): unknown;
15
19
  mergeCallAndResult?: boolean;
@@ -162,6 +166,65 @@ export function renderXdevCall(
162
166
  return renderDefaultToolExecution({ label: mounted?.label ?? name, args, options }, theme);
163
167
  }
164
168
 
169
+ /** Native form of {@link resolveDeviceRenderer}: the first renderer that can describe. */
170
+ function resolveDeviceDescriber(
171
+ name: string,
172
+ mounted: XdevMountedRenderer | undefined,
173
+ ): XdevMountedRenderer | undefined {
174
+ if (mounted && (mounted.describeCall || mounted.describeResult)) return mounted;
175
+ return rendererLookup?.(name);
176
+ }
177
+
178
+ /** TSP call view for an `xd://` write: queued card until execution, then the device renderer's own view. */
179
+ export function describeXdevCall(
180
+ name: string,
181
+ content: unknown,
182
+ options: RenderResultOptions,
183
+ resolveMounted?: (name: string) => XdevMountedRenderer | undefined,
184
+ ): NativeToolView | undefined {
185
+ const mounted = resolveMounted?.(name);
186
+ const args = decodeInnerArgs(content);
187
+ if (!options.executionStarted) {
188
+ return describeDefaultToolExecution({
189
+ label: `queued ${displayDeviceLabel(name, mounted)}`,
190
+ args: displayDeviceArgs(args),
191
+ options: { ...options, isPartial: true, spinnerFrame: undefined },
192
+ });
193
+ }
194
+ const renderer = resolveDeviceDescriber(name, mounted);
195
+ if (renderer?.describeCall) return renderer.describeCall(args, options);
196
+ return describeDefaultToolExecution({ label: mounted?.label ?? name, args, options });
197
+ }
198
+
199
+ /** TSP result view for an `xd://` dispatch, forwarded to the device renderer. */
200
+ export function describeXdevResult(
201
+ dispatch: XdevRenderDispatch,
202
+ result: { content: Array<{ type: string; text?: string }>; isError?: boolean },
203
+ options: RenderResultOptions,
204
+ resolveMounted?: (name: string) => XdevMountedRenderer | undefined,
205
+ ): NativeToolView | undefined {
206
+ const output = result.content
207
+ .map(block => (block.type === "text" ? block.text : ""))
208
+ .filter(Boolean)
209
+ .join("\n");
210
+ if (dispatch.mode === "help") return output ? { body: [text(output, { wrap: "word" })] } : undefined;
211
+ const mounted = resolveMounted?.(dispatch.tool);
212
+ const renderer = resolveDeviceDescriber(dispatch.tool, mounted);
213
+ if (renderer?.describeResult) {
214
+ const innerResult = { content: result.content, details: dispatch.inner, isError: result.isError };
215
+ const view = renderer.describeResult(innerResult, options, dispatch.args ?? {});
216
+ if (!view || renderer.mergeCallAndResult || !renderer.describeCall) return view;
217
+ const call = renderer.describeCall(dispatch.args ?? {}, { ...options, isPartial: false });
218
+ return { ...view, head: view.head ?? call?.head, body: [...(call?.body ?? []), ...(view.body ?? [])] };
219
+ }
220
+ return describeDefaultToolExecution({
221
+ label: mounted?.label ?? dispatch.tool,
222
+ args: dispatch.args ?? {},
223
+ result: { output, isError: result.isError },
224
+ options,
225
+ });
226
+ }
227
+
165
228
  /** Forward an `xd://` dispatch result to the mounted tool's renderer. */
166
229
  export function renderXdevResult(
167
230
  dispatch: XdevRenderDispatch,
package/src/tui.ts CHANGED
@@ -18,11 +18,16 @@ import { getDebugLogPath } from "@oh-my-pi/pi-utils/dirs";
18
18
  import { $flag } from "@oh-my-pi/pi-utils/env";
19
19
  import * as logger from "@oh-my-pi/pi-utils/logger";
20
20
  import * as postmortem from "@oh-my-pi/pi-utils/postmortem";
21
+ import type { TspFrame, TspNode, TspText } from "@oh-my-pi/pi-wire";
21
22
  import { DEFAULT_MAX_INLINE_IMAGES, ImageBudget } from "./components/image";
22
23
  import { TuiDebugServer } from "./debug-server";
23
24
  import { isKeyRelease, matchesKey } from "./keys";
24
25
  import { KITTY_PLACEHOLDER } from "./kitty-graphics";
25
26
  import { LoopWatchdog } from "./loop-watchdog";
27
+ import { assumedTspHello, NativeBackend, type NativeHost } from "./native/backend";
28
+ import { col } from "./native/describe";
29
+ import { TSP_PREFIX, type TspHello } from "./native/encode";
30
+ import type { DescribeContext, NativeNode, NativeSurfaceProvider, NativeUiEvent } from "./native/node";
26
31
  import { STDOUT_BACKLOG_CLEAR_BYTES, setAltScreenActive, type Terminal } from "./terminal";
27
32
  import {
28
33
  encodeKittyDeleteAllImages,
@@ -239,6 +244,39 @@ export interface Component {
239
244
  */
240
245
  render(width: number): readonly string[];
241
246
 
247
+ /**
248
+ * Describe the component semantically for a Tern Surface Protocol
249
+ * terminal (see `native/node.ts`). Called instead of `render()` when the
250
+ * native backend is active. Return the same node object while nothing
251
+ * changed; return null to fall back to `render()` rows.
252
+ */
253
+ describe?(cx: DescribeContext): NativeNode | null;
254
+
255
+ /**
256
+ * Props for the native `overlay` wrapper when this component is shown as
257
+ * an overlay: the sheet's `role` (Tern styles `omp.overlay.*` roles as
258
+ * glass sheets, so the component's own root must not draw a second frame),
259
+ * `head` spans for the sheet's title row, and `size`/`anchor` overriding
260
+ * the ones derived from the overlay options.
261
+ */
262
+ nativeOverlay?: {
263
+ role?: string;
264
+ head?: TspText;
265
+ size?: "sm" | "md" | "lg" | "full";
266
+ anchor?: "center" | "top" | "bottom";
267
+ };
268
+
269
+ /**
270
+ * True when this component, shown as an overlay, describes a data-first
271
+ * sheet (`picker`, `prefs`) that is its own frame: the backend puts it
272
+ * directly in `layer` with no `overlay` wrapper. Usually
273
+ * `cx.supports("picker")`, so older terminals keep the wrapped fallback.
274
+ */
275
+ nativeSheet?(cx: DescribeContext): boolean;
276
+
277
+ /** User actions on nodes this component described (toggle, select, activate, custom). */
278
+ handleNativeEvent?(event: NativeUiEvent): void;
279
+
242
280
  /**
243
281
  * Optional handler for keyboard input when component has focus
244
282
  */
@@ -450,6 +488,10 @@ export class Container implements Component {
450
488
  #memoLines: string[] | undefined;
451
489
  #memoChildLines: (readonly string[])[] = [];
452
490
  #memoWidth = -1;
491
+ // Memoized native description: rebuilt only when the child list changes, so
492
+ // the reconciler can skip an unchanged container by reference.
493
+ #nativeNode: NativeNode | undefined;
494
+ #nativeChildren: readonly Component[] = [];
453
495
 
454
496
  #ignoreTight = false;
455
497
 
@@ -535,6 +577,17 @@ export class Container implements Component {
535
577
  this.#memoLines = lines;
536
578
  return lines;
537
579
  }
580
+
581
+ describe(_cx: DescribeContext): NativeNode | null {
582
+ const children = this.children;
583
+ const previous = this.#nativeChildren;
584
+ let unchanged = this.#nativeNode !== undefined && previous.length === children.length;
585
+ for (let i = 0; unchanged && i < children.length; i++) unchanged = previous[i] === children[i];
586
+ if (unchanged) return this.#nativeNode!;
587
+ this.#nativeChildren = children.slice();
588
+ this.#nativeNode = col(this.#nativeChildren);
589
+ return this.#nativeNode;
590
+ }
538
591
  }
539
592
 
540
593
  interface HardwareCursorState {
@@ -915,6 +968,26 @@ export class TUI extends Container {
915
968
  // atomically. It must survive a deferred Ghostty image frame.
916
969
  #pendingAltExit = "";
917
970
 
971
+ // Tern Surface Protocol backend, created when the terminal answers the
972
+ // `hello` probe, or at start when the environment names Tern. Kept across
973
+ // stop/start so a restart adopts the surface.
974
+ #native: NativeBackend | undefined;
975
+ #nativeLive = false;
976
+ // Holds the first paint while the `hello` probe is outstanding, so a TSP
977
+ // terminal never sees a row paint it would have to erase.
978
+ #nativeHoldTimer: RenderTimer | undefined;
979
+ static readonly #NATIVE_PROBE_HOLD_MS = 300;
980
+ // Optimistic start (`TERM_PROGRAM=tern`): the surface is live on assumed
981
+ // capabilities until the real `hello` reply confirms it. Without a reply
982
+ // by the deadline the surface closes and rows repaint.
983
+ #nativeUnconfirmed = false;
984
+ #nativeConfirmTimer: RenderTimer | undefined;
985
+ static readonly #NATIVE_CONFIRM_MS = 1000;
986
+ // A deadline that fires this late ran after an event-loop stall (module
987
+ // loading), possibly with the reply already queued on stdin: re-arm for
988
+ // this long so input gets a turn before giving up.
989
+ static readonly #NATIVE_CONFIRM_GRACE_MS = 100;
990
+
918
991
  // Overlay stack for modal components rendered on top of base content
919
992
  overlayStack: {
920
993
  component: Component;
@@ -1246,7 +1319,12 @@ export class TUI extends Container {
1246
1319
  this.#debugServer = new TuiDebugServer(this, debugPath);
1247
1320
  this.#debugServer.start();
1248
1321
  }
1249
- this.#inputDeferred = options?.deferInput === true;
1322
+ // A terminal expected to speak TSP gets its surface from the first frame,
1323
+ // and a live surface needs raw input from the start: its events (resize,
1324
+ // acks) would otherwise be echoed into the grid by the cooked tty, and the
1325
+ // `hello` query must go out now to confirm the surface.
1326
+ const nativeExpected = this.terminal.tspExpected === true;
1327
+ this.#inputDeferred = options?.deferInput === true && !nativeExpected;
1250
1328
  this.#watchdog.start();
1251
1329
  this.#ghosttyInitialImageDelayDone = false;
1252
1330
  this.#ghosttyImageReadyAtMs = this.#renderScheduler.now() + TUI.#GHOSTTY_INITIAL_IMAGE_DELAY_MS;
@@ -1275,9 +1353,16 @@ export class TUI extends Container {
1275
1353
  this.invalidate();
1276
1354
  this.requestRender(true);
1277
1355
  });
1356
+ this.terminal.onTspHello?.(hello => this.#onTspHello(hello));
1278
1357
  this.terminal.start(
1279
1358
  data => this.#handleInput(data),
1280
1359
  () => {
1360
+ if (this.#nativeLive) {
1361
+ // The terminal lays the surface out: a resize only refreshes the
1362
+ // width `rows` fallback nodes render at. No replay.
1363
+ this.#native!.noteTerminalColumns(this.terminal.columns);
1364
+ return;
1365
+ }
1281
1366
  if (this.#resizeProbe) {
1282
1367
  // Warp echoes a height-only ±1 SIGWINCH on CSI ?1049l. The echo
1283
1368
  // must not restart the alt borrow (that is the flicker loop),
@@ -1346,8 +1431,191 @@ export class TUI extends Container {
1346
1431
  this.#querySixelSupport();
1347
1432
  this.#queryCellSize();
1348
1433
  }
1434
+ if (nativeExpected && this.terminal.tspProbePending && !this.#nativeLive) {
1435
+ this.#startNative(assumedTspHello(this.terminal));
1436
+ this.#nativeUnconfirmed = true;
1437
+ this.#armNativeConfirm(TUI.#NATIVE_CONFIRM_MS);
1438
+ } else if (this.terminal.tspProbePending && !this.#nativeLive) {
1439
+ this.#nativeHoldTimer = this.#renderScheduler.scheduleRender(() => {
1440
+ this.#nativeHoldTimer = undefined;
1441
+ if (!this.#stopped && !this.#nativeLive) this.requestRender(true);
1442
+ }, TUI.#NATIVE_PROBE_HOLD_MS);
1443
+ }
1349
1444
  this.requestRender(true, { clearScrollback: options?.clearScrollback === true });
1350
1445
  }
1446
+
1447
+ /** Whether frames go to a Tern Surface Protocol surface instead of the row renderer. */
1448
+ get nativeRendering(): boolean {
1449
+ return this.#nativeLive;
1450
+ }
1451
+
1452
+ /**
1453
+ * Close the TSP surfaces ahead of {@link stop}, before the exit drains
1454
+ * input: the terminal answers nothing once they are closed, and whatever it
1455
+ * already sent (acks, events) is read and dropped here instead of reaching
1456
+ * the shell as typed text. A render already requested (the exit's status
1457
+ * line) goes out first; none is forced, and nothing renders from now until
1458
+ * `stop()`.
1459
+ */
1460
+ closeNative(): void {
1461
+ if (!this.#nativeLive) return;
1462
+ if (this.#renderRequested) {
1463
+ this.#renderTimer?.cancel();
1464
+ this.#runScheduledRender();
1465
+ }
1466
+ this.#native!.stop();
1467
+ }
1468
+
1469
+ /** Reference document the TSP terminal should hold (debug mirror only). */
1470
+ getNativeDocument(): TspNode | undefined {
1471
+ return this.#nativeLive ? this.#native?.document() : undefined;
1472
+ }
1473
+
1474
+ /** Most recent TSP frames sent (debug mirror only). */
1475
+ getNativeFrames(count?: number): readonly TspFrame[] {
1476
+ return this.#native?.recentFrames(count) ?? [];
1477
+ }
1478
+
1479
+ /** `rows` fallback nodes in the last native frame. */
1480
+ get nativeFallbackCount(): number {
1481
+ return this.#native?.fallbackCount ?? 0;
1482
+ }
1483
+
1484
+ #onTspHello(hello: TspHello | null): void {
1485
+ const held = this.#nativeHoldTimer !== undefined;
1486
+ this.#nativeHoldTimer?.cancel();
1487
+ this.#nativeHoldTimer = undefined;
1488
+ if (this.#stopped) return;
1489
+ if (this.#nativeUnconfirmed) {
1490
+ if (hello === null) {
1491
+ this.#revokeNative("the DA1 sentinel came before a TSP hello reply, or the reply's version is unsupported");
1492
+ return;
1493
+ }
1494
+ this.#clearNativeConfirm();
1495
+ this.#native!.confirm(hello);
1496
+ return;
1497
+ }
1498
+ if (this.#nativeLive) return;
1499
+ if (hello === null) {
1500
+ if (held) this.requestRender(true);
1501
+ return;
1502
+ }
1503
+ this.#startNative(hello);
1504
+ }
1505
+
1506
+ /** Switch to the surface (adopting the one a stop/start cycle closed). */
1507
+ #startNative(hello: TspHello): void {
1508
+ this.#eraseRowPaintForNative();
1509
+ this.#nativeLive = true;
1510
+ if (this.#native) {
1511
+ this.#native.resume(hello);
1512
+ return;
1513
+ }
1514
+ this.#native = new NativeBackend(this.#nativeHost(), hello, { mirror: this.#debugServer !== undefined });
1515
+ this.#native.start();
1516
+ }
1517
+
1518
+ /**
1519
+ * Deadline for the `hello` reply after an optimistic start. A deadline that
1520
+ * fired late ran after an event-loop stall, possibly with the reply already
1521
+ * queued on stdin, so it re-arms briefly to let input run first.
1522
+ */
1523
+ #armNativeConfirm(delayMs: number): void {
1524
+ const due = this.#renderScheduler.now() + delayMs;
1525
+ this.#nativeConfirmTimer = this.#renderScheduler.scheduleRender(() => {
1526
+ this.#nativeConfirmTimer = undefined;
1527
+ if (!this.#nativeUnconfirmed || this.#stopped) return;
1528
+ if (this.#renderScheduler.now() - due > TUI.#NATIVE_CONFIRM_GRACE_MS) {
1529
+ this.#armNativeConfirm(TUI.#NATIVE_CONFIRM_GRACE_MS);
1530
+ return;
1531
+ }
1532
+ this.#revokeNative(`no TSP hello reply within ${TUI.#NATIVE_CONFIRM_MS} ms`);
1533
+ }, delayMs);
1534
+ }
1535
+
1536
+ #clearNativeConfirm(): void {
1537
+ this.#nativeUnconfirmed = false;
1538
+ this.#nativeConfirmTimer?.cancel();
1539
+ this.#nativeConfirmTimer = undefined;
1540
+ }
1541
+
1542
+ /**
1543
+ * The optimistic surface was never confirmed: close it without keeping
1544
+ * anything and repaint every row. A reply that still arrives later switches
1545
+ * to a fresh surface the usual way.
1546
+ */
1547
+ #revokeNative(reason: string): void {
1548
+ this.#clearNativeConfirm();
1549
+ logger.warn("TSP: terminal did not confirm the surface; falling back to rows", { reason });
1550
+ this.#nativeLive = false;
1551
+ this.#native?.stop(false);
1552
+ this.#native = undefined;
1553
+ this.requestRender(true);
1554
+ }
1555
+
1556
+ /**
1557
+ * The handshake landed after the row renderer painted (deferred input):
1558
+ * leave the alternate screen if borrowed and erase the painted viewport so
1559
+ * the surface opens where the rows began.
1560
+ */
1561
+ #eraseRowPaintForNative(): void {
1562
+ this.#cancelResizeProbe();
1563
+ this.#resizeSettleTimer?.cancel();
1564
+ this.#resizeSettleTimer = undefined;
1565
+ if (this.#altActive || this.#resizeAltActive) {
1566
+ this.terminal.write(`${this.#keyboardEnhancementExit()}\x1b[?1049l`);
1567
+ setAltScreenActive(false);
1568
+ this.#altActive = false;
1569
+ this.#resizeAltActive = false;
1570
+ }
1571
+ this.#setMouseTracking("off");
1572
+ if (this.#previousFrameLength > 0) {
1573
+ const lineDiff = this.#providerViewportTop - this.#hardwareCursorRow;
1574
+ if (lineDiff > 0) this.terminal.write(`\x1b[${lineDiff}B`);
1575
+ else if (lineDiff < 0) this.terminal.write(`\x1b[${-lineDiff}A`);
1576
+ this.terminal.write("\r\x1b[J");
1577
+ this.#previousFrameLength = 0;
1578
+ this.#providerWindow = [];
1579
+ this.#providerPreparedRows = [];
1580
+ }
1581
+ this.terminal.hideCursor();
1582
+ this.#forgetHardwareCursorState();
1583
+ }
1584
+
1585
+ #nativeHost(): NativeHost {
1586
+ return {
1587
+ terminal: this.terminal,
1588
+ describeSurface: cx => {
1589
+ const provider = this.#frameProvider as
1590
+ | (TerminalFrameProvider & Partial<NativeSurfaceProvider>)
1591
+ | undefined;
1592
+ if (provider?.describeSurface) return provider.describeSurface(cx);
1593
+ return { main: this.children, dock: [] };
1594
+ },
1595
+ overlays: () => {
1596
+ const visible = [];
1597
+ for (const entry of this.overlayStack) {
1598
+ if (!this.#isOverlayVisible(entry)) continue;
1599
+ visible.push({
1600
+ component: entry.component,
1601
+ options: entry.options,
1602
+ focused: isOverlayFocusTarget(entry.component, this.#focusedComponent),
1603
+ });
1604
+ }
1605
+ return visible;
1606
+ },
1607
+ focused: () => this.#focusedComponent,
1608
+ requestRender: () => this.requestRender(),
1609
+ appearanceChanged: () => {
1610
+ this.terminal.refreshAppearance?.();
1611
+ },
1612
+ motionChanged: () => {
1613
+ this.invalidate();
1614
+ this.requestRender();
1615
+ },
1616
+ invalidate: () => this.invalidate(),
1617
+ };
1618
+ }
1351
1619
  /**
1352
1620
  * Whether a resize repaints the visible window in place — no alternate-screen
1353
1621
  * borrow. Warp-only: Warp re-reports its size on alt-buffer toggles, so borrowing
@@ -1974,6 +2242,14 @@ export class TUI extends Container {
1974
2242
  this.#cancelPostmortemRestore = undefined;
1975
2243
  this.#debugServer?.stop();
1976
2244
  this.#debugServer = undefined;
2245
+ this.#nativeHoldTimer?.cancel();
2246
+ this.#nativeHoldTimer = undefined;
2247
+ this.#clearNativeConfirm();
2248
+ const nativeWasLive = this.#nativeLive;
2249
+ if (nativeWasLive) {
2250
+ this.#native!.stop();
2251
+ this.#nativeLive = false;
2252
+ }
1977
2253
  this.#resizeSettleTimer?.cancel();
1978
2254
  this.#resizeSettleTimer = undefined;
1979
2255
  if (this.#resizeInPlaceActive && this.terminal.rows > 0) {
@@ -2021,7 +2297,8 @@ export class TUI extends Container {
2021
2297
  // erase native history and re-stream the whole transcript at quit; drop
2022
2298
  // the latch so the flush below writes only un-retired rows.
2023
2299
  this.#clearScrollbackOnNextRender = false;
2024
- this.#flushHistoryBeforeStop();
2300
+ // The surface already holds the transcript; there's no row history to retire.
2301
+ if (!nativeWasLive) this.#flushHistoryBeforeStop();
2025
2302
  // Deliberately leave transmitted images in the terminal's graphics store:
2026
2303
  // placeholder cells committed to native scrollback render only while their
2027
2304
  // image data lives, so a delete-by-id here blanks every transcript image
@@ -2193,7 +2470,9 @@ export class TUI extends Container {
2193
2470
  const adaptiveFloor = Math.min(TUI.#MAX_ADAPTIVE_RENDER_MS, this.#lastFrameCostMs * 2);
2194
2471
  const adaptiveDelay = Math.max(0, adaptiveFloor - elapsed);
2195
2472
  const inputGraceDelay = Math.max(0, this.#inputRenderGraceUntilMs - now);
2196
- const delay = Math.max(cadenceDelay, adaptiveDelay, inputGraceDelay);
2473
+ // Native frames are paced by the terminal's acknowledgements (credits),
2474
+ // not by the row renderer's cadence.
2475
+ const delay = this.#nativeLive ? 0 : Math.max(cadenceDelay, adaptiveDelay, inputGraceDelay);
2197
2476
  this.#renderTimer = this.#renderScheduler.scheduleRender(this.#runScheduledRender, delay);
2198
2477
  }
2199
2478
 
@@ -2227,6 +2506,12 @@ export class TUI extends Container {
2227
2506
  }
2228
2507
 
2229
2508
  #handleInput(data: string): void {
2509
+ // Tern Surface Protocol events (acks, pointer actions, resize, theme)
2510
+ // are terminal reports, never keystrokes.
2511
+ if (data.startsWith(TSP_PREFIX)) {
2512
+ if (this.#nativeLive) this.#native!.handleInput(data);
2513
+ return;
2514
+ }
2230
2515
  // Consume CPR replies (CSI row;col R) while an anchor probe is unanswered;
2231
2516
  // they are terminal reports, never keystrokes, and must not reach the
2232
2517
  // focused component.
@@ -2982,6 +3267,12 @@ export class TUI extends Container {
2982
3267
  /** Render one frame: alt-screen modal, provider plan, or children fallback. */
2983
3268
  #doRender(): void {
2984
3269
  if (this.#stopped) return;
3270
+ if (this.#nativeLive) {
3271
+ this.#native!.render();
3272
+ return;
3273
+ }
3274
+ // Awaiting the TSP hello: its resolution (or the hold timeout) repaints.
3275
+ if (this.#nativeHoldTimer) return;
2985
3276
  const width = this.terminal.columns;
2986
3277
  const height = this.terminal.rows;
2987
3278
  if (this.#resizeAltActive) {