@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
@@ -14,7 +14,10 @@ import { wrapTextWithAnsi } from "../../utils";
14
14
  import { getAgentDbPath } from "@oh-my-pi/pi-utils";
15
15
  import { OAuthSelectorComponent } from "../../overlays/oauth-selector";
16
16
  import { theme } from "../../theme/theme";
17
- import type { SetupScene, SetupSceneController, SetupSceneHost } from "./types";
17
+ import { col, node, span, text } from "../../native/describe";
18
+ import type { NativeChild, NativeNode } from "../../native/node";
19
+ import { Memo } from "../../native/memo";
20
+ import type { SetupScene, SetupSceneController, SetupSceneHost, StyledLine } from "./types";
18
21
 
19
22
  function loginUrlLink(url: string): string {
20
23
  return `\x1b]8;;${url}\x07Open login URL\x1b]8;;\x07`;
@@ -27,10 +30,12 @@ function loginCopyHint(): string {
27
30
  class CopyablePromptInput implements Component, Focusable {
28
31
  #input: Input;
29
32
  #onCopy: () => void;
33
+ readonly #native: NativeNode;
30
34
 
31
35
  constructor(input: Input, onCopy: () => void) {
32
36
  this.#input = input;
33
37
  this.#onCopy = onCopy;
38
+ this.#native = col([input]);
34
39
  }
35
40
 
36
41
  get focused(): boolean {
@@ -49,6 +54,11 @@ class CopyablePromptInput implements Component, Focusable {
49
54
  return this.#input.render(width);
50
55
  }
51
56
 
57
+ /** The wrapped field describes itself (`input`, caret while focused); alt+c stays a key here. */
58
+ describe(): NativeNode {
59
+ return this.#native;
60
+ }
61
+
52
62
  handleInput(data: string): void {
53
63
  if (matchesKey(data, "alt+c")) {
54
64
  this.#onCopy();
@@ -81,7 +91,8 @@ export class SignInScene implements SetupSceneController {
81
91
 
82
92
  #authStorage: AuthStorage;
83
93
  #selector: OAuthSelectorComponent;
84
- #statusLines: string[] = [];
94
+ /** Status copy under the selector or login flow; replaced (never mutated) on change. */
95
+ #statusLines: readonly StyledLine[] = [];
85
96
  #authUrl: string | undefined;
86
97
  #authLaunchUrl: string | undefined;
87
98
  #prompt: PromptState | undefined;
@@ -92,6 +103,7 @@ export class SignInScene implements SetupSceneController {
92
103
  #loggingInProvider: string | undefined;
93
104
  #disposed = false;
94
105
  #step: WizardStep | undefined;
106
+ #native = new Memo();
95
107
 
96
108
  readonly #host: SetupSceneHost;
97
109
 
@@ -109,6 +121,7 @@ export class SignInScene implements SetupSceneController {
109
121
  }
110
122
 
111
123
  invalidate(): void {
124
+ this.#native.clear();
112
125
  this.#step?.invalidate();
113
126
  this.#selector.invalidate();
114
127
  this.#prompt?.input.invalidate();
@@ -174,7 +187,7 @@ export class SignInScene implements SetupSceneController {
174
187
  }
175
188
  }
176
189
  for (const line of this.#statusLines) {
177
- for (const wrapped of wrapTextWithAnsi(line, width)) {
190
+ for (const wrapped of wrapTextWithAnsi(theme.fg(line.color, line.text), width)) {
178
191
  tail.addChild(new Text(wrapped, 0, 0));
179
192
  }
180
193
  }
@@ -205,6 +218,98 @@ export class SignInScene implements SetupSceneController {
205
218
  return this.#step.render(width);
206
219
  }
207
220
 
221
+ /**
222
+ * Picking: intro, the provider selector (describes itself) and the last
223
+ * outcome. Signing in: a spinner heading, the login link (with the full
224
+ * URL once, char-wrapped), any code prompt and the flow's progress lines.
225
+ */
226
+ describe(): NativeNode {
227
+ const provider = this.#loggingInProvider;
228
+ const authUrl = this.#authUrl;
229
+ const launchUrl = this.#authLaunchUrl;
230
+ const prompt = this.#prompt;
231
+ const statusLines = this.#statusLines;
232
+ const copyKey = formatKeyHint("alt+c");
233
+ return this.#native.get([provider, authUrl, launchUrl, prompt, statusLines, this.#selector, copyKey], () => {
234
+ const tail: NativeChild[] = [];
235
+ if (authUrl) {
236
+ tail.push(
237
+ node(
238
+ "text",
239
+ {
240
+ spans: [
241
+ span("Browser login: ", "accent"),
242
+ span("Open login URL", "accent link", { href: authUrl }),
243
+ span(` (clipboard copy attempted; ${copyKey} retries)`, "dim"),
244
+ ],
245
+ },
246
+ undefined,
247
+ "login",
248
+ ),
249
+ node(
250
+ "text",
251
+ { spans: [span(authUrl, "dim link", { href: authUrl })], wrap: "char", actions: { menu: ["copy"] } },
252
+ undefined,
253
+ "url",
254
+ ),
255
+ );
256
+ if (launchUrl) {
257
+ tail.push(
258
+ node(
259
+ "text",
260
+ {
261
+ spans: [
262
+ span("Local shortcut (this machine only): ", "dim"),
263
+ span(launchUrl, "dim link", { href: launchUrl }),
264
+ ],
265
+ wrap: "char",
266
+ },
267
+ undefined,
268
+ "launch",
269
+ ),
270
+ );
271
+ }
272
+ }
273
+ if (prompt) {
274
+ const promptChildren: NativeChild[] = [text([span(prompt.message, "warning")])];
275
+ if (prompt.placeholder) promptChildren.push(text([span(prompt.placeholder, "dim")]));
276
+ promptChildren.push(prompt.input);
277
+ tail.push(node("col", {}, promptChildren, "prompt"));
278
+ }
279
+ statusLines.forEach((line, index) => {
280
+ tail.push(node("text", { spans: [span(line.text, line.color)] }, undefined, `status:${index}`));
281
+ });
282
+
283
+ if (provider) {
284
+ return col(
285
+ [
286
+ node(
287
+ "row",
288
+ { gap: "sm", align: "center" },
289
+ [node("spinner", {}), text([span(`Signing in to ${provider}`, "strong")])],
290
+ "heading",
291
+ ),
292
+ ...tail,
293
+ ],
294
+ { gap: "sm", role: "omp.setup.sign-in", tone: "pending" },
295
+ );
296
+ }
297
+ return col(
298
+ [
299
+ node(
300
+ "text",
301
+ { spans: [span("Pick a provider to sign in — you can connect more than one.", "muted")] },
302
+ undefined,
303
+ "intro",
304
+ ),
305
+ this.#selector,
306
+ ...tail,
307
+ ],
308
+ { gap: "sm", role: "omp.setup.sign-in" },
309
+ );
310
+ });
311
+ }
312
+
208
313
  #createSelector(): OAuthSelectorComponent {
209
314
  return new OAuthSelectorComponent(
210
315
  "login",
@@ -222,7 +327,7 @@ export class SignInScene implements SetupSceneController {
222
327
  const useManualInput = PASTE_CODE_LOGIN_PROVIDERS.has(providerId);
223
328
  this.#selector.stopValidation();
224
329
  this.#loggingInProvider = providerId;
225
- this.#statusLines = [theme.fg("dim", "Starting OAuth flow…")];
330
+ this.#statusLines = [{ text: "Starting OAuth flow…", color: "dim" }];
226
331
  this.#authUrl = undefined;
227
332
  this.#authLaunchUrl = undefined;
228
333
  this.#loginAbort = new AbortController();
@@ -244,20 +349,21 @@ export class SignInScene implements SetupSceneController {
244
349
  // shortcut for wide-terminal local users.
245
350
  this.#authUrl = info.url;
246
351
  this.#authLaunchUrl = info.launchUrl && info.launchUrl !== info.url ? info.launchUrl : undefined;
247
- this.#statusLines = [];
352
+ const statusLines: StyledLine[] = [];
248
353
  if (info.instructions) {
249
- this.#statusLines.push(theme.fg("warning", info.instructions));
354
+ statusLines.push({ text: info.instructions, color: "warning" });
250
355
  }
251
356
  if (useManualInput) {
252
- this.#statusLines.push(theme.fg("dim", "Paste the returned code or redirect URL when prompted."));
357
+ statusLines.push({ text: "Paste the returned code or redirect URL when prompted.", color: "dim" });
253
358
  }
359
+ this.#statusLines = statusLines;
254
360
  void this.#copyAuthUrl();
255
361
  this.#host.ctx.openInBrowser(info.url);
256
362
  this.#host.requestRender();
257
363
  },
258
364
  onPrompt: prompt => this.#showPrompt(prompt),
259
365
  onProgress: message => {
260
- this.#statusLines.push(theme.fg("dim", message));
366
+ this.#statusLines = [...this.#statusLines, { text: message, color: "dim" }];
261
367
  this.#host.requestRender();
262
368
  },
263
369
  onManualCodeInput: signal =>
@@ -268,8 +374,8 @@ export class SignInScene implements SetupSceneController {
268
374
  await this.#host.ctx.refreshProvider(providerId);
269
375
  if (this.#disposed) return;
270
376
  this.#statusLines = [
271
- theme.fg("success", `${theme.status.success} Signed in to ${providerId}`),
272
- theme.fg("dim", `Credentials saved to ${getAgentDbPath()}`),
377
+ { text: `${theme.status.success} Signed in to ${providerId}`, color: "success" },
378
+ { text: `Credentials saved to ${getAgentDbPath()}`, color: "dim" },
273
379
  ];
274
380
  this.#authUrl = undefined;
275
381
  this.#authLaunchUrl = undefined;
@@ -282,14 +388,17 @@ export class SignInScene implements SetupSceneController {
282
388
  } catch (error) {
283
389
  if (this.#disposed) return;
284
390
  if (this.#loginAbort?.signal.aborted) {
285
- this.#statusLines = [theme.fg("dim", "Login cancelled.")];
391
+ this.#statusLines = [{ text: "Login cancelled.", color: "dim" }];
286
392
  this.#authUrl = undefined;
287
393
  this.#authLaunchUrl = undefined;
288
394
  } else {
289
395
  const message = error instanceof Error ? error.message : String(error);
290
396
  this.#statusLines = [
291
- theme.fg("error", `Login failed: ${message}`),
292
- theme.fg("dim", `Choose another provider or press ${editorKey("tui.select.cancel")} to continue.`),
397
+ { text: `Login failed: ${message}`, color: "error" },
398
+ {
399
+ text: `Choose another provider or press ${editorKey("tui.select.cancel")} to continue.`,
400
+ color: "dim",
401
+ },
293
402
  ];
294
403
  this.#authUrl = undefined;
295
404
  this.#authLaunchUrl = undefined;
@@ -1,8 +1,11 @@
1
1
  import { centerLine, visibleWidth } from "../../utils";
2
2
  import { padToWidth } from "../../render/utils";
3
- import { gradientEscape, gradientLogo, PI_LOGO, type ShineConfig } from "../../prompt/welcome";
3
+ import { gradientEscape, gradientLogo, logoNode, PI_LOGO, type ShineConfig } from "../../prompt/welcome";
4
4
  import { theme } from "../../theme/theme";
5
5
  import { formatKeyHint } from "../../app-keybindings";
6
+ import { col, node, span, text } from "../../native/describe";
7
+ import type { NativeNode } from "../../native/node";
8
+ import { Memo } from "../../native/memo";
6
9
 
7
10
  export const SETUP_SPLASH_MS = 2600;
8
11
  export const SETUP_TICK_MS = 33;
@@ -181,6 +184,30 @@ export function renderSetupSplash(width: number, height: number, elapsedMs: numb
181
184
  return cells.map(row => row.join(""));
182
185
  }
183
186
 
187
+ const splashMemo = new Memo();
188
+
189
+ /**
190
+ * Native splash: the 2x brand mark with a terminal-clocked shimmer, the
191
+ * wordmark, and the skip hint pinned to the bottom. The water and starfield
192
+ * are cell paintings with no semantic counterpart. A click on the splash
193
+ * sends the `skip` action.
194
+ */
195
+ export function describeSetupSplash(): NativeNode {
196
+ const hint = skipHint();
197
+ return splashMemo.get([hint], () =>
198
+ col(
199
+ [
200
+ node("spacer", { grow: 1 }),
201
+ logoNode(LARGE_LOGO, true),
202
+ text([span("O h M y P i", "strong")], { wrap: "none" }),
203
+ node("spacer", { grow: 1 }),
204
+ text([span(hint, "dim")], { wrap: "none" }),
205
+ ],
206
+ { align: "center", gap: "md", grow: 1, role: "omp.setup.splash", actions: { click: "skip" } },
207
+ ),
208
+ );
209
+ }
210
+
184
211
  /** Centered fallback for windows too small to hold the full scene. */
185
212
  function renderCompactSplash(width: number, height: number, phase: number, shine: ShineConfig): string[] {
186
213
  const art = height >= 14 ? LARGE_LOGO : PI_LOGO;
@@ -18,6 +18,9 @@ import {
18
18
  setSymbolPreset,
19
19
  theme,
20
20
  } from "../../theme/theme";
21
+ import { col, node, span, text } from "../../native/describe";
22
+ import type { NativeChild, NativeNode } from "../../native/node";
23
+ import { Memo } from "../../native/memo";
21
24
  import type { SetupScene, SetupSceneController, SetupSceneHost } from "./types";
22
25
 
23
26
  type ThemeMode = "curated" | "all";
@@ -85,6 +88,50 @@ function renderThemePreview(width: number): string[] {
85
88
  ];
86
89
  }
87
90
 
91
+ /**
92
+ * Native theme preview: status samples, a status bar and a read-only editor
93
+ * in the highlighted theme's tokens. The terminal draws the bar and editor
94
+ * chrome, so none of the ANSI mock's box or fill math applies.
95
+ */
96
+ function describeThemePreview(): NativeNode {
97
+ return col(
98
+ [
99
+ text([span("Preview", "strong")]),
100
+ text([
101
+ span(`${theme.status.success} success`, "success"),
102
+ span(" "),
103
+ span(`${theme.status.warning} warning`, "warning"),
104
+ span(" "),
105
+ span(`${theme.status.error} error`, "error"),
106
+ span(" "),
107
+ span("accent", "accent"),
108
+ ]),
109
+ text([span("Status line", "muted")]),
110
+ node("status", {}, [
111
+ node("seg", { spans: [span(`${theme.icon.model} sonnet`, "statusLineModel")], side: "left" }),
112
+ node("seg", { spans: [span("~/project", "statusLinePath")], side: "left" }),
113
+ node("seg", { spans: [span(`${theme.icon.git} main +2`, "statusLineGitDirty")], side: "left" }),
114
+ node("seg", { spans: [span(`${theme.icon.context} 42%`, "statusLineContext")], side: "right" }),
115
+ node("seg", { spans: [span(`${theme.icon.cost} 0.18`, "statusLineCost")], side: "right" }),
116
+ ]),
117
+ text([span("Editor", "muted")]),
118
+ node("input", {
119
+ text: "Ask anything, edit files, run tools",
120
+ cursor: "Ask anything, edit files, run tools".length,
121
+ prompt: [span(">", "accent")],
122
+ readonly: true,
123
+ }),
124
+ text([
125
+ span(
126
+ `${editorKey("tui.input.submit")} send · ${editorKey("tui.input.newLine")} newline · / commands`,
127
+ "dim",
128
+ ),
129
+ ]),
130
+ ],
131
+ { role: "omp.setup.theme.preview" },
132
+ );
133
+ }
134
+
88
135
  class ThemeSceneController implements SetupSceneController {
89
136
  title = "Pick a theme";
90
137
  get subtitle(): string {
@@ -93,10 +140,12 @@ class ThemeSceneController implements SetupSceneController {
93
140
  #mode: ThemeMode = "curated";
94
141
  #selectList: SelectList;
95
142
  #loadingAllThemes = false;
143
+ /** Error copy shown under the list (raw text; styled at render/describe time). */
96
144
  #message: string | undefined;
97
145
  #previewRequest = 0;
98
146
  #disposed = false;
99
147
  #step: WizardStep | undefined;
148
+ #native = new Memo();
100
149
  readonly #originalTheme = getCurrentThemeName();
101
150
  readonly #originalSymbolPreset: SymbolPreset;
102
151
  readonly #originalColorBlindMode: boolean;
@@ -115,6 +164,7 @@ class ThemeSceneController implements SetupSceneController {
115
164
  }
116
165
 
117
166
  invalidate(): void {
167
+ this.#native.clear();
118
168
  this.#step?.invalidate();
119
169
  this.#selectList.invalidate();
120
170
  }
@@ -168,7 +218,7 @@ class ThemeSceneController implements SetupSceneController {
168
218
  preview.addChild(new Text(line, 0, 0));
169
219
  }
170
220
  const loading = this.#loadingAllThemes ? new Text(theme.fg("dim", "Loading themes…"), 0, 0) : undefined;
171
- const status = this.#message ? new Text(this.#message, 0, 0) : undefined;
221
+ const status = this.#message ? new Text(theme.fg("error", this.#message), 0, 0) : undefined;
172
222
  if (!this.#step) {
173
223
  this.#step = new WizardStep({
174
224
  kind: loading ? "async" : "choice",
@@ -194,6 +244,41 @@ class ThemeSceneController implements SetupSceneController {
194
244
  return this.#step.render(width);
195
245
  }
196
246
 
247
+ /**
248
+ * Intro hints, the live preview, the theme list (a spinner while every
249
+ * theme loads) and any preview error. Theme switches re-describe through
250
+ * `invalidate()`, so the preview's tokens follow the highlighted theme.
251
+ */
252
+ describe(): NativeNode {
253
+ const confirm = editorKey("tui.select.confirm");
254
+ const cancel = editorKey("tui.select.cancel");
255
+ return this.#native.get(
256
+ [this.#mode, this.#loadingAllThemes, this.#selectList, this.#message, confirm, cancel],
257
+ () => {
258
+ const children: NativeChild[] = [
259
+ col([
260
+ text([span(`Theme changes preview live. Nothing is saved until you press ${confirm}.`, "muted")]),
261
+ text([
262
+ span(
263
+ this.#mode === "all"
264
+ ? `Browsing all themes · ${cancel} returns to curated choices`
265
+ : `${cancel} skips this step`,
266
+ "dim",
267
+ ),
268
+ ]),
269
+ ]),
270
+ describeThemePreview(),
271
+ this.#loadingAllThemes
272
+ ? node("spinner", { label: [span("Loading themes…", "dim")] }, undefined, "loading")
273
+ : this.#selectList,
274
+ ];
275
+ if (this.#message)
276
+ children.push(node("text", { spans: [span(this.#message, "error")] }, undefined, "status"));
277
+ return col(children, { gap: "sm", role: "omp.setup.theme" });
278
+ },
279
+ );
280
+ }
281
+
197
282
  #createSelectList(items: readonly SelectItem[], selectedIndex: number): SelectList {
198
283
  const list = new SelectList(items, Math.min(10, Math.max(1, items.length)), getSelectListTheme());
199
284
  list.setSelectedIndex(selectedIndex);
@@ -256,7 +341,7 @@ class ThemeSceneController implements SetupSceneController {
256
341
  this.#selectList = this.#createSelectList(items, selectedIndex);
257
342
  } catch (error) {
258
343
  const message = error instanceof Error ? error.message : String(error);
259
- this.#message = theme.fg("error", `Failed to load themes: ${message}`);
344
+ this.#message = `Failed to load themes: ${message}`;
260
345
  } finally {
261
346
  this.#loadingAllThemes = false;
262
347
  this.#host.requestRender();
@@ -320,7 +405,7 @@ class ThemeSceneController implements SetupSceneController {
320
405
  }
321
406
  if (request !== this.#previewRequest || this.#disposed) return;
322
407
  if (!result.success) {
323
- this.#message = theme.fg("error", result.error ?? "Theme preview failed");
408
+ this.#message = result.error ?? "Theme preview failed";
324
409
  }
325
410
  this.#host.ctx.ui.invalidate();
326
411
  this.#host.requestRender();
@@ -4,9 +4,15 @@ import type { SgrMouseEvent } from "../../mouse";
4
4
  import type { ComposerPreviewStatusSource } from "../../overlays/composer-shape-preview";
5
5
  import type { ComposerShape } from "../../overlays/composer-shape-registry";
6
6
  import type { ModelBrowserSource } from "../../overlays/model-browser";
7
- import type { SymbolPreset } from "../../theme/theme";
7
+ import type { SymbolPreset, ThemeColor } from "../../theme/theme";
8
8
  import type { Component, TUI } from "../../tui";
9
9
 
10
+ /** One line of status copy kept as raw text plus its theme colour, never as pre-styled ANSI. */
11
+ export interface StyledLine {
12
+ readonly text: string;
13
+ readonly color: ThemeColor;
14
+ }
15
+
10
16
  /** Terminal capabilities used by setup overlays and the startup splash. */
11
17
  export interface SetupUiHost {
12
18
  readonly ui: Pick<TUI, "showOverlay" | "setFocus" | "requestRender" | "invalidate"> & {
@@ -1,7 +1,11 @@
1
1
  import { type Component, type OverlayFocusOwner } from "../tui";
2
2
  import { matchesKey } from "../keys";
3
+ import { col } from "../native/describe";
4
+ import type { NativeNode, NativeUiEvent } from "../native/node";
5
+ import { Memo } from "../native/memo";
6
+ import { isNativeRendering } from "../native/state";
3
7
  import type { SetupUiHost } from "./scenes/types";
4
- import { renderSetupSplash, SETUP_SPLASH_MS, SETUP_TICK_MS } from "./scenes/splash";
8
+ import { describeSetupSplash, renderSetupSplash, SETUP_SPLASH_MS, SETUP_TICK_MS } from "./scenes/splash";
5
9
 
6
10
  /** Timing controls for the standalone startup animation. */
7
11
  export interface RunStartupSplashOptions {
@@ -13,6 +17,9 @@ export interface RunStartupSplashOptions {
13
17
  class StartupSplashComponent implements Component, OverlayFocusOwner {
14
18
  #phaseStartedAt = 0;
15
19
  #timer: NodeJS.Timeout | undefined;
20
+ /** Native path: one-shot at the end of the splash; the terminal clocks the motion. */
21
+ #deadline: NodeJS.Timeout | undefined;
22
+ #native = new Memo();
16
23
  #done = Promise.withResolvers<void>();
17
24
  #disposed = false;
18
25
  readonly #durationMs: number;
@@ -60,20 +67,50 @@ class StartupSplashComponent implements Component, OverlayFocusOwner {
60
67
  return renderSetupSplash(Math.max(1, width), Math.max(1, this.ctx.ui.terminal.rows), elapsedMs);
61
68
  }
62
69
 
70
+ describe(): NativeNode {
71
+ const splash = describeSetupSplash();
72
+ return this.#native.get([splash], () => col([splash], { grow: 1, role: "omp.app.startup-splash" }));
73
+ }
74
+
75
+ handleNativeEvent(event: NativeUiEvent): void {
76
+ if (event.type === "action" && event.act === "skip") this.#complete();
77
+ }
78
+
79
+ /**
80
+ * ANSI: repaint every tick. Native: no repaint loop, only a one-shot at
81
+ * the end of the splash (re-armed if it fires early).
82
+ */
63
83
  #startTimer(): void {
64
- if (this.#timer) return;
65
- this.#timer = setInterval(() => {
66
- if (this.#disposed) return;
67
- const elapsed = this.#now() - this.#phaseStartedAt;
68
- if (elapsed >= this.#durationMs) {
69
- this.#complete();
70
- return;
84
+ if (isNativeRendering()) {
85
+ if (this.#timer) {
86
+ clearInterval(this.#timer);
87
+ this.#timer = undefined;
71
88
  }
89
+ clearTimeout(this.#deadline);
90
+ const remaining = this.#durationMs - (this.#now() - this.#phaseStartedAt);
91
+ this.#deadline = setTimeout(() => this.#tick(), Math.max(0, remaining));
92
+ return;
93
+ }
94
+ if (this.#timer) return;
95
+ this.#timer = setInterval(() => this.#tick(), this.#tickMs);
96
+ }
97
+
98
+ #tick(): void {
99
+ if (this.#disposed) return;
100
+ const elapsed = this.#now() - this.#phaseStartedAt;
101
+ if (elapsed >= this.#durationMs) {
102
+ this.#complete();
103
+ } else if (isNativeRendering() || !this.#timer) {
104
+ // Re-arm the deadline, or switch loops when the native surface opened/closed.
105
+ this.#startTimer();
106
+ } else {
72
107
  this.ctx.ui.requestRender();
73
- }, this.#tickMs);
108
+ }
74
109
  }
75
110
 
76
111
  #stopTimer(): void {
112
+ clearTimeout(this.#deadline);
113
+ this.#deadline = undefined;
77
114
  if (!this.#timer) return;
78
115
  clearInterval(this.#timer);
79
116
  this.#timer = undefined;