@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
@@ -1,10 +1,18 @@
1
+ import type { TspSpan, TspText } from "@oh-my-pi/pi-wire";
1
2
  import { Text } from "../components/text";
3
+ import { compactText, rowsText, styledSpans } from "../native/spans";
4
+ import { sameItems } from "../native/memo";
5
+ import { colorTone } from "../native/tone";
6
+ import { span } from "../native/describe";
7
+ import type { DescribeContext, NativeChild, NativeNode } from "../native/node";
2
8
  import type { Theme, ThemeColor } from "../theme/theme";
3
9
  import type { Component } from "../tui";
4
10
  import { getPaddingX } from "../utils";
5
11
  import {
6
12
  CachedOutputBlock,
13
+ describeOutputBlock,
7
14
  markFramedBlockComponent,
15
+ type NativeOutputBlockSection,
8
16
  outputBlockContentWidth,
9
17
  type OutputBlockOptions,
10
18
  } from "./output-block";
@@ -75,6 +83,39 @@ function isRenderedLines(value: readonly string[] | Component): value is readonl
75
83
  return Array.isArray(value);
76
84
  }
77
85
 
86
+ /** Header spans for a structured status line; the status icon is the card's status chip. */
87
+ function statusSpans(status: StatusLineOptions, theme: Theme): TspSpan[] {
88
+ const flatten = (text: string): string => text.replace(/\r\n?|\n/g, " ");
89
+ const spans: TspSpan[] = [];
90
+ if (status.iconOverride) spans.push(...styledSpans(status.iconOverride), span(" "));
91
+ spans.push(span(flatten(status.title), status.titleColor ?? "accent"));
92
+ if (status.description) spans.push(span(": "), span(flatten(status.description), "muted"));
93
+ if (status.badge) {
94
+ const label = `${theme.format.bracketLeft}${flatten(status.badge.label)}${theme.format.bracketRight}`;
95
+ spans.push(span(" "), span(label, status.badge.color));
96
+ }
97
+ const meta = status.meta?.map(flatten).filter(value => value.trim().length > 0) ?? [];
98
+ if (meta.length > 0) spans.push(span(" "), span(meta.join(theme.sep.dot), "dim"));
99
+ return spans;
100
+ }
101
+
102
+ /** Last description of a card, reused while the snapshot's inputs are unchanged. */
103
+ interface NativeCardMemo {
104
+ head: string;
105
+ meta: string | undefined;
106
+ phase: ToolCardPhase | undefined;
107
+ applyBg: boolean | undefined;
108
+ borderColor: ThemeColor | undefined;
109
+ /** Per slot: label plus resolved content (component or rows array) and its described child. */
110
+ slots: readonly {
111
+ label: string | undefined;
112
+ separator: boolean;
113
+ content: readonly string[] | Component;
114
+ child: NativeChild;
115
+ }[];
116
+ node: NativeNode;
117
+ }
118
+
78
119
  function resolveContent(content: ToolCardContent, width: number): { lines: readonly string[]; component?: Component } {
79
120
  const resolved = typeof content === "function" ? content(width) : content;
80
121
  if (isRenderedLines(resolved)) return { lines: resolved };
@@ -96,6 +137,7 @@ export class ToolCard implements Component {
96
137
  #plainKey = "";
97
138
  #renderedChildren: Component[] = [];
98
139
  #disposed = false;
140
+ #native: NativeCardMemo | undefined;
99
141
 
100
142
  constructor(theme: Theme, options: ToolCardOptions, build: (context: ToolCardBuildContext) => ToolCardSnapshot) {
101
143
  this.#theme = theme;
@@ -216,6 +258,100 @@ export class ToolCard implements Component {
216
258
  return this.#plainText.render(width);
217
259
  }
218
260
 
261
+ /**
262
+ * A `card` (flat `inset` for plain cards) with the state's tone and status
263
+ * chip, head from the header/status line, and body slots: components as
264
+ * children, pre-rendered rows as unwrapped text, labelled sections as
265
+ * `section`s. The builder gets the surface width as its only width.
266
+ */
267
+ describe(cx: DescribeContext): NativeNode {
268
+ const snapshot = this.#build({ width: cx.cols, contentWidth: cx.cols, contentWidthFor: () => cx.cols });
269
+ const previous = this.#native;
270
+ const slotInputs: { label: string | undefined; separator: boolean; content: ToolCardContent }[] = [];
271
+ if (snapshot.body) slotInputs.push({ label: undefined, separator: false, content: snapshot.body });
272
+ for (const section of snapshot.sections ?? []) {
273
+ slotInputs.push({ label: section.label, separator: section.separator === true, content: section.content });
274
+ }
275
+ if (snapshot.footer)
276
+ slotInputs.push({ label: undefined, separator: slotInputs.length > 0, content: snapshot.footer });
277
+
278
+ const nextChildren: Component[] = [];
279
+ const slots = slotInputs.map((input, index) => {
280
+ const content = typeof input.content === "function" ? input.content(cx.cols) : input.content;
281
+ const prior = previous?.slots[index];
282
+ let child: NativeChild;
283
+ if (isRenderedLines(content)) {
284
+ child =
285
+ prior !== undefined && isRenderedLines(prior.content) && sameItems(prior.content, content)
286
+ ? prior.child
287
+ : rowsText(content);
288
+ } else {
289
+ child = content;
290
+ if (!nextChildren.includes(content)) nextChildren.push(content);
291
+ }
292
+ return { label: input.label, separator: input.separator, content, child };
293
+ });
294
+ for (const child of this.#renderedChildren) {
295
+ if (!nextChildren.includes(child)) child.dispose?.();
296
+ }
297
+ this.#renderedChildren = nextChildren;
298
+
299
+ const head = snapshot.header ?? (snapshot.status ? JSON.stringify(snapshot.status) : "");
300
+ if (
301
+ previous !== undefined &&
302
+ previous.head === head &&
303
+ previous.meta === snapshot.headerMeta &&
304
+ previous.phase === snapshot.phase &&
305
+ previous.applyBg === snapshot.applyBg &&
306
+ previous.borderColor === snapshot.borderColor &&
307
+ previous.slots.length === slots.length &&
308
+ slots.every((slot, index) => {
309
+ const prior = previous.slots[index]!;
310
+ return prior.label === slot.label && prior.separator === slot.separator && prior.child === slot.child;
311
+ })
312
+ ) {
313
+ return previous.node;
314
+ }
315
+
316
+ const headSpans: TspText | undefined = snapshot.header
317
+ ? compactText(styledSpans(snapshot.header))
318
+ : snapshot.status
319
+ ? statusSpans(snapshot.status, this.#theme)
320
+ : undefined;
321
+ const sections: NativeOutputBlockSection[] = slots.map((slot, index) => ({
322
+ label: slot.label === undefined ? undefined : compactText(styledSpans(slot.label)),
323
+ separator: slot.separator,
324
+ body: [slot.child],
325
+ key: String(index),
326
+ }));
327
+ const state = toolCardState(snapshot.phase);
328
+ const plain = this.#options.variant === "plain";
329
+ const described = describeOutputBlock({
330
+ head: headSpans,
331
+ meta: snapshot.headerMeta ? compactText(styledSpans(snapshot.headerMeta)) : undefined,
332
+ state,
333
+ role: "omp.tool",
334
+ tone:
335
+ snapshot.borderColor !== undefined
336
+ ? (colorTone(snapshot.borderColor) ?? "neutral")
337
+ : plain && snapshot.applyBg === false
338
+ ? "neutral"
339
+ : undefined,
340
+ sections,
341
+ inset: plain || undefined,
342
+ });
343
+ this.#native = {
344
+ head,
345
+ meta: snapshot.headerMeta,
346
+ phase: snapshot.phase,
347
+ applyBg: snapshot.applyBg,
348
+ borderColor: snapshot.borderColor,
349
+ slots,
350
+ node: described,
351
+ };
352
+ return described;
353
+ }
354
+
219
355
  get debugChildren(): readonly Component[] {
220
356
  return this.#renderedChildren;
221
357
  }
@@ -236,6 +372,7 @@ export class ToolCard implements Component {
236
372
  }
237
373
 
238
374
  invalidate(): void {
375
+ this.#native = undefined;
239
376
  this.#block.invalidate();
240
377
  this.#lastBlockOptions = undefined;
241
378
  this.#plainKey = "";
@@ -2,6 +2,10 @@
2
2
  * Hierarchical tree list rendering helper.
3
3
  */
4
4
 
5
+ import type { TspTreeNode } from "@oh-my-pi/pi-wire";
6
+ import { styledSpans } from "../native/spans";
7
+ import { node, span } from "../native/describe";
8
+ import type { NativeNode } from "../native/node";
5
9
  import type { Theme } from "../theme/theme";
6
10
  import { visibleWidth } from "../utils";
7
11
  import { TreeView } from "../components/tree-view";
@@ -169,6 +173,41 @@ export function renderTreeList<T>(options: TreeListOptions<T>, theme: Theme): st
169
173
  return renderTreeListRows(roots, theme);
170
174
  }
171
175
 
176
+ /**
177
+ * The native form of {@link renderTreeList}: a flat `tree` whose branch
178
+ * gutters are the terminal's. The collapsed item cap (and the caller's
179
+ * trailing summary) are kept as a muted "… N more" node; the visual line
180
+ * budget (`maxCollapsedLines`) is layout and is left to the terminal.
181
+ */
182
+ export function describeTreeList<T>(options: TreeListOptions<T>, theme: Theme): NativeNode {
183
+ const { items, expanded = false, maxCollapsed = 8, itemType = "item", truncateFrom = "end", renderItem } = options;
184
+ const itemNode = (index: number): TspTreeNode => {
185
+ const rendered = renderItem(items[index], {
186
+ index,
187
+ isLast: index === items.length - 1,
188
+ depth: 0,
189
+ theme,
190
+ prefix: "",
191
+ continuePrefix: "",
192
+ });
193
+ return { id: `i${index}`, label: styledSpans(Array.isArray(rendered) ? rendered.join("\n") : rendered) };
194
+ };
195
+ const summaryNode = (text: string): TspTreeNode => ({ id: "more", label: [span(text, "muted")] });
196
+ const nodes: TspTreeNode[] = [];
197
+ if (!expanded && options.trailingSummary !== undefined) {
198
+ for (let index = 0; index < items.length; index++) nodes.push(itemNode(index));
199
+ if (options.trailingSummary !== "") nodes.push(summaryNode(options.trailingSummary));
200
+ return node("tree", { nodes });
201
+ }
202
+ const shown = expanded ? items.length : Math.min(items.length, maxCollapsed);
203
+ const hidden = items.length - shown;
204
+ const start = truncateFrom === "start" ? items.length - shown : 0;
205
+ if (hidden > 0 && truncateFrom === "start") nodes.push(summaryNode(formatMoreItems(hidden, itemType)));
206
+ for (let index = start; index < start + shown; index++) nodes.push(itemNode(index));
207
+ if (hidden > 0 && truncateFrom === "end") nodes.push(summaryNode(formatMoreItems(hidden, itemType)));
208
+ return node("tree", { nodes });
209
+ }
210
+
172
211
  /**
173
212
  * Emit pre-rendered item/summary roots through the shared keyed hierarchy.
174
213
  * Roots are siblings in display order, so the first N-1 rows draw `├` and the
@@ -1,4 +1,6 @@
1
1
  import { Text } from "../components/text";
2
+ import { col } from "../native/describe";
3
+ import type { DescribeContext, NativeNode } from "../native/node";
2
4
  import type { Component } from "../tui";
3
5
  import { getPaddingX } from "../utils";
4
6
 
@@ -23,6 +25,8 @@ export class WidthAwareText implements Component {
23
25
  #cachedContentWidth = -1;
24
26
  #cachedText: string | undefined;
25
27
  #ignoreTight = false;
28
+ #nativeCols = -1;
29
+ #native: NativeNode | undefined;
26
30
 
27
31
  constructor(format: (contentWidth: number) => string, paddingX = 1, paddingY = 1) {
28
32
  this.#format = format;
@@ -56,6 +60,22 @@ export class WidthAwareText implements Component {
56
60
  this.#cachedText = undefined;
57
61
  }
58
62
 
63
+ /**
64
+ * The inner `Text` as the only child, fed the formatter's output. The
65
+ * formatter is width-bound by contract, so it runs once per surface width
66
+ * (the only width a describer knows) and again on `reformat()`.
67
+ */
68
+ describe(cx: DescribeContext): NativeNode {
69
+ if (this.#cachedText === undefined || cx.cols !== this.#nativeCols) {
70
+ this.#nativeCols = cx.cols;
71
+ this.#cachedContentWidth = -1;
72
+ this.#cachedText = this.#format(Math.max(1, cx.cols));
73
+ this.#inner.setText(this.#cachedText);
74
+ }
75
+ this.#native ??= col([this.#inner]);
76
+ return this.#native;
77
+ }
78
+
59
79
  render(width: number): readonly string[] {
60
80
  const paddingX = this.#ignoreTight ? this.#paddingX : getPaddingX(this.#paddingX);
61
81
  const contentWidth = Math.max(1, width - paddingX * 2);
@@ -8,6 +8,9 @@ import { renderComposerShapePreview } from "../../overlays/composer-shape-previe
8
8
  import { getComposerShapeOptions } from "../../overlays/composer-shape-registry";
9
9
  import { editorKey } from "../../chrome/keybinding-hints";
10
10
  import { getSelectListTheme, theme } from "../../theme/theme";
11
+ import { ansi, col, span, text } from "../../native/describe";
12
+ import type { DescribeContext, NativeNode } from "../../native/node";
13
+ import { Memo } from "../../native/memo";
11
14
  import type { SetupScene, SetupSceneController, SetupSceneHost } from "./types";
12
15
 
13
16
  class ComposerSceneController implements SetupSceneController {
@@ -19,6 +22,7 @@ class ComposerSceneController implements SetupSceneController {
19
22
  #currentShape: ComposerShape = "band";
20
23
  #committing = false;
21
24
  #step: WizardStep | undefined;
25
+ #native = new Memo();
22
26
 
23
27
  readonly #host: SetupSceneHost;
24
28
 
@@ -52,6 +56,7 @@ class ComposerSceneController implements SetupSceneController {
52
56
  }
53
57
 
54
58
  invalidate(): void {
59
+ this.#native.clear();
55
60
  if (this.#step) this.#step.invalidate();
56
61
  else this.#selectList.invalidate();
57
62
  }
@@ -106,6 +111,30 @@ class ComposerSceneController implements SetupSceneController {
106
111
  return this.#step.render(width);
107
112
  }
108
113
 
114
+ /**
115
+ * Intro, the shape list, and the live preview. The preview is omp's own
116
+ * composer chrome for the highlighted shape — exactly what the classic
117
+ * renderer paints — so it travels as `ansi` rendered at the surface width.
118
+ */
119
+ describe(cx: DescribeContext): NativeNode {
120
+ const confirm = editorKey("tui.select.confirm");
121
+ return this.#native.get([this.#currentShape, cx.cols, confirm], () =>
122
+ col(
123
+ [
124
+ text([span(`Select a layout; live preview updates below. Press ${confirm} to confirm.`, "muted")]),
125
+ this.#selectList,
126
+ col([
127
+ text([span("Preview:", "muted")]),
128
+ ansi(renderComposerShapePreview(this.#currentShape, cx.cols, this.#host.ctx.statusLine).join("\n"), {
129
+ cols: cx.cols,
130
+ }),
131
+ ]),
132
+ ],
133
+ { gap: "sm", role: "omp.setup.composer" },
134
+ ),
135
+ );
136
+ }
137
+
109
138
  async #commit(shape: ComposerShape): Promise<void> {
110
139
  if (this.#committing) return;
111
140
  this.#committing = true;
@@ -4,6 +4,9 @@ import { type SelectItem, SelectList } from "../../components/select-list";
4
4
  import { Text } from "../../components/text";
5
5
  import { WizardStep } from "../../components/wizard-step";
6
6
  import { getSelectListTheme, type SymbolPreset, setSymbolPreset, theme } from "../../theme/theme";
7
+ import { col, span, text } from "../../native/describe";
8
+ import type { NativeNode } from "../../native/node";
9
+ import { Memo } from "../../native/memo";
7
10
  import type { SetupScene, SetupSceneController, SetupSceneHost } from "./types";
8
11
 
9
12
  const GLYPH_PRESETS = ["nerd", "unicode", "ascii"] as const satisfies readonly SymbolPreset[];
@@ -34,6 +37,7 @@ class GlyphSceneController implements SetupSceneController {
34
37
  #previewRequest = 0;
35
38
  #committing = false;
36
39
  #step: WizardStep | undefined;
40
+ #native = new Memo();
37
41
 
38
42
  readonly #host: SetupSceneHost;
39
43
 
@@ -53,6 +57,7 @@ class GlyphSceneController implements SetupSceneController {
53
57
  }
54
58
 
55
59
  invalidate(): void {
60
+ this.#native.clear();
56
61
  if (this.#step) this.#step.invalidate();
57
62
  else this.#selectList.invalidate();
58
63
  }
@@ -92,6 +97,16 @@ class GlyphSceneController implements SetupSceneController {
92
97
  return this.#step.render(width);
93
98
  }
94
99
 
100
+ /** Hint line over the preset list; the list (sample glyphs as item detail) describes itself. */
101
+ describe(): NativeNode {
102
+ return this.#native.get([this.#selectList], () =>
103
+ col(
104
+ [text([span("If a row shows boxes, tofu, or misaligned icons, pick another.", "muted")]), this.#selectList],
105
+ { gap: "sm", role: "omp.setup.glyph" },
106
+ ),
107
+ );
108
+ }
109
+
95
110
  async #commit(preset: SymbolPreset): Promise<void> {
96
111
  if (this.#committing) return;
97
112
  this.#committing = true;
@@ -6,7 +6,10 @@ import { buildBrowserItems, ModelBrowser, resolveRoleAssignments, sortModelItems
6
6
  import { BROWSER_FRAME_ROWS } from "../../overlays/model-picker";
7
7
  import { formatKeyHint } from "../../app-keybindings";
8
8
  import { theme } from "../../theme/theme";
9
- import type { SetupScene, SetupSceneController, SetupSceneHost } from "./types";
9
+ import { col, node, span, text } from "../../native/describe";
10
+ import type { NativeNode } from "../../native/node";
11
+ import { Memo } from "../../native/memo";
12
+ import type { SetupScene, SetupSceneController, SetupSceneHost, StyledLine } from "./types";
10
13
 
11
14
  const MAX_VISIBLE_MODELS = 10;
12
15
 
@@ -14,10 +17,12 @@ class ModelSceneController implements SetupSceneController {
14
17
  title = "Choose your default model";
15
18
  subtitle = "Search configured models and save the model used for new sessions.";
16
19
  #browser: ModelBrowser;
17
- #status: string | undefined;
20
+ /** Intro override; `busy` while discovering or saving (a spinner on the native path). */
21
+ #status: (StyledLine & { readonly busy: boolean }) | undefined;
18
22
  #selecting = false;
19
23
  #disposed = false;
20
24
  #step: WizardStep | undefined;
25
+ #native = new Memo();
21
26
 
22
27
  readonly #host: SetupSceneHost;
23
28
 
@@ -32,7 +37,7 @@ class ModelSceneController implements SetupSceneController {
32
37
  }
33
38
 
34
39
  async onMount(): Promise<void> {
35
- this.#status = theme.fg("muted", "Discovering available models…");
40
+ this.#status = { text: "Discovering available models…", color: "muted", busy: true };
36
41
  this.#host.requestRender();
37
42
  await this.#refreshModels();
38
43
  }
@@ -42,6 +47,7 @@ class ModelSceneController implements SetupSceneController {
42
47
  }
43
48
 
44
49
  invalidate(): void {
50
+ this.#native.clear();
45
51
  if (this.#step) this.#step.invalidate();
46
52
  else this.#browser.invalidate();
47
53
  }
@@ -58,9 +64,14 @@ class ModelSceneController implements SetupSceneController {
58
64
  }
59
65
 
60
66
  render(width: number, maxLines?: number): readonly string[] {
67
+ const status = this.#status;
61
68
  const intro = new Text(
62
- this.#status ??
63
- theme.fg("muted", `Type to search. ${formatKeyHint("enter")} saves the highlighted model as your default.`),
69
+ status
70
+ ? theme.fg(status.color, status.text)
71
+ : theme.fg(
72
+ "muted",
73
+ `Type to search. ${formatKeyHint("enter")} saves the highlighted model as your default.`,
74
+ ),
64
75
  0,
65
76
  0,
66
77
  );
@@ -82,6 +93,23 @@ class ModelSceneController implements SetupSceneController {
82
93
  return this.#step.render(width);
83
94
  }
84
95
 
96
+ /** Intro (a spinner while models load or the choice saves) over the model browser, which describes itself. */
97
+ describe(): NativeNode {
98
+ const status = this.#status;
99
+ const enter = formatKeyHint("enter");
100
+ return this.#native.get([status, enter], () => {
101
+ let intro: NativeNode;
102
+ if (status?.busy) {
103
+ intro = node("spinner", { label: [span(status.text, status.color)] });
104
+ } else if (status) {
105
+ intro = text([span(status.text, status.color)]);
106
+ } else {
107
+ intro = text([span(`Type to search. ${enter} saves the highlighted model as your default.`, "muted")]);
108
+ }
109
+ return col([{ ...intro, key: "intro" }, this.#browser], { gap: "sm", role: "omp.setup.model" });
110
+ });
111
+ }
112
+
85
113
  #syncModels(): void {
86
114
  const { available, all, current } = this.#host.ctx.getModels();
87
115
  const source = this.#host.ctx.modelSource;
@@ -109,7 +137,7 @@ class ModelSceneController implements SetupSceneController {
109
137
  this.#host.requestRender();
110
138
  } catch (error) {
111
139
  if (this.#disposed) return;
112
- this.#status = theme.fg("error", error instanceof Error ? error.message : String(error));
140
+ this.#status = { text: error instanceof Error ? error.message : String(error), color: "error", busy: false };
113
141
  this.#host.requestRender();
114
142
  }
115
143
  }
@@ -117,7 +145,7 @@ class ModelSceneController implements SetupSceneController {
117
145
  async #select(model: Model, selector: string): Promise<void> {
118
146
  if (this.#selecting) return;
119
147
  this.#selecting = true;
120
- this.#status = theme.fg("muted", `Saving ${selector} as the default model…`);
148
+ this.#status = { text: `Saving ${selector} as the default model…`, color: "muted", busy: true };
121
149
  this.#host.requestRender();
122
150
  try {
123
151
  await this.#host.ctx.selectModel(model, selector);
@@ -125,7 +153,7 @@ class ModelSceneController implements SetupSceneController {
125
153
  } catch (error) {
126
154
  if (this.#disposed) return;
127
155
  this.#selecting = false;
128
- this.#status = theme.fg("error", error instanceof Error ? error.message : String(error));
156
+ this.#status = { text: error instanceof Error ? error.message : String(error), color: "error", busy: false };
129
157
  this.#host.requestRender();
130
158
  }
131
159
  }
@@ -1,11 +1,38 @@
1
1
  import { centerLine } from "../../utils";
2
2
  import { padToWidth } from "../../render/utils";
3
- import { gradientLogo, PI_LOGO } from "../../prompt/welcome";
3
+ import { gradientLogo, logoNode, PI_LOGO } from "../../prompt/welcome";
4
4
  import { theme } from "../../theme/theme";
5
+ import { col, node, span, text } from "../../native/describe";
6
+ import type { NativeNode } from "../../native/node";
7
+ import { Memo } from "../../native/memo";
5
8
  import { renderStarfield, SETUP_TICK_MS } from "./splash";
6
9
 
7
10
  export const SETUP_OUTRO_MS = 1200;
8
11
 
12
+ const outroMemo = new Memo();
13
+
14
+ /**
15
+ * Native outro: the shimmering mark, the saved confirmation, and an
16
+ * indeterminate progress bar standing in for the timed sweep. A click sends
17
+ * the `continue` action.
18
+ */
19
+ export function describeSetupOutro(): NativeNode {
20
+ const saved = `${theme.status.success} Setup saved`;
21
+ return outroMemo.get([saved], () =>
22
+ col(
23
+ [
24
+ node("spacer", { grow: 1 }),
25
+ logoNode(PI_LOGO, true),
26
+ text([span(saved, "success strong")], { wrap: "none" }),
27
+ text([span("Handing off to the normal CLI…", "muted")], { wrap: "none" }),
28
+ node("progress", { value: null, max: { w: "48ch" } }),
29
+ node("spacer", { grow: 1 }),
30
+ ],
31
+ { align: "center", gap: "sm", grow: 1, role: "omp.setup.outro", actions: { click: "continue" } },
32
+ ),
33
+ );
34
+ }
35
+
9
36
  export function renderSetupOutro(width: number, height: number, elapsedMs: number): string[] {
10
37
  const frame = Math.floor(elapsedMs / SETUP_TICK_MS);
11
38
  const lines = renderStarfield(width, height, frame + 1000);