@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
@@ -30,7 +30,15 @@ import { editorKey, editorKeys } from "../chrome/keybinding-hints";
30
30
  import { formatKeyHint } from "../app-keybindings";
31
31
  import { OverlayPanel } from "../chrome/overlay-box";
32
32
  import { renderSegmentTrack } from "../chrome/segment-track";
33
+ import { Input } from "../components/input";
33
34
  import { MenuSelection, getMenuWindow } from "../components/menu-selection";
35
+ import type { KeyName } from "../key-hint-format";
36
+ import { node, span } from "../native/describe";
37
+ import type { DescribeContext, NativeChild, NativeNode, NativeUiEvent } from "../native/node";
38
+ import { actionHint, hintsRow, overlayCard } from "../native/overlay";
39
+ import { plainText } from "../native/spans";
40
+ import { CLOSE_ACTION, dockedPicker, PICKER_KEY, pickerAction, pickerEvent, pickerQuery } from "../native/picker";
41
+ import type { TspPickerItem } from "@oh-my-pi/pi-wire";
34
42
 
35
43
  /** One segment of a {@link HookSelectorSlider} — a label and an optional
36
44
  * detail line (e.g. the resolved model name) shown beneath the track while
@@ -161,9 +169,27 @@ class OutlinedList extends Container {
161
169
  * disabled-index lookups survive fuzzy filtering and reordering. */
162
170
  type FilteredOption = { option: HookSelectorOption; index: number };
163
171
 
