@oh-my-pi/pi-tui 18.4.3 → 18.4.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (447) hide show
  1. package/CHANGELOG.md +36 -0
  2. package/dist/types/apps/cleanse-board.d.ts +17 -0
  3. package/dist/types/apps/git/help.d.ts +16 -0
  4. package/dist/types/apps/git/sidebar.d.ts +18 -0
  5. package/dist/types/apps/live-visualizer.d.ts +10 -0
  6. package/dist/types/apps/ps-top.d.ts +15 -0
  7. package/dist/types/apps/standalone-picker.d.ts +3 -0
  8. package/dist/types/autocomplete.d.ts +8 -0
  9. package/dist/types/chat/advisor-message.d.ts +12 -2
  10. package/dist/types/chat/assistant-message.d.ts +20 -0
  11. package/dist/types/chat/bash-execution.d.ts +9 -0
  12. package/dist/types/chat/chat-transcript-builder.d.ts +6 -1
  13. package/dist/types/chat/collab-prompt-message.d.ts +4 -0
  14. package/dist/types/chat/compaction-summary-message.d.ts +15 -0
  15. package/dist/types/chat/eval-execution.d.ts +8 -0
  16. package/dist/types/chat/execution-shared.d.ts +58 -0
  17. package/dist/types/chat/late-diagnostics-message.d.ts +15 -1
  18. package/dist/types/chat/read-tool-group.d.ts +15 -0
  19. package/dist/types/chat/skill-message.d.ts +9 -0
  20. package/dist/types/chat/stripped-tool-calls-placeholder.d.ts +3 -0
  21. package/dist/types/chat/todo-reminder.d.ts +4 -1
  22. package/dist/types/chat/tool-execution.d.ts +25 -1
  23. package/dist/types/chat/transcript-actions.d.ts +37 -0
  24. package/dist/types/chat/transcript-entry.d.ts +3 -1
  25. package/dist/types/chat/user-message.d.ts +25 -0
  26. package/dist/types/chrome/collab-qrcode.d.ts +7 -0
  27. package/dist/types/chrome/context-thresholds.d.ts +11 -0
  28. package/dist/types/chrome/countdown-timer.d.ts +9 -3
  29. package/dist/types/chrome/diff.d.ts +12 -0
  30. package/dist/types/chrome/dynamic-border.d.ts +3 -0
  31. package/dist/types/chrome/format.d.ts +6 -0
  32. package/dist/types/chrome/message-divider.d.ts +13 -0
  33. package/dist/types/chrome/message-frame.d.ts +10 -0
  34. package/dist/types/chrome/message-notice.d.ts +23 -0
  35. package/dist/types/chrome/overlay-box.d.ts +11 -2
  36. package/dist/types/chrome/qrcode.d.ts +7 -0
  37. package/dist/types/chrome/segment-track.d.ts +7 -0
  38. package/dist/types/chrome/selector-helpers.d.ts +0 -6
  39. package/dist/types/chrome/status-notice.d.ts +14 -0
  40. package/dist/types/chrome/streaming-panel.d.ts +3 -0
  41. package/dist/types/chrome/tool-activity.d.ts +3 -0
  42. package/dist/types/chrome/transcript-container.d.ts +13 -0
  43. package/dist/types/chrome/transcript-status.d.ts +15 -3
  44. package/dist/types/components/box.d.ts +7 -0
  45. package/dist/types/components/disclosure.d.ts +9 -0
  46. package/dist/types/components/editor.d.ts +37 -0
  47. package/dist/types/components/form.d.ts +14 -0
  48. package/dist/types/components/image.d.ts +7 -0
  49. package/dist/types/components/input.d.ts +20 -1
  50. package/dist/types/components/key-value-list.d.ts +3 -0
  51. package/dist/types/components/layout/geometry.d.ts +16 -0
  52. package/dist/types/components/layout/row.d.ts +7 -0
  53. package/dist/types/components/layout/split-pane.d.ts +8 -0
  54. package/dist/types/components/layout/stack.d.ts +6 -0
  55. package/dist/types/components/loader.d.ts +48 -0
  56. package/dist/types/components/markdown.d.ts +8 -0
  57. package/dist/types/components/metric.d.ts +7 -0
  58. package/dist/types/components/progress-bar.d.ts +3 -0
  59. package/dist/types/components/scroll-view.d.ts +6 -0
  60. package/dist/types/components/section.d.ts +3 -0
  61. package/dist/types/components/select-list.d.ts +33 -0
  62. package/dist/types/components/settings-list.d.ts +68 -0
  63. package/dist/types/components/spacer.d.ts +4 -0
  64. package/dist/types/components/tab-bar.d.ts +9 -0
  65. package/dist/types/components/table.d.ts +7 -0
  66. package/dist/types/components/text.d.ts +6 -0
  67. package/dist/types/components/tree-view.d.ts +9 -0
  68. package/dist/types/components/truncated-text.d.ts +3 -0
  69. package/dist/types/components/wizard-step.d.ts +6 -0
  70. package/dist/types/hotkeys-markdown.d.ts +24 -0
  71. package/dist/types/index.d.ts +5 -0
  72. package/dist/types/latex-block.d.ts +7 -0
  73. package/dist/types/native/apply.d.ts +39 -0
  74. package/dist/types/native/backend.d.ts +94 -0
  75. package/dist/types/native/blobs.d.ts +29 -0
  76. package/dist/types/native/describe.d.ts +56 -0
  77. package/dist/types/native/encode.d.ts +50 -0
  78. package/dist/types/native/icons.d.ts +18 -0
  79. package/dist/types/native/memo.d.ts +27 -0
  80. package/dist/types/native/node.d.ts +107 -0
  81. package/dist/types/native/overlay.d.ts +64 -0
  82. package/dist/types/native/picker.d.ts +124 -0
  83. package/dist/types/native/reconcile.d.ts +41 -0
  84. package/dist/types/native/settle.d.ts +11 -0
  85. package/dist/types/native/spans.d.ts +21 -0
  86. package/dist/types/native/state.d.ts +6 -0
  87. package/dist/types/native/tone.d.ts +24 -0
  88. package/dist/types/overlays/advisor-config.d.ts +20 -0
  89. package/dist/types/overlays/agent-hub-renderer.d.ts +27 -11
  90. package/dist/types/overlays/agent-hub.d.ts +10 -0
  91. package/dist/types/overlays/agent-transcript-viewer.d.ts +7 -0
  92. package/dist/types/overlays/agents-hub.d.ts +8 -0
  93. package/dist/types/overlays/annotation-overlay.d.ts +3 -0
  94. package/dist/types/overlays/ask-dialog.d.ts +23 -0
  95. package/dist/types/overlays/bordered-loader.d.ts +3 -0
  96. package/dist/types/overlays/btw-history-panel.d.ts +9 -0
  97. package/dist/types/overlays/btw-panel.d.ts +3 -0
  98. package/dist/types/overlays/cleanse-panel.d.ts +5 -2
  99. package/dist/types/overlays/composer-shape-preview.d.ts +7 -1
  100. package/dist/types/overlays/copy-selector.d.ts +38 -0
  101. package/dist/types/overlays/error-banner.d.ts +9 -0
  102. package/dist/types/overlays/extensions/extension-list.d.ts +1 -0
  103. package/dist/types/overlays/history-search.d.ts +18 -0
  104. package/dist/types/overlays/hook-editor.d.ts +28 -1
  105. package/dist/types/overlays/hook-input.d.ts +3 -0
  106. package/dist/types/overlays/hook-selector.d.ts +10 -0
  107. package/dist/types/overlays/hub-frame.d.ts +18 -0
  108. package/dist/types/overlays/jobs-panel.d.ts +24 -0
  109. package/dist/types/overlays/login-dialog.d.ts +10 -0
  110. package/dist/types/overlays/logout-account-selector.d.ts +4 -0
  111. package/dist/types/overlays/mcp-add-wizard.d.ts +4 -0
  112. package/dist/types/overlays/model-browser.d.ts +69 -0
  113. package/dist/types/overlays/model-hub.d.ts +7 -0
  114. package/dist/types/overlays/model-picker.d.ts +7 -0
  115. package/dist/types/overlays/move-overlay.d.ts +3 -0
  116. package/dist/types/overlays/oauth-selector.d.ts +4 -0
  117. package/dist/types/overlays/omfg-panel.d.ts +3 -0
  118. package/dist/types/overlays/pause-screen.d.ts +12 -0
  119. package/dist/types/overlays/plan-review-overlay.d.ts +5 -0
  120. package/dist/types/overlays/plan-save-overlay.d.ts +10 -0
  121. package/dist/types/overlays/plugin-selector.d.ts +4 -0
  122. package/dist/types/overlays/plugin-settings.d.ts +30 -0
  123. package/dist/types/overlays/queue-mode-selector.d.ts +4 -0
  124. package/dist/types/overlays/reset-usage-selector.d.ts +8 -0
  125. package/dist/types/overlays/rewind-selector.d.ts +5 -0
  126. package/dist/types/overlays/session-info-overlay.d.ts +19 -1
  127. package/dist/types/overlays/session-selector.d.ts +64 -1
  128. package/dist/types/overlays/settings-defs.d.ts +14 -0
  129. package/dist/types/overlays/settings-selector.d.ts +21 -0
  130. package/dist/types/overlays/show-images-selector.d.ts +4 -0
  131. package/dist/types/overlays/snapcompact-shape-preview.d.ts +2 -0
  132. package/dist/types/overlays/stats-notice.d.ts +8 -0
  133. package/dist/types/overlays/theme-selector.d.ts +11 -0
  134. package/dist/types/overlays/thinking-selector.d.ts +4 -0
  135. package/dist/types/overlays/tree-selector.d.ts +60 -2
  136. package/dist/types/overlays/usage-dashboard.d.ts +26 -3
  137. package/dist/types/overlays/usage-row.d.ts +36 -2
  138. package/dist/types/prompt/attachment-chips.d.ts +8 -0
  139. package/dist/types/prompt/composer-attachments.d.ts +15 -3
  140. package/dist/types/prompt/composer.d.ts +18 -1
  141. package/dist/types/prompt/custom-editor.d.ts +48 -2
  142. package/dist/types/prompt/editor-top-gap.d.ts +2 -0
  143. package/dist/types/prompt/macos-spelling.d.ts +6 -0
  144. package/dist/types/prompt/magic-keywords.d.ts +9 -0
  145. package/dist/types/prompt/queued-messages.d.ts +23 -0
  146. package/dist/types/prompt/skill-tokens.d.ts +4 -2
  147. package/dist/types/prompt/welcome.d.ts +16 -0
  148. package/dist/types/render/code-cell.d.ts +10 -0
  149. package/dist/types/render/file-list.d.ts +8 -0
  150. package/dist/types/render/hyperlink.d.ts +14 -0
  151. package/dist/types/render/output-block.d.ts +43 -0
  152. package/dist/types/render/output-pane.d.ts +17 -0
  153. package/dist/types/render/tool-card.d.ts +8 -0
  154. package/dist/types/render/tree-list.d.ts +8 -0
  155. package/dist/types/render/width-aware-text.d.ts +7 -0
  156. package/dist/types/setup/scenes/outro.d.ts +7 -0
  157. package/dist/types/setup/scenes/sign-in.d.ts +7 -0
  158. package/dist/types/setup/scenes/splash.d.ts +8 -0
  159. package/dist/types/setup/scenes/types.d.ts +6 -1
  160. package/dist/types/setup/wizard-overlay.d.ts +9 -0
  161. package/dist/types/status-line/component.d.ts +49 -5
  162. package/dist/types/status-line/context-usage.d.ts +20 -0
  163. package/dist/types/status-line/footer.d.ts +8 -0
  164. package/dist/types/status-line/metrics.d.ts +1 -2
  165. package/dist/types/status-line/segments.d.ts +10 -1
  166. package/dist/types/status-line/types.d.ts +40 -1
  167. package/dist/types/terminal-capabilities.d.ts +1 -1
  168. package/dist/types/terminal.d.ts +27 -0
  169. package/dist/types/theme/defaults/index.d.ts +100 -0
  170. package/dist/types/theme/schema.d.ts +2 -0
  171. package/dist/types/theme/shimmer.d.ts +8 -0
  172. package/dist/types/theme/theme-class.d.ts +2 -0
  173. package/dist/types/theme/theme.d.ts +33 -1
  174. package/dist/types/tools/ask.d.ts +3 -0
  175. package/dist/types/tools/ast-edit.d.ts +10 -1
  176. package/dist/types/tools/ast-grep.d.ts +10 -1
  177. package/dist/types/tools/autoresearch.d.ts +21 -4
  178. package/dist/types/tools/bash.d.ts +30 -1
  179. package/dist/types/tools/cfg-render.d.ts +5 -1
  180. package/dist/types/tools/debug.d.ts +3 -1
  181. package/dist/types/tools/default-renderer.d.ts +6 -1
  182. package/dist/types/tools/edit.d.ts +14 -1
  183. package/dist/types/tools/eval.d.ts +12 -1
  184. package/dist/types/tools/fetch.d.ts +16 -1
  185. package/dist/types/tools/find.d.ts +15 -1
  186. package/dist/types/tools/github.d.ts +3 -0
  187. package/dist/types/tools/glob.d.ts +14 -1
  188. package/dist/types/tools/goal.d.ts +3 -1
  189. package/dist/types/tools/grep.d.ts +14 -1
  190. package/dist/types/tools/grouped-file-output.d.ts +58 -0
  191. package/dist/types/tools/json-tree.d.ts +20 -0
  192. package/dist/types/tools/lsp.d.ts +3 -10
  193. package/dist/types/tools/mcp.d.ts +16 -1
  194. package/dist/types/tools/memory.d.ts +26 -0
  195. package/dist/types/tools/native-view.d.ts +90 -0
  196. package/dist/types/tools/proc-render.d.ts +7 -1
  197. package/dist/types/tools/read.d.ts +23 -1
  198. package/dist/types/tools/renderer.d.ts +57 -0
  199. package/dist/types/tools/report-tool-issue.d.ts +3 -0
  200. package/dist/types/tools/resolve.d.ts +14 -1
  201. package/dist/types/tools/task.d.ts +3 -0
  202. package/dist/types/tools/think.d.ts +3 -0
  203. package/dist/types/tools/todo.d.ts +12 -0
  204. package/dist/types/tools/vibe.d.ts +3 -1
  205. package/dist/types/tools/wait.d.ts +3 -0
  206. package/dist/types/tools/web-search.d.ts +8 -0
  207. package/dist/types/tools/write.d.ts +14 -1
  208. package/dist/types/tools/xdev.d.ts +13 -0
  209. package/dist/types/tui.d.ts +49 -0
  210. package/package.json +9 -9
  211. package/src/apps/autoresearch-dashboard.ts +362 -4
  212. package/src/apps/cleanse-board.ts +219 -1
  213. package/src/apps/cleanse-picker.ts +9 -1
  214. package/src/apps/debug/log-viewer.ts +15 -15
  215. package/src/apps/debug/protocol-probe.ts +2 -20
  216. package/src/apps/git/avatar.ts +2 -22
  217. package/src/apps/git/git-tui.ts +406 -59
  218. package/src/apps/git/help.ts +130 -0
  219. package/src/apps/git/sidebar.ts +491 -0
  220. package/src/apps/live-visualizer.ts +65 -0
  221. package/src/apps/ps-top.ts +448 -17
  222. package/src/apps/session-picker.ts +1 -0
  223. package/src/apps/setup-model-picker.ts +5 -1
  224. package/src/apps/standalone-picker.ts +55 -1
  225. package/src/autocomplete.ts +44 -1
  226. package/src/chat/advisor-message.ts +72 -3
  227. package/src/chat/assistant-message.ts +346 -11
  228. package/src/chat/background-tan-message.ts +4 -4
  229. package/src/chat/bash-execution.ts +81 -0
  230. package/src/chat/cache-invalidation-marker.ts +8 -0
  231. package/src/chat/chat-transcript-builder.ts +37 -6
  232. package/src/chat/collab-prompt-message.ts +15 -3
  233. package/src/chat/compaction-summary-message.ts +74 -1
  234. package/src/chat/custom-message.ts +3 -1
  235. package/src/chat/eval-execution.ts +57 -0
  236. package/src/chat/execution-shared.ts +153 -1
  237. package/src/chat/hook-message.ts +1 -0
  238. package/src/chat/late-diagnostics-message.ts +85 -2
  239. package/src/chat/read-tool-group.ts +341 -1
  240. package/src/chat/served-model-marker.ts +8 -0
  241. package/src/chat/skill-message.ts +63 -0
  242. package/src/chat/stripped-tool-calls-placeholder.ts +18 -10
  243. package/src/chat/todo-reminder.ts +34 -4
  244. package/src/chat/tool-execution.ts +385 -10
  245. package/src/chat/transcript-actions.ts +38 -0
  246. package/src/chat/transcript-browser.ts +2 -9
  247. package/src/chat/transcript-entry.ts +10 -1
  248. package/src/chat/transcript-render-helpers.ts +16 -15
  249. package/src/chat/ttsr-notification.ts +38 -1
  250. package/src/chat/user-message.ts +167 -4
  251. package/src/chrome/collab-qrcode.ts +27 -2
  252. package/src/chrome/context-thresholds.ts +34 -0
  253. package/src/chrome/countdown-timer.ts +24 -1
  254. package/src/chrome/diff.ts +60 -1
  255. package/src/chrome/dynamic-border.ts +7 -0
  256. package/src/chrome/format.ts +12 -0
  257. package/src/chrome/message-divider.ts +33 -1
  258. package/src/chrome/message-frame.ts +53 -10
  259. package/src/chrome/message-notice.ts +71 -0
  260. package/src/chrome/overlay-box.ts +45 -2
  261. package/src/chrome/qrcode.ts +28 -0
  262. package/src/chrome/segment-track.ts +26 -0
  263. package/src/chrome/selector-helpers.ts +1 -17
  264. package/src/chrome/status-notice.ts +46 -0
  265. package/src/chrome/streaming-panel.ts +25 -1
  266. package/src/chrome/tool-activity.ts +20 -0
  267. package/src/chrome/transcript-container.ts +37 -0
  268. package/src/chrome/transcript-status.ts +40 -11
  269. package/src/components/box.ts +51 -0
  270. package/src/components/disclosure.ts +33 -0
  271. package/src/components/editor.ts +216 -27
  272. package/src/components/form.ts +93 -9
  273. package/src/components/image.ts +30 -0
  274. package/src/components/input.ts +77 -26
  275. package/src/components/key-value-list.ts +18 -0
  276. package/src/components/layout/geometry.ts +44 -0
  277. package/src/components/layout/row.ts +30 -0
  278. package/src/components/layout/split-pane.ts +65 -0
  279. package/src/components/layout/stack.ts +27 -0
  280. package/src/components/loader.ts +183 -1
  281. package/src/components/markdown.ts +21 -0
  282. package/src/components/metric.ts +39 -0
  283. package/src/components/progress-bar.ts +28 -0
  284. package/src/components/scroll-view.ts +23 -0
  285. package/src/components/section.ts +16 -0
  286. package/src/components/select-list.ts +174 -25
  287. package/src/components/settings-list.ts +264 -7
  288. package/src/components/spacer.ts +17 -0
  289. package/src/components/tab-bar.ts +42 -0
  290. package/src/components/table.ts +34 -0
  291. package/src/components/text.ts +23 -0
  292. package/src/components/tree-view.ts +65 -0
  293. package/src/components/truncated-text.ts +14 -0
  294. package/src/components/wizard-step.ts +18 -9
  295. package/src/debug-server.ts +14 -7
  296. package/src/hotkeys-markdown.ts +251 -69
  297. package/src/index.ts +6 -0
  298. package/src/latex-block.ts +11 -0
  299. package/src/native/apply.ts +275 -0
  300. package/src/native/backend.ts +682 -0
  301. package/src/native/blobs.ts +70 -0
  302. package/src/native/describe.ts +124 -0
  303. package/src/native/encode.ts +233 -0
  304. package/src/native/icons.ts +184 -0
  305. package/src/native/memo.ts +64 -0
  306. package/src/native/node.ts +112 -0
  307. package/src/native/overlay.ts +131 -0
  308. package/src/native/picker.ts +333 -0
  309. package/src/native/reconcile.ts +1063 -0
  310. package/src/native/settle.ts +23 -0
  311. package/src/native/spans.ts +308 -0
  312. package/src/native/state.ts +28 -0
  313. package/src/native/tone.ts +79 -0
  314. package/src/overlays/advisor-config.ts +420 -0
  315. package/src/overlays/agent-hub-renderer.ts +155 -18
  316. package/src/overlays/agent-hub.ts +1037 -44
  317. package/src/overlays/agent-transcript-viewer.ts +129 -0
  318. package/src/overlays/agents-hub.ts +688 -53
  319. package/src/overlays/annotation-overlay.ts +391 -52
  320. package/src/overlays/ask-dialog.ts +435 -24
  321. package/src/overlays/bordered-loader.ts +17 -0
  322. package/src/overlays/btw-history-panel.ts +182 -0
  323. package/src/overlays/btw-panel.ts +68 -2
  324. package/src/overlays/cleanse-panel.ts +105 -15
  325. package/src/overlays/codex-reset-fireworks.ts +65 -5
  326. package/src/overlays/composer-shape-preview.ts +26 -1
  327. package/src/overlays/copy-selector.ts +545 -23
  328. package/src/overlays/error-banner.ts +44 -0
  329. package/src/overlays/extensions/extension-list.ts +23 -17
  330. package/src/overlays/history-search.ts +175 -33
  331. package/src/overlays/hook-editor.ts +113 -6
  332. package/src/overlays/hook-input.ts +22 -1
  333. package/src/overlays/hook-selector.ts +311 -22
  334. package/src/overlays/hub-frame.ts +80 -0
  335. package/src/overlays/jobs-panel.ts +112 -0
  336. package/src/overlays/login-dialog.ts +183 -1
  337. package/src/overlays/logout-account-selector.ts +102 -4
  338. package/src/overlays/mcp-add-wizard.ts +232 -53
  339. package/src/overlays/model-browser.ts +681 -22
  340. package/src/overlays/model-hub.ts +1402 -78
  341. package/src/overlays/model-picker.ts +175 -1
  342. package/src/overlays/move-overlay.ts +45 -0
  343. package/src/overlays/oauth-selector.ts +227 -23
  344. package/src/overlays/omfg-panel.ts +86 -2
  345. package/src/overlays/pause-screen.ts +55 -4
  346. package/src/overlays/plan-review-overlay.ts +352 -0
  347. package/src/overlays/plan-save-overlay.ts +45 -0
  348. package/src/overlays/plugin-selector.ts +21 -1
  349. package/src/overlays/plugin-settings.ts +195 -5
  350. package/src/overlays/queue-mode-selector.ts +19 -1
  351. package/src/overlays/reset-usage-selector.ts +110 -40
  352. package/src/overlays/rewind-selector.ts +458 -44
  353. package/src/overlays/session-account-selector.ts +1 -1
  354. package/src/overlays/session-info-overlay.ts +137 -1
  355. package/src/overlays/session-selector.ts +807 -46
  356. package/src/overlays/settings-defs.ts +41 -0
  357. package/src/overlays/settings-selector.ts +583 -23
  358. package/src/overlays/show-images-selector.ts +18 -1
  359. package/src/overlays/snapcompact-shape-preview.ts +48 -3
  360. package/src/overlays/stats-notice.ts +31 -0
  361. package/src/overlays/theme-selector.ts +31 -1
  362. package/src/overlays/thinking-selector.ts +22 -1
  363. package/src/overlays/tree-selector.ts +570 -110
  364. package/src/overlays/usage-dashboard.ts +587 -12
  365. package/src/overlays/usage-row.ts +161 -25
  366. package/src/prompt/attachment-chips.ts +60 -0
  367. package/src/prompt/composer-attachments.ts +31 -9
  368. package/src/prompt/composer.ts +55 -2
  369. package/src/prompt/custom-editor.ts +302 -4
  370. package/src/prompt/editor-top-gap.ts +8 -0
  371. package/src/prompt/macos-spelling.ts +24 -9
  372. package/src/prompt/magic-keywords.ts +22 -1
  373. package/src/prompt/model-mention-autocomplete.ts +1 -0
  374. package/src/prompt/queued-messages.ts +101 -0
  375. package/src/prompt/skill-tokens.ts +5 -3
  376. package/src/prompt/welcome.ts +213 -6
  377. package/src/render/code-cell.ts +103 -2
  378. package/src/render/file-list.ts +29 -0
  379. package/src/render/hyperlink.ts +32 -0
  380. package/src/render/output-block.ts +110 -0
  381. package/src/render/output-pane.ts +37 -0
  382. package/src/render/tool-card.ts +137 -0
  383. package/src/render/tree-list.ts +39 -0
  384. package/src/render/width-aware-text.ts +20 -0
  385. package/src/setup/scenes/composer.ts +29 -0
  386. package/src/setup/scenes/glyph.ts +15 -0
  387. package/src/setup/scenes/model.ts +36 -8
  388. package/src/setup/scenes/outro.ts +28 -1
  389. package/src/setup/scenes/sign-in.ts +122 -13
  390. package/src/setup/scenes/splash.ts +28 -1
  391. package/src/setup/scenes/theme.ts +88 -3
  392. package/src/setup/scenes/types.ts +7 -1
  393. package/src/setup/startup-splash.ts +46 -9
  394. package/src/setup/wizard-overlay.ts +140 -23
  395. package/src/status-line/component.ts +398 -16
  396. package/src/status-line/context-usage.ts +279 -50
  397. package/src/status-line/footer.ts +94 -1
  398. package/src/status-line/metrics.ts +3 -6
  399. package/src/status-line/segments.ts +463 -97
  400. package/src/status-line/separators.ts +3 -16
  401. package/src/status-line/types.ts +43 -1
  402. package/src/terminal-capabilities.ts +7 -0
  403. package/src/terminal.ts +130 -2
  404. package/src/theme/defaults/dark-neon-noir.json +100 -0
  405. package/src/theme/defaults/index.ts +2 -0
  406. package/src/theme/defaults/light-prism.json +5 -5
  407. package/src/theme/defaults/onyx.json +62 -62
  408. package/src/theme/schema.ts +15 -0
  409. package/src/theme/shimmer.ts +33 -0
  410. package/src/theme/theme-class.ts +24 -19
  411. package/src/theme/theme.ts +111 -9
  412. package/src/tools/ask.ts +157 -1
  413. package/src/tools/ast-edit.ts +174 -1
  414. package/src/tools/ast-grep.ts +82 -2
  415. package/src/tools/autoresearch.ts +149 -1
  416. package/src/tools/bash.ts +180 -49
  417. package/src/tools/cfg-render.ts +66 -1
  418. package/src/tools/debug.ts +78 -1
  419. package/src/tools/default-renderer.ts +35 -1
  420. package/src/tools/edit.ts +247 -2
  421. package/src/tools/eval.ts +257 -1
  422. package/src/tools/fetch.ts +102 -3
  423. package/src/tools/find.ts +127 -1
  424. package/src/tools/github.ts +119 -10
  425. package/src/tools/glob.ts +116 -1
  426. package/src/tools/goal.ts +56 -1
  427. package/src/tools/grep.ts +83 -2
  428. package/src/tools/grouped-file-output.ts +205 -0
  429. package/src/tools/json-tree.ts +116 -1
  430. package/src/tools/lsp.ts +318 -1
  431. package/src/tools/mcp.ts +80 -1
  432. package/src/tools/memory.ts +93 -11
  433. package/src/tools/native-view.ts +292 -0
  434. package/src/tools/proc-render.ts +221 -1
  435. package/src/tools/read.ts +199 -4
  436. package/src/tools/renderer.ts +61 -0
  437. package/src/tools/report-tool-issue.ts +7 -1
  438. package/src/tools/resolve.ts +63 -1
  439. package/src/tools/task.ts +406 -4
  440. package/src/tools/think.ts +19 -1
  441. package/src/tools/todo.ts +89 -0
  442. package/src/tools/vibe.ts +230 -2
  443. package/src/tools/wait.ts +181 -1
  444. package/src/tools/web-search.ts +113 -1
  445. package/src/tools/write.ts +144 -5
  446. package/src/tools/xdev.ts +64 -1
  447. package/src/tui.ts +294 -3
