apex-code 0.5.0 → 0.5.1

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 (509) hide show
  1. package/CHANGELOG.md +257 -5
  2. package/README.md +624 -0
  3. package/dist/bun/cli.d.ts +3 -1
  4. package/dist/bun/cli.d.ts.map +1 -1
  5. package/dist/bun/cli.js +3 -9
  6. package/dist/bun/cli.js.map +1 -1
  7. package/dist/bun/runtime-setup.d.ts +2 -0
  8. package/dist/bun/runtime-setup.d.ts.map +1 -0
  9. package/dist/bun/runtime-setup.js +9 -0
  10. package/dist/bun/runtime-setup.js.map +1 -0
  11. package/dist/bun/sandbox-env-setup.d.ts +2 -0
  12. package/dist/bun/sandbox-env-setup.d.ts.map +1 -0
  13. package/dist/bun/sandbox-env-setup.js +4 -0
  14. package/dist/bun/sandbox-env-setup.js.map +1 -0
  15. package/dist/cli/args.d.ts.map +1 -1
  16. package/dist/cli/args.js +16 -5
  17. package/dist/cli/args.js.map +1 -1
  18. package/dist/cli/config-selector.d.ts.map +1 -1
  19. package/dist/cli/config-selector.js +2 -1
  20. package/dist/cli/config-selector.js.map +1 -1
  21. package/dist/cli/file-processor.d.ts +1 -1
  22. package/dist/cli/file-processor.d.ts.map +1 -1
  23. package/dist/cli/file-processor.js.map +1 -1
  24. package/dist/cli/session-picker.d.ts +1 -1
  25. package/dist/cli/session-picker.d.ts.map +1 -1
  26. package/dist/cli/session-picker.js.map +1 -1
  27. package/dist/cli/setup.d.ts +2 -0
  28. package/dist/cli/setup.d.ts.map +1 -0
  29. package/dist/cli/setup.js +13 -0
  30. package/dist/cli/setup.js.map +1 -0
  31. package/dist/config.d.ts +1 -0
  32. package/dist/config.d.ts.map +1 -1
  33. package/dist/config.js +1 -0
  34. package/dist/config.js.map +1 -1
  35. package/dist/core/agent-session-runtime.d.ts.map +1 -1
  36. package/dist/core/agent-session-runtime.js +16 -8
  37. package/dist/core/agent-session-runtime.js.map +1 -1
  38. package/dist/core/agent-session.d.ts +85 -15
  39. package/dist/core/agent-session.d.ts.map +1 -1
  40. package/dist/core/agent-session.js +685 -266
  41. package/dist/core/agent-session.js.map +1 -1
  42. package/dist/core/auth-storage.d.ts +4 -0
  43. package/dist/core/auth-storage.d.ts.map +1 -1
  44. package/dist/core/auth-storage.js +55 -0
  45. package/dist/core/auth-storage.js.map +1 -1
  46. package/dist/core/bug-report.d.ts +176 -0
  47. package/dist/core/bug-report.d.ts.map +1 -0
  48. package/dist/core/bug-report.js +291 -0
  49. package/dist/core/bug-report.js.map +1 -0
  50. package/dist/core/cache-stats.d.ts.map +1 -1
  51. package/dist/core/cache-stats.js +12 -1
  52. package/dist/core/cache-stats.js.map +1 -1
  53. package/dist/core/cache-warmer.d.ts +103 -0
  54. package/dist/core/cache-warmer.d.ts.map +1 -0
  55. package/dist/core/cache-warmer.js +356 -0
  56. package/dist/core/cache-warmer.js.map +1 -0
  57. package/dist/core/compaction/branch-summarization.d.ts +1 -1
  58. package/dist/core/compaction/branch-summarization.d.ts.map +1 -1
  59. package/dist/core/compaction/branch-summarization.js +4 -3
  60. package/dist/core/compaction/branch-summarization.js.map +1 -1
  61. package/dist/core/compaction/compaction.d.ts +5 -3
  62. package/dist/core/compaction/compaction.d.ts.map +1 -1
  63. package/dist/core/compaction/compaction.js +163 -66
  64. package/dist/core/compaction/compaction.js.map +1 -1
  65. package/dist/core/context/pipeline.d.ts +8 -8
  66. package/dist/core/context/pipeline.d.ts.map +1 -1
  67. package/dist/core/context/pipeline.js +21 -12
  68. package/dist/core/context/pipeline.js.map +1 -1
  69. package/dist/core/crash-log.d.ts +27 -0
  70. package/dist/core/crash-log.d.ts.map +1 -0
  71. package/dist/core/crash-log.js +139 -0
  72. package/dist/core/crash-log.js.map +1 -0
  73. package/dist/core/export-html/template.js +6 -1
  74. package/dist/core/extensions/index.d.ts +1 -1
  75. package/dist/core/extensions/index.d.ts.map +1 -1
  76. package/dist/core/extensions/index.js.map +1 -1
  77. package/dist/core/extensions/jiti-loader.d.ts +2 -0
  78. package/dist/core/extensions/jiti-loader.d.ts.map +1 -0
  79. package/dist/core/extensions/jiti-loader.js +4 -0
  80. package/dist/core/extensions/jiti-loader.js.map +1 -0
  81. package/dist/core/extensions/jiti-static-loader.d.ts +2 -0
  82. package/dist/core/extensions/jiti-static-loader.d.ts.map +1 -0
  83. package/dist/core/extensions/jiti-static-loader.js +4 -0
  84. package/dist/core/extensions/jiti-static-loader.js.map +1 -0
  85. package/dist/core/extensions/loader.d.ts.map +1 -1
  86. package/dist/core/extensions/loader.js +40 -59
  87. package/dist/core/extensions/loader.js.map +1 -1
  88. package/dist/core/extensions/runner.d.ts +24 -8
  89. package/dist/core/extensions/runner.d.ts.map +1 -1
  90. package/dist/core/extensions/runner.js +195 -72
  91. package/dist/core/extensions/runner.js.map +1 -1
  92. package/dist/core/extensions/types.d.ts +142 -55
  93. package/dist/core/extensions/types.d.ts.map +1 -1
  94. package/dist/core/extensions/types.js.map +1 -1
  95. package/dist/core/extensions/virtual-modules.d.ts +3 -0
  96. package/dist/core/extensions/virtual-modules.d.ts.map +1 -0
  97. package/dist/core/extensions/virtual-modules.js +41 -0
  98. package/dist/core/extensions/virtual-modules.js.map +1 -0
  99. package/dist/core/extensions/wrapper.d.ts.map +1 -1
  100. package/dist/core/extensions/wrapper.js +1 -20
  101. package/dist/core/extensions/wrapper.js.map +1 -1
  102. package/dist/core/http-dispatcher.d.ts.map +1 -1
  103. package/dist/core/http-dispatcher.js +2 -0
  104. package/dist/core/http-dispatcher.js.map +1 -1
  105. package/dist/core/index.d.ts +2 -1
  106. package/dist/core/index.d.ts.map +1 -1
  107. package/dist/core/index.js.map +1 -1
  108. package/dist/core/keybindings.d.ts +6 -1
  109. package/dist/core/keybindings.d.ts.map +1 -1
  110. package/dist/core/keybindings.js +5 -1
  111. package/dist/core/keybindings.js.map +1 -1
  112. package/dist/core/messages.d.ts +1 -1
  113. package/dist/core/messages.d.ts.map +1 -1
  114. package/dist/core/messages.js +1 -0
  115. package/dist/core/messages.js.map +1 -1
  116. package/dist/core/model-config.d.ts +168 -20
  117. package/dist/core/model-config.d.ts.map +1 -1
  118. package/dist/core/model-config.js +44 -18
  119. package/dist/core/model-config.js.map +1 -1
  120. package/dist/core/model-registry.d.ts +5 -1
  121. package/dist/core/model-registry.d.ts.map +1 -1
  122. package/dist/core/model-registry.js +8 -0
  123. package/dist/core/model-registry.js.map +1 -1
  124. package/dist/core/model-resolver.d.ts.map +1 -1
  125. package/dist/core/model-resolver.js +4 -3
  126. package/dist/core/model-resolver.js.map +1 -1
  127. package/dist/core/model-runtime.d.ts +1 -0
  128. package/dist/core/model-runtime.d.ts.map +1 -1
  129. package/dist/core/model-runtime.js +12 -6
  130. package/dist/core/model-runtime.js.map +1 -1
  131. package/dist/core/permissions/store.d.ts.map +1 -1
  132. package/dist/core/permissions/store.js +11 -8
  133. package/dist/core/permissions/store.js.map +1 -1
  134. package/dist/core/prompt-templates.d.ts +6 -1
  135. package/dist/core/prompt-templates.d.ts.map +1 -1
  136. package/dist/core/prompt-templates.js +61 -35
  137. package/dist/core/prompt-templates.js.map +1 -1
  138. package/dist/core/provider-composer.d.ts +4 -2
  139. package/dist/core/provider-composer.d.ts.map +1 -1
  140. package/dist/core/provider-composer.js +29 -2
  141. package/dist/core/provider-composer.js.map +1 -1
  142. package/dist/core/radius.d.ts +3 -0
  143. package/dist/core/radius.d.ts.map +1 -1
  144. package/dist/core/radius.js +6 -0
  145. package/dist/core/radius.js.map +1 -1
  146. package/dist/core/resource-loader.d.ts.map +1 -1
  147. package/dist/core/resource-loader.js +6 -2
  148. package/dist/core/resource-loader.js.map +1 -1
  149. package/dist/core/sdk.d.ts.map +1 -1
  150. package/dist/core/sdk.js +63 -42
  151. package/dist/core/sdk.js.map +1 -1
  152. package/dist/core/session-export.d.ts +6 -2
  153. package/dist/core/session-export.d.ts.map +1 -1
  154. package/dist/core/session-export.js +14 -13
  155. package/dist/core/session-export.js.map +1 -1
  156. package/dist/core/session-manager.d.ts +66 -15
  157. package/dist/core/session-manager.d.ts.map +1 -1
  158. package/dist/core/session-manager.js +295 -115
  159. package/dist/core/session-manager.js.map +1 -1
  160. package/dist/core/settings-manager.d.ts +27 -4
  161. package/dist/core/settings-manager.d.ts.map +1 -1
  162. package/dist/core/settings-manager.js +55 -7
  163. package/dist/core/settings-manager.js.map +1 -1
  164. package/dist/core/skills.d.ts +1 -1
  165. package/dist/core/skills.d.ts.map +1 -1
  166. package/dist/core/skills.js +5 -4
  167. package/dist/core/skills.js.map +1 -1
  168. package/dist/core/slash-commands.d.ts.map +1 -1
  169. package/dist/core/slash-commands.js +1 -0
  170. package/dist/core/slash-commands.js.map +1 -1
  171. package/dist/core/system-prompt.d.ts +49 -6
  172. package/dist/core/system-prompt.d.ts.map +1 -1
  173. package/dist/core/system-prompt.js +140 -79
  174. package/dist/core/system-prompt.js.map +1 -1
  175. package/dist/core/tools/bash.d.ts +7 -8
  176. package/dist/core/tools/bash.d.ts.map +1 -1
  177. package/dist/core/tools/bash.js +20 -115
  178. package/dist/core/tools/bash.js.map +1 -1
  179. package/dist/core/tools/edit.d.ts +1 -12
  180. package/dist/core/tools/edit.d.ts.map +1 -1
  181. package/dist/core/tools/edit.js +6 -204
  182. package/dist/core/tools/edit.js.map +1 -1
  183. package/dist/core/tools/find.d.ts.map +1 -1
  184. package/dist/core/tools/find.js +4 -55
  185. package/dist/core/tools/find.js.map +1 -1
  186. package/dist/core/tools/grep.d.ts.map +1 -1
  187. package/dist/core/tools/grep.js +4 -60
  188. package/dist/core/tools/grep.js.map +1 -1
  189. package/dist/core/tools/ls.d.ts.map +1 -1
  190. package/dist/core/tools/ls.js +4 -49
  191. package/dist/core/tools/ls.js.map +1 -1
  192. package/dist/core/tools/read.d.ts +4 -1
  193. package/dist/core/tools/read.d.ts.map +1 -1
  194. package/dist/core/tools/read.js +10 -125
  195. package/dist/core/tools/read.js.map +1 -1
  196. package/dist/core/tools/renderers/bash.d.ts +18 -0
  197. package/dist/core/tools/renderers/bash.d.ts.map +1 -0
  198. package/dist/core/tools/renderers/bash.js +126 -0
  199. package/dist/core/tools/renderers/bash.js.map +1 -0
  200. package/dist/core/tools/renderers/edit.d.ts +23 -0
  201. package/dist/core/tools/renderers/edit.d.ts.map +1 -0
  202. package/dist/core/tools/renderers/edit.js +207 -0
  203. package/dist/core/tools/renderers/edit.js.map +1 -0
  204. package/dist/core/tools/renderers/find.d.ts +10 -0
  205. package/dist/core/tools/renderers/find.d.ts.map +1 -0
  206. package/dist/core/tools/renderers/find.js +64 -0
  207. package/dist/core/tools/renderers/find.js.map +1 -0
  208. package/dist/core/tools/renderers/grep.d.ts +10 -0
  209. package/dist/core/tools/renderers/grep.d.ts.map +1 -0
  210. package/dist/core/tools/renderers/grep.js +69 -0
  211. package/dist/core/tools/renderers/grep.js.map +1 -0
  212. package/dist/core/tools/renderers/index.d.ts +34 -0
  213. package/dist/core/tools/renderers/index.d.ts.map +1 -0
  214. package/dist/core/tools/renderers/index.js +47 -0
  215. package/dist/core/tools/renderers/index.js.map +1 -0
  216. package/dist/core/tools/renderers/ls.d.ts +10 -0
  217. package/dist/core/tools/renderers/ls.d.ts.map +1 -0
  218. package/dist/core/tools/renderers/ls.js +58 -0
  219. package/dist/core/tools/renderers/ls.js.map +1 -0
  220. package/dist/core/tools/renderers/read.d.ts +11 -0
  221. package/dist/core/tools/renderers/read.d.ts.map +1 -0
  222. package/dist/core/tools/renderers/read.js +132 -0
  223. package/dist/core/tools/renderers/read.js.map +1 -0
  224. package/dist/core/tools/renderers/write.d.ts +10 -0
  225. package/dist/core/tools/renderers/write.d.ts.map +1 -0
  226. package/dist/core/tools/renderers/write.js +152 -0
  227. package/dist/core/tools/renderers/write.js.map +1 -0
  228. package/dist/core/tools/write.d.ts.map +1 -1
  229. package/dist/core/tools/write.js +6 -146
  230. package/dist/core/tools/write.js.map +1 -1
  231. package/dist/core/usage-totals.d.ts +1 -1
  232. package/dist/core/usage-totals.d.ts.map +1 -1
  233. package/dist/core/usage-totals.js +5 -1
  234. package/dist/core/usage-totals.js.map +1 -1
  235. package/dist/extensions/llama/client.d.ts +2 -0
  236. package/dist/extensions/llama/client.d.ts.map +1 -1
  237. package/dist/extensions/llama/client.js +7 -3
  238. package/dist/extensions/llama/client.js.map +1 -1
  239. package/dist/extensions/llama/provider.d.ts.map +1 -1
  240. package/dist/extensions/llama/provider.js +20 -5
  241. package/dist/extensions/llama/provider.js.map +1 -1
  242. package/dist/index.d.ts +5 -4
  243. package/dist/index.d.ts.map +1 -1
  244. package/dist/index.js +1 -1
  245. package/dist/index.js.map +1 -1
  246. package/dist/main.d.ts.map +1 -1
  247. package/dist/main.js +21 -13
  248. package/dist/main.js.map +1 -1
  249. package/dist/modes/interactive/bug-report.d.ts +16 -0
  250. package/dist/modes/interactive/bug-report.d.ts.map +1 -0
  251. package/dist/modes/interactive/bug-report.js +167 -0
  252. package/dist/modes/interactive/bug-report.js.map +1 -0
  253. package/dist/modes/interactive/chat-viewport.d.ts +20 -0
  254. package/dist/modes/interactive/chat-viewport.d.ts.map +1 -0
  255. package/dist/modes/interactive/chat-viewport.js +28 -0
  256. package/dist/modes/interactive/chat-viewport.js.map +1 -0
  257. package/dist/modes/interactive/components/assistant-message.d.ts +7 -0
  258. package/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
  259. package/dist/modes/interactive/components/assistant-message.js +63 -17
  260. package/dist/modes/interactive/components/assistant-message.js.map +1 -1
  261. package/dist/modes/interactive/components/branch-summary-message.d.ts.map +1 -1
  262. package/dist/modes/interactive/components/branch-summary-message.js +12 -5
  263. package/dist/modes/interactive/components/branch-summary-message.js.map +1 -1
  264. package/dist/modes/interactive/components/compaction-summary-message.d.ts.map +1 -1
  265. package/dist/modes/interactive/components/compaction-summary-message.js +12 -5
  266. package/dist/modes/interactive/components/compaction-summary-message.js.map +1 -1
  267. package/dist/modes/interactive/components/custom-editor.d.ts +12 -0
  268. package/dist/modes/interactive/components/custom-editor.d.ts.map +1 -1
  269. package/dist/modes/interactive/components/custom-editor.js +38 -0
  270. package/dist/modes/interactive/components/custom-editor.js.map +1 -1
  271. package/dist/modes/interactive/components/error-summary.d.ts +11 -0
  272. package/dist/modes/interactive/components/error-summary.d.ts.map +1 -0
  273. package/dist/modes/interactive/components/error-summary.js +31 -0
  274. package/dist/modes/interactive/components/error-summary.js.map +1 -0
  275. package/dist/modes/interactive/components/extension-editor.d.ts +4 -1
  276. package/dist/modes/interactive/components/extension-editor.d.ts.map +1 -1
  277. package/dist/modes/interactive/components/extension-editor.js +6 -1
  278. package/dist/modes/interactive/components/extension-editor.js.map +1 -1
  279. package/dist/modes/interactive/components/extension-input.d.ts +2 -0
  280. package/dist/modes/interactive/components/extension-input.d.ts.map +1 -1
  281. package/dist/modes/interactive/components/extension-input.js +6 -0
  282. package/dist/modes/interactive/components/extension-input.js.map +1 -1
  283. package/dist/modes/interactive/components/extension-selector.d.ts +2 -0
  284. package/dist/modes/interactive/components/extension-selector.d.ts.map +1 -1
  285. package/dist/modes/interactive/components/extension-selector.js +4 -0
  286. package/dist/modes/interactive/components/extension-selector.js.map +1 -1
  287. package/dist/modes/interactive/components/footer.d.ts.map +1 -1
  288. package/dist/modes/interactive/components/footer.js +4 -1
  289. package/dist/modes/interactive/components/footer.js.map +1 -1
  290. package/dist/modes/interactive/components/index.d.ts +1 -1
  291. package/dist/modes/interactive/components/index.d.ts.map +1 -1
  292. package/dist/modes/interactive/components/index.js.map +1 -1
  293. package/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
  294. package/dist/modes/interactive/components/model-selector.js +3 -3
  295. package/dist/modes/interactive/components/model-selector.js.map +1 -1
  296. package/dist/modes/interactive/components/scoped-models-selector.d.ts.map +1 -1
  297. package/dist/modes/interactive/components/scoped-models-selector.js +13 -15
  298. package/dist/modes/interactive/components/scoped-models-selector.js.map +1 -1
  299. package/dist/modes/interactive/components/session-selector.d.ts +5 -5
  300. package/dist/modes/interactive/components/session-selector.d.ts.map +1 -1
  301. package/dist/modes/interactive/components/session-selector.js +74 -48
  302. package/dist/modes/interactive/components/session-selector.js.map +1 -1
  303. package/dist/modes/interactive/components/settings-selector.d.ts +5 -1
  304. package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  305. package/dist/modes/interactive/components/settings-selector.js +35 -10
  306. package/dist/modes/interactive/components/settings-selector.js.map +1 -1
  307. package/dist/modes/interactive/components/skill-invocation-message.d.ts.map +1 -1
  308. package/dist/modes/interactive/components/skill-invocation-message.js +11 -4
  309. package/dist/modes/interactive/components/skill-invocation-message.js.map +1 -1
  310. package/dist/modes/interactive/components/status-indicator.d.ts +3 -1
  311. package/dist/modes/interactive/components/status-indicator.d.ts.map +1 -1
  312. package/dist/modes/interactive/components/status-indicator.js +10 -3
  313. package/dist/modes/interactive/components/status-indicator.js.map +1 -1
  314. package/dist/modes/interactive/components/thinking-selector.d.ts.map +1 -1
  315. package/dist/modes/interactive/components/thinking-selector.js +6 -6
  316. package/dist/modes/interactive/components/thinking-selector.js.map +1 -1
  317. package/dist/modes/interactive/components/tool-execution.d.ts +27 -5
  318. package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  319. package/dist/modes/interactive/components/tool-execution.js +64 -42
  320. package/dist/modes/interactive/components/tool-execution.js.map +1 -1
  321. package/dist/modes/interactive/components/tree-selector.d.ts.map +1 -1
  322. package/dist/modes/interactive/components/tree-selector.js +9 -0
  323. package/dist/modes/interactive/components/tree-selector.js.map +1 -1
  324. package/dist/modes/interactive/components/trust-selector.d.ts.map +1 -1
  325. package/dist/modes/interactive/components/trust-selector.js +2 -2
  326. package/dist/modes/interactive/components/trust-selector.js.map +1 -1
  327. package/dist/modes/interactive/interactive-mode.d.ts +43 -29
  328. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  329. package/dist/modes/interactive/interactive-mode.js +431 -166
  330. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  331. package/dist/modes/interactive/theme/dark.json +2 -1
  332. package/dist/modes/interactive/theme/light.json +2 -1
  333. package/dist/modes/interactive/theme/theme-controller.d.ts +1 -0
  334. package/dist/modes/interactive/theme/theme-controller.d.ts.map +1 -1
  335. package/dist/modes/interactive/theme/theme-controller.js +5 -0
  336. package/dist/modes/interactive/theme/theme-controller.js.map +1 -1
  337. package/dist/modes/interactive/theme/theme-json.d.ts +84 -0
  338. package/dist/modes/interactive/theme/theme-json.d.ts.map +1 -0
  339. package/dist/modes/interactive/theme/theme-json.js +130 -0
  340. package/dist/modes/interactive/theme/theme-json.js.map +1 -0
  341. package/dist/modes/interactive/theme/theme-schema.json +6 -2
  342. package/dist/modes/interactive/theme/theme.d.ts +14 -5
  343. package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  344. package/dist/modes/interactive/theme/theme.js +18 -119
  345. package/dist/modes/interactive/theme/theme.js.map +1 -1
  346. package/dist/modes/interactive/tui-renderer.d.ts +21 -0
  347. package/dist/modes/interactive/tui-renderer.d.ts.map +1 -0
  348. package/dist/modes/interactive/tui-renderer.js +66 -0
  349. package/dist/modes/interactive/tui-renderer.js.map +1 -0
  350. package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  351. package/dist/modes/rpc/rpc-mode.js +2 -2
  352. package/dist/modes/rpc/rpc-mode.js.map +1 -1
  353. package/dist/testing/replay/recorded-provider.d.ts +2 -2
  354. package/dist/testing/replay/recorded-provider.d.ts.map +1 -1
  355. package/dist/testing/replay/recorded-provider.js +2 -5
  356. package/dist/testing/replay/recorded-provider.js.map +1 -1
  357. package/dist/testing/replay/runner.d.ts.map +1 -1
  358. package/dist/testing/replay/runner.js +0 -1
  359. package/dist/testing/replay/runner.js.map +1 -1
  360. package/dist/utils/clipboard-command.d.ts +7 -0
  361. package/dist/utils/clipboard-command.d.ts.map +1 -0
  362. package/dist/utils/clipboard-command.js +45 -0
  363. package/dist/utils/clipboard-command.js.map +1 -0
  364. package/dist/utils/clipboard-image.d.ts.map +1 -1
  365. package/dist/utils/clipboard-image.js +53 -81
  366. package/dist/utils/clipboard-image.js.map +1 -1
  367. package/dist/utils/clipboard.d.ts.map +1 -1
  368. package/dist/utils/clipboard.js +111 -121
  369. package/dist/utils/clipboard.js.map +1 -1
  370. package/dist/utils/exif-orientation.d.ts.map +1 -1
  371. package/dist/utils/exif-orientation.js +2 -3
  372. package/dist/utils/exif-orientation.js.map +1 -1
  373. package/dist/utils/mime.d.ts.map +1 -1
  374. package/dist/utils/mime.js +1 -1
  375. package/dist/utils/mime.js.map +1 -1
  376. package/dist/utils/syntax-highlight.d.ts.map +1 -1
  377. package/dist/utils/syntax-highlight.js +21 -21
  378. package/dist/utils/syntax-highlight.js.map +1 -1
  379. package/dist/utils/tool-result-images.d.ts +3 -1
  380. package/dist/utils/tool-result-images.d.ts.map +1 -1
  381. package/dist/utils/tool-result-images.js +4 -1
  382. package/dist/utils/tool-result-images.js.map +1 -1
  383. package/dist/utils/tools-manager.d.ts.map +1 -1
  384. package/dist/utils/tools-manager.js +11 -2
  385. package/dist/utils/tools-manager.js.map +1 -1
  386. package/dist/utils/wsl.d.ts +3 -0
  387. package/dist/utils/wsl.d.ts.map +1 -0
  388. package/dist/utils/wsl.js +15 -0
  389. package/dist/utils/wsl.js.map +1 -0
  390. package/dist/utils/zip.d.ts +7 -0
  391. package/dist/utils/zip.d.ts.map +1 -0
  392. package/dist/utils/zip.js +60 -0
  393. package/dist/utils/zip.js.map +1 -0
  394. package/docs/cli-integration.md +107 -0
  395. package/docs/cli.md +269 -0
  396. package/docs/compaction.md +73 -24
  397. package/docs/configuration.md +45 -0
  398. package/docs/containerization.md +22 -21
  399. package/docs/custom-provider.md +21 -12
  400. package/docs/development.md +19 -0
  401. package/docs/docs.json +139 -95
  402. package/docs/extensions.md +243 -371
  403. package/docs/how-pi-works.md +49 -0
  404. package/docs/images/interactive-mode.png +0 -0
  405. package/docs/index.md +3 -3
  406. package/docs/json.md +7 -3
  407. package/docs/keybindings.md +57 -54
  408. package/docs/llama-cpp.md +2 -2
  409. package/docs/message-types.md +261 -0
  410. package/docs/models.md +103 -49
  411. package/docs/packages.md +49 -47
  412. package/docs/prompt-templates.md +29 -56
  413. package/docs/providers.md +91 -139
  414. package/docs/quickstart.md +42 -14
  415. package/docs/rpc-commands.md +854 -0
  416. package/docs/rpc-extension-ui.md +200 -0
  417. package/docs/rpc.md +121 -223
  418. package/docs/sdk.md +99 -124
  419. package/docs/security.md +7 -7
  420. package/docs/session-format.md +85 -82
  421. package/docs/sessions.md +41 -57
  422. package/docs/settings.md +94 -87
  423. package/docs/shell-aliases.md +68 -3
  424. package/docs/skills.md +62 -49
  425. package/docs/slash-commands.md +60 -0
  426. package/docs/terminal-setup.md +86 -55
  427. package/docs/termux.md +65 -75
  428. package/docs/themes.md +64 -86
  429. package/docs/tmux.md +29 -7
  430. package/docs/tui.md +67 -122
  431. package/docs/usage.md +31 -42
  432. package/docs/windows.md +41 -13
  433. package/examples/README.md +16 -2
  434. package/examples/extensions/README.md +0 -1
  435. package/examples/extensions/custom-provider-anthropic/index.ts +18 -12
  436. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  437. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  438. package/examples/extensions/custom-provider-gitlab-duo/index.ts +2 -2
  439. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  440. package/examples/extensions/dynamic-resources/dynamic.json +2 -0
  441. package/examples/extensions/gondolin/package-lock.json +2 -2
  442. package/examples/extensions/gondolin/package.json +1 -1
  443. package/examples/extensions/prompt-customizer.ts +19 -67
  444. package/examples/extensions/sandbox/package-lock.json +2 -2
  445. package/examples/extensions/sandbox/package.json +1 -1
  446. package/examples/extensions/with-deps/package-lock.json +2 -2
  447. package/examples/extensions/with-deps/package.json +1 -1
  448. package/examples/plugins/pi-example-plugin/README.md +38 -0
  449. package/examples/plugins/pi-example-plugin/package.json +10 -0
  450. package/examples/plugins/pi-example-plugin/src/contract.ts +13 -0
  451. package/examples/plugins/pi-example-plugin/src/session.ts +24 -0
  452. package/examples/plugins/pi-example-plugin/src/tui.ts +31 -0
  453. package/examples/rpc-client.ts +35 -0
  454. package/examples/rpc-extension-ui.ts +25 -5
  455. package/examples/sdk/README.md +1 -1
  456. package/npm-shrinkwrap.json +861 -513
  457. package/package.json +27 -18
  458. package/dist/bun/register-bedrock.d.ts +0 -2
  459. package/dist/bun/register-bedrock.d.ts.map +0 -1
  460. package/dist/bun/register-bedrock.js +0 -4
  461. package/dist/bun/register-bedrock.js.map +0 -1
  462. package/dist/cli/experimental/auth.d.ts +0 -16
  463. package/dist/cli/experimental/auth.d.ts.map +0 -1
  464. package/dist/cli/experimental/auth.js +0 -13
  465. package/dist/cli/experimental/auth.js.map +0 -1
  466. package/dist/cli/experimental/cli.d.ts +0 -6
  467. package/dist/cli/experimental/cli.d.ts.map +0 -1
  468. package/dist/cli/experimental/cli.js +0 -5
  469. package/dist/cli/experimental/cli.js.map +0 -1
  470. package/dist/cli/experimental/command-options.d.ts +0 -17
  471. package/dist/cli/experimental/command-options.d.ts.map +0 -1
  472. package/dist/cli/experimental/command-options.js +0 -35
  473. package/dist/cli/experimental/command-options.js.map +0 -1
  474. package/dist/cli/experimental/command.d.ts +0 -63
  475. package/dist/cli/experimental/command.d.ts.map +0 -1
  476. package/dist/cli/experimental/command.js +0 -130
  477. package/dist/cli/experimental/command.js.map +0 -1
  478. package/dist/cli/experimental/commands/client.d.ts +0 -13
  479. package/dist/cli/experimental/commands/client.d.ts.map +0 -1
  480. package/dist/cli/experimental/commands/client.js +0 -25
  481. package/dist/cli/experimental/commands/client.js.map +0 -1
  482. package/dist/cli/experimental/commands/pi.d.ts +0 -15
  483. package/dist/cli/experimental/commands/pi.d.ts.map +0 -1
  484. package/dist/cli/experimental/commands/pi.js +0 -28
  485. package/dist/cli/experimental/commands/pi.js.map +0 -1
  486. package/dist/cli/experimental/commands/server.d.ts +0 -13
  487. package/dist/cli/experimental/commands/server.d.ts.map +0 -1
  488. package/dist/cli/experimental/commands/server.js +0 -25
  489. package/dist/cli/experimental/commands/server.js.map +0 -1
  490. package/dist/cli/experimental/transport-address.d.ts +0 -10
  491. package/dist/cli/experimental/transport-address.d.ts.map +0 -1
  492. package/dist/cli/experimental/transport-address.js +0 -38
  493. package/dist/cli/experimental/transport-address.js.map +0 -1
  494. package/dist/client/index.d.ts +0 -3
  495. package/dist/client/index.d.ts.map +0 -1
  496. package/dist/client/index.js +0 -3
  497. package/dist/client/index.js.map +0 -1
  498. package/dist/client/remote-session.d.ts +0 -53
  499. package/dist/client/remote-session.d.ts.map +0 -1
  500. package/dist/client/remote-session.js +0 -340
  501. package/dist/client/remote-session.js.map +0 -1
  502. package/dist/client/transcript.d.ts +0 -12
  503. package/dist/client/transcript.d.ts.map +0 -1
  504. package/dist/client/transcript.js +0 -98
  505. package/dist/client/transcript.js.map +0 -1
  506. package/dist/utils/clipboard-native.d.ts +0 -11
  507. package/dist/utils/clipboard-native.d.ts.map +0 -1
  508. package/dist/utils/clipboard-native.js +0 -20
  509. package/dist/utils/clipboard-native.js.map +0 -1
