openvibe 0.60.2 → 0.60.3
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/dist/cli/args.d.ts +1 -1
- package/dist/cli/args.d.ts.map +1 -1
- package/dist/cli/args.js.map +1 -1
- package/dist/cli/config-selector.d.ts.map +1 -1
- package/dist/cli/config-selector.js +1 -1
- package/dist/cli/config-selector.js.map +1 -1
- package/dist/cli/file-processor.d.ts +1 -1
- package/dist/cli/file-processor.d.ts.map +1 -1
- package/dist/cli/file-processor.js.map +1 -1
- package/dist/cli/session-picker.d.ts.map +1 -1
- package/dist/cli/session-picker.js +1 -1
- package/dist/cli/session-picker.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +2 -2
- package/dist/cli.js.map +1 -1
- package/dist/core/accelerated-client.d.ts +1 -1
- package/dist/core/accelerated-client.d.ts.map +1 -1
- package/dist/core/accelerated-client.js +2 -2
- package/dist/core/accelerated-client.js.map +1 -1
- package/dist/core/accelerated-stream.d.ts +1 -1
- package/dist/core/accelerated-stream.d.ts.map +1 -1
- package/dist/core/accelerated-stream.js.map +1 -1
- package/dist/core/agent-session.d.ts +2 -2
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +1 -1
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/api-concurrency.d.ts +1 -1
- package/dist/core/api-concurrency.d.ts.map +1 -1
- package/dist/core/api-concurrency.js +3 -3
- package/dist/core/api-concurrency.js.map +1 -1
- package/dist/core/auth-storage.d.ts +2 -2
- package/dist/core/auth-storage.d.ts.map +1 -1
- package/dist/core/auth-storage.js +2 -2
- package/dist/core/auth-storage.js.map +1 -1
- package/dist/core/branded-ai.d.ts +1 -1
- package/dist/core/branded-ai.d.ts.map +1 -1
- package/dist/core/branded-ai.js.map +1 -1
- package/dist/core/compaction/branch-summarization.d.ts +2 -2
- package/dist/core/compaction/branch-summarization.d.ts.map +1 -1
- package/dist/core/compaction/branch-summarization.js +1 -1
- package/dist/core/compaction/branch-summarization.js.map +1 -1
- package/dist/core/compaction/compaction.d.ts +2 -2
- package/dist/core/compaction/compaction.d.ts.map +1 -1
- package/dist/core/compaction/compaction.js +1 -1
- package/dist/core/compaction/compaction.js.map +1 -1
- package/dist/core/compaction/utils.d.ts +2 -2
- package/dist/core/compaction/utils.d.ts.map +1 -1
- package/dist/core/compaction/utils.js.map +1 -1
- package/dist/core/context-manager.d.ts +1 -1
- package/dist/core/context-manager.d.ts.map +1 -1
- package/dist/core/context-manager.js.map +1 -1
- package/dist/core/context-provider-interface.d.ts +1 -1
- package/dist/core/context-provider-interface.d.ts.map +1 -1
- package/dist/core/context-provider-interface.js.map +1 -1
- package/dist/core/context-provider-registry.d.ts +1 -1
- package/dist/core/context-provider-registry.d.ts.map +1 -1
- package/dist/core/context-provider-registry.js.map +1 -1
- package/dist/core/defaults.d.ts +1 -1
- package/dist/core/defaults.d.ts.map +1 -1
- package/dist/core/defaults.js.map +1 -1
- package/dist/core/export-html/index.d.ts +1 -1
- package/dist/core/export-html/index.d.ts.map +1 -1
- package/dist/core/export-html/index.js.map +1 -1
- package/dist/core/export-html/tool-renderer.d.ts.map +1 -1
- package/dist/core/export-html/tool-renderer.js.map +1 -1
- package/dist/core/extensions/loader.d.ts.map +1 -1
- package/dist/core/extensions/loader.js +14 -14
- package/dist/core/extensions/loader.js.map +1 -1
- package/dist/core/extensions/runner.d.ts +3 -3
- package/dist/core/extensions/runner.d.ts.map +1 -1
- package/dist/core/extensions/runner.js.map +1 -1
- package/dist/core/extensions/types.d.ts +3 -3
- package/dist/core/extensions/types.d.ts.map +1 -1
- package/dist/core/extensions/types.js.map +1 -1
- package/dist/core/extensions/wrapper.d.ts +1 -1
- package/dist/core/extensions/wrapper.d.ts.map +1 -1
- package/dist/core/extensions/wrapper.js.map +1 -1
- package/dist/core/keybindings.d.ts +1 -1
- package/dist/core/keybindings.d.ts.map +1 -1
- package/dist/core/keybindings.js +1 -1
- package/dist/core/keybindings.js.map +1 -1
- package/dist/core/large-context-provider.d.ts +1 -1
- package/dist/core/large-context-provider.d.ts.map +1 -1
- package/dist/core/large-context-provider.js.map +1 -1
- package/dist/core/messages.d.ts +3 -3
- package/dist/core/messages.d.ts.map +1 -1
- package/dist/core/messages.js.map +1 -1
- package/dist/core/model-registry.d.ts +1 -1
- package/dist/core/model-registry.d.ts.map +1 -1
- package/dist/core/model-registry.js +1 -1
- package/dist/core/model-registry.js.map +1 -1
- package/dist/core/model-resolver.d.ts +2 -2
- package/dist/core/model-resolver.d.ts.map +1 -1
- package/dist/core/model-resolver.js.map +1 -1
- package/dist/core/response-accelerator.d.ts +1 -1
- package/dist/core/response-accelerator.d.ts.map +1 -1
- package/dist/core/response-accelerator.js.map +1 -1
- package/dist/core/sdk.d.ts +2 -2
- package/dist/core/sdk.d.ts.map +1 -1
- package/dist/core/sdk.js +1 -1
- package/dist/core/sdk.js.map +1 -1
- package/dist/core/session-manager.d.ts +2 -2
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/settings-manager.d.ts +1 -1
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/system-prompt.d.ts +1 -1
- package/dist/core/system-prompt.d.ts.map +1 -1
- package/dist/core/system-prompt.js.map +1 -1
- package/dist/core/tools/bash.d.ts +1 -1
- package/dist/core/tools/bash.d.ts.map +1 -1
- package/dist/core/tools/bash.js.map +1 -1
- package/dist/core/tools/edit.d.ts +1 -1
- package/dist/core/tools/edit.d.ts.map +1 -1
- package/dist/core/tools/edit.js.map +1 -1
- package/dist/core/tools/fast-executor.d.ts +2 -2
- package/dist/core/tools/fast-executor.d.ts.map +1 -1
- package/dist/core/tools/fast-executor.js.map +1 -1
- package/dist/core/tools/find.d.ts +1 -1
- package/dist/core/tools/find.d.ts.map +1 -1
- package/dist/core/tools/find.js.map +1 -1
- package/dist/core/tools/grep.d.ts +1 -1
- package/dist/core/tools/grep.d.ts.map +1 -1
- package/dist/core/tools/grep.js.map +1 -1
- package/dist/core/tools/index.d.ts +1 -1
- package/dist/core/tools/index.d.ts.map +1 -1
- package/dist/core/tools/index.js.map +1 -1
- package/dist/core/tools/ls.d.ts +1 -1
- package/dist/core/tools/ls.d.ts.map +1 -1
- package/dist/core/tools/ls.js.map +1 -1
- package/dist/core/tools/read.d.ts +1 -1
- package/dist/core/tools/read.d.ts.map +1 -1
- package/dist/core/tools/read.js.map +1 -1
- package/dist/core/tools/write.d.ts +1 -1
- package/dist/core/tools/write.d.ts.map +1 -1
- package/dist/core/tools/write.js.map +1 -1
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +1 -1
- package/dist/main.js.map +1 -1
- package/dist/modes/interactive/components/armin.d.ts +1 -1
- package/dist/modes/interactive/components/armin.d.ts.map +1 -1
- package/dist/modes/interactive/components/armin.js.map +1 -1
- package/dist/modes/interactive/components/assistant-message.d.ts +2 -2
- package/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/assistant-message.js +1 -1
- package/dist/modes/interactive/components/assistant-message.js.map +1 -1
- package/dist/modes/interactive/components/bash-execution.d.ts +1 -1
- package/dist/modes/interactive/components/bash-execution.d.ts.map +1 -1
- package/dist/modes/interactive/components/bash-execution.js +1 -1
- package/dist/modes/interactive/components/bash-execution.js.map +1 -1
- package/dist/modes/interactive/components/bordered-loader.d.ts +1 -1
- package/dist/modes/interactive/components/bordered-loader.d.ts.map +1 -1
- package/dist/modes/interactive/components/bordered-loader.js +1 -1
- package/dist/modes/interactive/components/bordered-loader.js.map +1 -1
- package/dist/modes/interactive/components/branch-summary-message.d.ts +1 -1
- package/dist/modes/interactive/components/branch-summary-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/branch-summary-message.js +1 -1
- package/dist/modes/interactive/components/branch-summary-message.js.map +1 -1
- package/dist/modes/interactive/components/compaction-summary-message.d.ts +1 -1
- package/dist/modes/interactive/components/compaction-summary-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/compaction-summary-message.js +1 -1
- package/dist/modes/interactive/components/compaction-summary-message.js.map +1 -1
- package/dist/modes/interactive/components/config-selector.d.ts +1 -1
- package/dist/modes/interactive/components/config-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/config-selector.js +1 -1
- package/dist/modes/interactive/components/config-selector.js.map +1 -1
- package/dist/modes/interactive/components/countdown-timer.d.ts +1 -1
- package/dist/modes/interactive/components/countdown-timer.d.ts.map +1 -1
- package/dist/modes/interactive/components/countdown-timer.js.map +1 -1
- package/dist/modes/interactive/components/custom-editor.d.ts +1 -1
- package/dist/modes/interactive/components/custom-editor.d.ts.map +1 -1
- package/dist/modes/interactive/components/custom-editor.js +1 -1
- package/dist/modes/interactive/components/custom-editor.js.map +1 -1
- package/dist/modes/interactive/components/custom-message.d.ts +1 -1
- package/dist/modes/interactive/components/custom-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/custom-message.js +1 -1
- package/dist/modes/interactive/components/custom-message.js.map +1 -1
- package/dist/modes/interactive/components/daxnuts.d.ts +1 -1
- package/dist/modes/interactive/components/daxnuts.d.ts.map +1 -1
- package/dist/modes/interactive/components/daxnuts.js.map +1 -1
- package/dist/modes/interactive/components/dynamic-border.d.ts +1 -1
- package/dist/modes/interactive/components/dynamic-border.d.ts.map +1 -1
- package/dist/modes/interactive/components/dynamic-border.js.map +1 -1
- package/dist/modes/interactive/components/extension-editor.d.ts +1 -1
- package/dist/modes/interactive/components/extension-editor.d.ts.map +1 -1
- package/dist/modes/interactive/components/extension-editor.js +1 -1
- package/dist/modes/interactive/components/extension-editor.js.map +1 -1
- package/dist/modes/interactive/components/extension-input.d.ts +1 -1
- package/dist/modes/interactive/components/extension-input.d.ts.map +1 -1
- package/dist/modes/interactive/components/extension-input.js +1 -1
- package/dist/modes/interactive/components/extension-input.js.map +1 -1
- package/dist/modes/interactive/components/extension-selector.d.ts +1 -1
- package/dist/modes/interactive/components/extension-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/extension-selector.js +1 -1
- package/dist/modes/interactive/components/extension-selector.js.map +1 -1
- package/dist/modes/interactive/components/footer.d.ts +1 -1
- package/dist/modes/interactive/components/footer.d.ts.map +1 -1
- package/dist/modes/interactive/components/footer.js +1 -1
- package/dist/modes/interactive/components/footer.js.map +1 -1
- package/dist/modes/interactive/components/keybinding-hints.d.ts +1 -1
- package/dist/modes/interactive/components/keybinding-hints.d.ts.map +1 -1
- package/dist/modes/interactive/components/keybinding-hints.js +1 -1
- package/dist/modes/interactive/components/keybinding-hints.js.map +1 -1
- package/dist/modes/interactive/components/onboarding-wizard.d.ts +2 -2
- package/dist/modes/interactive/components/onboarding-wizard.d.ts.map +1 -1
- package/dist/modes/interactive/components/onboarding-wizard.js +1 -1
- package/dist/modes/interactive/components/onboarding-wizard.js.map +1 -1
- package/dist/modes/interactive/components/session-selector-search.d.ts.map +1 -1
- package/dist/modes/interactive/components/session-selector-search.js +1 -1
- package/dist/modes/interactive/components/session-selector-search.js.map +1 -1
- package/dist/modes/interactive/components/session-selector.d.ts +1 -1
- package/dist/modes/interactive/components/session-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/session-selector.js +1 -1
- package/dist/modes/interactive/components/session-selector.js.map +1 -1
- package/dist/modes/interactive/components/settings-selector.d.ts +3 -3
- package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/settings-selector.js +1 -1
- package/dist/modes/interactive/components/settings-selector.js.map +1 -1
- package/dist/modes/interactive/components/show-images-selector.d.ts +1 -1
- package/dist/modes/interactive/components/show-images-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/show-images-selector.js +1 -1
- package/dist/modes/interactive/components/show-images-selector.js.map +1 -1
- package/dist/modes/interactive/components/skill-invocation-message.d.ts +1 -1
- package/dist/modes/interactive/components/skill-invocation-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/skill-invocation-message.js +1 -1
- package/dist/modes/interactive/components/skill-invocation-message.js.map +1 -1
- package/dist/modes/interactive/components/skills-selector.d.ts +1 -1
- package/dist/modes/interactive/components/skills-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/skills-selector.js +1 -1
- package/dist/modes/interactive/components/skills-selector.js.map +1 -1
- package/dist/modes/interactive/components/theme-selector.d.ts +1 -1
- package/dist/modes/interactive/components/theme-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/theme-selector.js +1 -1
- package/dist/modes/interactive/components/theme-selector.js.map +1 -1
- package/dist/modes/interactive/components/thinking-selector.d.ts +2 -2
- package/dist/modes/interactive/components/thinking-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/thinking-selector.js +1 -1
- package/dist/modes/interactive/components/thinking-selector.js.map +1 -1
- package/dist/modes/interactive/components/tool-execution.d.ts +1 -1
- package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
- package/dist/modes/interactive/components/tool-execution.js +1 -1
- package/dist/modes/interactive/components/tool-execution.js.map +1 -1
- package/dist/modes/interactive/components/tree-selector.d.ts +1 -1
- package/dist/modes/interactive/components/tree-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/tree-selector.js +1 -1
- package/dist/modes/interactive/components/tree-selector.js.map +1 -1
- package/dist/modes/interactive/components/user-message-selector.d.ts +1 -1
- package/dist/modes/interactive/components/user-message-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/user-message-selector.js +1 -1
- package/dist/modes/interactive/components/user-message-selector.js.map +1 -1
- package/dist/modes/interactive/components/user-message.d.ts +1 -1
- package/dist/modes/interactive/components/user-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/user-message.js +1 -1
- package/dist/modes/interactive/components/user-message.js.map +1 -1
- package/dist/modes/interactive/components/visual-truncate.d.ts.map +1 -1
- package/dist/modes/interactive/components/visual-truncate.js +1 -1
- package/dist/modes/interactive/components/visual-truncate.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +1 -1
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/interactive/theme/theme.d.ts +2 -2
- package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
- package/dist/modes/interactive/theme/theme.js +1 -1
- package/dist/modes/interactive/theme/theme.js.map +1 -1
- package/dist/modes/print-mode.d.ts +1 -1
- package/dist/modes/print-mode.d.ts.map +1 -1
- package/dist/modes/print-mode.js.map +1 -1
- package/dist/modes/rpc/rpc-client.d.ts +2 -2
- package/dist/modes/rpc/rpc-client.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-client.js.map +1 -1
- package/dist/modes/rpc/rpc-types.d.ts +2 -2
- package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-types.js.map +1 -1
- package/dist/utils/image-resize.d.ts +1 -1
- package/dist/utils/image-resize.d.ts.map +1 -1
- package/dist/utils/image-resize.js.map +1 -1
- package/examples/extensions/antigravity-image-gen.ts +1 -1
- package/examples/extensions/built-in-tool-renderer.ts +1 -1
- package/examples/extensions/custom-compaction.ts +1 -1
- package/examples/extensions/custom-footer.ts +2 -2
- package/examples/extensions/custom-provider-anthropic/index.ts +1 -1
- package/examples/extensions/custom-provider-gitlab-duo/index.ts +1 -1
- package/examples/extensions/custom-provider-gitlab-duo/test.ts +1 -1
- package/examples/extensions/custom-provider-qwen-cli/index.ts +1 -1
- package/examples/extensions/doom-overlay/doom-component.ts +2 -2
- package/examples/extensions/doom-overlay/doom-keys.ts +1 -1
- package/examples/extensions/handoff.ts +1 -1
- package/examples/extensions/hello.ts +1 -1
- package/examples/extensions/message-renderer.ts +1 -1
- package/examples/extensions/minimal-mode.ts +1 -1
- package/examples/extensions/modal-editor.ts +1 -1
- package/examples/extensions/overlay-qa-tests.ts +2 -2
- package/examples/extensions/overlay-test.ts +1 -1
- package/examples/extensions/plan-mode/index.ts +3 -3
- package/examples/extensions/preset.ts +1 -1
- package/examples/extensions/qna.ts +1 -1
- package/examples/extensions/question.ts +1 -1
- package/examples/extensions/questionnaire.ts +1 -1
- package/examples/extensions/snake.ts +1 -1
- package/examples/extensions/space-invaders.ts +1 -1
- package/examples/extensions/subagent/index.ts +4 -4
- package/examples/extensions/summarize.ts +2 -2
- package/examples/extensions/todo.ts +2 -2
- package/examples/extensions/tool-override.ts +1 -1
- package/examples/extensions/tools.ts +1 -1
- package/examples/extensions/truncated-tool.ts +1 -1
- package/examples/rpc-extension-ui.ts +1 -1
- package/examples/sdk/02-custom-model.ts +1 -1
- package/examples/sdk/12-full-control.ts +1 -1
- package/package.json +4 -4
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"keybinding-hints.js","sourceRoot":"","sources":["../../../../src/modes/interactive/components/keybinding-hints.ts"],"names":[],"mappings":"AAAA,OAAO,EAAqB,oBAAoB,EAAc,MAAM,
|
|
1
|
+
{"version":3,"file":"keybinding-hints.js","sourceRoot":"","sources":["../../../../src/modes/interactive/components/keybinding-hints.ts"],"names":[],"mappings":"AAAA,OAAO,EAAqB,oBAAoB,EAAc,MAAM,yBAAyB,CAAC;AAE9F,OAAO,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAE1C,SAAS,UAAU,CAAC,IAAa,EAAU;IAC1C,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IACjC,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC,CAAC,CAAE,CAAC;IACvC,OAAO,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAAA,CACtB;AACD,MAAM,UAAU,SAAS,CAAC,MAAoB,EAAU;IACvD,OAAO,UAAU,CAAC,oBAAoB,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC;AAAA,CAC1D;AACD,MAAM,UAAU,MAAM,CAAC,WAA+B,EAAE,MAAiB,EAAU;IAClF,OAAO,UAAU,CAAC,WAAW,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC;AAAA,CAC/C;AACD,MAAM,UAAU,OAAO,CAAC,MAAoB,EAAE,WAAmB,EAAU;IAC1E,OAAO,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,SAAS,CAAC,MAAM,CAAC,CAAC,GAAG,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,IAAI,WAAW,EAAE,CAAC,CAAC;AAAA,CACjF;AACD,MAAM,UAAU,UAAU,CAAC,WAA+B,EAAE,MAAiB,EAAE,WAAmB,EAAU;IAC3G,OAAO,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC,GAAG,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,IAAI,WAAW,EAAE,CAAC,CAAC;AAAA,CAC3F;AACD,MAAM,UAAU,UAAU,CAAC,GAAW,EAAE,WAAmB,EAAU;IACpE,OAAO,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,CAAC,GAAG,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,IAAI,WAAW,EAAE,CAAC,CAAC;AAAA,CACnE","sourcesContent":["import { type EditorAction, getEditorKeybindings, type KeyId } from \"@boxiaolanya2008/pi-tui\";\nimport type { AppAction, KeybindingsManager } from \"../../../core/keybindings.js\";\nimport { theme } from \"../theme/theme.js\";\n\nfunction formatKeys(keys: KeyId[]): string {\n\tif (keys.length === 0) return \"\";\n\tif (keys.length === 1) return keys[0]!;\n\treturn keys.join(\"/\");\n}\nexport function editorKey(action: EditorAction): string {\n\treturn formatKeys(getEditorKeybindings().getKeys(action));\n}\nexport function appKey(keybindings: KeybindingsManager, action: AppAction): string {\n\treturn formatKeys(keybindings.getKeys(action));\n}\nexport function keyHint(action: EditorAction, description: string): string {\n\treturn theme.fg(\"dim\", editorKey(action)) + theme.fg(\"muted\", ` ${description}`);\n}\nexport function appKeyHint(keybindings: KeybindingsManager, action: AppAction, description: string): string {\n\treturn theme.fg(\"dim\", appKey(keybindings, action)) + theme.fg(\"muted\", ` ${description}`);\n}\nexport function rawKeyHint(key: string, description: string): string {\n\treturn theme.fg(\"dim\", key) + theme.fg(\"muted\", ` ${description}`);\n}\n"]}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { Component } from "@
|
|
2
|
-
import { type TUI } from "@
|
|
1
|
+
import type { Component } from "@boxiaolanya2008/pi-tui";
|
|
2
|
+
import { type TUI } from "@boxiaolanya2008/pi-tui";
|
|
3
3
|
export declare class OnboardingWizard {
|
|
4
4
|
private container;
|
|
5
5
|
private state;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"onboarding-wizard.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/onboarding-wizard.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAA2B,KAAK,GAAG,EAAE,MAAM,sBAAsB,CAAC;AAoBzE,qBAAa,gBAAgB;IAC5B,OAAO,CAAC,SAAS,CAAY;IAC7B,OAAO,CAAC,KAAK,CAAc;IAC3B,OAAO,CAAC,UAAU,CAAa;IAC/B,OAAO,CAAC,QAAQ,CAAa;IAE7B,YAAY,IAAI,EAAE,GAAG,EAAE,UAAU,EAAE,MAAM,IAAI,EAAE,QAAQ,EAAE,MAAM,IAAI,EAalE;IAED,YAAY,IAAI,SAAS,CAExB;IAED,OAAO,CAAC,MAAM;IA0Bd,OAAO,CAAC,YAAY;IAyBpB,OAAO,CAAC,iBAAiB;IAYzB,OAAO,CAAC,gBAAgB;IAaxB,OAAO,CAAC,gBAAgB;IAUxB,OAAO,CAAC,iBAAiB;IAazB,OAAO,CAAC,eAAe;IASvB,OAAO,CAAC,kBAAkB;IAY1B,OAAO,CAAC,YAAY;IAMpB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CA6E7B;IAED,OAAO,CAAC,iBAAiB;CAmBzB","sourcesContent":["import type { Component } from \"@mariozechner/pi-tui\";\nimport { Container, Spacer, Text, type TUI } from \"@mariozechner/pi-tui\";\nimport {\n\tloadUserConfig,\n\ttype ModelConfig,\n\tsaveUserConfig,\n\tsetModel,\n\tupdateBrandSettings,\n} from \"../../../core/user-config.js\";\n\ntype OnboardingStep = \"welcome\" | \"api-url\" | \"api-key\" | \"model-id\" | \"brand\" | \"complete\";\n\ninterface WizardState {\n\tstep: OnboardingStep;\n\tapiUrl: string;\n\tapiKey: string;\n\tmodelId: string;\n\tbrandName: string;\n\twelcomeMessage: string;\n}\n\nexport class OnboardingWizard {\n\tprivate container: Container;\n\tprivate state: WizardState;\n\tprivate onComplete: () => void;\n\tprivate onCancel: () => void;\n\n\tconstructor(_tui: TUI, onComplete: () => void, onCancel: () => void) {\n\t\tthis.onComplete = onComplete;\n\t\tthis.onCancel = onCancel;\n\t\tthis.state = {\n\t\t\tstep: \"welcome\",\n\t\t\tapiUrl: \"\",\n\t\t\tapiKey: \"\",\n\t\t\tmodelId: \"\",\n\t\t\tbrandName: \"OpenVibe\",\n\t\t\twelcomeMessage: \"Welcome to OpenVibe - Your AI Coding Assistant\",\n\t\t};\n\t\tthis.container = new Container();\n\t\tthis.render();\n\t}\n\n\tgetContainer(): Component {\n\t\treturn this.container;\n\t}\n\n\tprivate render(): void {\n\t\tthis.container.clear();\n\t\tthis.renderHeader();\n\t\tswitch (this.state.step) {\n\t\t\tcase \"welcome\":\n\t\t\t\tthis.renderWelcomeStep();\n\t\t\t\tbreak;\n\t\t\tcase \"api-url\":\n\t\t\t\tthis.renderApiUrlStep();\n\t\t\t\tbreak;\n\t\t\tcase \"api-key\":\n\t\t\t\tthis.renderApiKeyStep();\n\t\t\t\tbreak;\n\t\t\tcase \"model-id\":\n\t\t\t\tthis.renderModelIdStep();\n\t\t\t\tbreak;\n\t\t\tcase \"brand\":\n\t\t\t\tthis.renderBrandStep();\n\t\t\t\tbreak;\n\t\t\tcase \"complete\":\n\t\t\t\tthis.renderCompleteStep();\n\t\t\t\tbreak;\n\t\t}\n\t\tthis.renderFooter();\n\t}\n\n\tprivate renderHeader(): void {\n\t\tconst logo = `\n ____ __ __\n / __ \\\\____ ___ ____ / /__ / /_\n / / / / __ \\\\/ _ \\\\/ __ \\\\/ / _ \\\\/ __/\n/ /_/ / /_/ / __/ / / / / __/ /_\n/_____/ .___/\\\\___/_/ /_/_/\\\\___/\\\\__/\n /_/\n `;\n\t\tthis.container.addChild(new Text(logo, 1, 0));\n\t\tthis.container.addChild(new Text(\"=\".repeat(60), 1, 0));\n\t\tconst steps: OnboardingStep[] = [\"welcome\", \"api-url\", \"api-key\", \"model-id\", \"brand\", \"complete\"];\n\t\tconst currentIndex = steps.indexOf(this.state.step);\n\t\tconst progress = steps\n\t\t\t.map((_step, index) => {\n\t\t\t\tif (index < currentIndex) return \"✓\";\n\t\t\t\tif (index === currentIndex) return \"●\";\n\t\t\t\treturn \"○\";\n\t\t\t})\n\t\t\t.join(\" → \");\n\t\tthis.container.addChild(new Text(` ${progress}`, 1, 0));\n\t\tthis.container.addChild(new Text(\"=\".repeat(60), 1, 0));\n\t\tthis.container.addChild(new Spacer(1));\n\t}\n\n\tprivate renderWelcomeStep(): void {\n\t\tthis.container.addChild(new Text(\" Welcome to OpenVibe!\", 1, 0));\n\t\tthis.container.addChild(new Spacer(1));\n\t\tthis.container.addChild(\n\t\t\tnew Text(\" OpenVibe is your AI coding assistant that works with your own API keys.\", 1, 0),\n\t\t);\n\t\tthis.container.addChild(new Spacer(1));\n\t\tthis.container.addChild(new Text(\" This quick setup will help you configure your AI model.\", 1, 0));\n\t\tthis.container.addChild(new Spacer(1));\n\t\tthis.container.addChild(new Text(\" Press Enter to continue or Esc to exit.\", 1, 0));\n\t}\n\n\tprivate renderApiUrlStep(): void {\n\t\tthis.container.addChild(new Text(\" Step 1: Enter API Base URL\", 1, 0));\n\t\tthis.container.addChild(new Spacer(1));\n\t\tthis.container.addChild(new Text(\" Examples:\", 1, 0));\n\t\tthis.container.addChild(new Text(\" • https://api.openai.com/v1\", 1, 0));\n\t\tthis.container.addChild(new Text(\" • https://api.anthropic.com\", 1, 0));\n\t\tthis.container.addChild(new Text(\" • http://localhost:11434/v1\", 1, 0));\n\t\tthis.container.addChild(new Spacer(1));\n\t\tthis.container.addChild(new Text(` URL: ${this.state.apiUrl || \"_\"}`, 1, 0));\n\t\tthis.container.addChild(new Spacer(1));\n\t\tthis.container.addChild(new Text(\" Type the URL and press Enter to continue.\", 1, 0));\n\t}\n\n\tprivate renderApiKeyStep(): void {\n\t\tthis.container.addChild(new Text(\" Step 2: Enter Your API Key\", 1, 0));\n\t\tthis.container.addChild(new Spacer(1));\n\t\tthis.container.addChild(new Text(\" Your API key is stored locally and never shared.\", 1, 0));\n\t\tthis.container.addChild(new Spacer(1));\n\t\tthis.container.addChild(new Text(` API Key: ${\"*\".repeat(this.state.apiKey.length) || \"_\"}`, 1, 0));\n\t\tthis.container.addChild(new Spacer(1));\n\t\tthis.container.addChild(new Text(\" Type your API key and press Enter to continue.\", 1, 0));\n\t}\n\n\tprivate renderModelIdStep(): void {\n\t\tthis.container.addChild(new Text(\" Step 3: Enter Model ID\", 1, 0));\n\t\tthis.container.addChild(new Spacer(1));\n\t\tthis.container.addChild(new Text(\" Examples:\", 1, 0));\n\t\tthis.container.addChild(new Text(\" • gpt-4o\", 1, 0));\n\t\tthis.container.addChild(new Text(\" • claude-3-5-sonnet-20241022\", 1, 0));\n\t\tthis.container.addChild(new Text(\" • grok-2\", 1, 0));\n\t\tthis.container.addChild(new Spacer(1));\n\t\tthis.container.addChild(new Text(` Model ID: ${this.state.modelId || \"_\"}`, 1, 0));\n\t\tthis.container.addChild(new Spacer(1));\n\t\tthis.container.addChild(new Text(\" Type the model ID and press Enter to continue.\", 1, 0));\n\t}\n\n\tprivate renderBrandStep(): void {\n\t\tthis.container.addChild(new Text(\" Step 4: Customize (Optional)\", 1, 0));\n\t\tthis.container.addChild(new Spacer(1));\n\t\tthis.container.addChild(new Text(` Product Name: ${this.state.brandName}`, 1, 0));\n\t\tthis.container.addChild(new Text(` Welcome Message: ${this.state.welcomeMessage}`, 1, 0));\n\t\tthis.container.addChild(new Spacer(1));\n\t\tthis.container.addChild(new Text(\" Press Enter to continue with defaults.\", 1, 0));\n\t}\n\n\tprivate renderCompleteStep(): void {\n\t\tthis.container.addChild(new Text(\" Setup Complete!\", 1, 0));\n\t\tthis.container.addChild(new Spacer(1));\n\t\tthis.container.addChild(new Text(` Model: ${this.state.modelId}`, 1, 0));\n\t\tthis.container.addChild(new Text(` API URL: ${this.state.apiUrl}`, 1, 0));\n\t\tthis.container.addChild(new Text(` Product: ${this.state.brandName}`, 1, 0));\n\t\tthis.container.addChild(new Spacer(1));\n\t\tthis.container.addChild(new Text(\" Your configuration has been saved.\", 1, 0));\n\t\tthis.container.addChild(new Spacer(1));\n\t\tthis.container.addChild(new Text(\" Press Enter to start using OpenVibe!\", 1, 0));\n\t}\n\n\tprivate renderFooter(): void {\n\t\tthis.container.addChild(new Spacer(2));\n\t\tthis.container.addChild(new Text(\"-\".repeat(60), 1, 0));\n\t\tthis.container.addChild(new Text(\" Navigation: Enter = Continue | Esc = Back/Cancel\", 1, 0));\n\t}\n\n\thandleInput(key: string): void {\n\t\tswitch (this.state.step) {\n\t\t\tcase \"welcome\":\n\t\t\t\tif (key === \"\\r\" || key === \"\\n\") {\n\t\t\t\t\tthis.state.step = \"api-url\";\n\t\t\t\t\tthis.render();\n\t\t\t\t} else if (key === \"\\u001b\") {\n\t\t\t\t\tthis.onCancel();\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase \"api-url\":\n\t\t\t\tif (key === \"\\r\" || key === \"\\n\") {\n\t\t\t\t\tif (this.state.apiUrl.trim()) {\n\t\t\t\t\t\tthis.state.step = \"api-key\";\n\t\t\t\t\t\tthis.render();\n\t\t\t\t\t}\n\t\t\t\t} else if (key === \"\\u001b\") {\n\t\t\t\t\tthis.state.step = \"welcome\";\n\t\t\t\t\tthis.render();\n\t\t\t\t} else if (key === \"\\b\" || key === \"\\x7f\") {\n\t\t\t\t\tthis.state.apiUrl = this.state.apiUrl.slice(0, -1);\n\t\t\t\t\tthis.render();\n\t\t\t\t} else if (key.length === 1 && key.charCodeAt(0) >= 32) {\n\t\t\t\t\tthis.state.apiUrl += key;\n\t\t\t\t\tthis.render();\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase \"api-key\":\n\t\t\t\tif (key === \"\\r\" || key === \"\\n\") {\n\t\t\t\t\tif (this.state.apiKey.trim()) {\n\t\t\t\t\t\tthis.state.step = \"model-id\";\n\t\t\t\t\t\tthis.render();\n\t\t\t\t\t}\n\t\t\t\t} else if (key === \"\\u001b\") {\n\t\t\t\t\tthis.state.step = \"api-url\";\n\t\t\t\t\tthis.render();\n\t\t\t\t} else if (key === \"\\b\" || key === \"\\x7f\") {\n\t\t\t\t\tthis.state.apiKey = this.state.apiKey.slice(0, -1);\n\t\t\t\t\tthis.render();\n\t\t\t\t} else if (key.length === 1 && key.charCodeAt(0) >= 32) {\n\t\t\t\t\tthis.state.apiKey += key;\n\t\t\t\t\tthis.render();\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase \"model-id\":\n\t\t\t\tif (key === \"\\r\" || key === \"\\n\") {\n\t\t\t\t\tif (this.state.modelId.trim()) {\n\t\t\t\t\t\tthis.state.step = \"brand\";\n\t\t\t\t\t\tthis.render();\n\t\t\t\t\t}\n\t\t\t\t} else if (key === \"\\u001b\") {\n\t\t\t\t\tthis.state.step = \"api-key\";\n\t\t\t\t\tthis.render();\n\t\t\t\t} else if (key === \"\\b\" || key === \"\\x7f\") {\n\t\t\t\t\tthis.state.modelId = this.state.modelId.slice(0, -1);\n\t\t\t\t\tthis.render();\n\t\t\t\t} else if (key.length === 1 && key.charCodeAt(0) >= 32) {\n\t\t\t\t\tthis.state.modelId += key;\n\t\t\t\t\tthis.render();\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase \"brand\":\n\t\t\t\tif (key === \"\\r\" || key === \"\\n\") {\n\t\t\t\t\tthis.saveConfiguration();\n\t\t\t\t\tthis.state.step = \"complete\";\n\t\t\t\t\tthis.render();\n\t\t\t\t} else if (key === \"\\u001b\") {\n\t\t\t\t\tthis.state.step = \"model-id\";\n\t\t\t\t\tthis.render();\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase \"complete\":\n\t\t\t\tif (key === \"\\r\" || key === \"\\n\") {\n\t\t\t\t\tthis.onComplete();\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\tprivate saveConfiguration(): void {\n\t\tlet config = loadUserConfig();\n\n\t\tconst modelConfig: ModelConfig = {\n\t\t\tid: this.state.modelId.trim(),\n\t\t\tname: this.state.modelId.trim(),\n\t\t\tprovider: \"custom\",\n\t\t\tapiKey: this.state.apiKey.trim(),\n\t\t\tbaseUrl: this.state.apiUrl.trim(),\n\t\t};\n\n\t\tconfig = setModel(config, modelConfig);\n\t\tconfig = updateBrandSettings(config, {\n\t\t\tproductName: this.state.brandName,\n\t\t\twelcomeMessage: this.state.welcomeMessage,\n\t\t});\n\n\t\tsaveUserConfig(config);\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"onboarding-wizard.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/onboarding-wizard.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AACzD,OAAO,EAA2B,KAAK,GAAG,EAAE,MAAM,yBAAyB,CAAC;AAoB5E,qBAAa,gBAAgB;IAC5B,OAAO,CAAC,SAAS,CAAY;IAC7B,OAAO,CAAC,KAAK,CAAc;IAC3B,OAAO,CAAC,UAAU,CAAa;IAC/B,OAAO,CAAC,QAAQ,CAAa;IAE7B,YAAY,IAAI,EAAE,GAAG,EAAE,UAAU,EAAE,MAAM,IAAI,EAAE,QAAQ,EAAE,MAAM,IAAI,EAalE;IAED,YAAY,IAAI,SAAS,CAExB;IAED,OAAO,CAAC,MAAM;IA0Bd,OAAO,CAAC,YAAY;IAyBpB,OAAO,CAAC,iBAAiB;IAYzB,OAAO,CAAC,gBAAgB;IAaxB,OAAO,CAAC,gBAAgB;IAUxB,OAAO,CAAC,iBAAiB;IAazB,OAAO,CAAC,eAAe;IASvB,OAAO,CAAC,kBAAkB;IAY1B,OAAO,CAAC,YAAY;IAMpB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CA6E7B;IAED,OAAO,CAAC,iBAAiB;CAmBzB","sourcesContent":["import type { Component } from \"@boxiaolanya2008/pi-tui\";\nimport { Container, Spacer, Text, type TUI } from \"@boxiaolanya2008/pi-tui\";\nimport {\n\tloadUserConfig,\n\ttype ModelConfig,\n\tsaveUserConfig,\n\tsetModel,\n\tupdateBrandSettings,\n} from \"../../../core/user-config.js\";\n\ntype OnboardingStep = \"welcome\" | \"api-url\" | \"api-key\" | \"model-id\" | \"brand\" | \"complete\";\n\ninterface WizardState {\n\tstep: OnboardingStep;\n\tapiUrl: string;\n\tapiKey: string;\n\tmodelId: string;\n\tbrandName: string;\n\twelcomeMessage: string;\n}\n\nexport class OnboardingWizard {\n\tprivate container: Container;\n\tprivate state: WizardState;\n\tprivate onComplete: () => void;\n\tprivate onCancel: () => void;\n\n\tconstructor(_tui: TUI, onComplete: () => void, onCancel: () => void) {\n\t\tthis.onComplete = onComplete;\n\t\tthis.onCancel = onCancel;\n\t\tthis.state = {\n\t\t\tstep: \"welcome\",\n\t\t\tapiUrl: \"\",\n\t\t\tapiKey: \"\",\n\t\t\tmodelId: \"\",\n\t\t\tbrandName: \"OpenVibe\",\n\t\t\twelcomeMessage: \"Welcome to OpenVibe - Your AI Coding Assistant\",\n\t\t};\n\t\tthis.container = new Container();\n\t\tthis.render();\n\t}\n\n\tgetContainer(): Component {\n\t\treturn this.container;\n\t}\n\n\tprivate render(): void {\n\t\tthis.container.clear();\n\t\tthis.renderHeader();\n\t\tswitch (this.state.step) {\n\t\t\tcase \"welcome\":\n\t\t\t\tthis.renderWelcomeStep();\n\t\t\t\tbreak;\n\t\t\tcase \"api-url\":\n\t\t\t\tthis.renderApiUrlStep();\n\t\t\t\tbreak;\n\t\t\tcase \"api-key\":\n\t\t\t\tthis.renderApiKeyStep();\n\t\t\t\tbreak;\n\t\t\tcase \"model-id\":\n\t\t\t\tthis.renderModelIdStep();\n\t\t\t\tbreak;\n\t\t\tcase \"brand\":\n\t\t\t\tthis.renderBrandStep();\n\t\t\t\tbreak;\n\t\t\tcase \"complete\":\n\t\t\t\tthis.renderCompleteStep();\n\t\t\t\tbreak;\n\t\t}\n\t\tthis.renderFooter();\n\t}\n\n\tprivate renderHeader(): void {\n\t\tconst logo = `\n ____ __ __\n / __ \\\\____ ___ ____ / /__ / /_\n / / / / __ \\\\/ _ \\\\/ __ \\\\/ / _ \\\\/ __/\n/ /_/ / /_/ / __/ / / / / __/ /_\n/_____/ .___/\\\\___/_/ /_/_/\\\\___/\\\\__/\n /_/\n `;\n\t\tthis.container.addChild(new Text(logo, 1, 0));\n\t\tthis.container.addChild(new Text(\"=\".repeat(60), 1, 0));\n\t\tconst steps: OnboardingStep[] = [\"welcome\", \"api-url\", \"api-key\", \"model-id\", \"brand\", \"complete\"];\n\t\tconst currentIndex = steps.indexOf(this.state.step);\n\t\tconst progress = steps\n\t\t\t.map((_step, index) => {\n\t\t\t\tif (index < currentIndex) return \"✓\";\n\t\t\t\tif (index === currentIndex) return \"●\";\n\t\t\t\treturn \"○\";\n\t\t\t})\n\t\t\t.join(\" → \");\n\t\tthis.container.addChild(new Text(` ${progress}`, 1, 0));\n\t\tthis.container.addChild(new Text(\"=\".repeat(60), 1, 0));\n\t\tthis.container.addChild(new Spacer(1));\n\t}\n\n\tprivate renderWelcomeStep(): void {\n\t\tthis.container.addChild(new Text(\" Welcome to OpenVibe!\", 1, 0));\n\t\tthis.container.addChild(new Spacer(1));\n\t\tthis.container.addChild(\n\t\t\tnew Text(\" OpenVibe is your AI coding assistant that works with your own API keys.\", 1, 0),\n\t\t);\n\t\tthis.container.addChild(new Spacer(1));\n\t\tthis.container.addChild(new Text(\" This quick setup will help you configure your AI model.\", 1, 0));\n\t\tthis.container.addChild(new Spacer(1));\n\t\tthis.container.addChild(new Text(\" Press Enter to continue or Esc to exit.\", 1, 0));\n\t}\n\n\tprivate renderApiUrlStep(): void {\n\t\tthis.container.addChild(new Text(\" Step 1: Enter API Base URL\", 1, 0));\n\t\tthis.container.addChild(new Spacer(1));\n\t\tthis.container.addChild(new Text(\" Examples:\", 1, 0));\n\t\tthis.container.addChild(new Text(\" • https://api.openai.com/v1\", 1, 0));\n\t\tthis.container.addChild(new Text(\" • https://api.anthropic.com\", 1, 0));\n\t\tthis.container.addChild(new Text(\" • http://localhost:11434/v1\", 1, 0));\n\t\tthis.container.addChild(new Spacer(1));\n\t\tthis.container.addChild(new Text(` URL: ${this.state.apiUrl || \"_\"}`, 1, 0));\n\t\tthis.container.addChild(new Spacer(1));\n\t\tthis.container.addChild(new Text(\" Type the URL and press Enter to continue.\", 1, 0));\n\t}\n\n\tprivate renderApiKeyStep(): void {\n\t\tthis.container.addChild(new Text(\" Step 2: Enter Your API Key\", 1, 0));\n\t\tthis.container.addChild(new Spacer(1));\n\t\tthis.container.addChild(new Text(\" Your API key is stored locally and never shared.\", 1, 0));\n\t\tthis.container.addChild(new Spacer(1));\n\t\tthis.container.addChild(new Text(` API Key: ${\"*\".repeat(this.state.apiKey.length) || \"_\"}`, 1, 0));\n\t\tthis.container.addChild(new Spacer(1));\n\t\tthis.container.addChild(new Text(\" Type your API key and press Enter to continue.\", 1, 0));\n\t}\n\n\tprivate renderModelIdStep(): void {\n\t\tthis.container.addChild(new Text(\" Step 3: Enter Model ID\", 1, 0));\n\t\tthis.container.addChild(new Spacer(1));\n\t\tthis.container.addChild(new Text(\" Examples:\", 1, 0));\n\t\tthis.container.addChild(new Text(\" • gpt-4o\", 1, 0));\n\t\tthis.container.addChild(new Text(\" • claude-3-5-sonnet-20241022\", 1, 0));\n\t\tthis.container.addChild(new Text(\" • grok-2\", 1, 0));\n\t\tthis.container.addChild(new Spacer(1));\n\t\tthis.container.addChild(new Text(` Model ID: ${this.state.modelId || \"_\"}`, 1, 0));\n\t\tthis.container.addChild(new Spacer(1));\n\t\tthis.container.addChild(new Text(\" Type the model ID and press Enter to continue.\", 1, 0));\n\t}\n\n\tprivate renderBrandStep(): void {\n\t\tthis.container.addChild(new Text(\" Step 4: Customize (Optional)\", 1, 0));\n\t\tthis.container.addChild(new Spacer(1));\n\t\tthis.container.addChild(new Text(` Product Name: ${this.state.brandName}`, 1, 0));\n\t\tthis.container.addChild(new Text(` Welcome Message: ${this.state.welcomeMessage}`, 1, 0));\n\t\tthis.container.addChild(new Spacer(1));\n\t\tthis.container.addChild(new Text(\" Press Enter to continue with defaults.\", 1, 0));\n\t}\n\n\tprivate renderCompleteStep(): void {\n\t\tthis.container.addChild(new Text(\" Setup Complete!\", 1, 0));\n\t\tthis.container.addChild(new Spacer(1));\n\t\tthis.container.addChild(new Text(` Model: ${this.state.modelId}`, 1, 0));\n\t\tthis.container.addChild(new Text(` API URL: ${this.state.apiUrl}`, 1, 0));\n\t\tthis.container.addChild(new Text(` Product: ${this.state.brandName}`, 1, 0));\n\t\tthis.container.addChild(new Spacer(1));\n\t\tthis.container.addChild(new Text(\" Your configuration has been saved.\", 1, 0));\n\t\tthis.container.addChild(new Spacer(1));\n\t\tthis.container.addChild(new Text(\" Press Enter to start using OpenVibe!\", 1, 0));\n\t}\n\n\tprivate renderFooter(): void {\n\t\tthis.container.addChild(new Spacer(2));\n\t\tthis.container.addChild(new Text(\"-\".repeat(60), 1, 0));\n\t\tthis.container.addChild(new Text(\" Navigation: Enter = Continue | Esc = Back/Cancel\", 1, 0));\n\t}\n\n\thandleInput(key: string): void {\n\t\tswitch (this.state.step) {\n\t\t\tcase \"welcome\":\n\t\t\t\tif (key === \"\\r\" || key === \"\\n\") {\n\t\t\t\t\tthis.state.step = \"api-url\";\n\t\t\t\t\tthis.render();\n\t\t\t\t} else if (key === \"\\u001b\") {\n\t\t\t\t\tthis.onCancel();\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase \"api-url\":\n\t\t\t\tif (key === \"\\r\" || key === \"\\n\") {\n\t\t\t\t\tif (this.state.apiUrl.trim()) {\n\t\t\t\t\t\tthis.state.step = \"api-key\";\n\t\t\t\t\t\tthis.render();\n\t\t\t\t\t}\n\t\t\t\t} else if (key === \"\\u001b\") {\n\t\t\t\t\tthis.state.step = \"welcome\";\n\t\t\t\t\tthis.render();\n\t\t\t\t} else if (key === \"\\b\" || key === \"\\x7f\") {\n\t\t\t\t\tthis.state.apiUrl = this.state.apiUrl.slice(0, -1);\n\t\t\t\t\tthis.render();\n\t\t\t\t} else if (key.length === 1 && key.charCodeAt(0) >= 32) {\n\t\t\t\t\tthis.state.apiUrl += key;\n\t\t\t\t\tthis.render();\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase \"api-key\":\n\t\t\t\tif (key === \"\\r\" || key === \"\\n\") {\n\t\t\t\t\tif (this.state.apiKey.trim()) {\n\t\t\t\t\t\tthis.state.step = \"model-id\";\n\t\t\t\t\t\tthis.render();\n\t\t\t\t\t}\n\t\t\t\t} else if (key === \"\\u001b\") {\n\t\t\t\t\tthis.state.step = \"api-url\";\n\t\t\t\t\tthis.render();\n\t\t\t\t} else if (key === \"\\b\" || key === \"\\x7f\") {\n\t\t\t\t\tthis.state.apiKey = this.state.apiKey.slice(0, -1);\n\t\t\t\t\tthis.render();\n\t\t\t\t} else if (key.length === 1 && key.charCodeAt(0) >= 32) {\n\t\t\t\t\tthis.state.apiKey += key;\n\t\t\t\t\tthis.render();\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase \"model-id\":\n\t\t\t\tif (key === \"\\r\" || key === \"\\n\") {\n\t\t\t\t\tif (this.state.modelId.trim()) {\n\t\t\t\t\t\tthis.state.step = \"brand\";\n\t\t\t\t\t\tthis.render();\n\t\t\t\t\t}\n\t\t\t\t} else if (key === \"\\u001b\") {\n\t\t\t\t\tthis.state.step = \"api-key\";\n\t\t\t\t\tthis.render();\n\t\t\t\t} else if (key === \"\\b\" || key === \"\\x7f\") {\n\t\t\t\t\tthis.state.modelId = this.state.modelId.slice(0, -1);\n\t\t\t\t\tthis.render();\n\t\t\t\t} else if (key.length === 1 && key.charCodeAt(0) >= 32) {\n\t\t\t\t\tthis.state.modelId += key;\n\t\t\t\t\tthis.render();\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase \"brand\":\n\t\t\t\tif (key === \"\\r\" || key === \"\\n\") {\n\t\t\t\t\tthis.saveConfiguration();\n\t\t\t\t\tthis.state.step = \"complete\";\n\t\t\t\t\tthis.render();\n\t\t\t\t} else if (key === \"\\u001b\") {\n\t\t\t\t\tthis.state.step = \"model-id\";\n\t\t\t\t\tthis.render();\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase \"complete\":\n\t\t\t\tif (key === \"\\r\" || key === \"\\n\") {\n\t\t\t\t\tthis.onComplete();\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\tprivate saveConfiguration(): void {\n\t\tlet config = loadUserConfig();\n\n\t\tconst modelConfig: ModelConfig = {\n\t\t\tid: this.state.modelId.trim(),\n\t\t\tname: this.state.modelId.trim(),\n\t\t\tprovider: \"custom\",\n\t\t\tapiKey: this.state.apiKey.trim(),\n\t\t\tbaseUrl: this.state.apiUrl.trim(),\n\t\t};\n\n\t\tconfig = setModel(config, modelConfig);\n\t\tconfig = updateBrandSettings(config, {\n\t\t\tproductName: this.state.brandName,\n\t\t\twelcomeMessage: this.state.welcomeMessage,\n\t\t});\n\n\t\tsaveUserConfig(config);\n\t}\n}\n"]}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Container, Spacer, Text } from "@
|
|
1
|
+
import { Container, Spacer, Text } from "@boxiaolanya2008/pi-tui";
|
|
2
2
|
import { loadUserConfig, saveUserConfig, setModel, updateBrandSettings, } from "../../../core/user-config.js";
|
|
3
3
|
export class OnboardingWizard {
|
|
4
4
|
container;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"onboarding-wizard.js","sourceRoot":"","sources":["../../../../src/modes/interactive/components/onboarding-wizard.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Component } from \"@mariozechner/pi-tui\";\nimport { Container, Spacer, Text, type TUI } from \"@mariozechner/pi-tui\";\nimport {\n\tloadUserConfig,\n\ttype ModelConfig,\n\tsaveUserConfig,\n\tsetModel,\n\tupdateBrandSettings,\n} from \"../../../core/user-config.js\";\n\ntype OnboardingStep = \"welcome\" | \"api-url\" | \"api-key\" | \"model-id\" | \"brand\" | \"complete\";\n\ninterface WizardState {\n\tstep: OnboardingStep;\n\tapiUrl: string;\n\tapiKey: string;\n\tmodelId: string;\n\tbrandName: string;\n\twelcomeMessage: string;\n}\n\nexport class OnboardingWizard {\n\tprivate container: Container;\n\tprivate state: WizardState;\n\tprivate onComplete: () => void;\n\tprivate onCancel: () => void;\n\n\tconstructor(_tui: TUI, onComplete: () => void, onCancel: () => void) {\n\t\tthis.onComplete = onComplete;\n\t\tthis.onCancel = onCancel;\n\t\tthis.state = {\n\t\t\tstep: \"welcome\",\n\t\t\tapiUrl: \"\",\n\t\t\tapiKey: \"\",\n\t\t\tmodelId: \"\",\n\t\t\tbrandName: \"OpenVibe\",\n\t\t\twelcomeMessage: \"Welcome to OpenVibe - Your AI Coding Assistant\",\n\t\t};\n\t\tthis.container = new Container();\n\t\tthis.render();\n\t}\n\n\tgetContainer(): Component {\n\t\treturn this.container;\n\t}\n\n\tprivate render(): void {\n\t\tthis.container.clear();\n\t\tthis.renderHeader();\n\t\tswitch (this.state.step) {\n\t\t\tcase \"welcome\":\n\t\t\t\tthis.renderWelcomeStep();\n\t\t\t\tbreak;\n\t\t\tcase \"api-url\":\n\t\t\t\tthis.renderApiUrlStep();\n\t\t\t\tbreak;\n\t\t\tcase \"api-key\":\n\t\t\t\tthis.renderApiKeyStep();\n\t\t\t\tbreak;\n\t\t\tcase \"model-id\":\n\t\t\t\tthis.renderModelIdStep();\n\t\t\t\tbreak;\n\t\t\tcase \"brand\":\n\t\t\t\tthis.renderBrandStep();\n\t\t\t\tbreak;\n\t\t\tcase \"complete\":\n\t\t\t\tthis.renderCompleteStep();\n\t\t\t\tbreak;\n\t\t}\n\t\tthis.renderFooter();\n\t}\n\n\tprivate renderHeader(): void {\n\t\tconst logo = `\n ____ __ __\n / __ \\\\____ ___ ____ / /__ / /_\n / / / / __ \\\\/ _ \\\\/ __ \\\\/ / _ \\\\/ __/\n/ /_/ / /_/ / __/ / / / / __/ /_\n/_____/ .___/\\\\___/_/ /_/_/\\\\___/\\\\__/\n /_/\n `;\n\t\tthis.container.addChild(new Text(logo, 1, 0));\n\t\tthis.container.addChild(new Text(\"=\".repeat(60), 1, 0));\n\t\tconst steps: OnboardingStep[] = [\"welcome\", \"api-url\", \"api-key\", \"model-id\", \"brand\", \"complete\"];\n\t\tconst currentIndex = steps.indexOf(this.state.step);\n\t\tconst progress = steps\n\t\t\t.map((_step, index) => {\n\t\t\t\tif (index < currentIndex) return \"✓\";\n\t\t\t\tif (index === currentIndex) return \"●\";\n\t\t\t\treturn \"○\";\n\t\t\t})\n\t\t\t.join(\" → \");\n\t\tthis.container.addChild(new Text(` ${progress}`, 1, 0));\n\t\tthis.container.addChild(new Text(\"=\".repeat(60), 1, 0));\n\t\tthis.container.addChild(new Spacer(1));\n\t}\n\n\tprivate renderWelcomeStep(): void {\n\t\tthis.container.addChild(new Text(\" Welcome to OpenVibe!\", 1, 0));\n\t\tthis.container.addChild(new Spacer(1));\n\t\tthis.container.addChild(\n\t\t\tnew Text(\" OpenVibe is your AI coding assistant that works with your own API keys.\", 1, 0),\n\t\t);\n\t\tthis.container.addChild(new Spacer(1));\n\t\tthis.container.addChild(new Text(\" This quick setup will help you configure your AI model.\", 1, 0));\n\t\tthis.container.addChild(new Spacer(1));\n\t\tthis.container.addChild(new Text(\" Press Enter to continue or Esc to exit.\", 1, 0));\n\t}\n\n\tprivate renderApiUrlStep(): void {\n\t\tthis.container.addChild(new Text(\" Step 1: Enter API Base URL\", 1, 0));\n\t\tthis.container.addChild(new Spacer(1));\n\t\tthis.container.addChild(new Text(\" Examples:\", 1, 0));\n\t\tthis.container.addChild(new Text(\" • https://api.openai.com/v1\", 1, 0));\n\t\tthis.container.addChild(new Text(\" • https://api.anthropic.com\", 1, 0));\n\t\tthis.container.addChild(new Text(\" • http://localhost:11434/v1\", 1, 0));\n\t\tthis.container.addChild(new Spacer(1));\n\t\tthis.container.addChild(new Text(` URL: ${this.state.apiUrl || \"_\"}`, 1, 0));\n\t\tthis.container.addChild(new Spacer(1));\n\t\tthis.container.addChild(new Text(\" Type the URL and press Enter to continue.\", 1, 0));\n\t}\n\n\tprivate renderApiKeyStep(): void {\n\t\tthis.container.addChild(new Text(\" Step 2: Enter Your API Key\", 1, 0));\n\t\tthis.container.addChild(new Spacer(1));\n\t\tthis.container.addChild(new Text(\" Your API key is stored locally and never shared.\", 1, 0));\n\t\tthis.container.addChild(new Spacer(1));\n\t\tthis.container.addChild(new Text(` API Key: ${\"*\".repeat(this.state.apiKey.length) || \"_\"}`, 1, 0));\n\t\tthis.container.addChild(new Spacer(1));\n\t\tthis.container.addChild(new Text(\" Type your API key and press Enter to continue.\", 1, 0));\n\t}\n\n\tprivate renderModelIdStep(): void {\n\t\tthis.container.addChild(new Text(\" Step 3: Enter Model ID\", 1, 0));\n\t\tthis.container.addChild(new Spacer(1));\n\t\tthis.container.addChild(new Text(\" Examples:\", 1, 0));\n\t\tthis.container.addChild(new Text(\" • gpt-4o\", 1, 0));\n\t\tthis.container.addChild(new Text(\" • claude-3-5-sonnet-20241022\", 1, 0));\n\t\tthis.container.addChild(new Text(\" • grok-2\", 1, 0));\n\t\tthis.container.addChild(new Spacer(1));\n\t\tthis.container.addChild(new Text(` Model ID: ${this.state.modelId || \"_\"}`, 1, 0));\n\t\tthis.container.addChild(new Spacer(1));\n\t\tthis.container.addChild(new Text(\" Type the model ID and press Enter to continue.\", 1, 0));\n\t}\n\n\tprivate renderBrandStep(): void {\n\t\tthis.container.addChild(new Text(\" Step 4: Customize (Optional)\", 1, 0));\n\t\tthis.container.addChild(new Spacer(1));\n\t\tthis.container.addChild(new Text(` Product Name: ${this.state.brandName}`, 1, 0));\n\t\tthis.container.addChild(new Text(` Welcome Message: ${this.state.welcomeMessage}`, 1, 0));\n\t\tthis.container.addChild(new Spacer(1));\n\t\tthis.container.addChild(new Text(\" Press Enter to continue with defaults.\", 1, 0));\n\t}\n\n\tprivate renderCompleteStep(): void {\n\t\tthis.container.addChild(new Text(\" Setup Complete!\", 1, 0));\n\t\tthis.container.addChild(new Spacer(1));\n\t\tthis.container.addChild(new Text(` Model: ${this.state.modelId}`, 1, 0));\n\t\tthis.container.addChild(new Text(` API URL: ${this.state.apiUrl}`, 1, 0));\n\t\tthis.container.addChild(new Text(` Product: ${this.state.brandName}`, 1, 0));\n\t\tthis.container.addChild(new Spacer(1));\n\t\tthis.container.addChild(new Text(\" Your configuration has been saved.\", 1, 0));\n\t\tthis.container.addChild(new Spacer(1));\n\t\tthis.container.addChild(new Text(\" Press Enter to start using OpenVibe!\", 1, 0));\n\t}\n\n\tprivate renderFooter(): void {\n\t\tthis.container.addChild(new Spacer(2));\n\t\tthis.container.addChild(new Text(\"-\".repeat(60), 1, 0));\n\t\tthis.container.addChild(new Text(\" Navigation: Enter = Continue | Esc = Back/Cancel\", 1, 0));\n\t}\n\n\thandleInput(key: string): void {\n\t\tswitch (this.state.step) {\n\t\t\tcase \"welcome\":\n\t\t\t\tif (key === \"\\r\" || key === \"\\n\") {\n\t\t\t\t\tthis.state.step = \"api-url\";\n\t\t\t\t\tthis.render();\n\t\t\t\t} else if (key === \"\\u001b\") {\n\t\t\t\t\tthis.onCancel();\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase \"api-url\":\n\t\t\t\tif (key === \"\\r\" || key === \"\\n\") {\n\t\t\t\t\tif (this.state.apiUrl.trim()) {\n\t\t\t\t\t\tthis.state.step = \"api-key\";\n\t\t\t\t\t\tthis.render();\n\t\t\t\t\t}\n\t\t\t\t} else if (key === \"\\u001b\") {\n\t\t\t\t\tthis.state.step = \"welcome\";\n\t\t\t\t\tthis.render();\n\t\t\t\t} else if (key === \"\\b\" || key === \"\\x7f\") {\n\t\t\t\t\tthis.state.apiUrl = this.state.apiUrl.slice(0, -1);\n\t\t\t\t\tthis.render();\n\t\t\t\t} else if (key.length === 1 && key.charCodeAt(0) >= 32) {\n\t\t\t\t\tthis.state.apiUrl += key;\n\t\t\t\t\tthis.render();\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase \"api-key\":\n\t\t\t\tif (key === \"\\r\" || key === \"\\n\") {\n\t\t\t\t\tif (this.state.apiKey.trim()) {\n\t\t\t\t\t\tthis.state.step = \"model-id\";\n\t\t\t\t\t\tthis.render();\n\t\t\t\t\t}\n\t\t\t\t} else if (key === \"\\u001b\") {\n\t\t\t\t\tthis.state.step = \"api-url\";\n\t\t\t\t\tthis.render();\n\t\t\t\t} else if (key === \"\\b\" || key === \"\\x7f\") {\n\t\t\t\t\tthis.state.apiKey = this.state.apiKey.slice(0, -1);\n\t\t\t\t\tthis.render();\n\t\t\t\t} else if (key.length === 1 && key.charCodeAt(0) >= 32) {\n\t\t\t\t\tthis.state.apiKey += key;\n\t\t\t\t\tthis.render();\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase \"model-id\":\n\t\t\t\tif (key === \"\\r\" || key === \"\\n\") {\n\t\t\t\t\tif (this.state.modelId.trim()) {\n\t\t\t\t\t\tthis.state.step = \"brand\";\n\t\t\t\t\t\tthis.render();\n\t\t\t\t\t}\n\t\t\t\t} else if (key === \"\\u001b\") {\n\t\t\t\t\tthis.state.step = \"api-key\";\n\t\t\t\t\tthis.render();\n\t\t\t\t} else if (key === \"\\b\" || key === \"\\x7f\") {\n\t\t\t\t\tthis.state.modelId = this.state.modelId.slice(0, -1);\n\t\t\t\t\tthis.render();\n\t\t\t\t} else if (key.length === 1 && key.charCodeAt(0) >= 32) {\n\t\t\t\t\tthis.state.modelId += key;\n\t\t\t\t\tthis.render();\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase \"brand\":\n\t\t\t\tif (key === \"\\r\" || key === \"\\n\") {\n\t\t\t\t\tthis.saveConfiguration();\n\t\t\t\t\tthis.state.step = \"complete\";\n\t\t\t\t\tthis.render();\n\t\t\t\t} else if (key === \"\\u001b\") {\n\t\t\t\t\tthis.state.step = \"model-id\";\n\t\t\t\t\tthis.render();\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase \"complete\":\n\t\t\t\tif (key === \"\\r\" || key === \"\\n\") {\n\t\t\t\t\tthis.onComplete();\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\tprivate saveConfiguration(): void {\n\t\tlet config = loadUserConfig();\n\n\t\tconst modelConfig: ModelConfig = {\n\t\t\tid: this.state.modelId.trim(),\n\t\t\tname: this.state.modelId.trim(),\n\t\t\tprovider: \"custom\",\n\t\t\tapiKey: this.state.apiKey.trim(),\n\t\t\tbaseUrl: this.state.apiUrl.trim(),\n\t\t};\n\n\t\tconfig = setModel(config, modelConfig);\n\t\tconfig = updateBrandSettings(config, {\n\t\t\tproductName: this.state.brandName,\n\t\t\twelcomeMessage: this.state.welcomeMessage,\n\t\t});\n\n\t\tsaveUserConfig(config);\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"onboarding-wizard.js","sourceRoot":"","sources":["../../../../src/modes/interactive/components/onboarding-wizard.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Component } from \"@boxiaolanya2008/pi-tui\";\nimport { Container, Spacer, Text, type TUI } from \"@boxiaolanya2008/pi-tui\";\nimport {\n\tloadUserConfig,\n\ttype ModelConfig,\n\tsaveUserConfig,\n\tsetModel,\n\tupdateBrandSettings,\n} from \"../../../core/user-config.js\";\n\ntype OnboardingStep = \"welcome\" | \"api-url\" | \"api-key\" | \"model-id\" | \"brand\" | \"complete\";\n\ninterface WizardState {\n\tstep: OnboardingStep;\n\tapiUrl: string;\n\tapiKey: string;\n\tmodelId: string;\n\tbrandName: string;\n\twelcomeMessage: string;\n}\n\nexport class OnboardingWizard {\n\tprivate container: Container;\n\tprivate state: WizardState;\n\tprivate onComplete: () => void;\n\tprivate onCancel: () => void;\n\n\tconstructor(_tui: TUI, onComplete: () => void, onCancel: () => void) {\n\t\tthis.onComplete = onComplete;\n\t\tthis.onCancel = onCancel;\n\t\tthis.state = {\n\t\t\tstep: \"welcome\",\n\t\t\tapiUrl: \"\",\n\t\t\tapiKey: \"\",\n\t\t\tmodelId: \"\",\n\t\t\tbrandName: \"OpenVibe\",\n\t\t\twelcomeMessage: \"Welcome to OpenVibe - Your AI Coding Assistant\",\n\t\t};\n\t\tthis.container = new Container();\n\t\tthis.render();\n\t}\n\n\tgetContainer(): Component {\n\t\treturn this.container;\n\t}\n\n\tprivate render(): void {\n\t\tthis.container.clear();\n\t\tthis.renderHeader();\n\t\tswitch (this.state.step) {\n\t\t\tcase \"welcome\":\n\t\t\t\tthis.renderWelcomeStep();\n\t\t\t\tbreak;\n\t\t\tcase \"api-url\":\n\t\t\t\tthis.renderApiUrlStep();\n\t\t\t\tbreak;\n\t\t\tcase \"api-key\":\n\t\t\t\tthis.renderApiKeyStep();\n\t\t\t\tbreak;\n\t\t\tcase \"model-id\":\n\t\t\t\tthis.renderModelIdStep();\n\t\t\t\tbreak;\n\t\t\tcase \"brand\":\n\t\t\t\tthis.renderBrandStep();\n\t\t\t\tbreak;\n\t\t\tcase \"complete\":\n\t\t\t\tthis.renderCompleteStep();\n\t\t\t\tbreak;\n\t\t}\n\t\tthis.renderFooter();\n\t}\n\n\tprivate renderHeader(): void {\n\t\tconst logo = `\n ____ __ __\n / __ \\\\____ ___ ____ / /__ / /_\n / / / / __ \\\\/ _ \\\\/ __ \\\\/ / _ \\\\/ __/\n/ /_/ / /_/ / __/ / / / / __/ /_\n/_____/ .___/\\\\___/_/ /_/_/\\\\___/\\\\__/\n /_/\n `;\n\t\tthis.container.addChild(new Text(logo, 1, 0));\n\t\tthis.container.addChild(new Text(\"=\".repeat(60), 1, 0));\n\t\tconst steps: OnboardingStep[] = [\"welcome\", \"api-url\", \"api-key\", \"model-id\", \"brand\", \"complete\"];\n\t\tconst currentIndex = steps.indexOf(this.state.step);\n\t\tconst progress = steps\n\t\t\t.map((_step, index) => {\n\t\t\t\tif (index < currentIndex) return \"✓\";\n\t\t\t\tif (index === currentIndex) return \"●\";\n\t\t\t\treturn \"○\";\n\t\t\t})\n\t\t\t.join(\" → \");\n\t\tthis.container.addChild(new Text(` ${progress}`, 1, 0));\n\t\tthis.container.addChild(new Text(\"=\".repeat(60), 1, 0));\n\t\tthis.container.addChild(new Spacer(1));\n\t}\n\n\tprivate renderWelcomeStep(): void {\n\t\tthis.container.addChild(new Text(\" Welcome to OpenVibe!\", 1, 0));\n\t\tthis.container.addChild(new Spacer(1));\n\t\tthis.container.addChild(\n\t\t\tnew Text(\" OpenVibe is your AI coding assistant that works with your own API keys.\", 1, 0),\n\t\t);\n\t\tthis.container.addChild(new Spacer(1));\n\t\tthis.container.addChild(new Text(\" This quick setup will help you configure your AI model.\", 1, 0));\n\t\tthis.container.addChild(new Spacer(1));\n\t\tthis.container.addChild(new Text(\" Press Enter to continue or Esc to exit.\", 1, 0));\n\t}\n\n\tprivate renderApiUrlStep(): void {\n\t\tthis.container.addChild(new Text(\" Step 1: Enter API Base URL\", 1, 0));\n\t\tthis.container.addChild(new Spacer(1));\n\t\tthis.container.addChild(new Text(\" Examples:\", 1, 0));\n\t\tthis.container.addChild(new Text(\" • https://api.openai.com/v1\", 1, 0));\n\t\tthis.container.addChild(new Text(\" • https://api.anthropic.com\", 1, 0));\n\t\tthis.container.addChild(new Text(\" • http://localhost:11434/v1\", 1, 0));\n\t\tthis.container.addChild(new Spacer(1));\n\t\tthis.container.addChild(new Text(` URL: ${this.state.apiUrl || \"_\"}`, 1, 0));\n\t\tthis.container.addChild(new Spacer(1));\n\t\tthis.container.addChild(new Text(\" Type the URL and press Enter to continue.\", 1, 0));\n\t}\n\n\tprivate renderApiKeyStep(): void {\n\t\tthis.container.addChild(new Text(\" Step 2: Enter Your API Key\", 1, 0));\n\t\tthis.container.addChild(new Spacer(1));\n\t\tthis.container.addChild(new Text(\" Your API key is stored locally and never shared.\", 1, 0));\n\t\tthis.container.addChild(new Spacer(1));\n\t\tthis.container.addChild(new Text(` API Key: ${\"*\".repeat(this.state.apiKey.length) || \"_\"}`, 1, 0));\n\t\tthis.container.addChild(new Spacer(1));\n\t\tthis.container.addChild(new Text(\" Type your API key and press Enter to continue.\", 1, 0));\n\t}\n\n\tprivate renderModelIdStep(): void {\n\t\tthis.container.addChild(new Text(\" Step 3: Enter Model ID\", 1, 0));\n\t\tthis.container.addChild(new Spacer(1));\n\t\tthis.container.addChild(new Text(\" Examples:\", 1, 0));\n\t\tthis.container.addChild(new Text(\" • gpt-4o\", 1, 0));\n\t\tthis.container.addChild(new Text(\" • claude-3-5-sonnet-20241022\", 1, 0));\n\t\tthis.container.addChild(new Text(\" • grok-2\", 1, 0));\n\t\tthis.container.addChild(new Spacer(1));\n\t\tthis.container.addChild(new Text(` Model ID: ${this.state.modelId || \"_\"}`, 1, 0));\n\t\tthis.container.addChild(new Spacer(1));\n\t\tthis.container.addChild(new Text(\" Type the model ID and press Enter to continue.\", 1, 0));\n\t}\n\n\tprivate renderBrandStep(): void {\n\t\tthis.container.addChild(new Text(\" Step 4: Customize (Optional)\", 1, 0));\n\t\tthis.container.addChild(new Spacer(1));\n\t\tthis.container.addChild(new Text(` Product Name: ${this.state.brandName}`, 1, 0));\n\t\tthis.container.addChild(new Text(` Welcome Message: ${this.state.welcomeMessage}`, 1, 0));\n\t\tthis.container.addChild(new Spacer(1));\n\t\tthis.container.addChild(new Text(\" Press Enter to continue with defaults.\", 1, 0));\n\t}\n\n\tprivate renderCompleteStep(): void {\n\t\tthis.container.addChild(new Text(\" Setup Complete!\", 1, 0));\n\t\tthis.container.addChild(new Spacer(1));\n\t\tthis.container.addChild(new Text(` Model: ${this.state.modelId}`, 1, 0));\n\t\tthis.container.addChild(new Text(` API URL: ${this.state.apiUrl}`, 1, 0));\n\t\tthis.container.addChild(new Text(` Product: ${this.state.brandName}`, 1, 0));\n\t\tthis.container.addChild(new Spacer(1));\n\t\tthis.container.addChild(new Text(\" Your configuration has been saved.\", 1, 0));\n\t\tthis.container.addChild(new Spacer(1));\n\t\tthis.container.addChild(new Text(\" Press Enter to start using OpenVibe!\", 1, 0));\n\t}\n\n\tprivate renderFooter(): void {\n\t\tthis.container.addChild(new Spacer(2));\n\t\tthis.container.addChild(new Text(\"-\".repeat(60), 1, 0));\n\t\tthis.container.addChild(new Text(\" Navigation: Enter = Continue | Esc = Back/Cancel\", 1, 0));\n\t}\n\n\thandleInput(key: string): void {\n\t\tswitch (this.state.step) {\n\t\t\tcase \"welcome\":\n\t\t\t\tif (key === \"\\r\" || key === \"\\n\") {\n\t\t\t\t\tthis.state.step = \"api-url\";\n\t\t\t\t\tthis.render();\n\t\t\t\t} else if (key === \"\\u001b\") {\n\t\t\t\t\tthis.onCancel();\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase \"api-url\":\n\t\t\t\tif (key === \"\\r\" || key === \"\\n\") {\n\t\t\t\t\tif (this.state.apiUrl.trim()) {\n\t\t\t\t\t\tthis.state.step = \"api-key\";\n\t\t\t\t\t\tthis.render();\n\t\t\t\t\t}\n\t\t\t\t} else if (key === \"\\u001b\") {\n\t\t\t\t\tthis.state.step = \"welcome\";\n\t\t\t\t\tthis.render();\n\t\t\t\t} else if (key === \"\\b\" || key === \"\\x7f\") {\n\t\t\t\t\tthis.state.apiUrl = this.state.apiUrl.slice(0, -1);\n\t\t\t\t\tthis.render();\n\t\t\t\t} else if (key.length === 1 && key.charCodeAt(0) >= 32) {\n\t\t\t\t\tthis.state.apiUrl += key;\n\t\t\t\t\tthis.render();\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase \"api-key\":\n\t\t\t\tif (key === \"\\r\" || key === \"\\n\") {\n\t\t\t\t\tif (this.state.apiKey.trim()) {\n\t\t\t\t\t\tthis.state.step = \"model-id\";\n\t\t\t\t\t\tthis.render();\n\t\t\t\t\t}\n\t\t\t\t} else if (key === \"\\u001b\") {\n\t\t\t\t\tthis.state.step = \"api-url\";\n\t\t\t\t\tthis.render();\n\t\t\t\t} else if (key === \"\\b\" || key === \"\\x7f\") {\n\t\t\t\t\tthis.state.apiKey = this.state.apiKey.slice(0, -1);\n\t\t\t\t\tthis.render();\n\t\t\t\t} else if (key.length === 1 && key.charCodeAt(0) >= 32) {\n\t\t\t\t\tthis.state.apiKey += key;\n\t\t\t\t\tthis.render();\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase \"model-id\":\n\t\t\t\tif (key === \"\\r\" || key === \"\\n\") {\n\t\t\t\t\tif (this.state.modelId.trim()) {\n\t\t\t\t\t\tthis.state.step = \"brand\";\n\t\t\t\t\t\tthis.render();\n\t\t\t\t\t}\n\t\t\t\t} else if (key === \"\\u001b\") {\n\t\t\t\t\tthis.state.step = \"api-key\";\n\t\t\t\t\tthis.render();\n\t\t\t\t} else if (key === \"\\b\" || key === \"\\x7f\") {\n\t\t\t\t\tthis.state.modelId = this.state.modelId.slice(0, -1);\n\t\t\t\t\tthis.render();\n\t\t\t\t} else if (key.length === 1 && key.charCodeAt(0) >= 32) {\n\t\t\t\t\tthis.state.modelId += key;\n\t\t\t\t\tthis.render();\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase \"brand\":\n\t\t\t\tif (key === \"\\r\" || key === \"\\n\") {\n\t\t\t\t\tthis.saveConfiguration();\n\t\t\t\t\tthis.state.step = \"complete\";\n\t\t\t\t\tthis.render();\n\t\t\t\t} else if (key === \"\\u001b\") {\n\t\t\t\t\tthis.state.step = \"model-id\";\n\t\t\t\t\tthis.render();\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase \"complete\":\n\t\t\t\tif (key === \"\\r\" || key === \"\\n\") {\n\t\t\t\t\tthis.onComplete();\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\tprivate saveConfiguration(): void {\n\t\tlet config = loadUserConfig();\n\n\t\tconst modelConfig: ModelConfig = {\n\t\t\tid: this.state.modelId.trim(),\n\t\t\tname: this.state.modelId.trim(),\n\t\t\tprovider: \"custom\",\n\t\t\tapiKey: this.state.apiKey.trim(),\n\t\t\tbaseUrl: this.state.apiUrl.trim(),\n\t\t};\n\n\t\tconfig = setModel(config, modelConfig);\n\t\tconfig = updateBrandSettings(config, {\n\t\t\tproductName: this.state.brandName,\n\t\t\twelcomeMessage: this.state.welcomeMessage,\n\t\t});\n\n\t\tsaveUserConfig(config);\n\t}\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session-selector-search.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/session-selector-search.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kCAAkC,CAAC;AACpE,MAAM,MAAM,QAAQ,GAAG,UAAU,GAAG,QAAQ,GAAG,WAAW,CAAC;AAC3D,MAAM,MAAM,UAAU,GAAG,KAAK,GAAG,OAAO,CAAC;AACzC,MAAM,WAAW,iBAAiB;IACjC,IAAI,EAAE,QAAQ,GAAG,OAAO,CAAC;IACzB,MAAM,EAAE;QAAE,IAAI,EAAE,OAAO,GAAG,QAAQ,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IACtD,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;CACf;AACD,MAAM,WAAW,WAAW;IAC3B,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;CACd;AAOD,wBAAgB,cAAc,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAE5D;AAKD,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,iBAAiB,CA6DjE;AACD,wBAAgB,YAAY,CAAC,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,iBAAiB,GAAG,WAAW,CAgCzF;AACD,wBAAgB,qBAAqB,CACpC,QAAQ,EAAE,WAAW,EAAE,EACvB,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,QAAQ,EAClB,UAAU,GAAE,UAAkB,GAC5B,WAAW,EAAE,CA0Bf","sourcesContent":["import { fuzzyMatch } from \"@
|
|
1
|
+
{"version":3,"file":"session-selector-search.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/session-selector-search.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kCAAkC,CAAC;AACpE,MAAM,MAAM,QAAQ,GAAG,UAAU,GAAG,QAAQ,GAAG,WAAW,CAAC;AAC3D,MAAM,MAAM,UAAU,GAAG,KAAK,GAAG,OAAO,CAAC;AACzC,MAAM,WAAW,iBAAiB;IACjC,IAAI,EAAE,QAAQ,GAAG,OAAO,CAAC;IACzB,MAAM,EAAE;QAAE,IAAI,EAAE,OAAO,GAAG,QAAQ,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IACtD,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;CACf;AACD,MAAM,WAAW,WAAW;IAC3B,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;CACd;AAOD,wBAAgB,cAAc,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAE5D;AAKD,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,iBAAiB,CA6DjE;AACD,wBAAgB,YAAY,CAAC,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,iBAAiB,GAAG,WAAW,CAgCzF;AACD,wBAAgB,qBAAqB,CACpC,QAAQ,EAAE,WAAW,EAAE,EACvB,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,QAAQ,EAClB,UAAU,GAAE,UAAkB,GAC5B,WAAW,EAAE,CA0Bf","sourcesContent":["import { fuzzyMatch } from \"@boxiaolanya2008/pi-tui\";\nimport type { SessionInfo } from \"../../../core/session-manager.js\";\nexport type SortMode = \"threaded\" | \"recent\" | \"relevance\";\nexport type NameFilter = \"all\" | \"named\";\nexport interface ParsedSearchQuery {\n\tmode: \"tokens\" | \"regex\";\n\ttokens: { kind: \"fuzzy\" | \"phrase\"; value: string }[];\n\tregex: RegExp | null;\n\terror?: string;\n}\nexport interface MatchResult {\n\tmatches: boolean;\n\tscore: number;\n}\nfunction normalizeWhitespaceLower(text: string): string {\n\treturn text.toLowerCase().replace(/\\s+/g, \" \").trim();\n}\nfunction getSessionSearchText(session: SessionInfo): string {\n\treturn `${session.id} ${session.name ?? \"\"} ${session.allMessagesText} ${session.cwd}`;\n}\nexport function hasSessionName(session: SessionInfo): boolean {\n\treturn Boolean(session.name?.trim());\n}\nfunction matchesNameFilter(session: SessionInfo, filter: NameFilter): boolean {\n\tif (filter === \"all\") return true;\n\treturn hasSessionName(session);\n}\nexport function parseSearchQuery(query: string): ParsedSearchQuery {\n\tconst trimmed = query.trim();\n\tif (!trimmed) {\n\t\treturn { mode: \"tokens\", tokens: [], regex: null };\n\t}\n\tif (trimmed.startsWith(\"re:\")) {\n\t\tconst pattern = trimmed.slice(3).trim();\n\t\tif (!pattern) {\n\t\t\treturn { mode: \"regex\", tokens: [], regex: null, error: \"Empty regex\" };\n\t\t}\n\t\ttry {\n\t\t\treturn { mode: \"regex\", tokens: [], regex: new RegExp(pattern, \"i\") };\n\t\t} catch (err) {\n\t\t\tconst msg = err instanceof Error ? err.message : String(err);\n\t\t\treturn { mode: \"regex\", tokens: [], regex: null, error: msg };\n\t\t}\n\t}\n\tconst tokens: { kind: \"fuzzy\" | \"phrase\"; value: string }[] = [];\n\tlet buf = \"\";\n\tlet inQuote = false;\n\tlet hadUnclosedQuote = false;\n\tconst flush = (kind: \"fuzzy\" | \"phrase\"): void => {\n\t\tconst v = buf.trim();\n\t\tbuf = \"\";\n\t\tif (!v) return;\n\t\ttokens.push({ kind, value: v });\n\t};\n\tfor (let i = 0; i < trimmed.length; i++) {\n\t\tconst ch = trimmed[i]!;\n\t\tif (ch === '\"') {\n\t\t\tif (inQuote) {\n\t\t\t\tflush(\"phrase\");\n\t\t\t\tinQuote = false;\n\t\t\t} else {\n\t\t\t\tflush(\"fuzzy\");\n\t\t\t\tinQuote = true;\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\t\tif (!inQuote && /\\s/.test(ch)) {\n\t\t\tflush(\"fuzzy\");\n\t\t\tcontinue;\n\t\t}\n\t\tbuf += ch;\n\t}\n\tif (inQuote) {\n\t\thadUnclosedQuote = true;\n\t}\n\tif (hadUnclosedQuote) {\n\t\treturn {\n\t\t\tmode: \"tokens\",\n\t\t\ttokens: trimmed\n\t\t\t\t.split(/\\s+/)\n\t\t\t\t.map((t) => t.trim())\n\t\t\t\t.filter((t) => t.length > 0)\n\t\t\t\t.map((t) => ({ kind: \"fuzzy\" as const, value: t })),\n\t\t\tregex: null,\n\t\t};\n\t}\n\tflush(inQuote ? \"phrase\" : \"fuzzy\");\n\treturn { mode: \"tokens\", tokens, regex: null };\n}\nexport function matchSession(session: SessionInfo, parsed: ParsedSearchQuery): MatchResult {\n\tconst text = getSessionSearchText(session);\n\tif (parsed.mode === \"regex\") {\n\t\tif (!parsed.regex) {\n\t\t\treturn { matches: false, score: 0 };\n\t\t}\n\t\tconst idx = text.search(parsed.regex);\n\t\tif (idx < 0) return { matches: false, score: 0 };\n\t\treturn { matches: true, score: idx * 0.1 };\n\t}\n\tif (parsed.tokens.length === 0) {\n\t\treturn { matches: true, score: 0 };\n\t}\n\tlet totalScore = 0;\n\tlet normalizedText: string | null = null;\n\tfor (const token of parsed.tokens) {\n\t\tif (token.kind === \"phrase\") {\n\t\t\tif (normalizedText === null) {\n\t\t\t\tnormalizedText = normalizeWhitespaceLower(text);\n\t\t\t}\n\t\t\tconst phrase = normalizeWhitespaceLower(token.value);\n\t\t\tif (!phrase) continue;\n\t\t\tconst idx = normalizedText.indexOf(phrase);\n\t\t\tif (idx < 0) return { matches: false, score: 0 };\n\t\t\ttotalScore += idx * 0.1;\n\t\t\tcontinue;\n\t\t}\n\t\tconst m = fuzzyMatch(token.value, text);\n\t\tif (!m.matches) return { matches: false, score: 0 };\n\t\ttotalScore += m.score;\n\t}\n\treturn { matches: true, score: totalScore };\n}\nexport function filterAndSortSessions(\n\tsessions: SessionInfo[],\n\tquery: string,\n\tsortMode: SortMode,\n\tnameFilter: NameFilter = \"all\",\n): SessionInfo[] {\n\tconst nameFiltered =\n\t\tnameFilter === \"all\" ? sessions : sessions.filter((session) => matchesNameFilter(session, nameFilter));\n\tconst trimmed = query.trim();\n\tif (!trimmed) return nameFiltered;\n\tconst parsed = parseSearchQuery(query);\n\tif (parsed.error) return [];\n\tif (sortMode === \"recent\") {\n\t\tconst filtered: SessionInfo[] = [];\n\t\tfor (const s of nameFiltered) {\n\t\t\tconst res = matchSession(s, parsed);\n\t\t\tif (res.matches) filtered.push(s);\n\t\t}\n\t\treturn filtered;\n\t}\n\tconst scored: { session: SessionInfo; score: number }[] = [];\n\tfor (const s of nameFiltered) {\n\t\tconst res = matchSession(s, parsed);\n\t\tif (!res.matches) continue;\n\t\tscored.push({ session: s, score: res.score });\n\t}\n\tscored.sort((a, b) => {\n\t\tif (a.score !== b.score) return a.score - b.score;\n\t\treturn b.session.modified.getTime() - a.session.modified.getTime();\n\t});\n\treturn scored.map((r) => r.session);\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session-selector-search.js","sourceRoot":"","sources":["../../../../src/modes/interactive/components/session-selector-search.ts"],"names":[],"mappings":"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","sourcesContent":["import { fuzzyMatch } from \"@mariozechner/pi-tui\";\nimport type { SessionInfo } from \"../../../core/session-manager.js\";\nexport type SortMode = \"threaded\" | \"recent\" | \"relevance\";\nexport type NameFilter = \"all\" | \"named\";\nexport interface ParsedSearchQuery {\n\tmode: \"tokens\" | \"regex\";\n\ttokens: { kind: \"fuzzy\" | \"phrase\"; value: string }[];\n\tregex: RegExp | null;\n\terror?: string;\n}\nexport interface MatchResult {\n\tmatches: boolean;\n\tscore: number;\n}\nfunction normalizeWhitespaceLower(text: string): string {\n\treturn text.toLowerCase().replace(/\\s+/g, \" \").trim();\n}\nfunction getSessionSearchText(session: SessionInfo): string {\n\treturn `${session.id} ${session.name ?? \"\"} ${session.allMessagesText} ${session.cwd}`;\n}\nexport function hasSessionName(session: SessionInfo): boolean {\n\treturn Boolean(session.name?.trim());\n}\nfunction matchesNameFilter(session: SessionInfo, filter: NameFilter): boolean {\n\tif (filter === \"all\") return true;\n\treturn hasSessionName(session);\n}\nexport function parseSearchQuery(query: string): ParsedSearchQuery {\n\tconst trimmed = query.trim();\n\tif (!trimmed) {\n\t\treturn { mode: \"tokens\", tokens: [], regex: null };\n\t}\n\tif (trimmed.startsWith(\"re:\")) {\n\t\tconst pattern = trimmed.slice(3).trim();\n\t\tif (!pattern) {\n\t\t\treturn { mode: \"regex\", tokens: [], regex: null, error: \"Empty regex\" };\n\t\t}\n\t\ttry {\n\t\t\treturn { mode: \"regex\", tokens: [], regex: new RegExp(pattern, \"i\") };\n\t\t} catch (err) {\n\t\t\tconst msg = err instanceof Error ? err.message : String(err);\n\t\t\treturn { mode: \"regex\", tokens: [], regex: null, error: msg };\n\t\t}\n\t}\n\tconst tokens: { kind: \"fuzzy\" | \"phrase\"; value: string }[] = [];\n\tlet buf = \"\";\n\tlet inQuote = false;\n\tlet hadUnclosedQuote = false;\n\tconst flush = (kind: \"fuzzy\" | \"phrase\"): void => {\n\t\tconst v = buf.trim();\n\t\tbuf = \"\";\n\t\tif (!v) return;\n\t\ttokens.push({ kind, value: v });\n\t};\n\tfor (let i = 0; i < trimmed.length; i++) {\n\t\tconst ch = trimmed[i]!;\n\t\tif (ch === '\"') {\n\t\t\tif (inQuote) {\n\t\t\t\tflush(\"phrase\");\n\t\t\t\tinQuote = false;\n\t\t\t} else {\n\t\t\t\tflush(\"fuzzy\");\n\t\t\t\tinQuote = true;\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\t\tif (!inQuote && /\\s/.test(ch)) {\n\t\t\tflush(\"fuzzy\");\n\t\t\tcontinue;\n\t\t}\n\t\tbuf += ch;\n\t}\n\tif (inQuote) {\n\t\thadUnclosedQuote = true;\n\t}\n\tif (hadUnclosedQuote) {\n\t\treturn {\n\t\t\tmode: \"tokens\",\n\t\t\ttokens: trimmed\n\t\t\t\t.split(/\\s+/)\n\t\t\t\t.map((t) => t.trim())\n\t\t\t\t.filter((t) => t.length > 0)\n\t\t\t\t.map((t) => ({ kind: \"fuzzy\" as const, value: t })),\n\t\t\tregex: null,\n\t\t};\n\t}\n\tflush(inQuote ? \"phrase\" : \"fuzzy\");\n\treturn { mode: \"tokens\", tokens, regex: null };\n}\nexport function matchSession(session: SessionInfo, parsed: ParsedSearchQuery): MatchResult {\n\tconst text = getSessionSearchText(session);\n\tif (parsed.mode === \"regex\") {\n\t\tif (!parsed.regex) {\n\t\t\treturn { matches: false, score: 0 };\n\t\t}\n\t\tconst idx = text.search(parsed.regex);\n\t\tif (idx < 0) return { matches: false, score: 0 };\n\t\treturn { matches: true, score: idx * 0.1 };\n\t}\n\tif (parsed.tokens.length === 0) {\n\t\treturn { matches: true, score: 0 };\n\t}\n\tlet totalScore = 0;\n\tlet normalizedText: string | null = null;\n\tfor (const token of parsed.tokens) {\n\t\tif (token.kind === \"phrase\") {\n\t\t\tif (normalizedText === null) {\n\t\t\t\tnormalizedText = normalizeWhitespaceLower(text);\n\t\t\t}\n\t\t\tconst phrase = normalizeWhitespaceLower(token.value);\n\t\t\tif (!phrase) continue;\n\t\t\tconst idx = normalizedText.indexOf(phrase);\n\t\t\tif (idx < 0) return { matches: false, score: 0 };\n\t\t\ttotalScore += idx * 0.1;\n\t\t\tcontinue;\n\t\t}\n\t\tconst m = fuzzyMatch(token.value, text);\n\t\tif (!m.matches) return { matches: false, score: 0 };\n\t\ttotalScore += m.score;\n\t}\n\treturn { matches: true, score: totalScore };\n}\nexport function filterAndSortSessions(\n\tsessions: SessionInfo[],\n\tquery: string,\n\tsortMode: SortMode,\n\tnameFilter: NameFilter = \"all\",\n): SessionInfo[] {\n\tconst nameFiltered =\n\t\tnameFilter === \"all\" ? sessions : sessions.filter((session) => matchesNameFilter(session, nameFilter));\n\tconst trimmed = query.trim();\n\tif (!trimmed) return nameFiltered;\n\tconst parsed = parseSearchQuery(query);\n\tif (parsed.error) return [];\n\tif (sortMode === \"recent\") {\n\t\tconst filtered: SessionInfo[] = [];\n\t\tfor (const s of nameFiltered) {\n\t\t\tconst res = matchSession(s, parsed);\n\t\t\tif (res.matches) filtered.push(s);\n\t\t}\n\t\treturn filtered;\n\t}\n\tconst scored: { session: SessionInfo; score: number }[] = [];\n\tfor (const s of nameFiltered) {\n\t\tconst res = matchSession(s, parsed);\n\t\tif (!res.matches) continue;\n\t\tscored.push({ session: s, score: res.score });\n\t}\n\tscored.sort((a, b) => {\n\t\tif (a.score !== b.score) return a.score - b.score;\n\t\treturn b.session.modified.getTime() - a.session.modified.getTime();\n\t});\n\treturn scored.map((r) => r.session);\n}\n"]}
|
|
1
|
+
{"version":3,"file":"session-selector-search.js","sourceRoot":"","sources":["../../../../src/modes/interactive/components/session-selector-search.ts"],"names":[],"mappings":"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","sourcesContent":["import { fuzzyMatch } from \"@boxiaolanya2008/pi-tui\";\nimport type { SessionInfo } from \"../../../core/session-manager.js\";\nexport type SortMode = \"threaded\" | \"recent\" | \"relevance\";\nexport type NameFilter = \"all\" | \"named\";\nexport interface ParsedSearchQuery {\n\tmode: \"tokens\" | \"regex\";\n\ttokens: { kind: \"fuzzy\" | \"phrase\"; value: string }[];\n\tregex: RegExp | null;\n\terror?: string;\n}\nexport interface MatchResult {\n\tmatches: boolean;\n\tscore: number;\n}\nfunction normalizeWhitespaceLower(text: string): string {\n\treturn text.toLowerCase().replace(/\\s+/g, \" \").trim();\n}\nfunction getSessionSearchText(session: SessionInfo): string {\n\treturn `${session.id} ${session.name ?? \"\"} ${session.allMessagesText} ${session.cwd}`;\n}\nexport function hasSessionName(session: SessionInfo): boolean {\n\treturn Boolean(session.name?.trim());\n}\nfunction matchesNameFilter(session: SessionInfo, filter: NameFilter): boolean {\n\tif (filter === \"all\") return true;\n\treturn hasSessionName(session);\n}\nexport function parseSearchQuery(query: string): ParsedSearchQuery {\n\tconst trimmed = query.trim();\n\tif (!trimmed) {\n\t\treturn { mode: \"tokens\", tokens: [], regex: null };\n\t}\n\tif (trimmed.startsWith(\"re:\")) {\n\t\tconst pattern = trimmed.slice(3).trim();\n\t\tif (!pattern) {\n\t\t\treturn { mode: \"regex\", tokens: [], regex: null, error: \"Empty regex\" };\n\t\t}\n\t\ttry {\n\t\t\treturn { mode: \"regex\", tokens: [], regex: new RegExp(pattern, \"i\") };\n\t\t} catch (err) {\n\t\t\tconst msg = err instanceof Error ? err.message : String(err);\n\t\t\treturn { mode: \"regex\", tokens: [], regex: null, error: msg };\n\t\t}\n\t}\n\tconst tokens: { kind: \"fuzzy\" | \"phrase\"; value: string }[] = [];\n\tlet buf = \"\";\n\tlet inQuote = false;\n\tlet hadUnclosedQuote = false;\n\tconst flush = (kind: \"fuzzy\" | \"phrase\"): void => {\n\t\tconst v = buf.trim();\n\t\tbuf = \"\";\n\t\tif (!v) return;\n\t\ttokens.push({ kind, value: v });\n\t};\n\tfor (let i = 0; i < trimmed.length; i++) {\n\t\tconst ch = trimmed[i]!;\n\t\tif (ch === '\"') {\n\t\t\tif (inQuote) {\n\t\t\t\tflush(\"phrase\");\n\t\t\t\tinQuote = false;\n\t\t\t} else {\n\t\t\t\tflush(\"fuzzy\");\n\t\t\t\tinQuote = true;\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\t\tif (!inQuote && /\\s/.test(ch)) {\n\t\t\tflush(\"fuzzy\");\n\t\t\tcontinue;\n\t\t}\n\t\tbuf += ch;\n\t}\n\tif (inQuote) {\n\t\thadUnclosedQuote = true;\n\t}\n\tif (hadUnclosedQuote) {\n\t\treturn {\n\t\t\tmode: \"tokens\",\n\t\t\ttokens: trimmed\n\t\t\t\t.split(/\\s+/)\n\t\t\t\t.map((t) => t.trim())\n\t\t\t\t.filter((t) => t.length > 0)\n\t\t\t\t.map((t) => ({ kind: \"fuzzy\" as const, value: t })),\n\t\t\tregex: null,\n\t\t};\n\t}\n\tflush(inQuote ? \"phrase\" : \"fuzzy\");\n\treturn { mode: \"tokens\", tokens, regex: null };\n}\nexport function matchSession(session: SessionInfo, parsed: ParsedSearchQuery): MatchResult {\n\tconst text = getSessionSearchText(session);\n\tif (parsed.mode === \"regex\") {\n\t\tif (!parsed.regex) {\n\t\t\treturn { matches: false, score: 0 };\n\t\t}\n\t\tconst idx = text.search(parsed.regex);\n\t\tif (idx < 0) return { matches: false, score: 0 };\n\t\treturn { matches: true, score: idx * 0.1 };\n\t}\n\tif (parsed.tokens.length === 0) {\n\t\treturn { matches: true, score: 0 };\n\t}\n\tlet totalScore = 0;\n\tlet normalizedText: string | null = null;\n\tfor (const token of parsed.tokens) {\n\t\tif (token.kind === \"phrase\") {\n\t\t\tif (normalizedText === null) {\n\t\t\t\tnormalizedText = normalizeWhitespaceLower(text);\n\t\t\t}\n\t\t\tconst phrase = normalizeWhitespaceLower(token.value);\n\t\t\tif (!phrase) continue;\n\t\t\tconst idx = normalizedText.indexOf(phrase);\n\t\t\tif (idx < 0) return { matches: false, score: 0 };\n\t\t\ttotalScore += idx * 0.1;\n\t\t\tcontinue;\n\t\t}\n\t\tconst m = fuzzyMatch(token.value, text);\n\t\tif (!m.matches) return { matches: false, score: 0 };\n\t\ttotalScore += m.score;\n\t}\n\treturn { matches: true, score: totalScore };\n}\nexport function filterAndSortSessions(\n\tsessions: SessionInfo[],\n\tquery: string,\n\tsortMode: SortMode,\n\tnameFilter: NameFilter = \"all\",\n): SessionInfo[] {\n\tconst nameFiltered =\n\t\tnameFilter === \"all\" ? sessions : sessions.filter((session) => matchesNameFilter(session, nameFilter));\n\tconst trimmed = query.trim();\n\tif (!trimmed) return nameFiltered;\n\tconst parsed = parseSearchQuery(query);\n\tif (parsed.error) return [];\n\tif (sortMode === \"recent\") {\n\t\tconst filtered: SessionInfo[] = [];\n\t\tfor (const s of nameFiltered) {\n\t\t\tconst res = matchSession(s, parsed);\n\t\t\tif (res.matches) filtered.push(s);\n\t\t}\n\t\treturn filtered;\n\t}\n\tconst scored: { session: SessionInfo; score: number }[] = [];\n\tfor (const s of nameFiltered) {\n\t\tconst res = matchSession(s, parsed);\n\t\tif (!res.matches) continue;\n\t\tscored.push({ session: s, score: res.score });\n\t}\n\tscored.sort((a, b) => {\n\t\tif (a.score !== b.score) return a.score - b.score;\n\t\treturn b.session.modified.getTime() - a.session.modified.getTime();\n\t});\n\treturn scored.map((r) => r.session);\n}\n"]}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type Component, Container, type Focusable } from "@
|
|
1
|
+
import { type Component, Container, type Focusable } from "@boxiaolanya2008/pi-tui";
|
|
2
2
|
import { KeybindingsManager } from "../../../core/keybindings.js";
|
|
3
3
|
import type { SessionInfo, SessionListProgress } from "../../../core/session-manager.js";
|
|
4
4
|
import { type NameFilter, type SortMode } from "./session-selector-search.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session-selector.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/session-selector.ts"],"names":[],"mappings":"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","sourcesContent":["import { spawnSync } from \"node:child_process\";\nimport { existsSync } from \"node:fs\";\nimport { unlink } from \"node:fs/promises\";\nimport * as os from \"node:os\";\nimport {\n\ttype Component,\n\tContainer,\n\ttype Focusable,\n\tgetEditorKeybindings,\n\tInput,\n\tmatchesKey,\n\tSpacer,\n\tText,\n\ttruncateToWidth,\n\tvisibleWidth,\n} from \"@mariozechner/pi-tui\";\nimport { KeybindingsManager } from \"../../../core/keybindings.js\";\nimport type { SessionInfo, SessionListProgress } from \"../../../core/session-manager.js\";\nimport { theme } from \"../theme/theme.js\";\nimport { DynamicBorder } from \"./dynamic-border.js\";\nimport { appKey, appKeyHint, keyHint } from \"./keybinding-hints.js\";\nimport { filterAndSortSessions, hasSessionName, type NameFilter, type SortMode } from \"./session-selector-search.js\";\n\ntype SessionScope = \"current\" | \"all\";\nfunction shortenPath(path: string): string {\n\tconst home = os.homedir();\n\tif (!path) return path;\n\tif (path.startsWith(home)) {\n\t\treturn `~${path.slice(home.length)}`;\n\t}\n\treturn path;\n}\nfunction formatSessionDate(date: Date): string {\n\tconst now = new Date();\n\tconst diffMs = now.getTime() - date.getTime();\n\tconst diffMins = Math.floor(diffMs / 60000);\n\tconst diffHours = Math.floor(diffMs / 3600000);\n\tconst diffDays = Math.floor(diffMs / 86400000);\n\tif (diffMins < 1) return \"now\";\n\tif (diffMins < 60) return `${diffMins}m`;\n\tif (diffHours < 24) return `${diffHours}h`;\n\tif (diffDays < 7) return `${diffDays}d`;\n\tif (diffDays < 30) return `${Math.floor(diffDays / 7)}w`;\n\tif (diffDays < 365) return `${Math.floor(diffDays / 30)}mo`;\n\treturn `${Math.floor(diffDays / 365)}y`;\n}\nclass SessionSelectorHeader implements Component {\n\tprivate scope: SessionScope;\n\tprivate sortMode: SortMode;\n\tprivate nameFilter: NameFilter;\n\tprivate keybindings: KeybindingsManager;\n\tprivate requestRender: () => void;\n\tprivate loading = false;\n\tprivate loadProgress: { loaded: number; total: number } | null = null;\n\tprivate showPath = false;\n\tprivate confirmingDeletePath: string | null = null;\n\tprivate statusMessage: { type: \"info\" | \"error\"; message: string } | null = null;\n\tprivate statusTimeout: ReturnType<typeof setTimeout> | null = null;\n\tprivate showRenameHint = false;\n\tconstructor(\n\t\tscope: SessionScope,\n\t\tsortMode: SortMode,\n\t\tnameFilter: NameFilter,\n\t\tkeybindings: KeybindingsManager,\n\t\trequestRender: () => void,\n\t) {\n\t\tthis.scope = scope;\n\t\tthis.sortMode = sortMode;\n\t\tthis.nameFilter = nameFilter;\n\t\tthis.keybindings = keybindings;\n\t\tthis.requestRender = requestRender;\n\t}\n\tsetScope(scope: SessionScope): void {\n\t\tthis.scope = scope;\n\t}\n\tsetSortMode(sortMode: SortMode): void {\n\t\tthis.sortMode = sortMode;\n\t}\n\tsetNameFilter(nameFilter: NameFilter): void {\n\t\tthis.nameFilter = nameFilter;\n\t}\n\tsetLoading(loading: boolean): void {\n\t\tthis.loading = loading;\n\t\tthis.loadProgress = null;\n\t}\n\tsetProgress(loaded: number, total: number): void {\n\t\tthis.loadProgress = { loaded, total };\n\t}\n\tsetShowPath(showPath: boolean): void {\n\t\tthis.showPath = showPath;\n\t}\n\tsetShowRenameHint(show: boolean): void {\n\t\tthis.showRenameHint = show;\n\t}\n\tsetConfirmingDeletePath(path: string | null): void {\n\t\tthis.confirmingDeletePath = path;\n\t}\n\tprivate clearStatusTimeout(): void {\n\t\tif (!this.statusTimeout) return;\n\t\tclearTimeout(this.statusTimeout);\n\t\tthis.statusTimeout = null;\n\t}\n\tsetStatusMessage(msg: { type: \"info\" | \"error\"; message: string } | null, autoHideMs?: number): void {\n\t\tthis.clearStatusTimeout();\n\t\tthis.statusMessage = msg;\n\t\tif (!msg || !autoHideMs) return;\n\t\tthis.statusTimeout = setTimeout(() => {\n\t\t\tthis.statusMessage = null;\n\t\t\tthis.statusTimeout = null;\n\t\t\tthis.requestRender();\n\t\t}, autoHideMs);\n\t}\n\tinvalidate(): void {}\n\trender(width: number): string[] {\n\t\tconst title = this.scope === \"current\" ? \"Resume Session (Current Folder)\" : \"Resume Session (All)\";\n\t\tconst leftText = theme.bold(title);\n\t\tconst sortLabel = this.sortMode === \"threaded\" ? \"Threaded\" : this.sortMode === \"recent\" ? \"Recent\" : \"Fuzzy\";\n\t\tconst sortText = theme.fg(\"muted\", \"Sort: \") + theme.fg(\"accent\", sortLabel);\n\t\tconst nameLabel = this.nameFilter === \"all\" ? \"All\" : \"Named\";\n\t\tconst nameText = theme.fg(\"muted\", \"Name: \") + theme.fg(\"accent\", nameLabel);\n\t\tlet scopeText: string;\n\t\tif (this.loading) {\n\t\t\tconst progressText = this.loadProgress ? `${this.loadProgress.loaded}/${this.loadProgress.total}` : \"...\";\n\t\t\tscopeText = `${theme.fg(\"muted\", \"○ Current Folder | \")}${theme.fg(\"accent\", `Loading ${progressText}`)}`;\n\t\t} else if (this.scope === \"current\") {\n\t\t\tscopeText = `${theme.fg(\"accent\", \"◉ Current Folder\")}${theme.fg(\"muted\", \" | ○ All\")}`;\n\t\t} else {\n\t\t\tscopeText = `${theme.fg(\"muted\", \"○ Current Folder | \")}${theme.fg(\"accent\", \"◉ All\")}`;\n\t\t}\n\t\tconst rightText = truncateToWidth(`${scopeText} ${nameText} ${sortText}`, width, \"\");\n\t\tconst availableLeft = Math.max(0, width - visibleWidth(rightText) - 1);\n\t\tconst left = truncateToWidth(leftText, availableLeft, \"\");\n\t\tconst spacing = Math.max(0, width - visibleWidth(left) - visibleWidth(rightText));\n\t\tlet hintLine1: string;\n\t\tlet hintLine2: string;\n\t\tif (this.confirmingDeletePath !== null) {\n\t\t\tconst confirmHint = \"Delete session? [Enter] confirm · [Esc/Ctrl+C] cancel\";\n\t\t\thintLine1 = theme.fg(\"error\", truncateToWidth(confirmHint, width, \"…\"));\n\t\t\thintLine2 = \"\";\n\t\t} else if (this.statusMessage) {\n\t\t\tconst color = this.statusMessage.type === \"error\" ? \"error\" : \"accent\";\n\t\t\thintLine1 = theme.fg(color, truncateToWidth(this.statusMessage.message, width, \"…\"));\n\t\t\thintLine2 = \"\";\n\t\t} else {\n\t\t\tconst pathState = this.showPath ? \"(on)\" : \"(off)\";\n\t\t\tconst sep = theme.fg(\"muted\", \" · \");\n\t\t\tconst hint1 = keyHint(\"tab\", \"scope\") + sep + theme.fg(\"muted\", 're:<pattern> regex · \"phrase\" exact');\n\t\t\tconst hint2Parts = [\n\t\t\t\tkeyHint(\"toggleSessionSort\", \"sort\"),\n\t\t\t\tappKeyHint(this.keybindings, \"toggleSessionNamedFilter\", \"named\"),\n\t\t\t\tkeyHint(\"deleteSession\", \"delete\"),\n\t\t\t\tkeyHint(\"toggleSessionPath\", `path ${pathState}`),\n\t\t\t];\n\t\t\tif (this.showRenameHint) {\n\t\t\t\thint2Parts.push(keyHint(\"renameSession\", \"rename\"));\n\t\t\t}\n\t\t\tconst hint2 = hint2Parts.join(sep);\n\t\t\thintLine1 = truncateToWidth(hint1, width, \"…\");\n\t\t\thintLine2 = truncateToWidth(hint2, width, \"…\");\n\t\t}\n\t\treturn [`${left}${\" \".repeat(spacing)}${rightText}`, hintLine1, hintLine2];\n\t}\n}\ninterface SessionTreeNode {\n\tsession: SessionInfo;\n\tchildren: SessionTreeNode[];\n}\ninterface FlatSessionNode {\n\tsession: SessionInfo;\n\tdepth: number;\n\tisLast: boolean;\n\tancestorContinues: boolean[];\n}\nfunction buildSessionTree(sessions: SessionInfo[]): SessionTreeNode[] {\n\tconst byPath = new Map<string, SessionTreeNode>();\n\tfor (const session of sessions) {\n\t\tbyPath.set(session.path, { session, children: [] });\n\t}\n\tconst roots: SessionTreeNode[] = [];\n\tfor (const session of sessions) {\n\t\tconst node = byPath.get(session.path)!;\n\t\tconst parentPath = session.parentSessionPath;\n\t\tif (parentPath && byPath.has(parentPath)) {\n\t\t\tbyPath.get(parentPath)!.children.push(node);\n\t\t} else {\n\t\t\troots.push(node);\n\t\t}\n\t}\n\tconst sortNodes = (nodes: SessionTreeNode[]): void => {\n\t\tnodes.sort((a, b) => b.session.modified.getTime() - a.session.modified.getTime());\n\t\tfor (const node of nodes) {\n\t\t\tsortNodes(node.children);\n\t\t}\n\t};\n\tsortNodes(roots);\n\treturn roots;\n}\nfunction flattenSessionTree(roots: SessionTreeNode[]): FlatSessionNode[] {\n\tconst result: FlatSessionNode[] = [];\n\tconst walk = (node: SessionTreeNode, depth: number, ancestorContinues: boolean[], isLast: boolean): void => {\n\t\tresult.push({ session: node.session, depth, isLast, ancestorContinues });\n\t\tfor (let i = 0; i < node.children.length; i++) {\n\t\t\tconst childIsLast = i === node.children.length - 1;\n\t\t\tconst continues = depth > 0 ? !isLast : false;\n\t\t\twalk(node.children[i]!, depth + 1, [...ancestorContinues, continues], childIsLast);\n\t\t}\n\t};\n\tfor (let i = 0; i < roots.length; i++) {\n\t\twalk(roots[i]!, 0, [], i === roots.length - 1);\n\t}\n\treturn result;\n}\nclass SessionList implements Component, Focusable {\n\tpublic getSelectedSessionPath(): string | undefined {\n\t\tconst selected = this.filteredSessions[this.selectedIndex];\n\t\treturn selected?.session.path;\n\t}\n\tprivate allSessions: SessionInfo[] = [];\n\tprivate filteredSessions: FlatSessionNode[] = [];\n\tprivate selectedIndex: number = 0;\n\tprivate searchInput: Input;\n\tprivate showCwd = false;\n\tprivate sortMode: SortMode = \"threaded\";\n\tprivate nameFilter: NameFilter = \"all\";\n\tprivate keybindings: KeybindingsManager;\n\tprivate showPath = false;\n\tprivate confirmingDeletePath: string | null = null;\n\tprivate currentSessionFilePath?: string;\n\tpublic onSelect?: (sessionPath: string) => void;\n\tpublic onCancel?: () => void;\n\tpublic onExit: () => void = () => {};\n\tpublic onToggleScope?: () => void;\n\tpublic onToggleSort?: () => void;\n\tpublic onToggleNameFilter?: () => void;\n\tpublic onTogglePath?: (showPath: boolean) => void;\n\tpublic onDeleteConfirmationChange?: (path: string | null) => void;\n\tpublic onDeleteSession?: (sessionPath: string) => Promise<void>;\n\tpublic onRenameSession?: (sessionPath: string) => void;\n\tpublic onError?: (message: string) => void;\n\tprivate maxVisible: number = 10;\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\tconstructor(\n\t\tsessions: SessionInfo[],\n\t\tshowCwd: boolean,\n\t\tsortMode: SortMode,\n\t\tnameFilter: NameFilter,\n\t\tkeybindings: KeybindingsManager,\n\t\tcurrentSessionFilePath?: string,\n\t) {\n\t\tthis.allSessions = sessions;\n\t\tthis.filteredSessions = [];\n\t\tthis.searchInput = new Input();\n\t\tthis.showCwd = showCwd;\n\t\tthis.sortMode = sortMode;\n\t\tthis.nameFilter = nameFilter;\n\t\tthis.keybindings = keybindings;\n\t\tthis.currentSessionFilePath = currentSessionFilePath;\n\t\tthis.filterSessions(\"\");\n\t\tthis.searchInput.onSubmit = () => {\n\t\t\tif (this.filteredSessions[this.selectedIndex]) {\n\t\t\t\tconst selected = this.filteredSessions[this.selectedIndex];\n\t\t\t\tif (this.onSelect) {\n\t\t\t\t\tthis.onSelect(selected.session.path);\n\t\t\t\t}\n\t\t\t}\n\t\t};\n\t}\n\tsetSortMode(sortMode: SortMode): void {\n\t\tthis.sortMode = sortMode;\n\t\tthis.filterSessions(this.searchInput.getValue());\n\t}\n\tsetNameFilter(nameFilter: NameFilter): void {\n\t\tthis.nameFilter = nameFilter;\n\t\tthis.filterSessions(this.searchInput.getValue());\n\t}\n\tsetSessions(sessions: SessionInfo[], showCwd: boolean): void {\n\t\tthis.allSessions = sessions;\n\t\tthis.showCwd = showCwd;\n\t\tthis.filterSessions(this.searchInput.getValue());\n\t}\n\tprivate filterSessions(query: string): void {\n\t\tconst trimmed = query.trim();\n\t\tconst nameFiltered =\n\t\t\tthis.nameFilter === \"all\" ? this.allSessions : this.allSessions.filter((session) => hasSessionName(session));\n\t\tif (this.sortMode === \"threaded\" && !trimmed) {\n\t\t\tconst roots = buildSessionTree(nameFiltered);\n\t\t\tthis.filteredSessions = flattenSessionTree(roots);\n\t\t} else {\n\t\t\tconst filtered = filterAndSortSessions(nameFiltered, query, this.sortMode, \"all\");\n\t\t\tthis.filteredSessions = filtered.map((session) => ({\n\t\t\t\tsession,\n\t\t\t\tdepth: 0,\n\t\t\t\tisLast: true,\n\t\t\t\tancestorContinues: [],\n\t\t\t}));\n\t\t}\n\t\tthis.selectedIndex = Math.min(this.selectedIndex, Math.max(0, this.filteredSessions.length - 1));\n\t}\n\tprivate setConfirmingDeletePath(path: string | null): void {\n\t\tthis.confirmingDeletePath = path;\n\t\tthis.onDeleteConfirmationChange?.(path);\n\t}\n\tprivate startDeleteConfirmationForSelectedSession(): void {\n\t\tconst selected = this.filteredSessions[this.selectedIndex];\n\t\tif (!selected) return;\n\t\tif (this.currentSessionFilePath && selected.session.path === this.currentSessionFilePath) {\n\t\t\tthis.onError?.(\"Cannot delete the currently active session\");\n\t\t\treturn;\n\t\t}\n\t\tthis.setConfirmingDeletePath(selected.session.path);\n\t}\n\tinvalidate(): void {}\n\trender(width: number): string[] {\n\t\tconst lines: string[] = [];\n\t\tlines.push(...this.searchInput.render(width));\n\t\tlines.push(\"\");\n\t\tif (this.filteredSessions.length === 0) {\n\t\t\tlet emptyMessage: string;\n\t\t\tif (this.nameFilter === \"named\") {\n\t\t\t\tconst toggleKey = appKey(this.keybindings, \"toggleSessionNamedFilter\");\n\t\t\t\tif (this.showCwd) {\n\t\t\t\t\temptyMessage = ` No named sessions found. Press ${toggleKey} to show all.`;\n\t\t\t\t} else {\n\t\t\t\t\temptyMessage = ` No named sessions in current folder. Press ${toggleKey} to show all, or Tab to view all.`;\n\t\t\t\t}\n\t\t\t} else if (this.showCwd) {\n\t\t\t\temptyMessage = \" No sessions found\";\n\t\t\t} else {\n\t\t\t\temptyMessage = \" No sessions in current folder. Press Tab to view all.\";\n\t\t\t}\n\t\t\tlines.push(theme.fg(\"muted\", truncateToWidth(emptyMessage, width, \"…\")));\n\t\t\treturn lines;\n\t\t}\n\t\tconst startIndex = Math.max(\n\t\t\t0,\n\t\t\tMath.min(this.selectedIndex - Math.floor(this.maxVisible / 2), this.filteredSessions.length - this.maxVisible),\n\t\t);\n\t\tconst endIndex = Math.min(startIndex + this.maxVisible, this.filteredSessions.length);\n\t\tfor (let i = startIndex; i < endIndex; i++) {\n\t\t\tconst node = this.filteredSessions[i]!;\n\t\t\tconst session = node.session;\n\t\t\tconst isSelected = i === this.selectedIndex;\n\t\t\tconst isConfirmingDelete = session.path === this.confirmingDeletePath;\n\t\t\tconst isCurrent = this.currentSessionFilePath === session.path;\n\t\t\tconst prefix = this.buildTreePrefix(node);\n\t\t\tconst hasName = !!session.name;\n\t\t\tconst displayText = session.name ?? session.firstMessage;\n\t\t\tconst normalizedMessage = displayText.replace(/[\\x00-\\x1f\\x7f]/g, \" \").trim();\n\t\t\tconst age = formatSessionDate(session.modified);\n\t\t\tconst msgCount = String(session.messageCount);\n\t\t\tlet rightPart = `${msgCount} ${age}`;\n\t\t\tif (this.showCwd && session.cwd) {\n\t\t\t\trightPart = `${shortenPath(session.cwd)} ${rightPart}`;\n\t\t\t}\n\t\t\tif (this.showPath) {\n\t\t\t\trightPart = `${shortenPath(session.path)} ${rightPart}`;\n\t\t\t}\n\t\t\tconst cursor = isSelected ? theme.fg(\"accent\", \"› \") : \" \";\n\t\t\tconst prefixWidth = visibleWidth(prefix);\n\t\t\tconst rightWidth = visibleWidth(rightPart) + 2;\n\t\t\tconst availableForMsg = width - 2 - prefixWidth - rightWidth;\n\t\t\tconst truncatedMsg = truncateToWidth(normalizedMessage, Math.max(10, availableForMsg), \"…\");\n\t\t\tlet messageColor: \"error\" | \"warning\" | \"accent\" | null = null;\n\t\t\tif (isConfirmingDelete) {\n\t\t\t\tmessageColor = \"error\";\n\t\t\t} else if (isCurrent) {\n\t\t\t\tmessageColor = \"accent\";\n\t\t\t} else if (hasName) {\n\t\t\t\tmessageColor = \"warning\";\n\t\t\t}\n\t\t\tlet styledMsg = messageColor ? theme.fg(messageColor, truncatedMsg) : truncatedMsg;\n\t\t\tif (isSelected) {\n\t\t\t\tstyledMsg = theme.bold(styledMsg);\n\t\t\t}\n\t\t\tconst leftPart = cursor + theme.fg(\"dim\", prefix) + styledMsg;\n\t\t\tconst leftWidth = visibleWidth(leftPart);\n\t\t\tconst spacing = Math.max(1, width - leftWidth - visibleWidth(rightPart));\n\t\t\tconst styledRight = theme.fg(isConfirmingDelete ? \"error\" : \"dim\", rightPart);\n\t\t\tlet line = leftPart + \" \".repeat(spacing) + styledRight;\n\t\t\tif (isSelected) {\n\t\t\t\tline = theme.bg(\"selectedBg\", line);\n\t\t\t}\n\t\t\tlines.push(truncateToWidth(line, width));\n\t\t}\n\t\tif (startIndex > 0 || endIndex < this.filteredSessions.length) {\n\t\t\tconst scrollText = ` (${this.selectedIndex + 1}/${this.filteredSessions.length})`;\n\t\t\tconst scrollInfo = theme.fg(\"muted\", truncateToWidth(scrollText, width, \"\"));\n\t\t\tlines.push(scrollInfo);\n\t\t}\n\t\treturn lines;\n\t}\n\tprivate buildTreePrefix(node: FlatSessionNode): string {\n\t\tif (node.depth === 0) {\n\t\t\treturn \"\";\n\t\t}\n\t\tconst parts = node.ancestorContinues.map((continues) => (continues ? \"│ \" : \" \"));\n\t\tconst branch = node.isLast ? \"└─ \" : \"├─ \";\n\t\treturn parts.join(\"\") + branch;\n\t}\n\thandleInput(keyData: string): void {\n\t\tconst kb = getEditorKeybindings();\n\t\tif (this.confirmingDeletePath !== null) {\n\t\t\tif (kb.matches(keyData, \"selectConfirm\")) {\n\t\t\t\tconst pathToDelete = this.confirmingDeletePath;\n\t\t\t\tthis.setConfirmingDeletePath(null);\n\t\t\t\tvoid this.onDeleteSession?.(pathToDelete);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (kb.matches(keyData, \"selectCancel\") || matchesKey(keyData, \"ctrl+c\")) {\n\t\t\t\tthis.setConfirmingDeletePath(null);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\tif (kb.matches(keyData, \"tab\")) {\n\t\t\tif (this.onToggleScope) {\n\t\t\t\tthis.onToggleScope();\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\tif (kb.matches(keyData, \"toggleSessionSort\")) {\n\t\t\tthis.onToggleSort?.();\n\t\t\treturn;\n\t\t}\n\t\tif (this.keybindings.matches(keyData, \"toggleSessionNamedFilter\")) {\n\t\t\tthis.onToggleNameFilter?.();\n\t\t\treturn;\n\t\t}\n\t\tif (kb.matches(keyData, \"toggleSessionPath\")) {\n\t\t\tthis.showPath = !this.showPath;\n\t\t\tthis.onTogglePath?.(this.showPath);\n\t\t\treturn;\n\t\t}\n\t\tif (kb.matches(keyData, \"deleteSession\")) {\n\t\t\tthis.startDeleteConfirmationForSelectedSession();\n\t\t\treturn;\n\t\t}\n\t\tif (matchesKey(keyData, \"ctrl+r\")) {\n\t\t\tconst selected = this.filteredSessions[this.selectedIndex];\n\t\t\tif (selected) {\n\t\t\t\tthis.onRenameSession?.(selected.session.path);\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\tif (kb.matches(keyData, \"deleteSessionNoninvasive\")) {\n\t\t\tif (this.searchInput.getValue().length > 0) {\n\t\t\t\tthis.searchInput.handleInput(keyData);\n\t\t\t\tthis.filterSessions(this.searchInput.getValue());\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tthis.startDeleteConfirmationForSelectedSession();\n\t\t\treturn;\n\t\t}\n\t\tif (kb.matches(keyData, \"selectUp\")) {\n\t\t\tthis.selectedIndex = Math.max(0, this.selectedIndex - 1);\n\t\t} else if (kb.matches(keyData, \"selectDown\")) {\n\t\t\tthis.selectedIndex = Math.min(this.filteredSessions.length - 1, this.selectedIndex + 1);\n\t\t} else if (kb.matches(keyData, \"selectPageUp\")) {\n\t\t\tthis.selectedIndex = Math.max(0, this.selectedIndex - this.maxVisible);\n\t\t} else if (kb.matches(keyData, \"selectPageDown\")) {\n\t\t\tthis.selectedIndex = Math.min(this.filteredSessions.length - 1, this.selectedIndex + this.maxVisible);\n\t\t} else if (kb.matches(keyData, \"selectConfirm\")) {\n\t\t\tconst selected = this.filteredSessions[this.selectedIndex];\n\t\t\tif (selected && this.onSelect) {\n\t\t\t\tthis.onSelect(selected.session.path);\n\t\t\t}\n\t\t} else if (kb.matches(keyData, \"selectCancel\")) {\n\t\t\tif (this.onCancel) {\n\t\t\t\tthis.onCancel();\n\t\t\t}\n\t\t} else {\n\t\t\tthis.searchInput.handleInput(keyData);\n\t\t\tthis.filterSessions(this.searchInput.getValue());\n\t\t}\n\t}\n}\ntype SessionsLoader = (onProgress?: SessionListProgress) => Promise<SessionInfo[]>;\nasync function deleteSessionFile(\n\tsessionPath: string,\n): Promise<{ ok: boolean; method: \"trash\" | \"unlink\"; error?: string }> {\n\tconst trashArgs = sessionPath.startsWith(\"-\") ? [\"--\", sessionPath] : [sessionPath];\n\tconst trashResult = spawnSync(\"trash\", trashArgs, { encoding: \"utf-8\" });\n\tconst getTrashErrorHint = (): string | null => {\n\t\tconst parts: string[] = [];\n\t\tif (trashResult.error) {\n\t\t\tparts.push(trashResult.error.message);\n\t\t}\n\t\tconst stderr = trashResult.stderr?.trim();\n\t\tif (stderr) {\n\t\t\tparts.push(stderr.split(\"\\n\")[0] ?? stderr);\n\t\t}\n\t\tif (parts.length === 0) return null;\n\t\treturn `trash: ${parts.join(\" · \").slice(0, 200)}`;\n\t};\n\tif (trashResult.status === 0 || !existsSync(sessionPath)) {\n\t\treturn { ok: true, method: \"trash\" };\n\t}\n\ttry {\n\t\tawait unlink(sessionPath);\n\t\treturn { ok: true, method: \"unlink\" };\n\t} catch (err) {\n\t\tconst unlinkError = err instanceof Error ? err.message : String(err);\n\t\tconst trashErrorHint = getTrashErrorHint();\n\t\tconst error = trashErrorHint ? `${unlinkError} (${trashErrorHint})` : unlinkError;\n\t\treturn { ok: false, method: \"unlink\", error };\n\t}\n}\nexport class SessionSelectorComponent extends Container implements Focusable {\n\thandleInput(data: string): void {\n\t\tif (this.mode === \"rename\") {\n\t\t\tconst kb = getEditorKeybindings();\n\t\t\tif (kb.matches(data, \"selectCancel\") || matchesKey(data, \"ctrl+c\")) {\n\t\t\t\tthis.exitRenameMode();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tthis.renameInput.handleInput(data);\n\t\t\treturn;\n\t\t}\n\t\tthis.sessionList.handleInput(data);\n\t}\n\tprivate canRename = true;\n\tprivate sessionList: SessionList;\n\tprivate header: SessionSelectorHeader;\n\tprivate keybindings: KeybindingsManager;\n\tprivate scope: SessionScope = \"current\";\n\tprivate sortMode: SortMode = \"threaded\";\n\tprivate nameFilter: NameFilter = \"all\";\n\tprivate currentSessions: SessionInfo[] | null = null;\n\tprivate allSessions: SessionInfo[] | null = null;\n\tprivate currentSessionsLoader: SessionsLoader;\n\tprivate allSessionsLoader: SessionsLoader;\n\tprivate onCancel: () => void;\n\tprivate requestRender: () => void;\n\tprivate renameSession?: (sessionPath: string, currentName: string | undefined) => Promise<void>;\n\tprivate currentLoading = false;\n\tprivate allLoading = false;\n\tprivate allLoadSeq = 0;\n\tprivate mode: \"list\" | \"rename\" = \"list\";\n\tprivate renameInput = new Input();\n\tprivate renameTargetPath: string | null = null;\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.sessionList.focused = value;\n\t\tthis.renameInput.focused = value;\n\t\tif (value && this.mode === \"rename\") {\n\t\t\tthis.renameInput.focused = true;\n\t\t}\n\t}\n\tprivate buildBaseLayout(content: Component, options?: { showHeader?: boolean }): void {\n\t\tthis.clear();\n\t\tthis.addChild(new Spacer(1));\n\t\tthis.addChild(new DynamicBorder((s) => theme.fg(\"accent\", s)));\n\t\tthis.addChild(new Spacer(1));\n\t\tif (options?.showHeader ?? true) {\n\t\t\tthis.addChild(this.header);\n\t\t\tthis.addChild(new Spacer(1));\n\t\t}\n\t\tthis.addChild(content);\n\t\tthis.addChild(new Spacer(1));\n\t\tthis.addChild(new DynamicBorder((s) => theme.fg(\"accent\", s)));\n\t}\n\tconstructor(\n\t\tcurrentSessionsLoader: SessionsLoader,\n\t\tallSessionsLoader: SessionsLoader,\n\t\tonSelect: (sessionPath: string) => void,\n\t\tonCancel: () => void,\n\t\tonExit: () => void,\n\t\trequestRender: () => void,\n\t\toptions?: {\n\t\t\trenameSession?: (sessionPath: string, currentName: string | undefined) => Promise<void>;\n\t\t\tshowRenameHint?: boolean;\n\t\t\tkeybindings?: KeybindingsManager;\n\t\t},\n\t\tcurrentSessionFilePath?: string,\n\t) {\n\t\tsuper();\n\t\tthis.keybindings = options?.keybindings ?? KeybindingsManager.create();\n\t\tthis.currentSessionsLoader = currentSessionsLoader;\n\t\tthis.allSessionsLoader = allSessionsLoader;\n\t\tthis.onCancel = onCancel;\n\t\tthis.requestRender = requestRender;\n\t\tthis.header = new SessionSelectorHeader(\n\t\t\tthis.scope,\n\t\t\tthis.sortMode,\n\t\t\tthis.nameFilter,\n\t\t\tthis.keybindings,\n\t\t\tthis.requestRender,\n\t\t);\n\t\tconst renameSession = options?.renameSession;\n\t\tthis.renameSession = renameSession;\n\t\tthis.canRename = !!renameSession;\n\t\tthis.header.setShowRenameHint(options?.showRenameHint ?? this.canRename);\n\t\tthis.sessionList = new SessionList(\n\t\t\t[],\n\t\t\tfalse,\n\t\t\tthis.sortMode,\n\t\t\tthis.nameFilter,\n\t\t\tthis.keybindings,\n\t\t\tcurrentSessionFilePath,\n\t\t);\n\t\tthis.buildBaseLayout(this.sessionList);\n\t\tthis.renameInput.onSubmit = (value) => {\n\t\t\tvoid this.confirmRename(value);\n\t\t};\n\t\tconst clearStatusMessage = () => this.header.setStatusMessage(null);\n\t\tthis.sessionList.onSelect = (sessionPath) => {\n\t\t\tclearStatusMessage();\n\t\t\tonSelect(sessionPath);\n\t\t};\n\t\tthis.sessionList.onCancel = () => {\n\t\t\tclearStatusMessage();\n\t\t\tonCancel();\n\t\t};\n\t\tthis.sessionList.onExit = () => {\n\t\t\tclearStatusMessage();\n\t\t\tonExit();\n\t\t};\n\t\tthis.sessionList.onToggleScope = () => this.toggleScope();\n\t\tthis.sessionList.onToggleSort = () => this.toggleSortMode();\n\t\tthis.sessionList.onToggleNameFilter = () => this.toggleNameFilter();\n\t\tthis.sessionList.onRenameSession = (sessionPath) => {\n\t\t\tif (!renameSession) return;\n\t\t\tif (this.scope === \"current\" && this.currentLoading) return;\n\t\t\tif (this.scope === \"all\" && this.allLoading) return;\n\t\t\tconst sessions = this.scope === \"all\" ? (this.allSessions ?? []) : (this.currentSessions ?? []);\n\t\t\tconst session = sessions.find((s) => s.path === sessionPath);\n\t\t\tthis.enterRenameMode(sessionPath, session?.name);\n\t\t};\n\t\tthis.sessionList.onTogglePath = (showPath) => {\n\t\t\tthis.header.setShowPath(showPath);\n\t\t\tthis.requestRender();\n\t\t};\n\t\tthis.sessionList.onDeleteConfirmationChange = (path) => {\n\t\t\tthis.header.setConfirmingDeletePath(path);\n\t\t\tthis.requestRender();\n\t\t};\n\t\tthis.sessionList.onError = (msg) => {\n\t\t\tthis.header.setStatusMessage({ type: \"error\", message: msg }, 3000);\n\t\t\tthis.requestRender();\n\t\t};\n\t\tthis.sessionList.onDeleteSession = async (sessionPath: string) => {\n\t\t\tconst result = await deleteSessionFile(sessionPath);\n\t\t\tif (result.ok) {\n\t\t\t\tif (this.currentSessions) {\n\t\t\t\t\tthis.currentSessions = this.currentSessions.filter((s) => s.path !== sessionPath);\n\t\t\t\t}\n\t\t\t\tif (this.allSessions) {\n\t\t\t\t\tthis.allSessions = this.allSessions.filter((s) => s.path !== sessionPath);\n\t\t\t\t}\n\t\t\t\tconst sessions = this.scope === \"all\" ? (this.allSessions ?? []) : (this.currentSessions ?? []);\n\t\t\t\tconst showCwd = this.scope === \"all\";\n\t\t\t\tthis.sessionList.setSessions(sessions, showCwd);\n\t\t\t\tconst msg = result.method === \"trash\" ? \"Session moved to trash\" : \"Session deleted\";\n\t\t\t\tthis.header.setStatusMessage({ type: \"info\", message: msg }, 2000);\n\t\t\t\tawait this.refreshSessionsAfterMutation();\n\t\t\t} else {\n\t\t\t\tconst errorMessage = result.error ?? \"Unknown error\";\n\t\t\t\tthis.header.setStatusMessage({ type: \"error\", message: `Failed to delete: ${errorMessage}` }, 3000);\n\t\t\t}\n\t\t\tthis.requestRender();\n\t\t};\n\t\tthis.loadCurrentSessions();\n\t}\n\tprivate loadCurrentSessions(): void {\n\t\tvoid this.loadScope(\"current\", \"initial\");\n\t}\n\tprivate enterRenameMode(sessionPath: string, currentName: string | undefined): void {\n\t\tthis.mode = \"rename\";\n\t\tthis.renameTargetPath = sessionPath;\n\t\tthis.renameInput.setValue(currentName ?? \"\");\n\t\tthis.renameInput.focused = true;\n\t\tconst panel = new Container();\n\t\tpanel.addChild(new Text(theme.bold(\"Rename Session\"), 1, 0));\n\t\tpanel.addChild(new Spacer(1));\n\t\tpanel.addChild(this.renameInput);\n\t\tpanel.addChild(new Spacer(1));\n\t\tpanel.addChild(new Text(theme.fg(\"muted\", \"Enter to save · Esc/Ctrl+C to cancel\"), 1, 0));\n\t\tthis.buildBaseLayout(panel, { showHeader: false });\n\t\tthis.requestRender();\n\t}\n\tprivate exitRenameMode(): void {\n\t\tthis.mode = \"list\";\n\t\tthis.renameTargetPath = null;\n\t\tthis.buildBaseLayout(this.sessionList);\n\t\tthis.requestRender();\n\t}\n\tprivate async confirmRename(value: string): Promise<void> {\n\t\tconst next = value.trim();\n\t\tif (!next) return;\n\t\tconst target = this.renameTargetPath;\n\t\tif (!target) {\n\t\t\tthis.exitRenameMode();\n\t\t\treturn;\n\t\t}\n\t\tconst renameSession = this.renameSession;\n\t\tif (!renameSession) {\n\t\t\tthis.exitRenameMode();\n\t\t\treturn;\n\t\t}\n\t\ttry {\n\t\t\tawait renameSession(target, next);\n\t\t\tawait this.refreshSessionsAfterMutation();\n\t\t} finally {\n\t\t\tthis.exitRenameMode();\n\t\t}\n\t}\n\tprivate async loadScope(scope: SessionScope, reason: \"initial\" | \"refresh\" | \"toggle\"): Promise<void> {\n\t\tconst showCwd = scope === \"all\";\n\t\tif (scope === \"current\") {\n\t\t\tthis.currentLoading = true;\n\t\t} else {\n\t\t\tthis.allLoading = true;\n\t\t}\n\t\tconst seq = scope === \"all\" ? ++this.allLoadSeq : undefined;\n\t\tthis.header.setScope(scope);\n\t\tthis.header.setLoading(true);\n\t\tthis.requestRender();\n\t\tconst onProgress = (loaded: number, total: number) => {\n\t\t\tif (scope !== this.scope) return;\n\t\t\tif (seq !== undefined && seq !== this.allLoadSeq) return;\n\t\t\tthis.header.setProgress(loaded, total);\n\t\t\tthis.requestRender();\n\t\t};\n\t\ttry {\n\t\t\tconst sessions = await (scope === \"current\"\n\t\t\t\t? this.currentSessionsLoader(onProgress)\n\t\t\t\t: this.allSessionsLoader(onProgress));\n\t\t\tif (scope === \"current\") {\n\t\t\t\tthis.currentSessions = sessions;\n\t\t\t\tthis.currentLoading = false;\n\t\t\t} else {\n\t\t\t\tthis.allSessions = sessions;\n\t\t\t\tthis.allLoading = false;\n\t\t\t}\n\t\t\tif (scope !== this.scope) return;\n\t\t\tif (seq !== undefined && seq !== this.allLoadSeq) return;\n\t\t\tthis.header.setLoading(false);\n\t\t\tthis.sessionList.setSessions(sessions, showCwd);\n\t\t\tthis.requestRender();\n\t\t\tif (scope === \"all\" && sessions.length === 0 && (this.currentSessions?.length ?? 0) === 0) {\n\t\t\t\tthis.onCancel();\n\t\t\t}\n\t\t} catch (err) {\n\t\t\tif (scope === \"current\") {\n\t\t\t\tthis.currentLoading = false;\n\t\t\t} else {\n\t\t\t\tthis.allLoading = false;\n\t\t\t}\n\t\t\tif (scope !== this.scope) return;\n\t\t\tif (seq !== undefined && seq !== this.allLoadSeq) return;\n\t\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\t\tthis.header.setLoading(false);\n\t\t\tthis.header.setStatusMessage({ type: \"error\", message: `Failed to load sessions: ${message}` }, 4000);\n\t\t\tif (reason === \"initial\") {\n\t\t\t\tthis.sessionList.setSessions([], showCwd);\n\t\t\t}\n\t\t\tthis.requestRender();\n\t\t}\n\t}\n\tprivate toggleSortMode(): void {\n\t\tthis.sortMode = this.sortMode === \"threaded\" ? \"recent\" : this.sortMode === \"recent\" ? \"relevance\" : \"threaded\";\n\t\tthis.header.setSortMode(this.sortMode);\n\t\tthis.sessionList.setSortMode(this.sortMode);\n\t\tthis.requestRender();\n\t}\n\tprivate toggleNameFilter(): void {\n\t\tthis.nameFilter = this.nameFilter === \"all\" ? \"named\" : \"all\";\n\t\tthis.header.setNameFilter(this.nameFilter);\n\t\tthis.sessionList.setNameFilter(this.nameFilter);\n\t\tthis.requestRender();\n\t}\n\tprivate async refreshSessionsAfterMutation(): Promise<void> {\n\t\tawait this.loadScope(this.scope, \"refresh\");\n\t}\n\tprivate toggleScope(): void {\n\t\tif (this.scope === \"current\") {\n\t\t\tthis.scope = \"all\";\n\t\t\tthis.header.setScope(this.scope);\n\t\t\tif (this.allSessions !== null) {\n\t\t\t\tthis.header.setLoading(false);\n\t\t\t\tthis.sessionList.setSessions(this.allSessions, true);\n\t\t\t\tthis.requestRender();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (!this.allLoading) {\n\t\t\t\tvoid this.loadScope(\"all\", \"toggle\");\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\tthis.scope = \"current\";\n\t\tthis.header.setScope(this.scope);\n\t\tthis.header.setLoading(this.currentLoading);\n\t\tthis.sessionList.setSessions(this.currentSessions ?? [], false);\n\t\tthis.requestRender();\n\t}\n\tgetSessionList(): SessionList {\n\t\treturn this.sessionList;\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"session-selector.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/session-selector.ts"],"names":[],"mappings":"AAIA,OAAO,EACN,KAAK,SAAS,EACd,SAAS,EACT,KAAK,SAAS,EAQd,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAAE,kBAAkB,EAAE,MAAM,8BAA8B,CAAC;AAClE,OAAO,KAAK,EAAE,WAAW,EAAE,mBAAmB,EAAE,MAAM,kCAAkC,CAAC;AAIzF,OAAO,EAAyC,KAAK,UAAU,EAAE,KAAK,QAAQ,EAAE,MAAM,8BAA8B,CAAC;AA+LrH,cAAM,WAAY,YAAW,SAAS,EAAE,SAAS;IACzC,sBAAsB,IAAI,MAAM,GAAG,SAAS,CAGlD;IACD,OAAO,CAAC,WAAW,CAAqB;IACxC,OAAO,CAAC,gBAAgB,CAAyB;IACjD,OAAO,CAAC,aAAa,CAAa;IAClC,OAAO,CAAC,WAAW,CAAQ;IAC3B,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,QAAQ,CAAwB;IACxC,OAAO,CAAC,UAAU,CAAqB;IACvC,OAAO,CAAC,WAAW,CAAqB;IACxC,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,oBAAoB,CAAuB;IACnD,OAAO,CAAC,sBAAsB,CAAC,CAAS;IACjC,QAAQ,CAAC,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,IAAI,CAAC;IACzC,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;IACtB,MAAM,EAAE,MAAM,IAAI,CAAY;IAC9B,aAAa,CAAC,EAAE,MAAM,IAAI,CAAC;IAC3B,YAAY,CAAC,EAAE,MAAM,IAAI,CAAC;IAC1B,kBAAkB,CAAC,EAAE,MAAM,IAAI,CAAC;IAChC,YAAY,CAAC,EAAE,CAAC,QAAQ,EAAE,OAAO,KAAK,IAAI,CAAC;IAC3C,0BAA0B,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAC;IAC3D,eAAe,CAAC,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACzD,eAAe,CAAC,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,IAAI,CAAC;IAChD,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IAC3C,OAAO,CAAC,UAAU,CAAc;IAChC,OAAO,CAAC,QAAQ,CAAS;IACzB,IAAI,OAAO,IAAI,OAAO,CAErB;IACD,IAAI,OAAO,CAAC,KAAK,EAAE,OAAO,EAGzB;IACD,YACC,QAAQ,EAAE,WAAW,EAAE,EACvB,OAAO,EAAE,OAAO,EAChB,QAAQ,EAAE,QAAQ,EAClB,UAAU,EAAE,UAAU,EACtB,WAAW,EAAE,kBAAkB,EAC/B,sBAAsB,CAAC,EAAE,MAAM,EAmB/B;IACD,WAAW,CAAC,QAAQ,EAAE,QAAQ,GAAG,IAAI,CAGpC;IACD,aAAa,CAAC,UAAU,EAAE,UAAU,GAAG,IAAI,CAG1C;IACD,WAAW,CAAC,QAAQ,EAAE,WAAW,EAAE,EAAE,OAAO,EAAE,OAAO,GAAG,IAAI,CAI3D;IACD,OAAO,CAAC,cAAc;IAkBtB,OAAO,CAAC,uBAAuB;IAI/B,OAAO,CAAC,yCAAyC;IASjD,UAAU,IAAI,IAAI,CAAG;IACrB,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CA8E9B;IACD,OAAO,CAAC,eAAe;IAQvB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CA2EjC;CACD;AACD,KAAK,cAAc,GAAG,CAAC,UAAU,CAAC,EAAE,mBAAmB,KAAK,OAAO,CAAC,WAAW,EAAE,CAAC,CAAC;AA+BnF,qBAAa,wBAAyB,SAAQ,SAAU,YAAW,SAAS;IAC3E,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAW9B;IACD,OAAO,CAAC,SAAS,CAAQ;IACzB,OAAO,CAAC,WAAW,CAAc;IACjC,OAAO,CAAC,MAAM,CAAwB;IACtC,OAAO,CAAC,WAAW,CAAqB;IACxC,OAAO,CAAC,KAAK,CAA2B;IACxC,OAAO,CAAC,QAAQ,CAAwB;IACxC,OAAO,CAAC,UAAU,CAAqB;IACvC,OAAO,CAAC,eAAe,CAA8B;IACrD,OAAO,CAAC,WAAW,CAA8B;IACjD,OAAO,CAAC,qBAAqB,CAAiB;IAC9C,OAAO,CAAC,iBAAiB,CAAiB;IAC1C,OAAO,CAAC,QAAQ,CAAa;IAC7B,OAAO,CAAC,aAAa,CAAa;IAClC,OAAO,CAAC,aAAa,CAAC,CAA0E;IAChG,OAAO,CAAC,cAAc,CAAS;IAC/B,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,UAAU,CAAK;IACvB,OAAO,CAAC,IAAI,CAA6B;IACzC,OAAO,CAAC,WAAW,CAAe;IAClC,OAAO,CAAC,gBAAgB,CAAuB;IAC/C,OAAO,CAAC,QAAQ,CAAS;IACzB,IAAI,OAAO,IAAI,OAAO,CAErB;IACD,IAAI,OAAO,CAAC,KAAK,EAAE,OAAO,EAOzB;IACD,OAAO,CAAC,eAAe;IAavB,YACC,qBAAqB,EAAE,cAAc,EACrC,iBAAiB,EAAE,cAAc,EACjC,QAAQ,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,IAAI,EACvC,QAAQ,EAAE,MAAM,IAAI,EACpB,MAAM,EAAE,MAAM,IAAI,EAClB,aAAa,EAAE,MAAM,IAAI,EACzB,OAAO,CAAC,EAAE;QACT,aAAa,CAAC,EAAE,CAAC,WAAW,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,SAAS,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;QACxF,cAAc,CAAC,EAAE,OAAO,CAAC;QACzB,WAAW,CAAC,EAAE,kBAAkB,CAAC;KACjC,EACD,sBAAsB,CAAC,EAAE,MAAM,EAyF/B;IACD,OAAO,CAAC,mBAAmB;IAG3B,OAAO,CAAC,eAAe;IAcvB,OAAO,CAAC,cAAc;YAMR,aAAa;YAoBb,SAAS;IAqDvB,OAAO,CAAC,cAAc;IAMtB,OAAO,CAAC,gBAAgB;YAMV,4BAA4B;IAG1C,OAAO,CAAC,WAAW;IAqBnB,cAAc,IAAI,WAAW,CAE5B;CACD","sourcesContent":["import { spawnSync } from \"node:child_process\";\nimport { existsSync } from \"node:fs\";\nimport { unlink } from \"node:fs/promises\";\nimport * as os from \"node:os\";\nimport {\n\ttype Component,\n\tContainer,\n\ttype Focusable,\n\tgetEditorKeybindings,\n\tInput,\n\tmatchesKey,\n\tSpacer,\n\tText,\n\ttruncateToWidth,\n\tvisibleWidth,\n} from \"@boxiaolanya2008/pi-tui\";\nimport { KeybindingsManager } from \"../../../core/keybindings.js\";\nimport type { SessionInfo, SessionListProgress } from \"../../../core/session-manager.js\";\nimport { theme } from \"../theme/theme.js\";\nimport { DynamicBorder } from \"./dynamic-border.js\";\nimport { appKey, appKeyHint, keyHint } from \"./keybinding-hints.js\";\nimport { filterAndSortSessions, hasSessionName, type NameFilter, type SortMode } from \"./session-selector-search.js\";\n\ntype SessionScope = \"current\" | \"all\";\nfunction shortenPath(path: string): string {\n\tconst home = os.homedir();\n\tif (!path) return path;\n\tif (path.startsWith(home)) {\n\t\treturn `~${path.slice(home.length)}`;\n\t}\n\treturn path;\n}\nfunction formatSessionDate(date: Date): string {\n\tconst now = new Date();\n\tconst diffMs = now.getTime() - date.getTime();\n\tconst diffMins = Math.floor(diffMs / 60000);\n\tconst diffHours = Math.floor(diffMs / 3600000);\n\tconst diffDays = Math.floor(diffMs / 86400000);\n\tif (diffMins < 1) return \"now\";\n\tif (diffMins < 60) return `${diffMins}m`;\n\tif (diffHours < 24) return `${diffHours}h`;\n\tif (diffDays < 7) return `${diffDays}d`;\n\tif (diffDays < 30) return `${Math.floor(diffDays / 7)}w`;\n\tif (diffDays < 365) return `${Math.floor(diffDays / 30)}mo`;\n\treturn `${Math.floor(diffDays / 365)}y`;\n}\nclass SessionSelectorHeader implements Component {\n\tprivate scope: SessionScope;\n\tprivate sortMode: SortMode;\n\tprivate nameFilter: NameFilter;\n\tprivate keybindings: KeybindingsManager;\n\tprivate requestRender: () => void;\n\tprivate loading = false;\n\tprivate loadProgress: { loaded: number; total: number } | null = null;\n\tprivate showPath = false;\n\tprivate confirmingDeletePath: string | null = null;\n\tprivate statusMessage: { type: \"info\" | \"error\"; message: string } | null = null;\n\tprivate statusTimeout: ReturnType<typeof setTimeout> | null = null;\n\tprivate showRenameHint = false;\n\tconstructor(\n\t\tscope: SessionScope,\n\t\tsortMode: SortMode,\n\t\tnameFilter: NameFilter,\n\t\tkeybindings: KeybindingsManager,\n\t\trequestRender: () => void,\n\t) {\n\t\tthis.scope = scope;\n\t\tthis.sortMode = sortMode;\n\t\tthis.nameFilter = nameFilter;\n\t\tthis.keybindings = keybindings;\n\t\tthis.requestRender = requestRender;\n\t}\n\tsetScope(scope: SessionScope): void {\n\t\tthis.scope = scope;\n\t}\n\tsetSortMode(sortMode: SortMode): void {\n\t\tthis.sortMode = sortMode;\n\t}\n\tsetNameFilter(nameFilter: NameFilter): void {\n\t\tthis.nameFilter = nameFilter;\n\t}\n\tsetLoading(loading: boolean): void {\n\t\tthis.loading = loading;\n\t\tthis.loadProgress = null;\n\t}\n\tsetProgress(loaded: number, total: number): void {\n\t\tthis.loadProgress = { loaded, total };\n\t}\n\tsetShowPath(showPath: boolean): void {\n\t\tthis.showPath = showPath;\n\t}\n\tsetShowRenameHint(show: boolean): void {\n\t\tthis.showRenameHint = show;\n\t}\n\tsetConfirmingDeletePath(path: string | null): void {\n\t\tthis.confirmingDeletePath = path;\n\t}\n\tprivate clearStatusTimeout(): void {\n\t\tif (!this.statusTimeout) return;\n\t\tclearTimeout(this.statusTimeout);\n\t\tthis.statusTimeout = null;\n\t}\n\tsetStatusMessage(msg: { type: \"info\" | \"error\"; message: string } | null, autoHideMs?: number): void {\n\t\tthis.clearStatusTimeout();\n\t\tthis.statusMessage = msg;\n\t\tif (!msg || !autoHideMs) return;\n\t\tthis.statusTimeout = setTimeout(() => {\n\t\t\tthis.statusMessage = null;\n\t\t\tthis.statusTimeout = null;\n\t\t\tthis.requestRender();\n\t\t}, autoHideMs);\n\t}\n\tinvalidate(): void {}\n\trender(width: number): string[] {\n\t\tconst title = this.scope === \"current\" ? \"Resume Session (Current Folder)\" : \"Resume Session (All)\";\n\t\tconst leftText = theme.bold(title);\n\t\tconst sortLabel = this.sortMode === \"threaded\" ? \"Threaded\" : this.sortMode === \"recent\" ? \"Recent\" : \"Fuzzy\";\n\t\tconst sortText = theme.fg(\"muted\", \"Sort: \") + theme.fg(\"accent\", sortLabel);\n\t\tconst nameLabel = this.nameFilter === \"all\" ? \"All\" : \"Named\";\n\t\tconst nameText = theme.fg(\"muted\", \"Name: \") + theme.fg(\"accent\", nameLabel);\n\t\tlet scopeText: string;\n\t\tif (this.loading) {\n\t\t\tconst progressText = this.loadProgress ? `${this.loadProgress.loaded}/${this.loadProgress.total}` : \"...\";\n\t\t\tscopeText = `${theme.fg(\"muted\", \"○ Current Folder | \")}${theme.fg(\"accent\", `Loading ${progressText}`)}`;\n\t\t} else if (this.scope === \"current\") {\n\t\t\tscopeText = `${theme.fg(\"accent\", \"◉ Current Folder\")}${theme.fg(\"muted\", \" | ○ All\")}`;\n\t\t} else {\n\t\t\tscopeText = `${theme.fg(\"muted\", \"○ Current Folder | \")}${theme.fg(\"accent\", \"◉ All\")}`;\n\t\t}\n\t\tconst rightText = truncateToWidth(`${scopeText} ${nameText} ${sortText}`, width, \"\");\n\t\tconst availableLeft = Math.max(0, width - visibleWidth(rightText) - 1);\n\t\tconst left = truncateToWidth(leftText, availableLeft, \"\");\n\t\tconst spacing = Math.max(0, width - visibleWidth(left) - visibleWidth(rightText));\n\t\tlet hintLine1: string;\n\t\tlet hintLine2: string;\n\t\tif (this.confirmingDeletePath !== null) {\n\t\t\tconst confirmHint = \"Delete session? [Enter] confirm · [Esc/Ctrl+C] cancel\";\n\t\t\thintLine1 = theme.fg(\"error\", truncateToWidth(confirmHint, width, \"…\"));\n\t\t\thintLine2 = \"\";\n\t\t} else if (this.statusMessage) {\n\t\t\tconst color = this.statusMessage.type === \"error\" ? \"error\" : \"accent\";\n\t\t\thintLine1 = theme.fg(color, truncateToWidth(this.statusMessage.message, width, \"…\"));\n\t\t\thintLine2 = \"\";\n\t\t} else {\n\t\t\tconst pathState = this.showPath ? \"(on)\" : \"(off)\";\n\t\t\tconst sep = theme.fg(\"muted\", \" · \");\n\t\t\tconst hint1 = keyHint(\"tab\", \"scope\") + sep + theme.fg(\"muted\", 're:<pattern> regex · \"phrase\" exact');\n\t\t\tconst hint2Parts = [\n\t\t\t\tkeyHint(\"toggleSessionSort\", \"sort\"),\n\t\t\t\tappKeyHint(this.keybindings, \"toggleSessionNamedFilter\", \"named\"),\n\t\t\t\tkeyHint(\"deleteSession\", \"delete\"),\n\t\t\t\tkeyHint(\"toggleSessionPath\", `path ${pathState}`),\n\t\t\t];\n\t\t\tif (this.showRenameHint) {\n\t\t\t\thint2Parts.push(keyHint(\"renameSession\", \"rename\"));\n\t\t\t}\n\t\t\tconst hint2 = hint2Parts.join(sep);\n\t\t\thintLine1 = truncateToWidth(hint1, width, \"…\");\n\t\t\thintLine2 = truncateToWidth(hint2, width, \"…\");\n\t\t}\n\t\treturn [`${left}${\" \".repeat(spacing)}${rightText}`, hintLine1, hintLine2];\n\t}\n}\ninterface SessionTreeNode {\n\tsession: SessionInfo;\n\tchildren: SessionTreeNode[];\n}\ninterface FlatSessionNode {\n\tsession: SessionInfo;\n\tdepth: number;\n\tisLast: boolean;\n\tancestorContinues: boolean[];\n}\nfunction buildSessionTree(sessions: SessionInfo[]): SessionTreeNode[] {\n\tconst byPath = new Map<string, SessionTreeNode>();\n\tfor (const session of sessions) {\n\t\tbyPath.set(session.path, { session, children: [] });\n\t}\n\tconst roots: SessionTreeNode[] = [];\n\tfor (const session of sessions) {\n\t\tconst node = byPath.get(session.path)!;\n\t\tconst parentPath = session.parentSessionPath;\n\t\tif (parentPath && byPath.has(parentPath)) {\n\t\t\tbyPath.get(parentPath)!.children.push(node);\n\t\t} else {\n\t\t\troots.push(node);\n\t\t}\n\t}\n\tconst sortNodes = (nodes: SessionTreeNode[]): void => {\n\t\tnodes.sort((a, b) => b.session.modified.getTime() - a.session.modified.getTime());\n\t\tfor (const node of nodes) {\n\t\t\tsortNodes(node.children);\n\t\t}\n\t};\n\tsortNodes(roots);\n\treturn roots;\n}\nfunction flattenSessionTree(roots: SessionTreeNode[]): FlatSessionNode[] {\n\tconst result: FlatSessionNode[] = [];\n\tconst walk = (node: SessionTreeNode, depth: number, ancestorContinues: boolean[], isLast: boolean): void => {\n\t\tresult.push({ session: node.session, depth, isLast, ancestorContinues });\n\t\tfor (let i = 0; i < node.children.length; i++) {\n\t\t\tconst childIsLast = i === node.children.length - 1;\n\t\t\tconst continues = depth > 0 ? !isLast : false;\n\t\t\twalk(node.children[i]!, depth + 1, [...ancestorContinues, continues], childIsLast);\n\t\t}\n\t};\n\tfor (let i = 0; i < roots.length; i++) {\n\t\twalk(roots[i]!, 0, [], i === roots.length - 1);\n\t}\n\treturn result;\n}\nclass SessionList implements Component, Focusable {\n\tpublic getSelectedSessionPath(): string | undefined {\n\t\tconst selected = this.filteredSessions[this.selectedIndex];\n\t\treturn selected?.session.path;\n\t}\n\tprivate allSessions: SessionInfo[] = [];\n\tprivate filteredSessions: FlatSessionNode[] = [];\n\tprivate selectedIndex: number = 0;\n\tprivate searchInput: Input;\n\tprivate showCwd = false;\n\tprivate sortMode: SortMode = \"threaded\";\n\tprivate nameFilter: NameFilter = \"all\";\n\tprivate keybindings: KeybindingsManager;\n\tprivate showPath = false;\n\tprivate confirmingDeletePath: string | null = null;\n\tprivate currentSessionFilePath?: string;\n\tpublic onSelect?: (sessionPath: string) => void;\n\tpublic onCancel?: () => void;\n\tpublic onExit: () => void = () => {};\n\tpublic onToggleScope?: () => void;\n\tpublic onToggleSort?: () => void;\n\tpublic onToggleNameFilter?: () => void;\n\tpublic onTogglePath?: (showPath: boolean) => void;\n\tpublic onDeleteConfirmationChange?: (path: string | null) => void;\n\tpublic onDeleteSession?: (sessionPath: string) => Promise<void>;\n\tpublic onRenameSession?: (sessionPath: string) => void;\n\tpublic onError?: (message: string) => void;\n\tprivate maxVisible: number = 10;\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\tconstructor(\n\t\tsessions: SessionInfo[],\n\t\tshowCwd: boolean,\n\t\tsortMode: SortMode,\n\t\tnameFilter: NameFilter,\n\t\tkeybindings: KeybindingsManager,\n\t\tcurrentSessionFilePath?: string,\n\t) {\n\t\tthis.allSessions = sessions;\n\t\tthis.filteredSessions = [];\n\t\tthis.searchInput = new Input();\n\t\tthis.showCwd = showCwd;\n\t\tthis.sortMode = sortMode;\n\t\tthis.nameFilter = nameFilter;\n\t\tthis.keybindings = keybindings;\n\t\tthis.currentSessionFilePath = currentSessionFilePath;\n\t\tthis.filterSessions(\"\");\n\t\tthis.searchInput.onSubmit = () => {\n\t\t\tif (this.filteredSessions[this.selectedIndex]) {\n\t\t\t\tconst selected = this.filteredSessions[this.selectedIndex];\n\t\t\t\tif (this.onSelect) {\n\t\t\t\t\tthis.onSelect(selected.session.path);\n\t\t\t\t}\n\t\t\t}\n\t\t};\n\t}\n\tsetSortMode(sortMode: SortMode): void {\n\t\tthis.sortMode = sortMode;\n\t\tthis.filterSessions(this.searchInput.getValue());\n\t}\n\tsetNameFilter(nameFilter: NameFilter): void {\n\t\tthis.nameFilter = nameFilter;\n\t\tthis.filterSessions(this.searchInput.getValue());\n\t}\n\tsetSessions(sessions: SessionInfo[], showCwd: boolean): void {\n\t\tthis.allSessions = sessions;\n\t\tthis.showCwd = showCwd;\n\t\tthis.filterSessions(this.searchInput.getValue());\n\t}\n\tprivate filterSessions(query: string): void {\n\t\tconst trimmed = query.trim();\n\t\tconst nameFiltered =\n\t\t\tthis.nameFilter === \"all\" ? this.allSessions : this.allSessions.filter((session) => hasSessionName(session));\n\t\tif (this.sortMode === \"threaded\" && !trimmed) {\n\t\t\tconst roots = buildSessionTree(nameFiltered);\n\t\t\tthis.filteredSessions = flattenSessionTree(roots);\n\t\t} else {\n\t\t\tconst filtered = filterAndSortSessions(nameFiltered, query, this.sortMode, \"all\");\n\t\t\tthis.filteredSessions = filtered.map((session) => ({\n\t\t\t\tsession,\n\t\t\t\tdepth: 0,\n\t\t\t\tisLast: true,\n\t\t\t\tancestorContinues: [],\n\t\t\t}));\n\t\t}\n\t\tthis.selectedIndex = Math.min(this.selectedIndex, Math.max(0, this.filteredSessions.length - 1));\n\t}\n\tprivate setConfirmingDeletePath(path: string | null): void {\n\t\tthis.confirmingDeletePath = path;\n\t\tthis.onDeleteConfirmationChange?.(path);\n\t}\n\tprivate startDeleteConfirmationForSelectedSession(): void {\n\t\tconst selected = this.filteredSessions[this.selectedIndex];\n\t\tif (!selected) return;\n\t\tif (this.currentSessionFilePath && selected.session.path === this.currentSessionFilePath) {\n\t\t\tthis.onError?.(\"Cannot delete the currently active session\");\n\t\t\treturn;\n\t\t}\n\t\tthis.setConfirmingDeletePath(selected.session.path);\n\t}\n\tinvalidate(): void {}\n\trender(width: number): string[] {\n\t\tconst lines: string[] = [];\n\t\tlines.push(...this.searchInput.render(width));\n\t\tlines.push(\"\");\n\t\tif (this.filteredSessions.length === 0) {\n\t\t\tlet emptyMessage: string;\n\t\t\tif (this.nameFilter === \"named\") {\n\t\t\t\tconst toggleKey = appKey(this.keybindings, \"toggleSessionNamedFilter\");\n\t\t\t\tif (this.showCwd) {\n\t\t\t\t\temptyMessage = ` No named sessions found. Press ${toggleKey} to show all.`;\n\t\t\t\t} else {\n\t\t\t\t\temptyMessage = ` No named sessions in current folder. Press ${toggleKey} to show all, or Tab to view all.`;\n\t\t\t\t}\n\t\t\t} else if (this.showCwd) {\n\t\t\t\temptyMessage = \" No sessions found\";\n\t\t\t} else {\n\t\t\t\temptyMessage = \" No sessions in current folder. Press Tab to view all.\";\n\t\t\t}\n\t\t\tlines.push(theme.fg(\"muted\", truncateToWidth(emptyMessage, width, \"…\")));\n\t\t\treturn lines;\n\t\t}\n\t\tconst startIndex = Math.max(\n\t\t\t0,\n\t\t\tMath.min(this.selectedIndex - Math.floor(this.maxVisible / 2), this.filteredSessions.length - this.maxVisible),\n\t\t);\n\t\tconst endIndex = Math.min(startIndex + this.maxVisible, this.filteredSessions.length);\n\t\tfor (let i = startIndex; i < endIndex; i++) {\n\t\t\tconst node = this.filteredSessions[i]!;\n\t\t\tconst session = node.session;\n\t\t\tconst isSelected = i === this.selectedIndex;\n\t\t\tconst isConfirmingDelete = session.path === this.confirmingDeletePath;\n\t\t\tconst isCurrent = this.currentSessionFilePath === session.path;\n\t\t\tconst prefix = this.buildTreePrefix(node);\n\t\t\tconst hasName = !!session.name;\n\t\t\tconst displayText = session.name ?? session.firstMessage;\n\t\t\tconst normalizedMessage = displayText.replace(/[\\x00-\\x1f\\x7f]/g, \" \").trim();\n\t\t\tconst age = formatSessionDate(session.modified);\n\t\t\tconst msgCount = String(session.messageCount);\n\t\t\tlet rightPart = `${msgCount} ${age}`;\n\t\t\tif (this.showCwd && session.cwd) {\n\t\t\t\trightPart = `${shortenPath(session.cwd)} ${rightPart}`;\n\t\t\t}\n\t\t\tif (this.showPath) {\n\t\t\t\trightPart = `${shortenPath(session.path)} ${rightPart}`;\n\t\t\t}\n\t\t\tconst cursor = isSelected ? theme.fg(\"accent\", \"› \") : \" \";\n\t\t\tconst prefixWidth = visibleWidth(prefix);\n\t\t\tconst rightWidth = visibleWidth(rightPart) + 2;\n\t\t\tconst availableForMsg = width - 2 - prefixWidth - rightWidth;\n\t\t\tconst truncatedMsg = truncateToWidth(normalizedMessage, Math.max(10, availableForMsg), \"…\");\n\t\t\tlet messageColor: \"error\" | \"warning\" | \"accent\" | null = null;\n\t\t\tif (isConfirmingDelete) {\n\t\t\t\tmessageColor = \"error\";\n\t\t\t} else if (isCurrent) {\n\t\t\t\tmessageColor = \"accent\";\n\t\t\t} else if (hasName) {\n\t\t\t\tmessageColor = \"warning\";\n\t\t\t}\n\t\t\tlet styledMsg = messageColor ? theme.fg(messageColor, truncatedMsg) : truncatedMsg;\n\t\t\tif (isSelected) {\n\t\t\t\tstyledMsg = theme.bold(styledMsg);\n\t\t\t}\n\t\t\tconst leftPart = cursor + theme.fg(\"dim\", prefix) + styledMsg;\n\t\t\tconst leftWidth = visibleWidth(leftPart);\n\t\t\tconst spacing = Math.max(1, width - leftWidth - visibleWidth(rightPart));\n\t\t\tconst styledRight = theme.fg(isConfirmingDelete ? \"error\" : \"dim\", rightPart);\n\t\t\tlet line = leftPart + \" \".repeat(spacing) + styledRight;\n\t\t\tif (isSelected) {\n\t\t\t\tline = theme.bg(\"selectedBg\", line);\n\t\t\t}\n\t\t\tlines.push(truncateToWidth(line, width));\n\t\t}\n\t\tif (startIndex > 0 || endIndex < this.filteredSessions.length) {\n\t\t\tconst scrollText = ` (${this.selectedIndex + 1}/${this.filteredSessions.length})`;\n\t\t\tconst scrollInfo = theme.fg(\"muted\", truncateToWidth(scrollText, width, \"\"));\n\t\t\tlines.push(scrollInfo);\n\t\t}\n\t\treturn lines;\n\t}\n\tprivate buildTreePrefix(node: FlatSessionNode): string {\n\t\tif (node.depth === 0) {\n\t\t\treturn \"\";\n\t\t}\n\t\tconst parts = node.ancestorContinues.map((continues) => (continues ? \"│ \" : \" \"));\n\t\tconst branch = node.isLast ? \"└─ \" : \"├─ \";\n\t\treturn parts.join(\"\") + branch;\n\t}\n\thandleInput(keyData: string): void {\n\t\tconst kb = getEditorKeybindings();\n\t\tif (this.confirmingDeletePath !== null) {\n\t\t\tif (kb.matches(keyData, \"selectConfirm\")) {\n\t\t\t\tconst pathToDelete = this.confirmingDeletePath;\n\t\t\t\tthis.setConfirmingDeletePath(null);\n\t\t\t\tvoid this.onDeleteSession?.(pathToDelete);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (kb.matches(keyData, \"selectCancel\") || matchesKey(keyData, \"ctrl+c\")) {\n\t\t\t\tthis.setConfirmingDeletePath(null);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\tif (kb.matches(keyData, \"tab\")) {\n\t\t\tif (this.onToggleScope) {\n\t\t\t\tthis.onToggleScope();\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\tif (kb.matches(keyData, \"toggleSessionSort\")) {\n\t\t\tthis.onToggleSort?.();\n\t\t\treturn;\n\t\t}\n\t\tif (this.keybindings.matches(keyData, \"toggleSessionNamedFilter\")) {\n\t\t\tthis.onToggleNameFilter?.();\n\t\t\treturn;\n\t\t}\n\t\tif (kb.matches(keyData, \"toggleSessionPath\")) {\n\t\t\tthis.showPath = !this.showPath;\n\t\t\tthis.onTogglePath?.(this.showPath);\n\t\t\treturn;\n\t\t}\n\t\tif (kb.matches(keyData, \"deleteSession\")) {\n\t\t\tthis.startDeleteConfirmationForSelectedSession();\n\t\t\treturn;\n\t\t}\n\t\tif (matchesKey(keyData, \"ctrl+r\")) {\n\t\t\tconst selected = this.filteredSessions[this.selectedIndex];\n\t\t\tif (selected) {\n\t\t\t\tthis.onRenameSession?.(selected.session.path);\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\tif (kb.matches(keyData, \"deleteSessionNoninvasive\")) {\n\t\t\tif (this.searchInput.getValue().length > 0) {\n\t\t\t\tthis.searchInput.handleInput(keyData);\n\t\t\t\tthis.filterSessions(this.searchInput.getValue());\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tthis.startDeleteConfirmationForSelectedSession();\n\t\t\treturn;\n\t\t}\n\t\tif (kb.matches(keyData, \"selectUp\")) {\n\t\t\tthis.selectedIndex = Math.max(0, this.selectedIndex - 1);\n\t\t} else if (kb.matches(keyData, \"selectDown\")) {\n\t\t\tthis.selectedIndex = Math.min(this.filteredSessions.length - 1, this.selectedIndex + 1);\n\t\t} else if (kb.matches(keyData, \"selectPageUp\")) {\n\t\t\tthis.selectedIndex = Math.max(0, this.selectedIndex - this.maxVisible);\n\t\t} else if (kb.matches(keyData, \"selectPageDown\")) {\n\t\t\tthis.selectedIndex = Math.min(this.filteredSessions.length - 1, this.selectedIndex + this.maxVisible);\n\t\t} else if (kb.matches(keyData, \"selectConfirm\")) {\n\t\t\tconst selected = this.filteredSessions[this.selectedIndex];\n\t\t\tif (selected && this.onSelect) {\n\t\t\t\tthis.onSelect(selected.session.path);\n\t\t\t}\n\t\t} else if (kb.matches(keyData, \"selectCancel\")) {\n\t\t\tif (this.onCancel) {\n\t\t\t\tthis.onCancel();\n\t\t\t}\n\t\t} else {\n\t\t\tthis.searchInput.handleInput(keyData);\n\t\t\tthis.filterSessions(this.searchInput.getValue());\n\t\t}\n\t}\n}\ntype SessionsLoader = (onProgress?: SessionListProgress) => Promise<SessionInfo[]>;\nasync function deleteSessionFile(\n\tsessionPath: string,\n): Promise<{ ok: boolean; method: \"trash\" | \"unlink\"; error?: string }> {\n\tconst trashArgs = sessionPath.startsWith(\"-\") ? [\"--\", sessionPath] : [sessionPath];\n\tconst trashResult = spawnSync(\"trash\", trashArgs, { encoding: \"utf-8\" });\n\tconst getTrashErrorHint = (): string | null => {\n\t\tconst parts: string[] = [];\n\t\tif (trashResult.error) {\n\t\t\tparts.push(trashResult.error.message);\n\t\t}\n\t\tconst stderr = trashResult.stderr?.trim();\n\t\tif (stderr) {\n\t\t\tparts.push(stderr.split(\"\\n\")[0] ?? stderr);\n\t\t}\n\t\tif (parts.length === 0) return null;\n\t\treturn `trash: ${parts.join(\" · \").slice(0, 200)}`;\n\t};\n\tif (trashResult.status === 0 || !existsSync(sessionPath)) {\n\t\treturn { ok: true, method: \"trash\" };\n\t}\n\ttry {\n\t\tawait unlink(sessionPath);\n\t\treturn { ok: true, method: \"unlink\" };\n\t} catch (err) {\n\t\tconst unlinkError = err instanceof Error ? err.message : String(err);\n\t\tconst trashErrorHint = getTrashErrorHint();\n\t\tconst error = trashErrorHint ? `${unlinkError} (${trashErrorHint})` : unlinkError;\n\t\treturn { ok: false, method: \"unlink\", error };\n\t}\n}\nexport class SessionSelectorComponent extends Container implements Focusable {\n\thandleInput(data: string): void {\n\t\tif (this.mode === \"rename\") {\n\t\t\tconst kb = getEditorKeybindings();\n\t\t\tif (kb.matches(data, \"selectCancel\") || matchesKey(data, \"ctrl+c\")) {\n\t\t\t\tthis.exitRenameMode();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tthis.renameInput.handleInput(data);\n\t\t\treturn;\n\t\t}\n\t\tthis.sessionList.handleInput(data);\n\t}\n\tprivate canRename = true;\n\tprivate sessionList: SessionList;\n\tprivate header: SessionSelectorHeader;\n\tprivate keybindings: KeybindingsManager;\n\tprivate scope: SessionScope = \"current\";\n\tprivate sortMode: SortMode = \"threaded\";\n\tprivate nameFilter: NameFilter = \"all\";\n\tprivate currentSessions: SessionInfo[] | null = null;\n\tprivate allSessions: SessionInfo[] | null = null;\n\tprivate currentSessionsLoader: SessionsLoader;\n\tprivate allSessionsLoader: SessionsLoader;\n\tprivate onCancel: () => void;\n\tprivate requestRender: () => void;\n\tprivate renameSession?: (sessionPath: string, currentName: string | undefined) => Promise<void>;\n\tprivate currentLoading = false;\n\tprivate allLoading = false;\n\tprivate allLoadSeq = 0;\n\tprivate mode: \"list\" | \"rename\" = \"list\";\n\tprivate renameInput = new Input();\n\tprivate renameTargetPath: string | null = null;\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.sessionList.focused = value;\n\t\tthis.renameInput.focused = value;\n\t\tif (value && this.mode === \"rename\") {\n\t\t\tthis.renameInput.focused = true;\n\t\t}\n\t}\n\tprivate buildBaseLayout(content: Component, options?: { showHeader?: boolean }): void {\n\t\tthis.clear();\n\t\tthis.addChild(new Spacer(1));\n\t\tthis.addChild(new DynamicBorder((s) => theme.fg(\"accent\", s)));\n\t\tthis.addChild(new Spacer(1));\n\t\tif (options?.showHeader ?? true) {\n\t\t\tthis.addChild(this.header);\n\t\t\tthis.addChild(new Spacer(1));\n\t\t}\n\t\tthis.addChild(content);\n\t\tthis.addChild(new Spacer(1));\n\t\tthis.addChild(new DynamicBorder((s) => theme.fg(\"accent\", s)));\n\t}\n\tconstructor(\n\t\tcurrentSessionsLoader: SessionsLoader,\n\t\tallSessionsLoader: SessionsLoader,\n\t\tonSelect: (sessionPath: string) => void,\n\t\tonCancel: () => void,\n\t\tonExit: () => void,\n\t\trequestRender: () => void,\n\t\toptions?: {\n\t\t\trenameSession?: (sessionPath: string, currentName: string | undefined) => Promise<void>;\n\t\t\tshowRenameHint?: boolean;\n\t\t\tkeybindings?: KeybindingsManager;\n\t\t},\n\t\tcurrentSessionFilePath?: string,\n\t) {\n\t\tsuper();\n\t\tthis.keybindings = options?.keybindings ?? KeybindingsManager.create();\n\t\tthis.currentSessionsLoader = currentSessionsLoader;\n\t\tthis.allSessionsLoader = allSessionsLoader;\n\t\tthis.onCancel = onCancel;\n\t\tthis.requestRender = requestRender;\n\t\tthis.header = new SessionSelectorHeader(\n\t\t\tthis.scope,\n\t\t\tthis.sortMode,\n\t\t\tthis.nameFilter,\n\t\t\tthis.keybindings,\n\t\t\tthis.requestRender,\n\t\t);\n\t\tconst renameSession = options?.renameSession;\n\t\tthis.renameSession = renameSession;\n\t\tthis.canRename = !!renameSession;\n\t\tthis.header.setShowRenameHint(options?.showRenameHint ?? this.canRename);\n\t\tthis.sessionList = new SessionList(\n\t\t\t[],\n\t\t\tfalse,\n\t\t\tthis.sortMode,\n\t\t\tthis.nameFilter,\n\t\t\tthis.keybindings,\n\t\t\tcurrentSessionFilePath,\n\t\t);\n\t\tthis.buildBaseLayout(this.sessionList);\n\t\tthis.renameInput.onSubmit = (value) => {\n\t\t\tvoid this.confirmRename(value);\n\t\t};\n\t\tconst clearStatusMessage = () => this.header.setStatusMessage(null);\n\t\tthis.sessionList.onSelect = (sessionPath) => {\n\t\t\tclearStatusMessage();\n\t\t\tonSelect(sessionPath);\n\t\t};\n\t\tthis.sessionList.onCancel = () => {\n\t\t\tclearStatusMessage();\n\t\t\tonCancel();\n\t\t};\n\t\tthis.sessionList.onExit = () => {\n\t\t\tclearStatusMessage();\n\t\t\tonExit();\n\t\t};\n\t\tthis.sessionList.onToggleScope = () => this.toggleScope();\n\t\tthis.sessionList.onToggleSort = () => this.toggleSortMode();\n\t\tthis.sessionList.onToggleNameFilter = () => this.toggleNameFilter();\n\t\tthis.sessionList.onRenameSession = (sessionPath) => {\n\t\t\tif (!renameSession) return;\n\t\t\tif (this.scope === \"current\" && this.currentLoading) return;\n\t\t\tif (this.scope === \"all\" && this.allLoading) return;\n\t\t\tconst sessions = this.scope === \"all\" ? (this.allSessions ?? []) : (this.currentSessions ?? []);\n\t\t\tconst session = sessions.find((s) => s.path === sessionPath);\n\t\t\tthis.enterRenameMode(sessionPath, session?.name);\n\t\t};\n\t\tthis.sessionList.onTogglePath = (showPath) => {\n\t\t\tthis.header.setShowPath(showPath);\n\t\t\tthis.requestRender();\n\t\t};\n\t\tthis.sessionList.onDeleteConfirmationChange = (path) => {\n\t\t\tthis.header.setConfirmingDeletePath(path);\n\t\t\tthis.requestRender();\n\t\t};\n\t\tthis.sessionList.onError = (msg) => {\n\t\t\tthis.header.setStatusMessage({ type: \"error\", message: msg }, 3000);\n\t\t\tthis.requestRender();\n\t\t};\n\t\tthis.sessionList.onDeleteSession = async (sessionPath: string) => {\n\t\t\tconst result = await deleteSessionFile(sessionPath);\n\t\t\tif (result.ok) {\n\t\t\t\tif (this.currentSessions) {\n\t\t\t\t\tthis.currentSessions = this.currentSessions.filter((s) => s.path !== sessionPath);\n\t\t\t\t}\n\t\t\t\tif (this.allSessions) {\n\t\t\t\t\tthis.allSessions = this.allSessions.filter((s) => s.path !== sessionPath);\n\t\t\t\t}\n\t\t\t\tconst sessions = this.scope === \"all\" ? (this.allSessions ?? []) : (this.currentSessions ?? []);\n\t\t\t\tconst showCwd = this.scope === \"all\";\n\t\t\t\tthis.sessionList.setSessions(sessions, showCwd);\n\t\t\t\tconst msg = result.method === \"trash\" ? \"Session moved to trash\" : \"Session deleted\";\n\t\t\t\tthis.header.setStatusMessage({ type: \"info\", message: msg }, 2000);\n\t\t\t\tawait this.refreshSessionsAfterMutation();\n\t\t\t} else {\n\t\t\t\tconst errorMessage = result.error ?? \"Unknown error\";\n\t\t\t\tthis.header.setStatusMessage({ type: \"error\", message: `Failed to delete: ${errorMessage}` }, 3000);\n\t\t\t}\n\t\t\tthis.requestRender();\n\t\t};\n\t\tthis.loadCurrentSessions();\n\t}\n\tprivate loadCurrentSessions(): void {\n\t\tvoid this.loadScope(\"current\", \"initial\");\n\t}\n\tprivate enterRenameMode(sessionPath: string, currentName: string | undefined): void {\n\t\tthis.mode = \"rename\";\n\t\tthis.renameTargetPath = sessionPath;\n\t\tthis.renameInput.setValue(currentName ?? \"\");\n\t\tthis.renameInput.focused = true;\n\t\tconst panel = new Container();\n\t\tpanel.addChild(new Text(theme.bold(\"Rename Session\"), 1, 0));\n\t\tpanel.addChild(new Spacer(1));\n\t\tpanel.addChild(this.renameInput);\n\t\tpanel.addChild(new Spacer(1));\n\t\tpanel.addChild(new Text(theme.fg(\"muted\", \"Enter to save · Esc/Ctrl+C to cancel\"), 1, 0));\n\t\tthis.buildBaseLayout(panel, { showHeader: false });\n\t\tthis.requestRender();\n\t}\n\tprivate exitRenameMode(): void {\n\t\tthis.mode = \"list\";\n\t\tthis.renameTargetPath = null;\n\t\tthis.buildBaseLayout(this.sessionList);\n\t\tthis.requestRender();\n\t}\n\tprivate async confirmRename(value: string): Promise<void> {\n\t\tconst next = value.trim();\n\t\tif (!next) return;\n\t\tconst target = this.renameTargetPath;\n\t\tif (!target) {\n\t\t\tthis.exitRenameMode();\n\t\t\treturn;\n\t\t}\n\t\tconst renameSession = this.renameSession;\n\t\tif (!renameSession) {\n\t\t\tthis.exitRenameMode();\n\t\t\treturn;\n\t\t}\n\t\ttry {\n\t\t\tawait renameSession(target, next);\n\t\t\tawait this.refreshSessionsAfterMutation();\n\t\t} finally {\n\t\t\tthis.exitRenameMode();\n\t\t}\n\t}\n\tprivate async loadScope(scope: SessionScope, reason: \"initial\" | \"refresh\" | \"toggle\"): Promise<void> {\n\t\tconst showCwd = scope === \"all\";\n\t\tif (scope === \"current\") {\n\t\t\tthis.currentLoading = true;\n\t\t} else {\n\t\t\tthis.allLoading = true;\n\t\t}\n\t\tconst seq = scope === \"all\" ? ++this.allLoadSeq : undefined;\n\t\tthis.header.setScope(scope);\n\t\tthis.header.setLoading(true);\n\t\tthis.requestRender();\n\t\tconst onProgress = (loaded: number, total: number) => {\n\t\t\tif (scope !== this.scope) return;\n\t\t\tif (seq !== undefined && seq !== this.allLoadSeq) return;\n\t\t\tthis.header.setProgress(loaded, total);\n\t\t\tthis.requestRender();\n\t\t};\n\t\ttry {\n\t\t\tconst sessions = await (scope === \"current\"\n\t\t\t\t? this.currentSessionsLoader(onProgress)\n\t\t\t\t: this.allSessionsLoader(onProgress));\n\t\t\tif (scope === \"current\") {\n\t\t\t\tthis.currentSessions = sessions;\n\t\t\t\tthis.currentLoading = false;\n\t\t\t} else {\n\t\t\t\tthis.allSessions = sessions;\n\t\t\t\tthis.allLoading = false;\n\t\t\t}\n\t\t\tif (scope !== this.scope) return;\n\t\t\tif (seq !== undefined && seq !== this.allLoadSeq) return;\n\t\t\tthis.header.setLoading(false);\n\t\t\tthis.sessionList.setSessions(sessions, showCwd);\n\t\t\tthis.requestRender();\n\t\t\tif (scope === \"all\" && sessions.length === 0 && (this.currentSessions?.length ?? 0) === 0) {\n\t\t\t\tthis.onCancel();\n\t\t\t}\n\t\t} catch (err) {\n\t\t\tif (scope === \"current\") {\n\t\t\t\tthis.currentLoading = false;\n\t\t\t} else {\n\t\t\t\tthis.allLoading = false;\n\t\t\t}\n\t\t\tif (scope !== this.scope) return;\n\t\t\tif (seq !== undefined && seq !== this.allLoadSeq) return;\n\t\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\t\tthis.header.setLoading(false);\n\t\t\tthis.header.setStatusMessage({ type: \"error\", message: `Failed to load sessions: ${message}` }, 4000);\n\t\t\tif (reason === \"initial\") {\n\t\t\t\tthis.sessionList.setSessions([], showCwd);\n\t\t\t}\n\t\t\tthis.requestRender();\n\t\t}\n\t}\n\tprivate toggleSortMode(): void {\n\t\tthis.sortMode = this.sortMode === \"threaded\" ? \"recent\" : this.sortMode === \"recent\" ? \"relevance\" : \"threaded\";\n\t\tthis.header.setSortMode(this.sortMode);\n\t\tthis.sessionList.setSortMode(this.sortMode);\n\t\tthis.requestRender();\n\t}\n\tprivate toggleNameFilter(): void {\n\t\tthis.nameFilter = this.nameFilter === \"all\" ? \"named\" : \"all\";\n\t\tthis.header.setNameFilter(this.nameFilter);\n\t\tthis.sessionList.setNameFilter(this.nameFilter);\n\t\tthis.requestRender();\n\t}\n\tprivate async refreshSessionsAfterMutation(): Promise<void> {\n\t\tawait this.loadScope(this.scope, \"refresh\");\n\t}\n\tprivate toggleScope(): void {\n\t\tif (this.scope === \"current\") {\n\t\t\tthis.scope = \"all\";\n\t\t\tthis.header.setScope(this.scope);\n\t\t\tif (this.allSessions !== null) {\n\t\t\t\tthis.header.setLoading(false);\n\t\t\t\tthis.sessionList.setSessions(this.allSessions, true);\n\t\t\t\tthis.requestRender();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (!this.allLoading) {\n\t\t\t\tvoid this.loadScope(\"all\", \"toggle\");\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\t\tthis.scope = \"current\";\n\t\tthis.header.setScope(this.scope);\n\t\tthis.header.setLoading(this.currentLoading);\n\t\tthis.sessionList.setSessions(this.currentSessions ?? [], false);\n\t\tthis.requestRender();\n\t}\n\tgetSessionList(): SessionList {\n\t\treturn this.sessionList;\n\t}\n}\n"]}
|
|
@@ -2,7 +2,7 @@ import { spawnSync } from "node:child_process";
|
|
|
2
2
|
import { existsSync } from "node:fs";
|
|
3
3
|
import { unlink } from "node:fs/promises";
|
|
4
4
|
import * as os from "node:os";
|
|
5
|
-
import { Container, getEditorKeybindings, Input, matchesKey, Spacer, Text, truncateToWidth, visibleWidth, } from "@
|
|
5
|
+
import { Container, getEditorKeybindings, Input, matchesKey, Spacer, Text, truncateToWidth, visibleWidth, } from "@boxiaolanya2008/pi-tui";
|
|
6
6
|
import { KeybindingsManager } from "../../../core/keybindings.js";
|
|
7
7
|
import { theme } from "../theme/theme.js";
|
|
8
8
|
import { DynamicBorder } from "./dynamic-border.js";
|