@ponythewhite/base-context 1.0.0 → 1.0.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 (285) hide show
  1. package/CHANGELOG.md +33 -0
  2. package/NOTICE +0 -5
  3. package/README.md +44 -27
  4. package/dist/NOTICE +0 -5
  5. package/dist/base-context-runtime/pyproject.toml +1 -1
  6. package/dist/base-context-runtime/src/rlm/__init__.py +7 -5
  7. package/dist/base-context-runtime/test/test_subagent_registry.py +28 -1
  8. package/dist/base-context-runtime/uv.lock +1 -1
  9. package/dist/build-info.json +1 -1
  10. package/dist/bundle/amazon-bedrock.js +21 -0
  11. package/dist/bundle/{anthropic-YY4S534V.js → anthropic-UV5GZDGU.js} +5 -5
  12. package/dist/bundle/{azure-openai-responses-JRP6MIM3.js → azure-openai-responses-WSNPXLYC.js} +6 -6
  13. package/dist/bundle/{bundled-modules-P5HARVZA.js → bundled-modules-TXUMJ2XQ.js} +71 -16
  14. package/dist/bundle/chunk-27QCJ2AZ.js +2578 -0
  15. package/dist/bundle/{chunk-MZHTNNVA.js → chunk-4GXPNXOJ.js} +8 -0
  16. package/dist/bundle/{chunk-SC5KG5R4.js → chunk-DGNX7HQ3.js} +1 -7
  17. package/dist/bundle/{chunk-A53O363M.js → chunk-GRJFXLBM.js} +0 -1
  18. package/dist/bundle/chunk-HKINIBP4.js +29 -0
  19. package/dist/bundle/{chunk-UDSW6FNG.js → chunk-KTNETTHI.js} +1 -1
  20. package/dist/bundle/{chunk-BYVP7LWV.js → chunk-S3INFPZZ.js} +1861 -4662
  21. package/dist/bundle/{chunk-73DCPU3D.js → chunk-V3L54UD6.js} +1019 -3427
  22. package/dist/bundle/{chunk-U7AUVG3B.js → chunk-VSIYFWYI.js} +24 -6
  23. package/dist/bundle/{chunk-IW77OXFS.js → chunk-W46VZFI6.js} +0 -2099
  24. package/dist/bundle/{cli-main-O4X7GFGY.js → cli-main-BYMSRPH6.js} +8 -6
  25. package/dist/bundle/cli.js +2 -2
  26. package/dist/bundle/{google-AUTVGDGZ.js → google-T32FMD4G.js} +6 -6
  27. package/dist/bundle/{google-vertex-Q75D7RKS.js → google-vertex-56KP6I3B.js} +3 -3
  28. package/dist/bundle/{main-XKOW2BV6.js → main-ODOYCEQC.js} +7 -5
  29. package/dist/bundle/{mistral-7ESB7QUD.js → mistral-WGBMYIVU.js} +5 -5
  30. package/dist/bundle/{openai-codex-responses-LV3QRPN4.js → openai-codex-responses-PIV2RQ3G.js} +6 -6
  31. package/dist/bundle/{openai-completions-OJECLPXJ.js → openai-completions-64C3JPR4.js} +11 -22
  32. package/dist/bundle/{openai-responses-FN6HZMBC.js → openai-responses-UI7RSTDV.js} +6 -6
  33. package/dist/cli/args.d.ts.map +1 -1
  34. package/dist/cli/args.js +108 -59
  35. package/dist/cli/args.js.map +1 -1
  36. package/dist/cli/daemon-ps.d.ts +4 -3
  37. package/dist/cli/daemon-ps.d.ts.map +1 -1
  38. package/dist/cli/daemon-ps.js +6 -1
  39. package/dist/cli/daemon-ps.js.map +1 -1
  40. package/dist/cli/product-doctor.d.ts +0 -1
  41. package/dist/cli/product-doctor.d.ts.map +1 -1
  42. package/dist/cli/product-doctor.js +6 -3
  43. package/dist/cli/product-doctor.js.map +1 -1
  44. package/dist/config.d.ts +0 -3
  45. package/dist/config.d.ts.map +1 -1
  46. package/dist/config.js +0 -8
  47. package/dist/config.js.map +1 -1
  48. package/dist/core/agent-session-config.d.ts +1 -2
  49. package/dist/core/agent-session-config.d.ts.map +1 -1
  50. package/dist/core/agent-session-config.js +0 -3
  51. package/dist/core/agent-session-config.js.map +1 -1
  52. package/dist/core/agent-session-runtime.d.ts.map +1 -1
  53. package/dist/core/agent-session-runtime.js +5 -4
  54. package/dist/core/agent-session-runtime.js.map +1 -1
  55. package/dist/core/agent-session-services.d.ts +5 -3
  56. package/dist/core/agent-session-services.d.ts.map +1 -1
  57. package/dist/core/agent-session-services.js +2 -27
  58. package/dist/core/agent-session-services.js.map +1 -1
  59. package/dist/core/agent-session.d.ts +10 -6
  60. package/dist/core/agent-session.d.ts.map +1 -1
  61. package/dist/core/agent-session.js +107 -107
  62. package/dist/core/agent-session.js.map +1 -1
  63. package/dist/core/auth-storage.d.ts +8 -35
  64. package/dist/core/auth-storage.d.ts.map +1 -1
  65. package/dist/core/auth-storage.js +33 -243
  66. package/dist/core/auth-storage.js.map +1 -1
  67. package/dist/core/cron-jobs.d.ts.map +1 -1
  68. package/dist/core/cron-jobs.js +7 -1
  69. package/dist/core/cron-jobs.js.map +1 -1
  70. package/dist/core/inference-coordinator.d.ts +6 -1
  71. package/dist/core/inference-coordinator.d.ts.map +1 -1
  72. package/dist/core/inference-coordinator.js +18 -2
  73. package/dist/core/inference-coordinator.js.map +1 -1
  74. package/dist/core/kernel/bootstrap.d.ts.map +1 -1
  75. package/dist/core/kernel/bootstrap.js +2 -2
  76. package/dist/core/kernel/bootstrap.js.map +1 -1
  77. package/dist/core/kernel/repl-manager.d.ts +2 -0
  78. package/dist/core/kernel/repl-manager.d.ts.map +1 -1
  79. package/dist/core/kernel/repl-manager.js +44 -1
  80. package/dist/core/kernel/repl-manager.js.map +1 -1
  81. package/dist/core/logging.d.ts +4 -3
  82. package/dist/core/logging.d.ts.map +1 -1
  83. package/dist/core/logging.js +6 -6
  84. package/dist/core/logging.js.map +1 -1
  85. package/dist/core/mcp/mcp-manager.d.ts.map +1 -1
  86. package/dist/core/mcp/mcp-manager.js +3 -3
  87. package/dist/core/mcp/mcp-manager.js.map +1 -1
  88. package/dist/core/model-registry.d.ts +1 -16
  89. package/dist/core/model-registry.d.ts.map +1 -1
  90. package/dist/core/model-registry.js +88 -196
  91. package/dist/core/model-registry.js.map +1 -1
  92. package/dist/core/model-resolver.d.ts +2 -19
  93. package/dist/core/model-resolver.d.ts.map +1 -1
  94. package/dist/core/model-resolver.js +36 -266
  95. package/dist/core/model-resolver.js.map +1 -1
  96. package/dist/core/provider-contracts.d.ts +5 -6
  97. package/dist/core/provider-contracts.d.ts.map +1 -1
  98. package/dist/core/provider-contracts.js +25 -36
  99. package/dist/core/provider-contracts.js.map +1 -1
  100. package/dist/core/provider-display-names.d.ts.map +1 -1
  101. package/dist/core/provider-display-names.js +0 -2
  102. package/dist/core/provider-display-names.js.map +1 -1
  103. package/dist/core/rlm-max-subagents.d.ts +32 -0
  104. package/dist/core/rlm-max-subagents.d.ts.map +1 -0
  105. package/dist/core/rlm-max-subagents.js +50 -0
  106. package/dist/core/rlm-max-subagents.js.map +1 -0
  107. package/dist/core/rlm-runtime.d.ts +3 -3
  108. package/dist/core/rlm-runtime.d.ts.map +1 -1
  109. package/dist/core/rlm-runtime.js.map +1 -1
  110. package/dist/core/sdk.d.ts +4 -3
  111. package/dist/core/sdk.d.ts.map +1 -1
  112. package/dist/core/sdk.js +27 -14
  113. package/dist/core/sdk.js.map +1 -1
  114. package/dist/core/settings-manager.d.ts +3 -16
  115. package/dist/core/settings-manager.d.ts.map +1 -1
  116. package/dist/core/settings-manager.js +12 -44
  117. package/dist/core/settings-manager.js.map +1 -1
  118. package/dist/core/slash-commands.d.ts.map +1 -1
  119. package/dist/core/slash-commands.js +6 -6
  120. package/dist/core/slash-commands.js.map +1 -1
  121. package/dist/index.d.ts +1 -1
  122. package/dist/index.d.ts.map +1 -1
  123. package/dist/index.js.map +1 -1
  124. package/dist/installer.mjs +2 -3
  125. package/dist/main.d.ts.map +1 -1
  126. package/dist/main.js +39 -34
  127. package/dist/main.js.map +1 -1
  128. package/dist/modes/acp/acp-mode.d.ts.map +1 -1
  129. package/dist/modes/acp/acp-mode.js +1 -1
  130. package/dist/modes/acp/acp-mode.js.map +1 -1
  131. package/dist/modes/agent-connection/daemon-agent-connection.d.ts +6 -2
  132. package/dist/modes/agent-connection/daemon-agent-connection.d.ts.map +1 -1
  133. package/dist/modes/agent-connection/daemon-agent-connection.js +13 -2
  134. package/dist/modes/agent-connection/daemon-agent-connection.js.map +1 -1
  135. package/dist/modes/agent-connection/in-process-agent-connection.d.ts +2 -0
  136. package/dist/modes/agent-connection/in-process-agent-connection.d.ts.map +1 -1
  137. package/dist/modes/agent-connection/in-process-agent-connection.js +6 -0
  138. package/dist/modes/agent-connection/in-process-agent-connection.js.map +1 -1
  139. package/dist/modes/agent-connection/types.d.ts +3 -0
  140. package/dist/modes/agent-connection/types.d.ts.map +1 -1
  141. package/dist/modes/agent-connection/types.js.map +1 -1
  142. package/dist/modes/agents-view/agents-view-mode.d.ts.map +1 -1
  143. package/dist/modes/agents-view/agents-view-mode.js +2 -19
  144. package/dist/modes/agents-view/agents-view-mode.js.map +1 -1
  145. package/dist/modes/daemon/daemon-mode.d.ts +5 -0
  146. package/dist/modes/daemon/daemon-mode.d.ts.map +1 -1
  147. package/dist/modes/daemon/daemon-mode.js +107 -12
  148. package/dist/modes/daemon/daemon-mode.js.map +1 -1
  149. package/dist/modes/daemon/daemon-protocol.d.ts +194 -165
  150. package/dist/modes/daemon/daemon-protocol.d.ts.map +1 -1
  151. package/dist/modes/daemon/daemon-protocol.js +16 -11
  152. package/dist/modes/daemon/daemon-protocol.js.map +1 -1
  153. package/dist/modes/daemon/daemon-rlm-capacity-coordinator.d.ts +19 -0
  154. package/dist/modes/daemon/daemon-rlm-capacity-coordinator.d.ts.map +1 -0
  155. package/dist/modes/daemon/daemon-rlm-capacity-coordinator.js +74 -0
  156. package/dist/modes/daemon/daemon-rlm-capacity-coordinator.js.map +1 -0
  157. package/dist/modes/daemon/daemon-rlm-capacity.d.ts +19 -0
  158. package/dist/modes/daemon/daemon-rlm-capacity.d.ts.map +1 -0
  159. package/dist/modes/daemon/daemon-rlm-capacity.js +85 -0
  160. package/dist/modes/daemon/daemon-rlm-capacity.js.map +1 -0
  161. package/dist/modes/daemon/daemon-session-summarizer.d.ts +2 -3
  162. package/dist/modes/daemon/daemon-session-summarizer.d.ts.map +1 -1
  163. package/dist/modes/daemon/daemon-session-summarizer.js +32 -23
  164. package/dist/modes/daemon/daemon-session-summarizer.js.map +1 -1
  165. package/dist/modes/daemon/daemon-state-root.d.ts +15 -0
  166. package/dist/modes/daemon/daemon-state-root.d.ts.map +1 -0
  167. package/dist/modes/daemon/daemon-state-root.js +40 -0
  168. package/dist/modes/daemon/daemon-state-root.js.map +1 -0
  169. package/dist/modes/daemon/daemon-supervisor-ownership.d.ts +2 -0
  170. package/dist/modes/daemon/daemon-supervisor-ownership.d.ts.map +1 -1
  171. package/dist/modes/daemon/daemon-supervisor-ownership.js +27 -0
  172. package/dist/modes/daemon/daemon-supervisor-ownership.js.map +1 -1
  173. package/dist/modes/daemon/daemon-supervisor.d.ts +10 -1
  174. package/dist/modes/daemon/daemon-supervisor.d.ts.map +1 -1
  175. package/dist/modes/daemon/daemon-supervisor.js +213 -20
  176. package/dist/modes/daemon/daemon-supervisor.js.map +1 -1
  177. package/dist/modes/daemon/daemon-worker-protocol.d.ts +28 -1
  178. package/dist/modes/daemon/daemon-worker-protocol.d.ts.map +1 -1
  179. package/dist/modes/daemon/daemon-worker-protocol.js +16 -2
  180. package/dist/modes/daemon/daemon-worker-protocol.js.map +1 -1
  181. package/dist/modes/daemon/saved-session-catalog.d.ts.map +1 -1
  182. package/dist/modes/daemon/saved-session-catalog.js +2 -0
  183. package/dist/modes/daemon/saved-session-catalog.js.map +1 -1
  184. package/dist/modes/interactive/auth-flows.d.ts +0 -6
  185. package/dist/modes/interactive/auth-flows.d.ts.map +1 -1
  186. package/dist/modes/interactive/auth-flows.js +6 -126
  187. package/dist/modes/interactive/auth-flows.js.map +1 -1
  188. package/dist/modes/interactive/components/configuration-menu.d.ts +3 -0
  189. package/dist/modes/interactive/components/configuration-menu.d.ts.map +1 -1
  190. package/dist/modes/interactive/components/configuration-menu.js +13 -2
  191. package/dist/modes/interactive/components/configuration-menu.js.map +1 -1
  192. package/dist/modes/interactive/components/footer.d.ts +1 -1
  193. package/dist/modes/interactive/components/footer.d.ts.map +1 -1
  194. package/dist/modes/interactive/components/footer.js +2 -2
  195. package/dist/modes/interactive/components/footer.js.map +1 -1
  196. package/dist/modes/interactive/components/index.d.ts +0 -1
  197. package/dist/modes/interactive/components/index.d.ts.map +1 -1
  198. package/dist/modes/interactive/components/index.js +0 -1
  199. package/dist/modes/interactive/components/index.js.map +1 -1
  200. package/dist/modes/interactive/components/login-dialog.d.ts +0 -1
  201. package/dist/modes/interactive/components/login-dialog.d.ts.map +1 -1
  202. package/dist/modes/interactive/components/login-dialog.js +3 -40
  203. package/dist/modes/interactive/components/login-dialog.js.map +1 -1
  204. package/dist/modes/interactive/components/model-selector.d.ts +2 -0
  205. package/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
  206. package/dist/modes/interactive/components/model-selector.js +13 -2
  207. package/dist/modes/interactive/components/model-selector.js.map +1 -1
  208. package/dist/modes/interactive/components/oauth-selector.d.ts.map +1 -1
  209. package/dist/modes/interactive/components/oauth-selector.js +2 -11
  210. package/dist/modes/interactive/components/oauth-selector.js.map +1 -1
  211. package/dist/modes/interactive/feature-hints.d.ts.map +1 -1
  212. package/dist/modes/interactive/feature-hints.js +0 -4
  213. package/dist/modes/interactive/feature-hints.js.map +1 -1
  214. package/dist/modes/interactive/interactive-mode.d.ts +3 -12
  215. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  216. package/dist/modes/interactive/interactive-mode.js +81 -507
  217. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  218. package/dist/modes/interactive/onboarding.d.ts +0 -3
  219. package/dist/modes/interactive/onboarding.d.ts.map +1 -1
  220. package/dist/modes/interactive/onboarding.js +0 -17
  221. package/dist/modes/interactive/onboarding.js.map +1 -1
  222. package/dist/modes/rpc/rpc-client.d.ts +2 -1
  223. package/dist/modes/rpc/rpc-client.d.ts.map +1 -1
  224. package/dist/modes/rpc/rpc-client.js +24 -11
  225. package/dist/modes/rpc/rpc-client.js.map +1 -1
  226. package/dist/node/amazon-bedrock.d.ts +2 -0
  227. package/dist/node/amazon-bedrock.d.ts.map +1 -0
  228. package/dist/node/amazon-bedrock.js +6 -0
  229. package/dist/node/amazon-bedrock.js.map +1 -0
  230. package/dist/product-identity.d.ts +0 -1
  231. package/dist/product-identity.d.ts.map +1 -1
  232. package/dist/product-identity.js +0 -1
  233. package/dist/product-identity.js.map +1 -1
  234. package/docs/development.md +1 -1
  235. package/docs/fork-philosophy.md +4 -8
  236. package/docs/installation.md +83 -62
  237. package/docs/providers.md +23 -25
  238. package/docs/quickstart.md +19 -10
  239. package/docs/rlm-runtime.md +13 -5
  240. package/docs/rpc.md +4 -4
  241. package/docs/sdk.md +20 -11
  242. package/docs/sessions.md +0 -1
  243. package/docs/settings.md +9 -42
  244. package/docs/skills.md +0 -1
  245. package/docs/upstream-0.9.5.md +171 -0
  246. package/docs/usage.md +14 -10
  247. package/package.json +4 -4
  248. package/dist/core/agent-traces.d.ts +0 -149
  249. package/dist/core/agent-traces.d.ts.map +0 -1
  250. package/dist/core/agent-traces.js +0 -904
  251. package/dist/core/agent-traces.js.map +0 -1
  252. package/dist/core/prime-inference-auth.d.ts +0 -70
  253. package/dist/core/prime-inference-auth.d.ts.map +0 -1
  254. package/dist/core/prime-inference-auth.js +0 -352
  255. package/dist/core/prime-inference-auth.js.map +0 -1
  256. package/dist/core/prime-inference-model-selection.d.ts +0 -16
  257. package/dist/core/prime-inference-model-selection.d.ts.map +0 -1
  258. package/dist/core/prime-inference-model-selection.js +0 -16
  259. package/dist/core/prime-inference-model-selection.js.map +0 -1
  260. package/dist/core/prime-inference-models.d.ts +0 -6
  261. package/dist/core/prime-inference-models.d.ts.map +0 -1
  262. package/dist/core/prime-inference-models.js +0 -62
  263. package/dist/core/prime-inference-models.js.map +0 -1
  264. package/dist/core/telemetry.d.ts +0 -95
  265. package/dist/core/telemetry.d.ts.map +0 -1
  266. package/dist/core/telemetry.js +0 -637
  267. package/dist/core/telemetry.js.map +0 -1
  268. package/dist/modes/interactive/components/prime-onboarding-splash.d.ts +0 -39
  269. package/dist/modes/interactive/components/prime-onboarding-splash.d.ts.map +0 -1
  270. package/dist/modes/interactive/components/prime-onboarding-splash.js +0 -286
  271. package/dist/modes/interactive/components/prime-onboarding-splash.js.map +0 -1
  272. package/dist/modes/interactive/components/prime-team-selector.d.ts +0 -30
  273. package/dist/modes/interactive/components/prime-team-selector.d.ts.map +0 -1
  274. package/dist/modes/interactive/components/prime-team-selector.js +0 -166
  275. package/dist/modes/interactive/components/prime-team-selector.js.map +0 -1
  276. package/dist/skills/prime-intellect/SKILL.md +0 -87
  277. package/dist/skills/prime-intellect/references/compute.md +0 -45
  278. package/dist/skills/prime-intellect/references/environments.md +0 -73
  279. package/dist/skills/prime-intellect/references/inference.md +0 -47
  280. package/dist/skills/prime-intellect/references/sandboxes.md +0 -75
  281. package/skills/prime-intellect/SKILL.md +0 -87
  282. package/skills/prime-intellect/references/compute.md +0 -45
  283. package/skills/prime-intellect/references/environments.md +0 -73
  284. package/skills/prime-intellect/references/inference.md +0 -47
  285. package/skills/prime-intellect/references/sandboxes.md +0 -75
@@ -1 +1 @@
1
- {"version":3,"file":"footer.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/footer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gCAAgC,CAAC;AAChE,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,uCAAuC,CAAC;AAExF;;;;;;;GAOG;AACH,qBAAa,eAAgB,YAAW,SAAS;IACpC,OAAO,CAAC,UAAU;IAA9B,YAAoB,UAAU,EAAE,0BAA0B,EAEzD;IAED,qBAAqB,CAAC,QAAQ,EAAE,OAAO,GAAG,IAAI,CAE7C;IAED;;;OAGG;IACH,UAAU,IAAI,IAAI,CAEjB;IAED;;;OAGG;IACH,OAAO,IAAI,IAAI,CAEd;IAED,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAI/B;CACD","sourcesContent":["import type { Component } from \"@ponythewhite/base-context-tui\";\nimport type { ReadonlyFooterDataProvider } from \"../../../core/footer-data-provider.js\";\n\n/**\n * Footer component for the prime brand TUI.\n *\n * Renders nothing by default — token counters, cost, model name, cwd, and context %\n * are intentionally hidden. The setters and invalidate/dispose hooks are kept so the\n * existing call sites in interactive-mode keep working without modification, and so\n * `/usage` can expose telemetry without re-plumbing.\n */\nexport class FooterComponent implements Component {\n\tconstructor(private footerData: ReadonlyFooterDataProvider) {\n\t\tvoid this.footerData;\n\t}\n\n\tsetAutoCompactEnabled(_enabled: boolean): void {\n\t\t// no-op while the footer is empty\n\t}\n\n\t/**\n\t * No-op: git branch caching now handled by provider.\n\t * Kept for compatibility with existing call sites in interactive-mode.\n\t */\n\tinvalidate(): void {\n\t\t// No-op: git branch is cached/invalidated by provider\n\t}\n\n\t/**\n\t * Clean up resources.\n\t * Git watcher cleanup now handled by provider.\n\t */\n\tdispose(): void {\n\t\t// Git watcher cleanup handled by provider\n\t}\n\n\trender(_width: number): string[] {\n\t\t// Footer is intentionally empty in the prime brand TUI. Telemetry (cost, tokens, model,\n\t\t// cwd, context %) is hidden by default; bring it back via /usage when needed.\n\t\treturn [];\n\t}\n}\n"]}
1
+ {"version":3,"file":"footer.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/footer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gCAAgC,CAAC;AAChE,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,uCAAuC,CAAC;AAExF;;;;;;;GAOG;AACH,qBAAa,eAAgB,YAAW,SAAS;IACpC,OAAO,CAAC,UAAU;IAA9B,YAAoB,UAAU,EAAE,0BAA0B,EAEzD;IAED,qBAAqB,CAAC,QAAQ,EAAE,OAAO,GAAG,IAAI,CAE7C;IAED;;;OAGG;IACH,UAAU,IAAI,IAAI,CAEjB;IAED;;;OAGG;IACH,OAAO,IAAI,IAAI,CAEd;IAED,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAI/B;CACD","sourcesContent":["import type { Component } from \"@ponythewhite/base-context-tui\";\nimport type { ReadonlyFooterDataProvider } from \"../../../core/footer-data-provider.js\";\n\n/**\n * Footer component for the prime brand TUI.\n *\n * Renders nothing by default — token counters, cost, model name, cwd, and context %\n * are intentionally hidden. The setters and invalidate/dispose hooks are kept so the\n * existing call sites in interactive-mode keep working without modification, and so\n * `/usage` can expose local usage statistics without re-plumbing.\n */\nexport class FooterComponent implements Component {\n\tconstructor(private footerData: ReadonlyFooterDataProvider) {\n\t\tvoid this.footerData;\n\t}\n\n\tsetAutoCompactEnabled(_enabled: boolean): void {\n\t\t// no-op while the footer is empty\n\t}\n\n\t/**\n\t * No-op: git branch caching now handled by provider.\n\t * Kept for compatibility with existing call sites in interactive-mode.\n\t */\n\tinvalidate(): void {\n\t\t// No-op: git branch is cached/invalidated by provider\n\t}\n\n\t/**\n\t * Clean up resources.\n\t * Git watcher cleanup now handled by provider.\n\t */\n\tdispose(): void {\n\t\t// Git watcher cleanup handled by provider\n\t}\n\n\trender(_width: number): string[] {\n\t\t// Footer is intentionally empty in the prime brand TUI. Local usage (cost, tokens, model,\n\t\t// cwd, context %) is hidden by default; bring it back via /usage when needed.\n\t\treturn [];\n\t}\n}\n"]}
@@ -4,7 +4,7 @@
4
4
  * Renders nothing by default — token counters, cost, model name, cwd, and context %
