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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (463) hide show
  1. package/CHANGELOG.md +56 -0
  2. package/dist/types/apps/cleanse-board.d.ts +17 -0
  3. package/dist/types/apps/git/help.d.ts +16 -0
  4. package/dist/types/apps/git/sidebar.d.ts +18 -0
  5. package/dist/types/apps/live-visualizer.d.ts +10 -0
  6. package/dist/types/apps/ps-top.d.ts +15 -0
  7. package/dist/types/apps/standalone-picker.d.ts +3 -0
  8. package/dist/types/autocomplete.d.ts +8 -0
  9. package/dist/types/chat/advisor-message.d.ts +12 -2
  10. package/dist/types/chat/assistant-message.d.ts +20 -0
  11. package/dist/types/chat/bash-execution.d.ts +9 -0
  12. package/dist/types/chat/chat-transcript-builder.d.ts +6 -1
  13. package/dist/types/chat/collab-prompt-message.d.ts +4 -0
  14. package/dist/types/chat/compaction-summary-message.d.ts +15 -0
  15. package/dist/types/chat/eval-execution.d.ts +8 -0
  16. package/dist/types/chat/execution-shared.d.ts +58 -0
  17. package/dist/types/chat/late-diagnostics-message.d.ts +15 -1
  18. package/dist/types/chat/read-tool-group.d.ts +15 -0
  19. package/dist/types/chat/skill-message.d.ts +9 -0
  20. package/dist/types/chat/stripped-tool-calls-placeholder.d.ts +3 -0
  21. package/dist/types/chat/thinking-display.d.ts +2 -0
  22. package/dist/types/chat/todo-reminder.d.ts +4 -1
  23. package/dist/types/chat/tool-execution.d.ts +25 -1
  24. package/dist/types/chat/transcript-actions.d.ts +37 -0
  25. package/dist/types/chat/transcript-entry.d.ts +3 -1
  26. package/dist/types/chat/user-message.d.ts +25 -0
  27. package/dist/types/chrome/collab-qrcode.d.ts +7 -0
  28. package/dist/types/chrome/context-thresholds.d.ts +11 -0
  29. package/dist/types/chrome/countdown-timer.d.ts +9 -3
  30. package/dist/types/chrome/diff.d.ts +12 -0
  31. package/dist/types/chrome/dynamic-border.d.ts +3 -0
  32. package/dist/types/chrome/format.d.ts +6 -0
  33. package/dist/types/chrome/message-divider.d.ts +13 -0
  34. package/dist/types/chrome/message-frame.d.ts +10 -0
  35. package/dist/types/chrome/message-notice.d.ts +23 -0
  36. package/dist/types/chrome/overlay-box.d.ts +11 -2
  37. package/dist/types/chrome/qrcode.d.ts +7 -0
  38. package/dist/types/chrome/segment-track.d.ts +7 -0
  39. package/dist/types/chrome/selector-helpers.d.ts +0 -6
  40. package/dist/types/chrome/status-notice.d.ts +14 -0
  41. package/dist/types/chrome/streaming-panel.d.ts +3 -0
  42. package/dist/types/chrome/tool-activity.d.ts +3 -0
  43. package/dist/types/chrome/transcript-container.d.ts +13 -0
  44. package/dist/types/chrome/transcript-status.d.ts +15 -3
  45. package/dist/types/components/box.d.ts +7 -0
  46. package/dist/types/components/disclosure.d.ts +9 -0
  47. package/dist/types/components/editor.d.ts +42 -0
  48. package/dist/types/components/form.d.ts +14 -0
  49. package/dist/types/components/image.d.ts +7 -0
  50. package/dist/types/components/input.d.ts +20 -1
  51. package/dist/types/components/key-value-list.d.ts +3 -0
  52. package/dist/types/components/layout/geometry.d.ts +16 -0
  53. package/dist/types/components/layout/row.d.ts +7 -0
  54. package/dist/types/components/layout/split-pane.d.ts +8 -0
  55. package/dist/types/components/layout/stack.d.ts +6 -0
  56. package/dist/types/components/loader.d.ts +48 -0
  57. package/dist/types/components/markdown.d.ts +8 -0
  58. package/dist/types/components/metric.d.ts +7 -0
  59. package/dist/types/components/progress-bar.d.ts +3 -0
  60. package/dist/types/components/scroll-view.d.ts +6 -0
  61. package/dist/types/components/section.d.ts +3 -0
  62. package/dist/types/components/select-list.d.ts +33 -0
  63. package/dist/types/components/settings-list.d.ts +68 -0
  64. package/dist/types/components/spacer.d.ts +4 -0
  65. package/dist/types/components/tab-bar.d.ts +9 -0
  66. package/dist/types/components/table.d.ts +7 -0
  67. package/dist/types/components/text.d.ts +6 -0
  68. package/dist/types/components/tree-view.d.ts +9 -0
  69. package/dist/types/components/truncated-text.d.ts +3 -0
  70. package/dist/types/components/wizard-step.d.ts +6 -0
  71. package/dist/types/hotkeys-markdown.d.ts +24 -0
  72. package/dist/types/index.d.ts +5 -0
  73. package/dist/types/latex-block.d.ts +7 -0
  74. package/dist/types/loop-watchdog.d.ts +9 -0
  75. package/dist/types/native/apply.d.ts +39 -0
  76. package/dist/types/native/backend.d.ts +94 -0
  77. package/dist/types/native/blobs.d.ts +29 -0
  78. package/dist/types/native/describe.d.ts +56 -0
  79. package/dist/types/native/encode.d.ts +50 -0
  80. package/dist/types/native/icons.d.ts +18 -0
  81. package/dist/types/native/memo.d.ts +27 -0
  82. package/dist/types/native/node.d.ts +107 -0
  83. package/dist/types/native/overlay.d.ts +64 -0
  84. package/dist/types/native/picker.d.ts +124 -0
  85. package/dist/types/native/reconcile.d.ts +41 -0
  86. package/dist/types/native/settle.d.ts +11 -0
  87. package/dist/types/native/spans.d.ts +21 -0
  88. package/dist/types/native/state.d.ts +6 -0
  89. package/dist/types/native/tone.d.ts +24 -0
  90. package/dist/types/overlays/advisor-config.d.ts +20 -0
  91. package/dist/types/overlays/agent-hub-renderer.d.ts +27 -11
  92. package/dist/types/overlays/agent-hub.d.ts +10 -0
  93. package/dist/types/overlays/agent-transcript-viewer.d.ts +7 -0
  94. package/dist/types/overlays/agents-hub.d.ts +8 -0
  95. package/dist/types/overlays/annotation-overlay.d.ts +3 -0
  96. package/dist/types/overlays/ask-dialog.d.ts +23 -0
  97. package/dist/types/overlays/bordered-loader.d.ts +3 -0
  98. package/dist/types/overlays/btw-history-panel.d.ts +9 -0
  99. package/dist/types/overlays/btw-panel.d.ts +3 -0
  100. package/dist/types/overlays/cleanse-panel.d.ts +5 -2
  101. package/dist/types/overlays/composer-shape-preview.d.ts +7 -1
  102. package/dist/types/overlays/copy-selector.d.ts +38 -0
  103. package/dist/types/overlays/error-banner.d.ts +9 -0
  104. package/dist/types/overlays/extensions/extension-list.d.ts +1 -0
  105. package/dist/types/overlays/history-search.d.ts +18 -0
  106. package/dist/types/overlays/hook-editor.d.ts +28 -1
  107. package/dist/types/overlays/hook-input.d.ts +3 -0
  108. package/dist/types/overlays/hook-selector.d.ts +10 -0
  109. package/dist/types/overlays/hub-frame.d.ts +18 -0
  110. package/dist/types/overlays/jobs-panel.d.ts +24 -0
  111. package/dist/types/overlays/login-dialog.d.ts +10 -0
  112. package/dist/types/overlays/logout-account-selector.d.ts +4 -0
  113. package/dist/types/overlays/mcp-add-wizard.d.ts +4 -0
  114. package/dist/types/overlays/model-browser.d.ts +69 -0
  115. package/dist/types/overlays/model-hub.d.ts +7 -0
  116. package/dist/types/overlays/model-picker.d.ts +9 -1
  117. package/dist/types/overlays/move-overlay.d.ts +3 -0
  118. package/dist/types/overlays/oauth-selector.d.ts +4 -0
  119. package/dist/types/overlays/omfg-panel.d.ts +3 -0
  120. package/dist/types/overlays/pause-screen.d.ts +12 -0
  121. package/dist/types/overlays/plan-review-overlay.d.ts +5 -0
  122. package/dist/types/overlays/plan-save-overlay.d.ts +10 -0
  123. package/dist/types/overlays/plugin-selector.d.ts +4 -0
  124. package/dist/types/overlays/plugin-settings.d.ts +30 -0
  125. package/dist/types/overlays/queue-mode-selector.d.ts +4 -0
  126. package/dist/types/overlays/reset-usage-selector.d.ts +8 -0
  127. package/dist/types/overlays/rewind-selector.d.ts +5 -0
  128. package/dist/types/overlays/session-info-overlay.d.ts +19 -1
  129. package/dist/types/overlays/session-selector.d.ts +64 -1
  130. package/dist/types/overlays/settings-defs.d.ts +14 -0
  131. package/dist/types/overlays/settings-selector.d.ts +21 -0
  132. package/dist/types/overlays/show-images-selector.d.ts +4 -0
  133. package/dist/types/overlays/snapcompact-shape-preview.d.ts +2 -0
  134. package/dist/types/overlays/stats-notice.d.ts +8 -0
  135. package/dist/types/overlays/theme-selector.d.ts +11 -0
  136. package/dist/types/overlays/thinking-selector.d.ts +4 -0
  137. package/dist/types/overlays/tree-selector.d.ts +60 -2
  138. package/dist/types/overlays/usage-dashboard.d.ts +26 -3
  139. package/dist/types/overlays/usage-row.d.ts +36 -2
  140. package/dist/types/prompt/attachment-chips.d.ts +8 -0
  141. package/dist/types/prompt/composer-attachments.d.ts +15 -3
  142. package/dist/types/prompt/composer.d.ts +18 -1
  143. package/dist/types/prompt/custom-editor.d.ts +48 -2
  144. package/dist/types/prompt/editor-top-gap.d.ts +2 -0
  145. package/dist/types/prompt/macos-spelling.d.ts +6 -0
  146. package/dist/types/prompt/magic-keywords.d.ts +9 -0
  147. package/dist/types/prompt/queued-messages.d.ts +23 -0
  148. package/dist/types/prompt/skill-tokens.d.ts +4 -2
  149. package/dist/types/prompt/welcome.d.ts +16 -0
  150. package/dist/types/render/code-cell.d.ts +10 -0
  151. package/dist/types/render/file-list.d.ts +8 -0
  152. package/dist/types/render/hyperlink.d.ts +14 -0
  153. package/dist/types/render/output-block.d.ts +43 -0
  154. package/dist/types/render/output-pane.d.ts +17 -0
  155. package/dist/types/render/tool-card.d.ts +8 -0
  156. package/dist/types/render/tree-list.d.ts +8 -0
  157. package/dist/types/render/width-aware-text.d.ts +7 -0
  158. package/dist/types/setup/scenes/outro.d.ts +7 -0
  159. package/dist/types/setup/scenes/sign-in.d.ts +16 -9
  160. package/dist/types/setup/scenes/splash.d.ts +8 -0
  161. package/dist/types/setup/scenes/types.d.ts +6 -27
  162. package/dist/types/setup/wizard-overlay.d.ts +9 -0
  163. package/dist/types/status-line/component.d.ts +49 -5
  164. package/dist/types/status-line/context-usage.d.ts +20 -0
  165. package/dist/types/status-line/footer.d.ts +8 -0
  166. package/dist/types/status-line/metrics.d.ts +1 -2
  167. package/dist/types/status-line/segments.d.ts +10 -1
  168. package/dist/types/status-line/types.d.ts +40 -1
  169. package/dist/types/stdin-buffer.d.ts +10 -7
  170. package/dist/types/terminal-capabilities.d.ts +1 -1
  171. package/dist/types/terminal.d.ts +36 -0
  172. package/dist/types/theme/defaults/index.d.ts +100 -0
  173. package/dist/types/theme/schema.d.ts +2 -0
  174. package/dist/types/theme/shimmer.d.ts +8 -0
  175. package/dist/types/theme/theme-class.d.ts +2 -0
  176. package/dist/types/theme/theme.d.ts +33 -1
  177. package/dist/types/tools/ask.d.ts +3 -0
  178. package/dist/types/tools/ast-edit.d.ts +10 -1
  179. package/dist/types/tools/ast-grep.d.ts +10 -1
  180. package/dist/types/tools/autoresearch.d.ts +21 -4
  181. package/dist/types/tools/bash.d.ts +30 -1
  182. package/dist/types/tools/cfg-render.d.ts +5 -1
  183. package/dist/types/tools/debug.d.ts +3 -1
  184. package/dist/types/tools/default-renderer.d.ts +6 -1
  185. package/dist/types/tools/edit.d.ts +14 -1
  186. package/dist/types/tools/eval.d.ts +12 -1
  187. package/dist/types/tools/fetch.d.ts +16 -1
  188. package/dist/types/tools/find.d.ts +15 -1
  189. package/dist/types/tools/github.d.ts +3 -0
  190. package/dist/types/tools/glob.d.ts +14 -1
  191. package/dist/types/tools/goal.d.ts +3 -1
  192. package/dist/types/tools/grep.d.ts +14 -1
  193. package/dist/types/tools/grouped-file-output.d.ts +58 -0
  194. package/dist/types/tools/json-tree.d.ts +20 -0
  195. package/dist/types/tools/lsp.d.ts +3 -10
  196. package/dist/types/tools/mcp.d.ts +16 -1
  197. package/dist/types/tools/memory.d.ts +26 -0
  198. package/dist/types/tools/native-view.d.ts +90 -0
  199. package/dist/types/tools/proc-render.d.ts +7 -1
  200. package/dist/types/tools/read.d.ts +23 -1
  201. package/dist/types/tools/renderer.d.ts +57 -0
  202. package/dist/types/tools/report-tool-issue.d.ts +3 -0
  203. package/dist/types/tools/resolve.d.ts +14 -1
  204. package/dist/types/tools/task.d.ts +3 -0
  205. package/dist/types/tools/think.d.ts +3 -0
  206. package/dist/types/tools/todo.d.ts +12 -0
  207. package/dist/types/tools/vibe.d.ts +3 -1
  208. package/dist/types/tools/wait.d.ts +3 -0
  209. package/dist/types/tools/web-search-types.d.ts +90 -0
  210. package/dist/types/tools/web-search.d.ts +9 -168
  211. package/dist/types/tools/write.d.ts +14 -1
  212. package/dist/types/tools/xdev.d.ts +13 -0
  213. package/dist/types/tui.d.ts +49 -0
  214. package/package.json +9 -9
  215. package/src/apps/autoresearch-dashboard.ts +362 -4
  216. package/src/apps/cleanse-board.ts +219 -1
  217. package/src/apps/cleanse-picker.ts +9 -1
  218. package/src/apps/debug/log-viewer.ts +15 -15
  219. package/src/apps/debug/protocol-probe.ts +2 -20
  220. package/src/apps/git/avatar.ts +2 -22
  221. package/src/apps/git/git-tui.ts +406 -59
  222. package/src/apps/git/help.ts +130 -0
  223. package/src/apps/git/sidebar.ts +491 -0
  224. package/src/apps/live-visualizer.ts +65 -0
  225. package/src/apps/ps-top.ts +448 -17
  226. package/src/apps/session-picker.ts +1 -0
  227. package/src/apps/setup-model-picker.ts +5 -1
  228. package/src/apps/standalone-picker.ts +55 -1
  229. package/src/autocomplete.ts +44 -1
  230. package/src/chat/advisor-message.ts +72 -3
  231. package/src/chat/assistant-message.ts +538 -51
  232. package/src/chat/background-tan-message.ts +4 -4
  233. package/src/chat/bash-execution.ts +81 -0
  234. package/src/chat/cache-invalidation-marker.ts +8 -0
  235. package/src/chat/chat-transcript-builder.ts +37 -6
  236. package/src/chat/collab-prompt-message.ts +15 -3
  237. package/src/chat/compaction-summary-message.ts +74 -1
  238. package/src/chat/custom-message.ts +3 -1
  239. package/src/chat/eval-execution.ts +57 -0
  240. package/src/chat/execution-shared.ts +153 -1
  241. package/src/chat/hook-message.ts +1 -0
  242. package/src/chat/late-diagnostics-message.ts +85 -2
  243. package/src/chat/read-tool-group.ts +341 -1
  244. package/src/chat/served-model-marker.ts +8 -0
  245. package/src/chat/skill-message.ts +63 -0
  246. package/src/chat/stripped-tool-calls-placeholder.ts +18 -10
  247. package/src/chat/thinking-display.ts +73 -11
  248. package/src/chat/todo-reminder.ts +34 -4
  249. package/src/chat/tool-execution.ts +385 -10
  250. package/src/chat/transcript-actions.ts +38 -0
  251. package/src/chat/transcript-browser.ts +2 -9
  252. package/src/chat/transcript-entry.ts +10 -1
  253. package/src/chat/transcript-render-helpers.ts +16 -15
  254. package/src/chat/ttsr-notification.ts +38 -1
  255. package/src/chat/user-message.ts +167 -4
  256. package/src/chrome/collab-qrcode.ts +27 -2
  257. package/src/chrome/context-thresholds.ts +34 -0
  258. package/src/chrome/countdown-timer.ts +24 -1
  259. package/src/chrome/diff.ts +60 -1
  260. package/src/chrome/dynamic-border.ts +7 -0
  261. package/src/chrome/format.ts +12 -0
  262. package/src/chrome/message-divider.ts +33 -1
  263. package/src/chrome/message-frame.ts +53 -10
  264. package/src/chrome/message-notice.ts +71 -0
  265. package/src/chrome/overlay-box.ts +45 -2
  266. package/src/chrome/qrcode.ts +28 -0
  267. package/src/chrome/segment-track.ts +26 -0
  268. package/src/chrome/selector-helpers.ts +1 -17
  269. package/src/chrome/status-notice.ts +46 -0
  270. package/src/chrome/streaming-panel.ts +25 -1
  271. package/src/chrome/tool-activity.ts +20 -0
  272. package/src/chrome/transcript-container.ts +139 -17
  273. package/src/chrome/transcript-status.ts +40 -11
  274. package/src/components/box.ts +51 -0
  275. package/src/components/disclosure.ts +33 -0
  276. package/src/components/editor.ts +239 -36
  277. package/src/components/form.ts +93 -9
  278. package/src/components/image.ts +30 -0
  279. package/src/components/input.ts +77 -26
  280. package/src/components/key-value-list.ts +18 -0
  281. package/src/components/layout/geometry.ts +44 -0
  282. package/src/components/layout/row.ts +30 -0
  283. package/src/components/layout/split-pane.ts +65 -0
  284. package/src/components/layout/stack.ts +27 -0
  285. package/src/components/loader.ts +183 -1
  286. package/src/components/markdown.ts +131 -56
  287. package/src/components/metric.ts +39 -0
  288. package/src/components/progress-bar.ts +28 -0
  289. package/src/components/scroll-view.ts +23 -0
  290. package/src/components/section.ts +16 -0
  291. package/src/components/select-list.ts +174 -25
  292. package/src/components/settings-list.ts +264 -7
  293. package/src/components/spacer.ts +17 -0
  294. package/src/components/tab-bar.ts +42 -0
  295. package/src/components/table.ts +34 -0
  296. package/src/components/text.ts +23 -0
  297. package/src/components/tree-view.ts +65 -0
  298. package/src/components/truncated-text.ts +14 -0
  299. package/src/components/wizard-step.ts +18 -9
  300. package/src/debug-server.ts +14 -7
  301. package/src/hotkeys-markdown.ts +251 -69
  302. package/src/index.ts +6 -0
  303. package/src/latex-block.ts +11 -0
  304. package/src/loop-watchdog.ts +38 -10
  305. package/src/native/apply.ts +275 -0
  306. package/src/native/backend.ts +682 -0
  307. package/src/native/blobs.ts +70 -0
  308. package/src/native/describe.ts +124 -0
  309. package/src/native/encode.ts +233 -0
  310. package/src/native/icons.ts +184 -0
  311. package/src/native/memo.ts +64 -0
  312. package/src/native/node.ts +112 -0
  313. package/src/native/overlay.ts +131 -0
  314. package/src/native/picker.ts +333 -0
  315. package/src/native/reconcile.ts +1063 -0
  316. package/src/native/settle.ts +23 -0
  317. package/src/native/spans.ts +308 -0
  318. package/src/native/state.ts +28 -0
  319. package/src/native/tone.ts +79 -0
  320. package/src/overlays/advisor-config.ts +420 -0
  321. package/src/overlays/agent-hub-renderer.ts +155 -18
  322. package/src/overlays/agent-hub.ts +1037 -44
  323. package/src/overlays/agent-transcript-viewer.ts +129 -0
  324. package/src/overlays/agents-hub.ts +688 -53
  325. package/src/overlays/annotation-overlay.ts +391 -52
  326. package/src/overlays/ask-dialog.ts +435 -24
  327. package/src/overlays/bordered-loader.ts +17 -0
  328. package/src/overlays/btw-history-panel.ts +182 -0
  329. package/src/overlays/btw-panel.ts +68 -2
  330. package/src/overlays/cleanse-panel.ts +105 -15
  331. package/src/overlays/codex-reset-fireworks.ts +65 -5
  332. package/src/overlays/composer-shape-preview.ts +26 -1
  333. package/src/overlays/copy-selector.ts +545 -23
  334. package/src/overlays/error-banner.ts +44 -0
  335. package/src/overlays/extensions/extension-list.ts +23 -17
  336. package/src/overlays/history-search.ts +175 -33
  337. package/src/overlays/hook-editor.ts +113 -6
  338. package/src/overlays/hook-input.ts +22 -1
  339. package/src/overlays/hook-selector.ts +311 -22
  340. package/src/overlays/hub-frame.ts +80 -0
  341. package/src/overlays/jobs-panel.ts +112 -0
  342. package/src/overlays/login-dialog.ts +183 -1
  343. package/src/overlays/logout-account-selector.ts +102 -4
  344. package/src/overlays/mcp-add-wizard.ts +232 -53
  345. package/src/overlays/model-browser.ts +681 -22
  346. package/src/overlays/model-hub.ts +1402 -78
  347. package/src/overlays/model-picker.ts +184 -7
  348. package/src/overlays/move-overlay.ts +45 -0
  349. package/src/overlays/oauth-selector.ts +227 -23
  350. package/src/overlays/omfg-panel.ts +86 -2
  351. package/src/overlays/pause-screen.ts +55 -4
  352. package/src/overlays/plan-review-overlay.ts +352 -0
  353. package/src/overlays/plan-save-overlay.ts +45 -0
  354. package/src/overlays/plugin-selector.ts +21 -1
  355. package/src/overlays/plugin-settings.ts +195 -5
  356. package/src/overlays/queue-mode-selector.ts +19 -1
  357. package/src/overlays/reset-usage-selector.ts +110 -40
  358. package/src/overlays/rewind-selector.ts +458 -44
  359. package/src/overlays/session-account-selector.ts +1 -1
  360. package/src/overlays/session-info-overlay.ts +137 -1
  361. package/src/overlays/session-selector.ts +807 -46
  362. package/src/overlays/settings-defs.ts +41 -0
  363. package/src/overlays/settings-selector.ts +585 -33
  364. package/src/overlays/show-images-selector.ts +18 -1
  365. package/src/overlays/snapcompact-shape-preview.ts +48 -3
  366. package/src/overlays/stats-notice.ts +31 -0
  367. package/src/overlays/theme-selector.ts +31 -1
  368. package/src/overlays/thinking-selector.ts +22 -1
  369. package/src/overlays/tree-selector.ts +570 -110
  370. package/src/overlays/usage-dashboard.ts +587 -12
  371. package/src/overlays/usage-row.ts +161 -25
  372. package/src/prompt/attachment-chips.ts +60 -0
  373. package/src/prompt/composer-attachments.ts +31 -9
  374. package/src/prompt/composer.ts +55 -2
  375. package/src/prompt/custom-editor.ts +302 -4
  376. package/src/prompt/editor-top-gap.ts +8 -0
  377. package/src/prompt/macos-spelling.ts +24 -9
  378. package/src/prompt/magic-keywords.ts +22 -1
  379. package/src/prompt/model-mention-autocomplete.ts +1 -0
  380. package/src/prompt/queued-messages.ts +101 -0
  381. package/src/prompt/skill-tokens.ts +5 -3
  382. package/src/prompt/welcome.ts +213 -6
  383. package/src/render/code-cell.ts +103 -2
  384. package/src/render/file-list.ts +29 -0
  385. package/src/render/hyperlink.ts +32 -0
  386. package/src/render/output-block.ts +110 -0
  387. package/src/render/output-pane.ts +37 -0
  388. package/src/render/tool-card.ts +137 -0
  389. package/src/render/tree-list.ts +39 -0
  390. package/src/render/width-aware-text.ts +20 -0
  391. package/src/setup/scenes/composer.ts +29 -0
  392. package/src/setup/scenes/glyph.ts +15 -0
  393. package/src/setup/scenes/model.ts +36 -8
  394. package/src/setup/scenes/outro.ts +28 -1
  395. package/src/setup/scenes/sign-in.ts +138 -24
  396. package/src/setup/scenes/splash.ts +28 -1
  397. package/src/setup/scenes/theme.ts +88 -3
  398. package/src/setup/scenes/types.ts +7 -28
  399. package/src/setup/startup-splash.ts +46 -9
  400. package/src/setup/wizard-overlay.ts +140 -23
  401. package/src/setup/wizard.ts +1 -1
  402. package/src/status-line/component.ts +414 -18
  403. package/src/status-line/context-usage.ts +279 -50
  404. package/src/status-line/footer.ts +94 -1
  405. package/src/status-line/metrics.ts +3 -6
  406. package/src/status-line/segments.ts +463 -97
  407. package/src/status-line/separators.ts +3 -16
  408. package/src/status-line/types.ts +43 -1
  409. package/src/stdin-buffer.ts +27 -21
  410. package/src/terminal-capabilities.ts +7 -0
  411. package/src/terminal.ts +153 -11
  412. package/src/theme/defaults/dark-neon-noir.json +100 -0
  413. package/src/theme/defaults/index.ts +2 -0
  414. package/src/theme/defaults/light-prism.json +5 -5
  415. package/src/theme/defaults/onyx.json +62 -62
  416. package/src/theme/mermaid-cache.ts +10 -1
  417. package/src/theme/schema.ts +15 -0
  418. package/src/theme/shimmer.ts +33 -0
  419. package/src/theme/theme-class.ts +24 -19
  420. package/src/theme/theme.ts +111 -9
  421. package/src/theme/tui-adapters.ts +14 -1
  422. package/src/tools/ask.ts +157 -1
  423. package/src/tools/ast-edit.ts +174 -1
  424. package/src/tools/ast-grep.ts +82 -2
  425. package/src/tools/autoresearch.ts +149 -1
  426. package/src/tools/bash.ts +233 -62
  427. package/src/tools/cfg-render.ts +66 -1
  428. package/src/tools/debug.ts +78 -1
  429. package/src/tools/default-renderer.ts +35 -1
  430. package/src/tools/edit.ts +357 -31
  431. package/src/tools/eval.ts +257 -1
  432. package/src/tools/fetch.ts +102 -3
  433. package/src/tools/find.ts +127 -1
  434. package/src/tools/github.ts +119 -10
  435. package/src/tools/glob.ts +116 -1
  436. package/src/tools/goal.ts +56 -1
  437. package/src/tools/grep.ts +83 -2
  438. package/src/tools/grouped-file-output.ts +205 -0
  439. package/src/tools/json-tree.ts +116 -1
  440. package/src/tools/lsp.ts +318 -1
  441. package/src/tools/mcp.ts +80 -1
  442. package/src/tools/memory.ts +93 -11
  443. package/src/tools/native-view.ts +292 -0
  444. package/src/tools/proc-render.ts +221 -1
  445. package/src/tools/read.ts +199 -4
  446. package/src/tools/renderer.ts +61 -0
  447. package/src/tools/report-tool-issue.ts +7 -1
  448. package/src/tools/resolve.ts +63 -1
  449. package/src/tools/streaming-output.ts +79 -16
  450. package/src/tools/task.ts +406 -4
  451. package/src/tools/think.ts +19 -1
  452. package/src/tools/todo.ts +89 -0
  453. package/src/tools/vibe.ts +230 -2
  454. package/src/tools/wait.ts +181 -1
  455. package/src/tools/web-search-types.ts +99 -0
  456. package/src/tools/web-search.ts +111 -170
  457. package/src/tools/write.ts +144 -5
  458. package/src/tools/xdev.ts +64 -1
  459. package/src/tui.ts +295 -4
  460. package/dist/types/setup/scenes/providers.d.ts +0 -3
  461. package/dist/types/setup/scenes/web-search.d.ts +0 -22
  462. package/src/setup/scenes/providers.ts +0 -110
  463. package/src/setup/scenes/web-search.ts +0 -165
