@hoilab/ada-cli 0.84.21 → 0.84.22
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.
- package/CHANGELOG.md +20 -0
- package/README.es.md +163 -0
- package/README.md +99 -652
- package/dist/cli/args.d.ts +3 -0
- package/dist/cli/args.d.ts.map +1 -1
- package/dist/cli/args.js +11 -0
- package/dist/cli/args.js.map +1 -1
- package/dist/cli/file-processor.d.ts.map +1 -1
- package/dist/cli/file-processor.js +2 -1
- package/dist/cli/file-processor.js.map +1 -1
- package/dist/cli/home-screen.d.ts.map +1 -1
- package/dist/cli/home-screen.js +2 -2
- package/dist/cli/home-screen.js.map +1 -1
- package/dist/cli/startup-ui.d.ts.map +1 -1
- package/dist/cli/startup-ui.js +9 -3
- package/dist/cli/startup-ui.js.map +1 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +2 -1
- package/dist/config.js.map +1 -1
- package/dist/core/agent-session.d.ts +57 -7
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +449 -73
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/auth-storage.d.ts.map +1 -1
- package/dist/core/auth-storage.js +4 -3
- package/dist/core/auth-storage.js.map +1 -1
- package/dist/core/compaction/branch-summarization.d.ts.map +1 -1
- package/dist/core/compaction/branch-summarization.js +3 -0
- package/dist/core/compaction/branch-summarization.js.map +1 -1
- package/dist/core/compaction/compaction.d.ts +4 -3
- package/dist/core/compaction/compaction.d.ts.map +1 -1
- package/dist/core/compaction/compaction.js +41 -32
- package/dist/core/compaction/compaction.js.map +1 -1
- package/dist/core/defaults.d.ts +1 -0
- package/dist/core/defaults.d.ts.map +1 -1
- package/dist/core/defaults.js +9 -0
- package/dist/core/defaults.js.map +1 -1
- package/dist/core/engine-personality.d.ts +12 -0
- package/dist/core/engine-personality.d.ts.map +1 -0
- package/dist/core/engine-personality.js +53 -0
- package/dist/core/engine-personality.js.map +1 -0
- package/dist/core/extensions/index.d.ts +1 -1
- package/dist/core/extensions/index.d.ts.map +1 -1
- package/dist/core/extensions/index.js.map +1 -1
- package/dist/core/extensions/loader.d.ts.map +1 -1
- package/dist/core/extensions/loader.js +10 -5
- package/dist/core/extensions/loader.js.map +1 -1
- package/dist/core/extensions/types.d.ts +23 -4
- package/dist/core/extensions/types.d.ts.map +1 -1
- package/dist/core/extensions/types.js.map +1 -1
- package/dist/core/keybindings.d.ts.map +1 -1
- package/dist/core/keybindings.js +2 -1
- package/dist/core/keybindings.js.map +1 -1
- package/dist/core/memory-engine/audit-export.d.ts +54 -0
- package/dist/core/memory-engine/audit-export.d.ts.map +1 -0
- package/dist/core/memory-engine/audit-export.js +85 -0
- package/dist/core/memory-engine/audit-export.js.map +1 -0
- package/dist/core/memory-engine/audit.d.ts.map +1 -1
- package/dist/core/memory-engine/audit.js +7 -8
- package/dist/core/memory-engine/audit.js.map +1 -1
- package/dist/core/memory-engine/engine.d.ts +2 -3
- package/dist/core/memory-engine/engine.d.ts.map +1 -1
- package/dist/core/memory-engine/engine.js +148 -50
- package/dist/core/memory-engine/engine.js.map +1 -1
- package/dist/core/memory-engine/inverted-index.d.ts.map +1 -1
- package/dist/core/memory-engine/inverted-index.js +67 -10
- package/dist/core/memory-engine/inverted-index.js.map +1 -1
- package/dist/core/memory-engine/keychain.d.ts.map +1 -1
- package/dist/core/memory-engine/keychain.js +1 -6
- package/dist/core/memory-engine/keychain.js.map +1 -1
- package/dist/core/memory-engine/observation-store.d.ts +1 -0
- package/dist/core/memory-engine/observation-store.d.ts.map +1 -1
- package/dist/core/memory-engine/observation-store.js +37 -8
- package/dist/core/memory-engine/observation-store.js.map +1 -1
- package/dist/core/memory-engine/scanner.d.ts.map +1 -1
- package/dist/core/memory-engine/scanner.js +25 -5
- package/dist/core/memory-engine/scanner.js.map +1 -1
- package/dist/core/memory-engine/security.d.ts.map +1 -1
- package/dist/core/memory-engine/security.js +131 -23
- package/dist/core/memory-engine/security.js.map +1 -1
- package/dist/core/memory-engine/session-indexer.d.ts.map +1 -1
- package/dist/core/memory-engine/session-indexer.js +8 -2
- package/dist/core/memory-engine/session-indexer.js.map +1 -1
- package/dist/core/memory-engine/tools.d.ts +2 -2
- package/dist/core/memory-engine/tools.d.ts.map +1 -1
- package/dist/core/memory-engine/tools.js +14 -7
- package/dist/core/memory-engine/tools.js.map +1 -1
- package/dist/core/memory-engine/vault.d.ts.map +1 -1
- package/dist/core/memory-engine/vault.js +2 -2
- package/dist/core/memory-engine/vault.js.map +1 -1
- package/dist/core/model-config.d.ts.map +1 -1
- package/dist/core/model-config.js +2 -1
- package/dist/core/model-config.js.map +1 -1
- package/dist/core/model-resolver.d.ts +1 -0
- package/dist/core/model-resolver.d.ts.map +1 -1
- package/dist/core/model-resolver.js +12 -6
- package/dist/core/model-resolver.js.map +1 -1
- package/dist/core/models-store.d.ts.map +1 -1
- package/dist/core/models-store.js +2 -1
- package/dist/core/models-store.js.map +1 -1
- package/dist/core/package-manager.d.ts.map +1 -1
- package/dist/core/package-manager.js +11 -6
- package/dist/core/package-manager.js.map +1 -1
- package/dist/core/pi-manifest.d.ts.map +1 -1
- package/dist/core/pi-manifest.js +2 -1
- package/dist/core/pi-manifest.js.map +1 -1
- package/dist/core/prompt-optimizer.d.ts +53 -0
- package/dist/core/prompt-optimizer.d.ts.map +1 -0
- package/dist/core/prompt-optimizer.js +138 -0
- package/dist/core/prompt-optimizer.js.map +1 -0
- package/dist/core/provider-attribution.d.ts.map +1 -1
- package/dist/core/provider-attribution.js +1 -1
- package/dist/core/provider-attribution.js.map +1 -1
- package/dist/core/remote-catalog-provider.d.ts.map +1 -1
- package/dist/core/remote-catalog-provider.js +2 -1
- package/dist/core/remote-catalog-provider.js.map +1 -1
- package/dist/core/resource-loader.d.ts +50 -0
- package/dist/core/resource-loader.d.ts.map +1 -1
- package/dist/core/resource-loader.js +233 -7
- package/dist/core/resource-loader.js.map +1 -1
- package/dist/core/sdk.d.ts +8 -0
- package/dist/core/sdk.d.ts.map +1 -1
- package/dist/core/sdk.js +11 -1
- package/dist/core/sdk.js.map +1 -1
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +2 -1
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/settings-diagnostics.d.ts +9 -0
- package/dist/core/settings-diagnostics.d.ts.map +1 -0
- package/dist/core/settings-diagnostics.js +21 -0
- package/dist/core/settings-diagnostics.js.map +1 -0
- package/dist/core/settings-manager.d.ts +52 -0
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +204 -10
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/skills.d.ts.map +1 -1
- package/dist/core/skills.js +51 -33
- package/dist/core/skills.js.map +1 -1
- package/dist/core/slash-commands.d.ts.map +1 -1
- package/dist/core/slash-commands.js +2 -1
- package/dist/core/slash-commands.js.map +1 -1
- package/dist/core/tools/edit-diff.d.ts +0 -5
- package/dist/core/tools/edit-diff.d.ts.map +1 -1
- package/dist/core/tools/edit-diff.js +2 -5
- package/dist/core/tools/edit-diff.js.map +1 -1
- package/dist/core/tools/edit.d.ts.map +1 -1
- package/dist/core/tools/edit.js +20 -4
- package/dist/core/tools/edit.js.map +1 -1
- package/dist/core/tools/index.d.ts.map +1 -1
- package/dist/core/tools/index.js +2 -2
- package/dist/core/tools/index.js.map +1 -1
- package/dist/core/tools/project-memory-tool.d.ts.map +1 -1
- package/dist/core/tools/project-memory-tool.js +4 -1
- package/dist/core/tools/project-memory-tool.js.map +1 -1
- package/dist/core/trust-manager.d.ts.map +1 -1
- package/dist/core/trust-manager.js +2 -1
- package/dist/core/trust-manager.js.map +1 -1
- package/dist/core/workflows/agent-runner.d.ts +86 -0
- package/dist/core/workflows/agent-runner.d.ts.map +1 -0
- package/dist/core/workflows/agent-runner.js +225 -0
- package/dist/core/workflows/agent-runner.js.map +1 -0
- package/dist/core/workflows/builtins.d.ts +41 -0
- package/dist/core/workflows/builtins.d.ts.map +1 -0
- package/dist/core/workflows/builtins.js +282 -0
- package/dist/core/workflows/builtins.js.map +1 -0
- package/dist/core/workflows/goal-engine/engine.d.ts +134 -0
- package/dist/core/workflows/goal-engine/engine.d.ts.map +1 -0
- package/dist/core/workflows/goal-engine/engine.js +462 -0
- package/dist/core/workflows/goal-engine/engine.js.map +1 -0
- package/dist/core/workflows/goal-engine/errors.d.ts +7 -0
- package/dist/core/workflows/goal-engine/errors.d.ts.map +1 -0
- package/dist/core/workflows/goal-engine/errors.js +9 -0
- package/dist/core/workflows/goal-engine/errors.js.map +1 -0
- package/dist/core/workflows/goal-engine/index.d.ts +13 -0
- package/dist/core/workflows/goal-engine/index.d.ts.map +1 -0
- package/dist/core/workflows/goal-engine/index.js +8 -0
- package/dist/core/workflows/goal-engine/index.js.map +1 -0
- package/dist/core/workflows/goal-engine/persistence.d.ts +31 -0
- package/dist/core/workflows/goal-engine/persistence.d.ts.map +1 -0
- package/dist/core/workflows/goal-engine/persistence.js +145 -0
- package/dist/core/workflows/goal-engine/persistence.js.map +1 -0
- package/dist/core/workflows/goal-engine/prompts.d.ts +37 -0
- package/dist/core/workflows/goal-engine/prompts.d.ts.map +1 -0
- package/dist/core/workflows/goal-engine/prompts.js +131 -0
- package/dist/core/workflows/goal-engine/prompts.js.map +1 -0
- package/dist/core/workflows/goal-engine/state.d.ts +41 -0
- package/dist/core/workflows/goal-engine/state.d.ts.map +1 -0
- package/dist/core/workflows/goal-engine/state.js +397 -0
- package/dist/core/workflows/goal-engine/state.js.map +1 -0
- package/dist/core/workflows/goal-engine/tools.d.ts +31 -0
- package/dist/core/workflows/goal-engine/tools.d.ts.map +1 -0
- package/dist/core/workflows/goal-engine/tools.js +211 -0
- package/dist/core/workflows/goal-engine/tools.js.map +1 -0
- package/dist/core/workflows/goal-engine/types.d.ts +286 -0
- package/dist/core/workflows/goal-engine/types.d.ts.map +1 -0
- package/dist/core/workflows/goal-engine/types.js +17 -0
- package/dist/core/workflows/goal-engine/types.js.map +1 -0
- package/dist/core/workflows/goal-engine/worktree-fingerprint.d.ts +23 -0
- package/dist/core/workflows/goal-engine/worktree-fingerprint.d.ts.map +1 -0
- package/dist/core/workflows/goal-engine/worktree-fingerprint.js +72 -0
- package/dist/core/workflows/goal-engine/worktree-fingerprint.js.map +1 -0
- package/dist/core/workflows/graph-sync.d.ts +67 -0
- package/dist/core/workflows/graph-sync.d.ts.map +1 -0
- package/dist/core/workflows/graph-sync.js +188 -0
- package/dist/core/workflows/graph-sync.js.map +1 -0
- package/dist/core/workflows/graph.d.ts +143 -0
- package/dist/core/workflows/graph.d.ts.map +1 -0
- package/dist/core/workflows/graph.js +319 -0
- package/dist/core/workflows/graph.js.map +1 -0
- package/dist/core/workflows/index.d.ts +18 -0
- package/dist/core/workflows/index.d.ts.map +1 -0
- package/dist/core/workflows/index.js +14 -0
- package/dist/core/workflows/index.js.map +1 -0
- package/dist/core/workflows/journal.d.ts +68 -0
- package/dist/core/workflows/journal.d.ts.map +1 -0
- package/dist/core/workflows/journal.js +83 -0
- package/dist/core/workflows/journal.js.map +1 -0
- package/dist/core/workflows/manager.d.ts +112 -0
- package/dist/core/workflows/manager.d.ts.map +1 -0
- package/dist/core/workflows/manager.js +327 -0
- package/dist/core/workflows/manager.js.map +1 -0
- package/dist/core/workflows/resolve-script.d.ts +35 -0
- package/dist/core/workflows/resolve-script.d.ts.map +1 -0
- package/dist/core/workflows/resolve-script.js +66 -0
- package/dist/core/workflows/resolve-script.js.map +1 -0
- package/dist/core/workflows/runtime.d.ts +49 -0
- package/dist/core/workflows/runtime.d.ts.map +1 -0
- package/dist/core/workflows/runtime.js +301 -0
- package/dist/core/workflows/runtime.js.map +1 -0
- package/dist/core/workflows/shared-store.d.ts +31 -0
- package/dist/core/workflows/shared-store.d.ts.map +1 -0
- package/dist/core/workflows/shared-store.js +80 -0
- package/dist/core/workflows/shared-store.js.map +1 -0
- package/dist/core/workflows/subagent-host.d.ts +50 -0
- package/dist/core/workflows/subagent-host.d.ts.map +1 -0
- package/dist/core/workflows/subagent-host.js +225 -0
- package/dist/core/workflows/subagent-host.js.map +1 -0
- package/dist/core/workflows/tool-workflow.d.ts +22 -0
- package/dist/core/workflows/tool-workflow.d.ts.map +1 -0
- package/dist/core/workflows/tool-workflow.js +120 -0
- package/dist/core/workflows/tool-workflow.js.map +1 -0
- package/dist/core/workflows/types.d.ts +142 -0
- package/dist/core/workflows/types.d.ts.map +1 -0
- package/dist/core/workflows/types.js +26 -0
- package/dist/core/workflows/types.js.map +1 -0
- package/dist/core/workflows/worktree.d.ts +42 -0
- package/dist/core/workflows/worktree.d.ts.map +1 -0
- package/dist/core/workflows/worktree.js +107 -0
- package/dist/core/workflows/worktree.js.map +1 -0
- package/dist/extensions/llama/index.d.ts.map +1 -1
- package/dist/extensions/llama/index.js +2 -0
- package/dist/extensions/llama/index.js.map +1 -1
- package/dist/extensions/llama/provider.d.ts.map +1 -1
- package/dist/extensions/llama/provider.js +6 -2
- package/dist/extensions/llama/provider.js.map +1 -1
- package/dist/index.d.ts +16 -12
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +14 -11
- package/dist/index.js.map +1 -1
- package/dist/main.d.ts +4 -0
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +20 -15
- package/dist/main.js.map +1 -1
- package/dist/migrations.d.ts.map +1 -1
- package/dist/migrations.js +4 -3
- package/dist/migrations.js.map +1 -1
- package/dist/modes/interactive/components/home-screen.d.ts.map +1 -1
- package/dist/modes/interactive/components/home-screen.js +2 -2
- package/dist/modes/interactive/components/home-screen.js.map +1 -1
- package/dist/modes/interactive/components/model-selector.d.ts +2 -3
- package/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/model-selector.js +16 -6
- package/dist/modes/interactive/components/model-selector.js.map +1 -1
- package/dist/modes/interactive/components/prompt-optimization.d.ts +16 -0
- package/dist/modes/interactive/components/prompt-optimization.d.ts.map +1 -0
- package/dist/modes/interactive/components/prompt-optimization.js +46 -0
- package/dist/modes/interactive/components/prompt-optimization.js.map +1 -0
- package/dist/modes/interactive/components/settings-selector.d.ts +19 -1
- package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/settings-selector.js +247 -46
- package/dist/modes/interactive/components/settings-selector.js.map +1 -1
- package/dist/modes/interactive/components/settings-submenu.d.ts +72 -0
- package/dist/modes/interactive/components/settings-submenu.d.ts.map +1 -0
- package/dist/modes/interactive/components/settings-submenu.js +165 -0
- package/dist/modes/interactive/components/settings-submenu.js.map +1 -0
- package/dist/modes/interactive/components/thinking-selector.d.ts +8 -3
- package/dist/modes/interactive/components/thinking-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/thinking-selector.js +29 -3
- package/dist/modes/interactive/components/thinking-selector.js.map +1 -1
- package/dist/modes/interactive/external-editor.d.ts.map +1 -1
- package/dist/modes/interactive/external-editor.js +2 -1
- package/dist/modes/interactive/external-editor.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts +24 -0
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +677 -34
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
- package/dist/modes/interactive/theme/theme.js +4 -3
- package/dist/modes/interactive/theme/theme.js.map +1 -1
- package/dist/modes/json-event.d.ts +16 -13
- package/dist/modes/json-event.d.ts.map +1 -1
- package/dist/modes/json-event.js +20 -6
- package/dist/modes/json-event.js.map +1 -1
- package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-mode.js +173 -0
- package/dist/modes/rpc/rpc-mode.js.map +1 -1
- package/dist/modes/rpc/rpc-types.d.ts +29 -0
- package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-types.js.map +1 -1
- package/dist/package-manager-cli.d.ts.map +1 -1
- package/dist/package-manager-cli.js +1 -1
- package/dist/package-manager-cli.js.map +1 -1
- package/dist/skills/ada-engine/SKILL.md +153 -0
- package/dist/utils/frontmatter.d.ts.map +1 -1
- package/dist/utils/frontmatter.js +2 -1
- package/dist/utils/frontmatter.js.map +1 -1
- package/dist/utils/management-http.d.ts +5 -3
- package/dist/utils/management-http.d.ts.map +1 -1
- package/dist/utils/management-http.js +14 -10
- package/dist/utils/management-http.js.map +1 -1
- package/dist/utils/text.d.ts +8 -0
- package/dist/utils/text.d.ts.map +1 -0
- package/dist/utils/text.js +9 -0
- package/dist/utils/text.js.map +1 -0
- package/docs/compaction.md +15 -0
- package/docs/custom-provider.md +5 -2
- package/docs/extensions.md +11 -2
- package/docs/json.md +7 -2
- package/docs/llama-cpp.md +2 -0
- package/docs/models.md +8 -2
- package/docs/rpc.md +9 -3
- package/docs/settings.md +2 -2
- package/docs/skills.md +4 -3
- package/examples/extensions/border-status-editor.ts +1 -1
- package/examples/extensions/git-checkpoint.ts +2 -2
- package/examples/extensions/notify.ts +3 -1
- package/examples/extensions/subagent/README.md +1 -1
- package/examples/extensions/subagent/index.ts +6 -1
- package/examples/extensions/titlebar-spinner.ts +1 -1
- package/examples/rpc-extension-ui.ts +1 -1
- package/examples/sdk/06-extensions.ts +1 -1
- package/examples/sdk/README.md +1 -1
- package/npm-shrinkwrap.json +2 -12
- package/package.json +3 -3
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"home-screen.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/home-screen.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,SAAS,EAAE,SAAS,EAAwD,MAAM,wBAAwB,CAAC;AACzH,OAAO,KAAK,EAAE,WAAW,EAAE,mBAAmB,EAAE,MAAM,kCAAkC,CAAC;AAKzF,MAAM,MAAM,gBAAgB,GAAG;IAAE,IAAI,EAAE,KAAK,CAAA;CAAE,GAAG;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAAC;AAEzF,KAAK,cAAc,GAAG,CAAC,UAAU,CAAC,EAAE,mBAAmB,KAAK,OAAO,CAAC,WAAW,EAAE,CAAC,CAAC;AAwDnF;;;;;GAKG;AACH,qBAAa,mBAAoB,SAAQ,SAAU,YAAW,SAAS;IACtE,OAAO,CAAC,KAAK,CAA6B;IAC1C,OAAO,CAAC,QAAQ,CAAqB;IACrC,OAAO,CAAC,QAAQ,CAAqB;IACrC,OAAO,CAAC,aAAa,CAAK;IAC1B,OAAO,CAAC,KAAK,CAAM;IACnB,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,eAAe,CAAuB;IAC9C,OAAO,CAAC,aAAa,CAAS;IAC9B,OAAO,CAAC,WAAW,CAAQ;IAC3B,OAAO,CAAC,YAAY,CAAiB;IACrC,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,UAAU,CAAuB;IACzC,OAAO,CAAC,aAAa,CAAuB;IAC5C,OAAO,CAAC,aAAa,CAAQ;IAC7B,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,eAAe,CAAwB;IAExC,KAAK,CAAC,EAAE,MAAM,IAAI,CAAC;IACnB,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;IAC7B,kEAAkE;IAC3D,WAAW,CAAC,EAAE,MAAM,IAAI,CAAC;IAEhC,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAExC;IAED,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAE/B;IAED,gBAAgB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAEvC;IAED,YAAY,YAAY,EAAE,cAAc,EAAE,OAAO,EAAE,MAAM,EAMxD;IAGD,IAAI,OAAO,IAAI,OAAO,CAErB;IACD,IAAI,OAAO,CAAC,KAAK,EAAE,OAAO,EAGzB;IAEK,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC,CAW5B;IAED,OAAO,CAAC,WAAW;IAQnB,OAAO,CAAC,gBAAgB;IAaxB,OAAO,CAAC,SAAS;YAQH,WAAW;IAczB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAkD9B;IAED,UAAU,IAAI,IAAI,CAAG;IAErB,gBAAgB,CAAC,EAAE,EAAE,MAAM,IAAI,GAAG,IAAI,CAErC;IAED,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CA2H9B;IAED,8DAA8D;IAC9D,OAAO,CAAC,YAAY;IAOpB,OAAO,CAAC,iBAAiB;IAIzB,OAAO,CAAC,QAAQ;IAIhB,OAAO,CAAC,WAAW;CAGnB","sourcesContent":["import { type Component, Container, Input, getKeybindings, truncateToWidth, visibleWidth } from \"@earendil-works/pi-tui\";\nimport type { SessionInfo, SessionListProgress } from \"../../../core/session-manager.ts\";\nimport { checkForNewPiVersion } from \"../../../utils/version-check.ts\";\nimport { theme } from \"../theme/theme.ts\";\nimport { filterAndSortSessions } from \"./session-selector-search.ts\";\n\nexport type HomeScreenResult = { kind: \"new\" } | { kind: \"resume\"; path: string } | null;\n\ntype SessionsLoader = (onProgress?: SessionListProgress) => Promise<SessionInfo[]>;\n\nconst ASCII_LOGO = [\n\t\" █████╗ ██████╗ █████╗\",\n\t\" ██╔══██╗██╔══██╗██╔══██╗\",\n\t\" ███████║██║ ██║███████║\",\n\t\" ██╔══██║██║ ██║██╔══██║\",\n\t\" ██║ ██║██████╔╝██║ ██║\",\n\t\" ╚═╝ ╚═╝╚═════╝ ╚═╝ ╚═╝\",\n].join(\"\\n\");\n\n/** Vertical teal-to-gray gradient for the ASCII logo. */\nconst LOGO_GRADIENT = [\"accent\", \"accent\", \"borderAccent\", \"border\", \"borderMuted\", \"muted\"] as const;\n\n/** Draw a rounded box around centered content with an optional title. */\nfunction box(\n\tcontentLines: string[],\n\twidth: number,\n\toptions: { title?: string; color?: (str: string) => string } = {},\n): string[] {\n\tconst color = options.color ?? ((str: string) => theme.fg(\"border\", str));\n\tconst innerWidth = Math.max(10, width - 4);\n\tconst top =\n\t\toptions.title !== undefined\n\t\t\t? color(`╭─ ${options.title} `) + \"─\".repeat(Math.max(1, innerWidth - options.title.length - 2)) + color(\"─╮\")\n\t\t\t: color(`╭${\"─\".repeat(innerWidth)}╮`);\n\tconst bottom = color(`╰${\"─\".repeat(innerWidth)}╯`);\n\tconst pad = (line: string) => {\n\t\tconst visible = visibleWidth(line);\n\t\tconst left = Math.max(0, Math.floor((innerWidth - visible) / 2));\n\t\tconst right = Math.max(0, innerWidth - visible - left);\n\t\treturn color(\"│ \") + \" \".repeat(left) + line + \" \".repeat(right) + color(\" │\");\n\t};\n\treturn [top, ...contentLines.map(pad), bottom];\n}\n\nfunction sessionTitle(session: SessionInfo): string {\n\tif (session.name) return session.name;\n\tconst first = (session.firstMessage ?? \"\").replace(/\\s+/g, \" \").trim();\n\tif (first.length > 0) return first.length > 60 ? `${first.slice(0, 60)}…` : first;\n\treturn \"Conversation\";\n}\n\nfunction relativeTime(iso: string): string {\n\tconst diffMs = Date.now() - new Date(iso).getTime();\n\tconst mins = Math.floor(diffMs / 60000);\n\tif (mins < 1) return \"now\";\n\tif (mins < 60) return `${mins}m`;\n\tconst hours = Math.floor(mins / 60);\n\tif (hours < 24) return `${hours}h`;\n\tconst days = Math.floor(hours / 24);\n\tif (days < 7) return `${days}d`;\n\tif (days < 30) return `${Math.floor(days / 7)}w`;\n\treturn `${Math.floor(days / 30)}mo`;\n}\n\n/**\n * Central welcome flow (opencode style) shown when the CLI starts:\n * 1) entry screen: \"Enter as guest\" (login is disabled for now)\n * 2) home screen: welcome banner, update notice, recent conversations and\n * a \"New conversation\" action.\n */\nexport class HomeScreenComponent extends Container implements Component {\n\tprivate phase: \"login\" | \"home\" = \"login\";\n\tprivate sessions: SessionInfo[] = [];\n\tprivate filtered: SessionInfo[] = [];\n\tprivate selectedIndex = 0; // 0 = \"New conversation\", then sessions\n\tprivate query = \"\";\n\tprivate loading = false;\n\tprivate updateAvailable: string | null = null;\n\tprivate updateChecked = false;\n\tprivate searchInput: Input;\n\tprivate loadSessions: SessionsLoader;\n\tprivate version: string;\n\tprivate modelLabel: string | null = null;\n\tprivate cwdLabelValue: string | null = null;\n\tprivate memoryEnabled = true;\n\tprivate _focused = false;\n\tprivate requestRenderFn: () => void = () => {};\n\n\tpublic onNew?: () => void;\n\tpublic onResume?: (path: string) => void;\n\tpublic onExit?: () => void;\n\tpublic onUpdate?: () => void;\n\t/** Fired when the entry screen transitions to the home screen. */\n\tpublic onEnterHome?: () => void;\n\n\tsetModelLabel(label: string | null): void {\n\t\tthis.modelLabel = label;\n\t}\n\n\tsetCwdLabel(label: string): void {\n\t\tthis.cwdLabelValue = label;\n\t}\n\n\tsetMemoryEnabled(enabled: boolean): void {\n\t\tthis.memoryEnabled = enabled;\n\t}\n\n\tconstructor(loadSessions: SessionsLoader, version: string) {\n\t\tsuper();\n\t\tthis.loadSessions = loadSessions;\n\t\tthis.version = version;\n\t\tthis.searchInput = new Input();\n\t\tthis.searchInput.onSubmit = () => this.confirmSelection();\n\t}\n\n\t// Focusable\n\tget focused(): boolean {\n\t\treturn this._focused;\n\t}\n\tset focused(value: boolean) {\n\t\tthis._focused = value;\n\t\tthis.searchInput.focused = value;\n\t}\n\n\tasync reload(): Promise<void> {\n\t\tthis.loading = true;\n\t\tthis.requestRenderFn();\n\t\ttry {\n\t\t\tthis.sessions = await this.loadSessions();\n\t\t} catch {\n\t\t\tthis.sessions = [];\n\t\t}\n\t\tthis.loading = false;\n\t\tthis.applyFilter();\n\t\tthis.requestRenderFn();\n\t}\n\n\tprivate applyFilter(): void {\n\t\tconst q = this.query.trim();\n\t\tthis.filtered = q\n\t\t\t? filterAndSortSessions(this.sessions, q, \"recent\", \"all\")\n\t\t\t: [...this.sessions].sort((a, b) => b.modified.getTime() - a.modified.getTime());\n\t\tthis.selectedIndex = Math.min(this.selectedIndex, this.filtered.length);\n\t}\n\n\tprivate confirmSelection(): void {\n\t\tif (this.phase === \"login\") {\n\t\t\tthis.enterHome();\n\t\t\treturn;\n\t\t}\n\t\tif (this.selectedIndex === 0) {\n\t\t\tthis.onNew?.();\n\t\t} else {\n\t\t\tconst session = this.filtered[this.selectedIndex - 1];\n\t\t\tif (session) this.onResume?.(session.path);\n\t\t}\n\t}\n\n\tprivate enterHome(): void {\n\t\tthis.phase = \"home\";\n\t\tthis.selectedIndex = 0;\n\t\tthis.onEnterHome?.();\n\t\tvoid this.reload();\n\t\tvoid this.checkUpdate();\n\t}\n\n\tprivate async checkUpdate(): Promise<void> {\n\t\tif (this.updateChecked) return;\n\t\tthis.updateChecked = true;\n\t\ttry {\n\t\t\tconst release = await checkForNewPiVersion(this.version);\n\t\t\tif (release) {\n\t\t\t\tthis.updateAvailable = release.version;\n\t\t\t\tthis.requestRenderFn();\n\t\t\t}\n\t\t} catch {\n\t\t\t// Offline or unreachable — ignore silently.\n\t\t}\n\t}\n\n\thandleInput(data: string): void {\n\t\tconst kb = getKeybindings();\n\t\tif (this.phase === \"login\") {\n\t\t\tif (kb.matches(data, \"tui.select.confirm\") || data === \"\\r\" || data === \"\\n\") {\n\t\t\t\tthis.enterHome();\n\t\t\t} else if (kb.matches(data, \"tui.select.cancel\") || data === \"q\" || data === \"Q\") {\n\t\t\t\tthis.onExit?.();\n\t\t\t} else if (kb.matches(data, \"tui.select.down\")) {\n\t\t\t\t// Only one actionable option on the login screen; ignore.\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\tif (kb.matches(data, \"tui.select.up\")) {\n\t\t\tthis.selectedIndex = Math.max(0, this.selectedIndex - 1);\n\t\t\tthis.requestRenderFn();\n\t\t\treturn;\n\t\t}\n\t\tif (kb.matches(data, \"tui.select.down\")) {\n\t\t\tthis.selectedIndex = Math.min(this.filtered.length, this.selectedIndex + 1);\n\t\t\tthis.requestRenderFn();\n\t\t\treturn;\n\t\t}\n\t\tif (kb.matches(data, \"tui.select.confirm\") || data === \"\\r\" || data === \"\\n\") {\n\t\t\tthis.confirmSelection();\n\t\t\treturn;\n\t\t}\n\t\tif (kb.matches(data, \"tui.select.cancel\")) {\n\t\t\tthis.onExit?.();\n\t\t\treturn;\n\t\t}\n\t\tif (data === \"n\" || data === \"N\") {\n\t\t\tthis.onNew?.();\n\t\t\treturn;\n\t\t}\n\t\tif (data === \"u\" || data === \"U\") {\n\t\t\tif (this.updateAvailable) {\n\t\t\t\tthis.onUpdate?.();\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\tif (data === \"q\" || data === \"Q\") {\n\t\t\tthis.onExit?.();\n\t\t\treturn;\n\t\t}\n\t\t// Anything else goes to the search input (filters sessions)\n\t\tthis.searchInput.handleInput(data);\n\t\tthis.query = this.searchInput.getValue();\n\t\tthis.applyFilter();\n\t\tthis.requestRenderFn();\n\t}\n\n\tinvalidate(): void {}\n\n\tsetRequestRender(fn: () => void): void {\n\t\tthis.requestRenderFn = fn;\n\t}\n\n\trender(width: number): string[] {\n\t\tconst lines: string[] = [];\n\t\tconst center = (text: string) => {\n\t\t\tconst pad = Math.max(0, Math.floor((width - visibleWidth(text)) / 2));\n\t\t\treturn \" \".repeat(pad) + text;\n\t\t};\n\t\tconst dim = (t: string) => theme.fg(\"dim\", t);\n\t\tconst muted = (t: string) => theme.fg(\"muted\", t);\n\t\tconst accent = (t: string) => theme.fg(\"accent\", t);\n\t\tconst border = (t: string) => theme.fg(\"border\", t);\n\t\tconst success = (t: string) => theme.fg(\"success\", t);\n\t\tconst warning = (t: string) => theme.fg(\"warning\", t);\n\n\t\t// Gradient logo (teal -> gray)\n\t\tconst logoLines = ASCII_LOGO.split(\"\\n\").map((line, i) =>\n\t\t\ttheme.fg(LOGO_GRADIENT[Math.min(i, LOGO_GRADIENT.length - 1)], line),\n\t\t);\n\t\tconst tagline =\n\t\t\taccent(\"Ada\") + dim(\" · coding agent · HOILAB TECH\") + muted(` v${this.version}`);\n\n\t\tlines.push(\"\");\n\n\t\tif (this.phase === \"login\") {\n\t\t\t// ---- Entry screen -------------------------------------------\n\t\t\tconst boxW = Math.min(56, width - 4);\n\t\t\tconst boxLines = box(\n\t\t\t\t[\n\t\t\t\t\t\"\",\n\t\t\t\t\t...logoLines,\n\t\t\t\t\ttagline,\n\t\t\t\t\t\"\",\n\t\t\t\t\tdim(\"Bienvenido a Ada — el agente de codificación de HOILAB TECH\"),\n\t\t\t\t\t\"\",\n\t\t\t\t\taccent(\"▸ \") + theme.bold(\"Entrar como invitado\"),\n\t\t\t\t\tmuted(\" Loguearme\") + dim(\" (próximamente)\"),\n\t\t\t\t\t\"\",\n\t\t\t\t],\n\t\t\t\tboxW,\n\t\t\t\t{ title: \" ✦ HOILAB TECH ✦ \" },\n\t\t\t);\n\t\t\tlines.push(...boxLines.map((l) => center(l)));\n\t\t\tlines.push(\"\");\n\t\t\tlines.push(center(success(\"●\") + dim(\" En línea · sin conexión a cuentas\") + muted(\" · todo queda en tu máquina\")));\n\t\t\tlines.push(\"\");\n\t\t\tlines.push(\n\t\t\t\tcenter(muted([\"Enter para continuar\", \"q para salir\"].join(\" · \"))),\n\t\t\t);\n\t\t\treturn this.finishRender(lines, width);\n\t\t}\n\n\t\t// ---- Home screen ------------------------------------------------\n\t\tconst boxW = Math.min(width - 4, 72);\n\n\t\t// Header box: logo + status\n\t\tconst statusParts = [\n\t\t\tdim(`model: ${this.defaultModelLabel()}`),\n\t\t\tdim(`cwd: ${this.cwdLabel()}`),\n\t\t\tdim(`memoria: ${this.memoryLabel()}`),\n\t\t\t\"\",\n\t\t];\n\t\tif (this.updateAvailable) {\n\t\t\tstatusParts.push(\n\t\t\t\twarning(\n\t\t\t\t\t`⚠ Nueva versión disponible: v${this.updateAvailable} ` +\n\t\t\t\t\t\taccent(\"[u] actualizar\") +\n\t\t\t\t\t\tdim(\" · q para ignorar\"),\n\t\t\t\t),\n\t\t\t);\n\t\t} else if (this.updateChecked) {\n\t\t\tstatusParts.push(success(\"✓ Sistema actualizado\"));\n\t\t}\n\t\tlines.push(...box([\"\", ...logoLines, tagline, \"\", ...statusParts], boxW, { title: \" ✦ HOILAB TECH ✦ \" }).map((l) => center(l)));\n\t\tlines.push(\"\");\n\n\t\t// Conversations box\n\t\tconst convLines: string[] = [\"\"];\n\t\tconst isNew = this.selectedIndex === 0;\n\t\tconvLines.push(\n\t\t\t`${isNew ? accent(\"▸ \") : \" \"}${accent(\"✦\")} ${isNew ? theme.bold(\"Nueva conversación\") : \"Nueva conversación\"}`,\n\t\t);\n\t\tconvLines.push(\"\");\n\n\t\tif (this.loading) {\n\t\t\tconvLines.push(dim(\"Cargando conversaciones…\"));\n\t\t} else if (this.filtered.length === 0) {\n\t\t\tconvLines.push(\n\t\t\t\tmuted(\n\t\t\t\t\tthis.query\n\t\t\t\t\t\t? \"No hay conversaciones que coincidan con tu búsqueda\"\n\t\t\t\t\t\t: \"Todavía no hay conversaciones anteriores\",\n\t\t\t\t),\n\t\t\t);\n\t\t} else {\n\t\t\tconst maxVisible = Math.max(1, Math.min(12, Math.floor((width * 2) / 3)));\n\t\t\tconst visible = this.filtered.slice(0, maxVisible);\n\t\t\tfor (let i = 0; i < visible.length; i++) {\n\t\t\t\tconst session = visible[i];\n\t\t\t\tconst isSelected = this.selectedIndex === i + 1;\n\t\t\t\tconst title = sessionTitle(session);\n\t\t\t\tconst meta = muted(\n\t\t\t\t\t`${relativeTime(session.modified.toISOString())} · ${session.messageCount} msgs`,\n\t\t\t\t);\n\t\t\t\tconst arrow = isSelected ? accent(\"▸ \") : \" \";\n\t\t\t\tconst titleStyled = isSelected ? theme.bold(accent(title)) : title;\n\t\t\t\tconvLines.push(`${arrow}${titleStyled} ${meta}`);\n\t\t\t}\n\t\t\tif (this.filtered.length > maxVisible) {\n\t\t\t\tconvLines.push(dim(`… y ${this.filtered.length - maxVisible} más`));\n\t\t\t}\n\t\t}\n\t\tconvLines.push(\"\");\n\t\tlines.push(...box(convLines, boxW, { title: \" Conversaciones \" }).map((l) => center(l)));\n\t\tlines.push(\"\");\n\n\t\tconst hints = [\n\t\t\t\"↑/↓ navegar\",\n\t\t\t\"Enter seleccionar\",\n\t\t\t\"n nueva\",\n\t\t\tthis.updateAvailable ? \"u actualizar\" : null,\n\t\t\t\"q salir\",\n\t\t].filter((h): h is string => h !== null);\n\t\tlines.push(center(muted(hints.join(\" · \"))));\n\t\treturn this.finishRender(lines, width);\n\t}\n\n\t/** Vertical centering + truncation of every rendered line. */\n\tprivate finishRender(lines: string[], width: number): string[] {\n\t\tconst rows = process.stdout.rows ?? 24;\n\t\tconst contentHeight = lines.length;\n\t\tconst topPad = Math.min(3, Math.max(0, Math.floor((rows - contentHeight) / 3)));\n\t\treturn [...Array<string>(topPad).fill(\"\"), ...lines.map((line) => truncateToWidth(line, width))];\n\t}\n\n\tprivate defaultModelLabel(): string {\n\t\treturn this.modelLabel ?? \"no configurado (/model)\";\n\t}\n\n\tprivate cwdLabel(): string {\n\t\treturn this.cwdLabelValue ?? process.cwd();\n\t}\n\n\tprivate memoryLabel(): string {\n\t\treturn this.memoryEnabled ? \"activada\" : \"desactivada\";\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"home-screen.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/home-screen.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,KAAK,SAAS,EACd,SAAS,EAKT,MAAM,wBAAwB,CAAC;AAChC,OAAO,KAAK,EAAE,WAAW,EAAE,mBAAmB,EAAE,MAAM,kCAAkC,CAAC;AAKzF,MAAM,MAAM,gBAAgB,GAAG;IAAE,IAAI,EAAE,KAAK,CAAA;CAAE,GAAG;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAAC;AAEzF,KAAK,cAAc,GAAG,CAAC,UAAU,CAAC,EAAE,mBAAmB,KAAK,OAAO,CAAC,WAAW,EAAE,CAAC,CAAC;AAwDnF;;;;;GAKG;AACH,qBAAa,mBAAoB,SAAQ,SAAU,YAAW,SAAS;IACtE,OAAO,CAAC,KAAK,CAA6B;IAC1C,OAAO,CAAC,QAAQ,CAAqB;IACrC,OAAO,CAAC,QAAQ,CAAqB;IACrC,OAAO,CAAC,aAAa,CAAK;IAC1B,OAAO,CAAC,KAAK,CAAM;IACnB,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,eAAe,CAAuB;IAC9C,OAAO,CAAC,aAAa,CAAS;IAC9B,OAAO,CAAC,WAAW,CAAQ;IAC3B,OAAO,CAAC,YAAY,CAAiB;IACrC,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,UAAU,CAAuB;IACzC,OAAO,CAAC,aAAa,CAAuB;IAC5C,OAAO,CAAC,aAAa,CAAQ;IAC7B,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,eAAe,CAAwB;IAExC,KAAK,CAAC,EAAE,MAAM,IAAI,CAAC;IACnB,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;IAC7B,kEAAkE;IAC3D,WAAW,CAAC,EAAE,MAAM,IAAI,CAAC;IAEhC,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAExC;IAED,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAE/B;IAED,gBAAgB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAEvC;IAED,YAAY,YAAY,EAAE,cAAc,EAAE,OAAO,EAAE,MAAM,EAMxD;IAGD,IAAI,OAAO,IAAI,OAAO,CAErB;IACD,IAAI,OAAO,CAAC,KAAK,EAAE,OAAO,EAGzB;IAEK,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC,CAW5B;IAED,OAAO,CAAC,WAAW;IAQnB,OAAO,CAAC,gBAAgB;IAaxB,OAAO,CAAC,SAAS;YAQH,WAAW;IAczB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAkD9B;IAED,UAAU,IAAI,IAAI,CAAG;IAErB,gBAAgB,CAAC,EAAE,EAAE,MAAM,IAAI,GAAG,IAAI,CAErC;IAED,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CA4H9B;IAED,8DAA8D;IAC9D,OAAO,CAAC,YAAY;IAOpB,OAAO,CAAC,iBAAiB;IAIzB,OAAO,CAAC,QAAQ;IAIhB,OAAO,CAAC,WAAW;CAGnB","sourcesContent":["import {\n\ttype Component,\n\tContainer,\n\tgetKeybindings,\n\tInput,\n\ttruncateToWidth,\n\tvisibleWidth,\n} from \"@earendil-works/pi-tui\";\nimport type { SessionInfo, SessionListProgress } from \"../../../core/session-manager.ts\";\nimport { checkForNewPiVersion } from \"../../../utils/version-check.ts\";\nimport { theme } from \"../theme/theme.ts\";\nimport { filterAndSortSessions } from \"./session-selector-search.ts\";\n\nexport type HomeScreenResult = { kind: \"new\" } | { kind: \"resume\"; path: string } | null;\n\ntype SessionsLoader = (onProgress?: SessionListProgress) => Promise<SessionInfo[]>;\n\nconst ASCII_LOGO = [\n\t\" █████╗ ██████╗ █████╗\",\n\t\" ██╔══██╗██╔══██╗██╔══██╗\",\n\t\" ███████║██║ ██║███████║\",\n\t\" ██╔══██║██║ ██║██╔══██║\",\n\t\" ██║ ██║██████╔╝██║ ██║\",\n\t\" ╚═╝ ╚═╝╚═════╝ ╚═╝ ╚═╝\",\n].join(\"\\n\");\n\n/** Vertical teal-to-gray gradient for the ASCII logo. */\nconst LOGO_GRADIENT = [\"accent\", \"accent\", \"borderAccent\", \"border\", \"borderMuted\", \"muted\"] as const;\n\n/** Draw a rounded box around centered content with an optional title. */\nfunction box(\n\tcontentLines: string[],\n\twidth: number,\n\toptions: { title?: string; color?: (str: string) => string } = {},\n): string[] {\n\tconst color = options.color ?? ((str: string) => theme.fg(\"border\", str));\n\tconst innerWidth = Math.max(10, width - 4);\n\tconst top =\n\t\toptions.title !== undefined\n\t\t\t? color(`╭─ ${options.title} `) + \"─\".repeat(Math.max(1, innerWidth - options.title.length - 2)) + color(\"─╮\")\n\t\t\t: color(`╭${\"─\".repeat(innerWidth)}╮`);\n\tconst bottom = color(`╰${\"─\".repeat(innerWidth)}╯`);\n\tconst pad = (line: string) => {\n\t\tconst visible = visibleWidth(line);\n\t\tconst left = Math.max(0, Math.floor((innerWidth - visible) / 2));\n\t\tconst right = Math.max(0, innerWidth - visible - left);\n\t\treturn color(\"│ \") + \" \".repeat(left) + line + \" \".repeat(right) + color(\" │\");\n\t};\n\treturn [top, ...contentLines.map(pad), bottom];\n}\n\nfunction sessionTitle(session: SessionInfo): string {\n\tif (session.name) return session.name;\n\tconst first = (session.firstMessage ?? \"\").replace(/\\s+/g, \" \").trim();\n\tif (first.length > 0) return first.length > 60 ? `${first.slice(0, 60)}…` : first;\n\treturn \"Conversation\";\n}\n\nfunction relativeTime(iso: string): string {\n\tconst diffMs = Date.now() - new Date(iso).getTime();\n\tconst mins = Math.floor(diffMs / 60000);\n\tif (mins < 1) return \"now\";\n\tif (mins < 60) return `${mins}m`;\n\tconst hours = Math.floor(mins / 60);\n\tif (hours < 24) return `${hours}h`;\n\tconst days = Math.floor(hours / 24);\n\tif (days < 7) return `${days}d`;\n\tif (days < 30) return `${Math.floor(days / 7)}w`;\n\treturn `${Math.floor(days / 30)}mo`;\n}\n\n/**\n * Central welcome flow (opencode style) shown when the CLI starts:\n * 1) entry screen: \"Enter as guest\" (login is disabled for now)\n * 2) home screen: welcome banner, update notice, recent conversations and\n * a \"New conversation\" action.\n */\nexport class HomeScreenComponent extends Container implements Component {\n\tprivate phase: \"login\" | \"home\" = \"login\";\n\tprivate sessions: SessionInfo[] = [];\n\tprivate filtered: SessionInfo[] = [];\n\tprivate selectedIndex = 0; // 0 = \"New conversation\", then sessions\n\tprivate query = \"\";\n\tprivate loading = false;\n\tprivate updateAvailable: string | null = null;\n\tprivate updateChecked = false;\n\tprivate searchInput: Input;\n\tprivate loadSessions: SessionsLoader;\n\tprivate version: string;\n\tprivate modelLabel: string | null = null;\n\tprivate cwdLabelValue: string | null = null;\n\tprivate memoryEnabled = true;\n\tprivate _focused = false;\n\tprivate requestRenderFn: () => void = () => {};\n\n\tpublic onNew?: () => void;\n\tpublic onResume?: (path: string) => void;\n\tpublic onExit?: () => void;\n\tpublic onUpdate?: () => void;\n\t/** Fired when the entry screen transitions to the home screen. */\n\tpublic onEnterHome?: () => void;\n\n\tsetModelLabel(label: string | null): void {\n\t\tthis.modelLabel = label;\n\t}\n\n\tsetCwdLabel(label: string): void {\n\t\tthis.cwdLabelValue = label;\n\t}\n\n\tsetMemoryEnabled(enabled: boolean): void {\n\t\tthis.memoryEnabled = enabled;\n\t}\n\n\tconstructor(loadSessions: SessionsLoader, version: string) {\n\t\tsuper();\n\t\tthis.loadSessions = loadSessions;\n\t\tthis.version = version;\n\t\tthis.searchInput = new Input();\n\t\tthis.searchInput.onSubmit = () => this.confirmSelection();\n\t}\n\n\t// Focusable\n\tget focused(): boolean {\n\t\treturn this._focused;\n\t}\n\tset focused(value: boolean) {\n\t\tthis._focused = value;\n\t\tthis.searchInput.focused = value;\n\t}\n\n\tasync reload(): Promise<void> {\n\t\tthis.loading = true;\n\t\tthis.requestRenderFn();\n\t\ttry {\n\t\t\tthis.sessions = await this.loadSessions();\n\t\t} catch {\n\t\t\tthis.sessions = [];\n\t\t}\n\t\tthis.loading = false;\n\t\tthis.applyFilter();\n\t\tthis.requestRenderFn();\n\t}\n\n\tprivate applyFilter(): void {\n\t\tconst q = this.query.trim();\n\t\tthis.filtered = q\n\t\t\t? filterAndSortSessions(this.sessions, q, \"recent\", \"all\")\n\t\t\t: [...this.sessions].sort((a, b) => b.modified.getTime() - a.modified.getTime());\n\t\tthis.selectedIndex = Math.min(this.selectedIndex, this.filtered.length);\n\t}\n\n\tprivate confirmSelection(): void {\n\t\tif (this.phase === \"login\") {\n\t\t\tthis.enterHome();\n\t\t\treturn;\n\t\t}\n\t\tif (this.selectedIndex === 0) {\n\t\t\tthis.onNew?.();\n\t\t} else {\n\t\t\tconst session = this.filtered[this.selectedIndex - 1];\n\t\t\tif (session) this.onResume?.(session.path);\n\t\t}\n\t}\n\n\tprivate enterHome(): void {\n\t\tthis.phase = \"home\";\n\t\tthis.selectedIndex = 0;\n\t\tthis.onEnterHome?.();\n\t\tvoid this.reload();\n\t\tvoid this.checkUpdate();\n\t}\n\n\tprivate async checkUpdate(): Promise<void> {\n\t\tif (this.updateChecked) return;\n\t\tthis.updateChecked = true;\n\t\ttry {\n\t\t\tconst release = await checkForNewPiVersion(this.version);\n\t\t\tif (release) {\n\t\t\t\tthis.updateAvailable = release.version;\n\t\t\t\tthis.requestRenderFn();\n\t\t\t}\n\t\t} catch {\n\t\t\t// Offline or unreachable — ignore silently.\n\t\t}\n\t}\n\n\thandleInput(data: string): void {\n\t\tconst kb = getKeybindings();\n\t\tif (this.phase === \"login\") {\n\t\t\tif (kb.matches(data, \"tui.select.confirm\") || data === \"\\r\" || data === \"\\n\") {\n\t\t\t\tthis.enterHome();\n\t\t\t} else if (kb.matches(data, \"tui.select.cancel\") || data === \"q\" || data === \"Q\") {\n\t\t\t\tthis.onExit?.();\n\t\t\t} else if (kb.matches(data, \"tui.select.down\")) {\n\t\t\t\t// Only one actionable option on the login screen; ignore.\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\tif (kb.matches(data, \"tui.select.up\")) {\n\t\t\tthis.selectedIndex = Math.max(0, this.selectedIndex - 1);\n\t\t\tthis.requestRenderFn();\n\t\t\treturn;\n\t\t}\n\t\tif (kb.matches(data, \"tui.select.down\")) {\n\t\t\tthis.selectedIndex = Math.min(this.filtered.length, this.selectedIndex + 1);\n\t\t\tthis.requestRenderFn();\n\t\t\treturn;\n\t\t}\n\t\tif (kb.matches(data, \"tui.select.confirm\") || data === \"\\r\" || data === \"\\n\") {\n\t\t\tthis.confirmSelection();\n\t\t\treturn;\n\t\t}\n\t\tif (kb.matches(data, \"tui.select.cancel\")) {\n\t\t\tthis.onExit?.();\n\t\t\treturn;\n\t\t}\n\t\tif (data === \"n\" || data === \"N\") {\n\t\t\tthis.onNew?.();\n\t\t\treturn;\n\t\t}\n\t\tif (data === \"u\" || data === \"U\") {\n\t\t\tif (this.updateAvailable) {\n\t\t\t\tthis.onUpdate?.();\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\tif (data === \"q\" || data === \"Q\") {\n\t\t\tthis.onExit?.();\n\t\t\treturn;\n\t\t}\n\t\t// Anything else goes to the search input (filters sessions)\n\t\tthis.searchInput.handleInput(data);\n\t\tthis.query = this.searchInput.getValue();\n\t\tthis.applyFilter();\n\t\tthis.requestRenderFn();\n\t}\n\n\tinvalidate(): void {}\n\n\tsetRequestRender(fn: () => void): void {\n\t\tthis.requestRenderFn = fn;\n\t}\n\n\trender(width: number): string[] {\n\t\tconst lines: string[] = [];\n\t\tconst center = (text: string) => {\n\t\t\tconst pad = Math.max(0, Math.floor((width - visibleWidth(text)) / 2));\n\t\t\treturn \" \".repeat(pad) + text;\n\t\t};\n\t\tconst dim = (t: string) => theme.fg(\"dim\", t);\n\t\tconst muted = (t: string) => theme.fg(\"muted\", t);\n\t\tconst accent = (t: string) => theme.fg(\"accent\", t);\n\t\tconst _border = (t: string) => theme.fg(\"border\", t);\n\t\tconst success = (t: string) => theme.fg(\"success\", t);\n\t\tconst warning = (t: string) => theme.fg(\"warning\", t);\n\n\t\t// Gradient logo (teal -> gray)\n\t\tconst logoLines = ASCII_LOGO.split(\"\\n\").map((line, i) =>\n\t\t\ttheme.fg(LOGO_GRADIENT[Math.min(i, LOGO_GRADIENT.length - 1)], line),\n\t\t);\n\t\tconst tagline = accent(\"Ada\") + dim(\" · coding agent · HOILAB TECH\") + muted(` v${this.version}`);\n\n\t\tlines.push(\"\");\n\n\t\tif (this.phase === \"login\") {\n\t\t\t// ---- Entry screen -------------------------------------------\n\t\t\tconst boxW = Math.min(56, width - 4);\n\t\t\tconst boxLines = box(\n\t\t\t\t[\n\t\t\t\t\t\"\",\n\t\t\t\t\t...logoLines,\n\t\t\t\t\ttagline,\n\t\t\t\t\t\"\",\n\t\t\t\t\tdim(\"Bienvenido a Ada — el agente de codificación de HOILAB TECH\"),\n\t\t\t\t\t\"\",\n\t\t\t\t\taccent(\"▸ \") + theme.bold(\"Entrar como invitado\"),\n\t\t\t\t\tmuted(\" Loguearme\") + dim(\" (próximamente)\"),\n\t\t\t\t\t\"\",\n\t\t\t\t],\n\t\t\t\tboxW,\n\t\t\t\t{ title: \" ✦ HOILAB TECH ✦ \" },\n\t\t\t);\n\t\t\tlines.push(...boxLines.map((l) => center(l)));\n\t\t\tlines.push(\"\");\n\t\t\tlines.push(\n\t\t\t\tcenter(success(\"●\") + dim(\" En línea · sin conexión a cuentas\") + muted(\" · todo queda en tu máquina\")),\n\t\t\t);\n\t\t\tlines.push(\"\");\n\t\t\tlines.push(center(muted([\"Enter para continuar\", \"q para salir\"].join(\" · \"))));\n\t\t\treturn this.finishRender(lines, width);\n\t\t}\n\n\t\t// ---- Home screen ------------------------------------------------\n\t\tconst boxW = Math.min(width - 4, 72);\n\n\t\t// Header box: logo + status\n\t\tconst statusParts = [\n\t\t\tdim(`model: ${this.defaultModelLabel()}`),\n\t\t\tdim(`cwd: ${this.cwdLabel()}`),\n\t\t\tdim(`memoria: ${this.memoryLabel()}`),\n\t\t\t\"\",\n\t\t];\n\t\tif (this.updateAvailable) {\n\t\t\tstatusParts.push(\n\t\t\t\twarning(\n\t\t\t\t\t`⚠ Nueva versión disponible: v${this.updateAvailable} ` +\n\t\t\t\t\t\taccent(\"[u] actualizar\") +\n\t\t\t\t\t\tdim(\" · q para ignorar\"),\n\t\t\t\t),\n\t\t\t);\n\t\t} else if (this.updateChecked) {\n\t\t\tstatusParts.push(success(\"✓ Sistema actualizado\"));\n\t\t}\n\t\tlines.push(\n\t\t\t...box([\"\", ...logoLines, tagline, \"\", ...statusParts], boxW, { title: \" ✦ HOILAB TECH ✦ \" }).map((l) =>\n\t\t\t\tcenter(l),\n\t\t\t),\n\t\t);\n\t\tlines.push(\"\");\n\n\t\t// Conversations box\n\t\tconst convLines: string[] = [\"\"];\n\t\tconst isNew = this.selectedIndex === 0;\n\t\tconvLines.push(\n\t\t\t`${isNew ? accent(\"▸ \") : \" \"}${accent(\"✦\")} ${isNew ? theme.bold(\"Nueva conversación\") : \"Nueva conversación\"}`,\n\t\t);\n\t\tconvLines.push(\"\");\n\n\t\tif (this.loading) {\n\t\t\tconvLines.push(dim(\"Cargando conversaciones…\"));\n\t\t} else if (this.filtered.length === 0) {\n\t\t\tconvLines.push(\n\t\t\t\tmuted(\n\t\t\t\t\tthis.query\n\t\t\t\t\t\t? \"No hay conversaciones que coincidan con tu búsqueda\"\n\t\t\t\t\t\t: \"Todavía no hay conversaciones anteriores\",\n\t\t\t\t),\n\t\t\t);\n\t\t} else {\n\t\t\tconst maxVisible = Math.max(1, Math.min(12, Math.floor((width * 2) / 3)));\n\t\t\tconst visible = this.filtered.slice(0, maxVisible);\n\t\t\tfor (let i = 0; i < visible.length; i++) {\n\t\t\t\tconst session = visible[i];\n\t\t\t\tconst isSelected = this.selectedIndex === i + 1;\n\t\t\t\tconst title = sessionTitle(session);\n\t\t\t\tconst meta = muted(`${relativeTime(session.modified.toISOString())} · ${session.messageCount} msgs`);\n\t\t\t\tconst arrow = isSelected ? accent(\"▸ \") : \" \";\n\t\t\t\tconst titleStyled = isSelected ? theme.bold(accent(title)) : title;\n\t\t\t\tconvLines.push(`${arrow}${titleStyled} ${meta}`);\n\t\t\t}\n\t\t\tif (this.filtered.length > maxVisible) {\n\t\t\t\tconvLines.push(dim(`… y ${this.filtered.length - maxVisible} más`));\n\t\t\t}\n\t\t}\n\t\tconvLines.push(\"\");\n\t\tlines.push(...box(convLines, boxW, { title: \" Conversaciones \" }).map((l) => center(l)));\n\t\tlines.push(\"\");\n\n\t\tconst hints = [\n\t\t\t\"↑/↓ navegar\",\n\t\t\t\"Enter seleccionar\",\n\t\t\t\"n nueva\",\n\t\t\tthis.updateAvailable ? \"u actualizar\" : null,\n\t\t\t\"q salir\",\n\t\t].filter((h): h is string => h !== null);\n\t\tlines.push(center(muted(hints.join(\" · \"))));\n\t\treturn this.finishRender(lines, width);\n\t}\n\n\t/** Vertical centering + truncation of every rendered line. */\n\tprivate finishRender(lines: string[], width: number): string[] {\n\t\tconst rows = process.stdout.rows ?? 24;\n\t\tconst contentHeight = lines.length;\n\t\tconst topPad = Math.min(3, Math.max(0, Math.floor((rows - contentHeight) / 3)));\n\t\treturn [...Array<string>(topPad).fill(\"\"), ...lines.map((line) => truncateToWidth(line, width))];\n\t}\n\n\tprivate defaultModelLabel(): string {\n\t\treturn this.modelLabel ?? \"no configurado (/model)\";\n\t}\n\n\tprivate cwdLabel(): string {\n\t\treturn this.cwdLabelValue ?? process.cwd();\n\t}\n\n\tprivate memoryLabel(): string {\n\t\treturn this.memoryEnabled ? \"activada\" : \"desactivada\";\n\t}\n}\n"]}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Container,
|
|
1
|
+
import { Container, getKeybindings, Input, truncateToWidth, visibleWidth, } from "@earendil-works/pi-tui";
|
|
2
2
|
import { checkForNewPiVersion } from "../../../utils/version-check.js";
|
|
3
3
|
import { theme } from "../theme/theme.js";
|
|
4
4
|
import { filterAndSortSessions } from "./session-selector-search.js";
|
|
@@ -227,7 +227,7 @@ export class HomeScreenComponent extends Container {
|
|
|
227
227
|
const dim = (t) => theme.fg("dim", t);
|
|
228
228
|
const muted = (t) => theme.fg("muted", t);
|
|
229
229
|
const accent = (t) => theme.fg("accent", t);
|
|
230
|
-
const
|
|
230
|
+
const _border = (t) => theme.fg("border", t);
|
|
231
231
|
const success = (t) => theme.fg("success", t);
|
|
232
232
|
const warning = (t) => theme.fg("warning", t);
|
|
233
233
|
// Gradient logo (teal -> gray)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"home-screen.js","sourceRoot":"","sources":["../../../../src/modes/interactive/components/home-screen.ts"],"names":[],"mappings":"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","sourcesContent":["import { type Component, Container, Input, getKeybindings, truncateToWidth, visibleWidth } from \"@earendil-works/pi-tui\";\nimport type { SessionInfo, SessionListProgress } from \"../../../core/session-manager.ts\";\nimport { checkForNewPiVersion } from \"../../../utils/version-check.ts\";\nimport { theme } from \"../theme/theme.ts\";\nimport { filterAndSortSessions } from \"./session-selector-search.ts\";\n\nexport type HomeScreenResult = { kind: \"new\" } | { kind: \"resume\"; path: string } | null;\n\ntype SessionsLoader = (onProgress?: SessionListProgress) => Promise<SessionInfo[]>;\n\nconst ASCII_LOGO = [\n\t\" █████╗ ██████╗ █████╗\",\n\t\" ██╔══██╗██╔══██╗██╔══██╗\",\n\t\" ███████║██║ ██║███████║\",\n\t\" ██╔══██║██║ ██║██╔══██║\",\n\t\" ██║ ██║██████╔╝██║ ██║\",\n\t\" ╚═╝ ╚═╝╚═════╝ ╚═╝ ╚═╝\",\n].join(\"\\n\");\n\n/** Vertical teal-to-gray gradient for the ASCII logo. */\nconst LOGO_GRADIENT = [\"accent\", \"accent\", \"borderAccent\", \"border\", \"borderMuted\", \"muted\"] as const;\n\n/** Draw a rounded box around centered content with an optional title. */\nfunction box(\n\tcontentLines: string[],\n\twidth: number,\n\toptions: { title?: string; color?: (str: string) => string } = {},\n): string[] {\n\tconst color = options.color ?? ((str: string) => theme.fg(\"border\", str));\n\tconst innerWidth = Math.max(10, width - 4);\n\tconst top =\n\t\toptions.title !== undefined\n\t\t\t? color(`╭─ ${options.title} `) + \"─\".repeat(Math.max(1, innerWidth - options.title.length - 2)) + color(\"─╮\")\n\t\t\t: color(`╭${\"─\".repeat(innerWidth)}╮`);\n\tconst bottom = color(`╰${\"─\".repeat(innerWidth)}╯`);\n\tconst pad = (line: string) => {\n\t\tconst visible = visibleWidth(line);\n\t\tconst left = Math.max(0, Math.floor((innerWidth - visible) / 2));\n\t\tconst right = Math.max(0, innerWidth - visible - left);\n\t\treturn color(\"│ \") + \" \".repeat(left) + line + \" \".repeat(right) + color(\" │\");\n\t};\n\treturn [top, ...contentLines.map(pad), bottom];\n}\n\nfunction sessionTitle(session: SessionInfo): string {\n\tif (session.name) return session.name;\n\tconst first = (session.firstMessage ?? \"\").replace(/\\s+/g, \" \").trim();\n\tif (first.length > 0) return first.length > 60 ? `${first.slice(0, 60)}…` : first;\n\treturn \"Conversation\";\n}\n\nfunction relativeTime(iso: string): string {\n\tconst diffMs = Date.now() - new Date(iso).getTime();\n\tconst mins = Math.floor(diffMs / 60000);\n\tif (mins < 1) return \"now\";\n\tif (mins < 60) return `${mins}m`;\n\tconst hours = Math.floor(mins / 60);\n\tif (hours < 24) return `${hours}h`;\n\tconst days = Math.floor(hours / 24);\n\tif (days < 7) return `${days}d`;\n\tif (days < 30) return `${Math.floor(days / 7)}w`;\n\treturn `${Math.floor(days / 30)}mo`;\n}\n\n/**\n * Central welcome flow (opencode style) shown when the CLI starts:\n * 1) entry screen: \"Enter as guest\" (login is disabled for now)\n * 2) home screen: welcome banner, update notice, recent conversations and\n * a \"New conversation\" action.\n */\nexport class HomeScreenComponent extends Container implements Component {\n\tprivate phase: \"login\" | \"home\" = \"login\";\n\tprivate sessions: SessionInfo[] = [];\n\tprivate filtered: SessionInfo[] = [];\n\tprivate selectedIndex = 0; // 0 = \"New conversation\", then sessions\n\tprivate query = \"\";\n\tprivate loading = false;\n\tprivate updateAvailable: string | null = null;\n\tprivate updateChecked = false;\n\tprivate searchInput: Input;\n\tprivate loadSessions: SessionsLoader;\n\tprivate version: string;\n\tprivate modelLabel: string | null = null;\n\tprivate cwdLabelValue: string | null = null;\n\tprivate memoryEnabled = true;\n\tprivate _focused = false;\n\tprivate requestRenderFn: () => void = () => {};\n\n\tpublic onNew?: () => void;\n\tpublic onResume?: (path: string) => void;\n\tpublic onExit?: () => void;\n\tpublic onUpdate?: () => void;\n\t/** Fired when the entry screen transitions to the home screen. */\n\tpublic onEnterHome?: () => void;\n\n\tsetModelLabel(label: string | null): void {\n\t\tthis.modelLabel = label;\n\t}\n\n\tsetCwdLabel(label: string): void {\n\t\tthis.cwdLabelValue = label;\n\t}\n\n\tsetMemoryEnabled(enabled: boolean): void {\n\t\tthis.memoryEnabled = enabled;\n\t}\n\n\tconstructor(loadSessions: SessionsLoader, version: string) {\n\t\tsuper();\n\t\tthis.loadSessions = loadSessions;\n\t\tthis.version = version;\n\t\tthis.searchInput = new Input();\n\t\tthis.searchInput.onSubmit = () => this.confirmSelection();\n\t}\n\n\t// Focusable\n\tget focused(): boolean {\n\t\treturn this._focused;\n\t}\n\tset focused(value: boolean) {\n\t\tthis._focused = value;\n\t\tthis.searchInput.focused = value;\n\t}\n\n\tasync reload(): Promise<void> {\n\t\tthis.loading = true;\n\t\tthis.requestRenderFn();\n\t\ttry {\n\t\t\tthis.sessions = await this.loadSessions();\n\t\t} catch {\n\t\t\tthis.sessions = [];\n\t\t}\n\t\tthis.loading = false;\n\t\tthis.applyFilter();\n\t\tthis.requestRenderFn();\n\t}\n\n\tprivate applyFilter(): void {\n\t\tconst q = this.query.trim();\n\t\tthis.filtered = q\n\t\t\t? filterAndSortSessions(this.sessions, q, \"recent\", \"all\")\n\t\t\t: [...this.sessions].sort((a, b) => b.modified.getTime() - a.modified.getTime());\n\t\tthis.selectedIndex = Math.min(this.selectedIndex, this.filtered.length);\n\t}\n\n\tprivate confirmSelection(): void {\n\t\tif (this.phase === \"login\") {\n\t\t\tthis.enterHome();\n\t\t\treturn;\n\t\t}\n\t\tif (this.selectedIndex === 0) {\n\t\t\tthis.onNew?.();\n\t\t} else {\n\t\t\tconst session = this.filtered[this.selectedIndex - 1];\n\t\t\tif (session) this.onResume?.(session.path);\n\t\t}\n\t}\n\n\tprivate enterHome(): void {\n\t\tthis.phase = \"home\";\n\t\tthis.selectedIndex = 0;\n\t\tthis.onEnterHome?.();\n\t\tvoid this.reload();\n\t\tvoid this.checkUpdate();\n\t}\n\n\tprivate async checkUpdate(): Promise<void> {\n\t\tif (this.updateChecked) return;\n\t\tthis.updateChecked = true;\n\t\ttry {\n\t\t\tconst release = await checkForNewPiVersion(this.version);\n\t\t\tif (release) {\n\t\t\t\tthis.updateAvailable = release.version;\n\t\t\t\tthis.requestRenderFn();\n\t\t\t}\n\t\t} catch {\n\t\t\t// Offline or unreachable — ignore silently.\n\t\t}\n\t}\n\n\thandleInput(data: string): void {\n\t\tconst kb = getKeybindings();\n\t\tif (this.phase === \"login\") {\n\t\t\tif (kb.matches(data, \"tui.select.confirm\") || data === \"\\r\" || data === \"\\n\") {\n\t\t\t\tthis.enterHome();\n\t\t\t} else if (kb.matches(data, \"tui.select.cancel\") || data === \"q\" || data === \"Q\") {\n\t\t\t\tthis.onExit?.();\n\t\t\t} else if (kb.matches(data, \"tui.select.down\")) {\n\t\t\t\t// Only one actionable option on the login screen; ignore.\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\tif (kb.matches(data, \"tui.select.up\")) {\n\t\t\tthis.selectedIndex = Math.max(0, this.selectedIndex - 1);\n\t\t\tthis.requestRenderFn();\n\t\t\treturn;\n\t\t}\n\t\tif (kb.matches(data, \"tui.select.down\")) {\n\t\t\tthis.selectedIndex = Math.min(this.filtered.length, this.selectedIndex + 1);\n\t\t\tthis.requestRenderFn();\n\t\t\treturn;\n\t\t}\n\t\tif (kb.matches(data, \"tui.select.confirm\") || data === \"\\r\" || data === \"\\n\") {\n\t\t\tthis.confirmSelection();\n\t\t\treturn;\n\t\t}\n\t\tif (kb.matches(data, \"tui.select.cancel\")) {\n\t\t\tthis.onExit?.();\n\t\t\treturn;\n\t\t}\n\t\tif (data === \"n\" || data === \"N\") {\n\t\t\tthis.onNew?.();\n\t\t\treturn;\n\t\t}\n\t\tif (data === \"u\" || data === \"U\") {\n\t\t\tif (this.updateAvailable) {\n\t\t\t\tthis.onUpdate?.();\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\tif (data === \"q\" || data === \"Q\") {\n\t\t\tthis.onExit?.();\n\t\t\treturn;\n\t\t}\n\t\t// Anything else goes to the search input (filters sessions)\n\t\tthis.searchInput.handleInput(data);\n\t\tthis.query = this.searchInput.getValue();\n\t\tthis.applyFilter();\n\t\tthis.requestRenderFn();\n\t}\n\n\tinvalidate(): void {}\n\n\tsetRequestRender(fn: () => void): void {\n\t\tthis.requestRenderFn = fn;\n\t}\n\n\trender(width: number): string[] {\n\t\tconst lines: string[] = [];\n\t\tconst center = (text: string) => {\n\t\t\tconst pad = Math.max(0, Math.floor((width - visibleWidth(text)) / 2));\n\t\t\treturn \" \".repeat(pad) + text;\n\t\t};\n\t\tconst dim = (t: string) => theme.fg(\"dim\", t);\n\t\tconst muted = (t: string) => theme.fg(\"muted\", t);\n\t\tconst accent = (t: string) => theme.fg(\"accent\", t);\n\t\tconst border = (t: string) => theme.fg(\"border\", t);\n\t\tconst success = (t: string) => theme.fg(\"success\", t);\n\t\tconst warning = (t: string) => theme.fg(\"warning\", t);\n\n\t\t// Gradient logo (teal -> gray)\n\t\tconst logoLines = ASCII_LOGO.split(\"\\n\").map((line, i) =>\n\t\t\ttheme.fg(LOGO_GRADIENT[Math.min(i, LOGO_GRADIENT.length - 1)], line),\n\t\t);\n\t\tconst tagline =\n\t\t\taccent(\"Ada\") + dim(\" · coding agent · HOILAB TECH\") + muted(` v${this.version}`);\n\n\t\tlines.push(\"\");\n\n\t\tif (this.phase === \"login\") {\n\t\t\t// ---- Entry screen -------------------------------------------\n\t\t\tconst boxW = Math.min(56, width - 4);\n\t\t\tconst boxLines = box(\n\t\t\t\t[\n\t\t\t\t\t\"\",\n\t\t\t\t\t...logoLines,\n\t\t\t\t\ttagline,\n\t\t\t\t\t\"\",\n\t\t\t\t\tdim(\"Bienvenido a Ada — el agente de codificación de HOILAB TECH\"),\n\t\t\t\t\t\"\",\n\t\t\t\t\taccent(\"▸ \") + theme.bold(\"Entrar como invitado\"),\n\t\t\t\t\tmuted(\" Loguearme\") + dim(\" (próximamente)\"),\n\t\t\t\t\t\"\",\n\t\t\t\t],\n\t\t\t\tboxW,\n\t\t\t\t{ title: \" ✦ HOILAB TECH ✦ \" },\n\t\t\t);\n\t\t\tlines.push(...boxLines.map((l) => center(l)));\n\t\t\tlines.push(\"\");\n\t\t\tlines.push(center(success(\"●\") + dim(\" En línea · sin conexión a cuentas\") + muted(\" · todo queda en tu máquina\")));\n\t\t\tlines.push(\"\");\n\t\t\tlines.push(\n\t\t\t\tcenter(muted([\"Enter para continuar\", \"q para salir\"].join(\" · \"))),\n\t\t\t);\n\t\t\treturn this.finishRender(lines, width);\n\t\t}\n\n\t\t// ---- Home screen ------------------------------------------------\n\t\tconst boxW = Math.min(width - 4, 72);\n\n\t\t// Header box: logo + status\n\t\tconst statusParts = [\n\t\t\tdim(`model: ${this.defaultModelLabel()}`),\n\t\t\tdim(`cwd: ${this.cwdLabel()}`),\n\t\t\tdim(`memoria: ${this.memoryLabel()}`),\n\t\t\t\"\",\n\t\t];\n\t\tif (this.updateAvailable) {\n\t\t\tstatusParts.push(\n\t\t\t\twarning(\n\t\t\t\t\t`⚠ Nueva versión disponible: v${this.updateAvailable} ` +\n\t\t\t\t\t\taccent(\"[u] actualizar\") +\n\t\t\t\t\t\tdim(\" · q para ignorar\"),\n\t\t\t\t),\n\t\t\t);\n\t\t} else if (this.updateChecked) {\n\t\t\tstatusParts.push(success(\"✓ Sistema actualizado\"));\n\t\t}\n\t\tlines.push(...box([\"\", ...logoLines, tagline, \"\", ...statusParts], boxW, { title: \" ✦ HOILAB TECH ✦ \" }).map((l) => center(l)));\n\t\tlines.push(\"\");\n\n\t\t// Conversations box\n\t\tconst convLines: string[] = [\"\"];\n\t\tconst isNew = this.selectedIndex === 0;\n\t\tconvLines.push(\n\t\t\t`${isNew ? accent(\"▸ \") : \" \"}${accent(\"✦\")} ${isNew ? theme.bold(\"Nueva conversación\") : \"Nueva conversación\"}`,\n\t\t);\n\t\tconvLines.push(\"\");\n\n\t\tif (this.loading) {\n\t\t\tconvLines.push(dim(\"Cargando conversaciones…\"));\n\t\t} else if (this.filtered.length === 0) {\n\t\t\tconvLines.push(\n\t\t\t\tmuted(\n\t\t\t\t\tthis.query\n\t\t\t\t\t\t? \"No hay conversaciones que coincidan con tu búsqueda\"\n\t\t\t\t\t\t: \"Todavía no hay conversaciones anteriores\",\n\t\t\t\t),\n\t\t\t);\n\t\t} else {\n\t\t\tconst maxVisible = Math.max(1, Math.min(12, Math.floor((width * 2) / 3)));\n\t\t\tconst visible = this.filtered.slice(0, maxVisible);\n\t\t\tfor (let i = 0; i < visible.length; i++) {\n\t\t\t\tconst session = visible[i];\n\t\t\t\tconst isSelected = this.selectedIndex === i + 1;\n\t\t\t\tconst title = sessionTitle(session);\n\t\t\t\tconst meta = muted(\n\t\t\t\t\t`${relativeTime(session.modified.toISOString())} · ${session.messageCount} msgs`,\n\t\t\t\t);\n\t\t\t\tconst arrow = isSelected ? accent(\"▸ \") : \" \";\n\t\t\t\tconst titleStyled = isSelected ? theme.bold(accent(title)) : title;\n\t\t\t\tconvLines.push(`${arrow}${titleStyled} ${meta}`);\n\t\t\t}\n\t\t\tif (this.filtered.length > maxVisible) {\n\t\t\t\tconvLines.push(dim(`… y ${this.filtered.length - maxVisible} más`));\n\t\t\t}\n\t\t}\n\t\tconvLines.push(\"\");\n\t\tlines.push(...box(convLines, boxW, { title: \" Conversaciones \" }).map((l) => center(l)));\n\t\tlines.push(\"\");\n\n\t\tconst hints = [\n\t\t\t\"↑/↓ navegar\",\n\t\t\t\"Enter seleccionar\",\n\t\t\t\"n nueva\",\n\t\t\tthis.updateAvailable ? \"u actualizar\" : null,\n\t\t\t\"q salir\",\n\t\t].filter((h): h is string => h !== null);\n\t\tlines.push(center(muted(hints.join(\" · \"))));\n\t\treturn this.finishRender(lines, width);\n\t}\n\n\t/** Vertical centering + truncation of every rendered line. */\n\tprivate finishRender(lines: string[], width: number): string[] {\n\t\tconst rows = process.stdout.rows ?? 24;\n\t\tconst contentHeight = lines.length;\n\t\tconst topPad = Math.min(3, Math.max(0, Math.floor((rows - contentHeight) / 3)));\n\t\treturn [...Array<string>(topPad).fill(\"\"), ...lines.map((line) => truncateToWidth(line, width))];\n\t}\n\n\tprivate defaultModelLabel(): string {\n\t\treturn this.modelLabel ?? \"no configurado (/model)\";\n\t}\n\n\tprivate cwdLabel(): string {\n\t\treturn this.cwdLabelValue ?? process.cwd();\n\t}\n\n\tprivate memoryLabel(): string {\n\t\treturn this.memoryEnabled ? \"activada\" : \"desactivada\";\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"home-screen.js","sourceRoot":"","sources":["../../../../src/modes/interactive/components/home-screen.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n\ttype Component,\n\tContainer,\n\tgetKeybindings,\n\tInput,\n\ttruncateToWidth,\n\tvisibleWidth,\n} from \"@earendil-works/pi-tui\";\nimport type { SessionInfo, SessionListProgress } from \"../../../core/session-manager.ts\";\nimport { checkForNewPiVersion } from \"../../../utils/version-check.ts\";\nimport { theme } from \"../theme/theme.ts\";\nimport { filterAndSortSessions } from \"./session-selector-search.ts\";\n\nexport type HomeScreenResult = { kind: \"new\" } | { kind: \"resume\"; path: string } | null;\n\ntype SessionsLoader = (onProgress?: SessionListProgress) => Promise<SessionInfo[]>;\n\nconst ASCII_LOGO = [\n\t\" █████╗ ██████╗ █████╗\",\n\t\" ██╔══██╗██╔══██╗██╔══██╗\",\n\t\" ███████║██║ ██║███████║\",\n\t\" ██╔══██║██║ ██║██╔══██║\",\n\t\" ██║ ██║██████╔╝██║ ██║\",\n\t\" ╚═╝ ╚═╝╚═════╝ ╚═╝ ╚═╝\",\n].join(\"\\n\");\n\n/** Vertical teal-to-gray gradient for the ASCII logo. */\nconst LOGO_GRADIENT = [\"accent\", \"accent\", \"borderAccent\", \"border\", \"borderMuted\", \"muted\"] as const;\n\n/** Draw a rounded box around centered content with an optional title. */\nfunction box(\n\tcontentLines: string[],\n\twidth: number,\n\toptions: { title?: string; color?: (str: string) => string } = {},\n): string[] {\n\tconst color = options.color ?? ((str: string) => theme.fg(\"border\", str));\n\tconst innerWidth = Math.max(10, width - 4);\n\tconst top =\n\t\toptions.title !== undefined\n\t\t\t? color(`╭─ ${options.title} `) + \"─\".repeat(Math.max(1, innerWidth - options.title.length - 2)) + color(\"─╮\")\n\t\t\t: color(`╭${\"─\".repeat(innerWidth)}╮`);\n\tconst bottom = color(`╰${\"─\".repeat(innerWidth)}╯`);\n\tconst pad = (line: string) => {\n\t\tconst visible = visibleWidth(line);\n\t\tconst left = Math.max(0, Math.floor((innerWidth - visible) / 2));\n\t\tconst right = Math.max(0, innerWidth - visible - left);\n\t\treturn color(\"│ \") + \" \".repeat(left) + line + \" \".repeat(right) + color(\" │\");\n\t};\n\treturn [top, ...contentLines.map(pad), bottom];\n}\n\nfunction sessionTitle(session: SessionInfo): string {\n\tif (session.name) return session.name;\n\tconst first = (session.firstMessage ?? \"\").replace(/\\s+/g, \" \").trim();\n\tif (first.length > 0) return first.length > 60 ? `${first.slice(0, 60)}…` : first;\n\treturn \"Conversation\";\n}\n\nfunction relativeTime(iso: string): string {\n\tconst diffMs = Date.now() - new Date(iso).getTime();\n\tconst mins = Math.floor(diffMs / 60000);\n\tif (mins < 1) return \"now\";\n\tif (mins < 60) return `${mins}m`;\n\tconst hours = Math.floor(mins / 60);\n\tif (hours < 24) return `${hours}h`;\n\tconst days = Math.floor(hours / 24);\n\tif (days < 7) return `${days}d`;\n\tif (days < 30) return `${Math.floor(days / 7)}w`;\n\treturn `${Math.floor(days / 30)}mo`;\n}\n\n/**\n * Central welcome flow (opencode style) shown when the CLI starts:\n * 1) entry screen: \"Enter as guest\" (login is disabled for now)\n * 2) home screen: welcome banner, update notice, recent conversations and\n * a \"New conversation\" action.\n */\nexport class HomeScreenComponent extends Container implements Component {\n\tprivate phase: \"login\" | \"home\" = \"login\";\n\tprivate sessions: SessionInfo[] = [];\n\tprivate filtered: SessionInfo[] = [];\n\tprivate selectedIndex = 0; // 0 = \"New conversation\", then sessions\n\tprivate query = \"\";\n\tprivate loading = false;\n\tprivate updateAvailable: string | null = null;\n\tprivate updateChecked = false;\n\tprivate searchInput: Input;\n\tprivate loadSessions: SessionsLoader;\n\tprivate version: string;\n\tprivate modelLabel: string | null = null;\n\tprivate cwdLabelValue: string | null = null;\n\tprivate memoryEnabled = true;\n\tprivate _focused = false;\n\tprivate requestRenderFn: () => void = () => {};\n\n\tpublic onNew?: () => void;\n\tpublic onResume?: (path: string) => void;\n\tpublic onExit?: () => void;\n\tpublic onUpdate?: () => void;\n\t/** Fired when the entry screen transitions to the home screen. */\n\tpublic onEnterHome?: () => void;\n\n\tsetModelLabel(label: string | null): void {\n\t\tthis.modelLabel = label;\n\t}\n\n\tsetCwdLabel(label: string): void {\n\t\tthis.cwdLabelValue = label;\n\t}\n\n\tsetMemoryEnabled(enabled: boolean): void {\n\t\tthis.memoryEnabled = enabled;\n\t}\n\n\tconstructor(loadSessions: SessionsLoader, version: string) {\n\t\tsuper();\n\t\tthis.loadSessions = loadSessions;\n\t\tthis.version = version;\n\t\tthis.searchInput = new Input();\n\t\tthis.searchInput.onSubmit = () => this.confirmSelection();\n\t}\n\n\t// Focusable\n\tget focused(): boolean {\n\t\treturn this._focused;\n\t}\n\tset focused(value: boolean) {\n\t\tthis._focused = value;\n\t\tthis.searchInput.focused = value;\n\t}\n\n\tasync reload(): Promise<void> {\n\t\tthis.loading = true;\n\t\tthis.requestRenderFn();\n\t\ttry {\n\t\t\tthis.sessions = await this.loadSessions();\n\t\t} catch {\n\t\t\tthis.sessions = [];\n\t\t}\n\t\tthis.loading = false;\n\t\tthis.applyFilter();\n\t\tthis.requestRenderFn();\n\t}\n\n\tprivate applyFilter(): void {\n\t\tconst q = this.query.trim();\n\t\tthis.filtered = q\n\t\t\t? filterAndSortSessions(this.sessions, q, \"recent\", \"all\")\n\t\t\t: [...this.sessions].sort((a, b) => b.modified.getTime() - a.modified.getTime());\n\t\tthis.selectedIndex = Math.min(this.selectedIndex, this.filtered.length);\n\t}\n\n\tprivate confirmSelection(): void {\n\t\tif (this.phase === \"login\") {\n\t\t\tthis.enterHome();\n\t\t\treturn;\n\t\t}\n\t\tif (this.selectedIndex === 0) {\n\t\t\tthis.onNew?.();\n\t\t} else {\n\t\t\tconst session = this.filtered[this.selectedIndex - 1];\n\t\t\tif (session) this.onResume?.(session.path);\n\t\t}\n\t}\n\n\tprivate enterHome(): void {\n\t\tthis.phase = \"home\";\n\t\tthis.selectedIndex = 0;\n\t\tthis.onEnterHome?.();\n\t\tvoid this.reload();\n\t\tvoid this.checkUpdate();\n\t}\n\n\tprivate async checkUpdate(): Promise<void> {\n\t\tif (this.updateChecked) return;\n\t\tthis.updateChecked = true;\n\t\ttry {\n\t\t\tconst release = await checkForNewPiVersion(this.version);\n\t\t\tif (release) {\n\t\t\t\tthis.updateAvailable = release.version;\n\t\t\t\tthis.requestRenderFn();\n\t\t\t}\n\t\t} catch {\n\t\t\t// Offline or unreachable — ignore silently.\n\t\t}\n\t}\n\n\thandleInput(data: string): void {\n\t\tconst kb = getKeybindings();\n\t\tif (this.phase === \"login\") {\n\t\t\tif (kb.matches(data, \"tui.select.confirm\") || data === \"\\r\" || data === \"\\n\") {\n\t\t\t\tthis.enterHome();\n\t\t\t} else if (kb.matches(data, \"tui.select.cancel\") || data === \"q\" || data === \"Q\") {\n\t\t\t\tthis.onExit?.();\n\t\t\t} else if (kb.matches(data, \"tui.select.down\")) {\n\t\t\t\t// Only one actionable option on the login screen; ignore.\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\tif (kb.matches(data, \"tui.select.up\")) {\n\t\t\tthis.selectedIndex = Math.max(0, this.selectedIndex - 1);\n\t\t\tthis.requestRenderFn();\n\t\t\treturn;\n\t\t}\n\t\tif (kb.matches(data, \"tui.select.down\")) {\n\t\t\tthis.selectedIndex = Math.min(this.filtered.length, this.selectedIndex + 1);\n\t\t\tthis.requestRenderFn();\n\t\t\treturn;\n\t\t}\n\t\tif (kb.matches(data, \"tui.select.confirm\") || data === \"\\r\" || data === \"\\n\") {\n\t\t\tthis.confirmSelection();\n\t\t\treturn;\n\t\t}\n\t\tif (kb.matches(data, \"tui.select.cancel\")) {\n\t\t\tthis.onExit?.();\n\t\t\treturn;\n\t\t}\n\t\tif (data === \"n\" || data === \"N\") {\n\t\t\tthis.onNew?.();\n\t\t\treturn;\n\t\t}\n\t\tif (data === \"u\" || data === \"U\") {\n\t\t\tif (this.updateAvailable) {\n\t\t\t\tthis.onUpdate?.();\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\tif (data === \"q\" || data === \"Q\") {\n\t\t\tthis.onExit?.();\n\t\t\treturn;\n\t\t}\n\t\t// Anything else goes to the search input (filters sessions)\n\t\tthis.searchInput.handleInput(data);\n\t\tthis.query = this.searchInput.getValue();\n\t\tthis.applyFilter();\n\t\tthis.requestRenderFn();\n\t}\n\n\tinvalidate(): void {}\n\n\tsetRequestRender(fn: () => void): void {\n\t\tthis.requestRenderFn = fn;\n\t}\n\n\trender(width: number): string[] {\n\t\tconst lines: string[] = [];\n\t\tconst center = (text: string) => {\n\t\t\tconst pad = Math.max(0, Math.floor((width - visibleWidth(text)) / 2));\n\t\t\treturn \" \".repeat(pad) + text;\n\t\t};\n\t\tconst dim = (t: string) => theme.fg(\"dim\", t);\n\t\tconst muted = (t: string) => theme.fg(\"muted\", t);\n\t\tconst accent = (t: string) => theme.fg(\"accent\", t);\n\t\tconst _border = (t: string) => theme.fg(\"border\", t);\n\t\tconst success = (t: string) => theme.fg(\"success\", t);\n\t\tconst warning = (t: string) => theme.fg(\"warning\", t);\n\n\t\t// Gradient logo (teal -> gray)\n\t\tconst logoLines = ASCII_LOGO.split(\"\\n\").map((line, i) =>\n\t\t\ttheme.fg(LOGO_GRADIENT[Math.min(i, LOGO_GRADIENT.length - 1)], line),\n\t\t);\n\t\tconst tagline = accent(\"Ada\") + dim(\" · coding agent · HOILAB TECH\") + muted(` v${this.version}`);\n\n\t\tlines.push(\"\");\n\n\t\tif (this.phase === \"login\") {\n\t\t\t// ---- Entry screen -------------------------------------------\n\t\t\tconst boxW = Math.min(56, width - 4);\n\t\t\tconst boxLines = box(\n\t\t\t\t[\n\t\t\t\t\t\"\",\n\t\t\t\t\t...logoLines,\n\t\t\t\t\ttagline,\n\t\t\t\t\t\"\",\n\t\t\t\t\tdim(\"Bienvenido a Ada — el agente de codificación de HOILAB TECH\"),\n\t\t\t\t\t\"\",\n\t\t\t\t\taccent(\"▸ \") + theme.bold(\"Entrar como invitado\"),\n\t\t\t\t\tmuted(\" Loguearme\") + dim(\" (próximamente)\"),\n\t\t\t\t\t\"\",\n\t\t\t\t],\n\t\t\t\tboxW,\n\t\t\t\t{ title: \" ✦ HOILAB TECH ✦ \" },\n\t\t\t);\n\t\t\tlines.push(...boxLines.map((l) => center(l)));\n\t\t\tlines.push(\"\");\n\t\t\tlines.push(\n\t\t\t\tcenter(success(\"●\") + dim(\" En línea · sin conexión a cuentas\") + muted(\" · todo queda en tu máquina\")),\n\t\t\t);\n\t\t\tlines.push(\"\");\n\t\t\tlines.push(center(muted([\"Enter para continuar\", \"q para salir\"].join(\" · \"))));\n\t\t\treturn this.finishRender(lines, width);\n\t\t}\n\n\t\t// ---- Home screen ------------------------------------------------\n\t\tconst boxW = Math.min(width - 4, 72);\n\n\t\t// Header box: logo + status\n\t\tconst statusParts = [\n\t\t\tdim(`model: ${this.defaultModelLabel()}`),\n\t\t\tdim(`cwd: ${this.cwdLabel()}`),\n\t\t\tdim(`memoria: ${this.memoryLabel()}`),\n\t\t\t\"\",\n\t\t];\n\t\tif (this.updateAvailable) {\n\t\t\tstatusParts.push(\n\t\t\t\twarning(\n\t\t\t\t\t`⚠ Nueva versión disponible: v${this.updateAvailable} ` +\n\t\t\t\t\t\taccent(\"[u] actualizar\") +\n\t\t\t\t\t\tdim(\" · q para ignorar\"),\n\t\t\t\t),\n\t\t\t);\n\t\t} else if (this.updateChecked) {\n\t\t\tstatusParts.push(success(\"✓ Sistema actualizado\"));\n\t\t}\n\t\tlines.push(\n\t\t\t...box([\"\", ...logoLines, tagline, \"\", ...statusParts], boxW, { title: \" ✦ HOILAB TECH ✦ \" }).map((l) =>\n\t\t\t\tcenter(l),\n\t\t\t),\n\t\t);\n\t\tlines.push(\"\");\n\n\t\t// Conversations box\n\t\tconst convLines: string[] = [\"\"];\n\t\tconst isNew = this.selectedIndex === 0;\n\t\tconvLines.push(\n\t\t\t`${isNew ? accent(\"▸ \") : \" \"}${accent(\"✦\")} ${isNew ? theme.bold(\"Nueva conversación\") : \"Nueva conversación\"}`,\n\t\t);\n\t\tconvLines.push(\"\");\n\n\t\tif (this.loading) {\n\t\t\tconvLines.push(dim(\"Cargando conversaciones…\"));\n\t\t} else if (this.filtered.length === 0) {\n\t\t\tconvLines.push(\n\t\t\t\tmuted(\n\t\t\t\t\tthis.query\n\t\t\t\t\t\t? \"No hay conversaciones que coincidan con tu búsqueda\"\n\t\t\t\t\t\t: \"Todavía no hay conversaciones anteriores\",\n\t\t\t\t),\n\t\t\t);\n\t\t} else {\n\t\t\tconst maxVisible = Math.max(1, Math.min(12, Math.floor((width * 2) / 3)));\n\t\t\tconst visible = this.filtered.slice(0, maxVisible);\n\t\t\tfor (let i = 0; i < visible.length; i++) {\n\t\t\t\tconst session = visible[i];\n\t\t\t\tconst isSelected = this.selectedIndex === i + 1;\n\t\t\t\tconst title = sessionTitle(session);\n\t\t\t\tconst meta = muted(`${relativeTime(session.modified.toISOString())} · ${session.messageCount} msgs`);\n\t\t\t\tconst arrow = isSelected ? accent(\"▸ \") : \" \";\n\t\t\t\tconst titleStyled = isSelected ? theme.bold(accent(title)) : title;\n\t\t\t\tconvLines.push(`${arrow}${titleStyled} ${meta}`);\n\t\t\t}\n\t\t\tif (this.filtered.length > maxVisible) {\n\t\t\t\tconvLines.push(dim(`… y ${this.filtered.length - maxVisible} más`));\n\t\t\t}\n\t\t}\n\t\tconvLines.push(\"\");\n\t\tlines.push(...box(convLines, boxW, { title: \" Conversaciones \" }).map((l) => center(l)));\n\t\tlines.push(\"\");\n\n\t\tconst hints = [\n\t\t\t\"↑/↓ navegar\",\n\t\t\t\"Enter seleccionar\",\n\t\t\t\"n nueva\",\n\t\t\tthis.updateAvailable ? \"u actualizar\" : null,\n\t\t\t\"q salir\",\n\t\t].filter((h): h is string => h !== null);\n\t\tlines.push(center(muted(hints.join(\" · \"))));\n\t\treturn this.finishRender(lines, width);\n\t}\n\n\t/** Vertical centering + truncation of every rendered line. */\n\tprivate finishRender(lines: string[], width: number): string[] {\n\t\tconst rows = process.stdout.rows ?? 24;\n\t\tconst contentHeight = lines.length;\n\t\tconst topPad = Math.min(3, Math.max(0, Math.floor((rows - contentHeight) / 3)));\n\t\treturn [...Array<string>(topPad).fill(\"\"), ...lines.map((line) => truncateToWidth(line, width))];\n\t}\n\n\tprivate defaultModelLabel(): string {\n\t\treturn this.modelLabel ?? \"no configurado (/model)\";\n\t}\n\n\tprivate cwdLabel(): string {\n\t\treturn this.cwdLabelValue ?? process.cwd();\n\t}\n\n\tprivate memoryLabel(): string {\n\t\treturn this.memoryEnabled ? \"activada\" : \"desactivada\";\n\t}\n}\n"]}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { type Model } from "@earendil-works/pi-ai";
|
|
2
2
|
import { Container, type Focusable, Input, type TUI } from "@earendil-works/pi-tui";
|
|
3
3
|
import type { ModelRuntime } from "../../../core/model-runtime.ts";
|
|
4
|
-
import type { SettingsManager } from "../../../core/settings-manager.ts";
|
|
5
4
|
interface ScopedModelItem {
|
|
6
5
|
model: Model<any>;
|
|
7
6
|
thinkingLevel?: string;
|
|
@@ -21,9 +20,9 @@ export declare class ModelSelectorComponent extends Container implements Focusab
|
|
|
21
20
|
private filteredModels;
|
|
22
21
|
private selectedIndex;
|
|
23
22
|
private currentModel?;
|
|
24
|
-
private settingsManager;
|
|
25
23
|
private modelRuntime;
|
|
26
24
|
private onSelectCallback;
|
|
25
|
+
private onSelectAsDefaultCallback?;
|
|
27
26
|
private onCancelCallback;
|
|
28
27
|
private errorMessage?;
|
|
29
28
|
private refreshStatusMessage;
|
|
@@ -36,7 +35,7 @@ export declare class ModelSelectorComponent extends Container implements Focusab
|
|
|
36
35
|
private readonly refreshAbortController;
|
|
37
36
|
private refreshTimeout?;
|
|
38
37
|
private closed;
|
|
39
|
-
constructor(tui: TUI, currentModel: Model<any> | undefined,
|
|
38
|
+
constructor(tui: TUI, currentModel: Model<any> | undefined, modelRuntime: ModelRuntime, scopedModels: ReadonlyArray<ScopedModelItem>, onSelect: (model: Model<any>) => void, onCancel: () => void, initialSearchInput?: string, onSelectAsDefault?: (model: Model<any>) => void);
|
|
40
39
|
private loadModelsFromSnapshot;
|
|
41
40
|
private refreshModels;
|
|
42
41
|
dispose(): void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"model-selector.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/model-selector.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,KAAK,EAAkB,MAAM,uBAAuB,CAAC;AACnE,OAAO,EACN,SAAS,EACT,KAAK,SAAS,EAGd,KAAK,EAGL,KAAK,GAAG,EACR,MAAM,wBAAwB,CAAC;AAChC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gCAAgC,CAAC;AACnE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mCAAmC,CAAC;AAazE,UAAU,eAAe;IACxB,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC;IAClB,aAAa,CAAC,EAAE,MAAM,CAAC;CACvB;AAID;;GAEG;AACH,qBAAa,sBAAuB,SAAQ,SAAU,YAAW,SAAS;IACzE,OAAO,CAAC,WAAW,CAAQ;IAG3B,OAAO,CAAC,QAAQ,CAAS;IACzB,IAAI,OAAO,IAAI,OAAO,CAErB;IACD,IAAI,OAAO,CAAC,KAAK,EAAE,OAAO,EAGzB;IACD,OAAO,CAAC,aAAa,CAAY;IACjC,OAAO,CAAC,SAAS,CAAmB;IACpC,OAAO,CAAC,gBAAgB,CAAmB;IAC3C,OAAO,CAAC,YAAY,CAAmB;IACvC,OAAO,CAAC,cAAc,CAAmB;IACzC,OAAO,CAAC,aAAa,CAAa;IAClC,OAAO,CAAC,YAAY,CAAC,CAAa;IAClC,OAAO,CAAC,eAAe,CAAkB;IACzC,OAAO,CAAC,YAAY,CAAe;IACnC,OAAO,CAAC,gBAAgB,CAA8B;IACtD,OAAO,CAAC,gBAAgB,CAAa;IACrC,OAAO,CAAC,YAAY,CAAC,CAAS;IAC9B,OAAO,CAAC,oBAAoB,CAAgC;IAC5D,OAAO,CAAC,oBAAoB,CAAS;IACrC,OAAO,CAAC,GAAG,CAAM;IACjB,OAAO,CAAC,YAAY,CAAiC;IACrD,OAAO,CAAC,KAAK,CAAqB;IAClC,OAAO,CAAC,SAAS,CAAC,CAAO;IACzB,OAAO,CAAC,aAAa,CAAC,CAAO;IAC7B,OAAO,CAAC,QAAQ,CAAC,sBAAsB,CAAyB;IAChE,OAAO,CAAC,cAAc,CAAC,CAAgC;IACvD,OAAO,CAAC,MAAM,CAAS;IAEvB,YACC,GAAG,EAAE,GAAG,EACR,YAAY,EAAE,KAAK,CAAC,GAAG,CAAC,GAAG,SAAS,EACpC,eAAe,EAAE,eAAe,EAChC,YAAY,EAAE,YAAY,EAC1B,YAAY,EAAE,aAAa,CAAC,eAAe,CAAC,EAC5C,QAAQ,EAAE,CAAC,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,KAAK,IAAI,EACrC,QAAQ,EAAE,MAAM,IAAI,EACpB,kBAAkB,CAAC,EAAE,MAAM,EA2D3B;IAED,OAAO,CAAC,sBAAsB;YAuBhB,aAAa;IAwC3B,OAAO,IAAI,IAAI,CAKd;IAED,OAAO,CAAC,UAAU;IAalB,OAAO,CAAC,YAAY;IAMpB,OAAO,CAAC,gBAAgB;IAIxB,OAAO,CAAC,QAAQ;IAYhB,OAAO,CAAC,YAAY;IAapB,OAAO,CAAC,UAAU;IA+DlB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAyCjC;IAED,OAAO,CAAC,YAAY;IAOpB,cAAc,IAAI,KAAK,CAEtB;CACD","sourcesContent":["import { type Model, modelsAreEqual } from \"@earendil-works/pi-ai\";\nimport {\n\tContainer,\n\ttype Focusable,\n\tfuzzyFilter,\n\tgetKeybindings,\n\tInput,\n\tSpacer,\n\tText,\n\ttype TUI,\n} from \"@earendil-works/pi-tui\";\nimport type { ModelRuntime } from \"../../../core/model-runtime.ts\";\nimport type { SettingsManager } from \"../../../core/settings-manager.ts\";\nimport { refreshModelCatalogs } from \"../model-catalog-refresh.ts\";\nimport { getModelSelectorSearchText } from \"../model-search.ts\";\nimport { theme } from \"../theme/theme.ts\";\nimport { DynamicBorder } from \"./dynamic-border.ts\";\nimport { keyHint } from \"./keybinding-hints.ts\";\n\ninterface ModelItem {\n\tprovider: string;\n\tid: string;\n\tmodel: Model<any>;\n}\n\ninterface ScopedModelItem {\n\tmodel: Model<any>;\n\tthinkingLevel?: string;\n}\n\ntype ModelScope = \"all\" | \"scoped\";\n\n/**\n * Component that renders a model selector with search\n */\nexport class ModelSelectorComponent extends Container implements Focusable {\n\tprivate searchInput: Input;\n\n\t// Focusable implementation - propagate to searchInput for IME cursor positioning\n\tprivate _focused = false;\n\tget focused(): boolean {\n\t\treturn this._focused;\n\t}\n\tset focused(value: boolean) {\n\t\tthis._focused = value;\n\t\tthis.searchInput.focused = value;\n\t}\n\tprivate listContainer: Container;\n\tprivate allModels: ModelItem[] = [];\n\tprivate scopedModelItems: ModelItem[] = [];\n\tprivate activeModels: ModelItem[] = [];\n\tprivate filteredModels: ModelItem[] = [];\n\tprivate selectedIndex: number = 0;\n\tprivate currentModel?: Model<any>;\n\tprivate settingsManager: SettingsManager;\n\tprivate modelRuntime: ModelRuntime;\n\tprivate onSelectCallback: (model: Model<any>) => void;\n\tprivate onCancelCallback: () => void;\n\tprivate errorMessage?: string;\n\tprivate refreshStatusMessage = \"Refreshing model catalogs…\";\n\tprivate refreshStatusSuccess = false;\n\tprivate tui: TUI;\n\tprivate scopedModels: ReadonlyArray<ScopedModelItem>;\n\tprivate scope: ModelScope = \"all\";\n\tprivate scopeText?: Text;\n\tprivate scopeHintText?: Text;\n\tprivate readonly refreshAbortController = new AbortController();\n\tprivate refreshTimeout?: ReturnType<typeof setTimeout>;\n\tprivate closed = false;\n\n\tconstructor(\n\t\ttui: TUI,\n\t\tcurrentModel: Model<any> | undefined,\n\t\tsettingsManager: SettingsManager,\n\t\tmodelRuntime: ModelRuntime,\n\t\tscopedModels: ReadonlyArray<ScopedModelItem>,\n\t\tonSelect: (model: Model<any>) => void,\n\t\tonCancel: () => void,\n\t\tinitialSearchInput?: string,\n\t) {\n\t\tsuper();\n\n\t\tthis.tui = tui;\n\t\tthis.currentModel = currentModel;\n\t\tthis.settingsManager = settingsManager;\n\t\tthis.modelRuntime = modelRuntime;\n\t\tthis.scopedModels = scopedModels;\n\t\tthis.scope = scopedModels.length > 0 ? \"scoped\" : \"all\";\n\t\tthis.onSelectCallback = onSelect;\n\t\tthis.onCancelCallback = onCancel;\n\n\t\t// Add top border\n\t\tthis.addChild(new DynamicBorder());\n\t\tthis.addChild(new Spacer(1));\n\n\t\t// Add hint about model filtering\n\t\tif (scopedModels.length > 0) {\n\t\t\tthis.scopeText = new Text(this.getScopeText(), 0, 0);\n\t\t\tthis.addChild(this.scopeText);\n\t\t\tthis.scopeHintText = new Text(this.getScopeHintText(), 0, 0);\n\t\t\tthis.addChild(this.scopeHintText);\n\t\t} else {\n\t\t\tconst hintText = \"Only showing models from configured providers. Use /login to add providers.\";\n\t\t\tthis.addChild(new Text(theme.fg(\"warning\", hintText), 0, 0));\n\t\t}\n\t\tthis.addChild(new Spacer(1));\n\n\t\t// Create search input\n\t\tthis.searchInput = new Input();\n\t\tif (initialSearchInput) {\n\t\t\tthis.searchInput.setValue(initialSearchInput);\n\t\t}\n\t\tthis.searchInput.onSubmit = () => {\n\t\t\t// Enter on search input selects the first filtered item\n\t\t\tif (this.filteredModels[this.selectedIndex]) {\n\t\t\t\tthis.handleSelect(this.filteredModels[this.selectedIndex].model);\n\t\t\t}\n\t\t};\n\t\tthis.addChild(this.searchInput);\n\n\t\tthis.addChild(new Spacer(1));\n\n\t\t// Create list container\n\t\tthis.listContainer = new Container();\n\t\tthis.addChild(this.listContainer);\n\n\t\tthis.addChild(new Spacer(1));\n\n\t\t// Add bottom border\n\t\tthis.addChild(new DynamicBorder());\n\n\t\t// Render the current snapshot immediately, then refresh in the background.\n\t\tthis.loadModelsFromSnapshot();\n\t\tif (initialSearchInput) this.filterModels(initialSearchInput);\n\t\telse this.updateList();\n\t\tthis.tui.requestRender();\n\t\tvoid this.refreshModels();\n\t}\n\n\tprivate loadModelsFromSnapshot(): void {\n\t\tconst models = this.modelRuntime.getAvailableSnapshot().map((model: Model<any>) => ({\n\t\t\tprovider: model.provider,\n\t\t\tid: model.id,\n\t\t\tmodel,\n\t\t}));\n\t\tthis.allModels = this.sortModels(models);\n\t\tthis.scopedModels = this.scopedModels.map((scoped) => {\n\t\t\tconst refreshed = this.modelRuntime.getModel(scoped.model.provider, scoped.model.id);\n\t\t\treturn refreshed ? { ...scoped, model: refreshed } : scoped;\n\t\t});\n\t\tthis.scopedModelItems = this.scopedModels.map((scoped) => ({\n\t\t\tprovider: scoped.model.provider,\n\t\t\tid: scoped.model.id,\n\t\t\tmodel: scoped.model,\n\t\t}));\n\t\tthis.activeModels = this.scope === \"scoped\" ? this.scopedModelItems : this.allModels;\n\t\tthis.filteredModels = this.activeModels;\n\t\tconst currentIndex = this.filteredModels.findIndex((item) => modelsAreEqual(this.currentModel, item.model));\n\t\tthis.selectedIndex =\n\t\t\tcurrentIndex >= 0 ? currentIndex : Math.min(this.selectedIndex, Math.max(0, this.filteredModels.length - 1));\n\t}\n\n\tprivate async refreshModels(): Promise<void> {\n\t\tconst timeoutMs = 15_000;\n\t\tlet timedOut = false;\n\t\tthis.refreshTimeout = setTimeout(() => {\n\t\t\ttimedOut = true;\n\t\t\tthis.refreshAbortController.abort();\n\t\t}, timeoutMs);\n\t\ttry {\n\t\t\tconst result = await refreshModelCatalogs(this.modelRuntime, this.refreshAbortController.signal);\n\t\t\tif (this.closed) return;\n\t\t\tthis.refreshStatusMessage = \"\";\n\t\t\tif (result.aborted && timedOut) {\n\t\t\t\tthis.errorMessage = \"Model refresh timed out; showing cached models.\";\n\t\t\t} else if (result.errors.size === 1) {\n\t\t\t\tthis.errorMessage = `Could not refresh ${result.errors.keys().next().value}; showing cached models.`;\n\t\t\t} else if (result.errors.size > 1) {\n\t\t\t\tthis.errorMessage = `Could not refresh ${result.errors.size} model catalogs (${[...result.errors.keys()].join(\", \")}); showing cached models.`;\n\t\t\t} else {\n\t\t\t\tthis.errorMessage = this.modelRuntime.getError();\n\t\t\t\tif (!this.errorMessage) {\n\t\t\t\t\tthis.refreshStatusMessage = \"Model catalogs refreshed.\";\n\t\t\t\t\tthis.refreshStatusSuccess = true;\n\t\t\t\t}\n\t\t\t}\n\t\t\tthis.loadModelsFromSnapshot();\n\t\t\tthis.filterModels(this.searchInput.getValue());\n\t\t\tthis.tui.requestRender();\n\t\t} catch (error) {\n\t\t\tif (this.closed) return;\n\t\t\tthis.refreshStatusMessage = \"\";\n\t\t\tthis.errorMessage = timedOut\n\t\t\t\t? \"Model refresh timed out; showing cached models.\"\n\t\t\t\t: `Could not refresh model catalogs: ${error instanceof Error ? error.message : String(error)}`;\n\t\t\tthis.updateList();\n\t\t\tthis.tui.requestRender();\n\t\t} finally {\n\t\t\tif (this.refreshTimeout) clearTimeout(this.refreshTimeout);\n\t\t}\n\t}\n\n\tdispose(): void {\n\t\tif (this.closed) return;\n\t\tthis.closed = true;\n\t\tif (this.refreshTimeout) clearTimeout(this.refreshTimeout);\n\t\tthis.refreshAbortController.abort();\n\t}\n\n\tprivate sortModels(models: ModelItem[]): ModelItem[] {\n\t\tconst sorted = [...models];\n\t\t// Sort: current model first, then by provider\n\t\tsorted.sort((a, b) => {\n\t\t\tconst aIsCurrent = modelsAreEqual(this.currentModel, a.model);\n\t\t\tconst bIsCurrent = modelsAreEqual(this.currentModel, b.model);\n\t\t\tif (aIsCurrent && !bIsCurrent) return -1;\n\t\t\tif (!aIsCurrent && bIsCurrent) return 1;\n\t\t\treturn a.provider.localeCompare(b.provider);\n\t\t});\n\t\treturn sorted;\n\t}\n\n\tprivate getScopeText(): string {\n\t\tconst allText = this.scope === \"all\" ? theme.fg(\"accent\", \"all\") : theme.fg(\"muted\", \"all\");\n\t\tconst scopedText = this.scope === \"scoped\" ? theme.fg(\"accent\", \"scoped\") : theme.fg(\"muted\", \"scoped\");\n\t\treturn `${theme.fg(\"muted\", \"Scope: \")}${allText}${theme.fg(\"muted\", \" | \")}${scopedText}`;\n\t}\n\n\tprivate getScopeHintText(): string {\n\t\treturn keyHint(\"tui.input.tab\", \"scope\") + theme.fg(\"muted\", \" (all/scoped)\");\n\t}\n\n\tprivate setScope(scope: ModelScope): void {\n\t\tif (this.scope === scope) return;\n\t\tthis.scope = scope;\n\t\tthis.activeModels = this.scope === \"scoped\" ? this.scopedModelItems : this.allModels;\n\t\tconst currentIndex = this.activeModels.findIndex((item) => modelsAreEqual(this.currentModel, item.model));\n\t\tthis.selectedIndex = currentIndex >= 0 ? currentIndex : 0;\n\t\tthis.filterModels(this.searchInput.getValue());\n\t\tif (this.scopeText) {\n\t\t\tthis.scopeText.setText(this.getScopeText());\n\t\t}\n\t}\n\n\tprivate filterModels(query: string): void {\n\t\tthis.filteredModels = query\n\t\t\t? fuzzyFilter(this.activeModels, query, ({ id, provider, model }) =>\n\t\t\t\t\tgetModelSelectorSearchText({ id, provider, name: model.name }),\n\t\t\t\t)\n\t\t\t: this.activeModels;\n\t\t// When filtering by a query, move the selector to the top row so the best\n\t\t// match is highlighted. When the query is cleared, keep the current position\n\t\t// clamped to the (restored) list length.\n\t\tthis.selectedIndex = query ? 0 : Math.min(this.selectedIndex, Math.max(0, this.filteredModels.length - 1));\n\t\tthis.updateList();\n\t}\n\n\tprivate updateList(): void {\n\t\tthis.listContainer.clear();\n\n\t\tconst maxVisible = 10;\n\t\tconst startIndex = Math.max(\n\t\t\t0,\n\t\t\tMath.min(this.selectedIndex - Math.floor(maxVisible / 2), this.filteredModels.length - maxVisible),\n\t\t);\n\t\tconst endIndex = Math.min(startIndex + maxVisible, this.filteredModels.length);\n\n\t\t// Show visible slice of filtered models\n\t\tfor (let i = startIndex; i < endIndex; i++) {\n\t\t\tconst item = this.filteredModels[i];\n\t\t\tif (!item) continue;\n\n\t\t\tconst isSelected = i === this.selectedIndex;\n\t\t\tconst isCurrent = modelsAreEqual(this.currentModel, item.model);\n\n\t\t\tlet line = \"\";\n\t\t\tif (isSelected) {\n\t\t\t\tconst prefix = theme.fg(\"accent\", \"→ \");\n\t\t\t\tconst modelText = `${item.id}`;\n\t\t\t\tconst providerBadge = theme.fg(\"muted\", `[${item.provider}]`);\n\t\t\t\tconst checkmark = isCurrent ? theme.fg(\"success\", \" ✓\") : \"\";\n\t\t\t\tline = `${prefix + theme.fg(\"accent\", modelText)} ${providerBadge}${checkmark}`;\n\t\t\t} else {\n\t\t\t\tconst modelText = ` ${item.id}`;\n\t\t\t\tconst providerBadge = theme.fg(\"muted\", `[${item.provider}]`);\n\t\t\t\tconst checkmark = isCurrent ? theme.fg(\"success\", \" ✓\") : \"\";\n\t\t\t\tline = `${modelText} ${providerBadge}${checkmark}`;\n\t\t\t}\n\n\t\t\tthis.listContainer.addChild(new Text(line, 0, 0));\n\t\t}\n\n\t\t// Add scroll indicator if needed\n\t\tif (startIndex > 0 || endIndex < this.filteredModels.length) {\n\t\t\tconst scrollInfo = theme.fg(\"muted\", ` (${this.selectedIndex + 1}/${this.filteredModels.length})`);\n\t\t\tthis.listContainer.addChild(new Text(scrollInfo, 0, 0));\n\t\t}\n\n\t\t// Show error message or \"no results\" if empty\n\t\tif (this.errorMessage) {\n\t\t\t// Show error in red\n\t\t\tconst errorLines = this.errorMessage.split(\"\\n\");\n\t\t\tfor (const line of errorLines) {\n\t\t\t\tthis.listContainer.addChild(new Text(theme.fg(\"error\", line), 0, 0));\n\t\t\t}\n\t\t} else if (this.filteredModels.length === 0) {\n\t\t\tthis.listContainer.addChild(new Text(theme.fg(\"muted\", \" No matching models\"), 0, 0));\n\t\t} else {\n\t\t\tconst selected = this.filteredModels[this.selectedIndex];\n\t\t\tthis.listContainer.addChild(new Spacer(1));\n\t\t\tthis.listContainer.addChild(new Text(theme.fg(\"muted\", ` Model Name: ${selected.model.name}`), 0, 0));\n\t\t}\n\t\tif (this.refreshStatusMessage) {\n\t\t\tthis.listContainer.addChild(new Spacer(1));\n\t\t\tthis.listContainer.addChild(\n\t\t\t\tnew Text(theme.fg(this.refreshStatusSuccess ? \"success\" : \"muted\", ` ${this.refreshStatusMessage}`), 0, 0),\n\t\t\t);\n\t\t}\n\t}\n\n\thandleInput(keyData: string): void {\n\t\tconst kb = getKeybindings();\n\t\tif (kb.matches(keyData, \"tui.input.tab\")) {\n\t\t\tif (this.scopedModelItems.length > 0) {\n\t\t\t\tconst nextScope: ModelScope = this.scope === \"all\" ? \"scoped\" : \"all\";\n\t\t\t\tthis.setScope(nextScope);\n\t\t\t\tif (this.scopeHintText) {\n\t\t\t\t\tthis.scopeHintText.setText(this.getScopeHintText());\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\t// Up arrow - wrap to bottom when at top\n\t\tif (kb.matches(keyData, \"tui.select.up\")) {\n\t\t\tif (this.filteredModels.length === 0) return;\n\t\t\tthis.selectedIndex = this.selectedIndex === 0 ? this.filteredModels.length - 1 : this.selectedIndex - 1;\n\t\t\tthis.updateList();\n\t\t}\n\t\t// Down arrow - wrap to top when at bottom\n\t\telse if (kb.matches(keyData, \"tui.select.down\")) {\n\t\t\tif (this.filteredModels.length === 0) return;\n\t\t\tthis.selectedIndex = this.selectedIndex === this.filteredModels.length - 1 ? 0 : this.selectedIndex + 1;\n\t\t\tthis.updateList();\n\t\t}\n\t\t// Enter\n\t\telse if (kb.matches(keyData, \"tui.select.confirm\")) {\n\t\t\tconst selectedModel = this.filteredModels[this.selectedIndex];\n\t\t\tif (selectedModel) {\n\t\t\t\tthis.handleSelect(selectedModel.model);\n\t\t\t}\n\t\t}\n\t\t// Escape or Ctrl+C\n\t\telse if (kb.matches(keyData, \"tui.select.cancel\")) {\n\t\t\tthis.dispose();\n\t\t\tthis.onCancelCallback();\n\t\t}\n\t\t// Pass everything else to search input\n\t\telse {\n\t\t\tthis.searchInput.handleInput(keyData);\n\t\t\tthis.filterModels(this.searchInput.getValue());\n\t\t}\n\t}\n\n\tprivate handleSelect(model: Model<any>): void {\n\t\tthis.dispose();\n\t\t// Save as new default\n\t\tthis.settingsManager.setDefaultModelAndProvider(model.provider, model.id);\n\t\tthis.onSelectCallback(model);\n\t}\n\n\tgetSearchInput(): Input {\n\t\treturn this.searchInput;\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"model-selector.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/model-selector.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,KAAK,EAAkB,MAAM,uBAAuB,CAAC;AACnE,OAAO,EACN,SAAS,EACT,KAAK,SAAS,EAGd,KAAK,EAIL,KAAK,GAAG,EACR,MAAM,wBAAwB,CAAC;AAChC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gCAAgC,CAAC;AAanE,UAAU,eAAe;IACxB,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC;IAClB,aAAa,CAAC,EAAE,MAAM,CAAC;CACvB;AAID;;GAEG;AACH,qBAAa,sBAAuB,SAAQ,SAAU,YAAW,SAAS;IACzE,OAAO,CAAC,WAAW,CAAQ;IAG3B,OAAO,CAAC,QAAQ,CAAS;IACzB,IAAI,OAAO,IAAI,OAAO,CAErB;IACD,IAAI,OAAO,CAAC,KAAK,EAAE,OAAO,EAGzB;IACD,OAAO,CAAC,aAAa,CAAY;IACjC,OAAO,CAAC,SAAS,CAAmB;IACpC,OAAO,CAAC,gBAAgB,CAAmB;IAC3C,OAAO,CAAC,YAAY,CAAmB;IACvC,OAAO,CAAC,cAAc,CAAmB;IACzC,OAAO,CAAC,aAAa,CAAa;IAClC,OAAO,CAAC,YAAY,CAAC,CAAa;IAClC,OAAO,CAAC,YAAY,CAAe;IACnC,OAAO,CAAC,gBAAgB,CAA8B;IACtD,OAAO,CAAC,yBAAyB,CAAC,CAA8B;IAChE,OAAO,CAAC,gBAAgB,CAAa;IACrC,OAAO,CAAC,YAAY,CAAC,CAAS;IAC9B,OAAO,CAAC,oBAAoB,CAAgC;IAC5D,OAAO,CAAC,oBAAoB,CAAS;IACrC,OAAO,CAAC,GAAG,CAAM;IACjB,OAAO,CAAC,YAAY,CAAiC;IACrD,OAAO,CAAC,KAAK,CAAqB;IAClC,OAAO,CAAC,SAAS,CAAC,CAAO;IACzB,OAAO,CAAC,aAAa,CAAC,CAAO;IAC7B,OAAO,CAAC,QAAQ,CAAC,sBAAsB,CAAyB;IAChE,OAAO,CAAC,cAAc,CAAC,CAAgC;IACvD,OAAO,CAAC,MAAM,CAAS;IAEvB,YACC,GAAG,EAAE,GAAG,EACR,YAAY,EAAE,KAAK,CAAC,GAAG,CAAC,GAAG,SAAS,EACpC,YAAY,EAAE,YAAY,EAC1B,YAAY,EAAE,aAAa,CAAC,eAAe,CAAC,EAC5C,QAAQ,EAAE,CAAC,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,KAAK,IAAI,EACrC,QAAQ,EAAE,MAAM,IAAI,EACpB,kBAAkB,CAAC,EAAE,MAAM,EAC3B,iBAAiB,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,KAAK,IAAI,EAkE/C;IAED,OAAO,CAAC,sBAAsB;YAuBhB,aAAa;IAwC3B,OAAO,IAAI,IAAI,CAKd;IAED,OAAO,CAAC,UAAU;IAalB,OAAO,CAAC,YAAY;IAMpB,OAAO,CAAC,gBAAgB;IAIxB,OAAO,CAAC,QAAQ;IAYhB,OAAO,CAAC,YAAY;IAapB,OAAO,CAAC,UAAU;IA+DlB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAiDjC;IAED,OAAO,CAAC,YAAY;IAKpB,cAAc,IAAI,KAAK,CAEtB;CACD","sourcesContent":["import { type Model, modelsAreEqual } from \"@earendil-works/pi-ai\";\nimport {\n\tContainer,\n\ttype Focusable,\n\tfuzzyFilter,\n\tgetKeybindings,\n\tInput,\n\tmatchesKey,\n\tSpacer,\n\tText,\n\ttype TUI,\n} from \"@earendil-works/pi-tui\";\nimport type { ModelRuntime } from \"../../../core/model-runtime.ts\";\nimport { refreshModelCatalogs } from \"../model-catalog-refresh.ts\";\nimport { getModelSelectorSearchText } from \"../model-search.ts\";\nimport { theme } from \"../theme/theme.ts\";\nimport { DynamicBorder } from \"./dynamic-border.ts\";\nimport { keyHint } from \"./keybinding-hints.ts\";\n\ninterface ModelItem {\n\tprovider: string;\n\tid: string;\n\tmodel: Model<any>;\n}\n\ninterface ScopedModelItem {\n\tmodel: Model<any>;\n\tthinkingLevel?: string;\n}\n\ntype ModelScope = \"all\" | \"scoped\";\n\n/**\n * Component that renders a model selector with search\n */\nexport class ModelSelectorComponent extends Container implements Focusable {\n\tprivate searchInput: Input;\n\n\t// Focusable implementation - propagate to searchInput for IME cursor positioning\n\tprivate _focused = false;\n\tget focused(): boolean {\n\t\treturn this._focused;\n\t}\n\tset focused(value: boolean) {\n\t\tthis._focused = value;\n\t\tthis.searchInput.focused = value;\n\t}\n\tprivate listContainer: Container;\n\tprivate allModels: ModelItem[] = [];\n\tprivate scopedModelItems: ModelItem[] = [];\n\tprivate activeModels: ModelItem[] = [];\n\tprivate filteredModels: ModelItem[] = [];\n\tprivate selectedIndex: number = 0;\n\tprivate currentModel?: Model<any>;\n\tprivate modelRuntime: ModelRuntime;\n\tprivate onSelectCallback: (model: Model<any>) => void;\n\tprivate onSelectAsDefaultCallback?: (model: Model<any>) => void;\n\tprivate onCancelCallback: () => void;\n\tprivate errorMessage?: string;\n\tprivate refreshStatusMessage = \"Refreshing model catalogs…\";\n\tprivate refreshStatusSuccess = false;\n\tprivate tui: TUI;\n\tprivate scopedModels: ReadonlyArray<ScopedModelItem>;\n\tprivate scope: ModelScope = \"all\";\n\tprivate scopeText?: Text;\n\tprivate scopeHintText?: Text;\n\tprivate readonly refreshAbortController = new AbortController();\n\tprivate refreshTimeout?: ReturnType<typeof setTimeout>;\n\tprivate closed = false;\n\n\tconstructor(\n\t\ttui: TUI,\n\t\tcurrentModel: Model<any> | undefined,\n\t\tmodelRuntime: ModelRuntime,\n\t\tscopedModels: ReadonlyArray<ScopedModelItem>,\n\t\tonSelect: (model: Model<any>) => void,\n\t\tonCancel: () => void,\n\t\tinitialSearchInput?: string,\n\t\tonSelectAsDefault?: (model: Model<any>) => void,\n\t) {\n\t\tsuper();\n\n\t\tthis.tui = tui;\n\t\tthis.currentModel = currentModel;\n\t\tthis.modelRuntime = modelRuntime;\n\t\tthis.scopedModels = scopedModels;\n\t\tthis.scope = scopedModels.length > 0 ? \"scoped\" : \"all\";\n\t\tthis.onSelectCallback = onSelect;\n\t\tthis.onSelectAsDefaultCallback = onSelectAsDefault;\n\t\tthis.onCancelCallback = onCancel;\n\n\t\t// Add top border\n\t\tthis.addChild(new DynamicBorder());\n\t\tthis.addChild(new Spacer(1));\n\n\t\t// Add hint about model filtering\n\t\tif (scopedModels.length > 0) {\n\t\t\tthis.scopeText = new Text(this.getScopeText(), 0, 0);\n\t\t\tthis.addChild(this.scopeText);\n\t\t\tthis.scopeHintText = new Text(this.getScopeHintText(), 0, 0);\n\t\t\tthis.addChild(this.scopeHintText);\n\t\t} else {\n\t\t\tconst hintText = \"Only showing models from configured providers. Use /login to add providers.\";\n\t\t\tthis.addChild(new Text(theme.fg(\"warning\", hintText), 0, 0));\n\t\t}\n\t\tthis.addChild(new Spacer(1));\n\n\t\t// Create search input\n\t\tthis.searchInput = new Input();\n\t\tif (initialSearchInput) {\n\t\t\tthis.searchInput.setValue(initialSearchInput);\n\t\t}\n\t\tthis.searchInput.onSubmit = () => {\n\t\t\t// Enter on search input selects the first filtered item\n\t\t\tif (this.filteredModels[this.selectedIndex]) {\n\t\t\t\tthis.handleSelect(this.filteredModels[this.selectedIndex].model);\n\t\t\t}\n\t\t};\n\t\tthis.addChild(this.searchInput);\n\n\t\tthis.addChild(new Spacer(1));\n\n\t\t// Create list container\n\t\tthis.listContainer = new Container();\n\t\tthis.addChild(this.listContainer);\n\n\t\tthis.addChild(new Spacer(1));\n\n\t\t// Hint\n\t\tif (this.onSelectAsDefaultCallback) {\n\t\t\tthis.addChild(\n\t\t\t\tnew Text(theme.fg(\"dim\", \" Enter to select \\u00b7 Ctrl+S to set as default \\u00b7 Esc to cancel\"), 0, 0),\n\t\t\t);\n\t\t}\n\n\t\t// Add bottom border\n\t\tthis.addChild(new DynamicBorder());\n\n\t\t// Render the current snapshot immediately, then refresh in the background.\n\t\tthis.loadModelsFromSnapshot();\n\t\tif (initialSearchInput) this.filterModels(initialSearchInput);\n\t\telse this.updateList();\n\t\tthis.tui.requestRender();\n\t\tvoid this.refreshModels();\n\t}\n\n\tprivate loadModelsFromSnapshot(): void {\n\t\tconst models = this.modelRuntime.getAvailableSnapshot().map((model: Model<any>) => ({\n\t\t\tprovider: model.provider,\n\t\t\tid: model.id,\n\t\t\tmodel,\n\t\t}));\n\t\tthis.allModels = this.sortModels(models);\n\t\tthis.scopedModels = this.scopedModels.map((scoped) => {\n\t\t\tconst refreshed = this.modelRuntime.getModel(scoped.model.provider, scoped.model.id);\n\t\t\treturn refreshed ? { ...scoped, model: refreshed } : scoped;\n\t\t});\n\t\tthis.scopedModelItems = this.scopedModels.map((scoped) => ({\n\t\t\tprovider: scoped.model.provider,\n\t\t\tid: scoped.model.id,\n\t\t\tmodel: scoped.model,\n\t\t}));\n\t\tthis.activeModels = this.scope === \"scoped\" ? this.scopedModelItems : this.allModels;\n\t\tthis.filteredModels = this.activeModels;\n\t\tconst currentIndex = this.filteredModels.findIndex((item) => modelsAreEqual(this.currentModel, item.model));\n\t\tthis.selectedIndex =\n\t\t\tcurrentIndex >= 0 ? currentIndex : Math.min(this.selectedIndex, Math.max(0, this.filteredModels.length - 1));\n\t}\n\n\tprivate async refreshModels(): Promise<void> {\n\t\tconst timeoutMs = 15_000;\n\t\tlet timedOut = false;\n\t\tthis.refreshTimeout = setTimeout(() => {\n\t\t\ttimedOut = true;\n\t\t\tthis.refreshAbortController.abort();\n\t\t}, timeoutMs);\n\t\ttry {\n\t\t\tconst result = await refreshModelCatalogs(this.modelRuntime, this.refreshAbortController.signal);\n\t\t\tif (this.closed) return;\n\t\t\tthis.refreshStatusMessage = \"\";\n\t\t\tif (result.aborted && timedOut) {\n\t\t\t\tthis.errorMessage = \"Model refresh timed out; showing cached models.\";\n\t\t\t} else if (result.errors.size === 1) {\n\t\t\t\tthis.errorMessage = `Could not refresh ${result.errors.keys().next().value}; showing cached models.`;\n\t\t\t} else if (result.errors.size > 1) {\n\t\t\t\tthis.errorMessage = `Could not refresh ${result.errors.size} model catalogs (${[...result.errors.keys()].join(\", \")}); showing cached models.`;\n\t\t\t} else {\n\t\t\t\tthis.errorMessage = this.modelRuntime.getError();\n\t\t\t\tif (!this.errorMessage) {\n\t\t\t\t\tthis.refreshStatusMessage = \"Model catalogs refreshed.\";\n\t\t\t\t\tthis.refreshStatusSuccess = true;\n\t\t\t\t}\n\t\t\t}\n\t\t\tthis.loadModelsFromSnapshot();\n\t\t\tthis.filterModels(this.searchInput.getValue());\n\t\t\tthis.tui.requestRender();\n\t\t} catch (error) {\n\t\t\tif (this.closed) return;\n\t\t\tthis.refreshStatusMessage = \"\";\n\t\t\tthis.errorMessage = timedOut\n\t\t\t\t? \"Model refresh timed out; showing cached models.\"\n\t\t\t\t: `Could not refresh model catalogs: ${error instanceof Error ? error.message : String(error)}`;\n\t\t\tthis.updateList();\n\t\t\tthis.tui.requestRender();\n\t\t} finally {\n\t\t\tif (this.refreshTimeout) clearTimeout(this.refreshTimeout);\n\t\t}\n\t}\n\n\tdispose(): void {\n\t\tif (this.closed) return;\n\t\tthis.closed = true;\n\t\tif (this.refreshTimeout) clearTimeout(this.refreshTimeout);\n\t\tthis.refreshAbortController.abort();\n\t}\n\n\tprivate sortModels(models: ModelItem[]): ModelItem[] {\n\t\tconst sorted = [...models];\n\t\t// Sort: current model first, then by provider\n\t\tsorted.sort((a, b) => {\n\t\t\tconst aIsCurrent = modelsAreEqual(this.currentModel, a.model);\n\t\t\tconst bIsCurrent = modelsAreEqual(this.currentModel, b.model);\n\t\t\tif (aIsCurrent && !bIsCurrent) return -1;\n\t\t\tif (!aIsCurrent && bIsCurrent) return 1;\n\t\t\treturn a.provider.localeCompare(b.provider);\n\t\t});\n\t\treturn sorted;\n\t}\n\n\tprivate getScopeText(): string {\n\t\tconst allText = this.scope === \"all\" ? theme.fg(\"accent\", \"all\") : theme.fg(\"muted\", \"all\");\n\t\tconst scopedText = this.scope === \"scoped\" ? theme.fg(\"accent\", \"scoped\") : theme.fg(\"muted\", \"scoped\");\n\t\treturn `${theme.fg(\"muted\", \"Scope: \")}${allText}${theme.fg(\"muted\", \" | \")}${scopedText}`;\n\t}\n\n\tprivate getScopeHintText(): string {\n\t\treturn keyHint(\"tui.input.tab\", \"scope\") + theme.fg(\"muted\", \" (all/scoped)\");\n\t}\n\n\tprivate setScope(scope: ModelScope): void {\n\t\tif (this.scope === scope) return;\n\t\tthis.scope = scope;\n\t\tthis.activeModels = this.scope === \"scoped\" ? this.scopedModelItems : this.allModels;\n\t\tconst currentIndex = this.activeModels.findIndex((item) => modelsAreEqual(this.currentModel, item.model));\n\t\tthis.selectedIndex = currentIndex >= 0 ? currentIndex : 0;\n\t\tthis.filterModels(this.searchInput.getValue());\n\t\tif (this.scopeText) {\n\t\t\tthis.scopeText.setText(this.getScopeText());\n\t\t}\n\t}\n\n\tprivate filterModels(query: string): void {\n\t\tthis.filteredModels = query\n\t\t\t? fuzzyFilter(this.activeModels, query, ({ id, provider, model }) =>\n\t\t\t\t\tgetModelSelectorSearchText({ id, provider, name: model.name }),\n\t\t\t\t)\n\t\t\t: this.activeModels;\n\t\t// When filtering by a query, move the selector to the top row so the best\n\t\t// match is highlighted. When the query is cleared, keep the current position\n\t\t// clamped to the (restored) list length.\n\t\tthis.selectedIndex = query ? 0 : Math.min(this.selectedIndex, Math.max(0, this.filteredModels.length - 1));\n\t\tthis.updateList();\n\t}\n\n\tprivate updateList(): void {\n\t\tthis.listContainer.clear();\n\n\t\tconst maxVisible = 10;\n\t\tconst startIndex = Math.max(\n\t\t\t0,\n\t\t\tMath.min(this.selectedIndex - Math.floor(maxVisible / 2), this.filteredModels.length - maxVisible),\n\t\t);\n\t\tconst endIndex = Math.min(startIndex + maxVisible, this.filteredModels.length);\n\n\t\t// Show visible slice of filtered models\n\t\tfor (let i = startIndex; i < endIndex; i++) {\n\t\t\tconst item = this.filteredModels[i];\n\t\t\tif (!item) continue;\n\n\t\t\tconst isSelected = i === this.selectedIndex;\n\t\t\tconst isCurrent = modelsAreEqual(this.currentModel, item.model);\n\n\t\t\tlet line = \"\";\n\t\t\tif (isSelected) {\n\t\t\t\tconst prefix = theme.fg(\"accent\", \"→ \");\n\t\t\t\tconst modelText = `${item.id}`;\n\t\t\t\tconst providerBadge = theme.fg(\"muted\", `[${item.provider}]`);\n\t\t\t\tconst checkmark = isCurrent ? theme.fg(\"success\", \" ✓\") : \"\";\n\t\t\t\tline = `${prefix + theme.fg(\"accent\", modelText)} ${providerBadge}${checkmark}`;\n\t\t\t} else {\n\t\t\t\tconst modelText = ` ${item.id}`;\n\t\t\t\tconst providerBadge = theme.fg(\"muted\", `[${item.provider}]`);\n\t\t\t\tconst checkmark = isCurrent ? theme.fg(\"success\", \" ✓\") : \"\";\n\t\t\t\tline = `${modelText} ${providerBadge}${checkmark}`;\n\t\t\t}\n\n\t\t\tthis.listContainer.addChild(new Text(line, 0, 0));\n\t\t}\n\n\t\t// Add scroll indicator if needed\n\t\tif (startIndex > 0 || endIndex < this.filteredModels.length) {\n\t\t\tconst scrollInfo = theme.fg(\"muted\", ` (${this.selectedIndex + 1}/${this.filteredModels.length})`);\n\t\t\tthis.listContainer.addChild(new Text(scrollInfo, 0, 0));\n\t\t}\n\n\t\t// Show error message or \"no results\" if empty\n\t\tif (this.errorMessage) {\n\t\t\t// Show error in red\n\t\t\tconst errorLines = this.errorMessage.split(\"\\n\");\n\t\t\tfor (const line of errorLines) {\n\t\t\t\tthis.listContainer.addChild(new Text(theme.fg(\"error\", line), 0, 0));\n\t\t\t}\n\t\t} else if (this.filteredModels.length === 0) {\n\t\t\tthis.listContainer.addChild(new Text(theme.fg(\"muted\", \" No matching models\"), 0, 0));\n\t\t} else {\n\t\t\tconst selected = this.filteredModels[this.selectedIndex];\n\t\t\tthis.listContainer.addChild(new Spacer(1));\n\t\t\tthis.listContainer.addChild(new Text(theme.fg(\"muted\", ` Model Name: ${selected.model.name}`), 0, 0));\n\t\t}\n\t\tif (this.refreshStatusMessage) {\n\t\t\tthis.listContainer.addChild(new Spacer(1));\n\t\t\tthis.listContainer.addChild(\n\t\t\t\tnew Text(theme.fg(this.refreshStatusSuccess ? \"success\" : \"muted\", ` ${this.refreshStatusMessage}`), 0, 0),\n\t\t\t);\n\t\t}\n\t}\n\n\thandleInput(keyData: string): void {\n\t\tconst kb = getKeybindings();\n\t\tif (kb.matches(keyData, \"tui.input.tab\")) {\n\t\t\tif (this.scopedModelItems.length > 0) {\n\t\t\t\tconst nextScope: ModelScope = this.scope === \"all\" ? \"scoped\" : \"all\";\n\t\t\t\tthis.setScope(nextScope);\n\t\t\t\tif (this.scopeHintText) {\n\t\t\t\t\tthis.scopeHintText.setText(this.getScopeHintText());\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\t// Up arrow - wrap to bottom when at top\n\t\tif (kb.matches(keyData, \"tui.select.up\")) {\n\t\t\tif (this.filteredModels.length === 0) return;\n\t\t\tthis.selectedIndex = this.selectedIndex === 0 ? this.filteredModels.length - 1 : this.selectedIndex - 1;\n\t\t\tthis.updateList();\n\t\t}\n\t\t// Down arrow - wrap to top when at bottom\n\t\telse if (kb.matches(keyData, \"tui.select.down\")) {\n\t\t\tif (this.filteredModels.length === 0) return;\n\t\t\tthis.selectedIndex = this.selectedIndex === this.filteredModels.length - 1 ? 0 : this.selectedIndex + 1;\n\t\t\tthis.updateList();\n\t\t}\n\t\t// Enter\n\t\telse if (kb.matches(keyData, \"tui.select.confirm\")) {\n\t\t\tconst selectedModel = this.filteredModels[this.selectedIndex];\n\t\t\tif (selectedModel) {\n\t\t\t\tthis.handleSelect(selectedModel.model);\n\t\t\t}\n\t\t}\n\t\t// Escape or Ctrl+C\n\t\telse if (kb.matches(keyData, \"tui.select.cancel\")) {\n\t\t\tthis.dispose();\n\t\t\tthis.onCancelCallback();\n\t\t}\n\t\t// Ctrl+S — select and save as default\n\t\telse if (matchesKey(keyData, \"ctrl+s\") && this.onSelectAsDefaultCallback) {\n\t\t\tconst selectedModel = this.filteredModels[this.selectedIndex];\n\t\t\tif (selectedModel) {\n\t\t\t\tthis.dispose();\n\t\t\t\tthis.onSelectAsDefaultCallback(selectedModel.model);\n\t\t\t}\n\t\t}\n\t\t// Pass everything else to search input\n\t\telse {\n\t\t\tthis.searchInput.handleInput(keyData);\n\t\t\tthis.filterModels(this.searchInput.getValue());\n\t\t}\n\t}\n\n\tprivate handleSelect(model: Model<any>): void {\n\t\tthis.dispose();\n\t\tthis.onSelectCallback(model);\n\t}\n\n\tgetSearchInput(): Input {\n\t\treturn this.searchInput;\n\t}\n}\n"]}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { modelsAreEqual } from "@earendil-works/pi-ai";
|
|
2
|
-
import { Container, fuzzyFilter, getKeybindings, Input, Spacer, Text, } from "@earendil-works/pi-tui";
|
|
2
|
+
import { Container, fuzzyFilter, getKeybindings, Input, matchesKey, Spacer, Text, } from "@earendil-works/pi-tui";
|
|
3
3
|
import { refreshModelCatalogs } from "../model-catalog-refresh.js";
|
|
4
4
|
import { getModelSelectorSearchText } from "../model-search.js";
|
|
5
5
|
import { theme } from "../theme/theme.js";
|
|
@@ -26,9 +26,9 @@ export class ModelSelectorComponent extends Container {
|
|
|
26
26
|
filteredModels = [];
|
|
27
27
|
selectedIndex = 0;
|
|
28
28
|
currentModel;
|
|
29
|
-
settingsManager;
|
|
30
29
|
modelRuntime;
|
|
31
30
|
onSelectCallback;
|
|
31
|
+
onSelectAsDefaultCallback;
|
|
32
32
|
onCancelCallback;
|
|
33
33
|
errorMessage;
|
|
34
34
|
refreshStatusMessage = "Refreshing model catalogs…";
|
|
@@ -41,15 +41,15 @@ export class ModelSelectorComponent extends Container {
|
|
|
41
41
|
refreshAbortController = new AbortController();
|
|
42
42
|
refreshTimeout;
|
|
43
43
|
closed = false;
|
|
44
|
-
constructor(tui, currentModel,
|
|
44
|
+
constructor(tui, currentModel, modelRuntime, scopedModels, onSelect, onCancel, initialSearchInput, onSelectAsDefault) {
|
|
45
45
|
super();
|
|
46
46
|
this.tui = tui;
|
|
47
47
|
this.currentModel = currentModel;
|
|
48
|
-
this.settingsManager = settingsManager;
|
|
49
48
|
this.modelRuntime = modelRuntime;
|
|
50
49
|
this.scopedModels = scopedModels;
|
|
51
50
|
this.scope = scopedModels.length > 0 ? "scoped" : "all";
|
|
52
51
|
this.onSelectCallback = onSelect;
|
|
52
|
+
this.onSelectAsDefaultCallback = onSelectAsDefault;
|
|
53
53
|
this.onCancelCallback = onCancel;
|
|
54
54
|
// Add top border
|
|
55
55
|
this.addChild(new DynamicBorder());
|
|
@@ -83,6 +83,10 @@ export class ModelSelectorComponent extends Container {
|
|
|
83
83
|
this.listContainer = new Container();
|
|
84
84
|
this.addChild(this.listContainer);
|
|
85
85
|
this.addChild(new Spacer(1));
|
|
86
|
+
// Hint
|
|
87
|
+
if (this.onSelectAsDefaultCallback) {
|
|
88
|
+
this.addChild(new Text(theme.fg("dim", " Enter to select \u00b7 Ctrl+S to set as default \u00b7 Esc to cancel"), 0, 0));
|
|
89
|
+
}
|
|
86
90
|
// Add bottom border
|
|
87
91
|
this.addChild(new DynamicBorder());
|
|
88
92
|
// Render the current snapshot immediately, then refresh in the background.
|
|
@@ -307,6 +311,14 @@ export class ModelSelectorComponent extends Container {
|
|
|
307
311
|
this.dispose();
|
|
308
312
|
this.onCancelCallback();
|
|
309
313
|
}
|
|
314
|
+
// Ctrl+S — select and save as default
|
|
315
|
+
else if (matchesKey(keyData, "ctrl+s") && this.onSelectAsDefaultCallback) {
|
|
316
|
+
const selectedModel = this.filteredModels[this.selectedIndex];
|
|
317
|
+
if (selectedModel) {
|
|
318
|
+
this.dispose();
|
|
319
|
+
this.onSelectAsDefaultCallback(selectedModel.model);
|
|
320
|
+
}
|
|
321
|
+
}
|
|
310
322
|
// Pass everything else to search input
|
|
311
323
|
else {
|
|
312
324
|
this.searchInput.handleInput(keyData);
|
|
@@ -315,8 +327,6 @@ export class ModelSelectorComponent extends Container {
|
|
|
315
327
|
}
|
|
316
328
|
handleSelect(model) {
|
|
317
329
|
this.dispose();
|
|
318
|
-
// Save as new default
|
|
319
|
-
this.settingsManager.setDefaultModelAndProvider(model.provider, model.id);
|
|
320
330
|
this.onSelectCallback(model);
|
|
321
331
|
}
|
|
322
332
|
getSearchInput() {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"model-selector.js","sourceRoot":"","sources":["../../../../src/modes/interactive/components/model-selector.ts"],"names":[],"mappings":"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","sourcesContent":["import { type Model, modelsAreEqual } from \"@earendil-works/pi-ai\";\nimport {\n\tContainer,\n\ttype Focusable,\n\tfuzzyFilter,\n\tgetKeybindings,\n\tInput,\n\tSpacer,\n\tText,\n\ttype TUI,\n} from \"@earendil-works/pi-tui\";\nimport type { ModelRuntime } from \"../../../core/model-runtime.ts\";\nimport type { SettingsManager } from \"../../../core/settings-manager.ts\";\nimport { refreshModelCatalogs } from \"../model-catalog-refresh.ts\";\nimport { getModelSelectorSearchText } from \"../model-search.ts\";\nimport { theme } from \"../theme/theme.ts\";\nimport { DynamicBorder } from \"./dynamic-border.ts\";\nimport { keyHint } from \"./keybinding-hints.ts\";\n\ninterface ModelItem {\n\tprovider: string;\n\tid: string;\n\tmodel: Model<any>;\n}\n\ninterface ScopedModelItem {\n\tmodel: Model<any>;\n\tthinkingLevel?: string;\n}\n\ntype ModelScope = \"all\" | \"scoped\";\n\n/**\n * Component that renders a model selector with search\n */\nexport class ModelSelectorComponent extends Container implements Focusable {\n\tprivate searchInput: Input;\n\n\t// Focusable implementation - propagate to searchInput for IME cursor positioning\n\tprivate _focused = false;\n\tget focused(): boolean {\n\t\treturn this._focused;\n\t}\n\tset focused(value: boolean) {\n\t\tthis._focused = value;\n\t\tthis.searchInput.focused = value;\n\t}\n\tprivate listContainer: Container;\n\tprivate allModels: ModelItem[] = [];\n\tprivate scopedModelItems: ModelItem[] = [];\n\tprivate activeModels: ModelItem[] = [];\n\tprivate filteredModels: ModelItem[] = [];\n\tprivate selectedIndex: number = 0;\n\tprivate currentModel?: Model<any>;\n\tprivate settingsManager: SettingsManager;\n\tprivate modelRuntime: ModelRuntime;\n\tprivate onSelectCallback: (model: Model<any>) => void;\n\tprivate onCancelCallback: () => void;\n\tprivate errorMessage?: string;\n\tprivate refreshStatusMessage = \"Refreshing model catalogs…\";\n\tprivate refreshStatusSuccess = false;\n\tprivate tui: TUI;\n\tprivate scopedModels: ReadonlyArray<ScopedModelItem>;\n\tprivate scope: ModelScope = \"all\";\n\tprivate scopeText?: Text;\n\tprivate scopeHintText?: Text;\n\tprivate readonly refreshAbortController = new AbortController();\n\tprivate refreshTimeout?: ReturnType<typeof setTimeout>;\n\tprivate closed = false;\n\n\tconstructor(\n\t\ttui: TUI,\n\t\tcurrentModel: Model<any> | undefined,\n\t\tsettingsManager: SettingsManager,\n\t\tmodelRuntime: ModelRuntime,\n\t\tscopedModels: ReadonlyArray<ScopedModelItem>,\n\t\tonSelect: (model: Model<any>) => void,\n\t\tonCancel: () => void,\n\t\tinitialSearchInput?: string,\n\t) {\n\t\tsuper();\n\n\t\tthis.tui = tui;\n\t\tthis.currentModel = currentModel;\n\t\tthis.settingsManager = settingsManager;\n\t\tthis.modelRuntime = modelRuntime;\n\t\tthis.scopedModels = scopedModels;\n\t\tthis.scope = scopedModels.length > 0 ? \"scoped\" : \"all\";\n\t\tthis.onSelectCallback = onSelect;\n\t\tthis.onCancelCallback = onCancel;\n\n\t\t// Add top border\n\t\tthis.addChild(new DynamicBorder());\n\t\tthis.addChild(new Spacer(1));\n\n\t\t// Add hint about model filtering\n\t\tif (scopedModels.length > 0) {\n\t\t\tthis.scopeText = new Text(this.getScopeText(), 0, 0);\n\t\t\tthis.addChild(this.scopeText);\n\t\t\tthis.scopeHintText = new Text(this.getScopeHintText(), 0, 0);\n\t\t\tthis.addChild(this.scopeHintText);\n\t\t} else {\n\t\t\tconst hintText = \"Only showing models from configured providers. Use /login to add providers.\";\n\t\t\tthis.addChild(new Text(theme.fg(\"warning\", hintText), 0, 0));\n\t\t}\n\t\tthis.addChild(new Spacer(1));\n\n\t\t// Create search input\n\t\tthis.searchInput = new Input();\n\t\tif (initialSearchInput) {\n\t\t\tthis.searchInput.setValue(initialSearchInput);\n\t\t}\n\t\tthis.searchInput.onSubmit = () => {\n\t\t\t// Enter on search input selects the first filtered item\n\t\t\tif (this.filteredModels[this.selectedIndex]) {\n\t\t\t\tthis.handleSelect(this.filteredModels[this.selectedIndex].model);\n\t\t\t}\n\t\t};\n\t\tthis.addChild(this.searchInput);\n\n\t\tthis.addChild(new Spacer(1));\n\n\t\t// Create list container\n\t\tthis.listContainer = new Container();\n\t\tthis.addChild(this.listContainer);\n\n\t\tthis.addChild(new Spacer(1));\n\n\t\t// Add bottom border\n\t\tthis.addChild(new DynamicBorder());\n\n\t\t// Render the current snapshot immediately, then refresh in the background.\n\t\tthis.loadModelsFromSnapshot();\n\t\tif (initialSearchInput) this.filterModels(initialSearchInput);\n\t\telse this.updateList();\n\t\tthis.tui.requestRender();\n\t\tvoid this.refreshModels();\n\t}\n\n\tprivate loadModelsFromSnapshot(): void {\n\t\tconst models = this.modelRuntime.getAvailableSnapshot().map((model: Model<any>) => ({\n\t\t\tprovider: model.provider,\n\t\t\tid: model.id,\n\t\t\tmodel,\n\t\t}));\n\t\tthis.allModels = this.sortModels(models);\n\t\tthis.scopedModels = this.scopedModels.map((scoped) => {\n\t\t\tconst refreshed = this.modelRuntime.getModel(scoped.model.provider, scoped.model.id);\n\t\t\treturn refreshed ? { ...scoped, model: refreshed } : scoped;\n\t\t});\n\t\tthis.scopedModelItems = this.scopedModels.map((scoped) => ({\n\t\t\tprovider: scoped.model.provider,\n\t\t\tid: scoped.model.id,\n\t\t\tmodel: scoped.model,\n\t\t}));\n\t\tthis.activeModels = this.scope === \"scoped\" ? this.scopedModelItems : this.allModels;\n\t\tthis.filteredModels = this.activeModels;\n\t\tconst currentIndex = this.filteredModels.findIndex((item) => modelsAreEqual(this.currentModel, item.model));\n\t\tthis.selectedIndex =\n\t\t\tcurrentIndex >= 0 ? currentIndex : Math.min(this.selectedIndex, Math.max(0, this.filteredModels.length - 1));\n\t}\n\n\tprivate async refreshModels(): Promise<void> {\n\t\tconst timeoutMs = 15_000;\n\t\tlet timedOut = false;\n\t\tthis.refreshTimeout = setTimeout(() => {\n\t\t\ttimedOut = true;\n\t\t\tthis.refreshAbortController.abort();\n\t\t}, timeoutMs);\n\t\ttry {\n\t\t\tconst result = await refreshModelCatalogs(this.modelRuntime, this.refreshAbortController.signal);\n\t\t\tif (this.closed) return;\n\t\t\tthis.refreshStatusMessage = \"\";\n\t\t\tif (result.aborted && timedOut) {\n\t\t\t\tthis.errorMessage = \"Model refresh timed out; showing cached models.\";\n\t\t\t} else if (result.errors.size === 1) {\n\t\t\t\tthis.errorMessage = `Could not refresh ${result.errors.keys().next().value}; showing cached models.`;\n\t\t\t} else if (result.errors.size > 1) {\n\t\t\t\tthis.errorMessage = `Could not refresh ${result.errors.size} model catalogs (${[...result.errors.keys()].join(\", \")}); showing cached models.`;\n\t\t\t} else {\n\t\t\t\tthis.errorMessage = this.modelRuntime.getError();\n\t\t\t\tif (!this.errorMessage) {\n\t\t\t\t\tthis.refreshStatusMessage = \"Model catalogs refreshed.\";\n\t\t\t\t\tthis.refreshStatusSuccess = true;\n\t\t\t\t}\n\t\t\t}\n\t\t\tthis.loadModelsFromSnapshot();\n\t\t\tthis.filterModels(this.searchInput.getValue());\n\t\t\tthis.tui.requestRender();\n\t\t} catch (error) {\n\t\t\tif (this.closed) return;\n\t\t\tthis.refreshStatusMessage = \"\";\n\t\t\tthis.errorMessage = timedOut\n\t\t\t\t? \"Model refresh timed out; showing cached models.\"\n\t\t\t\t: `Could not refresh model catalogs: ${error instanceof Error ? error.message : String(error)}`;\n\t\t\tthis.updateList();\n\t\t\tthis.tui.requestRender();\n\t\t} finally {\n\t\t\tif (this.refreshTimeout) clearTimeout(this.refreshTimeout);\n\t\t}\n\t}\n\n\tdispose(): void {\n\t\tif (this.closed) return;\n\t\tthis.closed = true;\n\t\tif (this.refreshTimeout) clearTimeout(this.refreshTimeout);\n\t\tthis.refreshAbortController.abort();\n\t}\n\n\tprivate sortModels(models: ModelItem[]): ModelItem[] {\n\t\tconst sorted = [...models];\n\t\t// Sort: current model first, then by provider\n\t\tsorted.sort((a, b) => {\n\t\t\tconst aIsCurrent = modelsAreEqual(this.currentModel, a.model);\n\t\t\tconst bIsCurrent = modelsAreEqual(this.currentModel, b.model);\n\t\t\tif (aIsCurrent && !bIsCurrent) return -1;\n\t\t\tif (!aIsCurrent && bIsCurrent) return 1;\n\t\t\treturn a.provider.localeCompare(b.provider);\n\t\t});\n\t\treturn sorted;\n\t}\n\n\tprivate getScopeText(): string {\n\t\tconst allText = this.scope === \"all\" ? theme.fg(\"accent\", \"all\") : theme.fg(\"muted\", \"all\");\n\t\tconst scopedText = this.scope === \"scoped\" ? theme.fg(\"accent\", \"scoped\") : theme.fg(\"muted\", \"scoped\");\n\t\treturn `${theme.fg(\"muted\", \"Scope: \")}${allText}${theme.fg(\"muted\", \" | \")}${scopedText}`;\n\t}\n\n\tprivate getScopeHintText(): string {\n\t\treturn keyHint(\"tui.input.tab\", \"scope\") + theme.fg(\"muted\", \" (all/scoped)\");\n\t}\n\n\tprivate setScope(scope: ModelScope): void {\n\t\tif (this.scope === scope) return;\n\t\tthis.scope = scope;\n\t\tthis.activeModels = this.scope === \"scoped\" ? this.scopedModelItems : this.allModels;\n\t\tconst currentIndex = this.activeModels.findIndex((item) => modelsAreEqual(this.currentModel, item.model));\n\t\tthis.selectedIndex = currentIndex >= 0 ? currentIndex : 0;\n\t\tthis.filterModels(this.searchInput.getValue());\n\t\tif (this.scopeText) {\n\t\t\tthis.scopeText.setText(this.getScopeText());\n\t\t}\n\t}\n\n\tprivate filterModels(query: string): void {\n\t\tthis.filteredModels = query\n\t\t\t? fuzzyFilter(this.activeModels, query, ({ id, provider, model }) =>\n\t\t\t\t\tgetModelSelectorSearchText({ id, provider, name: model.name }),\n\t\t\t\t)\n\t\t\t: this.activeModels;\n\t\t// When filtering by a query, move the selector to the top row so the best\n\t\t// match is highlighted. When the query is cleared, keep the current position\n\t\t// clamped to the (restored) list length.\n\t\tthis.selectedIndex = query ? 0 : Math.min(this.selectedIndex, Math.max(0, this.filteredModels.length - 1));\n\t\tthis.updateList();\n\t}\n\n\tprivate updateList(): void {\n\t\tthis.listContainer.clear();\n\n\t\tconst maxVisible = 10;\n\t\tconst startIndex = Math.max(\n\t\t\t0,\n\t\t\tMath.min(this.selectedIndex - Math.floor(maxVisible / 2), this.filteredModels.length - maxVisible),\n\t\t);\n\t\tconst endIndex = Math.min(startIndex + maxVisible, this.filteredModels.length);\n\n\t\t// Show visible slice of filtered models\n\t\tfor (let i = startIndex; i < endIndex; i++) {\n\t\t\tconst item = this.filteredModels[i];\n\t\t\tif (!item) continue;\n\n\t\t\tconst isSelected = i === this.selectedIndex;\n\t\t\tconst isCurrent = modelsAreEqual(this.currentModel, item.model);\n\n\t\t\tlet line = \"\";\n\t\t\tif (isSelected) {\n\t\t\t\tconst prefix = theme.fg(\"accent\", \"→ \");\n\t\t\t\tconst modelText = `${item.id}`;\n\t\t\t\tconst providerBadge = theme.fg(\"muted\", `[${item.provider}]`);\n\t\t\t\tconst checkmark = isCurrent ? theme.fg(\"success\", \" ✓\") : \"\";\n\t\t\t\tline = `${prefix + theme.fg(\"accent\", modelText)} ${providerBadge}${checkmark}`;\n\t\t\t} else {\n\t\t\t\tconst modelText = ` ${item.id}`;\n\t\t\t\tconst providerBadge = theme.fg(\"muted\", `[${item.provider}]`);\n\t\t\t\tconst checkmark = isCurrent ? theme.fg(\"success\", \" ✓\") : \"\";\n\t\t\t\tline = `${modelText} ${providerBadge}${checkmark}`;\n\t\t\t}\n\n\t\t\tthis.listContainer.addChild(new Text(line, 0, 0));\n\t\t}\n\n\t\t// Add scroll indicator if needed\n\t\tif (startIndex > 0 || endIndex < this.filteredModels.length) {\n\t\t\tconst scrollInfo = theme.fg(\"muted\", ` (${this.selectedIndex + 1}/${this.filteredModels.length})`);\n\t\t\tthis.listContainer.addChild(new Text(scrollInfo, 0, 0));\n\t\t}\n\n\t\t// Show error message or \"no results\" if empty\n\t\tif (this.errorMessage) {\n\t\t\t// Show error in red\n\t\t\tconst errorLines = this.errorMessage.split(\"\\n\");\n\t\t\tfor (const line of errorLines) {\n\t\t\t\tthis.listContainer.addChild(new Text(theme.fg(\"error\", line), 0, 0));\n\t\t\t}\n\t\t} else if (this.filteredModels.length === 0) {\n\t\t\tthis.listContainer.addChild(new Text(theme.fg(\"muted\", \" No matching models\"), 0, 0));\n\t\t} else {\n\t\t\tconst selected = this.filteredModels[this.selectedIndex];\n\t\t\tthis.listContainer.addChild(new Spacer(1));\n\t\t\tthis.listContainer.addChild(new Text(theme.fg(\"muted\", ` Model Name: ${selected.model.name}`), 0, 0));\n\t\t}\n\t\tif (this.refreshStatusMessage) {\n\t\t\tthis.listContainer.addChild(new Spacer(1));\n\t\t\tthis.listContainer.addChild(\n\t\t\t\tnew Text(theme.fg(this.refreshStatusSuccess ? \"success\" : \"muted\", ` ${this.refreshStatusMessage}`), 0, 0),\n\t\t\t);\n\t\t}\n\t}\n\n\thandleInput(keyData: string): void {\n\t\tconst kb = getKeybindings();\n\t\tif (kb.matches(keyData, \"tui.input.tab\")) {\n\t\t\tif (this.scopedModelItems.length > 0) {\n\t\t\t\tconst nextScope: ModelScope = this.scope === \"all\" ? \"scoped\" : \"all\";\n\t\t\t\tthis.setScope(nextScope);\n\t\t\t\tif (this.scopeHintText) {\n\t\t\t\t\tthis.scopeHintText.setText(this.getScopeHintText());\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\t// Up arrow - wrap to bottom when at top\n\t\tif (kb.matches(keyData, \"tui.select.up\")) {\n\t\t\tif (this.filteredModels.length === 0) return;\n\t\t\tthis.selectedIndex = this.selectedIndex === 0 ? this.filteredModels.length - 1 : this.selectedIndex - 1;\n\t\t\tthis.updateList();\n\t\t}\n\t\t// Down arrow - wrap to top when at bottom\n\t\telse if (kb.matches(keyData, \"tui.select.down\")) {\n\t\t\tif (this.filteredModels.length === 0) return;\n\t\t\tthis.selectedIndex = this.selectedIndex === this.filteredModels.length - 1 ? 0 : this.selectedIndex + 1;\n\t\t\tthis.updateList();\n\t\t}\n\t\t// Enter\n\t\telse if (kb.matches(keyData, \"tui.select.confirm\")) {\n\t\t\tconst selectedModel = this.filteredModels[this.selectedIndex];\n\t\t\tif (selectedModel) {\n\t\t\t\tthis.handleSelect(selectedModel.model);\n\t\t\t}\n\t\t}\n\t\t// Escape or Ctrl+C\n\t\telse if (kb.matches(keyData, \"tui.select.cancel\")) {\n\t\t\tthis.dispose();\n\t\t\tthis.onCancelCallback();\n\t\t}\n\t\t// Pass everything else to search input\n\t\telse {\n\t\t\tthis.searchInput.handleInput(keyData);\n\t\t\tthis.filterModels(this.searchInput.getValue());\n\t\t}\n\t}\n\n\tprivate handleSelect(model: Model<any>): void {\n\t\tthis.dispose();\n\t\t// Save as new default\n\t\tthis.settingsManager.setDefaultModelAndProvider(model.provider, model.id);\n\t\tthis.onSelectCallback(model);\n\t}\n\n\tgetSearchInput(): Input {\n\t\treturn this.searchInput;\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"model-selector.js","sourceRoot":"","sources":["../../../../src/modes/interactive/components/model-selector.ts"],"names":[],"mappings":"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","sourcesContent":["import { type Model, modelsAreEqual } from \"@earendil-works/pi-ai\";\nimport {\n\tContainer,\n\ttype Focusable,\n\tfuzzyFilter,\n\tgetKeybindings,\n\tInput,\n\tmatchesKey,\n\tSpacer,\n\tText,\n\ttype TUI,\n} from \"@earendil-works/pi-tui\";\nimport type { ModelRuntime } from \"../../../core/model-runtime.ts\";\nimport { refreshModelCatalogs } from \"../model-catalog-refresh.ts\";\nimport { getModelSelectorSearchText } from \"../model-search.ts\";\nimport { theme } from \"../theme/theme.ts\";\nimport { DynamicBorder } from \"./dynamic-border.ts\";\nimport { keyHint } from \"./keybinding-hints.ts\";\n\ninterface ModelItem {\n\tprovider: string;\n\tid: string;\n\tmodel: Model<any>;\n}\n\ninterface ScopedModelItem {\n\tmodel: Model<any>;\n\tthinkingLevel?: string;\n}\n\ntype ModelScope = \"all\" | \"scoped\";\n\n/**\n * Component that renders a model selector with search\n */\nexport class ModelSelectorComponent extends Container implements Focusable {\n\tprivate searchInput: Input;\n\n\t// Focusable implementation - propagate to searchInput for IME cursor positioning\n\tprivate _focused = false;\n\tget focused(): boolean {\n\t\treturn this._focused;\n\t}\n\tset focused(value: boolean) {\n\t\tthis._focused = value;\n\t\tthis.searchInput.focused = value;\n\t}\n\tprivate listContainer: Container;\n\tprivate allModels: ModelItem[] = [];\n\tprivate scopedModelItems: ModelItem[] = [];\n\tprivate activeModels: ModelItem[] = [];\n\tprivate filteredModels: ModelItem[] = [];\n\tprivate selectedIndex: number = 0;\n\tprivate currentModel?: Model<any>;\n\tprivate modelRuntime: ModelRuntime;\n\tprivate onSelectCallback: (model: Model<any>) => void;\n\tprivate onSelectAsDefaultCallback?: (model: Model<any>) => void;\n\tprivate onCancelCallback: () => void;\n\tprivate errorMessage?: string;\n\tprivate refreshStatusMessage = \"Refreshing model catalogs…\";\n\tprivate refreshStatusSuccess = false;\n\tprivate tui: TUI;\n\tprivate scopedModels: ReadonlyArray<ScopedModelItem>;\n\tprivate scope: ModelScope = \"all\";\n\tprivate scopeText?: Text;\n\tprivate scopeHintText?: Text;\n\tprivate readonly refreshAbortController = new AbortController();\n\tprivate refreshTimeout?: ReturnType<typeof setTimeout>;\n\tprivate closed = false;\n\n\tconstructor(\n\t\ttui: TUI,\n\t\tcurrentModel: Model<any> | undefined,\n\t\tmodelRuntime: ModelRuntime,\n\t\tscopedModels: ReadonlyArray<ScopedModelItem>,\n\t\tonSelect: (model: Model<any>) => void,\n\t\tonCancel: () => void,\n\t\tinitialSearchInput?: string,\n\t\tonSelectAsDefault?: (model: Model<any>) => void,\n\t) {\n\t\tsuper();\n\n\t\tthis.tui = tui;\n\t\tthis.currentModel = currentModel;\n\t\tthis.modelRuntime = modelRuntime;\n\t\tthis.scopedModels = scopedModels;\n\t\tthis.scope = scopedModels.length > 0 ? \"scoped\" : \"all\";\n\t\tthis.onSelectCallback = onSelect;\n\t\tthis.onSelectAsDefaultCallback = onSelectAsDefault;\n\t\tthis.onCancelCallback = onCancel;\n\n\t\t// Add top border\n\t\tthis.addChild(new DynamicBorder());\n\t\tthis.addChild(new Spacer(1));\n\n\t\t// Add hint about model filtering\n\t\tif (scopedModels.length > 0) {\n\t\t\tthis.scopeText = new Text(this.getScopeText(), 0, 0);\n\t\t\tthis.addChild(this.scopeText);\n\t\t\tthis.scopeHintText = new Text(this.getScopeHintText(), 0, 0);\n\t\t\tthis.addChild(this.scopeHintText);\n\t\t} else {\n\t\t\tconst hintText = \"Only showing models from configured providers. Use /login to add providers.\";\n\t\t\tthis.addChild(new Text(theme.fg(\"warning\", hintText), 0, 0));\n\t\t}\n\t\tthis.addChild(new Spacer(1));\n\n\t\t// Create search input\n\t\tthis.searchInput = new Input();\n\t\tif (initialSearchInput) {\n\t\t\tthis.searchInput.setValue(initialSearchInput);\n\t\t}\n\t\tthis.searchInput.onSubmit = () => {\n\t\t\t// Enter on search input selects the first filtered item\n\t\t\tif (this.filteredModels[this.selectedIndex]) {\n\t\t\t\tthis.handleSelect(this.filteredModels[this.selectedIndex].model);\n\t\t\t}\n\t\t};\n\t\tthis.addChild(this.searchInput);\n\n\t\tthis.addChild(new Spacer(1));\n\n\t\t// Create list container\n\t\tthis.listContainer = new Container();\n\t\tthis.addChild(this.listContainer);\n\n\t\tthis.addChild(new Spacer(1));\n\n\t\t// Hint\n\t\tif (this.onSelectAsDefaultCallback) {\n\t\t\tthis.addChild(\n\t\t\t\tnew Text(theme.fg(\"dim\", \" Enter to select \\u00b7 Ctrl+S to set as default \\u00b7 Esc to cancel\"), 0, 0),\n\t\t\t);\n\t\t}\n\n\t\t// Add bottom border\n\t\tthis.addChild(new DynamicBorder());\n\n\t\t// Render the current snapshot immediately, then refresh in the background.\n\t\tthis.loadModelsFromSnapshot();\n\t\tif (initialSearchInput) this.filterModels(initialSearchInput);\n\t\telse this.updateList();\n\t\tthis.tui.requestRender();\n\t\tvoid this.refreshModels();\n\t}\n\n\tprivate loadModelsFromSnapshot(): void {\n\t\tconst models = this.modelRuntime.getAvailableSnapshot().map((model: Model<any>) => ({\n\t\t\tprovider: model.provider,\n\t\t\tid: model.id,\n\t\t\tmodel,\n\t\t}));\n\t\tthis.allModels = this.sortModels(models);\n\t\tthis.scopedModels = this.scopedModels.map((scoped) => {\n\t\t\tconst refreshed = this.modelRuntime.getModel(scoped.model.provider, scoped.model.id);\n\t\t\treturn refreshed ? { ...scoped, model: refreshed } : scoped;\n\t\t});\n\t\tthis.scopedModelItems = this.scopedModels.map((scoped) => ({\n\t\t\tprovider: scoped.model.provider,\n\t\t\tid: scoped.model.id,\n\t\t\tmodel: scoped.model,\n\t\t}));\n\t\tthis.activeModels = this.scope === \"scoped\" ? this.scopedModelItems : this.allModels;\n\t\tthis.filteredModels = this.activeModels;\n\t\tconst currentIndex = this.filteredModels.findIndex((item) => modelsAreEqual(this.currentModel, item.model));\n\t\tthis.selectedIndex =\n\t\t\tcurrentIndex >= 0 ? currentIndex : Math.min(this.selectedIndex, Math.max(0, this.filteredModels.length - 1));\n\t}\n\n\tprivate async refreshModels(): Promise<void> {\n\t\tconst timeoutMs = 15_000;\n\t\tlet timedOut = false;\n\t\tthis.refreshTimeout = setTimeout(() => {\n\t\t\ttimedOut = true;\n\t\t\tthis.refreshAbortController.abort();\n\t\t}, timeoutMs);\n\t\ttry {\n\t\t\tconst result = await refreshModelCatalogs(this.modelRuntime, this.refreshAbortController.signal);\n\t\t\tif (this.closed) return;\n\t\t\tthis.refreshStatusMessage = \"\";\n\t\t\tif (result.aborted && timedOut) {\n\t\t\t\tthis.errorMessage = \"Model refresh timed out; showing cached models.\";\n\t\t\t} else if (result.errors.size === 1) {\n\t\t\t\tthis.errorMessage = `Could not refresh ${result.errors.keys().next().value}; showing cached models.`;\n\t\t\t} else if (result.errors.size > 1) {\n\t\t\t\tthis.errorMessage = `Could not refresh ${result.errors.size} model catalogs (${[...result.errors.keys()].join(\", \")}); showing cached models.`;\n\t\t\t} else {\n\t\t\t\tthis.errorMessage = this.modelRuntime.getError();\n\t\t\t\tif (!this.errorMessage) {\n\t\t\t\t\tthis.refreshStatusMessage = \"Model catalogs refreshed.\";\n\t\t\t\t\tthis.refreshStatusSuccess = true;\n\t\t\t\t}\n\t\t\t}\n\t\t\tthis.loadModelsFromSnapshot();\n\t\t\tthis.filterModels(this.searchInput.getValue());\n\t\t\tthis.tui.requestRender();\n\t\t} catch (error) {\n\t\t\tif (this.closed) return;\n\t\t\tthis.refreshStatusMessage = \"\";\n\t\t\tthis.errorMessage = timedOut\n\t\t\t\t? \"Model refresh timed out; showing cached models.\"\n\t\t\t\t: `Could not refresh model catalogs: ${error instanceof Error ? error.message : String(error)}`;\n\t\t\tthis.updateList();\n\t\t\tthis.tui.requestRender();\n\t\t} finally {\n\t\t\tif (this.refreshTimeout) clearTimeout(this.refreshTimeout);\n\t\t}\n\t}\n\n\tdispose(): void {\n\t\tif (this.closed) return;\n\t\tthis.closed = true;\n\t\tif (this.refreshTimeout) clearTimeout(this.refreshTimeout);\n\t\tthis.refreshAbortController.abort();\n\t}\n\n\tprivate sortModels(models: ModelItem[]): ModelItem[] {\n\t\tconst sorted = [...models];\n\t\t// Sort: current model first, then by provider\n\t\tsorted.sort((a, b) => {\n\t\t\tconst aIsCurrent = modelsAreEqual(this.currentModel, a.model);\n\t\t\tconst bIsCurrent = modelsAreEqual(this.currentModel, b.model);\n\t\t\tif (aIsCurrent && !bIsCurrent) return -1;\n\t\t\tif (!aIsCurrent && bIsCurrent) return 1;\n\t\t\treturn a.provider.localeCompare(b.provider);\n\t\t});\n\t\treturn sorted;\n\t}\n\n\tprivate getScopeText(): string {\n\t\tconst allText = this.scope === \"all\" ? theme.fg(\"accent\", \"all\") : theme.fg(\"muted\", \"all\");\n\t\tconst scopedText = this.scope === \"scoped\" ? theme.fg(\"accent\", \"scoped\") : theme.fg(\"muted\", \"scoped\");\n\t\treturn `${theme.fg(\"muted\", \"Scope: \")}${allText}${theme.fg(\"muted\", \" | \")}${scopedText}`;\n\t}\n\n\tprivate getScopeHintText(): string {\n\t\treturn keyHint(\"tui.input.tab\", \"scope\") + theme.fg(\"muted\", \" (all/scoped)\");\n\t}\n\n\tprivate setScope(scope: ModelScope): void {\n\t\tif (this.scope === scope) return;\n\t\tthis.scope = scope;\n\t\tthis.activeModels = this.scope === \"scoped\" ? this.scopedModelItems : this.allModels;\n\t\tconst currentIndex = this.activeModels.findIndex((item) => modelsAreEqual(this.currentModel, item.model));\n\t\tthis.selectedIndex = currentIndex >= 0 ? currentIndex : 0;\n\t\tthis.filterModels(this.searchInput.getValue());\n\t\tif (this.scopeText) {\n\t\t\tthis.scopeText.setText(this.getScopeText());\n\t\t}\n\t}\n\n\tprivate filterModels(query: string): void {\n\t\tthis.filteredModels = query\n\t\t\t? fuzzyFilter(this.activeModels, query, ({ id, provider, model }) =>\n\t\t\t\t\tgetModelSelectorSearchText({ id, provider, name: model.name }),\n\t\t\t\t)\n\t\t\t: this.activeModels;\n\t\t// When filtering by a query, move the selector to the top row so the best\n\t\t// match is highlighted. When the query is cleared, keep the current position\n\t\t// clamped to the (restored) list length.\n\t\tthis.selectedIndex = query ? 0 : Math.min(this.selectedIndex, Math.max(0, this.filteredModels.length - 1));\n\t\tthis.updateList();\n\t}\n\n\tprivate updateList(): void {\n\t\tthis.listContainer.clear();\n\n\t\tconst maxVisible = 10;\n\t\tconst startIndex = Math.max(\n\t\t\t0,\n\t\t\tMath.min(this.selectedIndex - Math.floor(maxVisible / 2), this.filteredModels.length - maxVisible),\n\t\t);\n\t\tconst endIndex = Math.min(startIndex + maxVisible, this.filteredModels.length);\n\n\t\t// Show visible slice of filtered models\n\t\tfor (let i = startIndex; i < endIndex; i++) {\n\t\t\tconst item = this.filteredModels[i];\n\t\t\tif (!item) continue;\n\n\t\t\tconst isSelected = i === this.selectedIndex;\n\t\t\tconst isCurrent = modelsAreEqual(this.currentModel, item.model);\n\n\t\t\tlet line = \"\";\n\t\t\tif (isSelected) {\n\t\t\t\tconst prefix = theme.fg(\"accent\", \"→ \");\n\t\t\t\tconst modelText = `${item.id}`;\n\t\t\t\tconst providerBadge = theme.fg(\"muted\", `[${item.provider}]`);\n\t\t\t\tconst checkmark = isCurrent ? theme.fg(\"success\", \" ✓\") : \"\";\n\t\t\t\tline = `${prefix + theme.fg(\"accent\", modelText)} ${providerBadge}${checkmark}`;\n\t\t\t} else {\n\t\t\t\tconst modelText = ` ${item.id}`;\n\t\t\t\tconst providerBadge = theme.fg(\"muted\", `[${item.provider}]`);\n\t\t\t\tconst checkmark = isCurrent ? theme.fg(\"success\", \" ✓\") : \"\";\n\t\t\t\tline = `${modelText} ${providerBadge}${checkmark}`;\n\t\t\t}\n\n\t\t\tthis.listContainer.addChild(new Text(line, 0, 0));\n\t\t}\n\n\t\t// Add scroll indicator if needed\n\t\tif (startIndex > 0 || endIndex < this.filteredModels.length) {\n\t\t\tconst scrollInfo = theme.fg(\"muted\", ` (${this.selectedIndex + 1}/${this.filteredModels.length})`);\n\t\t\tthis.listContainer.addChild(new Text(scrollInfo, 0, 0));\n\t\t}\n\n\t\t// Show error message or \"no results\" if empty\n\t\tif (this.errorMessage) {\n\t\t\t// Show error in red\n\t\t\tconst errorLines = this.errorMessage.split(\"\\n\");\n\t\t\tfor (const line of errorLines) {\n\t\t\t\tthis.listContainer.addChild(new Text(theme.fg(\"error\", line), 0, 0));\n\t\t\t}\n\t\t} else if (this.filteredModels.length === 0) {\n\t\t\tthis.listContainer.addChild(new Text(theme.fg(\"muted\", \" No matching models\"), 0, 0));\n\t\t} else {\n\t\t\tconst selected = this.filteredModels[this.selectedIndex];\n\t\t\tthis.listContainer.addChild(new Spacer(1));\n\t\t\tthis.listContainer.addChild(new Text(theme.fg(\"muted\", ` Model Name: ${selected.model.name}`), 0, 0));\n\t\t}\n\t\tif (this.refreshStatusMessage) {\n\t\t\tthis.listContainer.addChild(new Spacer(1));\n\t\t\tthis.listContainer.addChild(\n\t\t\t\tnew Text(theme.fg(this.refreshStatusSuccess ? \"success\" : \"muted\", ` ${this.refreshStatusMessage}`), 0, 0),\n\t\t\t);\n\t\t}\n\t}\n\n\thandleInput(keyData: string): void {\n\t\tconst kb = getKeybindings();\n\t\tif (kb.matches(keyData, \"tui.input.tab\")) {\n\t\t\tif (this.scopedModelItems.length > 0) {\n\t\t\t\tconst nextScope: ModelScope = this.scope === \"all\" ? \"scoped\" : \"all\";\n\t\t\t\tthis.setScope(nextScope);\n\t\t\t\tif (this.scopeHintText) {\n\t\t\t\t\tthis.scopeHintText.setText(this.getScopeHintText());\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\t// Up arrow - wrap to bottom when at top\n\t\tif (kb.matches(keyData, \"tui.select.up\")) {\n\t\t\tif (this.filteredModels.length === 0) return;\n\t\t\tthis.selectedIndex = this.selectedIndex === 0 ? this.filteredModels.length - 1 : this.selectedIndex - 1;\n\t\t\tthis.updateList();\n\t\t}\n\t\t// Down arrow - wrap to top when at bottom\n\t\telse if (kb.matches(keyData, \"tui.select.down\")) {\n\t\t\tif (this.filteredModels.length === 0) return;\n\t\t\tthis.selectedIndex = this.selectedIndex === this.filteredModels.length - 1 ? 0 : this.selectedIndex + 1;\n\t\t\tthis.updateList();\n\t\t}\n\t\t// Enter\n\t\telse if (kb.matches(keyData, \"tui.select.confirm\")) {\n\t\t\tconst selectedModel = this.filteredModels[this.selectedIndex];\n\t\t\tif (selectedModel) {\n\t\t\t\tthis.handleSelect(selectedModel.model);\n\t\t\t}\n\t\t}\n\t\t// Escape or Ctrl+C\n\t\telse if (kb.matches(keyData, \"tui.select.cancel\")) {\n\t\t\tthis.dispose();\n\t\t\tthis.onCancelCallback();\n\t\t}\n\t\t// Ctrl+S — select and save as default\n\t\telse if (matchesKey(keyData, \"ctrl+s\") && this.onSelectAsDefaultCallback) {\n\t\t\tconst selectedModel = this.filteredModels[this.selectedIndex];\n\t\t\tif (selectedModel) {\n\t\t\t\tthis.dispose();\n\t\t\t\tthis.onSelectAsDefaultCallback(selectedModel.model);\n\t\t\t}\n\t\t}\n\t\t// Pass everything else to search input\n\t\telse {\n\t\t\tthis.searchInput.handleInput(keyData);\n\t\t\tthis.filterModels(this.searchInput.getValue());\n\t\t}\n\t}\n\n\tprivate handleSelect(model: Model<any>): void {\n\t\tthis.dispose();\n\t\tthis.onSelectCallback(model);\n\t}\n\n\tgetSearchInput(): Input {\n\t\treturn this.searchInput;\n\t}\n}\n"]}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { Container, type TUI } from "@earendil-works/pi-tui";
|
|
2
|
+
/**
|
|
3
|
+
* Muestra en el chat del CLI cómo el optimizador autónomo de prompts
|
|
4
|
+
* mejoró la petición del usuario (antes → después) o qué preguntas
|
|
5
|
+
* necesita para dejar el prompt sin huecos.
|
|
6
|
+
*/
|
|
7
|
+
export declare class PromptOptimization extends Container {
|
|
8
|
+
constructor(_ui: TUI, event: {
|
|
9
|
+
decision: "clarify" | "enhance";
|
|
10
|
+
original: string;
|
|
11
|
+
enhanced?: string;
|
|
12
|
+
questions?: string[];
|
|
13
|
+
summary?: string;
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=prompt-optimization.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prompt-optimization.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/prompt-optimization.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAgB,KAAK,GAAG,EAAE,MAAM,wBAAwB,CAAC;AAI3E;;;;GAIG;AACH,qBAAa,kBAAmB,SAAQ,SAAS;IAChD,YACC,GAAG,EAAE,GAAG,EACR,KAAK,EAAE;QACN,QAAQ,EAAE,SAAS,GAAG,SAAS,CAAC;QAChC,QAAQ,EAAE,MAAM,CAAC;QACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;QACrB,OAAO,CAAC,EAAE,MAAM,CAAC;KACjB,EAmDD;CACD","sourcesContent":["import { Container, Spacer, Text, type TUI } from \"@earendil-works/pi-tui\";\nimport { theme } from \"../theme/theme.ts\";\nimport { DynamicBorder } from \"./dynamic-border.ts\";\n\n/**\n * Muestra en el chat del CLI cómo el optimizador autónomo de prompts\n * mejoró la petición del usuario (antes → después) o qué preguntas\n * necesita para dejar el prompt sin huecos.\n */\nexport class PromptOptimization extends Container {\n\tconstructor(\n\t\t_ui: TUI,\n\t\tevent: {\n\t\t\tdecision: \"clarify\" | \"enhance\";\n\t\t\toriginal: string;\n\t\t\tenhanced?: string;\n\t\t\tquestions?: string[];\n\t\t\tsummary?: string;\n\t\t},\n\t) {\n\t\tsuper();\n\t\tconst borderColor =\n\t\t\tevent.decision === \"clarify\" ? (s: string) => theme.fg(\"warning\", s) : (s: string) => theme.fg(\"accent\", s);\n\n\t\tthis.addChild(new Spacer(1));\n\t\tthis.addChild(new DynamicBorder(borderColor));\n\n\t\tconst content = new Container();\n\t\tthis.addChild(content);\n\n\t\t// Header\n\t\tcontent.addChild(\n\t\t\tnew Text(\n\t\t\t\ttheme.fg(\n\t\t\t\t\tevent.decision === \"clarify\" ? \"warning\" : \"accent\",\n\t\t\t\t\ttheme.bold(\n\t\t\t\t\t\tevent.decision === \"clarify\"\n\t\t\t\t\t\t\t? \"✻ Optimizador: necesito aclarar\"\n\t\t\t\t\t\t\t: \"✻ Prompt mejorado automáticamente\",\n\t\t\t\t\t),\n\t\t\t\t),\n\t\t\t\t1,\n\t\t\t\t0,\n\t\t\t),\n\t\t);\n\t\tif (event.summary) {\n\t\t\tcontent.addChild(new Text(theme.fg(\"muted\", event.summary), 1, 0));\n\t\t}\n\t\tcontent.addChild(new Spacer(1));\n\n\t\tif (event.decision === \"enhance\" && event.enhanced) {\n\t\t\tcontent.addChild(new Text(theme.fg(\"muted\", \"▸ Prompt original:\"), 1, 0));\n\t\t\tfor (const line of event.original.split(\"\\n\")) {\n\t\t\t\tcontent.addChild(new Text(theme.fg(\"dim\", line), 1, 0));\n\t\t\t}\n\t\t\tcontent.addChild(new Spacer(1));\n\t\t\tcontent.addChild(new Text(theme.fg(\"accent\", \"▸ Prompt enviado al modelo:\"), 1, 0));\n\t\t\tfor (const line of event.enhanced.split(\"\\n\")) {\n\t\t\t\tcontent.addChild(new Text(theme.fg(\"text\", line), 1, 0));\n\t\t\t}\n\t\t} else if (event.decision === \"clarify\" && event.questions?.length) {\n\t\t\tcontent.addChild(new Text(theme.fg(\"muted\", \"Preguntas:\"), 1, 0));\n\t\t\tevent.questions.forEach((q, i) => {\n\t\t\t\tcontent.addChild(new Text(theme.fg(\"text\", `${i + 1}. ${q}`), 1, 0));\n\t\t\t});\n\t\t\tcontent.addChild(new Text(theme.fg(\"muted\", \"Responde y continuaré con el prompt mejorado.\"), 1, 0));\n\t\t}\n\n\t\tthis.addChild(new DynamicBorder(borderColor));\n\t}\n}\n"]}
|