5
5
  * are intentionally hidden. The setters and invalidate/dispose hooks are kept so the
6
6
  * existing call sites in interactive-mode keep working without modification, and so
7
- * `/usage` can expose telemetry without re-plumbing.
7
+ * `/usage` can expose local usage statistics without re-plumbing.
8
8
  */
9
9
  export class FooterComponent {
10
10
  footerData;
@@ -30,7 +30,7 @@ export class FooterComponent {
30
30
  // Git watcher cleanup handled by provider
31
31
  }
32
32
  render(_width) {
33
- // Footer is intentionally empty in the prime brand TUI. Telemetry (cost, tokens, model,
33
+ // Footer is intentionally empty in the prime brand TUI. Local usage (cost, tokens, model,
34
34
  // cwd, context %) is hidden by default; bring it back via /usage when needed.
35
35
  return [];
36
36
  }
@@ -1 +1 @@
1
- {"version":3,"file":"footer.js","sourceRoot":"","sources":["../../../../src/modes/interactive/components/footer.ts"],"names":[],"mappings":"AAGA;;;;;;;GAOG;AACH,MAAM,OAAO,eAAe;IACP,UAAU;IAA9B,YAAoB,UAAsC,EAAE;0BAAxC,UAAU;QAC7B,KAAK,IAAI,CAAC,UAAU,CAAC;IAAA,CACrB;IAED,qBAAqB,CAAC,QAAiB,EAAQ;QAC9C,kCAAkC;IADa,CAE/C;IAED;;;OAGG;IACH,UAAU,GAAS;QAClB,sDAAsD;IADnC,CAEnB;IAED;;;OAGG;IACH,OAAO,GAAS;QACf,0CAA0C;IAD1B,CAEhB;IAED,MAAM,CAAC,MAAc,EAAY;QAChC,wFAAwF;QACxF,8EAA8E;QAC9E,OAAO,EAAE,CAAC;IAAA,CACV;CACD","sourcesContent":["import type { Component } from \"@ponythewhite/base-context-tui\";\nimport type { ReadonlyFooterDataProvider } from \"../../../core/footer-data-provider.js\";\n\n/**\n * Footer component for the prime brand TUI.\n *\n * Renders nothing by default — token counters, cost, model name, cwd, and context %\n * are intentionally hidden. The setters and invalidate/dispose hooks are kept so the\n * existing call sites in interactive-mode keep working without modification, and so\n * `/usage` can expose telemetry without re-plumbing.\n */\nexport class FooterComponent implements Component {\n\tconstructor(private footerData: ReadonlyFooterDataProvider) {\n\t\tvoid this.footerData;\n\t}\n\n\tsetAutoCompactEnabled(_enabled: boolean): void {\n\t\t// no-op while the footer is empty\n\t}\n\n\t/**\n\t * No-op: git branch caching now handled by provider.\n\t * Kept for compatibility with existing call sites in interactive-mode.\n\t */\n\tinvalidate(): void {\n\t\t// No-op: git branch is cached/invalidated by provider\n\t}\n\n\t/**\n\t * Clean up resources.\n\t * Git watcher cleanup now handled by provider.\n\t */\n\tdispose(): void {\n\t\t// Git watcher cleanup handled by provider\n\t}\n\n\trender(_width: number): string[] {\n\t\t// Footer is intentionally empty in the prime brand TUI. Telemetry (cost, tokens, model,\n\t\t// cwd, context %) is hidden by default; bring it back via /usage when needed.\n\t\treturn [];\n\t}\n}\n"]}
1
+ {"version":3,"file":"footer.js","sourceRoot":"","sources":["../../../../src/modes/interactive/components/footer.ts"],"names":[],"mappings":"AAGA;;;;;;;GAOG;AACH,MAAM,OAAO,eAAe;IACP,UAAU;IAA9B,YAAoB,UAAsC,EAAE;0BAAxC,UAAU;QAC7B,KAAK,IAAI,CAAC,UAAU,CAAC;IAAA,CACrB;IAED,qBAAqB,CAAC,QAAiB,EAAQ;QAC9C,kCAAkC;IADa,CAE/C;IAED;;;OAGG;IACH,UAAU,GAAS;QAClB,sDAAsD;IADnC,CAEnB;IAED;;;OAGG;IACH,OAAO,GAAS;QACf,0CAA0C;IAD1B,CAEhB;IAED,MAAM,CAAC,MAAc,EAAY;QAChC,0FAA0F;QAC1F,8EAA8E;QAC9E,OAAO,EAAE,CAAC;IAAA,CACV;CACD","sourcesContent":["import type { Component } from \"@ponythewhite/base-context-tui\";\nimport type { ReadonlyFooterDataProvider } from \"../../../core/footer-data-provider.js\";\n\n/**\n * Footer component for the prime brand TUI.\n *\n * Renders nothing by default — token counters, cost, model name, cwd, and context %\n * are intentionally hidden. The setters and invalidate/dispose hooks are kept so the\n * existing call sites in interactive-mode keep working without modification, and so\n * `/usage` can expose local usage statistics without re-plumbing.\n */\nexport class FooterComponent implements Component {\n\tconstructor(private footerData: ReadonlyFooterDataProvider) {\n\t\tvoid this.footerData;\n\t}\n\n\tsetAutoCompactEnabled(_enabled: boolean): void {\n\t\t// no-op while the footer is empty\n\t}\n\n\t/**\n\t * No-op: git branch caching now handled by provider.\n\t * Kept for compatibility with existing call sites in interactive-mode.\n\t */\n\tinvalidate(): void {\n\t\t// No-op: git branch is cached/invalidated by provider\n\t}\n\n\t/**\n\t * Clean up resources.\n\t * Git watcher cleanup now handled by provider.\n\t */\n\tdispose(): void {\n\t\t// Git watcher cleanup handled by provider\n\t}\n\n\trender(_width: number): string[] {\n\t\t// Footer is intentionally empty in the prime brand TUI. Local usage (cost, tokens, model,\n\t\t// cwd, context %) is hidden by default; bring it back via /usage when needed.\n\t\treturn [];\n\t}\n}\n"]}
@@ -22,7 +22,6 @@ export { keyHint, keyText, rawKeyHint } from "./keybinding-hints.js";
22
22
  export { LoginDialogComponent } from "./login-dialog.js";
23
23
  export { ModelSelectorComponent } from "./model-selector.js";
24
24
  export { OAuthSelectorComponent } from "./oauth-selector.js";
25
- export { PrimeOnboardingSplashComponent } from "./prime-onboarding-splash.js";
26
25
  export { type ModelsCallbacks, type ModelsConfig, ScopedModelsSelectorComponent } from "./scoped-models-selector.js";
27
26
  export { type SettingsCallbacks, type SettingsConfig, SettingsSelectorComponent } from "./settings-selector.js";
28
27
  export { ShowImagesSelectorComponent } from "./show-images-selector.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAC3D,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,EACN,iCAAiC,EACjC,0CAA0C,GAC1C,MAAM,iCAAiC,CAAC;AACzC,OAAO,EAAE,iCAAiC,EAAE,MAAM,iCAAiC,CAAC;AACpF,OAAO,EACN,0BAA0B,EAC1B,KAAK,oBAAoB,GACzB,MAAM,yBAAyB,CAAC;AACjC,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,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,EAAE,8BAA8B,EAAE,uBAAuB,EAAE,MAAM,8BAA8B,CAAC;AACvG,OAAO,EACN,sBAAsB,EACtB,oBAAoB,EACpB,KAAK,uBAAuB,EAC5B,KAAK,gBAAgB,GACrB,MAAM,mBAAmB,CAAC;AAC3B,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,8BAA8B,EAAE,MAAM,8BAA8B,CAAC;AAC9E,OAAO,EAAE,KAAK,eAAe,EAAE,KAAK,YAAY,EAAE,6BAA6B,EAAE,MAAM,6BAA6B,CAAC;AACrH,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,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AACjE,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,OAAO,EAAE,oBAAoB,EAAE,SAAS,EAAE,qBAAqB,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AACxG,OAAO,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAC3D,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\n\nexport { AgentMessageComponent } from \"./agent-message.js\";\nexport { ArminComponent } from \"./armin.js\";\nexport { AssistantMessageComponent } from \"./assistant-message.js\";\nexport { BashExecutionComponent } from \"./bash-execution.js\";\nexport { BorderedLoader } from \"./bordered-loader.js\";\nexport { BranchSummaryMessageComponent } from \"./branch-summary-message.js\";\nexport {\n\tCompactionOutcomeMessageComponent,\n\tMalformedCompactionOutcomeMessageComponent,\n} from \"./compaction-outcome-message.js\";\nexport { CompactionSummaryMessageComponent } from \"./compaction-summary-message.js\";\nexport {\n\tConfigurationMenuComponent,\n\ttype ConfigurationMenuTab,\n} from \"./configuration-menu.js\";\nexport { CustomEditor } from \"./custom-editor.js\";\nexport { CustomMessageComponent } from \"./custom-message.js\";\nexport { DaxnutsComponent } from \"./daxnuts.js\";\nexport { type RenderDiffOptions, renderDiff } from \"./diff.js\";\nexport { DynamicBorder } from \"./dynamic-border.js\";\nexport { ExtensionEditorComponent } from \"./extension-editor.js\";\nexport { ExtensionInputComponent } from \"./extension-input.js\";\nexport { ExtensionSelectorComponent } from \"./extension-selector.js\";\nexport { FooterComponent } from \"./footer.js\";\nexport { InjectedPromptMessageComponent, isInjectedPromptMessage } from \"./injected-prompt-message.js\";\nexport {\n\tgetIpythonCodeFromArgs,\n\tIPythonCellComponent,\n\ttype IPythonCellContentBlock,\n\ttype IPythonCellState,\n} from \"./ipython-cell.js\";\nexport { keyHint, keyText, rawKeyHint } from \"./keybinding-hints.js\";\nexport { LoginDialogComponent } from \"./login-dialog.js\";\nexport { ModelSelectorComponent } from \"./model-selector.js\";\nexport { OAuthSelectorComponent } from \"./oauth-selector.js\";\nexport { PrimeOnboardingSplashComponent } from \"./prime-onboarding-splash.js\";\nexport { type ModelsCallbacks, type ModelsConfig, ScopedModelsSelectorComponent } from \"./scoped-models-selector.js\";\nexport { type SettingsCallbacks, type SettingsConfig, SettingsSelectorComponent } from \"./settings-selector.js\";\nexport { ShowImagesSelectorComponent } from \"./show-images-selector.js\";\nexport { SkillInvocationMessageComponent } from \"./skill-invocation-message.js\";\nexport { SubagentSummaryLine } from \"./subagent-summary-line.js\";\nexport { ThemeSelectorComponent } from \"./theme-selector.js\";\nexport { ThinkingSelectorComponent } from \"./thinking-selector.js\";\nexport { ToolExecutionComponent, type ToolExecutionOptions } from \"./tool-execution.js\";\nexport { TOOL_PANEL_PADDING_X, ToolPanel, toolPanelContentWidth, toolPanelLine } from \"./tool-panel.js\";\nexport { TreeSelectorComponent } from \"./tree-selector.js\";\nexport { UserMessageComponent } from \"./user-message.js\";\nexport { UserMessageSelectorComponent } from \"./user-message-selector.js\";\nexport { truncateToVisualLines, type VisualTruncateResult } from \"./visual-truncate.js\";\n"]}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAC3D,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,EACN,iCAAiC,EACjC,0CAA0C,GAC1C,MAAM,iCAAiC,CAAC;AACzC,OAAO,EAAE,iCAAiC,EAAE,MAAM,iCAAiC,CAAC;AACpF,OAAO,EACN,0BAA0B,EAC1B,KAAK,oBAAoB,GACzB,MAAM,yBAAyB,CAAC;AACjC,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,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,EAAE,8BAA8B,EAAE,uBAAuB,EAAE,MAAM,8BAA8B,CAAC;AACvG,OAAO,EACN,sBAAsB,EACtB,oBAAoB,EACpB,KAAK,uBAAuB,EAC5B,KAAK,gBAAgB,GACrB,MAAM,mBAAmB,CAAC;AAC3B,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,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,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AACjE,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,OAAO,EAAE,oBAAoB,EAAE,SAAS,EAAE,qBAAqB,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AACxG,OAAO,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAC3D,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\n\nexport { AgentMessageComponent } from \"./agent-message.js\";\nexport { ArminComponent } from \"./armin.js\";\nexport { AssistantMessageComponent } from \"./assistant-message.js\";\nexport { BashExecutionComponent } from \"./bash-execution.js\";\nexport { BorderedLoader } from \"./bordered-loader.js\";\nexport { BranchSummaryMessageComponent } from \"./branch-summary-message.js\";\nexport {\n\tCompactionOutcomeMessageComponent,\n\tMalformedCompactionOutcomeMessageComponent,\n} from \"./compaction-outcome-message.js\";\nexport { CompactionSummaryMessageComponent } from \"./compaction-summary-message.js\";\nexport {\n\tConfigurationMenuComponent,\n\ttype ConfigurationMenuTab,\n} from \"./configuration-menu.js\";\nexport { CustomEditor } from \"./custom-editor.js\";\nexport { CustomMessageComponent } from \"./custom-message.js\";\nexport { DaxnutsComponent } from \"./daxnuts.js\";\nexport { type RenderDiffOptions, renderDiff } from \"./diff.js\";\nexport { DynamicBorder } from \"./dynamic-border.js\";\nexport { ExtensionEditorComponent } from \"./extension-editor.js\";\nexport { ExtensionInputComponent } from \"./extension-input.js\";\nexport { ExtensionSelectorComponent } from \"./extension-selector.js\";\nexport { FooterComponent } from \"./footer.js\";\nexport { InjectedPromptMessageComponent, isInjectedPromptMessage } from \"./injected-prompt-message.js\";\nexport {\n\tgetIpythonCodeFromArgs,\n\tIPythonCellComponent,\n\ttype IPythonCellContentBlock,\n\ttype IPythonCellState,\n} from \"./ipython-cell.js\";\nexport { keyHint, keyText, rawKeyHint } from \"./keybinding-hints.js\";\nexport { LoginDialogComponent } from \"./login-dialog.js\";\nexport { ModelSelectorComponent } from \"./model-selector.js\";\nexport { OAuthSelectorComponent } from \"./oauth-selector.js\";\nexport { type ModelsCallbacks, type ModelsConfig, ScopedModelsSelectorComponent } from \"./scoped-models-selector.js\";\nexport { type SettingsCallbacks, type SettingsConfig, SettingsSelectorComponent } from \"./settings-selector.js\";\nexport { ShowImagesSelectorComponent } from \"./show-images-selector.js\";\nexport { SkillInvocationMessageComponent } from \"./skill-invocation-message.js\";\nexport { SubagentSummaryLine } from \"./subagent-summary-line.js\";\nexport { ThemeSelectorComponent } from \"./theme-selector.js\";\nexport { ThinkingSelectorComponent } from \"./thinking-selector.js\";\nexport { ToolExecutionComponent, type ToolExecutionOptions } from \"./tool-execution.js\";\nexport { TOOL_PANEL_PADDING_X, ToolPanel, toolPanelContentWidth, toolPanelLine } from \"./tool-panel.js\";\nexport { TreeSelectorComponent } from \"./tree-selector.js\";\nexport { UserMessageComponent } from \"./user-message.js\";\nexport { UserMessageSelectorComponent } from \"./user-message-selector.js\";\nexport { truncateToVisualLines, type VisualTruncateResult } from \"./visual-truncate.js\";\n"]}
@@ -23,7 +23,6 @@ export { keyHint, keyText, rawKeyHint } from "./keybinding-hints.js";
23
23
  export { LoginDialogComponent } from "./login-dialog.js";
24
24
  export { ModelSelectorComponent } from "./model-selector.js";
25
25
  export { OAuthSelectorComponent } from "./oauth-selector.js";
26
- export { PrimeOnboardingSplashComponent } from "./prime-onboarding-splash.js";
27
26
  export { ScopedModelsSelectorComponent } from "./scoped-models-selector.js";
28
27
  export { SettingsSelectorComponent } from "./settings-selector.js";
29
28
  export { ShowImagesSelectorComponent } from "./show-images-selector.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/modes/interactive/components/index.ts"],"names":[],"mappings":"AAAA,+BAA+B;AAE/B,OAAO,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAC3D,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,EACN,iCAAiC,EACjC,0CAA0C,GAC1C,MAAM,iCAAiC,CAAC;AACzC,OAAO,EAAE,iCAAiC,EAAE,MAAM,iCAAiC,CAAC;AACpF,OAAO,EACN,0BAA0B,GAE1B,MAAM,yBAAyB,CAAC;AACjC,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,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,EAAE,8BAA8B,EAAE,uBAAuB,EAAE,MAAM,8BAA8B,CAAC;AACvG,OAAO,EACN,sBAAsB,EACtB,oBAAoB,GAGpB,MAAM,mBAAmB,CAAC;AAC3B,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,8BAA8B,EAAE,MAAM,8BAA8B,CAAC;AAC9E,OAAO,EAA2C,6BAA6B,EAAE,MAAM,6BAA6B,CAAC;AACrH,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,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AACjE,OAAO,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AAC7D,OAAO,EAAE,yBAAyB,EAAE,MAAM,wBAAwB,CAAC;AACnE,OAAO,EAAE,sBAAsB,EAA6B,MAAM,qBAAqB,CAAC;AACxF,OAAO,EAAE,oBAAoB,EAAE,SAAS,EAAE,qBAAqB,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AACxG,OAAO,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAC3D,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\n\nexport { AgentMessageComponent } from \"./agent-message.js\";\nexport { ArminComponent } from \"./armin.js\";\nexport { AssistantMessageComponent } from \"./assistant-message.js\";\nexport { BashExecutionComponent } from \"./bash-execution.js\";\nexport { BorderedLoader } from \"./bordered-loader.js\";\nexport { BranchSummaryMessageComponent } from \"./branch-summary-message.js\";\nexport {\n\tCompactionOutcomeMessageComponent,\n\tMalformedCompactionOutcomeMessageComponent,\n} from \"./compaction-outcome-message.js\";\nexport { CompactionSummaryMessageComponent } from \"./compaction-summary-message.js\";\nexport {\n\tConfigurationMenuComponent,\n\ttype ConfigurationMenuTab,\n} from \"./configuration-menu.js\";\nexport { CustomEditor } from \"./custom-editor.js\";\nexport { CustomMessageComponent } from \"./custom-message.js\";\nexport { DaxnutsComponent } from \"./daxnuts.js\";\nexport { type RenderDiffOptions, renderDiff } from \"./diff.js\";\nexport { DynamicBorder } from \"./dynamic-border.js\";\nexport { ExtensionEditorComponent } from \"./extension-editor.js\";\nexport { ExtensionInputComponent } from \"./extension-input.js\";\nexport { ExtensionSelectorComponent } from \"./extension-selector.js\";\nexport { FooterComponent } from \"./footer.js\";\nexport { InjectedPromptMessageComponent, isInjectedPromptMessage } from \"./injected-prompt-message.js\";\nexport {\n\tgetIpythonCodeFromArgs,\n\tIPythonCellComponent,\n\ttype IPythonCellContentBlock,\n\ttype IPythonCellState,\n} from \"./ipython-cell.js\";\nexport { keyHint, keyText, rawKeyHint } from \"./keybinding-hints.js\";\nexport { LoginDialogComponent } from \"./login-dialog.js\";\nexport { ModelSelectorComponent } from \"./model-selector.js\";\nexport { OAuthSelectorComponent } from \"./oauth-selector.js\";\nexport { PrimeOnboardingSplashComponent } from \"./prime-onboarding-splash.js\";\nexport { type ModelsCallbacks, type ModelsConfig, ScopedModelsSelectorComponent } from \"./scoped-models-selector.js\";\nexport { type SettingsCallbacks, type SettingsConfig, SettingsSelectorComponent } from \"./settings-selector.js\";\nexport { ShowImagesSelectorComponent } from \"./show-images-selector.js\";\nexport { SkillInvocationMessageComponent } from \"./skill-invocation-message.js\";\nexport { SubagentSummaryLine } from \"./subagent-summary-line.js\";\nexport { ThemeSelectorComponent } from \"./theme-selector.js\";\nexport { ThinkingSelectorComponent } from \"./thinking-selector.js\";\nexport { ToolExecutionComponent, type ToolExecutionOptions } from \"./tool-execution.js\";\nexport { TOOL_PANEL_PADDING_X, ToolPanel, toolPanelContentWidth, toolPanelLine } from \"./tool-panel.js\";\nexport { TreeSelectorComponent } from \"./tree-selector.js\";\nexport { UserMessageComponent } from \"./user-message.js\";\nexport { UserMessageSelectorComponent } from \"./user-message-selector.js\";\nexport { truncateToVisualLines, type VisualTruncateResult } from \"./visual-truncate.js\";\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/modes/interactive/components/index.ts"],"names":[],"mappings":"AAAA,+BAA+B;AAE/B,OAAO,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAC3D,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,EACN,iCAAiC,EACjC,0CAA0C,GAC1C,MAAM,iCAAiC,CAAC;AACzC,OAAO,EAAE,iCAAiC,EAAE,MAAM,iCAAiC,CAAC;AACpF,OAAO,EACN,0BAA0B,GAE1B,MAAM,yBAAyB,CAAC;AACjC,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,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,EAAE,8BAA8B,EAAE,uBAAuB,EAAE,MAAM,8BAA8B,CAAC;AACvG,OAAO,EACN,sBAAsB,EACtB,oBAAoB,GAGpB,MAAM,mBAAmB,CAAC;AAC3B,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,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,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AACjE,OAAO,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AAC7D,OAAO,EAAE,yBAAyB,EAAE,MAAM,wBAAwB,CAAC;AACnE,OAAO,EAAE,sBAAsB,EAA6B,MAAM,qBAAqB,CAAC;AACxF,OAAO,EAAE,oBAAoB,EAAE,SAAS,EAAE,qBAAqB,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AACxG,OAAO,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAC3D,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\n\nexport { AgentMessageComponent } from \"./agent-message.js\";\nexport { ArminComponent } from \"./armin.js\";\nexport { AssistantMessageComponent } from \"./assistant-message.js\";\nexport { BashExecutionComponent } from \"./bash-execution.js\";\nexport { BorderedLoader } from \"./bordered-loader.js\";\nexport { BranchSummaryMessageComponent } from \"./branch-summary-message.js\";\nexport {\n\tCompactionOutcomeMessageComponent,\n\tMalformedCompactionOutcomeMessageComponent,\n} from \"./compaction-outcome-message.js\";\nexport { CompactionSummaryMessageComponent } from \"./compaction-summary-message.js\";\nexport {\n\tConfigurationMenuComponent,\n\ttype ConfigurationMenuTab,\n} from \"./configuration-menu.js\";\nexport { CustomEditor } from \"./custom-editor.js\";\nexport { CustomMessageComponent } from \"./custom-message.js\";\nexport { DaxnutsComponent } from \"./daxnuts.js\";\nexport { type RenderDiffOptions, renderDiff } from \"./diff.js\";\nexport { DynamicBorder } from \"./dynamic-border.js\";\nexport { ExtensionEditorComponent } from \"./extension-editor.js\";\nexport { ExtensionInputComponent } from \"./extension-input.js\";\nexport { ExtensionSelectorComponent } from \"./extension-selector.js\";\nexport { FooterComponent } from \"./footer.js\";\nexport { InjectedPromptMessageComponent, isInjectedPromptMessage } from \"./injected-prompt-message.js\";\nexport {\n\tgetIpythonCodeFromArgs,\n\tIPythonCellComponent,\n\ttype IPythonCellContentBlock,\n\ttype IPythonCellState,\n} from \"./ipython-cell.js\";\nexport { keyHint, keyText, rawKeyHint } from \"./keybinding-hints.js\";\nexport { LoginDialogComponent } from \"./login-dialog.js\";\nexport { ModelSelectorComponent } from \"./model-selector.js\";\nexport { OAuthSelectorComponent } from \"./oauth-selector.js\";\nexport { type ModelsCallbacks, type ModelsConfig, ScopedModelsSelectorComponent } from \"./scoped-models-selector.js\";\nexport { type SettingsCallbacks, type SettingsConfig, SettingsSelectorComponent } from \"./settings-selector.js\";\nexport { ShowImagesSelectorComponent } from \"./show-images-selector.js\";\nexport { SkillInvocationMessageComponent } from \"./skill-invocation-message.js\";\nexport { SubagentSummaryLine } from \"./subagent-summary-line.js\";\nexport { ThemeSelectorComponent } from \"./theme-selector.js\";\nexport { ThinkingSelectorComponent } from \"./thinking-selector.js\";\nexport { ToolExecutionComponent, type ToolExecutionOptions } from \"./tool-execution.js\";\nexport { TOOL_PANEL_PADDING_X, ToolPanel, toolPanelContentWidth, toolPanelLine } from \"./tool-panel.js\";\nexport { TreeSelectorComponent } from \"./tree-selector.js\";\nexport { UserMessageComponent } from \"./user-message.js\";\nexport { UserMessageSelectorComponent } from \"./user-message-selector.js\";\nexport { truncateToVisualLines, type VisualTruncateResult } from \"./visual-truncate.js\";\n"]}