@@ -30,12 +30,18 @@ import type {
30
30
  StatusLineSeparatorStyle,
31
31
  } from "../status-line/schema";
32
32
  import {
33
+ numberSteps,
34
+ numericOption,
33
35
  SETTING_TABS,
36
+ type SubmenuOption,
37
+ TAB_LEADS,
34
38
  TAB_METADATA,
35
39
  type SettingTab,
36
40
  type SettingsHost,
37
41
  type SettingsDisplayEntry,
38
42
  } from "./settings-defs";
43
+ import type { TspPrefsControl, TspPrefsProps } from "@oh-my-pi/pi-wire";
44
+ import { prefsSectionId } from "../components/settings-list";
39
45
  import { getCurrentThemeName, getSelectListTheme, getSettingsListTheme, theme } from "../theme/theme";
40
46
  import { AUTO_THINKING, type ConfiguredThinkingLevel } from "../thinking";
41
47
  import { getTabBarTheme } from "../chrome/shared";
@@ -45,11 +51,108 @@ import { bottomBorder, divider, row, topBorder } from "../chrome/overlay-box";
45
51
  import { PluginSettingsComponent, type PluginSettingsHost } from "./plugin-settings";
46
52
  import { getSettingDef, getSettingsForTab, type SettingDef } from "./settings-defs";
