apex-code 0.0.1-alpha.1 → 0.0.1-alpha.10

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 (739) hide show
  1. package/CHANGELOG.md +471 -0
  2. package/README.md +74 -680
  3. package/dist/cli/args.d.ts +21 -1
  4. package/dist/cli/args.d.ts.map +1 -1
  5. package/dist/cli/args.js +119 -15
  6. package/dist/cli/args.js.map +1 -1
  7. package/dist/cli/auth-command.d.ts.map +1 -1
  8. package/dist/cli/auth-command.js +3 -3
  9. package/dist/cli/auth-command.js.map +1 -1
  10. package/dist/cli/concurrent-session.d.ts +3 -0
  11. package/dist/cli/concurrent-session.d.ts.map +1 -0
  12. package/dist/cli/concurrent-session.js +13 -0
  13. package/dist/cli/concurrent-session.js.map +1 -0
  14. package/dist/cli/cost-command.d.ts +19 -0
  15. package/dist/cli/cost-command.d.ts.map +1 -0
  16. package/dist/cli/cost-command.js +107 -0
  17. package/dist/cli/cost-command.js.map +1 -0
  18. package/dist/cli/file-processor.d.ts.map +1 -1
  19. package/dist/cli/file-processor.js +2 -1
  20. package/dist/cli/file-processor.js.map +1 -1
  21. package/dist/cli/startup-ui.d.ts +1 -1
  22. package/dist/cli/startup-ui.d.ts.map +1 -1
  23. package/dist/cli/startup-ui.js +1 -2
  24. package/dist/cli/startup-ui.js.map +1 -1
  25. package/dist/cli.d.ts.map +1 -1
  26. package/dist/cli.js +99 -16
  27. package/dist/cli.js.map +1 -1
  28. package/dist/config.d.ts +5 -4
  29. package/dist/config.d.ts.map +1 -1
  30. package/dist/config.js +35 -20
  31. package/dist/config.js.map +1 -1
  32. package/dist/core/agent-session-runtime.d.ts.map +1 -1
  33. package/dist/core/agent-session-runtime.js +2 -0
  34. package/dist/core/agent-session-runtime.js.map +1 -1
  35. package/dist/core/agent-session-services.d.ts +28 -0
  36. package/dist/core/agent-session-services.d.ts.map +1 -1
  37. package/dist/core/agent-session-services.js +140 -4
  38. package/dist/core/agent-session-services.js.map +1 -1
  39. package/dist/core/agent-session.d.ts +121 -9
  40. package/dist/core/agent-session.d.ts.map +1 -1
  41. package/dist/core/agent-session.js +364 -123
  42. package/dist/core/agent-session.js.map +1 -1
  43. package/dist/core/auth-storage.d.ts +4 -1
  44. package/dist/core/auth-storage.d.ts.map +1 -1
  45. package/dist/core/auth-storage.js +17 -14
  46. package/dist/core/auth-storage.js.map +1 -1
  47. package/dist/core/bash-executor.d.ts.map +1 -1
  48. package/dist/core/bash-executor.js +11 -10
  49. package/dist/core/bash-executor.js.map +1 -1
  50. package/dist/core/compaction/branch-summarization.d.ts.map +1 -1
  51. package/dist/core/compaction/branch-summarization.js +3 -0
  52. package/dist/core/compaction/branch-summarization.js.map +1 -1
  53. package/dist/core/compaction/compaction.d.ts +4 -3
  54. package/dist/core/compaction/compaction.d.ts.map +1 -1
  55. package/dist/core/compaction/compaction.js +41 -32
  56. package/dist/core/compaction/compaction.js.map +1 -1
  57. package/dist/core/context/deferred-schemas.d.ts +92 -0
  58. package/dist/core/context/deferred-schemas.d.ts.map +1 -0
  59. package/dist/core/context/deferred-schemas.js +74 -0
  60. package/dist/core/context/deferred-schemas.js.map +1 -0
  61. package/dist/core/context/eviction.d.ts +47 -0
  62. package/dist/core/context/eviction.d.ts.map +1 -0
  63. package/dist/core/context/eviction.js +83 -0
  64. package/dist/core/context/eviction.js.map +1 -0
  65. package/dist/core/context/pipeline.d.ts +90 -0
  66. package/dist/core/context/pipeline.d.ts.map +1 -0
  67. package/dist/core/context/pipeline.js +109 -0
  68. package/dist/core/context/pipeline.js.map +1 -0
  69. package/dist/core/credential-failover.d.ts +30 -0
  70. package/dist/core/credential-failover.d.ts.map +1 -0
  71. package/dist/core/credential-failover.js +46 -0
  72. package/dist/core/credential-failover.js.map +1 -0
  73. package/dist/core/credential-pool.d.ts +60 -0
  74. package/dist/core/credential-pool.d.ts.map +1 -0
  75. package/dist/core/credential-pool.js +84 -0
  76. package/dist/core/credential-pool.js.map +1 -0
  77. package/dist/core/defaults.d.ts +1 -0
  78. package/dist/core/defaults.d.ts.map +1 -1
  79. package/dist/core/defaults.js +9 -0
  80. package/dist/core/defaults.js.map +1 -1
  81. package/dist/core/delegation/agents.d.ts +15 -0
  82. package/dist/core/delegation/agents.d.ts.map +1 -0
  83. package/dist/core/delegation/agents.js +55 -0
  84. package/dist/core/delegation/agents.js.map +1 -0
  85. package/dist/core/delegation/ceiling.d.ts +29 -0
  86. package/dist/core/delegation/ceiling.d.ts.map +1 -0
  87. package/dist/core/delegation/ceiling.js +34 -0
  88. package/dist/core/delegation/ceiling.js.map +1 -0
  89. package/dist/core/delegation/runtime.d.ts +94 -0
  90. package/dist/core/delegation/runtime.d.ts.map +1 -0
  91. package/dist/core/delegation/runtime.js +117 -0
  92. package/dist/core/delegation/runtime.js.map +1 -0
  93. package/dist/core/durable-state/daemon.d.ts +37 -0
  94. package/dist/core/durable-state/daemon.d.ts.map +1 -0
  95. package/dist/core/durable-state/daemon.js +62 -0
  96. package/dist/core/durable-state/daemon.js.map +1 -0
  97. package/dist/core/durable-state/provenance.d.ts +7 -0
  98. package/dist/core/durable-state/provenance.d.ts.map +1 -0
  99. package/dist/core/durable-state/provenance.js +24 -0
  100. package/dist/core/durable-state/provenance.js.map +1 -0
  101. package/dist/core/durable-state/sqlite.d.ts +84 -0
  102. package/dist/core/durable-state/sqlite.d.ts.map +1 -0
  103. package/dist/core/durable-state/sqlite.js +355 -0
  104. package/dist/core/durable-state/sqlite.js.map +1 -0
  105. package/dist/core/environment.d.ts +12 -0
  106. package/dist/core/environment.d.ts.map +1 -0
  107. package/dist/core/environment.js +69 -0
  108. package/dist/core/environment.js.map +1 -0
  109. package/dist/core/evidence-policy.d.ts +17 -0
  110. package/dist/core/evidence-policy.d.ts.map +1 -0
  111. package/dist/core/evidence-policy.js +25 -0
  112. package/dist/core/evidence-policy.js.map +1 -0
  113. package/dist/core/evidence.d.ts +16 -0
  114. package/dist/core/evidence.d.ts.map +1 -0
  115. package/dist/core/evidence.js +51 -0
  116. package/dist/core/evidence.js.map +1 -0
  117. package/dist/core/experimental.d.ts +4 -0
  118. package/dist/core/experimental.d.ts.map +1 -1
  119. package/dist/core/experimental.js +6 -1
  120. package/dist/core/experimental.js.map +1 -1
  121. package/dist/core/extensions/index.d.ts +2 -2
  122. package/dist/core/extensions/index.d.ts.map +1 -1
  123. package/dist/core/extensions/index.js +1 -1
  124. package/dist/core/extensions/index.js.map +1 -1
  125. package/dist/core/extensions/loader.d.ts.map +1 -1
  126. package/dist/core/extensions/loader.js +115 -48
  127. package/dist/core/extensions/loader.js.map +1 -1
  128. package/dist/core/extensions/types.d.ts +44 -9
  129. package/dist/core/extensions/types.d.ts.map +1 -1
  130. package/dist/core/extensions/types.js +3 -0
  131. package/dist/core/extensions/types.js.map +1 -1
  132. package/dist/core/footer-data-provider.d.ts +1 -1
  133. package/dist/core/footer-data-provider.d.ts.map +1 -1
  134. package/dist/core/footer-data-provider.js +1 -1
  135. package/dist/core/footer-data-provider.js.map +1 -1
  136. package/dist/core/http-dispatcher.d.ts +1 -2
  137. package/dist/core/http-dispatcher.d.ts.map +1 -1
  138. package/dist/core/http-dispatcher.js +2 -17
  139. package/dist/core/http-dispatcher.js.map +1 -1
  140. package/dist/core/http-idle-timeout.d.ts +12 -0
  141. package/dist/core/http-idle-timeout.d.ts.map +1 -0
  142. package/dist/core/http-idle-timeout.js +27 -0
  143. package/dist/core/http-idle-timeout.js.map +1 -0
  144. package/dist/core/index.d.ts +1 -0
  145. package/dist/core/index.d.ts.map +1 -1
  146. package/dist/core/index.js +1 -0
  147. package/dist/core/index.js.map +1 -1
  148. package/dist/core/keybindings.d.ts +38 -13
  149. package/dist/core/keybindings.d.ts.map +1 -1
  150. package/dist/core/keybindings.js +26 -5
  151. package/dist/core/keybindings.js.map +1 -1
  152. package/dist/core/lsp/client.d.ts +78 -0
  153. package/dist/core/lsp/client.d.ts.map +1 -0
  154. package/dist/core/lsp/client.js +494 -0
  155. package/dist/core/lsp/client.js.map +1 -0
  156. package/dist/core/lsp/diagnostics.d.ts +47 -0
  157. package/dist/core/lsp/diagnostics.d.ts.map +1 -0
  158. package/dist/core/lsp/diagnostics.js +224 -0
  159. package/dist/core/lsp/diagnostics.js.map +1 -0
  160. package/dist/core/lsp/pool.d.ts +48 -0
  161. package/dist/core/lsp/pool.d.ts.map +1 -0
  162. package/dist/core/lsp/pool.js +150 -0
  163. package/dist/core/lsp/pool.js.map +1 -0
  164. package/dist/core/lsp/registry.d.ts +49 -0
  165. package/dist/core/lsp/registry.d.ts.map +1 -0
  166. package/dist/core/lsp/registry.js +215 -0
  167. package/dist/core/lsp/registry.js.map +1 -0
  168. package/dist/core/lsp/status.d.ts +23 -0
  169. package/dist/core/lsp/status.d.ts.map +1 -0
  170. package/dist/core/lsp/status.js +35 -0
  171. package/dist/core/lsp/status.js.map +1 -0
  172. package/dist/core/lsp/sync.d.ts +13 -0
  173. package/dist/core/lsp/sync.d.ts.map +1 -0
  174. package/dist/core/lsp/sync.js +71 -0
  175. package/dist/core/lsp/sync.js.map +1 -0
  176. package/dist/core/model-config.d.ts +18 -0
  177. package/dist/core/model-config.d.ts.map +1 -1
  178. package/dist/core/model-config.js +25 -6
  179. package/dist/core/model-config.js.map +1 -1
  180. package/dist/core/model-resolver.d.ts +21 -0
  181. package/dist/core/model-resolver.d.ts.map +1 -1
  182. package/dist/core/model-resolver.js +73 -8
  183. package/dist/core/model-resolver.js.map +1 -1
  184. package/dist/core/model-runtime.d.ts +73 -0
  185. package/dist/core/model-runtime.d.ts.map +1 -1
  186. package/dist/core/model-runtime.js +225 -8
  187. package/dist/core/model-runtime.js.map +1 -1
  188. package/dist/core/models-store.d.ts.map +1 -1
  189. package/dist/core/models-store.js +2 -1
  190. package/dist/core/models-store.js.map +1 -1
  191. package/dist/core/observability/aggregate.d.ts +21 -0
  192. package/dist/core/observability/aggregate.d.ts.map +1 -0
  193. package/dist/core/observability/aggregate.js +36 -0
  194. package/dist/core/observability/aggregate.js.map +1 -0
  195. package/dist/core/observability/otlp.d.ts +66 -0
  196. package/dist/core/observability/otlp.d.ts.map +1 -0
  197. package/dist/core/observability/otlp.js +95 -0
  198. package/dist/core/observability/otlp.js.map +1 -0
  199. package/dist/core/package-manager.d.ts.map +1 -1
  200. package/dist/core/package-manager.js +40 -16
  201. package/dist/core/package-manager.js.map +1 -1
  202. package/dist/core/permissions/gate.d.ts +42 -0
  203. package/dist/core/permissions/gate.d.ts.map +1 -0
  204. package/dist/core/permissions/gate.js +72 -0
  205. package/dist/core/permissions/gate.js.map +1 -0
  206. package/dist/core/permissions/modes.d.ts +18 -0
  207. package/dist/core/permissions/modes.d.ts.map +1 -0
  208. package/dist/core/permissions/modes.js +57 -0
  209. package/dist/core/permissions/modes.js.map +1 -0
  210. package/dist/core/permissions/responder.d.ts +30 -0
  211. package/dist/core/permissions/responder.d.ts.map +1 -0
  212. package/dist/core/permissions/responder.js +32 -0
  213. package/dist/core/permissions/responder.js.map +1 -0
  214. package/dist/core/permissions/rules.d.ts +34 -0
  215. package/dist/core/permissions/rules.d.ts.map +1 -0
  216. package/dist/core/permissions/rules.js +74 -0
  217. package/dist/core/permissions/rules.js.map +1 -0
  218. package/dist/core/permissions/startup.d.ts +45 -0
  219. package/dist/core/permissions/startup.d.ts.map +1 -0
  220. package/dist/core/permissions/startup.js +63 -0
  221. package/dist/core/permissions/startup.js.map +1 -0
  222. package/dist/core/permissions/store.d.ts +114 -0
  223. package/dist/core/permissions/store.d.ts.map +1 -0
  224. package/dist/core/permissions/store.js +229 -0
  225. package/dist/core/permissions/store.js.map +1 -0
  226. package/dist/core/pi-manifest.d.ts.map +1 -1
  227. package/dist/core/pi-manifest.js +2 -1
  228. package/dist/core/pi-manifest.js.map +1 -1
  229. package/dist/core/project-trust.d.ts.map +1 -1
  230. package/dist/core/project-trust.js +1 -1
  231. package/dist/core/project-trust.js.map +1 -1
  232. package/dist/core/provider-attribution.d.ts.map +1 -1
  233. package/dist/core/provider-attribution.js +6 -7
  234. package/dist/core/provider-attribution.js.map +1 -1
  235. package/dist/core/remote-catalog-provider.d.ts +2 -2
  236. package/dist/core/remote-catalog-provider.d.ts.map +1 -1
  237. package/dist/core/remote-catalog-provider.js +22 -17
  238. package/dist/core/remote-catalog-provider.js.map +1 -1
  239. package/dist/core/resource-loader.d.ts.map +1 -1
  240. package/dist/core/resource-loader.js +3 -2
  241. package/dist/core/resource-loader.js.map +1 -1
  242. package/dist/core/sandbox/bwrap-arguments.d.ts +52 -0
  243. package/dist/core/sandbox/bwrap-arguments.d.ts.map +1 -0
  244. package/dist/core/sandbox/bwrap-arguments.js +86 -0
  245. package/dist/core/sandbox/bwrap-arguments.js.map +1 -0
  246. package/dist/core/sandbox/child-entry.d.ts +2 -0
  247. package/dist/core/sandbox/child-entry.d.ts.map +1 -0
  248. package/dist/core/sandbox/child-entry.js +22 -0
  249. package/dist/core/sandbox/child-entry.js.map +1 -0
  250. package/dist/core/sandbox/cli-launch.d.ts +88 -0
  251. package/dist/core/sandbox/cli-launch.d.ts.map +1 -0
  252. package/dist/core/sandbox/cli-launch.js +272 -0
  253. package/dist/core/sandbox/cli-launch.js.map +1 -0
  254. package/dist/core/sandbox/cli-supervisor.d.ts +30 -0
  255. package/dist/core/sandbox/cli-supervisor.d.ts.map +1 -0
  256. package/dist/core/sandbox/cli-supervisor.js +123 -0
  257. package/dist/core/sandbox/cli-supervisor.js.map +1 -0
  258. package/dist/core/sandbox/default-hosts.d.ts +28 -0
  259. package/dist/core/sandbox/default-hosts.d.ts.map +1 -0
  260. package/dist/core/sandbox/default-hosts.js +69 -0
  261. package/dist/core/sandbox/default-hosts.js.map +1 -0
  262. package/dist/core/sandbox/full-access.d.ts +28 -0
  263. package/dist/core/sandbox/full-access.d.ts.map +1 -0
  264. package/dist/core/sandbox/full-access.js +63 -0
  265. package/dist/core/sandbox/full-access.js.map +1 -0
  266. package/dist/core/sandbox/git-identity.d.ts +57 -0
  267. package/dist/core/sandbox/git-identity.d.ts.map +1 -0
  268. package/dist/core/sandbox/git-identity.js +78 -0
  269. package/dist/core/sandbox/git-identity.js.map +1 -0
  270. package/dist/core/sandbox/host-approval.d.ts +42 -0
  271. package/dist/core/sandbox/host-approval.d.ts.map +1 -0
  272. package/dist/core/sandbox/host-approval.js +99 -0
  273. package/dist/core/sandbox/host-approval.js.map +1 -0
  274. package/dist/core/sandbox/linux-backend.d.ts +46 -0
  275. package/dist/core/sandbox/linux-backend.d.ts.map +1 -0
  276. package/dist/core/sandbox/linux-backend.js +304 -0
  277. package/dist/core/sandbox/linux-backend.js.map +1 -0
  278. package/dist/core/sandbox/macos-backend.d.ts +28 -0
  279. package/dist/core/sandbox/macos-backend.d.ts.map +1 -0
  280. package/dist/core/sandbox/macos-backend.js +313 -0
  281. package/dist/core/sandbox/macos-backend.js.map +1 -0
  282. package/dist/core/sandbox/network-proxy.d.ts +38 -0
  283. package/dist/core/sandbox/network-proxy.d.ts.map +1 -0
  284. package/dist/core/sandbox/network-proxy.js +153 -0
  285. package/dist/core/sandbox/network-proxy.js.map +1 -0
  286. package/dist/core/sandbox/network-refusal.d.ts +24 -0
  287. package/dist/core/sandbox/network-refusal.d.ts.map +1 -0
  288. package/dist/core/sandbox/network-refusal.js +72 -0
  289. package/dist/core/sandbox/network-refusal.js.map +1 -0
  290. package/dist/core/sandbox/policy.d.ts +40 -0
  291. package/dist/core/sandbox/policy.d.ts.map +1 -0
  292. package/dist/core/sandbox/policy.js +55 -0
  293. package/dist/core/sandbox/policy.js.map +1 -0
  294. package/dist/core/sandbox/profiles.d.ts +25 -0
  295. package/dist/core/sandbox/profiles.d.ts.map +1 -0
  296. package/dist/core/sandbox/profiles.js +23 -0
  297. package/dist/core/sandbox/profiles.js.map +1 -0
  298. package/dist/core/sandbox/rpc/command-client.d.ts +28 -0
  299. package/dist/core/sandbox/rpc/command-client.d.ts.map +1 -0
  300. package/dist/core/sandbox/rpc/command-client.js +86 -0
  301. package/dist/core/sandbox/rpc/command-client.js.map +1 -0
  302. package/dist/core/sandbox/rpc/command-proxy.d.ts +48 -0
  303. package/dist/core/sandbox/rpc/command-proxy.d.ts.map +1 -0
  304. package/dist/core/sandbox/rpc/command-proxy.js +120 -0
  305. package/dist/core/sandbox/rpc/command-proxy.js.map +1 -0
  306. package/dist/core/sandbox/rpc/credential-client.d.ts +20 -0
  307. package/dist/core/sandbox/rpc/credential-client.d.ts.map +1 -0
  308. package/dist/core/sandbox/rpc/credential-client.js +170 -0
  309. package/dist/core/sandbox/rpc/credential-client.js.map +1 -0
  310. package/dist/core/sandbox/rpc/credential-proxy.d.ts +44 -0
  311. package/dist/core/sandbox/rpc/credential-proxy.d.ts.map +1 -0
  312. package/dist/core/sandbox/rpc/credential-proxy.js +287 -0
  313. package/dist/core/sandbox/rpc/credential-proxy.js.map +1 -0
  314. package/dist/core/sandbox/rpc/framing.d.ts +35 -0
  315. package/dist/core/sandbox/rpc/framing.d.ts.map +1 -0
  316. package/dist/core/sandbox/rpc/framing.js +114 -0
  317. package/dist/core/sandbox/rpc/framing.js.map +1 -0
  318. package/dist/core/sandbox/rpc/git-credential-helper.d.ts +40 -0
  319. package/dist/core/sandbox/rpc/git-credential-helper.d.ts.map +1 -0
  320. package/dist/core/sandbox/rpc/git-credential-helper.js +152 -0
  321. package/dist/core/sandbox/rpc/git-credential-helper.js.map +1 -0
  322. package/dist/core/sandbox/rpc/git-credential-proxy.d.ts +48 -0
  323. package/dist/core/sandbox/rpc/git-credential-proxy.d.ts.map +1 -0
  324. package/dist/core/sandbox/rpc/git-credential-proxy.js +142 -0
  325. package/dist/core/sandbox/rpc/git-credential-proxy.js.map +1 -0
  326. package/dist/core/sandbox/supervisor-temp.d.ts +18 -0
  327. package/dist/core/sandbox/supervisor-temp.d.ts.map +1 -0
  328. package/dist/core/sandbox/supervisor-temp.js +21 -0
  329. package/dist/core/sandbox/supervisor-temp.js.map +1 -0
  330. package/dist/core/sandbox/supervisor.d.ts +51 -0
  331. package/dist/core/sandbox/supervisor.d.ts.map +1 -0
  332. package/dist/core/sandbox/supervisor.js +18 -0
  333. package/dist/core/sandbox/supervisor.js.map +1 -0
  334. package/dist/core/sandbox/terminal-handoff.d.ts +45 -0
  335. package/dist/core/sandbox/terminal-handoff.d.ts.map +1 -0
  336. package/dist/core/sandbox/terminal-handoff.js +176 -0
  337. package/dist/core/sandbox/terminal-handoff.js.map +1 -0
  338. package/dist/core/sandbox/terminal-size.d.ts +40 -0
  339. package/dist/core/sandbox/terminal-size.d.ts.map +1 -0
  340. package/dist/core/sandbox/terminal-size.js +105 -0
  341. package/dist/core/sandbox/terminal-size.js.map +1 -0
  342. package/dist/core/sandbox/violations.d.ts +23 -0
  343. package/dist/core/sandbox/violations.d.ts.map +1 -0
  344. package/dist/core/sandbox/violations.js +30 -0
  345. package/dist/core/sandbox/violations.js.map +1 -0
  346. package/dist/core/sdk.d.ts +32 -41
  347. package/dist/core/sdk.d.ts.map +1 -1
  348. package/dist/core/sdk.js +161 -6
  349. package/dist/core/sdk.js.map +1 -1
  350. package/dist/core/session-export.d.ts +4 -0
  351. package/dist/core/session-export.d.ts.map +1 -0
  352. package/dist/core/session-export.js +32 -0
  353. package/dist/core/session-export.js.map +1 -0
  354. package/dist/core/session-lease.d.ts +31 -0
  355. package/dist/core/session-lease.d.ts.map +1 -0
  356. package/dist/core/session-lease.js +138 -0
  357. package/dist/core/session-lease.js.map +1 -0
  358. package/dist/core/session-manager.d.ts +46 -2
  359. package/dist/core/session-manager.d.ts.map +1 -1
  360. package/dist/core/session-manager.js +62 -4
  361. package/dist/core/session-manager.js.map +1 -1
  362. package/dist/core/session-share.d.ts +31 -0
  363. package/dist/core/session-share.d.ts.map +1 -0
  364. package/dist/core/session-share.js +45 -0
  365. package/dist/core/session-share.js.map +1 -0
  366. package/dist/core/settings-diagnostics.d.ts +9 -0
  367. package/dist/core/settings-diagnostics.d.ts.map +1 -0
  368. package/dist/core/settings-diagnostics.js +21 -0
  369. package/dist/core/settings-diagnostics.js.map +1 -0
  370. package/dist/core/settings-manager.d.ts +92 -9
  371. package/dist/core/settings-manager.d.ts.map +1 -1
  372. package/dist/core/settings-manager.js +138 -36
  373. package/dist/core/settings-manager.js.map +1 -1
  374. package/dist/core/skills.d.ts +19 -7
  375. package/dist/core/skills.d.ts.map +1 -1
  376. package/dist/core/skills.js +128 -52
  377. package/dist/core/skills.js.map +1 -1
  378. package/dist/core/slash-commands.d.ts.map +1 -1
  379. package/dist/core/slash-commands.js +5 -2
  380. package/dist/core/slash-commands.js.map +1 -1
  381. package/dist/core/system-prompt.d.ts +2 -2
  382. package/dist/core/system-prompt.d.ts.map +1 -1
  383. package/dist/core/system-prompt.js +59 -68
  384. package/dist/core/system-prompt.js.map +1 -1
  385. package/dist/core/timings.d.ts +1 -1
  386. package/dist/core/timings.d.ts.map +1 -1
  387. package/dist/core/timings.js +3 -2
  388. package/dist/core/timings.js.map +1 -1
  389. package/dist/core/tools/ask-user.d.ts +25 -0
  390. package/dist/core/tools/ask-user.d.ts.map +1 -0
  391. package/dist/core/tools/ask-user.js +49 -0
  392. package/dist/core/tools/ask-user.js.map +1 -0
  393. package/dist/core/tools/bash-command-segments.d.ts +22 -0
  394. package/dist/core/tools/bash-command-segments.d.ts.map +1 -0
  395. package/dist/core/tools/bash-command-segments.js +128 -0
  396. package/dist/core/tools/bash-command-segments.js.map +1 -0
  397. package/dist/core/tools/bash.d.ts +40 -4
  398. package/dist/core/tools/bash.d.ts.map +1 -1
  399. package/dist/core/tools/bash.js +183 -32
  400. package/dist/core/tools/bash.js.map +1 -1
  401. package/dist/core/tools/contract.d.ts +183 -0
  402. package/dist/core/tools/contract.d.ts.map +1 -0
  403. package/dist/core/tools/contract.js +48 -0
  404. package/dist/core/tools/contract.js.map +1 -0
  405. package/dist/core/tools/delegate.d.ts +38 -0
  406. package/dist/core/tools/delegate.d.ts.map +1 -0
  407. package/dist/core/tools/delegate.js +90 -0
  408. package/dist/core/tools/delegate.js.map +1 -0
  409. package/dist/core/tools/diagnostics.d.ts +39 -0
  410. package/dist/core/tools/diagnostics.d.ts.map +1 -0
  411. package/dist/core/tools/diagnostics.js +62 -0
  412. package/dist/core/tools/diagnostics.js.map +1 -0
  413. package/dist/core/tools/edit-diff.d.ts +0 -5
  414. package/dist/core/tools/edit-diff.d.ts.map +1 -1
  415. package/dist/core/tools/edit-diff.js +2 -5
  416. package/dist/core/tools/edit-diff.js.map +1 -1
  417. package/dist/core/tools/edit.d.ts +12 -2
  418. package/dist/core/tools/edit.d.ts.map +1 -1
  419. package/dist/core/tools/edit.js +61 -10
  420. package/dist/core/tools/edit.js.map +1 -1
  421. package/dist/core/tools/find.d.ts +2 -2
  422. package/dist/core/tools/find.d.ts.map +1 -1
  423. package/dist/core/tools/find.js +13 -1
  424. package/dist/core/tools/find.js.map +1 -1
  425. package/dist/core/tools/grep.d.ts +4 -2
  426. package/dist/core/tools/grep.d.ts.map +1 -1
  427. package/dist/core/tools/grep.js +47 -10
  428. package/dist/core/tools/grep.js.map +1 -1
  429. package/dist/core/tools/index.d.ts +51 -6
  430. package/dist/core/tools/index.d.ts.map +1 -1
  431. package/dist/core/tools/index.js +145 -2
  432. package/dist/core/tools/index.js.map +1 -1
  433. package/dist/core/tools/ls.d.ts +4 -2
  434. package/dist/core/tools/ls.d.ts.map +1 -1
  435. package/dist/core/tools/ls.js +17 -1
  436. package/dist/core/tools/ls.js.map +1 -1
  437. package/dist/core/tools/lsp.d.ts +62 -0
  438. package/dist/core/tools/lsp.d.ts.map +1 -0
  439. package/dist/core/tools/lsp.js +216 -0
  440. package/dist/core/tools/lsp.js.map +1 -0
  441. package/dist/core/tools/output-accumulator.d.ts.map +1 -1
  442. package/dist/core/tools/output-accumulator.js +1 -1
  443. package/dist/core/tools/output-accumulator.js.map +1 -1
  444. package/dist/core/tools/path-permission.d.ts +18 -0
  445. package/dist/core/tools/path-permission.d.ts.map +1 -0
  446. package/dist/core/tools/path-permission.js +28 -0
  447. package/dist/core/tools/path-permission.js.map +1 -0
  448. package/dist/core/tools/plan-present.d.ts +27 -0
  449. package/dist/core/tools/plan-present.d.ts.map +1 -0
  450. package/dist/core/tools/plan-present.js +55 -0
  451. package/dist/core/tools/plan-present.js.map +1 -0
  452. package/dist/core/tools/powershell.d.ts +16 -0
  453. package/dist/core/tools/powershell.d.ts.map +1 -0
  454. package/dist/core/tools/powershell.js +39 -0
  455. package/dist/core/tools/powershell.js.map +1 -0
  456. package/dist/core/tools/read.d.ts +2 -2
  457. package/dist/core/tools/read.d.ts.map +1 -1
  458. package/dist/core/tools/read.js +14 -0
  459. package/dist/core/tools/read.js.map +1 -1
  460. package/dist/core/tools/skill-search.d.ts +32 -0
  461. package/dist/core/tools/skill-search.d.ts.map +1 -0
  462. package/dist/core/tools/skill-search.js +56 -0
  463. package/dist/core/tools/skill-search.js.map +1 -0
  464. package/dist/core/tools/test.d.ts +34 -0
  465. package/dist/core/tools/test.d.ts.map +1 -0
  466. package/dist/core/tools/test.js +75 -0
  467. package/dist/core/tools/test.js.map +1 -0
  468. package/dist/core/tools/todo-write.d.ts +31 -0
  469. package/dist/core/tools/todo-write.d.ts.map +1 -0
  470. package/dist/core/tools/todo-write.js +51 -0
  471. package/dist/core/tools/todo-write.js.map +1 -0
  472. package/dist/core/tools/tool-definition-wrapper.d.ts +13 -2
  473. package/dist/core/tools/tool-definition-wrapper.d.ts.map +1 -1
  474. package/dist/core/tools/tool-definition-wrapper.js +11 -1
  475. package/dist/core/tools/tool-definition-wrapper.js.map +1 -1
  476. package/dist/core/tools/tool-schema.d.ts +22 -0
  477. package/dist/core/tools/tool-schema.d.ts.map +1 -0
  478. package/dist/core/tools/tool-schema.js +45 -0
  479. package/dist/core/tools/tool-schema.js.map +1 -0
  480. package/dist/core/tools/web-fetch.d.ts +26 -0
  481. package/dist/core/tools/web-fetch.d.ts.map +1 -0
  482. package/dist/core/tools/web-fetch.js +62 -0
  483. package/dist/core/tools/web-fetch.js.map +1 -0
  484. package/dist/core/tools/web-search-exa.d.ts +33 -0
  485. package/dist/core/tools/web-search-exa.d.ts.map +1 -0
  486. package/dist/core/tools/web-search-exa.js +128 -0
  487. package/dist/core/tools/web-search-exa.js.map +1 -0
  488. package/dist/core/tools/web-search.d.ts +26 -0
  489. package/dist/core/tools/web-search.d.ts.map +1 -0
  490. package/dist/core/tools/web-search.js +53 -0
  491. package/dist/core/tools/web-search.js.map +1 -0
  492. package/dist/core/tools/write.d.ts +16 -2
  493. package/dist/core/tools/write.d.ts.map +1 -1
  494. package/dist/core/tools/write.js +48 -2
  495. package/dist/core/tools/write.js.map +1 -1
  496. package/dist/core/trust-manager.d.ts.map +1 -1
  497. package/dist/core/trust-manager.js +2 -1
  498. package/dist/core/trust-manager.js.map +1 -1
  499. package/dist/core/usage-performance-store.d.ts +56 -0
  500. package/dist/core/usage-performance-store.d.ts.map +1 -0
  501. package/dist/core/usage-performance-store.js +85 -0
  502. package/dist/core/usage-performance-store.js.map +1 -0
  503. package/dist/core/web-search-provider.d.ts +64 -0
  504. package/dist/core/web-search-provider.d.ts.map +1 -0
  505. package/dist/core/web-search-provider.js +149 -0
  506. package/dist/core/web-search-provider.js.map +1 -0
  507. package/dist/extensions/llama/index.d.ts.map +1 -1
  508. package/dist/extensions/llama/index.js +2 -0
  509. package/dist/extensions/llama/index.js.map +1 -1
  510. package/dist/extensions/llama/provider.d.ts.map +1 -1
  511. package/dist/extensions/llama/provider.js +6 -2
  512. package/dist/extensions/llama/provider.js.map +1 -1
  513. package/dist/index.d.ts +7 -6
  514. package/dist/index.d.ts.map +1 -1
  515. package/dist/index.js +5 -4
  516. package/dist/index.js.map +1 -1
  517. package/dist/main.d.ts +5 -0
  518. package/dist/main.d.ts.map +1 -1
  519. package/dist/main.js +87 -22
  520. package/dist/main.js.map +1 -1
  521. package/dist/migrations.d.ts.map +1 -1
  522. package/dist/migrations.js +6 -5
  523. package/dist/migrations.js.map +1 -1
  524. package/dist/modes/interactive/components/config-selector.d.ts.map +1 -1
  525. package/dist/modes/interactive/components/config-selector.js +9 -6
  526. package/dist/modes/interactive/components/config-selector.js.map +1 -1
  527. package/dist/modes/interactive/components/custom-editor.d.ts +72 -1
  528. package/dist/modes/interactive/components/custom-editor.d.ts.map +1 -1
  529. package/dist/modes/interactive/components/custom-editor.js +338 -1
  530. package/dist/modes/interactive/components/custom-editor.js.map +1 -1
  531. package/dist/modes/interactive/components/dynamic-border.d.ts +7 -1
  532. package/dist/modes/interactive/components/dynamic-border.d.ts.map +1 -1
  533. package/dist/modes/interactive/components/dynamic-border.js +11 -2
  534. package/dist/modes/interactive/components/dynamic-border.js.map +1 -1
  535. package/dist/modes/interactive/components/extension-selector.d.ts +4 -0
  536. package/dist/modes/interactive/components/extension-selector.d.ts.map +1 -1
  537. package/dist/modes/interactive/components/extension-selector.js +24 -6
  538. package/dist/modes/interactive/components/extension-selector.js.map +1 -1
  539. package/dist/modes/interactive/components/first-time-setup.d.ts +1 -4
  540. package/dist/modes/interactive/components/first-time-setup.d.ts.map +1 -1
  541. package/dist/modes/interactive/components/first-time-setup.js +17 -41
  542. package/dist/modes/interactive/components/first-time-setup.js.map +1 -1
  543. package/dist/modes/interactive/components/first-use-hints.d.ts +8 -0
  544. package/dist/modes/interactive/components/first-use-hints.d.ts.map +1 -0
  545. package/dist/modes/interactive/components/first-use-hints.js +22 -0
  546. package/dist/modes/interactive/components/first-use-hints.js.map +1 -0
  547. package/dist/modes/interactive/components/footer.d.ts +23 -1
  548. package/dist/modes/interactive/components/footer.d.ts.map +1 -1
  549. package/dist/modes/interactive/components/footer.js +182 -21
  550. package/dist/modes/interactive/components/footer.js.map +1 -1
  551. package/dist/modes/interactive/components/index.d.ts +2 -0
  552. package/dist/modes/interactive/components/index.d.ts.map +1 -1
  553. package/dist/modes/interactive/components/index.js +2 -0
  554. package/dist/modes/interactive/components/index.js.map +1 -1
  555. package/dist/modes/interactive/components/model-selector.d.ts +31 -9
  556. package/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
  557. package/dist/modes/interactive/components/model-selector.js +236 -96
  558. package/dist/modes/interactive/components/model-selector.js.map +1 -1
  559. package/dist/modes/interactive/components/settings-selector.d.ts +25 -5
  560. package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  561. package/dist/modes/interactive/components/settings-selector.js +226 -58
  562. package/dist/modes/interactive/components/settings-selector.js.map +1 -1
  563. package/dist/modes/interactive/components/settings-submenu.d.ts +72 -0
  564. package/dist/modes/interactive/components/settings-submenu.d.ts.map +1 -0
  565. package/dist/modes/interactive/components/settings-submenu.js +165 -0
  566. package/dist/modes/interactive/components/settings-submenu.js.map +1 -0
  567. package/dist/modes/interactive/components/splash-header.d.ts +66 -0
  568. package/dist/modes/interactive/components/splash-header.d.ts.map +1 -0
  569. package/dist/modes/interactive/components/splash-header.js +180 -0
  570. package/dist/modes/interactive/components/splash-header.js.map +1 -0
  571. package/dist/modes/interactive/components/thinking-selector.d.ts +15 -3
  572. package/dist/modes/interactive/components/thinking-selector.d.ts.map +1 -1
  573. package/dist/modes/interactive/components/thinking-selector.js +74 -17
  574. package/dist/modes/interactive/components/thinking-selector.js.map +1 -1
  575. package/dist/modes/interactive/components/tool-execution.d.ts +16 -1
  576. package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  577. package/dist/modes/interactive/components/tool-execution.js +113 -35
  578. package/dist/modes/interactive/components/tool-execution.js.map +1 -1
  579. package/dist/modes/interactive/components/tool-panel.d.ts +30 -0
  580. package/dist/modes/interactive/components/tool-panel.d.ts.map +1 -0
  581. package/dist/modes/interactive/components/tool-panel.js +130 -0
  582. package/dist/modes/interactive/components/tool-panel.js.map +1 -0
  583. package/dist/modes/interactive/external-editor.d.ts.map +1 -1
  584. package/dist/modes/interactive/external-editor.js +60 -10
  585. package/dist/modes/interactive/external-editor.js.map +1 -1
  586. package/dist/modes/interactive/interactive-mode.d.ts +61 -5
  587. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  588. package/dist/modes/interactive/interactive-mode.js +642 -203
  589. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  590. package/dist/modes/interactive/model-catalog-refresh.d.ts +7 -0
  591. package/dist/modes/interactive/model-catalog-refresh.d.ts.map +1 -0
  592. package/dist/modes/interactive/model-catalog-refresh.js +34 -0
  593. package/dist/modes/interactive/model-catalog-refresh.js.map +1 -0
  594. package/dist/modes/interactive/theme/apex.json +93 -0
  595. package/dist/modes/interactive/theme/dark.json +2 -0
  596. package/dist/modes/interactive/theme/light.json +3 -1
  597. package/dist/modes/interactive/theme/theme-controller.d.ts +10 -2
  598. package/dist/modes/interactive/theme/theme-controller.d.ts.map +1 -1
  599. package/dist/modes/interactive/theme/theme-controller.js +25 -11
  600. package/dist/modes/interactive/theme/theme-controller.js.map +1 -1
  601. package/dist/modes/interactive/theme/theme-schema.json +11 -3
  602. package/dist/modes/interactive/theme/theme.d.ts +14 -3
  603. package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  604. package/dist/modes/interactive/theme/theme.js +65 -10
  605. package/dist/modes/interactive/theme/theme.js.map +1 -1
  606. package/dist/modes/json-event.d.ts +18 -12
  607. package/dist/modes/json-event.d.ts.map +1 -1
  608. package/dist/modes/json-event.js +22 -5
  609. package/dist/modes/json-event.js.map +1 -1
  610. package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  611. package/dist/modes/rpc/rpc-mode.js +2 -1
  612. package/dist/modes/rpc/rpc-mode.js.map +1 -1
  613. package/dist/package-manager-cli.d.ts +1 -0
  614. package/dist/package-manager-cli.d.ts.map +1 -1
  615. package/dist/package-manager-cli.js +211 -14
  616. package/dist/package-manager-cli.js.map +1 -1
  617. package/dist/rpc-entry.d.ts.map +1 -1
  618. package/dist/rpc-entry.js +2 -1
  619. package/dist/rpc-entry.js.map +1 -1
  620. package/dist/server/create-harness.d.ts +1 -1
  621. package/dist/server/create-harness.d.ts.map +1 -1
  622. package/dist/server/create-harness.js +8 -5
  623. package/dist/server/create-harness.js.map +1 -1
  624. package/dist/testing/replay/metrics.d.ts +24 -0
  625. package/dist/testing/replay/metrics.d.ts.map +1 -0
  626. package/dist/testing/replay/metrics.js +35 -0
  627. package/dist/testing/replay/metrics.js.map +1 -0
  628. package/dist/testing/replay/recorded-provider.d.ts +10 -0
  629. package/dist/testing/replay/recorded-provider.d.ts.map +1 -0
  630. package/dist/testing/replay/recorded-provider.js +108 -0
  631. package/dist/testing/replay/recorded-provider.js.map +1 -0
  632. package/dist/testing/replay/runner.d.ts +56 -0
  633. package/dist/testing/replay/runner.d.ts.map +1 -0
  634. package/dist/testing/replay/runner.js +406 -0
  635. package/dist/testing/replay/runner.js.map +1 -0
  636. package/dist/themes/apex-logo.d.ts +41 -0
  637. package/dist/themes/apex-logo.d.ts.map +1 -0
  638. package/dist/themes/apex-logo.js +35 -0
  639. package/dist/themes/apex-logo.js.map +1 -0
  640. package/dist/utils/apex-code-user-agent.d.ts +2 -0
  641. package/dist/utils/apex-code-user-agent.d.ts.map +1 -0
  642. package/dist/utils/apex-code-user-agent.js +5 -0
  643. package/dist/utils/apex-code-user-agent.js.map +1 -0
  644. package/dist/utils/changelog.d.ts +7 -2
  645. package/dist/utils/changelog.d.ts.map +1 -1
  646. package/dist/utils/changelog.js +82 -13
  647. package/dist/utils/changelog.js.map +1 -1
  648. package/dist/utils/clipboard-image.d.ts.map +1 -1
  649. package/dist/utils/clipboard-image.js +1 -1
  650. package/dist/utils/clipboard-image.js.map +1 -1
  651. package/dist/utils/frontmatter.d.ts.map +1 -1
  652. package/dist/utils/frontmatter.js +2 -1
  653. package/dist/utils/frontmatter.js.map +1 -1
  654. package/dist/utils/management-http.d.ts +5 -3
  655. package/dist/utils/management-http.d.ts.map +1 -1
  656. package/dist/utils/management-http.js +14 -10
  657. package/dist/utils/management-http.js.map +1 -1
  658. package/dist/utils/shell.d.ts +3 -0
  659. package/dist/utils/shell.d.ts.map +1 -1
  660. package/dist/utils/shell.js +17 -5
  661. package/dist/utils/shell.js.map +1 -1
  662. package/dist/utils/syntax-highlight.d.ts +1 -0
  663. package/dist/utils/syntax-highlight.d.ts.map +1 -1
  664. package/dist/utils/syntax-highlight.js +60 -1
  665. package/dist/utils/syntax-highlight.js.map +1 -1
  666. package/dist/utils/text.d.ts +8 -0
  667. package/dist/utils/text.d.ts.map +1 -0
  668. package/dist/utils/text.js +9 -0
  669. package/dist/utils/text.js.map +1 -0
  670. package/dist/utils/tools-manager.d.ts +70 -2
  671. package/dist/utils/tools-manager.d.ts.map +1 -1
  672. package/dist/utils/tools-manager.js +280 -124
  673. package/dist/utils/tools-manager.js.map +1 -1
  674. package/dist/utils/version-check.d.ts +7 -5
  675. package/dist/utils/version-check.d.ts.map +1 -1
  676. package/dist/utils/version-check.js +25 -14
  677. package/dist/utils/version-check.js.map +1 -1
  678. package/dist/utils/windows-self-update.d.ts.map +1 -1
  679. package/dist/utils/windows-self-update.js +1 -1
  680. package/dist/utils/windows-self-update.js.map +1 -1
  681. package/docs/compaction.md +39 -24
  682. package/docs/containerization.md +24 -21
  683. package/docs/custom-provider.md +15 -12
  684. package/docs/development.md +11 -11
  685. package/docs/environment-variables.md +37 -77
  686. package/docs/extensions.md +68 -54
  687. package/docs/index.md +12 -24
  688. package/docs/json.md +18 -11
  689. package/docs/keybindings.md +18 -12
  690. package/docs/llama-cpp.md +9 -7
  691. package/docs/models.md +17 -11
  692. package/docs/packages.md +47 -43
  693. package/docs/prompt-templates.md +3 -3
  694. package/docs/providers.md +10 -10
  695. package/docs/quickstart.md +49 -32
  696. package/docs/rpc.md +35 -18
  697. package/docs/sdk.md +20 -6
  698. package/docs/security.md +18 -18
  699. package/docs/session-format.md +9 -9
  700. package/docs/sessions.md +13 -13
  701. package/docs/settings.md +83 -16
  702. package/docs/shell-aliases.md +1 -1
  703. package/docs/skills.md +66 -15
  704. package/docs/terminal-setup.md +44 -19
  705. package/docs/termux.md +4 -4
  706. package/docs/themes.md +32 -11
  707. package/docs/tmux.md +4 -4
  708. package/docs/tui.md +5 -5
  709. package/docs/usage.md +61 -53
  710. package/docs/windows.md +24 -2
  711. package/examples/extensions/border-status-editor.ts +1 -1
  712. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  713. package/examples/extensions/custom-provider-anthropic/package.json +2 -2
  714. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  715. package/examples/extensions/git-checkpoint.ts +2 -2
  716. package/examples/extensions/gondolin/package-lock.json +2 -2
  717. package/examples/extensions/gondolin/package.json +1 -1
  718. package/examples/extensions/notify.ts +3 -1
  719. package/examples/extensions/sandbox/package-lock.json +2 -2
  720. package/examples/extensions/sandbox/package.json +1 -1
  721. package/examples/extensions/subagent/README.md +3 -1
  722. package/examples/extensions/subagent/agents.ts +40 -9
  723. package/examples/extensions/subagent/index.ts +27 -4
  724. package/examples/extensions/titlebar-spinner.ts +1 -1
  725. package/examples/extensions/with-deps/package-lock.json +2 -2
  726. package/examples/extensions/with-deps/package.json +1 -1
  727. package/examples/rpc-extension-ui.ts +1 -1
  728. package/examples/sdk/06-extensions.ts +1 -1
  729. package/examples/sdk/README.md +1 -1
  730. package/npm-shrinkwrap.json +280 -445
  731. package/package.json +9 -12
  732. package/dist/core/telemetry.d.ts +0 -3
  733. package/dist/core/telemetry.d.ts.map +0 -1
  734. package/dist/core/telemetry.js +0 -9
  735. package/dist/core/telemetry.js.map +0 -1
  736. package/dist/utils/pi-user-agent.d.ts +0 -2
  737. package/dist/utils/pi-user-agent.d.ts.map +0 -1
  738. package/dist/utils/pi-user-agent.js +0 -5
  739. package/dist/utils/pi-user-agent.js.map +0 -1