@@ -1,4 +1,7 @@
1
1
  import type { MouseRoutable, SgrMouseEvent } from "../../mouse";
2
+ import type { TspProps } from "@oh-my-pi/pi-wire";
3
+ import { col, node, row } from "../../native/describe";
4
+ import type { DescribeContext, NativeNode } from "../../native/node";
2
5
  import type { Component } from "../../tui";
3
6
  import { visibleWidth } from "../../utils";
4
7
  import {
@@ -11,6 +14,9 @@ import {
11
14
  type LayoutRect,
12
15
  layoutRatio,
13
16
  layoutSize,
17
+ nativeLayoutChild,
18
+ nativeLayoutGap,
19
+ nativeSlotProps,
14
20
  optionalLayoutSize,
15
21
  uniqueLayoutComponents,
16
22
  } from "./geometry";
@@ -88,6 +94,16 @@ export class SplitPane implements Component, MouseRoutable {
88
94
  #measureMemo: SplitMeasureMemo | undefined;
89
95
  #rowMode: "split" | PaneSide = "split";
90
96
  #rowLeftWidth = -1;
97
+ #native:
98
+ | {
99
+ leftSize: SplitPaneSize;
100
+ rightMinWidth: number;
101
+ narrowPane: PaneSide | undefined;
102
+ divider: LayoutDecoration | undefined;
103
+ height: number | undefined;
104
+ node: NativeNode;
105
+ }
106
+ | undefined;
91
107
 
92
108
  constructor(options: SplitPaneOptions) {
93
109
  this.#left = options.left;
@@ -268,6 +284,55 @@ export class SplitPane implements Component, MouseRoutable {
268
284
  return geometry;
269
285
  }
270
286
 
287
+ /**
288
+ * Two flex slots in a `row`: the left pane fixed (`ch`) or a fraction of
289
+ * the width within its bounds, the right pane growing from its minimum.
290
+ * With a narrow pane configured the row may wrap, stacking the panes on
291
+ * narrow terminals instead of hiding one.
292
+ */
293
+ describe(_cx: DescribeContext): NativeNode {
294
+ const cached = this.#native;
295
+ if (
296
+ cached?.leftSize === this.#leftSize &&
297
+ cached.rightMinWidth === this.#rightMinWidth &&
298
+ cached.narrowPane === this.#narrowPane &&
299
+ cached.divider === this.#divider &&
300
+ cached.height === this.#height
301
+ ) {
302
+ return cached.node;
303
+ }
304
+ const size = this.#leftSize;
305
+ const leftProps: TspProps<"col"> =
306
+ size.fixed !== undefined
307
+ ? nativeSlotProps("w", { fixed: size.fixed })
308
+ : {
309
+ ...nativeSlotProps("w", { grow: 0, min: size.min, max: size.max }),
310
+ basis: layoutRatio(size.ratio, 0.5),
311
+ };
312
+ const rightProps = nativeSlotProps("w", { grow: 1, min: this.#rightMinWidth });
313
+ const height = this.#height;
314
+ const described = row(
315
+ [
316
+ node("col", leftProps, [nativeLayoutChild(this.#left)], "left"),
317
+ node("col", rightProps, [nativeLayoutChild(this.#right)], "right"),
318
+ ],
319
+ { gap: nativeLayoutGap(this.#divider), align: this.#align, wrap: this.#narrowPane !== undefined },
320
+ );
321
+ const result =
322
+ height === undefined
323
+ ? described
324
+ : col([described], { min: { h: `${height}lines` }, max: { h: `${height}lines` } });
325
+ this.#native = {
326
+ leftSize: size,
327
+ rightMinWidth: this.#rightMinWidth,
328
+ narrowPane: this.#narrowPane,
329
+ divider: this.#divider,
330
+ height,
331
+ node: result,
332
+ };
333
+ return result;
334
+ }
335
+
271
336
  /** Translate a split-local point into pane-local coordinates. */
272
337
  locate(line: number, col: number): SplitPaneHit | undefined {
273
338
  const panes: readonly [PaneSide, LayoutContent, LayoutRect | undefined][] = [
@@ -1,4 +1,6 @@
1
1
  import type { MouseRoutable, SgrMouseEvent } from "../../mouse";
2
+ import { col } from "../../native/describe";
3
+ import type { DescribeContext, NativeNode } from "../../native/node";
2
4
  import type { Component } from "../../tui";
3
5
  import { padding } from "../../utils";
4
6
  import {
@@ -13,6 +15,8 @@ import {
13
15
  type LayoutInsets,
14
16
  type LayoutRect,
15
17
  layoutSize,
18
+ nativeLayoutChild,
19
+ nativeSlotProps,
16
20
  optionalLayoutSize,
17
21
  renderLayoutContent,
18
22
  uniqueLayoutComponents,
@@ -76,6 +80,7 @@ export class Stack implements Component, MouseRoutable {
76
80
  #frames: LayoutRect[] = [];
77
81
  #memo: StackMemo | undefined;
78
82
  #ignoreTight = false;
83
+ #native: { children: StackChild[]; height: number | undefined; node: NativeNode } | undefined;
79
84
 
80
85
  constructor(options: StackOptions) {
81
86
  this.#children = options.children.map(child => ({ ...child, padding: { ...child.padding } }));
@@ -141,6 +146,28 @@ export class Stack implements Component, MouseRoutable {
141
146
  }
142
147
  }
143
148
 
149
+ /**
150
+ * A `col` of slots carrying each child's height bounds (fixed → min = max
151
+ * `lines`, else grow/min/max); an exact stack height bounds the column.
152
+ */
153
+ describe(_cx: DescribeContext): NativeNode {
154
+ const cached = this.#native;
155
+ if (cached?.children === this.#children && cached.height === this.#height) return cached.node;
156
+ const slots = this.#children.map(child =>
157
+ col(
158
+ [nativeLayoutChild(child.content)],
159
+ nativeSlotProps("h", { fixed: child.height, grow: child.grow, min: child.minHeight, max: child.maxHeight }),
160
+ ),
161
+ );
162
+ const height = this.#height;
163
+ const described = col(
164
+ slots,
165
+ height === undefined ? undefined : { min: { h: `${height}lines` }, max: { h: `${height}lines` } },
166
+ );
167
+ this.#native = { children: this.#children, height, node: described };
168
+ return described;
169
+ }
170
+
144
171
  /** Translate a stack-local point into a child's local coordinates. */
145
172
  locate(line: number, col: number): StackHit | undefined {
146
173
  for (let index = 0; index < this.#frames.length; index++) {
@@ -1,13 +1,99 @@
1
+ import type { TspProps } from "@oh-my-pi/pi-wire";
2
+ import { elapsed, kbd, keyed, node, row, span, text } from "../native/describe";
3
+ import { plainText } from "../native/spans";
4
+ import type { DescribeContext, NativeChild, NativeNode, NativeUiEvent } from "../native/node";
5
+ import { isNativeRendering } from "../native/state";
6
+ import { describeShimmer, type ShimmerPalette, shimmerEnabled } from "../theme/shimmer";
1
7
  import type { TUI } from "../tui";
2
8
  import { getPaddingX, padding, sliceByColumn, visibleWidth } from "../utils";
3
9
  import { Text } from "./text";
4
10
 
11
+ const DEFAULT_SPINNER_FRAMES = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
5
12
  const RENDER_INTERVAL_MS = 1000 / 30;
6
13
  /** Milliseconds between spinner-frame advances; exported so time-derived spinners elsewhere tick at the Loader cadence. */
7
14
  export const SPINNER_ADVANCE_MS = 80;
8
15
  const RENDER_BACKPRESSURE_MULTIPLIER = 9;
9
16
  const MAX_BACKPRESSURE_FRAME_COST_MS = 200;
10
17
 
18
+ /** Native repaint cadence of a retry countdown ring (the terminal eases the ring between frames). */
19
+ const RETRY_METER_REPAINT_MS = 500;
20
+
21
+ /** A working-row variant (§6.5): a retry countdown, or context compaction. */
22
+ export type WorkingRowVariant =
23
+ | {
24
+ readonly kind: "retry";
25
+ readonly attempt: number;
26
+ readonly max: number;
27
+ /** The wait before the attempt, in ms, counted from the row's `startedAt`. */
28
+ readonly delayMs: number;
29
+ }
30
+ | { readonly kind: "compaction" };
31
+
32
+ /** What the dock's working row (§8.1, role `omp.working`) shows. */
33
+ export interface WorkingRowSpec {
34
+ /** The intent, or the variant's label ("Retrying · attempt 1 of 3", "Compacting context…"). */
35
+ readonly label: string;
36
+ /** Epoch ms the work started: the `elapsed` origin and the retry countdown start. */
37
+ readonly startedAt: number;
38
+ readonly variant?: WorkingRowVariant;
39
+ /** Shimmer palette of the label (a session-accented intent). */
40
+ readonly palette?: ShimmerPalette;
41
+ /** Live tok/s, docked before the stop control. */
42
+ readonly rate?: number;
43
+ /** Key id that interrupts (`escape`); undefined hides the stop control (Esc would not cancel). */
44
+ readonly interruptKey?: string;
45
+ }
46
+
47
+ /** A key id as tooltip keys: `escape` reads `esc`, the rest as bound. */
48
+ function titleKey(key: string): string {
49
+ return key === "escape" ? "esc" : key;
50
+ }
51
+
52
+ /**
53
+ * The dock's working row: `starburst` spinner (or a retry countdown ring),
54
+ * the intent shimmering, `·`, the elapsed time, a grow spacer, then the stop
55
+ * control whose click sends `interrupt`. Compaction adds an indeterminate
56
+ * `progress` before the stop control; a retry says "Cancel".
57
+ */
58
+ export function describeWorkingRow(spec: WorkingRowSpec, cx: DescribeContext, now = Date.now()): NativeNode {
59
+ const variant = spec.variant;
60
+ const children: NativeChild[] = [];
61
+ if (variant?.kind === "retry" && cx.supports("meter")) {
62
+ const remaining = Math.max(0, variant.delayMs - (now - spec.startedAt));
63
+ const value = variant.delayMs > 0 ? Math.round((remaining / variant.delayMs) * 100) / 100 : 0;
64
+ children.push(node("meter", { value, style: "ring", size: "sm" }, undefined, "countdown"));
65
+ } else {
66
+ children.push(node("spinner", { style: "starburst", tone: "accent" }, undefined, "spinner"));
67
+ }
68
+ const label = describeShimmer([{ text: spec.label, palette: spec.palette }], "label");
69
+ children.push(
70
+ node(label.k, { ...label.p, role: "omp.working.label" } as TspProps, label.c, label.key),
71
+ node("text", { text: "·", role: "omp.working.sep" }, undefined, "sep"),
72
+ keyed(elapsed(now - spec.startedAt), "elapsed"),
73
+ node("row", { grow: 1 }, undefined, "fill"),
74
+ );
75
+ if (variant?.kind === "compaction") children.push(node("progress", { value: null }, undefined, "progress"));
76
+ if (spec.rate !== undefined) children.push(node("rate", { value: spec.rate, unit: "tok/s" }, undefined, "rate"));
77
+ if (spec.interruptKey !== undefined) {
78
+ const verb = variant?.kind === "retry" ? "Cancel" : "Stop";
79
+ children.push(
80
+ node(
81
+ "row",
82
+ {
83
+ role: "omp.working.stop",
84
+ gap: "xs",
85
+ align: "center",
86
+ title: `${verb} ${titleKey(spec.interruptKey)}`,
87
+ actions: { click: "interrupt" },
88
+ },
89
+ [kbd(titleKey(spec.interruptKey)), text(verb)],
90
+ "stop",
91
+ ),
92
+ );
93
+ }
94
+ return row(children, { role: "omp.working", align: "center", gap: "sm" });
95
+ }
96
+
11
97
  type ColorFn = (str: string) => string;
12
98
 
13
99
  /**
@@ -20,7 +106,7 @@ export type LoaderMessageColorFn = ColorFn & {
20
106
 
21
107
  /** Animates a spinner and colorized message while asynchronous work is pending. */
22
108
  export class Loader extends Text {
23
- #frames = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
109
+ #frames = DEFAULT_SPINNER_FRAMES;
24
110
  #currentFrame = 0;
25
111
  #intervalId?: NodeJS.Timeout;
26
112
  #ui: TUI | null = null;
@@ -37,6 +123,10 @@ export class Loader extends Text {
37
123
  #layoutFrames: readonly string[];
38
124
  #layoutFrame: string;
39
125
  #trailer?: () => string | undefined;
126
+ #native?: { message: string; trailer: string; shimmer: boolean; node: NativeNode };
127
+ #working?: { spec: () => WorkingRowSpec; interrupt: () => void };
128
+ #workingNative?: { key: string; node: NativeNode };
129
+ #nativeTimer?: NodeJS.Timeout;
40
130
 
41
131
  constructor(
42
132
  ui: TUI,
@@ -136,10 +226,93 @@ export class Loader extends Text {
136
226
  return lines;
137
227
  }
138
228
 
229
+ /**
230
+ * A `spinner` (or the static glyph of a one-frame loader, e.g. an interrupt
231
+ * key hint) followed by the message, shimmering when the message colorizer
232
+ * animates, and the right-docked trailer. The terminal clocks the motion.
233
+ * Function messages are re-read whenever the engine revisits the loader.
234
+ */
235
+ override describe(cx: DescribeContext): NativeNode {
236
+ if (this.#working) return this.#describeWorking(cx);
237
+ const message = plainText(this.#resolveMessage());
238
+ const trailer = plainText(this.#trailer?.() ?? "");
239
+ const shimmer = this.messageColorFn.animated === true && shimmerEnabled();
240
+ const cached = this.#native;
241
+ if (cached && cached.message === message && cached.trailer === trailer && cached.shimmer === shimmer) {
242
+ return cached.node;
243
+ }
244
+ const indicator =
245
+ this.#frames.length > 1
246
+ ? node("spinner", this.#frames === DEFAULT_SPINNER_FRAMES ? { style: "braille" } : {})
247
+ : text([span(plainText(this.#frames[0] ?? "").trim(), "muted")], { wrap: "none" });
248
+ const label = shimmer
249
+ ? describeShimmer([{ text: message }], "message")
250
+ : node("text", { spans: [span(message, "muted")] }, undefined, "message");
251
+ const children: NativeChild[] = [row([indicator, label], { gap: "sm", align: "baseline" })];
252
+ if (trailer) children.push(text(trailer, { wrap: "none", truncate: "end" }));
253
+ const described = row(children, { justify: "between", role: "omp.loader" });
254
+ this.#native = { message, trailer, shimmer, node: described };
255
+ return described;
256
+ }
257
+
258
+ /**
259
+ * Describe as the dock's working row ({@link describeWorkingRow}) instead
260
+ * of a spinner line; `spec` is re-read whenever the engine revisits the
261
+ * loader and `interrupt` answers a click on its stop control.
262
+ */
263
+ setWorkingRow(spec: () => WorkingRowSpec, interrupt: () => void): void {
264
+ this.#working = { spec, interrupt };
265
+ this.#workingNative = undefined;
266
+ this.#startNativeCountdown();
267
+ }
268
+
269
+ /** A click on the working row's stop control. */
270
+ handleNativeEvent(event: NativeUiEvent): void {
271
+ if (event.type === "action" && event.act === "interrupt") this.#working?.interrupt();
272
+ }
273
+
274
+ #describeWorking(cx: DescribeContext): NativeNode {
275
+ const spec = this.#working!.spec();
276
+ const now = Date.now();
277
+ const variant = spec.variant;
278
+ const countdown =
279
+ variant?.kind === "retry" && cx.supports("meter")
280
+ ? Math.round((Math.max(0, variant.delayMs - (now - spec.startedAt)) / Math.max(1, variant.delayMs)) * 100)
281
+ : -1;
282
+ const key = [
283
+ spec.label,
284
+ spec.startedAt,
285
+ variant?.kind,
286
+ variant?.kind === "retry" ? `${variant.attempt}/${variant.max}` : "",
287
+ countdown,
288
+ spec.rate,
289
+ spec.interruptKey,
290
+ spec.palette?.mid,
291
+ shimmerEnabled(),
292
+ ].join("\u0000");
293
+ const cached = this.#workingNative;
294
+ if (cached?.key === key) return cached.node;
295
+ const described = describeWorkingRow(spec, cx, now);
296
+ this.#workingNative = { key, node: described };
297
+ return described;
298
+ }
299
+
300
+ /** A TSP terminal clocks the spinner and timer, but a retry ring's fill is omp's: repaint it while counting down. */
301
+ #startNativeCountdown(): void {
302
+ if (this.#nativeTimer || !isNativeRendering() || this.#working?.spec().variant?.kind !== "retry") return;
303
+ this.#nativeTimer = setInterval(() => this.#requestPaint(), RETRY_METER_REPAINT_MS);
304
+ this.#nativeTimer.unref?.();
305
+ }
306
+
139
307
  start() {
140
308
  this.#lastSpinnerTick = performance.now();
141
309
  this.#syncText();
142
310
  this.#requestPaint();
311
+ // The TSP terminal animates the described spinner; nothing to repaint.
312
+ if (isNativeRendering()) {
313
+ this.#startNativeCountdown();
314
+ return;
315
+ }
143
316
  const intervalMs = this.messageColorFn.animated === true ? RENDER_INTERVAL_MS : SPINNER_ADVANCE_MS;
144
317
  this.#scheduleTick(intervalMs, intervalMs);
145
318
  }
@@ -149,6 +322,10 @@ export class Loader extends Text {
149
322
  clearTimeout(this.#intervalId);
150
323
  this.#intervalId = undefined;
151
324
  }
325
+ if (this.#nativeTimer) {
326
+ clearInterval(this.#nativeTimer);
327
+ this.#nativeTimer = undefined;
328
+ }
152
329
  }
153
330
 
154
331
  /** Lifecycle teardown: stop the animation timer. Idempotent. */
@@ -174,6 +351,11 @@ export class Loader extends Text {
174
351
  #scheduleTick(intervalMs: number, delayMs: number): void {
175
352
  const timer = setTimeout(() => {
176
353
  if (this.#intervalId !== timer) return;
354
+ if (isNativeRendering()) {
355
+ // A TSP surface opened mid-animation: the terminal clocks it from here.
356
+ this.#intervalId = undefined;
357
+ return;
358
+ }
177
359
  const startedAt = performance.now();
178
360
  const elapsed = startedAt - this.#lastSpinnerTick;
179
361
  const shouldAdvanceSpinner = elapsed >= SPINNER_ADVANCE_MS;
@@ -10,6 +10,9 @@ import {
10
10
  import { mathBlockAt, mathSpanAt, mathStartIndex } from "@oh-my-pi/pi-utils/math-delimiters";
11
11
  import { latexToBlock } from "../latex-block";
12
12
  import { isBareMathEnvironment, latexToUnicode } from "../latex-to-unicode";
13
+ import { plainText } from "../native/spans";
14
+ import { md } from "../native/describe";
15
+ import type { DescribeContext, NativeNode } from "../native/node";
13
16
  import type { SymbolTheme } from "../symbols";
14
17
  import { TERMINAL } from "../terminal-capabilities";
15
18
  import type { Component } from "../tui";
@@ -1817,6 +1820,7 @@ export class Markdown implements Component {
1817
1820
  // B+ capture plumbing: #renderContentLines records the last rendered paragraph row.
1818
1821
  #lastTailCapture?: { kind: "paragraph"; open: boolean; rowInput: string; rowRaw: string };
1819
1822
  #ignoreTight = false;
1823
+ #native?: { text: string; stream: boolean; node: NativeNode };
1820
1824
  setIgnoreTight(ignore: boolean): this {
1821
1825
  this.#ignoreTight = ignore;
1822
1826
  this.invalidate();
@@ -1913,6 +1917,23 @@ export class Markdown implements Component {
1913
1917
  this.#cachedLines = undefined;
1914
1918
  }
1915
1919
 
1920
+ /**
1921
+ * The unrendered source as an `md` node, `stream` while the streaming
1922
+ * (transient) cache is on. Append-only growth yields a fresh node of the
1923
+ * same kind and key whose text extends the previous one, which the
1924
+ * reconciler sends as `text append`.
1925
+ */
1926
+ describe(_cx: DescribeContext): NativeNode {
1927
+ const stream = this.#transientRenderCache;
1928
+ const cached = this.#native;
1929
+ if (cached?.text === this.#text && cached.stream === stream) return cached.node;
1930
+ // Sources are model text; OSC 8 / SGR bytes a caller spliced in are the terminal's to style, not ours.
1931
+ const source = this.#text.includes("\x1b") ? plainText(this.#text) : this.#text;
1932
+ const described = md(source, stream ? { stream: true } : undefined);
1933
+ this.#native = { text: this.#text, stream, node: described };
1934
+ return described;
1935
+ }
1936
+
1916
1937
  /**
1917
1938
  * Width-independent source prefix of the last render ending at a frozen
1918
1939
  * Markdown block boundary. Only meaningful while streaming (transient
@@ -1,3 +1,6 @@
1
+ import { styleSpans } from "../native/spans";
2
+ import { node, text } from "../native/describe";
3
+ import type { DescribeContext, NativeNode } from "../native/node";
1
4
  import type { Component } from "../tui";
2
5
  import { Ellipsis, getWidthConfigEpoch, truncateToWidth, visibleWidth } from "../utils";
3
6
  import { Text } from "./text";
@@ -85,6 +88,7 @@ export class MetricRow implements Component {
85
88
  readonly #options: MetricRowOptions;
86
89
  readonly #text: Text;
87
90
  #cache: { width: number; epoch: number; source: readonly string[]; lines: readonly string[] } | undefined;
91
+ #native: NativeNode | undefined;
88
92
 
89
93
  constructor(metrics: readonly MetricSpec[], options: MetricRowOptions = {}) {
90
94
  this.#metrics = metrics;
@@ -96,12 +100,47 @@ export class MetricRow implements Component {
96
100
  if (this.#metrics === metrics) return false;
97
101
  this.#metrics = metrics;
98
102
  this.#cache = undefined;
103
+ this.#native = undefined;
99
104
  return true;
100
105
  }
101
106
 
102
107
  invalidate(): void {
103
108
  this.#text.invalidate();
104
109
  this.#cache = undefined;
110
+ this.#native = undefined;
111
+ }
112
+
113
+ /**
114
+ * `drop` overflow becomes a transparent `status` strip whose segments keep
115
+ * the metric priorities (the terminal drops the lowest first); the other
116
+ * modes are one text run that wraps or truncates.
117
+ */
118
+ describe(_cx: DescribeContext): NativeNode {
119
+ if (this.#native) return this.#native;
120
+ const options = this.#options;
121
+ if (options.overflow === "drop") {
122
+ const segs: NativeNode[] = [];
123
+ this.#metrics.forEach((metric, index) => {
124
+ const formatted = formatMetric(metric);
125
+ if (formatted === undefined) return;
126
+ segs.push(
127
+ node(
128
+ "seg",
129
+ { side: "left", priority: metric.priority ?? 0, spans: styleSpans(formatted, options.style) },
130
+ undefined,
131
+ String(index),
132
+ ),
133
+ );
134
+ });
135
+ this.#native = node("status", { transparent: true }, segs);
136
+ return this.#native;
137
+ }
138
+ const joined = formatMetricRow(this.#metrics, { separator: options.separator });
139
+ this.#native =
140
+ options.overflow === "wrap"
141
+ ? text(styleSpans(joined, options.style), { wrap: "word" })
142
+ : text(styleSpans(joined, options.style), { wrap: "none", truncate: "end" });
143
+ return this.#native;
105
144
  }
106
145
 
107
146
  render(width: number): readonly string[] {
@@ -1,3 +1,6 @@
1
+ import { compactText, styledSpans } from "../native/spans";
2
+ import { node } from "../native/describe";
3
+ import type { DescribeContext, NativeNode } from "../native/node";
1
4
  import type { Component } from "../tui";
2
5
  import { Ellipsis, getWidthConfigEpoch, truncateToWidth, visibleWidth } from "../utils";
3
6
 
@@ -87,6 +90,7 @@ export class ProgressBar implements Component {
87
90
  #value: number | undefined;
88
91
  readonly #options: ProgressBarOptions;
89
92
  #cache: { width: number; epoch: number; value: number | undefined; lines: readonly string[] } | undefined;
93
+ #native: NativeNode | undefined;
90
94
 
91
95
  constructor(value: number | undefined, options: ProgressBarOptions) {
92
96
  this.#value = value;
@@ -97,11 +101,35 @@ export class ProgressBar implements Component {
97
101
  if (Object.is(this.#value, value)) return false;
98
102
  this.#value = value;
99
103
  this.#cache = undefined;
104
+ this.#native = undefined;
100
105
  return true;
101
106
  }
102
107
 
103
108
  invalidate(): void {
104
109
  this.#cache = undefined;
110
+ this.#native = undefined;
111
+ }
112
+
113
+ /** A native `progress` bar (indeterminate when the value is unknown); prefix, suffix and percentage label it. */
114
+ describe(_cx: DescribeContext): NativeNode {
115
+ if (this.#native) return this.#native;
116
+ const options = this.#options;
117
+ const fraction = progressFraction(this.#value, options.min, options.max);
118
+ const parts: string[] = [];
119
+ // Frame punctuation (`[`, `]`, `▕`) around the bar is the terminal's chrome now; only wording labels it.
120
+ const frame = /^[^\p{L}\p{N}]+|[^\p{L}\p{N}%)]+$/gu;
121
+ const prefix = singleLine(options.prefix ?? "").replace(frame, "");
122
+ const suffix = singleLine(options.suffix ?? "").replace(frame, "");
123
+ if (prefix) parts.push(prefix);
124
+ if (suffix) parts.push(suffix);
125
+ if (fraction !== undefined && options.showPercentage === true) {
126
+ parts.push(singleLine(options.formatPercentage?.(fraction) ?? `${Math.round(fraction * 100)}%`));
127
+ }
128
+ this.#native = node("progress", {
129
+ value: fraction ?? null,
130
+ label: parts.length > 0 ? compactText(styledSpans(parts.join(" "))) : undefined,
131
+ });
132
+ return this.#native;
105
133
  }
106
134
 
107
135
  render(width: number): readonly string[] {
@@ -1,4 +1,7 @@
1
1
  import { matchesKey } from "../keys";
2
+ import { rowsText } from "../native/spans";
3
+ import { col } from "../native/describe";
4
+ import type { DescribeContext, NativeNode } from "../native/node";
2
5
  import type { Component } from "../tui";
3
6
  import { Ellipsis, getWidthConfigEpoch, padding, replaceTabs, truncateToWidth, visibleWidth } from "../utils";
4
7
  import {
@@ -126,6 +129,7 @@ export class ScrollView implements Component {
126
129
  #rangeSnapshot: RangeSnapshot | undefined;
127
130
  #cache: RenderCache | undefined;
128
131
  #disposed = false;
132
+ #native: { content: readonly string[] | Component; height: number; node: NativeNode } | undefined;
129
133
 
130
134
  constructor(content: readonly string[] | Component, options: ScrollViewOptions) {
131
135
  this.#child = isComponent(content) ? content : undefined;
@@ -400,6 +404,7 @@ export class ScrollView implements Component {
400
404
  invalidate(): void {
401
405
  if (this.#disposed) return;
402
406
  this.#cache = undefined;
407
+ this.#native = undefined;
403
408
  this.#child?.invalidate?.();
404
409
  }
405
410
 
@@ -415,6 +420,24 @@ export class ScrollView implements Component {
415
420
  child?.dispose?.();
416
421
  }
417
422
 
423
+ /**
424
+ * The whole content in a `col` capped at the viewport height: the
425
+ * terminal scrolls it natively and draws its own scrollbar.
426
+ */
427
+ describe(_cx: DescribeContext): NativeNode {
428
+ const content = this.#child ?? this.#lines;
429
+ const cached = this.#native;
430
+ if (cached?.content === content && cached.height === this.#height) return cached.node;
431
+ const described = this.#disposed
432
+ ? col([], { hidden: true })
433
+ : col([this.#child ?? rowsText(this.#lines)], {
434
+ max: { h: `${this.#height}lines` },
435
+ hidden: this.#height === 0 ? true : undefined,
436
+ });
437
+ this.#native = { content, height: this.#height, node: described };
438
+ return described;
439
+ }
440
+
418
441
  render(width: number): readonly string[] {
419
442
  if (this.#disposed) return EMPTY_LINES;
420
443
  const safeWidth = normalizedRows(width);
@@ -1,3 +1,6 @@
1
+ import { compactText, rowsText, styledSpans } from "../native/spans";
2
+ import { node } from "../native/describe";
3
+ import type { DescribeContext, NativeNode } from "../native/node";
1
4
  import type { Component } from "../tui";
2
5
  import { Ellipsis, getWidthConfigEpoch, replaceTabs, truncateToWidth, visibleWidth } from "../utils";
3
6
 
@@ -37,6 +40,7 @@ export class Section implements Component {
37
40
  readonly #options: SectionOptions;
38
41
  #cache: { width: number; epoch: number; child: readonly string[]; lines: readonly string[] } | undefined;
39
42
  #disposed = false;
43
+ #native: { title: string; node: NativeNode } | undefined;
40
44
 
41
45
  constructor(options: SectionOptions) {
42
46
  this.#options = options;
@@ -53,6 +57,18 @@ export class Section implements Component {
53
57
  if (isComponent(this.#options.body)) this.#options.body.dispose?.();
54
58
  }
55
59
 
60
+ /** A `section` headed by the styled title; the rule is the terminal's. */
61
+ describe(_cx: DescribeContext): NativeNode {
62
+ const options = this.#options;
63
+ const rawTitle = singleLine(options.title);
64
+ const title = options.titleStyle?.(rawTitle) ?? rawTitle;
65
+ if (this.#native?.title === title) return this.#native.node;
66
+ const body = isComponent(options.body) ? options.body : rowsText(options.body);
67
+ const described = node("section", { head: compactText(styledSpans(title)) }, [body]);
68
+ this.#native = { title, node: described };
69
+ return described;
70
+ }
71
+
56
72
  render(width: number): readonly string[] {
57
73
  const renderWidth = finiteWidth(width);
58
74
  const epoch = getWidthConfigEpoch();