47
53
  import { SnapcompactShapePreview } from "./snapcompact-shape-preview";
54
+ import { themePickerOptions } from "./theme-selector";
55
+ import type { SelectPickerOptions } from "../native/picker";
48
56
  import { getPreset } from "../status-line/presets";
49
57
  import { FormField, SelectFormField, TextFormField } from "../components/form";
50
58
  import { formTheme } from "../chrome/form-theme";
51
59
  import { formatKeyHint, formatKeyHints } from "../app-keybindings";
52
60
  import { editorKey, editorKeys } from "../chrome/keybinding-hints";
61
+ import type { DescribeContext, NativeChild, NativeNode, NativeUiEvent } from "../native/node";
62
+ import { col, node, span, text } from "../native/describe";
63
+ import { actionHint, hintsRow, type NativeHint, overlayCard } from "../native/overlay";
64
+ import { styledSpans } from "../native/spans";
65
+ import { sameItems } from "../native/memo";
66
+ import type { KeyName } from "../key-hint-format";
67
+
68
+ /** The native page id of the plugins tab. */
69
+ const PLUGINS_PAGE = "plugins";
70
+ /** Role of the status-line preview child: the page places it after the status-line section. */
71
+ const PREFS_STATUS_ROLE = "omp.prefs.preview.status";
72
+ /** Role of a sub-editor child without a native control: the page shows it in a card over itself. */
73
+ const PREFS_EDITOR_ROLE = "omp.prefs.editor";
74
+ /** Most choices a native popup menu lists; larger submenus open as a picker over the page. */
75
+ const PREFS_MENU_MAX = 12;
76
+ /** Text settings whose values are paths, ids or commands (drawn in mono). */
77
+ const MONO_TEXT_SETTING = /(path|dir|url|endpoint|host|command|model|id)$/i;
78
+ /** Submenus whose choices preview live while highlighted (the branches of `#createSubmenu`). */
79
+ const PREVIEWED_SETTINGS = new Set([
80
+ "theme.dark",
81
+ "theme.light",
82
+ "statusLine.preset",
83
+ "statusLine.separator",
84
+ "statusLine.contextLine",
85
+ "snapcompact.shape",
86
+ "composer.shape",
87
+ ]);
88
+ /** The byte a cancel key sends: native menu closes go through the same cancel handling as the key. */
89
+ const ESCAPE = "\x1b";
90
+
91
+ /** Footer hint set of the settings overlay, by what currently owns the keys. */
92
+ type SettingsHintMode = "search" | "plugins" | "sections" | "rows" | "rows-sections";
93
+
94
+ /** The settings tab strip; muted tabs (no search matches) keep their place, dimmed. */
95
+ function settingsTabsNode(tabs: readonly Tab[], active: string | undefined): NativeNode {
96
+ return node(
97
+ "tabs",
98
+ {
99
+ items: tabs.map(tab => ({ id: tab.id, label: tab.muted ? [span(tab.label, "muted")] : tab.label })),
100
+ active,
101
+ },
102
+ undefined,
103
+ "tabs",
104
+ );
105
+ }
106
+
107
+ /** Footer hints for `mode`, mirroring the ANSI footer line. */
108
+ function settingsHintsNode(mode: SettingsHintMode): NativeNode {
109
+ const confirmKeys: readonly KeyName[] = actionHint("tui.select.confirm", "")?.keys ?? [];
110
+ const close = actionHint("tui.select.cancel", "close");
111
+ const switchTabs: NativeHint = { keys: ["left", "right"], label: "switch tabs" };
112
+ let hints: (NativeHint | undefined)[];
113
+ switch (mode) {
114
+ case "search":
115
+ hints = [
116
+ actionHint("tui.select.confirm", "change"),
117
+ { keys: ["tab"], label: "jump tabs" },
118
+ actionHint("tui.select.cancel", "exit search"),
119
+ ];
120
+ break;
121
+ case "plugins":
122
+ hints = [{ keys: ["tab"], label: "switch tabs" }, close];
123
+ break;
124
+ case "sections":
125
+ hints = [
126
+ actionHint(["tui.select.up", "tui.select.down"], "jump sections"),
127
+ { keys: ["tab", ...confirmKeys], label: "to settings" },
128
+ switchTabs,
129
+ close,
130
+ ];
131
+ break;
132
+ case "rows":
133
+ case "rows-sections":
134
+ hints = [
135
+ { keys: [...confirmKeys, "space"], label: "change" },
136
+ ...(mode === "rows-sections"
137
+ ? [{ keys: ["tab"], label: "jump sections" } satisfies NativeHint, switchTabs]
138
+ : [{ keys: ["tab"], label: "switch tabs" } satisfies NativeHint]),
139
+ { keys: [], label: "type to search" },
140
+ close,
141
+ ];
142
+ break;
143
+ }
144
+ return hintsRow(hints);
145
+ }
146
+
147
+ interface SettingsNativeMemo {
148
+ prefs?: { signature: string; children: readonly NativeChild[]; node: NativeNode };
149
+ prefsPreview?: { source: string; node: NativeNode };
150
+ tabs?: { tabs: readonly Tab[]; active: string | undefined; node: NativeNode };
151
+ search?: { input: Input; count: number; node: NativeNode };
152
+ preview?: { source: string; node: NativeNode };
153
+ hints: Partial<Record<SettingsHintMode, NativeNode>>;
154
+ root?: { children: readonly NativeChild[]; node: NativeNode };
155
+ }
53
156
 
