@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
@@ -3,7 +3,10 @@ import { replaceTabs } from "../render/render-utils";
3
3
  import { getMarkdownTheme, theme } from "../theme/theme";
4
4
  import { OverlayPanel } from "../chrome/overlay-box";
5
5
  import { StreamingPanelContent } from "../chrome/streaming-panel";
6
- import { interruptKey } from "../chrome/keybinding-hints";
6
+ import { boundKeys, interruptKey } from "../chrome/keybinding-hints";
7
+ import type { NativeNode } from "../native/node";
8
+ import { col, md, node, span, text } from "../native/describe";
9
+ import { hintsRow, type NativeHint, statusHintsRow } from "../native/overlay";
7
10
 
8
11
  export type OmfgPanelState =
9
12
  | "generating"
@@ -15,6 +18,18 @@ export type OmfgPanelState =
15
18
  | "aborted"
16
19
  | "error";
17
20
 
21
+ /** States with work still in flight (the sheet head shows the live spinner). */
22
+ const LIVE_STATES: Record<OmfgPanelState, boolean> = {
23
+ generating: true,
24
+ validating: true,
25
+ confirming: true,
26
+ saving: true,
27
+ saved: false,
28
+ rejected: false,
29
+ aborted: false,
30
+ error: false,
31
+ };
32
+
18
33
  interface OmfgPanelComponentOptions {
19
34
  complaint: string;
20
35
  tui: TUI;
@@ -28,11 +43,14 @@ export class OmfgPanelComponent extends OverlayPanel {
28
43
  #savedPath: string | undefined;
29
44
  #errorMessage: string | undefined;
30
45
  #closed = false;
46
+ readonly #complaint: string;
31
47
  readonly #content: StreamingPanelContent;
48
+ #native: NativeNode | undefined;
32
49
 
33
50
  constructor(options: OmfgPanelComponentOptions) {
34
- super(`/omfg ${replaceTabs(options.complaint)}`);
51
+ super(`/omfg ${replaceTabs(options.complaint)}`, "omp.overlay.omfg");
35
52
  this.#tui = options.tui;
53
+ this.#complaint = replaceTabs(options.complaint);
36
54
  this.#content = new StreamingPanelContent(() => ({
37
55
  sections: [new Text(theme.fg("muted", replaceTabs(this.#status)), 0, 0), this.#contentComponent()],
38
56
  footer: this.#footerLine(),
@@ -98,7 +116,73 @@ export class OmfgPanelComponent extends OverlayPanel {
98
116
  this.#closed = true;
99
117
  }
100
118
 
119
+ override invalidate(): void {
120
+ this.#native = undefined;
121
+ super.invalidate();
122
+ }
123
+
124
+ override describe(): NativeNode {
125
+ if (this.#native) return this.#native;
126
+ const tone =
127
+ this.#state === "error"
128
+ ? "error"
129
+ : this.#state === "rejected" || this.#state === "aborted"
130
+ ? "warning"
131
+ : undefined;
132
+ // Inline in the dock, styled as a sheet by role: a borderless column headed by the complaint.
133
+ const head = node("row", { role: "omp.sheet.head", gap: "sm", align: "center" }, [
134
+ ...(LIVE_STATES[this.#state] ? [node("spinner", {})] : []),
135
+ text([span("/omfg", "accent"), span(` ${this.#complaint}`)], { truncate: "end", lines: 1 }),
136
+ ]);
137
+ this.#native = col(
138
+ [
139
+ head,
140
+ text([span(replaceTabs(this.#status), "muted")], { wrap: "word" }),
141
+ this.#describeBody(),
142
+ this.#describeFooter(),
143
+ ],
144
+ { role: this.nativeRole, gap: "sm", tone },
145
+ );
146
+ return this.#native;
147
+ }
148
+
149
+ #describeBody(): NativeNode {
150
+ if (this.#state === "error") {
151
+ return text([span(replaceTabs(this.#errorMessage ?? "Unknown error"), "error")], { wrap: "word" });
152
+ }
153
+ const preview = replaceTabs(this.#preview).trim();
154
+ if (preview) return md(preview, { stream: this.#state === "generating" });
155
+ return text([span(`${theme.status.pending} Waiting for candidate rule…`, "dim")]);
156
+ }
157
+
158
+ #describeFooter(): NativeNode {
159
+ const esc: NativeHint = { keys: boundKeys("app.interrupt", ["escape"]).slice(0, 1), label: "dismiss" };
160
+ switch (this.#state) {
161
+ case "generating":
162
+ case "validating":
163
+ case "confirming":
164
+ case "saving":
165
+ return hintsRow([{ ...esc, label: "cancel /omfg" }]);
166
+ case "saved":
167
+ return statusHintsRow(
168
+ [
169
+ span(`${theme.status.success} Registered live`, "success"),
170
+ span(" · ", "muted"),
171
+ span(replaceTabs(this.#savedPath ?? "saved"), "path"),
172
+ ],
173
+ [esc],
174
+ );
175
+ case "rejected":
176
+ return statusHintsRow([span(`${theme.status.warning} Not saved`, "warning")], [esc]);
177
+ case "aborted":
178
+ return statusHintsRow([span(`${theme.status.warning} Cancelled`, "warning")], [esc]);
179
+ case "error":
180
+ return statusHintsRow([span(`${theme.status.error} Error`, "error")], [esc]);
181
+ }
182
+ }
183
+
101
184
  #rebuild(): void {
185
+ this.#native = undefined;
102
186
  this.#content.refresh();
103
187
  this.#tui.requestRender();
104
188
  }
@@ -18,7 +18,11 @@ import { centerLine } from "../utils";
18
18
  import { theme } from "../theme/theme";
19
19
  import { matchesAppInterrupt } from "../keybinding-matchers";
20
20
  import { formatKeyHint } from "../app-keybindings";
21
- import { interruptKey } from "../chrome/keybinding-hints";
21
+ import { boundKeys, interruptKey } from "../chrome/keybinding-hints";
22
+ import type { NativeNode, NativeUiEvent } from "../native/node";
23
+ import { col, node, row, span, text } from "../native/describe";
24
+ import { actionBar, actionButton } from "../native/overlay";
25
+ import { isNativeRendering } from "../native/state";
22
26
 
23
27
  /**
24
28
  * Slice of `InteractiveModeContext` the pause screen drives. Narrow so tests
@@ -120,15 +124,20 @@ export class PauseScreenComponent implements Component, OverlayFocusOwner {
120
124
  #done = Promise.withResolvers<void>();
121
125
  #disposed = false;
122
126
  #startedAt = Date.now();
127
+ #native: NativeNode | undefined;
123
128
 
124
129
  constructor(readonly host: PauseScreenHost) {}
125
130
 
126
131
  /** Start the clock; resolves once the user asks to resume. */
127
132
  run(): Promise<void> {
128
133
  this.#startedAt = agentPauseGate.pausedAt ?? Date.now();
129
- this.#timer ??= setInterval(() => {
130
- if (!this.#disposed) this.host.ui.requestRender();
131
- }, TICK_MS);
134
+ this.#native = undefined;
135
+ // The tick only repaints the hold clock; natively the `elapsed` node counts on its own.
136
+ if (!isNativeRendering()) {
137
+ this.#timer ??= setInterval(() => {
138
+ if (!this.#disposed) this.host.ui.requestRender();
139
+ }, TICK_MS);
140
+ }
132
141
  this.host.ui.requestRender();
133
142
  return this.#done.promise;
134
143
  }
@@ -160,6 +169,48 @@ export class PauseScreenComponent implements Component, OverlayFocusOwner {
160
169
  }
161
170
  }
162
171
 
172
+ invalidate(): void {
173
+ this.#native = undefined;
174
+ }
175
+
176
+ /** A glass sheet titled "Paused" (not the whole pane: the transcript stays visible, frozen). */
177
+ readonly nativeOverlay = { role: "omp.overlay.pause", size: "md", anchor: "center", head: "Paused" } as const;
178
+
179
+ describe(): NativeNode {
180
+ if (this.#native) return this.#native;
181
+ const children: NativeNode[] = [];
182
+ if (this.host.sessionName) children.push(text([span(this.host.sessionName, "strong")]));
183
+ const resumeKey = boundKeys("app.interrupt", ["escape"])[0] ?? "escape";
184
+ children.push(
185
+ text(
186
+ BODY_LINES.map((line, index) => span(index === 0 ? line : `\n${line}`, "muted")),
187
+ { wrap: "word" },
188
+ ),
189
+ row(
190
+ [
191
+ text([span("Paused for", "dim")]),
192
+ node("elapsed", { age: Date.now() - this.#startedAt, format: "clock" }),
193
+ ],
194
+ { gap: "xs", align: "baseline" },
195
+ ),
196
+ actionBar([
197
+ null,
198
+ actionButton("Resume", "resume", {
199
+ keys: resumeKey,
200
+ tone: "accent",
201
+ title: `Resume ${resumeKey} · enter · space`,
202
+ }),
203
+ ]),
204
+ );
205
+ this.#native = col(children, { gap: "md" });
206
+ return this.#native;
207
+ }
208
+
209
+ /** Resume runs what Esc/Enter/Space run. */
210
+ handleNativeEvent(event: NativeUiEvent): void {
211
+ if (event.type === "action" && event.act === "resume" && !this.#disposed) this.#done.resolve();
212
+ }
213
+
163
214
  render(width: number): readonly string[] {
164
215
  const elapsed = Date.now() - this.#startedAt;
165
216
  return renderPauseScreen(
@@ -54,9 +54,16 @@ import { padToWidth } from "../render/utils";
54
54
  import { renderSegmentTrack } from "../chrome/segment-track";
55
55
  import { formatKeyHint, formatKeyHints } from "../app-keybindings";
56
56
  import { editorKey, editorKeys } from "../chrome/keybinding-hints";
57
+ import type { KeyName } from "../key-hint-format";
58
+ import { col, item, keyed, md, node, row as rowNode, span, text } from "../native/describe";
59
+ import { leafKey, type NativeChild, type NativeNode, type NativeUiEvent } from "../native/node";
60
+ import { actionButton, actionHint, hintsRow, itemIndex, type NativeHint, selectList } from "../native/overlay";
61
+ import { getKeybindings } from "../keybindings";
57
62
 
58
63
  /** Title shown in the overlay's top border. */
59
64
  const OVERLAY_TITLE = "Plan Review";
65
+ /** Native sheet title when the plan has no title heading of its own. */
66
+ const NATIVE_TITLE = "Plan review";
60
67
  /** Minimum plan-body rows kept visible even on short terminals. */
61
68
  const MIN_BODY_ROWS = 3;
62
69
  /** Visible rows for the in-overlay annotation editor before it scrolls. */
@@ -107,6 +114,14 @@ interface AnnotationChooser {
107
114
  selected: number;
108
115
  }
109
116
 
117
+ /** Described plan body and Contents entries, rebuilt when sections or annotations change. */
118
+ interface NativePlanContent {
119
+ sections: OverlaySection[];
120
+ rev: number;
121
+ body: NativeNode[];
122
+ toc: NativeNode[];
123
+ }
124
+
110
125
  /** Serializable annotations retained by the plan-review owner between overlays. */
111
126
  export interface PlanReviewAnnotationState {
112
127
  annotations: Array<{
@@ -173,6 +188,10 @@ export class PlanReviewOverlay implements Component {
173
188
  #scrollView: ScrollView;
174
189
  #sections: OverlaySection[] = [];
175
190
  #toc: number[] = [];
191
+ /** Section holding the plan's title heading (dropped from the ToC; the native sheet title), or -1. */
192
+ #titleIndex = -1;
193
+ /** Memoized {@link nativeOverlay} for the current title. */
194
+ #nativeOverlay: { title: string; props: NonNullable<Component["nativeOverlay"]> } | undefined;
176
195
  /** Shallowest level among ToC entries, used to flatten indentation. */
177
196
  #tocBaseLevel = 1;
178
197
  #sectionOffsets: number[] = [];
@@ -223,6 +242,14 @@ export class PlanReviewOverlay implements Component {
223
242
  #editingAnnotation: AnnotationSelection | undefined;
224
243
  #annotationChooser: AnnotationChooser | undefined;
225
244
 
245
+ /** Bumped on every section/annotation change (all of them funnel through {@link #recomputeFeedback}). */
246
+ #annotationRev = 0;
247
+ #nativeContent: NativePlanContent | undefined;
248
+ #nativeSig = "";
249
+ #nativeRoot: NativeNode | undefined;
250
+ /** Content the memoized root was built from. */
251
+ #nativeRootContent: NativePlanContent | undefined;
252
+
226
253
  constructor(
227
254
  planContent: string,
228
255
  options: PlanReviewOverlayOptions,
@@ -443,6 +470,7 @@ export class PlanReviewOverlay implements Component {
443
470
  for (const i of headings) minLevel = Math.min(minLevel, this.#sections[i]!.level);
444
471
  const topLevel = headings.filter(i => this.#sections[i]!.level === minLevel);
445
472
  const titleIndex = topLevel.length === 1 && headings[0] === topLevel[0] ? topLevel[0] : -1;
473
+ this.#titleIndex = titleIndex ?? -1;
446
474
  this.#toc = headings.filter(i => i !== titleIndex);
447
475
  this.#tocBaseLevel = this.#toc.length > 0 ? Math.min(...this.#toc.map(i => this.#sections[i]!.level)) : 1;
448
476
  }
@@ -996,6 +1024,7 @@ export class PlanReviewOverlay implements Component {
996
1024
  }
997
1025
 
998
1026
  #recomputeFeedback(): void {
1027
+ this.#annotationRev++;
999
1028
  this.callbacks.onAnnotationStateChange?.(this.#annotationState());
1000
1029
  const annotated = this.#sections.filter(section => section.annotations.length > 0);
1001
1030
  if (annotated.length === 0 && this.#deleted.length === 0) {
@@ -1351,6 +1380,329 @@ export class PlanReviewOverlay implements Component {
1351
1380
  return [theme.fg("dim", this.#buildHelp())];
1352
1381
  }
1353
1382
 
1383
+ /** The native sheet: large glass, titled by the plan's own title heading (the body never repeats it). */
1384
+ get nativeOverlay(): NonNullable<Component["nativeOverlay"]> {
1385
+ const heading = this.#sections[this.#titleIndex]?.title;
1386
+ const title = heading ? sanitizeStatusText(heading) : NATIVE_TITLE;
1387
+ if (this.#nativeOverlay?.title !== title) {
1388
+ this.#nativeOverlay = {
1389
+ title,
1390
+ props: { role: "omp.overlay.planReview", size: "lg", anchor: "center", head: title },
1391
+ };
1392
+ }
1393
+ return this.#nativeOverlay.props;
1394
+ }
1395
+
1396
+ describe(): NativeNode {
1397
+ // The terminal lays out the sidebar; it is shown whenever the Contents has
1398
+ // enough entries, which is also what Tab cycling consults.
1399
+ this.#sidebarShown = this.#toc.length >= SIDEBAR_MIN_HEADINGS;
1400
+ if (this.#annotating) this.#editor.focused = true;
1401
+ const content = this.#describeContent();
1402
+ const chooser = this.#annotationChooser;
1403
+ const target = this.#annotationTarget;
1404
+ const sig = [
1405
+ this.#focus,
1406
+ this.#tocCursor,
1407
+ this.#selectedIndex,
1408
+ this.#sliderIndex,
1409
+ this.#committed ? `committed:${this.#committedLabel ?? ""}` : "",
1410
+ this.#annotating
1411
+ ? `annotating:${target?.sectionIndex}:${target?.row}:${target?.context}:${this.#editingAnnotation !== undefined}`
1412
+ : "",
1413
+ chooser
1414
+ ? `chooser:${chooser.selected}:${chooser.entries.map(e => `${e.sectionIndex}.${e.annotationIndex}`).join()}`
1415
+ : "",
1416
+ ].join("|");
1417
+ if (this.#nativeRoot && sig === this.#nativeSig && content === this.#nativeRootContent) return this.#nativeRoot;
1418
+
1419
+ const children: NativeChild[] = [];
1420
+ const tools = this.#describeTools();
1421
+ if (tools) children.push(tools);
1422
+ const bodyCol = node(
1423
+ "col",
1424
+ { role: "omp.plan.body", grow: 1, gap: "md", tone: this.#focus === "body" ? "accent" : undefined },
1425
+ content.body,
1426
+ "body",
1427
+ );
1428
+ if (this.#sidebarShown) {
1429
+ const tocSection = this.#toc[this.#tocCursor];
1430
+ const toc = selectList("toc", content.toc, {
1431
+ selected: tocSection === undefined ? null : `h${tocSection}`,
1432
+ role: "omp.plan.toc",
1433
+ tone: this.#focus === "toc" ? "accent" : undefined,
1434
+ });
1435
+ const sidebar = node("col", { max: { w: "32ch" }, shrink: 0 }, [toc], "sidebar");
1436
+ children.push(keyed(rowNode([sidebar, bodyCol], { gap: "lg", align: "start" }), "split"));
1437
+ } else {
1438
+ children.push(bodyCol);
1439
+ }
1440
+ if (this.#promptTitle) {
1441
+ children.push(keyed(text([span(this.#promptTitle, "muted")], { role: "omp.plan.prompt" }), "prompt"));
1442
+ }
1443
+ if (this.#committed) {
1444
+ const label = this.#committedLabel ? `${this.#committedLabel} — submitting…` : "Submitting…";
1445
+ children.push(node("spinner", { label: [span(label, "accent strong")] }, undefined, "options"));
1446
+ children.push(
1447
+ keyed(
1448
+ text([span("Applying your selection — this can take a moment while context is prepared.", "dim")], {
1449
+ wrap: "word",
1450
+ }),
1451
+ "footer",
1452
+ ),
1453
+ );
1454
+ } else {
1455
+ children.push(...this.#describeSlider());
1456
+ // The decision bar: Tern lays the list out as buttons, the first one the hero.
1457
+ const optionItems = this.#options.map((label, i) =>
1458
+ item(`o${i}`, {
1459
+ label,
1460
+ disabled: this.#disabled.has(i) || undefined,
1461
+ hint: i === this.#selectedIndex ? ["enter"] : undefined,
1462
+ }),
1463
+ );
1464
+ children.push(
1465
+ selectList("options", optionItems, {
1466
+ selected: this.#selectedIndex >= 0 ? `o${this.#selectedIndex}` : null,
1467
+ role: "omp.plan.options",
1468
+ tone: this.#focus === "actions" ? "accent" : undefined,
1469
+ }),
1470
+ );
1471
+ children.push(...this.#describeFooter());
1472
+ }
1473
+
1474
+ this.#nativeSig = sig;
1475
+ this.#nativeRootContent = content;
1476
+ // The sheet is the frame: a borderless column, titled by `nativeOverlay`.
1477
+ this.#nativeRoot = col(children, { gap: "md" });
1478
+ return this.#nativeRoot;
1479
+ }
1480
+
1481
+ /** Head tools: Copy `c` and the external editor, mirroring their keys. */
1482
+ #describeTools(): NativeNode | undefined {
1483
+ if (this.#committed) return undefined;
1484
+ const buttons: NativeNode[] = [];
1485
+ if (this.callbacks.onCopyPlan)
1486
+ buttons.push(actionButton("Copy", "copyPlan", { keys: "c", title: "Copy plan c" }));
1487
+ // Unbound: the button still mirrors the callback, without a keycap.
1488
+ const editorKeyId = getKeybindings().getKeys("app.editor.external")[0];
1489
+ if (this.callbacks.onExternalEditor) {
1490
+ buttons.push(actionButton("Edit in $EDITOR", "externalEditor", editorKeyId ? { keys: editorKeyId } : {}));
1491
+ }
1492
+ if (buttons.length === 0) return undefined;
1493
+ return node("row", { role: "omp.plan.tools", gap: "sm", align: "center", justify: "end" }, buttons, "tools");
1494
+ }
1495
+
1496
+ handleNativeEvent(event: NativeUiEvent): void {
1497
+ if (this.#committed) return;
1498
+ if (event.type === "action") {
1499
+ // Head tools run exactly what `c` and the external-editor key run.
1500
+ if (this.#annotating || this.#annotationChooser) return;
1501
+ if (event.act === "copyPlan" && this.callbacks.onCopyPlan) {
1502
+ void this.callbacks.onCopyPlan(joinPlanSections(this.#sections));
1503
+ } else if (event.act === "externalEditor") {
1504
+ this.callbacks.onExternalEditor?.();
1505
+ }
1506
+ return;
1507
+ }
1508
+ if (event.type !== "select" && event.type !== "activate") return;
1509
+ const activate = event.type === "activate";
1510
+ switch (leafKey(event.key)) {
1511
+ case "toc": {
1512
+ const pos = this.#toc.indexOf(itemIndex(event.item, "h"));
1513
+ if (pos < 0) return;
1514
+ this.#focus = "toc";
1515
+ this.#tocCursor = pos;
1516
+ this.#scrubBodyToToc();
1517
+ if (activate) this.#setFocus("body");
1518
+ return;
1519
+ }
1520
+ case "options": {
1521
+ const index = itemIndex(event.item, "o");
1522
+ if (index < 0 || index >= this.#options.length || this.#disabled.has(index)) return;
1523
+ this.#focus = "actions";
1524
+ this.#selectedIndex = index;
1525
+ if (activate) this.#confirmSelection();
1526
+ return;
1527
+ }
1528
+ case "tabs": {
1529
+ const index = itemIndex(event.item, "t");
1530
+ if (index >= 0) this.#moveSlider(index - this.#sliderIndex);
1531
+ return;
1532
+ }
1533
+ case "chooser": {
1534
+ const chooser = this.#annotationChooser;
1535
+ const index = itemIndex(event.item, "c");
1536
+ if (!chooser || index < 0 || index >= chooser.entries.length) return;
1537
+ chooser.selected = index;
1538
+ if (activate) {
1539
+ this.#annotationChooser = undefined;
1540
+ this.#startExistingAnnotation(chooser.entries[index]!);
1541
+ }
1542
+ return;
1543
+ }
1544
+ }
1545
+ }
1546
+
1547
+ /** Plan body (one `md` per section, annotation callouts after it) and Contents items. */
1548
+ #describeContent(): NativePlanContent {
1549
+ const cached = this.#nativeContent;
1550
+ if (cached && cached.sections === this.#sections && cached.rev === this.#annotationRev) return cached;
1551
+ const body: NativeNode[] = [];
1552
+ for (const [sectionIndex, section] of this.#sections.entries()) {
1553
+ const key = `s${sectionIndex}`;
1554
+ // The title heading is the sheet's title; the body keeps only what follows it.
1555
+ const newline = section.raw.indexOf("\n");
1556
+ const source =
1557
+ sectionIndex !== this.#titleIndex ? section.raw : newline < 0 ? "" : section.raw.slice(newline + 1);
1558
+ const hasText = sectionIndex !== this.#titleIndex || source.trim() !== "";
1559
+ if (section.annotations.length === 0) {
1560
+ if (hasText) body.push(keyed(md(source), key));
1561
+ continue;
1562
+ }
1563
+ const children: NativeNode[] = hasText ? [keyed(md(source), "md")] : [];
1564
+ for (const [n, annotation] of section.annotations.entries()) {
1565
+ // A margin note: the anchored line quoted, then the note, on one ink-pen bar.
1566
+ const note: NativeNode[] = [];
1567
+ if (annotation.target.kind === "line") {
1568
+ note.push(text([span(annotation.target.context, "muted")], { truncate: "end", lines: 1 }));
1569
+ }
1570
+ note.push(text(sanitizeText(annotation.note), { wrap: "word" }));
1571
+ children.push(node("col", { role: "omp.plan.note", gap: "xs" }, note, `n${n}`));
1572
+ }
1573
+ body.push(keyed(col(children, { gap: "sm" }), key));
1574
+ }
1575
+ const toc = this.#toc.map(sectionIndex => {
1576
+ const section = this.#sections[sectionIndex]!;
1577
+ const count = section.annotations.length;
1578
+ return item(`h${sectionIndex}`, {
1579
+ label: section.title || "(untitled)",
1580
+ value: count > 0 ? [span(`✎${count}`, "warning")] : undefined,
1581
+ role: `omp.plan.toc.depth${section.level - this.#tocBaseLevel}`,
1582
+ });
1583
+ });
1584
+ this.#nativeContent = { sections: this.#sections, rev: this.#annotationRev, body, toc };
1585
+ return this.#nativeContent;
1586
+ }
1587
+
1588
+ #describeSlider(): NativeNode[] {
1589
+ const slider = this.#slider;
1590
+ if (!slider) return [];
1591
+ const tabs = node(
1592
+ "tabs",
1593
+ {
1594
+ items: slider.segments.map((segment, i) => ({ id: `t${i}`, label: segment.label })),
1595
+ active: `t${this.#sliderIndex}`,
1596
+ actions: { click: "select" },
1597
+ role: "omp.plan.strategy",
1598
+ },
1599
+ undefined,
1600
+ "tabs",
1601
+ );
1602
+ const track: NativeChild[] = slider.caption ? [text([span(slider.caption, "dim")]), tabs] : [tabs];
1603
+ const out = [keyed(rowNode(track, { gap: "sm", align: "center" }), "slider")];
1604
+ const detail = slider.segments[this.#sliderIndex]?.detail;
1605
+ if (detail) out.push(keyed(text([span("↳ ", "dim"), span(detail, "muted")], { wrap: "word" }), "sliderDetail"));
1606
+ return out;
1607
+ }
1608
+
1609
+ #describeFooter(): NativeChild[] {
1610
+ const chooser = this.#annotationChooser;
1611
+ if (chooser) {
1612
+ const entries = chooser.entries.map((selection, i) => {
1613
+ const section = this.#sections[selection.sectionIndex];
1614
+ const annotation = section?.annotations[selection.annotationIndex];
1615
+ const where = annotation?.target.kind === "line" ? `line ${annotation.target.context}` : "section";
1616
+ return item(`c${i}`, {
1617
+ label: sanitizeStatusText(annotation?.note ?? ""),
1618
+ detail: `${sanitizeStatusText(section?.title || "Plan preamble")} · ${where}`,
1619
+ });
1620
+ });
1621
+ return [
1622
+ keyed(text([span("Edit annotation", "accent strong")]), "chooserHead"),
1623
+ selectList("chooser", entries, { selected: `c${chooser.selected}` }),
1624
+ hintsRow(
1625
+ [
1626
+ actionHint(["tui.select.up", "tui.select.down"], "choose"),
1627
+ { keys: ["enter"], label: "edit" },
1628
+ actionHint("tui.select.cancel", "cancel"),
1629
+ ],
1630
+ "chooserHints",
1631
+ ),
1632
+ ];
1633
+ }
1634
+ if (this.#annotating) {
1635
+ const target = this.#annotationTarget;
1636
+ const title = sanitizeStatusText(this.#sections[target?.sectionIndex ?? -1]?.title || "Plan preamble");
1637
+ const location = target?.row === null ? `‹${title}›` : `‹${title}› · ${target?.context ?? ""}`;
1638
+ const hints: (NativeHint | undefined)[] = [
1639
+ actionHint("tui.input.submit", "save"),
1640
+ actionHint("tui.input.newLine", "newline"),
1641
+ actionHint("tui.select.cancel", "cancel"),
1642
+ ];
1643
+ if (this.#editingAnnotation) hints.push({ keys: [], label: "empty deletes" });
1644
+ if (this.#externalEditorLabel) hints.push({ keys: [], label: `${this.#externalEditorLabel} editor` });
1645
+ // The feedback field, inline under the plan: its caption names the anchor, omp's editor holds the text.
1646
+ return [
1647
+ node(
1648
+ "col",
1649
+ { role: "omp.plan.feedback", gap: "xs" },
1650
+ [
1651
+ keyed(
1652
+ text([span("Note on ", "muted"), span(location, "accent")], { truncate: "end" }),
1653
+ "annotateHead",
1654
+ ),
1655
+ this.#editor,
1656
+ hintsRow(hints, "annotateHints"),
1657
+ ],
1658
+ "feedback",
1659
+ ),
1660
+ ];
1661
+ }
1662
+ return [hintsRow(this.#nativeHelpHints())];
1663
+ }
1664
+
1665
+ /** {@link #buildHelp} as keycap hints. */
1666
+ #nativeHelpHints(): (NativeHint | undefined)[] {
1667
+ const upDown = (label: string) => actionHint(["tui.select.up", "tui.select.down"], label);
1668
+ const key = (keys: KeyName | readonly KeyName[], label: string): NativeHint => ({
1669
+ keys: typeof keys === "string" ? [keys] : keys,
1670
+ label,
1671
+ });
1672
+ const hints: (NativeHint | undefined)[] = [];
1673
+ switch (this.#focus) {
1674
+ case "actions":
1675
+ hints.push(upDown("select"), key("enter", "confirm"));
1676
+ if (this.#slider) hints.push(key(["left", "right"], "model"));
1677
+ break;
1678
+ case "toc":
1679
+ hints.push(
1680
+ upDown("section"),
1681
+ key("enter", "open"),
1682
+ key("a", "annotate"),
1683
+ key("e", "edit"),
1684
+ key("d", "delete"),
1685
+ key("u", "undo"),
1686
+ );
1687
+ break;
1688
+ case "body":
1689
+ hints.push(
1690
+ upDown("scroll"),
1691
+ key("shift", "faster"),
1692
+ key(["pageUp", "pageDown"], "page"),
1693
+ key(["g", "shift+g"], "ends"),
1694
+ key("a", "annotate"),
1695
+ key("e", "edit"),
1696
+ key("u", "undo"),
1697
+ );
1698
+ break;
1699
+ }
1700
+ // Copy and the external editor are head buttons with their keycaps; the hints skip them.
1701
+ hints.push(key("tab", "regions"));
1702
+ hints.push(this.#helpSuffix ? { keys: [], label: this.#helpSuffix } : actionHint("tui.select.cancel", "cancel"));
1703
+ return hints;
1704
+ }
1705
+
1354
1706
  render(width: number): readonly string[] {
1355
1707
  const termHeight = process.stdout.rows || 40;
1356
1708
  const sidebarShown = this.#sidebarVisible(width);
@@ -2,6 +2,10 @@ import { type Component, CURSOR_MARKER, type Focusable, Input, truncateToWidth,
2
2
  import { theme } from "../theme/theme";
3
3
  import { OverlayPanel, PanelRows } from "../chrome/overlay-box";
4
4
  import { editorKey } from "../chrome/keybinding-hints";
5
+ import { col, keyed, span, text } from "../native/describe";
6
+ import type { NativeChild, NativeNode, NativeUiEvent } from "../native/node";
7
+ import { actionBar, actionButton } from "../native/overlay";
8
+ import { getKeybindings } from "../keybindings";
5
9
 
6
10
  /** A confirmed destination chosen from {@link PlanSaveOverlay}. */
7
11
  export interface PlanSaveOverlayResult {
@@ -16,6 +20,8 @@ export class PlanSaveOverlay implements Component, Focusable {
16
20
  #focused = false;
17
21
  readonly #panel: OverlayPanel;
18
22
  readonly #body: PanelRows;
23
+ #nativeSig = "";
24
+ #nativeRoot: NativeNode | undefined;
19
25
 
20
26
  constructor(suggestedPath: string, done: (result: PlanSaveOverlayResult | undefined) => void) {
21
27
  this.#suggestedPath = suggestedPath;
@@ -64,6 +70,45 @@ export class PlanSaveOverlay implements Component, Focusable {
64
70
  return this.#panel.render(width);
65
71
  }
66
72
 
73
+ /** A small glass sheet titled "Save and quit". */
74
+ readonly nativeOverlay = { role: "omp.overlay.planSave", size: "sm", head: "Save and quit" } as const;
75
+
76
+ describe(): NativeNode {
77
+ this.#input.focused = this.#focused;
78
+ const showDefault = this.#input.getValue().length === 0;
79
+ const bindings = getKeybindings();
80
+ const submitKey = bindings.getKeys("tui.input.submit")[0];
81
+ const cancelKey = bindings.getKeys("tui.select.cancel")[0];
82
+ const sig = `${showDefault}|${this.#suggestedPath}|${submitKey}|${cancelKey}`;
83
+ if (this.#nativeRoot && sig === this.#nativeSig) return this.#nativeRoot;
84
+ const children: NativeChild[] = [this.#input];
85
+ if (showDefault) {
86
+ children.push(
87
+ keyed(
88
+ text([span("Empty saves to ", "dim"), span(this.#suggestedPath, "path")], { truncate: "middle" }),
89
+ "default",
90
+ ),
91
+ );
92
+ }
93
+ children.push(
94
+ actionBar([
95
+ null,
96
+ actionButton("Cancel", "cancel", cancelKey ? { keys: cancelKey } : {}),
97
+ actionButton("Save and quit", "save", { tone: "accent", ...(submitKey ? { keys: submitKey } : {}) }),
98
+ ]),
99
+ );
100
+ this.#nativeSig = sig;
101
+ this.#nativeRoot = col(children, { gap: "md" });
102
+ return this.#nativeRoot;
103
+ }
104
+
105
+ /** Save and Cancel run what the submit and cancel keys run. */
106
+ handleNativeEvent(event: NativeUiEvent): void {
107
+ if (event.type !== "action") return;
108
+ if (event.act === "save") this.#input.onSubmit?.(this.#input.getValue());
109
+ else if (event.act === "cancel") this.#input.onEscape?.();
110
+ }
111
+
67
112
  #renderInput(width: number): string {
68
113
  if (this.#input.getValue().length > 0) return this.#input.render(width)[0] ?? "";
69
114
  const prompt = this.#input.prompt;