@@ -7,7 +7,6 @@ export declare class LoginDialogComponent extends Container implements Focusable
7
7
  private contentContainer;
8
8
  private input;
9
9
  private tui;
10
- private readonly isPrimeInference;
11
10
  private abortController;
12
11
  private inputResolver?;
13
12
  private inputRejecter?;
@@ -1 +1 @@
1
- {"version":3,"file":"login-dialog.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/login-dialog.ts"],"names":[],"mappings":"AAEA,OAAO,EAEN,SAAS,EACT,KAAK,SAAS,EAKd,KAAK,GAAG,EAGR,MAAM,gCAAgC,CAAC;AAuDxC;;GAEG;AACH,qBAAa,oBAAqB,SAAQ,SAAU,YAAW,SAAS;IA8BtE,OAAO,CAAC,UAAU;IA7BnB,OAAO,CAAC,gBAAgB,CAAY;IACpC,OAAO,CAAC,KAAK,CAAkB;IAC/B,OAAO,CAAC,GAAG,CAAM;IACjB,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAU;IAC3C,OAAO,CAAC,eAAe,CAAyB;IAChD,OAAO,CAAC,aAAa,CAAC,CAA0B;IAChD,OAAO,CAAC,aAAa,CAAC,CAAyB;IAI/C,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,gBAAgB,CAAC,CAAa;IACtC,OAAO,CAAC,gBAAgB,CAAC,CAAyB;IAClD,OAAO,CAAC,OAAO,CAAC,CAAS;IACzB,OAAO,CAAC,WAAW,CAAC,CAAO;IAG3B,OAAO,CAAC,QAAQ,CAAS;IACzB,IAAI,OAAO,IAAI,OAAO,CAErB;IACD,IAAI,OAAO,CAAC,KAAK,EAAE,OAAO,EAGzB;IAED,YACC,GAAG,EAAE,GAAG,EACR,UAAU,EAAE,MAAM,EACV,UAAU,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,MAAM,KAAK,IAAI,EAChE,oBAAoB,CAAC,EAAE,MAAM,EAC7B,aAAa,CAAC,EAAE,MAAM,EA6BtB;IAED,IAAI,MAAM,IAAI,WAAW,CAExB;IAED,OAAO,CAAC,MAAM;IAed;;OAEG;IACH,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CA+BjD;IAED;;OAEG;IACH,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAW/C;IAED;;OAEG;IACH,YAAY,IAAI,OAAO,CAAC,MAAM,CAAC,CAK9B;IAED;;;OAGG;IACH,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAoBjE;IAED;;OAEG;IACH,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,IAAI,CAQ9B;IAED,gBAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAsB/C;IAED;;OAEG;IACH,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAKjC;IAED;;OAEG;IACH,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAOlC;IAED,OAAO,CAAC,YAAY;IAcpB,OAAO,CAAC,gBAAgB;IAQxB,OAAO,CAAC,eAAe;IAWvB,OAAO,CAAC,eAAe;IAIvB,OAAO,CAAC,QAAQ;IAIhB,OAAO,CAAC,YAAY;IAIpB,OAAO,CAAC,kBAAkB;YAqBZ,WAAW;IAmBzB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CA+B9B;CACD","sourcesContent":["import { win32 } from \"node:path\";\nimport { getOAuthProviders } from \"@ponythewhite/base-context-ai/oauth\";\nimport {\n\ttype Component,\n\tContainer,\n\ttype Focusable,\n\tgetCapabilities,\n\tgetKeybindings,\n\tSpacer,\n\tText,\n\ttype TUI,\n\ttruncateToWidth,\n\tvisibleWidth,\n} from \"@ponythewhite/base-context-tui\";\nimport { execFile } from \"child_process\";\nimport { PRIME_BUTTERFLY_LOGO } from \"../../../themes/prime-logo.js\";\nimport { copyToClipboard } from \"../../../utils/clipboard.js\";\nimport { theme } from \"../theme/theme.js\";\nimport { formatKeyText, keyHint } from \"./keybinding-hints.js\";\nimport { MenuPanel, MenuSearchInput } from \"./menu-panel.js\";\nimport { shouldTreatAsBack } from \"./modal-back.js\";\n\nconst PRIME_INFERENCE_PROVIDER_ID = \"prime-inference\";\nconst PRIME_LOGO_LINES = PRIME_BUTTERFLY_LOGO.split(\"\\n\");\nconst PRIME_LOGO_WIDTH = PRIME_LOGO_LINES.reduce((max, line) => Math.max(max, visibleWidth(line)), 0);\n\nfunction centeredLine(text: string, width: number): string {\n\tconst safeWidth = Math.max(1, width);\n\tconst content = truncateToWidth(text, safeWidth, \"\");\n\tconst padding = Math.max(0, safeWidth - visibleWidth(content));\n\tconst left = Math.floor(padding / 2);\n\treturn \" \".repeat(left) + content + \" \".repeat(padding - left);\n}\n\nfunction isTextEntryKeybinding(key: string): boolean {\n\tconst parts = key.toLowerCase().split(\"+\");\n\tconst keyPart = parts.at(-1);\n\treturn !parts.includes(\"ctrl\") && !parts.includes(\"alt\") && (keyPart === \"space\" || keyPart?.length === 1);\n}\n\nfunction isPrintableInput(data: string): boolean {\n\treturn data.length === 1 && data >= \" \" && data !== \"\\x7f\";\n}\n\nclass PrimeLoginHeader implements Component {\n\tinvalidate(): void {\n\t\t// Header render is derived from the current theme.\n\t}\n\n\trender(width: number): string[] {\n\t\tconst safeWidth = Math.max(1, width);\n\t\tconst logoWidth = Math.min(PRIME_LOGO_WIDTH, safeWidth);\n\t\tconst logoLines = PRIME_LOGO_LINES.map((line) => {\n\t\t\tconst paddedLogoLine = line + \" \".repeat(Math.max(0, PRIME_LOGO_WIDTH - visibleWidth(line)));\n\t\t\treturn centeredLine(theme.fg(\"text\", truncateToWidth(paddedLogoLine, logoWidth, \"\")), safeWidth);\n\t\t});\n\t\treturn [\n\t\t\t...logoLines,\n\t\t\tcenteredLine(\"\", safeWidth),\n\t\t\tcenteredLine(theme.bold(theme.fg(\"text\", \"Login to Prime Inference\")), safeWidth),\n\t\t\tcenteredLine(\n\t\t\t\ttheme.fg(\"muted\", \"Connect your Prime Intellect account to enable Prime Inference models.\"),\n\t\t\t\tsafeWidth,\n\t\t\t),\n\t\t];\n\t}\n}\n\n/**\n * Login dialog component - replaces editor during OAuth login flow\n */\nexport class LoginDialogComponent extends Container implements Focusable {\n\tprivate contentContainer: Container;\n\tprivate input: MenuSearchInput;\n\tprivate tui: TUI;\n\tprivate readonly isPrimeInference: boolean;\n\tprivate abortController = new AbortController();\n\tprivate inputResolver?: (value: string) => void;\n\tprivate inputRejecter?: (error: Error) => void;\n\t// True only while the editable paste field is actually shown in the panel.\n\t// Tracks visibility directly rather than inferring it from inputResolver,\n\t// which can outlive the field when a new screen clears the content.\n\tprivate inputVisible = false;\n\tprivate continueResolver?: () => void;\n\tprivate continueRejecter?: (error: Error) => void;\n\tprivate authUrl?: string;\n\tprivate authActions?: Text;\n\n\t// Focusable implementation - propagate to input 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.input.focused = value;\n\t}\n\n\tconstructor(\n\t\ttui: TUI,\n\t\tproviderId: string,\n\t\tprivate onComplete: (success: boolean, message?: string) => void,\n\t\tproviderNameOverride?: string,\n\t\ttitleOverride?: string,\n\t) {\n\t\tsuper();\n\t\tthis.tui = tui;\n\n\t\tconst providerInfo = getOAuthProviders().find((p) => p.id === providerId);\n\t\tconst providerName = providerNameOverride || providerInfo?.name || providerId;\n\t\tthis.isPrimeInference = providerId === PRIME_INFERENCE_PROVIDER_ID;\n\t\tconst title = titleOverride ?? `Login to ${providerName}`;\n\n\t\tconst panel = new MenuPanel({\n\t\t\ttitle: this.isPrimeInference ? \"\" : title,\n\t\t\tsubtitle: this.isPrimeInference ? undefined : \"Complete this step to continue setup.\",\n\t\t});\n\t\tthis.addChild(panel);\n\n\t\t// Dynamic content area\n\t\tthis.contentContainer = new Container();\n\t\tpanel.addChild(this.contentContainer);\n\n\t\t// Input (always present, used when needed)\n\t\tthis.input = new MenuSearchInput(\"Paste value\");\n\t\tthis.input.onSubmit = () => {\n\t\t\tif (this.inputResolver) {\n\t\t\t\tthis.inputResolver(this.input.getValue());\n\t\t\t\tthis.inputResolver = undefined;\n\t\t\t\tthis.inputRejecter = undefined;\n\t\t\t}\n\t\t};\n\t}\n\n\tget signal(): AbortSignal {\n\t\treturn this.abortController.signal;\n\t}\n\n\tprivate cancel(): void {\n\t\tthis.abortController.abort();\n\t\tif (this.inputRejecter) {\n\t\t\tthis.inputRejecter(new Error(\"Login cancelled\"));\n\t\t\tthis.inputResolver = undefined;\n\t\t\tthis.inputRejecter = undefined;\n\t\t}\n\t\tif (this.continueRejecter) {\n\t\t\tthis.continueRejecter(new Error(\"Login cancelled\"));\n\t\t\tthis.continueResolver = undefined;\n\t\t\tthis.continueRejecter = undefined;\n\t\t}\n\t\tthis.onComplete(false, \"Login cancelled\");\n\t}\n\n\t/**\n\t * Called by onAuth callback - show URL and optional instructions\n\t */\n\tshowAuth(url: string, instructions?: string): void {\n\t\tthis.startContent();\n\t\tthis.authUrl = url;\n\t\tthis.addSectionTitle(\"Browser sign-in\");\n\t\tthis.addMutedText(\"The sign-in page should already be opening. If it did not open, use the link below.\");\n\t\tthis.contentContainer.addChild(new Spacer(1));\n\t\tthis.addLabel(\"Sign-in link\");\n\t\tconst linkedUrl = getCapabilities().hyperlinks ? `\\x1b]8;;${url}\\x07${url}\\x1b]8;;\\x07` : url;\n\t\tthis.contentContainer.addChild(new Text(theme.fg(\"text\", linkedUrl), 0, 0));\n\t\tthis.authActions = new Text(this.getAuthActionsText(), 0, 0);\n\t\tthis.contentContainer.addChild(this.authActions);\n\n\t\tif (instructions) {\n\t\t\tthis.contentContainer.addChild(new Spacer(1));\n\t\t\tthis.addInstructions(instructions);\n\t\t}\n\n\t\t// Try to open browser\n\t\tconst [command, ...args] =\n\t\t\tprocess.platform === \"darwin\"\n\t\t\t\t? [\"open\", url]\n\t\t\t\t: process.platform === \"win32\"\n\t\t\t\t\t? [\n\t\t\t\t\t\t\twin32.join(process.env.SystemRoot ?? \"C:\\\\Windows\", \"System32\", \"rundll32.exe\"),\n\t\t\t\t\t\t\t\"url.dll,FileProtocolHandler\",\n\t\t\t\t\t\t\turl,\n\t\t\t\t\t\t]\n\t\t\t\t\t: [\"xdg-open\", url];\n\t\texecFile(command, args, () => {});\n\n\t\tthis.tui.requestRender();\n\t}\n\n\t/**\n\t * Show input for manual code/URL entry (for callback server providers)\n\t */\n\tshowManualInput(prompt: string): Promise<string> {\n\t\tthis.addSectionSpacer();\n\t\tthis.addSectionTitle(\"Manual fallback\");\n\t\tthis.addMutedText(prompt);\n\t\tthis.contentContainer.addChild(this.input);\n\t\tthis.inputVisible = true;\n\t\tthis.authActions?.setText(this.getAuthActionsText());\n\t\tthis.contentContainer.addChild(new Text(theme.fg(\"muted\", keyHint(\"tui.select.cancel\", \"cancel\")), 0, 0));\n\t\tthis.tui.requestRender();\n\n\t\treturn this.waitForInput();\n\t}\n\n\t/**\n\t * Wait for the next submission of the already-visible input.\n\t */\n\twaitForInput(): Promise<string> {\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tthis.inputResolver = resolve;\n\t\t\tthis.inputRejecter = reject;\n\t\t});\n\t}\n\n\t/**\n\t * Called by onPrompt callback - show prompt and wait for input\n\t * Note: Does NOT clear content, appends to existing (preserves URL from showAuth)\n\t */\n\tshowPrompt(message: string, placeholder?: string): Promise<string> {\n\t\tthis.addSectionSpacer();\n\t\tthis.addSectionTitle(message);\n\t\tif (placeholder) {\n\t\t\tthis.contentContainer.addChild(new Text(theme.fg(\"muted\", `e.g., ${placeholder}`), 0, 0));\n\t\t}\n\t\tthis.contentContainer.addChild(this.input);\n\t\tthis.inputVisible = true;\n\t\tthis.authActions?.setText(this.getAuthActionsText());\n\t\tthis.contentContainer.addChild(\n\t\t\tnew Text(\n\t\t\t\ttheme.fg(\"muted\", `${keyHint(\"tui.select.confirm\", \"submit\")} ${keyHint(\"tui.select.cancel\", \"cancel\")}`),\n\t\t\t\t0,\n\t\t\t),\n\t\t);\n\n\t\tthis.input.setValue(\"\");\n\t\tthis.tui.requestRender();\n\n\t\treturn this.waitForInput();\n\t}\n\n\t/**\n\t * Show informational text without prompting for input.\n\t */\n\tshowInfo(lines: string[]): void {\n\t\tthis.startContent();\n\t\tfor (const line of lines) {\n\t\t\tthis.contentContainer.addChild(new Text(line, 0, 0));\n\t\t}\n\t\tthis.contentContainer.addChild(new Spacer(1));\n\t\tthis.contentContainer.addChild(new Text(theme.fg(\"muted\", keyHint(\"tui.select.cancel\", \"close\")), 0, 0));\n\t\tthis.tui.requestRender();\n\t}\n\n\tshowContinueInfo(lines: string[]): Promise<void> {\n\t\tthis.startContent();\n\t\tfor (const line of lines) {\n\t\t\tthis.contentContainer.addChild(new Text(line, 0, 0));\n\t\t}\n\t\tthis.contentContainer.addChild(new Spacer(1));\n\t\tthis.contentContainer.addChild(\n\t\t\tnew Text(\n\t\t\t\ttheme.fg(\n\t\t\t\t\t\"muted\",\n\t\t\t\t\t`${keyHint(\"tui.select.confirm\", \"continue\")} ${keyHint(\"tui.select.cancel\", \"cancel\")}`,\n\t\t\t\t),\n\t\t\t\t0,\n\t\t\t\t0,\n\t\t\t),\n\t\t);\n\t\tthis.tui.requestRender();\n\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tthis.continueResolver = resolve;\n\t\t\tthis.continueRejecter = reject;\n\t\t});\n\t}\n\n\t/**\n\t * Show waiting message (for polling flows like GitHub Copilot)\n\t */\n\tshowWaiting(message: string): void {\n\t\tthis.addSectionSpacer();\n\t\tthis.contentContainer.addChild(new Text(theme.fg(\"accent\", message), 0, 0));\n\t\tthis.contentContainer.addChild(new Text(theme.fg(\"muted\", keyHint(\"tui.select.cancel\", \"cancel\")), 0, 0));\n\t\tthis.tui.requestRender();\n\t}\n\n\t/**\n\t * Called by onProgress callback\n\t */\n\tshowProgress(message: string): void {\n\t\tif (this.contentContainer.children.length === 0) {\n\t\t\tthis.startContent();\n\t\t\tthis.addSectionTitle(\"Preparing authentication\");\n\t\t}\n\t\tthis.contentContainer.addChild(new Text(theme.fg(\"muted\", message), 0, 0));\n\t\tthis.tui.requestRender();\n\t}\n\n\tprivate startContent(): void {\n\t\tthis.contentContainer.clear();\n\t\tthis.authUrl = undefined;\n\t\tthis.authActions = undefined;\n\t\t// The cleared panel no longer shows the paste field.\n\t\tthis.inputVisible = false;\n\t\tif (this.isPrimeInference) {\n\t\t\tthis.contentContainer.addChild(new PrimeLoginHeader());\n\t\t\tthis.contentContainer.addChild(new Spacer(1));\n\t\t\treturn;\n\t\t}\n\t\tthis.contentContainer.addChild(new Spacer(1));\n\t}\n\n\tprivate addSectionSpacer(): void {\n\t\tif (this.contentContainer.children.length === 0) {\n\t\t\tthis.startContent();\n\t\t\treturn;\n\t\t}\n\t\tthis.contentContainer.addChild(new Spacer(1));\n\t}\n\n\tprivate addInstructions(instructions: string): void {\n\t\tconst codeMatch = /^(?:Code|Enter code):\\s*(.+)$/i.exec(instructions.trim());\n\t\tif (codeMatch?.[1]) {\n\t\t\tthis.addLabel(\"Verification code\");\n\t\t\tthis.contentContainer.addChild(new Text(theme.bold(theme.fg(\"text\", codeMatch[1])), 0, 0));\n\t\t\treturn;\n\t\t}\n\t\tthis.addLabel(\"Next step\");\n\t\tthis.contentContainer.addChild(new Text(theme.fg(\"text\", instructions), 0, 0));\n\t}\n\n\tprivate addSectionTitle(text: string): void {\n\t\tthis.contentContainer.addChild(new Text(theme.bold(theme.fg(\"text\", text)), 0, 0));\n\t}\n\n\tprivate addLabel(text: string): void {\n\t\tthis.contentContainer.addChild(new Text(theme.fg(\"muted\", text), 0, 0));\n\t}\n\n\tprivate addMutedText(text: string): void {\n\t\tthis.contentContainer.addChild(new Text(theme.fg(\"muted\", text), 0, 0));\n\t}\n\n\tprivate getAuthActionsText(status?: \"copied\" | \"failed\"): string {\n\t\tconst configuredCopyKeys = getKeybindings().getKeys(\"app.clipboard.copyLoginUrl\");\n\t\tconst copyKeys = this.inputVisible\n\t\t\t? configuredCopyKeys.filter((key) => !isTextEntryKeybinding(key))\n\t\t\t: configuredCopyKeys.slice(0, 1);\n\t\tconst copyHint =\n\t\t\tcopyKeys.length > 0\n\t\t\t\t? theme.fg(\"dim\", formatKeyText(copyKeys.join(\"/\"))) +\n\t\t\t\t\ttheme.fg(\"muted\", ` ${status === \"failed\" ? \"retry\" : \"copy\"}`)\n\t\t\t\t: undefined;\n\t\tconst statusText =\n\t\t\tstatus === \"copied\"\n\t\t\t\t? theme.fg(\"success\", \"Copied sign-in link\")\n\t\t\t\t: status === \"failed\"\n\t\t\t\t\t? theme.fg(\"error\", \"Failed to copy sign-in link\")\n\t\t\t\t\t: undefined;\n\t\treturn [statusText, copyHint, keyHint(\"tui.select.cancel\", \"cancel\")]\n\t\t\t.filter((part): part is string => part !== undefined)\n\t\t\t.join(\" \");\n\t}\n\n\tprivate async copyAuthUrl(): Promise<void> {\n\t\tconst url = this.authUrl;\n\t\tconst actions = this.authActions;\n\t\tif (!url || !actions) return;\n\n\t\ttry {\n\t\t\tawait copyToClipboard(url);\n\t\t\tif (this.authUrl === url && this.authActions === actions) {\n\t\t\t\tactions.setText(this.getAuthActionsText(\"copied\"));\n\t\t\t\tthis.tui.requestRender();\n\t\t\t}\n\t\t} catch {\n\t\t\tif (this.authUrl === url && this.authActions === actions) {\n\t\t\t\tactions.setText(this.getAuthActionsText(\"failed\"));\n\t\t\t\tthis.tui.requestRender();\n\t\t\t}\n\t\t}\n\t}\n\n\thandleInput(data: string): void {\n\t\tconst kb = getKeybindings();\n\n\t\tif (\n\t\t\tthis.authUrl &&\n\t\t\tkb.matches(data, \"app.clipboard.copyLoginUrl\") &&\n\t\t\t(!this.inputVisible || !isPrintableInput(data))\n\t\t) {\n\t\t\tvoid this.copyAuthUrl();\n\t\t\treturn;\n\t\t}\n\n\t\t// Left arrow acts as \"back\" like Esc. While the editable field is actually\n\t\t// shown, only treat it as back at the start of the text so left still moves\n\t\t// the cursor mid-edit; on info/continue screens there is no field to guard.\n\t\tconst backGuardInput = this.inputVisible ? this.input : undefined;\n\t\tif (kb.matches(data, \"tui.select.cancel\") || shouldTreatAsBack(data, backGuardInput)) {\n\t\t\tthis.cancel();\n\t\t\treturn;\n\t\t}\n\n\t\tif (this.continueResolver && kb.matches(data, \"tui.select.confirm\")) {\n\t\t\tconst resolve = this.continueResolver;\n\t\t\tthis.continueResolver = undefined;\n\t\t\tthis.continueRejecter = undefined;\n\t\t\tresolve();\n\t\t\treturn;\n\t\t}\n\n\t\t// Pass to input\n\t\tthis.input.handleInput(data);\n\t}\n}\n"]}
1
+ {"version":3,"file":"login-dialog.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/login-dialog.ts"],"names":[],"mappings":"AAEA,OAAO,EACN,SAAS,EACT,KAAK,SAAS,EAKd,KAAK,GAAG,EACR,MAAM,gCAAgC,CAAC;AAkBxC;;GAEG;AACH,qBAAa,oBAAqB,SAAQ,SAAU,YAAW,SAAS;IA6BtE,OAAO,CAAC,UAAU;IA5BnB,OAAO,CAAC,gBAAgB,CAAY;IACpC,OAAO,CAAC,KAAK,CAAkB;IAC/B,OAAO,CAAC,GAAG,CAAM;IACjB,OAAO,CAAC,eAAe,CAAyB;IAChD,OAAO,CAAC,aAAa,CAAC,CAA0B;IAChD,OAAO,CAAC,aAAa,CAAC,CAAyB;IAI/C,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,gBAAgB,CAAC,CAAa;IACtC,OAAO,CAAC,gBAAgB,CAAC,CAAyB;IAClD,OAAO,CAAC,OAAO,CAAC,CAAS;IACzB,OAAO,CAAC,WAAW,CAAC,CAAO;IAG3B,OAAO,CAAC,QAAQ,CAAS;IACzB,IAAI,OAAO,IAAI,OAAO,CAErB;IACD,IAAI,OAAO,CAAC,KAAK,EAAE,OAAO,EAGzB;IAED,YACC,GAAG,EAAE,GAAG,EACR,UAAU,EAAE,MAAM,EACV,UAAU,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,MAAM,KAAK,IAAI,EAChE,oBAAoB,CAAC,EAAE,MAAM,EAC7B,aAAa,CAAC,EAAE,MAAM,EA4BtB;IAED,IAAI,MAAM,IAAI,WAAW,CAExB;IAED,OAAO,CAAC,MAAM;IAed;;OAEG;IACH,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CA+BjD;IAED;;OAEG;IACH,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAW/C;IAED;;OAEG;IACH,YAAY,IAAI,OAAO,CAAC,MAAM,CAAC,CAK9B;IAED;;;OAGG;IACH,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAoBjE;IAED;;OAEG;IACH,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,IAAI,CAQ9B;IAED,gBAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAsB/C;IAED;;OAEG;IACH,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAKjC;IAED;;OAEG;IACH,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAOlC;IAED,OAAO,CAAC,YAAY;IASpB,OAAO,CAAC,gBAAgB;IAQxB,OAAO,CAAC,eAAe;IAWvB,OAAO,CAAC,eAAe;IAIvB,OAAO,CAAC,QAAQ;IAIhB,OAAO,CAAC,YAAY;IAIpB,OAAO,CAAC,kBAAkB;YAqBZ,WAAW;IAmBzB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CA+B9B;CACD","sourcesContent":["import { win32 } from \"node:path\";\nimport { getOAuthProviders } from \"@ponythewhite/base-context-ai/oauth\";\nimport {\n\tContainer,\n\ttype Focusable,\n\tgetCapabilities,\n\tgetKeybindings,\n\tSpacer,\n\tText,\n\ttype TUI,\n} from \"@ponythewhite/base-context-tui\";\nimport { execFile } from \"child_process\";\nimport { copyToClipboard } from \"../../../utils/clipboard.js\";\nimport { theme } from \"../theme/theme.js\";\nimport { formatKeyText, keyHint } from \"./keybinding-hints.js\";\nimport { MenuPanel, MenuSearchInput } from \"./menu-panel.js\";\nimport { shouldTreatAsBack } from \"./modal-back.js\";\n\nfunction isTextEntryKeybinding(key: string): boolean {\n\tconst parts = key.toLowerCase().split(\"+\");\n\tconst keyPart = parts.at(-1);\n\treturn !parts.includes(\"ctrl\") && !parts.includes(\"alt\") && (keyPart === \"space\" || keyPart?.length === 1);\n}\n\nfunction isPrintableInput(data: string): boolean {\n\treturn data.length === 1 && data >= \" \" && data !== \"\\x7f\";\n}\n\n/**\n * Login dialog component - replaces editor during OAuth login flow\n */\nexport class LoginDialogComponent extends Container implements Focusable {\n\tprivate contentContainer: Container;\n\tprivate input: MenuSearchInput;\n\tprivate tui: TUI;\n\tprivate abortController = new AbortController();\n\tprivate inputResolver?: (value: string) => void;\n\tprivate inputRejecter?: (error: Error) => void;\n\t// True only while the editable paste field is actually shown in the panel.\n\t// Tracks visibility directly rather than inferring it from inputResolver,\n\t// which can outlive the field when a new screen clears the content.\n\tprivate inputVisible = false;\n\tprivate continueResolver?: () => void;\n\tprivate continueRejecter?: (error: Error) => void;\n\tprivate authUrl?: string;\n\tprivate authActions?: Text;\n\n\t// Focusable implementation - propagate to input 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.input.focused = value;\n\t}\n\n\tconstructor(\n\t\ttui: TUI,\n\t\tproviderId: string,\n\t\tprivate onComplete: (success: boolean, message?: string) => void,\n\t\tproviderNameOverride?: string,\n\t\ttitleOverride?: string,\n\t) {\n\t\tsuper();\n\t\tthis.tui = tui;\n\n\t\tconst providerInfo = getOAuthProviders().find((p) => p.id === providerId);\n\t\tconst providerName = providerNameOverride || providerInfo?.name || providerId;\n\t\tconst title = titleOverride ?? `Login to ${providerName}`;\n\n\t\tconst panel = new MenuPanel({\n\t\t\ttitle,\n\t\t\tsubtitle: \"Complete this step to continue setup.\",\n\t\t});\n\t\tthis.addChild(panel);\n\n\t\t// Dynamic content area\n\t\tthis.contentContainer = new Container();\n\t\tpanel.addChild(this.contentContainer);\n\n\t\t// Input (always present, used when needed)\n\t\tthis.input = new MenuSearchInput(\"Paste value\");\n\t\tthis.input.onSubmit = () => {\n\t\t\tif (this.inputResolver) {\n\t\t\t\tthis.inputResolver(this.input.getValue());\n\t\t\t\tthis.inputResolver = undefined;\n\t\t\t\tthis.inputRejecter = undefined;\n\t\t\t}\n\t\t};\n\t}\n\n\tget signal(): AbortSignal {\n\t\treturn this.abortController.signal;\n\t}\n\n\tprivate cancel(): void {\n\t\tthis.abortController.abort();\n\t\tif (this.inputRejecter) {\n\t\t\tthis.inputRejecter(new Error(\"Login cancelled\"));\n\t\t\tthis.inputResolver = undefined;\n\t\t\tthis.inputRejecter = undefined;\n\t\t}\n\t\tif (this.continueRejecter) {\n\t\t\tthis.continueRejecter(new Error(\"Login cancelled\"));\n\t\t\tthis.continueResolver = undefined;\n\t\t\tthis.continueRejecter = undefined;\n\t\t}\n\t\tthis.onComplete(false, \"Login cancelled\");\n\t}\n\n\t/**\n\t * Called by onAuth callback - show URL and optional instructions\n\t */\n\tshowAuth(url: string, instructions?: string): void {\n\t\tthis.startContent();\n\t\tthis.authUrl = url;\n\t\tthis.addSectionTitle(\"Browser sign-in\");\n\t\tthis.addMutedText(\"The sign-in page should already be opening. If it did not open, use the link below.\");\n\t\tthis.contentContainer.addChild(new Spacer(1));\n\t\tthis.addLabel(\"Sign-in link\");\n\t\tconst linkedUrl = getCapabilities().hyperlinks ? `\\x1b]8;;${url}\\x07${url}\\x1b]8;;\\x07` : url;\n\t\tthis.contentContainer.addChild(new Text(theme.fg(\"text\", linkedUrl), 0, 0));\n\t\tthis.authActions = new Text(this.getAuthActionsText(), 0, 0);\n\t\tthis.contentContainer.addChild(this.authActions);\n\n\t\tif (instructions) {\n\t\t\tthis.contentContainer.addChild(new Spacer(1));\n\t\t\tthis.addInstructions(instructions);\n\t\t}\n\n\t\t// Try to open browser\n\t\tconst [command, ...args] =\n\t\t\tprocess.platform === \"darwin\"\n\t\t\t\t? [\"open\", url]\n\t\t\t\t: process.platform === \"win32\"\n\t\t\t\t\t? [\n\t\t\t\t\t\t\twin32.join(process.env.SystemRoot ?? \"C:\\\\Windows\", \"System32\", \"rundll32.exe\"),\n\t\t\t\t\t\t\t\"url.dll,FileProtocolHandler\",\n\t\t\t\t\t\t\turl,\n\t\t\t\t\t\t]\n\t\t\t\t\t: [\"xdg-open\", url];\n\t\texecFile(command, args, () => {});\n\n\t\tthis.tui.requestRender();\n\t}\n\n\t/**\n\t * Show input for manual code/URL entry (for callback server providers)\n\t */\n\tshowManualInput(prompt: string): Promise<string> {\n\t\tthis.addSectionSpacer();\n\t\tthis.addSectionTitle(\"Manual fallback\");\n\t\tthis.addMutedText(prompt);\n\t\tthis.contentContainer.addChild(this.input);\n\t\tthis.inputVisible = true;\n\t\tthis.authActions?.setText(this.getAuthActionsText());\n\t\tthis.contentContainer.addChild(new Text(theme.fg(\"muted\", keyHint(\"tui.select.cancel\", \"cancel\")), 0, 0));\n\t\tthis.tui.requestRender();\n\n\t\treturn this.waitForInput();\n\t}\n\n\t/**\n\t * Wait for the next submission of the already-visible input.\n\t */\n\twaitForInput(): Promise<string> {\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tthis.inputResolver = resolve;\n\t\t\tthis.inputRejecter = reject;\n\t\t});\n\t}\n\n\t/**\n\t * Called by onPrompt callback - show prompt and wait for input\n\t * Note: Does NOT clear content, appends to existing (preserves URL from showAuth)\n\t */\n\tshowPrompt(message: string, placeholder?: string): Promise<string> {\n\t\tthis.addSectionSpacer();\n\t\tthis.addSectionTitle(message);\n\t\tif (placeholder) {\n\t\t\tthis.contentContainer.addChild(new Text(theme.fg(\"muted\", `e.g., ${placeholder}`), 0, 0));\n\t\t}\n\t\tthis.contentContainer.addChild(this.input);\n\t\tthis.inputVisible = true;\n\t\tthis.authActions?.setText(this.getAuthActionsText());\n\t\tthis.contentContainer.addChild(\n\t\t\tnew Text(\n\t\t\t\ttheme.fg(\"muted\", `${keyHint(\"tui.select.confirm\", \"submit\")} ${keyHint(\"tui.select.cancel\", \"cancel\")}`),\n\t\t\t\t0,\n\t\t\t),\n\t\t);\n\n\t\tthis.input.setValue(\"\");\n\t\tthis.tui.requestRender();\n\n\t\treturn this.waitForInput();\n\t}\n\n\t/**\n\t * Show informational text without prompting for input.\n\t */\n\tshowInfo(lines: string[]): void {\n\t\tthis.startContent();\n\t\tfor (const line of lines) {\n\t\t\tthis.contentContainer.addChild(new Text(line, 0, 0));\n\t\t}\n\t\tthis.contentContainer.addChild(new Spacer(1));\n\t\tthis.contentContainer.addChild(new Text(theme.fg(\"muted\", keyHint(\"tui.select.cancel\", \"close\")), 0, 0));\n\t\tthis.tui.requestRender();\n\t}\n\n\tshowContinueInfo(lines: string[]): Promise<void> {\n\t\tthis.startContent();\n\t\tfor (const line of lines) {\n\t\t\tthis.contentContainer.addChild(new Text(line, 0, 0));\n\t\t}\n\t\tthis.contentContainer.addChild(new Spacer(1));\n\t\tthis.contentContainer.addChild(\n\t\t\tnew Text(\n\t\t\t\ttheme.fg(\n\t\t\t\t\t\"muted\",\n\t\t\t\t\t`${keyHint(\"tui.select.confirm\", \"continue\")} ${keyHint(\"tui.select.cancel\", \"cancel\")}`,\n\t\t\t\t),\n\t\t\t\t0,\n\t\t\t\t0,\n\t\t\t),\n\t\t);\n\t\tthis.tui.requestRender();\n\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tthis.continueResolver = resolve;\n\t\t\tthis.continueRejecter = reject;\n\t\t});\n\t}\n\n\t/**\n\t * Show waiting message (for polling flows like GitHub Copilot)\n\t */\n\tshowWaiting(message: string): void {\n\t\tthis.addSectionSpacer();\n\t\tthis.contentContainer.addChild(new Text(theme.fg(\"accent\", message), 0, 0));\n\t\tthis.contentContainer.addChild(new Text(theme.fg(\"muted\", keyHint(\"tui.select.cancel\", \"cancel\")), 0, 0));\n\t\tthis.tui.requestRender();\n\t}\n\n\t/**\n\t * Called by onProgress callback\n\t */\n\tshowProgress(message: string): void {\n\t\tif (this.contentContainer.children.length === 0) {\n\t\t\tthis.startContent();\n\t\t\tthis.addSectionTitle(\"Preparing authentication\");\n\t\t}\n\t\tthis.contentContainer.addChild(new Text(theme.fg(\"muted\", message), 0, 0));\n\t\tthis.tui.requestRender();\n\t}\n\n\tprivate startContent(): void {\n\t\tthis.contentContainer.clear();\n\t\tthis.authUrl = undefined;\n\t\tthis.authActions = undefined;\n\t\t// The cleared panel no longer shows the paste field.\n\t\tthis.inputVisible = false;\n\t\tthis.contentContainer.addChild(new Spacer(1));\n\t}\n\n\tprivate addSectionSpacer(): void {\n\t\tif (this.contentContainer.children.length === 0) {\n\t\t\tthis.startContent();\n\t\t\treturn;\n\t\t}\n\t\tthis.contentContainer.addChild(new Spacer(1));\n\t}\n\n\tprivate addInstructions(instructions: string): void {\n\t\tconst codeMatch = /^(?:Code|Enter code):\\s*(.+)$/i.exec(instructions.trim());\n\t\tif (codeMatch?.[1]) {\n\t\t\tthis.addLabel(\"Verification code\");\n\t\t\tthis.contentContainer.addChild(new Text(theme.bold(theme.fg(\"text\", codeMatch[1])), 0, 0));\n\t\t\treturn;\n\t\t}\n\t\tthis.addLabel(\"Next step\");\n\t\tthis.contentContainer.addChild(new Text(theme.fg(\"text\", instructions), 0, 0));\n\t}\n\n\tprivate addSectionTitle(text: string): void {\n\t\tthis.contentContainer.addChild(new Text(theme.bold(theme.fg(\"text\", text)), 0, 0));\n\t}\n\n\tprivate addLabel(text: string): void {\n\t\tthis.contentContainer.addChild(new Text(theme.fg(\"muted\", text), 0, 0));\n\t}\n\n\tprivate addMutedText(text: string): void {\n\t\tthis.contentContainer.addChild(new Text(theme.fg(\"muted\", text), 0, 0));\n\t}\n\n\tprivate getAuthActionsText(status?: \"copied\" | \"failed\"): string {\n\t\tconst configuredCopyKeys = getKeybindings().getKeys(\"app.clipboard.copyLoginUrl\");\n\t\tconst copyKeys = this.inputVisible\n\t\t\t? configuredCopyKeys.filter((key) => !isTextEntryKeybinding(key))\n\t\t\t: configuredCopyKeys.slice(0, 1);\n\t\tconst copyHint =\n\t\t\tcopyKeys.length > 0\n\t\t\t\t? theme.fg(\"dim\", formatKeyText(copyKeys.join(\"/\"))) +\n\t\t\t\t\ttheme.fg(\"muted\", ` ${status === \"failed\" ? \"retry\" : \"copy\"}`)\n\t\t\t\t: undefined;\n\t\tconst statusText =\n\t\t\tstatus === \"copied\"\n\t\t\t\t? theme.fg(\"success\", \"Copied sign-in link\")\n\t\t\t\t: status === \"failed\"\n\t\t\t\t\t? theme.fg(\"error\", \"Failed to copy sign-in link\")\n\t\t\t\t\t: undefined;\n\t\treturn [statusText, copyHint, keyHint(\"tui.select.cancel\", \"cancel\")]\n\t\t\t.filter((part): part is string => part !== undefined)\n\t\t\t.join(\" \");\n\t}\n\n\tprivate async copyAuthUrl(): Promise<void> {\n\t\tconst url = this.authUrl;\n\t\tconst actions = this.authActions;\n\t\tif (!url || !actions) return;\n\n\t\ttry {\n\t\t\tawait copyToClipboard(url);\n\t\t\tif (this.authUrl === url && this.authActions === actions) {\n\t\t\t\tactions.setText(this.getAuthActionsText(\"copied\"));\n\t\t\t\tthis.tui.requestRender();\n\t\t\t}\n\t\t} catch {\n\t\t\tif (this.authUrl === url && this.authActions === actions) {\n\t\t\t\tactions.setText(this.getAuthActionsText(\"failed\"));\n\t\t\t\tthis.tui.requestRender();\n\t\t\t}\n\t\t}\n\t}\n\n\thandleInput(data: string): void {\n\t\tconst kb = getKeybindings();\n\n\t\tif (\n\t\t\tthis.authUrl &&\n\t\t\tkb.matches(data, \"app.clipboard.copyLoginUrl\") &&\n\t\t\t(!this.inputVisible || !isPrintableInput(data))\n\t\t) {\n\t\t\tvoid this.copyAuthUrl();\n\t\t\treturn;\n\t\t}\n\n\t\t// Left arrow acts as \"back\" like Esc. While the editable field is actually\n\t\t// shown, only treat it as back at the start of the text so left still moves\n\t\t// the cursor mid-edit; on info/continue screens there is no field to guard.\n\t\tconst backGuardInput = this.inputVisible ? this.input : undefined;\n\t\tif (kb.matches(data, \"tui.select.cancel\") || shouldTreatAsBack(data, backGuardInput)) {\n\t\t\tthis.cancel();\n\t\t\treturn;\n\t\t}\n\n\t\tif (this.continueResolver && kb.matches(data, \"tui.select.confirm\")) {\n\t\t\tconst resolve = this.continueResolver;\n\t\t\tthis.continueResolver = undefined;\n\t\t\tthis.continueRejecter = undefined;\n\t\t\tresolve();\n\t\t\treturn;\n\t\t}\n\n\t\t// Pass to input\n\t\tthis.input.handleInput(data);\n\t}\n}\n"]}
@@ -1,23 +1,12 @@
1
1
  import { win32 } from "node:path";