54
157
  /**
55
158
  * Free-text string setting field backed by the shared text form field.
@@ -95,6 +198,7 @@ function createSettingsSelectField(
95
198
  getPreview?: () => string,
96
199
  footer?: Component,
97
200
  requestRender?: () => void,
201
+ picker?: SelectPickerOptions,
98
202
  ): SelectFormField {
99
203
  return new SelectFormField({
100
204
  theme: formTheme,
@@ -111,6 +215,7 @@ function createSettingsSelectField(
111
215
  hint: ` ${editorKey("tui.select.confirm")} to select · ${editorKey("tui.select.cancel")} to go back`,
112
216
  footer,
113
217
  requestRender,
218
+ picker,
114
219
  });
115
220
  }
116
221
 
@@ -187,6 +292,16 @@ class MultiSelectSubmenu extends Container {
187
292
  this.addChild(this.#field);
188
293
  }
189
294
 
295
+ /** The highlighted option (a native settings page rings it). */
296
+ get prefsOption(): string | undefined {
297
+ return this.#options[this.#cursor]?.value;
298
+ }
299
+
300
+ /** Applies a whole new selection (a native page's chip toggle or drag), as a key toggle or move does. */
301
+ setValues(next: readonly string[]): void {
302
+ this.#apply(next.filter(id => this.#options.some(option => option.value === id)));
303
+ }
304
+
190
305
  #apply(next: string[]): void {
