@oh-my-pi/pi-tui 18.2.4 → 18.2.5

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 (748) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/README.md +43 -0
  3. package/dist/types/app-keybindings.d.ts +395 -0
  4. package/dist/types/apps/autoresearch-dashboard.d.ts +43 -0
  5. package/dist/types/apps/autoresearch-data.d.ts +15 -0
  6. package/dist/types/apps/cleanse-board.d.ts +66 -0
  7. package/dist/types/apps/cleanse-picker.d.ts +23 -0
  8. package/dist/types/apps/debug/log-formatting.d.ts +6 -0
  9. package/dist/types/apps/debug/log-viewer.d.ts +81 -0
  10. package/dist/types/apps/debug/protocol-probe.d.ts +42 -0
  11. package/dist/types/apps/debug/raw-sse-buffer.d.ts +58 -0
  12. package/dist/types/apps/debug/raw-sse.d.ts +26 -0
  13. package/dist/types/apps/debug/terminal-info.d.ts +34 -0
  14. package/dist/types/apps/debug/viewer-frame.d.ts +52 -0
  15. package/dist/types/apps/git/avatar.d.ts +9 -0
  16. package/dist/types/apps/git/colors.d.ts +49 -0
  17. package/dist/types/apps/git/diff-pane.d.ts +188 -0
  18. package/dist/types/apps/git/git-tui.d.ts +66 -0
  19. package/dist/types/apps/git/sidebar.d.ts +149 -0
  20. package/dist/types/apps/git/state.d.ts +117 -0
  21. package/dist/types/apps/if-bench-board.d.ts +75 -0
  22. package/dist/types/apps/live-visualizer.d.ts +33 -0
  23. package/dist/types/apps/ps-data.d.ts +51 -0
  24. package/dist/types/apps/ps-top.d.ts +33 -0
  25. package/dist/types/apps/session-picker.d.ts +26 -0
  26. package/dist/types/apps/setup-model-picker.d.ts +13 -0
  27. package/dist/types/apps/standalone-picker.d.ts +42 -0
  28. package/dist/types/autocomplete.d.ts +4 -0
  29. package/dist/types/chat/advisor-message.d.ts +11 -0
  30. package/dist/types/chat/assistant-message.d.ts +106 -0
  31. package/dist/types/chat/background-tan-message.d.ts +9 -0
  32. package/dist/types/chat/bash-execution.d.ts +47 -0
  33. package/dist/types/chat/cache-invalidation-marker.d.ts +50 -0
  34. package/dist/types/chat/chat-transcript-builder.d.ts +50 -0
  35. package/dist/types/chat/collab-prompt-message.d.ts +10 -0
  36. package/dist/types/chat/compaction-summary-message.d.ts +54 -0
  37. package/dist/types/chat/custom-message.d.ts +10 -0
  38. package/dist/types/chat/display-preferences.d.ts +13 -0
  39. package/dist/types/chat/eval-execution.d.ts +29 -0
  40. package/dist/types/chat/execution-shared.d.ts +51 -0
  41. package/dist/types/chat/extension-types.d.ts +28 -0
  42. package/dist/types/chat/hook-message.d.ts +10 -0
  43. package/dist/types/chat/image-loading.d.ts +46 -0
  44. package/dist/types/chat/late-diagnostics-message.d.ts +21 -0
  45. package/dist/types/chat/messages.d.ts +189 -0
  46. package/dist/types/chat/reaction.d.ts +30 -0
  47. package/dist/types/chat/read-target.d.ts +4 -0
  48. package/dist/types/chat/read-tool-group.d.ts +67 -0
  49. package/dist/types/chat/served-model-marker.d.ts +47 -0
  50. package/dist/types/chat/skill-message.d.ts +33 -0
  51. package/dist/types/chat/skill-title-input.d.ts +14 -0
  52. package/dist/types/chat/stripped-tool-calls-placeholder.d.ts +12 -0
  53. package/dist/types/chat/thinking-display.d.ts +12 -0
  54. package/dist/types/chat/todo-reminder.d.ts +13 -0
  55. package/dist/types/chat/tool-execution.d.ts +138 -0
  56. package/dist/types/chat/transcript-browser.d.ts +93 -0
  57. package/dist/types/chat/transcript-entry.d.ts +45 -0
  58. package/dist/types/chat/transcript-outline.d.ts +86 -0
  59. package/dist/types/chat/transcript-render-helpers.d.ts +92 -0
  60. package/dist/types/chat/ttsr-notification.d.ts +23 -0
  61. package/dist/types/chat/user-message.d.ts +52 -0
  62. package/dist/types/chrome/chat-block.d.ts +64 -0
  63. package/dist/types/chrome/collab-qrcode.d.ts +28 -0
  64. package/dist/types/chrome/context-thresholds.d.ts +10 -0
  65. package/dist/types/chrome/countdown-timer.d.ts +14 -0
  66. package/dist/types/chrome/diff.d.ts +14 -0
  67. package/dist/types/chrome/dynamic-border.d.ts +16 -0
  68. package/dist/types/chrome/error-block.d.ts +17 -0
  69. package/dist/types/chrome/form-theme.d.ts +3 -0
  70. package/dist/types/chrome/format.d.ts +12 -0
  71. package/dist/types/chrome/index.d.ts +20 -0
  72. package/dist/types/chrome/keybinding-hints.d.ts +40 -0
  73. package/dist/types/chrome/live-board.d.ts +23 -0
  74. package/dist/types/chrome/local-date.d.ts +4 -0
  75. package/dist/types/chrome/message-divider.d.ts +22 -0
  76. package/dist/types/chrome/message-frame.d.ts +52 -0
  77. package/dist/types/chrome/message-notice.d.ts +35 -0
  78. package/dist/types/chrome/overlay-box.d.ts +74 -0
  79. package/dist/types/chrome/qrcode.d.ts +48 -0
  80. package/dist/types/chrome/segment-track.d.ts +27 -0
  81. package/dist/types/chrome/select-list-mouse-routing.d.ts +4 -0
  82. package/dist/types/chrome/selector-helpers.d.ts +52 -0
  83. package/dist/types/chrome/shared.d.ts +5 -0
  84. package/dist/types/chrome/streaming-panel.d.ts +19 -0
  85. package/dist/types/chrome/tool-activity.d.ts +17 -0
  86. package/dist/types/chrome/transcript-container.d.ts +129 -0
  87. package/dist/types/chrome/transcript-status.d.ts +12 -0
  88. package/dist/types/chrome/visual-truncate.d.ts +19 -0
  89. package/dist/types/components/disclosure.d.ts +42 -0
  90. package/dist/types/components/form.d.ts +130 -0
  91. package/dist/types/components/key-value-list.d.ts +24 -0
  92. package/dist/types/components/layout/geometry.d.ts +73 -0
  93. package/dist/types/components/layout/row.d.ts +55 -0
  94. package/dist/types/components/layout/split-pane.d.ts +80 -0
  95. package/dist/types/components/layout/stack.d.ts +50 -0
  96. package/dist/types/components/menu-selection.d.ts +71 -0
  97. package/dist/types/components/metric.d.ts +44 -0
  98. package/dist/types/components/progress-bar.d.ts +36 -0
  99. package/dist/types/components/scroll-view.d.ts +53 -19
  100. package/dist/types/components/scroll-viewport.d.ts +46 -0
  101. package/dist/types/components/section.d.ts +19 -0
  102. package/dist/types/components/select-list.d.ts +46 -2
  103. package/dist/types/components/settings-list.d.ts +20 -0
  104. package/dist/types/components/table.d.ts +32 -0
  105. package/dist/types/components/tree-view.d.ts +130 -0
  106. package/dist/types/components/wizard-step.d.ts +60 -0
  107. package/dist/types/hotkeys-markdown.d.ts +7 -0
  108. package/dist/types/index.d.ts +17 -0
  109. package/dist/types/keybinding-matchers.d.ts +35 -0
  110. package/dist/types/keys.d.ts +2 -2
  111. package/dist/types/lang-from-path.d.ts +9 -0
  112. package/dist/types/overlays/advisor-config.d.ts +114 -0
  113. package/dist/types/overlays/agent-activity.d.ts +41 -0
  114. package/dist/types/overlays/agent-hub-projection.d.ts +33 -0
  115. package/dist/types/overlays/agent-hub-renderer.d.ts +53 -0
  116. package/dist/types/overlays/agent-hub-types.d.ts +99 -0
  117. package/dist/types/overlays/agent-hub.d.ts +126 -0
  118. package/dist/types/overlays/agent-transcript-viewer.d.ts +71 -0
  119. package/dist/types/overlays/agents-hub.d.ts +72 -0
  120. package/dist/types/overlays/ask-dialog.d.ts +88 -0
  121. package/dist/types/overlays/bordered-loader.d.ts +11 -0
  122. package/dist/types/overlays/btw-history-panel.d.ts +29 -0
  123. package/dist/types/overlays/btw-history.d.ts +17 -0
  124. package/dist/types/overlays/btw-panel.d.ts +26 -0
  125. package/dist/types/overlays/cleanse-panel.d.ts +36 -0
  126. package/dist/types/overlays/codex-reset-fireworks.d.ts +52 -0
  127. package/dist/types/overlays/composer-shape-preview.d.ts +47 -0
  128. package/dist/types/overlays/composer-shape-registry.d.ts +50 -0
  129. package/dist/types/overlays/copy-selector.d.ts +48 -0
  130. package/dist/types/overlays/copy-targets.d.ts +63 -0
  131. package/dist/types/overlays/error-banner.d.ts +13 -0
  132. package/dist/types/overlays/extensions/display-text.d.ts +10 -0
  133. package/dist/types/overlays/extensions/extension-dashboard.d.ts +63 -0
  134. package/dist/types/overlays/extensions/extension-list.d.ts +54 -0
  135. package/dist/types/overlays/extensions/inspector-model.d.ts +109 -0
  136. package/dist/types/overlays/extensions/inspector-panel.d.ts +17 -0
  137. package/dist/types/overlays/extensions/live-tool-session.d.ts +28 -0
  138. package/dist/types/overlays/extensions/mcp-runtime.d.ts +107 -0
  139. package/dist/types/overlays/extensions/state-manager.d.ts +42 -0
  140. package/dist/types/overlays/extensions/types.d.ts +165 -0
  141. package/dist/types/overlays/history-search.d.ts +16 -0
  142. package/dist/types/overlays/hook-editor.d.ts +44 -0
  143. package/dist/types/overlays/hook-input.d.ts +20 -0
  144. package/dist/types/overlays/hook-selector.d.ts +71 -0
  145. package/dist/types/overlays/hub-frame.d.ts +77 -0
  146. package/dist/types/overlays/login-dialog.d.ts +43 -0
  147. package/dist/types/overlays/logout-account-selector.d.ts +15 -0
  148. package/dist/types/overlays/mcp-add-wizard.d.ts +87 -0
  149. package/dist/types/overlays/model-browser.d.ts +213 -0
  150. package/dist/types/overlays/model-hub.d.ts +72 -0
  151. package/dist/types/overlays/model-picker.d.ts +74 -0
  152. package/dist/types/overlays/model-selector.d.ts +52 -0
  153. package/dist/types/overlays/move-overlay.d.ts +39 -0
  154. package/dist/types/overlays/oauth-selector.d.ts +41 -0
  155. package/dist/types/overlays/omfg-panel.d.ts +20 -0
  156. package/dist/types/overlays/pause-screen.d.ts +43 -0
  157. package/dist/types/overlays/plan-review-overlay.d.ts +89 -0
  158. package/dist/types/overlays/plan-save-overlay.d.ts +19 -0
  159. package/dist/types/overlays/plan-toc.d.ts +41 -0
  160. package/dist/types/overlays/plugin-selector.d.ts +28 -0
  161. package/dist/types/overlays/plugin-settings.d.ts +180 -0
  162. package/dist/types/overlays/queue-mode-selector.d.ts +11 -0
  163. package/dist/types/overlays/reset-usage-selector.d.ts +23 -0
  164. package/dist/types/overlays/rewind-selector.d.ts +61 -0
  165. package/dist/types/overlays/running-subagent-badge.d.ts +6 -0
  166. package/dist/types/overlays/session-account-selector.d.ts +24 -0
  167. package/dist/types/overlays/session-info-overlay.d.ts +17 -0
  168. package/dist/types/overlays/session-observer-registry.d.ts +77 -0
  169. package/dist/types/overlays/session-selector.d.ts +131 -0
  170. package/dist/types/overlays/settings-defs.d.ts +119 -0
  171. package/dist/types/overlays/settings-selector.d.ts +73 -0
  172. package/dist/types/overlays/show-images-selector.d.ts +11 -0
  173. package/dist/types/overlays/snapcompact-shape-preview.d.ts +31 -0
  174. package/dist/types/overlays/theme-selector.d.ts +12 -0
  175. package/dist/types/overlays/thinking-selector.d.ts +12 -0
  176. package/dist/types/overlays/tiny-title-download-progress.d.ts +21 -0
  177. package/dist/types/overlays/tree-selector.d.ts +92 -0
  178. package/dist/types/overlays/usage-dashboard.d.ts +94 -0
  179. package/dist/types/overlays/usage-display.d.ts +5 -0
  180. package/dist/types/overlays/usage-row.d.ts +17 -0
  181. package/dist/types/prompt/attachment-chips.d.ts +19 -0
  182. package/dist/types/prompt/composer-attachments.d.ts +71 -0
  183. package/dist/types/prompt/composer-cache.d.ts +37 -0
  184. package/dist/types/prompt/composer.d.ts +159 -0
  185. package/dist/types/prompt/custom-editor.d.ts +261 -0
  186. package/dist/types/prompt/editor-top-gap.d.ts +18 -0
  187. package/dist/types/prompt/emoji-autocomplete.d.ts +16 -0
  188. package/dist/types/prompt/github-ref-autocomplete.d.ts +35 -0
  189. package/dist/types/prompt/gradient-highlight.d.ts +32 -0
  190. package/dist/types/prompt/image-format.d.ts +4 -0
  191. package/dist/types/prompt/image-references.d.ts +26 -0
  192. package/dist/types/prompt/interactive-context-helpers.d.ts +43 -0
  193. package/dist/types/prompt/internal-url-autocomplete.d.ts +56 -0
  194. package/dist/types/prompt/macos-spelling.d.ts +44 -0
  195. package/dist/types/prompt/magic-keyword-boundary.d.ts +9 -0
  196. package/dist/types/prompt/magic-keywords.d.ts +26 -0
  197. package/dist/types/prompt/markdown-prose.d.ts +15 -0
  198. package/dist/types/prompt/model-mention-autocomplete.d.ts +25 -0
  199. package/dist/types/prompt/model-mention-syntax.d.ts +24 -0
  200. package/dist/types/prompt/orchestrate.d.ts +13 -0
  201. package/dist/types/prompt/prompt-action-autocomplete.d.ts +56 -0
  202. package/dist/types/prompt/queue-input.d.ts +8 -0
  203. package/dist/types/prompt/skill-tokens.d.ts +13 -0
  204. package/dist/types/prompt/tools-markdown.d.ts +12 -0
  205. package/dist/types/prompt/ultrathink.d.ts +13 -0
  206. package/dist/types/prompt/usage-amounts.d.ts +26 -0
  207. package/dist/types/prompt/video.d.ts +18 -0
  208. package/dist/types/prompt/welcome.d.ts +83 -0
  209. package/dist/types/prompt/workflow.d.ts +13 -0
  210. package/dist/types/render/code-cell.d.ts +51 -0
  211. package/dist/types/render/file-list.d.ts +22 -0
  212. package/dist/types/render/hyperlink.d.ts +80 -0
  213. package/dist/types/render/index.d.ts +16 -0
  214. package/dist/types/render/output-block.d.ts +54 -0
  215. package/dist/types/render/output-pane.d.ts +80 -0
  216. package/dist/types/render/render-utils.d.ts +312 -0
  217. package/dist/types/render/sixel.d.ts +21 -0
  218. package/dist/types/render/status-line.d.ts +23 -0
  219. package/dist/types/render/tool-card.d.ts +71 -0
  220. package/dist/types/render/tree-list.d.ts +29 -0
  221. package/dist/types/render/types.d.ts +17 -0
  222. package/dist/types/render/utils.d.ts +43 -0
  223. package/dist/types/render/width-aware-text.d.ts +23 -0
  224. package/dist/types/setup/lazy.d.ts +3 -0
  225. package/dist/types/setup/scenes/composer.d.ts +3 -0
  226. package/dist/types/setup/scenes/glyph.d.ts +3 -0
  227. package/dist/types/setup/scenes/model.d.ts +3 -0
  228. package/dist/types/setup/scenes/outro.d.ts +2 -0
  229. package/dist/types/setup/scenes/providers.d.ts +3 -0
  230. package/dist/types/setup/scenes/sign-in.d.ts +21 -0
  231. package/dist/types/setup/scenes/splash.d.ts +11 -0
  232. package/dist/types/setup/scenes/theme.d.ts +3 -0
  233. package/dist/types/setup/scenes/types.d.ts +110 -0
  234. package/dist/types/setup/scenes/web-search.d.ts +22 -0
  235. package/dist/types/setup/setup-version.d.ts +11 -0
  236. package/dist/types/setup/startup-splash.d.ts +9 -0
  237. package/dist/types/setup/wizard-overlay.d.ts +16 -0
  238. package/dist/types/setup/wizard.d.ts +25 -0
  239. package/dist/types/status-line/component.d.ts +182 -0
  240. package/dist/types/status-line/context-usage.d.ts +156 -0
  241. package/dist/types/status-line/footer.d.ts +30 -0
  242. package/dist/types/status-line/git-utils.d.ts +22 -0
  243. package/dist/types/status-line/host.d.ts +129 -0
  244. package/dist/types/status-line/index.d.ts +6 -0
  245. package/dist/types/status-line/loop.d.ts +18 -0
  246. package/dist/types/status-line/metrics.d.ts +21 -0
  247. package/dist/types/status-line/presets.d.ts +3 -0
  248. package/dist/types/status-line/schema.d.ts +15 -0
  249. package/dist/types/status-line/segments.d.ts +5 -0
  250. package/dist/types/status-line/separators.d.ts +3 -0
  251. package/dist/types/status-line/types.d.ts +236 -0
  252. package/dist/types/stdin-buffer.d.ts +4 -0
  253. package/dist/types/theme/color.d.ts +24 -0
  254. package/dist/types/theme/defaults/index.d.ts +9489 -0
  255. package/dist/types/theme/index.d.ts +4 -0
  256. package/dist/types/theme/loader.d.ts +23 -0
  257. package/dist/types/theme/mermaid-cache.d.ts +26 -0
  258. package/dist/types/theme/schema-validation.d.ts +3 -0
  259. package/dist/types/theme/schema.d.ts +25 -0
  260. package/dist/types/theme/session-color.d.ts +43 -0
  261. package/dist/types/theme/shimmer.d.ts +44 -0
  262. package/dist/types/theme/symbols.d.ts +34 -0
  263. package/dist/types/theme/theme-class.d.ts +299 -0
  264. package/dist/types/theme/theme.d.ts +111 -0
  265. package/dist/types/theme/tui-adapters.d.ts +30 -0
  266. package/dist/types/thinking.d.ts +165 -0
  267. package/dist/types/tools/agent-tree.d.ts +36 -0
  268. package/dist/types/tools/ask.d.ts +67 -0
  269. package/dist/types/tools/ast-edit.d.ts +53 -0
  270. package/dist/types/tools/ast-grep.d.ts +52 -0
  271. package/dist/types/tools/autoresearch.d.ts +156 -0
  272. package/dist/types/tools/bash-interactive.d.ts +28 -0
  273. package/dist/types/tools/bash.d.ts +103 -0
  274. package/dist/types/tools/conflict-detect.d.ts +43 -0
  275. package/dist/types/tools/debug.d.ts +60 -0
  276. package/dist/types/tools/default-renderer.d.ts +24 -0
  277. package/dist/types/tools/edit.d.ts +158 -0
  278. package/dist/types/tools/eval-format/code-mode-declarations.d.ts +5 -0
  279. package/dist/types/tools/eval-format/index.d.ts +5 -0
  280. package/dist/types/tools/eval-format/javascript.d.ts +2 -0
  281. package/dist/types/tools/eval-format/python.d.ts +2 -0
  282. package/dist/types/tools/eval.d.ts +94 -0
  283. package/dist/types/tools/fetch.d.ts +42 -0
  284. package/dist/types/tools/gh-format.d.ts +8 -0
  285. package/dist/types/tools/github.d.ts +144 -0
  286. package/dist/types/tools/glob.d.ts +44 -0
  287. package/dist/types/tools/goal.d.ts +42 -0
  288. package/dist/types/tools/grep.d.ts +64 -0
  289. package/dist/types/tools/grouped-file-output.d.ts +71 -0
  290. package/dist/types/tools/hashline-format.d.ts +28 -0
  291. package/dist/types/tools/hub.d.ts +340 -0
  292. package/dist/types/tools/index.d.ts +4 -0
  293. package/dist/types/tools/json-tree.d.ts +59 -0
  294. package/dist/types/tools/line-ranges.d.ts +29 -0
  295. package/dist/types/tools/list-limit.d.ts +15 -0
  296. package/dist/types/tools/lsp.d.ts +72 -0
  297. package/dist/types/tools/match-line-format.d.ts +11 -0
  298. package/dist/types/tools/mcp.d.ts +81 -0
  299. package/dist/types/tools/memory.d.ts +67 -0
  300. package/dist/types/tools/output-meta.d.ts +151 -0
  301. package/dist/types/tools/read.d.ts +139 -0
  302. package/dist/types/tools/renderer.d.ts +98 -0
  303. package/dist/types/tools/report-tool-issue.d.ts +8 -0
  304. package/dist/types/tools/resolve.d.ts +47 -0
  305. package/dist/types/tools/streaming-output.d.ts +282 -0
  306. package/dist/types/tools/subprocess.d.ts +24 -0
  307. package/dist/types/tools/task-repair-args.d.ts +53 -0
  308. package/dist/types/tools/task-yield-assembly.d.ts +20 -0
  309. package/dist/types/tools/task.d.ts +425 -0
  310. package/dist/types/tools/terminal-output.d.ts +8 -0
  311. package/dist/types/tools/think.d.ts +13 -0
  312. package/dist/types/tools/todo.d.ts +118 -0
  313. package/dist/types/tools/tool-errors.d.ts +7 -0
  314. package/dist/types/tools/vibe.d.ts +120 -0
  315. package/dist/types/tools/web-search.d.ts +195 -0
  316. package/dist/types/tools/write.d.ts +76 -0
  317. package/dist/types/tools/xd-url.d.ts +13 -0
  318. package/dist/types/tools/xdev.d.ts +52 -0
  319. package/dist/types/tui.d.ts +18 -0
  320. package/dist/types/utils.d.ts +2 -0
  321. package/package.json +37 -3
  322. package/src/app-keybindings.ts +760 -0
  323. package/src/apps/autoresearch-dashboard.ts +500 -0
  324. package/src/apps/autoresearch-data.ts +64 -0
  325. package/src/apps/cleanse-board.ts +341 -0
  326. package/src/apps/cleanse-picker.ts +57 -0
  327. package/src/apps/debug/log-formatting.ts +53 -0
  328. package/src/apps/debug/log-viewer.ts +868 -0
  329. package/src/apps/debug/protocol-probe.ts +267 -0
  330. package/src/apps/debug/raw-sse-buffer.ts +449 -0
  331. package/src/apps/debug/raw-sse.ts +282 -0
  332. package/src/apps/debug/terminal-info.ts +115 -0
  333. package/src/apps/debug/viewer-frame.ts +174 -0
  334. package/src/apps/git/avatar.ts +52 -0
  335. package/src/apps/git/colors.ts +109 -0
  336. package/src/apps/git/diff-pane.ts +1512 -0
  337. package/src/apps/git/git-tui.ts +889 -0
  338. package/src/apps/git/sidebar.ts +1204 -0
  339. package/src/apps/git/state.ts +113 -0
  340. package/src/apps/if-bench-board.ts +324 -0
  341. package/src/apps/live-visualizer.ts +238 -0
  342. package/src/apps/ps-data.ts +116 -0
  343. package/src/apps/ps-top.ts +422 -0
  344. package/src/apps/session-picker.ts +95 -0
  345. package/src/apps/setup-model-picker.ts +21 -0
  346. package/src/apps/standalone-picker.ts +111 -0
  347. package/src/autocomplete.ts +9 -13
  348. package/src/chat/advisor-message.ts +175 -0
  349. package/src/chat/assistant-message.ts +1214 -0
  350. package/src/chat/background-tan-message.ts +35 -0
  351. package/src/chat/bash-execution.ts +394 -0
  352. package/src/chat/cache-invalidation-marker.ts +94 -0
  353. package/src/chat/chat-transcript-builder.ts +593 -0
  354. package/src/chat/collab-prompt-message.ts +34 -0
  355. package/src/chat/compaction-summary-message.ts +261 -0
  356. package/src/chat/custom-message.ts +23 -0
  357. package/src/chat/display-preferences.ts +24 -0
  358. package/src/chat/eval-execution.ts +172 -0
  359. package/src/chat/execution-shared.ts +113 -0
  360. package/src/chat/extension-types.ts +46 -0
  361. package/src/chat/hook-message.ts +20 -0
  362. package/src/chat/image-loading.ts +122 -0
  363. package/src/chat/late-diagnostics-message.ts +92 -0
  364. package/src/chat/messages.ts +305 -0
  365. package/src/chat/reaction.ts +65 -0
  366. package/src/chat/read-target.ts +7 -0
  367. package/src/chat/read-tool-group.ts +873 -0
  368. package/src/chat/served-model-marker.ts +91 -0
  369. package/src/chat/skill-message.ts +220 -0
  370. package/src/chat/skill-title-input.ts +41 -0
  371. package/src/chat/stripped-tool-calls-placeholder.ts +35 -0
  372. package/src/chat/thinking-display.ts +312 -0
  373. package/src/chat/todo-reminder.ts +33 -0
  374. package/src/chat/tool-execution.ts +1395 -0
  375. package/src/chat/transcript-browser.ts +268 -0
  376. package/src/chat/transcript-entry.ts +96 -0
  377. package/src/chat/transcript-outline.ts +260 -0
  378. package/src/chat/transcript-render-helpers.ts +315 -0
  379. package/src/chat/ttsr-notification.ts +123 -0
  380. package/src/chat/user-message.ts +287 -0
  381. package/src/chrome/chat-block.ts +110 -0
  382. package/src/chrome/collab-qrcode.ts +74 -0
  383. package/src/chrome/context-thresholds.ts +85 -0
  384. package/src/chrome/countdown-timer.ts +74 -0
  385. package/src/chrome/diff.ts +266 -0
  386. package/src/chrome/dynamic-border.ts +36 -0
  387. package/src/chrome/error-block.ts +63 -0
  388. package/src/chrome/form-theme.ts +10 -0
  389. package/src/chrome/format.ts +66 -0
  390. package/src/chrome/index.ts +21 -0
  391. package/src/chrome/keybinding-hints.ts +55 -0
  392. package/src/chrome/live-board.ts +123 -0
  393. package/src/chrome/local-date.ts +20 -0
  394. package/src/chrome/message-divider.ts +51 -0
  395. package/src/chrome/message-frame.ts +164 -0
  396. package/src/chrome/message-notice.ts +103 -0
  397. package/src/chrome/overlay-box.ts +301 -0
  398. package/src/chrome/qrcode.ts +539 -0
  399. package/src/chrome/segment-track.ts +86 -0
  400. package/src/chrome/select-list-mouse-routing.ts +11 -0
  401. package/src/chrome/selector-helpers.ts +119 -0
  402. package/src/chrome/shared.ts +27 -0
  403. package/src/chrome/streaming-panel.ts +72 -0
  404. package/src/chrome/tool-activity.ts +44 -0
  405. package/src/chrome/transcript-container.ts +902 -0
  406. package/src/chrome/transcript-status.ts +27 -0
  407. package/src/chrome/visual-truncate.ts +70 -0
  408. package/src/components/disclosure.ts +235 -0
  409. package/src/components/editor.ts +10 -12
  410. package/src/components/form.ts +566 -0
  411. package/src/components/input.ts +6 -27
  412. package/src/components/key-value-list.ts +88 -0
  413. package/src/components/layout/geometry.ts +202 -0
  414. package/src/components/layout/row.ts +283 -0
  415. package/src/components/layout/split-pane.ts +349 -0
  416. package/src/components/layout/stack.ts +261 -0
  417. package/src/components/markdown.ts +16 -12
  418. package/src/components/menu-selection.ts +248 -0
  419. package/src/components/metric.ts +132 -0
  420. package/src/components/progress-bar.ts +140 -0
  421. package/src/components/scroll-view.ts +372 -70
  422. package/src/components/scroll-viewport.ts +154 -0
  423. package/src/components/section.ts +77 -0
  424. package/src/components/select-list.ts +238 -159
  425. package/src/components/settings-list.ts +103 -80
  426. package/src/components/table.ts +127 -0
  427. package/src/components/tree-view.ts +481 -0
  428. package/src/components/wizard-step.ts +229 -0
  429. package/src/deccara.ts +1 -3
  430. package/src/hotkeys-markdown.ts +100 -0
  431. package/src/index.ts +18 -0
  432. package/src/keybinding-matchers.ts +99 -0
  433. package/src/keys.ts +2 -2
  434. package/src/lang-from-path.ts +252 -0
  435. package/src/overlays/advisor-config.ts +761 -0
  436. package/src/overlays/agent-activity.ts +113 -0
  437. package/src/overlays/agent-hub-projection.ts +250 -0
  438. package/src/overlays/agent-hub-renderer.ts +281 -0
  439. package/src/overlays/agent-hub-types.ts +74 -0
  440. package/src/overlays/agent-hub.ts +1562 -0
  441. package/src/overlays/agent-transcript-viewer.ts +669 -0
  442. package/src/overlays/agents-hub.ts +1203 -0
  443. package/src/overlays/ask-dialog.ts +1202 -0
  444. package/src/overlays/bordered-loader.ts +41 -0
  445. package/src/overlays/btw-history-panel.ts +591 -0
  446. package/src/overlays/btw-history.ts +33 -0
  447. package/src/overlays/btw-panel.ts +167 -0
  448. package/src/overlays/cleanse-panel.ts +167 -0
  449. package/src/overlays/codex-reset-fireworks.ts +375 -0
  450. package/src/overlays/composer-shape-preview.ts +137 -0
  451. package/src/overlays/composer-shape-registry.ts +82 -0
  452. package/src/overlays/copy-selector.ts +618 -0
  453. package/src/overlays/copy-targets.ts +210 -0
  454. package/src/overlays/error-banner.ts +39 -0
  455. package/src/overlays/extensions/display-text.ts +39 -0
  456. package/src/overlays/extensions/extension-dashboard.ts +645 -0
  457. package/src/overlays/extensions/extension-list.ts +667 -0
  458. package/src/overlays/extensions/inspector-model.ts +484 -0
  459. package/src/overlays/extensions/inspector-panel.ts +669 -0
  460. package/src/overlays/extensions/live-tool-session.ts +67 -0
  461. package/src/overlays/extensions/mcp-runtime.ts +289 -0
  462. package/src/overlays/extensions/state-manager.ts +337 -0
  463. package/src/overlays/extensions/types.ts +208 -0
  464. package/src/overlays/history-search.ts +268 -0
  465. package/src/overlays/hook-editor.ts +272 -0
  466. package/src/overlays/hook-input.ts +84 -0
  467. package/src/overlays/hook-selector.ts +635 -0
  468. package/src/overlays/hub-frame.ts +262 -0
  469. package/src/overlays/login-dialog.ts +231 -0
  470. package/src/overlays/logout-account-selector.ts +115 -0
  471. package/src/overlays/mcp-add-wizard.ts +1436 -0
  472. package/src/overlays/model-browser.ts +1176 -0
  473. package/src/overlays/model-hub.ts +2279 -0
  474. package/src/overlays/model-picker.ts +285 -0
  475. package/src/overlays/model-selector.ts +107 -0
  476. package/src/overlays/move-overlay.ts +197 -0
  477. package/src/overlays/oauth-selector.ts +446 -0
  478. package/src/overlays/omfg-panel.ts +136 -0
  479. package/src/overlays/pause-screen.ts +198 -0
  480. package/src/overlays/plan-review-overlay.ts +1227 -0
  481. package/src/overlays/plan-save-overlay.ts +74 -0
  482. package/src/overlays/plan-toc.ts +139 -0
  483. package/src/overlays/plugin-selector.ts +97 -0
  484. package/src/overlays/plugin-settings.ts +839 -0
  485. package/src/overlays/queue-mode-selector.ts +55 -0
  486. package/src/overlays/reset-usage-selector.ts +170 -0
  487. package/src/overlays/rewind-selector.ts +499 -0
  488. package/src/overlays/running-subagent-badge.ts +19 -0
  489. package/src/overlays/session-account-selector.ts +68 -0
  490. package/src/overlays/session-info-overlay.ts +96 -0
  491. package/src/overlays/session-observer-registry.ts +289 -0
  492. package/src/overlays/session-selector.ts +1137 -0
  493. package/src/overlays/settings-defs.ts +315 -0
  494. package/src/overlays/settings-selector.ts +1415 -0
  495. package/src/overlays/show-images-selector.ts +44 -0
  496. package/src/overlays/snapcompact-shape-preview-doc.md +14 -0
  497. package/src/overlays/snapcompact-shape-preview.ts +192 -0
  498. package/src/overlays/theme-selector.ts +62 -0
  499. package/src/overlays/thinking-selector.ts +51 -0
  500. package/src/overlays/tiny-title-download-progress.ts +102 -0
  501. package/src/overlays/tree-selector.ts +1121 -0
  502. package/src/overlays/usage-dashboard.ts +629 -0
  503. package/src/overlays/usage-display.ts +45 -0
  504. package/src/overlays/usage-row.ts +118 -0
  505. package/src/prompt/attachment-chips.ts +201 -0
  506. package/src/prompt/composer-attachments.ts +280 -0
  507. package/src/prompt/composer-cache.ts +304 -0
  508. package/src/prompt/composer.ts +934 -0
  509. package/src/prompt/custom-editor.ts +1392 -0
  510. package/src/prompt/data/emojis.json +1 -0
  511. package/src/prompt/editor-top-gap.ts +28 -0
  512. package/src/prompt/emoji-autocomplete.ts +285 -0
  513. package/src/prompt/github-ref-autocomplete.ts +75 -0
  514. package/src/prompt/gradient-highlight.ts +101 -0
  515. package/src/prompt/image-format.ts +28 -0
  516. package/src/prompt/image-references.ts +102 -0
  517. package/src/prompt/interactive-context-helpers.ts +126 -0
  518. package/src/prompt/internal-url-autocomplete.ts +155 -0
  519. package/src/prompt/macos-spelling.ts +456 -0
  520. package/src/prompt/magic-keyword-boundary.ts +23 -0
  521. package/src/prompt/magic-keywords.ts +42 -0
  522. package/src/prompt/markdown-prose.ts +245 -0
  523. package/src/prompt/model-mention-autocomplete.ts +94 -0
  524. package/src/prompt/model-mention-syntax.ts +41 -0
  525. package/src/prompt/orchestrate.ts +26 -0
  526. package/src/prompt/prompt-action-autocomplete.ts +337 -0
  527. package/src/prompt/queue-input.ts +132 -0
  528. package/src/prompt/skill-tokens.ts +36 -0
  529. package/src/prompt/tips.txt +27 -0
  530. package/src/prompt/tools-markdown.ts +29 -0
  531. package/src/prompt/ultrathink.ts +26 -0
  532. package/src/prompt/usage-amounts.ts +99 -0
  533. package/src/prompt/video.ts +47 -0
  534. package/src/prompt/welcome.ts +605 -0
  535. package/src/prompt/workflow.ts +26 -0
  536. package/src/render/code-cell.ts +244 -0
  537. package/src/render/file-list.ts +58 -0
  538. package/src/render/hyperlink.ts +192 -0
  539. package/src/render/index.ts +17 -0
  540. package/src/render/output-block.ts +267 -0
  541. package/src/render/output-pane.ts +299 -0
  542. package/src/render/render-utils.ts +1182 -0
  543. package/src/render/sixel.ts +69 -0
  544. package/src/render/status-line.ts +55 -0
  545. package/src/render/tool-card.ts +272 -0
  546. package/src/render/tree-list.ts +188 -0
  547. package/src/render/types.ts +19 -0
  548. package/src/render/utils.ts +109 -0
  549. package/src/render/width-aware-text.ts +60 -0
  550. package/src/setup/lazy.ts +17 -0
  551. package/src/setup/scenes/composer.ts +127 -0
  552. package/src/setup/scenes/glyph.ts +120 -0
  553. package/src/setup/scenes/model.ts +138 -0
  554. package/src/setup/scenes/outro.ts +24 -0
  555. package/src/setup/scenes/providers.ts +107 -0
  556. package/src/setup/scenes/sign-in.ts +373 -0
  557. package/src/setup/scenes/splash.ts +192 -0
  558. package/src/setup/scenes/theme.ts +343 -0
  559. package/src/setup/scenes/types.ts +111 -0
  560. package/src/setup/scenes/web-search.ts +165 -0
  561. package/src/setup/setup-version.ts +11 -0
  562. package/src/setup/startup-splash.ts +107 -0
  563. package/src/setup/wizard-overlay.ts +319 -0
  564. package/src/setup/wizard.ts +101 -0
  565. package/src/status-line/component.ts +3003 -0
  566. package/src/status-line/context-usage.ts +688 -0
  567. package/src/status-line/footer.ts +323 -0
  568. package/src/status-line/git-utils.ts +42 -0
  569. package/src/status-line/host.ts +97 -0
  570. package/src/status-line/index.ts +6 -0
  571. package/src/status-line/loop.ts +28 -0
  572. package/src/status-line/metrics.ts +94 -0
  573. package/src/status-line/presets.ts +107 -0
  574. package/src/status-line/schema.ts +57 -0
  575. package/src/status-line/segments.ts +934 -0
  576. package/src/status-line/separators.ts +55 -0
  577. package/src/status-line/types.ts +227 -0
  578. package/src/stdin-buffer.ts +4 -2
  579. package/src/theme/color.ts +95 -0
  580. package/src/theme/dark.json +95 -0
  581. package/src/theme/defaults/alabaster.json +93 -0
  582. package/src/theme/defaults/amethyst.json +96 -0
  583. package/src/theme/defaults/anthracite.json +93 -0
  584. package/src/theme/defaults/basalt.json +91 -0
  585. package/src/theme/defaults/birch.json +95 -0
  586. package/src/theme/defaults/dark-abyss.json +91 -0
  587. package/src/theme/defaults/dark-arctic.json +104 -0
  588. package/src/theme/defaults/dark-aurora.json +95 -0
  589. package/src/theme/defaults/dark-catppuccin.json +107 -0
  590. package/src/theme/defaults/dark-cavern.json +91 -0
  591. package/src/theme/defaults/dark-celestial.json +106 -0
  592. package/src/theme/defaults/dark-copper.json +95 -0
  593. package/src/theme/defaults/dark-cosmos.json +90 -0
  594. package/src/theme/defaults/dark-cyberpunk.json +102 -0
  595. package/src/theme/defaults/dark-dracula.json +98 -0
  596. package/src/theme/defaults/dark-eclipse.json +91 -0
  597. package/src/theme/defaults/dark-ember.json +95 -0
  598. package/src/theme/defaults/dark-equinox.json +90 -0
  599. package/src/theme/defaults/dark-forest.json +96 -0
  600. package/src/theme/defaults/dark-github.json +105 -0
  601. package/src/theme/defaults/dark-gruvbox.json +112 -0
  602. package/src/theme/defaults/dark-lavender.json +95 -0
  603. package/src/theme/defaults/dark-lunar.json +89 -0
  604. package/src/theme/defaults/dark-midnight.json +95 -0
  605. package/src/theme/defaults/dark-monochrome.json +94 -0
  606. package/src/theme/defaults/dark-monokai.json +98 -0
  607. package/src/theme/defaults/dark-nebula.json +90 -0
  608. package/src/theme/defaults/dark-nord.json +98 -0
  609. package/src/theme/defaults/dark-ocean.json +101 -0
  610. package/src/theme/defaults/dark-one.json +100 -0
  611. package/src/theme/defaults/dark-poimandres.json +115 -0
  612. package/src/theme/defaults/dark-rainforest.json +91 -0
  613. package/src/theme/defaults/dark-reef.json +91 -0
  614. package/src/theme/defaults/dark-retro.json +92 -0
  615. package/src/theme/defaults/dark-rose-pine.json +96 -0
  616. package/src/theme/defaults/dark-sakura.json +95 -0
  617. package/src/theme/defaults/dark-slate.json +95 -0
  618. package/src/theme/defaults/dark-solarized.json +97 -0
  619. package/src/theme/defaults/dark-solstice.json +90 -0
  620. package/src/theme/defaults/dark-starfall.json +91 -0
  621. package/src/theme/defaults/dark-sunset.json +99 -0
  622. package/src/theme/defaults/dark-swamp.json +90 -0
  623. package/src/theme/defaults/dark-synthwave.json +103 -0
  624. package/src/theme/defaults/dark-taiga.json +91 -0
  625. package/src/theme/defaults/dark-terminal.json +95 -0
  626. package/src/theme/defaults/dark-tokyo-night.json +101 -0
  627. package/src/theme/defaults/dark-tundra.json +91 -0
  628. package/src/theme/defaults/dark-twilight.json +91 -0
  629. package/src/theme/defaults/dark-volcanic.json +91 -0
  630. package/src/theme/defaults/graphite.json +92 -0
  631. package/src/theme/defaults/index.ts +201 -0
  632. package/src/theme/defaults/light-arctic.json +107 -0
  633. package/src/theme/defaults/light-aurora-day.json +91 -0
  634. package/src/theme/defaults/light-canyon.json +91 -0
  635. package/src/theme/defaults/light-catppuccin.json +106 -0
  636. package/src/theme/defaults/light-cirrus.json +90 -0
  637. package/src/theme/defaults/light-coral.json +95 -0
  638. package/src/theme/defaults/light-cyberpunk.json +96 -0
  639. package/src/theme/defaults/light-dawn.json +90 -0
  640. package/src/theme/defaults/light-dunes.json +91 -0
  641. package/src/theme/defaults/light-eucalyptus.json +95 -0
  642. package/src/theme/defaults/light-forest.json +100 -0
  643. package/src/theme/defaults/light-frost.json +95 -0
  644. package/src/theme/defaults/light-github.json +115 -0
  645. package/src/theme/defaults/light-glacier.json +91 -0
  646. package/src/theme/defaults/light-gruvbox.json +108 -0
  647. package/src/theme/defaults/light-haze.json +90 -0
  648. package/src/theme/defaults/light-honeycomb.json +95 -0
  649. package/src/theme/defaults/light-lagoon.json +91 -0
  650. package/src/theme/defaults/light-lavender.json +95 -0
  651. package/src/theme/defaults/light-meadow.json +91 -0
  652. package/src/theme/defaults/light-mint.json +95 -0
  653. package/src/theme/defaults/light-monochrome.json +101 -0
  654. package/src/theme/defaults/light-ocean.json +99 -0
  655. package/src/theme/defaults/light-one.json +99 -0
  656. package/src/theme/defaults/light-opal.json +91 -0
  657. package/src/theme/defaults/light-orchard.json +91 -0
  658. package/src/theme/defaults/light-paper.json +95 -0
  659. package/src/theme/defaults/light-poimandres.json +115 -0
  660. package/src/theme/defaults/light-prism.json +90 -0
  661. package/src/theme/defaults/light-retro.json +98 -0
  662. package/src/theme/defaults/light-sand.json +95 -0
  663. package/src/theme/defaults/light-savanna.json +91 -0
  664. package/src/theme/defaults/light-solarized.json +102 -0
  665. package/src/theme/defaults/light-soleil.json +90 -0
  666. package/src/theme/defaults/light-sunset.json +99 -0
  667. package/src/theme/defaults/light-synthwave.json +98 -0
  668. package/src/theme/defaults/light-tokyo-night.json +111 -0
  669. package/src/theme/defaults/light-wetland.json +91 -0
  670. package/src/theme/defaults/light-zenith.json +89 -0
  671. package/src/theme/defaults/limestone.json +94 -0
  672. package/src/theme/defaults/mahogany.json +97 -0
  673. package/src/theme/defaults/marble.json +93 -0
  674. package/src/theme/defaults/obsidian.json +91 -0
  675. package/src/theme/defaults/onyx.json +91 -0
  676. package/src/theme/defaults/pearl.json +93 -0
  677. package/src/theme/defaults/porcelain.json +91 -0
  678. package/src/theme/defaults/quartz.json +96 -0
  679. package/src/theme/defaults/sandstone.json +95 -0
  680. package/src/theme/defaults/titanium.json +90 -0
  681. package/src/theme/index.ts +5 -0
  682. package/src/theme/light.json +93 -0
  683. package/src/theme/loader.ts +200 -0
  684. package/src/theme/mermaid-cache.ts +92 -0
  685. package/src/theme/schema-validation.ts +119 -0
  686. package/src/theme/schema.ts +168 -0
  687. package/src/theme/session-color.ts +253 -0
  688. package/src/theme/shimmer.ts +307 -0
  689. package/src/theme/symbols.ts +1433 -0
  690. package/src/theme/theme-class.ts +787 -0
  691. package/src/theme/theme-schema.json +463 -0
  692. package/src/theme/theme.ts +810 -0
  693. package/src/theme/tui-adapters.ts +339 -0
  694. package/src/thinking.ts +380 -0
  695. package/src/tools/agent-tree.ts +128 -0
  696. package/src/tools/ask.ts +482 -0
  697. package/src/tools/ast-edit.ts +210 -0
  698. package/src/tools/ast-grep.ts +171 -0
  699. package/src/tools/autoresearch.ts +287 -0
  700. package/src/tools/bash-interactive.ts +295 -0
  701. package/src/tools/bash.ts +468 -0
  702. package/src/tools/conflict-detect.ts +77 -0
  703. package/src/tools/debug.ts +167 -0
  704. package/src/tools/default-renderer.ts +172 -0
  705. package/src/tools/edit.ts +1258 -0
  706. package/src/tools/eval-format/code-mode-declarations.ts +61 -0
  707. package/src/tools/eval-format/index.ts +16 -0
  708. package/src/tools/eval-format/javascript.ts +952 -0
  709. package/src/tools/eval-format/python.ts +544 -0
  710. package/src/tools/eval.ts +835 -0
  711. package/src/tools/fetch.ts +226 -0
  712. package/src/tools/gh-format.ts +18 -0
  713. package/src/tools/github.ts +686 -0
  714. package/src/tools/glob.ts +213 -0
  715. package/src/tools/goal.ts +153 -0
  716. package/src/tools/grep.ts +385 -0
  717. package/src/tools/grouped-file-output.ts +211 -0
  718. package/src/tools/hashline-format.ts +59 -0
  719. package/src/tools/hub.ts +1488 -0
  720. package/src/tools/index.ts +71 -0
  721. package/src/tools/json-tree.ts +388 -0
  722. package/src/tools/line-ranges.ts +98 -0
  723. package/src/tools/list-limit.ts +43 -0
  724. package/src/tools/lsp.ts +702 -0
  725. package/src/tools/match-line-format.ts +20 -0
  726. package/src/tools/mcp.ts +309 -0
  727. package/src/tools/memory.ts +220 -0
  728. package/src/tools/output-meta.ts +367 -0
  729. package/src/tools/read.ts +628 -0
  730. package/src/tools/renderer.ts +107 -0
  731. package/src/tools/report-tool-issue.ts +13 -0
  732. package/src/tools/resolve.ts +139 -0
  733. package/src/tools/streaming-output.ts +1576 -0
  734. package/src/tools/subprocess.ts +39 -0
  735. package/src/tools/task-repair-args.ts +118 -0
  736. package/src/tools/task-yield-assembly.ts +147 -0
  737. package/src/tools/task.ts +2110 -0
  738. package/src/tools/terminal-output.ts +153 -0
  739. package/src/tools/think.ts +28 -0
  740. package/src/tools/todo.ts +580 -0
  741. package/src/tools/tool-errors.ts +15 -0
  742. package/src/tools/vibe.ts +420 -0
  743. package/src/tools/web-search.ts +409 -0
  744. package/src/tools/write.ts +511 -0
  745. package/src/tools/xd-url.ts +28 -0
  746. package/src/tools/xdev.ts +201 -0
  747. package/src/tui.ts +57 -4
  748. package/src/utils.ts +8 -0