2
2
  import { getOAuthProviders } from "@ponythewhite/base-context-ai/oauth";
3
- import { Container, getCapabilities, getKeybindings, Spacer, Text, truncateToWidth, visibleWidth, } from "@ponythewhite/base-context-tui";
3
+ import { Container, getCapabilities, getKeybindings, Spacer, Text, } from "@ponythewhite/base-context-tui";
4
4
  import { execFile } from "child_process";
5
- import { PRIME_BUTTERFLY_LOGO } from "../../../themes/prime-logo.js";
6
5
  import { copyToClipboard } from "../../../utils/clipboard.js";
7
6
  import { theme } from "../theme/theme.js";
8
7
  import { formatKeyText, keyHint } from "./keybinding-hints.js";
9
8
  import { MenuPanel, MenuSearchInput } from "./menu-panel.js";
10
9
  import { shouldTreatAsBack } from "./modal-back.js";
11
- const PRIME_INFERENCE_PROVIDER_ID = "prime-inference";
12
- const PRIME_LOGO_LINES = PRIME_BUTTERFLY_LOGO.split("\n");
13
- const PRIME_LOGO_WIDTH = PRIME_LOGO_LINES.reduce((max, line) => Math.max(max, visibleWidth(line)), 0);
14
- function centeredLine(text, width) {
15
- const safeWidth = Math.max(1, width);
16
- const content = truncateToWidth(text, safeWidth, "");
17
- const padding = Math.max(0, safeWidth - visibleWidth(content));
18
- const left = Math.floor(padding / 2);
19
- return " ".repeat(left) + content + " ".repeat(padding - left);
20
- }
21
10
  function isTextEntryKeybinding(key) {
22
11
  const parts = key.toLowerCase().split("+");
23
12
  const keyPart = parts.at(-1);
@@ -26,25 +15,6 @@ function isTextEntryKeybinding(key) {
26
15
  function isPrintableInput(data) {
27
16
  return data.length === 1 && data >= " " && data !== "\x7f";
28
17
  }
29
- class PrimeLoginHeader {
30
- invalidate() {
31
- // Header render is derived from the current theme.
32
- }
33
- render(width) {
34
- const safeWidth = Math.max(1, width);
35
- const logoWidth = Math.min(PRIME_LOGO_WIDTH, safeWidth);
36
- const logoLines = PRIME_LOGO_LINES.map((line) => {
37
- const paddedLogoLine = line + " ".repeat(Math.max(0, PRIME_LOGO_WIDTH - visibleWidth(line)));
38
- return centeredLine(theme.fg("text", truncateToWidth(paddedLogoLine, logoWidth, "")), safeWidth);
39
- });
40
- return [
41
- ...logoLines,
42
- centeredLine("", safeWidth),
43
- centeredLine(theme.bold(theme.fg("text", "Login to Prime Inference")), safeWidth),
44
- centeredLine(theme.fg("muted", "Connect your Prime Intellect account to enable Prime Inference models."), safeWidth),
45
- ];
46
- }
47
- }
48
18
  /**
49
19
  * Login dialog component - replaces editor during OAuth login flow
50
20
  */
@@ -53,7 +23,6 @@ export class LoginDialogComponent extends Container {
53
23
  contentContainer;
54
24
  input;
55
25
  tui;
56
- isPrimeInference;
57
26
  abortController = new AbortController();
58
27
  inputResolver;
59
28
  inputRejecter;
@@ -80,11 +49,10 @@ export class LoginDialogComponent extends Container {
80
49
  this.tui = tui;
81
50
  const providerInfo = getOAuthProviders().find((p) => p.id === providerId);
82
51
  const providerName = providerNameOverride || providerInfo?.name || providerId;
83
- this.isPrimeInference = providerId === PRIME_INFERENCE_PROVIDER_ID;
84
52
  const title = titleOverride ?? `Login to ${providerName}`;
85
53
  const panel = new MenuPanel({
86
- title: this.isPrimeInference ? "" : title,
87
- subtitle: this.isPrimeInference ? undefined : "Complete this step to continue setup.",
54
+ title,
55
+ subtitle: "Complete this step to continue setup.",
88
56
  });
89
57
  this.addChild(panel);
90
58
  // Dynamic content area
@@ -240,11 +208,6 @@ export class LoginDialogComponent extends Container {
240
208
  this.authActions = undefined;
241
209
  // The cleared panel no longer shows the paste field.
242
210
  this.inputVisible = false;
243
- if (this.isPrimeInference) {
244
- this.contentContainer.addChild(new PrimeLoginHeader());
245
- this.contentContainer.addChild(new Spacer(1));
246
- return;
247
- }
248
211
  this.contentContainer.addChild(new Spacer(1));
249
212
  }
250
213
  addSectionSpacer() {
@@ -1 +1 @@
1
- {"version":3,"file":"login-dialog.js","sourceRoot":"","sources":["../../../../src/modes/interactive/components/login-dialog.ts"],"names":[],"mappings":"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","sourcesContent":["import { win32 } from \"node:path\";\nimport { getOAuthProviders } from \"@ponythewhite/base-context-ai/oauth\";\nimport {\n\ttype Component,\n\tContainer,\n\ttype Focusable,\n\tgetCapabilities,\n\tgetKeybindings,\n\tSpacer,\n\tText,\n\ttype TUI,\n\ttruncateToWidth,\n\tvisibleWidth,\n} from \"@ponythewhite/base-context-tui\";\nimport { execFile } from \"child_process\";\nimport { PRIME_BUTTERFLY_LOGO } from \"../../../themes/prime-logo.js\";\nimport { copyToClipboard } from \"../../../utils/clipboard.js\";\nimport { theme } from \"../theme/theme.js\";\nimport { formatKeyText, keyHint } from \"./keybinding-hints.js\";\nimport { MenuPanel, MenuSearchInput } from \"./menu-panel.js\";\nimport { shouldTreatAsBack } from \"./modal-back.js\";\n\nconst PRIME_INFERENCE_PROVIDER_ID = \"prime-inference\";\nconst PRIME_LOGO_LINES = PRIME_BUTTERFLY_LOGO.split(\"\\n\");\nconst PRIME_LOGO_WIDTH = PRIME_LOGO_LINES.reduce((max, line) => Math.max(max, visibleWidth(line)), 0);\n\nfunction centeredLine(text: string, width: number): string {\n\tconst safeWidth = Math.max(1, width);\n\tconst content = truncateToWidth(text, safeWidth, \"\");\n\tconst padding = Math.max(0, safeWidth - visibleWidth(content));\n\tconst left = Math.floor(padding / 2);\n\treturn \" \".repeat(left) + content + \" \".repeat(padding - left);\n}\n\nfunction isTextEntryKeybinding(key: string): boolean {\n\tconst parts = key.toLowerCase().split(\"+\");\n\tconst keyPart = parts.at(-1);\n\treturn !parts.includes(\"ctrl\") && !parts.includes(\"alt\") && (keyPart === \"space\" || keyPart?.length === 1);\n}\n\nfunction isPrintableInput(data: string): boolean {\n\treturn data.length === 1 && data >= \" \" && data !== \"\\x7f\";\n}\n\nclass PrimeLoginHeader implements Component {\n\tinvalidate(): void {\n\t\t// Header render is derived from the current theme.\n\t}\n\n\trender(width: number): string[] {\n\t\tconst safeWidth = Math.max(1, width);\n\t\tconst logoWidth = Math.min(PRIME_LOGO_WIDTH, safeWidth);\n\t\tconst logoLines = PRIME_LOGO_LINES.map((line) => {\n\t\t\tconst paddedLogoLine = line + \" \".repeat(Math.max(0, PRIME_LOGO_WIDTH - visibleWidth(line)));\n\t\t\treturn centeredLine(theme.fg(\"text\", truncateToWidth(paddedLogoLine, logoWidth, \"\")), safeWidth);\n\t\t});\n\t\treturn [\n\t\t\t...logoLines,\n\t\t\tcenteredLine(\"\", safeWidth),\n\t\t\tcenteredLine(theme.bold(theme.fg(\"text\", \"Login to Prime Inference\")), safeWidth),\n\t\t\tcenteredLine(\n\t\t\t\ttheme.fg(\"muted\", \"Connect your Prime Intellect account to enable Prime Inference models.\"),\n\t\t\t\tsafeWidth,\n\t\t\t),\n\t\t];\n\t}\n}\n\n/**\n * Login dialog component - replaces editor during OAuth login flow\n */\nexport class LoginDialogComponent extends Container implements Focusable {\n\tprivate contentContainer: Container;\n\tprivate input: MenuSearchInput;\n\tprivate tui: TUI;\n\tprivate readonly isPrimeInference: boolean;\n\tprivate abortController = new AbortController();\n\tprivate inputResolver?: (value: string) => void;\n\tprivate inputRejecter?: (error: Error) => void;\n\t// True only while the editable paste field is actually shown in the panel.\n\t// Tracks visibility directly rather than inferring it from inputResolver,\n\t// which can outlive the field when a new screen clears the content.\n\tprivate inputVisible = false;\n\tprivate continueResolver?: () => void;\n\tprivate continueRejecter?: (error: Error) => void;\n\tprivate authUrl?: string;\n\tprivate authActions?: Text;\n\n\t// Focusable implementation - propagate to input 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.input.focused = value;\n\t}\n\n\tconstructor(\n\t\ttui: TUI,\n\t\tproviderId: string,\n\t\tprivate onComplete: (success: boolean, message?: string) => void,\n\t\tproviderNameOverride?: string,\n\t\ttitleOverride?: string,\n\t) {\n\t\tsuper();\n\t\tthis.tui = tui;\n\n\t\tconst providerInfo = getOAuthProviders().find((p) => p.id === providerId);\n\t\tconst providerName = providerNameOverride || providerInfo?.name || providerId;\n\t\tthis.isPrimeInference = providerId === PRIME_INFERENCE_PROVIDER_ID;\n\t\tconst title = titleOverride ?? `Login to ${providerName}`;\n\n\t\tconst panel = new MenuPanel({\n\t\t\ttitle: this.isPrimeInference ? \"\" : title,\n\t\t\tsubtitle: this.isPrimeInference ? undefined : \"Complete this step to continue setup.\",\n\t\t});\n\t\tthis.addChild(panel);\n\n\t\t// Dynamic content area\n\t\tthis.contentContainer = new Container();\n\t\tpanel.addChild(this.contentContainer);\n\n\t\t// Input (always present, used when needed)\n\t\tthis.input = new MenuSearchInput(\"Paste value\");\n\t\tthis.input.onSubmit = () => {\n\t\t\tif (this.inputResolver) {\n\t\t\t\tthis.inputResolver(this.input.getValue());\n\t\t\t\tthis.inputResolver = undefined;\n\t\t\t\tthis.inputRejecter = undefined;\n\t\t\t}\n\t\t};\n\t}\n\n\tget signal(): AbortSignal {\n\t\treturn this.abortController.signal;\n\t}\n\n\tprivate cancel(): void {\n\t\tthis.abortController.abort();\n\t\tif (this.inputRejecter) {\n\t\t\tthis.inputRejecter(new Error(\"Login cancelled\"));\n\t\t\tthis.inputResolver = undefined;\n\t\t\tthis.inputRejecter = undefined;\n\t\t}\n\t\tif (this.continueRejecter) {\n\t\t\tthis.continueRejecter(new Error(\"Login cancelled\"));\n\t\t\tthis.continueResolver = undefined;\n\t\t\tthis.continueRejecter = undefined;\n\t\t}\n\t\tthis.onComplete(false, \"Login cancelled\");\n\t}\n\n\t/**\n\t * Called by onAuth callback - show URL and optional instructions\n\t */\n\tshowAuth(url: string, instructions?: string): void {\n\t\tthis.startContent();\n\t\tthis.authUrl = url;\n\t\tthis.addSectionTitle(\"Browser sign-in\");\n\t\tthis.addMutedText(\"The sign-in page should already be opening. If it did not open, use the link below.\");\n\t\tthis.contentContainer.addChild(new Spacer(1));\n\t\tthis.addLabel(\"Sign-in link\");\n\t\tconst linkedUrl = getCapabilities().hyperlinks ? `\\x1b]8;;${url}\\x07${url}\\x1b]8;;\\x07` : url;\n\t\tthis.contentContainer.addChild(new Text(theme.fg(\"text\", linkedUrl), 0, 0));\n\t\tthis.authActions = new Text(this.getAuthActionsText(), 0, 0);\n\t\tthis.contentContainer.addChild(this.authActions);\n\n\t\tif (instructions) {\n\t\t\tthis.contentContainer.addChild(new Spacer(1));\n\t\t\tthis.addInstructions(instructions);\n\t\t}\n\n\t\t// Try to open browser\n\t\tconst [command, ...args] =\n\t\t\tprocess.platform === \"darwin\"\n\t\t\t\t? [\"open\", url]\n\t\t\t\t: process.platform === \"win32\"\n\t\t\t\t\t? [\n\t\t\t\t\t\t\twin32.join(process.env.SystemRoot ?? \"C:\\\\Windows\", \"System32\", \"rundll32.exe\"),\n\t\t\t\t\t\t\t\"url.dll,FileProtocolHandler\",\n\t\t\t\t\t\t\turl,\n\t\t\t\t\t\t]\n\t\t\t\t\t: [\"xdg-open\", url];\n\t\texecFile(command, args, () => {});\n\n\t\tthis.tui.requestRender();\n\t}\n\n\t/**\n\t * Show input for manual code/URL entry (for callback server providers)\n\t */\n\tshowManualInput(prompt: string): Promise<string> {\n\t\tthis.addSectionSpacer();\n\t\tthis.addSectionTitle(\"Manual fallback\");\n\t\tthis.addMutedText(prompt);\n\t\tthis.contentContainer.addChild(this.input);\n\t\tthis.inputVisible = true;\n\t\tthis.authActions?.setText(this.getAuthActionsText());\n\t\tthis.contentContainer.addChild(new Text(theme.fg(\"muted\", keyHint(\"tui.select.cancel\", \"cancel\")), 0, 0));\n\t\tthis.tui.requestRender();\n\n\t\treturn this.waitForInput();\n\t}\n\n\t/**\n\t * Wait for the next submission of the already-visible input.\n\t */\n\twaitForInput(): Promise<string> {\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tthis.inputResolver = resolve;\n\t\t\tthis.inputRejecter = reject;\n\t\t});\n\t}\n\n\t/**\n\t * Called by onPrompt callback - show prompt and wait for input\n\t * Note: Does NOT clear content, appends to existing (preserves URL from showAuth)\n\t */\n\tshowPrompt(message: string, placeholder?: string): Promise<string> {\n\t\tthis.addSectionSpacer();\n\t\tthis.addSectionTitle(message);\n\t\tif (placeholder) {\n\t\t\tthis.contentContainer.addChild(new Text(theme.fg(\"muted\", `e.g., ${placeholder}`), 0, 0));\n\t\t}\n\t\tthis.contentContainer.addChild(this.input);\n\t\tthis.inputVisible = true;\n\t\tthis.authActions?.setText(this.getAuthActionsText());\n\t\tthis.contentContainer.addChild(\n\t\t\tnew Text(\n\t\t\t\ttheme.fg(\"muted\", `${keyHint(\"tui.select.confirm\", \"submit\")} ${keyHint(\"tui.select.cancel\", \"cancel\")}`),\n\t\t\t\t0,\n\t\t\t),\n\t\t);\n\n\t\tthis.input.setValue(\"\");\n\t\tthis.tui.requestRender();\n\n\t\treturn this.waitForInput();\n\t}\n\n\t/**\n\t * Show informational text without prompting for input.\n\t */\n\tshowInfo(lines: string[]): void {\n\t\tthis.startContent();\n\t\tfor (const line of lines) {\n\t\t\tthis.contentContainer.addChild(new Text(line, 0, 0));\n\t\t}\n\t\tthis.contentContainer.addChild(new Spacer(1));\n\t\tthis.contentContainer.addChild(new Text(theme.fg(\"muted\", keyHint(\"tui.select.cancel\", \"close\")), 0, 0));\n\t\tthis.tui.requestRender();\n\t}\n\n\tshowContinueInfo(lines: string[]): Promise<void> {\n\t\tthis.startContent();\n\t\tfor (const line of lines) {\n\t\t\tthis.contentContainer.addChild(new Text(line, 0, 0));\n\t\t}\n\t\tthis.contentContainer.addChild(new Spacer(1));\n\t\tthis.contentContainer.addChild(\n\t\t\tnew Text(\n\t\t\t\ttheme.fg(\n\t\t\t\t\t\"muted\",\n\t\t\t\t\t`${keyHint(\"tui.select.confirm\", \"continue\")} ${keyHint(\"tui.select.cancel\", \"cancel\")}`,\n\t\t\t\t),\n\t\t\t\t0,\n\t\t\t\t0,\n\t\t\t),\n\t\t);\n\t\tthis.tui.requestRender();\n\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tthis.continueResolver = resolve;\n\t\t\tthis.continueRejecter = reject;\n\t\t});\n\t}\n\n\t/**\n\t * Show waiting message (for polling flows like GitHub Copilot)\n\t */\n\tshowWaiting(message: string): void {\n\t\tthis.addSectionSpacer();\n\t\tthis.contentContainer.addChild(new Text(theme.fg(\"accent\", message), 0, 0));\n\t\tthis.contentContainer.addChild(new Text(theme.fg(\"muted\", keyHint(\"tui.select.cancel\", \"cancel\")), 0, 0));\n\t\tthis.tui.requestRender();\n\t}\n\n\t/**\n\t * Called by onProgress callback\n\t */\n\tshowProgress(message: string): void {\n\t\tif (this.contentContainer.children.length === 0) {\n\t\t\tthis.startContent();\n\t\t\tthis.addSectionTitle(\"Preparing authentication\");\n\t\t}\n\t\tthis.contentContainer.addChild(new Text(theme.fg(\"muted\", message), 0, 0));\n\t\tthis.tui.requestRender();\n\t}\n\n\tprivate startContent(): void {\n\t\tthis.contentContainer.clear();\n\t\tthis.authUrl = undefined;\n\t\tthis.authActions = undefined;\n\t\t// The cleared panel no longer shows the paste field.\n\t\tthis.inputVisible = false;\n\t\tif (this.isPrimeInference) {\n\t\t\tthis.contentContainer.addChild(new PrimeLoginHeader());\n\t\t\tthis.contentContainer.addChild(new Spacer(1));\n\t\t\treturn;\n\t\t}\n\t\tthis.contentContainer.addChild(new Spacer(1));\n\t}\n\n\tprivate addSectionSpacer(): void {\n\t\tif (this.contentContainer.children.length === 0) {\n\t\t\tthis.startContent();\n\t\t\treturn;\n\t\t}\n\t\tthis.contentContainer.addChild(new Spacer(1));\n\t}\n\n\tprivate addInstructions(instructions: string): void {\n\t\tconst codeMatch = /^(?:Code|Enter code):\\s*(.+)$/i.exec(instructions.trim());\n\t\tif (codeMatch?.[1]) {\n\t\t\tthis.addLabel(\"Verification code\");\n\t\t\tthis.contentContainer.addChild(new Text(theme.bold(theme.fg(\"text\", codeMatch[1])), 0, 0));\n\t\t\treturn;\n\t\t}\n\t\tthis.addLabel(\"Next step\");\n\t\tthis.contentContainer.addChild(new Text(theme.fg(\"text\", instructions), 0, 0));\n\t}\n\n\tprivate addSectionTitle(text: string): void {\n\t\tthis.contentContainer.addChild(new Text(theme.bold(theme.fg(\"text\", text)), 0, 0));\n\t}\n\n\tprivate addLabel(text: string): void {\n\t\tthis.contentContainer.addChild(new Text(theme.fg(\"muted\", text), 0, 0));\n\t}\n\n\tprivate addMutedText(text: string): void {\n\t\tthis.contentContainer.addChild(new Text(theme.fg(\"muted\", text), 0, 0));\n\t}\n\n\tprivate getAuthActionsText(status?: \"copied\" | \"failed\"): string {\n\t\tconst configuredCopyKeys = getKeybindings().getKeys(\"app.clipboard.copyLoginUrl\");\n\t\tconst copyKeys = this.inputVisible\n\t\t\t? configuredCopyKeys.filter((key) => !isTextEntryKeybinding(key))\n\t\t\t: configuredCopyKeys.slice(0, 1);\n\t\tconst copyHint =\n\t\t\tcopyKeys.length > 0\n\t\t\t\t? theme.fg(\"dim\", formatKeyText(copyKeys.join(\"/\"))) +\n\t\t\t\t\ttheme.fg(\"muted\", ` ${status === \"failed\" ? \"retry\" : \"copy\"}`)\n\t\t\t\t: undefined;\n\t\tconst statusText =\n\t\t\tstatus === \"copied\"\n\t\t\t\t? theme.fg(\"success\", \"Copied sign-in link\")\n\t\t\t\t: status === \"failed\"\n\t\t\t\t\t? theme.fg(\"error\", \"Failed to copy sign-in link\")\n\t\t\t\t\t: undefined;\n\t\treturn [statusText, copyHint, keyHint(\"tui.select.cancel\", \"cancel\")]\n\t\t\t.filter((part): part is string => part !== undefined)\n\t\t\t.join(\" \");\n\t}\n\n\tprivate async copyAuthUrl(): Promise<void> {\n\t\tconst url = this.authUrl;\n\t\tconst actions = this.authActions;\n\t\tif (!url || !actions) return;\n\n\t\ttry {\n\t\t\tawait copyToClipboard(url);\n\t\t\tif (this.authUrl === url && this.authActions === actions) {\n\t\t\t\tactions.setText(this.getAuthActionsText(\"copied\"));\n\t\t\t\tthis.tui.requestRender();\n\t\t\t}\n\t\t} catch {\n\t\t\tif (this.authUrl === url && this.authActions === actions) {\n\t\t\t\tactions.setText(this.getAuthActionsText(\"failed\"));\n\t\t\t\tthis.tui.requestRender();\n\t\t\t}\n\t\t}\n\t}\n\n\thandleInput(data: string): void {\n\t\tconst kb = getKeybindings();\n\n\t\tif (\n\t\t\tthis.authUrl &&\n\t\t\tkb.matches(data, \"app.clipboard.copyLoginUrl\") &&\n\t\t\t(!this.inputVisible || !isPrintableInput(data))\n\t\t) {\n\t\t\tvoid this.copyAuthUrl();\n\t\t\treturn;\n\t\t}\n\n\t\t// Left arrow acts as \"back\" like Esc. While the editable field is actually\n\t\t// shown, only treat it as back at the start of the text so left still moves\n\t\t// the cursor mid-edit; on info/continue screens there is no field to guard.\n\t\tconst backGuardInput = this.inputVisible ? this.input : undefined;\n\t\tif (kb.matches(data, \"tui.select.cancel\") || shouldTreatAsBack(data, backGuardInput)) {\n\t\t\tthis.cancel();\n\t\t\treturn;\n\t\t}\n\n\t\tif (this.continueResolver && kb.matches(data, \"tui.select.confirm\")) {\n\t\t\tconst resolve = this.continueResolver;\n\t\t\tthis.continueResolver = undefined;\n\t\t\tthis.continueRejecter = undefined;\n\t\t\tresolve();\n\t\t\treturn;\n\t\t}\n\n\t\t// Pass to input\n\t\tthis.input.handleInput(data);\n\t}\n}\n"]}
1
+ {"version":3,"file":"login-dialog.js","sourceRoot":"","sources":["../../../../src/modes/interactive/components/login-dialog.ts"],"names":[],"mappings":"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","sourcesContent":["import { win32 } from \"node:path\";\nimport { getOAuthProviders } from \"@ponythewhite/base-context-ai/oauth\";\nimport {\n\tContainer,\n\ttype Focusable,\n\tgetCapabilities,\n\tgetKeybindings,\n\tSpacer,\n\tText,\n\ttype TUI,\n} from \"@ponythewhite/base-context-tui\";\nimport { execFile } from \"child_process\";\nimport { copyToClipboard } from \"../../../utils/clipboard.js\";\nimport { theme } from \"../theme/theme.js\";\nimport { formatKeyText, keyHint } from \"./keybinding-hints.js\";\nimport { MenuPanel, MenuSearchInput } from \"./menu-panel.js\";\nimport { shouldTreatAsBack } from \"./modal-back.js\";\n\nfunction isTextEntryKeybinding(key: string): boolean {\n\tconst parts = key.toLowerCase().split(\"+\");\n\tconst keyPart = parts.at(-1);\n\treturn !parts.includes(\"ctrl\") && !parts.includes(\"alt\") && (keyPart === \"space\" || keyPart?.length === 1);\n}\n\nfunction isPrintableInput(data: string): boolean {\n\treturn data.length === 1 && data >= \" \" && data !== \"\\x7f\";\n}\n\n/**\n * Login dialog component - replaces editor during OAuth login flow\n */\nexport class LoginDialogComponent extends Container implements Focusable {\n\tprivate contentContainer: Container;\n\tprivate input: MenuSearchInput;\n\tprivate tui: TUI;\n\tprivate abortController = new AbortController();\n\tprivate inputResolver?: (value: string) => void;\n\tprivate inputRejecter?: (error: Error) => void;\n\t// True only while the editable paste field is actually shown in the panel.\n\t// Tracks visibility directly rather than inferring it from inputResolver,\n\t// which can outlive the field when a new screen clears the content.\n\tprivate inputVisible = false;\n\tprivate continueResolver?: () => void;\n\tprivate continueRejecter?: (error: Error) => void;\n\tprivate authUrl?: string;\n\tprivate authActions?: Text;\n\n\t// Focusable implementation - propagate to input 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.input.focused = value;\n\t}\n\n\tconstructor(\n\t\ttui: TUI,\n\t\tproviderId: string,\n\t\tprivate onComplete: (success: boolean, message?: string) => void,\n\t\tproviderNameOverride?: string,\n\t\ttitleOverride?: string,\n\t) {\n\t\tsuper();\n\t\tthis.tui = tui;\n\n\t\tconst providerInfo = getOAuthProviders().find((p) => p.id === providerId);\n\t\tconst providerName = providerNameOverride || providerInfo?.name || providerId;\n\t\tconst title = titleOverride ?? `Login to ${providerName}`;\n\n\t\tconst panel = new MenuPanel({\n\t\t\ttitle,\n\t\t\tsubtitle: \"Complete this step to continue setup.\",\n\t\t});\n\t\tthis.addChild(panel);\n\n\t\t// Dynamic content area\n\t\tthis.contentContainer = new Container();\n\t\tpanel.addChild(this.contentContainer);\n\n\t\t// Input (always present, used when needed)\n\t\tthis.input = new MenuSearchInput(\"Paste value\");\n\t\tthis.input.onSubmit = () => {\n\t\t\tif (this.inputResolver) {\n\t\t\t\tthis.inputResolver(this.input.getValue());\n\t\t\t\tthis.inputResolver = undefined;\n\t\t\t\tthis.inputRejecter = undefined;\n\t\t\t}\n\t\t};\n\t}\n\n\tget signal(): AbortSignal {\n\t\treturn this.abortController.signal;\n\t}\n\n\tprivate cancel(): void {\n\t\tthis.abortController.abort();\n\t\tif (this.inputRejecter) {\n\t\t\tthis.inputRejecter(new Error(\"Login cancelled\"));\n\t\t\tthis.inputResolver = undefined;\n\t\t\tthis.inputRejecter = undefined;\n\t\t}\n\t\tif (this.continueRejecter) {\n\t\t\tthis.continueRejecter(new Error(\"Login cancelled\"));\n\t\t\tthis.continueResolver = undefined;\n\t\t\tthis.continueRejecter = undefined;\n\t\t}\n\t\tthis.onComplete(false, \"Login cancelled\");\n\t}\n\n\t/**\n\t * Called by onAuth callback - show URL and optional instructions\n\t */\n\tshowAuth(url: string, instructions?: string): void {\n\t\tthis.startContent();\n\t\tthis.authUrl = url;\n\t\tthis.addSectionTitle(\"Browser sign-in\");\n\t\tthis.addMutedText(\"The sign-in page should already be opening. If it did not open, use the link below.\");\n\t\tthis.contentContainer.addChild(new Spacer(1));\n\t\tthis.addLabel(\"Sign-in link\");\n\t\tconst linkedUrl = getCapabilities().hyperlinks ? `\\x1b]8;;${url}\\x07${url}\\x1b]8;;\\x07` : url;\n\t\tthis.contentContainer.addChild(new Text(theme.fg(\"text\", linkedUrl), 0, 0));\n\t\tthis.authActions = new Text(this.getAuthActionsText(), 0, 0);\n\t\tthis.contentContainer.addChild(this.authActions);\n\n\t\tif (instructions) {\n\t\t\tthis.contentContainer.addChild(new Spacer(1));\n\t\t\tthis.addInstructions(instructions);\n\t\t}\n\n\t\t// Try to open browser\n\t\tconst [command, ...args] =\n\t\t\tprocess.platform === \"darwin\"\n\t\t\t\t? [\"open\", url]\n\t\t\t\t: process.platform === \"win32\"\n\t\t\t\t\t? [\n\t\t\t\t\t\t\twin32.join(process.env.SystemRoot ?? \"C:\\\\Windows\", \"System32\", \"rundll32.exe\"),\n\t\t\t\t\t\t\t\"url.dll,FileProtocolHandler\",\n\t\t\t\t\t\t\turl,\n\t\t\t\t\t\t]\n\t\t\t\t\t: [\"xdg-open\", url];\n\t\texecFile(command, args, () => {});\n\n\t\tthis.tui.requestRender();\n\t}\n\n\t/**\n\t * Show input for manual code/URL entry (for callback server providers)\n\t */\n\tshowManualInput(prompt: string): Promise<string> {\n\t\tthis.addSectionSpacer();\n\t\tthis.addSectionTitle(\"Manual fallback\");\n\t\tthis.addMutedText(prompt);\n\t\tthis.contentContainer.addChild(this.input);\n\t\tthis.inputVisible = true;\n\t\tthis.authActions?.setText(this.getAuthActionsText());\n\t\tthis.contentContainer.addChild(new Text(theme.fg(\"muted\", keyHint(\"tui.select.cancel\", \"cancel\")), 0, 0));\n\t\tthis.tui.requestRender();\n\n\t\treturn this.waitForInput();\n\t}\n\n\t/**\n\t * Wait for the next submission of the already-visible input.\n\t */\n\twaitForInput(): Promise<string> {\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tthis.inputResolver = resolve;\n\t\t\tthis.inputRejecter = reject;\n\t\t});\n\t}\n\n\t/**\n\t * Called by onPrompt callback - show prompt and wait for input\n\t * Note: Does NOT clear content, appends to existing (preserves URL from showAuth)\n\t */\n\tshowPrompt(message: string, placeholder?: string): Promise<string> {\n\t\tthis.addSectionSpacer();\n\t\tthis.addSectionTitle(message);\n\t\tif (placeholder) {\n\t\t\tthis.contentContainer.addChild(new Text(theme.fg(\"muted\", `e.g., ${placeholder}`), 0, 0));\n\t\t}\n\t\tthis.contentContainer.addChild(this.input);\n\t\tthis.inputVisible = true;\n\t\tthis.authActions?.setText(this.getAuthActionsText());\n\t\tthis.contentContainer.addChild(\n\t\t\tnew Text(\n\t\t\t\ttheme.fg(\"muted\", `${keyHint(\"tui.select.confirm\", \"submit\")} ${keyHint(\"tui.select.cancel\", \"cancel\")}`),\n\t\t\t\t0,\n\t\t\t),\n\t\t);\n\n\t\tthis.input.setValue(\"\");\n\t\tthis.tui.requestRender();\n\n\t\treturn this.waitForInput();\n\t}\n\n\t/**\n\t * Show informational text without prompting for input.\n\t */\n\tshowInfo(lines: string[]): void {\n\t\tthis.startContent();\n\t\tfor (const line of lines) {\n\t\t\tthis.contentContainer.addChild(new Text(line, 0, 0));\n\t\t}\n\t\tthis.contentContainer.addChild(new Spacer(1));\n\t\tthis.contentContainer.addChild(new Text(theme.fg(\"muted\", keyHint(\"tui.select.cancel\", \"close\")), 0, 0));\n\t\tthis.tui.requestRender();\n\t}\n\n\tshowContinueInfo(lines: string[]): Promise<void> {\n\t\tthis.startContent();\n\t\tfor (const line of lines) {\n\t\t\tthis.contentContainer.addChild(new Text(line, 0, 0));\n\t\t}\n\t\tthis.contentContainer.addChild(new Spacer(1));\n\t\tthis.contentContainer.addChild(\n\t\t\tnew Text(\n\t\t\t\ttheme.fg(\n\t\t\t\t\t\"muted\",\n\t\t\t\t\t`${keyHint(\"tui.select.confirm\", \"continue\")} ${keyHint(\"tui.select.cancel\", \"cancel\")}`,\n\t\t\t\t),\n\t\t\t\t0,\n\t\t\t\t0,\n\t\t\t),\n\t\t);\n\t\tthis.tui.requestRender();\n\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tthis.continueResolver = resolve;\n\t\t\tthis.continueRejecter = reject;\n\t\t});\n\t}\n\n\t/**\n\t * Show waiting message (for polling flows like GitHub Copilot)\n\t */\n\tshowWaiting(message: string): void {\n\t\tthis.addSectionSpacer();\n\t\tthis.contentContainer.addChild(new Text(theme.fg(\"accent\", message), 0, 0));\n\t\tthis.contentContainer.addChild(new Text(theme.fg(\"muted\", keyHint(\"tui.select.cancel\", \"cancel\")), 0, 0));\n\t\tthis.tui.requestRender();\n\t}\n\n\t/**\n\t * Called by onProgress callback\n\t */\n\tshowProgress(message: string): void {\n\t\tif (this.contentContainer.children.length === 0) {\n\t\t\tthis.startContent();\n\t\t\tthis.addSectionTitle(\"Preparing authentication\");\n\t\t}\n\t\tthis.contentContainer.addChild(new Text(theme.fg(\"muted\", message), 0, 0));\n\t\tthis.tui.requestRender();\n\t}\n\n\tprivate startContent(): void {\n\t\tthis.contentContainer.clear();\n\t\tthis.authUrl = undefined;\n\t\tthis.authActions = undefined;\n\t\t// The cleared panel no longer shows the paste field.\n\t\tthis.inputVisible = false;\n\t\tthis.contentContainer.addChild(new Spacer(1));\n\t}\n\n\tprivate addSectionSpacer(): void {\n\t\tif (this.contentContainer.children.length === 0) {\n\t\t\tthis.startContent();\n\t\t\treturn;\n\t\t}\n\t\tthis.contentContainer.addChild(new Spacer(1));\n\t}\n\n\tprivate addInstructions(instructions: string): void {\n\t\tconst codeMatch = /^(?:Code|Enter code):\\s*(.+)$/i.exec(instructions.trim());\n\t\tif (codeMatch?.[1]) {\n\t\t\tthis.addLabel(\"Verification code\");\n\t\t\tthis.contentContainer.addChild(new Text(theme.bold(theme.fg(\"text\", codeMatch[1])), 0, 0));\n\t\t\treturn;\n\t\t}\n\t\tthis.addLabel(\"Next step\");\n\t\tthis.contentContainer.addChild(new Text(theme.fg(\"text\", instructions), 0, 0));\n\t}\n\n\tprivate addSectionTitle(text: string): void {\n\t\tthis.contentContainer.addChild(new Text(theme.bold(theme.fg(\"text\", text)), 0, 0));\n\t}\n\n\tprivate addLabel(text: string): void {\n\t\tthis.contentContainer.addChild(new Text(theme.fg(\"muted\", text), 0, 0));\n\t}\n\n\tprivate addMutedText(text: string): void {\n\t\tthis.contentContainer.addChild(new Text(theme.fg(\"muted\", text), 0, 0));\n\t}\n\n\tprivate getAuthActionsText(status?: \"copied\" | \"failed\"): string {\n\t\tconst configuredCopyKeys = getKeybindings().getKeys(\"app.clipboard.copyLoginUrl\");\n\t\tconst copyKeys = this.inputVisible\n\t\t\t? configuredCopyKeys.filter((key) => !isTextEntryKeybinding(key))\n\t\t\t: configuredCopyKeys.slice(0, 1);\n\t\tconst copyHint =\n\t\t\tcopyKeys.length > 0\n\t\t\t\t? theme.fg(\"dim\", formatKeyText(copyKeys.join(\"/\"))) +\n\t\t\t\t\ttheme.fg(\"muted\", ` ${status === \"failed\" ? \"retry\" : \"copy\"}`)\n\t\t\t\t: undefined;\n\t\tconst statusText =\n\t\t\tstatus === \"copied\"\n\t\t\t\t? theme.fg(\"success\", \"Copied sign-in link\")\n\t\t\t\t: status === \"failed\"\n\t\t\t\t\t? theme.fg(\"error\", \"Failed to copy sign-in link\")\n\t\t\t\t\t: undefined;\n\t\treturn [statusText, copyHint, keyHint(\"tui.select.cancel\", \"cancel\")]\n\t\t\t.filter((part): part is string => part !== undefined)\n\t\t\t.join(\" \");\n\t}\n\n\tprivate async copyAuthUrl(): Promise<void> {\n\t\tconst url = this.authUrl;\n\t\tconst actions = this.authActions;\n\t\tif (!url || !actions) return;\n\n\t\ttry {\n\t\t\tawait copyToClipboard(url);\n\t\t\tif (this.authUrl === url && this.authActions === actions) {\n\t\t\t\tactions.setText(this.getAuthActionsText(\"copied\"));\n\t\t\t\tthis.tui.requestRender();\n\t\t\t}\n\t\t} catch {\n\t\t\tif (this.authUrl === url && this.authActions === actions) {\n\t\t\t\tactions.setText(this.getAuthActionsText(\"failed\"));\n\t\t\t\tthis.tui.requestRender();\n\t\t\t}\n\t\t}\n\t}\n\n\thandleInput(data: string): void {\n\t\tconst kb = getKeybindings();\n\n\t\tif (\n\t\t\tthis.authUrl &&\n\t\t\tkb.matches(data, \"app.clipboard.copyLoginUrl\") &&\n\t\t\t(!this.inputVisible || !isPrintableInput(data))\n\t\t) {\n\t\t\tvoid this.copyAuthUrl();\n\t\t\treturn;\n\t\t}\n\n\t\t// Left arrow acts as \"back\" like Esc. While the editable field is actually\n\t\t// shown, only treat it as back at the start of the text so left still moves\n\t\t// the cursor mid-edit; on info/continue screens there is no field to guard.\n\t\tconst backGuardInput = this.inputVisible ? this.input : undefined;\n\t\tif (kb.matches(data, \"tui.select.cancel\") || shouldTreatAsBack(data, backGuardInput)) {\n\t\t\tthis.cancel();\n\t\t\treturn;\n\t\t}\n\n\t\tif (this.continueResolver && kb.matches(data, \"tui.select.confirm\")) {\n\t\t\tconst resolve = this.continueResolver;\n\t\t\tthis.continueResolver = undefined;\n\t\t\tthis.continueRejecter = undefined;\n\t\t\tresolve();\n\t\t\treturn;\n\t\t}\n\n\t\t// Pass to input\n\t\tthis.input.handleInput(data);\n\t}\n}\n"]}
@@ -36,6 +36,7 @@ export declare class ModelSelectorComponent extends Container implements Focusab
36
36
  private onCancelCallback;
37
37
  private availableModels?;
38
38
  private configuredProviders?;
39
+ private providerFilter?;
39
40
  private recentRank;
40
41
  private errorMessage?;
41
42
  private tui;
@@ -51,6 +52,7 @@ export declare class ModelSelectorComponent extends Container implements Focusab
51
52
  private readonly viewport;
52
53
  private readonly getHeaderRows;
53
54
  constructor(tui: TUI, currentModel: Model<any> | undefined, modelRegistry: ModelRegistry, scopedModels: ReadonlyArray<ScopedModelItem>, onSelect: (model: Model<any>) => void, onCancel: () => void, initialSearchInput?: string, options?: ModelSelectorOptions);
55
+ setProviderFilter(provider: string): void;
54
56
  updateAvailableModels(availableModels: ReadonlyArray<Model<any>>): void;
55
57
  updateState(currentModel: Model<any> | undefined, availableModels?: readonly Model<any>[] | undefined, configuredProviders?: ReadonlySet<string> | undefined): void;
56
58
  private loadModels;
@@ -1 +1 @@
1
- {"version":3,"file":"model-selector.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/model-selector.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,KAAK,EAAkB,MAAM,+BAA+B,CAAC;AAC3E,OAAO,EACN,KAAK,SAAS,EACd,SAAS,EACT,KAAK,SAAS,EAKd,KAAK,GAAG,EACR,MAAM,gCAAgC,CAAC;AACxC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iCAAiC,CAAC;AAGrE,OAAO,EAKN,eAAe,EAEf,MAAM,iBAAiB,CAAC;AASzB,UAAU,eAAe;IACxB,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC;IAClB,aAAa,CAAC,EAAE,MAAM,CAAC;CACvB;AAyED,MAAM,WAAW,oBAAoB;IACpC,eAAe,CAAC,EAAE,aAAa,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC;IAC5C,mBAAmB,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IAC1C,MAAM,CAAC,EAAE,SAAS,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,MAAM,CAAC;IAC7B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,MAAM,CAAC;IACvB,YAAY,CAAC,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;CACrC;AAaD;;GAEG;AACH,qBAAa,sBAAuB,SAAQ,SAAU,YAAW,SAAS;IACzE,OAAO,CAAC,WAAW,CAAkB;IAGrC,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,SAAS,CAAmB;IACpC,OAAO,CAAC,gBAAgB,CAAmB;IAC3C,OAAO,CAAC,YAAY,CAAmB;IACvC,OAAO,CAAC,cAAc,CAAmB;IACzC,OAAO,CAAC,aAAa,CAAa;IAClC,OAAO,CAAC,WAAW,CAAM;IACzB,OAAO,CAAC,YAAY,CAAC,CAAa;IAClC,OAAO,CAAC,aAAa,CAAgB;IACrC,OAAO,CAAC,gBAAgB,CAA8B;IACtD,OAAO,CAAC,gBAAgB,CAAa;IACrC,OAAO,CAAC,eAAe,CAAC,CAA4B;IACpD,OAAO,CAAC,mBAAmB,CAAC,CAAsB;IAClD,OAAO,CAAC,UAAU,CAAsB;IACxC,OAAO,CAAC,YAAY,CAAC,CAAS;IAC9B,OAAO,CAAC,GAAG,CAAM;IACjB,OAAO,CAAC,YAAY,CAAiC;IACrD,OAAO,CAAC,KAAK,CAAqB;IAClC,OAAO,CAAC,SAAS,CAAC,CAAO;IACzB,OAAO,CAAC,aAAa,CAAC,CAAO;IAC7B,OAAO,CAAC,KAAK,CAAY;IACzB,OAAO,CAAC,mBAAmB,CAAY;IACvC,OAAO,CAAC,WAAW,CAAC,CAAO;IAC3B,OAAO,CAAC,UAAU,CAKf;IACH,OAAO,CAAC,mBAAmB,CAAM;IACjC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAuB;IAChD,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAe;IAE7C,YACC,GAAG,EAAE,GAAG,EACR,YAAY,EAAE,KAAK,CAAC,GAAG,CAAC,GAAG,SAAS,EACpC,aAAa,EAAE,aAAa,EAC5B,YAAY,EAAE,aAAa,CAAC,eAAe,CAAC,EAC5C,QAAQ,EAAE,CAAC,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,KAAK,IAAI,EACrC,QAAQ,EAAE,MAAM,IAAI,EACpB,kBAAkB,CAAC,EAAE,MAAM,EAC3B,OAAO,GAAE,oBAAyB,EA8DlC;IAED,qBAAqB,CAAC,eAAe,EAAE,aAAa,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,IAAI,CAEtE;IAED,WAAW,CACV,YAAY,EAAE,KAAK,CAAC,GAAG,CAAC,GAAG,SAAS,EACpC,eAAe,oCAAuB,EACtC,mBAAmB,kCAA2B,GAC5C,IAAI,CAmBN;IAED,OAAO,CAAC,UAAU;IAsDlB,OAAO,CAAC,WAAW;IAInB,OAAO,CAAC,mBAAmB;IAK3B,OAAO,CAAC,YAAY;IAKpB,OAAO,CAAC,oBAAoB;IAI5B,OAAO,CAAC,UAAU;IAoBlB,OAAO,CAAC,YAAY;IAMpB,OAAO,CAAC,gBAAgB;IAIxB,OAAO,CAAC,QAAQ;IAYhB,OAAO,CAAC,YAAY;IA0BX,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAOvC;IAED,OAAO,CAAC,UAAU;IA4DlB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAuCjC;IAED,OAAO,CAAC,YAAY;IAIpB,OAAO,CAAC,aAAa;IAQrB,OAAO,CAAC,kBAAkB;IAI1B,cAAc,IAAI,eAAe,CAEhC;IAED,OAAO,CAAC,sBAAsB;IA0C9B,OAAO,CAAC,oBAAoB;IAI5B,OAAO,CAAC,yBAAyB;IAIjC,OAAO,CAAC,OAAO;CAIf","sourcesContent":["import { type Model, modelsAreEqual } from \"@ponythewhite/base-context-ai\";\nimport {\n\ttype Component,\n\tContainer,\n\ttype Focusable,\n\tfuzzyMatch,\n\tgetKeybindings,\n\tSpacer,\n\tText,\n\ttype TUI,\n} from \"@ponythewhite/base-context-tui\";\nimport type { ModelRegistry } from \"../../../core/model-registry.js\";\nimport { theme } from \"../theme/theme.js\";\nimport { keyHint } from \"./keybinding-hints.js\";\nimport {\n\tgetMenuListLayout,\n\tMenuList,\n\tMenuPanel,\n\tMenuRow,\n\tMenuSearchInput,\n\ttype MenuViewportProvider,\n} from \"./menu-panel.js\";\nimport { shouldTreatAsBack } from \"./modal-back.js\";\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\nenum ModelSearchMatchQuality {\n\tExactShortId,\n\tExactFullId,\n\tPrefixOrToken,\n\tFuzzy,\n}\n\ninterface ModelSearchMatch {\n\tquality: ModelSearchMatchQuality;\n\tscore: number;\n}\n\nfunction normalizeModelSearchText(value: string): string {\n\treturn value.toLowerCase().replace(/[\\s\\-_.:/]+/g, \"\");\n}\n\nfunction getModelSearchFields(item: ModelItem): { shortId: string; fullIds: string[]; all: string[] } {\n\tconst shortId = item.id.slice(item.id.lastIndexOf(\"/\") + 1);\n\tconst fullIds = [item.id, `${item.provider}/${item.id}`];\n\treturn {\n\t\tshortId,\n\t\tfullIds,\n\t\tall: [shortId, ...fullIds, item.model.name, item.provider],\n\t};\n}\n\nfunction getBestFuzzyScore(queryTokens: string[], fields: string[]): number | null {\n\tlet total = 0;\n\tfor (const token of queryTokens) {\n\t\tlet best = Number.POSITIVE_INFINITY;\n\t\tfor (const field of fields) {\n\t\t\tconst match = fuzzyMatch(token, field);\n\t\t\tif (match.matches) best = Math.min(best, match.score);\n\t\t}\n\t\tif (!Number.isFinite(best)) return null;\n\t\ttotal += best;\n\t}\n\treturn total;\n}\n\nfunction scoreModelSearch(item: ModelItem, query: string): ModelSearchMatch | null {\n\tconst queryTokens = query.trim().split(/\\s+/);\n\tconst normalizedQuery = normalizeModelSearchText(query);\n\tconst normalizedTokens = queryTokens.map(normalizeModelSearchText).filter(Boolean);\n\tif (!normalizedQuery || normalizedTokens.length === 0) return null;\n\n\tconst fields = getModelSearchFields(item);\n\tif (normalizeModelSearchText(fields.shortId) === normalizedQuery) {\n\t\treturn { quality: ModelSearchMatchQuality.ExactShortId, score: 0 };\n\t}\n\tif (fields.fullIds.some((field) => normalizeModelSearchText(field) === normalizedQuery)) {\n\t\treturn { quality: ModelSearchMatchQuality.ExactFullId, score: 0 };\n\t}\n\n\tconst normalizedFields = fields.all.map(normalizeModelSearchText);\n\tconst fieldTokens = fields.all\n\t\t.flatMap((field) => field.split(/[\\s/_-]+/))\n\t\t.map(normalizeModelSearchText)\n\t\t.filter(Boolean);\n\tconst fuzzyScore = getBestFuzzyScore(normalizedTokens, normalizedFields);\n\tconst isPrefixOrToken = normalizedTokens.every(\n\t\t(token) =>\n\t\t\tnormalizedFields.some((field) => field.startsWith(token)) ||\n\t\t\tfieldTokens.some((field) => field.startsWith(token)),\n\t);\n\tif (isPrefixOrToken && fuzzyScore !== null) {\n\t\treturn { quality: ModelSearchMatchQuality.PrefixOrToken, score: fuzzyScore };\n\t}\n\treturn fuzzyScore === null ? null : { quality: ModelSearchMatchQuality.Fuzzy, score: fuzzyScore };\n}\n\nexport interface ModelSelectorOptions {\n\tavailableModels?: ReadonlyArray<Model<any>>;\n\tconfiguredProviders?: ReadonlySet<string>;\n\theader?: Component;\n\tgetHeaderRows?: () => number;\n\tsubtitle?: string;\n\tgetRows?: () => number;\n\trecentModels?: ReadonlyArray<string>;\n}\n\ntype ModelScope = \"all\" | \"scoped\";\n\nconst PREFERRED_VISIBLE_MODELS = 10;\nconst MODEL_LIST_RESERVED_ROWS = {\n\tbase: 7,\n\tdetail: 2,\n};\nconst MODEL_SCROLL_INDICATOR_ROWS = 1;\nconst MODEL_HELP_MIN_ROWS = 12;\nconst MODEL_DETAIL_MIN_ROWS = 14;\n\n/**\n * Component that renders a model selector with search\n */\nexport class ModelSelectorComponent extends Container implements Focusable {\n\tprivate searchInput: MenuSearchInput;\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 allModels: ModelItem[] = [];\n\tprivate scopedModelItems: ModelItem[] = [];\n\tprivate activeModels: ModelItem[] = [];\n\tprivate filteredModels: ModelItem[] = [];\n\tprivate selectedIndex: number = 0;\n\tprivate searchQuery = \"\";\n\tprivate currentModel?: Model<any>;\n\tprivate modelRegistry: ModelRegistry;\n\tprivate onSelectCallback: (model: Model<any>) => void;\n\tprivate onCancelCallback: () => void;\n\tprivate availableModels?: ReadonlyArray<Model<any>>;\n\tprivate configuredProviders?: ReadonlySet<string>;\n\tprivate recentRank: Map<string, number>;\n\tprivate errorMessage?: string;\n\tprivate tui: TUI;\n\tprivate scopedModels: ReadonlyArray<ScopedModelItem>;\n\tprivate scope: ModelScope = \"all\";\n\tprivate scopeText?: Text;\n\tprivate scopeHintText?: Text;\n\tprivate panel: MenuPanel;\n\tprivate headerHelpContainer: Container;\n\tprivate warningText?: Text;\n\tprivate listLayout = getMenuListLayout({\n\t\tpreferredVisibleItems: PREFERRED_VISIBLE_MODELS,\n\t\treservedRows: MODEL_LIST_RESERVED_ROWS.base,\n\t\tcomfortableItemRows: 3,\n\t\tcompactItemRows: 2,\n\t});\n\tprivate responsiveLayoutKey = \"\";\n\tprivate readonly viewport: MenuViewportProvider;\n\tprivate readonly getHeaderRows: () => number;\n\n\tconstructor(\n\t\ttui: TUI,\n\t\tcurrentModel: Model<any> | undefined,\n\t\tmodelRegistry: ModelRegistry,\n\t\tscopedModels: ReadonlyArray<ScopedModelItem>,\n\t\tonSelect: (model: Model<any>) => void,\n\t\tonCancel: () => void,\n\t\tinitialSearchInput?: string,\n\t\toptions: ModelSelectorOptions = {},\n\t) {\n\t\tsuper();\n\n\t\tthis.tui = tui;\n\t\tthis.currentModel = currentModel;\n\t\tthis.modelRegistry = modelRegistry;\n\t\tthis.scopedModels = scopedModels;\n\t\tthis.scope = scopedModels.length > 0 ? \"scoped\" : \"all\";\n\t\tthis.onSelectCallback = onSelect;\n\t\tthis.onCancelCallback = onCancel;\n\t\tthis.availableModels = options.availableModels;\n\t\tthis.configuredProviders = options.configuredProviders;\n\t\tthis.recentRank = new Map((options.recentModels ?? []).map((key, i) => [key, i]));\n\t\tthis.viewport = { getRows: options.getRows };\n\t\tthis.getHeaderRows = options.header ? (options.getHeaderRows ?? (() => 2)) : () => 0;\n\n\t\tthis.panel = new MenuPanel({\n\t\t\ttitle: \"Models\",\n\t\t\tsubtitle: options.subtitle ?? \"All models across supported providers.\",\n\t\t});\n\t\tthis.addChild(this.panel);\n\t\tif (options.header) {\n\t\t\tthis.panel.addChild(options.header);\n\t\t\tthis.panel.addChild(new Spacer(1));\n\t\t}\n\n\t\t// Add hint about model filtering\n\t\tif (scopedModels.length > 0) {\n\t\t\tthis.scopeText = new Text(this.getScopeText(), 0, 0);\n\t\t\tthis.scopeHintText = new Text(this.getScopeHintText(), 0, 0);\n\t\t} else {\n\t\t\tconst hintText = \"Signed-in providers first. Other models prompt sign-in.\";\n\t\t\tthis.warningText = new Text(theme.fg(\"muted\", hintText), 0, 0);\n\t\t}\n\t\tthis.headerHelpContainer = new Container();\n\t\tthis.panel.addChild(this.headerHelpContainer);\n\n\t\t// Create search input\n\t\tthis.searchInput = new MenuSearchInput(\"Search models\");\n\t\tif (initialSearchInput) {\n\t\t\tthis.searchInput.setValue(initialSearchInput);\n\t\t}\n\t\tthis.searchInput.onSubmit = () => {\n\t\t\tthis.handleConfirm();\n\t\t};\n\t\tthis.panel.addChild(this.searchInput);\n\n\t\tthis.panel.addChild(new Spacer(1));\n\n\t\t// Create list container\n\t\tthis.listContainer = new MenuList({ compact: () => this.listLayout.compact });\n\t\tthis.panel.addChild(this.listContainer);\n\t\tthis.updateResponsiveLayout();\n\n\t\tthis.loadModels();\n\t\tif (initialSearchInput) {\n\t\t\tthis.filterModels(initialSearchInput);\n\t\t} else {\n\t\t\tthis.updateList();\n\t\t}\n\t\tthis.tui.requestRender();\n\t}\n\n\tupdateAvailableModels(availableModels: ReadonlyArray<Model<any>>): void {\n\t\tthis.updateState(this.currentModel, availableModels);\n\t}\n\n\tupdateState(\n\t\tcurrentModel: Model<any> | undefined,\n\t\tavailableModels = this.availableModels,\n\t\tconfiguredProviders = this.configuredProviders,\n\t): void {\n\t\tthis.currentModel = currentModel;\n\t\tthis.availableModels = availableModels;\n\t\tthis.configuredProviders = configuredProviders;\n\t\tconst query = this.searchInput.getValue();\n\t\tconst selectedKey = this.getSelectedModelKey();\n\n\t\tthis.loadModels();\n\t\tthis.filterModels(query);\n\n\t\tif (selectedKey) {\n\t\t\tconst selectedIndex = this.filteredModels.findIndex((item) => this.getModelKey(item) === selectedKey);\n\t\t\tif (selectedIndex >= 0) {\n\t\t\t\tthis.selectedIndex = selectedIndex;\n\t\t\t\tthis.updateList();\n\t\t\t}\n\t\t}\n\n\t\tthis.tui.requestRender();\n\t}\n\n\tprivate loadModels(): void {\n\t\tlet models: ModelItem[];\n\t\tthis.errorMessage = undefined;\n\n\t\tif (this.availableModels === undefined) {\n\t\t\tthis.modelRegistry.refresh();\n\t\t\tconst loadError = this.modelRegistry.getError();\n\t\t\tif (loadError) {\n\t\t\t\tthis.errorMessage = loadError;\n\t\t\t}\n\t\t}\n\n\t\t// Load available models (built-in models still work even if models.json failed)\n\t\tlet availableModels: ReadonlyArray<Model<any>>;\n\t\ttry {\n\t\t\tavailableModels =\n\t\t\t\tthis.availableModels !== undefined ? this.availableModels : this.modelRegistry.getAvailable();\n\t\t\tmodels = availableModels.map((model: Model<any>) => ({\n\t\t\t\tprovider: model.provider,\n\t\t\t\tid: model.id,\n\t\t\t\tmodel,\n\t\t\t}));\n\t\t} catch (error) {\n\t\t\tthis.allModels = [];\n\t\t\tthis.scopedModelItems = [];\n\t\t\tthis.activeModels = [];\n\t\t\tthis.filteredModels = [];\n\t\t\tthis.errorMessage = error instanceof Error ? error.message : String(error);\n\t\t\treturn;\n\t\t}\n\n\t\tthis.allModels = this.sortModels(models);\n\t\tconst availableModelsById = new Map(availableModels.map((model) => [`${model.provider}/${model.id}`, model]));\n\t\tthis.scopedModels = this.scopedModels.map((scoped) => {\n\t\t\tconst scopedModelId = `${scoped.model.provider}/${scoped.model.id}`;\n\t\t\tconst refreshed =\n\t\t\t\tavailableModelsById.get(scopedModelId) ??\n\t\t\t\t(this.availableModels !== undefined\n\t\t\t\t\t? undefined\n\t\t\t\t\t: this.modelRegistry.find(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\tthis.filteredModels = this.activeModels;\n\t\tconst currentIndex = this.filteredModels.findIndex((item) => modelsAreEqual(this.currentModel, item.model));\n\t\tthis.selectedIndex =\n\t\t\tcurrentIndex >= 0 ? currentIndex : Math.min(this.selectedIndex, Math.max(0, this.getSelectableCount() - 1));\n\t}\n\n\tprivate getModelKey(item: ModelItem): string {\n\t\treturn `${item.provider}/${item.id}`;\n\t}\n\n\tprivate getSelectedModelKey(): string | undefined {\n\t\tconst selected = this.filteredModels[this.selectedIndex];\n\t\treturn selected ? this.getModelKey(selected) : undefined;\n\t}\n\n\tprivate recentRankOf(item: ModelItem): number {\n\t\t// Finite sentinel so subtracting two non-recent ranks yields 0, not NaN.\n\t\treturn this.recentRank.get(`${item.provider}/${item.id}`) ?? Number.MAX_SAFE_INTEGER;\n\t}\n\n\tprivate isProviderConfigured(item: ModelItem): boolean {\n\t\treturn this.configuredProviders?.has(item.provider) || this.modelRegistry.hasConfiguredAuth(item.model);\n\t}\n\n\tprivate sortModels(models: ModelItem[]): ModelItem[] {\n\t\tconst sorted = [...models];\n\t\tsorted.sort((a, b) => {\n\t\t\tconst configuredDiff = Number(this.isProviderConfigured(b)) - Number(this.isProviderConfigured(a));\n\t\t\tif (configuredDiff !== 0) return configuredDiff;\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 aIsCurrent ? -1 : 1;\n\t\t\tconst rankDiff = this.recentRankOf(a) - this.recentRankOf(b);\n\t\t\tif (rankDiff !== 0) return rankDiff;\n\t\t\tconst providerDiff = a.provider.localeCompare(b.provider);\n\t\t\tif (providerDiff !== 0) return providerDiff;\n\t\t\tconst aFeatured = a.model.featured === true;\n\t\t\tconst bFeatured = b.model.featured === true;\n\t\t\tif (aFeatured !== bFeatured) return aFeatured ? -1 : 1;\n\t\t\treturn a.id.localeCompare(b.id, undefined, { numeric: true });\n\t\t});\n\t\treturn sorted;\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 getScopeHintText(): string {\n\t\treturn keyHint(\"app.model.toggleScope\", \"scope\") + theme.fg(\"muted\", \" (all/scoped)\");\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\tconst currentIndex = this.activeModels.findIndex((item) => modelsAreEqual(this.currentModel, item.model));\n\t\tthis.selectedIndex = currentIndex >= 0 ? currentIndex : 0;\n\t\tthis.filterModels(this.searchInput.getValue());\n\t\tif (this.scopeText) {\n\t\t\tthis.scopeText.setText(this.getScopeText());\n\t\t}\n\t}\n\n\tprivate filterModels(query: string): void {\n\t\tconst queryChanged = query !== this.searchQuery;\n\t\tthis.searchQuery = query;\n\t\tif (query.trim()) {\n\t\t\tconst matches = this.activeModels.flatMap((item) => {\n\t\t\t\tconst match = scoreModelSearch(item, query);\n\t\t\t\treturn match ? [{ item, ...match }] : [];\n\t\t\t});\n\t\t\tmatches.sort(\n\t\t\t\t(a, b) =>\n\t\t\t\t\ta.quality - b.quality ||\n\t\t\t\t\ta.score - b.score ||\n\t\t\t\t\tNumber(this.isProviderConfigured(b.item)) - Number(this.isProviderConfigured(a.item)) ||\n\t\t\t\t\tNumber(modelsAreEqual(this.currentModel, b.item.model)) -\n\t\t\t\t\t\tNumber(modelsAreEqual(this.currentModel, a.item.model)) ||\n\t\t\t\t\tthis.recentRankOf(a.item) - this.recentRankOf(b.item) ||\n\t\t\t\t\tthis.getModelKey(a.item).localeCompare(this.getModelKey(b.item), undefined, { numeric: true }),\n\t\t\t);\n\t\t\tthis.filteredModels = matches.map(({ item }) => item);\n\t\t} else {\n\t\t\tthis.filteredModels = this.activeModels;\n\t\t}\n\t\tthis.selectedIndex = queryChanged ? 0 : Math.min(this.selectedIndex, Math.max(0, this.getSelectableCount() - 1));\n\t\tthis.updateList();\n\t}\n\n\toverride render(width: number): string[] {\n\t\tconst previousLayoutKey = this.responsiveLayoutKey;\n\t\tthis.updateResponsiveLayout();\n\t\tif (this.responsiveLayoutKey !== previousLayoutKey) {\n\t\t\tthis.updateList();\n\t\t}\n\t\treturn super.render(width);\n\t}\n\n\tprivate updateList(): void {\n\t\tthis.updateResponsiveLayout();\n\t\tthis.listContainer.clear();\n\n\t\tconst maxVisible = this.listLayout.visibleItems;\n\t\tconst selectedModelIndex = Math.min(this.selectedIndex, Math.max(0, this.filteredModels.length - 1));\n\t\tconst startIndex = Math.max(\n\t\t\t0,\n\t\t\tMath.min(selectedModelIndex - Math.floor(maxVisible / 2), this.filteredModels.length - maxVisible),\n\t\t);\n\t\tconst endIndex = Math.min(startIndex + maxVisible, this.filteredModels.length);\n\n\t\t// Show visible slice of filtered models\n\t\tfor (let i = startIndex; i < endIndex; i++) {\n\t\t\tconst item = this.filteredModels[i];\n\t\t\tif (!item) continue;\n\n\t\t\tconst isSelected = i === this.selectedIndex;\n\t\t\tconst isCurrent = modelsAreEqual(this.currentModel, item.model);\n\t\t\tconst isConfigured = this.isProviderConfigured(item);\n\t\t\tconst meta = isConfigured\n\t\t\t\t? isCurrent\n\t\t\t\t\t? theme.fg(\"success\", \"current\")\n\t\t\t\t\t: undefined\n\t\t\t\t: theme.fg(\"warning\", isCurrent ? \"current · sign in\" : \"sign in\");\n\n\t\t\tthis.listContainer.addChild(\n\t\t\t\tnew MenuRow({\n\t\t\t\t\tprimary: item.id,\n\t\t\t\t\tsecondary: item.provider,\n\t\t\t\t\tmeta,\n\t\t\t\t\tselected: isSelected,\n\t\t\t\t}),\n\t\t\t);\n\t\t}\n\n\t\t// Add scroll indicator if needed\n\t\tif (startIndex > 0 || endIndex < this.filteredModels.length) {\n\t\t\tconst scrollInfo = theme.fg(\"muted\", ` (${selectedModelIndex + 1}/${this.filteredModels.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.filteredModels.length === 0) {\n\t\t\tthis.listContainer.addChild(new Text(theme.fg(\"muted\", \"No matching models\"), 0, 0));\n\t\t} else {\n\t\t\tconst selected = this.filteredModels[this.selectedIndex];\n\t\t\tif (selected && this.shouldShowSelectedDetails()) {\n\t\t\t\tthis.listContainer.addChild(new Spacer(1));\n\t\t\t\tthis.listContainer.addChild(new Text(theme.fg(\"muted\", selected.model.name), 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, \"app.model.toggleScope\")) {\n\t\t\tif (this.scopedModelItems.length > 0) {\n\t\t\t\tconst nextScope: ModelScope = this.scope === \"all\" ? \"scoped\" : \"all\";\n\t\t\t\tthis.setScope(nextScope);\n\t\t\t\tif (this.scopeHintText) {\n\t\t\t\t\tthis.scopeHintText.setText(this.getScopeHintText());\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn;\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\tconst selectableCount = this.getSelectableCount();\n\t\t\tif (selectableCount === 0) return;\n\t\t\tthis.selectedIndex = this.selectedIndex === 0 ? selectableCount - 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\tconst selectableCount = this.getSelectableCount();\n\t\t\tif (selectableCount === 0) return;\n\t\t\tthis.selectedIndex = this.selectedIndex === selectableCount - 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.handleConfirm();\n\t\t}\n\t\t// Escape / Ctrl+C, or left arrow when the search field is at its start\n\t\telse if (kb.matches(keyData, \"tui.select.cancel\") || shouldTreatAsBack(keyData, this.searchInput)) {\n\t\t\tthis.onCancelCallback();\n\t\t}\n\t\t// Pass everything else to search input\n\t\telse {\n\t\t\tthis.searchInput.handleInput(keyData);\n\t\t\tthis.filterModels(this.searchInput.getValue());\n\t\t}\n\t}\n\n\tprivate handleSelect(model: Model<any>): void {\n\t\tthis.onSelectCallback(model);\n\t}\n\n\tprivate handleConfirm(): void {\n\t\tconst selectedModel = this.filteredModels[this.selectedIndex];\n\t\tif (selectedModel) {\n\t\t\tthis.handleSelect(selectedModel.model);\n\t\t\treturn;\n\t\t}\n\t}\n\n\tprivate getSelectableCount(): number {\n\t\treturn this.filteredModels.length;\n\t}\n\n\tgetSearchInput(): MenuSearchInput {\n\t\treturn this.searchInput;\n\t}\n\n\tprivate updateResponsiveLayout(): void {\n\t\tconst showHeaderHelp = this.shouldShowHeaderHelp();\n\t\tlet headerHelpRows = 0;\n\t\tthis.headerHelpContainer.clear();\n\t\tif (showHeaderHelp) {\n\t\t\tif (this.scopeText && this.scopeHintText) {\n\t\t\t\tthis.headerHelpContainer.addChild(this.scopeText);\n\t\t\t\tthis.headerHelpContainer.addChild(this.scopeHintText);\n\t\t\t\theaderHelpRows += 2;\n\t\t\t} else if (this.warningText) {\n\t\t\t\tthis.headerHelpContainer.addChild(this.warningText);\n\t\t\t\theaderHelpRows += 1;\n\t\t\t}\n\t\t\tthis.headerHelpContainer.addChild(new Spacer(1));\n\t\t\theaderHelpRows += 1;\n\t\t}\n\n\t\tconst headerRows = this.getHeaderRows();\n\t\tconst reservedRows =\n\t\t\tMODEL_LIST_RESERVED_ROWS.base +\n\t\t\theaderRows +\n\t\t\theaderHelpRows +\n\t\t\t(this.shouldShowSelectedDetails() ? MODEL_LIST_RESERVED_ROWS.detail : 0);\n\t\tthis.listLayout = getMenuListLayout({\n\t\t\tgetRows: this.viewport.getRows,\n\t\t\tpreferredVisibleItems: PREFERRED_VISIBLE_MODELS,\n\t\t\ttotalItems: this.filteredModels.length,\n\t\t\treservedRows,\n\t\t\tcomfortableItemRows: 3,\n\t\t\tcompactItemRows: 2,\n\t\t\tscrollIndicatorRows: MODEL_SCROLL_INDICATOR_ROWS,\n\t\t});\n\t\tthis.responsiveLayoutKey = [\n\t\t\theaderRows,\n\t\t\tshowHeaderHelp ? \"help\" : \"no-help\",\n\t\t\theaderHelpRows,\n\t\t\tthis.shouldShowSelectedDetails() ? \"detail\" : \"no-detail\",\n\t\t\tthis.listLayout.compact ? \"compact\" : \"comfortable\",\n\t\t\tthis.listLayout.visibleItems,\n\t\t].join(\":\");\n\t}\n\n\tprivate shouldShowHeaderHelp(): boolean {\n\t\treturn this.hasRows(MODEL_HELP_MIN_ROWS);\n\t}\n\n\tprivate shouldShowSelectedDetails(): boolean {\n\t\treturn this.hasRows(MODEL_DETAIL_MIN_ROWS);\n\t}\n\n\tprivate hasRows(minRows: number): boolean {\n\t\tconst rows = this.viewport.getRows?.();\n\t\treturn rows === undefined || !Number.isFinite(rows) || rows >= minRows;\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 { type Model, modelsAreEqual } from \"@ponythewhite/base-context-ai\";\nimport {\n\ttype Component,\n\tContainer,\n\ttype Focusable,\n\tfuzzyMatch,\n\tgetKeybindings,\n\tSpacer,\n\tText,\n\ttype TUI,\n} from \"@ponythewhite/base-context-tui\";\nimport type { ModelRegistry } from \"../../../core/model-registry.js\";\nimport { theme } from \"../theme/theme.js\";\nimport { keyHint } from \"./keybinding-hints.js\";\nimport {\n\tgetMenuListLayout,\n\tMenuList,\n\tMenuPanel,\n\tMenuRow,\n\tMenuSearchInput,\n\ttype MenuViewportProvider,\n} from \"./menu-panel.js\";\nimport { shouldTreatAsBack } from \"./modal-back.js\";\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\nenum ModelSearchMatchQuality {\n\tExactShortId,\n\tExactFullId,\n\tPrefixOrToken,\n\tFuzzy,\n}\n\ninterface ModelSearchMatch {\n\tquality: ModelSearchMatchQuality;\n\tscore: number;\n}\n\nfunction normalizeModelSearchText(value: string): string {\n\treturn value.toLowerCase().replace(/[\\s\\-_.:/]+/g, \"\");\n}\n\nfunction getModelSearchFields(item: ModelItem): { shortId: string; fullIds: string[]; all: string[] } {\n\tconst shortId = item.id.slice(item.id.lastIndexOf(\"/\") + 1);\n\tconst fullIds = [item.id, `${item.provider}/${item.id}`];\n\treturn {\n\t\tshortId,\n\t\tfullIds,\n\t\tall: [shortId, ...fullIds, item.model.name, item.provider],\n\t};\n}\n\nfunction getBestFuzzyScore(queryTokens: string[], fields: string[]): number | null {\n\tlet total = 0;\n\tfor (const token of queryTokens) {\n\t\tlet best = Number.POSITIVE_INFINITY;\n\t\tfor (const field of fields) {\n\t\t\tconst match = fuzzyMatch(token, field);\n\t\t\tif (match.matches) best = Math.min(best, match.score);\n\t\t}\n\t\tif (!Number.isFinite(best)) return null;\n\t\ttotal += best;\n\t}\n\treturn total;\n}\n\nfunction scoreModelSearch(item: ModelItem, query: string): ModelSearchMatch | null {\n\tconst queryTokens = query.trim().split(/\\s+/);\n\tconst normalizedQuery = normalizeModelSearchText(query);\n\tconst normalizedTokens = queryTokens.map(normalizeModelSearchText).filter(Boolean);\n\tif (!normalizedQuery || normalizedTokens.length === 0) return null;\n\n\tconst fields = getModelSearchFields(item);\n\tif (normalizeModelSearchText(fields.shortId) === normalizedQuery) {\n\t\treturn { quality: ModelSearchMatchQuality.ExactShortId, score: 0 };\n\t}\n\tif (fields.fullIds.some((field) => normalizeModelSearchText(field) === normalizedQuery)) {\n\t\treturn { quality: ModelSearchMatchQuality.ExactFullId, score: 0 };\n\t}\n\n\tconst normalizedFields = fields.all.map(normalizeModelSearchText);\n\tconst fieldTokens = fields.all\n\t\t.flatMap((field) => field.split(/[\\s/_-]+/))\n\t\t.map(normalizeModelSearchText)\n\t\t.filter(Boolean);\n\tconst fuzzyScore = getBestFuzzyScore(normalizedTokens, normalizedFields);\n\tconst isPrefixOrToken = normalizedTokens.every(\n\t\t(token) =>\n\t\t\tnormalizedFields.some((field) => field.startsWith(token)) ||\n\t\t\tfieldTokens.some((field) => field.startsWith(token)),\n\t);\n\tif (isPrefixOrToken && fuzzyScore !== null) {\n\t\treturn { quality: ModelSearchMatchQuality.PrefixOrToken, score: fuzzyScore };\n\t}\n\treturn fuzzyScore === null ? null : { quality: ModelSearchMatchQuality.Fuzzy, score: fuzzyScore };\n}\n\nexport interface ModelSelectorOptions {\n\tavailableModels?: ReadonlyArray<Model<any>>;\n\tconfiguredProviders?: ReadonlySet<string>;\n\theader?: Component;\n\tgetHeaderRows?: () => number;\n\tsubtitle?: string;\n\tgetRows?: () => number;\n\trecentModels?: ReadonlyArray<string>;\n}\n\ntype ModelScope = \"all\" | \"scoped\";\n\nconst PREFERRED_VISIBLE_MODELS = 10;\nconst MODEL_LIST_RESERVED_ROWS = {\n\tbase: 7,\n\tdetail: 2,\n};\nconst MODEL_SCROLL_INDICATOR_ROWS = 1;\nconst MODEL_HELP_MIN_ROWS = 12;\nconst MODEL_DETAIL_MIN_ROWS = 14;\n\n/**\n * Component that renders a model selector with search\n */\nexport class ModelSelectorComponent extends Container implements Focusable {\n\tprivate searchInput: MenuSearchInput;\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 allModels: ModelItem[] = [];\n\tprivate scopedModelItems: ModelItem[] = [];\n\tprivate activeModels: ModelItem[] = [];\n\tprivate filteredModels: ModelItem[] = [];\n\tprivate selectedIndex: number = 0;\n\tprivate searchQuery = \"\";\n\tprivate currentModel?: Model<any>;\n\tprivate modelRegistry: ModelRegistry;\n\tprivate onSelectCallback: (model: Model<any>) => void;\n\tprivate onCancelCallback: () => void;\n\tprivate availableModels?: ReadonlyArray<Model<any>>;\n\tprivate configuredProviders?: ReadonlySet<string>;\n\tprivate providerFilter?: string;\n\tprivate recentRank: Map<string, number>;\n\tprivate errorMessage?: string;\n\tprivate tui: TUI;\n\tprivate scopedModels: ReadonlyArray<ScopedModelItem>;\n\tprivate scope: ModelScope = \"all\";\n\tprivate scopeText?: Text;\n\tprivate scopeHintText?: Text;\n\tprivate panel: MenuPanel;\n\tprivate headerHelpContainer: Container;\n\tprivate warningText?: Text;\n\tprivate listLayout = getMenuListLayout({\n\t\tpreferredVisibleItems: PREFERRED_VISIBLE_MODELS,\n\t\treservedRows: MODEL_LIST_RESERVED_ROWS.base,\n\t\tcomfortableItemRows: 3,\n\t\tcompactItemRows: 2,\n\t});\n\tprivate responsiveLayoutKey = \"\";\n\tprivate readonly viewport: MenuViewportProvider;\n\tprivate readonly getHeaderRows: () => number;\n\n\tconstructor(\n\t\ttui: TUI,\n\t\tcurrentModel: Model<any> | undefined,\n\t\tmodelRegistry: ModelRegistry,\n\t\tscopedModels: ReadonlyArray<ScopedModelItem>,\n\t\tonSelect: (model: Model<any>) => void,\n\t\tonCancel: () => void,\n\t\tinitialSearchInput?: string,\n\t\toptions: ModelSelectorOptions = {},\n\t) {\n\t\tsuper();\n\n\t\tthis.tui = tui;\n\t\tthis.currentModel = currentModel;\n\t\tthis.modelRegistry = modelRegistry;\n\t\tthis.scopedModels = scopedModels;\n\t\tthis.scope = scopedModels.length > 0 ? \"scoped\" : \"all\";\n\t\tthis.onSelectCallback = onSelect;\n\t\tthis.onCancelCallback = onCancel;\n\t\tthis.availableModels = options.availableModels;\n\t\tthis.configuredProviders = options.configuredProviders;\n\t\tthis.recentRank = new Map((options.recentModels ?? []).map((key, i) => [key, i]));\n\t\tthis.viewport = { getRows: options.getRows };\n\t\tthis.getHeaderRows = options.header ? (options.getHeaderRows ?? (() => 2)) : () => 0;\n\n\t\tthis.panel = new MenuPanel({\n\t\t\ttitle: \"Models\",\n\t\t\tsubtitle: options.subtitle ?? \"All models across supported providers.\",\n\t\t});\n\t\tthis.addChild(this.panel);\n\t\tif (options.header) {\n\t\t\tthis.panel.addChild(options.header);\n\t\t\tthis.panel.addChild(new Spacer(1));\n\t\t}\n\n\t\t// Add hint about model filtering\n\t\tif (scopedModels.length > 0) {\n\t\t\tthis.scopeText = new Text(this.getScopeText(), 0, 0);\n\t\t\tthis.scopeHintText = new Text(this.getScopeHintText(), 0, 0);\n\t\t} else {\n\t\t\tconst hintText = \"Signed-in providers first. Other models prompt sign-in.\";\n\t\t\tthis.warningText = new Text(theme.fg(\"muted\", hintText), 0, 0);\n\t\t}\n\t\tthis.headerHelpContainer = new Container();\n\t\tthis.panel.addChild(this.headerHelpContainer);\n\n\t\t// Create search input\n\t\tthis.searchInput = new MenuSearchInput(\"Search models\");\n\t\tif (initialSearchInput) {\n\t\t\tthis.searchInput.setValue(initialSearchInput);\n\t\t}\n\t\tthis.searchInput.onSubmit = () => {\n\t\t\tthis.handleConfirm();\n\t\t};\n\t\tthis.panel.addChild(this.searchInput);\n\n\t\tthis.panel.addChild(new Spacer(1));\n\n\t\t// Create list container\n\t\tthis.listContainer = new MenuList({ compact: () => this.listLayout.compact });\n\t\tthis.panel.addChild(this.listContainer);\n\t\tthis.updateResponsiveLayout();\n\n\t\tthis.loadModels();\n\t\tif (initialSearchInput) {\n\t\t\tthis.filterModels(initialSearchInput);\n\t\t} else {\n\t\t\tthis.updateList();\n\t\t}\n\t\tthis.tui.requestRender();\n\t}\n\n\tsetProviderFilter(provider: string): void {\n\t\tthis.providerFilter = provider;\n\t\tthis.scope = \"all\";\n\t\tthis.panel.setTitle(`Models: ${provider}`);\n\t\tif (this.scopeText) this.scopeText.setText(this.getScopeText());\n\t\tthis.updateState(this.currentModel);\n\t}\n\n\tupdateAvailableModels(availableModels: ReadonlyArray<Model<any>>): void {\n\t\tthis.updateState(this.currentModel, availableModels);\n\t}\n\n\tupdateState(\n\t\tcurrentModel: Model<any> | undefined,\n\t\tavailableModels = this.availableModels,\n\t\tconfiguredProviders = this.configuredProviders,\n\t): void {\n\t\tthis.currentModel = currentModel;\n\t\tthis.availableModels = availableModels;\n\t\tthis.configuredProviders = configuredProviders;\n\t\tconst query = this.searchInput.getValue();\n\t\tconst selectedKey = this.getSelectedModelKey();\n\n\t\tthis.loadModels();\n\t\tthis.filterModels(query);\n\n\t\tif (selectedKey) {\n\t\t\tconst selectedIndex = this.filteredModels.findIndex((item) => this.getModelKey(item) === selectedKey);\n\t\t\tif (selectedIndex >= 0) {\n\t\t\t\tthis.selectedIndex = selectedIndex;\n\t\t\t\tthis.updateList();\n\t\t\t}\n\t\t}\n\n\t\tthis.tui.requestRender();\n\t}\n\n\tprivate loadModels(): void {\n\t\tlet models: ModelItem[];\n\t\tthis.errorMessage = undefined;\n\n\t\tif (this.availableModels === undefined) {\n\t\t\tthis.modelRegistry.refresh();\n\t\t\tconst loadError = this.modelRegistry.getError();\n\t\t\tif (loadError) {\n\t\t\t\tthis.errorMessage = loadError;\n\t\t\t}\n\t\t}\n\n\t\t// Load available models (built-in models still work even if models.json failed)\n\t\tlet availableModels: ReadonlyArray<Model<any>>;\n\t\ttry {\n\t\t\tavailableModels =\n\t\t\t\tthis.availableModels !== undefined ? this.availableModels : this.modelRegistry.getAvailable();\n\t\t\tmodels = availableModels.map((model: Model<any>) => ({\n\t\t\t\tprovider: model.provider,\n\t\t\t\tid: model.id,\n\t\t\t\tmodel,\n\t\t\t}));\n\t\t} catch (error) {\n\t\t\tthis.allModels = [];\n\t\t\tthis.scopedModelItems = [];\n\t\t\tthis.activeModels = [];\n\t\t\tthis.filteredModels = [];\n\t\t\tthis.errorMessage = error instanceof Error ? error.message : String(error);\n\t\t\treturn;\n\t\t}\n\n\t\tthis.allModels = this.sortModels(\n\t\t\tmodels.filter((model) => this.providerFilter === undefined || model.provider === this.providerFilter),\n\t\t);\n\t\tconst availableModelsById = new Map(availableModels.map((model) => [`${model.provider}/${model.id}`, model]));\n\t\tthis.scopedModels = this.scopedModels.map((scoped) => {\n\t\t\tconst scopedModelId = `${scoped.model.provider}/${scoped.model.id}`;\n\t\t\tconst refreshed =\n\t\t\t\tavailableModelsById.get(scopedModelId) ??\n\t\t\t\t(this.availableModels !== undefined\n\t\t\t\t\t? undefined\n\t\t\t\t\t: this.modelRegistry.find(scoped.model.provider, scoped.model.id));\n\t\t\treturn refreshed ? { ...scoped, model: refreshed } : scoped;\n\t\t});\n\t\tthis.scopedModelItems = this.scopedModels\n\t\t\t.filter((scoped) => this.providerFilter === undefined || scoped.model.provider === this.providerFilter)\n\t\t\t.map((scoped) => ({\n\t\t\t\tprovider: scoped.model.provider,\n\t\t\t\tid: scoped.model.id,\n\t\t\t\tmodel: scoped.model,\n\t\t\t}));\n\t\tthis.activeModels = this.scope === \"scoped\" ? this.scopedModelItems : this.allModels;\n\t\tthis.filteredModels = this.activeModels;\n\t\tconst currentIndex = this.filteredModels.findIndex((item) => modelsAreEqual(this.currentModel, item.model));\n\t\tthis.selectedIndex =\n\t\t\tcurrentIndex >= 0 ? currentIndex : Math.min(this.selectedIndex, Math.max(0, this.getSelectableCount() - 1));\n\t}\n\n\tprivate getModelKey(item: ModelItem): string {\n\t\treturn `${item.provider}/${item.id}`;\n\t}\n\n\tprivate getSelectedModelKey(): string | undefined {\n\t\tconst selected = this.filteredModels[this.selectedIndex];\n\t\treturn selected ? this.getModelKey(selected) : undefined;\n\t}\n\n\tprivate recentRankOf(item: ModelItem): number {\n\t\t// Finite sentinel so subtracting two non-recent ranks yields 0, not NaN.\n\t\treturn this.recentRank.get(`${item.provider}/${item.id}`) ?? Number.MAX_SAFE_INTEGER;\n\t}\n\n\tprivate isProviderConfigured(item: ModelItem): boolean {\n\t\treturn this.configuredProviders?.has(item.provider) || this.modelRegistry.hasConfiguredAuth(item.model);\n\t}\n\n\tprivate sortModels(models: ModelItem[]): ModelItem[] {\n\t\tconst sorted = [...models];\n\t\tsorted.sort((a, b) => {\n\t\t\tconst configuredDiff = Number(this.isProviderConfigured(b)) - Number(this.isProviderConfigured(a));\n\t\t\tif (configuredDiff !== 0) return configuredDiff;\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 aIsCurrent ? -1 : 1;\n\t\t\tconst rankDiff = this.recentRankOf(a) - this.recentRankOf(b);\n\t\t\tif (rankDiff !== 0) return rankDiff;\n\t\t\tconst providerDiff = a.provider.localeCompare(b.provider);\n\t\t\tif (providerDiff !== 0) return providerDiff;\n\t\t\tconst aFeatured = a.model.featured === true;\n\t\t\tconst bFeatured = b.model.featured === true;\n\t\t\tif (aFeatured !== bFeatured) return aFeatured ? -1 : 1;\n\t\t\treturn a.id.localeCompare(b.id, undefined, { numeric: true });\n\t\t});\n\t\treturn sorted;\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 getScopeHintText(): string {\n\t\treturn keyHint(\"app.model.toggleScope\", \"scope\") + theme.fg(\"muted\", \" (all/scoped)\");\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\tconst currentIndex = this.activeModels.findIndex((item) => modelsAreEqual(this.currentModel, item.model));\n\t\tthis.selectedIndex = currentIndex >= 0 ? currentIndex : 0;\n\t\tthis.filterModels(this.searchInput.getValue());\n\t\tif (this.scopeText) {\n\t\t\tthis.scopeText.setText(this.getScopeText());\n\t\t}\n\t}\n\n\tprivate filterModels(query: string): void {\n\t\tconst queryChanged = query !== this.searchQuery;\n\t\tthis.searchQuery = query;\n\t\tif (query.trim()) {\n\t\t\tconst matches = this.activeModels.flatMap((item) => {\n\t\t\t\tconst match = scoreModelSearch(item, query);\n\t\t\t\treturn match ? [{ item, ...match }] : [];\n\t\t\t});\n\t\t\tmatches.sort(\n\t\t\t\t(a, b) =>\n\t\t\t\t\ta.quality - b.quality ||\n\t\t\t\t\ta.score - b.score ||\n\t\t\t\t\tNumber(this.isProviderConfigured(b.item)) - Number(this.isProviderConfigured(a.item)) ||\n\t\t\t\t\tNumber(modelsAreEqual(this.currentModel, b.item.model)) -\n\t\t\t\t\t\tNumber(modelsAreEqual(this.currentModel, a.item.model)) ||\n\t\t\t\t\tthis.recentRankOf(a.item) - this.recentRankOf(b.item) ||\n\t\t\t\t\tthis.getModelKey(a.item).localeCompare(this.getModelKey(b.item), undefined, { numeric: true }),\n\t\t\t);\n\t\t\tthis.filteredModels = matches.map(({ item }) => item);\n\t\t} else {\n\t\t\tthis.filteredModels = this.activeModels;\n\t\t}\n\t\tthis.selectedIndex = queryChanged ? 0 : Math.min(this.selectedIndex, Math.max(0, this.getSelectableCount() - 1));\n\t\tthis.updateList();\n\t}\n\n\toverride render(width: number): string[] {\n\t\tconst previousLayoutKey = this.responsiveLayoutKey;\n\t\tthis.updateResponsiveLayout();\n\t\tif (this.responsiveLayoutKey !== previousLayoutKey) {\n\t\t\tthis.updateList();\n\t\t}\n\t\treturn super.render(width);\n\t}\n\n\tprivate updateList(): void {\n\t\tthis.updateResponsiveLayout();\n\t\tthis.listContainer.clear();\n\n\t\tconst maxVisible = this.listLayout.visibleItems;\n\t\tconst selectedModelIndex = Math.min(this.selectedIndex, Math.max(0, this.filteredModels.length - 1));\n\t\tconst startIndex = Math.max(\n\t\t\t0,\n\t\t\tMath.min(selectedModelIndex - Math.floor(maxVisible / 2), this.filteredModels.length - maxVisible),\n\t\t);\n\t\tconst endIndex = Math.min(startIndex + maxVisible, this.filteredModels.length);\n\n\t\t// Show visible slice of filtered models\n\t\tfor (let i = startIndex; i < endIndex; i++) {\n\t\t\tconst item = this.filteredModels[i];\n\t\t\tif (!item) continue;\n\n\t\t\tconst isSelected = i === this.selectedIndex;\n\t\t\tconst isCurrent = modelsAreEqual(this.currentModel, item.model);\n\t\t\tconst isConfigured = this.isProviderConfigured(item);\n\t\t\tconst meta = isConfigured\n\t\t\t\t? isCurrent\n\t\t\t\t\t? theme.fg(\"success\", \"current\")\n\t\t\t\t\t: undefined\n\t\t\t\t: theme.fg(\"warning\", isCurrent ? \"current · sign in\" : \"sign in\");\n\n\t\t\tthis.listContainer.addChild(\n\t\t\t\tnew MenuRow({\n\t\t\t\t\tprimary: item.id,\n\t\t\t\t\tsecondary: item.provider,\n\t\t\t\t\tmeta,\n\t\t\t\t\tselected: isSelected,\n\t\t\t\t}),\n\t\t\t);\n\t\t}\n\n\t\t// Add scroll indicator if needed\n\t\tif (startIndex > 0 || endIndex < this.filteredModels.length) {\n\t\t\tconst scrollInfo = theme.fg(\"muted\", ` (${selectedModelIndex + 1}/${this.filteredModels.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.filteredModels.length === 0) {\n\t\t\tthis.listContainer.addChild(new Text(theme.fg(\"muted\", \"No matching models\"), 0, 0));\n\t\t} else {\n\t\t\tconst selected = this.filteredModels[this.selectedIndex];\n\t\t\tif (selected && this.shouldShowSelectedDetails()) {\n\t\t\t\tthis.listContainer.addChild(new Spacer(1));\n\t\t\t\tthis.listContainer.addChild(new Text(theme.fg(\"muted\", selected.model.name), 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, \"app.model.toggleScope\")) {\n\t\t\tif (this.scopedModelItems.length > 0) {\n\t\t\t\tconst nextScope: ModelScope = this.scope === \"all\" ? \"scoped\" : \"all\";\n\t\t\t\tthis.setScope(nextScope);\n\t\t\t\tif (this.scopeHintText) {\n\t\t\t\t\tthis.scopeHintText.setText(this.getScopeHintText());\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn;\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\tconst selectableCount = this.getSelectableCount();\n\t\t\tif (selectableCount === 0) return;\n\t\t\tthis.selectedIndex = this.selectedIndex === 0 ? selectableCount - 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\tconst selectableCount = this.getSelectableCount();\n\t\t\tif (selectableCount === 0) return;\n\t\t\tthis.selectedIndex = this.selectedIndex === selectableCount - 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.handleConfirm();\n\t\t}\n\t\t// Escape / Ctrl+C, or left arrow when the search field is at its start\n\t\telse if (kb.matches(keyData, \"tui.select.cancel\") || shouldTreatAsBack(keyData, this.searchInput)) {\n\t\t\tthis.onCancelCallback();\n\t\t}\n\t\t// Pass everything else to search input\n\t\telse {\n\t\t\tthis.searchInput.handleInput(keyData);\n\t\t\tthis.filterModels(this.searchInput.getValue());\n\t\t}\n\t}\n\n\tprivate handleSelect(model: Model<any>): void {\n\t\tthis.onSelectCallback(model);\n\t}\n\n\tprivate handleConfirm(): void {\n\t\tconst selectedModel = this.filteredModels[this.selectedIndex];\n\t\tif (selectedModel) {\n\t\t\tthis.handleSelect(selectedModel.model);\n\t\t\treturn;\n\t\t}\n\t}\n\n\tprivate getSelectableCount(): number {\n\t\treturn this.filteredModels.length;\n\t}\n\n\tgetSearchInput(): MenuSearchInput {\n\t\treturn this.searchInput;\n\t}\n\n\tprivate updateResponsiveLayout(): void {\n\t\tconst showHeaderHelp = this.shouldShowHeaderHelp();\n\t\tlet headerHelpRows = 0;\n\t\tthis.headerHelpContainer.clear();\n\t\tif (showHeaderHelp) {\n\t\t\tif (this.scopeText && this.scopeHintText) {\n\t\t\t\tthis.headerHelpContainer.addChild(this.scopeText);\n\t\t\t\tthis.headerHelpContainer.addChild(this.scopeHintText);\n\t\t\t\theaderHelpRows += 2;\n\t\t\t} else if (this.warningText) {\n\t\t\t\tthis.headerHelpContainer.addChild(this.warningText);\n\t\t\t\theaderHelpRows += 1;\n\t\t\t}\n\t\t\tthis.headerHelpContainer.addChild(new Spacer(1));\n\t\t\theaderHelpRows += 1;\n\t\t}\n\n\t\tconst headerRows = this.getHeaderRows();\n\t\tconst reservedRows =\n\t\t\tMODEL_LIST_RESERVED_ROWS.base +\n\t\t\theaderRows +\n\t\t\theaderHelpRows +\n\t\t\t(this.shouldShowSelectedDetails() ? MODEL_LIST_RESERVED_ROWS.detail : 0);\n\t\tthis.listLayout = getMenuListLayout({\n\t\t\tgetRows: this.viewport.getRows,\n\t\t\tpreferredVisibleItems: PREFERRED_VISIBLE_MODELS,\n\t\t\ttotalItems: this.filteredModels.length,\n\t\t\treservedRows,\n\t\t\tcomfortableItemRows: 3,\n\t\t\tcompactItemRows: 2,\n\t\t\tscrollIndicatorRows: MODEL_SCROLL_INDICATOR_ROWS,\n\t\t});\n\t\tthis.responsiveLayoutKey = [\n\t\t\theaderRows,\n\t\t\tshowHeaderHelp ? \"help\" : \"no-help\",\n\t\t\theaderHelpRows,\n\t\t\tthis.shouldShowSelectedDetails() ? \"detail\" : \"no-detail\",\n\t\t\tthis.listLayout.compact ? \"compact\" : \"comfortable\",\n\t\t\tthis.listLayout.visibleItems,\n\t\t].join(\":\");\n\t}\n\n\tprivate shouldShowHeaderHelp(): boolean {\n\t\treturn this.hasRows(MODEL_HELP_MIN_ROWS);\n\t}\n\n\tprivate shouldShowSelectedDetails(): boolean {\n\t\treturn this.hasRows(MODEL_DETAIL_MIN_ROWS);\n\t}\n\n\tprivate hasRows(minRows: number): boolean {\n\t\tconst rows = this.viewport.getRows?.();\n\t\treturn rows === undefined || !Number.isFinite(rows) || rows >= minRows;\n\t}\n}\n"]}