191
306
  this.#value = next;
192
307
  this.#onApply([...next]);
@@ -485,6 +600,8 @@ export interface SettingsCallbacks {
485
600
  onStatusLinePreview?: (settings: StatusLinePreviewSettings) => void;
486
601
  /** Get current rendered status line for inline preview */
487
602
  getStatusLinePreview?: () => string;
603
+ /** Native status bar for the inline preview (TSP terminals dock the bar; its ANSI border geometry doesn't apply) */
604
+ describeStatusLinePreview?: () => NativeNode;
488
605
  /** Called when plugins change */
489
606
  onPluginsChanged?: () => void | Promise<void>;
490
607
  /** Called when settings panel is closed */
@@ -497,6 +614,11 @@ export interface SettingsCallbacks {
497
614
  */
498
615
  export class SettingsSelectorComponent implements Component {
499
616
  #tabBar: TabBar;
617
+ /** The tab bar's current tab list (the bar keeps no public getter). */
618
+ #tabs: Tab[];
619
+ readonly #native: SettingsNativeMemo = { hints: {} };
620
+ /** Changed-setting counts per tab for the native page nav, cleared whenever the items rebuild. */
621
+ readonly #changedCounts = new Map<SettingTab, number>();
500
622
  #currentList: SettingsList | null = null;
501
623
  #searchList: SettingsList | null = null;
502
624
  #pluginComponent: PluginSettingsComponent | null = null;
@@ -526,7 +648,9 @@ export class SettingsSelectorComponent implements Component {
526
648
  this.#sidebarWidth = settingsSidebarWidth(context.settings.entries);
527
649
  // No label prefix (the frame title already says Settings) and no
528
650
  // "(tab to cycle)" hint (folded into the footer hint line).
529
- this.#tabBar = new TabBar("", getSettingsTabs(), getTabBarTheme());
651
+ const tabs = getSettingsTabs();
652
+ this.#tabs = tabs;
653
+ this.#tabBar = new TabBar("", tabs, getTabBarTheme());
530
654
  this.#tabBar.showHint = false;
531
655
  this.#tabBar.onTabChange = () => {
532
656
  const tabId = this.#tabBar.getActiveTab().id as SettingTab | "plugins";
@@ -655,6 +779,437 @@ export class SettingsSelectorComponent implements Component {
655
779
  return out;
656
780
  }
657
781
 
782
+ /**
783
+ * Over a live session the native page is its own sheet: the backend puts
784
+ * it in `layer`, where a terminal with the `aside` feature docks it at the
785
+ * pane's right edge with the transcript beside it. Without `aside` it
786
+ * fills a screen surface instead.
787
+ */
788
+ nativeSheet(cx: DescribeContext): boolean {
789
+ return cx.supports("prefs") && cx.feature("aside");
790
+ }
791
+
792
+ /**
793
+ * The native settings page (`prefs`) when the terminal draws it, else the
794
+ * root card of today's generic composition.
795
+ */
796
+ describe(cx: DescribeContext): NativeNode {
797
+ return cx.supports("prefs") ? this.#describePrefs() : this.#describeCard();
798
+ }
799
+
800
+ /**
801
+ * The settings as a native page: every tab (and the plugins page) in the
802
+ * nav with its changed count, the current tab's sections (or the search
803
+ * results grouped by tab), the keyboard focus and open editor, the
804
+ * status-line preview on Appearance, and editors without a native control
805
+ * (large choice pickers, provider limits) as children over the page.
806
+ */
807
+ #describePrefs(): NativeNode {
808
+ const memo = this.#native;
809
+ const searching = this.#searchList !== null;
810
+ const list = this.#searchList ?? this.#currentList;
811
+ const entries = this.#context.settings.entries;
812
+ const plugins = this.#currentTabId === "plugins" ? this.#pluginComponent : null;
813
+ const pluginPage = plugins?.prefsPage();
814
+ const tab = this.#currentTabId === "plugins" ? undefined : this.#currentTabId;
815
+
816
+ const pages: TspPrefsProps["pages"][number][] = SETTING_TABS.map(id => {
817
+ const changed = this.#changedCount(id);
818
+ return { id, label: TAB_METADATA[id].label, icon: id, changed: changed > 0 ? changed : undefined };
819
+ });
820
+ pages.push({ id: PLUGINS_PAGE, label: "Installed", icon: "plugins", group: "Plugins" });
821
+
822
+ let sections: TspPrefsProps["sections"] = [];
823
+ let focus: string | null = null;
824
+ let editing: TspPrefsProps["editing"] = null;
825
+ const children: NativeChild[] = [];
826
+ if (pluginPage) {
827
+ sections = pluginPage.sections;
828
+ focus = pluginPage.focus;
829
+ editing = pluginPage.editing;
830
+ if (pluginPage.editor) children.push(pluginPage.editor);
831
+ } else if (list) {
832
+ sections = list.prefsSections(
833
+ item => {
834
+ const def = getSettingDef(entries, item.id);
835
+ const group = def?.group ?? "General";
836
+ const id = prefsSectionId(group);
837
+ return searching && def ? { id: `${def.tab}/${id}`, title: group, page: def.tab } : { id, title: group };
838
+ },
839
+ item => this.#prefsControl(item.id, item.currentValue),
840
+ );
841
+ const at = list.prefsFocus();
842
+ focus = at.row ?? (at.section ? prefsSectionId(at.section) : null);
843
+ editing = list.prefsEditing(id => this.#inlineMenu(id));
844
+ const open = list.openSubmenu;
845
+ if (open && !editing) {
846
+ // Pickers describe themselves as a sheet; anything else sits in a card over the page.
847
+ children.push(
848
+ open.component instanceof SelectFormField
849
+ ? open.component
850
+ : col([open.component], { role: PREFS_EDITOR_ROLE }),
851
+ );
852
+ }
853
+ }
854
+
855
+ if (tab === "appearance" && !searching) {
856
+ const source = this.#getStatusPreviewString();
857
+ if (memo.prefsPreview?.source !== source) {
858
+ memo.prefsPreview = {
859
+ source,
860
+ node: node(
861
+ "col",
862
+ { role: PREFS_STATUS_ROLE },
863
+ [
864
+ this.#callbacks.describeStatusLinePreview?.() ??
865
+ text(styledSpans(source), { wrap: "none", truncate: "end" }),
866
+ ],
867
+ "status-preview",
868
+ ),
869
+ };
870
+ }
871
+ children.push(memo.prefsPreview.node);
872
+ }
873
+
874
+ const props: TspPrefsProps = {
875
+ title: "omp settings",
876
+ pages,
877
+ page: searching ? this.#preSearchTabId : this.#currentTabId,
878
+ lead: searching ? undefined : (pluginPage?.lead ?? (tab ? TAB_LEADS[tab] : undefined)),
879
+ query: searching ? this.#searchQuery : undefined,
880
+ cursor: searching ? this.#searchInput.getCursor() : undefined,
881
+ sections,
882
+ focus,
883
+ editing,
884
+ };
885
+ const signature = JSON.stringify(props);
886
+ if (memo.prefs && memo.prefs.signature === signature && sameItems(memo.prefs.children, children)) {
887
+ return memo.prefs.node;
888
+ }
889
+ const root = node("prefs", props, children);
890
+ memo.prefs = { signature, children, node: root };
891
+ return root;
892
+ }
893
+
894
+ /** Settings changed from their default on `tab` (visible ones only); cached until the items rebuild. */
895
+ #changedCount(tab: SettingTab): number {
896
+ const cached = this.#changedCounts.get(tab);
897
+ if (cached !== undefined) return cached;
898
+ let count = 0;
899
+ for (const def of getSettingsForTab(this.#context.settings.entries, tab)) {
900
+ if (def.condition && !def.condition()) continue;
901
+ if (this.#isChanged(def, this.#getCurrentValue(def))) count++;
902
+ }
903
+ this.#changedCounts.set(tab, count);
904
+ return count;
905
+ }
906
+
907
+ /** Whether row `id`'s choices fit its popup menu (else the submenu opens as a picker over the page). */
908
+ #inlineMenu(id: string): boolean {
909
+ const def = getSettingDef(this.#context.settings.entries, id);
910
+ return def?.type !== "submenu" || this.#submenuOptions(def).length <= PREFS_MENU_MAX;
911
+ }
912
+
913
+ /** Setting `id`'s typed control for the native page; `shown` is the list's display value. */
914
+ #prefsControl(id: string, shown: string): TspPrefsControl | undefined {
915
+ const def = getSettingDef(this.#context.settings.entries, id);
916
+ if (!def) return undefined;
917
+ const value = this.#getCurrentValue(def);
918
+ switch (def.type) {
919
+ case "boolean":
920
+ return { k: "switch", on: value === true };
921
+ case "enum":
922
+ return { k: "choice", value: shown, options: def.values.map(v => ({ value: v, label: v })) };
923
+ case "submenu": {
924
+ const labels = numberSteps(def);
925
+ if (labels) {
926
+ const n = Number(value);
927
+ return { k: "number", value: Number.isFinite(n) ? n : -1, labels };
928
+ }
929
+ const options = this.#submenuOptions(def).map(o => ({
930
+ value: o.value,
931
+ label: o.label,
932
+ detail: o.description,
933
+ }));
934
+ // Few short plain choices segment; described ones and live-previewed ones
935
+ // (hovering the menu previews, see #createSubmenu) keep the menu.
936
+ return {
937
+ k: "choice",
938
+ value: shown,
939
+ options,
940
+ style: PREVIEWED_SETTINGS.has(def.path) || options.some(o => o.detail) ? "menu" : "auto",
941
+ mono: options.every(o => o.label === o.value),
942
+ };
943
+ }
944
+ case "text":
945
+ return {
946
+ k: "text",
947
+ value: shown,
948
+ secret: def.secret || undefined,
949
+ mono: !def.secret && MONO_TEXT_SETTING.test(def.path) ? true : undefined,
950
+ };
951
+ case "providerLimits":
952
+ return { k: "action", label: "Edit…", act: "edit" };
953
+ case "multiselect":
954
+ return {
955
+ k: "multi",
956
+ values: Array.isArray(value) ? value.filter((v): v is string => typeof v === "string") : [],
957
+ options: def.options.map(o => ({ value: o.value, label: o.label, detail: o.description })),
958
+ ordered: def.ordered || undefined,
959
+ };
960
+ }
961
+ }
962
+
963
+ /** The default of `def` as its control names it (the changed dot's title). */
964
+ #defaultLabel(def: SettingDef): string {
965
+ const value: unknown = def.defaultValue;
966
+ switch (def.type) {
967
+ case "boolean":
968
+ return value ? "On" : "Off";
969
+ case "submenu": {
970
+ const raw = this.#getSubmenuCurrentValue(def.path, value);
971
+ return this.#submenuOptions(def).find(o => o.value === raw)?.label ?? raw;
972
+ }
973
+ case "multiselect":
974
+ return this.#formatMultiSelectValue(def, value);
975
+ case "text":
976
+ return def.secret ? "" : this.#formatTextInputEditValue(def.path, value);
977
+ default:
978
+ return value === undefined || value === null ? "" : String(value);
979
+ }
980
+ }
981
+
982
+ /**
983
+ * Pointer actions on the native page, each through the code the keys use:
984
+ * a page click switches tabs (leaving a search), a row click moves the
985
+ * selection, a value change goes through the row's cycle, submenu or text
986
+ * field (`null` unsets the setting), menu hovers preview, and closing an
987
+ * open menu cancels it (reverting its preview).
988
+ */
989
+ #handlePrefsEvent(event: NativeUiEvent): void {
990
+ if (this.#currentTabId === "plugins" && this.#pluginComponent) {
991
+ if (event.type === "action" && event.act === "page" && event.value === PLUGINS_PAGE) {
992
+ this.#switchToTab("plugins");
993
+ return;
994
+ }
995
+ if (
996
+ !(event.type === "action" && (event.act === "page" || (event.act === "close" && event.value === undefined)))
997
+ ) {
998
+ this.#pluginComponent.handlePrefsEvent(event);
999
+ return;
1000
+ }
1001
+ }
1002
+ const list = this.#searchList ?? this.#currentList;
1003
+ switch (event.type) {
1004
+ case "action":
1005
+ if (event.act === "page" && event.value) {
1006
+ this.#showPage(event.value);
1007
+ } else if (event.act === "close") {
1008
+ if (event.value === undefined) this.#callbacks.onCancel();
1009
+ else if (list?.openSubmenu?.id === event.value) list.handleInput(ESCAPE);
1010
+ } else if (event.act === "edit" && event.value) {
1011
+ list?.openSubmenuFor(event.value);
1012
+ }
1013
+ return;
1014
+ case "select": {
1015
+ if (!list) return;
1016
+ const eq = event.item.indexOf("=");
1017
+ if (eq >= 0) {
1018
+ const open = list.openSubmenu;
1019
+ if (open?.id !== event.item.slice(0, eq) || !(open.component instanceof SelectFormField)) return;
1020
+ const select = open.component.selectList;
1021
+ select.setSelectedValue(event.item.slice(eq + 1));
1022
+ const item = select.getSelectedItem();
1023
+ if (item) select.onSelectionChange?.(item);
1024
+ return;
1025
+ }
1026
+ if (list.openSubmenu) list.handleInput(ESCAPE);
1027
+ list.selectItem(event.item);
1028
+ return;
1029
+ }
1030
+ case "activate":
1031
+ list?.openSubmenuFor(event.item);
1032
+ return;
1033
+ case "change":
1034
+ if (list) this.#applyPrefsChange(list, event.item, event.value);
1035
+ return;
1036
+ case "toggle":
1037
+ return;
1038
+ }
1039
+ }
1040
+
1041
+ /** Shows page `id` (a tab or the plugins page), leaving a search. */
1042
+ #showPage(id: string): void {
1043
+ if (this.#searchList) this.#endSearch(false);
1044
+ if (id !== this.#currentTabId) this.#tabBar.selectTab(id);
1045
+ }
1046
+
1047
+ /** A pointer value change on row `id`, through the path its keys take (see {@link #handlePrefsEvent}). */
1048
+ #applyPrefsChange(
1049
+ list: SettingsList,
1050
+ id: string,
1051
+ value: boolean | number | string | readonly string[] | null,
1052
+ ): void {
1053
+ const def = getSettingDef(this.#context.settings.entries, id);
1054
+ if (!def) return;
1055
+ if (value === null) {
1056
+ if (list.openSubmenu) list.handleInput(ESCAPE);
1057
+ this.#context.settings.unset(def.path);
1058
+ this.#callbacks.onChange(def.path, this.#context.settings.get(def.path));
1059
+ if (def.tab === "appearance") this.#triggerStatusLinePreview();
1060
+ this.#refreshItems();
1061
+ return;
1062
+ }
1063
+ switch (def.type) {
1064
+ case "boolean":
1065
+ if (typeof value === "boolean") list.applyValue(id, String(value));
1066
+ return;
1067
+ case "enum":
1068
+ if (typeof value === "string") list.applyValue(id, value);
1069
+ return;
1070
+ case "submenu": {
1071
+ const choice =
1072
+ typeof value === "number"
1073
+ ? this.#submenuOptions(def).find(o => numericOption(o.value) === value)?.value
1074
+ : typeof value === "string"
1075
+ ? value
1076
+ : undefined;
1077
+ if (choice === undefined) return;
1078
+ const field = list.openSubmenuFor(id);
1079
+ if (field instanceof SelectFormField) {
1080
+ field.selectList.handleNativeEvent({ type: "activate", key: "", item: choice });
1081
+ }
1082
+ return;
1083
+ }
1084
+ case "text": {
1085
+ if (typeof value !== "string") return;
1086
+ const field = list.openSubmenuFor(id);
1087
+ if (field instanceof TextFormField) {
1088
+ field.setValue(value);
1089
+ field.submit();
1090
+ }
1091
+ return;
1092
+ }
1093
+ case "multiselect": {
1094
+ if (!Array.isArray(value)) return;
1095
+ const open = list.openSubmenu;
1096
+ if (open?.id === id && open.component instanceof MultiSelectSubmenu) {
1097
+ open.component.setValues(value);
1098
+ return;
1099
+ }
1100
+ const next = [...value];
1101
+ this.#context.settings.set(def.path, next);
1102
+ this.#callbacks.onChange(def.path, next);
1103
+ list.updateValue(id, this.#formatMultiSelectValue(def, next));
1104
+ this.#refreshItems();
1105
+ return;
1106
+ }
1107
+ case "providerLimits":
1108
+ return;
1109
+ }
1110
+ }
1111
+
1112
+ /** Rebuilds the visible items after a change made outside a list's own `onChange` (changed flags, conditions). */
1113
+ #refreshItems(): void {
1114
+ if (this.#searchList) this.#setSearchQuery(this.#searchQuery);
1115
+ else if (this.#currentTabId !== "plugins")
1116
+ this.#refreshCurrentTabItems(getSettingsForTab(this.#context.settings.entries, this.#currentTabId));
1117
+ }
1118
+
1119
+ /**
1120
+ * Root card: tab strip, search field while searching, the active content
1121
+ * component (settings list, search results, or plugins), the appearance
1122
+ * status-line preview, and the footer hints.
1123
+ */
1124
+ #describeCard(): NativeNode {
1125
+ const memo = this.#native;
1126
+ const searching = this.#searchList !== null;
1127
+ const children: NativeChild[] = [];
1128
+
1129
+ const active = this.#tabBar.getActiveTab()?.id;
1130
+ if (memo.tabs?.tabs !== this.#tabs || memo.tabs.active !== active) {
1131
+ memo.tabs = { tabs: this.#tabs, active, node: settingsTabsNode(this.#tabs, active) };
1132
+ }
1133
+ children.push(memo.tabs.node);
1134
+
1135
+ if (searching) {
1136
+ const count = this.#searchMatchCount;
1137
+ if (memo.search?.input !== this.#searchInput || memo.search.count !== count) {
1138
+ const countText = count === 1 ? "1 match" : `${count} matches`;
1139
+ memo.search = {
1140
+ input: this.#searchInput,
1141
+ count,
1142
+ node: node(
1143
+ "row",
1144
+ { gap: "xs", align: "center", role: "omp.settings.search" },
1145
+ [
1146
+ text([span(theme.symbol("icon.search"), "accent")]),
1147
+ col([this.#searchInput], { grow: 1 }),
1148
+ text([span(countText, count > 0 ? "dim" : "warning")], { wrap: "none" }),
1149
+ ],
1150
+ "search",
1151
+ ),
1152
+ };
1153
+ }
1154
+ children.push(memo.search.node);
1155
+ }
1156
+
1157
+ const content = this.#searchList ?? this.#currentList ?? this.#pluginComponent;
1158
+ if (content) children.push(content);
1159
+
1160
+ if (!searching && this.#currentTabId === "appearance") {
1161
+ const source = this.#getStatusPreviewString();
1162
+ if (memo.preview?.source !== source) {
1163
+ memo.preview = {
1164
+ source,
1165
+ node: node(
1166
+ "col",
1167
+ { gap: "xs" },
1168
+ [
1169
+ text([span("Preview:", "muted")]),
1170
+ this.#callbacks.describeStatusLinePreview?.() ??
1171
+ text(styledSpans(source), { wrap: "none", truncate: "end" }),
1172
+ ],
1173
+ "preview",
1174
+ ),
1175
+ };
1176
+ }
1177
+ children.push(memo.preview.node);
1178
+ }
1179
+
1180
+ const mode: SettingsHintMode = searching
1181
+ ? "search"
1182
+ : this.#currentTabId === "plugins"
1183
+ ? "plugins"
1184
+ : this.#currentList?.sectionFocused
1185
+ ? "sections"
1186
+ : this.#hasSectionJump
1187
+ ? "rows-sections"
1188
+ : "rows";
1189
+ memo.hints[mode] ??= settingsHintsNode(mode);
1190
+ children.push(memo.hints[mode]);
1191
+
1192
+ if (memo.root && sameItems(memo.root.children, children)) return memo.root.node;
1193
+ const root = overlayCard("omp.overlay.settings", "Settings", children);
1194
+ memo.root = { children, node: root };
1195
+ return root;
1196
+ }
1197
+
1198
+ /**
1199
+ * Native page events (the root node, see {@link #handlePrefsEvent}); in the
1200
+ * generic composition a tab pick does what a tab click does: switch tabs (or
1201
+ * jump, while searching), unless an open submenu owns the pointer.
1202
+ */
1203
+ handleNativeEvent(event: NativeUiEvent): void {
1204
+ if (event.key === "") {
1205
+ this.#handlePrefsEvent(event);
1206
+ return;
1207
+ }
1208
+ if ((event.type !== "select" && event.type !== "activate") || event.key !== "tabs") return;
1209
+ if ((this.#searchList ?? this.#currentList)?.hasOpenSubmenu()) return;
1210
+ this.#tabBar.selectTab(event.item);
1211
+ }
1212
+
658
1213
  /**
659
1214
  * Route an SGR mouse report against the frame geometry of the last render.
660
1215
  * Wheel scrolls the focused list, motion drives the hover highlights (tabs
@@ -762,6 +1317,7 @@ export class SettingsSelectorComponent implements Component {
762
1317
  return;
763
1318
  }
764
1319
  this.#searchQuery = query;
1320
+ this.#changedCounts.clear();
765
1321
 
766
1322
  const counts = new Map<SettingTab, number>();
767
1323
  const items: SettingItem[] = [];
@@ -802,12 +1358,11 @@ export class SettingsSelectorComponent implements Component {
802
1358
 
803
1359
  this.#searchList.setItems(items);
804
1360
  this.#searchMatchCount = total;
805
- this.#tabBar.setTabs(
806
- this.#buildSearchTabs(
807
- counts,
808
- tabResults.map(result => result.tab),
809
- ),
1361
+ this.#tabs = this.#buildSearchTabs(
1362
+ counts,
1363
+ tabResults.map(result => result.tab),
810
1364
  );
1365
+ this.#tabBar.setTabs(this.#tabs);
811
1366
  this.#syncTabBarToSelection(this.#searchList.getSelectedItem());
812
1367
  }
813
1368
 
@@ -825,7 +1380,8 @@ export class SettingsSelectorComponent implements Component {
825
1380
  this.#searchQuery = "";
826
1381
  this.#searchFirstMatch.clear();
827
1382
  this.#searchMatchCount = 0;
828
- this.#tabBar.setTabs(getSettingsTabs(), targetTab);
1383
+ this.#tabs = getSettingsTabs();
1384
+ this.#tabBar.setTabs(this.#tabs, targetTab);
829
1385
  this.#switchToTab(targetTab);
830
1386
  if (selectedDef) {
831
1387
  this.#currentList?.selectItem(selectedDef.path);
@@ -898,12 +1454,14 @@ export class SettingsSelectorComponent implements Component {
898
1454
  }
899
1455
 
900
1456
  const currentValue = this.#getCurrentValue(def);
1457
+ const changed = this.#isChanged(def, currentValue);
901
1458
  const item = {
902
1459
  id: def.path,
903
1460
  label: def.label,
904
1461
  description: def.description,
905
1462
  warning: def.warning,
906
- changed: this.#isChanged(def, currentValue),
1463
+ changed,
1464
+ defaultLabel: changed ? this.#defaultLabel(def) : undefined,
907
1465
  };
908
1466
 
909
1467
  switch (def.type) {
@@ -972,6 +1530,20 @@ export class SettingsSelectorComponent implements Component {
972
1530
  return rawValue;
973
1531
  }
974
1532
 
1533
+ /** A submenu's choices: the declared ones, or the runtime ones (thinking levels, themes, composer shapes). */
1534
+ #submenuOptions(def: SettingDef & { type: "submenu" }): readonly SubmenuOption[] {
1535
+ if (def.path === "defaultThinkingLevel") {
1536
+ // Prepend `auto`; the rest are the model's runtime-supported efforts.
1537
+ const levels: ConfiguredThinkingLevel[] = [AUTO_THINKING, ...this.#context.availableThinkingLevels];
1538
+ return levels.map(level => def.options.find(o => o.value === level) ?? { value: level, label: level });
1539
+ }
1540
+ if (def.path === "theme.dark" || def.path === "theme.light") {
1541
+ return this.#context.availableThemes.map(t => ({ value: t, label: t }));
1542
+ }
1543
+ if (def.path === "composer.shape") return getComposerShapeOptions();
1544
+ return def.options;
1545
+ }
1546
+
975
1547
  /**
976
1548
  * Create a submenu for a submenu-type setting.
977
1549
  */
@@ -980,21 +1552,7 @@ export class SettingsSelectorComponent implements Component {
980
1552
  currentValue: string,
981
1553
  done: (value?: string) => void,
982
1554
  ): Component {
983
- let options = def.options;
984
-
985
- // Special case: inject runtime options for thinking level
986
- if (def.path === "defaultThinkingLevel") {
987
- // Prepend `auto`; the rest are the model's runtime-supported efforts.
988
- const levels: ConfiguredThinkingLevel[] = [AUTO_THINKING, ...this.#context.availableThinkingLevels];
989
- options = levels.map(level => {
990
- const baseOpt = options.find(o => o.value === level);
991
- return baseOpt || { value: level, label: level };
992
- });
993
- } else if (def.path === "theme.dark" || def.path === "theme.light") {
994
- options = this.#context.availableThemes.map(t => ({ value: t, label: t }));
995
- } else if (def.path === "composer.shape") {
996
- options = getComposerShapeOptions();
997
- }
1555
+ const options = this.#submenuOptions(def);
998
1556
  // Preview handlers
999
1557
  let onPreview: ((value: string) => void | Promise<void>) | undefined;
1000
1558
  let onPreviewCancel: (() => void) | undefined;
@@ -1085,6 +1643,7 @@ export class SettingsSelectorComponent implements Component {
1085
1643
  getPreview,
1086
1644
  footer,
1087
1645
  this.#context.requestRender,
1646
+ isThemeSetting ? themePickerOptions(def.label, String(currentValue)) : undefined,
1088
1647
  );
1089
1648
  }
1090
1649
 
@@ -1139,15 +1698,7 @@ export class SettingsSelectorComponent implements Component {
1139
1698
  return entries.map(([provider, limit]) => `${provider}: ${limit}`).join(", ");
1140
1699
  }
1141
1700
 
1142
- #getMultiSelectOptions(def: SettingDef & { type: "multiselect" }) {
1143
- if (def.path !== "providers.webSearchOrder") return def.options;
1144
- const excluded: unknown = this.#context.settings.get("providers.webSearchExclude");
1145
- if (!Array.isArray(excluded)) return def.options;
1146
- return def.options.filter(option => !excluded.includes(option.value));
1147
- }
1148
-
1149
1701
  #createMultiSelect(def: SettingDef & { type: "multiselect" }, done: (value?: string) => void): Container {
1150
- const options = this.#getMultiSelectOptions(def);
1151
1702
  const current: unknown = this.#context.settings.get(def.path);
1152
1703
  const initial = Array.isArray(current)
1153
1704
  ? current.filter((entry): entry is string => typeof entry === "string")
@@ -1155,7 +1706,7 @@ export class SettingsSelectorComponent implements Component {
1155
1706
  return new MultiSelectSubmenu(
1156
1707
  def.label,
1157
1708
  def.description,
1158
- options,
1709
+ def.options,
1159
1710
  initial,
1160
1711
  def.ordered,
1161
1712
  value => {
@@ -1167,7 +1718,7 @@ export class SettingsSelectorComponent implements Component {
1167
1718
  }
1168
1719
 
1169
1720
  #formatMultiSelectValue(def: SettingDef & { type: "multiselect" }, value: unknown): string {
1170
- const options = this.#getMultiSelectOptions(def);
1721
+ const { options } = def;
1171
1722
  const labels = Array.isArray(value)
1172
1723
  ? value.flatMap(entry => {
1173
1724
  if (typeof entry !== "string") return [];
@@ -1278,6 +1829,7 @@ export class SettingsSelectorComponent implements Component {
1278
1829
  * into a new group; groups whose items are all condition-hidden emit none.
1279
1830
  */
1280
1831
  #buildItemsForDefs(defs: SettingDef[]): SettingItem[] {
1832
+ this.#changedCounts.clear();
1281
1833
  const items: SettingItem[] = [];
1282
1834
  let lastGroup: string | undefined;
1283
1835
  for (const def of defs) {