@oh-my-pi/pi-tui 18.4.2 → 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 (463) hide show
  1. package/CHANGELOG.md +56 -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/thinking-display.d.ts +2 -0
  22. package/dist/types/chat/todo-reminder.d.ts +4 -1
  23. package/dist/types/chat/tool-execution.d.ts +25 -1
  24. package/dist/types/chat/transcript-actions.d.ts +37 -0
  25. package/dist/types/chat/transcript-entry.d.ts +3 -1
  26. package/dist/types/chat/user-message.d.ts +25 -0
  27. package/dist/types/chrome/collab-qrcode.d.ts +7 -0
  28. package/dist/types/chrome/context-thresholds.d.ts +11 -0
  29. package/dist/types/chrome/countdown-timer.d.ts +9 -3
  30. package/dist/types/chrome/diff.d.ts +12 -0
  31. package/dist/types/chrome/dynamic-border.d.ts +3 -0
  32. package/dist/types/chrome/format.d.ts +6 -0
  33. package/dist/types/chrome/message-divider.d.ts +13 -0
  34. package/dist/types/chrome/message-frame.d.ts +10 -0
  35. package/dist/types/chrome/message-notice.d.ts +23 -0
  36. package/dist/types/chrome/overlay-box.d.ts +11 -2
  37. package/dist/types/chrome/qrcode.d.ts +7 -0
  38. package/dist/types/chrome/segment-track.d.ts +7 -0
  39. package/dist/types/chrome/selector-helpers.d.ts +0 -6
  40. package/dist/types/chrome/status-notice.d.ts +14 -0
  41. package/dist/types/chrome/streaming-panel.d.ts +3 -0
  42. package/dist/types/chrome/tool-activity.d.ts +3 -0
  43. package/dist/types/chrome/transcript-container.d.ts +13 -0
  44. package/dist/types/chrome/transcript-status.d.ts +15 -3
  45. package/dist/types/components/box.d.ts +7 -0
  46. package/dist/types/components/disclosure.d.ts +9 -0
  47. package/dist/types/components/editor.d.ts +42 -0
  48. package/dist/types/components/form.d.ts +14 -0
  49. package/dist/types/components/image.d.ts +7 -0
  50. package/dist/types/components/input.d.ts +20 -1
  51. package/dist/types/components/key-value-list.d.ts +3 -0
  52. package/dist/types/components/layout/geometry.d.ts +16 -0
  53. package/dist/types/components/layout/row.d.ts +7 -0
  54. package/dist/types/components/layout/split-pane.d.ts +8 -0
  55. package/dist/types/components/layout/stack.d.ts +6 -0
  56. package/dist/types/components/loader.d.ts +48 -0
  57. package/dist/types/components/markdown.d.ts +8 -0
  58. package/dist/types/components/metric.d.ts +7 -0
  59. package/dist/types/components/progress-bar.d.ts +3 -0
  60. package/dist/types/components/scroll-view.d.ts +6 -0
  61. package/dist/types/components/section.d.ts +3 -0
  62. package/dist/types/components/select-list.d.ts +33 -0
  63. package/dist/types/components/settings-list.d.ts +68 -0
  64. package/dist/types/components/spacer.d.ts +4 -0
  65. package/dist/types/components/tab-bar.d.ts +9 -0
  66. package/dist/types/components/table.d.ts +7 -0
  67. package/dist/types/components/text.d.ts +6 -0
  68. package/dist/types/components/tree-view.d.ts +9 -0
  69. package/dist/types/components/truncated-text.d.ts +3 -0
  70. package/dist/types/components/wizard-step.d.ts +6 -0
  71. package/dist/types/hotkeys-markdown.d.ts +24 -0
  72. package/dist/types/index.d.ts +5 -0
  73. package/dist/types/latex-block.d.ts +7 -0
  74. package/dist/types/loop-watchdog.d.ts +9 -0
  75. package/dist/types/native/apply.d.ts +39 -0
  76. package/dist/types/native/backend.d.ts +94 -0
  77. package/dist/types/native/blobs.d.ts +29 -0
  78. package/dist/types/native/describe.d.ts +56 -0
  79. package/dist/types/native/encode.d.ts +50 -0
  80. package/dist/types/native/icons.d.ts +18 -0
  81. package/dist/types/native/memo.d.ts +27 -0
  82. package/dist/types/native/node.d.ts +107 -0
  83. package/dist/types/native/overlay.d.ts +64 -0
  84. package/dist/types/native/picker.d.ts +124 -0
  85. package/dist/types/native/reconcile.d.ts +41 -0
  86. package/dist/types/native/settle.d.ts +11 -0
  87. package/dist/types/native/spans.d.ts +21 -0
  88. package/dist/types/native/state.d.ts +6 -0
  89. package/dist/types/native/tone.d.ts +24 -0
  90. package/dist/types/overlays/advisor-config.d.ts +20 -0
  91. package/dist/types/overlays/agent-hub-renderer.d.ts +27 -11
  92. package/dist/types/overlays/agent-hub.d.ts +10 -0
  93. package/dist/types/overlays/agent-transcript-viewer.d.ts +7 -0
  94. package/dist/types/overlays/agents-hub.d.ts +8 -0
  95. package/dist/types/overlays/annotation-overlay.d.ts +3 -0
  96. package/dist/types/overlays/ask-dialog.d.ts +23 -0
  97. package/dist/types/overlays/bordered-loader.d.ts +3 -0
  98. package/dist/types/overlays/btw-history-panel.d.ts +9 -0
  99. package/dist/types/overlays/btw-panel.d.ts +3 -0
  100. package/dist/types/overlays/cleanse-panel.d.ts +5 -2
  101. package/dist/types/overlays/composer-shape-preview.d.ts +7 -1
  102. package/dist/types/overlays/copy-selector.d.ts +38 -0
  103. package/dist/types/overlays/error-banner.d.ts +9 -0
  104. package/dist/types/overlays/extensions/extension-list.d.ts +1 -0
  105. package/dist/types/overlays/history-search.d.ts +18 -0
  106. package/dist/types/overlays/hook-editor.d.ts +28 -1
  107. package/dist/types/overlays/hook-input.d.ts +3 -0
  108. package/dist/types/overlays/hook-selector.d.ts +10 -0
  109. package/dist/types/overlays/hub-frame.d.ts +18 -0
  110. package/dist/types/overlays/jobs-panel.d.ts +24 -0
  111. package/dist/types/overlays/login-dialog.d.ts +10 -0
  112. package/dist/types/overlays/logout-account-selector.d.ts +4 -0
  113. package/dist/types/overlays/mcp-add-wizard.d.ts +4 -0
  114. package/dist/types/overlays/model-browser.d.ts +69 -0
  115. package/dist/types/overlays/model-hub.d.ts +7 -0
  116. package/dist/types/overlays/model-picker.d.ts +9 -1
  117. package/dist/types/overlays/move-overlay.d.ts +3 -0
  118. package/dist/types/overlays/oauth-selector.d.ts +4 -0
  119. package/dist/types/overlays/omfg-panel.d.ts +3 -0
  120. package/dist/types/overlays/pause-screen.d.ts +12 -0
  121. package/dist/types/overlays/plan-review-overlay.d.ts +5 -0
  122. package/dist/types/overlays/plan-save-overlay.d.ts +10 -0
  123. package/dist/types/overlays/plugin-selector.d.ts +4 -0
  124. package/dist/types/overlays/plugin-settings.d.ts +30 -0
  125. package/dist/types/overlays/queue-mode-selector.d.ts +4 -0
  126. package/dist/types/overlays/reset-usage-selector.d.ts +8 -0
  127. package/dist/types/overlays/rewind-selector.d.ts +5 -0
  128. package/dist/types/overlays/session-info-overlay.d.ts +19 -1
  129. package/dist/types/overlays/session-selector.d.ts +64 -1
  130. package/dist/types/overlays/settings-defs.d.ts +14 -0
  131. package/dist/types/overlays/settings-selector.d.ts +21 -0
  132. package/dist/types/overlays/show-images-selector.d.ts +4 -0
  133. package/dist/types/overlays/snapcompact-shape-preview.d.ts +2 -0
  134. package/dist/types/overlays/stats-notice.d.ts +8 -0
  135. package/dist/types/overlays/theme-selector.d.ts +11 -0
  136. package/dist/types/overlays/thinking-selector.d.ts +4 -0
  137. package/dist/types/overlays/tree-selector.d.ts +60 -2
  138. package/dist/types/overlays/usage-dashboard.d.ts +26 -3
  139. package/dist/types/overlays/usage-row.d.ts +36 -2
  140. package/dist/types/prompt/attachment-chips.d.ts +8 -0
  141. package/dist/types/prompt/composer-attachments.d.ts +15 -3
  142. package/dist/types/prompt/composer.d.ts +18 -1
  143. package/dist/types/prompt/custom-editor.d.ts +48 -2
  144. package/dist/types/prompt/editor-top-gap.d.ts +2 -0
  145. package/dist/types/prompt/macos-spelling.d.ts +6 -0
  146. package/dist/types/prompt/magic-keywords.d.ts +9 -0
  147. package/dist/types/prompt/queued-messages.d.ts +23 -0
  148. package/dist/types/prompt/skill-tokens.d.ts +4 -2
  149. package/dist/types/prompt/welcome.d.ts +16 -0
  150. package/dist/types/render/code-cell.d.ts +10 -0
  151. package/dist/types/render/file-list.d.ts +8 -0
  152. package/dist/types/render/hyperlink.d.ts +14 -0
  153. package/dist/types/render/output-block.d.ts +43 -0
  154. package/dist/types/render/output-pane.d.ts +17 -0
  155. package/dist/types/render/tool-card.d.ts +8 -0
  156. package/dist/types/render/tree-list.d.ts +8 -0
  157. package/dist/types/render/width-aware-text.d.ts +7 -0
  158. package/dist/types/setup/scenes/outro.d.ts +7 -0
  159. package/dist/types/setup/scenes/sign-in.d.ts +16 -9
  160. package/dist/types/setup/scenes/splash.d.ts +8 -0
  161. package/dist/types/setup/scenes/types.d.ts +6 -27
  162. package/dist/types/setup/wizard-overlay.d.ts +9 -0
  163. package/dist/types/status-line/component.d.ts +49 -5
  164. package/dist/types/status-line/context-usage.d.ts +20 -0
  165. package/dist/types/status-line/footer.d.ts +8 -0
  166. package/dist/types/status-line/metrics.d.ts +1 -2
  167. package/dist/types/status-line/segments.d.ts +10 -1
  168. package/dist/types/status-line/types.d.ts +40 -1
  169. package/dist/types/stdin-buffer.d.ts +10 -7
  170. package/dist/types/terminal-capabilities.d.ts +1 -1
  171. package/dist/types/terminal.d.ts +36 -0
  172. package/dist/types/theme/defaults/index.d.ts +100 -0
  173. package/dist/types/theme/schema.d.ts +2 -0
  174. package/dist/types/theme/shimmer.d.ts +8 -0
  175. package/dist/types/theme/theme-class.d.ts +2 -0
  176. package/dist/types/theme/theme.d.ts +33 -1
  177. package/dist/types/tools/ask.d.ts +3 -0
  178. package/dist/types/tools/ast-edit.d.ts +10 -1
  179. package/dist/types/tools/ast-grep.d.ts +10 -1
  180. package/dist/types/tools/autoresearch.d.ts +21 -4
  181. package/dist/types/tools/bash.d.ts +30 -1
  182. package/dist/types/tools/cfg-render.d.ts +5 -1
  183. package/dist/types/tools/debug.d.ts +3 -1
  184. package/dist/types/tools/default-renderer.d.ts +6 -1
  185. package/dist/types/tools/edit.d.ts +14 -1
  186. package/dist/types/tools/eval.d.ts +12 -1
  187. package/dist/types/tools/fetch.d.ts +16 -1
  188. package/dist/types/tools/find.d.ts +15 -1
  189. package/dist/types/tools/github.d.ts +3 -0
  190. package/dist/types/tools/glob.d.ts +14 -1
  191. package/dist/types/tools/goal.d.ts +3 -1
  192. package/dist/types/tools/grep.d.ts +14 -1
  193. package/dist/types/tools/grouped-file-output.d.ts +58 -0
  194. package/dist/types/tools/json-tree.d.ts +20 -0
  195. package/dist/types/tools/lsp.d.ts +3 -10
  196. package/dist/types/tools/mcp.d.ts +16 -1
  197. package/dist/types/tools/memory.d.ts +26 -0
  198. package/dist/types/tools/native-view.d.ts +90 -0
  199. package/dist/types/tools/proc-render.d.ts +7 -1
  200. package/dist/types/tools/read.d.ts +23 -1
  201. package/dist/types/tools/renderer.d.ts +57 -0
  202. package/dist/types/tools/report-tool-issue.d.ts +3 -0
  203. package/dist/types/tools/resolve.d.ts +14 -1
  204. package/dist/types/tools/task.d.ts +3 -0
  205. package/dist/types/tools/think.d.ts +3 -0
  206. package/dist/types/tools/todo.d.ts +12 -0
  207. package/dist/types/tools/vibe.d.ts +3 -1
  208. package/dist/types/tools/wait.d.ts +3 -0
  209. package/dist/types/tools/web-search-types.d.ts +90 -0
  210. package/dist/types/tools/web-search.d.ts +9 -168
  211. package/dist/types/tools/write.d.ts +14 -1
  212. package/dist/types/tools/xdev.d.ts +13 -0
  213. package/dist/types/tui.d.ts +49 -0
  214. package/package.json +9 -9
  215. package/src/apps/autoresearch-dashboard.ts +362 -4
  216. package/src/apps/cleanse-board.ts +219 -1
  217. package/src/apps/cleanse-picker.ts +9 -1
  218. package/src/apps/debug/log-viewer.ts +15 -15
  219. package/src/apps/debug/protocol-probe.ts +2 -20
  220. package/src/apps/git/avatar.ts +2 -22
  221. package/src/apps/git/git-tui.ts +406 -59
  222. package/src/apps/git/help.ts +130 -0
  223. package/src/apps/git/sidebar.ts +491 -0
  224. package/src/apps/live-visualizer.ts +65 -0
  225. package/src/apps/ps-top.ts +448 -17
  226. package/src/apps/session-picker.ts +1 -0
  227. package/src/apps/setup-model-picker.ts +5 -1
  228. package/src/apps/standalone-picker.ts +55 -1
  229. package/src/autocomplete.ts +44 -1
  230. package/src/chat/advisor-message.ts +72 -3
  231. package/src/chat/assistant-message.ts +538 -51
  232. package/src/chat/background-tan-message.ts +4 -4
  233. package/src/chat/bash-execution.ts +81 -0
  234. package/src/chat/cache-invalidation-marker.ts +8 -0
  235. package/src/chat/chat-transcript-builder.ts +37 -6
  236. package/src/chat/collab-prompt-message.ts +15 -3
  237. package/src/chat/compaction-summary-message.ts +74 -1
  238. package/src/chat/custom-message.ts +3 -1
  239. package/src/chat/eval-execution.ts +57 -0
  240. package/src/chat/execution-shared.ts +153 -1
  241. package/src/chat/hook-message.ts +1 -0
  242. package/src/chat/late-diagnostics-message.ts +85 -2
  243. package/src/chat/read-tool-group.ts +341 -1
  244. package/src/chat/served-model-marker.ts +8 -0
  245. package/src/chat/skill-message.ts +63 -0
  246. package/src/chat/stripped-tool-calls-placeholder.ts +18 -10
  247. package/src/chat/thinking-display.ts +73 -11
  248. package/src/chat/todo-reminder.ts +34 -4
  249. package/src/chat/tool-execution.ts +385 -10
  250. package/src/chat/transcript-actions.ts +38 -0
  251. package/src/chat/transcript-browser.ts +2 -9
  252. package/src/chat/transcript-entry.ts +10 -1
  253. package/src/chat/transcript-render-helpers.ts +16 -15
  254. package/src/chat/ttsr-notification.ts +38 -1
  255. package/src/chat/user-message.ts +167 -4
  256. package/src/chrome/collab-qrcode.ts +27 -2
  257. package/src/chrome/context-thresholds.ts +34 -0
  258. package/src/chrome/countdown-timer.ts +24 -1
  259. package/src/chrome/diff.ts +60 -1
  260. package/src/chrome/dynamic-border.ts +7 -0
  261. package/src/chrome/format.ts +12 -0
  262. package/src/chrome/message-divider.ts +33 -1
  263. package/src/chrome/message-frame.ts +53 -10
  264. package/src/chrome/message-notice.ts +71 -0
  265. package/src/chrome/overlay-box.ts +45 -2
  266. package/src/chrome/qrcode.ts +28 -0
  267. package/src/chrome/segment-track.ts +26 -0
  268. package/src/chrome/selector-helpers.ts +1 -17
  269. package/src/chrome/status-notice.ts +46 -0
  270. package/src/chrome/streaming-panel.ts +25 -1
  271. package/src/chrome/tool-activity.ts +20 -0
  272. package/src/chrome/transcript-container.ts +139 -17
  273. package/src/chrome/transcript-status.ts +40 -11
  274. package/src/components/box.ts +51 -0
  275. package/src/components/disclosure.ts +33 -0
  276. package/src/components/editor.ts +239 -36
  277. package/src/components/form.ts +93 -9
  278. package/src/components/image.ts +30 -0
  279. package/src/components/input.ts +77 -26
  280. package/src/components/key-value-list.ts +18 -0
  281. package/src/components/layout/geometry.ts +44 -0
  282. package/src/components/layout/row.ts +30 -0
  283. package/src/components/layout/split-pane.ts +65 -0
  284. package/src/components/layout/stack.ts +27 -0
  285. package/src/components/loader.ts +183 -1
  286. package/src/components/markdown.ts +131 -56
  287. package/src/components/metric.ts +39 -0
  288. package/src/components/progress-bar.ts +28 -0
  289. package/src/components/scroll-view.ts +23 -0
  290. package/src/components/section.ts +16 -0
  291. package/src/components/select-list.ts +174 -25
  292. package/src/components/settings-list.ts +264 -7
  293. package/src/components/spacer.ts +17 -0
  294. package/src/components/tab-bar.ts +42 -0
  295. package/src/components/table.ts +34 -0
  296. package/src/components/text.ts +23 -0
  297. package/src/components/tree-view.ts +65 -0
  298. package/src/components/truncated-text.ts +14 -0
  299. package/src/components/wizard-step.ts +18 -9
  300. package/src/debug-server.ts +14 -7
  301. package/src/hotkeys-markdown.ts +251 -69
  302. package/src/index.ts +6 -0
  303. package/src/latex-block.ts +11 -0
  304. package/src/loop-watchdog.ts +38 -10
  305. package/src/native/apply.ts +275 -0
  306. package/src/native/backend.ts +682 -0
  307. package/src/native/blobs.ts +70 -0
  308. package/src/native/describe.ts +124 -0
  309. package/src/native/encode.ts +233 -0
  310. package/src/native/icons.ts +184 -0
  311. package/src/native/memo.ts +64 -0
  312. package/src/native/node.ts +112 -0
  313. package/src/native/overlay.ts +131 -0
  314. package/src/native/picker.ts +333 -0
  315. package/src/native/reconcile.ts +1063 -0
  316. package/src/native/settle.ts +23 -0
  317. package/src/native/spans.ts +308 -0
  318. package/src/native/state.ts +28 -0
  319. package/src/native/tone.ts +79 -0
  320. package/src/overlays/advisor-config.ts +420 -0
  321. package/src/overlays/agent-hub-renderer.ts +155 -18
  322. package/src/overlays/agent-hub.ts +1037 -44
  323. package/src/overlays/agent-transcript-viewer.ts +129 -0
  324. package/src/overlays/agents-hub.ts +688 -53
  325. package/src/overlays/annotation-overlay.ts +391 -52
  326. package/src/overlays/ask-dialog.ts +435 -24
  327. package/src/overlays/bordered-loader.ts +17 -0
  328. package/src/overlays/btw-history-panel.ts +182 -0
  329. package/src/overlays/btw-panel.ts +68 -2
  330. package/src/overlays/cleanse-panel.ts +105 -15
  331. package/src/overlays/codex-reset-fireworks.ts +65 -5
  332. package/src/overlays/composer-shape-preview.ts +26 -1
  333. package/src/overlays/copy-selector.ts +545 -23
  334. package/src/overlays/error-banner.ts +44 -0
  335. package/src/overlays/extensions/extension-list.ts +23 -17
  336. package/src/overlays/history-search.ts +175 -33
  337. package/src/overlays/hook-editor.ts +113 -6
  338. package/src/overlays/hook-input.ts +22 -1
  339. package/src/overlays/hook-selector.ts +311 -22
  340. package/src/overlays/hub-frame.ts +80 -0
  341. package/src/overlays/jobs-panel.ts +112 -0
  342. package/src/overlays/login-dialog.ts +183 -1
  343. package/src/overlays/logout-account-selector.ts +102 -4
  344. package/src/overlays/mcp-add-wizard.ts +232 -53
  345. package/src/overlays/model-browser.ts +681 -22
  346. package/src/overlays/model-hub.ts +1402 -78
  347. package/src/overlays/model-picker.ts +184 -7
  348. package/src/overlays/move-overlay.ts +45 -0
  349. package/src/overlays/oauth-selector.ts +227 -23
  350. package/src/overlays/omfg-panel.ts +86 -2
  351. package/src/overlays/pause-screen.ts +55 -4
  352. package/src/overlays/plan-review-overlay.ts +352 -0
  353. package/src/overlays/plan-save-overlay.ts +45 -0
  354. package/src/overlays/plugin-selector.ts +21 -1
  355. package/src/overlays/plugin-settings.ts +195 -5
  356. package/src/overlays/queue-mode-selector.ts +19 -1
  357. package/src/overlays/reset-usage-selector.ts +110 -40
  358. package/src/overlays/rewind-selector.ts +458 -44
  359. package/src/overlays/session-account-selector.ts +1 -1
  360. package/src/overlays/session-info-overlay.ts +137 -1
  361. package/src/overlays/session-selector.ts +807 -46
  362. package/src/overlays/settings-defs.ts +41 -0
  363. package/src/overlays/settings-selector.ts +585 -33
  364. package/src/overlays/show-images-selector.ts +18 -1
  365. package/src/overlays/snapcompact-shape-preview.ts +48 -3
  366. package/src/overlays/stats-notice.ts +31 -0
  367. package/src/overlays/theme-selector.ts +31 -1
  368. package/src/overlays/thinking-selector.ts +22 -1
  369. package/src/overlays/tree-selector.ts +570 -110
  370. package/src/overlays/usage-dashboard.ts +587 -12
  371. package/src/overlays/usage-row.ts +161 -25
  372. package/src/prompt/attachment-chips.ts +60 -0
  373. package/src/prompt/composer-attachments.ts +31 -9
  374. package/src/prompt/composer.ts +55 -2
  375. package/src/prompt/custom-editor.ts +302 -4
  376. package/src/prompt/editor-top-gap.ts +8 -0
  377. package/src/prompt/macos-spelling.ts +24 -9
  378. package/src/prompt/magic-keywords.ts +22 -1
  379. package/src/prompt/model-mention-autocomplete.ts +1 -0
  380. package/src/prompt/queued-messages.ts +101 -0
  381. package/src/prompt/skill-tokens.ts +5 -3
  382. package/src/prompt/welcome.ts +213 -6
  383. package/src/render/code-cell.ts +103 -2
  384. package/src/render/file-list.ts +29 -0
  385. package/src/render/hyperlink.ts +32 -0
  386. package/src/render/output-block.ts +110 -0
  387. package/src/render/output-pane.ts +37 -0
  388. package/src/render/tool-card.ts +137 -0
  389. package/src/render/tree-list.ts +39 -0
  390. package/src/render/width-aware-text.ts +20 -0
  391. package/src/setup/scenes/composer.ts +29 -0
  392. package/src/setup/scenes/glyph.ts +15 -0
  393. package/src/setup/scenes/model.ts +36 -8
  394. package/src/setup/scenes/outro.ts +28 -1
  395. package/src/setup/scenes/sign-in.ts +138 -24
  396. package/src/setup/scenes/splash.ts +28 -1
  397. package/src/setup/scenes/theme.ts +88 -3
  398. package/src/setup/scenes/types.ts +7 -28
  399. package/src/setup/startup-splash.ts +46 -9
  400. package/src/setup/wizard-overlay.ts +140 -23
  401. package/src/setup/wizard.ts +1 -1
  402. package/src/status-line/component.ts +414 -18
  403. package/src/status-line/context-usage.ts +279 -50
  404. package/src/status-line/footer.ts +94 -1
  405. package/src/status-line/metrics.ts +3 -6
  406. package/src/status-line/segments.ts +463 -97
  407. package/src/status-line/separators.ts +3 -16
  408. package/src/status-line/types.ts +43 -1
  409. package/src/stdin-buffer.ts +27 -21
  410. package/src/terminal-capabilities.ts +7 -0
  411. package/src/terminal.ts +153 -11
  412. package/src/theme/defaults/dark-neon-noir.json +100 -0
  413. package/src/theme/defaults/index.ts +2 -0
  414. package/src/theme/defaults/light-prism.json +5 -5
  415. package/src/theme/defaults/onyx.json +62 -62
  416. package/src/theme/mermaid-cache.ts +10 -1
  417. package/src/theme/schema.ts +15 -0
  418. package/src/theme/shimmer.ts +33 -0
  419. package/src/theme/theme-class.ts +24 -19
  420. package/src/theme/theme.ts +111 -9
  421. package/src/theme/tui-adapters.ts +14 -1
  422. package/src/tools/ask.ts +157 -1
  423. package/src/tools/ast-edit.ts +174 -1
  424. package/src/tools/ast-grep.ts +82 -2
  425. package/src/tools/autoresearch.ts +149 -1
  426. package/src/tools/bash.ts +233 -62
  427. package/src/tools/cfg-render.ts +66 -1
  428. package/src/tools/debug.ts +78 -1
  429. package/src/tools/default-renderer.ts +35 -1
  430. package/src/tools/edit.ts +357 -31
  431. package/src/tools/eval.ts +257 -1
  432. package/src/tools/fetch.ts +102 -3
  433. package/src/tools/find.ts +127 -1
  434. package/src/tools/github.ts +119 -10
  435. package/src/tools/glob.ts +116 -1
  436. package/src/tools/goal.ts +56 -1
  437. package/src/tools/grep.ts +83 -2
  438. package/src/tools/grouped-file-output.ts +205 -0
  439. package/src/tools/json-tree.ts +116 -1
  440. package/src/tools/lsp.ts +318 -1
  441. package/src/tools/mcp.ts +80 -1
  442. package/src/tools/memory.ts +93 -11
  443. package/src/tools/native-view.ts +292 -0
  444. package/src/tools/proc-render.ts +221 -1
  445. package/src/tools/read.ts +199 -4
  446. package/src/tools/renderer.ts +61 -0
  447. package/src/tools/report-tool-issue.ts +7 -1
  448. package/src/tools/resolve.ts +63 -1
  449. package/src/tools/streaming-output.ts +79 -16
  450. package/src/tools/task.ts +406 -4
  451. package/src/tools/think.ts +19 -1
  452. package/src/tools/todo.ts +89 -0
  453. package/src/tools/vibe.ts +230 -2
  454. package/src/tools/wait.ts +181 -1
  455. package/src/tools/web-search-types.ts +99 -0
  456. package/src/tools/web-search.ts +111 -170
  457. package/src/tools/write.ts +144 -5
  458. package/src/tools/xdev.ts +64 -1
  459. package/src/tui.ts +295 -4
  460. package/dist/types/setup/scenes/providers.d.ts +0 -3
  461. package/dist/types/setup/scenes/web-search.d.ts +0 -22
  462. package/src/setup/scenes/providers.ts +0 -110
  463. package/src/setup/scenes/web-search.ts +0 -165