@@ -1,21 +1,30 @@
1
1
  import { theme } from "../theme/theme.js";
2
+ /** Dotted, so an overlay boundary never reads as the solid structural rule. */
3
+ const OVERLAY_RULE = "┄";
2
4
  /**
3
5
  * Dynamic border component that adjusts to viewport width.
4
6
  *
7
+ * It is the boundary every overlay draws, so it is deliberately quiet: a dotted
8
+ * rule in `borderMuted` rather than a solid one in the brand accent. A
9
+ * full-width accent rule above and below every selector made the accent the
10
+ * loudest thing on screen in exactly the moment the user is reading a list.
11
+ *
5
12
  * Note: When used from extensions loaded via jiti, the global `theme` may be undefined
6
13
  * because jiti creates a separate module cache. Always pass an explicit color
7
14
  * function when using DynamicBorder in components exported for extension use.
8
15
  */
9
16
  export class DynamicBorder {
10
17
  color;
11
- constructor(color = (str) => theme.fg("border", str)) {
18
+ glyph;
19
+ constructor(color = (str) => theme.fg("borderMuted", str), glyph = OVERLAY_RULE) {
12
20
  this.color = color;
21
+ this.glyph = glyph;
13
22
  }
14
23
  invalidate() {
15
24
  // No cached state to invalidate currently
16
25
  }
17
26
  render(width) {
18
- return [this.color("─".repeat(Math.max(1, width)))];
27
+ return [this.color(this.glyph.repeat(Math.max(1, width)))];
19
28
  }
20
29
  }
21
30
  //# sourceMappingURL=dynamic-border.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"dynamic-border.js","sourceRoot":"","sources":["../../../../src/modes/interactive/components/dynamic-border.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAE1C;;;;;;GAMG;AACH,MAAM,OAAO,aAAa;IACjB,KAAK,CAA0B;IAEvC,YAAY,KAAK,GAA4B,CAAC,GAAG,EAAE,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC,QAAQ,EAAE,GAAG,CAAC,EAAE;QAC9E,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;IAAA,CACnB;IAED,UAAU,GAAS;QAClB,0CAA0C;IADvB,CAEnB;IAED,MAAM,CAAC,KAAa,EAAY;QAC/B,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,KAAG,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IAAA,CACpD;CACD","sourcesContent":["import type { Component } from \"@earendil-works/pi-tui\";\nimport { theme } from \"../theme/theme.ts\";\n\n/**\n * Dynamic border component that adjusts to viewport width.\n *\n * Note: When used from extensions loaded via jiti, the global `theme` may be undefined\n * because jiti creates a separate module cache. Always pass an explicit color\n * function when using DynamicBorder in components exported for extension use.\n */\nexport class DynamicBorder implements Component {\n\tprivate color: (str: string) => string;\n\n\tconstructor(color: (str: string) => string = (str) => theme.fg(\"border\", str)) {\n\t\tthis.color = color;\n\t}\n\n\tinvalidate(): void {\n\t\t// No cached state to invalidate currently\n\t}\n\n\trender(width: number): string[] {\n\t\treturn [this.color(\"─\".repeat(Math.max(1, width)))];\n\t}\n}\n"]}
1
+ {"version":3,"file":"dynamic-border.js","sourceRoot":"","sources":["../../../../src/modes/interactive/components/dynamic-border.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAE1C,+EAA+E;AAC/E,MAAM,YAAY,GAAG,KAAG,CAAC;AAEzB;;;;;;;;;;;GAWG;AACH,MAAM,OAAO,aAAa;IACjB,KAAK,CAA0B;IAC/B,KAAK,CAAS;IAEtB,YAAY,KAAK,GAA4B,CAAC,GAAG,EAAE,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC,aAAa,EAAE,GAAG,CAAC,EAAE,KAAK,GAAG,YAAY,EAAE;QACzG,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;QACnB,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;IAAA,CACnB;IAED,UAAU,GAAS;QAClB,0CAA0C;IADvB,CAEnB;IAED,MAAM,CAAC,KAAa,EAAY;QAC/B,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IAAA,CAC3D;CACD","sourcesContent":["import type { Component } from \"@earendil-works/pi-tui\";\nimport { theme } from \"../theme/theme.ts\";\n\n/** Dotted, so an overlay boundary never reads as the solid structural rule. */\nconst OVERLAY_RULE = \"┄\";\n\n/**\n * Dynamic border component that adjusts to viewport width.\n *\n * It is the boundary every overlay draws, so it is deliberately quiet: a dotted\n * rule in `borderMuted` rather than a solid one in the brand accent. A\n * full-width accent rule above and below every selector made the accent the\n * loudest thing on screen in exactly the moment the user is reading a list.\n *\n * Note: When used from extensions loaded via jiti, the global `theme` may be undefined\n * because jiti creates a separate module cache. Always pass an explicit color\n * function when using DynamicBorder in components exported for extension use.\n */\nexport class DynamicBorder implements Component {\n\tprivate color: (str: string) => string;\n\tprivate glyph: string;\n\n\tconstructor(color: (str: string) => string = (str) => theme.fg(\"borderMuted\", str), glyph = OVERLAY_RULE) {\n\t\tthis.color = color;\n\t\tthis.glyph = glyph;\n\t}\n\n\tinvalidate(): void {\n\t\t// No cached state to invalidate currently\n\t}\n\n\trender(width: number): string[] {\n\t\treturn [this.color(this.glyph.repeat(Math.max(1, width)))];\n\t}\n}\n"]}
@@ -7,9 +7,11 @@ export interface ExtensionSelectorOptions {
7
7
  tui?: TUI;
8
8
  timeout?: number;
9
9
  onToggleToolsExpanded?: () => void;
10
+ enableSearch?: boolean;
10
11
  }
11
12
  export declare class ExtensionSelectorComponent extends Container {
12
13
  private options;
14
+ private filteredOptions;
13
15
  private selectedIndex;
14
16
  private listContainer;
15
17
  private onSelectCallback;
@@ -18,8 +20,10 @@ export declare class ExtensionSelectorComponent extends Container {
18
20
  private baseTitle;
19
21
  private countdown;
20
22
  private onToggleToolsExpanded;
23
+ private searchInput;
21
24
  constructor(title: string, options: string[], onSelect: (option: string) => void, onCancel: () => void, opts?: ExtensionSelectorOptions);
22
25
  private updateList;
26
+ private filterOptions;
23
27
  handleInput(keyData: string): void;
24
28
  dispose(): void;
25
29
  }
@@ -1 +1 @@
1
- {"version":3,"file":"extension-selector.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/extension-selector.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,SAAS,EAAgC,KAAK,GAAG,EAAE,MAAM,wBAAwB,CAAC;AAM3F,MAAM,WAAW,wBAAwB;IACxC,GAAG,CAAC,EAAE,GAAG,CAAC;IACV,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,qBAAqB,CAAC,EAAE,MAAM,IAAI,CAAC;CACnC;AAED,qBAAa,0BAA2B,SAAQ,SAAS;IACxD,OAAO,CAAC,OAAO,CAAW;IAC1B,OAAO,CAAC,aAAa,CAAK;IAC1B,OAAO,CAAC,aAAa,CAAY;IACjC,OAAO,CAAC,gBAAgB,CAA2B;IACnD,OAAO,CAAC,gBAAgB,CAAa;IACrC,OAAO,CAAC,SAAS,CAAO;IACxB,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,SAAS,CAA6B;IAC9C,OAAO,CAAC,qBAAqB,CAA2B;IAExD,YACC,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,MAAM,EAAE,EACjB,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,EAClC,QAAQ,EAAE,MAAM,IAAI,EACpB,IAAI,CAAC,EAAE,wBAAwB,EA4C/B;IAED,OAAO,CAAC,UAAU;IAWlB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAgBjC;IAED,OAAO,IAAI,IAAI,CAEd;CACD","sourcesContent":["/**\n * Generic selector component for extensions.\n * Displays a list of string options with keyboard navigation.\n */\n\nimport { Container, getKeybindings, Spacer, Text, type TUI } from \"@earendil-works/pi-tui\";\nimport { theme } from \"../theme/theme.ts\";\nimport { CountdownTimer } from \"./countdown-timer.ts\";\nimport { DynamicBorder } from \"./dynamic-border.ts\";\nimport { keyHint, rawKeyHint } from \"./keybinding-hints.ts\";\n\nexport interface ExtensionSelectorOptions {\n\ttui?: TUI;\n\ttimeout?: number;\n\tonToggleToolsExpanded?: () => void;\n}\n\nexport class ExtensionSelectorComponent extends Container {\n\tprivate options: string[];\n\tprivate selectedIndex = 0;\n\tprivate listContainer: Container;\n\tprivate onSelectCallback: (option: string) => void;\n\tprivate onCancelCallback: () => void;\n\tprivate titleText: Text;\n\tprivate baseTitle: string;\n\tprivate countdown: CountdownTimer | undefined;\n\tprivate onToggleToolsExpanded: (() => void) | undefined;\n\n\tconstructor(\n\t\ttitle: string,\n\t\toptions: string[],\n\t\tonSelect: (option: string) => void,\n\t\tonCancel: () => void,\n\t\topts?: ExtensionSelectorOptions,\n\t) {\n\t\tsuper();\n\n\t\tthis.options = options;\n\t\tthis.onSelectCallback = onSelect;\n\t\tthis.onCancelCallback = onCancel;\n\t\tthis.onToggleToolsExpanded = opts?.onToggleToolsExpanded;\n\t\tthis.baseTitle = title;\n\n\t\tthis.addChild(new DynamicBorder());\n\t\tthis.addChild(new Spacer(1));\n\n\t\tthis.titleText = new Text(theme.fg(\"accent\", theme.bold(title)), 1, 0);\n\t\tthis.addChild(this.titleText);\n\t\tthis.addChild(new Spacer(1));\n\n\t\tif (opts?.timeout && opts.timeout > 0 && opts.tui) {\n\t\t\tthis.countdown = new CountdownTimer(\n\t\t\t\topts.timeout,\n\t\t\t\topts.tui,\n\t\t\t\t(s) => this.titleText.setText(theme.fg(\"accent\", theme.bold(`${this.baseTitle} (${s}s)`))),\n\t\t\t\t() => this.onCancelCallback(),\n\t\t\t);\n\t\t}\n\n\t\tthis.listContainer = new Container();\n\t\tthis.addChild(this.listContainer);\n\t\tthis.addChild(new Spacer(1));\n\t\tthis.addChild(\n\t\t\tnew Text(\n\t\t\t\trawKeyHint(\"↑↓\", \"navigate\") +\n\t\t\t\t\t\" \" +\n\t\t\t\t\tkeyHint(\"tui.select.confirm\", \"select\") +\n\t\t\t\t\t\" \" +\n\t\t\t\t\tkeyHint(\"tui.select.cancel\", \"cancel\"),\n\t\t\t\t1,\n\t\t\t\t0,\n\t\t\t),\n\t\t);\n\t\tthis.addChild(new Spacer(1));\n\t\tthis.addChild(new DynamicBorder());\n\n\t\tthis.updateList();\n\t}\n\n\tprivate updateList(): void {\n\t\tthis.listContainer.clear();\n\t\tfor (let i = 0; i < this.options.length; i++) {\n\t\t\tconst isSelected = i === this.selectedIndex;\n\t\t\tconst text = isSelected\n\t\t\t\t? theme.fg(\"accent\", \"→ \") + theme.fg(\"accent\", this.options[i])\n\t\t\t\t: ` ${theme.fg(\"text\", this.options[i])}`;\n\t\t\tthis.listContainer.addChild(new Text(text, 1, 0));\n\t\t}\n\t}\n\n\thandleInput(keyData: string): void {\n\t\tconst kb = getKeybindings();\n\t\tif (kb.matches(keyData, \"app.tools.expand\")) {\n\t\t\tthis.onToggleToolsExpanded?.();\n\t\t} else if (kb.matches(keyData, \"tui.select.up\") || keyData === \"k\") {\n\t\t\tthis.selectedIndex = Math.max(0, this.selectedIndex - 1);\n\t\t\tthis.updateList();\n\t\t} else if (kb.matches(keyData, \"tui.select.down\") || keyData === \"j\") {\n\t\t\tthis.selectedIndex = Math.min(this.options.length - 1, this.selectedIndex + 1);\n\t\t\tthis.updateList();\n\t\t} else if (kb.matches(keyData, \"tui.select.confirm\") || keyData === \"\\n\") {\n\t\t\tconst selected = this.options[this.selectedIndex];\n\t\t\tif (selected) this.onSelectCallback(selected);\n\t\t} else if (kb.matches(keyData, \"tui.select.cancel\")) {\n\t\t\tthis.onCancelCallback();\n\t\t}\n\t}\n\n\tdispose(): void {\n\t\tthis.countdown?.dispose();\n\t}\n}\n"]}
1
+ {"version":3,"file":"extension-selector.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/extension-selector.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,SAAS,EAAoD,KAAK,GAAG,EAAE,MAAM,wBAAwB,CAAC;AAM/G,MAAM,WAAW,wBAAwB;IACxC,GAAG,CAAC,EAAE,GAAG,CAAC;IACV,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,qBAAqB,CAAC,EAAE,MAAM,IAAI,CAAC;IACnC,YAAY,CAAC,EAAE,OAAO,CAAC;CACvB;AAED,qBAAa,0BAA2B,SAAQ,SAAS;IACxD,OAAO,CAAC,OAAO,CAAW;IAC1B,OAAO,CAAC,eAAe,CAAW;IAClC,OAAO,CAAC,aAAa,CAAK;IAC1B,OAAO,CAAC,aAAa,CAAY;IACjC,OAAO,CAAC,gBAAgB,CAA2B;IACnD,OAAO,CAAC,gBAAgB,CAAa;IACrC,OAAO,CAAC,SAAS,CAAO;IACxB,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,SAAS,CAA6B;IAC9C,OAAO,CAAC,qBAAqB,CAA2B;IACxD,OAAO,CAAC,WAAW,CAAoB;IAEvC,YACC,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,MAAM,EAAE,EACjB,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,EAClC,QAAQ,EAAE,MAAM,IAAI,EACpB,IAAI,CAAC,EAAE,wBAAwB,EAkD/B;IAED,OAAO,CAAC,UAAU;IAWlB,OAAO,CAAC,aAAa;IAOrB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAmBjC;IAED,OAAO,IAAI,IAAI,CAEd;CACD","sourcesContent":["/**\n * Generic selector component for extensions.\n * Displays a list of string options with keyboard navigation.\n */\n\nimport { Container, fuzzyFilter, getKeybindings, Input, Spacer, Text, type TUI } from \"@earendil-works/pi-tui\";\nimport { theme } from \"../theme/theme.ts\";\nimport { CountdownTimer } from \"./countdown-timer.ts\";\nimport { DynamicBorder } from \"./dynamic-border.ts\";\nimport { keyHint, rawKeyHint } from \"./keybinding-hints.ts\";\n\nexport interface ExtensionSelectorOptions {\n\ttui?: TUI;\n\ttimeout?: number;\n\tonToggleToolsExpanded?: () => void;\n\tenableSearch?: boolean;\n}\n\nexport class ExtensionSelectorComponent extends Container {\n\tprivate options: string[];\n\tprivate filteredOptions: string[];\n\tprivate selectedIndex = 0;\n\tprivate listContainer: Container;\n\tprivate onSelectCallback: (option: string) => void;\n\tprivate onCancelCallback: () => void;\n\tprivate titleText: Text;\n\tprivate baseTitle: string;\n\tprivate countdown: CountdownTimer | undefined;\n\tprivate onToggleToolsExpanded: (() => void) | undefined;\n\tprivate searchInput: Input | undefined;\n\n\tconstructor(\n\t\ttitle: string,\n\t\toptions: string[],\n\t\tonSelect: (option: string) => void,\n\t\tonCancel: () => void,\n\t\topts?: ExtensionSelectorOptions,\n\t) {\n\t\tsuper();\n\n\t\tthis.options = options;\n\t\tthis.filteredOptions = options;\n\t\tthis.onSelectCallback = onSelect;\n\t\tthis.onCancelCallback = onCancel;\n\t\tthis.onToggleToolsExpanded = opts?.onToggleToolsExpanded;\n\t\tthis.baseTitle = title;\n\n\t\tthis.addChild(new DynamicBorder());\n\t\tthis.addChild(new Spacer(1));\n\n\t\tthis.titleText = new Text(theme.fg(\"accent\", theme.bold(title)), 1, 0);\n\t\tthis.addChild(this.titleText);\n\t\tthis.addChild(new Spacer(1));\n\t\tif (opts?.enableSearch) {\n\t\t\tthis.searchInput = new Input();\n\t\t\tthis.addChild(this.searchInput);\n\t\t\tthis.addChild(new Spacer(1));\n\t\t}\n\n\t\tif (opts?.timeout && opts.timeout > 0 && opts.tui) {\n\t\t\tthis.countdown = new CountdownTimer(\n\t\t\t\topts.timeout,\n\t\t\t\topts.tui,\n\t\t\t\t(s) => this.titleText.setText(theme.fg(\"accent\", theme.bold(`${this.baseTitle} (${s}s)`))),\n\t\t\t\t() => this.onCancelCallback(),\n\t\t\t);\n\t\t}\n\n\t\tthis.listContainer = new Container();\n\t\tthis.addChild(this.listContainer);\n\t\tthis.addChild(new Spacer(1));\n\t\tthis.addChild(\n\t\t\tnew Text(\n\t\t\t\trawKeyHint(\"↑↓\", \"navigate\") +\n\t\t\t\t\t\" \" +\n\t\t\t\t\tkeyHint(\"tui.select.confirm\", \"select\") +\n\t\t\t\t\t\" \" +\n\t\t\t\t\tkeyHint(\"tui.select.cancel\", \"cancel\"),\n\t\t\t\t1,\n\t\t\t\t0,\n\t\t\t),\n\t\t);\n\t\tthis.addChild(new Spacer(1));\n\t\tthis.addChild(new DynamicBorder());\n\n\t\tthis.updateList();\n\t}\n\n\tprivate updateList(): void {\n\t\tthis.listContainer.clear();\n\t\tfor (let i = 0; i < this.filteredOptions.length; i++) {\n\t\t\tconst isSelected = i === this.selectedIndex;\n\t\t\tconst text = isSelected\n\t\t\t\t? theme.fg(\"accent\", \"→ \") + theme.fg(\"accent\", this.filteredOptions[i])\n\t\t\t\t: ` ${theme.fg(\"text\", this.filteredOptions[i])}`;\n\t\t\tthis.listContainer.addChild(new Text(text, 1, 0));\n\t\t}\n\t}\n\n\tprivate filterOptions(): void {\n\t\tconst query = this.searchInput?.getValue() ?? \"\";\n\t\tthis.filteredOptions = query ? fuzzyFilter(this.options, query, (option) => option) : this.options;\n\t\tthis.selectedIndex = 0;\n\t\tthis.updateList();\n\t}\n\n\thandleInput(keyData: string): void {\n\t\tconst kb = getKeybindings();\n\t\tif (kb.matches(keyData, \"app.tools.expand\")) {\n\t\t\tthis.onToggleToolsExpanded?.();\n\t\t} else if (kb.matches(keyData, \"tui.select.up\") || keyData === \"k\") {\n\t\t\tthis.selectedIndex = Math.max(0, this.selectedIndex - 1);\n\t\t\tthis.updateList();\n\t\t} else if (kb.matches(keyData, \"tui.select.down\") || keyData === \"j\") {\n\t\t\tthis.selectedIndex = Math.min(this.filteredOptions.length - 1, this.selectedIndex + 1);\n\t\t\tthis.updateList();\n\t\t} else if (kb.matches(keyData, \"tui.select.confirm\") || keyData === \"\\n\") {\n\t\t\tconst selected = this.filteredOptions[this.selectedIndex];\n\t\t\tif (selected) this.onSelectCallback(selected);\n\t\t} else if (kb.matches(keyData, \"tui.select.cancel\")) {\n\t\t\tthis.onCancelCallback();\n\t\t} else if (this.searchInput) {\n\t\t\tthis.searchInput.handleInput(keyData);\n\t\t\tthis.filterOptions();\n\t\t}\n\t}\n\n\tdispose(): void {\n\t\tthis.countdown?.dispose();\n\t}\n}\n"]}
@@ -2,13 +2,14 @@
2
2
  * Generic selector component for extensions.
3
3
  * Displays a list of string options with keyboard navigation.
4
4
  */
5
- import { Container, getKeybindings, Spacer, Text } from "@earendil-works/pi-tui";
5
+ import { Container, fuzzyFilter, getKeybindings, Input, Spacer, Text } from "@earendil-works/pi-tui";
6
6
  import { theme } from "../theme/theme.js";
7
7
  import { CountdownTimer } from "./countdown-timer.js";
8
8
  import { DynamicBorder } from "./dynamic-border.js";
9
9
  import { keyHint, rawKeyHint } from "./keybinding-hints.js";
10
10
  export class ExtensionSelectorComponent extends Container {
11
11
  options;
12
+ filteredOptions;
12
13
  selectedIndex = 0;
13
14
  listContainer;
14
15
  onSelectCallback;
@@ -17,9 +18,11 @@ export class ExtensionSelectorComponent extends Container {
17
18
  baseTitle;
18
19
  countdown;
19
20
  onToggleToolsExpanded;
21
+ searchInput;
20
22
  constructor(title, options, onSelect, onCancel, opts) {
21
23
  super();
22
24
  this.options = options;
25
+ this.filteredOptions = options;
23
26
  this.onSelectCallback = onSelect;
24
27
  this.onCancelCallback = onCancel;
25
28
  this.onToggleToolsExpanded = opts?.onToggleToolsExpanded;
@@ -29,6 +32,11 @@ export class ExtensionSelectorComponent extends Container {
29
32
  this.titleText = new Text(theme.fg("accent", theme.bold(title)), 1, 0);
30
33
  this.addChild(this.titleText);
31
34
  this.addChild(new Spacer(1));
35
+ if (opts?.enableSearch) {
36
+ this.searchInput = new Input();
37
+ this.addChild(this.searchInput);
38
+ this.addChild(new Spacer(1));
39
+ }
32
40
  if (opts?.timeout && opts.timeout > 0 && opts.tui) {
33
41
  this.countdown = new CountdownTimer(opts.timeout, opts.tui, (s) => this.titleText.setText(theme.fg("accent", theme.bold(`${this.baseTitle} (${s}s)`))), () => this.onCancelCallback());
34
42
  }
@@ -46,14 +54,20 @@ export class ExtensionSelectorComponent extends Container {
46
54
  }
47
55
  updateList() {
48
56
  this.listContainer.clear();
49
- for (let i = 0; i < this.options.length; i++) {
57
+ for (let i = 0; i < this.filteredOptions.length; i++) {
50
58
  const isSelected = i === this.selectedIndex;
51
59
  const text = isSelected
52
- ? theme.fg("accent", "→ ") + theme.fg("accent", this.options[i])
53
- : ` ${theme.fg("text", this.options[i])}`;
60
+ ? theme.fg("accent", "→ ") + theme.fg("accent", this.filteredOptions[i])
61
+ : ` ${theme.fg("text", this.filteredOptions[i])}`;
54
62
  this.listContainer.addChild(new Text(text, 1, 0));
55
63
  }
56
64
  }
65
+ filterOptions() {
66
+ const query = this.searchInput?.getValue() ?? "";
67
+ this.filteredOptions = query ? fuzzyFilter(this.options, query, (option) => option) : this.options;
68
+ this.selectedIndex = 0;
69
+ this.updateList();
70
+ }
57
71
  handleInput(keyData) {
58
72
  const kb = getKeybindings();
59
73
  if (kb.matches(keyData, "app.tools.expand")) {
@@ -64,17 +78,21 @@ export class ExtensionSelectorComponent extends Container {
64
78
  this.updateList();
65
79
  }
66
80
  else if (kb.matches(keyData, "tui.select.down") || keyData === "j") {
67
- this.selectedIndex = Math.min(this.options.length - 1, this.selectedIndex + 1);
81
+ this.selectedIndex = Math.min(this.filteredOptions.length - 1, this.selectedIndex + 1);
68
82
  this.updateList();
69
83
  }
70
84
  else if (kb.matches(keyData, "tui.select.confirm") || keyData === "\n") {
71
- const selected = this.options[this.selectedIndex];
85
+ const selected = this.filteredOptions[this.selectedIndex];
72
86
  if (selected)
73
87
  this.onSelectCallback(selected);
74
88
  }
75
89
  else if (kb.matches(keyData, "tui.select.cancel")) {
76
90
  this.onCancelCallback();
77
91
  }
92
+ else if (this.searchInput) {
93
+ this.searchInput.handleInput(keyData);
94
+ this.filterOptions();
95
+ }
78
96
  }
79
97
  dispose() {
80
98
  this.countdown?.dispose();
@@ -1 +1 @@
1
- {"version":3,"file":"extension-selector.js","sourceRoot":"","sources":["../../../../src/modes/interactive/components/extension-selector.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Generic selector component for extensions.\n * Displays a list of string options with keyboard navigation.\n */\n\nimport { Container, getKeybindings, Spacer, Text, type TUI } from \"@earendil-works/pi-tui\";\nimport { theme } from \"../theme/theme.ts\";\nimport { CountdownTimer } from \"./countdown-timer.ts\";\nimport { DynamicBorder } from \"./dynamic-border.ts\";\nimport { keyHint, rawKeyHint } from \"./keybinding-hints.ts\";\n\nexport interface ExtensionSelectorOptions {\n\ttui?: TUI;\n\ttimeout?: number;\n\tonToggleToolsExpanded?: () => void;\n}\n\nexport class ExtensionSelectorComponent extends Container {\n\tprivate options: string[];\n\tprivate selectedIndex = 0;\n\tprivate listContainer: Container;\n\tprivate onSelectCallback: (option: string) => void;\n\tprivate onCancelCallback: () => void;\n\tprivate titleText: Text;\n\tprivate baseTitle: string;\n\tprivate countdown: CountdownTimer | undefined;\n\tprivate onToggleToolsExpanded: (() => void) | undefined;\n\n\tconstructor(\n\t\ttitle: string,\n\t\toptions: string[],\n\t\tonSelect: (option: string) => void,\n\t\tonCancel: () => void,\n\t\topts?: ExtensionSelectorOptions,\n\t) {\n\t\tsuper();\n\n\t\tthis.options = options;\n\t\tthis.onSelectCallback = onSelect;\n\t\tthis.onCancelCallback = onCancel;\n\t\tthis.onToggleToolsExpanded = opts?.onToggleToolsExpanded;\n\t\tthis.baseTitle = title;\n\n\t\tthis.addChild(new DynamicBorder());\n\t\tthis.addChild(new Spacer(1));\n\n\t\tthis.titleText = new Text(theme.fg(\"accent\", theme.bold(title)), 1, 0);\n\t\tthis.addChild(this.titleText);\n\t\tthis.addChild(new Spacer(1));\n\n\t\tif (opts?.timeout && opts.timeout > 0 && opts.tui) {\n\t\t\tthis.countdown = new CountdownTimer(\n\t\t\t\topts.timeout,\n\t\t\t\topts.tui,\n\t\t\t\t(s) => this.titleText.setText(theme.fg(\"accent\", theme.bold(`${this.baseTitle} (${s}s)`))),\n\t\t\t\t() => this.onCancelCallback(),\n\t\t\t);\n\t\t}\n\n\t\tthis.listContainer = new Container();\n\t\tthis.addChild(this.listContainer);\n\t\tthis.addChild(new Spacer(1));\n\t\tthis.addChild(\n\t\t\tnew Text(\n\t\t\t\trawKeyHint(\"↑↓\", \"navigate\") +\n\t\t\t\t\t\" \" +\n\t\t\t\t\tkeyHint(\"tui.select.confirm\", \"select\") +\n\t\t\t\t\t\" \" +\n\t\t\t\t\tkeyHint(\"tui.select.cancel\", \"cancel\"),\n\t\t\t\t1,\n\t\t\t\t0,\n\t\t\t),\n\t\t);\n\t\tthis.addChild(new Spacer(1));\n\t\tthis.addChild(new DynamicBorder());\n\n\t\tthis.updateList();\n\t}\n\n\tprivate updateList(): void {\n\t\tthis.listContainer.clear();\n\t\tfor (let i = 0; i < this.options.length; i++) {\n\t\t\tconst isSelected = i === this.selectedIndex;\n\t\t\tconst text = isSelected\n\t\t\t\t? theme.fg(\"accent\", \"→ \") + theme.fg(\"accent\", this.options[i])\n\t\t\t\t: ` ${theme.fg(\"text\", this.options[i])}`;\n\t\t\tthis.listContainer.addChild(new Text(text, 1, 0));\n\t\t}\n\t}\n\n\thandleInput(keyData: string): void {\n\t\tconst kb = getKeybindings();\n\t\tif (kb.matches(keyData, \"app.tools.expand\")) {\n\t\t\tthis.onToggleToolsExpanded?.();\n\t\t} else if (kb.matches(keyData, \"tui.select.up\") || keyData === \"k\") {\n\t\t\tthis.selectedIndex = Math.max(0, this.selectedIndex - 1);\n\t\t\tthis.updateList();\n\t\t} else if (kb.matches(keyData, \"tui.select.down\") || keyData === \"j\") {\n\t\t\tthis.selectedIndex = Math.min(this.options.length - 1, this.selectedIndex + 1);\n\t\t\tthis.updateList();\n\t\t} else if (kb.matches(keyData, \"tui.select.confirm\") || keyData === \"\\n\") {\n\t\t\tconst selected = this.options[this.selectedIndex];\n\t\t\tif (selected) this.onSelectCallback(selected);\n\t\t} else if (kb.matches(keyData, \"tui.select.cancel\")) {\n\t\t\tthis.onCancelCallback();\n\t\t}\n\t}\n\n\tdispose(): void {\n\t\tthis.countdown?.dispose();\n\t}\n}\n"]}
1
+ {"version":3,"file":"extension-selector.js","sourceRoot":"","sources":["../../../../src/modes/interactive/components/extension-selector.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Generic selector component for extensions.\n * Displays a list of string options with keyboard navigation.\n */\n\nimport { Container, fuzzyFilter, getKeybindings, Input, Spacer, Text, type TUI } from \"@earendil-works/pi-tui\";\nimport { theme } from \"../theme/theme.ts\";\nimport { CountdownTimer } from \"./countdown-timer.ts\";\nimport { DynamicBorder } from \"./dynamic-border.ts\";\nimport { keyHint, rawKeyHint } from \"./keybinding-hints.ts\";\n\nexport interface ExtensionSelectorOptions {\n\ttui?: TUI;\n\ttimeout?: number;\n\tonToggleToolsExpanded?: () => void;\n\tenableSearch?: boolean;\n}\n\nexport class ExtensionSelectorComponent extends Container {\n\tprivate options: string[];\n\tprivate filteredOptions: string[];\n\tprivate selectedIndex = 0;\n\tprivate listContainer: Container;\n\tprivate onSelectCallback: (option: string) => void;\n\tprivate onCancelCallback: () => void;\n\tprivate titleText: Text;\n\tprivate baseTitle: string;\n\tprivate countdown: CountdownTimer | undefined;\n\tprivate onToggleToolsExpanded: (() => void) | undefined;\n\tprivate searchInput: Input | undefined;\n\n\tconstructor(\n\t\ttitle: string,\n\t\toptions: string[],\n\t\tonSelect: (option: string) => void,\n\t\tonCancel: () => void,\n\t\topts?: ExtensionSelectorOptions,\n\t) {\n\t\tsuper();\n\n\t\tthis.options = options;\n\t\tthis.filteredOptions = options;\n\t\tthis.onSelectCallback = onSelect;\n\t\tthis.onCancelCallback = onCancel;\n\t\tthis.onToggleToolsExpanded = opts?.onToggleToolsExpanded;\n\t\tthis.baseTitle = title;\n\n\t\tthis.addChild(new DynamicBorder());\n\t\tthis.addChild(new Spacer(1));\n\n\t\tthis.titleText = new Text(theme.fg(\"accent\", theme.bold(title)), 1, 0);\n\t\tthis.addChild(this.titleText);\n\t\tthis.addChild(new Spacer(1));\n\t\tif (opts?.enableSearch) {\n\t\t\tthis.searchInput = new Input();\n\t\t\tthis.addChild(this.searchInput);\n\t\t\tthis.addChild(new Spacer(1));\n\t\t}\n\n\t\tif (opts?.timeout && opts.timeout > 0 && opts.tui) {\n\t\t\tthis.countdown = new CountdownTimer(\n\t\t\t\topts.timeout,\n\t\t\t\topts.tui,\n\t\t\t\t(s) => this.titleText.setText(theme.fg(\"accent\", theme.bold(`${this.baseTitle} (${s}s)`))),\n\t\t\t\t() => this.onCancelCallback(),\n\t\t\t);\n\t\t}\n\n\t\tthis.listContainer = new Container();\n\t\tthis.addChild(this.listContainer);\n\t\tthis.addChild(new Spacer(1));\n\t\tthis.addChild(\n\t\t\tnew Text(\n\t\t\t\trawKeyHint(\"↑↓\", \"navigate\") +\n\t\t\t\t\t\" \" +\n\t\t\t\t\tkeyHint(\"tui.select.confirm\", \"select\") +\n\t\t\t\t\t\" \" +\n\t\t\t\t\tkeyHint(\"tui.select.cancel\", \"cancel\"),\n\t\t\t\t1,\n\t\t\t\t0,\n\t\t\t),\n\t\t);\n\t\tthis.addChild(new Spacer(1));\n\t\tthis.addChild(new DynamicBorder());\n\n\t\tthis.updateList();\n\t}\n\n\tprivate updateList(): void {\n\t\tthis.listContainer.clear();\n\t\tfor (let i = 0; i < this.filteredOptions.length; i++) {\n\t\t\tconst isSelected = i === this.selectedIndex;\n\t\t\tconst text = isSelected\n\t\t\t\t? theme.fg(\"accent\", \"→ \") + theme.fg(\"accent\", this.filteredOptions[i])\n\t\t\t\t: ` ${theme.fg(\"text\", this.filteredOptions[i])}`;\n\t\t\tthis.listContainer.addChild(new Text(text, 1, 0));\n\t\t}\n\t}\n\n\tprivate filterOptions(): void {\n\t\tconst query = this.searchInput?.getValue() ?? \"\";\n\t\tthis.filteredOptions = query ? fuzzyFilter(this.options, query, (option) => option) : this.options;\n\t\tthis.selectedIndex = 0;\n\t\tthis.updateList();\n\t}\n\n\thandleInput(keyData: string): void {\n\t\tconst kb = getKeybindings();\n\t\tif (kb.matches(keyData, \"app.tools.expand\")) {\n\t\t\tthis.onToggleToolsExpanded?.();\n\t\t} else if (kb.matches(keyData, \"tui.select.up\") || keyData === \"k\") {\n\t\t\tthis.selectedIndex = Math.max(0, this.selectedIndex - 1);\n\t\t\tthis.updateList();\n\t\t} else if (kb.matches(keyData, \"tui.select.down\") || keyData === \"j\") {\n\t\t\tthis.selectedIndex = Math.min(this.filteredOptions.length - 1, this.selectedIndex + 1);\n\t\t\tthis.updateList();\n\t\t} else if (kb.matches(keyData, \"tui.select.confirm\") || keyData === \"\\n\") {\n\t\t\tconst selected = this.filteredOptions[this.selectedIndex];\n\t\t\tif (selected) this.onSelectCallback(selected);\n\t\t} else if (kb.matches(keyData, \"tui.select.cancel\")) {\n\t\t\tthis.onCancelCallback();\n\t\t} else if (this.searchInput) {\n\t\t\tthis.searchInput.handleInput(keyData);\n\t\t\tthis.filterOptions();\n\t\t}\n\t}\n\n\tdispose(): void {\n\t\tthis.countdown?.dispose();\n\t}\n}\n"]}
@@ -2,7 +2,6 @@ import { Container } from "@earendil-works/pi-tui";
2
2
  import { type TerminalTheme } from "../theme/theme.ts";
3
3
  export interface FirstTimeSetupResult {
4
4
  theme: TerminalTheme;
5
- shareAnalytics: boolean;
6
5
  }
7
6
  export interface FirstTimeSetupOptions {
8
7
  detectedTheme: TerminalTheme;
@@ -10,11 +9,9 @@ export interface FirstTimeSetupOptions {
10
9
  onSubmit: (result: FirstTimeSetupResult) => void;
11
10
  onCancel: () => void;
12
11
  }
13
- /** First-time setup dialog: theme choice and analytics opt-in. */
12
+ /** First-time setup dialog: theme choice. */
14
13
  export declare class FirstTimeSetupComponent extends Container {
15
- private step;
16
14
  private themeIndex;
17
- private analyticsIndex;
18
15
  private readonly options;
19
16
  constructor(options: FirstTimeSetupOptions);
20
17
  private update;
@@ -1 +1 @@
1
- {"version":3,"file":"first-time-setup.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/first-time-setup.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAgC,MAAM,wBAAwB,CAAC;AAEjF,OAAO,EAAE,KAAK,aAAa,EAAS,MAAM,mBAAmB,CAAC;AAI9D,MAAM,WAAW,oBAAoB;IACpC,KAAK,EAAE,aAAa,CAAC;IACrB,cAAc,EAAE,OAAO,CAAC;CACxB;AAED,MAAM,WAAW,qBAAqB;IACrC,aAAa,EAAE,aAAa,CAAC;IAC7B,cAAc,EAAE,CAAC,SAAS,EAAE,aAAa,KAAK,IAAI,CAAC;IACnD,QAAQ,EAAE,CAAC,MAAM,EAAE,oBAAoB,KAAK,IAAI,CAAC;IACjD,QAAQ,EAAE,MAAM,IAAI,CAAC;CACrB;AAcD,kEAAkE;AAClE,qBAAa,uBAAwB,SAAQ,SAAS;IACrD,OAAO,CAAC,IAAI,CAAkC;IAC9C,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,cAAc,CAAK;IAC3B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAwB;IAEhD,YAAY,OAAO,EAAE,qBAAqB,EAQzC;IAGD,OAAO,CAAC,MAAM;IAsDd,OAAO,CAAC,aAAa;IASrB,OAAO,CAAC,aAAa;IAarB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAmBjC;CACD","sourcesContent":["import { Container, getKeybindings, Spacer, Text } from \"@earendil-works/pi-tui\";\nimport { APP_NAME } from \"../../../config.ts\";\nimport { type TerminalTheme, theme } from \"../theme/theme.ts\";\nimport { DynamicBorder } from \"./dynamic-border.ts\";\nimport { keyHint, rawKeyHint } from \"./keybinding-hints.ts\";\n\nexport interface FirstTimeSetupResult {\n\ttheme: TerminalTheme;\n\tshareAnalytics: boolean;\n}\n\nexport interface FirstTimeSetupOptions {\n\tdetectedTheme: TerminalTheme;\n\tonThemePreview: (themeName: TerminalTheme) => void;\n\tonSubmit: (result: FirstTimeSetupResult) => void;\n\tonCancel: () => void;\n}\n\nconst THEME_OPTIONS: Array<{ value: TerminalTheme; label: string }> = [\n\t{ value: \"dark\", label: \"Dark\" },\n\t{ value: \"light\", label: \"Light\" },\n];\n\nconst ANALYTICS_OPTIONS: Array<{ value: boolean; label: string }> = [\n\t{ value: true, label: \"Share anonymous usage data\" },\n\t{ value: false, label: \"Don't share\" },\n];\n\nconst SETUP_LOGO_LINES = [\"██████\", \"██ ██\", \"████ ██\", \"██ ██\"];\n\n/** First-time setup dialog: theme choice and analytics opt-in. */\nexport class FirstTimeSetupComponent extends Container {\n\tprivate step: \"theme\" | \"analytics\" = \"theme\";\n\tprivate themeIndex: number;\n\tprivate analyticsIndex = 0;\n\tprivate readonly options: FirstTimeSetupOptions;\n\n\tconstructor(options: FirstTimeSetupOptions) {\n\t\tsuper();\n\t\tthis.options = options;\n\t\tthis.themeIndex = Math.max(\n\t\t\t0,\n\t\t\tTHEME_OPTIONS.findIndex((option) => option.value === options.detectedTheme),\n\t\t);\n\t\tthis.update();\n\t}\n\n\t// Rebuild the whole dialog on every change so theme previews recolor all text.\n\tprivate update(): void {\n\t\tthis.clear();\n\t\tthis.addChild(new DynamicBorder());\n\t\tthis.addChild(new Spacer(1));\n\t\tthis.addChild(new Text(theme.fg(\"accent\", SETUP_LOGO_LINES.join(\"\\n\")), 1, 0));\n\t\tthis.addChild(new Spacer(1));\n\t\tthis.addChild(\n\t\t\tnew Text(theme.fg(\"accent\", theme.bold(`Welcome to ${APP_NAME}, the minimal coding agent.`)), 1, 0),\n\t\t);\n\t\tthis.addChild(new Spacer(1));\n\n\t\tif (this.step === \"theme\") {\n\t\t\tthis.addChild(new Text(theme.fg(\"text\", \"Pick a theme.\"), 1, 0));\n\t\t\tthis.addChild(new Text(theme.fg(\"muted\", `Detected system appearance: ${this.options.detectedTheme}`), 1, 0));\n\t\t\tthis.addChild(new Spacer(1));\n\t\t\tthis.addOptionList(\n\t\t\t\tTHEME_OPTIONS.map((option) => option.label),\n\t\t\t\tthis.themeIndex,\n\t\t\t);\n\t\t} else {\n\t\t\tthis.addChild(new Text(theme.fg(\"text\", \"Opt-in to anonymous usage data sharing?\"), 1, 0));\n\t\t\tthis.addChild(\n\t\t\t\tnew Text(\n\t\t\t\t\ttheme.fg(\n\t\t\t\t\t\t\"muted\",\n\t\t\t\t\t\t\"Opting in stores a tracking identifier in settings.json and enables anonymous\\nusage analytics. This helps us to better debug, reproduce, and resolve issues\\nand bugs within Pi. You can observe what is shared using /privacy and make\\nchanges anytime in settings.json.\",\n\t\t\t\t\t),\n\t\t\t\t\t1,\n\t\t\t\t\t0,\n\t\t\t\t),\n\t\t\t);\n\t\t\tthis.addChild(new Spacer(1));\n\t\t\tthis.addOptionList(\n\t\t\t\tANALYTICS_OPTIONS.map((option) => option.label),\n\t\t\t\tthis.analyticsIndex,\n\t\t\t);\n\t\t}\n\n\t\tthis.addChild(new Spacer(1));\n\t\tthis.addChild(\n\t\t\tnew Text(\n\t\t\t\trawKeyHint(\"↑↓\", \"navigate\") +\n\t\t\t\t\t\" \" +\n\t\t\t\t\tkeyHint(\"tui.select.confirm\", this.step === \"theme\" ? \"continue\" : \"finish\") +\n\t\t\t\t\t\" \" +\n\t\t\t\t\tkeyHint(\"tui.select.cancel\", \"skip setup\"),\n\t\t\t\t1,\n\t\t\t\t0,\n\t\t\t),\n\t\t);\n\t\tthis.addChild(new Spacer(1));\n\t\tthis.addChild(new DynamicBorder());\n\t}\n\n\tprivate addOptionList(labels: string[], selectedIndex: number): void {\n\t\tfor (let i = 0; i < labels.length; i++) {\n\t\t\tconst isSelected = i === selectedIndex;\n\t\t\tconst prefix = isSelected ? theme.fg(\"accent\", \"→ \") : \" \";\n\t\t\tconst label = isSelected ? theme.fg(\"accent\", labels[i]) : theme.fg(\"text\", labels[i]);\n\t\t\tthis.addChild(new Text(`${prefix}${label}`, 1, 0));\n\t\t}\n\t}\n\n\tprivate moveSelection(delta: number): void {\n\t\tif (this.step === \"theme\") {\n\t\t\tconst next = Math.max(0, Math.min(THEME_OPTIONS.length - 1, this.themeIndex + delta));\n\t\t\tif (next !== this.themeIndex) {\n\t\t\t\tthis.themeIndex = next;\n\t\t\t\tthis.options.onThemePreview(THEME_OPTIONS[this.themeIndex].value);\n\t\t\t}\n\t\t} else {\n\t\t\tthis.analyticsIndex = Math.max(0, Math.min(ANALYTICS_OPTIONS.length - 1, this.analyticsIndex + delta));\n\t\t}\n\t\tthis.update();\n\t}\n\n\thandleInput(keyData: string): void {\n\t\tconst kb = getKeybindings();\n\t\tif (kb.matches(keyData, \"tui.select.up\") || keyData === \"k\") {\n\t\t\tthis.moveSelection(-1);\n\t\t} else if (kb.matches(keyData, \"tui.select.down\") || keyData === \"j\") {\n\t\t\tthis.moveSelection(1);\n\t\t} else if (kb.matches(keyData, \"tui.select.confirm\") || keyData === \"\\n\") {\n\t\t\tif (this.step === \"theme\") {\n\t\t\t\tthis.step = \"analytics\";\n\t\t\t\tthis.update();\n\t\t\t} else {\n\t\t\t\tthis.options.onSubmit({\n\t\t\t\t\ttheme: THEME_OPTIONS[this.themeIndex].value,\n\t\t\t\t\tshareAnalytics: ANALYTICS_OPTIONS[this.analyticsIndex].value,\n\t\t\t\t});\n\t\t\t}\n\t\t} else if (kb.matches(keyData, \"tui.select.cancel\")) {\n\t\t\tthis.options.onCancel();\n\t\t}\n\t}\n}\n"]}
1
+ {"version":3,"file":"first-time-setup.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/first-time-setup.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAgC,MAAM,wBAAwB,CAAC;AAGjF,OAAO,EAAE,KAAK,aAAa,EAAS,MAAM,mBAAmB,CAAC;AAK9D,MAAM,WAAW,oBAAoB;IACpC,KAAK,EAAE,aAAa,CAAC;CACrB;AAED,MAAM,WAAW,qBAAqB;IACrC,aAAa,EAAE,aAAa,CAAC;IAC7B,cAAc,EAAE,CAAC,SAAS,EAAE,aAAa,KAAK,IAAI,CAAC;IACnD,QAAQ,EAAE,CAAC,MAAM,EAAE,oBAAoB,KAAK,IAAI,CAAC;IACjD,QAAQ,EAAE,MAAM,IAAI,CAAC;CACrB;AAOD,6CAA6C;AAC7C,qBAAa,uBAAwB,SAAQ,SAAS;IACrD,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAwB;IAEhD,YAAY,OAAO,EAAE,qBAAqB,EAQzC;IAGD,OAAO,CAAC,MAAM;IAsCd,OAAO,CAAC,aAAa;IASrB,OAAO,CAAC,aAAa;IASrB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAWjC;CACD","sourcesContent":["import { Container, getKeybindings, Spacer, Text } from \"@earendil-works/pi-tui\";\nimport { APP_NAME } from \"../../../config.ts\";\nimport { APEX_MARK_BLOCK } from \"../../../themes/apex-logo.ts\";\nimport { type TerminalTheme, theme } from \"../theme/theme.ts\";\nimport { DynamicBorder } from \"./dynamic-border.ts\";\nimport { keyHint, rawKeyHint } from \"./keybinding-hints.ts\";\nimport { paintBrandMark } from \"./splash-header.ts\";\n\nexport interface FirstTimeSetupResult {\n\ttheme: TerminalTheme;\n}\n\nexport interface FirstTimeSetupOptions {\n\tdetectedTheme: TerminalTheme;\n\tonThemePreview: (themeName: TerminalTheme) => void;\n\tonSubmit: (result: FirstTimeSetupResult) => void;\n\tonCancel: () => void;\n}\n\nconst THEME_OPTIONS: Array<{ value: TerminalTheme; label: string }> = [\n\t{ value: \"dark\", label: \"Dark\" },\n\t{ value: \"light\", label: \"Light\" },\n];\n\n/** First-time setup dialog: theme choice. */\nexport class FirstTimeSetupComponent extends Container {\n\tprivate themeIndex: number;\n\tprivate readonly options: FirstTimeSetupOptions;\n\n\tconstructor(options: FirstTimeSetupOptions) {\n\t\tsuper();\n\t\tthis.options = options;\n\t\tthis.themeIndex = Math.max(\n\t\t\t0,\n\t\t\tTHEME_OPTIONS.findIndex((option) => option.value === options.detectedTheme),\n\t\t);\n\t\tthis.update();\n\t}\n\n\t// Rebuild the whole dialog on every change so theme previews recolor all text.\n\tprivate update(): void {\n\t\tthis.clear();\n\t\tthis.addChild(new DynamicBorder());\n\t\tthis.addChild(new Spacer(1));\n\t\t// The same mark the splash header stamps, so a new user's first two screens\n\t\t// agree. Painted here rather than at module load: theme.fg throws until\n\t\t// initTheme has run, and this module is imported from the CLI entry point.\n\t\tthis.addChild(new Text(paintBrandMark(APEX_MARK_BLOCK).join(\"\\n\"), 1, 0));\n\t\tthis.addChild(new Spacer(1));\n\t\tthis.addChild(\n\t\t\tnew Text(theme.fg(\"accent\", theme.bold(`Welcome to ${APP_NAME}, the minimal coding agent.`)), 1, 0),\n\t\t);\n\t\tthis.addChild(new Spacer(1));\n\n\t\tthis.addChild(new Text(theme.fg(\"text\", \"Pick a theme.\"), 1, 0));\n\t\tthis.addChild(new Text(theme.fg(\"muted\", `Detected system appearance: ${this.options.detectedTheme}`), 1, 0));\n\t\tthis.addChild(new Spacer(1));\n\t\tthis.addOptionList(\n\t\t\tTHEME_OPTIONS.map((option) => option.label),\n\t\t\tthis.themeIndex,\n\t\t);\n\n\t\tthis.addChild(new Spacer(1));\n\t\tthis.addChild(\n\t\t\tnew Text(\n\t\t\t\trawKeyHint(\"↑↓\", \"navigate\") +\n\t\t\t\t\t\" \" +\n\t\t\t\t\tkeyHint(\"tui.select.confirm\", \"finish\") +\n\t\t\t\t\t\" \" +\n\t\t\t\t\tkeyHint(\"tui.select.cancel\", \"skip setup\"),\n\t\t\t\t1,\n\t\t\t\t0,\n\t\t\t),\n\t\t);\n\t\tthis.addChild(new Spacer(1));\n\t\tthis.addChild(new DynamicBorder());\n\t}\n\n\tprivate addOptionList(labels: string[], selectedIndex: number): void {\n\t\tfor (let i = 0; i < labels.length; i++) {\n\t\t\tconst isSelected = i === selectedIndex;\n\t\t\tconst prefix = isSelected ? theme.fg(\"accent\", \"→ \") : \" \";\n\t\t\tconst label = isSelected ? theme.fg(\"accent\", labels[i]) : theme.fg(\"text\", labels[i]);\n\t\t\tthis.addChild(new Text(`${prefix}${label}`, 1, 0));\n\t\t}\n\t}\n\n\tprivate moveSelection(delta: number): void {\n\t\tconst next = Math.max(0, Math.min(THEME_OPTIONS.length - 1, this.themeIndex + delta));\n\t\tif (next !== this.themeIndex) {\n\t\t\tthis.themeIndex = next;\n\t\t\tthis.options.onThemePreview(THEME_OPTIONS[this.themeIndex].value);\n\t\t}\n\t\tthis.update();\n\t}\n\n\thandleInput(keyData: string): void {\n\t\tconst kb = getKeybindings();\n\t\tif (kb.matches(keyData, \"tui.select.up\") || keyData === \"k\") {\n\t\t\tthis.moveSelection(-1);\n\t\t} else if (kb.matches(keyData, \"tui.select.down\") || keyData === \"j\") {\n\t\t\tthis.moveSelection(1);\n\t\t} else if (kb.matches(keyData, \"tui.select.confirm\") || keyData === \"\\n\") {\n\t\t\tthis.options.onSubmit({ theme: THEME_OPTIONS[this.themeIndex].value });\n\t\t} else if (kb.matches(keyData, \"tui.select.cancel\")) {\n\t\t\tthis.options.onCancel();\n\t\t}\n\t}\n}\n"]}
@@ -1,22 +1,17 @@
1
1
  import { Container, getKeybindings, Spacer, Text } from "@earendil-works/pi-tui";
2
2
  import { APP_NAME } from "../../../config.js";
3
+ import { APEX_MARK_BLOCK } from "../../../themes/apex-logo.js";
3
4
  import { theme } from "../theme/theme.js";
4
5
  import { DynamicBorder } from "./dynamic-border.js";
5
6
  import { keyHint, rawKeyHint } from "./keybinding-hints.js";
7
+ import { paintBrandMark } from "./splash-header.js";
6
8
  const THEME_OPTIONS = [
7
9
  { value: "dark", label: "Dark" },
8
10
  { value: "light", label: "Light" },
9
11
  ];
10
- const ANALYTICS_OPTIONS = [
11
- { value: true, label: "Share anonymous usage data" },
12
- { value: false, label: "Don't share" },
13
- ];
14
- const SETUP_LOGO_LINES = ["██████", "██ ██", "████ ██", "██ ██"];
15
- /** First-time setup dialog: theme choice and analytics opt-in. */
12
+ /** First-time setup dialog: theme choice. */
16
13
  export class FirstTimeSetupComponent extends Container {
17
- step = "theme";
18
14
  themeIndex;
19
- analyticsIndex = 0;
20
15
  options;
21
16
  constructor(options) {
22
17
  super();
@@ -29,26 +24,21 @@ export class FirstTimeSetupComponent extends Container {
29
24
  this.clear();
30
25
  this.addChild(new DynamicBorder());
31
26
  this.addChild(new Spacer(1));
32
- this.addChild(new Text(theme.fg("accent", SETUP_LOGO_LINES.join("\n")), 1, 0));
27
+ // The same mark the splash header stamps, so a new user's first two screens
28
+ // agree. Painted here rather than at module load: theme.fg throws until
29
+ // initTheme has run, and this module is imported from the CLI entry point.
30
+ this.addChild(new Text(paintBrandMark(APEX_MARK_BLOCK).join("\n"), 1, 0));
33
31
  this.addChild(new Spacer(1));
34
32
  this.addChild(new Text(theme.fg("accent", theme.bold(`Welcome to ${APP_NAME}, the minimal coding agent.`)), 1, 0));
35
33
  this.addChild(new Spacer(1));
36
- if (this.step === "theme") {
37
- this.addChild(new Text(theme.fg("text", "Pick a theme."), 1, 0));
38
- this.addChild(new Text(theme.fg("muted", `Detected system appearance: ${this.options.detectedTheme}`), 1, 0));
39
- this.addChild(new Spacer(1));
40
- this.addOptionList(THEME_OPTIONS.map((option) => option.label), this.themeIndex);
41
- }
42
- else {
43
- this.addChild(new Text(theme.fg("text", "Opt-in to anonymous usage data sharing?"), 1, 0));
44
- this.addChild(new Text(theme.fg("muted", "Opting in stores a tracking identifier in settings.json and enables anonymous\nusage analytics. This helps us to better debug, reproduce, and resolve issues\nand bugs within Pi. You can observe what is shared using /privacy and make\nchanges anytime in settings.json."), 1, 0));
45
- this.addChild(new Spacer(1));
46
- this.addOptionList(ANALYTICS_OPTIONS.map((option) => option.label), this.analyticsIndex);
47
- }
34
+ this.addChild(new Text(theme.fg("text", "Pick a theme."), 1, 0));
35
+ this.addChild(new Text(theme.fg("muted", `Detected system appearance: ${this.options.detectedTheme}`), 1, 0));
36
+ this.addChild(new Spacer(1));
37
+ this.addOptionList(THEME_OPTIONS.map((option) => option.label), this.themeIndex);
48
38
  this.addChild(new Spacer(1));
49
39
  this.addChild(new Text(rawKeyHint("↑↓", "navigate") +
50
40
  " " +
51
- keyHint("tui.select.confirm", this.step === "theme" ? "continue" : "finish") +
41
+ keyHint("tui.select.confirm", "finish") +
52
42
  " " +
53
43
  keyHint("tui.select.cancel", "skip setup"), 1, 0));
54
44
  this.addChild(new Spacer(1));
@@ -63,15 +53,10 @@ export class FirstTimeSetupComponent extends Container {
63
53
  }
64
54
  }
65
55
  moveSelection(delta) {
66
- if (this.step === "theme") {
67
- const next = Math.max(0, Math.min(THEME_OPTIONS.length - 1, this.themeIndex + delta));
68
- if (next !== this.themeIndex) {
69
- this.themeIndex = next;
70
- this.options.onThemePreview(THEME_OPTIONS[this.themeIndex].value);
71
- }
72
- }
73
- else {
74
- this.analyticsIndex = Math.max(0, Math.min(ANALYTICS_OPTIONS.length - 1, this.analyticsIndex + delta));
56
+ const next = Math.max(0, Math.min(THEME_OPTIONS.length - 1, this.themeIndex + delta));
57
+ if (next !== this.themeIndex) {
58
+ this.themeIndex = next;
59
+ this.options.onThemePreview(THEME_OPTIONS[this.themeIndex].value);
75
60
  }
76
61
  this.update();
77
62
  }
@@ -84,16 +69,7 @@ export class FirstTimeSetupComponent extends Container {
84
69
  this.moveSelection(1);
85
70
  }
86
71
  else if (kb.matches(keyData, "tui.select.confirm") || keyData === "\n") {
87
- if (this.step === "theme") {
88
- this.step = "analytics";
89
- this.update();
90
- }
91
- else {
92
- this.options.onSubmit({
93
- theme: THEME_OPTIONS[this.themeIndex].value,
94
- shareAnalytics: ANALYTICS_OPTIONS[this.analyticsIndex].value,
95
- });
96
- }
72
+ this.options.onSubmit({ theme: THEME_OPTIONS[this.themeIndex].value });
97
73
  }
98
74
  else if (kb.matches(keyData, "tui.select.cancel")) {
99
75
  this.options.onCancel();
@@ -1 +1 @@
1
- {"version":3,"file":"first-time-setup.js","sourceRoot":"","sources":["../../../../src/modes/interactive/components/first-time-setup.ts"],"names":[],"mappings":"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","sourcesContent":["import { Container, getKeybindings, Spacer, Text } from \"@earendil-works/pi-tui\";\nimport { APP_NAME } from \"../../../config.ts\";\nimport { type TerminalTheme, theme } from \"../theme/theme.ts\";\nimport { DynamicBorder } from \"./dynamic-border.ts\";\nimport { keyHint, rawKeyHint } from \"./keybinding-hints.ts\";\n\nexport interface FirstTimeSetupResult {\n\ttheme: TerminalTheme;\n\tshareAnalytics: boolean;\n}\n\nexport interface FirstTimeSetupOptions {\n\tdetectedTheme: TerminalTheme;\n\tonThemePreview: (themeName: TerminalTheme) => void;\n\tonSubmit: (result: FirstTimeSetupResult) => void;\n\tonCancel: () => void;\n}\n\nconst THEME_OPTIONS: Array<{ value: TerminalTheme; label: string }> = [\n\t{ value: \"dark\", label: \"Dark\" },\n\t{ value: \"light\", label: \"Light\" },\n];\n\nconst ANALYTICS_OPTIONS: Array<{ value: boolean; label: string }> = [\n\t{ value: true, label: \"Share anonymous usage data\" },\n\t{ value: false, label: \"Don't share\" },\n];\n\nconst SETUP_LOGO_LINES = [\"██████\", \"██ ██\", \"████ ██\", \"██ ██\"];\n\n/** First-time setup dialog: theme choice and analytics opt-in. */\nexport class FirstTimeSetupComponent extends Container {\n\tprivate step: \"theme\" | \"analytics\" = \"theme\";\n\tprivate themeIndex: number;\n\tprivate analyticsIndex = 0;\n\tprivate readonly options: FirstTimeSetupOptions;\n\n\tconstructor(options: FirstTimeSetupOptions) {\n\t\tsuper();\n\t\tthis.options = options;\n\t\tthis.themeIndex = Math.max(\n\t\t\t0,\n\t\t\tTHEME_OPTIONS.findIndex((option) => option.value === options.detectedTheme),\n\t\t);\n\t\tthis.update();\n\t}\n\n\t// Rebuild the whole dialog on every change so theme previews recolor all text.\n\tprivate update(): void {\n\t\tthis.clear();\n\t\tthis.addChild(new DynamicBorder());\n\t\tthis.addChild(new Spacer(1));\n\t\tthis.addChild(new Text(theme.fg(\"accent\", SETUP_LOGO_LINES.join(\"\\n\")), 1, 0));\n\t\tthis.addChild(new Spacer(1));\n\t\tthis.addChild(\n\t\t\tnew Text(theme.fg(\"accent\", theme.bold(`Welcome to ${APP_NAME}, the minimal coding agent.`)), 1, 0),\n\t\t);\n\t\tthis.addChild(new Spacer(1));\n\n\t\tif (this.step === \"theme\") {\n\t\t\tthis.addChild(new Text(theme.fg(\"text\", \"Pick a theme.\"), 1, 0));\n\t\t\tthis.addChild(new Text(theme.fg(\"muted\", `Detected system appearance: ${this.options.detectedTheme}`), 1, 0));\n\t\t\tthis.addChild(new Spacer(1));\n\t\t\tthis.addOptionList(\n\t\t\t\tTHEME_OPTIONS.map((option) => option.label),\n\t\t\t\tthis.themeIndex,\n\t\t\t);\n\t\t} else {\n\t\t\tthis.addChild(new Text(theme.fg(\"text\", \"Opt-in to anonymous usage data sharing?\"), 1, 0));\n\t\t\tthis.addChild(\n\t\t\t\tnew Text(\n\t\t\t\t\ttheme.fg(\n\t\t\t\t\t\t\"muted\",\n\t\t\t\t\t\t\"Opting in stores a tracking identifier in settings.json and enables anonymous\\nusage analytics. This helps us to better debug, reproduce, and resolve issues\\nand bugs within Pi. You can observe what is shared using /privacy and make\\nchanges anytime in settings.json.\",\n\t\t\t\t\t),\n\t\t\t\t\t1,\n\t\t\t\t\t0,\n\t\t\t\t),\n\t\t\t);\n\t\t\tthis.addChild(new Spacer(1));\n\t\t\tthis.addOptionList(\n\t\t\t\tANALYTICS_OPTIONS.map((option) => option.label),\n\t\t\t\tthis.analyticsIndex,\n\t\t\t);\n\t\t}\n\n\t\tthis.addChild(new Spacer(1));\n\t\tthis.addChild(\n\t\t\tnew Text(\n\t\t\t\trawKeyHint(\"↑↓\", \"navigate\") +\n\t\t\t\t\t\" \" +\n\t\t\t\t\tkeyHint(\"tui.select.confirm\", this.step === \"theme\" ? \"continue\" : \"finish\") +\n\t\t\t\t\t\" \" +\n\t\t\t\t\tkeyHint(\"tui.select.cancel\", \"skip setup\"),\n\t\t\t\t1,\n\t\t\t\t0,\n\t\t\t),\n\t\t);\n\t\tthis.addChild(new Spacer(1));\n\t\tthis.addChild(new DynamicBorder());\n\t}\n\n\tprivate addOptionList(labels: string[], selectedIndex: number): void {\n\t\tfor (let i = 0; i < labels.length; i++) {\n\t\t\tconst isSelected = i === selectedIndex;\n\t\t\tconst prefix = isSelected ? theme.fg(\"accent\", \"→ \") : \" \";\n\t\t\tconst label = isSelected ? theme.fg(\"accent\", labels[i]) : theme.fg(\"text\", labels[i]);\n\t\t\tthis.addChild(new Text(`${prefix}${label}`, 1, 0));\n\t\t}\n\t}\n\n\tprivate moveSelection(delta: number): void {\n\t\tif (this.step === \"theme\") {\n\t\t\tconst next = Math.max(0, Math.min(THEME_OPTIONS.length - 1, this.themeIndex + delta));\n\t\t\tif (next !== this.themeIndex) {\n\t\t\t\tthis.themeIndex = next;\n\t\t\t\tthis.options.onThemePreview(THEME_OPTIONS[this.themeIndex].value);\n\t\t\t}\n\t\t} else {\n\t\t\tthis.analyticsIndex = Math.max(0, Math.min(ANALYTICS_OPTIONS.length - 1, this.analyticsIndex + delta));\n\t\t}\n\t\tthis.update();\n\t}\n\n\thandleInput(keyData: string): void {\n\t\tconst kb = getKeybindings();\n\t\tif (kb.matches(keyData, \"tui.select.up\") || keyData === \"k\") {\n\t\t\tthis.moveSelection(-1);\n\t\t} else if (kb.matches(keyData, \"tui.select.down\") || keyData === \"j\") {\n\t\t\tthis.moveSelection(1);\n\t\t} else if (kb.matches(keyData, \"tui.select.confirm\") || keyData === \"\\n\") {\n\t\t\tif (this.step === \"theme\") {\n\t\t\t\tthis.step = \"analytics\";\n\t\t\t\tthis.update();\n\t\t\t} else {\n\t\t\t\tthis.options.onSubmit({\n\t\t\t\t\ttheme: THEME_OPTIONS[this.themeIndex].value,\n\t\t\t\t\tshareAnalytics: ANALYTICS_OPTIONS[this.analyticsIndex].value,\n\t\t\t\t});\n\t\t\t}\n\t\t} else if (kb.matches(keyData, \"tui.select.cancel\")) {\n\t\t\tthis.options.onCancel();\n\t\t}\n\t}\n}\n"]}
1
+ {"version":3,"file":"first-time-setup.js","sourceRoot":"","sources":["../../../../src/modes/interactive/components/first-time-setup.ts"],"names":[],"mappings":"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","sourcesContent":["import { Container, getKeybindings, Spacer, Text } from \"@earendil-works/pi-tui\";\nimport { APP_NAME } from \"../../../config.ts\";\nimport { APEX_MARK_BLOCK } from \"../../../themes/apex-logo.ts\";\nimport { type TerminalTheme, theme } from \"../theme/theme.ts\";\nimport { DynamicBorder } from \"./dynamic-border.ts\";\nimport { keyHint, rawKeyHint } from \"./keybinding-hints.ts\";\nimport { paintBrandMark } from \"./splash-header.ts\";\n\nexport interface FirstTimeSetupResult {\n\ttheme: TerminalTheme;\n}\n\nexport interface FirstTimeSetupOptions {\n\tdetectedTheme: TerminalTheme;\n\tonThemePreview: (themeName: TerminalTheme) => void;\n\tonSubmit: (result: FirstTimeSetupResult) => void;\n\tonCancel: () => void;\n}\n\nconst THEME_OPTIONS: Array<{ value: TerminalTheme; label: string }> = [\n\t{ value: \"dark\", label: \"Dark\" },\n\t{ value: \"light\", label: \"Light\" },\n];\n\n/** First-time setup dialog: theme choice. */\nexport class FirstTimeSetupComponent extends Container {\n\tprivate themeIndex: number;\n\tprivate readonly options: FirstTimeSetupOptions;\n\n\tconstructor(options: FirstTimeSetupOptions) {\n\t\tsuper();\n\t\tthis.options = options;\n\t\tthis.themeIndex = Math.max(\n\t\t\t0,\n\t\t\tTHEME_OPTIONS.findIndex((option) => option.value === options.detectedTheme),\n\t\t);\n\t\tthis.update();\n\t}\n\n\t// Rebuild the whole dialog on every change so theme previews recolor all text.\n\tprivate update(): void {\n\t\tthis.clear();\n\t\tthis.addChild(new DynamicBorder());\n\t\tthis.addChild(new Spacer(1));\n\t\t// The same mark the splash header stamps, so a new user's first two screens\n\t\t// agree. Painted here rather than at module load: theme.fg throws until\n\t\t// initTheme has run, and this module is imported from the CLI entry point.\n\t\tthis.addChild(new Text(paintBrandMark(APEX_MARK_BLOCK).join(\"\\n\"), 1, 0));\n\t\tthis.addChild(new Spacer(1));\n\t\tthis.addChild(\n\t\t\tnew Text(theme.fg(\"accent\", theme.bold(`Welcome to ${APP_NAME}, the minimal coding agent.`)), 1, 0),\n\t\t);\n\t\tthis.addChild(new Spacer(1));\n\n\t\tthis.addChild(new Text(theme.fg(\"text\", \"Pick a theme.\"), 1, 0));\n\t\tthis.addChild(new Text(theme.fg(\"muted\", `Detected system appearance: ${this.options.detectedTheme}`), 1, 0));\n\t\tthis.addChild(new Spacer(1));\n\t\tthis.addOptionList(\n\t\t\tTHEME_OPTIONS.map((option) => option.label),\n\t\t\tthis.themeIndex,\n\t\t);\n\n\t\tthis.addChild(new Spacer(1));\n\t\tthis.addChild(\n\t\t\tnew Text(\n\t\t\t\trawKeyHint(\"↑↓\", \"navigate\") +\n\t\t\t\t\t\" \" +\n\t\t\t\t\tkeyHint(\"tui.select.confirm\", \"finish\") +\n\t\t\t\t\t\" \" +\n\t\t\t\t\tkeyHint(\"tui.select.cancel\", \"skip setup\"),\n\t\t\t\t1,\n\t\t\t\t0,\n\t\t\t),\n\t\t);\n\t\tthis.addChild(new Spacer(1));\n\t\tthis.addChild(new DynamicBorder());\n\t}\n\n\tprivate addOptionList(labels: string[], selectedIndex: number): void {\n\t\tfor (let i = 0; i < labels.length; i++) {\n\t\t\tconst isSelected = i === selectedIndex;\n\t\t\tconst prefix = isSelected ? theme.fg(\"accent\", \"→ \") : \" \";\n\t\t\tconst label = isSelected ? theme.fg(\"accent\", labels[i]) : theme.fg(\"text\", labels[i]);\n\t\t\tthis.addChild(new Text(`${prefix}${label}`, 1, 0));\n\t\t}\n\t}\n\n\tprivate moveSelection(delta: number): void {\n\t\tconst next = Math.max(0, Math.min(THEME_OPTIONS.length - 1, this.themeIndex + delta));\n\t\tif (next !== this.themeIndex) {\n\t\t\tthis.themeIndex = next;\n\t\t\tthis.options.onThemePreview(THEME_OPTIONS[this.themeIndex].value);\n\t\t}\n\t\tthis.update();\n\t}\n\n\thandleInput(keyData: string): void {\n\t\tconst kb = getKeybindings();\n\t\tif (kb.matches(keyData, \"tui.select.up\") || keyData === \"k\") {\n\t\t\tthis.moveSelection(-1);\n\t\t} else if (kb.matches(keyData, \"tui.select.down\") || keyData === \"j\") {\n\t\t\tthis.moveSelection(1);\n\t\t} else if (kb.matches(keyData, \"tui.select.confirm\") || keyData === \"\\n\") {\n\t\t\tthis.options.onSubmit({ theme: THEME_OPTIONS[this.themeIndex].value });\n\t\t} else if (kb.matches(keyData, \"tui.select.cancel\")) {\n\t\t\tthis.options.onCancel();\n\t\t}\n\t}\n}\n"]}
@@ -0,0 +1,8 @@
1
+ export type FirstUseHintId = "queue" | "tool-expand" | "thinking" | "bash";
2
+ export declare class FirstUseHints {
3
+ private readonly seen;
4
+ constructor(seen?: readonly string[]);
5
+ offer(hint: FirstUseHintId): string | undefined;
6
+ getSeen(): FirstUseHintId[];
7
+ }
8
+ //# sourceMappingURL=first-use-hints.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"first-use-hints.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/first-use-hints.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,cAAc,GAAG,OAAO,GAAG,aAAa,GAAG,UAAU,GAAG,MAAM,CAAC;AAS3E,qBAAa,aAAa;IACzB,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAsB;IAE3C,YAAY,IAAI,GAAE,SAAS,MAAM,EAAO,EAEvC;IAED,KAAK,CAAC,IAAI,EAAE,cAAc,GAAG,MAAM,GAAG,SAAS,CAI9C;IAED,OAAO,IAAI,cAAc,EAAE,CAE1B;CACD","sourcesContent":["export type FirstUseHintId = \"queue\" | \"tool-expand\" | \"thinking\" | \"bash\";\n\nconst HINTS: Record<FirstUseHintId, string> = {\n\tqueue: \"Queued messages run after the current turn.\",\n\t\"tool-expand\": \"Use the tool expansion shortcut to see full output.\",\n\tthinking: \"Use the thinking shortcut to show or hide reasoning blocks.\",\n\tbash: \"Press Escape to leave bash input mode.\",\n};\n\nexport class FirstUseHints {\n\tprivate readonly seen: Set<FirstUseHintId>;\n\n\tconstructor(seen: readonly string[] = []) {\n\t\tthis.seen = new Set(seen.filter((hint): hint is FirstUseHintId => hint in HINTS));\n\t}\n\n\toffer(hint: FirstUseHintId): string | undefined {\n\t\tif (this.seen.has(hint)) return undefined;\n\t\tthis.seen.add(hint);\n\t\treturn HINTS[hint];\n\t}\n\n\tgetSeen(): FirstUseHintId[] {\n\t\treturn [...this.seen];\n\t}\n}\n"]}
@@ -0,0 +1,22 @@
1
+ const HINTS = {
2
+ queue: "Queued messages run after the current turn.",
3
+ "tool-expand": "Use the tool expansion shortcut to see full output.",
4
+ thinking: "Use the thinking shortcut to show or hide reasoning blocks.",
5
+ bash: "Press Escape to leave bash input mode.",
6
+ };
7
+ export class FirstUseHints {
8
+ seen;
9
+ constructor(seen = []) {
10
+ this.seen = new Set(seen.filter((hint) => hint in HINTS));
11
+ }
12
+ offer(hint) {
13
+ if (this.seen.has(hint))
14
+ return undefined;
15
+ this.seen.add(hint);
16
+ return HINTS[hint];
17
+ }
18
+ getSeen() {
19
+ return [...this.seen];
20
+ }
21
+ }
22
+ //# sourceMappingURL=first-use-hints.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"first-use-hints.js","sourceRoot":"","sources":["../../../../src/modes/interactive/components/first-use-hints.ts"],"names":[],"mappings":"AAEA,MAAM,KAAK,GAAmC;IAC7C,KAAK,EAAE,6CAA6C;IACpD,aAAa,EAAE,qDAAqD;IACpE,QAAQ,EAAE,6DAA6D;IACvE,IAAI,EAAE,wCAAwC;CAC9C,CAAC;AAEF,MAAM,OAAO,aAAa;IACR,IAAI,CAAsB;IAE3C,YAAY,IAAI,GAAsB,EAAE,EAAE;QACzC,IAAI,CAAC,IAAI,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,IAAI,EAA0B,EAAE,CAAC,IAAI,IAAI,KAAK,CAAC,CAAC,CAAC;IAAA,CAClF;IAED,KAAK,CAAC,IAAoB,EAAsB;QAC/C,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC;YAAE,OAAO,SAAS,CAAC;QAC1C,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACpB,OAAO,KAAK,CAAC,IAAI,CAAC,CAAC;IAAA,CACnB;IAED,OAAO,GAAqB;QAC3B,OAAO,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC;IAAA,CACtB;CACD","sourcesContent":["export type FirstUseHintId = \"queue\" | \"tool-expand\" | \"thinking\" | \"bash\";\n\nconst HINTS: Record<FirstUseHintId, string> = {\n\tqueue: \"Queued messages run after the current turn.\",\n\t\"tool-expand\": \"Use the tool expansion shortcut to see full output.\",\n\tthinking: \"Use the thinking shortcut to show or hide reasoning blocks.\",\n\tbash: \"Press Escape to leave bash input mode.\",\n};\n\nexport class FirstUseHints {\n\tprivate readonly seen: Set<FirstUseHintId>;\n\n\tconstructor(seen: readonly string[] = []) {\n\t\tthis.seen = new Set(seen.filter((hint): hint is FirstUseHintId => hint in HINTS));\n\t}\n\n\toffer(hint: FirstUseHintId): string | undefined {\n\t\tif (this.seen.has(hint)) return undefined;\n\t\tthis.seen.add(hint);\n\t\treturn HINTS[hint];\n\t}\n\n\tgetSeen(): FirstUseHintId[] {\n\t\treturn [...this.seen];\n\t}\n}\n"]}
@@ -1,10 +1,29 @@
1
1
  import { type Component } from "@earendil-works/pi-tui";
2
2
  import type { AgentSession } from "../../../core/agent-session.ts";
3
3
  import type { ReadonlyFooterDataProvider } from "../../../core/footer-data-provider.ts";
4
+ import type { PermissionMode } from "../../../core/permissions/store.ts";
4
5
  /**
5
6
  * Format token counts for compact footer display.
6
7
  */
7
8
  export declare function formatTokens(count: number): string;
9
+ /**
10
+ * A filled bar for the context window, split into its filled and empty runs so
11
+ * the caller can colour them separately.
12
+ *
13
+ * This is a redundant channel and never the only one: the percentage beside it
14
+ * states the same value as text (WCAG 1.4.1). An unknown percentage renders an
15
+ * empty track rather than guessing a position.
16
+ */
17
+ export declare function renderContextGauge(percent: number | undefined, symbolPreset: "unicode" | "ascii"): {
18
+ filled: string;
19
+ empty: string;
20
+ };
21
+ /** Accessibility/display settings the footer reads (roadmap Phase 8, task 8.6). */
22
+ export interface FooterAccessibilitySettings {
23
+ getSymbolPreset(): "unicode" | "ascii";
24
+ getColorBlindMode(): boolean;
25
+ getTokenUsageDisplay(): "off" | "compact" | "full";
26
+ }
8
27
  export declare function formatCwdForFooter(cwd: string, home: string | undefined): string;
9
28
  /**
10
29
  * Footer component that shows pwd, token stats, and context usage.
@@ -12,11 +31,14 @@ export declare function formatCwdForFooter(cwd: string, home: string | undefined
12
31
  */
13
32
  export declare class FooterComponent implements Component {
14
33
  private autoCompactEnabled;
34
+ private permissionMode;
15
35
  private session;
16
36
  private footerData;
17
- constructor(session: AgentSession, footerData: ReadonlyFooterDataProvider);
37
+ private accessibilitySettings;
38
+ constructor(session: AgentSession, footerData: ReadonlyFooterDataProvider, accessibilitySettings?: FooterAccessibilitySettings);
18
39
  setSession(session: AgentSession): void;
19
40
  setAutoCompactEnabled(enabled: boolean): void;
41
+ setPermissionMode(mode: PermissionMode): void;
20
42
  /**
21
43
  * No-op: git branch caching now handled by provider.
22
44
  * Kept for compatibility with existing call sites in interactive-mode.
@@ -1 +1 @@
1
- {"version":3,"file":"footer.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/footer.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,SAAS,EAAiC,MAAM,wBAAwB,CAAC;AACvF,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gCAAgC,CAAC;AAEnE,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,uCAAuC,CAAC;AAgBxF;;GAEG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAMlD;AAED,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAYhF;AAED;;;GAGG;AACH,qBAAa,eAAgB,YAAW,SAAS;IAChD,OAAO,CAAC,kBAAkB,CAAQ;IAClC,OAAO,CAAC,OAAO,CAAe;IAC9B,OAAO,CAAC,UAAU,CAA6B;IAE/C,YAAY,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,0BAA0B,EAGxE;IAED,UAAU,CAAC,OAAO,EAAE,YAAY,GAAG,IAAI,CAEtC;IAED,qBAAqB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAE5C;IAED;;;OAGG;IACH,UAAU,IAAI,IAAI,CAEjB;IAED;;;OAGG;IACH,OAAO,IAAI,IAAI,CAEd;IAED,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAgK9B;CACD","sourcesContent":["import { isAbsolute, relative, resolve, sep } from \"node:path\";\nimport { type Component, truncateToWidth, visibleWidth } from \"@earendil-works/pi-tui\";\nimport type { AgentSession } from \"../../../core/agent-session.ts\";\nimport { areExperimentalFeaturesEnabled } from \"../../../core/experimental.ts\";\nimport type { ReadonlyFooterDataProvider } from \"../../../core/footer-data-provider.ts\";\nimport { addUsageToTotals, createUsageTotals } from \"../../../core/usage-totals.ts\";\nimport { theme } from \"../theme/theme.ts\";\n\n/**\n * Sanitize text for display in a single-line status.\n * Removes newlines, tabs, carriage returns, and other control characters.\n */\nfunction sanitizeStatusText(text: string): string {\n\t// Replace newlines, tabs, carriage returns with space, then collapse multiple spaces\n\treturn text\n\t\t.replace(/[\\r\\n\\t]/g, \" \")\n\t\t.replace(/ +/g, \" \")\n\t\t.trim();\n}\n\n/**\n * Format token counts for compact footer display.\n */\nexport function formatTokens(count: number): string {\n\tif (count < 1000) return count.toString();\n\tif (count < 10000) return `${(count / 1000).toFixed(1)}k`;\n\tif (count < 1000000) return `${Math.round(count / 1000)}k`;\n\tif (count < 10000000) return `${(count / 1000000).toFixed(1)}M`;\n\treturn `${Math.round(count / 1000000)}M`;\n}\n\nexport function formatCwdForFooter(cwd: string, home: string | undefined): string {\n\tif (!home) return cwd;\n\n\tconst resolvedCwd = resolve(cwd);\n\tconst resolvedHome = resolve(home);\n\tconst relativeToHome = relative(resolvedHome, resolvedCwd);\n\tconst isInsideHome =\n\t\trelativeToHome === \"\" ||\n\t\t(relativeToHome !== \"..\" && !relativeToHome.startsWith(`..${sep}`) && !isAbsolute(relativeToHome));\n\n\tif (!isInsideHome) return cwd;\n\treturn relativeToHome === \"\" ? \"~\" : `~${sep}${relativeToHome}`;\n}\n\n/**\n * Footer component that shows pwd, token stats, and context usage.\n * Computes token/context stats from session, gets git branch and extension statuses from provider.\n */\nexport class FooterComponent implements Component {\n\tprivate autoCompactEnabled = true;\n\tprivate session: AgentSession;\n\tprivate footerData: ReadonlyFooterDataProvider;\n\n\tconstructor(session: AgentSession, footerData: ReadonlyFooterDataProvider) {\n\t\tthis.session = session;\n\t\tthis.footerData = footerData;\n\t}\n\n\tsetSession(session: AgentSession): void {\n\t\tthis.session = session;\n\t}\n\n\tsetAutoCompactEnabled(enabled: boolean): void {\n\t\tthis.autoCompactEnabled = enabled;\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\tconst state = this.session.state;\n\n\t\t// Calculate cumulative usage from ALL session entries (not just post-compaction messages)\n\t\tconst usageTotals = createUsageTotals();\n\t\tlet latestCacheHitRate: number | undefined;\n\n\t\tfor (const entry of this.session.sessionManager.getEntries()) {\n\t\t\tif (entry.type === \"message\" && entry.message.role === \"assistant\") {\n\t\t\t\taddUsageToTotals(usageTotals, entry.message.usage);\n\n\t\t\t\tconst latestPromptTokens =\n\t\t\t\t\tentry.message.usage.input + entry.message.usage.cacheRead + entry.message.usage.cacheWrite;\n\t\t\t\tlatestCacheHitRate =\n\t\t\t\t\tlatestPromptTokens > 0 ? (entry.message.usage.cacheRead / latestPromptTokens) * 100 : undefined;\n\t\t\t} else if (entry.type === \"message\" && entry.message.role === \"toolResult\" && entry.message.usage) {\n\t\t\t\taddUsageToTotals(usageTotals, entry.message.usage);\n\t\t\t} else if ((entry.type === \"branch_summary\" || entry.type === \"compaction\") && entry.usage) {\n\t\t\t\taddUsageToTotals(usageTotals, entry.usage);\n\t\t\t}\n\t\t}\n\n\t\t// Calculate context usage from session (handles compaction correctly).\n\t\t// After compaction, tokens are unknown until the next LLM response.\n\t\tconst contextUsage = this.session.getContextUsage();\n\t\tconst contextWindow = contextUsage?.contextWindow ?? state.model?.contextWindow ?? 0;\n\t\tconst contextPercentValue = contextUsage?.percent ?? 0;\n\t\tconst contextPercent = contextUsage?.percent !== null ? contextPercentValue.toFixed(1) : \"?\";\n\n\t\t// Replace home directory with ~\n\t\tlet pwd = formatCwdForFooter(this.session.sessionManager.getCwd(), process.env.HOME || process.env.USERPROFILE);\n\n\t\t// Add git branch if available\n\t\tconst branch = this.footerData.getGitBranch();\n\t\tif (branch) {\n\t\t\tpwd = `${pwd} (${branch})`;\n\t\t}\n\n\t\t// Add session name if set\n\t\tconst sessionName = this.session.sessionManager.getSessionName();\n\t\tif (sessionName) {\n\t\t\tpwd = `${pwd} • ${sessionName}`;\n\t\t}\n\n\t\t// Build stats line\n\t\tconst statsParts = [];\n\t\tif (usageTotals.input) statsParts.push(`↑${formatTokens(usageTotals.input)}`);\n\t\tif (usageTotals.output) statsParts.push(`↓${formatTokens(usageTotals.output)}`);\n\t\tif (usageTotals.cacheRead) statsParts.push(`R${formatTokens(usageTotals.cacheRead)}`);\n\t\tif (usageTotals.cacheWrite) statsParts.push(`W${formatTokens(usageTotals.cacheWrite)}`);\n\t\tif ((usageTotals.cacheRead > 0 || usageTotals.cacheWrite > 0) && latestCacheHitRate !== undefined) {\n\t\t\tstatsParts.push(`CH${latestCacheHitRate.toFixed(1)}%`);\n\t\t}\n\n\t\t// Kimi Coding is subscription-backed despite using API-key authentication.\n\t\tconst usingSubscription = state.model\n\t\t\t? state.model.provider === \"kimi-coding\" || this.session.modelRuntime.isUsingSubscription(state.model.provider)\n\t\t\t: false;\n\t\tif (usageTotals.cost || usingSubscription) {\n\t\t\tconst costStr = `$${usageTotals.cost.toFixed(3)}${usingSubscription ? \" (sub)\" : \"\"}`;\n\t\t\tstatsParts.push(costStr);\n\t\t}\n\n\t\t// Colorize context percentage based on usage\n\t\tlet contextPercentStr: string;\n\t\tconst autoIndicator = this.autoCompactEnabled ? \" (auto)\" : \"\";\n\t\tconst contextPercentDisplay =\n\t\t\tcontextPercent === \"?\"\n\t\t\t\t? `?/${formatTokens(contextWindow)}${autoIndicator}`\n\t\t\t\t: `${contextPercent}%/${formatTokens(contextWindow)}${autoIndicator}`;\n\t\tif (contextPercentValue > 90) {\n\t\t\tcontextPercentStr = theme.fg(\"error\", contextPercentDisplay);\n\t\t} else if (contextPercentValue > 70) {\n\t\t\tcontextPercentStr = theme.fg(\"warning\", contextPercentDisplay);\n\t\t} else {\n\t\t\tcontextPercentStr = contextPercentDisplay;\n\t\t}\n\t\tstatsParts.push(contextPercentStr);\n\t\tif (areExperimentalFeaturesEnabled()) {\n\t\t\tstatsParts.push(`${theme.fg(\"dim\", \"•\")} ${theme.bold(theme.fg(\"warning\", \"xp\"))}`);\n\t\t}\n\n\t\tlet statsLeft = statsParts.join(\" \");\n\n\t\t// Add model name on the right side, plus thinking level if model supports it\n\t\tconst modelName = state.model?.id || \"no-model\";\n\n\t\tlet statsLeftWidth = visibleWidth(statsLeft);\n\n\t\t// If statsLeft is too wide, truncate it\n\t\tif (statsLeftWidth > width) {\n\t\t\tstatsLeft = truncateToWidth(statsLeft, width, \"...\");\n\t\t\tstatsLeftWidth = visibleWidth(statsLeft);\n\t\t}\n\n\t\t// Calculate available space for padding (minimum 2 spaces between stats and model)\n\t\tconst minPadding = 2;\n\n\t\t// Add thinking level indicator if model supports reasoning\n\t\tlet rightSideWithoutProvider = modelName;\n\t\tif (state.model?.reasoning) {\n\t\t\tconst thinkingLevel = state.thinkingLevel || \"off\";\n\t\t\trightSideWithoutProvider =\n\t\t\t\tthinkingLevel === \"off\" ? `${modelName} • thinking off` : `${modelName} • ${thinkingLevel}`;\n\t\t}\n\n\t\t// Prepend the provider in parentheses if there are multiple providers and there's enough room\n\t\tlet rightSide = rightSideWithoutProvider;\n\t\tif (this.footerData.getAvailableProviderCount() > 1 && state.model) {\n\t\t\trightSide = `(${state.model!.provider}) ${rightSideWithoutProvider}`;\n\t\t\tif (statsLeftWidth + minPadding + visibleWidth(rightSide) > width) {\n\t\t\t\t// Too wide, fall back\n\t\t\t\trightSide = rightSideWithoutProvider;\n\t\t\t}\n\t\t}\n\n\t\tconst rightSideWidth = visibleWidth(rightSide);\n\t\tconst totalNeeded = statsLeftWidth + minPadding + rightSideWidth;\n\n\t\tlet statsLine: string;\n\t\tif (totalNeeded <= width) {\n\t\t\t// Both fit - add padding to right-align model\n\t\t\tconst padding = \" \".repeat(width - statsLeftWidth - rightSideWidth);\n\t\t\tstatsLine = statsLeft + padding + rightSide;\n\t\t} else {\n\t\t\t// Need to truncate right side\n\t\t\tconst availableForRight = width - statsLeftWidth - minPadding;\n\t\t\tif (availableForRight > 0) {\n\t\t\t\tconst truncatedRight = truncateToWidth(rightSide, availableForRight, \"\");\n\t\t\t\tconst truncatedRightWidth = visibleWidth(truncatedRight);\n\t\t\t\tconst padding = \" \".repeat(Math.max(0, width - statsLeftWidth - truncatedRightWidth));\n\t\t\t\tstatsLine = statsLeft + padding + truncatedRight;\n\t\t\t} else {\n\t\t\t\t// Not enough space for right side at all\n\t\t\t\tstatsLine = statsLeft;\n\t\t\t}\n\t\t}\n\n\t\t// Apply dim to each part separately. statsLeft may contain color codes (for context %)\n\t\t// that end with a reset, which would clear an outer dim wrapper. So we dim the parts\n\t\t// before and after the colored section independently.\n\t\tconst dimStatsLeft = theme.fg(\"dim\", statsLeft);\n\t\tconst remainder = statsLine.slice(statsLeft.length); // padding + rightSide\n\t\tconst dimRemainder = theme.fg(\"dim\", remainder);\n\n\t\tconst pwdLine = truncateToWidth(theme.fg(\"dim\", pwd), width, theme.fg(\"dim\", \"...\"));\n\t\tconst lines = [pwdLine, dimStatsLeft + dimRemainder];\n\n\t\t// Add extension statuses on a single line, sorted by key alphabetically\n\t\tconst extensionStatuses = this.footerData.getExtensionStatuses();\n\t\tif (extensionStatuses.size > 0) {\n\t\t\tconst sortedStatuses = Array.from(extensionStatuses.entries())\n\t\t\t\t.sort(([a], [b]) => a.localeCompare(b))\n\t\t\t\t.map(([, text]) => sanitizeStatusText(text));\n\t\t\tconst statusLine = sortedStatuses.join(\" \");\n\t\t\t// Truncate to terminal width with dim ellipsis for consistency with footer style\n\t\t\tlines.push(truncateToWidth(statusLine, width, theme.fg(\"dim\", \"...\")));\n\t\t}\n\n\t\treturn lines;\n\t}\n}\n"]}
1
+ {"version":3,"file":"footer.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/footer.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,SAAS,EAAiC,MAAM,wBAAwB,CAAC;AACvF,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gCAAgC,CAAC;AAEnE,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,uCAAuC,CAAC;AACxF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAC;AAgBzE;;GAEG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAMlD;AAKD;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CACjC,OAAO,EAAE,MAAM,GAAG,SAAS,EAC3B,YAAY,EAAE,SAAS,GAAG,OAAO,GAC/B;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAWnC;AAED,mFAAmF;AACnF,MAAM,WAAW,2BAA2B;IAC3C,eAAe,IAAI,SAAS,GAAG,OAAO,CAAC;IACvC,iBAAiB,IAAI,OAAO,CAAC;IAC7B,oBAAoB,IAAI,KAAK,GAAG,SAAS,GAAG,MAAM,CAAC;CACnD;AAQD,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAYhF;AAED;;;GAGG;AACH,qBAAa,eAAgB,YAAW,SAAS;IAChD,OAAO,CAAC,kBAAkB,CAAQ;IAClC,OAAO,CAAC,cAAc,CAA6B;IACnD,OAAO,CAAC,OAAO,CAAe;IAC9B,OAAO,CAAC,UAAU,CAA6B;IAC/C,OAAO,CAAC,qBAAqB,CAA8B;IAE3D,YACC,OAAO,EAAE,YAAY,EACrB,UAAU,EAAE,0BAA0B,EACtC,qBAAqB,GAAE,2BAA4D,EAKnF;IAED,UAAU,CAAC,OAAO,EAAE,YAAY,GAAG,IAAI,CAEtC;IAED,qBAAqB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAE5C;IAED,iBAAiB,CAAC,IAAI,EAAE,cAAc,GAAG,IAAI,CAE5C;IAED;;;OAGG;IACH,UAAU,IAAI,IAAI,CAEjB;IAED;;;OAGG;IACH,OAAO,IAAI,IAAI,CAEd;IAED,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAmT9B;CACD","sourcesContent":["import { isAbsolute, relative, resolve, sep } from \"node:path\";\nimport { type Component, truncateToWidth, visibleWidth } from \"@earendil-works/pi-tui\";\nimport type { AgentSession } from \"../../../core/agent-session.ts\";\nimport { areExperimentalFeaturesEnabled } from \"../../../core/experimental.ts\";\nimport type { ReadonlyFooterDataProvider } from \"../../../core/footer-data-provider.ts\";\nimport type { PermissionMode } from \"../../../core/permissions/store.ts\";\nimport { addUsageToTotals, createUsageTotals } from \"../../../core/usage-totals.ts\";\nimport { theme } from \"../theme/theme.ts\";\n\n/**\n * Sanitize text for display in a single-line status.\n * Removes newlines, tabs, carriage returns, and other control characters.\n */\nfunction sanitizeStatusText(text: string): string {\n\t// Replace newlines, tabs, carriage returns with space, then collapse multiple spaces\n\treturn text\n\t\t.replace(/[\\r\\n\\t]/g, \" \")\n\t\t.replace(/ +/g, \" \")\n\t\t.trim();\n}\n\n/**\n * Format token counts for compact footer display.\n */\nexport function formatTokens(count: number): string {\n\tif (count < 1000) return count.toString();\n\tif (count < 10000) return `${(count / 1000).toFixed(1)}k`;\n\tif (count < 1000000) return `${Math.round(count / 1000)}k`;\n\tif (count < 10000000) return `${(count / 1000000).toFixed(1)}M`;\n\treturn `${Math.round(count / 1000000)}M`;\n}\n\n/** Cells in the context gauge. Eight reads as a bar without crowding the tray. */\nconst CONTEXT_GAUGE_CELLS = 8;\n\n/**\n * A filled bar for the context window, split into its filled and empty runs so\n * the caller can colour them separately.\n *\n * This is a redundant channel and never the only one: the percentage beside it\n * states the same value as text (WCAG 1.4.1). An unknown percentage renders an\n * empty track rather than guessing a position.\n */\nexport function renderContextGauge(\n\tpercent: number | undefined,\n\tsymbolPreset: \"unicode\" | \"ascii\",\n): { filled: string; empty: string } {\n\tconst filledGlyph = symbolPreset === \"ascii\" ? \"#\" : \"█\";\n\tconst emptyGlyph = symbolPreset === \"ascii\" ? \"-\" : \"░\";\n\tif (percent === undefined) {\n\t\treturn { filled: \"\", empty: emptyGlyph.repeat(CONTEXT_GAUGE_CELLS) };\n\t}\n\tconst clamped = Math.min(100, Math.max(0, percent));\n\t// Any non-zero usage lights at least one cell, so the bar never reads empty\n\t// while the window is genuinely filling.\n\tconst cells = clamped === 0 ? 0 : Math.max(1, Math.round((clamped / 100) * CONTEXT_GAUGE_CELLS));\n\treturn { filled: filledGlyph.repeat(cells), empty: emptyGlyph.repeat(CONTEXT_GAUGE_CELLS - cells) };\n}\n\n/** Accessibility/display settings the footer reads (roadmap Phase 8, task 8.6). */\nexport interface FooterAccessibilitySettings {\n\tgetSymbolPreset(): \"unicode\" | \"ascii\";\n\tgetColorBlindMode(): boolean;\n\tgetTokenUsageDisplay(): \"off\" | \"compact\" | \"full\";\n}\n\nconst DEFAULT_ACCESSIBILITY_SETTINGS: FooterAccessibilitySettings = {\n\tgetSymbolPreset: () => \"unicode\",\n\tgetColorBlindMode: () => false,\n\tgetTokenUsageDisplay: () => \"compact\",\n};\n\nexport function formatCwdForFooter(cwd: string, home: string | undefined): string {\n\tif (!home) return cwd;\n\n\tconst resolvedCwd = resolve(cwd);\n\tconst resolvedHome = resolve(home);\n\tconst relativeToHome = relative(resolvedHome, resolvedCwd);\n\tconst isInsideHome =\n\t\trelativeToHome === \"\" ||\n\t\t(relativeToHome !== \"..\" && !relativeToHome.startsWith(`..${sep}`) && !isAbsolute(relativeToHome));\n\n\tif (!isInsideHome) return cwd;\n\treturn relativeToHome === \"\" ? \"~\" : `~/${relativeToHome.replaceAll(\"\\\\\", \"/\")}`;\n}\n\n/**\n * Footer component that shows pwd, token stats, and context usage.\n * Computes token/context stats from session, gets git branch and extension statuses from provider.\n */\nexport class FooterComponent implements Component {\n\tprivate autoCompactEnabled = true;\n\tprivate permissionMode: PermissionMode = \"default\";\n\tprivate session: AgentSession;\n\tprivate footerData: ReadonlyFooterDataProvider;\n\tprivate accessibilitySettings: FooterAccessibilitySettings;\n\n\tconstructor(\n\t\tsession: AgentSession,\n\t\tfooterData: ReadonlyFooterDataProvider,\n\t\taccessibilitySettings: FooterAccessibilitySettings = DEFAULT_ACCESSIBILITY_SETTINGS,\n\t) {\n\t\tthis.session = session;\n\t\tthis.footerData = footerData;\n\t\tthis.accessibilitySettings = accessibilitySettings;\n\t}\n\n\tsetSession(session: AgentSession): void {\n\t\tthis.session = session;\n\t}\n\n\tsetAutoCompactEnabled(enabled: boolean): void {\n\t\tthis.autoCompactEnabled = enabled;\n\t}\n\n\tsetPermissionMode(mode: PermissionMode): void {\n\t\tthis.permissionMode = mode;\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\tconst state = this.session.state;\n\t\tconst symbolPreset = this.accessibilitySettings.getSymbolPreset();\n\t\tconst colorBlindMode = this.accessibilitySettings.getColorBlindMode();\n\t\tconst tokenUsageDisplay = this.accessibilitySettings.getTokenUsageDisplay();\n\t\tconst upSymbol = symbolPreset === \"ascii\" ? \"^\" : \"↑\";\n\t\tconst downSymbol = symbolPreset === \"ascii\" ? \"v\" : \"↓\";\n\t\tconst bulletSymbol = symbolPreset === \"ascii\" ? \"-\" : \"•\";\n\n\t\t// Calculate cumulative usage from ALL session entries (not just post-compaction messages)\n\t\tconst usageTotals = createUsageTotals();\n\t\tlet latestCacheHitRate: number | undefined;\n\n\t\tfor (const entry of this.session.sessionManager.getEntries()) {\n\t\t\tif (entry.type === \"message\" && entry.message.role === \"assistant\") {\n\t\t\t\taddUsageToTotals(usageTotals, entry.message.usage);\n\n\t\t\t\tconst latestPromptTokens =\n\t\t\t\t\tentry.message.usage.input + entry.message.usage.cacheRead + entry.message.usage.cacheWrite;\n\t\t\t\tlatestCacheHitRate =\n\t\t\t\t\tlatestPromptTokens > 0 ? (entry.message.usage.cacheRead / latestPromptTokens) * 100 : undefined;\n\t\t\t} else if (entry.type === \"message\" && entry.message.role === \"toolResult\" && entry.message.usage) {\n\t\t\t\taddUsageToTotals(usageTotals, entry.message.usage);\n\t\t\t} else if ((entry.type === \"branch_summary\" || entry.type === \"compaction\") && entry.usage) {\n\t\t\t\taddUsageToTotals(usageTotals, entry.usage);\n\t\t\t}\n\t\t}\n\n\t\t// Calculate context usage from session (handles compaction correctly).\n\t\t// After compaction, tokens are unknown until the next LLM response.\n\t\tconst contextUsage = this.session.getContextUsage();\n\t\tconst contextWindow = contextUsage?.contextWindow ?? state.model?.contextWindow ?? 0;\n\t\tconst contextPercentValue = contextUsage?.percent ?? 0;\n\t\tconst contextPercent = contextUsage?.percent !== null ? contextPercentValue.toFixed(1) : \"?\";\n\n\t\t// Replace home directory with ~\n\t\tlet pwd = formatCwdForFooter(this.session.sessionManager.getCwd(), process.env.HOME || process.env.USERPROFILE);\n\n\t\t// Add git branch if available\n\t\tconst branch = this.footerData.getGitBranch();\n\t\tif (branch) {\n\t\t\tpwd = `${pwd} (${branch})`;\n\t\t}\n\n\t\t// Add session name if set\n\t\tconst sessionName = this.session.sessionManager.getSessionName();\n\t\tif (sessionName) {\n\t\t\tpwd = `${pwd} ${bulletSymbol} ${sessionName}`;\n\t\t}\n\n\t\t// Build stats line. tokenUsageDisplay: \"off\" hides this whole section\n\t\t// (context% and model name are separate and always shown); \"full\" shows\n\t\t// exact counts instead of formatTokens' abbreviation.\n\t\tconst formatTokenCount = tokenUsageDisplay === \"full\" ? (n: number) => n.toLocaleString() : formatTokens;\n\t\tconst statsParts = [];\n\t\tif (tokenUsageDisplay !== \"off\") {\n\t\t\tif (usageTotals.input) statsParts.push(`${upSymbol}${formatTokenCount(usageTotals.input)}`);\n\t\t\tif (usageTotals.output) statsParts.push(`${downSymbol}${formatTokenCount(usageTotals.output)}`);\n\t\t\tif (usageTotals.cacheRead) statsParts.push(`R${formatTokenCount(usageTotals.cacheRead)}`);\n\t\t\tif (usageTotals.cacheWrite) statsParts.push(`W${formatTokenCount(usageTotals.cacheWrite)}`);\n\t\t\tif ((usageTotals.cacheRead > 0 || usageTotals.cacheWrite > 0) && latestCacheHitRate !== undefined) {\n\t\t\t\tstatsParts.push(`CH${latestCacheHitRate.toFixed(1)}%`);\n\t\t\t}\n\t\t}\n\n\t\t// Kimi Coding is subscription-backed despite using API-key authentication.\n\t\tconst usingSubscription = state.model\n\t\t\t? state.model.provider === \"kimi-coding\" || this.session.modelRuntime.isUsingSubscription(state.model.provider)\n\t\t\t: false;\n\t\tif (tokenUsageDisplay !== \"off\" && (usageTotals.cost || usingSubscription)) {\n\t\t\tconst costStr = `$${usageTotals.cost.toFixed(3)}${usingSubscription ? \" (sub)\" : \"\"}`;\n\t\t\tstatsParts.push(costStr);\n\t\t}\n\n\t\t// Context pressure is signalled through text at every setting -- never\n\t\t// color alone (WCAG 1.4.1) -- with color as an additional channel. The\n\t\t// default error/warning pair (red/amber) is already distinguishable for\n\t\t// most red-green colorblindness; colorBlindMode's palette adjustment\n\t\t// moves the critical (>90%) threshold to \"accent\" -- a hue outside that\n\t\t// red/amber family entirely -- rather than relying on a style attribute\n\t\t// like bold/underline, which several terminals (and this theme's own\n\t\t// chalk-backed style methods, suppressed outside a real TTY) silently\n\t\t// drop, making it an unreliable second channel.\n\t\tlet contextPercentStr: string;\n\t\tconst autoIndicator = this.autoCompactEnabled ? \" (auto)\" : \"\";\n\t\tconst pressureMarker = contextPercentValue > 90 ? \"!!\" : contextPercentValue > 70 ? \"!\" : \"\";\n\t\tconst contextPercentDisplay =\n\t\t\tcontextPercent === \"?\"\n\t\t\t\t? `?/${formatTokens(contextWindow)}${autoIndicator}`\n\t\t\t\t: `${contextPercent}%${pressureMarker}/${formatTokens(contextWindow)}${autoIndicator}`;\n\t\tif (contextPercentValue > 90) {\n\t\t\tcontextPercentStr = theme.fg(colorBlindMode ? \"accent\" : \"error\", contextPercentDisplay);\n\t\t} else if (contextPercentValue > 70) {\n\t\t\tcontextPercentStr = theme.fg(\"warning\", contextPercentDisplay);\n\t\t} else {\n\t\t\tcontextPercentStr = contextPercentDisplay;\n\t\t}\n\t\tstatsParts.push(contextPercentStr);\n\t\tif (areExperimentalFeaturesEnabled()) {\n\t\t\tstatsParts.push(`${theme.fg(\"dim\", bulletSymbol)} ${theme.bold(theme.fg(\"warning\", \"xp\"))}`);\n\t\t}\n\n\t\t// Permission posture is always named, including `default`. It is operational\n\t\t// state rather than routine telemetry: hiding the safe default would make the\n\t\t// tray change meaning by omission, while hiding bypass would conceal risk.\n\t\t// Text carries the signal and color remains a second channel (WCAG 1.4.1).\n\t\tconst modeColor =\n\t\t\tthis.permissionMode === \"bypassPermissions\" ? \"error\" : this.permissionMode === \"default\" ? \"dim\" : \"warning\";\n\t\tstatsParts.unshift(theme.fg(modeColor, this.permissionMode));\n\n\t\tif (tokenUsageDisplay !== \"full\") {\n\t\t\tconst separator = theme.fg(\"dim\", symbolPreset === \"ascii\" ? \" - \" : \" · \");\n\t\t\tconst compactPermissionNames: Record<PermissionMode, string> = {\n\t\t\t\tdefault: \"default\",\n\t\t\t\tplan: \"plan\",\n\t\t\t\tacceptEdits: \"accept\",\n\t\t\t\tbypassPermissions: \"bypass\",\n\t\t\t\tdontAsk: \"dontAsk\",\n\t\t\t};\n\t\t\tconst permissionColor =\n\t\t\t\tthis.permissionMode === \"bypassPermissions\"\n\t\t\t\t\t? \"error\"\n\t\t\t\t\t: this.permissionMode === \"default\"\n\t\t\t\t\t\t? \"dim\"\n\t\t\t\t\t\t: \"warning\";\n\t\t\tconst permissionFull = theme.fg(permissionColor, this.permissionMode);\n\t\t\tconst permissionCompact = theme.fg(permissionColor, compactPermissionNames[this.permissionMode]);\n\t\t\tconst compactPercent = contextPercent === \"?\" ? \"?\" : contextPercentValue.toFixed(0);\n\t\t\tconst compactPressure = contextPercentValue > 90 ? \"!!\" : contextPercentValue > 70 ? \"!\" : \"\";\n\t\t\tconst contextColor =\n\t\t\t\tcontextPercentValue > 90\n\t\t\t\t\t? colorBlindMode\n\t\t\t\t\t\t? \"accent\"\n\t\t\t\t\t\t: \"error\"\n\t\t\t\t\t: contextPercentValue > 70\n\t\t\t\t\t\t? \"warning\"\n\t\t\t\t\t\t: \"dim\";\n\t\t\t// The gauge rides along only in the roomy form. The compact form drops\n\t\t\t// it, which is what lets it keep winning the fit ladder below.\n\t\t\tconst gauge = renderContextGauge(contextPercent === \"?\" ? undefined : contextPercentValue, symbolPreset);\n\t\t\t// Below the warning threshold the text is dim, which would make the bar\n\t\t\t// invisible; the fill takes the accent there and tracks the text colour\n\t\t\t// at every level that actually signals pressure.\n\t\t\tconst gaugeColor = contextColor === \"dim\" ? \"accent\" : contextColor;\n\t\t\t// An empty run would still emit an open/close SGR pair, so skip it.\n\t\t\tconst paintRun = (color: Parameters<typeof theme.fg>[0], text: string) => (text ? theme.fg(color, text) : \"\");\n\t\t\tconst contextGauged =\n\t\t\t\ttheme.fg(contextColor, \"context \") +\n\t\t\t\tpaintRun(gaugeColor, gauge.filled) +\n\t\t\t\tpaintRun(\"borderMuted\", gauge.empty) +\n\t\t\t\ttheme.fg(contextColor, ` ${contextPercent}%${pressureMarker}`);\n\t\t\tconst contextFull = theme.fg(contextColor, `context ${contextPercent}%${pressureMarker}`);\n\t\t\tconst contextCompact = theme.fg(contextColor, `ctx ${compactPercent}%${compactPressure}`);\n\n\t\t\tconst joinSegments = (segments: string[]): string => segments.join(separator);\n\t\t\tconst fits = (segments: string[]): boolean => visibleWidth(joinSegments(segments)) <= width;\n\t\t\tlet left: string[] = [permissionFull];\n\t\t\tlet right = contextGauged;\n\n\t\t\t// The gauge is decoration over a value the text already states, so it\n\t\t\t// is the first thing to go. Dropping it must not cost the tray its\n\t\t\t// spelled-out permission mode, which is why this is its own rung.\n\t\t\tif (!fits([...left, right])) {\n\t\t\t\tright = contextFull;\n\t\t\t}\n\n\t\t\tif (!fits([...left, right])) {\n\t\t\t\tleft = [permissionCompact];\n\t\t\t\tright = contextCompact;\n\t\t\t}\n\n\t\t\tif (!fits([...left, right])) {\n\t\t\t\tconst safetyOnly = left[0];\n\t\t\t\tconst line =\n\t\t\t\t\tvisibleWidth(safetyOnly) <= width ? safetyOnly : truncateToWidth(safetyOnly, Math.max(0, width), \"\");\n\t\t\t\treturn width > 0 ? [line] : [];\n\t\t\t}\n\n\t\t\tconst optional: string[] = [];\n\t\t\toptional.push(theme.fg(\"dim\", state.model?.id || \"no-model\"));\n\t\t\tif (state.model?.reasoning && state.thinkingLevel && state.thinkingLevel !== \"off\") {\n\t\t\t\toptional.push(theme.fg(\"dim\", state.thinkingLevel));\n\t\t\t}\n\t\t\tif (tokenUsageDisplay !== \"off\" && (usageTotals.input || usageTotals.output)) {\n\t\t\t\toptional.push(\n\t\t\t\t\ttheme.fg(\n\t\t\t\t\t\t\"dim\",\n\t\t\t\t\t\t`${upSymbol}${formatTokens(usageTotals.input)} ${downSymbol}${formatTokens(usageTotals.output)}`,\n\t\t\t\t\t),\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (\n\t\t\t\ttokenUsageDisplay !== \"off\" &&\n\t\t\t\t(usageTotals.cacheRead > 0 || usageTotals.cacheWrite > 0) &&\n\t\t\t\tlatestCacheHitRate !== undefined\n\t\t\t) {\n\t\t\t\toptional.push(theme.fg(\"dim\", `CH${latestCacheHitRate.toFixed(1)}%`));\n\t\t\t}\n\t\t\tif (tokenUsageDisplay !== \"off\" && (usageTotals.cost || usingSubscription)) {\n\t\t\t\toptional.push(theme.fg(\"dim\", `$${usageTotals.cost.toFixed(3)}${usingSubscription ? \" (sub)\" : \"\"}`));\n\t\t\t}\n\t\t\tif (this.footerData.getAvailableProviderCount() > 1 && state.model) {\n\t\t\t\toptional.push(theme.fg(\"dim\", state.model.provider));\n\t\t\t}\n\t\t\tif (areExperimentalFeaturesEnabled()) optional.push(theme.fg(\"warning\", \"xp\"));\n\t\t\toptional.push(theme.fg(\"dim\", pwd));\n\n\t\t\tfor (const segment of optional) {\n\t\t\t\tif (fits([...left, segment, right])) left.push(segment);\n\t\t\t}\n\n\t\t\tconst leftText = joinSegments(left);\n\t\t\tconst padding = \" \".repeat(Math.max(1, width - visibleWidth(leftText) - visibleWidth(right)));\n\t\t\tconst tray = leftText ? `${leftText}${padding}${right}` : right;\n\t\t\tconst lines = [tray];\n\t\t\tconst extensionStatuses = this.footerData.getExtensionStatuses();\n\t\t\tif (extensionStatuses.size > 0) {\n\t\t\t\tconst sortedStatuses = Array.from(extensionStatuses.entries())\n\t\t\t\t\t.sort(([a], [b]) => a.localeCompare(b))\n\t\t\t\t\t.map(([, text]) => sanitizeStatusText(text));\n\t\t\t\tlines.push(truncateToWidth(sortedStatuses.join(\" \"), width, theme.fg(\"dim\", \"...\")));\n\t\t\t}\n\t\t\treturn lines;\n\t\t}\n\n\t\tlet statsLeft = statsParts.join(\" \");\n\n\t\t// Add model name on the right side, plus thinking level if model supports it\n\t\tconst modelName = state.model?.id || \"no-model\";\n\n\t\tlet statsLeftWidth = visibleWidth(statsLeft);\n\n\t\t// If statsLeft is too wide, truncate it\n\t\tif (statsLeftWidth > width) {\n\t\t\t// Permission mode is the first segment and must remain textual at every\n\t\t\t// positive width. An ellipsis suffix can consume the entire narrow line.\n\t\t\tstatsLeft = truncateToWidth(statsLeft, width, \"\");\n\t\t\tstatsLeftWidth = visibleWidth(statsLeft);\n\t\t}\n\n\t\t// Calculate available space for padding (minimum 2 spaces between stats and model)\n\t\tconst minPadding = 2;\n\n\t\t// Add thinking level indicator if model supports reasoning\n\t\tlet rightSideWithoutProvider = modelName;\n\t\tif (state.model?.reasoning) {\n\t\t\tconst thinkingLevel = state.thinkingLevel || \"off\";\n\t\t\trightSideWithoutProvider =\n\t\t\t\tthinkingLevel === \"off\"\n\t\t\t\t\t? `${modelName} ${bulletSymbol} thinking off`\n\t\t\t\t\t: `${modelName} ${bulletSymbol} ${thinkingLevel}`;\n\t\t}\n\n\t\t// Prepend the provider in parentheses if there are multiple providers and there's enough room\n\t\tlet rightSide = rightSideWithoutProvider;\n\t\tif (this.footerData.getAvailableProviderCount() > 1 && state.model) {\n\t\t\trightSide = `(${state.model!.provider}) ${rightSideWithoutProvider}`;\n\t\t\tif (statsLeftWidth + minPadding + visibleWidth(rightSide) > width) {\n\t\t\t\t// Too wide, fall back\n\t\t\t\trightSide = rightSideWithoutProvider;\n\t\t\t}\n\t\t}\n\n\t\tconst rightSideWidth = visibleWidth(rightSide);\n\t\tconst totalNeeded = statsLeftWidth + minPadding + rightSideWidth;\n\n\t\tlet statsLine: string;\n\t\tif (totalNeeded <= width) {\n\t\t\t// Both fit - add padding to right-align model\n\t\t\tconst padding = \" \".repeat(width - statsLeftWidth - rightSideWidth);\n\t\t\tstatsLine = statsLeft + padding + rightSide;\n\t\t} else {\n\t\t\t// Need to truncate right side\n\t\t\tconst availableForRight = width - statsLeftWidth - minPadding;\n\t\t\tif (availableForRight > 0) {\n\t\t\t\tconst truncatedRight = truncateToWidth(rightSide, availableForRight, \"\");\n\t\t\t\tconst truncatedRightWidth = visibleWidth(truncatedRight);\n\t\t\t\tconst padding = \" \".repeat(Math.max(0, width - statsLeftWidth - truncatedRightWidth));\n\t\t\t\tstatsLine = statsLeft + padding + truncatedRight;\n\t\t\t} else {\n\t\t\t\t// Not enough space for right side at all\n\t\t\t\tstatsLine = statsLeft;\n\t\t\t}\n\t\t}\n\n\t\t// Apply dim to each part separately. statsLeft may contain color codes (for context %)\n\t\t// that end with a reset, which would clear an outer dim wrapper. So we dim the parts\n\t\t// before and after the colored section independently.\n\t\tconst dimStatsLeft = theme.fg(\"dim\", statsLeft);\n\t\tconst remainder = statsLine.slice(statsLeft.length); // padding + rightSide\n\t\tconst dimRemainder = theme.fg(\"dim\", remainder);\n\n\t\tconst pwdLine = truncateToWidth(theme.fg(\"dim\", pwd), width, theme.fg(\"dim\", \"...\"));\n\t\tconst lines = [pwdLine, dimStatsLeft + dimRemainder];\n\n\t\t// Add extension statuses on a single line, sorted by key alphabetically\n\t\tconst extensionStatuses = this.footerData.getExtensionStatuses();\n\t\tif (extensionStatuses.size > 0) {\n\t\t\tconst sortedStatuses = Array.from(extensionStatuses.entries())\n\t\t\t\t.sort(([a], [b]) => a.localeCompare(b))\n\t\t\t\t.map(([, text]) => sanitizeStatusText(text));\n\t\t\tconst statusLine = sortedStatuses.join(\" \");\n\t\t\t// Truncate to terminal width with dim ellipsis for consistency with footer style\n\t\t\tlines.push(truncateToWidth(statusLine, width, theme.fg(\"dim\", \"...\")));\n\t\t}\n\n\t\treturn lines;\n\t}\n}\n"]}