@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
@@ -14,6 +14,11 @@ import { BracketedPasteHandler, decodeReencodedPasteControls } from "../brackete
14
14
  import { canonicalKeyId, getKeybindings, type KeybindingsManager } from "../keybindings";
15
15
  import { extractPrintableText, matchesKey, parseKey } from "../keys";
16
16
  import { KillRing } from "../kill-ring";
17
+ import type { TspEditorDecoration, TspEditorProps, TspTone } from "@oh-my-pi/pi-wire";
18
+ import { col, node } from "../native/describe";
19
+ import { sameItems, sameProps } from "../native/memo";
20
+ import { plainText } from "../native/spans";
21
+ import type { DescribeContext, NativeChild, NativeNode } from "../native/node";
17
22
  import type { SymbolTheme } from "../symbols";
18
23
  import { type Component, CURSOR_MARKER, type Focusable } from "../tui";
19
24
  import {
@@ -52,7 +57,36 @@ export type { EditorBorderStyle, EditorTopBorder };
52
57
 
53
58
  import { type SelectItem, SelectList, type SelectListLayoutOptions, type SelectListTheme } from "./select-list";
54
59
 
60
+ /** The TSP root an {@link Editor.describeLayout} hook builds around the `editor` node. */
61
+ export interface NativeEditorLayout {
62
+ readonly role: string;
63
+ /** Tone of the root (`pending` while a turn runs). */
64
+ readonly tone?: TspTone;
65
+ readonly children: readonly NativeChild[];
66
+ /** Keypath of the `editor` node inside the root (the autocomplete's caret anchor). */
67
+ readonly caret: string;
68
+ }
69
+
70
+ /** Vim mode as the TSP `editor.mode` label. */
71
+ const VIM_MODE_LABELS: Record<VimMode, string> = {
72
+ insert: "INSERT",
73
+ normal: "NORMAL",
74
+ visual: "VISUAL",
75
+ "visual-line": "VISUAL",
76
+ };
77
+
55
78
  const PASSTHROUGH_COLOR = (text: string): string => text;
79
+
80
+ /** Value equality of two decoration lists, so an unchanged set keeps its identity across text edits elsewhere. */
81
+ function sameDecorations(a: readonly TspEditorDecoration[], b: readonly TspEditorDecoration[]): boolean {
82
+ if (a.length !== b.length) return false;
83
+ for (let i = 0; i < a.length; i++) {
84
+ const x = a[i]!;
85
+ const y = b[i]!;
86
+ if (x.from !== y.from || x.to !== y.to || x.s !== y.s || x.fx !== y.fx) return false;
87
+ }
88
+ return true;
89
+ }
56
90
  const MENTION_CONTEXT_RE = /(?:^|\s)\^[^\s]*$/;
57
91
  const AT_TOKEN_RE = /(?:^|\s)(@[^\s]*)$/;
58
92
 
@@ -63,12 +97,12 @@ const AUTOCOMPLETE_SELECT_LIST_LAYOUT: SelectListLayoutOptions = {
63
97
  /**
64
98
  * `@` file lists are narrowed in place (`setFilter(liveToken)`) while a fresh
65
99
  * search runs, so a slow walk never leaves entries that contradict the typed
66
- * token on screen. An emptied list means the refresh is still pending.
100
+ * token on screen. An emptied list means the refresh is still pending; the
101
+ * popup stays open but renders nothing until results arrive.
67
102
  */
68
103
  const AT_FILE_SELECT_LIST_LAYOUT: SelectListLayoutOptions = {
69
104
  ...AUTOCOMPLETE_SELECT_LIST_LAYOUT,
70
105
  filterItems: (items, token) => items.filter(item => atCompletionMatches(token, item.value)),
71
- noMatchText: "Searching…",
72
106
  };
73
107
 
74
108
  const SLASH_COMMAND_SELECT_LIST_LAYOUT: SelectListLayoutOptions = {
@@ -568,7 +602,30 @@ export class Editor implements Component, Focusable {
568
602
  * Width-changing output is allowed on lines without the cursor; it is truncated
569
603
  * to the content width rather than reflowed. Cursor glyphs and inline hints are excluded. */
570
604
  decorateText: ((text: string, context: EditorTextDecorationContext) => string) | undefined;
605
+ /**
606
+ * TSP counterpart of {@link decorateText}: styled ranges over the buffer
607
+ * (UTF-16 offsets into the lines joined by `\n`). Called only when the text
608
+ * changed or the editor was invalidated since the last description.
609
+ */
610
+ describeDecorations: ((lines: readonly string[]) => readonly TspEditorDecoration[]) | undefined;
611
+ /** TSP code language of the buffer (`python`, `bash`): the terminal highlights it in the mono face. */
612
+ describeLanguage: (() => string | undefined) | undefined;
613
+ /**
614
+ * TSP chrome around the `editor` node (the prompt composer's chips, mode
615
+ * chip, send/stop): the root role, the children with `input` placed where
616
+ * the layout wants it, and the input's keypath for the caret-anchored
617
+ * autocomplete. Unset: a plain `omp.editor` column over the input.
618
+ */
619
+ describeLayout: ((input: NativeNode, cx: DescribeContext) => NativeEditorLayout) | undefined;
620
+ /** TSP placeholder for the empty buffer; natively it replaces the rotating ANSI {@link placeholder} hints. */
621
+ nativePlaceholder: string | undefined;
571
622
  #promptGutter: string | undefined;
623
+ /** Bumped by {@link invalidate}: host-side decoration inputs (spelling results, settings) changed. */
624
+ #nativeGeneration = 0;
625
+ #nativeDecor?: { text: string; generation: number; decor: readonly TspEditorDecoration[] | undefined };
626
+ #nativeEditor?: { props: TspEditorProps; node: NativeNode };
627
+ #nativeOverlay?: { list: SelectList; caret: string; node: NativeNode };
628
+ #nativeRoot?: { role: string; tone: TspTone | undefined; children: readonly NativeChild[]; node: NativeNode };
572
629
 
573
630
  // Store last layout width for cursor navigation
574
631
  #lastLayoutWidth: number = 80;
@@ -1032,7 +1089,7 @@ export class Editor implements Component, Focusable {
1032
1089
  }
1033
1090
 
1034
1091
  invalidate(): void {
1035
- // No cached state to invalidate currently
1092
+ this.#nativeGeneration++;
1036
1093
  }
1037
1094
 
1038
1095
  /** Active chrome style; a hidden border collapses every shape to borderless. */
@@ -1539,20 +1596,139 @@ export class Editor implements Component, Focusable {
1539
1596
  if (bottomRow !== undefined) result.push(bottomRow);
1540
1597
 
1541
1598
  // Add autocomplete list if active
1542
- if (this.#autocompleteState && this.#autocompleteList) {
1599
+ const autocompleteList = this.#visibleAutocompleteList();
1600
+ if (autocompleteList) {
1543
1601
  // Clamp the dropdown to the terminal viewport: the editor rows already
1544
1602
  // rendered above plus a small reserve must stay visible.
1545
1603
  const viewportRows = this.viewportRowsProvider?.() || process.stdout.rows || Number(Bun.env.LINES) || 24;
1546
- this.#autocompleteList.setMaxVisible(
1604
+ autocompleteList.setMaxVisible(
1547
1605
  Math.max(3, Math.min(this.#autocompleteMaxVisible, viewportRows - result.length - 2)),
1548
1606
  );
1549
- const autocompleteResult = this.#autocompleteList.render(width);
1550
- result.push(...autocompleteResult);
1607
+ result.push(...autocompleteList.render(width));
1551
1608
  }
1552
1609
 
1553
1610
  return result;
1554
1611
  }
1555
1612
 
1613
+ /**
1614
+ * An `editor` node (key `input`) carrying the buffer, caret, Vim selection
1615
+ * anchor and mode, decorations, ghost completion, placeholder, prompt and
1616
+ * height cap; the frame, wrapping, scrolling and caret are the terminal's,
1617
+ * so composer shapes play no part. An open autocomplete popup follows as an
1618
+ * `overlay` anchored to the caret around the completion list. Typing and
1619
+ * caret moves change only `text` and `cursor` on the same node.
1620
+ */
1621
+ describe(cx: DescribeContext): NativeNode {
1622
+ // Vertical caret motion walks visual rows; the terminal owns the real
1623
+ // wrap, so the surface width is the closest available layout width.
1624
+ this.#lastLayoutWidth = Math.max(1, cx.cols);
1625
+ const { lines, cursorLine, cursorCol } = this.#state;
1626
+ const text = lines.join("\n");
1627
+ let offset = 0;
1628
+ for (let i = 0; i < cursorLine; i++) offset += (lines[i]?.length ?? 0) + 1;
1629
+ const cursor = offset + cursorCol;
1630
+
1631
+ let decor = this.#nativeDecor;
1632
+ if (decor?.text !== text || decor.generation !== this.#nativeGeneration) {
1633
+ const next = this.describeDecorations?.(lines);
1634
+ decor = {
1635
+ text,
1636
+ generation: this.#nativeGeneration,
1637
+ decor:
1638
+ next && next.length > 0
1639
+ ? decor?.decor && sameDecorations(decor.decor, next)
1640
+ ? decor.decor
1641
+ : next
1642
+ : undefined,
1643
+ };
1644
+ this.#nativeDecor = decor;
1645
+ }
1646
+
1647
+ const placeholder = this.#getPlaceholder();
1648
+ const nativePlaceholder =
1649
+ this.nativePlaceholder !== undefined && !this.#autocompleteState && this.#isEditorEmpty()
1650
+ ? this.nativePlaceholder
1651
+ : placeholder && plainText(placeholder).trim();
1652
+ const atLineEnd = cursorCol >= (lines[cursorLine]?.length ?? 0);
1653
+ const ghost = nativePlaceholder || !atLineEnd ? null : this.#getInlineHint();
1654
+ const vim = this.#vim;
1655
+ let anchor: number | null = null;
1656
+ if (vim?.visual && vim.anchor !== null) {
1657
+ let anchorOffset = 0;
1658
+ for (let i = 0; i < vim.anchor.line; i++) anchorOffset += (lines[i]?.length ?? 0) + 1;
1659
+ if (vim.mode === "visual-line") {
1660
+ // Linewise: stretch the anchor to the far edge of its line.
1661
+ const before =
1662
+ vim.anchor.line < cursorLine || (vim.anchor.line === cursorLine && vim.anchor.col <= cursorCol);
1663
+ anchor = before ? anchorOffset : anchorOffset + (lines[vim.anchor.line]?.length ?? 0);
1664
+ } else {
1665
+ anchor = anchorOffset + vim.anchor.col;
1666
+ }
1667
+ }
1668
+ const prompt = this.#promptGutter === undefined ? "" : plainText(this.#promptGutter);
1669
+ const props: TspEditorProps = {
1670
+ text,
1671
+ cursor,
1672
+ anchor: anchor ?? undefined,
1673
+ decor: decor.decor,
1674
+ ghost: ghost ? plainText(ghost) : undefined,
1675
+ placeholder: nativePlaceholder || undefined,
1676
+ prompt: prompt || undefined,
1677
+ mode: vim ? VIM_MODE_LABELS[vim.mode] : undefined,
1678
+ lang: this.describeLanguage?.(),
1679
+ maxLines: this.#maxHeight,
1680
+ };
1681
+ let editor = this.#nativeEditor;
1682
+ if (!editor || !sameProps(editor.props, props)) {
1683
+ editor = { props, node: node("editor", props, undefined, "input") };
1684
+ this.#nativeEditor = editor;
1685
+ }
1686
+
1687
+ const layout = this.describeLayout?.(editor.node, cx) ?? {
1688
+ role: "omp.editor",
1689
+ children: [editor.node],
1690
+ caret: "input",
1691
+ };
1692
+ const children: NativeChild[] = [...layout.children];
1693
+ const list = this.#visibleAutocompleteList();
1694
+ if (list) {
1695
+ list.setNativeMark(this.#autocompleteMark());
1696
+ const overlay = this.#nativeOverlay;
1697
+ if (overlay?.list !== list || overlay.caret !== layout.caret) {
1698
+ this.#nativeOverlay = {
1699
+ list,
1700
+ caret: layout.caret,
1701
+ node: node("overlay", { anchor: { caret: layout.caret }, role: "omp.autocomplete" }, [list], "complete"),
1702
+ };
1703
+ }
1704
+ children.push(this.#nativeOverlay!.node);
1705
+ } else {
1706
+ this.#nativeOverlay = undefined;
1707
+ }
1708
+ const root = this.#nativeRoot;
1709
+ if (root && root.role === layout.role && root.tone === layout.tone && sameItems(root.children, children)) {
1710
+ return root.node;
1711
+ }
1712
+ const described = col(children, layout.tone ? { role: layout.role, tone: layout.tone } : { role: layout.role });
1713
+ this.#nativeRoot = { role: layout.role, tone: layout.tone, children, node: described };
1714
+ return described;
1715
+ }
1716
+
1717
+ /**
1718
+ * The typed text the open popup matches item labels against, for the TSP
1719
+ * `mark`: the completion prefix (the live `@` token while a file list
1720
+ * narrows in place) without its sigil, from its last path segment on.
1721
+ */
1722
+ #autocompleteMark(): string {
1723
+ let typed = this.#autocompletePrefix;
1724
+ if (typed.startsWith("@")) {
1725
+ const line = this.#state.lines[this.#state.cursorLine] ?? "";
1726
+ typed = AT_TOKEN_RE.exec(line.slice(0, this.#state.cursorCol))?.[1] ?? typed;
1727
+ }
1728
+ typed = typed.replace(/^[/@:#$^]"?/, "");
1729
+ return typed.slice(typed.lastIndexOf("/") + 1);
1730
+ }
1731
+
1556
1732
  handleInput(data: string): void {
1557
1733
  // Iterative, not recursive: the bytes trailing a completed bracketed
1558
1734
  // paste (which may themselves contain further pastes) loop back here,
@@ -1651,10 +1827,13 @@ export class Editor implements Component, Focusable {
1651
1827
 
1652
1828
  // Handle autocomplete special keys first (but don't block other input)
1653
1829
  if (this.#autocompleteState && this.#autocompleteList) {
1654
- // Escape - cancel autocomplete
1830
+ // Escape - cancel autocomplete. A hidden popup (empty narrowed `@` list) is
1831
+ // dropped too, so its pending refresh cannot pop up afterward, but the key
1832
+ // falls through: the user never saw anything to dismiss.
1655
1833
  if (kb.matchesCanonical(canonical, "tui.select.cancel")) {
1834
+ const visible = this.isShowingAutocomplete();
1656
1835
  this.#cancelAutocomplete(true);
1657
- return;
1836
+ if (visible) return;
1658
1837
  }
1659
1838
  // Right arrow at end of line accepts the selection like Tab (fish-style).
1660
1839
  // Mid-line, right arrow keeps its cursor-movement role and falls through.
@@ -1720,31 +1899,8 @@ export class Editor implements Component, Focusable {
1720
1899
  // arrow at end of line moves the cursor.
1721
1900
  this.#cancelAutocomplete();
1722
1901
  this.onAutocompleteUpdate?.();
1723
- } else if (this.#autocompleteProvider) {
1724
- const shouldChainAutocomplete =
1725
- this.#isSlashCommandNameAutocompleteSelection() || isDirectoryCompletionValue(selected.value);
1726
- const result = this.#autocompleteProvider.applyCompletion(
1727
- this.#state.lines,
1728
- this.#state.cursorLine,
1729
- this.#state.cursorCol,
1730
- selected,
1731
- this.#autocompletePrefix,
1732
- );
1733
-
1734
- this.#state.lines = result.lines;
1735
- this.#state.cursorLine = result.cursorLine;
1736
- this.#setCursorCol(result.cursorCol);
1737
-
1738
- this.#cancelAutocomplete();
1739
- this.onAutocompleteUpdate?.();
1740
-
1741
- this.#notifyChange();
1742
-
1743
- result.onApplied?.();
1744
-
1745
- if (shouldChainAutocomplete) {
1746
- queueMicrotask(() => void this.#tryTriggerAutocomplete());
1747
- }
1902
+ } else {
1903
+ this.#acceptAutocompleteSelection(selected);
1748
1904
  }
1749
1905
  // Only an accepted candidate consumes the key; an empty list falls through.
1750
1906
  if (selected) return;
@@ -4108,6 +4264,35 @@ export class Editor implements Component, Focusable {
4108
4264
  }
4109
4265
  await this.#queueAutocompleteRequest({ kind: "regular", explicitTab });
4110
4266
  }
4267
+ /** Apply `selected` the way Tab does: complete it in place and chain into directories/commands. */
4268
+ #acceptAutocompleteSelection(selected: SelectItem): void {
4269
+ if (!this.#autocompleteProvider) return;
4270
+ const shouldChainAutocomplete =
4271
+ this.#isSlashCommandNameAutocompleteSelection() || isDirectoryCompletionValue(selected.value);
4272
+ const result = this.#autocompleteProvider.applyCompletion(
4273
+ this.#state.lines,
4274
+ this.#state.cursorLine,
4275
+ this.#state.cursorCol,
4276
+ selected,
4277
+ this.#autocompletePrefix,
4278
+ );
4279
+
4280
+ this.#state.lines = result.lines;
4281
+ this.#state.cursorLine = result.cursorLine;
4282
+ this.#setCursorCol(result.cursorCol);
4283
+
4284
+ this.#cancelAutocomplete();
4285
+ this.onAutocompleteUpdate?.();
4286
+
4287
+ this.#notifyChange();
4288
+
4289
+ result.onApplied?.();
4290
+
4291
+ if (shouldChainAutocomplete) {
4292
+ queueMicrotask(() => void this.#tryTriggerAutocomplete());
4293
+ }
4294
+ }
4295
+
4111
4296
  #createAutocompleteList(
4112
4297
  prefix: string,
4113
4298
  items: Array<{ value: string; label: string; description?: string }>,
@@ -4117,7 +4302,14 @@ export class Editor implements Component, Focusable {
4117
4302
  : prefix.startsWith("@")
4118
4303
  ? AT_FILE_SELECT_LIST_LAYOUT
4119
4304
  : AUTOCOMPLETE_SELECT_LIST_LAYOUT;
4120
- return new SelectList(items, this.#autocompleteMaxVisible, this.#theme.selectList, layout);
4305
+ const list = new SelectList(items, this.#autocompleteMaxVisible, this.#theme.selectList, layout);
4306
+ // A pointer pick on the popup (TSP `select`/`activate`) accepts it like Tab.
4307
+ list.onSelect = item => {
4308
+ if (this.#autocompleteList !== list) return;
4309
+ if (this.#autocompleteState === "assist") this.#applySpellingSuggestion();
4310
+ else this.#acceptAutocompleteSelection(item);
4311
+ };
4312
+ return list;
4121
4313
  }
4122
4314
 
4123
4315
  async #handleTabCompletion(): Promise<void> {
@@ -4246,8 +4438,19 @@ export class Editor implements Component, Focusable {
4246
4438
  }
4247
4439
  }
4248
4440
 
4441
+ /**
4442
+ * Whether an autocomplete popup is on screen. An `@` list narrowed to no match
4443
+ * while its refresh is pending stays open internally but is hidden, so it does
4444
+ * not claim keys (Escape, Vim mode switches) meant for the editor or app.
4445
+ */
4249
4446
  isShowingAutocomplete(): boolean {
4250
- return this.#autocompleteState !== null;
4447
+ return this.#visibleAutocompleteList() !== undefined;
4448
+ }
4449
+
4450
+ /** The open autocomplete list, unless it has no candidate to show. */
4451
+ #visibleAutocompleteList(): SelectList | undefined {
4452
+ if (this.#autocompleteState === null) return undefined;
4453
+ return this.#autocompleteList?.getSelectedItem() ? this.#autocompleteList : undefined;
4251
4454
  }
4252
4455
 
4253
4456
  async #updateAutocomplete(): Promise<void> {
@@ -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;