@@ -34,6 +34,10 @@ import { bottomBorder, row, topBorder } from "../chrome/overlay-box";
34
34
  import { padToWidth } from "../render/utils";
35
35
  import { SplitPane } from "../components/layout/split-pane";
36
36
  import { clampSelection, contentRowWidth, padLinesToHeight, renderScrollableList } from "../chrome/selector-helpers";
37
+ import type { TspSpan } from "@oh-my-pi/pi-wire";
38
+ import type { NativeChild, NativeNode, NativeUiEvent } from "../native/node";
39
+ import { col, md, node, span, text } from "../native/describe";
40
+ import { actionHint, hintsRow, type NativeHint, statusHintsRow } from "../native/overlay";
37
41
 
38
42
  interface BtwHistoryPanelOptions {
39
43
  records: readonly BtwHistoryRecord[];
@@ -63,6 +67,20 @@ interface RenderedTurn {
63
67
  width: number;
64
68
  }
65
69
 
70
+ /** The visible inputs of the last native description; any change rebuilds it. */
71
+ interface BtwHistoryNativeMemo {
72
+ records: readonly BtwHistoryRecord[];
73
+ selectedId: string | undefined;
74
+ focus: "list" | "answer";
75
+ focused: boolean;
76
+ composer: FollowUpComposer | undefined;
77
+ notice: string | undefined;
78
+ followUpPending: boolean;
79
+ canFollowUp: boolean;
80
+ copied: boolean;
81
+ node: NativeNode;
82
+ }
83
+
66
84
  const STATUS: Record<BtwHistoryRecord["status"], { label: string; color: ThemeColor }> = {
67
85
  running: { label: "Running", color: "accent" },
68
86
  complete: { label: "Complete", color: "success" },
@@ -89,6 +107,7 @@ export class BtwHistoryPanel implements Component, Focusable {
89
107
  #detailDirty = true;
90
108
  #lastCopiedId: string | undefined;
91
109
  #lastCopiedText: string | undefined;
110
+ #native: BtwHistoryNativeMemo | undefined;
92
111
  readonly #detail = new ScrollView([], {
93
112
  height: 1,
94
113
  scrollbar: "auto",
@@ -190,6 +209,7 @@ export class BtwHistoryPanel implements Component, Focusable {
190
209
  this.#turns = [];
191
210
  this.#composer?.input.invalidate();
192
211
  this.#detailDirty = true;
212
+ this.#native = undefined;
193
213
  this.#split.invalidate();
194
214
  this.#labels.invalidate();
195
215
  }
@@ -340,6 +360,168 @@ export class BtwHistoryPanel implements Component, Focusable {
340
360
  this.#options.requestRender();
341
361
  }
342
362
 
363
+ /** A large glass sheet titled "BTW history". */
364
+ readonly nativeOverlay = { role: "omp.overlay.btwHistory", size: "lg", head: "BTW history" } as const;
365
+
366
+ describe(): NativeNode {
367
+ const record = this.#selected();
368
+ const composer = this.#composer;
369
+ const canFollowUp = record !== undefined && this.#canFollowUp(record);
370
+ const copied = record !== undefined && this.#isCopied(record);
371
+ const memo = this.#native;
372
+ if (
373
+ memo?.records === this.#records &&
374
+ memo.selectedId === this.#selectedId &&
375
+ memo.focus === this.#focus &&
376
+ memo.focused === this.#focused &&
377
+ memo.composer === composer &&
378
+ memo.notice === composer?.notice &&
379
+ memo.followUpPending === this.#followUpPending &&
380
+ memo.canFollowUp === canFollowUp &&
381
+ memo.copied === copied
382
+ ) {
383
+ return memo.node;
384
+ }
385
+ const children: NativeChild[] = [
386
+ node(
387
+ "row",
388
+ { gap: "md", align: "start" },
389
+ [
390
+ node(
391
+ "section",
392
+ { head: this.#describeFocusLabel("list"), basis: 0.42, max: { w: "46ch" } },
393
+ [this.#describeList()],
394
+ "history",
395
+ ),
396
+ node(
397
+ "section",
398
+ { head: this.#describeFocusLabel("answer"), grow: 1 },
399
+ [this.#describeDetail(record, copied)],
400
+ "details",
401
+ ),
402
+ ],
403
+ "body",
404
+ ),
405
+ ];
406
+ if (composer) {
407
+ composer.input.focused = this.#focused;
408
+ const lines: NativeChild[] = [
409
+ text([span(`Topic: ${sanitizeDisplayLine(record?.question ?? "")}`, "dim")], { truncate: "end" }),
410
+ ];
411
+ if (composer.notice) {
412
+ lines.push(text([span(composer.notice, this.#followUpPending ? "dim" : "warning")], { wrap: "word" }));
413
+ }
414
+ lines.push(composer.input);
415
+ children.push(node("col", undefined, lines, "composer"));
416
+ }
417
+ children.push({ ...this.#describeFooter(record, canFollowUp, copied), key: "footer" });
418
+ // The sheet is the frame (`nativeOverlay`): a borderless column.
419
+ const described = col(children, { gap: "md" });
420
+ this.#native = {
421
+ records: this.#records,
422
+ selectedId: this.#selectedId,
423
+ focus: this.#focus,
424
+ focused: this.#focused,
425
+ composer,
426
+ notice: composer?.notice,
427
+ followUpPending: this.#followUpPending,
428
+ canFollowUp,
429
+ copied,
430
+ node: described,
431
+ };
432
+ return described;
433
+ }
434
+
435
+ handleNativeEvent(event: NativeUiEvent): void {
436
+ if ((event.type !== "select" && event.type !== "activate") || this.#composer) return;
437
+ const index = this.#records.findIndex(record => record.id === event.item);
438
+ if (index === -1) return;
439
+ this.#select(index);
440
+ this.#focus = "list";
441
+ // Enter on a history row opens the follow-up composer.
442
+ if (event.type === "activate") this.openFollowUp(event.item);
443
+ this.#options.requestRender();
444
+ }
445
+
446
+ #describeFocusLabel(focus: "list" | "answer"): TspSpan[] {
447
+ const label = focus === "list" ? `History (${this.#records.length})` : "Details";
448
+ return this.#focus === focus ? [span(`${theme.nav.cursor} ${label}`, "accent strong")] : [span(label, "muted")];
449
+ }
450
+
451
+ #describeList(): NativeNode {
452
+ const items = this.#records.map(record => {
453
+ const status = STATUS[getBtwLatestTurn(record).status];
454
+ return node(
455
+ "item",
456
+ {
457
+ label: sanitizeDisplayLine(record.question),
458
+ detail: [span(this.#timeFormat.format(record.createdAt), "dim")],
459
+ value: [span(status.label, status.color)],
460
+ },
461
+ undefined,
462
+ record.id,
463
+ );
464
+ });
465
+ return node(
466
+ "list",
467
+ {
468
+ selected: this.#selectedId ?? null,
469
+ empty: [span("No side questions yet. Use /btw QUESTION to start one.", "muted")],
470
+ },
471
+ items,
472
+ "records",
473
+ );
474
+ }
475
+
476
+ #describeDetail(record: BtwHistoryRecord | undefined, copied: boolean): NativeNode {
477
+ if (!record)
478
+ return text([span("No side questions yet. Use /btw QUESTION to start one.", "muted")], { wrap: "word" });
479
+ const children: NativeChild[] = [];
480
+ if (copied)
481
+ children.push(node("text", { spans: [span("✓ Copied to clipboard", "success")] }, undefined, "copied"));
482
+ const turns = getBtwTurns(record);
483
+ for (let index = 0; index < turns.length; index++) {
484
+ if (index > 0) children.push(node("rule", undefined, undefined, `rule-${index}`));
485
+ children.push(this.#describeTurn(turns[index]!, `turn-${index}`));
486
+ }
487
+ return col(children, { gap: "md" });
488
+ }
489
+
490
+ #describeTurn(turn: BtwHistoryTurn, key: string): NativeNode {
491
+ const status = STATUS[turn.status];
492
+ const answer = turn.answer.trim()
493
+ ? md(sanitizeDisplayText(turn.answer), { stream: turn.status === "running" })
494
+ : text([span(turn.status === "running" ? "Waiting for response…" : "No answer text.", "dim")]);
495
+ const children: NativeChild[] = [
496
+ text([span(`${status.label} · ${this.#dateFormat.format(turn.createdAt)}`, status.color)], { wrap: "word" }),
497
+ node("section", { head: [span("Question", "accent strong")] }, [md(sanitizeDisplayText(turn.question))]),
498
+ node("section", { head: [span("Answer", "accent strong")] }, [answer]),
499
+ ];
500
+ if (turn.error) children.push(text([span(sanitizeErrorLine(turn.error), "error")], { wrap: "word" }));
501
+ if (turn.status === "interrupted") children.push(text([span("Not resumed in this view.", "muted")]));
502
+ return node("col", { gap: "sm" }, children, key);
503
+ }
504
+
505
+ #describeFooter(record: BtwHistoryRecord | undefined, canFollowUp: boolean, copied: boolean): NativeNode {
506
+ if (this.#composer) {
507
+ return hintsRow([
508
+ actionHint("tui.input.submit", this.#followUpPending ? "starting…" : "send"),
509
+ actionHint("tui.select.cancel", "cancel"),
510
+ ]);
511
+ }
512
+ const latest = record ? getBtwLatestTurn(record) : undefined;
513
+ const hints: (NativeHint | undefined)[] = [
514
+ actionHint("tui.select.cancel", latest?.status === "running" ? "cancel" : "close"),
515
+ { keys: ["tab", "ctrl+/"], label: "switch pane" },
516
+ actionHint(["tui.select.up", "tui.select.down"], this.#focus === "list" ? "select" : "scroll"),
517
+ ];
518
+ if (canFollowUp) hints.push({ keys: ["f", "enter"], label: "follow up" });
519
+ if (record && getBtwCopyText(record) !== undefined) {
520
+ hints.push({ keys: ["c"], label: copied ? "copy again" : "copy answer" });
521
+ }
522
+ return copied ? statusHintsRow([span("✓ copied", "success")], hints) : hintsRow(hints);
523
+ }
524
+
343
525
  #renderList(width: number, height: number): readonly string[] {
344
526
  const rowSpan = width < 36 && height >= 2 ? 2 : 1;
345
527
  this.#listHeight = Math.max(1, Math.floor(height / rowSpan));
@@ -4,8 +4,11 @@ import { getMarkdownTheme, theme } from "../theme/theme";
4
4
  import { sanitizeErrorLine } from "../chrome/error-block";
5
5
  import { OverlayPanel } from "../chrome/overlay-box";
6
6
  import { StreamingPanelContent } from "../chrome/streaming-panel";
7
- import { interruptKey } from "../chrome/keybinding-hints";
7
+ import { boundKeys, interruptKey } from "../chrome/keybinding-hints";
8
8
  import { formatKeyHint } from "../app-keybindings";
9
+ import type { NativeNode } from "../native/node";
10
+ import { col, md, node, span, text } from "../native/describe";
11
+ import { hintsRow, type NativeHint, statusHintsRow } from "../native/overlay";
9
12
 
10
13
  type BtwPanelState = "running" | "complete" | "branching" | "aborted" | "error";
11
14
 
@@ -27,12 +30,15 @@ export class BtwPanelComponent extends OverlayPanel {
27
30
  #closed = false;
28
31
  #copied = false;
29
32
  #baseTitle: string;
33
+ readonly #question: string;
30
34
  readonly #content: StreamingPanelContent;
35
+ #native: { node: NativeNode; canFollowUp: boolean; canBranch: boolean } | undefined;
31
36
 
32
37
  constructor(options: BtwPanelComponentOptions) {
33
38
  const baseTitle = `/btw ${replaceTabs(options.question)}`;
34
- super(baseTitle);
39
+ super(baseTitle, "omp.overlay.btw");
35
40
  this.#baseTitle = baseTitle;
41
+ this.#question = replaceTabs(options.question);
36
42
  this.#tui = options.tui;
37
43
  this.#canBranch = options.canBranch;
38
44
  this.#canFollowUp = options.canFollowUp;
@@ -121,7 +127,67 @@ export class BtwPanelComponent extends OverlayPanel {
121
127
  this.#closed = true;
122
128
  }
123
129
 
130
+ override invalidate(): void {
131
+ this.#native = undefined;
132
+ super.invalidate();
133
+ }
134
+
135
+ override describe(): NativeNode {
136
+ const canFollowUp = this.#canFollowUp?.() ?? false;
137
+ const canBranch = this.#canBranch?.() ?? this.isBranchable();
138
+ const memo = this.#native;
139
+ if (memo && memo.canFollowUp === canFollowUp && memo.canBranch === canBranch) return memo.node;
140
+ // Inline in the dock, styled as a sheet by role: a borderless column headed by the question.
141
+ const title = text([span("/btw", "accent"), span(` ${this.#question}`)], { truncate: "end", lines: 1 });
142
+ const live = this.#state === "running" || this.#state === "branching";
143
+ const head = node("row", { role: "omp.sheet.head", gap: "sm", align: "center" }, [
144
+ ...(live ? [node("spinner", {})] : []),
145
+ title,
146
+ ]);
147
+ const described = col([head, this.#describeBody(), this.#describeFooter(canFollowUp, canBranch)], {
148
+ role: this.nativeRole,
149
+ gap: "sm",
150
+ tone: this.#state === "error" ? "error" : this.#state === "aborted" ? "warning" : undefined,
151
+ });
152
+ this.#native = { node: described, canFollowUp, canBranch };
153
+ return described;
154
+ }
155
+
156
+ #describeBody(): NativeNode {
157
+ if (this.#state === "error") {
158
+ return text([span(sanitizeErrorLine(this.#errorMessage ?? "Unknown error"), "error")], { wrap: "word" });
159
+ }
160
+ const answer = this.#visibleAnswer;
161
+ if (answer) return md(answer, { stream: this.#state === "running" });
162
+ const waiting = this.#state === "running" ? `${theme.status.pending} Waiting for response…` : "No text returned.";
163
+ return text([span(waiting, "dim")]);
164
+ }
165
+
166
+ #describeFooter(canFollowUp: boolean, canBranch: boolean): NativeNode {
167
+ const esc: NativeHint = { keys: boundKeys("app.interrupt", ["escape"]).slice(0, 1), label: "to close" };
168
+ switch (this.#state) {
169
+ case "running":
170
+ return hintsRow([{ ...esc, label: "to cancel" }]);
171
+ case "complete": {
172
+ const hints: NativeHint[] = [];
173
+ if (this.isCopyable()) hints.push({ keys: ["c"], label: this.#copied ? "to copy again" : "to copy" });
174
+ if (canFollowUp) hints.push({ keys: ["f"], label: "to follow up" });
175
+ if (canBranch) hints.push({ keys: ["b"], label: "to branch" });
176
+ hints.push(esc);
177
+ if (!this.#copied) return hintsRow(hints);
178
+ return statusHintsRow([span("✓ Copied to clipboard", "success")], hints);
179
+ }
180
+ case "branching":
181
+ return text([span(`${theme.status.pending} Branching to chat…`, "muted")]);
182
+ case "aborted":
183
+ return statusHintsRow([span(`${theme.status.warning} Cancelled`, "warning")], [esc]);
184
+ case "error":
185
+ return statusHintsRow([span(`${theme.status.error} Error`, "error")], [esc]);
186
+ }
187
+ }
188
+
124
189
  #rebuild(): void {
190
+ this.#native = undefined;
125
191
  this.#content.refresh();
126
192
  // Component-scoped: a rebuild replaces only this panel's own children
127
193
  // (streaming deltas arrive per token, and a full compose would re-walk
@@ -5,7 +5,13 @@ import { replaceTabs } from "../render/render-utils";
5
5
  import { theme } from "../theme/theme";
6
6
  import { OverlayPanel } from "../chrome/overlay-box";
7
7
  import { StreamingPanelContent, type StreamingPanelPresentation } from "../chrome/streaming-panel";
8
- import { interruptKey } from "../chrome/keybinding-hints";
8
+ import { boundKeys, interruptKey } from "../chrome/keybinding-hints";
9
+ import type { TspTone } from "@oh-my-pi/pi-wire";
10
+ import type { DescribeContext, NativeNode } from "../native/node";
11
+ import { col, node, span, text } from "../native/describe";
12
+ import { hintsRow, type NativeHint, statusHintsRow } from "../native/overlay";
13
+ import { plainText } from "../native/spans";
14
+ import { isNativeRendering } from "../native/state";
9
15
  import type {
10
16
  CleanseBoardModel,
11
17
  CleanseCheckerDescriptor,
@@ -29,12 +35,26 @@ interface CleansePanelComponentOptions {
29
35
  /** Terminal state of the run, mirrored into the footer once the core settles. */
30
36
  type CleansePanelOutcome = CleansePanelRunStatus | "error";
31
37
 
38
+ /** Tone of the finished sheet per outcome. */
39
+ const OUTCOME_TONE: Record<CleansePanelOutcome, TspTone> = {
40
+ clean: "success",
41
+ unresolved: "warning",
42
+ unsupported: "warning",
43
+ cancelled: "warning",
44
+ error: "error",
45
+ };
46
+
32
47
  export class CleansePanelComponent extends OverlayPanel {
33
48
  readonly interactive = true;
34
49
 
35
50
  readonly #tui: TUI;
36
51
  readonly #model: CleanseBoardModel;
37
52
  readonly #logLines: string[] = [];
53
+ /** Semantic twins of {@link #logLines}, keyed by a running sequence so scrolled-off entries keep their ids. */
54
+ readonly #nativeLog: NativeNode[] = [];
55
+ #logSeq = 0;
56
+ readonly #request: string | undefined;
57
+ #native: NativeNode | undefined;
38
58
  #outcome: CleansePanelOutcome | undefined;
39
59
  #errorMessage: string | undefined;
40
60
  #frame = 0;
@@ -43,28 +63,39 @@ export class CleansePanelComponent extends OverlayPanel {
43
63
  readonly #content: StreamingPanelContent;
44
64
 
45
65
  constructor(options: CleansePanelComponentOptions) {
46
- super(options.request ? `/cleanse ${replaceTabs(options.request)}` : "/cleanse");
66
+ super(options.request ? `/cleanse ${replaceTabs(options.request)}` : "/cleanse", "omp.overlay.cleanse");
47
67
  this.#tui = options.tui;
48
68
  this.#model = options.model;
69
+ this.#request = options.request === undefined ? undefined : replaceTabs(options.request);
49
70
  this.#content = new StreamingPanelContent(() => this.#presentation());
50
71
  this.addChild(this.#content);
51
- this.#timer = setInterval(() => {
52
- this.#frame = (this.#frame + 1) % theme.getSpinnerFrames("activity").length;
53
- this.#rebuild();
54
- }, SPINNER_INTERVAL_MS);
55
- this.#timer.unref?.();
72
+ // The spinner frame only repaints; a native terminal clocks the described spinners itself.
73
+ if (!isNativeRendering()) {
74
+ this.#timer = setInterval(() => {
75
+ this.#frame = (this.#frame + 1) % theme.getSpinnerFrames("activity").length;
76
+ this.#rebuild();
77
+ }, SPINNER_INTERVAL_MS);
78
+ this.#timer.unref?.();
79
+ }
56
80
  this.#rebuild();
57
81
  }
58
82
 
59
- log(text: string): void {
60
- this.#logLines.push(text);
61
- if (this.#logLines.length > MAX_LOG_LINES) this.#logLines.splice(0, this.#logLines.length - MAX_LOG_LINES);
62
- this.#rebuild();
83
+ log(line: string): void {
84
+ this.#pushLog(line, text(replaceTabs(line), { wrap: "word" }));
63
85
  }
64
86
 
65
87
  /** Permanent line styled as a failure (the core's stderr-equivalent). */
66
- logError(text: string): void {
67
- this.log(theme.fg("error", text));
88
+ logError(line: string): void {
89
+ this.#pushLog(theme.fg("error", line), text([span(replaceTabs(line), "error")], { wrap: "word" }));
90
+ }
91
+
92
+ #pushLog(line: string, described: NativeNode | undefined): void {
93
+ this.#logLines.push(line);
94
+ if (this.#logLines.length > MAX_LOG_LINES) this.#logLines.splice(0, this.#logLines.length - MAX_LOG_LINES);
95
+ const entry = described ?? text(plainText(line), { wrap: "word" });
96
+ this.#nativeLog.push({ ...entry, key: `log-${this.#logSeq++}` });
97
+ if (this.#nativeLog.length > MAX_LOG_LINES) this.#nativeLog.splice(0, this.#nativeLog.length - MAX_LOG_LINES);
98
+ this.#rebuild();
68
99
  }
69
100
 
70
101
  phase(text: string | undefined): void {
@@ -78,7 +109,8 @@ export class CleansePanelComponent extends OverlayPanel {
78
109
  }
79
110
 
80
111
  checkerFinished(check: CleanseCheckResult, durationMs: number): void {
81
- this.log(this.#model.checkerFinished(check, durationMs));
112
+ const line = this.#model.checkerFinished(check, durationMs);
113
+ this.#pushLog(line, this.#model.lastSettled);
82
114
  }
83
115
 
84
116
  repairFinished(): void {
@@ -93,10 +125,13 @@ export class CleansePanelComponent extends OverlayPanel {
93
125
 
94
126
  agentProgress(name: string, progress: AgentProgress): void {
95
127
  this.#model.agentProgress(name, progress);
128
+ // The ANSI view picks progress up on its next spinner repaint; natively nothing else repaints.
129
+ if (isNativeRendering()) this.#rebuild();
96
130
  }
97
131
 
98
132
  agentFinished(outcome: CleanseAgentOutcome, assignment: CleanseAssignment): void {
99
- this.log(this.#model.agentFinished(outcome, assignment));
133
+ const line = this.#model.agentFinished(outcome, assignment);
134
+ this.#pushLog(line, this.#model.lastSettled);
100
135
  }
101
136
 
102
137
  /** Stop the live area; the panel stays mounted until the user dismisses it. */
@@ -144,7 +179,62 @@ export class CleansePanelComponent extends OverlayPanel {
144
179
  };
145
180
  }
146
181
 
182
+ override invalidate(): void {
183
+ this.#native = undefined;
184
+ super.invalidate();
185
+ }
186
+
187
+ override describe(cx: DescribeContext): NativeNode {
188
+ if (this.#native) return this.#native;
189
+ // Inline in the dock, styled as a sheet by role: a borderless column headed by the request.
190
+ const title =
191
+ this.#request === undefined
192
+ ? [span("/cleanse", "accent")]
193
+ : [span("/cleanse", "accent"), span(` ${this.#request}`)];
194
+ const head = node(
195
+ "row",
196
+ { role: "omp.sheet.head", gap: "sm", align: "center" },
197
+ [...(this.#outcome === undefined ? [node("spinner", {})] : []), text(title, { truncate: "end", lines: 1 })],
198
+ "head",
199
+ );
200
+ const body: NativeNode[] = [head];
201
+ if (this.#nativeLog.length > 0) body.push(node("col", undefined, [...this.#nativeLog], "log"));
202
+ const live = this.#liveClosed ? undefined : this.#model.describeLive(cx);
203
+ if (live) body.push({ ...live, key: "live" });
204
+ if (this.#errorMessage) {
205
+ body.push(
206
+ node("text", { spans: [span(replaceTabs(this.#errorMessage), "error")], wrap: "word" }, undefined, "error"),
207
+ );
208
+ }
209
+ body.push({ ...this.#describeFooter(), key: "footer" });
210
+ this.#native = col(body, {
211
+ role: this.nativeRole,
212
+ gap: "sm",
213
+ tone: this.#outcome === undefined ? undefined : OUTCOME_TONE[this.#outcome],
214
+ });
215
+ return this.#native;
216
+ }
217
+
218
+ #describeFooter(): NativeNode {
219
+ const esc: NativeHint = { keys: boundKeys("app.interrupt", ["escape"]).slice(0, 1), label: "dismiss" };
220
+ switch (this.#outcome) {
221
+ case undefined:
222
+ return hintsRow([{ ...esc, label: "cancel /cleanse" }]);
223
+ case "clean":
224
+ return statusHintsRow([span(`${theme.status.success} Clean`, "success")], [esc]);
225
+ case "unresolved":
226
+ return statusHintsRow([span(`${theme.status.warning} Diagnostics remain`, "warning")], [esc]);
227
+ case "unsupported":
228
+ return statusHintsRow([span(`${theme.status.warning} No runnable checker`, "warning")], [esc]);
229
+ case "cancelled":
230
+ return statusHintsRow([span(`${theme.status.warning} Cancelled`, "warning")], [esc]);
231
+ case "error":
232
+ return statusHintsRow([span(`${theme.status.error} Error`, "error")], [esc]);
233
+ }
234
+ }
235
+
147
236
  #rebuild(): void {
237
+ this.#native = undefined;
148
238
  this.#content.refresh();
149
239
  this.#tui.requestRender();
150
240
  }
@@ -9,6 +9,11 @@ import {
9
9
  } from "../index";
10
10
  import { type ThemeColor, theme } from "../theme/theme";
11
11
  import { formatKeyHint } from "../app-keybindings";
12
+ import type { TspSpan } from "@oh-my-pi/pi-wire";
13
+ import type { NativeNode, NativeUiEvent } from "../native/node";
14
+ import { col, node, span, text } from "../native/describe";
15
+ import { actionBar, actionButton } from "../native/overlay";
16
+ import { isNativeRendering } from "../native/state";
12
17
 
13
18
  const FRAME_INTERVAL_MS = 85;
14
19
  const FRAME_COUNT = 34;
@@ -256,6 +261,40 @@ function drawBurst(
256
261
  if (age <= 2) setCell(canvas, centerX, centerY, age === 0 ? "@" : "+", "white", 12);
257
262
  }
258
263
 
264
+ /**
265
+ * The celebration for a TSP terminal: the banner's title as a terminal-clocked
266
+ * shimmer between pulsing sparks, over the event's subtitle. The particle sky
267
+ * is ANSI-only art.
268
+ */
269
+ function describeCodexResetFireworks(event: CodexResetFireworksEvent): NativeNode {
270
+ const title = event.kind === "unscheduled-weekly-reset" ? "OpenAI reset" : "Saved reset";
271
+ const subtitle =
272
+ event.kind === "unscheduled-weekly-reset"
273
+ ? "Weekly usage cleared early"
274
+ : event.added === 1
275
+ ? `New reset banked · ${event.available} available`
276
+ : `${event.added} resets banked · ${event.available} available`;
277
+ const spark = (glyph: string, color: FireworkColor): TspSpan =>
278
+ span(glyph, FIREWORK_THEME_COLORS[color], { fx: "pulse" });
279
+ // The sheet is the frame (Tern bursts confetti from the pane's top edge for this role).
280
+ return col(
281
+ [
282
+ text([spark("✦ ", "pink"), spark("✧ ", "cyan"), spark("✦", "gold")]),
283
+ node("shimmer", {
284
+ text: title,
285
+ palette: {
286
+ low: FIREWORK_THEME_COLORS.violet,
287
+ mid: FIREWORK_THEME_COLORS.gold,
288
+ high: FIREWORK_THEME_COLORS.white,
289
+ },
290
+ }),
291
+ text([span(subtitle, FIREWORK_THEME_COLORS.cyan)]),
292
+ actionBar([null, actionButton("Close", "close", { keys: "escape" })]),
293
+ ],
294
+ { align: "center", gap: "sm" },
295
+ );
296
+ }
297
+
259
298
  function renderCanvas(canvas: Array<Array<CanvasCell | undefined>>): string[] {
260
299
  return canvas.map(row => {
261
300
  let output = "";
@@ -300,6 +339,7 @@ class CodexResetFireworksComponent implements Component {
300
339
  #done = Promise.withResolvers<void>();
301
340
  #frame = 0;
302
341
  #disposed = false;
342
+ #native: NativeNode | undefined;
303
343
 
304
344
  constructor(
305
345
  readonly host: CodexResetFireworksHost,
@@ -307,11 +347,14 @@ class CodexResetFireworksComponent implements Component {
307
347
  ) {}
308
348
 
309
349
  run(): Promise<void> {
310
- this.#timer ??= setInterval(() => {
311
- if (this.#disposed) return;
312
- this.#frame = (this.#frame + 1) % FRAME_COUNT;
313
- this.host.ui.requestRender();
314
- }, FRAME_INTERVAL_MS);
350
+ // Frames only repaint the particle sky; a native terminal clocks the described shimmer.
351
+ if (!isNativeRendering()) {
352
+ this.#timer ??= setInterval(() => {
353
+ if (this.#disposed) return;
354
+ this.#frame = (this.#frame + 1) % FRAME_COUNT;
355
+ this.host.ui.requestRender();
356
+ }, FRAME_INTERVAL_MS);
357
+ }
315
358
  this.host.ui.requestRender();
316
359
  return this.#done.promise;
317
360
  }
@@ -330,6 +373,23 @@ class CodexResetFireworksComponent implements Component {
330
373
  if (matchesKey(data, "escape") || matchesKey(data, "esc")) this.#done.resolve();
331
374
  }
332
375
 
376
+ /** Close runs what Esc runs. */
377
+ handleNativeEvent(event: NativeUiEvent): void {
378
+ if (event.type === "action" && event.act === "close") this.#done.resolve();
379
+ }
380
+
381
+ /** A glass sheet at the pane's top edge. */
382
+ readonly nativeOverlay = { role: "omp.overlay.fireworks", anchor: "top", size: "md" } as const;
383
+
384
+ invalidate(): void {
385
+ this.#native = undefined;
386
+ }
387
+
388
+ describe(): NativeNode {
389
+ this.#native ??= describeCodexResetFireworks(this.event);
390
+ return this.#native;
391
+ }
392
+
333
393
  render(width: number): readonly string[] {
334
394
  const height = Math.max(1, Math.floor(this.host.ui.terminal.rows * 0.33));
335
395
  return renderCodexResetFireworks(width, height, this.#frame, this.event);
@@ -17,8 +17,10 @@ import {
17
17
  truncateToWidth,
18
18
  visibleWidth,
19
19
  } from "../index";
20
- import type { ComposerShape } from "./composer-shape-registry";
20
+ import { type ComposerShape, getComposerShapeOptions } from "./composer-shape-registry";
21
21
  import { theme } from "../theme/theme";
22
+ import type { NativeNode } from "../native/node";
23
+ import { col, node, span, text } from "../native/describe";
22
24
 
23
25
  /**
24
26
  * Real status renderer the preview borrows rows from — structurally satisfied
@@ -120,6 +122,7 @@ export function renderComposerShapePreview(
120
122
  export class ComposerShapePreview implements Component {
121
123
  #shape: ComposerShape;
122
124
  #options: ComposerShapePreviewOptions;
125
+ #native: { shape: ComposerShape; node: NativeNode } | undefined;
123
126
 
124
127
  constructor(initialValue: ComposerShape = "band", options: ComposerShapePreviewOptions = {}) {
125
128
  this.#shape = initialValue;
@@ -132,6 +135,28 @@ export class ComposerShapePreview implements Component {
132
135
  this.#options.requestRender?.();
133
136
  }
134
137
 
138
+ /**
139
+ * A TSP terminal draws the composer itself, so the shape's glyph chrome has
140
+ * no native twin: the preview names the shape over a read-only prompt.
141
+ */
142
+ describe(): NativeNode {
143
+ const memo = this.#native;
144
+ if (memo?.shape === this.#shape) return memo.node;
145
+ const option = getComposerShapeOptions().find(candidate => candidate.value === this.#shape);
146
+ const caption = [span(option?.label ?? this.#shape, "strong")];
147
+ if (option?.description) caption.push(span(` · ${option.description}`, "muted"));
148
+ const described = col(
149
+ [
150
+ text([span("Preview:", "muted")]),
151
+ node("editor", { placeholder: "Ask anything, edit files, run tools", readonly: true, maxLines: 1 }),
152
+ text(caption, { wrap: "word" }),
153
+ ],
154
+ { role: "omp.preview.composer-shape", gap: "xs" },
155
+ );
156
+ this.#native = { shape: this.#shape, node: described };
157
+ return described;
158
+ }
159
+
135
160
  render(width: number): readonly string[] {
136
161
  const lines = renderComposerShapePreview(this.#shape, width, this.#options.status);
137
162
  return ["", theme.fg("muted", "Preview:"), ...lines];