@@ -4,7 +4,7 @@ export { BashExecutionComponent } from "./bash-execution.ts";
4
4
  export { BorderedLoader } from "./bordered-loader.ts";
5
5
  export { BranchSummaryMessageComponent } from "./branch-summary-message.ts";
6
6
  export { CompactionSummaryMessageComponent } from "./compaction-summary-message.ts";
7
- export { CustomEditor } from "./custom-editor.ts";
7
+ export { CustomEditor, type CustomEditorOptions } from "./custom-editor.ts";
8
8
  export { CustomMessageComponent } from "./custom-message.ts";
9
9
  export { DaxnutsComponent } from "./daxnuts.ts";
10
10
  export { type RenderDiffOptions, renderDiff } from "./diff.ts";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAC5C,OAAO,EAAE,yBAAyB,EAAE,MAAM,wBAAwB,CAAC;AACnE,OAAO,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AAC7D,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,6BAA6B,EAAE,MAAM,6BAA6B,CAAC;AAC5E,OAAO,EAAE,iCAAiC,EAAE,MAAM,iCAAiC,CAAC;AACpF,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AAC7D,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAChD,OAAO,EAAE,KAAK,iBAAiB,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAC/D,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,wBAAwB,EAAE,MAAM,uBAAuB,CAAC;AACjE,OAAO,EAAE,uBAAuB,EAAE,MAAM,sBAAsB,CAAC;AAC/D,OAAO,EAAE,0BAA0B,EAAE,MAAM,yBAAyB,CAAC;AACrE,OAAO,EACN,uBAAuB,EACvB,KAAK,qBAAqB,EAC1B,KAAK,oBAAoB,GACzB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AACrE,OAAO,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AACzD,OAAO,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AAC7D,OAAO,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AAC7D,OAAO,EAAE,KAAK,eAAe,EAAE,KAAK,YAAY,EAAE,6BAA6B,EAAE,MAAM,6BAA6B,CAAC;AACrH,OAAO,EAAE,wBAAwB,EAAE,MAAM,uBAAuB,CAAC;AACjE,OAAO,EAAE,KAAK,iBAAiB,EAAE,KAAK,cAAc,EAAE,yBAAyB,EAAE,MAAM,wBAAwB,CAAC;AAChH,OAAO,EAAE,2BAA2B,EAAE,MAAM,2BAA2B,CAAC;AACxE,OAAO,EAAE,+BAA+B,EAAE,MAAM,+BAA+B,CAAC;AAChF,OAAO,EACN,gBAAgB,EAChB,KAAK,uBAAuB,EAC5B,KAAK,wBAAwB,GAC7B,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AAC7D,OAAO,EAAE,yBAAyB,EAAE,MAAM,wBAAwB,CAAC;AACnE,OAAO,EAAE,sBAAsB,EAAE,KAAK,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AACxF,cAAc,iBAAiB,CAAC;AAChC,OAAO,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAC3D,OAAO,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AAC7D,OAAO,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AACzD,OAAO,EAAE,4BAA4B,EAAE,MAAM,4BAA4B,CAAC;AAC1E,OAAO,EAAE,qBAAqB,EAAE,KAAK,oBAAoB,EAAE,MAAM,sBAAsB,CAAC","sourcesContent":["// UI Components for extensions\nexport { ArminComponent } from \"./armin.ts\";\nexport { AssistantMessageComponent } from \"./assistant-message.ts\";\nexport { BashExecutionComponent } from \"./bash-execution.ts\";\nexport { BorderedLoader } from \"./bordered-loader.ts\";\nexport { BranchSummaryMessageComponent } from \"./branch-summary-message.ts\";\nexport { CompactionSummaryMessageComponent } from \"./compaction-summary-message.ts\";\nexport { CustomEditor } from \"./custom-editor.ts\";\nexport { CustomMessageComponent } from \"./custom-message.ts\";\nexport { DaxnutsComponent } from \"./daxnuts.ts\";\nexport { type RenderDiffOptions, renderDiff } from \"./diff.ts\";\nexport { DynamicBorder } from \"./dynamic-border.ts\";\nexport { ExtensionEditorComponent } from \"./extension-editor.ts\";\nexport { ExtensionInputComponent } from \"./extension-input.ts\";\nexport { ExtensionSelectorComponent } from \"./extension-selector.ts\";\nexport {\n\tFirstTimeSetupComponent,\n\ttype FirstTimeSetupOptions,\n\ttype FirstTimeSetupResult,\n} from \"./first-time-setup.ts\";\nexport { FooterComponent } from \"./footer.ts\";\nexport { keyHint, keyText, rawKeyHint } from \"./keybinding-hints.ts\";\nexport { LoginDialogComponent } from \"./login-dialog.ts\";\nexport { ModelSelectorComponent } from \"./model-selector.ts\";\nexport { OAuthSelectorComponent } from \"./oauth-selector.ts\";\nexport { type ModelsCallbacks, type ModelsConfig, ScopedModelsSelectorComponent } from \"./scoped-models-selector.ts\";\nexport { SessionSelectorComponent } from \"./session-selector.ts\";\nexport { type SettingsCallbacks, type SettingsConfig, SettingsSelectorComponent } from \"./settings-selector.ts\";\nexport { ShowImagesSelectorComponent } from \"./show-images-selector.ts\";\nexport { SkillInvocationMessageComponent } from \"./skill-invocation-message.ts\";\nexport {\n\tApexSplashHeader,\n\ttype ApexSplashHeaderOptions,\n\ttype SplashHeaderMetadataLine,\n} from \"./splash-header.ts\";\nexport { ThemeSelectorComponent } from \"./theme-selector.ts\";\nexport { ThinkingSelectorComponent } from \"./thinking-selector.ts\";\nexport { ToolExecutionComponent, type ToolExecutionOptions } from \"./tool-execution.ts\";\nexport * from \"./tool-panel.ts\";\nexport { TreeSelectorComponent } from \"./tree-selector.ts\";\nexport { TrustSelectorComponent } from \"./trust-selector.ts\";\nexport { UserMessageComponent } from \"./user-message.ts\";\nexport { UserMessageSelectorComponent } from \"./user-message-selector.ts\";\nexport { truncateToVisualLines, type VisualTruncateResult } from \"./visual-truncate.ts\";\n"]}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAC5C,OAAO,EAAE,yBAAyB,EAAE,MAAM,wBAAwB,CAAC;AACnE,OAAO,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AAC7D,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,6BAA6B,EAAE,MAAM,6BAA6B,CAAC;AAC5E,OAAO,EAAE,iCAAiC,EAAE,MAAM,iCAAiC,CAAC;AACpF,OAAO,EAAE,YAAY,EAAE,KAAK,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAC5E,OAAO,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AAC7D,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAChD,OAAO,EAAE,KAAK,iBAAiB,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAC/D,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,wBAAwB,EAAE,MAAM,uBAAuB,CAAC;AACjE,OAAO,EAAE,uBAAuB,EAAE,MAAM,sBAAsB,CAAC;AAC/D,OAAO,EAAE,0BAA0B,EAAE,MAAM,yBAAyB,CAAC;AACrE,OAAO,EACN,uBAAuB,EACvB,KAAK,qBAAqB,EAC1B,KAAK,oBAAoB,GACzB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AACrE,OAAO,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AACzD,OAAO,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AAC7D,OAAO,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AAC7D,OAAO,EAAE,KAAK,eAAe,EAAE,KAAK,YAAY,EAAE,6BAA6B,EAAE,MAAM,6BAA6B,CAAC;AACrH,OAAO,EAAE,wBAAwB,EAAE,MAAM,uBAAuB,CAAC;AACjE,OAAO,EAAE,KAAK,iBAAiB,EAAE,KAAK,cAAc,EAAE,yBAAyB,EAAE,MAAM,wBAAwB,CAAC;AAChH,OAAO,EAAE,2BAA2B,EAAE,MAAM,2BAA2B,CAAC;AACxE,OAAO,EAAE,+BAA+B,EAAE,MAAM,+BAA+B,CAAC;AAChF,OAAO,EACN,gBAAgB,EAChB,KAAK,uBAAuB,EAC5B,KAAK,wBAAwB,GAC7B,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AAC7D,OAAO,EAAE,yBAAyB,EAAE,MAAM,wBAAwB,CAAC;AACnE,OAAO,EAAE,sBAAsB,EAAE,KAAK,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AACxF,cAAc,iBAAiB,CAAC;AAChC,OAAO,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAC3D,OAAO,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AAC7D,OAAO,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AACzD,OAAO,EAAE,4BAA4B,EAAE,MAAM,4BAA4B,CAAC;AAC1E,OAAO,EAAE,qBAAqB,EAAE,KAAK,oBAAoB,EAAE,MAAM,sBAAsB,CAAC","sourcesContent":["// UI Components for extensions\nexport { ArminComponent } from \"./armin.ts\";\nexport { AssistantMessageComponent } from \"./assistant-message.ts\";\nexport { BashExecutionComponent } from \"./bash-execution.ts\";\nexport { BorderedLoader } from \"./bordered-loader.ts\";\nexport { BranchSummaryMessageComponent } from \"./branch-summary-message.ts\";\nexport { CompactionSummaryMessageComponent } from \"./compaction-summary-message.ts\";\nexport { CustomEditor, type CustomEditorOptions } from \"./custom-editor.ts\";\nexport { CustomMessageComponent } from \"./custom-message.ts\";\nexport { DaxnutsComponent } from \"./daxnuts.ts\";\nexport { type RenderDiffOptions, renderDiff } from \"./diff.ts\";\nexport { DynamicBorder } from \"./dynamic-border.ts\";\nexport { ExtensionEditorComponent } from \"./extension-editor.ts\";\nexport { ExtensionInputComponent } from \"./extension-input.ts\";\nexport { ExtensionSelectorComponent } from \"./extension-selector.ts\";\nexport {\n\tFirstTimeSetupComponent,\n\ttype FirstTimeSetupOptions,\n\ttype FirstTimeSetupResult,\n} from \"./first-time-setup.ts\";\nexport { FooterComponent } from \"./footer.ts\";\nexport { keyHint, keyText, rawKeyHint } from \"./keybinding-hints.ts\";\nexport { LoginDialogComponent } from \"./login-dialog.ts\";\nexport { ModelSelectorComponent } from \"./model-selector.ts\";\nexport { OAuthSelectorComponent } from \"./oauth-selector.ts\";\nexport { type ModelsCallbacks, type ModelsConfig, ScopedModelsSelectorComponent } from \"./scoped-models-selector.ts\";\nexport { SessionSelectorComponent } from \"./session-selector.ts\";\nexport { type SettingsCallbacks, type SettingsConfig, SettingsSelectorComponent } from \"./settings-selector.ts\";\nexport { ShowImagesSelectorComponent } from \"./show-images-selector.ts\";\nexport { SkillInvocationMessageComponent } from \"./skill-invocation-message.ts\";\nexport {\n\tApexSplashHeader,\n\ttype ApexSplashHeaderOptions,\n\ttype SplashHeaderMetadataLine,\n} from \"./splash-header.ts\";\nexport { ThemeSelectorComponent } from \"./theme-selector.ts\";\nexport { ThinkingSelectorComponent } from \"./thinking-selector.ts\";\nexport { ToolExecutionComponent, type ToolExecutionOptions } from \"./tool-execution.ts\";\nexport * from \"./tool-panel.ts\";\nexport { TreeSelectorComponent } from \"./tree-selector.ts\";\nexport { TrustSelectorComponent } from \"./trust-selector.ts\";\nexport { UserMessageComponent } from \"./user-message.ts\";\nexport { UserMessageSelectorComponent } from \"./user-message-selector.ts\";\nexport { truncateToVisualLines, type VisualTruncateResult } from \"./visual-truncate.ts\";\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/modes/interactive/components/index.ts"],"names":[],"mappings":"AAAA,+BAA+B;AAC/B,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAC5C,OAAO,EAAE,yBAAyB,EAAE,MAAM,wBAAwB,CAAC;AACnE,OAAO,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AAC7D,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,6BAA6B,EAAE,MAAM,6BAA6B,CAAC;AAC5E,OAAO,EAAE,iCAAiC,EAAE,MAAM,iCAAiC,CAAC;AACpF,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AAC7D,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAChD,OAAO,EAA0B,UAAU,EAAE,MAAM,WAAW,CAAC;AAC/D,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,wBAAwB,EAAE,MAAM,uBAAuB,CAAC;AACjE,OAAO,EAAE,uBAAuB,EAAE,MAAM,sBAAsB,CAAC;AAC/D,OAAO,EAAE,0BAA0B,EAAE,MAAM,yBAAyB,CAAC;AACrE,OAAO,EACN,uBAAuB,GAGvB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AACrE,OAAO,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AACzD,OAAO,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AAC7D,OAAO,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AAC7D,OAAO,EAA2C,6BAA6B,EAAE,MAAM,6BAA6B,CAAC;AACrH,OAAO,EAAE,wBAAwB,EAAE,MAAM,uBAAuB,CAAC;AACjE,OAAO,EAA+C,yBAAyB,EAAE,MAAM,wBAAwB,CAAC;AAChH,OAAO,EAAE,2BAA2B,EAAE,MAAM,2BAA2B,CAAC;AACxE,OAAO,EAAE,+BAA+B,EAAE,MAAM,+BAA+B,CAAC;AAChF,OAAO,EACN,gBAAgB,GAGhB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AAC7D,OAAO,EAAE,yBAAyB,EAAE,MAAM,wBAAwB,CAAC;AACnE,OAAO,EAAE,sBAAsB,EAA6B,MAAM,qBAAqB,CAAC;AACxF,cAAc,iBAAiB,CAAC;AAChC,OAAO,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAC3D,OAAO,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AAC7D,OAAO,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AACzD,OAAO,EAAE,4BAA4B,EAAE,MAAM,4BAA4B,CAAC;AAC1E,OAAO,EAAE,qBAAqB,EAA6B,MAAM,sBAAsB,CAAC","sourcesContent":["// UI Components for extensions\nexport { ArminComponent } from \"./armin.ts\";\nexport { AssistantMessageComponent } from \"./assistant-message.ts\";\nexport { BashExecutionComponent } from \"./bash-execution.ts\";\nexport { BorderedLoader } from \"./bordered-loader.ts\";\nexport { BranchSummaryMessageComponent } from \"./branch-summary-message.ts\";\nexport { CompactionSummaryMessageComponent } from \"./compaction-summary-message.ts\";\nexport { CustomEditor } from \"./custom-editor.ts\";\nexport { CustomMessageComponent } from \"./custom-message.ts\";\nexport { DaxnutsComponent } from \"./daxnuts.ts\";\nexport { type RenderDiffOptions, renderDiff } from \"./diff.ts\";\nexport { DynamicBorder } from \"./dynamic-border.ts\";\nexport { ExtensionEditorComponent } from \"./extension-editor.ts\";\nexport { ExtensionInputComponent } from \"./extension-input.ts\";\nexport { ExtensionSelectorComponent } from \"./extension-selector.ts\";\nexport {\n\tFirstTimeSetupComponent,\n\ttype FirstTimeSetupOptions,\n\ttype FirstTimeSetupResult,\n} from \"./first-time-setup.ts\";\nexport { FooterComponent } from \"./footer.ts\";\nexport { keyHint, keyText, rawKeyHint } from \"./keybinding-hints.ts\";\nexport { LoginDialogComponent } from \"./login-dialog.ts\";\nexport { ModelSelectorComponent } from \"./model-selector.ts\";\nexport { OAuthSelectorComponent } from \"./oauth-selector.ts\";\nexport { type ModelsCallbacks, type ModelsConfig, ScopedModelsSelectorComponent } from \"./scoped-models-selector.ts\";\nexport { SessionSelectorComponent } from \"./session-selector.ts\";\nexport { type SettingsCallbacks, type SettingsConfig, SettingsSelectorComponent } from \"./settings-selector.ts\";\nexport { ShowImagesSelectorComponent } from \"./show-images-selector.ts\";\nexport { SkillInvocationMessageComponent } from \"./skill-invocation-message.ts\";\nexport {\n\tApexSplashHeader,\n\ttype ApexSplashHeaderOptions,\n\ttype SplashHeaderMetadataLine,\n} from \"./splash-header.ts\";\nexport { ThemeSelectorComponent } from \"./theme-selector.ts\";\nexport { ThinkingSelectorComponent } from \"./thinking-selector.ts\";\nexport { ToolExecutionComponent, type ToolExecutionOptions } from \"./tool-execution.ts\";\nexport * from \"./tool-panel.ts\";\nexport { TreeSelectorComponent } from \"./tree-selector.ts\";\nexport { TrustSelectorComponent } from \"./trust-selector.ts\";\nexport { UserMessageComponent } from \"./user-message.ts\";\nexport { UserMessageSelectorComponent } from \"./user-message-selector.ts\";\nexport { truncateToVisualLines, type VisualTruncateResult } from \"./visual-truncate.ts\";\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/modes/interactive/components/index.ts"],"names":[],"mappings":"AAAA,+BAA+B;AAC/B,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAC5C,OAAO,EAAE,yBAAyB,EAAE,MAAM,wBAAwB,CAAC;AACnE,OAAO,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AAC7D,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,6BAA6B,EAAE,MAAM,6BAA6B,CAAC;AAC5E,OAAO,EAAE,iCAAiC,EAAE,MAAM,iCAAiC,CAAC;AACpF,OAAO,EAAE,YAAY,EAA4B,MAAM,oBAAoB,CAAC;AAC5E,OAAO,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AAC7D,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAChD,OAAO,EAA0B,UAAU,EAAE,MAAM,WAAW,CAAC;AAC/D,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,wBAAwB,EAAE,MAAM,uBAAuB,CAAC;AACjE,OAAO,EAAE,uBAAuB,EAAE,MAAM,sBAAsB,CAAC;AAC/D,OAAO,EAAE,0BAA0B,EAAE,MAAM,yBAAyB,CAAC;AACrE,OAAO,EACN,uBAAuB,GAGvB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AACrE,OAAO,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AACzD,OAAO,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AAC7D,OAAO,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AAC7D,OAAO,EAA2C,6BAA6B,EAAE,MAAM,6BAA6B,CAAC;AACrH,OAAO,EAAE,wBAAwB,EAAE,MAAM,uBAAuB,CAAC;AACjE,OAAO,EAA+C,yBAAyB,EAAE,MAAM,wBAAwB,CAAC;AAChH,OAAO,EAAE,2BAA2B,EAAE,MAAM,2BAA2B,CAAC;AACxE,OAAO,EAAE,+BAA+B,EAAE,MAAM,+BAA+B,CAAC;AAChF,OAAO,EACN,gBAAgB,GAGhB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AAC7D,OAAO,EAAE,yBAAyB,EAAE,MAAM,wBAAwB,CAAC;AACnE,OAAO,EAAE,sBAAsB,EAA6B,MAAM,qBAAqB,CAAC;AACxF,cAAc,iBAAiB,CAAC;AAChC,OAAO,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAC3D,OAAO,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AAC7D,OAAO,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AACzD,OAAO,EAAE,4BAA4B,EAAE,MAAM,4BAA4B,CAAC;AAC1E,OAAO,EAAE,qBAAqB,EAA6B,MAAM,sBAAsB,CAAC","sourcesContent":["// UI Components for extensions\nexport { ArminComponent } from \"./armin.ts\";\nexport { AssistantMessageComponent } from \"./assistant-message.ts\";\nexport { BashExecutionComponent } from \"./bash-execution.ts\";\nexport { BorderedLoader } from \"./bordered-loader.ts\";\nexport { BranchSummaryMessageComponent } from \"./branch-summary-message.ts\";\nexport { CompactionSummaryMessageComponent } from \"./compaction-summary-message.ts\";\nexport { CustomEditor, type CustomEditorOptions } from \"./custom-editor.ts\";\nexport { CustomMessageComponent } from \"./custom-message.ts\";\nexport { DaxnutsComponent } from \"./daxnuts.ts\";\nexport { type RenderDiffOptions, renderDiff } from \"./diff.ts\";\nexport { DynamicBorder } from \"./dynamic-border.ts\";\nexport { ExtensionEditorComponent } from \"./extension-editor.ts\";\nexport { ExtensionInputComponent } from \"./extension-input.ts\";\nexport { ExtensionSelectorComponent } from \"./extension-selector.ts\";\nexport {\n\tFirstTimeSetupComponent,\n\ttype FirstTimeSetupOptions,\n\ttype FirstTimeSetupResult,\n} from \"./first-time-setup.ts\";\nexport { FooterComponent } from \"./footer.ts\";\nexport { keyHint, keyText, rawKeyHint } from \"./keybinding-hints.ts\";\nexport { LoginDialogComponent } from \"./login-dialog.ts\";\nexport { ModelSelectorComponent } from \"./model-selector.ts\";\nexport { OAuthSelectorComponent } from \"./oauth-selector.ts\";\nexport { type ModelsCallbacks, type ModelsConfig, ScopedModelsSelectorComponent } from \"./scoped-models-selector.ts\";\nexport { SessionSelectorComponent } from \"./session-selector.ts\";\nexport { type SettingsCallbacks, type SettingsConfig, SettingsSelectorComponent } from \"./settings-selector.ts\";\nexport { ShowImagesSelectorComponent } from \"./show-images-selector.ts\";\nexport { SkillInvocationMessageComponent } from \"./skill-invocation-message.ts\";\nexport {\n\tApexSplashHeader,\n\ttype ApexSplashHeaderOptions,\n\ttype SplashHeaderMetadataLine,\n} from \"./splash-header.ts\";\nexport { ThemeSelectorComponent } from \"./theme-selector.ts\";\nexport { ThinkingSelectorComponent } from \"./thinking-selector.ts\";\nexport { ToolExecutionComponent, type ToolExecutionOptions } from \"./tool-execution.ts\";\nexport * from \"./tool-panel.ts\";\nexport { TreeSelectorComponent } from \"./tree-selector.ts\";\nexport { TrustSelectorComponent } from \"./trust-selector.ts\";\nexport { UserMessageComponent } from \"./user-message.ts\";\nexport { UserMessageSelectorComponent } from \"./user-message-selector.ts\";\nexport { truncateToVisualLines, type VisualTruncateResult } from \"./visual-truncate.ts\";\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"model-selector.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/model-selector.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n\tclampThinkingLevel,\n\tgetSupportedThinkingLevels,\n\ttype Model,\n\ttype ModelThinkingLevel,\n\tmodelsAreEqual,\n} from \"@earendil-works/pi-ai\";\nimport {\n\ttype Component,\n\tContainer,\n\ttype Focusable,\n\tfuzzyFilter,\n\tgetKeybindings,\n\ttype Input,\n\tKey,\n\tmatchesKey,\n\tSpacer,\n\tText,\n\ttype TUI,\n} from \"@earendil-works/pi-tui\";\nimport type { ModelRuntime } from \"../../../core/model-runtime.ts\";\nimport { refreshModelCatalogs } from \"../model-catalog-refresh.ts\";\nimport { getModelSelectorSearchText } from \"../model-search.ts\";\nimport { paintSelectedRow, paintSelectedRowToWidth, theme } from \"../theme/theme.ts\";\nimport { DynamicBorder } from \"./dynamic-border.ts\";\nimport { type HintKey, hintRow } from \"./keybinding-hints.ts\";\nimport {\n\tcomposeModelRow,\n\ttype EffortLayout,\n\ttype EffortLayoutRow,\n\tgetEffortLayout,\n\tNO_EFFORT_CLUSTER,\n\trenderPricePanel,\n\ttype TrailingSegment,\n} from \"./model-row.ts\";\nimport { PromptInput } from \"./prompt-input.ts\";\n\ninterface ModelItem {\n\tprovider: string;\n\tid: string;\n\tmodel: Model<any>;\n}\n\ninterface ScopedModelItem {\n\tmodel: Model<any>;\n\tthinkingLevel?: string;\n}\n\ninterface DefaultModelReference {\n\tprovider: string;\n\tid: string;\n}\n\ntype ModelScope = \"all\" | \"scoped\";\n\n/**\n * Which of the two steps the selector is on. `provider: null` lists every provider's models at once,\n * which is how a `/model <search>` invocation and a single-provider setup both open.\n */\ntype Step = { kind: \"providers\" } | { kind: \"models\"; provider: string | null };\n\ntype Row =\n\t| { kind: \"provider\"; provider: string; count: number; hasCurrent: boolean }\n\t| { kind: \"model\"; item: ModelItem };\n\nconst MAX_VISIBLE_ROWS = 10;\n\n/**\n * Component that renders a two-step model selector: provider first, then that provider's models.\n */\nexport class ModelSelectorComponent extends Container implements Focusable {\n\tprivate searchInput: Input;\n\n\t// Focusable implementation - propagate to searchInput for IME cursor positioning\n\tprivate _focused = false;\n\tget focused(): boolean {\n\t\treturn this._focused;\n\t}\n\tset focused(value: boolean) {\n\t\tthis._focused = value;\n\t\tthis.searchInput.focused = value;\n\t}\n\tprivate headerContainer: Container;\n\tprivate listContainer: Container;\n\tprivate footerContainer: Container;\n\tprivate allModels: ModelItem[] = [];\n\tprivate scopedModelItems: ModelItem[] = [];\n\tprivate activeModels: ModelItem[] = [];\n\tprivate rows: Row[] = [];\n\tprivate filteredRows: Row[] = [];\n\tprivate selectedIndex: number = 0;\n\tprivate currentModel?: Model<any>;\n\tprivate modelRuntime: ModelRuntime;\n\tprivate onSelectCallback: (model: Model<any>, thinkingLevel?: ModelThinkingLevel) => void;\n\tprivate onSelectAsDefaultCallback?: (model: Model<any>, thinkingLevel?: ModelThinkingLevel) => void;\n\tprivate onCancelCallback: () => void;\n\tprivate errorMessage?: string;\n\tprivate refreshStatusMessage = \"Refreshing model catalogs…\";\n\tprivate refreshStatusSuccess = false;\n\tprivate tui: TUI;\n\tprivate scopedModels: ReadonlyArray<ScopedModelItem>;\n\tprivate defaultModel?: DefaultModelReference;\n\tprivate scope: ModelScope = \"all\";\n\tprivate step: Step = { kind: \"providers\" };\n\tprivate readonly refreshAbortController = new AbortController();\n\tprivate refreshTimeout?: ReturnType<typeof setTimeout>;\n\tprivate closed = false;\n\t/** Effort dialled per model in this session of the picker, keyed by `provider/id`. */\n\tprivate readonly effortLevels = new Map<string, ModelThinkingLevel>();\n\t/** Models whose effort the user actually moved. Only those override the session level. */\n\tprivate readonly editedEffortModels = new Set<string>();\n\tprivate readonly initialThinkingLevel?: ModelThinkingLevel;\n\t/**\n\t * True once up or down has moved the selection into the list. Until then\n\t * left and right belong to the search field's cursor, which is where they\n\t * are needed while a query is still being typed.\n\t */\n\tprivate navigatedIntoList = false;\n\n\tconstructor(\n\t\ttui: TUI,\n\t\tcurrentModel: Model<any> | undefined,\n\t\tmodelRuntime: ModelRuntime,\n\t\tscopedModels: ReadonlyArray<ScopedModelItem>,\n\t\tonSelect: (model: Model<any>, thinkingLevel?: ModelThinkingLevel) => void,\n\t\tonCancel: () => void,\n\t\tinitialSearchInput?: string,\n\t\tonSelectAsDefault?: (model: Model<any>, thinkingLevel?: ModelThinkingLevel) => void,\n\t\tdefaultModel?: DefaultModelReference,\n\t\toptions: { thinkingLevel?: ModelThinkingLevel } = {},\n\t) {\n\t\tsuper();\n\n\t\tthis.tui = tui;\n\t\tthis.initialThinkingLevel = options.thinkingLevel;\n\t\tthis.currentModel = currentModel;\n\t\tthis.modelRuntime = modelRuntime;\n\t\tthis.scopedModels = scopedModels;\n\t\tthis.defaultModel = defaultModel;\n\t\tthis.scope = scopedModels.length > 0 ? \"scoped\" : \"all\";\n\t\tthis.onSelectCallback = onSelect;\n\t\tthis.onSelectAsDefaultCallback = onSelectAsDefault;\n\t\tthis.onCancelCallback = onCancel;\n\n\t\t// Add top border\n\t\tthis.addChild(new DynamicBorder());\n\t\tthis.addChild(new Spacer(1));\n\n\t\tthis.headerContainer = new Container();\n\t\tthis.addChild(this.headerContainer);\n\t\tthis.addChild(new Spacer(1));\n\n\t\t// Create search input\n\t\tthis.searchInput = new PromptInput();\n\t\tif (initialSearchInput) {\n\t\t\tthis.searchInput.setValue(initialSearchInput);\n\t\t}\n\t\tthis.searchInput.onSubmit = () => this.confirmSelection();\n\t\tthis.addChild(this.searchInput);\n\n\t\tthis.addChild(new Spacer(1));\n\n\t\t// Create list container\n\t\tthis.listContainer = new Container();\n\t\tthis.addChild(this.listContainer);\n\n\t\tthis.addChild(new Spacer(1));\n\n\t\t// Keybindings live under the list, where the eye already is once it has\n\t\t// finished reading. Above the list they were the first thing read and the\n\t\t// least useful.\n\t\tthis.footerContainer = new Container();\n\t\tthis.addChild(this.footerContainer);\n\n\t\t// Add bottom border\n\t\tthis.addChild(new DynamicBorder());\n\n\t\t// Render the current snapshot immediately, then refresh in the background.\n\t\tthis.loadModelsFromSnapshot();\n\t\tthis.step = this.initialStep(initialSearchInput);\n\t\tthis.rebuildRows();\n\t\tthis.selectedIndex = this.defaultSelectedIndex();\n\t\tthis.renderHeader();\n\t\tthis.filterRows(this.searchInput.getValue());\n\t\tthis.tui.requestRender();\n\t\tvoid this.refreshModels();\n\t}\n\n\tprivate initialStep(initialSearchInput: string | undefined): Step {\n\t\tconst providers = this.activeProviders();\n\t\tif (initialSearchInput) return { kind: \"models\", provider: null };\n\t\tif (providers.length <= 1) return { kind: \"models\", provider: providers[0] ?? null };\n\t\treturn { kind: \"providers\" };\n\t}\n\n\tprivate activeProviders(): string[] {\n\t\treturn [...new Set(this.activeModels.map((item) => item.provider))];\n\t}\n\n\tprivate loadModelsFromSnapshot(): void {\n\t\tconst models = this.modelRuntime.getAvailableSnapshot().map((model: Model<any>) => ({\n\t\t\tprovider: model.provider,\n\t\t\tid: model.id,\n\t\t\tmodel,\n\t\t}));\n\t\tthis.allModels = this.sortModels(models);\n\t\tthis.scopedModels = this.scopedModels.map((scoped) => {\n\t\t\tconst refreshed = this.modelRuntime.getModel(scoped.model.provider, scoped.model.id);\n\t\t\treturn refreshed ? { ...scoped, model: refreshed } : scoped;\n\t\t});\n\t\tthis.scopedModelItems = this.scopedModels.map((scoped) => ({\n\t\t\tprovider: scoped.model.provider,\n\t\t\tid: scoped.model.id,\n\t\t\tmodel: scoped.model,\n\t\t}));\n\t\tthis.activeModels = this.scope === \"scoped\" ? this.scopedModelItems : this.allModels;\n\t}\n\n\tprivate async refreshModels(): Promise<void> {\n\t\tconst timeoutMs = 15_000;\n\t\tlet timedOut = false;\n\t\tthis.refreshTimeout = setTimeout(() => {\n\t\t\ttimedOut = true;\n\t\t\tthis.refreshAbortController.abort();\n\t\t}, timeoutMs);\n\t\ttry {\n\t\t\tconst result = await refreshModelCatalogs(this.modelRuntime, this.refreshAbortController.signal);\n\t\t\tif (this.closed) return;\n\t\t\tthis.refreshStatusMessage = \"\";\n\t\t\tif (result.aborted && timedOut) {\n\t\t\t\tthis.errorMessage = \"Model refresh timed out; showing cached models.\";\n\t\t\t} else if (result.errors.size === 1) {\n\t\t\t\tthis.errorMessage = `Could not refresh ${result.errors.keys().next().value}; showing cached models.`;\n\t\t\t} else if (result.errors.size > 1) {\n\t\t\t\tthis.errorMessage = `Could not refresh ${result.errors.size} model catalogs (${[...result.errors.keys()].join(\", \")}); showing cached models.`;\n\t\t\t} else {\n\t\t\t\tthis.errorMessage = this.modelRuntime.getError();\n\t\t\t\tif (!this.errorMessage) {\n\t\t\t\t\tthis.refreshStatusMessage = \"Model catalogs refreshed.\";\n\t\t\t\t\tthis.refreshStatusSuccess = true;\n\t\t\t\t}\n\t\t\t}\n\t\t\tthis.loadModelsFromSnapshot();\n\t\t\tthis.rebuildRows();\n\t\t\tthis.renderHeader();\n\t\t\tthis.filterRows(this.searchInput.getValue());\n\t\t\tthis.tui.requestRender();\n\t\t} catch (error) {\n\t\t\tif (this.closed) return;\n\t\t\tthis.refreshStatusMessage = \"\";\n\t\t\tthis.errorMessage = timedOut\n\t\t\t\t? \"Model refresh timed out; showing cached models.\"\n\t\t\t\t: `Could not refresh model catalogs: ${error instanceof Error ? error.message : String(error)}`;\n\t\t\tthis.updateList();\n\t\t\tthis.tui.requestRender();\n\t\t} finally {\n\t\t\tif (this.refreshTimeout) clearTimeout(this.refreshTimeout);\n\t\t}\n\t}\n\n\tdispose(): void {\n\t\tif (this.closed) return;\n\t\tthis.closed = true;\n\t\tif (this.refreshTimeout) clearTimeout(this.refreshTimeout);\n\t\tthis.refreshAbortController.abort();\n\t}\n\n\tprivate sortModels(models: ModelItem[]): ModelItem[] {\n\t\tconst sorted = [...models];\n\t\t// Sort: current model first, default model second, then by provider.\n\t\tsorted.sort((a, b) => {\n\t\t\tconst aIsCurrent = modelsAreEqual(this.currentModel, a.model);\n\t\t\tconst bIsCurrent = modelsAreEqual(this.currentModel, b.model);\n\t\t\tif (aIsCurrent && !bIsCurrent) return -1;\n\t\t\tif (!aIsCurrent && bIsCurrent) return 1;\n\t\t\tconst aIsDefault = this.isDefaultModel(a.model);\n\t\t\tconst bIsDefault = this.isDefaultModel(b.model);\n\t\t\tif (aIsDefault && !bIsDefault) return -1;\n\t\t\tif (!aIsDefault && bIsDefault) return 1;\n\t\t\treturn a.provider.localeCompare(b.provider);\n\t\t});\n\t\treturn sorted;\n\t}\n\n\tprivate rebuildRows(): void {\n\t\tif (this.step.kind === \"providers\") {\n\t\t\tconst byProvider = new Map<string, ModelItem[]>();\n\t\t\tfor (const item of this.activeModels) {\n\t\t\t\tconst existing = byProvider.get(item.provider);\n\t\t\t\tif (existing) existing.push(item);\n\t\t\t\telse byProvider.set(item.provider, [item]);\n\t\t\t}\n\t\t\tconst providerRows: Row[] = [...byProvider].map(([provider, items]) => ({\n\t\t\t\tkind: \"provider\",\n\t\t\t\tprovider,\n\t\t\t\tcount: items.length,\n\t\t\t\thasCurrent: items.some((item) => modelsAreEqual(this.currentModel, item.model)),\n\t\t\t}));\n\t\t\tproviderRows.sort((a, b) => {\n\t\t\t\tif (a.kind !== \"provider\" || b.kind !== \"provider\") return 0;\n\t\t\t\tif (a.hasCurrent !== b.hasCurrent) return a.hasCurrent ? -1 : 1;\n\t\t\t\treturn a.provider.localeCompare(b.provider);\n\t\t\t});\n\t\t\tthis.rows = providerRows;\n\t\t\treturn;\n\t\t}\n\t\tconst provider = this.step.provider;\n\t\tconst models = provider === null ? this.activeModels : this.activeModels.filter((m) => m.provider === provider);\n\t\tthis.rows = models.map((item) => ({ kind: \"model\", item }));\n\t}\n\n\tprivate defaultSelectedIndex(): number {\n\t\tconst index = this.rows.findIndex((row) =>\n\t\t\trow.kind === \"provider\" ? row.hasCurrent : modelsAreEqual(this.currentModel, row.item.model),\n\t\t);\n\t\treturn index >= 0 ? index : 0;\n\t}\n\n\tprivate setStep(step: Step): void {\n\t\tthis.step = step;\n\t\tthis.navigatedIntoList = false;\n\t\tthis.searchInput.setValue(\"\");\n\t\tthis.rebuildRows();\n\t\tthis.selectedIndex = this.defaultSelectedIndex();\n\t\tthis.renderHeader();\n\t\tthis.filterRows(\"\");\n\t\tthis.tui.requestRender();\n\t}\n\n\tprivate isDrilledIn(): boolean {\n\t\treturn this.step.kind === \"models\" && this.step.provider !== null;\n\t}\n\n\t/** Escape returns to the provider list whenever there is a provider list worth returning to. */\n\tprivate canGoBack(): boolean {\n\t\treturn this.step.kind === \"models\" && this.activeProviders().length > 1;\n\t}\n\n\tprivate renderHeader(): void {\n\t\tthis.headerContainer.clear();\n\n\t\tif (this.step.kind === \"providers\") {\n\t\t\tthis.headerContainer.addChild(new Text(theme.bold(theme.fg(\"accent\", \"Select a provider\")), 0, 0));\n\t\t} else {\n\t\t\tconst label = this.step.provider ?? \"All providers\";\n\t\t\tconst crumb = `${theme.bold(theme.fg(\"accent\", label))}${theme.fg(\"muted\", \" › select a model\")}`;\n\t\t\tthis.headerContainer.addChild(new Text(crumb, 0, 0));\n\t\t}\n\n\t\tif (this.scopedModelItems.length > 0) {\n\t\t\tthis.headerContainer.addChild(new Text(this.getScopeText(), 0, 0));\n\t\t} else if (!this.isDrilledIn()) {\n\t\t\tconst hintText = \"Only showing models from configured providers. Use /login to add providers.\";\n\t\t\tthis.headerContainer.addChild(new Text(theme.fg(\"warning\", hintText), 0, 0));\n\t\t}\n\n\t\tthis.renderFooter();\n\t}\n\n\tprivate renderFooter(): void {\n\t\tthis.footerContainer.clear();\n\t\tthis.footerContainer.addChild(new Text(this.getHintText(), 0, 0));\n\t}\n\n\tprivate getScopeText(): string {\n\t\tconst allText = this.scope === \"all\" ? theme.fg(\"accent\", \"all\") : theme.fg(\"muted\", \"all\");\n\t\tconst scopedText = this.scope === \"scoped\" ? theme.fg(\"accent\", \"scoped\") : theme.fg(\"muted\", \"scoped\");\n\t\treturn `${theme.fg(\"muted\", \"Scope: \")}${allText}${theme.fg(\"muted\", \" | \")}${scopedText}`;\n\t}\n\n\tprivate getHintText(): string {\n\t\tconst hints: Array<readonly [HintKey, string]> = [\n\t\t\t[[\"tui.select.up\", \"tui.select.down\"], \"move\"],\n\t\t\t[\"tui.select.confirm\", \"select\"],\n\t\t];\n\t\tif (this.hasAdjustableEffort()) hints.push([[\"tui.editor.cursorLeft\", \"tui.editor.cursorRight\"], \"effort\"]);\n\t\tif (this.scopedModelItems.length > 0) hints.push([\"tui.input.tab\", \"scope\"]);\n\t\tif (this.onSelectAsDefaultCallback) hints.push([{ literal: \"ctrl+s\" }, \"set as default\"]);\n\t\tif (this.canGoBack()) hints.push([{ literal: \"escape\" }, \"providers\"], [{ literal: \"ctrl+c\" }, \"close\"]);\n\t\telse hints.push([\"tui.select.cancel\", \"close\"]);\n\t\treturn hintRow(hints);\n\t}\n\n\t/** Levels worth dialling. A model with nothing but \"off\" has no cluster to show. */\n\tprivate getSelectableLevels(item: ModelItem): ModelThinkingLevel[] {\n\t\tconst levels = getSupportedThinkingLevels(item.model);\n\t\tif (levels.length === 1 && levels[0] === \"off\") return [];\n\t\treturn levels;\n\t}\n\n\tprivate getEffort(item: ModelItem): ModelThinkingLevel | undefined {\n\t\tconst levels = this.getSelectableLevels(item);\n\t\tif (levels.length === 0) return undefined;\n\t\tconst key = `${item.provider}/${item.id}`;\n\t\tconst stored = this.effortLevels.get(key);\n\t\tif (stored !== undefined && levels.includes(stored)) return stored;\n\t\tconst requested = this.initialThinkingLevel ?? \"off\";\n\t\tconst clamped = levels.includes(requested) ? requested : clampThinkingLevel(item.model, requested);\n\t\tconst resolved = levels.includes(clamped) ? clamped : levels[0]!;\n\t\tthis.effortLevels.set(key, resolved);\n\t\treturn resolved;\n\t}\n\n\tprivate adjustEffort(item: ModelItem, direction: number): boolean {\n\t\tconst levels = this.getSelectableLevels(item);\n\t\tif (levels.length === 0) return false;\n\t\tconst key = `${item.provider}/${item.id}`;\n\t\tconst current = this.getEffort(item) ?? levels[0]!;\n\t\tconst next = levels[(levels.indexOf(current) + direction + levels.length) % levels.length]!;\n\t\tthis.effortLevels.set(key, next);\n\t\tthis.editedEffortModels.add(key);\n\t\treturn true;\n\t}\n\n\t/** The effort to hand back on select, or undefined when the user left it alone. */\n\tprivate getChosenEffort(item: ModelItem): ModelThinkingLevel | undefined {\n\t\treturn this.editedEffortModels.has(`${item.provider}/${item.id}`) ? this.getEffort(item) : undefined;\n\t}\n\n\tprivate hasAdjustableEffort(): boolean {\n\t\treturn this.filteredRows.some((row) => row.kind === \"model\" && this.getSelectableLevels(row.item).length > 0);\n\t}\n\n\t/**\n\t * Context that trails a model row, right-aligned, in reading order.\n\t *\n\t * Each segment carries what it costs to lose, because a narrow row drops by\n\t * that and not by position. `current` outranks the id: the row already shows\n\t * the model's name, so the id is largely recoverable from it, while nothing\n\t * else on screen says which model the session is on. The provider badge goes\n\t * first, being the least you lose.\n\t */\n\tprivate getTrailingSegments(item: ModelItem): TrailingSegment[] {\n\t\tconst segments: TrailingSegment[] = [];\n\t\tif (this.step.kind === \"models\" && this.step.provider === null) {\n\t\t\tsegments.push({ text: theme.fg(\"muted\", item.provider), priority: 0 });\n\t\t}\n\t\tsegments.push({ text: theme.fg(\"muted\", item.id), priority: 2 });\n\t\tif (this.isDefaultModel(item.model)) segments.push({ text: theme.fg(\"muted\", \"default\"), priority: 1 });\n\t\tif (modelsAreEqual(this.currentModel, item.model)) {\n\t\t\tsegments.push({ text: theme.fg(\"success\", \"current\"), priority: 3 });\n\t\t}\n\t\treturn segments;\n\t}\n\n\tprivate isDefaultModel(model: Model<any>): boolean {\n\t\treturn this.defaultModel?.provider === model.provider && this.defaultModel.id === model.id;\n\t}\n\n\tprivate isDefaultSearch(query: string): boolean {\n\t\tconst normalized = query.trim().toLowerCase();\n\t\treturn normalized.length > 0 && \"default\".startsWith(normalized);\n\t}\n\n\tprivate setScope(scope: ModelScope): void {\n\t\tif (this.scope === scope) return;\n\t\tthis.scope = scope;\n\t\tthis.activeModels = this.scope === \"scoped\" ? this.scopedModelItems : this.allModels;\n\t\tthis.rebuildRows();\n\t\tthis.selectedIndex = this.defaultSelectedIndex();\n\t\tthis.renderHeader();\n\t\tthis.filterRows(this.searchInput.getValue());\n\t}\n\n\tprivate rowSearchText(row: Row): string {\n\t\tif (row.kind === \"provider\") return row.provider;\n\t\tconst defaultText = this.isDefaultModel(row.item.model) ? \" default\" : \"\";\n\t\treturn `${getModelSelectorSearchText({ id: row.item.id, provider: row.item.provider, name: row.item.model.name })}${defaultText}`;\n\t}\n\n\tprivate isDefaultRow(row: Row): boolean {\n\t\treturn row.kind === \"model\" && this.isDefaultModel(row.item.model);\n\t}\n\n\tprivate filterRows(query: string): void {\n\t\tif (!query) {\n\t\t\tthis.filteredRows = this.rows;\n\t\t} else {\n\t\t\tconst filtered = fuzzyFilter(this.rows, query, (row) => this.rowSearchText(row));\n\t\t\tif (this.isDefaultSearch(query)) {\n\t\t\t\t// Typing \"default\" (or a prefix of it) surfaces the default model even\n\t\t\t\t// when its name doesn't otherwise fuzzy-match the query.\n\t\t\t\tconst defaultRows = this.rows.filter((row) => this.isDefaultRow(row));\n\t\t\t\tthis.filteredRows = [...defaultRows, ...filtered.filter((row) => !this.isDefaultRow(row))];\n\t\t\t} else {\n\t\t\t\tthis.filteredRows = filtered;\n\t\t\t}\n\t\t}\n\t\t// When filtering by a query, move the selector to the top row so the best\n\t\t// match is highlighted. When the query is cleared, keep the current position\n\t\t// clamped to the (restored) list length.\n\t\tthis.selectedIndex = query ? 0 : Math.min(this.selectedIndex, Math.max(0, this.filteredRows.length - 1));\n\t\t// The effort hint depends on what survived the filter, so the footer is\n\t\t// rebuilt here rather than with the header, which runs before filtering.\n\t\tthis.renderFooter();\n\t\tthis.updateList();\n\t}\n\n\tprivate renderProviderRow(row: Extract<Row, { kind: \"provider\" }>, isSelected: boolean): string {\n\t\tconst prefix = isSelected ? \"→ \" : \" \";\n\t\tconst count = theme.fg(\"muted\", ` (${row.count} model${row.count === 1 ? \"\" : \"s\"})`);\n\t\tconst checkmark = row.hasCurrent ? theme.fg(\"success\", \" ✓\") : \"\";\n\t\tconst composed = `${prefix}${row.provider}${count}${checkmark}`;\n\t\treturn isSelected ? paintSelectedRow(composed) : composed;\n\t}\n\n\t/**\n\t * The visible page of rows, rendered together because the effort cluster is\n\t * placed once for the whole page rather than per row.\n\t */\n\tprivate createRowsComponent(startIndex: number, endIndex: number): Component {\n\t\tconst rows = this.filteredRows.slice(startIndex, endIndex);\n\t\treturn {\n\t\t\trender: (width: number): string[] => {\n\t\t\t\tconst layout = this.pageEffortLayout(rows, width);\n\t\t\t\treturn rows.map((row, offset) => {\n\t\t\t\t\tconst isSelected = startIndex + offset === this.selectedIndex;\n\t\t\t\t\tif (row.kind === \"provider\") return this.renderProviderRow(row, isSelected);\n\t\t\t\t\tconst composed = composeModelRow({\n\t\t\t\t\t\tname: row.item.model.name,\n\t\t\t\t\t\tlevels: this.getSelectableLevels(row.item),\n\t\t\t\t\t\teffort: this.getEffort(row.item),\n\t\t\t\t\t\ttrailingSegments: this.getTrailingSegments(row.item),\n\t\t\t\t\t\tselected: isSelected,\n\t\t\t\t\t\tlayout,\n\t\t\t\t\t\twidth,\n\t\t\t\t\t});\n\t\t\t\t\treturn isSelected ? paintSelectedRowToWidth(composed, width) : composed;\n\t\t\t\t});\n\t\t\t},\n\t\t\tinvalidate: () => {},\n\t\t};\n\t}\n\n\tprivate pageEffortLayout(rows: ReadonlyArray<Row>, width: number): EffortLayout {\n\t\tconst layoutRows: EffortLayoutRow[] = [];\n\t\tfor (const row of rows) {\n\t\t\tif (row.kind !== \"model\") continue;\n\t\t\tlayoutRows.push({\n\t\t\t\tname: row.item.model.name,\n\t\t\t\tlevels: this.getSelectableLevels(row.item),\n\t\t\t\ttrailingSegments: this.getTrailingSegments(row.item),\n\t\t\t});\n\t\t}\n\t\treturn layoutRows.length > 0 ? getEffortLayout(layoutRows, width) : NO_EFFORT_CLUSTER;\n\t}\n\n\tprivate updateList(): void {\n\t\tthis.listContainer.clear();\n\n\t\tconst startIndex = Math.max(\n\t\t\t0,\n\t\t\tMath.min(this.selectedIndex - Math.floor(MAX_VISIBLE_ROWS / 2), this.filteredRows.length - MAX_VISIBLE_ROWS),\n\t\t);\n\t\tconst endIndex = Math.min(startIndex + MAX_VISIBLE_ROWS, this.filteredRows.length);\n\n\t\tif (endIndex > startIndex) {\n\t\t\tthis.listContainer.addChild(this.createRowsComponent(startIndex, endIndex));\n\t\t}\n\n\t\t// Add scroll indicator if needed\n\t\tif (startIndex > 0 || endIndex < this.filteredRows.length) {\n\t\t\tconst scrollInfo = theme.fg(\"muted\", ` (${this.selectedIndex + 1}/${this.filteredRows.length})`);\n\t\t\tthis.listContainer.addChild(new Text(scrollInfo, 0, 0));\n\t\t}\n\n\t\t// Show error message or \"no results\" if empty\n\t\tif (this.errorMessage) {\n\t\t\t// Show error in red\n\t\t\tconst errorLines = this.errorMessage.split(\"\\n\");\n\t\t\tfor (const line of errorLines) {\n\t\t\t\tthis.listContainer.addChild(new Text(theme.fg(\"error\", line), 0, 0));\n\t\t\t}\n\t\t} else if (this.filteredRows.length === 0) {\n\t\t\tconst empty = this.step.kind === \"providers\" ? \" No matching providers\" : \" No matching models\";\n\t\t\tthis.listContainer.addChild(new Text(theme.fg(\"muted\", empty), 0, 0));\n\t\t} else {\n\t\t\tconst selected = this.filteredRows[this.selectedIndex];\n\t\t\tif (selected?.kind === \"model\") {\n\t\t\t\tconst cost = selected.item.model.cost;\n\t\t\t\tthis.listContainer.addChild({\n\t\t\t\t\trender: (width: number) => renderPricePanel(cost, width),\n\t\t\t\t\tinvalidate: () => {},\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t\tif (this.refreshStatusMessage) {\n\t\t\tthis.listContainer.addChild(new Spacer(1));\n\t\t\tthis.listContainer.addChild(\n\t\t\t\tnew Text(theme.fg(this.refreshStatusSuccess ? \"success\" : \"muted\", ` ${this.refreshStatusMessage}`), 0, 0),\n\t\t\t);\n\t\t}\n\t}\n\n\thandleInput(keyData: string): void {\n\t\tconst kb = getKeybindings();\n\t\tif (kb.matches(keyData, \"tui.input.tab\")) {\n\t\t\tif (this.scopedModelItems.length > 0) {\n\t\t\t\tthis.setScope(this.scope === \"all\" ? \"scoped\" : \"all\");\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\t// Left/right dial the highlighted model's effort, but only once they are\n\t\t// free to: while a query is being typed they still drive the search cursor.\n\t\tif (\n\t\t\t(this.searchInput.getValue() === \"\" || this.navigatedIntoList) &&\n\t\t\t(kb.matches(keyData, \"tui.editor.cursorLeft\") || kb.matches(keyData, \"tui.editor.cursorRight\"))\n\t\t) {\n\t\t\tconst row = this.filteredRows[this.selectedIndex];\n\t\t\tconst direction = kb.matches(keyData, \"tui.editor.cursorLeft\") ? -1 : 1;\n\t\t\tif (row?.kind === \"model\" && this.adjustEffort(row.item, direction)) {\n\t\t\t\tthis.updateList();\n\t\t\t\tthis.tui.requestRender();\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\t// Up arrow - wrap to bottom when at top\n\t\tif (kb.matches(keyData, \"tui.select.up\")) {\n\t\t\tif (this.filteredRows.length === 0) return;\n\t\t\tthis.navigatedIntoList = true;\n\t\t\tthis.selectedIndex = this.selectedIndex === 0 ? this.filteredRows.length - 1 : this.selectedIndex - 1;\n\t\t\tthis.updateList();\n\t\t}\n\t\t// Down arrow - wrap to top when at bottom\n\t\telse if (kb.matches(keyData, \"tui.select.down\")) {\n\t\t\tif (this.filteredRows.length === 0) return;\n\t\t\tthis.navigatedIntoList = true;\n\t\t\tthis.selectedIndex = this.selectedIndex === this.filteredRows.length - 1 ? 0 : this.selectedIndex + 1;\n\t\t\tthis.updateList();\n\t\t}\n\t\t// Enter\n\t\telse if (kb.matches(keyData, \"tui.select.confirm\")) {\n\t\t\tthis.confirmSelection();\n\t\t}\n\t\t// Escape or Ctrl+C\n\t\telse if (kb.matches(keyData, \"tui.select.cancel\")) {\n\t\t\t// Escape steps back through the two levels; Ctrl+C always closes.\n\t\t\tif (matchesKey(keyData, Key.escape) && this.canGoBack()) {\n\t\t\t\tthis.setStep({ kind: \"providers\" });\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tthis.dispose();\n\t\t\tthis.onCancelCallback();\n\t\t}\n\t\t// Ctrl+S — select and save as default\n\t\telse if (matchesKey(keyData, \"ctrl+s\") && this.onSelectAsDefaultCallback) {\n\t\t\tconst row = this.filteredRows[this.selectedIndex];\n\t\t\tif (row?.kind === \"model\") {\n\t\t\t\tthis.dispose();\n\t\t\t\tthis.onSelectAsDefaultCallback(row.item.model, this.getChosenEffort(row.item));\n\t\t\t}\n\t\t}\n\t\t// Pass everything else to search input\n\t\telse {\n\t\t\tconst previousQuery = this.searchInput.getValue();\n\t\t\tthis.searchInput.handleInput(keyData);\n\t\t\tif (previousQuery !== this.searchInput.getValue()) {\n\t\t\t\tthis.navigatedIntoList = false;\n\t\t\t}\n\t\t\tthis.filterRows(this.searchInput.getValue());\n\t\t}\n\t}\n\n\tprivate confirmSelection(): void {\n\t\tconst row = this.filteredRows[this.selectedIndex];\n\t\tif (!row) return;\n\t\tif (row.kind === \"provider\") {\n\t\t\tthis.setStep({ kind: \"models\", provider: row.provider });\n\t\t\treturn;\n\t\t}\n\t\tthis.dispose();\n\t\tthis.onSelectCallback(row.item.model, this.getChosenEffort(row.item));\n\t}\n\n\tgetSearchInput(): Input {\n\t\treturn this.searchInput;\n\t}\n}\n"]}
