@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
@@ -4,10 +4,15 @@ import { fuzzyFilter } from "../fuzzy";
4
4
  import { getKeybindings } from "../keybindings";
5
5
  import { extractPrintableText } from "../keys";
6
6
  import type { MouseRoutable, SgrMouseEvent } from "../mouse";
7
+ import type { TspPrefsControl, TspPrefsRow, TspPrefsSection, TspProps, TspSpan } from "@oh-my-pi/pi-wire";
8
+ import { col, node, span } from "../native/describe";
9
+ import { sameItems, sameProps } from "../native/memo";
10
+ import { plainLine, plainText } from "../native/spans";
11
+ import type { DescribeContext, NativeChild, NativeNode, NativeUiEvent } from "../native/node";
7
12
  import type { Component } from "../tui";
8
13
  import { Ellipsis, padding, replaceTabs, truncateToWidth, visibleWidth, wrapTextWithAnsi } from "../utils";
9
14
  import { ScrollView } from "./scroll-view";
10
- import { FormField, type FormFieldOptions, type FormFieldTheme } from "./form";
15
+ import { FormField, type FormFieldOptions, type FormFieldTheme, SelectFormField, TextFormField } from "./form";
11
16
  import { MenuSelection } from "./menu-selection";
12
17
 
13
18
  function sanitizeSingleLine(text: string): string {
@@ -17,6 +22,13 @@ function sanitizeSingleLine(text: string): string {
17
22
  .trim();
18
23
  }
19
24
 
25
+ /** Described `item` node cached on the setting, rebuilt when a visible field (e.g. `currentValue`) changes. */
26
+ const kNativeItem = Symbol("settingsList.nativeItem");
27
+
28
+ interface NativeDescribedSetting extends SettingItem {
29
+ [kNativeItem]?: { signature: string; node: NativeNode };
30
+ }
31
+
20
32
  function hasMouseRouter(component: Component): component is Component & MouseRoutable {
21
33
  return "routeMouse" in component && typeof component.routeMouse === "function";
22
34
  }
@@ -38,10 +50,59 @@ export interface SettingItem {
38
50
  submenu?: (currentValue: string, done: (selectedValue?: string) => void) => Component;
39
51
  /** True when the displayed setting differs from its default value. */
40
52
  changed?: boolean;
53
+ /** The default, as a native settings page names it in the changed dot's title. */
54
+ defaultLabel?: string;
41
55
  /** Render as a non-interactive section heading. Skipped by navigation and search. */
42
56
  heading?: boolean;
43
57
  }
44
58
 
59
+ /** A row's open editor as a native settings page shows it (`TspPrefsProps.editing`). */
60
+ export interface PrefsEditing {
61
+ row: string;
62
+ draft?: string;
63
+ cursor?: number;
64
+ option?: string;
65
+ }
66
+
67
+ /** A submenu that reports the option it highlights (multiselect toggles) to a native settings page. */
68
+ export interface PrefsOptionSource {
69
+ readonly prefsOption: string | undefined;
70
+ }
71
+
72
+ /** Where a setting row goes on a native settings page: its section and, for search results, its page. */
73
+ export interface PrefsSectionRef {
74
+ id: string;
75
+ title: string;
76
+ page?: string;
77
+ }
78
+
79
+ /** The section id for a section title (`"Status Line"` → `"status-line"`). */
80
+ export function prefsSectionId(title: string): string {
81
+ return title
82
+ .toLowerCase()
83
+ .replace(/[^a-z0-9]+/g, "-")
84
+ .replace(/^-|-$/g, "");
85
+ }
86
+
87
+ /** A setting as a native settings row, given its typed control; `undefined` for headings. */
88
+ export function settingPrefsRow(item: SettingItem, control: TspPrefsControl): TspPrefsRow | undefined {
89
+ if (item.heading) return undefined;
90
+ const row: TspPrefsRow = {
91
+ id: item.id,
92
+ label: plainLine(item.label),
93
+ hint: item.description ? plainLine(item.description) : undefined,
94
+ warning: item.warning ? plainLine(item.warning) : undefined,
95
+ changed: item.changed === true ? true : undefined,
96
+ defaultLabel: item.changed && item.defaultLabel ? plainLine(item.defaultLabel) : undefined,
97
+ control,
98
+ };
99
+ return row;
100
+ }
101
+
102
+ function hasPrefsOption(component: Component): component is Component & PrefsOptionSource {
103
+ return "prefsOption" in component;
104
+ }
105
+
45
106
  export interface SettingsListTheme {
46
107
  label: (text: string, selected: boolean, changed: boolean) => string;
47
108
  value: (text: string, selected: boolean, changed: boolean) => string;
@@ -133,6 +194,8 @@ export class SettingsList implements Component {
133
194
  #hitRows: (string | undefined)[] = [];
134
195
  #sidebarHitRows: (string | undefined)[] = [];
135
196
  #sidebarHitCol = 0;
197
+ #nativeList?: { props: TspProps<"list">; items: readonly NativeNode[]; node: NativeNode };
198
+ #nativeRoot?: { body: NativeChild; hint: string; node: NativeNode };
136
199
  constructor(
137
200
  items: SettingItem[],
138
201
  maxVisible: number,
@@ -222,6 +285,98 @@ export class SettingsList implements Component {
222
285
  return this.#submenuComponent !== null;
223
286
  }
224
287
 
288
+ /** The open submenu and the row that opened it, if any. */
289
+ get openSubmenu(): { id: string; component: Component } | undefined {
290
+ const component = this.#submenuComponent;
291
+ const id = this.#submenuItemId;
292
+ return component && id !== null ? { id, component } : undefined;
293
+ }
294
+
295
+ /**
296
+ * The visible settings as native page sections: rows in list order, each
297
+ * in the section `sectionOf` (given the row and the heading above it) names,
298
+ * sections in order of their first row (ranked search results gather
299
+ * under their section). Rows `control` has no typed control for are left
300
+ * out.
301
+ */
302
+ prefsSections(
303
+ sectionOf: (item: SettingItem, heading: SettingItem | undefined) => PrefsSectionRef,
304
+ control: (item: SettingItem) => TspPrefsControl | undefined,
305
+ ): TspPrefsSection[] {
306
+ const sections: { ref: PrefsSectionRef; rows: TspPrefsRow[] }[] = [];
307
+ let heading: SettingItem | undefined;
308
+ for (const item of this.#filteredItems) {
309
+ if (item.heading) {
310
+ heading = item;
311
+ continue;
312
+ }
313
+ const typed = control(item);
314
+ const row = typed && settingPrefsRow(item, typed);
315
+ if (!row) continue;
316
+ const ref = sectionOf(item, heading);
317
+ const section = sections.find(candidate => candidate.ref.id === ref.id);
318
+ if (section) section.rows.push(row);
319
+ else sections.push({ ref, rows: [row] });
320
+ }
321
+ return sections.map(({ ref, rows }) => ({ ...ref, rows }));
322
+ }
323
+
324
+ /** The row with keyboard focus, or while the section rail has focus the focused section's heading label. */
325
+ prefsFocus(): { row?: string; section?: string } {
326
+ if (this.#sectionFocus) {
327
+ const sections = this.#sections();
328
+ return { section: sections[this.#activeSectionIndex(sections)]?.name };
329
+ }
330
+ return { row: this.getSelectedItem()?.id };
331
+ }
332
+
333
+ /**
334
+ * The open editor as a native page draws it: a choice's highlighted option
335
+ * (when `inlineMenu(id)` says the row's choices fit its popup menu), a text
336
+ * field's draft and cursor (dots when masked), a multiselect's highlighted
337
+ * option; `null` when nothing is open or the editor has no native control
338
+ * (the host shows that one itself).
339
+ */
340
+ prefsEditing(inlineMenu: (id: string) => boolean): PrefsEditing | null {
341
+ const open = this.openSubmenu;
342
+ if (!open) return null;
343
+ const { id, component } = open;
344
+ if (component instanceof SelectFormField) {
345
+ if (!inlineMenu(id)) return null;
346
+ return { row: id, option: component.selectList.getSelectedItem()?.value };
347
+ }
348
+ if (component instanceof TextFormField) {
349
+ const draft = component.getValue();
350
+ return {
351
+ row: id,
352
+ draft: component.input.mask ? "•".repeat(draft.length) : draft,
353
+ cursor: component.input.getCursor(),
354
+ };
355
+ }
356
+ if (hasPrefsOption(component)) return { row: id, option: component.prefsOption };
357
+ return null;
358
+ }
359
+
360
+ /** Applies `value` to a cycling (boolean/enum) row the way Enter does: the row shows it and `onChange` runs. */
361
+ applyValue(id: string, value: string): void {
362
+ const item = this.#items.find(candidate => !candidate.heading && candidate.id === id);
363
+ if (!item || item.submenu || !item.values?.includes(value)) return;
364
+ this.selectItem(id);
365
+ item.currentValue = value;
366
+ this.#onChange(item.id, value);
367
+ }
368
+
369
+ /** Opens row `id`'s submenu the way Enter does (leaving one already open for it); returns it. */
370
+ openSubmenuFor(id: string): Component | undefined {
371
+ const open = this.openSubmenu;
372
+ if (open?.id === id) return open.component;
373
+ if (open || !this.selectItem(id)) return undefined;
374
+ const item = this.getSelectedItem();
375
+ if (!item?.submenu) return undefined;
376
+ this.#activateItem();
377
+ return this.#submenuComponent ?? undefined;
378
+ }
379
+
225
380
  #notifySelection(): void {
226
381
  const item = this.getSelectedItem();
227
382
  if (item?.id === this.#lastNotifiedSelectionId) return;
@@ -484,6 +639,113 @@ export class SettingsList implements Component {
484
639
  return this.#padLines(this.#renderMainList(width));
485
640
  }
486
641
 
642
+ /**
643
+ * A `list` of setting `item`s (label, current value on the right, risk note
644
+ * and description as detail; headings as disabled items) keyed by setting id,
645
+ * with the selection and search query, plus the footer hint. An open submenu
646
+ * replaces the list and describes itself.
647
+ */
648
+ describe(_cx: DescribeContext): NativeNode {
649
+ let body: NativeChild;
650
+ let hint = "";
651
+ if (this.#submenuComponent) {
652
+ body = this.#submenuComponent;
653
+ } else {
654
+ body = this.#describeList();
655
+ hint = this.#options.hint === "" ? "" : plainLine(this.#hintText());
656
+ }
657
+ const cached = this.#nativeRoot;
658
+ if (cached && cached.body === body && cached.hint === hint) return cached.node;
659
+ const children: NativeChild[] = [body];
660
+ if (hint) children.push(node("text", { spans: [span(hint, "muted")] }, undefined, "hint"));
661
+ const root = col(children, { role: "omp.settings", gap: "sm" });
662
+ this.#nativeRoot = { body, hint, node: root };
663
+ return root;
664
+ }
665
+
666
+ /**
667
+ * Pointer actions do what a click does in the settings hosts: `select` moves
668
+ * the selection to the row, or activates it (Enter) when it already is the
669
+ * selection; `activate` selects and activates.
670
+ */
671
+ handleNativeEvent(event: NativeUiEvent): void {
672
+ if (this.#submenuComponent || (event.type !== "select" && event.type !== "activate")) return;
673
+ const wasSelected = this.getSelectedItem()?.id === event.item;
674
+ if (!this.selectItem(event.item)) return;
675
+ if (event.type === "activate" || wasSelected) this.#activateItem();
676
+ }
677
+
678
+ #describeList(): NativeNode {
679
+ const items: NativeNode[] = [];
680
+ for (const item of this.#filteredItems) items.push(this.#describeItem(item));
681
+ const cachedMax = this.#nativeList?.props.max;
682
+ const props: TspProps<"list"> = {
683
+ selected: this.getSelectedItem()?.id ?? null,
684
+ filter: this.#filterQuery.trim() || undefined,
685
+ empty:
686
+ this.#items.length === 0 ? (this.#options.emptyText ?? "No settings available") : "No matching settings",
687
+ max:
688
+ typeof cachedMax === "object" && cachedMax.lines === this.#maxVisible
689
+ ? cachedMax
690
+ : { lines: this.#maxVisible },
691
+ };
692
+ const cached = this.#nativeList;
693
+ if (cached && sameProps(cached.props, props) && sameItems(cached.items, items)) return cached.node;
694
+ const list = node("list", props, items, "list");
695
+ this.#nativeList = { props, items, node: list };
696
+ return list;
697
+ }
698
+
699
+ #describeItem(item: NativeDescribedSetting): NativeNode {
700
+ const value = plainLine(String(item.currentValue ?? ""));
701
+ const changed = item.changed === true;
702
+ const signature = `${item.heading === true}\0${item.label}\0${value}\0${changed}\0${item.warning ?? ""}\0${item.description ?? ""}`;
703
+ const cached = item[kNativeItem];
704
+ if (cached?.signature === signature) return cached.node;
705
+ let described: NativeNode;
706
+ if (item.heading) {
707
+ described = node(
708
+ "item",
709
+ { label: [span(plainLine(item.label), "strong")], disabled: true, role: "omp.settings.heading" },
710
+ undefined,
711
+ item.id,
712
+ );
713
+ } else {
714
+ const label: TspSpan[] = [span(plainLine(item.label), changed ? "accent" : undefined)];
715
+ if (item.warning && this.#theme.warningMark)
716
+ label.push(span(` ${plainText(this.#theme.warningMark)}`, "warning"));
717
+ const detail: TspSpan[] = [];
718
+ if (item.warning) detail.push(span(plainLine(item.warning), "warning"));
719
+ if (item.description) {
720
+ if (detail.length > 0) detail.push(span(" "));
721
+ detail.push(span(plainLine(item.description), "muted"));
722
+ }
723
+ described = node(
724
+ "item",
725
+ {
726
+ label,
727
+ value: [span(value, changed ? "accent" : "muted")],
728
+ detail: detail.length > 0 ? detail : undefined,
729
+ },
730
+ undefined,
731
+ item.id,
732
+ );
733
+ }
734
+ item[kNativeItem] = { signature, node: described };
735
+ return described;
736
+ }
737
+
738
+ #hintText(): string {
739
+ const jumpHint =
740
+ this.#sections().length >= 2
741
+ ? `${editorKeys("tui.select.pageUp", "tui.select.pageDown")} to jump sections · `
742
+ : "";
743
+ return (
744
+ this.#options.hint ??
745
+ `${editorKey("tui.select.confirm")}/${formatKeyHint("space")} to change · ${jumpHint}Type to search · ${editorKey("tui.select.cancel")} to cancel`
746
+ );
747
+ }
748
+
487
749
  /** Warning glyph suffix for a row that carries a risk note, or "" when none applies. */
488
750
  #warningMark(item: SettingItem): string {
489
751
  return item.warning && this.#theme.warningMark ? ` ${this.#theme.warningMark}` : "";
@@ -642,12 +904,7 @@ export class SettingsList implements Component {
642
904
  // Add hint (suppressed entirely when the host owns the footer)
643
905
  if (this.#options.hint !== "") {
644
906
  lines.push("");
645
- const jumpHint =
646
- sections.length >= 2 ? `${editorKeys("tui.select.pageUp", "tui.select.pageDown")} to jump sections · ` : "";
647
- const hintText =
648
- this.#options.hint ??
649
- `${editorKey("tui.select.confirm")}/${formatKeyHint("space")} to change · ${jumpHint}Type to search · ${editorKey("tui.select.cancel")} to cancel`;
650
- lines.push(truncateToWidth(this.#theme.hint(` ${hintText}`), width));
907
+ lines.push(truncateToWidth(this.#theme.hint(` ${this.#hintText()}`), width));
651
908
  }
652
909
 
653
910
  return lines;
@@ -1,11 +1,22 @@
1
+ import { node } from "../native/describe";
2
+ import type { DescribeContext, NativeNode } from "../native/node";
1
3
  import type { Component } from "../tui";
2
4
 
5
+ /** Terminal spacing step for a count of blank rows. */
6
+ export function spaceForRows(rows: number): "none" | "sm" | "md" | "lg" {
7
+ if (!(rows > 0)) return "none";
8
+ if (rows <= 1) return "sm";
9
+ if (rows <= 2) return "md";
10
+ return "lg";
11
+ }
12
+
3
13
  /**
4
14
  * Spacer component that renders empty lines
5
15
  */
6
16
  export class Spacer implements Component {
7
17
  #lines: number;
8
18
  #cached: string[] | undefined;
19
+ #native: NativeNode | undefined;
9
20
 
10
21
  constructor(lines: number = 1) {
11
22
  this.#lines = lines;
@@ -19,11 +30,17 @@ export class Spacer implements Component {
19
30
  if (lines === this.#lines) return;
20
31
  this.#lines = lines;
21
32
  this.#cached = undefined;
33
+ this.#native = undefined;
22
34
  }
23
35
  invalidate(): void {
24
36
  // No cached state to invalidate currently
25
37
  }
26
38
 
39
+ describe(_cx: DescribeContext): NativeNode {
40
+ this.#native ??= node("spacer", { size: spaceForRows(this.#lines) });
41
+ return this.#native;
42
+ }
43
+
27
44
  render(_width: number): readonly string[] {
28
45
  let cached = this.#cached;
29
46
  if (cached === undefined) {
@@ -10,6 +10,9 @@
10
10
  */
11
11
  import { formatKeyHint } from "../app-keybindings";
12
12
  import { matchesKey } from "../keys";
13
+ import { compactText, styledSpans, styleSpans } from "../native/spans";
14
+ import { node, row } from "../native/describe";
15
+ import type { DescribeContext, NativeNode, NativeUiEvent } from "../native/node";
13
16
  import type { Component } from "../tui";
14
17
  import { truncateToWidth, visibleWidth } from "../utils";
15
18
 
@@ -60,6 +63,7 @@ export class TabBar implements Component {
60
63
  #theme: TabBarTheme;
61
64
  #label: string;
62
65
  #hoverTabId: string | null = null;
66
+ #native?: { signature: string; node: NativeNode };
63
67
  /** Per-render tab hit zones: 0-based line + [start, end) columns. */
64
68
  #hitZones: { line: number; start: number; end: number; index: number }[] = [];
65
69
 
@@ -161,6 +165,44 @@ export class TabBar implements Component {
161
165
  // No cached state to invalidate
162
166
  }
163
167
 
168
+ /**
169
+ * A native `tabs` strip (muted tabs keep the muted token), preceded by the
170
+ * bar label and followed by the cycle hint when present. Shrinking to
171
+ * `short` labels and wrapping are the terminal's.
172
+ */
173
+ describe(_cx: DescribeContext): NativeNode {
174
+ // Tabs are a caller-owned mutable array (render keeps no cache either), so key on content.
175
+ let signature = `${this.#activeIndex}\0${this.#label}\0${this.showHint}`;
176
+ for (const tab of this.#tabs) signature += `\0${tab.id}\x01${tab.label}\x01${tab.muted === true}`;
177
+ if (this.#native?.signature === signature) return this.#native.node;
178
+ const tabs = node("tabs", {
179
+ items: this.#tabs.map(tab => {
180
+ const label = styledSpans(tab.label);
181
+ return {
182
+ id: tab.id,
183
+ label: tab.muted
184
+ ? label.map(part => ({ ...part, s: part.s ? `${part.s} muted` : "muted" }))
185
+ : compactText(label),
186
+ };
187
+ }),
188
+ active: this.#tabs[this.#activeIndex]?.id,
189
+ });
190
+ const parts: NativeNode[] = [];
191
+ if (this.#label) parts.push(node("text", { spans: styleSpans(`${this.#label}:`, this.#theme.label) }));
192
+ parts.push(tabs);
193
+ if (this.showHint) {
194
+ parts.push(node("text", { spans: styleSpans(`(${formatKeyHint("tab")} to cycle)`, this.#theme.hint) }));
195
+ }
196
+ const described = parts.length === 1 ? tabs : row(parts, { gap: "md", align: "center" });
197
+ this.#native = { signature, node: described };
198
+ return described;
199
+ }
200
+
201
+ /** A click on a tab activates it (muted tabs stay inert). */
202
+ handleNativeEvent(event: NativeUiEvent): void {
203
+ if (event.type === "select" || event.type === "activate") this.selectTab(event.item);
204
+ }
205
+
164
206
  /**
165
207
  * Handle keyboard input for tab navigation.
166
208
  * @returns true if the input was handled, false otherwise
@@ -1,3 +1,7 @@
1
+ import type { TspTableColumn, TspText } from "@oh-my-pi/pi-wire";
2
+ import { compactText, styleSpans } from "../native/spans";
3
+ import { node } from "../native/describe";
4
+ import type { DescribeContext, NativeNode } from "../native/node";
1
5
  import type { Component } from "../tui";
2
6
  import { Ellipsis, getWidthConfigEpoch, replaceTabs, truncateToWidth, visibleWidth } from "../utils";
3
7
 
@@ -92,6 +96,7 @@ export class Table implements Component {
92
96
  readonly #columns: readonly TableColumn[];
93
97
  readonly #options: TableOptions;
94
98
  #cache: { width: number; epoch: number; lines: readonly string[] } | undefined;
99
+ #native: NativeNode | undefined;
95
100
 
96
101
  constructor(rows: readonly (readonly TableCell[])[], columns: readonly TableColumn[], options: TableOptions = {}) {
97
102
  this.#rows = rows;
@@ -103,11 +108,40 @@ export class Table implements Component {
103
108
  if (this.#rows === rows) return false;
104
109
  this.#rows = rows;
105
110
  this.#cache = undefined;
111
+ this.#native = undefined;
106
112
  return true;
107
113
  }
108
114
 
109
115
  invalidate(): void {
110
116
  this.#cache = undefined;
117
+ this.#native = undefined;
118
+ }
119
+
120
+ /**
121
+ * A native `table`. Column priorities carry over unchanged (lower gives
122
+ * way first); truncating columns truncate at the end, the rest keep
123
+ * their content. Widths are the terminal's.
124
+ */
125
+ describe(_cx: DescribeContext): NativeNode {
126
+ if (this.#native) return this.#native;
127
+ const columns = this.#columns;
128
+ const cols: TspTableColumn[] = columns.map((column, index) => ({
129
+ id: `c${index}`,
130
+ align: column.align === "right" ? "end" : "start",
131
+ truncate: column.overflow === "truncate" ? "end" : undefined,
132
+ priority: column.priority ?? 0,
133
+ }));
134
+ const rows = this.#rows.map((row, rowIndex) => {
135
+ const cells: Record<string, TspText> = {};
136
+ const count = Math.min(row.length, columns.length);
137
+ for (let index = 0; index < count; index++) {
138
+ const cell = row[index]!;
139
+ cells[`c${index}`] = compactText(styleSpans(singleLine(cell.text), cell.style ?? columns[index]!.style));
140
+ }
141
+ return { id: `r${rowIndex}`, cells };
142
+ });
143
+ this.#native = node("table", { cols, rows });
144
+ return this.#native;
111
145
  }
112
146
 
113
147
  render(width: number): readonly string[] {
@@ -1,3 +1,7 @@
1
+ import { styledSpans } from "../native/spans";
2
+ import { backgroundChrome, sampleBackground } from "../native/tone";
3
+ import { node, text } from "../native/describe";
4
+ import type { DescribeContext, NativeNode } from "../native/node";
1
5
  import type { Component } from "../tui";
2
6
  import {
3
7
  applyBackgroundToLine,
@@ -39,6 +43,7 @@ export class Text implements Component {
39
43
  #cachedWidth?: number;
40
44
  #cachedWidthConfigEpoch?: number;
41
45
  #cachedLines?: string[];
46
+ #native?: { source: string; bg: string | undefined; node: NativeNode };
42
47
 
43
48
  constructor(text: string = "", paddingX: number = 1, paddingY: number = 1, customBgFn?: (text: string) => string) {
44
49
  this.#text = text;
@@ -104,6 +109,24 @@ export class Text implements Component {
104
109
  this.#cachedLines = undefined;
105
110
  }
106
111
 
112
+ /**
113
+ * Wrapped text; styling re-derived from the styled source so a theme swap
114
+ * re-resolves tokens. A background fill becomes the node's tone.
115
+ */
116
+ describe(_cx: DescribeContext): NativeNode {
117
+ const source = this.#styleFn ? this.#styleFn(this.#text) : this.#text;
118
+ const bg = sampleBackground(this.#customBgFn);
119
+ const cached = this.#native;
120
+ if (cached?.source === source && cached.bg === bg) return cached.node;
121
+ const spans = source.trim() === "" ? [] : styledSpans(source);
122
+ const described =
123
+ spans.length === 0
124
+ ? node("col", { hidden: true })
125
+ : text(spans, { wrap: "word", tone: backgroundChrome(bg).tone });
126
+ this.#native = { source, bg, node: described };
127
+ return described;
128
+ }
129
+
107
130
  render(width: number): readonly string[] {
108
131
  // Check cache
109
132
  if (
@@ -1,3 +1,7 @@
1
+ import type { TspSpan, TspTreeNode } from "@oh-my-pi/pi-wire";
2
+ import { styledSpans } from "../native/spans";
3
+ import { node } from "../native/describe";
4
+ import type { DescribeContext, NativeNode } from "../native/node";
1
5
  import type { Theme } from "../theme/theme";
2
6
  import type { Component } from "../tui";
3
7
  import { replaceTabs, truncateToWidth, visibleWidth } from "../utils";
@@ -191,6 +195,11 @@ export interface TreeViewOptions<T, K extends TreeKey> extends FlattenTreeOption
191
195
  scrollbarTheme?: ScrollViewTheme;
192
196
  }
193
197
 
198
+ /** A tree node under construction; frozen into the wire shape once described. */
199
+ type MutableTreeNode = {
200
+ -readonly [P in keyof TspTreeNode]: P extends "children" ? MutableTreeNode[] : TspTreeNode[P];
201
+ };
202
+
194
203
  /** Result of a tree render, including whether a row or line budget omitted content. */
195
204
  export interface TreeViewRenderResult {
196
205
  lines: readonly string[];
@@ -219,6 +228,7 @@ export class TreeView<T, K extends TreeKey = string> implements Component {
219
228
  #version = 0;
220
229
  #cache?: { width: number; version: number; result: TreeViewRenderResult };
221
230
  #childByKey = new Map<K, Component>();
231
+ #native?: { version: number; node: NativeNode | null };
222
232
 
223
233
  constructor(options: TreeViewOptions<T, K>) {
224
234
  this.#options = options;
@@ -327,6 +337,61 @@ export class TreeView<T, K extends TreeKey = string> implements Component {
327
337
  return this.renderWithState(width).lines;
328
338
  }
329
339
 
340
+ /**
341
+ * A native disclosure `tree` over the filtered rows, nested by hierarchy
342
+ * (a filtered-out ancestor lifts its visible descendants to the nearest
343
+ * visible one). Gutters, windowing and the scrollbar are the terminal's;
344
+ * the selected row is marked with the `mark` token. Rows whose body is a
345
+ * component can't be tree labels, so such a tree stays on `rows`.
346
+ */
347
+ describe(_cx: DescribeContext): NativeNode | null {
348
+ this.#ensureRows();
349
+ if (this.#native?.version === this.#version) return this.#native.node;
350
+ const rows = this.#rows;
351
+ const byKey = new Map<K, MutableTreeNode>();
352
+ const roots: MutableTreeNode[] = [];
353
+ let described: NativeNode | null = null;
354
+ let componentRow = false;
355
+ for (let rowIndex = 0; rowIndex < rows.length; rowIndex++) {
356
+ const row = rows[rowIndex]!;
357
+ const selected = row.key === this.#selectedKey;
358
+ const rendered = this.#options.renderRow(row.item, {
359
+ row,
360
+ selected,
361
+ rowIndex,
362
+ windowStart: 0,
363
+ windowEnd: rows.length,
364
+ windowRows: rows,
365
+ width: Number.POSITIVE_INFINITY,
366
+ contentWidth: Number.POSITIVE_INFINITY,
367
+ prefix: { first: "", continuation: "" },
368
+ });
369
+ if (typeof rendered !== "string" && "render" in rendered) {
370
+ this.#replaceChild(row.key, rendered);
371
+ componentRow = true;
372
+ continue;
373
+ }
374
+ this.#replaceChild(row.key, undefined);
375
+ let label: TspSpan[] = styledSpans(typeof rendered === "string" ? rendered : rendered.join("\n"));
376
+ if (selected) label = label.map(part => ({ ...part, s: part.s ? `${part.s} mark` : "mark" }));
377
+ const treeNode: MutableTreeNode = { id: String(row.key), label };
378
+ byKey.set(row.key, treeNode);
379
+ let parentKey = row.parentKey;
380
+ while (parentKey !== undefined && !byKey.has(parentKey)) parentKey = this.#rowByKey.get(parentKey)?.parentKey;
381
+ const parent = parentKey === undefined ? undefined : byKey.get(parentKey);
382
+ if (parent) {
383
+ parent.children ??= [];
384
+ parent.open = true;
385
+ parent.children.push(treeNode);
386
+ } else {
387
+ roots.push(treeNode);
388
+ }
389
+ }
390
+ if (!componentRow) described = node("tree", { nodes: roots });
391
+ this.#native = { version: this.#version, node: described };
392
+ return described;
393
+ }
394
+
330
395
  /** Render lines plus budget/window metadata for adapters that append an ellipsis. */
331
396
  renderWithState(width: number): TreeViewRenderResult {
332
397
  this.#ensureRows();
@@ -1,3 +1,6 @@
1
+ import { styledSpans } from "../native/spans";
2
+ import { text } from "../native/describe";
3
+ import type { DescribeContext, NativeNode } from "../native/node";
1
4
  import type { Component } from "../tui";
2
5
  import { padding, truncateToWidth, visibleWidth } from "../utils";
3
6
 
@@ -10,6 +13,7 @@ export class TruncatedText implements Component {
10
13
  #paddingY: number;
11
14
  #cachedWidth = -1;
12
15
  #cachedLines: string[] | undefined;
16
+ #native: NativeNode | undefined;
13
17
 
14
18
  constructor(text: string, paddingX: number = 0, paddingY: number = 0) {
15
19
  this.#text = text;
@@ -32,10 +36,20 @@ export class TruncatedText implements Component {
32
36
  }
33
37
 
34
38
  invalidate(): void {
39
+ this.#native = undefined;
35
40
  this.#cachedWidth = -1;
36
41
  this.#cachedLines = undefined;
37
42
  }
38
43
 
44
+ /** First line only, clamped to one visual line with an end ellipsis. */
45
+ describe(_cx: DescribeContext): NativeNode {
46
+ if (this.#native) return this.#native;
47
+ const newlineIndex = this.#text.indexOf("\n");
48
+ const firstLine = newlineIndex === -1 ? this.#text : this.#text.slice(0, newlineIndex);
49
+ this.#native = text(styledSpans(firstLine), { wrap: "none", truncate: "end", lines: 1 });
50
+ return this.#native;
51
+ }
52
+
39
53
  render(width: number): readonly string[] {
40
54
  if (this.#cachedLines && this.#cachedWidth === width) {
41
55
  return this.#cachedLines;