@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,6 +1,10 @@
1
1
  import { getKeybindings } from "../keybindings";
2
2
  import { matchesKey } from "../keys";
3
3
  import type { MouseRoutable, SgrMouseEvent } from "../mouse";
4
+ import { col, node, span } from "../native/describe";
5
+ import { plainText } from "../native/spans";
6
+ import type { DescribeContext, NativeChild, NativeNode, NativeUiEvent } from "../native/node";
7
+ import { type SelectPickerOptions, SelectListSheet } from "../native/picker";
4
8
  import { Container, type Component, type Focusable } from "../tui";
5
9
  import { replaceTabs, truncateToWidth } from "../utils";
6
10
  import { Input } from "./input";
@@ -48,10 +52,13 @@ function hasMouseRouter(component: FormControl): component is FormControl & Mous
48
52
  class StyledText implements Component {
49
53
  readonly #text: Text;
50
54
  #value: string;
55
+ #native: NativeNode | undefined;
51
56
 
52
57
  constructor(
53
58
  value: string,
54
59
  private readonly style: (text: string) => string,
60
+ /** Span token standing in for `style` on a TSP terminal. */
61
+ private readonly token?: string,
55
62
  ) {
56
63
  this.#value = value;
57
64
  this.#text = new Text(this.style(value), 0, 0);
@@ -60,9 +67,15 @@ class StyledText implements Component {
60
67
  setText(value: string): void {
61
68
  if (value === this.#value) return;
62
69
  this.#value = value;
70
+ this.#native = undefined;
63
71
  this.#text.setText(this.style(value));
64
72
  }
65
73
 
74
+ describe(_cx: DescribeContext): NativeNode {
75
+ this.#native ??= node("text", { spans: [span(plainText(this.#value), this.token)] });
76
+ return this.#native;
77
+ }
78
+
66
79
  render(width: number): readonly string[] {
67
80
  return this.#text.render(width);
68
81
  }
@@ -77,9 +90,14 @@ class OptionalText implements Component {
77
90
  readonly #text: StyledText;
78
91
  #value = "";
79
92
  readonly #empty: readonly string[] = [];
93
+ readonly #hidden = node("text", { text: "", hidden: true });
80
94
 
81
- constructor(style: (text: string) => string) {
82
- this.#text = new StyledText("", style);
95
+ constructor(style: (text: string) => string, token?: string) {
96
+ this.#text = new StyledText("", style, token);
97
+ }
98
+
99
+ describe(cx: DescribeContext): NativeNode {
100
+ return this.#value ? this.#text.describe(cx) : this.#hidden;
83
101
  }
84
102
 
85
103
  setText(value: string): void {
@@ -114,37 +132,67 @@ export class FormField implements Component, Focusable, MouseRoutable {
114
132
  #memoControl: readonly string[] | undefined;
115
133
  #memoAfter: readonly string[] | undefined;
116
134
  #memoLines: readonly string[] = [];
135
+ /** Fixed described structure: every child is a component or a static node, so it never changes. */
136
+ readonly #native: NativeNode;
117
137
 
118
138
  constructor(control: FormControl, options: FormFieldOptions) {
119
139
  this.control = control;
120
- this.#error = new OptionalText(options.theme.error);
140
+ this.#error = new OptionalText(options.theme.error, "error");
141
+ // Spacing is the terminal's (`gap`); only content becomes native children.
142
+ const native: NativeChild[] = [];
121
143
 
122
144
  if (options.leadingSpace) this.#beforeControl.addChild(new Spacer(1));
123
145
  if (options.label) {
124
- this.#beforeControl.addChild(new StyledText(options.label, options.theme.label));
146
+ const label = new StyledText(options.label, options.theme.label, "strong");
147
+ this.#beforeControl.addChild(label);
148
+ native.push(label);
125
149
  }
126
150
  if (options.description) {
127
151
  if (options.label) this.#beforeControl.addChild(new Spacer(1));
128
- this.#beforeControl.addChild(new StyledText(options.description, options.theme.description));
152
+ const description = new StyledText(options.description, options.theme.description, "muted");
153
+ this.#beforeControl.addChild(description);
154
+ native.push(description);
155
+ }
156
+ for (const detail of options.details ?? []) {
157
+ this.#beforeControl.addChild(detail);
158
+ native.push(detail);
129
159
  }
130
- for (const detail of options.details ?? []) this.#beforeControl.addChild(detail);
131
160
  if (options.preview) {
132
161
  this.#beforeControl.addChild(new Spacer(1));
133
162
  if (options.previewLabel) {
134
- this.#beforeControl.addChild(new StyledText(options.previewLabel, options.theme.description));
163
+ const previewLabel = new StyledText(options.previewLabel, options.theme.description, "muted");
164
+ this.#beforeControl.addChild(previewLabel);
165
+ native.push(previewLabel);
135
166
  }
136
167
  this.#beforeControl.addChild(options.preview);
168
+ native.push(options.preview);
137
169
  }
138
170
  if (options.spaceBeforeControl !== false) this.#beforeControl.addChild(new Spacer(1));
171
+ native.push(control);
139
172
 
140
173
  if (options.spaceAfterControl !== false) this.#afterControl.addChild(new Spacer(1));
141
174
  this.#afterControl.addChild(this.#error);
142
- for (const summary of options.summary ?? []) this.#afterControl.addChild(summary);
143
- if (options.hint) this.#afterControl.addChild(new StyledText(options.hint, options.theme.hint));
175
+ native.push(this.#error);
176
+ for (const summary of options.summary ?? []) {
177
+ this.#afterControl.addChild(summary);
178
+ native.push(summary);
179
+ }
180
+ if (options.hint) {
181
+ const hint = new StyledText(options.hint, options.theme.hint, "muted");
182
+ this.#afterControl.addChild(hint);
183
+ native.push(hint);
184
+ }
144
185
  if (options.footer) {
145
186
  this.#afterControl.addChild(new Spacer(1));
146
187
  this.#afterControl.addChild(options.footer);
188
+ native.push(options.footer);
147
189
  }
190
+ this.#native = col(native, { gap: "sm", role: "omp.form.field" });
191
+ }
192
+
193
+ /** Label, description, details, preview, the control, error, summary, hint and footer as one column. */
194
+ describe(_cx: DescribeContext): NativeNode {
195
+ return this.#native;
148
196
  }
149
197
 
150
198
  get focused(): boolean {
@@ -346,14 +394,22 @@ export interface SelectFormFieldOptions extends Omit<FormFieldOptions, "preview"
346
394
  onCancel(): void;
347
395
  /** Schedule a frame after an asynchronous preview update settles. */
348
396
  requestRender?: () => void;
397
+ /** The picker sheet a long choice (> {@link PICKER_MIN_ITEMS} items) describes as; defaults to the label. */
398
+ picker?: SelectPickerOptions;
349
399
  }
350
400
 
401
+ /** Choices longer than this describe as a `picker md` sheet where the terminal draws one (spec §5 controls). */
402
+ const PICKER_MIN_ITEMS = 12;
403
+
351
404
  /** SelectList field that centralizes selection, cancellation, preview refresh, and mouse routing. */
352
405
  export class SelectFormField extends FormField {
353
406
  readonly selectList: SelectList;
354
407
  readonly #previewText: StyledText | undefined;
355
408
  readonly #getPreview: (() => string) | undefined;
356
409
  readonly #requestRender: (() => void) | undefined;
410
+ readonly #sheet: SelectListSheet;
411
+ /** Number of choices (before filtering). */
412
+ readonly itemCount: number;
357
413
  #previewRequestId = 0;
358
414
 
359
415
  constructor(options: SelectFormFieldOptions) {
@@ -375,6 +431,16 @@ export class SelectFormField extends FormField {
375
431
  this.#previewText = previewText;
376
432
  this.#getPreview = options.getPreview;
377
433
  this.#requestRender = options.requestRender;
434
+ this.itemCount = options.items.length;
435
+ this.#sheet = new SelectListSheet(
436
+ selectList,
437
+ options.picker ?? {
438
+ title: options.label ?? "Select",
439
+ ...(options.description ? { subtitle: options.description } : {}),
440
+ searchable: true,
441
+ },
442
+ { docked: false },
443
+ );
378
444
 
379
445
  selectList.onSelect = item => {
380
446
  try {
@@ -420,9 +486,20 @@ export class SelectFormField extends FormField {
420
486
 
421
487
  override invalidate(): void {
422
488
  this.#updatePreview();
489
+ this.#sheet.invalidate();
423
490
  super.invalidate();
424
491
  }
425
492
 
493
+ /** Long choices describe as a picker sheet where supported; the host places it (layer or prefs editor). */
494
+ override describe(cx: DescribeContext): NativeNode {
495
+ return this.itemCount > PICKER_MIN_ITEMS && cx.supports("picker") ? this.#sheet.describe() : super.describe(cx);
496
+ }
497
+
498
+ /** Picker pointer events drive the list as its keys do (preview, pick, cancel). */
499
+ handleNativeEvent(event: NativeUiEvent): void {
500
+ this.#sheet.handle(event);
501
+ }
502
+
426
503
  #finishPreview(requestId: number): void {
427
504
  if (requestId !== this.#previewRequestId) return;
428
505
  this.setError(undefined);
@@ -467,13 +544,20 @@ export class Form implements Component, Focusable, MouseRoutable {
467
544
  #memoWidth = -1;
468
545
  #memoFieldLines: (readonly string[])[] = [];
469
546
  #memoLines: readonly string[] = [];
547
+ readonly #native: NativeNode;
470
548
 
471
549
  constructor(options: FormOptions) {
472
550
  this.#options = options;
473
551
  this.#fields = options.fields;
552
+ this.#native = col(this.#fields, { gap: "md", role: "omp.form" });
474
553
  this.#syncFocus();
475
554
  }
476
555
 
556
+ /** The fields as one column; focus traversal stays keyboard-driven. */
557
+ describe(_cx: DescribeContext): NativeNode {
558
+ return this.#native;
559
+ }
560
+
477
561
  get focused(): boolean {
478
562
  return this.#focused;
479
563
  }
@@ -8,6 +8,9 @@ import {
8
8
  renderImage,
9
9
  TERMINAL,
10
10
  } from "../terminal-capabilities";
11
+ import { registerNativeBlob } from "../native/blobs";
12
+ import { node } from "../native/describe";
13
+ import type { DescribeContext, NativeNode } from "../native/node";
11
14
  import type { Component } from "../tui";
12
15
 
13
16
  export interface ImageTheme {
@@ -731,6 +734,7 @@ export class Image implements Component {
731
734
  // pads itself to this height so a budget demotion never shrinks the block
732
735
  // (its rows may already be committed to native scrollback).
733
736
  #renderedGraphicRows = 0;
737
+ #native?: NativeNode;
734
738
 
735
739
  constructor(
736
740
  base64Data: string,
@@ -764,6 +768,32 @@ export class Image implements Component {
764
768
  this.#cachedWidth = undefined;
765
769
  }
766
770
 
771
+ /**
772
+ * A native `image` backed by a content-addressed blob; the terminal fits
773
+ * it. Cell caps become `ch`/`lines` bounds. The inline-image budget and
774
+ * graphics protocols do not apply.
775
+ */
776
+ describe(_cx: DescribeContext): NativeNode {
777
+ if (this.#native) return this.#native;
778
+ const blob = registerNativeBlob(Buffer.from(this.#base64Data, "base64"), this.#mimeType);
779
+ const maxW = this.#options.maxWidthCells;
780
+ const maxH = this.#options.maxHeightCells;
781
+ this.#native = node("image", {
782
+ blob,
783
+ alt: imageFallback(this.#mimeType, this.#dimensions, this.#options.filename),
784
+ w: this.#dimensions.widthPx,
785
+ h: this.#dimensions.heightPx,
786
+ max:
787
+ (maxW ?? 0) > 0 || (maxH ?? 0) > 0
788
+ ? {
789
+ w: maxW && maxW > 0 ? `${maxW}ch` : undefined,
790
+ h: maxH && maxH > 0 ? `${maxH}lines` : undefined,
791
+ }
792
+ : undefined,
793
+ });
794
+ return this.#native;
795
+ }
796
+
767
797
  render(width: number): readonly string[] {
768
798
  const imageProtocol = TERMINAL.imageProtocol;
769
799
  const hasProtocol = imageProtocol != null;
@@ -2,6 +2,11 @@ import { BracketedPasteHandler, decodeReencodedPasteControls } from "../brackete
2
2
  import { getKeybindings } from "../keybindings";
3
3
  import { extractPrintableText, matchesKey } from "../keys";
4
4
  import { KillRing } from "../kill-ring";
5
+ import type { TspInputProps } from "@oh-my-pi/pi-wire";
6
+ import { node } from "../native/describe";
7
+ import { sameProps } from "../native/memo";
8
+ import { plainText } from "../native/spans";
9
+ import type { DescribeContext, NativeNode } from "../native/node";
5
10
  import { SpaceHoldGesture } from "../space-hold";
6
11
  import { type Component, CURSOR_MARKER, type Focusable } from "../tui";
7
12
  import { cursorColumnWindow } from "./scroll-viewport";
@@ -58,6 +63,8 @@ export class Input implements Component, Focusable {
58
63
  prompt = "> ";
59
64
  /** Render the editable value as bullets while retaining the real value internally. */
60
65
  mask = false;
66
+ /** Dim hint a native terminal shows while the value is empty. */
67
+ placeholder: string | undefined;
61
68
  onSubmit?: (value: string) => void;
62
69
  onEscape?: () => void;
63
70
  /** Space-bar push-to-talk; set its `handler` to enable it. */
@@ -81,9 +88,17 @@ export class Input implements Component, Focusable {
81
88
  /** Code units of the current volatile speech-to-text preview (see {@link setVolatileText}). */
82
89
  #volatileTextLen = 0;
83
90
 
91
+ #native?: { props: TspInputProps; node: NativeNode };
92
+
84
93
  getValue(): string {
85
94
  return this.#value;
86
95
  }
96
+
97
+ /** The caret as a UTF-16 offset into {@link getValue}. */
98
+ getCursor(): number {
99
+ return this.#cursor;
100
+ }
101
+
87
102
  /** Return bounded input content and cursor state for debug inspection. */
88
103
  debugState(): Record<string, unknown> {
89
104
  return {
@@ -112,7 +127,14 @@ export class Input implements Component, Focusable {
112
127
  return this.#useTerminalCursor;
113
128
  }
114
129
 
115
- handleInput(data: string): void {
130
+ /**
131
+ * Apply one key: the editor's text bindings (motion, deletion, kill ring,
132
+ * undo), pastes and printable text. Returns whether the key was the
133
+ * field's: false for keys it has no binding for, and for cancel/submit
134
+ * without an `onEscape`/`onSubmit`, so a host (a list's search field) can
135
+ * route everything else on.
136
+ */
137
+ handleInput(data: string): boolean {
116
138
  // Handle bracketed paste mode
117
139
  const paste = this.#pasteHandler.process(data);
118
140
  if (paste.handled) {
@@ -122,77 +144,79 @@ export class Input implements Component, Focusable {
122
144
  this.handleInput(paste.remaining);
123
145
  }
124
146
  }
125
- return;
147
+ return true;
126
148
  }
127
149
 
128
150
  // Space-hold push-to-talk: a sustained space bar starts/stops STT instead of typing spaces.
129
151
  switch (this.spaceHold.process(matchesKey(data, "space"))) {
130
152
  case "type":
131
153
  this.#insertCharacter(" ");
132
- return;
154
+ return true;
133
155
  case "swallow":
134
- return;
156
+ return true;
135
157
  }
136
158
 
137
159
  const kb = getKeybindings();
138
160
 
139
161
  // Escape/Cancel
140
162
  if (kb.matches(data, "tui.select.cancel")) {
141
- if (this.onEscape) this.onEscape();
142
- return;
163
+ if (!this.onEscape) return false;
164
+ this.onEscape();
165
+ return true;
143
166
  }
144
167
 
145
168
  // Undo
146
169
  if (kb.matches(data, "tui.editor.undo")) {
147
170
  this.#undo();
148
- return;
171
+ return true;
149
172
  }
150
173
 
151
174
  // Submit
152
175
  if (kb.matches(data, "tui.input.submit") || data === "\n") {
153
- if (this.onSubmit) this.onSubmit(this.#value);
154
- return;
176
+ if (!this.onSubmit) return false;
177
+ this.onSubmit(this.#value);
178
+ return true;
155
179
  }
156
180
 
157
181
  // Deletion
158
182
  if (kb.matches(data, "tui.editor.deleteCharBackward")) {
159
183
  this.#handleBackspace();
160
- return;
184
+ return true;
161
185
  }
162
186
 
163
187
  if (kb.matches(data, "tui.editor.deleteCharForward")) {
164
188
  this.#handleForwardDelete();
165
- return;
189
+ return true;
166
190
  }
167
191
 
168
192
  if (kb.matches(data, "tui.editor.deleteWordBackward")) {
169
193
  this.#deleteWordBackwards();
170
- return;
194
+ return true;
171
195
  }
172
196
 
173
197
  if (kb.matches(data, "tui.editor.deleteWordForward")) {
174
198
  this.#deleteWordForward();
175
- return;
199
+ return true;
176
200
  }
177
201
 
178
202
  if (kb.matches(data, "tui.editor.deleteToLineStart")) {
179
203
  this.#deleteToLineStart();
180
- return;
204
+ return true;
181
205
  }
182
206
 
183
207
  if (kb.matches(data, "tui.editor.deleteToLineEnd")) {
184
208
  this.#deleteToLineEnd();
185
- return;
209
+ return true;
186
210
  }
187
211
 
188
212
  // Kill ring actions
189
213
  if (kb.matches(data, "tui.editor.yank")) {
190
214
  this.#yank();
191
- return;
215
+ return true;
192
216
  }
193
217
  if (kb.matches(data, "tui.editor.yankPop")) {
194
218
  this.#yankPop();
195
- return;
219
+ return true;
196
220
  }
197
221
 
198
222
  // Cursor movement
@@ -204,7 +228,7 @@ export class Input implements Component, Focusable {
204
228
  const lastGrapheme = graphemes[graphemes.length - 1];
205
229
  this.#cursor -= lastGrapheme ? lastGrapheme.segment.length : 1;
206
230
  }
207
- return;
231
+ return true;
208
232
  }
209
233
 
210
234
  if (kb.matches(data, "tui.editor.cursorRight")) {
@@ -215,36 +239,36 @@ export class Input implements Component, Focusable {
215
239
  const firstGrapheme = graphemes[0];
216
240
  this.#cursor += firstGrapheme ? firstGrapheme.segment.length : 1;
217
241
  }
218
- return;
242
+ return true;
219
243
  }
220
244
 
221
245
  if (kb.matches(data, "tui.editor.cursorLineStart")) {
222
246
  this.#lastAction = null;
223
247
  this.#cursor = 0;
224
- return;
248
+ return true;
225
249
  }
226
250
 
227
251
  if (kb.matches(data, "tui.editor.cursorLineEnd")) {
228
252
  this.#lastAction = null;
229
253
  this.#cursor = this.#value.length;
230
- return;
254
+ return true;
231
255
  }
232
256
 
233
257
  if (kb.matches(data, "tui.editor.cursorWordLeft")) {
234
258
  this.#moveWordBackwards();
235
- return;
259
+ return true;
236
260
  }
237
261
 
238
262
  if (kb.matches(data, "tui.editor.cursorWordRight")) {
239
263
  this.#moveWordForwards();
240
- return;
264
+ return true;
241
265
  }
242
266
 
243
267
  // Regular character input, including Kitty CSI-u text-producing sequences.
244
268
  const printableText = extractPrintableText(data);
245
- if (printableText) {
246
- this.#insertCharacter(printableText);
247
- }
269
+ if (!printableText) return false;
270
+ this.#insertCharacter(printableText);
271
+ return true;
248
272
  }
249
273
 
250
274
  /** Apply terminal paste semantics to text from non-bracketed paste transports
@@ -484,6 +508,33 @@ export class Input implements Component, Focusable {
484
508
  // No cached state to invalidate currently
485
509
  }
486
510
 
511
+ /**
512
+ * An `input` with the value, the caret as a UTF-16 offset and the prompt;
513
+ * the terminal draws the caret, scrolls horizontally and places the IME.
514
+ * A masked input sends one bullet per grapheme, with the caret mapped onto
515
+ * them, so the secret never leaves the process.
516
+ */
517
+ describe(_cx: DescribeContext): NativeNode {
518
+ let value = this.#value;
519
+ let cursor = this.#cursor;
520
+ if (this.mask) {
521
+ const graphemes = [...segmenter.segment(this.#value)];
522
+ value = "•".repeat(graphemes.length);
523
+ cursor = graphemes.filter(grapheme => grapheme.index < this.#cursor).length;
524
+ }
525
+ const prompt = plainText(this.prompt);
526
+ const props: TspInputProps = {
527
+ text: value,
528
+ cursor,
529
+ prompt: prompt || undefined,
530
+ placeholder: this.placeholder,
531
+ };
532
+ if (this.#native && sameProps(this.#native.props, props)) return this.#native.node;
533
+ const described = node("input", props);
534
+ this.#native = { props, node: described };
535
+ return described;
536
+ }
537
+
487
538
  render(width: number): readonly string[] {
488
539
  width = Number.isFinite(width) ? Math.max(0, Math.trunc(width)) : 0;
489
540
  // Calculate visible window
@@ -1,3 +1,6 @@
1
+ import { compactText, styleSpans } 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, wrapTextWithAnsi } from "../utils";
3
6
 
@@ -31,6 +34,7 @@ export class KeyValueList implements Component {
31
34
  #rows: readonly KeyValueRow[];
32
35
  readonly #options: KeyValueListOptions;
33
36
  #cache: { width: number; epoch: number; lines: readonly string[] } | undefined;
37
+ #native: NativeNode | undefined;
34
38
 
35
39
  constructor(rows: readonly KeyValueRow[], options: KeyValueListOptions) {
36
40
  this.#rows = rows;
@@ -41,11 +45,25 @@ export class KeyValueList implements Component {
41
45
  if (this.#rows === rows) return false;
42
46
  this.#rows = rows;
43
47
  this.#cache = undefined;
48
+ this.#native = undefined;
44
49
  return true;
45
50
  }
46
51
 
47
52
  invalidate(): void {
48
53
  this.#cache = undefined;
54
+ this.#native = undefined;
55
+ }
56
+
57
+ /** An aligned `kv` grid; label width and value wrapping are the terminal's. */
58
+ describe(_cx: DescribeContext): NativeNode {
59
+ this.#native ??= node("kv", {
60
+ layout: "grid",
61
+ items: this.#rows.map(row => ({
62
+ k: compactText(styleSpans(singleLine(row.label), row.labelStyle)),
63
+ v: compactText(styleSpans(singleLine(row.value), row.valueStyle)),
64
+ })),
65
+ });
66
+ return this.#native;
49
67
  }
50
68
 
51
69
  render(width: number): readonly string[] {
@@ -1,3 +1,4 @@
1
+ import type { TspProps, TspSpace } from "@oh-my-pi/pi-wire";
1
2
  import type { MouseRoutable } from "../../mouse";
2
3
  import type { Component } from "../../tui";
3
4
  import { padding, truncateToWidth, visibleWidth } from "../../utils";
@@ -51,6 +52,49 @@ export interface LayoutAllocation {
51
52
  max?: number;
52
53
  }
53
54
 
55
+ const kNativeAdapter = Symbol("layout.nativeAdapter");
56
+ type AdaptedRenderer = LayoutRenderer & { [kNativeAdapter]?: Component };
57
+
58
+ /**
59
+ * Describe child for layout content: components describe themselves; a lazy
60
+ * row renderer becomes a stable adapter component the native engine renders
61
+ * as a `rows` fallback (it can only produce width-bound rows).
62
+ */
63
+ export function nativeLayoutChild(content: LayoutContent): Component {
64
+ if (typeof content !== "function") return content;
65
+ const adapted = content as AdaptedRenderer;
66
+ adapted[kNativeAdapter] ??= { render: width => content(width, undefined) };
67
+ return adapted[kNativeAdapter];
68
+ }
69
+
70
+ /** Spacing step standing in for a textual gap decoration (the terminal draws no divider glyphs). */
71
+ export function nativeLayoutGap(decoration: LayoutDecoration | undefined): TspSpace | undefined {
72
+ const length = layoutDecorationText(decoration).length;
73
+ if (length === 0) return undefined;
74
+ if (length === 1) return "xs";
75
+ if (length === 2) return "sm";
76
+ return "md";
77
+ }
78
+
79
+ /** Flex props for one layout slot along `axis` (`w` for rows, `h` for stacks). */
80
+ export function nativeSlotProps(
81
+ axis: "w" | "h",
82
+ slot: { fixed?: number; grow?: number; min?: number; max?: number },
83
+ ): TspProps<"col"> {
84
+ const bound = (cells: number): { w: `${number}ch` } | { h: `${number}lines` } =>
85
+ axis === "w" ? { w: `${cells}ch` } : { h: `${cells}lines` };
86
+ const fixed = optionalLayoutSize(slot.fixed);
87
+ if (fixed !== undefined) return { grow: 0, shrink: 0, min: bound(fixed), max: bound(fixed) };
88
+ const min = optionalLayoutSize(slot.min);
89
+ const max = optionalLayoutSize(slot.max);
90
+ return {
91
+ grow: slot.grow === undefined ? undefined : layoutSize(slot.grow),
92
+ basis: "content",
93
+ min: min === undefined || min === 0 ? undefined : bound(min),
94
+ max: max === undefined ? undefined : bound(max),
95
+ };
96
+ }
97
+
54
98
  /** Clamp an external size to a finite, non-negative integer cell count. */
55
99
  export function layoutSize(value: number | undefined, fallback = 0): number {
56
100
  const resolved = value === undefined || !Number.isFinite(value) ? fallback : value;
@@ -1,4 +1,6 @@
1
1
  import type { MouseRoutable, SgrMouseEvent } from "../../mouse";
2
+ import { col, row } from "../../native/describe";
3
+ import type { DescribeContext, NativeNode } from "../../native/node";
2
4
  import type { Component } from "../../tui";
3
5
  import { padding, visibleWidth } from "../../utils";
4
6
  import {
@@ -15,6 +17,9 @@ import {
15
17
  type LayoutInsets,
16
18
  type LayoutRect,
17
19
  layoutSize,
20
+ nativeLayoutChild,
21
+ nativeLayoutGap,
22
+ nativeSlotProps,
18
23
  optionalLayoutSize,
19
24
  renderLayoutContent,
20
25
  uniqueLayoutComponents,
@@ -87,6 +92,7 @@ export class Row implements Component, MouseRoutable {
87
92
  #frames: LayoutRect[] = [];
88
93
  #memo: RowMemo | undefined;
89
94
  #ignoreTight = false;
95
+ #native: { children: RowChild[]; gap: LayoutDecoration | undefined; node: NativeNode } | undefined;
90
96
 
91
97
  constructor(options: RowOptions) {
92
98
  this.#children = options.children.map(child => ({ ...child, padding: { ...child.padding } }));
@@ -168,6 +174,30 @@ export class Row implements Component, MouseRoutable {
168
174
  }
169
175
  }
170
176
 
177
+ /**
178
+ * A flex `row`: each child sits in a slot carrying its width bounds
179
+ * (fixed → min = max `ch`, else grow/min/max). Decorations become gap
180
+ * spacing; clipping and the exact row budget are the terminal's.
181
+ */
182
+ describe(_cx: DescribeContext): NativeNode {
183
+ const cached = this.#native;
184
+ if (cached?.children === this.#children && cached.gap === this.#gap) return cached.node;
185
+ const slots = this.#children.map(child =>
186
+ col(
187
+ [nativeLayoutChild(child.content)],
188
+ nativeSlotProps("w", {
189
+ fixed: child.width,
190
+ grow: child.width === undefined ? layoutSize(child.grow, 1) : undefined,
191
+ min: child.minWidth,
192
+ max: child.maxWidth,
193
+ }),
194
+ ),
195
+ );
196
+ const described = row(slots, { gap: nativeLayoutGap(this.#gap), align: this.#align });
197
+ this.#native = { children: this.#children, gap: this.#gap, node: described };
198
+ return described;
199
+ }
200
+
171
201
  /** Translate a row-local point into a child's local coordinates. */
172
202
  locate(line: number, col: number): RowHit | undefined {
173
203
  for (let index = 0; index < this.#frames.length; index++) {