1
+ {"version":3,"file":"model-selector.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/model-selector.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n\tclampThinkingLevel,\n\tgetSupportedThinkingLevels,\n\ttype Model,\n\ttype ModelThinkingLevel,\n\tmodelsAreEqual,\n} from \"@earendil-works/pi-ai\";\nimport {\n\ttype Component,\n\tContainer,\n\ttype Focusable,\n\tfuzzyFilter,\n\tgetKeybindings,\n\ttype Input,\n\tKey,\n\tmatchesKey,\n\tSpacer,\n\tText,\n\ttype TUI,\n} from \"@earendil-works/pi-tui\";\nimport type { ModelRuntime } from \"../../../core/model-runtime.ts\";\nimport { refreshModelCatalogs } from \"../model-catalog-refresh.ts\";\nimport { getModelSelectorSearchText } from \"../model-search.ts\";\nimport { paintSelectedRow, paintSelectedRowToWidth, theme } from \"../theme/theme.ts\";\nimport { DynamicBorder } from \"./dynamic-border.ts\";\nimport { type HintKey, hintRow } from \"./keybinding-hints.ts\";\nimport {\n\tcomposeModelRow,\n\ttype EffortLayout,\n\ttype EffortLayoutRow,\n\tgetEffortLayout,\n\tNO_EFFORT_CLUSTER,\n\trenderPricePanel,\n\ttype TrailingSegment,\n} from \"./model-row.ts\";\nimport { PromptInput } from \"./prompt-input.ts\";\n\ninterface ModelItem {\n\tprovider: string;\n\tid: string;\n\tmodel: Model<any>;\n}\n\ninterface ScopedModelItem {\n\tmodel: Model<any>;\n\tthinkingLevel?: string;\n}\n\ninterface DefaultModelReference {\n\tprovider: string;\n\tid: string;\n}\n\ntype ModelScope = \"all\" | \"scoped\";\n\n/**\n * Which of the two steps the selector is on. `provider: null` lists every provider's models at once,\n * which is how a `/model <search>` invocation and a single-provider setup both open.\n */\ntype Step = { kind: \"providers\" } | { kind: \"models\"; provider: string | null };\n\ntype Row =\n\t| { kind: \"provider\"; provider: string; count: number; hasCurrent: boolean }\n\t| { kind: \"model\"; item: ModelItem };\n\nconst MAX_VISIBLE_ROWS = 10;\n\n/**\n * Component that renders a two-step model selector: provider first, then that provider's models.\n */\nexport class ModelSelectorComponent extends Container implements Focusable {\n\tprivate searchInput: Input;\n\n\t// Focusable implementation - propagate to searchInput for IME cursor positioning\n\tprivate _focused = false;\n\tget focused(): boolean {\n\t\treturn this._focused;\n\t}\n\tset focused(value: boolean) {\n\t\tthis._focused = value;\n\t\tthis.searchInput.focused = value;\n\t}\n\tprivate headerContainer: Container;\n\tprivate listContainer: Container;\n\tprivate footerContainer: Container;\n\tprivate allModels: ModelItem[] = [];\n\tprivate scopedModelItems: ModelItem[] = [];\n\tprivate activeModels: ModelItem[] = [];\n\tprivate rows: Row[] = [];\n\tprivate filteredRows: Row[] = [];\n\tprivate selectedIndex: number = 0;\n\tprivate currentModel?: Model<any>;\n\tprivate modelRuntime: ModelRuntime;\n\tprivate onSelectCallback: (model: Model<any>, thinkingLevel?: ModelThinkingLevel) => void;\n\tprivate onSelectAsDefaultCallback?: (model: Model<any>, thinkingLevel?: ModelThinkingLevel) => void;\n\tprivate onCancelCallback: () => void;\n\tprivate errorMessage?: string;\n\tprivate refreshStatusMessage = \"Refreshing model catalogs…\";\n\tprivate refreshStatusSuccess = false;\n\tprivate tui: TUI;\n\tprivate scopedModels: ReadonlyArray<ScopedModelItem>;\n\tprivate defaultModel?: DefaultModelReference;\n\tprivate scope: ModelScope = \"all\";\n\tprivate step: Step = { kind: \"providers\" };\n\tprivate readonly refreshAbortController = new AbortController();\n\tprivate refreshTimeout?: ReturnType<typeof setTimeout>;\n\tprivate closed = false;\n\t/** Effort dialled per model in this session of the picker, keyed by `provider/id`. */\n\tprivate readonly effortLevels = new Map<string, ModelThinkingLevel>();\n\t/** Models whose effort the user actually moved. Only those override the session level. */\n\tprivate readonly editedEffortModels = new Set<string>();\n\tprivate readonly initialThinkingLevel?: ModelThinkingLevel;\n\t/**\n\t * True once up or down has moved the selection into the list. Until then\n\t * left and right belong to the search field's cursor, which is where they\n\t * are needed while a query is still being typed.\n\t */\n\tprivate navigatedIntoList = false;\n\n\tconstructor(\n\t\ttui: TUI,\n\t\tcurrentModel: Model<any> | undefined,\n\t\tmodelRuntime: ModelRuntime,\n\t\tscopedModels: ReadonlyArray<ScopedModelItem>,\n\t\tonSelect: (model: Model<any>, thinkingLevel?: ModelThinkingLevel) => void,\n\t\tonCancel: () => void,\n\t\tinitialSearchInput?: string,\n\t\tonSelectAsDefault?: (model: Model<any>, thinkingLevel?: ModelThinkingLevel) => void,\n\t\tdefaultModel?: DefaultModelReference,\n\t\toptions: { thinkingLevel?: ModelThinkingLevel } = {},\n\t) {\n\t\tsuper();\n\n\t\tthis.tui = tui;\n\t\tthis.initialThinkingLevel = options.thinkingLevel;\n\t\tthis.currentModel = currentModel;\n\t\tthis.modelRuntime = modelRuntime;\n\t\tthis.scopedModels = scopedModels;\n\t\tthis.defaultModel = defaultModel;\n\t\tthis.scope = scopedModels.length > 0 ? \"scoped\" : \"all\";\n\t\tthis.onSelectCallback = onSelect;\n\t\tthis.onSelectAsDefaultCallback = onSelectAsDefault;\n\t\tthis.onCancelCallback = onCancel;\n\n\t\t// Add top border\n\t\tthis.addChild(new DynamicBorder());\n\t\tthis.addChild(new Spacer(1));\n\n\t\tthis.headerContainer = new Container();\n\t\tthis.addChild(this.headerContainer);\n\t\tthis.addChild(new Spacer(1));\n\n\t\t// Create search input\n\t\tthis.searchInput = new PromptInput();\n\t\tif (initialSearchInput) {\n\t\t\tthis.searchInput.setValue(initialSearchInput);\n\t\t}\n\t\tthis.searchInput.onSubmit = () => this.confirmSelection();\n\t\tthis.addChild(this.searchInput);\n\n\t\tthis.addChild(new Spacer(1));\n\n\t\t// Create list container\n\t\tthis.listContainer = new Container();\n\t\tthis.addChild(this.listContainer);\n\n\t\tthis.addChild(new Spacer(1));\n\n\t\t// Keybindings live under the list, where the eye already is once it has\n\t\t// finished reading. Above the list they were the first thing read and the\n\t\t// least useful.\n\t\tthis.footerContainer = new Container();\n\t\tthis.addChild(this.footerContainer);\n\n\t\t// Add bottom border\n\t\tthis.addChild(new DynamicBorder());\n\n\t\t// Render the current snapshot immediately, then refresh in the background.\n\t\tthis.loadModelsFromSnapshot();\n\t\tthis.step = this.initialStep(initialSearchInput);\n\t\tthis.rebuildRows();\n\t\tthis.selectedIndex = this.defaultSelectedIndex();\n\t\tthis.renderHeader();\n\t\tthis.filterRows(this.searchInput.getValue());\n\t\tthis.tui.requestRender();\n\t\tvoid this.refreshModels();\n\t}\n\n\tprivate initialStep(initialSearchInput: string | undefined): Step {\n\t\tconst providers = this.activeProviders();\n\t\tif (initialSearchInput) return { kind: \"models\", provider: null };\n\t\tif (providers.length <= 1) return { kind: \"models\", provider: providers[0] ?? null };\n\t\treturn { kind: \"providers\" };\n\t}\n\n\tprivate activeProviders(): string[] {\n\t\treturn [...new Set(this.activeModels.map((item) => item.provider))];\n\t}\n\n\tprivate loadModelsFromSnapshot(): void {\n\t\tconst models = this.modelRuntime.getAvailableSnapshot().map((model: Model<any>) => ({\n\t\t\tprovider: model.provider,\n\t\t\tid: model.id,\n\t\t\tmodel,\n\t\t}));\n\t\tthis.allModels = this.sortModels(models);\n\t\tthis.scopedModels = this.scopedModels.map((scoped) => {\n\t\t\tconst refreshed = this.modelRuntime.getModel(scoped.model.provider, scoped.model.id);\n\t\t\treturn refreshed ? { ...scoped, model: refreshed } : scoped;\n\t\t});\n\t\tthis.scopedModelItems = this.scopedModels.map((scoped) => ({\n\t\t\tprovider: scoped.model.provider,\n\t\t\tid: scoped.model.id,\n\t\t\tmodel: scoped.model,\n\t\t}));\n\t\tthis.activeModels = this.scope === \"scoped\" ? this.scopedModelItems : this.allModels;\n\t}\n\n\tprivate async refreshModels(): Promise<void> {\n\t\tconst timeoutMs = 15_000;\n\t\tlet timedOut = false;\n\t\tthis.refreshTimeout = setTimeout(() => {\n\t\t\ttimedOut = true;\n\t\t\tthis.refreshAbortController.abort();\n\t\t}, timeoutMs);\n\t\ttry {\n\t\t\tconst result = await refreshModelCatalogs(this.modelRuntime, this.refreshAbortController.signal);\n\t\t\tif (this.closed) return;\n\t\t\tthis.refreshStatusMessage = \"\";\n\t\t\tif (result.aborted && timedOut) {\n\t\t\t\tthis.errorMessage = \"Model refresh timed out; showing cached models.\";\n\t\t\t} else if (result.errors.size === 1) {\n\t\t\t\tthis.errorMessage = `Could not refresh ${result.errors.keys().next().value}; showing cached models.`;\n\t\t\t} else if (result.errors.size > 1) {\n\t\t\t\tthis.errorMessage = `Could not refresh ${result.errors.size} model catalogs (${[...result.errors.keys()].join(\", \")}); showing cached models.`;\n\t\t\t} else {\n\t\t\t\tthis.errorMessage = this.modelRuntime.getError();\n\t\t\t\tif (!this.errorMessage) {\n\t\t\t\t\tthis.refreshStatusMessage = \"Model catalogs refreshed.\";\n\t\t\t\t\tthis.refreshStatusSuccess = true;\n\t\t\t\t}\n\t\t\t}\n\t\t\tthis.loadModelsFromSnapshot();\n\t\t\tthis.rebuildRows();\n\t\t\tthis.renderHeader();\n\t\t\tthis.filterRows(this.searchInput.getValue());\n\t\t\tthis.tui.requestRender();\n\t\t} catch (error) {\n\t\t\tif (this.closed) return;\n\t\t\tthis.refreshStatusMessage = \"\";\n\t\t\tthis.errorMessage = timedOut\n\t\t\t\t? \"Model refresh timed out; showing cached models.\"\n\t\t\t\t: `Could not refresh model catalogs: ${error instanceof Error ? error.message : String(error)}`;\n\t\t\tthis.updateList();\n\t\t\tthis.tui.requestRender();\n\t\t} finally {\n\t\t\tif (this.refreshTimeout) clearTimeout(this.refreshTimeout);\n\t\t}\n\t}\n\n\tdispose(): void {\n\t\tif (this.closed) return;\n\t\tthis.closed = true;\n\t\tif (this.refreshTimeout) clearTimeout(this.refreshTimeout);\n\t\tthis.refreshAbortController.abort();\n\t}\n\n\tprivate sortModels(models: ModelItem[]): ModelItem[] {\n\t\tconst sorted = [...models];\n\t\t// Sort: current model first, default model second, then by provider.\n\t\tsorted.sort((a, b) => {\n\t\t\tconst aIsCurrent = modelsAreEqual(this.currentModel, a.model);\n\t\t\tconst bIsCurrent = modelsAreEqual(this.currentModel, b.model);\n\t\t\tif (aIsCurrent && !bIsCurrent) return -1;\n\t\t\tif (!aIsCurrent && bIsCurrent) return 1;\n\t\t\tconst aIsDefault = this.isDefaultModel(a.model);\n\t\t\tconst bIsDefault = this.isDefaultModel(b.model);\n\t\t\tif (aIsDefault && !bIsDefault) return -1;\n\t\t\tif (!aIsDefault && bIsDefault) return 1;\n\t\t\treturn a.provider.localeCompare(b.provider);\n\t\t});\n\t\treturn sorted;\n\t}\n\n\tprivate rebuildRows(): void {\n\t\tif (this.step.kind === \"providers\") {\n\t\t\tconst byProvider = new Map<string, ModelItem[]>();\n\t\t\tfor (const item of this.activeModels) {\n\t\t\t\tconst existing = byProvider.get(item.provider);\n\t\t\t\tif (existing) existing.push(item);\n\t\t\t\telse byProvider.set(item.provider, [item]);\n\t\t\t}\n\t\t\tconst providerRows: Row[] = [...byProvider].map(([provider, items]) => ({\n\t\t\t\tkind: \"provider\",\n\t\t\t\tprovider,\n\t\t\t\tcount: items.length,\n\t\t\t\thasCurrent: items.some((item) => modelsAreEqual(this.currentModel, item.model)),\n\t\t\t}));\n\t\t\tproviderRows.sort((a, b) => {\n\t\t\t\tif (a.kind !== \"provider\" || b.kind !== \"provider\") return 0;\n\t\t\t\tif (a.hasCurrent !== b.hasCurrent) return a.hasCurrent ? -1 : 1;\n\t\t\t\treturn a.provider.localeCompare(b.provider);\n\t\t\t});\n\t\t\tthis.rows = providerRows;\n\t\t\treturn;\n\t\t}\n\t\tconst provider = this.step.provider;\n\t\tconst models = provider === null ? this.activeModels : this.activeModels.filter((m) => m.provider === provider);\n\t\tthis.rows = models.map((item) => ({ kind: \"model\", item }));\n\t}\n\n\tprivate defaultSelectedIndex(): number {\n\t\tconst index = this.rows.findIndex((row) =>\n\t\t\trow.kind === \"provider\" ? row.hasCurrent : modelsAreEqual(this.currentModel, row.item.model),\n\t\t);\n\t\treturn index >= 0 ? index : 0;\n\t}\n\n\tprivate setStep(step: Step): void {\n\t\tthis.step = step;\n\t\tthis.navigatedIntoList = false;\n\t\tthis.searchInput.setValue(\"\");\n\t\tthis.rebuildRows();\n\t\tthis.selectedIndex = this.defaultSelectedIndex();\n\t\tthis.renderHeader();\n\t\tthis.filterRows(\"\");\n\t\tthis.tui.requestRender();\n\t}\n\n\tprivate isDrilledIn(): boolean {\n\t\treturn this.step.kind === \"models\" && this.step.provider !== null;\n\t}\n\n\t/** Escape returns to the provider list whenever there is a provider list worth returning to. */\n\tprivate canGoBack(): boolean {\n\t\treturn this.step.kind === \"models\" && this.activeProviders().length > 1;\n\t}\n\n\tprivate renderHeader(): void {\n\t\tthis.headerContainer.clear();\n\n\t\tif (this.step.kind === \"providers\") {\n\t\t\tthis.headerContainer.addChild(new Text(theme.bold(theme.fg(\"accent\", \"Select a provider\")), 0, 0));\n\t\t} else {\n\t\t\tconst label = this.step.provider ?? \"All providers\";\n\t\t\tconst crumb = `${theme.bold(theme.fg(\"accent\", label))}${theme.fg(\"muted\", \" › select a model\")}`;\n\t\t\tthis.headerContainer.addChild(new Text(crumb, 0, 0));\n\t\t}\n\n\t\tif (this.scopedModelItems.length > 0) {\n\t\t\tthis.headerContainer.addChild(new Text(this.getScopeText(), 0, 0));\n\t\t} else if (!this.isDrilledIn()) {\n\t\t\tconst hintText = \"Only showing models from configured providers. Use /login to add providers.\";\n\t\t\tthis.headerContainer.addChild(new Text(theme.fg(\"warning\", hintText), 0, 0));\n\t\t}\n\n\t\tthis.renderFooter();\n\t}\n\n\tprivate renderFooter(): void {\n\t\tthis.footerContainer.clear();\n\t\tthis.footerContainer.addChild(new Text(this.getHintText(), 0, 0));\n\t}\n\n\tprivate getScopeText(): string {\n\t\tconst allText = this.scope === \"all\" ? theme.fg(\"accent\", \"all\") : theme.fg(\"muted\", \"all\");\n\t\tconst scopedText = this.scope === \"scoped\" ? theme.fg(\"accent\", \"scoped\") : theme.fg(\"muted\", \"scoped\");\n\t\treturn `${theme.fg(\"muted\", \"Scope: \")}${allText}${theme.fg(\"muted\", \" | \")}${scopedText}`;\n\t}\n\n\tprivate getHintText(): string {\n\t\tconst hints: Array<readonly [HintKey, string]> = [\n\t\t\t[[\"tui.select.up\", \"tui.select.down\"], \"move\"],\n\t\t\t[\"tui.select.confirm\", \"select\"],\n\t\t];\n\t\tif (this.hasAdjustableEffort()) hints.push([[\"tui.editor.cursorLeft\", \"tui.editor.cursorRight\"], \"effort\"]);\n\t\tif (this.scopedModelItems.length > 0) hints.push([\"tui.input.tab\", \"scope\"]);\n\t\tif (this.onSelectAsDefaultCallback) hints.push([\"app.models.save\", \"set as default\"]);\n\t\tif (this.canGoBack()) hints.push([{ literal: \"escape\" }, \"providers\"], [{ literal: \"ctrl+c\" }, \"close\"]);\n\t\telse hints.push([\"tui.select.cancel\", \"close\"]);\n\t\treturn hintRow(hints);\n\t}\n\n\t/** Levels worth dialling. A model with nothing but \"off\" has no cluster to show. */\n\tprivate getSelectableLevels(item: ModelItem): ModelThinkingLevel[] {\n\t\tconst levels = getSupportedThinkingLevels(item.model);\n\t\tif (levels.length === 1 && levels[0] === \"off\") return [];\n\t\treturn levels;\n\t}\n\n\tprivate getEffort(item: ModelItem): ModelThinkingLevel | undefined {\n\t\tconst levels = this.getSelectableLevels(item);\n\t\tif (levels.length === 0) return undefined;\n\t\tconst key = `${item.provider}/${item.id}`;\n\t\tconst stored = this.effortLevels.get(key);\n\t\tif (stored !== undefined && levels.includes(stored)) return stored;\n\t\tconst requested = this.initialThinkingLevel ?? \"off\";\n\t\tconst clamped = levels.includes(requested) ? requested : clampThinkingLevel(item.model, requested);\n\t\tconst resolved = levels.includes(clamped) ? clamped : levels[0]!;\n\t\tthis.effortLevels.set(key, resolved);\n\t\treturn resolved;\n\t}\n\n\tprivate adjustEffort(item: ModelItem, direction: number): boolean {\n\t\tconst levels = this.getSelectableLevels(item);\n\t\tif (levels.length === 0) return false;\n\t\tconst key = `${item.provider}/${item.id}`;\n\t\tconst current = this.getEffort(item) ?? levels[0]!;\n\t\tconst next = levels[(levels.indexOf(current) + direction + levels.length) % levels.length]!;\n\t\tthis.effortLevels.set(key, next);\n\t\tthis.editedEffortModels.add(key);\n\t\treturn true;\n\t}\n\n\t/** The effort to hand back on select, or undefined when the user left it alone. */\n\tprivate getChosenEffort(item: ModelItem): ModelThinkingLevel | undefined {\n\t\treturn this.editedEffortModels.has(`${item.provider}/${item.id}`) ? this.getEffort(item) : undefined;\n\t}\n\n\tprivate hasAdjustableEffort(): boolean {\n\t\treturn this.filteredRows.some((row) => row.kind === \"model\" && this.getSelectableLevels(row.item).length > 0);\n\t}\n\n\t/**\n\t * Context that trails a model row, right-aligned, in reading order.\n\t *\n\t * Each segment carries what it costs to lose, because a narrow row drops by\n\t * that and not by position. `current` outranks the id: the row already shows\n\t * the model's name, so the id is largely recoverable from it, while nothing\n\t * else on screen says which model the session is on. The provider badge goes\n\t * first, being the least you lose.\n\t */\n\tprivate getTrailingSegments(item: ModelItem): TrailingSegment[] {\n\t\tconst segments: TrailingSegment[] = [];\n\t\tif (this.step.kind === \"models\" && this.step.provider === null) {\n\t\t\tsegments.push({ text: theme.fg(\"muted\", item.provider), priority: 0 });\n\t\t}\n\t\tsegments.push({ text: theme.fg(\"muted\", item.id), priority: 2 });\n\t\tif (this.isDefaultModel(item.model)) segments.push({ text: theme.fg(\"muted\", \"default\"), priority: 1 });\n\t\tif (modelsAreEqual(this.currentModel, item.model)) {\n\t\t\tsegments.push({ text: theme.fg(\"success\", \"current\"), priority: 3 });\n\t\t}\n\t\treturn segments;\n\t}\n\n\tprivate isDefaultModel(model: Model<any>): boolean {\n\t\treturn this.defaultModel?.provider === model.provider && this.defaultModel.id === model.id;\n\t}\n\n\tprivate isDefaultSearch(query: string): boolean {\n\t\tconst normalized = query.trim().toLowerCase();\n\t\treturn normalized.length > 0 && \"default\".startsWith(normalized);\n\t}\n\n\tprivate setScope(scope: ModelScope): void {\n\t\tif (this.scope === scope) return;\n\t\tthis.scope = scope;\n\t\tthis.activeModels = this.scope === \"scoped\" ? this.scopedModelItems : this.allModels;\n\t\tthis.rebuildRows();\n\t\tthis.selectedIndex = this.defaultSelectedIndex();\n\t\tthis.renderHeader();\n\t\tthis.filterRows(this.searchInput.getValue());\n\t}\n\n\tprivate rowSearchText(row: Row): string {\n\t\tif (row.kind === \"provider\") return row.provider;\n\t\tconst defaultText = this.isDefaultModel(row.item.model) ? \" default\" : \"\";\n\t\treturn `${getModelSelectorSearchText({ id: row.item.id, provider: row.item.provider, name: row.item.model.name })}${defaultText}`;\n\t}\n\n\tprivate isDefaultRow(row: Row): boolean {\n\t\treturn row.kind === \"model\" && this.isDefaultModel(row.item.model);\n\t}\n\n\tprivate filterRows(query: string): void {\n\t\tif (!query) {\n\t\t\tthis.filteredRows = this.rows;\n\t\t} else {\n\t\t\tconst filtered = fuzzyFilter(this.rows, query, (row) => this.rowSearchText(row));\n\t\t\tif (this.isDefaultSearch(query)) {\n\t\t\t\t// Typing \"default\" (or a prefix of it) surfaces the default model even\n\t\t\t\t// when its name doesn't otherwise fuzzy-match the query.\n\t\t\t\tconst defaultRows = this.rows.filter((row) => this.isDefaultRow(row));\n\t\t\t\tthis.filteredRows = [...defaultRows, ...filtered.filter((row) => !this.isDefaultRow(row))];\n\t\t\t} else {\n\t\t\t\tthis.filteredRows = filtered;\n\t\t\t}\n\t\t}\n\t\t// When filtering by a query, move the selector to the top row so the best\n\t\t// match is highlighted. When the query is cleared, keep the current position\n\t\t// clamped to the (restored) list length.\n\t\tthis.selectedIndex = query ? 0 : Math.min(this.selectedIndex, Math.max(0, this.filteredRows.length - 1));\n\t\t// The effort hint depends on what survived the filter, so the footer is\n\t\t// rebuilt here rather than with the header, which runs before filtering.\n\t\tthis.renderFooter();\n\t\tthis.updateList();\n\t}\n\n\tprivate renderProviderRow(row: Extract<Row, { kind: \"provider\" }>, isSelected: boolean): string {\n\t\tconst prefix = isSelected ? \"→ \" : \" \";\n\t\tconst count = theme.fg(\"muted\", ` (${row.count} model${row.count === 1 ? \"\" : \"s\"})`);\n\t\tconst checkmark = row.hasCurrent ? theme.fg(\"success\", \" ✓\") : \"\";\n\t\tconst composed = `${prefix}${row.provider}${count}${checkmark}`;\n\t\treturn isSelected ? paintSelectedRow(composed) : composed;\n\t}\n\n\t/**\n\t * The visible page of rows, rendered together because the effort cluster is\n\t * placed once for the whole page rather than per row.\n\t */\n\tprivate createRowsComponent(startIndex: number, endIndex: number): Component {\n\t\tconst rows = this.filteredRows.slice(startIndex, endIndex);\n\t\treturn {\n\t\t\trender: (width: number): string[] => {\n\t\t\t\tconst layout = this.pageEffortLayout(rows, width);\n\t\t\t\treturn rows.map((row, offset) => {\n\t\t\t\t\tconst isSelected = startIndex + offset === this.selectedIndex;\n\t\t\t\t\tif (row.kind === \"provider\") return this.renderProviderRow(row, isSelected);\n\t\t\t\t\tconst composed = composeModelRow({\n\t\t\t\t\t\tname: row.item.model.name,\n\t\t\t\t\t\tlevels: this.getSelectableLevels(row.item),\n\t\t\t\t\t\teffort: this.getEffort(row.item),\n\t\t\t\t\t\ttrailingSegments: this.getTrailingSegments(row.item),\n\t\t\t\t\t\tselected: isSelected,\n\t\t\t\t\t\tlayout,\n\t\t\t\t\t\twidth,\n\t\t\t\t\t});\n\t\t\t\t\treturn isSelected ? paintSelectedRowToWidth(composed, width) : composed;\n\t\t\t\t});\n\t\t\t},\n\t\t\tinvalidate: () => {},\n\t\t};\n\t}\n\n\tprivate pageEffortLayout(rows: ReadonlyArray<Row>, width: number): EffortLayout {\n\t\tconst layoutRows: EffortLayoutRow[] = [];\n\t\tfor (const row of rows) {\n\t\t\tif (row.kind !== \"model\") continue;\n\t\t\tlayoutRows.push({\n\t\t\t\tname: row.item.model.name,\n\t\t\t\tlevels: this.getSelectableLevels(row.item),\n\t\t\t\ttrailingSegments: this.getTrailingSegments(row.item),\n\t\t\t});\n\t\t}\n\t\treturn layoutRows.length > 0 ? getEffortLayout(layoutRows, width) : NO_EFFORT_CLUSTER;\n\t}\n\n\tprivate updateList(): void {\n\t\tthis.listContainer.clear();\n\n\t\tconst startIndex = Math.max(\n\t\t\t0,\n\t\t\tMath.min(this.selectedIndex - Math.floor(MAX_VISIBLE_ROWS / 2), this.filteredRows.length - MAX_VISIBLE_ROWS),\n\t\t);\n\t\tconst endIndex = Math.min(startIndex + MAX_VISIBLE_ROWS, this.filteredRows.length);\n\n\t\tif (endIndex > startIndex) {\n\t\t\tthis.listContainer.addChild(this.createRowsComponent(startIndex, endIndex));\n\t\t}\n\n\t\t// Add scroll indicator if needed\n\t\tif (startIndex > 0 || endIndex < this.filteredRows.length) {\n\t\t\tconst scrollInfo = theme.fg(\"muted\", ` (${this.selectedIndex + 1}/${this.filteredRows.length})`);\n\t\t\tthis.listContainer.addChild(new Text(scrollInfo, 0, 0));\n\t\t}\n\n\t\t// Show error message or \"no results\" if empty\n\t\tif (this.errorMessage) {\n\t\t\t// Show error in red\n\t\t\tconst errorLines = this.errorMessage.split(\"\\n\");\n\t\t\tfor (const line of errorLines) {\n\t\t\t\tthis.listContainer.addChild(new Text(theme.fg(\"error\", line), 0, 0));\n\t\t\t}\n\t\t} else if (this.filteredRows.length === 0) {\n\t\t\tconst empty = this.step.kind === \"providers\" ? \" No matching providers\" : \" No matching models\";\n\t\t\tthis.listContainer.addChild(new Text(theme.fg(\"muted\", empty), 0, 0));\n\t\t} else {\n\t\t\tconst selected = this.filteredRows[this.selectedIndex];\n\t\t\tif (selected?.kind === \"model\") {\n\t\t\t\tconst cost = selected.item.model.cost;\n\t\t\t\tthis.listContainer.addChild({\n\t\t\t\t\trender: (width: number) => renderPricePanel(cost, width),\n\t\t\t\t\tinvalidate: () => {},\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t\tif (this.refreshStatusMessage) {\n\t\t\tthis.listContainer.addChild(new Spacer(1));\n\t\t\tthis.listContainer.addChild(\n\t\t\t\tnew Text(theme.fg(this.refreshStatusSuccess ? \"success\" : \"muted\", ` ${this.refreshStatusMessage}`), 0, 0),\n\t\t\t);\n\t\t}\n\t}\n\n\thandleInput(keyData: string): void {\n\t\tconst kb = getKeybindings();\n\t\tif (kb.matches(keyData, \"tui.input.tab\")) {\n\t\t\tif (this.scopedModelItems.length > 0) {\n\t\t\t\tthis.setScope(this.scope === \"all\" ? \"scoped\" : \"all\");\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\t// Left/right dial the highlighted model's effort, but only once they are\n\t\t// free to: while a query is being typed they still drive the search cursor.\n\t\tif (\n\t\t\t(this.searchInput.getValue() === \"\" || this.navigatedIntoList) &&\n\t\t\t(kb.matches(keyData, \"tui.editor.cursorLeft\") || kb.matches(keyData, \"tui.editor.cursorRight\"))\n\t\t) {\n\t\t\tconst row = this.filteredRows[this.selectedIndex];\n\t\t\tconst direction = kb.matches(keyData, \"tui.editor.cursorLeft\") ? -1 : 1;\n\t\t\tif (row?.kind === \"model\" && this.adjustEffort(row.item, direction)) {\n\t\t\t\tthis.updateList();\n\t\t\t\tthis.tui.requestRender();\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\t// Up arrow - wrap to bottom when at top\n\t\tif (kb.matches(keyData, \"tui.select.up\")) {\n\t\t\tif (this.filteredRows.length === 0) return;\n\t\t\tthis.navigatedIntoList = true;\n\t\t\tthis.selectedIndex = this.selectedIndex === 0 ? this.filteredRows.length - 1 : this.selectedIndex - 1;\n\t\t\tthis.updateList();\n\t\t}\n\t\t// Down arrow - wrap to top when at bottom\n\t\telse if (kb.matches(keyData, \"tui.select.down\")) {\n\t\t\tif (this.filteredRows.length === 0) return;\n\t\t\tthis.navigatedIntoList = true;\n\t\t\tthis.selectedIndex = this.selectedIndex === this.filteredRows.length - 1 ? 0 : this.selectedIndex + 1;\n\t\t\tthis.updateList();\n\t\t}\n\t\t// Enter\n\t\telse if (kb.matches(keyData, \"tui.select.confirm\")) {\n\t\t\tthis.confirmSelection();\n\t\t}\n\t\t// Escape or Ctrl+C\n\t\telse if (kb.matches(keyData, \"tui.select.cancel\")) {\n\t\t\t// Escape steps back through the two levels; Ctrl+C always closes.\n\t\t\tif (matchesKey(keyData, Key.escape) && this.canGoBack()) {\n\t\t\t\tthis.setStep({ kind: \"providers\" });\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tthis.dispose();\n\t\t\tthis.onCancelCallback();\n\t\t}\n\t\t// Select and save as default\n\t\telse if (kb.matches(keyData, \"app.models.save\") && this.onSelectAsDefaultCallback) {\n\t\t\tconst row = this.filteredRows[this.selectedIndex];\n\t\t\tif (row?.kind === \"model\") {\n\t\t\t\tthis.dispose();\n\t\t\t\tthis.onSelectAsDefaultCallback(row.item.model, this.getChosenEffort(row.item));\n\t\t\t}\n\t\t}\n\t\t// Pass everything else to search input\n\t\telse {\n\t\t\tconst previousQuery = this.searchInput.getValue();\n\t\t\tthis.searchInput.handleInput(keyData);\n\t\t\tif (previousQuery !== this.searchInput.getValue()) {\n\t\t\t\tthis.navigatedIntoList = false;\n\t\t\t}\n\t\t\tthis.filterRows(this.searchInput.getValue());\n\t\t}\n\t}\n\n\tprivate confirmSelection(): void {\n\t\tconst row = this.filteredRows[this.selectedIndex];\n\t\tif (!row) return;\n\t\tif (row.kind === \"provider\") {\n\t\t\tthis.setStep({ kind: \"models\", provider: row.provider });\n\t\t\treturn;\n\t\t}\n\t\tthis.dispose();\n\t\tthis.onSelectCallback(row.item.model, this.getChosenEffort(row.item));\n\t}\n\n\tgetSearchInput(): Input {\n\t\treturn this.searchInput;\n\t}\n}\n"]}
@@ -300,7 +300,7 @@ export class ModelSelectorComponent extends Container {
300
300
  if (this.scopedModelItems.length > 0)
301
301
  hints.push(["tui.input.tab", "scope"]);
302
302
  if (this.onSelectAsDefaultCallback)
303
- hints.push([{ literal: "ctrl+s" }, "set as default"]);
303
+ hints.push(["app.models.save", "set as default"]);
304
304
  if (this.canGoBack())
305
305
  hints.push([{ literal: "escape" }, "providers"], [{ literal: "ctrl+c" }, "close"]);
306
306
  else
@@ -556,8 +556,8 @@ export class ModelSelectorComponent extends Container {
556
556
  this.dispose();
557
557
  this.onCancelCallback();
558
558
  }
559
- // Ctrl+S — select and save as default
560
- else if (matchesKey(keyData, "ctrl+s") && this.onSelectAsDefaultCallback) {
559
+ // Select and save as default
560
+ else if (kb.matches(keyData, "app.models.save") && this.onSelectAsDefaultCallback) {
561
561
  const row = this.filteredRows[this.selectedIndex];
562
562
  if (row?.kind === "model") {
563
563
  this.dispose();
@@ -1 +1 @@
1
- {"version":3,"file":"model-selector.js","sourceRoot":"","sources":["../../../../src/modes/interactive/components/model-selector.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n\tclampThinkingLevel,\n\tgetSupportedThinkingLevels,\n\ttype Model,\n\ttype ModelThinkingLevel,\n\tmodelsAreEqual,\n} from \"@earendil-works/pi-ai\";\nimport {\n\ttype Component,\n\tContainer,\n\ttype Focusable,\n\tfuzzyFilter,\n\tgetKeybindings,\n\ttype Input,\n\tKey,\n\tmatchesKey,\n\tSpacer,\n\tText,\n\ttype TUI,\n} from \"@earendil-works/pi-tui\";\nimport type { ModelRuntime } from \"../../../core/model-runtime.ts\";\nimport { refreshModelCatalogs } from \"../model-catalog-refresh.ts\";\nimport { getModelSelectorSearchText } from \"../model-search.ts\";\nimport { paintSelectedRow, paintSelectedRowToWidth, theme } from \"../theme/theme.ts\";\nimport { DynamicBorder } from \"./dynamic-border.ts\";\nimport { type HintKey, hintRow } from \"./keybinding-hints.ts\";\nimport {\n\tcomposeModelRow,\n\ttype EffortLayout,\n\ttype EffortLayoutRow,\n\tgetEffortLayout,\n\tNO_EFFORT_CLUSTER,\n\trenderPricePanel,\n\ttype TrailingSegment,\n} from \"./model-row.ts\";\nimport { PromptInput } from \"./prompt-input.ts\";\n\ninterface ModelItem {\n\tprovider: string;\n\tid: string;\n\tmodel: Model<any>;\n}\n\ninterface ScopedModelItem {\n\tmodel: Model<any>;\n\tthinkingLevel?: string;\n}\n\ninterface DefaultModelReference {\n\tprovider: string;\n\tid: string;\n}\n\ntype ModelScope = \"all\" | \"scoped\";\n\n/**\n * Which of the two steps the selector is on. `provider: null` lists every provider's models at once,\n * which is how a `/model <search>` invocation and a single-provider setup both open.\n */\ntype Step = { kind: \"providers\" } | { kind: \"models\"; provider: string | null };\n\ntype Row =\n\t| { kind: \"provider\"; provider: string; count: number; hasCurrent: boolean }\n\t| { kind: \"model\"; item: ModelItem };\n\nconst MAX_VISIBLE_ROWS = 10;\n\n/**\n * Component that renders a two-step model selector: provider first, then that provider's models.\n */\nexport class ModelSelectorComponent extends Container implements Focusable {\n\tprivate searchInput: Input;\n\n\t// Focusable implementation - propagate to searchInput for IME cursor positioning\n\tprivate _focused = false;\n\tget focused(): boolean {\n\t\treturn this._focused;\n\t}\n\tset focused(value: boolean) {\n\t\tthis._focused = value;\n\t\tthis.searchInput.focused = value;\n\t}\n\tprivate headerContainer: Container;\n\tprivate listContainer: Container;\n\tprivate footerContainer: Container;\n\tprivate allModels: ModelItem[] = [];\n\tprivate scopedModelItems: ModelItem[] = [];\n\tprivate activeModels: ModelItem[] = [];\n\tprivate rows: Row[] = [];\n\tprivate filteredRows: Row[] = [];\n\tprivate selectedIndex: number = 0;\n\tprivate currentModel?: Model<any>;\n\tprivate modelRuntime: ModelRuntime;\n\tprivate onSelectCallback: (model: Model<any>, thinkingLevel?: ModelThinkingLevel) => void;\n\tprivate onSelectAsDefaultCallback?: (model: Model<any>, thinkingLevel?: ModelThinkingLevel) => void;\n\tprivate onCancelCallback: () => void;\n\tprivate errorMessage?: string;\n\tprivate refreshStatusMessage = \"Refreshing model catalogs…\";\n\tprivate refreshStatusSuccess = false;\n\tprivate tui: TUI;\n\tprivate scopedModels: ReadonlyArray<ScopedModelItem>;\n\tprivate defaultModel?: DefaultModelReference;\n\tprivate scope: ModelScope = \"all\";\n\tprivate step: Step = { kind: \"providers\" };\n\tprivate readonly refreshAbortController = new AbortController();\n\tprivate refreshTimeout?: ReturnType<typeof setTimeout>;\n\tprivate closed = false;\n\t/** Effort dialled per model in this session of the picker, keyed by `provider/id`. */\n\tprivate readonly effortLevels = new Map<string, ModelThinkingLevel>();\n\t/** Models whose effort the user actually moved. Only those override the session level. */\n\tprivate readonly editedEffortModels = new Set<string>();\n\tprivate readonly initialThinkingLevel?: ModelThinkingLevel;\n\t/**\n\t * True once up or down has moved the selection into the list. Until then\n\t * left and right belong to the search field's cursor, which is where they\n\t * are needed while a query is still being typed.\n\t */\n\tprivate navigatedIntoList = false;\n\n\tconstructor(\n\t\ttui: TUI,\n\t\tcurrentModel: Model<any> | undefined,\n\t\tmodelRuntime: ModelRuntime,\n\t\tscopedModels: ReadonlyArray<ScopedModelItem>,\n\t\tonSelect: (model: Model<any>, thinkingLevel?: ModelThinkingLevel) => void,\n\t\tonCancel: () => void,\n\t\tinitialSearchInput?: string,\n\t\tonSelectAsDefault?: (model: Model<any>, thinkingLevel?: ModelThinkingLevel) => void,\n\t\tdefaultModel?: DefaultModelReference,\n\t\toptions: { thinkingLevel?: ModelThinkingLevel } = {},\n\t) {\n\t\tsuper();\n\n\t\tthis.tui = tui;\n\t\tthis.initialThinkingLevel = options.thinkingLevel;\n\t\tthis.currentModel = currentModel;\n\t\tthis.modelRuntime = modelRuntime;\n\t\tthis.scopedModels = scopedModels;\n\t\tthis.defaultModel = defaultModel;\n\t\tthis.scope = scopedModels.length > 0 ? \"scoped\" : \"all\";\n\t\tthis.onSelectCallback = onSelect;\n\t\tthis.onSelectAsDefaultCallback = onSelectAsDefault;\n\t\tthis.onCancelCallback = onCancel;\n\n\t\t// Add top border\n\t\tthis.addChild(new DynamicBorder());\n\t\tthis.addChild(new Spacer(1));\n\n\t\tthis.headerContainer = new Container();\n\t\tthis.addChild(this.headerContainer);\n\t\tthis.addChild(new Spacer(1));\n\n\t\t// Create search input\n\t\tthis.searchInput = new PromptInput();\n\t\tif (initialSearchInput) {\n\t\t\tthis.searchInput.setValue(initialSearchInput);\n\t\t}\n\t\tthis.searchInput.onSubmit = () => this.confirmSelection();\n\t\tthis.addChild(this.searchInput);\n\n\t\tthis.addChild(new Spacer(1));\n\n\t\t// Create list container\n\t\tthis.listContainer = new Container();\n\t\tthis.addChild(this.listContainer);\n\n\t\tthis.addChild(new Spacer(1));\n\n\t\t// Keybindings live under the list, where the eye already is once it has\n\t\t// finished reading. Above the list they were the first thing read and the\n\t\t// least useful.\n\t\tthis.footerContainer = new Container();\n\t\tthis.addChild(this.footerContainer);\n\n\t\t// Add bottom border\n\t\tthis.addChild(new DynamicBorder());\n\n\t\t// Render the current snapshot immediately, then refresh in the background.\n\t\tthis.loadModelsFromSnapshot();\n\t\tthis.step = this.initialStep(initialSearchInput);\n\t\tthis.rebuildRows();\n\t\tthis.selectedIndex = this.defaultSelectedIndex();\n\t\tthis.renderHeader();\n\t\tthis.filterRows(this.searchInput.getValue());\n\t\tthis.tui.requestRender();\n\t\tvoid this.refreshModels();\n\t}\n\n\tprivate initialStep(initialSearchInput: string | undefined): Step {\n\t\tconst providers = this.activeProviders();\n\t\tif (initialSearchInput) return { kind: \"models\", provider: null };\n\t\tif (providers.length <= 1) return { kind: \"models\", provider: providers[0] ?? null };\n\t\treturn { kind: \"providers\" };\n\t}\n\n\tprivate activeProviders(): string[] {\n\t\treturn [...new Set(this.activeModels.map((item) => item.provider))];\n\t}\n\n\tprivate loadModelsFromSnapshot(): void {\n\t\tconst models = this.modelRuntime.getAvailableSnapshot().map((model: Model<any>) => ({\n\t\t\tprovider: model.provider,\n\t\t\tid: model.id,\n\t\t\tmodel,\n\t\t}));\n\t\tthis.allModels = this.sortModels(models);\n\t\tthis.scopedModels = this.scopedModels.map((scoped) => {\n\t\t\tconst refreshed = this.modelRuntime.getModel(scoped.model.provider, scoped.model.id);\n\t\t\treturn refreshed ? { ...scoped, model: refreshed } : scoped;\n\t\t});\n\t\tthis.scopedModelItems = this.scopedModels.map((scoped) => ({\n\t\t\tprovider: scoped.model.provider,\n\t\t\tid: scoped.model.id,\n\t\t\tmodel: scoped.model,\n\t\t}));\n\t\tthis.activeModels = this.scope === \"scoped\" ? this.scopedModelItems : this.allModels;\n\t}\n\n\tprivate async refreshModels(): Promise<void> {\n\t\tconst timeoutMs = 15_000;\n\t\tlet timedOut = false;\n\t\tthis.refreshTimeout = setTimeout(() => {\n\t\t\ttimedOut = true;\n\t\t\tthis.refreshAbortController.abort();\n\t\t}, timeoutMs);\n\t\ttry {\n\t\t\tconst result = await refreshModelCatalogs(this.modelRuntime, this.refreshAbortController.signal);\n\t\t\tif (this.closed) return;\n\t\t\tthis.refreshStatusMessage = \"\";\n\t\t\tif (result.aborted && timedOut) {\n\t\t\t\tthis.errorMessage = \"Model refresh timed out; showing cached models.\";\n\t\t\t} else if (result.errors.size === 1) {\n\t\t\t\tthis.errorMessage = `Could not refresh ${result.errors.keys().next().value}; showing cached models.`;\n\t\t\t} else if (result.errors.size > 1) {\n\t\t\t\tthis.errorMessage = `Could not refresh ${result.errors.size} model catalogs (${[...result.errors.keys()].join(\", \")}); showing cached models.`;\n\t\t\t} else {\n\t\t\t\tthis.errorMessage = this.modelRuntime.getError();\n\t\t\t\tif (!this.errorMessage) {\n\t\t\t\t\tthis.refreshStatusMessage = \"Model catalogs refreshed.\";\n\t\t\t\t\tthis.refreshStatusSuccess = true;\n\t\t\t\t}\n\t\t\t}\n\t\t\tthis.loadModelsFromSnapshot();\n\t\t\tthis.rebuildRows();\n\t\t\tthis.renderHeader();\n\t\t\tthis.filterRows(this.searchInput.getValue());\n\t\t\tthis.tui.requestRender();\n\t\t} catch (error) {\n\t\t\tif (this.closed) return;\n\t\t\tthis.refreshStatusMessage = \"\";\n\t\t\tthis.errorMessage = timedOut\n\t\t\t\t? \"Model refresh timed out; showing cached models.\"\n\t\t\t\t: `Could not refresh model catalogs: ${error instanceof Error ? error.message : String(error)}`;\n\t\t\tthis.updateList();\n\t\t\tthis.tui.requestRender();\n\t\t} finally {\n\t\t\tif (this.refreshTimeout) clearTimeout(this.refreshTimeout);\n\t\t}\n\t}\n\n\tdispose(): void {\n\t\tif (this.closed) return;\n\t\tthis.closed = true;\n\t\tif (this.refreshTimeout) clearTimeout(this.refreshTimeout);\n\t\tthis.refreshAbortController.abort();\n\t}\n\n\tprivate sortModels(models: ModelItem[]): ModelItem[] {\n\t\tconst sorted = [...models];\n\t\t// Sort: current model first, default model second, then by provider.\n\t\tsorted.sort((a, b) => {\n\t\t\tconst aIsCurrent = modelsAreEqual(this.currentModel, a.model);\n\t\t\tconst bIsCurrent = modelsAreEqual(this.currentModel, b.model);\n\t\t\tif (aIsCurrent && !bIsCurrent) return -1;\n\t\t\tif (!aIsCurrent && bIsCurrent) return 1;\n\t\t\tconst aIsDefault = this.isDefaultModel(a.model);\n\t\t\tconst bIsDefault = this.isDefaultModel(b.model);\n\t\t\tif (aIsDefault && !bIsDefault) return -1;\n\t\t\tif (!aIsDefault && bIsDefault) return 1;\n\t\t\treturn a.provider.localeCompare(b.provider);\n\t\t});\n\t\treturn sorted;\n\t}\n\n\tprivate rebuildRows(): void {\n\t\tif (this.step.kind === \"providers\") {\n\t\t\tconst byProvider = new Map<string, ModelItem[]>();\n\t\t\tfor (const item of this.activeModels) {\n\t\t\t\tconst existing = byProvider.get(item.provider);\n\t\t\t\tif (existing) existing.push(item);\n\t\t\t\telse byProvider.set(item.provider, [item]);\n\t\t\t}\n\t\t\tconst providerRows: Row[] = [...byProvider].map(([provider, items]) => ({\n\t\t\t\tkind: \"provider\",\n\t\t\t\tprovider,\n\t\t\t\tcount: items.length,\n\t\t\t\thasCurrent: items.some((item) => modelsAreEqual(this.currentModel, item.model)),\n\t\t\t}));\n\t\t\tproviderRows.sort((a, b) => {\n\t\t\t\tif (a.kind !== \"provider\" || b.kind !== \"provider\") return 0;\n\t\t\t\tif (a.hasCurrent !== b.hasCurrent) return a.hasCurrent ? -1 : 1;\n\t\t\t\treturn a.provider.localeCompare(b.provider);\n\t\t\t});\n\t\t\tthis.rows = providerRows;\n\t\t\treturn;\n\t\t}\n\t\tconst provider = this.step.provider;\n\t\tconst models = provider === null ? this.activeModels : this.activeModels.filter((m) => m.provider === provider);\n\t\tthis.rows = models.map((item) => ({ kind: \"model\", item }));\n\t}\n\n\tprivate defaultSelectedIndex(): number {\n\t\tconst index = this.rows.findIndex((row) =>\n\t\t\trow.kind === \"provider\" ? row.hasCurrent : modelsAreEqual(this.currentModel, row.item.model),\n\t\t);\n\t\treturn index >= 0 ? index : 0;\n\t}\n\n\tprivate setStep(step: Step): void {\n\t\tthis.step = step;\n\t\tthis.navigatedIntoList = false;\n\t\tthis.searchInput.setValue(\"\");\n\t\tthis.rebuildRows();\n\t\tthis.selectedIndex = this.defaultSelectedIndex();\n\t\tthis.renderHeader();\n\t\tthis.filterRows(\"\");\n\t\tthis.tui.requestRender();\n\t}\n\n\tprivate isDrilledIn(): boolean {\n\t\treturn this.step.kind === \"models\" && this.step.provider !== null;\n\t}\n\n\t/** Escape returns to the provider list whenever there is a provider list worth returning to. */\n\tprivate canGoBack(): boolean {\n\t\treturn this.step.kind === \"models\" && this.activeProviders().length > 1;\n\t}\n\n\tprivate renderHeader(): void {\n\t\tthis.headerContainer.clear();\n\n\t\tif (this.step.kind === \"providers\") {\n\t\t\tthis.headerContainer.addChild(new Text(theme.bold(theme.fg(\"accent\", \"Select a provider\")), 0, 0));\n\t\t} else {\n\t\t\tconst label = this.step.provider ?? \"All providers\";\n\t\t\tconst crumb = `${theme.bold(theme.fg(\"accent\", label))}${theme.fg(\"muted\", \" › select a model\")}`;\n\t\t\tthis.headerContainer.addChild(new Text(crumb, 0, 0));\n\t\t}\n\n\t\tif (this.scopedModelItems.length > 0) {\n\t\t\tthis.headerContainer.addChild(new Text(this.getScopeText(), 0, 0));\n\t\t} else if (!this.isDrilledIn()) {\n\t\t\tconst hintText = \"Only showing models from configured providers. Use /login to add providers.\";\n\t\t\tthis.headerContainer.addChild(new Text(theme.fg(\"warning\", hintText), 0, 0));\n\t\t}\n\n\t\tthis.renderFooter();\n\t}\n\n\tprivate renderFooter(): void {\n\t\tthis.footerContainer.clear();\n\t\tthis.footerContainer.addChild(new Text(this.getHintText(), 0, 0));\n\t}\n\n\tprivate getScopeText(): string {\n\t\tconst allText = this.scope === \"all\" ? theme.fg(\"accent\", \"all\") : theme.fg(\"muted\", \"all\");\n\t\tconst scopedText = this.scope === \"scoped\" ? theme.fg(\"accent\", \"scoped\") : theme.fg(\"muted\", \"scoped\");\n\t\treturn `${theme.fg(\"muted\", \"Scope: \")}${allText}${theme.fg(\"muted\", \" | \")}${scopedText}`;\n\t}\n\n\tprivate getHintText(): string {\n\t\tconst hints: Array<readonly [HintKey, string]> = [\n\t\t\t[[\"tui.select.up\", \"tui.select.down\"], \"move\"],\n\t\t\t[\"tui.select.confirm\", \"select\"],\n\t\t];\n\t\tif (this.hasAdjustableEffort()) hints.push([[\"tui.editor.cursorLeft\", \"tui.editor.cursorRight\"], \"effort\"]);\n\t\tif (this.scopedModelItems.length > 0) hints.push([\"tui.input.tab\", \"scope\"]);\n\t\tif (this.onSelectAsDefaultCallback) hints.push([{ literal: \"ctrl+s\" }, \"set as default\"]);\n\t\tif (this.canGoBack()) hints.push([{ literal: \"escape\" }, \"providers\"], [{ literal: \"ctrl+c\" }, \"close\"]);\n\t\telse hints.push([\"tui.select.cancel\", \"close\"]);\n\t\treturn hintRow(hints);\n\t}\n\n\t/** Levels worth dialling. A model with nothing but \"off\" has no cluster to show. */\n\tprivate getSelectableLevels(item: ModelItem): ModelThinkingLevel[] {\n\t\tconst levels = getSupportedThinkingLevels(item.model);\n\t\tif (levels.length === 1 && levels[0] === \"off\") return [];\n\t\treturn levels;\n\t}\n\n\tprivate getEffort(item: ModelItem): ModelThinkingLevel | undefined {\n\t\tconst levels = this.getSelectableLevels(item);\n\t\tif (levels.length === 0) return undefined;\n\t\tconst key = `${item.provider}/${item.id}`;\n\t\tconst stored = this.effortLevels.get(key);\n\t\tif (stored !== undefined && levels.includes(stored)) return stored;\n\t\tconst requested = this.initialThinkingLevel ?? \"off\";\n\t\tconst clamped = levels.includes(requested) ? requested : clampThinkingLevel(item.model, requested);\n\t\tconst resolved = levels.includes(clamped) ? clamped : levels[0]!;\n\t\tthis.effortLevels.set(key, resolved);\n\t\treturn resolved;\n\t}\n\n\tprivate adjustEffort(item: ModelItem, direction: number): boolean {\n\t\tconst levels = this.getSelectableLevels(item);\n\t\tif (levels.length === 0) return false;\n\t\tconst key = `${item.provider}/${item.id}`;\n\t\tconst current = this.getEffort(item) ?? levels[0]!;\n\t\tconst next = levels[(levels.indexOf(current) + direction + levels.length) % levels.length]!;\n\t\tthis.effortLevels.set(key, next);\n\t\tthis.editedEffortModels.add(key);\n\t\treturn true;\n\t}\n\n\t/** The effort to hand back on select, or undefined when the user left it alone. */\n\tprivate getChosenEffort(item: ModelItem): ModelThinkingLevel | undefined {\n\t\treturn this.editedEffortModels.has(`${item.provider}/${item.id}`) ? this.getEffort(item) : undefined;\n\t}\n\n\tprivate hasAdjustableEffort(): boolean {\n\t\treturn this.filteredRows.some((row) => row.kind === \"model\" && this.getSelectableLevels(row.item).length > 0);\n\t}\n\n\t/**\n\t * Context that trails a model row, right-aligned, in reading order.\n\t *\n\t * Each segment carries what it costs to lose, because a narrow row drops by\n\t * that and not by position. `current` outranks the id: the row already shows\n\t * the model's name, so the id is largely recoverable from it, while nothing\n\t * else on screen says which model the session is on. The provider badge goes\n\t * first, being the least you lose.\n\t */\n\tprivate getTrailingSegments(item: ModelItem): TrailingSegment[] {\n\t\tconst segments: TrailingSegment[] = [];\n\t\tif (this.step.kind === \"models\" && this.step.provider === null) {\n\t\t\tsegments.push({ text: theme.fg(\"muted\", item.provider), priority: 0 });\n\t\t}\n\t\tsegments.push({ text: theme.fg(\"muted\", item.id), priority: 2 });\n\t\tif (this.isDefaultModel(item.model)) segments.push({ text: theme.fg(\"muted\", \"default\"), priority: 1 });\n\t\tif (modelsAreEqual(this.currentModel, item.model)) {\n\t\t\tsegments.push({ text: theme.fg(\"success\", \"current\"), priority: 3 });\n\t\t}\n\t\treturn segments;\n\t}\n\n\tprivate isDefaultModel(model: Model<any>): boolean {\n\t\treturn this.defaultModel?.provider === model.provider && this.defaultModel.id === model.id;\n\t}\n\n\tprivate isDefaultSearch(query: string): boolean {\n\t\tconst normalized = query.trim().toLowerCase();\n\t\treturn normalized.length > 0 && \"default\".startsWith(normalized);\n\t}\n\n\tprivate setScope(scope: ModelScope): void {\n\t\tif (this.scope === scope) return;\n\t\tthis.scope = scope;\n\t\tthis.activeModels = this.scope === \"scoped\" ? this.scopedModelItems : this.allModels;\n\t\tthis.rebuildRows();\n\t\tthis.selectedIndex = this.defaultSelectedIndex();\n\t\tthis.renderHeader();\n\t\tthis.filterRows(this.searchInput.getValue());\n\t}\n\n\tprivate rowSearchText(row: Row): string {\n\t\tif (row.kind === \"provider\") return row.provider;\n\t\tconst defaultText = this.isDefaultModel(row.item.model) ? \" default\" : \"\";\n\t\treturn `${getModelSelectorSearchText({ id: row.item.id, provider: row.item.provider, name: row.item.model.name })}${defaultText}`;\n\t}\n\n\tprivate isDefaultRow(row: Row): boolean {\n\t\treturn row.kind === \"model\" && this.isDefaultModel(row.item.model);\n\t}\n\n\tprivate filterRows(query: string): void {\n\t\tif (!query) {\n\t\t\tthis.filteredRows = this.rows;\n\t\t} else {\n\t\t\tconst filtered = fuzzyFilter(this.rows, query, (row) => this.rowSearchText(row));\n\t\t\tif (this.isDefaultSearch(query)) {\n\t\t\t\t// Typing \"default\" (or a prefix of it) surfaces the default model even\n\t\t\t\t// when its name doesn't otherwise fuzzy-match the query.\n\t\t\t\tconst defaultRows = this.rows.filter((row) => this.isDefaultRow(row));\n\t\t\t\tthis.filteredRows = [...defaultRows, ...filtered.filter((row) => !this.isDefaultRow(row))];\n\t\t\t} else {\n\t\t\t\tthis.filteredRows = filtered;\n\t\t\t}\n\t\t}\n\t\t// When filtering by a query, move the selector to the top row so the best\n\t\t// match is highlighted. When the query is cleared, keep the current position\n\t\t// clamped to the (restored) list length.\n\t\tthis.selectedIndex = query ? 0 : Math.min(this.selectedIndex, Math.max(0, this.filteredRows.length - 1));\n\t\t// The effort hint depends on what survived the filter, so the footer is\n\t\t// rebuilt here rather than with the header, which runs before filtering.\n\t\tthis.renderFooter();\n\t\tthis.updateList();\n\t}\n\n\tprivate renderProviderRow(row: Extract<Row, { kind: \"provider\" }>, isSelected: boolean): string {\n\t\tconst prefix = isSelected ? \"→ \" : \" \";\n\t\tconst count = theme.fg(\"muted\", ` (${row.count} model${row.count === 1 ? \"\" : \"s\"})`);\n\t\tconst checkmark = row.hasCurrent ? theme.fg(\"success\", \" ✓\") : \"\";\n\t\tconst composed = `${prefix}${row.provider}${count}${checkmark}`;\n\t\treturn isSelected ? paintSelectedRow(composed) : composed;\n\t}\n\n\t/**\n\t * The visible page of rows, rendered together because the effort cluster is\n\t * placed once for the whole page rather than per row.\n\t */\n\tprivate createRowsComponent(startIndex: number, endIndex: number): Component {\n\t\tconst rows = this.filteredRows.slice(startIndex, endIndex);\n\t\treturn {\n\t\t\trender: (width: number): string[] => {\n\t\t\t\tconst layout = this.pageEffortLayout(rows, width);\n\t\t\t\treturn rows.map((row, offset) => {\n\t\t\t\t\tconst isSelected = startIndex + offset === this.selectedIndex;\n\t\t\t\t\tif (row.kind === \"provider\") return this.renderProviderRow(row, isSelected);\n\t\t\t\t\tconst composed = composeModelRow({\n\t\t\t\t\t\tname: row.item.model.name,\n\t\t\t\t\t\tlevels: this.getSelectableLevels(row.item),\n\t\t\t\t\t\teffort: this.getEffort(row.item),\n\t\t\t\t\t\ttrailingSegments: this.getTrailingSegments(row.item),\n\t\t\t\t\t\tselected: isSelected,\n\t\t\t\t\t\tlayout,\n\t\t\t\t\t\twidth,\n\t\t\t\t\t});\n\t\t\t\t\treturn isSelected ? paintSelectedRowToWidth(composed, width) : composed;\n\t\t\t\t});\n\t\t\t},\n\t\t\tinvalidate: () => {},\n\t\t};\n\t}\n\n\tprivate pageEffortLayout(rows: ReadonlyArray<Row>, width: number): EffortLayout {\n\t\tconst layoutRows: EffortLayoutRow[] = [];\n\t\tfor (const row of rows) {\n\t\t\tif (row.kind !== \"model\") continue;\n\t\t\tlayoutRows.push({\n\t\t\t\tname: row.item.model.name,\n\t\t\t\tlevels: this.getSelectableLevels(row.item),\n\t\t\t\ttrailingSegments: this.getTrailingSegments(row.item),\n\t\t\t});\n\t\t}\n\t\treturn layoutRows.length > 0 ? getEffortLayout(layoutRows, width) : NO_EFFORT_CLUSTER;\n\t}\n\n\tprivate updateList(): void {\n\t\tthis.listContainer.clear();\n\n\t\tconst startIndex = Math.max(\n\t\t\t0,\n\t\t\tMath.min(this.selectedIndex - Math.floor(MAX_VISIBLE_ROWS / 2), this.filteredRows.length - MAX_VISIBLE_ROWS),\n\t\t);\n\t\tconst endIndex = Math.min(startIndex + MAX_VISIBLE_ROWS, this.filteredRows.length);\n\n\t\tif (endIndex > startIndex) {\n\t\t\tthis.listContainer.addChild(this.createRowsComponent(startIndex, endIndex));\n\t\t}\n\n\t\t// Add scroll indicator if needed\n\t\tif (startIndex > 0 || endIndex < this.filteredRows.length) {\n\t\t\tconst scrollInfo = theme.fg(\"muted\", ` (${this.selectedIndex + 1}/${this.filteredRows.length})`);\n\t\t\tthis.listContainer.addChild(new Text(scrollInfo, 0, 0));\n\t\t}\n\n\t\t// Show error message or \"no results\" if empty\n\t\tif (this.errorMessage) {\n\t\t\t// Show error in red\n\t\t\tconst errorLines = this.errorMessage.split(\"\\n\");\n\t\t\tfor (const line of errorLines) {\n\t\t\t\tthis.listContainer.addChild(new Text(theme.fg(\"error\", line), 0, 0));\n\t\t\t}\n\t\t} else if (this.filteredRows.length === 0) {\n\t\t\tconst empty = this.step.kind === \"providers\" ? \" No matching providers\" : \" No matching models\";\n\t\t\tthis.listContainer.addChild(new Text(theme.fg(\"muted\", empty), 0, 0));\n\t\t} else {\n\t\t\tconst selected = this.filteredRows[this.selectedIndex];\n\t\t\tif (selected?.kind === \"model\") {\n\t\t\t\tconst cost = selected.item.model.cost;\n\t\t\t\tthis.listContainer.addChild({\n\t\t\t\t\trender: (width: number) => renderPricePanel(cost, width),\n\t\t\t\t\tinvalidate: () => {},\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t\tif (this.refreshStatusMessage) {\n\t\t\tthis.listContainer.addChild(new Spacer(1));\n\t\t\tthis.listContainer.addChild(\n\t\t\t\tnew Text(theme.fg(this.refreshStatusSuccess ? \"success\" : \"muted\", ` ${this.refreshStatusMessage}`), 0, 0),\n\t\t\t);\n\t\t}\n\t}\n\n\thandleInput(keyData: string): void {\n\t\tconst kb = getKeybindings();\n\t\tif (kb.matches(keyData, \"tui.input.tab\")) {\n\t\t\tif (this.scopedModelItems.length > 0) {\n\t\t\t\tthis.setScope(this.scope === \"all\" ? \"scoped\" : \"all\");\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\t// Left/right dial the highlighted model's effort, but only once they are\n\t\t// free to: while a query is being typed they still drive the search cursor.\n\t\tif (\n\t\t\t(this.searchInput.getValue() === \"\" || this.navigatedIntoList) &&\n\t\t\t(kb.matches(keyData, \"tui.editor.cursorLeft\") || kb.matches(keyData, \"tui.editor.cursorRight\"))\n\t\t) {\n\t\t\tconst row = this.filteredRows[this.selectedIndex];\n\t\t\tconst direction = kb.matches(keyData, \"tui.editor.cursorLeft\") ? -1 : 1;\n\t\t\tif (row?.kind === \"model\" && this.adjustEffort(row.item, direction)) {\n\t\t\t\tthis.updateList();\n\t\t\t\tthis.tui.requestRender();\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\t// Up arrow - wrap to bottom when at top\n\t\tif (kb.matches(keyData, \"tui.select.up\")) {\n\t\t\tif (this.filteredRows.length === 0) return;\n\t\t\tthis.navigatedIntoList = true;\n\t\t\tthis.selectedIndex = this.selectedIndex === 0 ? this.filteredRows.length - 1 : this.selectedIndex - 1;\n\t\t\tthis.updateList();\n\t\t}\n\t\t// Down arrow - wrap to top when at bottom\n\t\telse if (kb.matches(keyData, \"tui.select.down\")) {\n\t\t\tif (this.filteredRows.length === 0) return;\n\t\t\tthis.navigatedIntoList = true;\n\t\t\tthis.selectedIndex = this.selectedIndex === this.filteredRows.length - 1 ? 0 : this.selectedIndex + 1;\n\t\t\tthis.updateList();\n\t\t}\n\t\t// Enter\n\t\telse if (kb.matches(keyData, \"tui.select.confirm\")) {\n\t\t\tthis.confirmSelection();\n\t\t}\n\t\t// Escape or Ctrl+C\n\t\telse if (kb.matches(keyData, \"tui.select.cancel\")) {\n\t\t\t// Escape steps back through the two levels; Ctrl+C always closes.\n\t\t\tif (matchesKey(keyData, Key.escape) && this.canGoBack()) {\n\t\t\t\tthis.setStep({ kind: \"providers\" });\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tthis.dispose();\n\t\t\tthis.onCancelCallback();\n\t\t}\n\t\t// Ctrl+S — select and save as default\n\t\telse if (matchesKey(keyData, \"ctrl+s\") && this.onSelectAsDefaultCallback) {\n\t\t\tconst row = this.filteredRows[this.selectedIndex];\n\t\t\tif (row?.kind === \"model\") {\n\t\t\t\tthis.dispose();\n\t\t\t\tthis.onSelectAsDefaultCallback(row.item.model, this.getChosenEffort(row.item));\n\t\t\t}\n\t\t}\n\t\t// Pass everything else to search input\n\t\telse {\n\t\t\tconst previousQuery = this.searchInput.getValue();\n\t\t\tthis.searchInput.handleInput(keyData);\n\t\t\tif (previousQuery !== this.searchInput.getValue()) {\n\t\t\t\tthis.navigatedIntoList = false;\n\t\t\t}\n\t\t\tthis.filterRows(this.searchInput.getValue());\n\t\t}\n\t}\n\n\tprivate confirmSelection(): void {\n\t\tconst row = this.filteredRows[this.selectedIndex];\n\t\tif (!row) return;\n\t\tif (row.kind === \"provider\") {\n\t\t\tthis.setStep({ kind: \"models\", provider: row.provider });\n\t\t\treturn;\n\t\t}\n\t\tthis.dispose();\n\t\tthis.onSelectCallback(row.item.model, this.getChosenEffort(row.item));\n\t}\n\n\tgetSearchInput(): Input {\n\t\treturn this.searchInput;\n\t}\n}\n"]}
1
+ {"version":3,"file":"model-selector.js","sourceRoot":"","sources":["../../../../src/modes/interactive/components/model-selector.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n\tclampThinkingLevel,\n\tgetSupportedThinkingLevels,\n\ttype Model,\n\ttype ModelThinkingLevel,\n\tmodelsAreEqual,\n} from \"@earendil-works/pi-ai\";\nimport {\n\ttype Component,\n\tContainer,\n\ttype Focusable,\n\tfuzzyFilter,\n\tgetKeybindings,\n\ttype Input,\n\tKey,\n\tmatchesKey,\n\tSpacer,\n\tText,\n\ttype TUI,\n} from \"@earendil-works/pi-tui\";\nimport type { ModelRuntime } from \"../../../core/model-runtime.ts\";\nimport { refreshModelCatalogs } from \"../model-catalog-refresh.ts\";\nimport { getModelSelectorSearchText } from \"../model-search.ts\";\nimport { paintSelectedRow, paintSelectedRowToWidth, theme } from \"../theme/theme.ts\";\nimport { DynamicBorder } from \"./dynamic-border.ts\";\nimport { type HintKey, hintRow } from \"./keybinding-hints.ts\";\nimport {\n\tcomposeModelRow,\n\ttype EffortLayout,\n\ttype EffortLayoutRow,\n\tgetEffortLayout,\n\tNO_EFFORT_CLUSTER,\n\trenderPricePanel,\n\ttype TrailingSegment,\n} from \"./model-row.ts\";\nimport { PromptInput } from \"./prompt-input.ts\";\n\ninterface ModelItem {\n\tprovider: string;\n\tid: string;\n\tmodel: Model<any>;\n}\n\ninterface ScopedModelItem {\n\tmodel: Model<any>;\n\tthinkingLevel?: string;\n}\n\ninterface DefaultModelReference {\n\tprovider: string;\n\tid: string;\n}\n\ntype ModelScope = \"all\" | \"scoped\";\n\n/**\n * Which of the two steps the selector is on. `provider: null` lists every provider's models at once,\n * which is how a `/model <search>` invocation and a single-provider setup both open.\n */\ntype Step = { kind: \"providers\" } | { kind: \"models\"; provider: string | null };\n\ntype Row =\n\t| { kind: \"provider\"; provider: string; count: number; hasCurrent: boolean }\n\t| { kind: \"model\"; item: ModelItem };\n\nconst MAX_VISIBLE_ROWS = 10;\n\n/**\n * Component that renders a two-step model selector: provider first, then that provider's models.\n */\nexport class ModelSelectorComponent extends Container implements Focusable {\n\tprivate searchInput: Input;\n\n\t// Focusable implementation - propagate to searchInput for IME cursor positioning\n\tprivate _focused = false;\n\tget focused(): boolean {\n\t\treturn this._focused;\n\t}\n\tset focused(value: boolean) {\n\t\tthis._focused = value;\n\t\tthis.searchInput.focused = value;\n\t}\n\tprivate headerContainer: Container;\n\tprivate listContainer: Container;\n\tprivate footerContainer: Container;\n\tprivate allModels: ModelItem[] = [];\n\tprivate scopedModelItems: ModelItem[] = [];\n\tprivate activeModels: ModelItem[] = [];\n\tprivate rows: Row[] = [];\n\tprivate filteredRows: Row[] = [];\n\tprivate selectedIndex: number = 0;\n\tprivate currentModel?: Model<any>;\n\tprivate modelRuntime: ModelRuntime;\n\tprivate onSelectCallback: (model: Model<any>, thinkingLevel?: ModelThinkingLevel) => void;\n\tprivate onSelectAsDefaultCallback?: (model: Model<any>, thinkingLevel?: ModelThinkingLevel) => void;\n\tprivate onCancelCallback: () => void;\n\tprivate errorMessage?: string;\n\tprivate refreshStatusMessage = \"Refreshing model catalogs…\";\n\tprivate refreshStatusSuccess = false;\n\tprivate tui: TUI;\n\tprivate scopedModels: ReadonlyArray<ScopedModelItem>;\n\tprivate defaultModel?: DefaultModelReference;\n\tprivate scope: ModelScope = \"all\";\n\tprivate step: Step = { kind: \"providers\" };\n\tprivate readonly refreshAbortController = new AbortController();\n\tprivate refreshTimeout?: ReturnType<typeof setTimeout>;\n\tprivate closed = false;\n\t/** Effort dialled per model in this session of the picker, keyed by `provider/id`. */\n\tprivate readonly effortLevels = new Map<string, ModelThinkingLevel>();\n\t/** Models whose effort the user actually moved. Only those override the session level. */\n\tprivate readonly editedEffortModels = new Set<string>();\n\tprivate readonly initialThinkingLevel?: ModelThinkingLevel;\n\t/**\n\t * True once up or down has moved the selection into the list. Until then\n\t * left and right belong to the search field's cursor, which is where they\n\t * are needed while a query is still being typed.\n\t */\n\tprivate navigatedIntoList = false;\n\n\tconstructor(\n\t\ttui: TUI,\n\t\tcurrentModel: Model<any> | undefined,\n\t\tmodelRuntime: ModelRuntime,\n\t\tscopedModels: ReadonlyArray<ScopedModelItem>,\n\t\tonSelect: (model: Model<any>, thinkingLevel?: ModelThinkingLevel) => void,\n\t\tonCancel: () => void,\n\t\tinitialSearchInput?: string,\n\t\tonSelectAsDefault?: (model: Model<any>, thinkingLevel?: ModelThinkingLevel) => void,\n\t\tdefaultModel?: DefaultModelReference,\n\t\toptions: { thinkingLevel?: ModelThinkingLevel } = {},\n\t) {\n\t\tsuper();\n\n\t\tthis.tui = tui;\n\t\tthis.initialThinkingLevel = options.thinkingLevel;\n\t\tthis.currentModel = currentModel;\n\t\tthis.modelRuntime = modelRuntime;\n\t\tthis.scopedModels = scopedModels;\n\t\tthis.defaultModel = defaultModel;\n\t\tthis.scope = scopedModels.length > 0 ? \"scoped\" : \"all\";\n\t\tthis.onSelectCallback = onSelect;\n\t\tthis.onSelectAsDefaultCallback = onSelectAsDefault;\n\t\tthis.onCancelCallback = onCancel;\n\n\t\t// Add top border\n\t\tthis.addChild(new DynamicBorder());\n\t\tthis.addChild(new Spacer(1));\n\n\t\tthis.headerContainer = new Container();\n\t\tthis.addChild(this.headerContainer);\n\t\tthis.addChild(new Spacer(1));\n\n\t\t// Create search input\n\t\tthis.searchInput = new PromptInput();\n\t\tif (initialSearchInput) {\n\t\t\tthis.searchInput.setValue(initialSearchInput);\n\t\t}\n\t\tthis.searchInput.onSubmit = () => this.confirmSelection();\n\t\tthis.addChild(this.searchInput);\n\n\t\tthis.addChild(new Spacer(1));\n\n\t\t// Create list container\n\t\tthis.listContainer = new Container();\n\t\tthis.addChild(this.listContainer);\n\n\t\tthis.addChild(new Spacer(1));\n\n\t\t// Keybindings live under the list, where the eye already is once it has\n\t\t// finished reading. Above the list they were the first thing read and the\n\t\t// least useful.\n\t\tthis.footerContainer = new Container();\n\t\tthis.addChild(this.footerContainer);\n\n\t\t// Add bottom border\n\t\tthis.addChild(new DynamicBorder());\n\n\t\t// Render the current snapshot immediately, then refresh in the background.\n\t\tthis.loadModelsFromSnapshot();\n\t\tthis.step = this.initialStep(initialSearchInput);\n\t\tthis.rebuildRows();\n\t\tthis.selectedIndex = this.defaultSelectedIndex();\n\t\tthis.renderHeader();\n\t\tthis.filterRows(this.searchInput.getValue());\n\t\tthis.tui.requestRender();\n\t\tvoid this.refreshModels();\n\t}\n\n\tprivate initialStep(initialSearchInput: string | undefined): Step {\n\t\tconst providers = this.activeProviders();\n\t\tif (initialSearchInput) return { kind: \"models\", provider: null };\n\t\tif (providers.length <= 1) return { kind: \"models\", provider: providers[0] ?? null };\n\t\treturn { kind: \"providers\" };\n\t}\n\n\tprivate activeProviders(): string[] {\n\t\treturn [...new Set(this.activeModels.map((item) => item.provider))];\n\t}\n\n\tprivate loadModelsFromSnapshot(): void {\n\t\tconst models = this.modelRuntime.getAvailableSnapshot().map((model: Model<any>) => ({\n\t\t\tprovider: model.provider,\n\t\t\tid: model.id,\n\t\t\tmodel,\n\t\t}));\n\t\tthis.allModels = this.sortModels(models);\n\t\tthis.scopedModels = this.scopedModels.map((scoped) => {\n\t\t\tconst refreshed = this.modelRuntime.getModel(scoped.model.provider, scoped.model.id);\n\t\t\treturn refreshed ? { ...scoped, model: refreshed } : scoped;\n\t\t});\n\t\tthis.scopedModelItems = this.scopedModels.map((scoped) => ({\n\t\t\tprovider: scoped.model.provider,\n\t\t\tid: scoped.model.id,\n\t\t\tmodel: scoped.model,\n\t\t}));\n\t\tthis.activeModels = this.scope === \"scoped\" ? this.scopedModelItems : this.allModels;\n\t}\n\n\tprivate async refreshModels(): Promise<void> {\n\t\tconst timeoutMs = 15_000;\n\t\tlet timedOut = false;\n\t\tthis.refreshTimeout = setTimeout(() => {\n\t\t\ttimedOut = true;\n\t\t\tthis.refreshAbortController.abort();\n\t\t}, timeoutMs);\n\t\ttry {\n\t\t\tconst result = await refreshModelCatalogs(this.modelRuntime, this.refreshAbortController.signal);\n\t\t\tif (this.closed) return;\n\t\t\tthis.refreshStatusMessage = \"\";\n\t\t\tif (result.aborted && timedOut) {\n\t\t\t\tthis.errorMessage = \"Model refresh timed out; showing cached models.\";\n\t\t\t} else if (result.errors.size === 1) {\n\t\t\t\tthis.errorMessage = `Could not refresh ${result.errors.keys().next().value}; showing cached models.`;\n\t\t\t} else if (result.errors.size > 1) {\n\t\t\t\tthis.errorMessage = `Could not refresh ${result.errors.size} model catalogs (${[...result.errors.keys()].join(\", \")}); showing cached models.`;\n\t\t\t} else {\n\t\t\t\tthis.errorMessage = this.modelRuntime.getError();\n\t\t\t\tif (!this.errorMessage) {\n\t\t\t\t\tthis.refreshStatusMessage = \"Model catalogs refreshed.\";\n\t\t\t\t\tthis.refreshStatusSuccess = true;\n\t\t\t\t}\n\t\t\t}\n\t\t\tthis.loadModelsFromSnapshot();\n\t\t\tthis.rebuildRows();\n\t\t\tthis.renderHeader();\n\t\t\tthis.filterRows(this.searchInput.getValue());\n\t\t\tthis.tui.requestRender();\n\t\t} catch (error) {\n\t\t\tif (this.closed) return;\n\t\t\tthis.refreshStatusMessage = \"\";\n\t\t\tthis.errorMessage = timedOut\n\t\t\t\t? \"Model refresh timed out; showing cached models.\"\n\t\t\t\t: `Could not refresh model catalogs: ${error instanceof Error ? error.message : String(error)}`;\n\t\t\tthis.updateList();\n\t\t\tthis.tui.requestRender();\n\t\t} finally {\n\t\t\tif (this.refreshTimeout) clearTimeout(this.refreshTimeout);\n\t\t}\n\t}\n\n\tdispose(): void {\n\t\tif (this.closed) return;\n\t\tthis.closed = true;\n\t\tif (this.refreshTimeout) clearTimeout(this.refreshTimeout);\n\t\tthis.refreshAbortController.abort();\n\t}\n\n\tprivate sortModels(models: ModelItem[]): ModelItem[] {\n\t\tconst sorted = [...models];\n\t\t// Sort: current model first, default model second, then by provider.\n\t\tsorted.sort((a, b) => {\n\t\t\tconst aIsCurrent = modelsAreEqual(this.currentModel, a.model);\n\t\t\tconst bIsCurrent = modelsAreEqual(this.currentModel, b.model);\n\t\t\tif (aIsCurrent && !bIsCurrent) return -1;\n\t\t\tif (!aIsCurrent && bIsCurrent) return 1;\n\t\t\tconst aIsDefault = this.isDefaultModel(a.model);\n\t\t\tconst bIsDefault = this.isDefaultModel(b.model);\n\t\t\tif (aIsDefault && !bIsDefault) return -1;\n\t\t\tif (!aIsDefault && bIsDefault) return 1;\n\t\t\treturn a.provider.localeCompare(b.provider);\n\t\t});\n\t\treturn sorted;\n\t}\n\n\tprivate rebuildRows(): void {\n\t\tif (this.step.kind === \"providers\") {\n\t\t\tconst byProvider = new Map<string, ModelItem[]>();\n\t\t\tfor (const item of this.activeModels) {\n\t\t\t\tconst existing = byProvider.get(item.provider);\n\t\t\t\tif (existing) existing.push(item);\n\t\t\t\telse byProvider.set(item.provider, [item]);\n\t\t\t}\n\t\t\tconst providerRows: Row[] = [...byProvider].map(([provider, items]) => ({\n\t\t\t\tkind: \"provider\",\n\t\t\t\tprovider,\n\t\t\t\tcount: items.length,\n\t\t\t\thasCurrent: items.some((item) => modelsAreEqual(this.currentModel, item.model)),\n\t\t\t}));\n\t\t\tproviderRows.sort((a, b) => {\n\t\t\t\tif (a.kind !== \"provider\" || b.kind !== \"provider\") return 0;\n\t\t\t\tif (a.hasCurrent !== b.hasCurrent) return a.hasCurrent ? -1 : 1;\n\t\t\t\treturn a.provider.localeCompare(b.provider);\n\t\t\t});\n\t\t\tthis.rows = providerRows;\n\t\t\treturn;\n\t\t}\n\t\tconst provider = this.step.provider;\n\t\tconst models = provider === null ? this.activeModels : this.activeModels.filter((m) => m.provider === provider);\n\t\tthis.rows = models.map((item) => ({ kind: \"model\", item }));\n\t}\n\n\tprivate defaultSelectedIndex(): number {\n\t\tconst index = this.rows.findIndex((row) =>\n\t\t\trow.kind === \"provider\" ? row.hasCurrent : modelsAreEqual(this.currentModel, row.item.model),\n\t\t);\n\t\treturn index >= 0 ? index : 0;\n\t}\n\n\tprivate setStep(step: Step): void {\n\t\tthis.step = step;\n\t\tthis.navigatedIntoList = false;\n\t\tthis.searchInput.setValue(\"\");\n\t\tthis.rebuildRows();\n\t\tthis.selectedIndex = this.defaultSelectedIndex();\n\t\tthis.renderHeader();\n\t\tthis.filterRows(\"\");\n\t\tthis.tui.requestRender();\n\t}\n\n\tprivate isDrilledIn(): boolean {\n\t\treturn this.step.kind === \"models\" && this.step.provider !== null;\n\t}\n\n\t/** Escape returns to the provider list whenever there is a provider list worth returning to. */\n\tprivate canGoBack(): boolean {\n\t\treturn this.step.kind === \"models\" && this.activeProviders().length > 1;\n\t}\n\n\tprivate renderHeader(): void {\n\t\tthis.headerContainer.clear();\n\n\t\tif (this.step.kind === \"providers\") {\n\t\t\tthis.headerContainer.addChild(new Text(theme.bold(theme.fg(\"accent\", \"Select a provider\")), 0, 0));\n\t\t} else {\n\t\t\tconst label = this.step.provider ?? \"All providers\";\n\t\t\tconst crumb = `${theme.bold(theme.fg(\"accent\", label))}${theme.fg(\"muted\", \" › select a model\")}`;\n\t\t\tthis.headerContainer.addChild(new Text(crumb, 0, 0));\n\t\t}\n\n\t\tif (this.scopedModelItems.length > 0) {\n\t\t\tthis.headerContainer.addChild(new Text(this.getScopeText(), 0, 0));\n\t\t} else if (!this.isDrilledIn()) {\n\t\t\tconst hintText = \"Only showing models from configured providers. Use /login to add providers.\";\n\t\t\tthis.headerContainer.addChild(new Text(theme.fg(\"warning\", hintText), 0, 0));\n\t\t}\n\n\t\tthis.renderFooter();\n\t}\n\n\tprivate renderFooter(): void {\n\t\tthis.footerContainer.clear();\n\t\tthis.footerContainer.addChild(new Text(this.getHintText(), 0, 0));\n\t}\n\n\tprivate getScopeText(): string {\n\t\tconst allText = this.scope === \"all\" ? theme.fg(\"accent\", \"all\") : theme.fg(\"muted\", \"all\");\n\t\tconst scopedText = this.scope === \"scoped\" ? theme.fg(\"accent\", \"scoped\") : theme.fg(\"muted\", \"scoped\");\n\t\treturn `${theme.fg(\"muted\", \"Scope: \")}${allText}${theme.fg(\"muted\", \" | \")}${scopedText}`;\n\t}\n\n\tprivate getHintText(): string {\n\t\tconst hints: Array<readonly [HintKey, string]> = [\n\t\t\t[[\"tui.select.up\", \"tui.select.down\"], \"move\"],\n\t\t\t[\"tui.select.confirm\", \"select\"],\n\t\t];\n\t\tif (this.hasAdjustableEffort()) hints.push([[\"tui.editor.cursorLeft\", \"tui.editor.cursorRight\"], \"effort\"]);\n\t\tif (this.scopedModelItems.length > 0) hints.push([\"tui.input.tab\", \"scope\"]);\n\t\tif (this.onSelectAsDefaultCallback) hints.push([\"app.models.save\", \"set as default\"]);\n\t\tif (this.canGoBack()) hints.push([{ literal: \"escape\" }, \"providers\"], [{ literal: \"ctrl+c\" }, \"close\"]);\n\t\telse hints.push([\"tui.select.cancel\", \"close\"]);\n\t\treturn hintRow(hints);\n\t}\n\n\t/** Levels worth dialling. A model with nothing but \"off\" has no cluster to show. */\n\tprivate getSelectableLevels(item: ModelItem): ModelThinkingLevel[] {\n\t\tconst levels = getSupportedThinkingLevels(item.model);\n\t\tif (levels.length === 1 && levels[0] === \"off\") return [];\n\t\treturn levels;\n\t}\n\n\tprivate getEffort(item: ModelItem): ModelThinkingLevel | undefined {\n\t\tconst levels = this.getSelectableLevels(item);\n\t\tif (levels.length === 0) return undefined;\n\t\tconst key = `${item.provider}/${item.id}`;\n\t\tconst stored = this.effortLevels.get(key);\n\t\tif (stored !== undefined && levels.includes(stored)) return stored;\n\t\tconst requested = this.initialThinkingLevel ?? \"off\";\n\t\tconst clamped = levels.includes(requested) ? requested : clampThinkingLevel(item.model, requested);\n\t\tconst resolved = levels.includes(clamped) ? clamped : levels[0]!;\n\t\tthis.effortLevels.set(key, resolved);\n\t\treturn resolved;\n\t}\n\n\tprivate adjustEffort(item: ModelItem, direction: number): boolean {\n\t\tconst levels = this.getSelectableLevels(item);\n\t\tif (levels.length === 0) return false;\n\t\tconst key = `${item.provider}/${item.id}`;\n\t\tconst current = this.getEffort(item) ?? levels[0]!;\n\t\tconst next = levels[(levels.indexOf(current) + direction + levels.length) % levels.length]!;\n\t\tthis.effortLevels.set(key, next);\n\t\tthis.editedEffortModels.add(key);\n\t\treturn true;\n\t}\n\n\t/** The effort to hand back on select, or undefined when the user left it alone. */\n\tprivate getChosenEffort(item: ModelItem): ModelThinkingLevel | undefined {\n\t\treturn this.editedEffortModels.has(`${item.provider}/${item.id}`) ? this.getEffort(item) : undefined;\n\t}\n\n\tprivate hasAdjustableEffort(): boolean {\n\t\treturn this.filteredRows.some((row) => row.kind === \"model\" && this.getSelectableLevels(row.item).length > 0);\n\t}\n\n\t/**\n\t * Context that trails a model row, right-aligned, in reading order.\n\t *\n\t * Each segment carries what it costs to lose, because a narrow row drops by\n\t * that and not by position. `current` outranks the id: the row already shows\n\t * the model's name, so the id is largely recoverable from it, while nothing\n\t * else on screen says which model the session is on. The provider badge goes\n\t * first, being the least you lose.\n\t */\n\tprivate getTrailingSegments(item: ModelItem): TrailingSegment[] {\n\t\tconst segments: TrailingSegment[] = [];\n\t\tif (this.step.kind === \"models\" && this.step.provider === null) {\n\t\t\tsegments.push({ text: theme.fg(\"muted\", item.provider), priority: 0 });\n\t\t}\n\t\tsegments.push({ text: theme.fg(\"muted\", item.id), priority: 2 });\n\t\tif (this.isDefaultModel(item.model)) segments.push({ text: theme.fg(\"muted\", \"default\"), priority: 1 });\n\t\tif (modelsAreEqual(this.currentModel, item.model)) {\n\t\t\tsegments.push({ text: theme.fg(\"success\", \"current\"), priority: 3 });\n\t\t}\n\t\treturn segments;\n\t}\n\n\tprivate isDefaultModel(model: Model<any>): boolean {\n\t\treturn this.defaultModel?.provider === model.provider && this.defaultModel.id === model.id;\n\t}\n\n\tprivate isDefaultSearch(query: string): boolean {\n\t\tconst normalized = query.trim().toLowerCase();\n\t\treturn normalized.length > 0 && \"default\".startsWith(normalized);\n\t}\n\n\tprivate setScope(scope: ModelScope): void {\n\t\tif (this.scope === scope) return;\n\t\tthis.scope = scope;\n\t\tthis.activeModels = this.scope === \"scoped\" ? this.scopedModelItems : this.allModels;\n\t\tthis.rebuildRows();\n\t\tthis.selectedIndex = this.defaultSelectedIndex();\n\t\tthis.renderHeader();\n\t\tthis.filterRows(this.searchInput.getValue());\n\t}\n\n\tprivate rowSearchText(row: Row): string {\n\t\tif (row.kind === \"provider\") return row.provider;\n\t\tconst defaultText = this.isDefaultModel(row.item.model) ? \" default\" : \"\";\n\t\treturn `${getModelSelectorSearchText({ id: row.item.id, provider: row.item.provider, name: row.item.model.name })}${defaultText}`;\n\t}\n\n\tprivate isDefaultRow(row: Row): boolean {\n\t\treturn row.kind === \"model\" && this.isDefaultModel(row.item.model);\n\t}\n\n\tprivate filterRows(query: string): void {\n\t\tif (!query) {\n\t\t\tthis.filteredRows = this.rows;\n\t\t} else {\n\t\t\tconst filtered = fuzzyFilter(this.rows, query, (row) => this.rowSearchText(row));\n\t\t\tif (this.isDefaultSearch(query)) {\n\t\t\t\t// Typing \"default\" (or a prefix of it) surfaces the default model even\n\t\t\t\t// when its name doesn't otherwise fuzzy-match the query.\n\t\t\t\tconst defaultRows = this.rows.filter((row) => this.isDefaultRow(row));\n\t\t\t\tthis.filteredRows = [...defaultRows, ...filtered.filter((row) => !this.isDefaultRow(row))];\n\t\t\t} else {\n\t\t\t\tthis.filteredRows = filtered;\n\t\t\t}\n\t\t}\n\t\t// When filtering by a query, move the selector to the top row so the best\n\t\t// match is highlighted. When the query is cleared, keep the current position\n\t\t// clamped to the (restored) list length.\n\t\tthis.selectedIndex = query ? 0 : Math.min(this.selectedIndex, Math.max(0, this.filteredRows.length - 1));\n\t\t// The effort hint depends on what survived the filter, so the footer is\n\t\t// rebuilt here rather than with the header, which runs before filtering.\n\t\tthis.renderFooter();\n\t\tthis.updateList();\n\t}\n\n\tprivate renderProviderRow(row: Extract<Row, { kind: \"provider\" }>, isSelected: boolean): string {\n\t\tconst prefix = isSelected ? \"→ \" : \" \";\n\t\tconst count = theme.fg(\"muted\", ` (${row.count} model${row.count === 1 ? \"\" : \"s\"})`);\n\t\tconst checkmark = row.hasCurrent ? theme.fg(\"success\", \" ✓\") : \"\";\n\t\tconst composed = `${prefix}${row.provider}${count}${checkmark}`;\n\t\treturn isSelected ? paintSelectedRow(composed) : composed;\n\t}\n\n\t/**\n\t * The visible page of rows, rendered together because the effort cluster is\n\t * placed once for the whole page rather than per row.\n\t */\n\tprivate createRowsComponent(startIndex: number, endIndex: number): Component {\n\t\tconst rows = this.filteredRows.slice(startIndex, endIndex);\n\t\treturn {\n\t\t\trender: (width: number): string[] => {\n\t\t\t\tconst layout = this.pageEffortLayout(rows, width);\n\t\t\t\treturn rows.map((row, offset) => {\n\t\t\t\t\tconst isSelected = startIndex + offset === this.selectedIndex;\n\t\t\t\t\tif (row.kind === \"provider\") return this.renderProviderRow(row, isSelected);\n\t\t\t\t\tconst composed = composeModelRow({\n\t\t\t\t\t\tname: row.item.model.name,\n\t\t\t\t\t\tlevels: this.getSelectableLevels(row.item),\n\t\t\t\t\t\teffort: this.getEffort(row.item),\n\t\t\t\t\t\ttrailingSegments: this.getTrailingSegments(row.item),\n\t\t\t\t\t\tselected: isSelected,\n\t\t\t\t\t\tlayout,\n\t\t\t\t\t\twidth,\n\t\t\t\t\t});\n\t\t\t\t\treturn isSelected ? paintSelectedRowToWidth(composed, width) : composed;\n\t\t\t\t});\n\t\t\t},\n\t\t\tinvalidate: () => {},\n\t\t};\n\t}\n\n\tprivate pageEffortLayout(rows: ReadonlyArray<Row>, width: number): EffortLayout {\n\t\tconst layoutRows: EffortLayoutRow[] = [];\n\t\tfor (const row of rows) {\n\t\t\tif (row.kind !== \"model\") continue;\n\t\t\tlayoutRows.push({\n\t\t\t\tname: row.item.model.name,\n\t\t\t\tlevels: this.getSelectableLevels(row.item),\n\t\t\t\ttrailingSegments: this.getTrailingSegments(row.item),\n\t\t\t});\n\t\t}\n\t\treturn layoutRows.length > 0 ? getEffortLayout(layoutRows, width) : NO_EFFORT_CLUSTER;\n\t}\n\n\tprivate updateList(): void {\n\t\tthis.listContainer.clear();\n\n\t\tconst startIndex = Math.max(\n\t\t\t0,\n\t\t\tMath.min(this.selectedIndex - Math.floor(MAX_VISIBLE_ROWS / 2), this.filteredRows.length - MAX_VISIBLE_ROWS),\n\t\t);\n\t\tconst endIndex = Math.min(startIndex + MAX_VISIBLE_ROWS, this.filteredRows.length);\n\n\t\tif (endIndex > startIndex) {\n\t\t\tthis.listContainer.addChild(this.createRowsComponent(startIndex, endIndex));\n\t\t}\n\n\t\t// Add scroll indicator if needed\n\t\tif (startIndex > 0 || endIndex < this.filteredRows.length) {\n\t\t\tconst scrollInfo = theme.fg(\"muted\", ` (${this.selectedIndex + 1}/${this.filteredRows.length})`);\n\t\t\tthis.listContainer.addChild(new Text(scrollInfo, 0, 0));\n\t\t}\n\n\t\t// Show error message or \"no results\" if empty\n\t\tif (this.errorMessage) {\n\t\t\t// Show error in red\n\t\t\tconst errorLines = this.errorMessage.split(\"\\n\");\n\t\t\tfor (const line of errorLines) {\n\t\t\t\tthis.listContainer.addChild(new Text(theme.fg(\"error\", line), 0, 0));\n\t\t\t}\n\t\t} else if (this.filteredRows.length === 0) {\n\t\t\tconst empty = this.step.kind === \"providers\" ? \" No matching providers\" : \" No matching models\";\n\t\t\tthis.listContainer.addChild(new Text(theme.fg(\"muted\", empty), 0, 0));\n\t\t} else {\n\t\t\tconst selected = this.filteredRows[this.selectedIndex];\n\t\t\tif (selected?.kind === \"model\") {\n\t\t\t\tconst cost = selected.item.model.cost;\n\t\t\t\tthis.listContainer.addChild({\n\t\t\t\t\trender: (width: number) => renderPricePanel(cost, width),\n\t\t\t\t\tinvalidate: () => {},\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t\tif (this.refreshStatusMessage) {\n\t\t\tthis.listContainer.addChild(new Spacer(1));\n\t\t\tthis.listContainer.addChild(\n\t\t\t\tnew Text(theme.fg(this.refreshStatusSuccess ? \"success\" : \"muted\", ` ${this.refreshStatusMessage}`), 0, 0),\n\t\t\t);\n\t\t}\n\t}\n\n\thandleInput(keyData: string): void {\n\t\tconst kb = getKeybindings();\n\t\tif (kb.matches(keyData, \"tui.input.tab\")) {\n\t\t\tif (this.scopedModelItems.length > 0) {\n\t\t\t\tthis.setScope(this.scope === \"all\" ? \"scoped\" : \"all\");\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\t// Left/right dial the highlighted model's effort, but only once they are\n\t\t// free to: while a query is being typed they still drive the search cursor.\n\t\tif (\n\t\t\t(this.searchInput.getValue() === \"\" || this.navigatedIntoList) &&\n\t\t\t(kb.matches(keyData, \"tui.editor.cursorLeft\") || kb.matches(keyData, \"tui.editor.cursorRight\"))\n\t\t) {\n\t\t\tconst row = this.filteredRows[this.selectedIndex];\n\t\t\tconst direction = kb.matches(keyData, \"tui.editor.cursorLeft\") ? -1 : 1;\n\t\t\tif (row?.kind === \"model\" && this.adjustEffort(row.item, direction)) {\n\t\t\t\tthis.updateList();\n\t\t\t\tthis.tui.requestRender();\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\t// Up arrow - wrap to bottom when at top\n\t\tif (kb.matches(keyData, \"tui.select.up\")) {\n\t\t\tif (this.filteredRows.length === 0) return;\n\t\t\tthis.navigatedIntoList = true;\n\t\t\tthis.selectedIndex = this.selectedIndex === 0 ? this.filteredRows.length - 1 : this.selectedIndex - 1;\n\t\t\tthis.updateList();\n\t\t}\n\t\t// Down arrow - wrap to top when at bottom\n\t\telse if (kb.matches(keyData, \"tui.select.down\")) {\n\t\t\tif (this.filteredRows.length === 0) return;\n\t\t\tthis.navigatedIntoList = true;\n\t\t\tthis.selectedIndex = this.selectedIndex === this.filteredRows.length - 1 ? 0 : this.selectedIndex + 1;\n\t\t\tthis.updateList();\n\t\t}\n\t\t// Enter\n\t\telse if (kb.matches(keyData, \"tui.select.confirm\")) {\n\t\t\tthis.confirmSelection();\n\t\t}\n\t\t// Escape or Ctrl+C\n\t\telse if (kb.matches(keyData, \"tui.select.cancel\")) {\n\t\t\t// Escape steps back through the two levels; Ctrl+C always closes.\n\t\t\tif (matchesKey(keyData, Key.escape) && this.canGoBack()) {\n\t\t\t\tthis.setStep({ kind: \"providers\" });\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tthis.dispose();\n\t\t\tthis.onCancelCallback();\n\t\t}\n\t\t// Select and save as default\n\t\telse if (kb.matches(keyData, \"app.models.save\") && this.onSelectAsDefaultCallback) {\n\t\t\tconst row = this.filteredRows[this.selectedIndex];\n\t\t\tif (row?.kind === \"model\") {\n\t\t\t\tthis.dispose();\n\t\t\t\tthis.onSelectAsDefaultCallback(row.item.model, this.getChosenEffort(row.item));\n\t\t\t}\n\t\t}\n\t\t// Pass everything else to search input\n\t\telse {\n\t\t\tconst previousQuery = this.searchInput.getValue();\n\t\t\tthis.searchInput.handleInput(keyData);\n\t\t\tif (previousQuery !== this.searchInput.getValue()) {\n\t\t\t\tthis.navigatedIntoList = false;\n\t\t\t}\n\t\t\tthis.filterRows(this.searchInput.getValue());\n\t\t}\n\t}\n\n\tprivate confirmSelection(): void {\n\t\tconst row = this.filteredRows[this.selectedIndex];\n\t\tif (!row) return;\n\t\tif (row.kind === \"provider\") {\n\t\t\tthis.setStep({ kind: \"models\", provider: row.provider });\n\t\t\treturn;\n\t\t}\n\t\tthis.dispose();\n\t\tthis.onSelectCallback(row.item.model, this.getChosenEffort(row.item));\n\t}\n\n\tgetSearchInput(): Input {\n\t\treturn this.searchInput;\n\t}\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"scoped-models-selector.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/scoped-models-selector.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,uBAAuB,CAAC;AACnD,OAAO,EACN,SAAS,EACT,KAAK,SAAS,EAGd,KAAK,KAAK,EAKV,MAAM,wBAAwB,CAAC;AA+DhC,MAAM,WAAW,YAAY;IAC5B,SAAS,EAAE,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;IACxB,eAAe,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;IACjC,aAAa,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,eAAe;IAC/B,wFAAwF;IACxF,QAAQ,EAAE,CAAC,eAAe,EAAE,MAAM,EAAE,GAAG,IAAI,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACrE,sEAAsE;IACtE,SAAS,EAAE,CAAC,eAAe,EAAE,MAAM,EAAE,GAAG,IAAI,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACtE,QAAQ,EAAE,MAAM,IAAI,CAAC;CACrB;AAED;;;GAGG;AACH,qBAAa,6BAA8B,SAAQ,SAAU,YAAW,SAAS;IAChF,OAAO,CAAC,UAAU,CAAsC;IACxD,OAAO,CAAC,MAAM,CAAgB;IAC9B,OAAO,CAAC,UAAU,CAAoB;IACtC,OAAO,CAAC,aAAa,CAAmB;IACxC,OAAO,CAAC,aAAa,CAAK;IAC1B,OAAO,CAAC,WAAW,CAAQ;IAG3B,OAAO,CAAC,QAAQ,CAAS;IACzB,IAAI,OAAO,IAAI,OAAO,CAErB;IACD,IAAI,OAAO,CAAC,KAAK,EAAE,OAAO,EAGzB;IACD,OAAO,CAAC,aAAa,CAAY;IACjC,OAAO,CAAC,UAAU,CAAO;IACzB,OAAO,CAAC,SAAS,CAAkB;IACnC,OAAO,CAAC,UAAU,CAAK;IACvB,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,iBAAiB,CAAC,CAAO;IAEjC,YAAY,MAAM,EAAE,YAAY,EAAE,SAAS,EAAE,eAAe,EA0C3D;IAED,YAAY,CAAC,MAAM,EAAE,SAAS,KAAK,CAAC,GAAG,CAAC,EAAE,EAAE,eAAe,CAAC,EAAE,MAAM,EAAE,GAAG,IAAI,GAAG,IAAI,CAgBnF;IAED,gBAAgB,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,SAAS,GAAG,SAAS,GAAG,IAAI,CAE7E;IAED,OAAO,CAAC,UAAU;IAQlB,OAAO,CAAC,aAAa;IAqBrB,OAAO,CAAC,OAAO;IAef,OAAO,CAAC,YAAY;IAIpB,OAAO,CAAC,UAAU;IAoDlB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAoH9B;IAED,cAAc,IAAI,KAAK,CAEtB;CACD","sourcesContent":["import type { Model } from \"@earendil-works/pi-ai\";\nimport {\n\tContainer,\n\ttype Focusable,\n\tfuzzyFilter,\n\tgetKeybindings,\n\ttype Input,\n\tKey,\n\tmatchesKey,\n\tSpacer,\n\tText,\n} from \"@earendil-works/pi-tui\";\nimport { getModelSearchText } from \"../model-search.ts\";\nimport { theme } from \"../theme/theme.ts\";\nimport { DynamicBorder } from \"./dynamic-border.ts\";\nimport { keyText } from \"./keybinding-hints.ts\";\nimport { PromptInput } from \"./prompt-input.ts\";\n\n// EnabledIds: null = all enabled (no filter), string[] = explicit ordered list\ntype EnabledIds = string[] | null;\n\nfunction isEnabled(enabledIds: EnabledIds, id: string): boolean {\n\treturn enabledIds === null || enabledIds.includes(id);\n}\n\nfunction toggle(enabledIds: EnabledIds, id: string): EnabledIds {\n\tif (enabledIds === null) return [id]; // First toggle: start with only this one\n\tconst index = enabledIds.indexOf(id);\n\tif (index >= 0) return [...enabledIds.slice(0, index), ...enabledIds.slice(index + 1)];\n\treturn [...enabledIds, id];\n}\n\nfunction enableAll(enabledIds: EnabledIds, allIds: string[], targetIds?: string[]): EnabledIds {\n\tif (enabledIds === null) return null; // Already all enabled\n\tconst targets = targetIds ?? allIds;\n\tconst result = [...enabledIds];\n\tfor (const id of targets) {\n\t\tif (!result.includes(id)) result.push(id);\n\t}\n\treturn result.length === allIds.length && result.every((id) => allIds.includes(id)) ? null : result;\n}\n\nfunction clearAll(enabledIds: EnabledIds, allIds: string[], targetIds?: string[]): EnabledIds {\n\tif (enabledIds === null) {\n\t\treturn targetIds ? allIds.filter((id) => !targetIds.includes(id)) : [];\n\t}\n\tconst targets = new Set(targetIds ?? enabledIds);\n\treturn enabledIds.filter((id) => !targets.has(id));\n}\n\nfunction move(enabledIds: EnabledIds, id: string, delta: number): EnabledIds {\n\tif (enabledIds === null) return null;\n\tconst list = [...enabledIds];\n\tconst index = list.indexOf(id);\n\tif (index < 0) return list;\n\tconst newIndex = index + delta;\n\tif (newIndex < 0 || newIndex >= list.length) return list;\n\tconst result = [...list];\n\t[result[index], result[newIndex]] = [result[newIndex], result[index]];\n\treturn result;\n}\n\nfunction getSortedIds(enabledIds: EnabledIds, allIds: string[]): string[] {\n\tif (enabledIds === null) return allIds;\n\tconst enabledSet = new Set(enabledIds);\n\treturn [...enabledIds, ...allIds.filter((id) => !enabledSet.has(id))];\n}\n\ninterface ModelItem {\n\tfullId: string;\n\tmodel: Model<any> | undefined;\n\tenabled: boolean;\n}\n\nexport interface ModelsConfig {\n\tallModels: Model<any>[];\n\tenabledModelIds: string[] | null;\n\trefreshStatus?: string;\n}\n\nexport interface ModelsCallbacks {\n\t/** Called whenever the enabled model set or order changes (session-only, no persist) */\n\tonChange: (enabledModelIds: string[] | null) => void | Promise<void>;\n\t/** Called when user wants to persist current selection to settings */\n\tonPersist: (enabledModelIds: string[] | null) => void | Promise<void>;\n\tonCancel: () => void;\n}\n\n/**\n * Component for enabling/disabling models for Ctrl+P cycling.\n * Changes are session-only until explicitly persisted with Ctrl+S.\n */\nexport class ScopedModelsSelectorComponent extends Container implements Focusable {\n\tprivate modelsById: Map<string, Model<any>> = new Map();\n\tprivate allIds: string[] = [];\n\tprivate enabledIds: EnabledIds = null;\n\tprivate filteredItems: ModelItem[] = [];\n\tprivate selectedIndex = 0;\n\tprivate searchInput: Input;\n\n\t// Focusable implementation - propagate to searchInput for IME cursor positioning\n\tprivate _focused = false;\n\tget focused(): boolean {\n\t\treturn this._focused;\n\t}\n\tset focused(value: boolean) {\n\t\tthis._focused = value;\n\t\tthis.searchInput.focused = value;\n\t}\n\tprivate listContainer: Container;\n\tprivate footerText: Text;\n\tprivate callbacks: ModelsCallbacks;\n\tprivate maxVisible = 8;\n\tprivate isDirty = false;\n\tprivate refreshStatusText?: Text;\n\n\tconstructor(config: ModelsConfig, callbacks: ModelsCallbacks) {\n\t\tsuper();\n\t\tthis.callbacks = callbacks;\n\n\t\tfor (const model of config.allModels) {\n\t\t\tconst fullId = `${model.provider}/${model.id}`;\n\t\t\tthis.modelsById.set(fullId, model);\n\t\t\tthis.allIds.push(fullId);\n\t\t}\n\n\t\tthis.enabledIds = config.enabledModelIds === null ? null : [...config.enabledModelIds];\n\t\tthis.filteredItems = this.buildItems();\n\n\t\t// Header\n\t\tthis.addChild(new DynamicBorder());\n\t\tthis.addChild(new Spacer(1));\n\t\tthis.addChild(new Text(theme.bold(theme.fg(\"accent\", \"Model configuration\")), 0, 0));\n\t\tthis.addChild(\n\t\t\tnew Text(theme.fg(\"muted\", `Session-only. ${keyText(\"app.models.save\")} to save to settings.`), 0, 0),\n\t\t);\n\t\tthis.addChild(new Spacer(1));\n\n\t\t// Search input\n\t\tthis.searchInput = new PromptInput();\n\t\tthis.addChild(this.searchInput);\n\t\tthis.addChild(new Spacer(1));\n\n\t\t// List container\n\t\tthis.listContainer = new Container();\n\t\tthis.addChild(this.listContainer);\n\n\t\t// Footer hint\n\t\tthis.addChild(new Spacer(1));\n\t\tif (config.refreshStatus) {\n\t\t\tthis.refreshStatusText = new Text(theme.fg(\"muted\", ` ${config.refreshStatus}`), 0, 0);\n\t\t\tthis.addChild(this.refreshStatusText);\n\t\t}\n\t\tthis.footerText = new Text(this.getFooterText(), 0, 0);\n\t\tthis.addChild(this.footerText);\n\n\t\tthis.addChild(new DynamicBorder());\n\t\tthis.updateList();\n\t}\n\n\tupdateModels(models: readonly Model<any>[], enabledModelIds?: string[] | null): void {\n\t\tconst selectedId = this.filteredItems[this.selectedIndex]?.fullId;\n\t\tif (enabledModelIds !== undefined) this.enabledIds = enabledModelIds === null ? null : [...enabledModelIds];\n\t\tthis.modelsById.clear();\n\t\tthis.allIds = [];\n\t\tfor (const model of models) {\n\t\t\tconst fullId = `${model.provider}/${model.id}`;\n\t\t\tthis.modelsById.set(fullId, model);\n\t\t\tthis.allIds.push(fullId);\n\t\t}\n\t\tthis.refresh();\n\t\tconst refreshedIndex = selectedId ? this.filteredItems.findIndex((item) => item.fullId === selectedId) : -1;\n\t\tif (refreshedIndex >= 0) {\n\t\t\tthis.selectedIndex = refreshedIndex;\n\t\t\tthis.updateList();\n\t\t}\n\t}\n\n\tsetRefreshStatus(message: string, kind: \"muted\" | \"success\" | \"warning\"): void {\n\t\tthis.refreshStatusText?.setText(theme.fg(kind, ` ${message}`));\n\t}\n\n\tprivate buildItems(): ModelItem[] {\n\t\treturn getSortedIds(this.enabledIds, this.allIds).map((id) => ({\n\t\t\tfullId: id,\n\t\t\tmodel: this.modelsById.get(id),\n\t\t\tenabled: isEnabled(this.enabledIds, id),\n\t\t}));\n\t}\n\n\tprivate getFooterText(): string {\n\t\tconst enabledCount = this.enabledIds?.filter((id) => this.modelsById.has(id)).length ?? this.allIds.length;\n\t\tconst unavailableCount = this.enabledIds?.filter((id) => !this.modelsById.has(id)).length ?? 0;\n\t\tconst allEnabled = this.enabledIds === null;\n\t\tconst countText = allEnabled\n\t\t\t? \"all enabled\"\n\t\t\t: `${enabledCount}/${this.allIds.length} enabled${unavailableCount ? ` · ${unavailableCount} unavailable` : \"\"}`;\n\t\tconst parts = [\n\t\t\t`${keyText(\"tui.select.confirm\")} toggle`,\n\t\t\t`${keyText(\"app.models.enableAll\")} all`,\n\t\t\t`${keyText(\"app.models.clearAll\")} clear`,\n\t\t\t`${keyText(\"app.models.toggleProvider\")} provider`,\n\t\t\t`${keyText(\"app.models.reorderUp\")}/${keyText(\"app.models.reorderDown\")} reorder`,\n\t\t\t`${keyText(\"app.models.save\")} save`,\n\t\t\tcountText,\n\t\t];\n\t\treturn this.isDirty\n\t\t\t? theme.fg(\"dim\", ` ${parts.join(\" · \")} `) + theme.fg(\"warning\", \"(unsaved)\")\n\t\t\t: theme.fg(\"dim\", ` ${parts.join(\" · \")}`);\n\t}\n\n\tprivate refresh(): void {\n\t\tconst query = this.searchInput.getValue();\n\t\tconst items = this.buildItems();\n\t\tthis.filteredItems = query\n\t\t\t? fuzzyFilter(items, query, (item) =>\n\t\t\t\t\titem.model\n\t\t\t\t\t\t? getModelSearchText({ id: item.model.id, provider: item.model.provider, name: item.model.name })\n\t\t\t\t\t\t: item.fullId,\n\t\t\t\t)\n\t\t\t: items;\n\t\tthis.selectedIndex = Math.min(this.selectedIndex, Math.max(0, this.filteredItems.length - 1));\n\t\tthis.updateList();\n\t\tthis.footerText.setText(this.getFooterText());\n\t}\n\n\tprivate notifyChange(): void {\n\t\tthis.callbacks.onChange(this.enabledIds === null ? null : [...this.enabledIds]);\n\t}\n\n\tprivate updateList(): void {\n\t\tthis.listContainer.clear();\n\n\t\tif (this.filteredItems.length === 0) {\n\t\t\tthis.listContainer.addChild(new Text(theme.fg(\"muted\", \" No matching models\"), 0, 0));\n\t\t\treturn;\n\t\t}\n\n\t\tconst startIndex = Math.max(\n\t\t\t0,\n\t\t\tMath.min(this.selectedIndex - Math.floor(this.maxVisible / 2), this.filteredItems.length - this.maxVisible),\n\t\t);\n\t\tconst endIndex = Math.min(startIndex + this.maxVisible, this.filteredItems.length);\n\t\tconst allEnabled = this.enabledIds === null;\n\n\t\tfor (let i = startIndex; i < endIndex; i++) {\n\t\t\tconst item = this.filteredItems[i]!;\n\t\t\tconst isSelected = i === this.selectedIndex;\n\t\t\tconst prefix = isSelected ? theme.fg(\"accent\", \"→ \") : \" \";\n\t\t\tconst id = item.model?.id ?? item.fullId;\n\t\t\tconst modelText = isSelected ? theme.fg(\"accent\", id) : id;\n\t\t\tconst providerBadge = theme.fg(\"muted\", item.model ? ` [${item.model.provider}]` : \" [unavailable]\");\n\t\t\tconst status = item.model\n\t\t\t\t? allEnabled\n\t\t\t\t\t? \"\"\n\t\t\t\t\t: item.enabled\n\t\t\t\t\t\t? theme.fg(\"success\", \" ✓\")\n\t\t\t\t\t\t: theme.fg(\"dim\", \" ✗\")\n\t\t\t\t: theme.fg(\"dim\", \" ✗\");\n\t\t\tthis.listContainer.addChild(new Text(`${prefix}${modelText}${providerBadge}${status}`, 0, 0));\n\t\t}\n\n\t\t// Add scroll indicator if needed\n\t\tif (startIndex > 0 || endIndex < this.filteredItems.length) {\n\t\t\tthis.listContainer.addChild(\n\t\t\t\tnew Text(theme.fg(\"muted\", ` (${this.selectedIndex + 1}/${this.filteredItems.length})`), 0, 0),\n\t\t\t);\n\t\t}\n\n\t\tif (this.filteredItems.length > 0) {\n\t\t\tconst selected = this.filteredItems[this.selectedIndex];\n\t\t\tthis.listContainer.addChild(new Spacer(1));\n\t\t\tthis.listContainer.addChild(\n\t\t\t\tnew Text(\n\t\t\t\t\ttheme.fg(\"muted\", ` ${selected.model ? `Model Name: ${selected.model.name}` : \"Model unavailable\"}`),\n\t\t\t\t\t0,\n\t\t\t\t\t0,\n\t\t\t\t),\n\t\t\t);\n\t\t}\n\t}\n\n\thandleInput(data: string): void {\n\t\tconst kb = getKeybindings();\n\n\t\t// Navigation\n\t\tif (kb.matches(data, \"tui.select.up\")) {\n\t\t\tif (this.filteredItems.length === 0) return;\n\t\t\tthis.selectedIndex = this.selectedIndex === 0 ? this.filteredItems.length - 1 : this.selectedIndex - 1;\n\t\t\tthis.updateList();\n\t\t\treturn;\n\t\t}\n\t\tif (kb.matches(data, \"tui.select.down\")) {\n\t\t\tif (this.filteredItems.length === 0) return;\n\t\t\tthis.selectedIndex = this.selectedIndex === this.filteredItems.length - 1 ? 0 : this.selectedIndex + 1;\n\t\t\tthis.updateList();\n\t\t\treturn;\n\t\t}\n\n\t\t// Reorder enabled models\n\t\tconst reorderUp = kb.matches(data, \"app.models.reorderUp\");\n\t\tconst reorderDown = kb.matches(data, \"app.models.reorderDown\");\n\t\tif (reorderUp || reorderDown) {\n\t\t\tif (this.enabledIds === null) return;\n\t\t\tconst item = this.filteredItems[this.selectedIndex];\n\t\t\tif (item && isEnabled(this.enabledIds, item.fullId)) {\n\t\t\t\tconst delta = reorderUp ? -1 : 1;\n\t\t\t\tconst currentIndex = this.enabledIds.indexOf(item.fullId);\n\t\t\t\tconst newIndex = currentIndex + delta;\n\t\t\t\t// Only move if within bounds\n\t\t\t\tif (newIndex >= 0 && newIndex < this.enabledIds.length) {\n\t\t\t\t\tthis.enabledIds = move(this.enabledIds, item.fullId, delta);\n\t\t\t\t\tthis.isDirty = true;\n\t\t\t\t\tthis.selectedIndex += delta;\n\t\t\t\t\tthis.refresh();\n\t\t\t\t\tthis.notifyChange();\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\t// Toggle on Enter\n\t\tif (kb.matches(data, \"tui.select.confirm\")) {\n\t\t\tconst item = this.filteredItems[this.selectedIndex];\n\t\t\tif (item) {\n\t\t\t\tthis.enabledIds = toggle(this.enabledIds, item.fullId);\n\t\t\t\tthis.isDirty = true;\n\t\t\t\tthis.refresh();\n\t\t\t\tthis.notifyChange();\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\t// Enable all (filtered if search active, otherwise all)\n\t\tif (kb.matches(data, \"app.models.enableAll\")) {\n\t\t\tconst targetIds = this.searchInput.getValue() ? this.filteredItems.map((i) => i.fullId) : undefined;\n\t\t\tthis.enabledIds = enableAll(this.enabledIds, this.allIds, targetIds);\n\t\t\tthis.isDirty = true;\n\t\t\tthis.refresh();\n\t\t\tthis.notifyChange();\n\t\t\treturn;\n\t\t}\n\n\t\t// Clear all (filtered if search active, otherwise all)\n\t\tif (kb.matches(data, \"app.models.clearAll\")) {\n\t\t\tconst targetIds = this.searchInput.getValue() ? this.filteredItems.map((i) => i.fullId) : undefined;\n\t\t\tthis.enabledIds = clearAll(this.enabledIds, this.allIds, targetIds);\n\t\t\tthis.isDirty = true;\n\t\t\tthis.refresh();\n\t\t\tthis.notifyChange();\n\t\t\treturn;\n\t\t}\n\n\t\t// Toggle provider of current item\n\t\tif (kb.matches(data, \"app.models.toggleProvider\")) {\n\t\t\tconst item = this.filteredItems[this.selectedIndex];\n\t\t\tif (item?.model) {\n\t\t\t\tconst provider = item.model.provider;\n\t\t\t\tconst providerIds = this.allIds.filter((id) => this.modelsById.get(id)!.provider === provider);\n\t\t\t\tconst allEnabled = providerIds.every((id) => isEnabled(this.enabledIds, id));\n\t\t\t\tthis.enabledIds = allEnabled\n\t\t\t\t\t? clearAll(this.enabledIds, this.allIds, providerIds)\n\t\t\t\t\t: enableAll(this.enabledIds, this.allIds, providerIds);\n\t\t\t\tthis.isDirty = true;\n\t\t\t\tthis.refresh();\n\t\t\t\tthis.notifyChange();\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\t// Save/persist to settings\n\t\tif (kb.matches(data, \"app.models.save\")) {\n\t\t\tthis.callbacks.onPersist(this.enabledIds === null ? null : [...this.enabledIds]);\n\t\t\tthis.isDirty = false;\n\t\t\tthis.footerText.setText(this.getFooterText());\n\t\t\treturn;\n\t\t}\n\n\t\t// Ctrl+C - clear search or cancel if empty\n\t\tif (matchesKey(data, Key.ctrl(\"c\"))) {\n\t\t\tif (this.searchInput.getValue()) {\n\t\t\t\tthis.searchInput.setValue(\"\");\n\t\t\t\tthis.refresh();\n\t\t\t} else {\n\t\t\t\tthis.callbacks.onCancel();\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\t// Escape - cancel\n\t\tif (matchesKey(data, Key.escape)) {\n\t\t\tthis.callbacks.onCancel();\n\t\t\treturn;\n\t\t}\n\n\t\t// Pass everything else to search input\n\t\tthis.searchInput.handleInput(data);\n\t\tthis.refresh();\n\t}\n\n\tgetSearchInput(): Input {\n\t\treturn this.searchInput;\n\t}\n}\n"]}
1
+ {"version":3,"file":"scoped-models-selector.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/scoped-models-selector.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,uBAAuB,CAAC;AACnD,OAAO,EACN,SAAS,EACT,KAAK,SAAS,EAGd,KAAK,KAAK,EAKV,MAAM,wBAAwB,CAAC;AAoEhC,MAAM,WAAW,YAAY;IAC5B,SAAS,EAAE,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;IACxB,eAAe,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;IACjC,aAAa,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,eAAe;IAC/B,wFAAwF;IACxF,QAAQ,EAAE,CAAC,eAAe,EAAE,MAAM,EAAE,GAAG,IAAI,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACrE,sEAAsE;IACtE,SAAS,EAAE,CAAC,eAAe,EAAE,MAAM,EAAE,GAAG,IAAI,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACtE,QAAQ,EAAE,MAAM,IAAI,CAAC;CACrB;AAED;;;GAGG;AACH,qBAAa,6BAA8B,SAAQ,SAAU,YAAW,SAAS;IAChF,OAAO,CAAC,UAAU,CAAsC;IACxD,OAAO,CAAC,MAAM,CAAgB;IAC9B,OAAO,CAAC,UAAU,CAAoB;IACtC,OAAO,CAAC,aAAa,CAAmB;IACxC,OAAO,CAAC,aAAa,CAAK;IAC1B,OAAO,CAAC,WAAW,CAAQ;IAG3B,OAAO,CAAC,QAAQ,CAAS;IACzB,IAAI,OAAO,IAAI,OAAO,CAErB;IACD,IAAI,OAAO,CAAC,KAAK,EAAE,OAAO,EAGzB;IACD,OAAO,CAAC,aAAa,CAAY;IACjC,OAAO,CAAC,UAAU,CAAO;IACzB,OAAO,CAAC,SAAS,CAAkB;IACnC,OAAO,CAAC,UAAU,CAAK;IACvB,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,iBAAiB,CAAC,CAAO;IAEjC,YAAY,MAAM,EAAE,YAAY,EAAE,SAAS,EAAE,eAAe,EA0C3D;IAED,YAAY,CAAC,MAAM,EAAE,SAAS,KAAK,CAAC,GAAG,CAAC,EAAE,EAAE,eAAe,CAAC,EAAE,MAAM,EAAE,GAAG,IAAI,GAAG,IAAI,CAgBnF;IAED,gBAAgB,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,SAAS,GAAG,SAAS,GAAG,IAAI,CAE7E;IAED,OAAO,CAAC,UAAU;IAQlB,OAAO,CAAC,aAAa;IAqBrB,OAAO,CAAC,OAAO;IAef,OAAO,CAAC,YAAY;IAIpB,OAAO,CAAC,UAAU;IA6ClB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAoH9B;IAED,cAAc,IAAI,KAAK,CAEtB;CACD","sourcesContent":["import type { Model } from \"@earendil-works/pi-ai\";\nimport {\n\tContainer,\n\ttype Focusable,\n\tfuzzyFilter,\n\tgetKeybindings,\n\ttype Input,\n\tKey,\n\tmatchesKey,\n\tSpacer,\n\tText,\n} from \"@earendil-works/pi-tui\";\nimport { getModelSearchText } from \"../model-search.ts\";\nimport { theme } from \"../theme/theme.ts\";\nimport { DynamicBorder } from \"./dynamic-border.ts\";\nimport { keyText } from \"./keybinding-hints.ts\";\nimport { PromptInput } from \"./prompt-input.ts\";\n\n// EnabledIds: null = all enabled (no filter), string[] = explicit ordered list\ntype EnabledIds = string[] | null;\n\nfunction isEnabled(enabledIds: EnabledIds, id: string): boolean {\n\treturn enabledIds === null || enabledIds.includes(id);\n}\n\n/** Collapse an explicit list back to null (= all enabled) when it covers every available model. */\nfunction normalizeEnabled(result: string[], allIds: string[]): EnabledIds {\n\treturn result.length === allIds.length && result.every((id) => allIds.includes(id)) ? null : result;\n}\n\nfunction toggle(enabledIds: EnabledIds, allIds: string[], id: string): EnabledIds {\n\tif (enabledIds === null) return allIds.filter((modelId) => modelId !== id);\n\tconst index = enabledIds.indexOf(id);\n\tif (index >= 0) return [...enabledIds.slice(0, index), ...enabledIds.slice(index + 1)];\n\treturn normalizeEnabled([...enabledIds, id], allIds);\n}\n\nfunction enableAll(enabledIds: EnabledIds, allIds: string[], targetIds?: string[]): EnabledIds {\n\tif (enabledIds === null) return null; // Already all enabled\n\tconst targets = targetIds ?? allIds;\n\tconst result = [...enabledIds];\n\tfor (const id of targets) {\n\t\tif (!result.includes(id)) result.push(id);\n\t}\n\treturn normalizeEnabled(result, allIds);\n}\n\nfunction clearAll(enabledIds: EnabledIds, allIds: string[], targetIds?: string[]): EnabledIds {\n\tif (enabledIds === null) {\n\t\treturn targetIds ? allIds.filter((id) => !targetIds.includes(id)) : [];\n\t}\n\tconst targets = new Set(targetIds ?? enabledIds);\n\treturn enabledIds.filter((id) => !targets.has(id));\n}\n\nfunction move(enabledIds: EnabledIds, id: string, delta: number): EnabledIds {\n\tif (enabledIds === null) return null;\n\tconst list = [...enabledIds];\n\tconst index = list.indexOf(id);\n\tif (index < 0) return list;\n\tconst newIndex = index + delta;\n\tif (newIndex < 0 || newIndex >= list.length) return list;\n\tconst result = [...list];\n\t[result[index], result[newIndex]] = [result[newIndex], result[index]];\n\treturn result;\n}\n\nfunction getSortedIds(enabledIds: EnabledIds, allIds: string[]): string[] {\n\tif (enabledIds === null) return allIds;\n\tconst enabledSet = new Set(enabledIds);\n\treturn [...enabledIds, ...allIds.filter((id) => !enabledSet.has(id))];\n}\n\ninterface ModelItem {\n\tfullId: string;\n\tmodel: Model<any> | undefined;\n\tenabled: boolean;\n}\n\nexport interface ModelsConfig {\n\tallModels: Model<any>[];\n\tenabledModelIds: string[] | null;\n\trefreshStatus?: string;\n}\n\nexport interface ModelsCallbacks {\n\t/** Called whenever the enabled model set or order changes (session-only, no persist) */\n\tonChange: (enabledModelIds: string[] | null) => void | Promise<void>;\n\t/** Called when user wants to persist current selection to settings */\n\tonPersist: (enabledModelIds: string[] | null) => void | Promise<void>;\n\tonCancel: () => void;\n}\n\n/**\n * Component for enabling/disabling models for Ctrl+P cycling.\n * Changes are session-only until explicitly persisted with Ctrl+S.\n */\nexport class ScopedModelsSelectorComponent extends Container implements Focusable {\n\tprivate modelsById: Map<string, Model<any>> = new Map();\n\tprivate allIds: string[] = [];\n\tprivate enabledIds: EnabledIds = null;\n\tprivate filteredItems: ModelItem[] = [];\n\tprivate selectedIndex = 0;\n\tprivate searchInput: Input;\n\n\t// Focusable implementation - propagate to searchInput for IME cursor positioning\n\tprivate _focused = false;\n\tget focused(): boolean {\n\t\treturn this._focused;\n\t}\n\tset focused(value: boolean) {\n\t\tthis._focused = value;\n\t\tthis.searchInput.focused = value;\n\t}\n\tprivate listContainer: Container;\n\tprivate footerText: Text;\n\tprivate callbacks: ModelsCallbacks;\n\tprivate maxVisible = 8;\n\tprivate isDirty = false;\n\tprivate refreshStatusText?: Text;\n\n\tconstructor(config: ModelsConfig, callbacks: ModelsCallbacks) {\n\t\tsuper();\n\t\tthis.callbacks = callbacks;\n\n\t\tfor (const model of config.allModels) {\n\t\t\tconst fullId = `${model.provider}/${model.id}`;\n\t\t\tthis.modelsById.set(fullId, model);\n\t\t\tthis.allIds.push(fullId);\n\t\t}\n\n\t\tthis.enabledIds = config.enabledModelIds === null ? null : [...config.enabledModelIds];\n\t\tthis.filteredItems = this.buildItems();\n\n\t\t// Header\n\t\tthis.addChild(new DynamicBorder());\n\t\tthis.addChild(new Spacer(1));\n\t\tthis.addChild(new Text(theme.bold(theme.fg(\"accent\", \"Model configuration\")), 0, 0));\n\t\tthis.addChild(\n\t\t\tnew Text(theme.fg(\"muted\", `Session-only. ${keyText(\"app.models.save\")} to save to settings.`), 0, 0),\n\t\t);\n\t\tthis.addChild(new Spacer(1));\n\n\t\t// Search input\n\t\tthis.searchInput = new PromptInput();\n\t\tthis.addChild(this.searchInput);\n\t\tthis.addChild(new Spacer(1));\n\n\t\t// List container\n\t\tthis.listContainer = new Container();\n\t\tthis.addChild(this.listContainer);\n\n\t\t// Footer hint\n\t\tthis.addChild(new Spacer(1));\n\t\tif (config.refreshStatus) {\n\t\t\tthis.refreshStatusText = new Text(theme.fg(\"muted\", ` ${config.refreshStatus}`), 0, 0);\n\t\t\tthis.addChild(this.refreshStatusText);\n\t\t}\n\t\tthis.footerText = new Text(this.getFooterText(), 0, 0);\n\t\tthis.addChild(this.footerText);\n\n\t\tthis.addChild(new DynamicBorder());\n\t\tthis.updateList();\n\t}\n\n\tupdateModels(models: readonly Model<any>[], enabledModelIds?: string[] | null): void {\n\t\tconst selectedId = this.filteredItems[this.selectedIndex]?.fullId;\n\t\tif (enabledModelIds !== undefined) this.enabledIds = enabledModelIds === null ? null : [...enabledModelIds];\n\t\tthis.modelsById.clear();\n\t\tthis.allIds = [];\n\t\tfor (const model of models) {\n\t\t\tconst fullId = `${model.provider}/${model.id}`;\n\t\t\tthis.modelsById.set(fullId, model);\n\t\t\tthis.allIds.push(fullId);\n\t\t}\n\t\tthis.refresh();\n\t\tconst refreshedIndex = selectedId ? this.filteredItems.findIndex((item) => item.fullId === selectedId) : -1;\n\t\tif (refreshedIndex >= 0) {\n\t\t\tthis.selectedIndex = refreshedIndex;\n\t\t\tthis.updateList();\n\t\t}\n\t}\n\n\tsetRefreshStatus(message: string, kind: \"muted\" | \"success\" | \"warning\"): void {\n\t\tthis.refreshStatusText?.setText(theme.fg(kind, ` ${message}`));\n\t}\n\n\tprivate buildItems(): ModelItem[] {\n\t\treturn getSortedIds(this.enabledIds, this.allIds).map((id) => ({\n\t\t\tfullId: id,\n\t\t\tmodel: this.modelsById.get(id),\n\t\t\tenabled: isEnabled(this.enabledIds, id),\n\t\t}));\n\t}\n\n\tprivate getFooterText(): string {\n\t\tconst enabledCount = this.enabledIds?.filter((id) => this.modelsById.has(id)).length ?? this.allIds.length;\n\t\tconst unavailableCount = this.enabledIds?.filter((id) => !this.modelsById.has(id)).length ?? 0;\n\t\tconst allEnabled = this.enabledIds === null;\n\t\tconst countText = allEnabled\n\t\t\t? \"all enabled\"\n\t\t\t: `${enabledCount}/${this.allIds.length} enabled${unavailableCount ? ` · ${unavailableCount} unavailable` : \"\"}`;\n\t\tconst parts = [\n\t\t\t`${keyText(\"tui.select.confirm\")} toggle`,\n\t\t\t`${keyText(\"app.models.enableAll\")} all`,\n\t\t\t`${keyText(\"app.models.clearAll\")} clear`,\n\t\t\t`${keyText(\"app.models.toggleProvider\")} provider`,\n\t\t\t`${keyText(\"app.models.reorderUp\")}/${keyText(\"app.models.reorderDown\")} reorder`,\n\t\t\t`${keyText(\"app.models.save\")} save`,\n\t\t\tcountText,\n\t\t];\n\t\treturn this.isDirty\n\t\t\t? theme.fg(\"dim\", ` ${parts.join(\" · \")} `) + theme.fg(\"warning\", \"(unsaved)\")\n\t\t\t: theme.fg(\"dim\", ` ${parts.join(\" · \")}`);\n\t}\n\n\tprivate refresh(): void {\n\t\tconst query = this.searchInput.getValue();\n\t\tconst items = this.buildItems();\n\t\tthis.filteredItems = query\n\t\t\t? fuzzyFilter(items, query, (item) =>\n\t\t\t\t\titem.model\n\t\t\t\t\t\t? getModelSearchText({ id: item.model.id, provider: item.model.provider, name: item.model.name })\n\t\t\t\t\t\t: item.fullId,\n\t\t\t\t)\n\t\t\t: items;\n\t\tthis.selectedIndex = Math.min(this.selectedIndex, Math.max(0, this.filteredItems.length - 1));\n\t\tthis.updateList();\n\t\tthis.footerText.setText(this.getFooterText());\n\t}\n\n\tprivate notifyChange(): void {\n\t\tthis.callbacks.onChange(this.enabledIds === null ? null : [...this.enabledIds]);\n\t}\n\n\tprivate updateList(): void {\n\t\tthis.listContainer.clear();\n\n\t\tif (this.filteredItems.length === 0) {\n\t\t\tthis.listContainer.addChild(new Text(theme.fg(\"muted\", \" No matching models\"), 0, 0));\n\t\t\treturn;\n\t\t}\n\n\t\tconst startIndex = Math.max(\n\t\t\t0,\n\t\t\tMath.min(this.selectedIndex - Math.floor(this.maxVisible / 2), this.filteredItems.length - this.maxVisible),\n\t\t);\n\t\tconst endIndex = Math.min(startIndex + this.maxVisible, this.filteredItems.length);\n\t\tfor (let i = startIndex; i < endIndex; i++) {\n\t\t\tconst item = this.filteredItems[i]!;\n\t\t\tconst isSelected = i === this.selectedIndex;\n\t\t\tconst prefix = isSelected ? theme.fg(\"accent\", \"→ \") : \" \";\n\t\t\tconst id = item.model?.id ?? item.fullId;\n\t\t\tconst styledId = item.model ? id : theme.strikethrough(id);\n\t\t\tconst modelText = isSelected ? theme.fg(\"accent\", styledId) : styledId;\n\t\t\tconst providerBadge = theme.fg(\"muted\", item.model ? ` [${item.model.provider}]` : \" [unavailable]\");\n\t\t\tconst status = item.model && item.enabled ? theme.fg(\"accent\", \"✓ \") : \" \";\n\t\t\tthis.listContainer.addChild(new Text(`${prefix}${status}${modelText}${providerBadge}`, 0, 0));\n\t\t}\n\n\t\t// Add scroll indicator if needed\n\t\tif (startIndex > 0 || endIndex < this.filteredItems.length) {\n\t\t\tthis.listContainer.addChild(\n\t\t\t\tnew Text(theme.fg(\"muted\", ` (${this.selectedIndex + 1}/${this.filteredItems.length})`), 0, 0),\n\t\t\t);\n\t\t}\n\n\t\tif (this.filteredItems.length > 0) {\n\t\t\tconst selected = this.filteredItems[this.selectedIndex];\n\t\t\tthis.listContainer.addChild(new Spacer(1));\n\t\t\tthis.listContainer.addChild(\n\t\t\t\tnew Text(\n\t\t\t\t\ttheme.fg(\"muted\", ` ${selected.model ? `Model Name: ${selected.model.name}` : \"Model unavailable\"}`),\n\t\t\t\t\t0,\n\t\t\t\t\t0,\n\t\t\t\t),\n\t\t\t);\n\t\t}\n\t}\n\n\thandleInput(data: string): void {\n\t\tconst kb = getKeybindings();\n\n\t\t// Navigation\n\t\tif (kb.matches(data, \"tui.select.up\")) {\n\t\t\tif (this.filteredItems.length === 0) return;\n\t\t\tthis.selectedIndex = this.selectedIndex === 0 ? this.filteredItems.length - 1 : this.selectedIndex - 1;\n\t\t\tthis.updateList();\n\t\t\treturn;\n\t\t}\n\t\tif (kb.matches(data, \"tui.select.down\")) {\n\t\t\tif (this.filteredItems.length === 0) return;\n\t\t\tthis.selectedIndex = this.selectedIndex === this.filteredItems.length - 1 ? 0 : this.selectedIndex + 1;\n\t\t\tthis.updateList();\n\t\t\treturn;\n\t\t}\n\n\t\t// Reorder enabled models\n\t\tconst reorderUp = kb.matches(data, \"app.models.reorderUp\");\n\t\tconst reorderDown = kb.matches(data, \"app.models.reorderDown\");\n\t\tif (reorderUp || reorderDown) {\n\t\t\tif (this.enabledIds === null) return;\n\t\t\tconst item = this.filteredItems[this.selectedIndex];\n\t\t\tif (item && isEnabled(this.enabledIds, item.fullId)) {\n\t\t\t\tconst delta = reorderUp ? -1 : 1;\n\t\t\t\tconst currentIndex = this.enabledIds.indexOf(item.fullId);\n\t\t\t\tconst newIndex = currentIndex + delta;\n\t\t\t\t// Only move if within bounds\n\t\t\t\tif (newIndex >= 0 && newIndex < this.enabledIds.length) {\n\t\t\t\t\tthis.enabledIds = move(this.enabledIds, item.fullId, delta);\n\t\t\t\t\tthis.isDirty = true;\n\t\t\t\t\tthis.selectedIndex += delta;\n\t\t\t\t\tthis.refresh();\n\t\t\t\t\tthis.notifyChange();\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\t// Toggle on Enter\n\t\tif (kb.matches(data, \"tui.select.confirm\")) {\n\t\t\tconst item = this.filteredItems[this.selectedIndex];\n\t\t\tif (item) {\n\t\t\t\tthis.enabledIds = toggle(this.enabledIds, this.allIds, item.fullId);\n\t\t\t\tthis.isDirty = true;\n\t\t\t\tthis.refresh();\n\t\t\t\tthis.notifyChange();\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\t// Enable all (filtered if search active, otherwise all)\n\t\tif (kb.matches(data, \"app.models.enableAll\")) {\n\t\t\tconst targetIds = this.searchInput.getValue() ? this.filteredItems.map((i) => i.fullId) : undefined;\n\t\t\tthis.enabledIds = enableAll(this.enabledIds, this.allIds, targetIds);\n\t\t\tthis.isDirty = true;\n\t\t\tthis.refresh();\n\t\t\tthis.notifyChange();\n\t\t\treturn;\n\t\t}\n\n\t\t// Clear all (filtered if search active, otherwise all)\n\t\tif (kb.matches(data, \"app.models.clearAll\")) {\n\t\t\tconst targetIds = this.searchInput.getValue() ? this.filteredItems.map((i) => i.fullId) : undefined;\n\t\t\tthis.enabledIds = clearAll(this.enabledIds, this.allIds, targetIds);\n\t\t\tthis.isDirty = true;\n\t\t\tthis.refresh();\n\t\t\tthis.notifyChange();\n\t\t\treturn;\n\t\t}\n\n\t\t// Toggle provider of current item\n\t\tif (kb.matches(data, \"app.models.toggleProvider\")) {\n\t\t\tconst item = this.filteredItems[this.selectedIndex];\n\t\t\tif (item?.model) {\n\t\t\t\tconst provider = item.model.provider;\n\t\t\t\tconst providerIds = this.allIds.filter((id) => this.modelsById.get(id)!.provider === provider);\n\t\t\t\tconst allEnabled = providerIds.every((id) => isEnabled(this.enabledIds, id));\n\t\t\t\tthis.enabledIds = allEnabled\n\t\t\t\t\t? clearAll(this.enabledIds, this.allIds, providerIds)\n\t\t\t\t\t: enableAll(this.enabledIds, this.allIds, providerIds);\n\t\t\t\tthis.isDirty = true;\n\t\t\t\tthis.refresh();\n\t\t\t\tthis.notifyChange();\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\t// Save/persist to settings\n\t\tif (kb.matches(data, \"app.models.save\")) {\n\t\t\tthis.callbacks.onPersist(this.enabledIds === null ? null : [...this.enabledIds]);\n\t\t\tthis.isDirty = false;\n\t\t\tthis.footerText.setText(this.getFooterText());\n\t\t\treturn;\n\t\t}\n\n\t\t// Ctrl+C - clear search or cancel if empty\n\t\tif (matchesKey(data, Key.ctrl(\"c\"))) {\n\t\t\tif (this.searchInput.getValue()) {\n\t\t\t\tthis.searchInput.setValue(\"\");\n\t\t\t\tthis.refresh();\n\t\t\t} else {\n\t\t\t\tthis.callbacks.onCancel();\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\t// Escape - cancel\n\t\tif (matchesKey(data, Key.escape)) {\n\t\t\tthis.callbacks.onCancel();\n\t\t\treturn;\n\t\t}\n\n\t\t// Pass everything else to search input\n\t\tthis.searchInput.handleInput(data);\n\t\tthis.refresh();\n\t}\n\n\tgetSearchInput(): Input {\n\t\treturn this.searchInput;\n\t}\n}\n"]}