@@ -0,0 +1,635 @@
1
+ /**
2
+ * Generic selector component for hooks.
3
+ * Displays a list of string options with keyboard navigation.
4
+ */
5
+ import {
6
+ Container,
7
+ Ellipsis,
8
+ extractPrintableText,
9
+ type MarkdownTheme,
10
+ matchesKey,
11
+ padding,
12
+ renderInlineMarkdown,
13
+ replaceTabs,
14
+ Spacer,
15
+ Text,
16
+ type TUI,
17
+ truncateToWidth,
18
+ visibleWidth,
19
+ wrapTextWithAnsi,
20
+ } from "../index";
21
+ import { getMarkdownTheme, type ThemeColor, theme } from "../theme/theme";
22
+ import {
23
+ matchesAppExternalEditor,
24
+ matchesSelectCancel,
25
+ matchesSelectDown,
26
+ matchesSelectUp,
27
+ } from "../keybinding-matchers";
28
+ import { CountdownTimer } from "../chrome/countdown-timer";
29
+ import { OverlayPanel } from "../chrome/overlay-box";
30
+ import { renderSegmentTrack } from "../chrome/segment-track";
31
+ import { MenuSelection, getMenuWindow } from "../components/menu-selection";
32
+
33
+ /** One segment of a {@link HookSelectorSlider} — a label and an optional
34
+ * detail line (e.g. the resolved model name) shown beneath the track while
35
+ * the segment is active. Segment colors come from the track's theme palette,
36
+ * assigned by position. */
37
+ export interface HookSelectorSliderSegment {
38
+ label: string;
39
+ /** Secondary line rendered under the track when this segment is selected. */
40
+ detail?: string;
41
+ }
42
+
43
+ /**
44
+ * A horizontal left/right selector rendered above the option list. Unlike the
45
+ * up/down option cursor, the slider is moved with the left/right arrows from
46
+ * any list position, letting the caller capture an orthogonal choice (e.g. the
47
+ * model tier to continue execution with) alongside the selected option.
48
+ */
49
+ export interface HookSelectorSlider {
50
+ /** Dim caption rendered before the slider track (e.g. "continue with"). */
51
+ caption?: string;
52
+ segments: HookSelectorSliderSegment[];
53
+ /** Initially highlighted segment index. */
54
+ index: number;
55
+ /** Invoked with the new index whenever the slider moves. */
56
+ onChange?: (index: number) => void;
57
+ }
58
+
59
+ export interface HookSelectorOptions {
60
+ tui?: TUI;
61
+ timeout?: number;
62
+ onTimeout?: () => void;
63
+ onTimeoutStart?: () => void;
64
+ onTimeoutReset?: () => void;
65
+ initialIndex?: number;
66
+ outline?: boolean;
67
+ maxVisible?: number;
68
+ onLeft?: () => void;
69
+ onRight?: () => void;
70
+ onExternalEditor?: () => void;
71
+ helpText?: string;
72
+ slider?: HookSelectorSlider;
73
+ /** Indices into the original options that cannot be selected: they render
74
+ * dimmed, are skipped during navigation, and reject enter/timeout. */
75
+ disabledIndices?: readonly number[];
76
+ /** Render a leading radio/checkbox marker before each markable option,
77
+ * matching the ask transcript. "radio" fills the marker on the cursor row
78
+ * (single-choice); "checkbox" reflects {@link checkedIndices} per row
79
+ * (multi-select). Options at or beyond {@link markableCount} keep the plain
80
+ * cursor prefix — used for trailing control rows like "Other"/"Done". */
81
+ selectionMarker?: "radio" | "checkbox";
82
+ /** For `selectionMarker: "checkbox"`: original-indices currently checked. */
83
+ checkedIndices?: readonly number[];
84
+ /** Number of leading options (original order) that receive a selection
85
+ * marker. Defaults to every option when {@link selectionMarker} is set. */
86
+ markableCount?: number;
87
+ }
88
+
89
+ export interface HookSelectorOption {
90
+ label: string;
91
+ description?: string;
92
+ }
93
+
94
+ export type HookSelectorOptionInput = string | HookSelectorOption;
95
+
96
+ function normalizeHookSelectorOption(option: HookSelectorOptionInput): HookSelectorOption {
97
+ if (typeof option === "string") return { label: option };
98
+ if (option.description?.trim()) {
99
+ return { label: option.label, description: option.description.trim() };
100
+ }
101
+ return { label: option.label };
102
+ }
103
+
104
+ function splitLeadingSpacesForWrap(line: string, width: number): { indent: string; body: string } {
105
+ let indentLength = 0;
106
+ while (indentLength < line.length && line.charCodeAt(indentLength) === 32) {
107
+ indentLength += 1;
108
+ }
109
+ const maxIndentLength = Math.max(0, width - 1);
110
+ const clampedIndentLength = Math.min(indentLength, maxIndentLength);
111
+ return {
112
+ indent: line.slice(0, clampedIndentLength),
113
+ body: line.slice(indentLength),
114
+ };
115
+ }
116
+
117
+ /** One row fed to {@link OutlinedList} or the plain list container. `highlight`
118
+ * causes the row (and its wrapped continuations, plus trailing padding) to be
119
+ * painted with the theme's `selectedBg` band — the focus cue that survives
120
+ * themes where `accent` fg is close to the terminal foreground. */
121
+ type SelectorRow = { text: string; highlight: boolean };
122
+
123
+ /** Paint `content` with the `selectedBg` background, applied AFTER any inner
124
+ * ANSI styling so the band spans padding as well as content. */
125
+ function paintSelectedRow(content: string): string {
126
+ return theme.bg("selectedBg", content);
127
+ }
128
+
129
+ class OutlinedList extends Container {
130
+ #rows: SelectorRow[] = [];
131
+
132
+ setLines(rows: readonly SelectorRow[]): void {
133
+ this.#rows = rows.slice();
134
+ this.invalidate();
135
+ }
136
+
137
+ override render(width: number): readonly string[] {
138
+ const borderColor = (text: string) => theme.fg("border", text);
139
+ const horizontal = borderColor(theme.boxRound.horizontal.repeat(Math.max(1, width)));
140
+ const innerWidth = Math.max(1, width - 2);
141
+ const content: string[] = [];
142
+ for (const row of this.#rows) {
143
+ const normalized = replaceTabs(row.text);
144
+ const { indent, body } = splitLeadingSpacesForWrap(normalized, innerWidth);
145
+ const wrapped = wrapTextWithAnsi(body, Math.max(1, innerWidth - visibleWidth(indent)));
146
+ for (const wrappedBody of wrapped.length > 0 ? wrapped : [""]) {
147
+ const wrappedLine = `${indent}${wrappedBody}`;
148
+ const pad = Math.max(0, innerWidth - visibleWidth(wrappedLine));
149
+ const filled = `${wrappedLine}${padding(pad)}`;
150
+ const painted = row.highlight ? paintSelectedRow(filled) : filled;
151
+ content.push(`${borderColor(theme.boxRound.vertical)}${painted}${borderColor(theme.boxRound.vertical)}`);
152
+ }
153
+ }
154
+ return [horizontal, ...content, horizontal];
155
+ }
156
+ }
157
+
158
+ /** A filtered option paired with its index into the original options array, so
159
+ * disabled-index lookups survive fuzzy filtering and reordering. */
160
+ type FilteredOption = { option: HookSelectorOption; index: number };
161
+
162
+ export class HookSelectorComponent extends OverlayPanel {
163
+ #options: HookSelectorOption[];
164
+ #menu: MenuSelection<FilteredOption>;
165
+ #disabledIndices: Set<number>;
166
+ #selectionMarker: "radio" | "checkbox" | undefined;
167
+ #checkedIndices: Set<number>;
168
+ #markableCount: number;
169
+ #maxVisible: number;
170
+ #listContainer: Container | undefined;
171
+ #outlinedList: OutlinedList | undefined;
172
+ #onSelectCallback: (option: string) => void;
173
+ #onCancelCallback: () => void;
174
+ #baseTitle: string;
175
+ #countdown: CountdownTimer | undefined;
176
+ #onLeftCallback: (() => void) | undefined;
177
+ #onRightCallback: (() => void) | undefined;
178
+ #onExternalEditorCallback: (() => void) | undefined;
179
+ #onTimeoutResetCallback: (() => void) | undefined;
180
+ #slider: HookSelectorSlider | undefined;
181
+ #sliderIndex: number = 0;
182
+ #sliderComponent: Text | undefined;
183
+ #lastRenderWidth: number | undefined;
184
+ constructor(
185
+ title: string,
186
+ options: HookSelectorOptionInput[],
187
+ onSelect: (option: string) => void,
188
+ onCancel: () => void,
189
+ opts?: HookSelectorOptions,
190
+ ) {
191
+ super(title.split(/\r?\n/, 1)[0] ?? "");
192
+
193
+ this.#options = options.map(normalizeHookSelectorOption);
194
+ this.#disabledIndices = new Set(
195
+ (opts?.disabledIndices ?? []).filter(
196
+ index => Number.isInteger(index) && index >= 0 && index < this.#options.length,
197
+ ),
198
+ );
199
+ this.#menu = new MenuSelection<FilteredOption>(
200
+ this.#options.map((option, index) => ({ option, index })),
201
+ {
202
+ getKey: filtered => String(filtered.index),
203
+ getSearchText: filtered => `${filtered.option.label} ${filtered.option.description ?? ""}`,
204
+ isDisabled: filtered => this.#disabledIndices.has(filtered.index),
205
+ },
206
+ );
207
+ this.#menu.setSelectedIndex(opts?.initialIndex ?? 0);
208
+ this.#selectionMarker = opts?.selectionMarker;
209
+ this.#checkedIndices = new Set(
210
+ (opts?.checkedIndices ?? []).filter(
211
+ index => Number.isInteger(index) && index >= 0 && index < this.#options.length,
212
+ ),
213
+ );
214
+ this.#markableCount = Math.max(0, Math.min(opts?.markableCount ?? this.#options.length, this.#options.length));
215
+ this.#maxVisible = Math.max(3, opts?.maxVisible ?? 12);
216
+ this.#onSelectCallback = onSelect;
217
+ this.#onCancelCallback = onCancel;
218
+ this.#baseTitle = this.title;
219
+ this.#onLeftCallback = opts?.onLeft;
220
+ this.#onRightCallback = opts?.onRight;
221
+ this.#onExternalEditorCallback = opts?.onExternalEditor;
222
+ this.#onTimeoutResetCallback = opts?.onTimeoutReset;
223
+ if (opts?.slider && opts.slider.segments.length > 0) {
224
+ this.#slider = opts.slider;
225
+ this.#sliderIndex = Math.max(0, Math.min(opts.slider.index, opts.slider.segments.length - 1));
226
+ }
227
+
228
+ this.addChild(new Spacer(1));
229
+ for (const line of title.split(/\r?\n/).slice(1)) {
230
+ this.addChild(new Text(theme.fg("accent", line), 0, 0));
231
+ }
232
+ this.addChild(new Spacer(1));
233
+
234
+ if (this.#slider) {
235
+ this.#sliderComponent = new Text(this.#renderSliderLine(), 0, 0);
236
+ this.addChild(this.#sliderComponent);
237
+ this.addChild(new Spacer(1));
238
+ }
239
+
240
+ if (opts?.timeout && opts.timeout > 0 && opts.tui) {
241
+ opts.onTimeoutStart?.();
242
+ this.#countdown = new CountdownTimer(
243
+ opts.timeout,
244
+ opts.tui,
245
+ s => (this.title = `${this.#baseTitle} (${s}s)`),
246
+ () => {
247
+ opts?.onTimeout?.();
248
+ // Auto-select current option on timeout (typically the first/recommended option)
249
+ const selected = this.#menu.selectedItem;
250
+ if (selected && !this.#menu.isDisabled(selected)) {
251
+ this.#onSelectCallback(selected.option.label);
252
+ } else {
253
+ this.#onCancelCallback();
254
+ }
255
+ },
256
+ );
257
+ }
258
+
259
+ if (opts?.outline) {
260
+ this.#outlinedList = new OutlinedList();
261
+ this.addChild(this.#outlinedList);
262
+ } else {
263
+ this.#listContainer = new Container();
264
+ this.addChild(this.#listContainer);
265
+ }
266
+ this.addChild(new Spacer(1));
267
+ const controlsHint = opts?.helpText ?? "up/down navigate enter select esc cancel";
268
+ this.addChild(new Text(theme.fg("dim", controlsHint), 0, 0));
269
+ this.addChild(new Spacer(1));
270
+
271
+ this.#updateList();
272
+ }
273
+
274
+ #isDisabled(index: number): boolean {
275
+ return this.#disabledIndices.has(index);
276
+ }
277
+
278
+ /** Move the cursor by `delta`, skipping disabled rows, stopping at the first
279
+ * enabled option reached or at the list edge. */
280
+ #moveSelection(delta: number): void {
281
+ if (this.#menu.move(delta, false)) {
282
+ this.#updateList();
283
+ }
284
+ }
285
+
286
+ #renderOptionLines(
287
+ option: HookSelectorOption,
288
+ isSelected: boolean,
289
+ isDisabled: boolean,
290
+ mdTheme: MarkdownTheme,
291
+ descRows: number | "full",
292
+ renderWidth?: number,
293
+ index?: number,
294
+ ): string[] {
295
+ const textColor = isDisabled ? "dim" : isSelected ? "accent" : "text";
296
+ const prefixColor = isDisabled ? "dim" : "accent";
297
+ const label = renderInlineMarkdown(option.label, mdTheme, t => theme.fg(textColor, t));
298
+ const marker = index !== undefined ? this.#renderMarkerPrefix(index, isSelected, isDisabled) : undefined;
299
+ const prefix = marker ?? (isSelected ? theme.fg(prefixColor, `${theme.nav.cursor} `) : " ");
300
+ const lines = [prefix + label];
301
+ if (option.description && descRows !== 0) {
302
+ const descriptionColor: ThemeColor = isDisabled ? "dim" : "muted";
303
+ if (descRows === "full") {
304
+ const description = renderInlineMarkdown(option.description, mdTheme, t => theme.fg(descriptionColor, t));
305
+ lines.push(` ${description}`);
306
+ } else {
307
+ lines.push(
308
+ ...this.#wrapDescriptionRows(option.description, descRows, descriptionColor, mdTheme, renderWidth),
309
+ );
310
+ }
311
+ }
312
+ return lines;
313
+ }
314
+
315
+ /** Styled leading marker (`"<glyph> "`) for a markable option row, or
316
+ * `undefined` when no marker applies (control rows beyond `markableCount`,
317
+ * or when {@link selectionMarker} is unset) so the caller falls back to the
318
+ * classic cursor prefix. Radio fills on the cursor row; checkbox reflects
319
+ * the per-row checked state, with the cursor row drawn in accent. */
320
+ #renderMarkerPrefix(index: number, isSelected: boolean, isDisabled: boolean): string | undefined {
321
+ if (this.#selectionMarker === undefined || index >= this.#markableCount) return undefined;
322
+ if (this.#selectionMarker === "radio") {
323
+ const glyph = isSelected ? theme.radio.selected : theme.radio.unselected;
324
+ const color = isDisabled ? "dim" : isSelected ? "accent" : "dim";
325
+ return theme.fg(color, `${glyph} `);
326
+ }
327
+ const checked = this.#checkedIndices.has(index);
328
+ const glyph = checked ? theme.checkbox.checked : theme.checkbox.unchecked;
329
+ const color = isDisabled ? "dim" : isSelected ? "accent" : checked ? "success" : "dim";
330
+ return theme.fg(color, `${glyph} `);
331
+ }
332
+
333
+ /** Wrap an option description into indented rows, truncating to `maxRows`
334
+ * with an ellipsis. Pre-wrapping (rather than emitting one long line that the
335
+ * list re-wraps) lets compact mode bound how much of the highlighted option's
336
+ * detail is shown, so every option label stays on screen on short terminals. */
337
+ #wrapDescriptionRows(
338
+ description: string,
339
+ maxRows: number,
340
+ color: ThemeColor,
341
+ mdTheme: MarkdownTheme,
342
+ renderWidth = this.#lastRenderWidth,
343
+ ): string[] {
344
+ if (maxRows <= 0) return [];
345
+ const indent = " ";
346
+ const innerWidth = Math.max(1, (renderWidth ?? 80) - 2);
347
+ const bodyWidth = Math.max(1, innerWidth - indent.length);
348
+ const colored = renderInlineMarkdown(description, mdTheme, t => theme.fg(color, t));
349
+ const wrapped = wrapTextWithAnsi(colored, bodyWidth);
350
+ if (wrapped.length <= maxRows) return wrapped.map(row => indent + row);
351
+ const kept = wrapped.slice(0, maxRows);
352
+ kept[maxRows - 1] = truncateToWidth(wrapped.slice(maxRows - 1).join(" "), bodyWidth, Ellipsis.Unicode);
353
+ return kept.map(row => indent + row);
354
+ }
355
+
356
+ #renderedLineRowCount(line: string, renderWidth: number): number {
357
+ const normalized = replaceTabs(line);
358
+ if (this.#outlinedList) {
359
+ const innerWidth = Math.max(1, renderWidth - 2);
360
+ const { indent, body } = splitLeadingSpacesForWrap(normalized, innerWidth);
361
+ const wrapped = wrapTextWithAnsi(body, Math.max(1, innerWidth - visibleWidth(indent)));
362
+ return Math.max(1, wrapped.length);
363
+ }
364
+ const wrapped = wrapTextWithAnsi(normalized, Math.max(1, renderWidth - 2));
365
+ return Math.max(1, wrapped.length);
366
+ }
367
+
368
+ #optionRowCount(
369
+ option: HookSelectorOption,
370
+ renderWidth: number | undefined,
371
+ isSelected: boolean,
372
+ mdTheme: MarkdownTheme,
373
+ descRows: number | "full",
374
+ ): number {
375
+ if (renderWidth === undefined) return option.description && descRows !== 0 ? 2 : 1;
376
+ let rows = 0;
377
+ for (const line of this.#renderOptionLines(option, isSelected, false, mdTheme, descRows, renderWidth)) {
378
+ rows += this.#renderedLineRowCount(line, renderWidth);
379
+ }
380
+ return rows;
381
+ }
382
+
383
+ #totalOptionRows(options: HookSelectorOption[], renderWidth?: number, mdTheme?: MarkdownTheme): number {
384
+ const themeForRows = mdTheme ?? getMarkdownTheme();
385
+ let rows = 0;
386
+ for (const option of options) {
387
+ rows += this.#optionRowCount(option, renderWidth, false, themeForRows, "full");
388
+ }
389
+ return rows;
390
+ }
391
+
392
+ /**
393
+ * Visual row budget for the option window. In compact mode every option
394
+ * contributes only its label rows; the highlighted option's description is
395
+ * layered on afterwards (see #updateList), so the window is sized to keep
396
+ * as many labels visible as possible rather than letting one long
397
+ * description swallow the budget.
398
+ */
399
+ #windowRowCounts(
400
+ items: readonly FilteredOption[],
401
+ renderWidth: number | undefined,
402
+ mdTheme: MarkdownTheme,
403
+ compact: boolean,
404
+ ): number[] {
405
+ const descMode: number | "full" = compact ? 0 : "full";
406
+ return items.map((filtered, i) =>
407
+ this.#optionRowCount(filtered.option, renderWidth, i === this.#menu.selectedIndex, mdTheme, descMode),
408
+ );
409
+ }
410
+
411
+ #updateList(renderWidth = this.#lastRenderWidth): void {
412
+ const rows: SelectorRow[] = [];
413
+ const items = this.#menu.visibleItems;
414
+ const total = items.length;
415
+ const mdTheme = getMarkdownTheme();
416
+ // Compact mode kicks in exactly when the fully-expanded list (all
417
+ // descriptions) would overflow the row budget — the same condition that
418
+ // enables search. There we collapse every option to its label and show
419
+ // only the highlighted option's description, so the whole menu stays
420
+ // visible on short terminals instead of collapsing to a single entry.
421
+ const compact = this.#isSearchEnabled(renderWidth, mdTheme);
422
+ const { startIndex, endIndex } =
423
+ total === 0
424
+ ? { startIndex: 0, endIndex: 0 }
425
+ : getMenuWindow(
426
+ this.#windowRowCounts(items, renderWidth, mdTheme, compact),
427
+ this.#menu.selectedIndex,
428
+ Math.max(1, this.#maxVisible),
429
+ );
430
+
431
+ let selectedDescRows = 0;
432
+ if (compact && renderWidth !== undefined) {
433
+ let labelRows = 0;
434
+ for (let i = startIndex; i < endIndex; i++) {
435
+ const filtered = items[i];
436
+ if (filtered === undefined) continue;
437
+ labelRows += this.#optionRowCount(filtered.option, renderWidth, i === this.#menu.selectedIndex, mdTheme, 0);
438
+ }
439
+ // Reserve one row for the status line; give the remainder to the
440
+ // highlighted option's description.
441
+ selectedDescRows = Math.max(0, Math.max(1, this.#maxVisible) - labelRows - 1);
442
+ }
443
+
444
+ for (let i = startIndex; i < endIndex; i++) {
445
+ const filtered = items[i];
446
+ if (filtered === undefined) continue;
447
+ const isSelected = i === this.#menu.selectedIndex;
448
+ const isDisabled = this.#isDisabled(filtered.index);
449
+ const descMode: number | "full" = compact ? (isSelected ? selectedDescRows : 0) : "full";
450
+ // Highlight the whole option block (label + wrapped description rows)
451
+ // so the focus band reads as one continuous bar rather than a stripe
452
+ // under the label alone. Disabled rows never claim focus even if the
453
+ // index momentarily lands on one during initial coercion.
454
+ const highlight = isSelected && !isDisabled;
455
+ for (const text of this.#renderOptionLines(
456
+ filtered.option,
457
+ isSelected,
458
+ isDisabled,
459
+ mdTheme,
460
+ descMode,
461
+ renderWidth,
462
+ filtered.index,
463
+ )) {
464
+ rows.push({ text, highlight });
465
+ }
466
+ }
467
+
468
+ if (total === 0) {
469
+ rows.push({ text: theme.fg("dim", " No matching options"), highlight: false });
470
+ }
471
+
472
+ if (startIndex > 0 || endIndex < total || this.#shouldRenderSearchStatus(renderWidth, mdTheme)) {
473
+ rows.push({ text: this.#renderStatusLine(total), highlight: false });
474
+ }
475
+ if (this.#outlinedList) {
476
+ this.#outlinedList.setLines(rows);
477
+ return;
478
+ }
479
+ this.#listContainer?.clear();
480
+ for (const row of rows) {
481
+ const bgFn = row.highlight ? paintSelectedRow : undefined;
482
+ this.#listContainer?.addChild(new Text(row.text, 1, 0, bgFn));
483
+ }
484
+ }
485
+
486
+ /** Render the slider block in the style of the status line: each option is a
487
+ * distinctly colored segment, the active one filled as a powerline chip
488
+ * (its accent as the background, a luminance-matched label, flanked by
489
+ * triangle caps) and the rest shown as plain colored labels joined by a thin
490
+ * separator. Edge arrows brighten while there is room to move. When the
491
+ * active segment carries a `detail` (e.g. the resolved model name) a muted
492
+ * second line is appended. Returns one or two `\n`-joined lines. */
493
+ #renderSliderLine(): string {
494
+ const slider = this.#slider;
495
+ if (!slider) return "";
496
+ const segments = slider.segments;
497
+ const active = this.#sliderIndex;
498
+ const track = renderSegmentTrack(segments, active);
499
+
500
+ const leftArrow = theme.fg(active > 0 ? "accent" : "dim", "◂");
501
+ const rightArrow = theme.fg(active < segments.length - 1 ? "accent" : "dim", "▸");
502
+ const caption = slider.caption ? `${theme.fg("dim", slider.caption)} ` : "";
503
+ const trackLine = `${caption}${leftArrow} ${track} ${rightArrow}`;
504
+ const detail = segments[active]?.detail;
505
+ if (!detail) return trackLine;
506
+ return `${trackLine}\n ${theme.fg("dim", "↳")} ${theme.fg("muted", detail)}`;
507
+ }
508
+
509
+ /** Move the slider by `delta`, clamped to the segment range, refresh the
510
+ * rendered track, and notify the caller only when the index actually moves. */
511
+ #moveSlider(delta: number): void {
512
+ const slider = this.#slider;
513
+ if (!slider) return;
514
+ const next = Math.max(0, Math.min(slider.segments.length - 1, this.#sliderIndex + delta));
515
+ if (next === this.#sliderIndex) return;
516
+ this.#sliderIndex = next;
517
+ this.#sliderComponent?.setText(this.#renderSliderLine());
518
+ slider.onChange?.(next);
519
+ }
520
+
521
+ #isSearchEnabled(renderWidth = this.#lastRenderWidth, mdTheme?: MarkdownTheme): boolean {
522
+ return this.#totalOptionRows(this.#options, renderWidth, mdTheme) > this.#maxVisible;
523
+ }
524
+
525
+ #shouldRenderSearchStatus(renderWidth = this.#lastRenderWidth, mdTheme?: MarkdownTheme): boolean {
526
+ return this.#isSearchEnabled(renderWidth, mdTheme) || this.#menu.query.length > 0;
527
+ }
528
+
529
+ #renderStatusLine(total: number): string {
530
+ const selectedCount = total === 0 ? 0 : this.#menu.selectedIndex + 1;
531
+ const count =
532
+ this.#menu.query.trim() && total !== this.#options.length
533
+ ? `${selectedCount}/${total} of ${this.#options.length}`
534
+ : `${selectedCount}/${total}`;
535
+ const suffix = this.#menu.query.trim() ? ` Search: ${this.#menu.query}` : " Type to search";
536
+ return theme.fg("dim", ` (${count})${suffix}`);
537
+ }
538
+
539
+ #setSearchQuery(query: string): void {
540
+ this.#menu.setQuery(query, false);
541
+ this.#updateList();
542
+ }
543
+
544
+ #handleSearchInput(keyData: string): boolean {
545
+ if (!this.#isSearchEnabled()) return false;
546
+
547
+ if (matchesKey(keyData, "backspace")) {
548
+ if (this.#menu.query.length === 0) return false;
549
+ const chars = [...this.#menu.query];
550
+ chars.pop();
551
+ this.#setSearchQuery(chars.join(""));
552
+ return true;
553
+ }
554
+
555
+ const printableText = extractPrintableText(keyData);
556
+ if (printableText === undefined) return false;
557
+ if (this.#menu.query.length === 0 && printableText.trim().length === 0) return false;
558
+
559
+ this.#setSearchQuery(this.#menu.query + printableText);
560
+ return true;
561
+ }
562
+
563
+ /** Jump to (and, for single-select menus, immediately confirm) the option
564
+ * whose label starts with the pressed digit and a period. Numbered options
565
+ * can follow unnumbered rows, as in `/review` after a detected PR. Once
566
+ * type-to-search is active, digits stay searchable. Checkbox menus only
567
+ * move the cursor — confirmation stays on `enter`. */
568
+ #handleQuickSelect(keyData: string): boolean {
569
+ if (this.#menu.query.length > 0 || keyData.length !== 1 || keyData < "1" || keyData > "9") return false;
570
+ const targetIndex = this.#menu.visibleItems.findIndex(({ option }) => option.label.startsWith(`${keyData}. `));
571
+ if (targetIndex < 0) return false;
572
+ const target = this.#menu.visibleItems[targetIndex];
573
+ if (!target || this.#isDisabled(target.index)) return true;
574
+ this.#menu.setSelectedIndex(targetIndex);
575
+ this.#updateList();
576
+ if (this.#selectionMarker !== "checkbox") this.#onSelectCallback(target.option.label);
577
+ return true;
578
+ }
579
+
580
+ handleInput(keyData: string): void {
581
+ if (this.#countdown) {
582
+ this.#countdown.reset();
583
+ this.#onTimeoutResetCallback?.();
584
+ }
585
+
586
+ if (matchesSelectCancel(keyData)) {
587
+ this.#onCancelCallback();
588
+ return;
589
+ }
590
+
591
+ if (this.#handleQuickSelect(keyData)) {
592
+ return;
593
+ }
594
+
595
+ if (this.#handleSearchInput(keyData)) {
596
+ return;
597
+ }
598
+
599
+ if (matchesSelectUp(keyData) || (!this.#isSearchEnabled() && matchesKey(keyData, "k"))) {
600
+ this.#moveSelection(-1);
601
+ } else if (matchesSelectDown(keyData) || (!this.#isSearchEnabled() && matchesKey(keyData, "j"))) {
602
+ this.#moveSelection(1);
603
+ } else if (matchesKey(keyData, "enter") || matchesKey(keyData, "return") || keyData === "\n") {
604
+ const selected = this.#menu.selectedItem;
605
+ if (selected && !this.#menu.isDisabled(selected)) this.#onSelectCallback(selected.option.label);
606
+ } else if (
607
+ matchesKey(keyData, "left") ||
608
+ (this.#slider && !this.#isSearchEnabled() && matchesKey(keyData, "h"))
609
+ ) {
610
+ if (this.#slider) this.#moveSlider(-1);
611
+ else this.#onLeftCallback?.();
612
+ } else if (
613
+ matchesKey(keyData, "right") ||
614
+ (this.#slider && !this.#isSearchEnabled() && matchesKey(keyData, "l"))
615
+ ) {
616
+ if (this.#slider) this.#moveSlider(1);
617
+ else this.#onRightCallback?.();
618
+ } else if (this.#onExternalEditorCallback && matchesAppExternalEditor(keyData)) {
619
+ this.#onExternalEditorCallback();
620
+ }
621
+ }
622
+
623
+ override render(width: number): readonly string[] {
624
+ const renderWidth = Math.max(1, width - 4);
625
+ if (this.#lastRenderWidth !== renderWidth) {
626
+ this.#lastRenderWidth = renderWidth;
627
+ this.#updateList(renderWidth);
628
+ }
629
+ return super.render(width);
630
+ }
631
+
632
+ override dispose(): void {
633
+ this.#countdown?.dispose();
634
+ }
635
+ }