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

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,839 @@
1
+ /**
2
+ * Plugin settings UI components.
3
+ *
4
+ * Provides a hierarchical settings interface:
5
+ * - Plugin list (npm plugins + marketplace plugins)
6
+ * - Plugin details (enablement, manifest settings, and marketplace metadata)
7
+ */
8
+ import {
9
+ type Component,
10
+ Container,
11
+ matchesKey,
12
+ type SelectItem,
13
+ SelectList,
14
+ type SettingItem,
15
+ type SgrMouseEvent,
16
+ Spacer,
17
+ Text,
18
+ } from "../index";
19
+ import { logger } from "@oh-my-pi/pi-utils";
20
+ import { getSelectListTheme, getSettingsListTheme, theme } from "../theme/theme";
21
+ import { shortenPath } from "../render/render-utils";
22
+ import { OverlayPanel } from "../chrome/overlay-box";
23
+ import { FormField, SelectFormField, TextFormField } from "../components/form";
24
+ import { formTheme } from "../chrome/form-theme";
25
+ import { SettingsFormField } from "../components/settings-list";
26
+
27
+ /** Setting metadata consumed by the plugin settings UI. */
28
+ export type PluginSettingSchema = {
29
+ description?: string;
30
+ secret?: boolean;
31
+ } & (
32
+ | { type: "string"; default?: string }
33
+ | { type: "number"; default?: number; min?: number; max?: number }
34
+ | { type: "boolean"; default?: boolean }
35
+ | { type: "enum"; default?: string; values: string[] }
36
+ );
37
+
38
+ /** Installed runtime plugin fields consumed by the settings UI. */
39
+ export interface InstalledPlugin {
40
+ name: string;
41
+ version: string;
42
+ enabled: boolean;
43
+ enabledFeatures: string[] | null;
44
+ manifest: {
45
+ description?: string;
46
+ features?: Record<string, { description?: string; default?: boolean }>;
47
+ settings?: Record<string, PluginSettingSchema>;
48
+ };
49
+ }
50
+
51
+ /** Installed marketplace metadata consumed by the settings UI. */
52
+ export interface InstalledPluginSummary {
53
+ id: string;
54
+ scope: "user" | "project";
55
+ shadowedBy?: "project";
56
+ entries: {
57
+ installPath: string;
58
+ version: string;
59
+ installedAt: string;
60
+ lastUpdated: string;
61
+ gitCommitSha?: string;
62
+ enabled?: boolean;
63
+ }[];
64
+ }
65
+
66
+ /** Runtime plugin manager capabilities required by the settings UI. */
67
+ export interface PluginSettingsManager {
68
+ list(): Promise<InstalledPlugin[]>;
69
+ getPlugin(name: string, options?: { path?: string }): Promise<InstalledPlugin | undefined>;
70
+ getPluginSettings(name: string): Promise<Record<string, unknown>>;
71
+ setEnabled(name: string, enabled: boolean): Promise<void>;
72
+ getEnabledFeatures(name: string): Promise<string[] | null>;
73
+ setEnabledFeatures(name: string, features: string[] | null): Promise<void>;
74
+ setPluginSetting(name: string, key: string, value: unknown): Promise<void>;
75
+ }
76
+
77
+ /** Marketplace manager capabilities required by the settings UI. */
78
+ export interface PluginSettingsMarketplaceManager {
79
+ listInstalledPlugins(): Promise<InstalledPluginSummary[]>;
80
+ setPluginEnabled(pluginId: string, enabled: boolean, scope?: "user" | "project"): Promise<void>;
81
+ }
82
+
83
+ /** Host-owned managers and plugin identifier parsing. */
84
+ export interface PluginSettingsHost {
85
+ manager: PluginSettingsManager;
86
+ createMarketplaceManager(): Promise<PluginSettingsMarketplaceManager>;
87
+ parsePluginId(id: string): { name: string } | null;
88
+ }
89
+
90
+ /**
91
+ * Forwards a keystroke to `input`, but cancels via `onCancel` when the user presses Escape.
92
+ *
93
+ * Escape is decoded via `matchesKey` rather than a raw `\x1b` compare: inside the
94
+ * fullscreen settings overlay the kitty keyboard protocol is active (ghostty/kitty),
95
+ * where the Escape key arrives as the CSI-u sequence `\x1b[27u`, not a bare `\x1b`.
96
+ * The literal fallbacks preserve legacy single/double-escape on terminals without it.
97
+ */
98
+ export function handleInputOrEscape(
99
+ data: string,
100
+ input: { handleInput(data: string): void },
101
+ onCancel: () => void,
102
+ ): void {
103
+ if (data === "\x1b" || data === "\x1b\x1b" || matchesKey(data, "escape")) {
104
+ onCancel();
105
+ return;
106
+ }
107
+ input.handleInput(data);
108
+ }
109
+
110
+ // =============================================================================
111
+ // Plugin List Component
112
+ // =============================================================================
113
+
114
+ /**
115
+ * One row in the unified plugin list. npm and marketplace plugins live in
116
+ * separate registries with different shapes, so a tagged union keeps both
117
+ * paths type-safe end-to-end (list rendering, value lookup, detail callback).
118
+ */
119
+ export type PluginListEntry =
120
+ | { kind: "npm"; plugin: InstalledPlugin }
121
+ | { kind: "marketplace"; plugin: InstalledPluginSummary };
122
+
123
+ export interface PluginListCallbacks {
124
+ onNpmSelect: (plugin: InstalledPlugin) => void;
125
+ onMarketplaceSelect: (plugin: InstalledPluginSummary) => void;
126
+ onCancel: () => void;
127
+ }
128
+
129
+ /**
130
+ * True when the marketplace summary's first entry is not explicitly disabled.
131
+ * Mirrors the `/plugins list` convention: a missing `enabled` flag means enabled.
132
+ */
133
+ function marketplaceEnabled(summary: InstalledPluginSummary): boolean {
134
+ return summary.entries[0]?.enabled !== false;
135
+ }
136
+
137
+ async function buildPluginConfigItems(
138
+ plugin: InstalledPlugin,
139
+ manager: PluginSettingsManager,
140
+ onConfigChange: (key: string, value: unknown) => void | Promise<void>,
141
+ requestRender?: () => void,
142
+ ): Promise<SettingItem[]> {
143
+ const schemaSettings = plugin.manifest.settings;
144
+ if (!schemaSettings) return [];
145
+
146
+ const settings = await manager.getPluginSettings(plugin.name);
147
+ const items: SettingItem[] = [];
148
+ for (const key in schemaSettings) {
149
+ const schema = schemaSettings[key];
150
+ const currentValue = settings[key] ?? schema.default;
151
+ const displayValue = schema.secret && currentValue ? "••••••••" : String(currentValue ?? "(not set)");
152
+
153
+ if (schema.type === "boolean") {
154
+ items.push({
155
+ id: `config:${key}`,
156
+ label: ` ${key}`,
157
+ description: schema.description || `Configure ${key}`,
158
+ currentValue: currentValue ? "true" : "false",
159
+ values: ["true", "false"],
160
+ });
161
+ } else if (schema.type === "enum") {
162
+ items.push({
163
+ id: `config:${key}`,
164
+ label: ` ${key}`,
165
+ description: schema.description || `Configure ${key}`,
166
+ currentValue: String(currentValue ?? schema.default ?? ""),
167
+ submenu: (cv, done) =>
168
+ createConfigEnumPanel(
169
+ key,
170
+ schema.description || `Select value for ${key}`,
171
+ schema.values,
172
+ cv,
173
+ value => {
174
+ const result = onConfigChange(key, value);
175
+ done(value);
176
+ return result;
177
+ },
178
+ () => done(),
179
+ requestRender,
180
+ ),
181
+ });
182
+ } else {
183
+ items.push({
184
+ id: `config:${key}`,
185
+ label: ` ${key}`,
186
+ description: schema.description || `Configure ${key}`,
187
+ currentValue: displayValue,
188
+ submenu: (cv, done) =>
189
+ createConfigInputPanel(
190
+ key,
191
+ schema,
192
+ cv === "(not set)" ? "" : cv,
193
+ value => {
194
+ const parsed = schema.type === "number" ? Number(value) : value;
195
+ const result = onConfigChange(key, parsed);
196
+ done(String(value));
197
+ return result;
198
+ },
199
+ () => done(),
200
+ requestRender,
201
+ ),
202
+ });
203
+ }
204
+ }
205
+ return items;
206
+ }
207
+
208
+ /**
209
+ * Stable SelectList value for a list entry. Combined with `findEntryByValue`
210
+ * this keeps lookup correct even when the same plugin id exists in both user
211
+ * and project scope (one of which is `shadowedBy: "project"`).
212
+ */
213
+ function entryValue(entry: PluginListEntry): string {
214
+ if (entry.kind === "npm") return `npm:${entry.plugin.name}`;
215
+ return `mkt:${entry.plugin.scope}:${entry.plugin.id}`;
216
+ }
217
+
218
+ function findEntryByValue(entries: ReadonlyArray<PluginListEntry>, value: string): PluginListEntry | undefined {
219
+ return entries.find(e => entryValue(e) === value);
220
+ }
221
+
222
+ /**
223
+ * Shows installed plugins from both registries (npm + marketplace) with
224
+ * enable/disable status, scope tag, and shadow indicator. Selecting an entry
225
+ * fans out to the kind-specific detail callback.
226
+ */
227
+ export class PluginListComponent extends OverlayPanel {
228
+ readonly #selectList: SelectList;
229
+
230
+ constructor(
231
+ private readonly entries: ReadonlyArray<PluginListEntry>,
232
+ callbacks: PluginListCallbacks,
233
+ ) {
234
+ super("Plugins");
235
+ this.addChild(new Spacer(1));
236
+
237
+ if (entries.length === 0) {
238
+ this.addChild(new Text(theme.fg("muted", "No plugins installed"), 0, 0));
239
+ this.addChild(new Spacer(1));
240
+ this.addChild(new Text(theme.fg("dim", "Install npm plugins: omp plugin install <package>"), 0, 0));
241
+ this.addChild(
242
+ new Text(theme.fg("dim", "Install marketplace plugins: omp plugin install <name>@<marketplace>"), 0, 0),
243
+ );
244
+ this.addChild(new Spacer(1));
245
+
246
+ // Empty list still handles Escape so the user can leave the panel.
247
+ this.#selectList = new SelectList([], 1, getSelectListTheme());
248
+ this.#selectList.onCancel = callbacks.onCancel;
249
+ return;
250
+ }
251
+
252
+ const items: SelectItem[] = entries.map(entry => this.#renderItem(entry));
253
+
254
+ // Marketplace plugin ids (`name@marketplace`) routinely run past the
255
+ // SelectList default primary column (32 chars). Widen the bound so the
256
+ // id remains readable; the description gets whatever width is left.
257
+ this.#selectList = new SelectList(items, Math.min(items.length, 8), getSelectListTheme(), {
258
+ minPrimaryColumnWidth: 24,
259
+ maxPrimaryColumnWidth: 64,
260
+ });
261
+
262
+ this.#selectList.onSelect = item => {
263
+ const found = findEntryByValue(this.entries, item.value);
264
+ if (!found) return;
265
+ if (found.kind === "npm") callbacks.onNpmSelect(found.plugin);
266
+ else callbacks.onMarketplaceSelect(found.plugin);
267
+ };
268
+
269
+ this.#selectList.onCancel = callbacks.onCancel;
270
+
271
+ this.addChild(this.#selectList);
272
+ this.addChild(new Spacer(1));
273
+ this.addChild(new Text(theme.fg("dim", "Enter to configure · Esc to go back"), 0, 0));
274
+ }
275
+
276
+ #renderItem(entry: PluginListEntry): SelectItem {
277
+ const kindBadge = theme.fg("dim", entry.kind === "npm" ? "[npm]" : "[marketplace]");
278
+
279
+ if (entry.kind === "npm") {
280
+ const p = entry.plugin;
281
+ const status = p.enabled
282
+ ? theme.fg("success", theme.status.enabled)
283
+ : theme.fg("muted", theme.status.disabled);
284
+ const featureCount = p.manifest.features ? Object.keys(p.manifest.features).length : 0;
285
+ const enabledCount = p.enabledFeatures?.length ?? featureCount;
286
+
287
+ let details = `${kindBadge} ${theme.sep.dot} v${p.version}`;
288
+ if (featureCount > 0) {
289
+ details += ` ${theme.sep.dot} ${enabledCount}/${featureCount} features`;
290
+ }
291
+
292
+ return {
293
+ value: entryValue(entry),
294
+ label: `${status} ${p.name}`,
295
+ description: details,
296
+ };
297
+ }
298
+
299
+ const summary = entry.plugin;
300
+ const enabled = marketplaceEnabled(summary);
301
+ const status = enabled ? theme.fg("success", theme.status.enabled) : theme.fg("muted", theme.status.disabled);
302
+ const scopeTag = theme.fg("dim", `[${summary.scope}]`);
303
+ const shadowMarker = summary.shadowedBy ? ` ${theme.fg("warning", theme.status.shadowed)}` : "";
304
+ const version = summary.entries[0]?.version ?? "?";
305
+
306
+ let details = `${kindBadge} ${scopeTag} ${theme.sep.dot} v${version}`;
307
+ if (summary.shadowedBy) {
308
+ details += ` ${theme.sep.dot} shadowed by ${summary.shadowedBy}`;
309
+ }
310
+
311
+ return {
312
+ value: entryValue(entry),
313
+ label: `${status} ${summary.id}${shadowMarker}`,
314
+ description: details,
315
+ };
316
+ }
317
+
318
+ handleInput(data: string): void {
319
+ this.#selectList.handleInput(data);
320
+ }
321
+ }
322
+
323
+ // =============================================================================
324
+ // Plugin Detail Component
325
+ // =============================================================================
326
+
327
+ export interface PluginDetailCallbacks {
328
+ onEnabledChange: (enabled: boolean) => void;
329
+ onFeatureChange: (feature: string, enabled: boolean) => void;
330
+ onConfigChange: (key: string, value: unknown) => void | Promise<void>;
331
+ /** Schedules a frame after an asynchronous config submission settles. */
332
+ requestRender?: () => void;
333
+ onBack: () => void;
334
+ }
335
+
336
+ /**
337
+ * Shows detail settings for a single plugin:
338
+ * - Enable/disable toggle
339
+ * - Feature toggles
340
+ * - Config settings
341
+ */
342
+ export class PluginDetailComponent extends OverlayPanel {
343
+ #settingsList!: SettingsFormField;
344
+
345
+ constructor(
346
+ private plugin: InstalledPlugin,
347
+ private readonly manager: PluginSettingsManager,
348
+ private readonly callbacks: PluginDetailCallbacks,
349
+ ) {
350
+ super(plugin.name);
351
+
352
+ void this.#rebuild();
353
+ }
354
+
355
+ async #rebuild(): Promise<void> {
356
+ this.clear();
357
+
358
+ const plugin = this.plugin;
359
+ const manifest = plugin.manifest;
360
+
361
+ this.title = plugin.name;
362
+
363
+ const items: SettingItem[] = [];
364
+
365
+ // Enable/disable toggle
366
+ items.push({
367
+ id: "__enabled__",
368
+ label: "Enabled",
369
+ description: "Enable or disable this plugin",
370
+ currentValue: plugin.enabled ? "true" : "false",
371
+ values: ["true", "false"],
372
+ });
373
+
374
+ // Feature toggles
375
+ if (manifest.features && Object.keys(manifest.features).length > 0) {
376
+ const enabledSet = new Set(plugin.enabledFeatures ?? []);
377
+ const defaultFeatures = Object.entries(manifest.features)
378
+ .filter(([_featureName, f]) => f.default)
379
+ .map(([name]) => name);
380
+
381
+ // If enabledFeatures is null, use defaults
382
+ const effectiveEnabled = plugin.enabledFeatures === null ? new Set(defaultFeatures) : enabledSet;
383
+
384
+ for (const [featName, feat] of Object.entries(manifest.features)) {
385
+ const isEnabled = effectiveEnabled.has(featName);
386
+ items.push({
387
+ id: `feature:${featName}`,
388
+ label: ` ${featName}`,
389
+ description: feat.description || `Enable ${featName} feature`,
390
+ currentValue: isEnabled ? "true" : "false",
391
+ values: ["true", "false"],
392
+ });
393
+ }
394
+ }
395
+
396
+ items.push(
397
+ ...(await buildPluginConfigItems(
398
+ plugin,
399
+ this.manager,
400
+ this.callbacks.onConfigChange,
401
+ this.callbacks.requestRender,
402
+ )),
403
+ );
404
+
405
+ this.#settingsList = new SettingsFormField({
406
+ fieldTheme: formTheme,
407
+ description: manifest.description || undefined,
408
+ items,
409
+ maxVisible: Math.min(items.length, 10),
410
+ settingsTheme: getSettingsListTheme(),
411
+ hint: DETAIL_FOOTER_HINT,
412
+ onChange: (id, newValue) => {
413
+ if (id === "__enabled__") {
414
+ this.callbacks.onEnabledChange(newValue === "true");
415
+ this.plugin = { ...this.plugin, enabled: newValue === "true" };
416
+ } else if (id.startsWith("feature:")) {
417
+ const featName = id.slice(8);
418
+ this.callbacks.onFeatureChange(featName, newValue === "true");
419
+ // Update local state
420
+ const current = new Set(this.plugin.enabledFeatures ?? []);
421
+ if (newValue === "true") {
422
+ current.add(featName);
423
+ } else {
424
+ current.delete(featName);
425
+ }
426
+ this.plugin = { ...this.plugin, enabledFeatures: [...current] };
427
+ } else if (id.startsWith("config:")) {
428
+ const key = id.slice(7);
429
+ const schema = this.plugin.manifest.settings?.[key];
430
+ if (schema?.type === "boolean") {
431
+ this.callbacks.onConfigChange(key, newValue === "true");
432
+ }
433
+ }
434
+ },
435
+ onCancel: this.callbacks.onBack,
436
+ });
437
+
438
+ this.addChild(this.#settingsList);
439
+ }
440
+
441
+ handleInput(data: string): void {
442
+ if (!this.#settingsList) return;
443
+ this.#settingsList.handleInput(data);
444
+ }
445
+ }
446
+
447
+ // =============================================================================
448
+ // Marketplace Plugin Detail Component
449
+ // =============================================================================
450
+
451
+ export interface MarketplacePluginDetailCallbacks {
452
+ parsePluginId: PluginSettingsHost["parsePluginId"];
453
+ onEnabledChange: (enabled: boolean) => void;
454
+ onConfigChange: (pluginName: string, key: string, value: unknown) => void;
455
+ /** Schedules a TUI frame after asynchronous manifest settings load. */
456
+ requestRender?: () => void;
457
+ onBack: () => void;
458
+ }
459
+
460
+ /**
461
+ * Detail view for a marketplace plugin, including settings declared by its
462
+ * runtime package and metadata from the installed-plugins registry.
463
+ */
464
+ export class MarketplacePluginDetailComponent extends OverlayPanel {
465
+ #settingsList!: SettingsFormField;
466
+
467
+ constructor(
468
+ private plugin: InstalledPluginSummary,
469
+ private readonly manager: PluginSettingsManager,
470
+ private readonly callbacks: MarketplacePluginDetailCallbacks,
471
+ ) {
472
+ super(plugin.id);
473
+ this.#render(undefined, []);
474
+ void this.#loadConfig();
475
+ }
476
+
477
+ async #loadConfig(): Promise<void> {
478
+ const entry = this.plugin.entries[0];
479
+ if (!entry) return;
480
+
481
+ const fallbackName = this.callbacks.parsePluginId(this.plugin.id)?.name ?? this.plugin.id;
482
+ try {
483
+ const runtimePlugin = await this.manager.getPlugin(fallbackName, { path: entry.installPath });
484
+ if (!runtimePlugin) return;
485
+ const configItems = await buildPluginConfigItems(
486
+ runtimePlugin,
487
+ this.manager,
488
+ (key, value) => this.callbacks.onConfigChange(runtimePlugin.name, key, value),
489
+ this.callbacks.requestRender,
490
+ );
491
+ this.#render(runtimePlugin, configItems);
492
+ this.callbacks.requestRender?.();
493
+ } catch (err) {
494
+ logger.error("Settings → Plugins: failed to load marketplace plugin settings", {
495
+ pluginId: this.plugin.id,
496
+ path: entry.installPath,
497
+ error: err instanceof Error ? err.message : String(err),
498
+ });
499
+ }
500
+ }
501
+
502
+ #render(runtimePlugin: InstalledPlugin | undefined, configItems: SettingItem[]): void {
503
+ this.clear();
504
+
505
+ const plugin = this.plugin;
506
+ const entry = plugin.entries[0];
507
+ this.title = plugin.id;
508
+ const subtitleParts = [`[${plugin.scope}]`];
509
+ if (plugin.shadowedBy) subtitleParts.push(`${theme.status.shadowed} shadowed by ${plugin.shadowedBy}`);
510
+
511
+ const items: SettingItem[] = [
512
+ {
513
+ id: "__enabled__",
514
+ label: "Enabled",
515
+ description: "Enable or disable this marketplace plugin",
516
+ currentValue: marketplaceEnabled(plugin) ? "true" : "false",
517
+ values: ["true", "false"],
518
+ },
519
+ ...configItems,
520
+ ];
521
+ const summary: Component[] = [
522
+ new Text(theme.fg("dim", `version ${entry?.version ?? "(unknown)"}`), 0, 0),
523
+ new Text(theme.fg("dim", `scope ${plugin.scope}`), 0, 0),
524
+ new Text(
525
+ theme.fg("dim", `install path ${entry?.installPath ? shortenPath(entry.installPath) : "(unknown)"}`),
526
+ 0,
527
+ 0,
528
+ ),
529
+ new Text(theme.fg("dim", `installed at ${entry?.installedAt ?? "(unknown)"}`), 0, 0),
530
+ new Text(theme.fg("dim", `last updated ${entry?.lastUpdated ?? "(unknown)"}`), 0, 0),
531
+ ];
532
+ if (entry?.gitCommitSha) {
533
+ summary.push(new Text(theme.fg("dim", `git sha ${entry.gitCommitSha}`), 0, 0));
534
+ }
535
+ summary.push(new Spacer(1));
536
+ this.#settingsList = new SettingsFormField({
537
+ fieldTheme: formTheme,
538
+ description: subtitleParts.join(" ") || undefined,
539
+ items,
540
+ maxVisible: Math.min(items.length, 10),
541
+ settingsTheme: getSettingsListTheme(),
542
+ summary,
543
+ hint: DETAIL_FOOTER_HINT,
544
+ onChange: (id, newValue) => {
545
+ if (id === "__enabled__") {
546
+ const next = newValue === "true";
547
+ this.callbacks.onEnabledChange(next);
548
+ this.plugin = {
549
+ ...this.plugin,
550
+ entries: this.plugin.entries.map(e => ({ ...e, enabled: next })),
551
+ };
552
+ } else if (id.startsWith("config:")) {
553
+ const key = id.slice(7);
554
+ if (runtimePlugin?.manifest.settings?.[key]?.type === "boolean") {
555
+ this.callbacks.onConfigChange(runtimePlugin.name, key, newValue === "true");
556
+ }
557
+ }
558
+ },
559
+ onCancel: this.callbacks.onBack,
560
+ });
561
+
562
+ this.addChild(this.#settingsList);
563
+ }
564
+
565
+ handleInput(data: string): void {
566
+ this.#settingsList.handleInput(data);
567
+ }
568
+ }
569
+
570
+ // =============================================================================
571
+ // Config Submenus
572
+ // =============================================================================
573
+
574
+ /** Shared footer hint for plugin detail lists. */
575
+ const DETAIL_FOOTER_HINT = "Enter to edit · Esc to go back";
576
+
577
+ /**
578
+ * Thin OverlayPanel boundary around a shared form field. The panel title stays
579
+ * the setting key; input, paste, focus, and mouse all forward to the field.
580
+ */
581
+ class ConfigFieldPanel extends OverlayPanel {
582
+ #field: FormField;
583
+
584
+ constructor(key: string, field: FormField) {
585
+ super(key);
586
+ this.#field = field;
587
+ this.addChild(field);
588
+ }
589
+
590
+ get focused(): boolean {
591
+ return this.#field.focused;
592
+ }
593
+
594
+ set focused(value: boolean) {
595
+ this.#field.focused = value;
596
+ }
597
+
598
+ setUseTerminalCursor(useTerminalCursor: boolean): void {
599
+ this.#field.setUseTerminalCursor(useTerminalCursor);
600
+ }
601
+
602
+ handleInput(data: string): void {
603
+ this.#field.handleInput(data);
604
+ }
605
+
606
+ pasteText(text: string): void {
607
+ this.#field.pasteText(text);
608
+ }
609
+
610
+ routeMouse(event: SgrMouseEvent, line: number, col: number): void {
611
+ // OverlayPanel contributes one top-border row and a two-column body inset.
612
+ this.#field.routeMouse(event, line - 1, col - 2);
613
+ }
614
+ }
615
+
616
+ /** Enum setting submenu backed by a shared select field. */
617
+ function createConfigEnumPanel(
618
+ key: string,
619
+ description: string,
620
+ values: string[],
621
+ currentValue: string,
622
+ onSelect: (value: string) => void | Promise<void>,
623
+ onCancel: () => void,
624
+ requestRender?: () => void,
625
+ ): Component {
626
+ const field = new SelectFormField({
627
+ theme: formTheme,
628
+ description: description || undefined,
629
+ leadingSpace: true,
630
+ items: values.map(v => ({ value: v, label: v })),
631
+ currentValue,
632
+ maxVisible: 8,
633
+ selectTheme: getSelectListTheme(),
634
+ hint: "Enter to select · Esc to cancel",
635
+ onSubmit: onSelect,
636
+ onCancel,
637
+ requestRender,
638
+ });
639
+ return new ConfigFieldPanel(key, field);
640
+ }
641
+
642
+ /** String/number setting submenu backed by a shared text field. */
643
+ function createConfigInputPanel(
644
+ key: string,
645
+ schema: PluginSettingSchema,
646
+ currentValue: string,
647
+ onSubmit: (value: string) => void | Promise<void>,
648
+ onCancel: () => void,
649
+ requestRender?: () => void,
650
+ ): Component {
651
+ let typeHint = `Type: ${schema.type}`;
652
+ if (schema.type === "number" && (schema.min !== undefined || schema.max !== undefined)) {
653
+ typeHint += ` (${schema.min ?? ""}..${schema.max ?? ""})`;
654
+ }
655
+ const field = new TextFormField({
656
+ theme: formTheme,
657
+ description: schema.description || undefined,
658
+ details: [...(schema.description ? [new Spacer(1)] : []), new Text(theme.fg("dim", typeHint), 0, 0)],
659
+ leadingSpace: true,
660
+ secret: schema.secret,
661
+ initialValue: !schema.secret ? currentValue : undefined,
662
+ empty: "cancel",
663
+ hint: "Enter to save · Esc to cancel",
664
+ onSubmit,
665
+ onCancel,
666
+ requestRender,
667
+ });
668
+ return new ConfigFieldPanel(key, field);
669
+ }
670
+
671
+ // =============================================================================
672
+ // Main Plugin Settings Selector
673
+ // =============================================================================
674
+
675
+ export interface PluginSettingsCallbacks {
676
+ onClose: () => void;
677
+ onPluginChanged: () => void | Promise<void>;
678
+ /** Schedules a TUI frame after asynchronous plugin data loads. */
679
+ requestRender?: () => void;
680
+ }
681
+
682
+ /** Component with handleInput method */
683
+ interface InputHandler {
684
+ handleInput(data: string): void;
685
+ }
686
+
687
+ /**
688
+ * Top-level plugin settings component.
689
+ * Manages navigation between plugin list and plugin detail views.
690
+ */
691
+ export class PluginSettingsComponent extends Container {
692
+ readonly #host: PluginSettingsHost;
693
+ #manager: PluginSettingsManager;
694
+ #viewComponent: (Component & InputHandler) | null = null;
695
+ // oxlint-disable-next-line no-unused-private-class-members -- state tracking for view management
696
+ #currentView: "list" | "npm-detail" | "marketplace-detail" = "list";
697
+ // oxlint-disable-next-line no-unused-private-class-members -- state tracking for view management
698
+ #currentPlugin: InstalledPlugin | null = null;
699
+ // oxlint-disable-next-line no-unused-private-class-members -- state tracking for view management
700
+ #currentMarketplacePlugin: InstalledPluginSummary | null = null;
701
+
702
+ constructor(
703
+ host: PluginSettingsHost,
704
+ private readonly callbacks: PluginSettingsCallbacks,
705
+ ) {
706
+ super();
707
+ this.#host = host;
708
+ this.#manager = host.manager;
709
+ this.#showPluginList();
710
+ }
711
+
712
+ async #showPluginList(): Promise<void> {
713
+ this.#currentView = "list";
714
+ this.#currentPlugin = null;
715
+ this.#currentMarketplacePlugin = null;
716
+ this.clear();
717
+
718
+ // Surface registry failures without taking the whole tab down — either
719
+ // registry can fail to load (corrupt JSON, missing project root) and the
720
+ // user still benefits from the other half. An uncaught rejection here
721
+ // would also leave the tab permanently blank: this method is invoked
722
+ // fire-and-forget from the constructor, so nothing awaits it.
723
+ const [npmPlugins, marketplacePlugins] = await Promise.all([
724
+ this.#manager.list().catch((err): InstalledPlugin[] => {
725
+ logger.error("Settings → Plugins: failed to list npm plugins", {
726
+ error: err instanceof Error ? err.message : String(err),
727
+ });
728
+ return [];
729
+ }),
730
+ this.#host
731
+ .createMarketplaceManager()
732
+ .then(mgr => mgr.listInstalledPlugins())
733
+ .catch((err): InstalledPluginSummary[] => {
734
+ logger.error("Settings → Plugins: failed to list marketplace plugins", {
735
+ error: err instanceof Error ? err.message : String(err),
736
+ });
737
+ return [];
738
+ }),
739
+ ]);
740
+
741
+ const entries: PluginListEntry[] = [
742
+ ...npmPlugins.map((plugin): PluginListEntry => ({ kind: "npm", plugin })),
743
+ ...marketplacePlugins.map((plugin): PluginListEntry => ({ kind: "marketplace", plugin })),
744
+ ];
745
+
746
+ this.#viewComponent = new PluginListComponent(entries, {
747
+ onNpmSelect: plugin => this.#showPluginDetail(plugin),
748
+ onMarketplaceSelect: plugin => this.#showMarketplaceDetail(plugin),
749
+ onCancel: () => this.callbacks.onClose(),
750
+ });
751
+
752
+ this.addChild(this.#viewComponent);
753
+
754
+ // The list mounts after the first frame (npm + marketplace listing is
755
+ // async and this method runs fire-and-forget), so ask for a repaint —
756
+ // otherwise the tab stays blank until an unrelated event forces a
757
+ // render, e.g. reopening /settings (issue #9526).
758
+ this.callbacks.requestRender?.();
759
+ }
760
+
761
+ #showPluginDetail(plugin: InstalledPlugin): void {
762
+ this.#currentView = "npm-detail";
763
+ this.#currentPlugin = plugin;
764
+ this.#currentMarketplacePlugin = null;
765
+ this.clear();
766
+
767
+ this.#viewComponent = new PluginDetailComponent(plugin, this.#manager, {
768
+ onEnabledChange: async enabled => {
769
+ await this.#manager.setEnabled(plugin.name, enabled);
770
+ await this.callbacks.onPluginChanged();
771
+ },
772
+ onFeatureChange: async (feature, enabled) => {
773
+ const current = new Set((await this.#manager.getEnabledFeatures(plugin.name)) ?? []);
774
+ if (enabled) {
775
+ current.add(feature);
776
+ } else {
777
+ current.delete(feature);
778
+ }
779
+ await this.#manager.setEnabledFeatures(plugin.name, [...current]);
780
+ await this.callbacks.onPluginChanged();
781
+ },
782
+ onConfigChange: async (key, value) => {
783
+ await this.#manager.setPluginSetting(plugin.name, key, value);
784
+ await this.callbacks.onPluginChanged();
785
+ },
786
+ onBack: () => this.#showPluginList(),
787
+ requestRender: this.callbacks.requestRender,
788
+ });
789
+
790
+ this.addChild(this.#viewComponent);
791
+ }
792
+
793
+ #showMarketplaceDetail(plugin: InstalledPluginSummary): void {
794
+ this.#currentView = "marketplace-detail";
795
+ this.#currentPlugin = null;
796
+ this.#currentMarketplacePlugin = plugin;
797
+ this.clear();
798
+
799
+ this.#viewComponent = new MarketplacePluginDetailComponent(plugin, this.#manager, {
800
+ parsePluginId: id => this.#host.parsePluginId(id),
801
+ onEnabledChange: async enabled => {
802
+ try {
803
+ const mgr = await this.#host.createMarketplaceManager();
804
+ await mgr.setPluginEnabled(plugin.id, enabled, plugin.scope);
805
+ await this.callbacks.onPluginChanged();
806
+ } catch (err) {
807
+ logger.error("Settings → Plugins: failed to toggle marketplace plugin", {
808
+ pluginId: plugin.id,
809
+ scope: plugin.scope,
810
+ enabled,
811
+ error: err instanceof Error ? err.message : String(err),
812
+ });
813
+ }
814
+ },
815
+ onConfigChange: async (pluginName, key, value) => {
816
+ await this.#manager.setPluginSetting(pluginName, key, value);
817
+ await this.callbacks.onPluginChanged();
818
+ },
819
+ onBack: () => this.#showPluginList(),
820
+ requestRender: this.callbacks.requestRender,
821
+ });
822
+
823
+ this.addChild(this.#viewComponent);
824
+ }
825
+
826
+ handleInput(data: string): void {
827
+ if (!this.#viewComponent) {
828
+ // The list view mounts asynchronously (npm + marketplace listing).
829
+ // Until it does — or if listing rejected and no view ever mounted —
830
+ // Escape must still close the panel instead of leaving /settings
831
+ // non-dismissible.
832
+ if (data === "\x1b" || data === "\x1b\x1b") {
833
+ this.callbacks.onClose();
834
+ }
835
+ return;
836
+ }
837
+ this.#viewComponent.handleInput(data);
838
+ }
839
+ }