172
+ /** Plain text of an extension-supplied inline-markdown string: markers rendered away, no ANSI. */
173
+ function plainInline(source: string, mdTheme: MarkdownTheme): string {
174
+ return plainText(renderInlineMarkdown(source, mdTheme));
175
+ }
176
+
177
+ const ENTER_KEYS: readonly KeyName[] = ["enter"];
178
+
179
+ interface HookSelectorNativeMemo {
180
+ items: readonly FilteredOption[];
181
+ selected: string | undefined;
182
+ query: string;
183
+ slider: number;
184
+ countdown: NativeNode | undefined;
185
+ node: NativeNode;
186
+ }
187
+
164
188
  export class HookSelectorComponent extends OverlayPanel {
165
189
  #options: HookSelectorOption[];
166
190
  #menu: MenuSelection<FilteredOption>;
191
+ /** The type-to-search field; its value drives `#menu`'s query. */
192
+ #search = Object.assign(new Input(), { prompt: "" });
167
193
  #disabledIndices: Set<number>;
168
194
  #selectionMarker: "radio" | "checkbox" | undefined;
169
195
  #checkedIndices: Set<number>;
@@ -183,6 +209,24 @@ export class HookSelectorComponent extends OverlayPanel {
183
209
  #sliderIndex: number = 0;
184
210
  #sliderComponent: Text | undefined;
185
211
  #lastRenderWidth: number | undefined;
212
+ readonly #detailLines: readonly string[];
213
+ readonly #helpText: string | undefined;
214
+ /** Described option rows by original index; `marker` is the radio state baked into the label. */
215
+ readonly #nativeItems = new Map<number, { marker: boolean; node: NativeNode }>();
216
+ /** Title/detail/hint nodes, fixed for the dialog's lifetime. */
217
+ #nativeStatic: { title: string; detail: NativeNode | undefined; hints: NativeNode } | undefined;
218
+ #nativeMemo: HookSelectorNativeMemo | undefined;
219
+ /** Every option as a picker row, by original index (built once; filtering only changes `order`). */
220
+ #pickerItems: readonly TspPickerItem[] | undefined;
221
+ #pickerMemo:
222
+ | {
223
+ items: readonly FilteredOption[];
224
+ selected: string | undefined;
225
+ query: string;
226
+ cursor: number;
227
+ node: NativeNode;
228
+ }
229
+ | undefined;
186
230
  constructor(
187
231
  title: string,
188
232
  options: HookSelectorOptionInput[],
@@ -190,7 +234,7 @@ export class HookSelectorComponent extends OverlayPanel {
190
234
  onCancel: () => void,
191
235
  opts?: HookSelectorOptions,
192
236
  ) {
193
- super(title.split(/\r?\n/, 1)[0] ?? "");
237
+ super(title.split(/\r?\n/, 1)[0] ?? "", "omp.overlay.hook-select");
194
238
 
195
239
  this.#options = options.map(normalizeHookSelectorOption);
196
240
  this.#disabledIndices = new Set(
@@ -218,6 +262,8 @@ export class HookSelectorComponent extends OverlayPanel {
218
262
  this.#onSelectCallback = onSelect;
219
263
  this.#onCancelCallback = onCancel;
220
264
  this.#baseTitle = this.title;
265
+ this.#detailLines = title.split(/\r?\n/).slice(1);
266
+ this.#helpText = opts?.helpText;
221
267
  this.#onLeftCallback = opts?.onLeft;
222
268
  this.#onRightCallback = opts?.onRight;
223
269
  this.#onExternalEditorCallback = opts?.onExternalEditor;
@@ -536,31 +582,30 @@ export class HookSelectorComponent extends OverlayPanel {
536
582
  this.#menu.query.trim() && total !== this.#options.length
537
583
  ? `${selectedCount}/${total} of ${this.#options.length}`
538
584
  : `${selectedCount}/${total}`;
539
- const suffix = this.#menu.query.trim() ? ` Search: ${this.#menu.query}` : " Type to search";
540
- return theme.fg("dim", ` (${count})${suffix}`);
585
+ if (!this.#menu.query.trim()) return theme.fg("dim", ` (${count}) Type to search`);
586
+ const field = this.#search.render(visibleWidth(this.#search.getValue()) + 1)[0] ?? "";
587
+ return `${theme.fg("dim", ` (${count}) Search: `)}${field}`;
541
588
  }
542
589
 
543
- #setSearchQuery(query: string): void {
544
- this.#menu.setQuery(query, false);
590
+ /** Applies the search field's value to the filter. */
591
+ #syncSearchQuery(): void {
592
+ this.#menu.setQuery(this.#search.getValue(), false);
545
593
  this.#updateList();
546
594
  }
547
595
 
596
+ /** Feeds keys the selector does not bind to the search field. Backspace on an empty query and a leading space bubble. */
548
597
  #handleSearchInput(keyData: string): boolean {
549
598
  if (!this.#isSearchEnabled()) return false;
550
-
551
- if (matchesKey(keyData, "backspace")) {
552
- if (this.#menu.query.length === 0) return false;
553
- const chars = [...this.#menu.query];
554
- chars.pop();
555
- this.#setSearchQuery(chars.join(""));
556
- return true;
599
+ const before = this.#search.getValue();
600
+ if (before.length === 0) {
601
+ if (matchesKey(keyData, "backspace")) return false;
602
+ const printableText = extractPrintableText(keyData);
603
+ if (printableText !== undefined && printableText.trim().length === 0) return false;
557
604
  }
558
-
559
- const printableText = extractPrintableText(keyData);
560
- if (printableText === undefined) return false;
561
- if (this.#menu.query.length === 0 && printableText.trim().length === 0) return false;
562
-
563
- this.#setSearchQuery(this.#menu.query + printableText);
605
+ const cursorBefore = this.#search.getCursor();
606
+ if (!this.#search.handleInput(keyData)) return false;
607
+ if (this.#search.getValue() !== before) this.#syncSearchQuery();
608
+ else if (this.#search.getCursor() !== cursorBefore) this.#updateList();
564
609
  return true;
565
610
  }
566
611
 
@@ -596,10 +641,6 @@ export class HookSelectorComponent extends OverlayPanel {
596
641
  return;
597
642
  }
598
643
 
599
- if (this.#handleSearchInput(keyData)) {
600
- return;
601
- }
602
-
603
644
  if (matchesSelectUp(keyData) || (!this.#isSearchEnabled() && matchesKey(keyData, "k"))) {
604
645
  this.#moveSelection(-1);
605
646
  } else if (matchesSelectDown(keyData) || (!this.#isSearchEnabled() && matchesKey(keyData, "j"))) {
@@ -621,6 +662,8 @@ export class HookSelectorComponent extends OverlayPanel {
621
662
  else this.#onRightCallback?.();
622
663
  } else if (this.#onExternalEditorCallback && matchesAppExternalEditor(keyData)) {
623
664
  this.#onExternalEditorCallback();
665
+ } else {
666
+ this.#handleSearchInput(keyData);
624
667
  }
625
668
  }
626
669
 
@@ -636,4 +679,250 @@ export class HookSelectorComponent extends OverlayPanel {
636
679
  override dispose(): void {
637
680
  this.#countdown?.dispose();
638
681
  }
682
+
683
+ /** The options as a `picker md` sheet: every option by original index, filtering sends only `order`. */
684
+ #describePicker(): NativeNode {
685
+ const items = this.#menu.visibleItems;
686
+ const selected = this.#menu.selectedKey;
687
+ const query = this.#menu.query;
688
+ const cursor = this.#search.getCursor();
689
+ const memo = this.#pickerMemo;
690
+ if (
691
+ memo &&
692
+ memo.items === items &&
693
+ memo.selected === selected &&
694
+ memo.query === query &&
695
+ memo.cursor === cursor
696
+ ) {
697
+ return memo.node;
698
+ }
699
+ const mdTheme = getMarkdownTheme();
700
+ const fixed = this.#describeStatic(mdTheme);
701
+ this.#pickerItems ??= this.#options.map((option, index) => ({
702
+ id: String(index),
703
+ label: plainInline(option.label, mdTheme),
704
+ ...(option.description ? { detail: plainInline(option.description, mdTheme) } : {}),
705
+ ...(this.#isDisabled(index) ? { disabled: true as const } : {}),
706
+ }));
707
+ const checkbox = this.#selectionMarker === "checkbox";
708
+ const result = dockedPicker({
709
+ title: fixed.title,
710
+ ...(this.#detailLines.length > 0 ? { subtitle: plainText(this.#detailLines.join(" ")) } : {}),
711
+ noun: "options",
712
+ size: "md",
713
+ layout: "rows",
714
+ preview: "none",
715
+ ...pickerQuery(this.#isSearchEnabled() || query.length > 0 ? this.#search : null),
716
+ items: this.#pickerItems,
717
+ ...(query.length > 0 ? { order: items.map(filtered => String(filtered.index)) } : {}),
718
+ selected: selected ?? null,
719
+ ...(checkbox ? { current: [...this.#checkedIndices].map(String) } : {}),
720
+ total: this.#options.length,
721
+ empty: "No matching options",
722
+ actions: [pickerAction("confirm", "Select", "enter", { primary: true }), CLOSE_ACTION],
723
+ });
724
+ this.#pickerMemo = { items, selected, query, cursor, node: result };
725
+ return result;
726
+ }
727
+
728
+ /**
729
+ * A plain option list is a picker sheet. Otherwise a card headed by the
730
+ * title (plus the countdown `elapsed` when a timeout runs), the extra title
731
+ * lines, the slider as `tabs`, the options as a `list` keyed by original
732
+ * option index, the search query while typing, and the key hints.
733
+ */
734
+ override describe(cx: DescribeContext): NativeNode {
735
+ if (cx.supports("picker") && !this.#slider && !this.#countdown) return this.#describePicker();
736
+ const items = this.#menu.visibleItems;
737
+ const selected = this.#menu.selectedKey;
738
+ const query = this.#menu.query;
739
+ const countdown = this.#countdown?.describe();
740
+ const memo = this.#nativeMemo;
741
+ if (
742
+ memo &&
743
+ memo.items === items &&
744
+ memo.selected === selected &&
745
+ memo.query === query &&
746
+ memo.slider === this.#sliderIndex &&
747
+ memo.countdown === countdown
748
+ ) {
749
+ return memo.node;
750
+ }
751
+
752
+ const mdTheme = getMarkdownTheme();
753
+ const fixed = this.#describeStatic(mdTheme);
754
+ const children: NativeChild[] = [];
755
+ if (countdown) {
756
+ children.push(
757
+ node("row", { gap: "sm", align: "baseline" }, [node("text", { text: fixed.title }), countdown], "head"),
758
+ );
759
+ }
760
+ if (fixed.detail) children.push(fixed.detail);
761
+ if (this.#slider) children.push(...this.#describeSlider(this.#slider));
762
+
763
+ const selectedItem = this.#menu.selectedItem;
764
+ const rows: NativeNode[] = [];
765
+ for (const filtered of items) {
766
+ rows.push(this.#describeOption(filtered, filtered === selectedItem, mdTheme));
767
+ }
768
+ children.push(
769
+ node(
770
+ "list",
771
+ {
772
+ selected: selectedItem && !this.#menu.isDisabled(selectedItem) ? (selected ?? null) : null,
773
+ filter: query.trim() || undefined,
774
+ empty: "No matching options",
775
+ max: { lines: this.#maxVisible },
776
+ },
777
+ rows,
778
+ "list",
779
+ ),
780
+ );
781
+ if (query) {
782
+ children.push(
783
+ node("text", { spans: [span("Search: ", "dim"), span(query)], wrap: "none" }, undefined, "search"),
784
+ );
785
+ }
786
+ children.push(fixed.hints);
787
+
788
+ const root = overlayCard(this.nativeRole, countdown ? undefined : fixed.title, children);
789
+ this.#nativeMemo = { items, selected, query, slider: this.#sliderIndex, countdown, node: root };
790
+ return root;
791
+ }
792
+
793
+ /** Pointer pick of an option does what highlighting it and pressing Enter does; a tab pick moves the slider. */
794
+ handleNativeEvent(event: NativeUiEvent): void {
795
+ const ev = pickerEvent(event, PICKER_KEY);
796
+ if (ev?.kind === "action") {
797
+ this.#resetCountdown();
798
+ if (ev.act === "close" || ev.act === "cancel") this.#onCancelCallback();
799
+ else if (ev.act === "clear") {
800
+ this.#search.setValue("");
801
+ this.#syncSearchQuery();
802
+ } else if (ev.act === "confirm") {
803
+ const selected = this.#menu.selectedItem;
804
+ if (selected && !this.#menu.isDisabled(selected)) this.#onSelectCallback(selected.option.label);
805
+ }
806
+ return;
807
+ }
808
+ if (ev?.kind === "select") {
809
+ const index = this.#menu.visibleItems.findIndex(filtered => String(filtered.index) === ev.item);
810
+ const target = this.#menu.visibleItems[index];
811
+ if (!target || this.#menu.isDisabled(target)) return;
812
+ this.#resetCountdown();
813
+ this.#menu.setSelectedIndex(index);
814
+ this.#updateList();
815
+ return;
816
+ }
817
+ if (event.type !== "select" && event.type !== "activate") return;
818
+ if (event.key === "slider") {
819
+ const target = Number(event.item);
820
+ if (!Number.isInteger(target)) return;
821
+ this.#resetCountdown();
822
+ this.#moveSlider(target - this.#sliderIndex);
823
+ return;
824
+ }
825
+ if (event.key !== "list" && ev?.kind !== "activate") return;
826
+ const index = this.#menu.visibleItems.findIndex(filtered => String(filtered.index) === event.item);
827
+ const target = this.#menu.visibleItems[index];
828
+ if (!target || this.#menu.isDisabled(target)) return;
829
+ this.#resetCountdown();
830
+ this.#menu.setSelectedIndex(index);
831
+ this.#updateList();
832
+ this.#onSelectCallback(target.option.label);
833
+ }
834
+
835
+ #resetCountdown(): void {
836
+ if (!this.#countdown) return;
837
+ this.#countdown.reset();
838
+ this.#onTimeoutResetCallback?.();
839
+ }
840
+
841
+ #describeStatic(mdTheme: MarkdownTheme): { title: string; detail: NativeNode | undefined; hints: NativeNode } {
842
+ if (this.#nativeStatic) return this.#nativeStatic;
843
+ const detail =
844
+ this.#detailLines.length > 0
845
+ ? node(
846
+ "text",
847
+ { spans: [span(plainText(this.#detailLines.join("\n")), "accent")], wrap: "word" },
848
+ undefined,
849
+ "detail",
850
+ )
851
+ : undefined;
852
+ const hints =
853
+ this.#helpText !== undefined
854
+ ? node("text", { spans: [span(plainText(this.#helpText), "dim")], wrap: "word" }, undefined, "hints")
855
+ : hintsRow([
856
+ actionHint(["tui.select.up", "tui.select.down"], "navigate"),
857
+ { keys: ENTER_KEYS, label: "select" },
858
+ actionHint("tui.select.cancel", "cancel"),
859
+ ]);
860
+ this.#nativeStatic = { title: plainInline(this.#baseTitle, mdTheme), detail, hints };
861
+ return this.#nativeStatic;
862
+ }
863
+
864
+ /** Caption, a `tabs` strip of the segments (active = slider index), and the active segment's detail. */
865
+ #describeSlider(slider: HookSelectorSlider): NativeNode[] {
866
+ const out: NativeNode[] = [];
867
+ if (slider.caption) {
868
+ out.push(node("text", { spans: [span(plainText(slider.caption), "dim")] }, undefined, "slider-caption"));
869
+ }
870
+ out.push(
871
+ node(
872
+ "tabs",
873
+ {
874
+ items: slider.segments.map((segment, i) => ({ id: String(i), label: plainText(segment.label) })),
875
+ active: String(this.#sliderIndex),
876
+ },
877
+ undefined,
878
+ "slider",
879
+ ),
880
+ );
881
+ const detail = slider.segments[this.#sliderIndex]?.detail;
882
+ if (detail) {
883
+ out.push(
884
+ node(
885
+ "text",
886
+ { spans: [span("↳ ", "dim"), span(plainText(detail), "muted")], wrap: "word" },
887
+ undefined,
888
+ "slider-detail",
889
+ ),
890
+ );
891
+ }
892
+ return out;
893
+ }
894
+
895
+ /** One option `item`; radio rows re-describe only when their filled state flips. */
896
+ #describeOption(filtered: FilteredOption, isSelected: boolean, mdTheme: MarkdownTheme): NativeNode {
897
+ const { option, index } = filtered;
898
+ const radio = this.#selectionMarker === "radio" && index < this.#markableCount;
899
+ const marker = radio && isSelected;
900
+ const cached = this.#nativeItems.get(index);
901
+ if (cached && cached.marker === marker) return cached.node;
902
+
903
+ const disabled = this.#isDisabled(index);
904
+ const label = plainInline(option.label, mdTheme);
905
+ let glyph: string | undefined;
906
+ let glyphColor = "dim";
907
+ if (radio) {
908
+ glyph = marker ? theme.radio.selected : theme.radio.unselected;
909
+ if (marker && !disabled) glyphColor = "accent";
910
+ } else if (this.#selectionMarker === "checkbox" && index < this.#markableCount) {
911
+ const checked = this.#checkedIndices.has(index);
912
+ glyph = checked ? theme.checkbox.checked : theme.checkbox.unchecked;
913
+ if (checked && !disabled) glyphColor = "success";
914
+ }
915
+ const described = node(
916
+ "item",
917
+ {
918
+ label: glyph ? [span(`${glyph} `, glyphColor), span(label)] : [span(label)],
919
+ detail: option.description ? [span(plainInline(option.description, mdTheme), "muted")] : undefined,
920
+ disabled: disabled || undefined,
921
+ },
922
+ undefined,
923
+ String(index),
924
+ );
925
+ this.#nativeItems.set(index, { marker, node: described });
926
+ return described;
927
+ }
639
928
  }
@@ -4,6 +4,11 @@ import { Stack } from "../components/layout/stack";
4
4
  import { matchesKey } from "../keys";
5
5
  import { theme } from "../theme/theme";
6
6
  import { truncateToWidth, visibleWidth } from "../utils";
7
+ import type { TspText } from "@oh-my-pi/pi-wire";
8
+ import { node, span, text } from "../native/describe";
9
+ import type { NativeChild, NativeNode } from "../native/node";
10
+ import { overlayCard } from "../native/overlay";
11
+ import { plainText } from "../native/spans";
7
12
 
8
13
  /** A scope row shared by fullscreen hubs, with hub-specific kinds and metadata. */
9
14
  export interface SidebarEntry<TKind extends string> {
@@ -55,6 +60,81 @@ export function moveStripSelection(strip: StripState<StripChip<unknown>>, data:
55
60
  return false;
56
61
  }
57
62
 
63
+ /**
64
+ * Native sidebar: a `list` whose `item`s are keyed by entry id (the `item` of
65
+ * `select`/`activate` events), separators as `rule`s. Scrolling and clipping
66
+ * are the terminal's; `style` supplies icon, annotation and muting.
67
+ */
68
+ export function describeHubSidebar<TEntry extends SidebarEntry<string>>(
69
+ entries: readonly TEntry[],
70
+ selectedId: string,
71
+ style: (entry: TEntry, index: number) => Pick<SidebarStyle, "icon" | "annotation" | "muted">,
72
+ key = "sidebar",
73
+ ): NativeNode {
74
+ const items: NativeNode[] = [];
75
+ for (let i = 0; i < entries.length; i++) {
76
+ const entry = entries[i]!;
77
+ if (entry.kind === "separator") {
78
+ items.push(node("rule", undefined, undefined, entry.id));
79
+ continue;
80
+ }
81
+ const decoration = style(entry, i);
82
+ items.push(
83
+ node(
84
+ "item",
85
+ {
86
+ label: decoration.icon
87
+ ? [
88
+ span(`${plainText(decoration.icon)} `, "muted"),
89
+ span(entry.label, decoration.muted ? "dim" : undefined),
90
+ ]
91
+ : [span(entry.label, decoration.muted ? "dim" : undefined)],
92
+ value: decoration.annotation ? plainText(decoration.annotation) : undefined,
93
+ },
94
+ undefined,
95
+ entry.id,
96
+ ),
97
+ );
98
+ }
99
+ return node("list", { selected: selectedId }, items, key);
100
+ }
101
+
102
+ /**
103
+ * Native footer chip strip: a `tabs` node whose tab ids are the chip indices
104
+ * (`select` events carry the index as `item`), with an optional prefix label.
105
+ */
106
+ export function describeHubChips(strip: StripState<StripChip<unknown>>, prefix?: TspText, key = "chips"): NativeNode {
107
+ const tabs = node(
108
+ "tabs",
109
+ {
110
+ items: strip.chips.map((chip, index) => ({ id: String(index), label: chip.label })),
111
+ active: String(strip.index),
112
+ },
113
+ undefined,
114
+ "tabs",
115
+ );
116
+ const children: NativeChild[] = prefix === undefined ? [tabs] : [text(prefix), tabs];
117
+ return node("row", { gap: "sm", align: "center" }, children, key);
118
+ }
119
+
120
+ /**
121
+ * Native fullscreen hub: a root card titled `title` over a sidebar/body row and
122
+ * a footer. The split geometry, ring and dividers are the terminal's.
123
+ */
124
+ export function describeHubFrame(
125
+ role: string,
126
+ title: TspText,
127
+ sidebar: NativeChild,
128
+ body: NativeChild,
129
+ footer: NativeChild,
130
+ ): NativeNode {
131
+ return overlayCard(role, title, [
132
+ node("row", { gap: "md", align: "start", grow: 1 }, [sidebar, body], "split"),
133
+ node("rule", undefined, undefined, "divider"),
134
+ footer,
135
+ ]);
136
+ }
137
+
58
138
  /** Persistent fullscreen split frame, sidebar viewport, and footer chip renderer. */
59
139
  export class HubFrame {
60
140
  /** Current sidebar viewport offset, shared with the hub's navigation. */
@@ -0,0 +1,112 @@
1
+ /**
2
+ * `/jobs` output: the async background jobs of a session. ANSI keeps the
3
+ * caller's text report; natively it is one frame whose rows are `agent`
4
+ * nodes for task jobs and a status dot, the label and a live `elapsed` for
5
+ * the others (running first, recent below a hairline).
6
+ */
7
+ import type { TspAgentProps, TspSpan } from "@oh-my-pi/pi-wire";
8
+ import { type Component, Container } from "../tui";
9
+ import { card, elapsed, node, span, text } from "../native/describe";
10
+ import type { DescribeContext, NativeChild, NativeNode } from "../native/node";
11
+
12
+ /** One async job as the session snapshots it. */
13
+ export interface JobsPanelJob {
14
+ readonly id: string;
15
+ readonly type: string;
16
+ readonly status: "running" | "completed" | "failed" | "cancelled";
17
+ readonly label: string;
18
+ readonly startTime: number;
19
+ readonly endTime?: number;
20
+ /** Registry id of the subagent a task job runs. */
21
+ readonly agentId?: string;
22
+ }
23
+
24
+ /** Running and recently settled jobs. */
25
+ export interface JobsPanelSnapshot {
26
+ readonly running: readonly JobsPanelJob[];
27
+ readonly recent: readonly JobsPanelJob[];
28
+ }
29
+
30
+ const AGENT_STATUS: Record<JobsPanelJob["status"], TspAgentProps["status"]> = {
31
+ running: "running",
32
+ completed: "done",
33
+ failed: "failed",
34
+ cancelled: "aborted",
35
+ };
36
+
37
+ const DOT: Record<JobsPanelJob["status"], TspSpan> = {
38
+ running: { t: "●", s: "accent", fx: "pulse" },
39
+ completed: { t: "●", s: "success" },
40
+ failed: { t: "●", s: "error" },
41
+ cancelled: { t: "●", s: "dim" },
42
+ };
43
+
44
+ /** The `/jobs` block: `ansi` renders in the terminal, the snapshot describes the native frame. */
45
+ export class JobsPanel extends Container {
46
+ readonly #snapshot: JobsPanelSnapshot;
47
+ readonly #nowMs: number;
48
+ #native: { agent: boolean; node: NativeNode } | undefined;
49
+
50
+ constructor(snapshot: JobsPanelSnapshot, nowMs: number, ansi: readonly Component[]) {
51
+ super();
52
+ this.#snapshot = snapshot;
53
+ this.#nowMs = nowMs;
54
+ for (const child of ansi) this.addChild(child);
55
+ }
56
+
57
+ override describe(cx: DescribeContext): NativeNode {
58
+ const agent = cx.supports("agent");
59
+ if (this.#native?.agent === agent) return this.#native.node;
60
+ const { running, recent } = this.#snapshot;
61
+ const children: NativeChild[] = [];
62
+ if (running.length === 0 && recent.length === 0) {
63
+ children.push(node("text", { text: "No background jobs", role: "omp.jobs.empty" }, undefined, "empty"));
64
+ }
65
+ for (const job of running) children.push(this.#describeJob(job, agent));
66
+ if (recent.length > 0) {
67
+ children.push(
68
+ node(
69
+ "section",
70
+ { head: [span("Recent", "muted")] },
71
+ recent.map(job => this.#describeJob(job, agent)),
72
+ "recent",
73
+ ),
74
+ );
75
+ }
76
+ const head: TspSpan[] = [span("Background jobs", "strong")];
77
+ if (running.length > 0) head.push(span(` · ${running.length} running`, "muted"));
78
+ const described = card({ role: "omp.jobs", head }, children);
79
+ this.#native = { agent, node: described };
80
+ return described;
81
+ }
82
+
83
+ #describeJob(job: JobsPanelJob, agent: boolean): NativeNode {
84
+ const settled = job.status !== "running";
85
+ const age = Math.max(0, (job.endTime ?? this.#nowMs) - job.startTime);
86
+ if (agent && job.type === "task") {
87
+ return node(
88
+ "agent",
89
+ {
90
+ name: job.agentId ?? job.id,
91
+ agent: "task",
92
+ task: job.label,
93
+ status: AGENT_STATUS[job.status],
94
+ stats: settled ? { took: age } : { age },
95
+ },
96
+ undefined,
97
+ job.id,
98
+ );
99
+ }
100
+ return node(
101
+ "row",
102
+ { role: "omp.jobs.row", gap: "sm", align: "center", title: `${job.id} · ${job.type} · ${job.status}` },
103
+ [
104
+ text([DOT[job.status]], { aria: job.status }),
105
+ text([span(job.label)], { truncate: "end", grow: 1 }),
106
+ text([span(job.type, "dim")]),
107
+ elapsed(age, settled),
108
+ ],
109
+ job.id,
110
+ );
111
+ }
112
+ }