@google/gemini-cli 0.19.0-nightly.20251121.5982abeff → 0.19.0-nightly.20251123.dadd606c0
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/google-gemini-cli-0.19.0-nightly.20251122.42c2e1b21.tgz +0 -0
- package/dist/index.js +8 -6
- package/dist/index.js.map +1 -1
- package/dist/package.json +3 -3
- package/dist/src/commands/extensions/disable.js +2 -0
- package/dist/src/commands/extensions/disable.js.map +1 -1
- package/dist/src/commands/extensions/disable.test.js +3 -0
- package/dist/src/commands/extensions/disable.test.js.map +1 -1
- package/dist/src/commands/extensions/enable.js +2 -0
- package/dist/src/commands/extensions/enable.js.map +1 -1
- package/dist/src/commands/extensions/enable.test.js +3 -0
- package/dist/src/commands/extensions/enable.test.js.map +1 -1
- package/dist/src/commands/extensions/install.js +2 -0
- package/dist/src/commands/extensions/install.js.map +1 -1
- package/dist/src/commands/extensions/install.test.js +3 -0
- package/dist/src/commands/extensions/install.test.js.map +1 -1
- package/dist/src/commands/extensions/link.js +2 -0
- package/dist/src/commands/extensions/link.js.map +1 -1
- package/dist/src/commands/extensions/link.test.js +3 -0
- package/dist/src/commands/extensions/link.test.js.map +1 -1
- package/dist/src/commands/extensions/list.js +2 -0
- package/dist/src/commands/extensions/list.js.map +1 -1
- package/dist/src/commands/extensions/list.test.js +3 -0
- package/dist/src/commands/extensions/list.test.js.map +1 -1
- package/dist/src/commands/extensions/new.js +2 -0
- package/dist/src/commands/extensions/new.js.map +1 -1
- package/dist/src/commands/extensions/new.test.js +3 -0
- package/dist/src/commands/extensions/new.test.js.map +1 -1
- package/dist/src/commands/extensions/uninstall.js +2 -0
- package/dist/src/commands/extensions/uninstall.js.map +1 -1
- package/dist/src/commands/extensions/uninstall.test.js +3 -0
- package/dist/src/commands/extensions/uninstall.test.js.map +1 -1
- package/dist/src/commands/extensions/update.js +2 -0
- package/dist/src/commands/extensions/update.js.map +1 -1
- package/dist/src/commands/extensions/update.test.js +3 -0
- package/dist/src/commands/extensions/update.test.js.map +1 -1
- package/dist/src/commands/extensions/validate.js +2 -0
- package/dist/src/commands/extensions/validate.js.map +1 -1
- package/dist/src/commands/extensions/validate.test.js +3 -0
- package/dist/src/commands/extensions/validate.test.js.map +1 -1
- package/dist/src/commands/extensions.js +2 -0
- package/dist/src/commands/extensions.js.map +1 -1
- package/dist/src/commands/mcp/add.js +2 -0
- package/dist/src/commands/mcp/add.js.map +1 -1
- package/dist/src/commands/mcp/add.test.js +3 -0
- package/dist/src/commands/mcp/add.test.js.map +1 -1
- package/dist/src/commands/mcp/list.js +2 -0
- package/dist/src/commands/mcp/list.js.map +1 -1
- package/dist/src/commands/mcp/list.test.js +3 -0
- package/dist/src/commands/mcp/list.test.js.map +1 -1
- package/dist/src/commands/mcp/remove.js +2 -0
- package/dist/src/commands/mcp/remove.js.map +1 -1
- package/dist/src/commands/mcp/remove.test.js +3 -0
- package/dist/src/commands/mcp/remove.test.js.map +1 -1
- package/dist/src/commands/mcp.js +2 -0
- package/dist/src/commands/mcp.js.map +1 -1
- package/dist/src/commands/mcp.test.js +1 -0
- package/dist/src/commands/mcp.test.js.map +1 -1
- package/dist/src/commands/utils.d.ts +6 -0
- package/dist/src/commands/utils.js +11 -0
- package/dist/src/commands/utils.js.map +1 -0
- package/dist/src/config/auth.js +4 -0
- package/dist/src/config/auth.js.map +1 -1
- package/dist/src/config/auth.test.js +61 -37
- package/dist/src/config/auth.test.js.map +1 -1
- package/dist/src/config/config.integration.test.js +81 -198
- package/dist/src/config/config.integration.test.js.map +1 -1
- package/dist/src/config/config.js +2 -15
- package/dist/src/config/config.js.map +1 -1
- package/dist/src/config/config.test.js +196 -299
- package/dist/src/config/config.test.js.map +1 -1
- package/dist/src/config/extension.test.js +109 -133
- package/dist/src/config/extension.test.js.map +1 -1
- package/dist/src/config/extensions/consent.test.d.ts +6 -0
- package/dist/src/config/extensions/consent.test.js +152 -0
- package/dist/src/config/extensions/consent.test.js.map +1 -0
- package/dist/src/config/extensions/extensionEnablement.test.js +82 -15
- package/dist/src/config/extensions/extensionEnablement.test.js.map +1 -1
- package/dist/src/config/extensions/extensionSettings.test.js +105 -1
- package/dist/src/config/extensions/extensionSettings.test.js.map +1 -1
- package/dist/src/config/extensions/github.d.ts +1 -0
- package/dist/src/config/extensions/github.js +1 -1
- package/dist/src/config/extensions/github.js.map +1 -1
- package/dist/src/config/extensions/github.test.js +197 -318
- package/dist/src/config/extensions/github.test.js.map +1 -1
- package/dist/src/config/extensions/storage.test.d.ts +6 -0
- package/dist/src/config/extensions/storage.test.js +64 -0
- package/dist/src/config/extensions/storage.test.js.map +1 -0
- package/dist/src/config/extensions/update.test.js +154 -263
- package/dist/src/config/extensions/update.test.js.map +1 -1
- package/dist/src/config/extensions/variables.test.js +87 -1
- package/dist/src/config/extensions/variables.test.js.map +1 -1
- package/dist/src/config/sandboxConfig.d.ts +1 -1
- package/dist/src/config/sandboxConfig.js.map +1 -1
- package/dist/src/config/sandboxConfig.test.d.ts +6 -0
- package/dist/src/config/sandboxConfig.test.js +178 -0
- package/dist/src/config/sandboxConfig.test.js.map +1 -0
- package/dist/src/config/settingPaths.test.d.ts +6 -0
- package/dist/src/config/settingPaths.test.js +22 -0
- package/dist/src/config/settingPaths.test.js.map +1 -0
- package/dist/src/config/settings.test.js +164 -226
- package/dist/src/config/settings.test.js.map +1 -1
- package/dist/src/config/settingsSchema.d.ts +10 -10
- package/dist/src/config/settingsSchema.js +10 -10
- package/dist/src/config/settingsSchema.js.map +1 -1
- package/dist/src/config/settingsSchema.test.js +0 -6
- package/dist/src/config/settingsSchema.test.js.map +1 -1
- package/dist/src/gemini.d.ts +2 -1
- package/dist/src/gemini.js +6 -7
- package/dist/src/gemini.js.map +1 -1
- package/dist/src/gemini.test.js +5 -0
- package/dist/src/gemini.test.js.map +1 -1
- package/dist/src/generated/git-commit.d.ts +2 -2
- package/dist/src/generated/git-commit.js +2 -2
- package/dist/src/services/BuiltinCommandLoader.js +1 -1
- package/dist/src/services/BuiltinCommandLoader.js.map +1 -1
- package/dist/src/services/BuiltinCommandLoader.test.js +0 -22
- package/dist/src/services/BuiltinCommandLoader.test.js.map +1 -1
- package/dist/src/test-utils/mockCommandContext.js +1 -1
- package/dist/src/test-utils/render.js +1 -1
- package/dist/src/test-utils/render.js.map +1 -1
- package/dist/src/ui/AppContainer.js +13 -11
- package/dist/src/ui/AppContainer.js.map +1 -1
- package/dist/src/ui/AppContainer.test.js +3 -6
- package/dist/src/ui/AppContainer.test.js.map +1 -1
- package/dist/src/ui/auth/AuthDialog.js +17 -10
- package/dist/src/ui/auth/AuthDialog.js.map +1 -1
- package/dist/src/ui/auth/AuthDialog.test.js +5 -2
- package/dist/src/ui/auth/AuthDialog.test.js.map +1 -1
- package/dist/src/ui/components/AboutBox.test.d.ts +6 -0
- package/dist/src/ui/components/AboutBox.test.js +53 -0
- package/dist/src/ui/components/AboutBox.test.js.map +1 -0
- package/dist/src/ui/components/AutoAcceptIndicator.test.d.ts +6 -0
- package/dist/src/ui/components/AutoAcceptIndicator.test.js +31 -0
- package/dist/src/ui/components/AutoAcceptIndicator.test.js.map +1 -0
- package/dist/src/ui/components/Banner.test.d.ts +6 -0
- package/dist/src/ui/components/Banner.test.js +24 -0
- package/dist/src/ui/components/Banner.test.js.map +1 -0
- package/dist/src/ui/components/ConfigInitDisplay.test.d.ts +6 -0
- package/dist/src/ui/components/ConfigInitDisplay.test.js +103 -0
- package/dist/src/ui/components/ConfigInitDisplay.test.js.map +1 -0
- package/dist/src/ui/components/ConsoleSummaryDisplay.test.d.ts +6 -0
- package/dist/src/ui/components/ConsoleSummaryDisplay.test.js +26 -0
- package/dist/src/ui/components/ConsoleSummaryDisplay.test.js.map +1 -0
- package/dist/src/ui/components/ContextUsageDisplay.test.d.ts +6 -0
- package/dist/src/ui/components/ContextUsageDisplay.test.js +39 -0
- package/dist/src/ui/components/ContextUsageDisplay.test.js.map +1 -0
- package/dist/src/ui/components/CopyModeWarning.test.d.ts +6 -0
- package/dist/src/ui/components/CopyModeWarning.test.js +33 -0
- package/dist/src/ui/components/CopyModeWarning.test.js.map +1 -0
- package/dist/src/ui/components/DebugProfiler.js +1 -1
- package/dist/src/ui/components/DebugProfiler.js.map +1 -1
- package/dist/src/ui/components/DebugProfiler.test.js +46 -1
- package/dist/src/ui/components/DebugProfiler.test.js.map +1 -1
- package/dist/src/ui/components/DetailedMessagesDisplay.test.d.ts +6 -0
- package/dist/src/ui/components/DetailedMessagesDisplay.test.js +49 -0
- package/dist/src/ui/components/DetailedMessagesDisplay.test.js.map +1 -0
- package/dist/src/ui/components/DialogManager.js +6 -1
- package/dist/src/ui/components/DialogManager.js.map +1 -1
- package/dist/src/ui/components/DialogManager.test.d.ts +6 -0
- package/dist/src/ui/components/DialogManager.test.js +167 -0
- package/dist/src/ui/components/DialogManager.test.js.map +1 -0
- package/dist/src/ui/components/EditorSettingsDialog.test.d.ts +6 -0
- package/dist/src/ui/components/EditorSettingsDialog.test.js +111 -0
- package/dist/src/ui/components/EditorSettingsDialog.test.js.map +1 -0
- package/dist/src/ui/components/ExitWarning.test.d.ts +6 -0
- package/dist/src/ui/components/ExitWarning.test.js +54 -0
- package/dist/src/ui/components/ExitWarning.test.js.map +1 -0
- package/dist/src/ui/components/GeminiRespondingSpinner.test.d.ts +6 -0
- package/dist/src/ui/components/GeminiRespondingSpinner.test.js +58 -0
- package/dist/src/ui/components/GeminiRespondingSpinner.test.js.map +1 -0
- package/dist/src/ui/components/InputPrompt.js +1 -1
- package/dist/src/ui/components/InputPrompt.js.map +1 -1
- package/dist/src/ui/components/LoadingIndicator.js +6 -1
- package/dist/src/ui/components/LoadingIndicator.js.map +1 -1
- package/dist/src/ui/components/MainContent.test.d.ts +6 -0
- package/dist/src/ui/components/MainContent.test.js +73 -0
- package/dist/src/ui/components/MainContent.test.js.map +1 -0
- package/dist/src/ui/components/MemoryUsageDisplay.test.d.ts +6 -0
- package/dist/src/ui/components/MemoryUsageDisplay.test.js +49 -0
- package/dist/src/ui/components/MemoryUsageDisplay.test.js.map +1 -0
- package/dist/src/ui/components/ModelDialog.test.js +1 -1
- package/dist/src/ui/components/ModelDialog.test.js.map +1 -1
- package/dist/src/ui/components/Notifications.js +5 -3
- package/dist/src/ui/components/Notifications.js.map +1 -1
- package/dist/src/ui/components/Notifications.test.d.ts +6 -0
- package/dist/src/ui/components/Notifications.test.js +153 -0
- package/dist/src/ui/components/Notifications.test.js.map +1 -0
- package/dist/src/ui/components/QuittingDisplay.test.d.ts +6 -0
- package/dist/src/ui/components/QuittingDisplay.test.js +49 -0
- package/dist/src/ui/components/QuittingDisplay.test.js.map +1 -0
- package/dist/src/ui/components/RawMarkdownIndicator.test.d.ts +6 -0
- package/dist/src/ui/components/RawMarkdownIndicator.test.js +34 -0
- package/dist/src/ui/components/RawMarkdownIndicator.test.js.map +1 -0
- package/dist/src/ui/components/SessionBrowser.d.ts +98 -0
- package/dist/src/ui/components/SessionBrowser.js +457 -0
- package/dist/src/ui/components/SessionBrowser.js.map +1 -0
- package/dist/src/ui/components/SessionBrowser.test.d.ts +6 -0
- package/dist/src/ui/components/SessionBrowser.test.js +239 -0
- package/dist/src/ui/components/SessionBrowser.test.js.map +1 -0
- package/dist/src/ui/components/ShellInputPrompt.test.d.ts +6 -0
- package/dist/src/ui/components/ShellInputPrompt.test.js +82 -0
- package/dist/src/ui/components/ShellInputPrompt.test.js.map +1 -0
- package/dist/src/ui/components/ShellModeIndicator.test.d.ts +6 -0
- package/dist/src/ui/components/ShellModeIndicator.test.js +17 -0
- package/dist/src/ui/components/ShellModeIndicator.test.js.map +1 -0
- package/dist/src/ui/components/ShowMoreLines.test.d.ts +6 -0
- package/dist/src/ui/components/ShowMoreLines.test.js +40 -0
- package/dist/src/ui/components/ShowMoreLines.test.js.map +1 -0
- package/dist/src/ui/components/SuggestionsDisplay.test.d.ts +6 -0
- package/dist/src/ui/components/SuggestionsDisplay.test.js +56 -0
- package/dist/src/ui/components/SuggestionsDisplay.test.js.map +1 -0
- package/dist/src/ui/components/ThemedGradient.test.d.ts +6 -0
- package/dist/src/ui/components/ThemedGradient.test.js +30 -0
- package/dist/src/ui/components/ThemedGradient.test.js.map +1 -0
- package/dist/src/ui/components/Tips.test.d.ts +6 -0
- package/dist/src/ui/components/Tips.test.js +23 -0
- package/dist/src/ui/components/Tips.test.js.map +1 -0
- package/dist/src/ui/components/UpdateNotification.test.d.ts +6 -0
- package/dist/src/ui/components/UpdateNotification.test.js +16 -0
- package/dist/src/ui/components/UpdateNotification.test.js.map +1 -0
- package/dist/src/ui/components/messages/ErrorMessage.test.d.ts +6 -0
- package/dist/src/ui/components/messages/ErrorMessage.test.js +23 -0
- package/dist/src/ui/components/messages/ErrorMessage.test.js.map +1 -0
- package/dist/src/ui/components/messages/InfoMessage.test.d.ts +6 -0
- package/dist/src/ui/components/messages/InfoMessage.test.js +28 -0
- package/dist/src/ui/components/messages/InfoMessage.test.js.map +1 -0
- package/dist/src/ui/components/messages/ShellToolMessage.js +2 -2
- package/dist/src/ui/components/messages/ShellToolMessage.js.map +1 -1
- package/dist/src/ui/components/messages/ToolConfirmationMessage.test.js +5 -6
- package/dist/src/ui/components/messages/ToolConfirmationMessage.test.js.map +1 -1
- package/dist/src/ui/components/messages/ToolMessage.d.ts +5 -0
- package/dist/src/ui/components/messages/ToolMessage.js +32 -3
- package/dist/src/ui/components/messages/ToolMessage.js.map +1 -1
- package/dist/src/ui/components/messages/ToolMessage.test.js +13 -21
- package/dist/src/ui/components/messages/ToolMessage.test.js.map +1 -1
- package/dist/src/ui/components/messages/ToolResultDisplay.js +1 -1
- package/dist/src/ui/components/messages/ToolResultDisplay.js.map +1 -1
- package/dist/src/ui/components/messages/ToolResultDisplay.test.d.ts +6 -0
- package/dist/src/ui/components/messages/ToolResultDisplay.test.js +96 -0
- package/dist/src/ui/components/messages/ToolResultDisplay.test.js.map +1 -0
- package/dist/src/ui/components/messages/UserMessage.test.d.ts +6 -0
- package/dist/src/ui/components/messages/UserMessage.test.js +32 -0
- package/dist/src/ui/components/messages/UserMessage.test.js.map +1 -0
- package/dist/src/ui/components/messages/WarningMessage.test.d.ts +6 -0
- package/dist/src/ui/components/messages/WarningMessage.test.js +23 -0
- package/dist/src/ui/components/messages/WarningMessage.test.js.map +1 -0
- package/dist/src/ui/constants.d.ts +1 -0
- package/dist/src/ui/constants.js +1 -0
- package/dist/src/ui/constants.js.map +1 -1
- package/dist/src/ui/hooks/shellCommandProcessor.d.ts +1 -0
- package/dist/src/ui/hooks/shellCommandProcessor.js +3 -1
- package/dist/src/ui/hooks/shellCommandProcessor.js.map +1 -1
- package/dist/src/ui/hooks/useAlternateBuffer.js +1 -1
- package/dist/src/ui/hooks/useAlternateBuffer.js.map +1 -1
- package/dist/src/ui/hooks/useBracketedPaste.js +3 -5
- package/dist/src/ui/hooks/useBracketedPaste.js.map +1 -1
- package/dist/src/ui/hooks/useGeminiStream.d.ts +1 -0
- package/dist/src/ui/hooks/useGeminiStream.js +6 -4
- package/dist/src/ui/hooks/useGeminiStream.js.map +1 -1
- package/dist/src/ui/hooks/useGeminiStream.test.js +1 -1
- package/dist/src/ui/hooks/useGeminiStream.test.js.map +1 -1
- package/dist/src/ui/hooks/useInactivityTimer.d.ts +14 -0
- package/dist/src/ui/hooks/useInactivityTimer.js +30 -0
- package/dist/src/ui/hooks/useInactivityTimer.js.map +1 -0
- package/dist/src/ui/hooks/useLoadingIndicator.d.ts +1 -1
- package/dist/src/ui/hooks/useLoadingIndicator.js +2 -2
- package/dist/src/ui/hooks/useLoadingIndicator.js.map +1 -1
- package/dist/src/ui/hooks/useLoadingIndicator.test.js +16 -5
- package/dist/src/ui/hooks/useLoadingIndicator.test.js.map +1 -1
- package/dist/src/ui/hooks/usePhraseCycler.d.ts +4 -1
- package/dist/src/ui/hooks/usePhraseCycler.js +52 -43
- package/dist/src/ui/hooks/usePhraseCycler.js.map +1 -1
- package/dist/src/ui/hooks/usePhraseCycler.test.js +52 -3
- package/dist/src/ui/hooks/usePhraseCycler.test.js.map +1 -1
- package/dist/src/ui/hooks/useReactToolScheduler.d.ts +2 -1
- package/dist/src/ui/hooks/useReactToolScheduler.js +3 -0
- package/dist/src/ui/hooks/useReactToolScheduler.js.map +1 -1
- package/dist/src/ui/hooks/useSessionBrowser.d.ts +18 -1
- package/dist/src/ui/hooks/useSessionBrowser.js +59 -0
- package/dist/src/ui/hooks/useSessionBrowser.js.map +1 -1
- package/dist/src/ui/hooks/useSessionBrowser.test.js +154 -526
- package/dist/src/ui/hooks/useSessionBrowser.test.js.map +1 -1
- package/dist/src/ui/hooks/useSlashCompletion.test.js +1 -1
- package/dist/src/ui/hooks/useSlashCompletion.test.js.map +1 -1
- package/dist/src/ui/hooks/useToolScheduler.test.js +1 -1
- package/dist/src/ui/hooks/useToolScheduler.test.js.map +1 -1
- package/dist/src/ui/privacy/CloudFreePrivacyNotice.test.d.ts +6 -0
- package/dist/src/ui/privacy/CloudFreePrivacyNotice.test.js +121 -0
- package/dist/src/ui/privacy/CloudFreePrivacyNotice.test.js.map +1 -0
- package/dist/src/ui/privacy/CloudPaidPrivacyNotice.test.d.ts +6 -0
- package/dist/src/ui/privacy/CloudPaidPrivacyNotice.test.js +34 -0
- package/dist/src/ui/privacy/CloudPaidPrivacyNotice.test.js.map +1 -0
- package/dist/src/ui/privacy/GeminiPrivacyNotice.test.d.ts +6 -0
- package/dist/src/ui/privacy/GeminiPrivacyNotice.test.js +34 -0
- package/dist/src/ui/privacy/GeminiPrivacyNotice.test.js.map +1 -0
- package/dist/src/ui/privacy/PrivacyNotice.test.d.ts +6 -0
- package/dist/src/ui/privacy/PrivacyNotice.test.js +62 -0
- package/dist/src/ui/privacy/PrivacyNotice.test.js.map +1 -0
- package/dist/src/ui/types.js +1 -1
- package/dist/src/ui/utils/bracketedPaste.d.ts +7 -0
- package/dist/src/ui/utils/bracketedPaste.js +15 -0
- package/dist/src/ui/utils/bracketedPaste.js.map +1 -0
- package/dist/src/ui/utils/kittyProtocolDetector.js +3 -4
- package/dist/src/ui/utils/kittyProtocolDetector.js.map +1 -1
- package/dist/src/ui/utils/mouse.d.ts +2 -2
- package/dist/src/ui/utils/mouse.js +2 -11
- package/dist/src/ui/utils/mouse.js.map +1 -1
- package/dist/src/utils/sessionCleanup.test.js +38 -0
- package/dist/src/utils/sessionCleanup.test.js.map +1 -1
- package/dist/src/utils/sessionUtils.d.ts +49 -4
- package/dist/src/utils/sessionUtils.js +100 -25
- package/dist/src/utils/sessionUtils.js.map +1 -1
- package/dist/src/utils/sessionUtils.test.js +46 -3
- package/dist/src/utils/sessionUtils.test.js.map +1 -1
- package/dist/src/utils/sessions.js +4 -1
- package/dist/src/utils/sessions.js.map +1 -1
- package/dist/src/utils/sessions.test.js +42 -0
- package/dist/src/utils/sessions.test.js.map +1 -1
- package/dist/src/zed-integration/connection.test.d.ts +6 -0
- package/dist/src/zed-integration/connection.test.js +175 -0
- package/dist/src/zed-integration/connection.test.js.map +1 -0
- package/dist/src/zed-integration/fileSystemService.test.d.ts +6 -0
- package/dist/src/zed-integration/fileSystemService.test.js +98 -0
- package/dist/src/zed-integration/fileSystemService.test.js.map +1 -0
- package/dist/src/zed-integration/zedIntegration.d.ts +31 -1
- package/dist/src/zed-integration/zedIntegration.js +5 -2
- package/dist/src/zed-integration/zedIntegration.js.map +1 -1
- package/dist/src/zed-integration/zedIntegration.test.d.ts +6 -0
- package/dist/src/zed-integration/zedIntegration.test.js +619 -0
- package/dist/src/zed-integration/zedIntegration.test.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +4 -4
- package/dist/google-gemini-cli-0.19.0-nightly.20251120.8e531dc02.tgz +0 -0
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* @license
|
|
4
|
+
* Copyright 2025 Google LLC
|
|
5
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
6
|
+
*/
|
|
7
|
+
import { render } from '../../test-utils/render.js';
|
|
8
|
+
import { QuittingDisplay } from './QuittingDisplay.js';
|
|
9
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
10
|
+
import React from 'react';
|
|
11
|
+
import { useUIState } from '../contexts/UIStateContext.js';
|
|
12
|
+
import { useTerminalSize } from '../hooks/useTerminalSize.js';
|
|
13
|
+
vi.mock('../contexts/UIStateContext.js');
|
|
14
|
+
vi.mock('../hooks/useTerminalSize.js');
|
|
15
|
+
vi.mock('./HistoryItemDisplay.js', async () => {
|
|
16
|
+
const { Text } = await vi.importActual('ink');
|
|
17
|
+
return {
|
|
18
|
+
HistoryItemDisplay: ({ item }) => React.createElement(Text, null, item.content),
|
|
19
|
+
};
|
|
20
|
+
});
|
|
21
|
+
describe('QuittingDisplay', () => {
|
|
22
|
+
const mockUseUIState = vi.mocked(useUIState);
|
|
23
|
+
const mockUseTerminalSize = vi.mocked(useTerminalSize);
|
|
24
|
+
beforeEach(() => {
|
|
25
|
+
vi.clearAllMocks();
|
|
26
|
+
mockUseTerminalSize.mockReturnValue({ rows: 20, columns: 80 });
|
|
27
|
+
});
|
|
28
|
+
it('renders nothing when no quitting messages', () => {
|
|
29
|
+
mockUseUIState.mockReturnValue({
|
|
30
|
+
quittingMessages: null,
|
|
31
|
+
});
|
|
32
|
+
const { lastFrame } = render(_jsx(QuittingDisplay, {}));
|
|
33
|
+
expect(lastFrame()).toBe('');
|
|
34
|
+
});
|
|
35
|
+
it('renders quitting messages', () => {
|
|
36
|
+
const mockMessages = [
|
|
37
|
+
{ id: '1', type: 'user', content: 'Goodbye' },
|
|
38
|
+
{ id: '2', type: 'model', content: 'See you later' },
|
|
39
|
+
];
|
|
40
|
+
mockUseUIState.mockReturnValue({
|
|
41
|
+
quittingMessages: mockMessages,
|
|
42
|
+
constrainHeight: false,
|
|
43
|
+
});
|
|
44
|
+
const { lastFrame } = render(_jsx(QuittingDisplay, {}));
|
|
45
|
+
expect(lastFrame()).toContain('Goodbye');
|
|
46
|
+
expect(lastFrame()).toContain('See you later');
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
//# sourceMappingURL=QuittingDisplay.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"QuittingDisplay.test.js","sourceRoot":"","sources":["../../../../src/ui/components/QuittingDisplay.test.tsx"],"names":[],"mappings":";AAAA;;;;GAIG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,4BAA4B,CAAC;AACpD,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AAC9D,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,UAAU,EAAgB,MAAM,+BAA+B,CAAC;AACzE,OAAO,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AAE9D,EAAE,CAAC,IAAI,CAAC,+BAA+B,CAAC,CAAC;AACzC,EAAE,CAAC,IAAI,CAAC,6BAA6B,CAAC,CAAC;AACvC,EAAE,CAAC,IAAI,CAAC,yBAAyB,EAAE,KAAK,IAAI,EAAE;IAC5C,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,EAAE,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC;IAC9C,OAAO;QACL,kBAAkB,EAAE,CAAC,EAAE,IAAI,EAAiC,EAAE,EAAE,CAC9D,KAAK,CAAC,aAAa,CAAC,IAA2B,EAAE,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC;KACvE,CAAC;AACJ,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,iBAAiB,EAAE,GAAG,EAAE;IAC/B,MAAM,cAAc,GAAG,EAAE,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;IAC7C,MAAM,mBAAmB,GAAG,EAAE,CAAC,MAAM,CAAC,eAAe,CAAC,CAAC;IAEvD,UAAU,CAAC,GAAG,EAAE;QACd,EAAE,CAAC,aAAa,EAAE,CAAC;QACnB,mBAAmB,CAAC,eAAe,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC,CAAC;IACjE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,2CAA2C,EAAE,GAAG,EAAE;QACnD,cAAc,CAAC,eAAe,CAAC;YAC7B,gBAAgB,EAAE,IAAI;SACD,CAAC,CAAC;QACzB,MAAM,EAAE,SAAS,EAAE,GAAG,MAAM,CAAC,KAAC,eAAe,KAAG,CAAC,CAAC;QAClD,MAAM,CAAC,SAAS,EAAE,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAC/B,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,2BAA2B,EAAE,GAAG,EAAE;QACnC,MAAM,YAAY,GAAG;YACnB,EAAE,EAAE,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE;YAC7C,EAAE,EAAE,EAAE,GAAG,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,eAAe,EAAE;SACrD,CAAC;QACF,cAAc,CAAC,eAAe,CAAC;YAC7B,gBAAgB,EAAE,YAAY;YAC9B,eAAe,EAAE,KAAK;SACD,CAAC,CAAC;QACzB,MAAM,EAAE,SAAS,EAAE,GAAG,MAAM,CAAC,KAAC,eAAe,KAAG,CAAC,CAAC;QAClD,MAAM,CAAC,SAAS,EAAE,CAAC,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC;QACzC,MAAM,CAAC,SAAS,EAAE,CAAC,CAAC,SAAS,CAAC,eAAe,CAAC,CAAC;IACjD,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* @license
|
|
4
|
+
* Copyright 2025 Google LLC
|
|
5
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
6
|
+
*/
|
|
7
|
+
import { render } from '../../test-utils/render.js';
|
|
8
|
+
import { RawMarkdownIndicator } from './RawMarkdownIndicator.js';
|
|
9
|
+
import { describe, it, expect, afterEach } from 'vitest';
|
|
10
|
+
describe('RawMarkdownIndicator', () => {
|
|
11
|
+
const originalPlatform = process.platform;
|
|
12
|
+
afterEach(() => {
|
|
13
|
+
Object.defineProperty(process, 'platform', {
|
|
14
|
+
value: originalPlatform,
|
|
15
|
+
});
|
|
16
|
+
});
|
|
17
|
+
it('renders correct key binding for darwin', () => {
|
|
18
|
+
Object.defineProperty(process, 'platform', {
|
|
19
|
+
value: 'darwin',
|
|
20
|
+
});
|
|
21
|
+
const { lastFrame } = render(_jsx(RawMarkdownIndicator, {}));
|
|
22
|
+
expect(lastFrame()).toContain('raw markdown mode');
|
|
23
|
+
expect(lastFrame()).toContain('option+m to toggle');
|
|
24
|
+
});
|
|
25
|
+
it('renders correct key binding for other platforms', () => {
|
|
26
|
+
Object.defineProperty(process, 'platform', {
|
|
27
|
+
value: 'linux',
|
|
28
|
+
});
|
|
29
|
+
const { lastFrame } = render(_jsx(RawMarkdownIndicator, {}));
|
|
30
|
+
expect(lastFrame()).toContain('raw markdown mode');
|
|
31
|
+
expect(lastFrame()).toContain('alt+m to toggle');
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
//# sourceMappingURL=RawMarkdownIndicator.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RawMarkdownIndicator.test.js","sourceRoot":"","sources":["../../../../src/ui/components/RawMarkdownIndicator.test.tsx"],"names":[],"mappings":";AAAA;;;;GAIG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,4BAA4B,CAAC;AACpD,OAAO,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AACjE,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AAEzD,QAAQ,CAAC,sBAAsB,EAAE,GAAG,EAAE;IACpC,MAAM,gBAAgB,GAAG,OAAO,CAAC,QAAQ,CAAC;IAE1C,SAAS,CAAC,GAAG,EAAE;QACb,MAAM,CAAC,cAAc,CAAC,OAAO,EAAE,UAAU,EAAE;YACzC,KAAK,EAAE,gBAAgB;SACxB,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,wCAAwC,EAAE,GAAG,EAAE;QAChD,MAAM,CAAC,cAAc,CAAC,OAAO,EAAE,UAAU,EAAE;YACzC,KAAK,EAAE,QAAQ;SAChB,CAAC,CAAC;QACH,MAAM,EAAE,SAAS,EAAE,GAAG,MAAM,CAAC,KAAC,oBAAoB,KAAG,CAAC,CAAC;QACvD,MAAM,CAAC,SAAS,EAAE,CAAC,CAAC,SAAS,CAAC,mBAAmB,CAAC,CAAC;QACnD,MAAM,CAAC,SAAS,EAAE,CAAC,CAAC,SAAS,CAAC,oBAAoB,CAAC,CAAC;IACtD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,iDAAiD,EAAE,GAAG,EAAE;QACzD,MAAM,CAAC,cAAc,CAAC,OAAO,EAAE,UAAU,EAAE;YACzC,KAAK,EAAE,OAAO;SACf,CAAC,CAAC;QACH,MAAM,EAAE,SAAS,EAAE,GAAG,MAAM,CAAC,KAAC,oBAAoB,KAAG,CAAC,CAAC;QACvD,MAAM,CAAC,SAAS,EAAE,CAAC,CAAC,SAAS,CAAC,mBAAmB,CAAC,CAAC;QACnD,MAAM,CAAC,SAAS,EAAE,CAAC,CAAC,SAAS,CAAC,iBAAiB,CAAC,CAAC;IACnD,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2025 Google LLC
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
import type React from 'react';
|
|
7
|
+
import type { Config } from '@google/gemini-cli-core';
|
|
8
|
+
import type { SessionInfo } from '../../utils/sessionUtils.js';
|
|
9
|
+
/**
|
|
10
|
+
* Props for the main SessionBrowser component.
|
|
11
|
+
*/
|
|
12
|
+
export interface SessionBrowserProps {
|
|
13
|
+
/** Application configuration object */
|
|
14
|
+
config: Config;
|
|
15
|
+
/** Callback when user selects a session to resume */
|
|
16
|
+
onResumeSession: (session: SessionInfo) => void;
|
|
17
|
+
/** Callback when user deletes a session */
|
|
18
|
+
onDeleteSession?: (session: SessionInfo) => void;
|
|
19
|
+
/** Callback when user exits the session browser */
|
|
20
|
+
onExit: () => void;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Centralized state interface for SessionBrowser component.
|
|
24
|
+
* Eliminates prop drilling by providing all state in a single object.
|
|
25
|
+
*/
|
|
26
|
+
export interface SessionBrowserState {
|
|
27
|
+
/** All loaded sessions */
|
|
28
|
+
sessions: SessionInfo[];
|
|
29
|
+
/** Sessions after filtering and sorting */
|
|
30
|
+
filteredAndSortedSessions: SessionInfo[];
|
|
31
|
+
/** Whether sessions are currently loading */
|
|
32
|
+
loading: boolean;
|
|
33
|
+
/** Error message if loading failed */
|
|
34
|
+
error: string | null;
|
|
35
|
+
/** Index of currently selected session */
|
|
36
|
+
activeIndex: number;
|
|
37
|
+
/** Current scroll offset for pagination */
|
|
38
|
+
scrollOffset: number;
|
|
39
|
+
/** Terminal width for layout calculations */
|
|
40
|
+
terminalWidth: number;
|
|
41
|
+
/** Current search query string */
|
|
42
|
+
searchQuery: string;
|
|
43
|
+
/** Whether user is in search input mode */
|
|
44
|
+
isSearchMode: boolean;
|
|
45
|
+
/** Whether full content has been loaded for search */
|
|
46
|
+
hasLoadedFullContent: boolean;
|
|
47
|
+
/** Current sort criteria */
|
|
48
|
+
sortOrder: 'date' | 'messages' | 'name';
|
|
49
|
+
/** Whether sort order is reversed */
|
|
50
|
+
sortReverse: boolean;
|
|
51
|
+
/** Total number of filtered sessions */
|
|
52
|
+
totalSessions: number;
|
|
53
|
+
/** Start index for current page */
|
|
54
|
+
startIndex: number;
|
|
55
|
+
/** End index for current page */
|
|
56
|
+
endIndex: number;
|
|
57
|
+
/** Sessions visible on current page */
|
|
58
|
+
visibleSessions: SessionInfo[];
|
|
59
|
+
/** Update sessions array */
|
|
60
|
+
setSessions: React.Dispatch<React.SetStateAction<SessionInfo[]>>;
|
|
61
|
+
/** Update loading state */
|
|
62
|
+
setLoading: React.Dispatch<React.SetStateAction<boolean>>;
|
|
63
|
+
/** Update error state */
|
|
64
|
+
setError: React.Dispatch<React.SetStateAction<string | null>>;
|
|
65
|
+
/** Update active session index */
|
|
66
|
+
setActiveIndex: React.Dispatch<React.SetStateAction<number>>;
|
|
67
|
+
/** Update scroll offset */
|
|
68
|
+
setScrollOffset: React.Dispatch<React.SetStateAction<number>>;
|
|
69
|
+
/** Update search query */
|
|
70
|
+
setSearchQuery: React.Dispatch<React.SetStateAction<string>>;
|
|
71
|
+
/** Update search mode state */
|
|
72
|
+
setIsSearchMode: React.Dispatch<React.SetStateAction<boolean>>;
|
|
73
|
+
/** Update sort order */
|
|
74
|
+
setSortOrder: React.Dispatch<React.SetStateAction<'date' | 'messages' | 'name'>>;
|
|
75
|
+
/** Update sort reverse flag */
|
|
76
|
+
setSortReverse: React.Dispatch<React.SetStateAction<boolean>>;
|
|
77
|
+
setHasLoadedFullContent: React.Dispatch<React.SetStateAction<boolean>>;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Hook to manage all SessionBrowser state.
|
|
81
|
+
*/
|
|
82
|
+
export declare const useSessionBrowserState: (initialSessions?: SessionInfo[], initialLoading?: boolean, initialError?: string | null) => SessionBrowserState;
|
|
83
|
+
/**
|
|
84
|
+
* Hook to handle selection movement.
|
|
85
|
+
*/
|
|
86
|
+
export declare const useMoveSelection: (state: SessionBrowserState) => (delta: number) => void;
|
|
87
|
+
/**
|
|
88
|
+
* Hook to handle sort order cycling.
|
|
89
|
+
*/
|
|
90
|
+
export declare const useCycleSortOrder: (state: SessionBrowserState) => () => void;
|
|
91
|
+
/**
|
|
92
|
+
* Hook to handle SessionBrowser input.
|
|
93
|
+
*/
|
|
94
|
+
export declare const useSessionBrowserInput: (state: SessionBrowserState, moveSelection: (delta: number) => void, cycleSortOrder: () => void, onResumeSession: (session: SessionInfo) => void, onDeleteSession: ((session: SessionInfo) => void) | undefined, onExit: () => void) => void;
|
|
95
|
+
export declare function SessionBrowserView({ state, }: {
|
|
96
|
+
state: SessionBrowserState;
|
|
97
|
+
}): React.JSX.Element;
|
|
98
|
+
export declare function SessionBrowser({ config, onResumeSession, onDeleteSession, onExit, }: SessionBrowserProps): React.JSX.Element;
|
|
@@ -0,0 +1,457 @@
|
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useState, useCallback, useMemo, useEffect, useRef } from 'react';
|
|
3
|
+
import { Box, Text } from 'ink';
|
|
4
|
+
import { Colors } from '../colors.js';
|
|
5
|
+
import { useTerminalSize } from '../hooks/useTerminalSize.js';
|
|
6
|
+
import { useKeypress } from '../hooks/useKeypress.js';
|
|
7
|
+
import path from 'node:path';
|
|
8
|
+
import { cleanMessage, formatRelativeTime, getSessionFiles, } from '../../utils/sessionUtils.js';
|
|
9
|
+
const SESSIONS_PER_PAGE = 20;
|
|
10
|
+
// Approximate total width reserved for non-message columns and separators
|
|
11
|
+
// (prefix, index, message count, age, pipes, and padding) in a session row.
|
|
12
|
+
// If the SessionItem layout changes, update this accordingly.
|
|
13
|
+
const FIXED_SESSION_COLUMNS_WIDTH = 30;
|
|
14
|
+
const Kbd = ({ name, shortcut }) => (_jsxs(_Fragment, { children: [name, ": ", _jsx(Text, { bold: true, children: shortcut })] }));
|
|
15
|
+
/**
|
|
16
|
+
* Loading state component displayed while sessions are being loaded.
|
|
17
|
+
*/
|
|
18
|
+
const SessionBrowserLoading = () => (_jsx(Box, { flexDirection: "column", paddingX: 1, children: _jsx(Text, { color: Colors.Gray, children: "Loading sessions\u2026" }) }));
|
|
19
|
+
/**
|
|
20
|
+
* Error state component displayed when session loading fails.
|
|
21
|
+
*/
|
|
22
|
+
const SessionBrowserError = ({ state, }) => (_jsxs(Box, { flexDirection: "column", paddingX: 1, children: [_jsxs(Text, { color: Colors.AccentRed, children: ["Error: ", state.error] }), _jsx(Text, { color: Colors.Gray, children: "Press q to exit" })] }));
|
|
23
|
+
/**
|
|
24
|
+
* Empty state component displayed when no sessions are found.
|
|
25
|
+
*/
|
|
26
|
+
const SessionBrowserEmpty = () => (_jsxs(Box, { flexDirection: "column", paddingX: 1, children: [_jsx(Text, { color: Colors.Gray, children: "No auto-saved conversations found." }), _jsx(Text, { color: Colors.Gray, children: "Press q to exit" })] }));
|
|
27
|
+
/**
|
|
28
|
+
* Sorts an array of sessions by the specified criteria.
|
|
29
|
+
* @param sessions - Array of sessions to sort
|
|
30
|
+
* @param sortBy - Sort criteria: 'date' (lastUpdated), 'messages' (messageCount), or 'name' (displayName)
|
|
31
|
+
* @param reverse - Whether to reverse the sort order (ascending instead of descending)
|
|
32
|
+
* @returns New sorted array of sessions
|
|
33
|
+
*/
|
|
34
|
+
const sortSessions = (sessions, sortBy, reverse) => {
|
|
35
|
+
const sorted = [...sessions].sort((a, b) => {
|
|
36
|
+
switch (sortBy) {
|
|
37
|
+
case 'date':
|
|
38
|
+
return (new Date(b.lastUpdated).getTime() - new Date(a.lastUpdated).getTime());
|
|
39
|
+
case 'messages':
|
|
40
|
+
return b.messageCount - a.messageCount;
|
|
41
|
+
case 'name':
|
|
42
|
+
return a.displayName.localeCompare(b.displayName);
|
|
43
|
+
default:
|
|
44
|
+
return 0;
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
return reverse ? sorted.reverse() : sorted;
|
|
48
|
+
};
|
|
49
|
+
/**
|
|
50
|
+
* Finds all text matches for a search query within conversation messages.
|
|
51
|
+
* Creates TextMatch objects with context (10 chars before/after) and role information.
|
|
52
|
+
* @param messages - Array of messages to search through
|
|
53
|
+
* @param query - Search query string (case-insensitive)
|
|
54
|
+
* @returns Array of TextMatch objects containing match context and metadata
|
|
55
|
+
*/
|
|
56
|
+
const findTextMatches = (messages, query) => {
|
|
57
|
+
if (!query.trim())
|
|
58
|
+
return [];
|
|
59
|
+
const lowerQuery = query.toLowerCase();
|
|
60
|
+
const matches = [];
|
|
61
|
+
for (const message of messages) {
|
|
62
|
+
const m = cleanMessage(message.content);
|
|
63
|
+
const lowerContent = m.toLowerCase();
|
|
64
|
+
let startIndex = 0;
|
|
65
|
+
while (true) {
|
|
66
|
+
const matchIndex = lowerContent.indexOf(lowerQuery, startIndex);
|
|
67
|
+
if (matchIndex === -1)
|
|
68
|
+
break;
|
|
69
|
+
const contextStart = Math.max(0, matchIndex - 10);
|
|
70
|
+
const contextEnd = Math.min(m.length, matchIndex + query.length + 10);
|
|
71
|
+
const snippet = m.slice(contextStart, contextEnd);
|
|
72
|
+
const relativeMatchStart = matchIndex - contextStart;
|
|
73
|
+
const relativeMatchEnd = relativeMatchStart + query.length;
|
|
74
|
+
let before = snippet.slice(0, relativeMatchStart);
|
|
75
|
+
const match = snippet.slice(relativeMatchStart, relativeMatchEnd);
|
|
76
|
+
let after = snippet.slice(relativeMatchEnd);
|
|
77
|
+
if (contextStart > 0)
|
|
78
|
+
before = '…' + before;
|
|
79
|
+
if (contextEnd < m.length)
|
|
80
|
+
after = after + '…';
|
|
81
|
+
matches.push({ before, match, after, role: message.role });
|
|
82
|
+
startIndex = matchIndex + 1;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return matches;
|
|
86
|
+
};
|
|
87
|
+
/**
|
|
88
|
+
* Filters sessions based on a search query, checking titles, IDs, and full content.
|
|
89
|
+
* Also populates matchSnippets and matchCount for sessions with content matches.
|
|
90
|
+
* @param sessions - Array of sessions to filter
|
|
91
|
+
* @param query - Search query string (case-insensitive)
|
|
92
|
+
* @returns Filtered array of sessions that match the query
|
|
93
|
+
*/
|
|
94
|
+
const filterSessions = (sessions, query) => {
|
|
95
|
+
if (!query.trim()) {
|
|
96
|
+
return sessions.map((session) => ({
|
|
97
|
+
...session,
|
|
98
|
+
matchSnippets: undefined,
|
|
99
|
+
matchCount: undefined,
|
|
100
|
+
}));
|
|
101
|
+
}
|
|
102
|
+
const lowerQuery = query.toLowerCase();
|
|
103
|
+
return sessions.filter((session) => {
|
|
104
|
+
const titleMatch = session.displayName.toLowerCase().includes(lowerQuery) ||
|
|
105
|
+
session.id.toLowerCase().includes(lowerQuery) ||
|
|
106
|
+
session.firstUserMessage.toLowerCase().includes(lowerQuery);
|
|
107
|
+
const contentMatch = session.fullContent
|
|
108
|
+
?.toLowerCase()
|
|
109
|
+
.includes(lowerQuery);
|
|
110
|
+
if (titleMatch || contentMatch) {
|
|
111
|
+
if (session.messages) {
|
|
112
|
+
session.matchSnippets = findTextMatches(session.messages, query);
|
|
113
|
+
session.matchCount = session.matchSnippets.length;
|
|
114
|
+
}
|
|
115
|
+
return true;
|
|
116
|
+
}
|
|
117
|
+
return false;
|
|
118
|
+
});
|
|
119
|
+
};
|
|
120
|
+
/**
|
|
121
|
+
* Search input display component.
|
|
122
|
+
*/
|
|
123
|
+
const SearchModeDisplay = ({ state, }) => (_jsxs(Box, { marginTop: 1, children: [_jsx(Text, { color: Colors.Gray, children: "Search: " }), _jsx(Text, { color: Colors.AccentPurple, children: state.searchQuery }), _jsx(Text, { color: Colors.Gray, children: " (Esc to cancel)" })] }));
|
|
124
|
+
/**
|
|
125
|
+
* Header component showing session count and sort information.
|
|
126
|
+
*/
|
|
127
|
+
const SessionListHeader = ({ state, }) => (_jsxs(Box, { flexDirection: "row", justifyContent: "space-between", children: [_jsxs(Text, { color: Colors.AccentPurple, children: ["Chat Sessions (", state.totalSessions, " total", state.searchQuery ? `, filtered` : '', ")"] }), _jsxs(Text, { color: Colors.Gray, children: ["sorted by ", state.sortOrder, " ", state.sortReverse ? 'asc' : 'desc'] })] }));
|
|
128
|
+
/**
|
|
129
|
+
* Navigation help component showing keyboard shortcuts.
|
|
130
|
+
*/
|
|
131
|
+
const NavigationHelp = () => (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { color: Colors.Gray, children: [_jsx(Kbd, { name: "Navigate", shortcut: "\u2191/\u2193" }), ' ', _jsx(Kbd, { name: "Resume", shortcut: "Enter" }), ' ', _jsx(Kbd, { name: "Search", shortcut: "/" }), ' ', _jsx(Kbd, { name: "Delete", shortcut: "x" }), ' ', _jsx(Kbd, { name: "Quit", shortcut: "q" })] }), _jsxs(Text, { color: Colors.Gray, children: [_jsx(Kbd, { name: "Sort", shortcut: "s" }), ' ', _jsx(Kbd, { name: "Reverse", shortcut: "r" }), ' ', _jsx(Kbd, { name: "First/Last", shortcut: "g/G" })] })] }));
|
|
132
|
+
/**
|
|
133
|
+
* Table header component with column labels and scroll indicators.
|
|
134
|
+
*/
|
|
135
|
+
const SessionTableHeader = ({ state, }) => (_jsxs(Box, { flexDirection: "row", marginTop: 1, children: [_jsx(Text, { children: state.scrollOffset > 0 ? _jsx(Text, { children: "\u25B2 " }) : ' ' }), _jsx(Box, { width: 5, flexShrink: 0, children: _jsx(Text, { color: Colors.Gray, bold: true, children: "Index" }) }), _jsx(Text, { color: Colors.Gray, children: " \u2502 " }), _jsx(Box, { width: 4, flexShrink: 0, children: _jsx(Text, { color: Colors.Gray, bold: true, children: "Msgs" }) }), _jsx(Text, { color: Colors.Gray, children: " \u2502 " }), _jsx(Box, { width: 4, flexShrink: 0, children: _jsx(Text, { color: Colors.Gray, bold: true, children: "Age" }) }), _jsx(Text, { color: Colors.Gray, children: " \u2502 " }), _jsx(Box, { flexShrink: 0, children: _jsx(Text, { color: Colors.Gray, bold: true, children: state.searchQuery ? 'Match' : 'Name' }) })] }));
|
|
136
|
+
/**
|
|
137
|
+
* No results display component for empty search results.
|
|
138
|
+
*/
|
|
139
|
+
const NoResultsDisplay = ({ state, }) => (_jsx(Box, { marginTop: 1, children: _jsxs(Text, { color: Colors.Gray, dimColor: true, children: ["No sessions found matching '", state.searchQuery, "'."] }) }));
|
|
140
|
+
/**
|
|
141
|
+
* Match snippet display component for search results.
|
|
142
|
+
*/
|
|
143
|
+
const MatchSnippetDisplay = ({ session, textColor, }) => {
|
|
144
|
+
if (!session.matchSnippets || session.matchSnippets.length === 0) {
|
|
145
|
+
return null;
|
|
146
|
+
}
|
|
147
|
+
const firstMatch = session.matchSnippets[0];
|
|
148
|
+
const rolePrefix = firstMatch.role === 'user' ? 'You: ' : 'Gemini:';
|
|
149
|
+
const roleColor = textColor(firstMatch.role === 'user' ? Colors.AccentGreen : Colors.AccentBlue);
|
|
150
|
+
return (_jsxs(_Fragment, { children: [_jsxs(Text, { color: roleColor, bold: true, children: [rolePrefix, ' '] }), firstMatch.before, _jsx(Text, { color: textColor(Colors.AccentRed), bold: true, children: firstMatch.match }), firstMatch.after] }));
|
|
151
|
+
};
|
|
152
|
+
/**
|
|
153
|
+
* Individual session row component.
|
|
154
|
+
*/
|
|
155
|
+
const SessionItem = ({ session, state, terminalWidth, formatRelativeTime, }) => {
|
|
156
|
+
const originalIndex = state.startIndex + state.visibleSessions.indexOf(session);
|
|
157
|
+
const isActive = originalIndex === state.activeIndex;
|
|
158
|
+
const isDisabled = session.isCurrentSession;
|
|
159
|
+
const textColor = (c = Colors.Foreground) => {
|
|
160
|
+
if (isDisabled) {
|
|
161
|
+
return Colors.Gray;
|
|
162
|
+
}
|
|
163
|
+
return isActive ? Colors.AccentPurple : c;
|
|
164
|
+
};
|
|
165
|
+
const prefix = isActive ? '❯ ' : ' ';
|
|
166
|
+
let additionalInfo = '';
|
|
167
|
+
let matchDisplay = null;
|
|
168
|
+
// Add "(current)" label for the current session
|
|
169
|
+
if (session.isCurrentSession) {
|
|
170
|
+
additionalInfo = ' (current)';
|
|
171
|
+
}
|
|
172
|
+
// Show match snippets if searching and matches exist
|
|
173
|
+
if (state.searchQuery &&
|
|
174
|
+
session.matchSnippets &&
|
|
175
|
+
session.matchSnippets.length > 0) {
|
|
176
|
+
matchDisplay = (_jsx(MatchSnippetDisplay, { session: session, textColor: textColor }));
|
|
177
|
+
if (session.matchCount && session.matchCount > 1) {
|
|
178
|
+
additionalInfo += ` (+${session.matchCount - 1} more)`;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
const availableMessageWidth = Math.max(20, terminalWidth - FIXED_SESSION_COLUMNS_WIDTH);
|
|
182
|
+
const truncatedMessage = matchDisplay ||
|
|
183
|
+
(session.displayName.length === 0 ? (_jsx(Text, { color: textColor(Colors.Gray), dimColor: true, children: "(No messages)" })) : session.displayName.length > availableMessageWidth ? (session.displayName.slice(0, availableMessageWidth - 1) + '…') : (session.displayName));
|
|
184
|
+
return (_jsxs(Box, { flexDirection: "row", children: [_jsx(Text, { color: textColor(), dimColor: isDisabled, children: prefix }), _jsx(Box, { width: 5, children: _jsxs(Text, { color: textColor(), dimColor: isDisabled, children: ["#", originalIndex + 1] }) }), _jsxs(Text, { color: textColor(Colors.Gray), dimColor: isDisabled, children: [' ', "\u2502", ' '] }), _jsx(Box, { width: 4, children: _jsx(Text, { color: textColor(), dimColor: isDisabled, children: session.messageCount }) }), _jsxs(Text, { color: textColor(Colors.Gray), dimColor: isDisabled, children: [' ', "\u2502", ' '] }), _jsx(Box, { width: 4, children: _jsx(Text, { color: textColor(), dimColor: isDisabled, children: formatRelativeTime(session.lastUpdated, 'short') }) }), _jsxs(Text, { color: textColor(Colors.Gray), dimColor: isDisabled, children: [' ', "\u2502", ' '] }), _jsx(Box, { flexGrow: 1, children: _jsxs(Text, { color: textColor(Colors.Comment), dimColor: isDisabled, children: [truncatedMessage, additionalInfo && (_jsx(Text, { color: textColor(Colors.Gray), dimColor: true, bold: false, children: additionalInfo }))] }) })] }));
|
|
185
|
+
};
|
|
186
|
+
/**
|
|
187
|
+
* Session list container component.
|
|
188
|
+
*/
|
|
189
|
+
const SessionList = ({ state, formatRelativeTime, }) => (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Box, { flexDirection: "column", children: [!state.isSearchMode && _jsx(NavigationHelp, {}), _jsx(SessionTableHeader, { state: state })] }), state.visibleSessions.map((session) => (_jsx(SessionItem, { session: session, state: state, terminalWidth: state.terminalWidth, formatRelativeTime: formatRelativeTime }, session.id))), _jsx(Text, { color: Colors.Gray, children: state.endIndex < state.totalSessions ? _jsx(_Fragment, { children: "\u25BC" }) : _jsx(Text, { dimColor: true, children: "\u25BC" }) })] }));
|
|
190
|
+
/**
|
|
191
|
+
* Hook to manage all SessionBrowser state.
|
|
192
|
+
*/
|
|
193
|
+
export const useSessionBrowserState = (initialSessions = [], initialLoading = true, initialError = null) => {
|
|
194
|
+
const { columns: terminalWidth } = useTerminalSize();
|
|
195
|
+
const [sessions, setSessions] = useState(initialSessions);
|
|
196
|
+
const [loading, setLoading] = useState(initialLoading);
|
|
197
|
+
const [error, setError] = useState(initialError);
|
|
198
|
+
const [activeIndex, setActiveIndex] = useState(0);
|
|
199
|
+
const [scrollOffset, setScrollOffset] = useState(0);
|
|
200
|
+
const [sortOrder, setSortOrder] = useState('date');
|
|
201
|
+
const [sortReverse, setSortReverse] = useState(false);
|
|
202
|
+
const [searchQuery, setSearchQuery] = useState('');
|
|
203
|
+
const [isSearchMode, setIsSearchMode] = useState(false);
|
|
204
|
+
const [hasLoadedFullContent, setHasLoadedFullContent] = useState(false);
|
|
205
|
+
const loadingFullContentRef = useRef(false);
|
|
206
|
+
const filteredAndSortedSessions = useMemo(() => {
|
|
207
|
+
const filtered = filterSessions(sessions, searchQuery);
|
|
208
|
+
return sortSessions(filtered, sortOrder, sortReverse);
|
|
209
|
+
}, [sessions, searchQuery, sortOrder, sortReverse]);
|
|
210
|
+
// Reset full content flag when search is cleared
|
|
211
|
+
useEffect(() => {
|
|
212
|
+
if (!searchQuery) {
|
|
213
|
+
setHasLoadedFullContent(false);
|
|
214
|
+
loadingFullContentRef.current = false;
|
|
215
|
+
}
|
|
216
|
+
}, [searchQuery]);
|
|
217
|
+
const totalSessions = filteredAndSortedSessions.length;
|
|
218
|
+
const startIndex = scrollOffset;
|
|
219
|
+
const endIndex = Math.min(scrollOffset + SESSIONS_PER_PAGE, totalSessions);
|
|
220
|
+
const visibleSessions = filteredAndSortedSessions.slice(startIndex, endIndex);
|
|
221
|
+
const state = {
|
|
222
|
+
sessions,
|
|
223
|
+
setSessions,
|
|
224
|
+
loading,
|
|
225
|
+
setLoading,
|
|
226
|
+
error,
|
|
227
|
+
setError,
|
|
228
|
+
activeIndex,
|
|
229
|
+
setActiveIndex,
|
|
230
|
+
scrollOffset,
|
|
231
|
+
setScrollOffset,
|
|
232
|
+
searchQuery,
|
|
233
|
+
setSearchQuery,
|
|
234
|
+
isSearchMode,
|
|
235
|
+
setIsSearchMode,
|
|
236
|
+
hasLoadedFullContent,
|
|
237
|
+
setHasLoadedFullContent,
|
|
238
|
+
sortOrder,
|
|
239
|
+
setSortOrder,
|
|
240
|
+
sortReverse,
|
|
241
|
+
setSortReverse,
|
|
242
|
+
terminalWidth,
|
|
243
|
+
filteredAndSortedSessions,
|
|
244
|
+
totalSessions,
|
|
245
|
+
startIndex,
|
|
246
|
+
endIndex,
|
|
247
|
+
visibleSessions,
|
|
248
|
+
};
|
|
249
|
+
return state;
|
|
250
|
+
};
|
|
251
|
+
/**
|
|
252
|
+
* Hook to load sessions on mount.
|
|
253
|
+
*/
|
|
254
|
+
const useLoadSessions = (config, state) => {
|
|
255
|
+
const { setSessions, setLoading, setError, isSearchMode, hasLoadedFullContent, setHasLoadedFullContent, } = state;
|
|
256
|
+
useEffect(() => {
|
|
257
|
+
const loadSessions = async () => {
|
|
258
|
+
try {
|
|
259
|
+
const chatsDir = path.join(config.storage.getProjectTempDir(), 'chats');
|
|
260
|
+
const sessionData = await getSessionFiles(chatsDir, config.getSessionId());
|
|
261
|
+
setSessions(sessionData);
|
|
262
|
+
setLoading(false);
|
|
263
|
+
}
|
|
264
|
+
catch (err) {
|
|
265
|
+
setError(err instanceof Error ? err.message : 'Failed to load sessions');
|
|
266
|
+
setLoading(false);
|
|
267
|
+
}
|
|
268
|
+
};
|
|
269
|
+
loadSessions();
|
|
270
|
+
}, [config, setSessions, setLoading, setError]);
|
|
271
|
+
useEffect(() => {
|
|
272
|
+
const loadFullContent = async () => {
|
|
273
|
+
if (isSearchMode && !hasLoadedFullContent) {
|
|
274
|
+
try {
|
|
275
|
+
const chatsDir = path.join(config.storage.getProjectTempDir(), 'chats');
|
|
276
|
+
const sessionData = await getSessionFiles(chatsDir, config.getSessionId(), { includeFullContent: true });
|
|
277
|
+
setSessions(sessionData);
|
|
278
|
+
setHasLoadedFullContent(true);
|
|
279
|
+
}
|
|
280
|
+
catch (err) {
|
|
281
|
+
setError(err instanceof Error
|
|
282
|
+
? err.message
|
|
283
|
+
: 'Failed to load full session content');
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
};
|
|
287
|
+
loadFullContent();
|
|
288
|
+
}, [
|
|
289
|
+
isSearchMode,
|
|
290
|
+
hasLoadedFullContent,
|
|
291
|
+
config,
|
|
292
|
+
setSessions,
|
|
293
|
+
setHasLoadedFullContent,
|
|
294
|
+
setError,
|
|
295
|
+
]);
|
|
296
|
+
};
|
|
297
|
+
/**
|
|
298
|
+
* Hook to handle selection movement.
|
|
299
|
+
*/
|
|
300
|
+
export const useMoveSelection = (state) => {
|
|
301
|
+
const { totalSessions, activeIndex, scrollOffset, setActiveIndex, setScrollOffset, } = state;
|
|
302
|
+
return useCallback((delta) => {
|
|
303
|
+
const newIndex = Math.max(0, Math.min(totalSessions - 1, activeIndex + delta));
|
|
304
|
+
setActiveIndex(newIndex);
|
|
305
|
+
// Adjust scroll offset if needed
|
|
306
|
+
if (newIndex < scrollOffset) {
|
|
307
|
+
setScrollOffset(newIndex);
|
|
308
|
+
}
|
|
309
|
+
else if (newIndex >= scrollOffset + SESSIONS_PER_PAGE) {
|
|
310
|
+
setScrollOffset(newIndex - SESSIONS_PER_PAGE + 1);
|
|
311
|
+
}
|
|
312
|
+
}, [totalSessions, activeIndex, scrollOffset, setActiveIndex, setScrollOffset]);
|
|
313
|
+
};
|
|
314
|
+
/**
|
|
315
|
+
* Hook to handle sort order cycling.
|
|
316
|
+
*/
|
|
317
|
+
export const useCycleSortOrder = (state) => {
|
|
318
|
+
const { sortOrder, setSortOrder } = state;
|
|
319
|
+
return useCallback(() => {
|
|
320
|
+
const orders = [
|
|
321
|
+
'date',
|
|
322
|
+
'messages',
|
|
323
|
+
'name',
|
|
324
|
+
];
|
|
325
|
+
const currentIndex = orders.indexOf(sortOrder);
|
|
326
|
+
const nextIndex = (currentIndex + 1) % orders.length;
|
|
327
|
+
setSortOrder(orders[nextIndex]);
|
|
328
|
+
}, [sortOrder, setSortOrder]);
|
|
329
|
+
};
|
|
330
|
+
/**
|
|
331
|
+
* Hook to handle SessionBrowser input.
|
|
332
|
+
*/
|
|
333
|
+
export const useSessionBrowserInput = (state, moveSelection, cycleSortOrder, onResumeSession, onDeleteSession, onExit) => {
|
|
334
|
+
useKeypress((key) => {
|
|
335
|
+
if (state.isSearchMode) {
|
|
336
|
+
// Search-specific input handling. Only control/symbols here.
|
|
337
|
+
if (key.name === 'escape') {
|
|
338
|
+
state.setIsSearchMode(false);
|
|
339
|
+
state.setSearchQuery('');
|
|
340
|
+
state.setActiveIndex(0);
|
|
341
|
+
state.setScrollOffset(0);
|
|
342
|
+
}
|
|
343
|
+
else if (key.name === 'backspace') {
|
|
344
|
+
state.setSearchQuery((prev) => prev.slice(0, -1));
|
|
345
|
+
state.setActiveIndex(0);
|
|
346
|
+
state.setScrollOffset(0);
|
|
347
|
+
}
|
|
348
|
+
else if (key.sequence &&
|
|
349
|
+
!key.ctrl &&
|
|
350
|
+
!key.meta &&
|
|
351
|
+
key.sequence.length === 1) {
|
|
352
|
+
state.setSearchQuery((prev) => prev + key.sequence);
|
|
353
|
+
state.setActiveIndex(0);
|
|
354
|
+
state.setScrollOffset(0);
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
else {
|
|
358
|
+
// Navigation mode input handling. We're keeping the letter-based controls for non-search
|
|
359
|
+
// mode only, because the letters need to act as input for the search.
|
|
360
|
+
if (key.sequence === 'g') {
|
|
361
|
+
state.setActiveIndex(0);
|
|
362
|
+
state.setScrollOffset(0);
|
|
363
|
+
}
|
|
364
|
+
else if (key.sequence === 'G') {
|
|
365
|
+
state.setActiveIndex(state.totalSessions - 1);
|
|
366
|
+
state.setScrollOffset(Math.max(0, state.totalSessions - SESSIONS_PER_PAGE));
|
|
367
|
+
}
|
|
368
|
+
// Sorting controls.
|
|
369
|
+
else if (key.sequence === 's') {
|
|
370
|
+
cycleSortOrder();
|
|
371
|
+
}
|
|
372
|
+
else if (key.sequence === 'r') {
|
|
373
|
+
state.setSortReverse(!state.sortReverse);
|
|
374
|
+
}
|
|
375
|
+
// Searching and exit controls.
|
|
376
|
+
else if (key.sequence === '/') {
|
|
377
|
+
state.setIsSearchMode(true);
|
|
378
|
+
}
|
|
379
|
+
else if (key.sequence === 'q' ||
|
|
380
|
+
key.sequence === 'Q' ||
|
|
381
|
+
key.name === 'escape') {
|
|
382
|
+
onExit();
|
|
383
|
+
}
|
|
384
|
+
// Delete session control.
|
|
385
|
+
else if (key.sequence === 'x' || key.sequence === 'X') {
|
|
386
|
+
const selectedSession = state.filteredAndSortedSessions[state.activeIndex];
|
|
387
|
+
if (selectedSession &&
|
|
388
|
+
!selectedSession.isCurrentSession &&
|
|
389
|
+
onDeleteSession) {
|
|
390
|
+
try {
|
|
391
|
+
onDeleteSession(selectedSession);
|
|
392
|
+
// Remove the session from the state
|
|
393
|
+
state.setSessions(state.sessions.filter((s) => s.id !== selectedSession.id));
|
|
394
|
+
// Adjust active index if needed
|
|
395
|
+
if (state.activeIndex >=
|
|
396
|
+
state.filteredAndSortedSessions.length - 1) {
|
|
397
|
+
state.setActiveIndex(Math.max(0, state.filteredAndSortedSessions.length - 2));
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
catch (error) {
|
|
401
|
+
state.setError(`Failed to delete session: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
// less-like u/d controls.
|
|
406
|
+
else if (key.sequence === 'd') {
|
|
407
|
+
moveSelection(-Math.round(SESSIONS_PER_PAGE / 2));
|
|
408
|
+
}
|
|
409
|
+
else if (key.sequence === 'u') {
|
|
410
|
+
moveSelection(Math.round(SESSIONS_PER_PAGE / 2));
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
// Handling regardless of search mode.
|
|
414
|
+
if (key.name === 'return' &&
|
|
415
|
+
state.filteredAndSortedSessions[state.activeIndex]) {
|
|
416
|
+
const selectedSession = state.filteredAndSortedSessions[state.activeIndex];
|
|
417
|
+
// Don't allow resuming the current session
|
|
418
|
+
if (!selectedSession.isCurrentSession) {
|
|
419
|
+
onResumeSession(selectedSession);
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
else if (key.name === 'up') {
|
|
423
|
+
moveSelection(-1);
|
|
424
|
+
}
|
|
425
|
+
else if (key.name === 'down') {
|
|
426
|
+
moveSelection(1);
|
|
427
|
+
}
|
|
428
|
+
else if (key.name === 'pageup') {
|
|
429
|
+
moveSelection(-SESSIONS_PER_PAGE);
|
|
430
|
+
}
|
|
431
|
+
else if (key.name === 'pagedown') {
|
|
432
|
+
moveSelection(SESSIONS_PER_PAGE);
|
|
433
|
+
}
|
|
434
|
+
}, { isActive: true });
|
|
435
|
+
};
|
|
436
|
+
export function SessionBrowserView({ state, }) {
|
|
437
|
+
if (state.loading) {
|
|
438
|
+
return _jsx(SessionBrowserLoading, {});
|
|
439
|
+
}
|
|
440
|
+
if (state.error) {
|
|
441
|
+
return _jsx(SessionBrowserError, { state: state });
|
|
442
|
+
}
|
|
443
|
+
if (state.sessions.length === 0) {
|
|
444
|
+
return _jsx(SessionBrowserEmpty, {});
|
|
445
|
+
}
|
|
446
|
+
return (_jsxs(Box, { flexDirection: "column", paddingX: 1, children: [_jsx(SessionListHeader, { state: state }), state.isSearchMode && _jsx(SearchModeDisplay, { state: state }), state.totalSessions === 0 ? (_jsx(NoResultsDisplay, { state: state })) : (_jsx(SessionList, { state: state, formatRelativeTime: formatRelativeTime }))] }));
|
|
447
|
+
}
|
|
448
|
+
export function SessionBrowser({ config, onResumeSession, onDeleteSession, onExit, }) {
|
|
449
|
+
// Use all our custom hooks
|
|
450
|
+
const state = useSessionBrowserState();
|
|
451
|
+
useLoadSessions(config, state);
|
|
452
|
+
const moveSelection = useMoveSelection(state);
|
|
453
|
+
const cycleSortOrder = useCycleSortOrder(state);
|
|
454
|
+
useSessionBrowserInput(state, moveSelection, cycleSortOrder, onResumeSession, onDeleteSession, onExit);
|
|
455
|
+
return _jsx(SessionBrowserView, { state: state });
|
|
456
|
+
}
|
|
457
|
+
//# sourceMappingURL=SessionBrowser.js.map
|