From ed9a4243c0bb53fb9e3629f128b6a04cc27fbf11 Mon Sep 17 00:00:00 2001 From: zhangmo8 Date: Fri, 17 Jul 2026 17:59:26 +0800 Subject: [PATCH 01/12] refactor(renderer): establish app boundaries --- .github/workflows/prcheck.yml | 3 + AGENTS.md | 2 +- ...derer-application-boundaries-baseline.json | 727 ++++++++++++++++++ .../renderer-application-boundaries/plan.md | 83 ++ .../renderer-application-boundaries/spec.md | 96 +++ .../renderer-application-boundaries/tasks.md | 24 + electron.vite.config.ts | 1 - package.json | 2 + ...enerate-renderer-architecture-baseline.mjs | 132 ++++ src/renderer/browser/assets/ChromeClose.svg | 3 - .../browser/assets/ChromeMaximize-1.svg | 3 - .../browser/assets/ChromeMaximize.svg | 3 - .../browser/assets/ChromeMinimize.svg | 3 - src/renderer/src/App.vue | 620 +-------------- .../src/apps/chat-main/ChatMainApp.vue | 616 +++++++++++++++ src/renderer/src/views/ChatTabView.vue | 11 +- test/renderer/components/App.startup.test.ts | 6 + test/renderer/components/ChatTabView.test.ts | 32 +- test/renderer/stores/sessionStore.test.ts | 17 + tsconfig.app.json | 10 +- tsconfig.app.tsgo.json | 10 +- vitest.config.renderer.ts | 1 - vitest.config.ts | 1 - 23 files changed, 1750 insertions(+), 656 deletions(-) create mode 100644 docs/architecture/baselines/renderer-application-boundaries-baseline.json create mode 100644 docs/architecture/renderer-application-boundaries/plan.md create mode 100644 docs/architecture/renderer-application-boundaries/spec.md create mode 100644 docs/architecture/renderer-application-boundaries/tasks.md create mode 100644 scripts/generate-renderer-architecture-baseline.mjs delete mode 100644 src/renderer/browser/assets/ChromeClose.svg delete mode 100644 src/renderer/browser/assets/ChromeMaximize-1.svg delete mode 100644 src/renderer/browser/assets/ChromeMaximize.svg delete mode 100644 src/renderer/browser/assets/ChromeMinimize.svg create mode 100644 src/renderer/src/apps/chat-main/ChatMainApp.vue diff --git a/.github/workflows/prcheck.yml b/.github/workflows/prcheck.yml index 6f88f91c86..b77d37ad52 100644 --- a/.github/workflows/prcheck.yml +++ b/.github/workflows/prcheck.yml @@ -74,6 +74,9 @@ jobs: - name: format:check run: pnpm run format:check + - name: Check renderer architecture baseline + run: pnpm run architecture:renderer-baseline:check + - name: Configure pnpm workspace for Linux ${{ matrix.arch }} run: pnpm run install:sharp env: diff --git a/AGENTS.md b/AGENTS.md index 391710c8e6..035031b1ee 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -3,7 +3,7 @@ ## Project Structure & Module Organization - `src/main/`: Electron main process; presenters in `presenter/` (Window/Tab/Thread/Mcp/Config/LLMProvider), `eventbus.ts` for app events. - `src/preload/`: Secure IPC bridge (contextIsolation on). -- `src/renderer/`: Vue 3 app. App code in `src/renderer/src` (`components/`, `stores`, `views`, `i18n`, `lib`). Secondary renderers live in `src/renderer/browser`, `src/renderer/settings`, `src/renderer/floating`, and `src/renderer/splash`. +- `src/renderer/`: Vue 3 app. Main-window composition lives in `src/renderer/src/apps/chat-main`; shared main renderer code remains in `components/`, `stores`, `views`, `i18n`, and `lib`. Secondary renderers live in `src/renderer/browser-overlay`, `src/renderer/settings`, `src/renderer/floating`, and `src/renderer/splash`. - `src/shared/`: Shared TS types/utilities. - `test/`: Vitest suites (`test/main`, `test/renderer`) with setup files. - `scripts/`: Build/signing/runtime installers, commit checks. diff --git a/docs/architecture/baselines/renderer-application-boundaries-baseline.json b/docs/architecture/baselines/renderer-application-boundaries-baseline.json new file mode 100644 index 0000000000..3e5cfcd4df --- /dev/null +++ b/docs/architecture/baselines/renderer-application-boundaries-baseline.json @@ -0,0 +1,727 @@ +{ + "schemaVersion": 1, + "apps": [ + { + "id": "chat-main", + "html": "src/renderer/index.html", + "entry": "src/renderer/src/main.ts", + "htmlExists": true, + "entryExists": true + }, + { + "id": "browser-overlay", + "html": "src/renderer/browser-overlay/index.html", + "entry": "src/renderer/browser-overlay/main.ts", + "htmlExists": true, + "entryExists": true + }, + { + "id": "floating", + "html": "src/renderer/floating/index.html", + "entry": "src/renderer/floating/main.ts", + "htmlExists": true, + "entryExists": true + }, + { + "id": "splash", + "html": "src/renderer/splash/index.html", + "entry": "src/renderer/splash/main.ts", + "htmlExists": true, + "entryExists": true + }, + { + "id": "settings", + "html": "src/renderer/settings/index.html", + "entry": "src/renderer/settings/main.ts", + "htmlExists": true, + "entryExists": true + } + ], + "browser": { + "legacyDirectoryExists": false, + "activeOverlayDirectory": "src/renderer/browser-overlay" + }, + "settingsToChatAppImports": [ + { + "file": "src/renderer/settings/App.vue", + "specifier": "../src/composables/useDeviceVersion" + }, + { + "file": "src/renderer/settings/App.vue", + "specifier": "../src/composables/useFontManager" + }, + { + "file": "src/renderer/settings/App.vue", + "specifier": "../src/lib/iconLoader" + }, + { + "file": "src/renderer/settings/App.vue", + "specifier": "../src/lib/startupDeferred" + }, + { + "file": "src/renderer/settings/App.vue", + "specifier": "../src/lib/storeInitializer" + }, + { + "file": "src/renderer/settings/App.vue", + "specifier": "../src/stores/language" + }, + { + "file": "src/renderer/settings/App.vue", + "specifier": "../src/stores/modelCheck" + }, + { + "file": "src/renderer/settings/App.vue", + "specifier": "../src/stores/uiSettingsStore" + }, + { + "file": "src/renderer/settings/App.vue", + "specifier": "@/components/settings/ModelCheckDialog.vue" + }, + { + "file": "src/renderer/settings/App.vue", + "specifier": "@/components/use-toast" + }, + { + "file": "src/renderer/settings/App.vue", + "specifier": "@/stores/modelStore" + }, + { + "file": "src/renderer/settings/App.vue", + "specifier": "@/stores/ollamaStore" + }, + { + "file": "src/renderer/settings/App.vue", + "specifier": "@/stores/providerDeeplinkImport" + }, + { + "file": "src/renderer/settings/App.vue", + "specifier": "@/stores/providerStore" + }, + { + "file": "src/renderer/settings/App.vue", + "specifier": "@/stores/startupWorkloadStore" + }, + { + "file": "src/renderer/settings/App.vue", + "specifier": "@/stores/theme" + }, + { + "file": "src/renderer/settings/components/AboutUsSettings.vue", + "specifier": "@/components/use-toast" + }, + { + "file": "src/renderer/settings/components/AboutUsSettings.vue", + "specifier": "@/stores/language" + }, + { + "file": "src/renderer/settings/components/AboutUsSettings.vue", + "specifier": "@/stores/theme" + }, + { + "file": "src/renderer/settings/components/AboutUsSettings.vue", + "specifier": "@/stores/upgrade" + }, + { + "file": "src/renderer/settings/components/AcpDebugDialog.vue", + "specifier": "@/components/use-toast" + }, + { + "file": "src/renderer/settings/components/AcpDebugDialog.vue", + "specifier": "@/stores/uiSettingsStore" + }, + { + "file": "src/renderer/settings/components/AcpDependencyDialog.vue", + "specifier": "@/components/use-toast" + }, + { + "file": "src/renderer/settings/components/AcpProfileDialog.vue", + "specifier": "@/components/use-toast" + }, + { + "file": "src/renderer/settings/components/AcpSettings.vue", + "specifier": "@/components/agent/AgentTransferDialog.vue" + }, + { + "file": "src/renderer/settings/components/AcpSettings.vue", + "specifier": "@/components/icons/AcpAgentIcon.vue" + }, + { + "file": "src/renderer/settings/components/AcpSettings.vue", + "specifier": "@/components/mcp-config/AgentMcpSelector.vue" + }, + { + "file": "src/renderer/settings/components/AcpSettings.vue", + "specifier": "@/components/use-toast" + }, + { + "file": "src/renderer/settings/components/AcpTerminalDialog.vue", + "specifier": "@/components/use-toast" + }, + { + "file": "src/renderer/settings/components/AddCustomModelButton.vue", + "specifier": "@/components/settings/ModelConfigDialog.vue" + }, + { + "file": "src/renderer/settings/components/AddCustomProviderDialog.vue", + "specifier": "@/stores/providerStore" + }, + { + "file": "src/renderer/settings/components/BedrockProviderSettingsDetail.vue", + "specifier": "@/stores/modelStore" + }, + { + "file": "src/renderer/settings/components/BedrockProviderSettingsDetail.vue", + "specifier": "@/stores/providerStore" + }, + { + "file": "src/renderer/settings/components/BuiltinKnowledgeSettings.vue", + "specifier": "@/components/icons/ModelIcon.vue" + }, + { + "file": "src/renderer/settings/components/BuiltinKnowledgeSettings.vue", + "specifier": "@/components/ModelSelect.vue" + }, + { + "file": "src/renderer/settings/components/BuiltinKnowledgeSettings.vue", + "specifier": "@/components/use-toast" + }, + { + "file": "src/renderer/settings/components/BuiltinKnowledgeSettings.vue", + "specifier": "@/stores/mcp" + }, + { + "file": "src/renderer/settings/components/BuiltinKnowledgeSettings.vue", + "specifier": "@/stores/modelStore" + }, + { + "file": "src/renderer/settings/components/BuiltinKnowledgeSettings.vue", + "specifier": "@/stores/theme" + }, + { + "file": "src/renderer/settings/components/common/AutoCompactionSettingsSection.vue", + "specifier": "@/stores/uiSettingsStore" + }, + { + "file": "src/renderer/settings/components/common/DefaultModelSettingsSection.vue", + "specifier": "@/components/icons/ModelIcon.vue" + }, + { + "file": "src/renderer/settings/components/common/DefaultModelSettingsSection.vue", + "specifier": "@/components/ModelSelect.vue" + }, + { + "file": "src/renderer/settings/components/common/DefaultModelSettingsSection.vue", + "specifier": "@/stores/modelStore" + }, + { + "file": "src/renderer/settings/components/common/DefaultModelSettingsSection.vue", + "specifier": "@/stores/theme" + }, + { + "file": "src/renderer/settings/components/common/LoggingSettingsSection.vue", + "specifier": "@/stores/language" + }, + { + "file": "src/renderer/settings/components/common/LoggingSettingsSection.vue", + "specifier": "@/stores/uiSettingsStore" + }, + { + "file": "src/renderer/settings/components/common/PrivacySettingsSection.vue", + "specifier": "@/components/use-toast" + }, + { + "file": "src/renderer/settings/components/common/PrivacySettingsSection.vue", + "specifier": "@/stores/uiSettingsStore" + }, + { + "file": "src/renderer/settings/components/common/ProxySettingsSection.vue", + "specifier": "@/stores/language" + }, + { + "file": "src/renderer/settings/components/common/SettingToggleRow.vue", + "specifier": "@/stores/language" + }, + { + "file": "src/renderer/settings/components/CommonSettings.vue", + "specifier": "@/stores/uiSettingsStore" + }, + { + "file": "src/renderer/settings/components/CronJobsSettings.vue", + "specifier": "@/components/use-toast" + }, + { + "file": "src/renderer/settings/components/DataSettings.vue", + "specifier": "@/components/use-toast" + }, + { + "file": "src/renderer/settings/components/DataSettings.vue", + "specifier": "@/lib/cloudSyncForm" + }, + { + "file": "src/renderer/settings/components/DataSettings.vue", + "specifier": "@/lib/utils" + }, + { + "file": "src/renderer/settings/components/DataSettings.vue", + "specifier": "@/stores/language" + }, + { + "file": "src/renderer/settings/components/DataSettings.vue", + "specifier": "@/stores/sync" + }, + { + "file": "src/renderer/settings/components/DeepChatAgentsSettings.vue", + "specifier": "@/components/agent/AgentTransferDialog.vue" + }, + { + "file": "src/renderer/settings/components/DeepChatAgentsSettings.vue", + "specifier": "@/components/icons/AgentAvatar.vue" + }, + { + "file": "src/renderer/settings/components/DeepChatAgentsSettings.vue", + "specifier": "@/components/icons/ModelIcon.vue" + }, + { + "file": "src/renderer/settings/components/DeepChatAgentsSettings.vue", + "specifier": "@/components/ModelSelect.vue" + }, + { + "file": "src/renderer/settings/components/DeepChatAgentsSettings.vue", + "specifier": "@/components/use-toast" + }, + { + "file": "src/renderer/settings/components/DeepChatAgentsSettings.vue", + "specifier": "@/stores/modelStore" + }, + { + "file": "src/renderer/settings/components/DifyKnowledgeSettings.vue", + "specifier": "@/components/use-toast" + }, + { + "file": "src/renderer/settings/components/DifyKnowledgeSettings.vue", + "specifier": "@/stores/mcp" + }, + { + "file": "src/renderer/settings/components/display/FontSettingsSection.vue", + "specifier": "@/stores/language" + }, + { + "file": "src/renderer/settings/components/display/FontSettingsSection.vue", + "specifier": "@/stores/uiSettingsStore" + }, + { + "file": "src/renderer/settings/components/DisplaySettings.vue", + "specifier": "@/stores/floatingButton" + }, + { + "file": "src/renderer/settings/components/DisplaySettings.vue", + "specifier": "@/stores/language" + }, + { + "file": "src/renderer/settings/components/DisplaySettings.vue", + "specifier": "@/stores/theme" + }, + { + "file": "src/renderer/settings/components/DisplaySettings.vue", + "specifier": "@/stores/uiSettingsStore" + }, + { + "file": "src/renderer/settings/components/EnvironmentsSettings.vue", + "specifier": "@/components/use-toast" + }, + { + "file": "src/renderer/settings/components/EnvironmentsSettings.vue", + "specifier": "@/stores/ui/project" + }, + { + "file": "src/renderer/settings/components/FastGptKnowledgeSettings.vue", + "specifier": "@/components/use-toast" + }, + { + "file": "src/renderer/settings/components/FastGptKnowledgeSettings.vue", + "specifier": "@/stores/mcp" + }, + { + "file": "src/renderer/settings/components/GeminiSafetyConfig.vue", + "specifier": "@/lib/gemini" + }, + { + "file": "src/renderer/settings/components/GitHubCopilotOAuth.vue", + "specifier": "@/stores/modelCheck" + }, + { + "file": "src/renderer/settings/components/GitHubCopilotOAuth.vue", + "specifier": "@/stores/providerStore" + }, + { + "file": "src/renderer/settings/components/GrokOAuth.vue", + "specifier": "@/stores/modelCheck" + }, + { + "file": "src/renderer/settings/components/KnowledgeFile.vue", + "specifier": "@/components/use-toast" + }, + { + "file": "src/renderer/settings/components/KnowledgeFileItem.vue", + "specifier": "@/lib/utils" + }, + { + "file": "src/renderer/settings/components/McpBuiltinMarket.vue", + "specifier": "@/components/use-toast" + }, + { + "file": "src/renderer/settings/components/McpSettings.vue", + "specifier": "@/components/mcp-config/components/McpServers.vue" + }, + { + "file": "src/renderer/settings/components/McpSettings.vue", + "specifier": "@/components/onboarding/GuidedOnboardingOverlay.vue" + }, + { + "file": "src/renderer/settings/components/McpSettings.vue", + "specifier": "@/components/use-toast" + }, + { + "file": "src/renderer/settings/components/McpSettings.vue", + "specifier": "@/composables/useGuidedOnboardingStep" + }, + { + "file": "src/renderer/settings/components/McpSettings.vue", + "specifier": "@/stores/language" + }, + { + "file": "src/renderer/settings/components/McpSettings.vue", + "specifier": "@/stores/mcp" + }, + { + "file": "src/renderer/settings/components/McpSettings.vue", + "specifier": "@/stores/ui/agent" + }, + { + "file": "src/renderer/settings/components/McpSettings.vue", + "specifier": "@/stores/ui/session" + }, + { + "file": "src/renderer/settings/components/MemoryConfigInlinePanel.vue", + "specifier": "@/components/icons/ModelIcon.vue" + }, + { + "file": "src/renderer/settings/components/MemoryConfigInlinePanel.vue", + "specifier": "@/components/ModelSelect.vue" + }, + { + "file": "src/renderer/settings/components/MemoryConfigInlinePanel.vue", + "specifier": "@/components/use-toast" + }, + { + "file": "src/renderer/settings/components/MemoryConfigInlinePanel.vue", + "specifier": "@/stores/modelStore" + }, + { + "file": "src/renderer/settings/components/MemoryDiagnosticsPanel.vue", + "specifier": "@/components/use-toast" + }, + { + "file": "src/renderer/settings/components/MemoryInboxBar.vue", + "specifier": "@/components/use-toast" + }, + { + "file": "src/renderer/settings/components/MemoryInlinePanel.vue", + "specifier": "@/components/use-toast" + }, + { + "file": "src/renderer/settings/components/MemoryListView.vue", + "specifier": "@/components/use-toast" + }, + { + "file": "src/renderer/settings/components/MemoryPersonaPanel.vue", + "specifier": "@/components/use-toast" + }, + { + "file": "src/renderer/settings/components/ModelProviderSettings.vue", + "specifier": "@/components/icons/ModelIcon.vue" + }, + { + "file": "src/renderer/settings/components/ModelProviderSettings.vue", + "specifier": "@/components/onboarding/GuidedOnboardingOverlay.vue" + }, + { + "file": "src/renderer/settings/components/ModelProviderSettings.vue", + "specifier": "@/composables/useGuidedOnboardingStep" + }, + { + "file": "src/renderer/settings/components/ModelProviderSettings.vue", + "specifier": "@/stores/language" + }, + { + "file": "src/renderer/settings/components/ModelProviderSettings.vue", + "specifier": "@/stores/modelStore" + }, + { + "file": "src/renderer/settings/components/ModelProviderSettings.vue", + "specifier": "@/stores/providerStore" + }, + { + "file": "src/renderer/settings/components/ModelProviderSettings.vue", + "specifier": "@/stores/startupWorkloadStore" + }, + { + "file": "src/renderer/settings/components/ModelProviderSettings.vue", + "specifier": "@/stores/theme" + }, + { + "file": "src/renderer/settings/components/ModelProviderSettingsDetail.vue", + "specifier": "@/lib/gemini" + }, + { + "file": "src/renderer/settings/components/ModelProviderSettingsDetail.vue", + "specifier": "@/lib/gemini" + }, + { + "file": "src/renderer/settings/components/ModelProviderSettingsDetail.vue", + "specifier": "@/stores/modelCheck" + }, + { + "file": "src/renderer/settings/components/ModelProviderSettingsDetail.vue", + "specifier": "@/stores/modelStore" + }, + { + "file": "src/renderer/settings/components/ModelProviderSettingsDetail.vue", + "specifier": "@/stores/providerStore" + }, + { + "file": "src/renderer/settings/components/ModelProviderSettingsDetail.vue", + "specifier": "@/stores/uiSettingsStore" + }, + { + "file": "src/renderer/settings/components/NotificationsHooksSettings.vue", + "specifier": "@/components/use-toast" + }, + { + "file": "src/renderer/settings/components/NowledgeMemSettings.vue", + "specifier": "@/components/use-toast" + }, + { + "file": "src/renderer/settings/components/OllamaProviderSettingsDetail.vue", + "specifier": "@/components/settings/ModelConfigItem.vue" + }, + { + "file": "src/renderer/settings/components/OllamaProviderSettingsDetail.vue", + "specifier": "@/stores/modelCheck" + }, + { + "file": "src/renderer/settings/components/OllamaProviderSettingsDetail.vue", + "specifier": "@/stores/modelStore" + }, + { + "file": "src/renderer/settings/components/OllamaProviderSettingsDetail.vue", + "specifier": "@/stores/ollamaStore" + }, + { + "file": "src/renderer/settings/components/OllamaProviderSettingsDetail.vue", + "specifier": "@/stores/providerStore" + }, + { + "file": "src/renderer/settings/components/OpenAICodexOAuth.vue", + "specifier": "@/stores/modelCheck" + }, + { + "file": "src/renderer/settings/components/prompt/CustomPromptSettingsSection.vue", + "specifier": "@/components/use-toast" + }, + { + "file": "src/renderer/settings/components/prompt/CustomPromptSettingsSection.vue", + "specifier": "@/lib/download" + }, + { + "file": "src/renderer/settings/components/prompt/CustomPromptSettingsSection.vue", + "specifier": "@/stores/prompts" + }, + { + "file": "src/renderer/settings/components/prompt/PromptEditorSheet.vue", + "specifier": "@/components/use-toast" + }, + { + "file": "src/renderer/settings/components/prompt/PromptEditorSheet.vue", + "specifier": "@/lib/utils" + }, + { + "file": "src/renderer/settings/components/prompt/SystemPromptSettingsSection.vue", + "specifier": "@/components/use-toast" + }, + { + "file": "src/renderer/settings/components/prompt/SystemPromptSettingsSection.vue", + "specifier": "@/stores/systemPromptStore" + }, + { + "file": "src/renderer/settings/components/ProviderApiConfig.vue", + "specifier": "@/components/use-toast" + }, + { + "file": "src/renderer/settings/components/ProviderApiConfig.vue", + "specifier": "@/stores/modelCheck" + }, + { + "file": "src/renderer/settings/components/ProviderConfigImportDialog.vue", + "specifier": "@/lib/utils" + }, + { + "file": "src/renderer/settings/components/ProviderDeeplinkImportDialog.vue", + "specifier": "@/components/icons/ModelIcon.vue" + }, + { + "file": "src/renderer/settings/components/ProviderDeeplinkImportDialog.vue", + "specifier": "@/stores/theme" + }, + { + "file": "src/renderer/settings/components/ProviderModelList.vue", + "specifier": "@/components/settings/ModelConfigItem.vue" + }, + { + "file": "src/renderer/settings/components/ProviderModelList.vue", + "specifier": "@/stores/modelStore" + }, + { + "file": "src/renderer/settings/components/ProviderModelList.vue", + "specifier": "@/stores/uiSettingsStore" + }, + { + "file": "src/renderer/settings/components/ProviderRateLimitConfig.vue", + "specifier": "@/components/use-toast" + }, + { + "file": "src/renderer/settings/components/RagflowKnowledgeSettings.vue", + "specifier": "@/components/use-toast" + }, + { + "file": "src/renderer/settings/components/RagflowKnowledgeSettings.vue", + "specifier": "@/stores/mcp" + }, + { + "file": "src/renderer/settings/components/RemoteSettings.vue", + "specifier": "@/components/use-toast" + }, + { + "file": "src/renderer/settings/components/SettingsOverview.vue", + "specifier": "@/stores/modelStore" + }, + { + "file": "src/renderer/settings/components/SettingsOverview.vue", + "specifier": "@/stores/providerStore" + }, + { + "file": "src/renderer/settings/components/SettingsOverview.vue", + "specifier": "@/stores/sync" + }, + { + "file": "src/renderer/settings/components/SettingsOverview.vue", + "specifier": "@/stores/ui/agent" + }, + { + "file": "src/renderer/settings/components/ShortcutSettings.vue", + "specifier": "@/stores/language" + }, + { + "file": "src/renderer/settings/components/ShortcutSettings.vue", + "specifier": "@/stores/shortcutKey" + }, + { + "file": "src/renderer/settings/components/skills/InstallFromGitDialog.vue", + "specifier": "@/components/use-toast" + }, + { + "file": "src/renderer/settings/components/skills/InstallSkillToAgentDialog.vue", + "specifier": "@/components/use-toast" + }, + { + "file": "src/renderer/settings/components/skills/SkillAgentsTab.vue", + "specifier": "@/components/use-toast" + }, + { + "file": "src/renderer/settings/components/skills/SkillAgentsTab.vue", + "specifier": "@/stores/skillsStore" + }, + { + "file": "src/renderer/settings/components/skills/SkillDetailDialog.vue", + "specifier": "@/components/markdown/MarkdownRenderer.vue" + }, + { + "file": "src/renderer/settings/components/skills/SkillFolderTree.vue", + "specifier": "@/stores/skillsStore" + }, + { + "file": "src/renderer/settings/components/skills/SkillImportExportTab.vue", + "specifier": "@/components/use-toast" + }, + { + "file": "src/renderer/settings/components/skills/SkillInstallDialog.vue", + "specifier": "@/components/use-toast" + }, + { + "file": "src/renderer/settings/components/skills/SkillInstallDialog.vue", + "specifier": "@/stores/skillsStore" + }, + { + "file": "src/renderer/settings/components/skills/SkillsHeader.vue", + "specifier": "@/stores/language" + }, + { + "file": "src/renderer/settings/components/skills/SkillsSettings.vue", + "specifier": "@/components/onboarding/GuidedOnboardingOverlay.vue" + }, + { + "file": "src/renderer/settings/components/skills/SkillsSettings.vue", + "specifier": "@/components/use-toast" + }, + { + "file": "src/renderer/settings/components/skills/SkillsSettings.vue", + "specifier": "@/composables/useGuidedOnboardingStep" + }, + { + "file": "src/renderer/settings/components/skills/SkillsSettings.vue", + "specifier": "@/stores/skillsStore" + }, + { + "file": "src/renderer/settings/components/skills/SkillsSettings.vue", + "specifier": "@/stores/ui/agent" + }, + { + "file": "src/renderer/settings/components/skills/SkillsSettings.vue", + "specifier": "@/stores/ui/session" + }, + { + "file": "src/renderer/settings/components/skills/SkillSyncDialog/ExportWizard.vue", + "specifier": "@/components/use-toast" + }, + { + "file": "src/renderer/settings/components/skills/SkillSyncDialog/ExportWizard.vue", + "specifier": "@/stores/skillsStore" + }, + { + "file": "src/renderer/settings/components/skills/SkillSyncDialog/ImportWizard.vue", + "specifier": "@/components/use-toast" + }, + { + "file": "src/renderer/settings/components/VertexProviderSettingsDetail.vue", + "specifier": "@/stores/providerStore" + }, + { + "file": "src/renderer/settings/components/VoiceAIProviderConfig.vue", + "specifier": "@/stores/providerStore" + }, + { + "file": "src/renderer/settings/lib/guidedOnboardingSettings.ts", + "specifier": "@/lib/onboardingResume" + }, + { + "file": "src/renderer/settings/main.ts", + "specifier": "../src/lib/iconLoader" + }, + { + "file": "src/renderer/settings/main.ts", + "specifier": "@/i18n" + } + ], + "settingsToChatAppImportCount": 170 +} diff --git a/docs/architecture/renderer-application-boundaries/plan.md b/docs/architecture/renderer-application-boundaries/plan.md new file mode 100644 index 0000000000..e9893947fa --- /dev/null +++ b/docs/architecture/renderer-application-boundaries/plan.md @@ -0,0 +1,83 @@ +# 实施计划 + +## Phase 0:建立可验证边界与修复启动时序 + +### 1. 保持 entry 稳定,先记录而不搬迁 + +保留 [`electron.vite.config.ts`](../../../electron.vite.config.ts) 当前的 HTML entry 和每个 `main.ts`。审计 `browser` 与 `browser-overlay` 的实际消费者、alias 与 TypeScript include 后,再单独删除遗留配置或补齐覆盖范围;不能依据目录名直接重命名或移动 entry。 + +新分层目录只在需要实际迁移代码时创建到 `src/renderer/src/`:`apps/`、`features/`、`platform/`、`foundation/`。不在 Phase 0 创建空目录或大规模改 import。 + +### 2. 明确启动 owner,保留并行性 + +`App.vue` 保留 main app shell 的 listener、图标、UI settings/provider 等工作,但移除其 session prefetch。`ChatTabView.vue` 作为 bootstrap snapshot、shell hydration、route init 和 session 首屏加载的唯一 owner: + +1. 请求 bootstrap snapshot。 +2. 成功路径应用 active session/project/agent shell snapshot,再初始化 route。 +3. fallback 路径先依据现有 session state 初始化 route。 +4. 在两条路径都形成确定 shell/route 后,只调用一次 `sessionStore.fetchSessions()`;该请求不阻塞 `isReady`。 +5. agent/project/model/ollama 继续 route init 后并行运行,deferred task 继续等待其 settle。 + +这让 `fetchSessions()` 读取到 `applyBootstrapShell()` 已写入的 active session,同时保留现有首屏并行加载。 + +### 3. 测试优先 + +在改启动流程前新增/调整以下测试: + +- `App + ChatTabView` 组合只请求一次 session 首屏数据。 +- bootstrap active session 先写入,再构建 `listLightweight` 的 `prioritizeSessionId`。 +- bootstrap 成功与失败 fallback 均初始化正确 route 并进入 interactive。 +- main runtime listener 与 MCP deeplink 的注册/cleanup 保持完整;settings listener 保持 setup 注册。 +- ChatPage session keyed remount 的最近测量缓存恢复不回退。 +- settings 使用独立 Pinia/启动/readiness 的既有覆盖继续通过。 + +### 4. 可观察基线与守卫 + +扩展或新增专用 renderer architecture baseline,至少输出: + +- 五个 renderer app 的 entry、HTML entry、别名/TypeScript 覆盖状态; +- `browser` 与 `browser-overlay` 的实际关系; +- settings → `src/renderer/src` 的 direct import 清单及数量; +- 新增跨 app import 的比较基线。 + +现有 `generate-architecture-baseline.mjs` 主要面向 main-kernel migration;若直接扩展会混淆责任,优先新增 renderer 专用脚本并以 CI/lint hook 明确执行。Phase 0 先确保其可生成可审阅报告;当基线格式稳定后再把“禁止新增”作为 hard failure。 + +### 5. Phase 1:迁移 chat-main composition root + +将现有主窗口 composition implementation 从根目录 `App.vue` 移至 `src/renderer/src/apps/chat-main/ChatMainApp.vue`。根目录 `App.vue` 保留为只渲染 `ChatMainApp` 的兼容 facade;`src/renderer/src/main.ts` 与 `src/renderer/index.html` 不改变,继续作为稳定 entry shim。 + +本切片保留 `src/renderer/src/router/index.ts` 与 `src/renderer/src/views/ChatTabView.vue` 的路径、路由契约和启动职责: + +- `ChatMainApp` 继续承载主窗口的全局 chrome、runtime listener、MCP deeplink、welcome/onboarding 路由协调与 cleanup; +- `ChatTabView` 继续独占 bootstrap snapshot、shell hydration、route init、首屏 session request 与 deferred hydration; +- 不移动 ChatPage 或 feature-local 页面,不新增 IPC facade,也不改变 settings 的独立启动。 + +验证通过根 `App.vue` 的既有 startup suite 覆盖 compatibility facade,同时运行 `ChatTabView` suite 以确认 bootstrap/session owner 未漂移。 + +### 6. 后续迁移切片 + +1. 逐个将 chat-page 私有逻辑落入 feature-local 目录,维持 `messageIpc.ts` stream gate 单一 owner。 +2. 为 settings 与 chat 真正共享的实现定义小而稳定的 contract,替换历史直接 import。 +3. 仅在存在真实跨 feature 编排重复时,提取 application service;不为 IPC client 建转发 facade。 + +## 兼容性与回滚 + +- 不改变 IPC channel、client 签名或持久化格式。 +- 不更改 Vite HTML entry 路径,因此可直接回滚启动 owner 调整。 +- session 单飞语义保留;改动只确保第一个 fetch 的输入在 snapshot 后已就绪。 +- fallback 仍在 bootstrap 失败时可用,避免把 startup outage 变为白屏。 + +## 验证命令 + +实施完成后执行: + +```bash +pnpm exec vitest --config vitest.config.renderer.ts test/renderer/components/App.startup.test.ts +pnpm run test:renderer +pnpm run format +pnpm run i18n +pnpm run lint +pnpm run typecheck +``` + +若新 architecture baseline 有脚本,也要在上述命令前后执行并审阅输出。 \ No newline at end of file diff --git a/docs/architecture/renderer-application-boundaries/spec.md b/docs/architecture/renderer-application-boundaries/spec.md new file mode 100644 index 0000000000..faf9ba8087 --- /dev/null +++ b/docs/architecture/renderer-application-boundaries/spec.md @@ -0,0 +1,96 @@ +# Renderer 应用边界与启动治理 + +## 背景 + +Renderer 当前由五个独立 HTML/Vite entry 驱动:主窗口、settings、floating、splash、browser-overlay。它们各自创建 Vue 应用、Pinia、i18n 与启动流程,不能假设共享单例或统一生命周期。 + +主窗口的启动职责分散在 [`App.vue`](../../../src/renderer/src/App.vue) 与 [`ChatTabView.vue`](../../../src/renderer/src/views/ChatTabView.vue):前者启动 shell 级 store、runtime listener、provider/model/session 预加载;后者取得 bootstrap snapshot、应用 route shell、启动 agent/project/model/ollama 的交互阶段加载。`sessionStore.fetchSessions()` 已以 `sessionFetchPromise` 保证单飞,但它可能在 bootstrap snapshot 写入 active session 前发起,进而以空的 `prioritizeSessionId` 请求首屏数据。 + +settings 目前直接依赖主窗口实现模块;该现实需要被明确记录并冻结增量,而不是在没有等价契约前机械拆开。IPC transport 已由 [`renderer/api/core.ts`](../../../src/renderer/api/core.ts) 统一封装,35 个 typed `*Client` adapter 已被主窗口和 settings 大量直接使用;再建立按 feature 聚合的 bridge facade 只会增加第二个 transport 边界。 + +## 目标 + +1. 为每个 renderer app 明确运行时所有权、入口与独立性约束。 +2. 将新的 renderer 分层目录定位在 `src/renderer/src/` 下,兼容现有 `@` alias 和 TypeScript 覆盖范围。 +3. 为主窗口 bootstrap 定义显式状态与每项启动工作的唯一 owner,修复首屏 session 优先级时序,不降低当前并行加载策略。 +4. 保留 `renderer/api/*Client` 作为唯一 IPC transport adapter;禁止新增 feature-aggregate bridge facade。 +5. 把当前 settings → chat-app 的跨 app 依赖作为可观察基线,并禁止新增依赖。 +6. 为启动、listener 生命周期、route fallback、会话优先级和 keyed ChatPage 恢复补回归测试。 + +## Phase 1:chat-main composition root + +Phase 1 将主窗口的 composition root 实现迁移到 `src/renderer/src/apps/chat-main/ChatMainApp.vue`。稳定 Vite/HTML entry 继续是 `src/renderer/src/main.ts` 与 `src/renderer/index.html`,根目录 `App.vue` 仅作为兼容 facade 组合 `ChatMainApp`。 + +本切片不移动 `ChatTabView.vue`、route contract 或 feature-local 页面逻辑。`ChatTabView` 继续是 bootstrap snapshot、route init 和首屏 session fetch 的唯一 owner;迁移不得改变 Phase 0 已验证的启动时序。 + +## 非目标 + +- 不变更 main/preload IPC contract、权限模型或 `window.deepchat` 暴露面。 +- 不移动五个 HTML entry 或强制五个应用共用同一个 bootstrap composable。 +- 不在 Phase 0 重写既有 `*Client`、Store 或 ChatPage。 +- 不把不同 webContents 的 Pinia、Vue Query/Colada 或 store 实例共享到进程级。 +- 不在本阶段消除既有 settings → chat-app 依赖;仅冻结其增长并规划后续迁移。 +- 不更改 stream event ownership;`messageIpc.ts` 继续作为 stream tombstone/generation gate 的唯一 owner。 + +## 已确认约束 + +- `@` 指向 `src/renderer/src`;`apps/`、`features/`、`platform/` 和 `foundation/` 必须放在该目录内,才能使用 `@/apps/*` 等导入并被当前 TypeScript 范围覆盖。 +- 保留五个现有 `main.ts` 作为稳定 Vite/HTML entry shim。不同 app 可以拥有不同 readiness 与 listener 注册时机。 +- settings 的 MCP deeplink listener 必须在 `setup` 阶段注册,不能等待 `onMounted` 或 provider 初始化,否则可能丢失早到 IPC。 +- `modelStore.initialize()` 代表 interactive parallel load,不是 shell-blocking critical load。 +- `sessionFetchPromise` 单飞的输入在首个请求创建时固定;bootstrap shell 写入前不能开始首屏 session fetch。 +- 任何 directory 名称不得使用 renderer `shared`,以免与根目录 `src/shared` 的跨进程 contracts/utilities 混淆;使用 `foundation`。 + +## Renderer app ownership + +| app | 稳定 entry | bootstrap 所有者 | 独立性要求 | +| --- | --- | --- | --- | +| chat main | `src/renderer/src/main.ts` | app shell + `ChatTabView` route host | 自己的 Pinia/i18n;shell snapshot 与交互数据分阶段启动 | +| settings | `src/renderer/settings/main.ts` | settings `App.vue` | listener 早注册;不得依赖 chat app 已启动 | +| floating | `src/renderer/floating/main.ts` | floating app | 不复用 chat main 的实例或 readiness | +| splash | `src/renderer/splash/main.ts` | splash app | 仅保持其最小窗口职责 | +| browser overlay | `src/renderer/browser-overlay/main.ts` | overlay app | 在 Phase 0 审计其与遗留 `browser` 配置的关系后再变更 | + +## 主窗口启动状态与 owner + +状态为 `uninitialized → snapshot-loading → shell-hydrated → interactive → deferred-settled`;任何失败可转入 `degraded`,仍应产生可用 fallback route。 + +| 工作 | 唯一 owner | 时机 | 是否阻塞 interactive | +| --- | --- | --- | --- | +| app runtime / MCP deeplink listener | main app shell | setup/onMounted 前后保持现有无 race 时序 | 否 | +| bootstrap snapshot | `ChatTabView` route host | `snapshot-loading` | 是 | +| shell snapshot application + route initialization | `ChatTabView` route host | `shell-hydrated` | 是 | +| first session page | `ChatTabView` route host | shell snapshot 后;fallback 也必须选择 route 后 | 否 | +| UI settings/provider initialization | main app shell | 与 snapshot 并行 | 否 | +| agents/projects/models/ollama | `ChatTabView` route host | route 初始化后并行 | 否 | +| icon loading | main app shell | app mount | 否 | +| deferred hydration | `ChatTabView` route host | interactive 后 | 否 | + +## 依赖规则 + +目标目录(逐步迁移,不要求 Phase 0 一次完成): + +```text +src/renderer/src/ + apps/ # 每个 renderer app 的 composition root / entry-adjacent assembly + features/ # feature-local views, composables, state composition + platform/ # browser/runtime infrastructure, app lifecycle helpers + foundation/ # renderer-only pure utilities、types、tokens +``` + +- `apps` 可以组合 `features`、`platform`、`foundation` 与 `api`。 +- `features` 可以依赖 `api`、跨进程 contracts、`platform` 与 `foundation`,不得 import 另一个 app 的 composition root。 +- `platform` 不得 import feature UI;`foundation` 不得 import Vue、Pinia、IPC 或 feature/app 模块。 +- `renderer/api/*Client` 是唯一 transport adapter;不得新增 `platform/bridge/*` 或按业务领域转发所有 client 的 facade。 +- 每个 app 创建自己的 Pinia/plugin 实例。可共享 store 定义、types 与 pure utilities,不可共享运行中的 store 实例。 +- settings → chat-app 的既有直接 import 是批准的历史基线;Phase 0 后不得新增,后续仅能减少或用明确 contract 替换。 + +## 验收标准 + +1. SDD 明确记录上述 ownership、状态、目录与依赖约束,且没有 `[NEEDS CLARIFICATION]`。 +2. 主窗口 session 首屏请求只由 route host 在 bootstrap shell 写入后触发;完整 app + route host 组合中只产生一次首屏 session request。 +3. bootstrap 成功和失败 fallback 都能得到确定 route,且不会阻塞 interactive readiness。 +4. runtime/deeplink listener 的注册与 cleanup 有测试;settings 保持 setup 阶段早注册。 +5. ChatPage keyed remount 仍恢复最近测量缓存。 +6. architecture baseline 记录五个 app、`browser`/`browser-overlay` 状态,以及 settings → chat-app import 数;增量校验会拒绝新增该跨 app import。 +7. 相关测试、format、i18n、lint 与类型检查通过。 diff --git a/docs/architecture/renderer-application-boundaries/tasks.md b/docs/architecture/renderer-application-boundaries/tasks.md new file mode 100644 index 0000000000..3c9996fec8 --- /dev/null +++ b/docs/architecture/renderer-application-boundaries/tasks.md @@ -0,0 +1,24 @@ +# 任务清单 + +## Phase 0 + +- [x] 记录多窗口 app ownership、目录约束、IPC transport 原则和启动状态机。 +- [x] 审计 `browser` 与 `browser-overlay`:确认前者为无消费者的遗留 SVG 目录,后者为 active overlay renderer。 +- [x] 为 browser-overlay 补齐 TypeScript 覆盖范围,移除遗留 `@browser` alias、注释和目录;更新项目结构说明。 +- [x] 调整主窗口启动 owner:shell 不再发起首屏 session fetch,route host 在 snapshot/fallback route 确定后唯一发起。 +- [x] 补组合启动和 session priority 时序测试。 +- [x] 保持现有 listener lifecycle、route fallback、ChatPage keyed remount、settings independent startup 回归测试;它们包含在 targeted suite 中。 +- [x] 建立 renderer architecture baseline,记录 entry 与 settings → chat-app 跨 app import 基线,并提供 `--check` 防止未经审阅的增量。 +- [x] 执行 targeted tests、format、i18n、lint、typecheck 和完整 renderer suite(167 files、1302 tests)。 + +## Phase 1 + +- [x] 将 main app composition 实现移至 `src/renderer/src/apps/chat-main/ChatMainApp.vue`,保留根 `App.vue` compatibility facade 与稳定 entry shim。 +- [x] 保留 `ChatTabView`、router 与 session/bootstrap owner 不变,并以现有 startup suites 保护时序。 +- [x] 执行 targeted startup suites、完整 renderer suite(167 files、1302 tests)、format、i18n、lint、typecheck 与 renderer baseline check。 + +## 后续切片(不在本次实施) + +- [ ] 将 feature-local ChatPage 逻辑逐个移动并维持 `messageIpc.ts` 的 stream gate 单一所有权。 +- [ ] 以明确 shared contract 替换 settings → chat-app 历史依赖。 +- [ ] 当真实编排重复出现时,提取 application service;不创建 IPC facade。 diff --git a/electron.vite.config.ts b/electron.vite.config.ts index dfca39839f..40fe686e1c 100644 --- a/electron.vite.config.ts +++ b/electron.vite.config.ts @@ -120,7 +120,6 @@ export default defineConfig({ ? [ vueDevTools({ appendTo: 'src/renderer/src/main.ts' - // appendTo:'src/renderer/browser/main.ts' }) ] : []) diff --git a/package.json b/package.json index 9b3dfba7a2..c36dff00d2 100644 --- a/package.json +++ b/package.json @@ -35,6 +35,8 @@ "icons:generate": "node scripts/generate-icon-collections.mjs", "icons:check": "node scripts/generate-icon-collections.mjs --check", "architecture:baseline": "node scripts/generate-architecture-baseline.mjs", + "architecture:renderer-baseline": "node scripts/generate-renderer-architecture-baseline.mjs", + "architecture:renderer-baseline:check": "node scripts/generate-renderer-architecture-baseline.mjs --check", "typecheck:node": "tsgo --noEmit -p tsconfig.node.json --composite false", "typecheck:web": "vue-tsgo --project tsconfig.app.tsgo.json", "typecheck": "pnpm run typecheck:node && pnpm run typecheck:web", diff --git a/scripts/generate-renderer-architecture-baseline.mjs b/scripts/generate-renderer-architecture-baseline.mjs new file mode 100644 index 0000000000..28f4a0385a --- /dev/null +++ b/scripts/generate-renderer-architecture-baseline.mjs @@ -0,0 +1,132 @@ +import fs from 'node:fs/promises' +import path from 'node:path' +import process from 'node:process' + +const ROOT = process.cwd() +const checkMode = process.argv.includes('--check') +const REPORT_PATH = path.join( + ROOT, + 'docs/architecture/baselines/renderer-application-boundaries-baseline.json' +) + +const apps = [ + { + id: 'chat-main', + html: 'src/renderer/index.html', + entry: 'src/renderer/src/main.ts' + }, + { + id: 'browser-overlay', + html: 'src/renderer/browser-overlay/index.html', + entry: 'src/renderer/browser-overlay/main.ts' + }, + { + id: 'floating', + html: 'src/renderer/floating/index.html', + entry: 'src/renderer/floating/main.ts' + }, + { + id: 'splash', + html: 'src/renderer/splash/index.html', + entry: 'src/renderer/splash/main.ts' + }, + { + id: 'settings', + html: 'src/renderer/settings/index.html', + entry: 'src/renderer/settings/main.ts' + } +] + +const exists = async (relativePath) => { + try { + await fs.access(path.join(ROOT, relativePath)) + return true + } catch { + return false + } +} + +const walk = async (relativePath) => { + const directory = path.join(ROOT, relativePath) + const entries = await fs.readdir(directory, { withFileTypes: true }) + const files = await Promise.all( + entries.map(async (entry) => { + const childPath = path.join(relativePath, entry.name) + if (entry.isDirectory()) return walk(childPath) + return [childPath] + }) + ) + return files.flat() +} + +const isSourceFile = (file) => /\.(?:ts|tsx|vue|js|jsx)$/.test(file) +const importPattern = /(?:from\s+|import\s*\()(['"])([^'"]+)\1/g + +const collectSettingsToChatImports = async () => { + const files = (await walk('src/renderer/settings')).filter(isSourceFile) + const imports = [] + + for (const file of files) { + const source = await fs.readFile(path.join(ROOT, file), 'utf8') + for (const match of source.matchAll(importPattern)) { + const specifier = match[2] + if (specifier.startsWith('@/') || specifier.startsWith('../src/')) { + imports.push({ file, specifier }) + } + } + } + + return imports.sort((left, right) => + `${left.file}:${left.specifier}`.localeCompare(`${right.file}:${right.specifier}`) + ) +} + +const main = async () => { + const [settingsToChatImports, legacyBrowserDirectoryExists] = await Promise.all([ + collectSettingsToChatImports(), + exists('src/renderer/browser') + ]) + const appStatus = await Promise.all( + apps.map(async (app) => ({ + ...app, + htmlExists: await exists(app.html), + entryExists: await exists(app.entry) + })) + ) + + const report = { + schemaVersion: 1, + apps: appStatus, + browser: { + legacyDirectoryExists: legacyBrowserDirectoryExists, + activeOverlayDirectory: 'src/renderer/browser-overlay' + }, + settingsToChatAppImports: settingsToChatImports, + settingsToChatAppImportCount: settingsToChatImports.length + } + + const serializedReport = `${JSON.stringify(report, null, 2)}\n` + + if (checkMode) { + const existingReport = await fs.readFile(REPORT_PATH, 'utf8').catch(() => null) + if (existingReport !== serializedReport) { + throw new Error( + `renderer architecture baseline changed: ${path.relative(ROOT, REPORT_PATH)}. ` + + 'Run pnpm run architecture:renderer-baseline and review the diff.' + ) + } + console.info( + `renderer architecture baseline is current (settings→chat imports: ${settingsToChatImports.length})` + ) + return + } + + await fs.mkdir(path.dirname(REPORT_PATH), { recursive: true }) + await fs.writeFile(REPORT_PATH, serializedReport) + console.info( + `renderer architecture baseline written: ${path.relative(ROOT, REPORT_PATH)} ` + + `(settings→chat imports: ${settingsToChatImports.length})` + ) +} + +await main() diff --git a/src/renderer/browser/assets/ChromeClose.svg b/src/renderer/browser/assets/ChromeClose.svg deleted file mode 100644 index 07c2052e39..0000000000 --- a/src/renderer/browser/assets/ChromeClose.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/renderer/browser/assets/ChromeMaximize-1.svg b/src/renderer/browser/assets/ChromeMaximize-1.svg deleted file mode 100644 index 9aa8ea3dac..0000000000 --- a/src/renderer/browser/assets/ChromeMaximize-1.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/renderer/browser/assets/ChromeMaximize.svg b/src/renderer/browser/assets/ChromeMaximize.svg deleted file mode 100644 index b382fbe371..0000000000 --- a/src/renderer/browser/assets/ChromeMaximize.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/renderer/browser/assets/ChromeMinimize.svg b/src/renderer/browser/assets/ChromeMinimize.svg deleted file mode 100644 index 2f4394ff74..0000000000 --- a/src/renderer/browser/assets/ChromeMinimize.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/renderer/src/App.vue b/src/renderer/src/App.vue index d6f040b42f..0c9822212c 100644 --- a/src/renderer/src/App.vue +++ b/src/renderer/src/App.vue @@ -1,623 +1,7 @@ diff --git a/src/renderer/src/apps/chat-main/ChatMainApp.vue b/src/renderer/src/apps/chat-main/ChatMainApp.vue new file mode 100644 index 0000000000..0aea96f702 --- /dev/null +++ b/src/renderer/src/apps/chat-main/ChatMainApp.vue @@ -0,0 +1,616 @@ + + + diff --git a/src/renderer/src/views/ChatTabView.vue b/src/renderer/src/views/ChatTabView.vue index ace6dc4c06..14ffc5b3dd 100644 --- a/src/renderer/src/views/ChatTabView.vue +++ b/src/renderer/src/views/ChatTabView.vue @@ -122,15 +122,16 @@ onMounted(async () => { await Promise.allSettled([agentStore.fetchAgents(), projectStore.loadDefaultProjectPath()]) await initializeRouteFromFallbackState() } finally { - isReady.value = true - console.info('[Startup][Renderer] ChatTabView interactive ready') - - // Session data is already loading in parallel from App.vue.onMounted - // Don't block on it here - let it load in background + // The route host owns the initial session request. It must start after bootstrap shell + // hydration (or fallback route recovery), otherwise its single-flight request can lose + // the bootstrap active session priority. if (!sessionStore.hasLoadedInitialPage) { void sessionStore.fetchSessions() } + isReady.value = true + console.info('[Startup][Renderer] ChatTabView interactive ready') + markStartupInteractive() cancelDeferredHydration = scheduleStartupDeferredTask(async () => { console.info('[Startup][Renderer] ChatTabView deferred hydration begin') diff --git a/test/renderer/components/App.startup.test.ts b/test/renderer/components/App.startup.test.ts index 0289d6059a..011f6fbd0e 100644 --- a/test/renderer/components/App.startup.test.ts +++ b/test/renderer/components/App.startup.test.ts @@ -467,6 +467,12 @@ afterEach(() => { }) describe('App startup welcome flow', () => { + it('leaves the initial session request to the chat route host', async () => { + const { sessionStore } = await mountApp({ initComplete: true, routeName: 'chat' }) + + expect(sessionStore.fetchSessions).not.toHaveBeenCalled() + }) + it('routes to welcome when init is incomplete', async () => { const { router, configService, onboardingClient } = await mountApp({ initComplete: false, diff --git a/test/renderer/components/ChatTabView.test.ts b/test/renderer/components/ChatTabView.test.ts index 4733f35cef..65d5db601e 100644 --- a/test/renderer/components/ChatTabView.test.ts +++ b/test/renderer/components/ChatTabView.test.ts @@ -10,6 +10,7 @@ type SetupOptions = { newConversationTargetAgentId?: string | null sessionError?: string | null activeSessionId?: string | null + bootstrapActiveSessionId?: string | null bootstrapReject?: boolean } @@ -43,7 +44,10 @@ const setup = async (options: SetupOptions = {}) => { error: options.sessionError ?? null, newConversationTargetAgentId: options.newConversationTargetAgentId ?? 'deepchat', hasLoadedInitialPage: false, - applyBootstrapShell: vi.fn().mockResolvedValue(undefined), + applyBootstrapShell: vi.fn().mockImplementation(async ({ activeSessionId, activeSession }) => { + sessionStore.activeSessionId = activeSessionId + sessionStore.activeSession = activeSession + }), fetchSessions: vi.fn().mockResolvedValue(undefined), startNewConversation: vi.fn().mockResolvedValue(undefined) }) @@ -97,8 +101,13 @@ const setup = async (options: SetupOptions = {}) => { return { startupRunId: 'run-1', - activeSessionId: sessionStore.activeSessionId, - activeSession: sessionStore.activeSession, + activeSessionId: options.bootstrapActiveSessionId ?? sessionStore.activeSessionId, + activeSession: + options.bootstrapActiveSessionId === undefined + ? sessionStore.activeSession + : options.bootstrapActiveSessionId + ? { ...sessionStore.activeSession, id: options.bootstrapActiveSessionId } + : null, agents: agentStore.selectedAgentId === null ? [] @@ -232,6 +241,23 @@ describe('ChatTabView startup and routing', () => { expect(ollamaStore.initialize).toHaveBeenCalledTimes(1) }) + it('starts the initial session request only after bootstrap shell hydration', async () => { + const { sessionStore } = await setup({ + currentRoute: 'chat', + activeSessionId: null, + bootstrapActiveSessionId: 'bootstrap-session' + }) + + expect(sessionStore.applyBootstrapShell).toHaveBeenCalledWith( + expect.objectContaining({ activeSessionId: 'bootstrap-session' }) + ) + expect(sessionStore.fetchSessions).toHaveBeenCalledTimes(1) + expect(sessionStore.fetchSessions.mock.invocationCallOrder[0]).toBeGreaterThan( + sessionStore.applyBootstrapShell.mock.invocationCallOrder[0] + ) + expect(sessionStore.activeSessionId).toBe('bootstrap-session') + }) + it('hydrates the route from the session store state and keeps provider warmup on demand', async () => { const { pageRouter, agentStore, projectStore, sessionStore, markStartupInteractive } = await setup({ diff --git a/test/renderer/stores/sessionStore.test.ts b/test/renderer/stores/sessionStore.test.ts index fabf07d6e3..7b6e6814d5 100644 --- a/test/renderer/stores/sessionStore.test.ts +++ b/test/renderer/stores/sessionStore.test.ts @@ -1281,6 +1281,23 @@ describe('sessionStore streaming cleanup', () => { }) describe('sessionStore pagination', () => { + it('prioritizes the active bootstrap session when the first page starts after shell hydration', async () => { + const { store, sessionClient } = await setupStore() + + await store.applyBootstrapShell({ + activeSessionId: 'bootstrap-session', + activeSession: createSession({ id: 'bootstrap-session' }) + }) + await store.fetchSessions() + + expect(sessionClient.listLightweight).toHaveBeenCalledWith( + expect.objectContaining({ + includeSubagents: false, + prioritizeSessionId: 'bootstrap-session' + }) + ) + }) + it('deduplicates concurrent initial fetch requests and allows a later fetch', async () => { const { store, sessionClient } = await setupStore() let resolveInitialFetch: (value: { diff --git a/tsconfig.app.json b/tsconfig.app.json index beb0562d51..04cc30745d 100644 --- a/tsconfig.app.json +++ b/tsconfig.app.json @@ -5,10 +5,9 @@ "src/renderer/src/**/*", "src/renderer/src/**/*.vue", "src/renderer/src/assets/**/*", - "src/renderer/browser/env.d.ts", - "src/renderer/browser/**/*", - "src/renderer/browser/**/*.vue", - "src/renderer/browser/assets/**/*", + "src/renderer/browser-overlay/env.d.ts", + "src/renderer/browser-overlay/**/*", + "src/renderer/browser-overlay/**/*.vue", "src/renderer/floating/env.d.ts", "src/renderer/floating/**/*", "src/renderer/floating/**/*.vue", @@ -36,9 +35,6 @@ "@api/*": [ "./src/renderer/api/*" ], - "@browser/*": [ - "./src/renderer/browser/*" - ], "@shared/*": [ "./src/shared/*" ], diff --git a/tsconfig.app.tsgo.json b/tsconfig.app.tsgo.json index b442523d62..17ee07165f 100644 --- a/tsconfig.app.tsgo.json +++ b/tsconfig.app.tsgo.json @@ -5,10 +5,9 @@ "src/renderer/src/**/*", "src/renderer/src/**/*.vue", "src/renderer/src/assets/**/*", - "src/renderer/browser/env.d.ts", - "src/renderer/browser/**/*", - "src/renderer/browser/**/*.vue", - "src/renderer/browser/assets/**/*", + "src/renderer/browser-overlay/env.d.ts", + "src/renderer/browser-overlay/**/*", + "src/renderer/browser-overlay/**/*.vue", "src/renderer/floating/env.d.ts", "src/renderer/floating/**/*", "src/renderer/floating/**/*.vue", @@ -37,9 +36,6 @@ "@api/*": [ "./src/renderer/api/*" ], - "@browser/*": [ - "./src/renderer/browser/*" - ], "@shared/*": [ "./src/shared/*" ], diff --git a/vitest.config.renderer.ts b/vitest.config.renderer.ts index 2fac87c1d0..a1567994ff 100644 --- a/vitest.config.renderer.ts +++ b/vitest.config.renderer.ts @@ -19,7 +19,6 @@ export default defineConfig({ alias: { '@': resolve('src/renderer/src'), '@api': resolve('src/renderer/api'), - '@browser': resolve('src/renderer/browser'), '@shadcn': resolve('src/shadcn'), '@shared': resolve('src/shared'), vue: 'vue/dist/vue.esm-bundler.js' diff --git a/vitest.config.ts b/vitest.config.ts index d5c204898e..564d0c875b 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -40,7 +40,6 @@ export default defineConfig({ // Renderer process aliases (match electron.vite.config.ts renderer config) { find: '@/', replacement: resolve('src/renderer/src/') + '/' }, { find: '@api', replacement: resolve('src/renderer/api') }, - { find: '@browser', replacement: resolve('src/renderer/browser/') }, { find: '@shared', replacement: resolve('src/shared') }, { find: '@shadcn', replacement: resolve('src/shadcn') }, { find: 'electron', replacement: resolve('test/mocks/electron.ts') }, From d2124c8a5652a6474d519a8408b28e75d847da4f Mon Sep 17 00:00:00 2001 From: zhangmo8 Date: Fri, 17 Jul 2026 18:10:18 +0800 Subject: [PATCH 02/12] fix(build): handle removed browser source root --- resources/acp-registry/registry.json | 123 +- resources/model-db/providers.json | 1564 ++++++++++++----- scripts/generate-icon-collections.mjs | 1 - .../lib/icons/icon-collections.generated.ts | 10 +- .../src/lib/icons/icon-whitelist.generated.ts | 2 - 5 files changed, 1238 insertions(+), 462 deletions(-) diff --git a/resources/acp-registry/registry.json b/resources/acp-registry/registry.json index 3ccb406df5..1c0b40ba43 100644 --- a/resources/acp-registry/registry.json +++ b/resources/acp-registry/registry.json @@ -122,7 +122,7 @@ { "id": "cline", "name": "Cline", - "version": "3.0.42", + "version": "3.0.44", "description": "Autonomous coding agent CLI - capable of creating/editing files, running commands, using the browser, and more", "repository": "https://github.com/cline/cline", "website": "https://cline.bot/cli", @@ -133,7 +133,7 @@ "icon": "https://cdn.agentclientprotocol.com/registry/v1/latest/cline.svg", "distribution": { "npx": { - "package": "cline@3.0.42", + "package": "cline@3.0.44", "args": [ "--acp" ] @@ -508,7 +508,7 @@ { "id": "factory-droid", "name": "Factory Droid", - "version": "0.173.0", + "version": "0.174.0", "description": "Factory Droid - AI coding agent powered by Factory AI", "website": "https://factory.ai/product/cli", "authors": [ @@ -517,7 +517,7 @@ "license": "proprietary", "distribution": { "npx": { - "package": "droid@0.173.0", + "package": "droid@0.174.0", "args": [ "exec", "--output-format", @@ -534,7 +534,7 @@ { "id": "fast-agent", "name": "fast-agent", - "version": "0.9.10", + "version": "0.9.13", "description": "Code and build agents with comprehensive multi-provider support", "repository": "https://github.com/evalstate/fast-agent", "website": "https://fast-agent.ai", @@ -544,7 +544,7 @@ "license": "Apache 2.0", "distribution": { "uvx": { - "package": "fast-agent-acp==0.9.10", + "package": "fast-agent-acp==0.9.13", "args": [ "-x" ] @@ -555,7 +555,7 @@ { "id": "gemini", "name": "Gemini CLI", - "version": "0.50.0", + "version": "0.51.0", "description": "Google's official CLI for Gemini", "repository": "https://github.com/google-gemini/gemini-cli", "website": "https://geminicli.com", @@ -565,7 +565,7 @@ "license": "Apache-2.0", "distribution": { "npx": { - "package": "@google/gemini-cli@0.50.0", + "package": "@google/gemini-cli@0.51.0", "args": [ "--acp" ] @@ -671,7 +671,7 @@ { "id": "grok-build", "name": "Grok Build", - "version": "0.2.101", + "version": "0.2.102", "description": "xAI's coding agent and CLI", "website": "https://x.ai/cli", "authors": [ @@ -680,7 +680,7 @@ "license": "proprietary", "distribution": { "npx": { - "package": "@xai-official/grok@0.2.101", + "package": "@xai-official/grok@0.2.102", "args": [ "agent", "stdio" @@ -692,7 +692,7 @@ { "id": "harn", "name": "Harn", - "version": "0.10.19", + "version": "0.10.21", "description": "Harn runs .harn agent pipelines as a native ACP coding agent over stdio.", "repository": "https://github.com/burin-labs/harn", "website": "https://harnlang.com", @@ -703,49 +703,49 @@ "distribution": { "binary": { "darwin-aarch64": { - "archive": "https://github.com/burin-labs/harn/releases/download/v0.10.19/harn-aarch64-apple-darwin.tar.gz", + "archive": "https://github.com/burin-labs/harn/releases/download/v0.10.21/harn-aarch64-apple-darwin.tar.gz", "cmd": "./harn", "args": [ "serve", "acp" ], - "sha256": "05ba15c0cc05a9afa8a05182c0893b02a50242f8cd804005850a331e6155394a" + "sha256": "9421bd8af7c007a21038bf3e0fb2964bb7c48f64f415e5442c74ae1c9622160b" }, "darwin-x86_64": { - "archive": "https://github.com/burin-labs/harn/releases/download/v0.10.19/harn-x86_64-apple-darwin.tar.gz", + "archive": "https://github.com/burin-labs/harn/releases/download/v0.10.21/harn-x86_64-apple-darwin.tar.gz", "cmd": "./harn", "args": [ "serve", "acp" ], - "sha256": "9804e91ce510c51a32e8c801a2865575c3091e577ff8c43aa5e2a67f16484959" + "sha256": "2e9a9e24c73123cc42dbd1f8fb9b9d5e64d7e9ab57902ffa91ad8fc9f16f50d7" }, "linux-aarch64": { - "archive": "https://github.com/burin-labs/harn/releases/download/v0.10.19/harn-aarch64-unknown-linux-gnu.tar.gz", + "archive": "https://github.com/burin-labs/harn/releases/download/v0.10.21/harn-aarch64-unknown-linux-gnu.tar.gz", "cmd": "./harn", "args": [ "serve", "acp" ], - "sha256": "718ba0a9d42c9f1155bd27d183a6e314900c5829e202d41195545c6071f47033" + "sha256": "35980f869692b65b1b5c8061d2da552aa95b2eb2f1f95aae15d47bebf9edcf04" }, "linux-x86_64": { - "archive": "https://github.com/burin-labs/harn/releases/download/v0.10.19/harn-x86_64-unknown-linux-gnu.tar.gz", + "archive": "https://github.com/burin-labs/harn/releases/download/v0.10.21/harn-x86_64-unknown-linux-gnu.tar.gz", "cmd": "./harn", "args": [ "serve", "acp" ], - "sha256": "5e1114e04105541a7d456ce6c20c031929b27471c271da34aa8dd9357c0b6122" + "sha256": "9fe05464712de8b524273eb927fcfc89dfe97d3250d3a8a70df075288ed311f1" }, "windows-x86_64": { - "archive": "https://github.com/burin-labs/harn/releases/download/v0.10.19/harn-x86_64-pc-windows-msvc.zip", + "archive": "https://github.com/burin-labs/harn/releases/download/v0.10.21/harn-x86_64-pc-windows-msvc.zip", "cmd": "harn.exe", "args": [ "serve", "acp" ], - "sha256": "ef8bb6b5643071cb482fa44bb56729df9af1a49d9212dcd9852ea817d051fe14" + "sha256": "aa7680629825e096461cd2951fe36c38c1199e7a3a8b07235599b428e574e0c1" } } }, @@ -806,7 +806,7 @@ { "id": "kilo", "name": "Kilo", - "version": "7.4.9", + "version": "7.4.11", "description": "The open source coding agent", "repository": "https://github.com/Kilo-Org/kilocode", "website": "https://kilo.ai/", @@ -818,48 +818,48 @@ "distribution": { "binary": { "darwin-aarch64": { - "archive": "https://github.com/Kilo-Org/kilocode/releases/download/v7.4.9/kilo-darwin-arm64.zip", + "archive": "https://github.com/Kilo-Org/kilocode/releases/download/v7.4.11/kilo-darwin-arm64.zip", "cmd": "./kilo", "args": [ "acp" ], - "sha256": "a3fab28d5b97f6d0b297f2d12302e2664344b7fcee3ccc9adc3a84db0be15c96" + "sha256": "14a030a354f3b51f0241662627702e7b06cddf3fcb6e0f1415279e9d3a3b8998" }, "darwin-x86_64": { - "archive": "https://github.com/Kilo-Org/kilocode/releases/download/v7.4.9/kilo-darwin-x64.zip", + "archive": "https://github.com/Kilo-Org/kilocode/releases/download/v7.4.11/kilo-darwin-x64.zip", "cmd": "./kilo", "args": [ "acp" ], - "sha256": "cf6fe6bc77e63bbdf387e34fc31d1aa86a603b6de48011a86ffdc28708bc1453" + "sha256": "66e302e09b96fd9794012bdb608622b589e4810ba31eca35918d8acd1a32a438" }, "linux-aarch64": { - "archive": "https://github.com/Kilo-Org/kilocode/releases/download/v7.4.9/kilo-linux-arm64.tar.gz", + "archive": "https://github.com/Kilo-Org/kilocode/releases/download/v7.4.11/kilo-linux-arm64.tar.gz", "cmd": "./kilo", "args": [ "acp" ], - "sha256": "e0c4ff2ec2f65689406c43e6aff06cea30be8d12ed7193e1ed46a0e0e28d86fd" + "sha256": "48c5405eb05efb3558d120b521d1a2b097cd8e99d36d7caf015e7c467922793c" }, "linux-x86_64": { - "archive": "https://github.com/Kilo-Org/kilocode/releases/download/v7.4.9/kilo-linux-x64.tar.gz", + "archive": "https://github.com/Kilo-Org/kilocode/releases/download/v7.4.11/kilo-linux-x64.tar.gz", "cmd": "./kilo", "args": [ "acp" ], - "sha256": "0d3fc944dbf2b987ce9953d5c4701dbdc950b0905a186c814a24e8915fd7012c" + "sha256": "b060dd4e094b0f9966de03d8a0d0d5cc8e6bb23ab04f1d04b7e3951d13079770" }, "windows-x86_64": { - "archive": "https://github.com/Kilo-Org/kilocode/releases/download/v7.4.9/kilo-windows-x64.zip", + "archive": "https://github.com/Kilo-Org/kilocode/releases/download/v7.4.11/kilo-windows-x64.zip", "cmd": "./kilo.exe", "args": [ "acp" ], - "sha256": "2452f063e6149bffca64e862ae5b2063fe2be6a14200012f92743a8d1a3b4c0c" + "sha256": "1c04d25b1484526b1eb8abe44bbcfc179fb71b1efa88b0164cd5709ca8bb00e7" } }, "npx": { - "package": "@kilocode/cli@7.4.9", + "package": "@kilocode/cli@7.4.11", "args": [ "acp" ] @@ -869,7 +869,7 @@ { "id": "kimi", "name": "Kimi CLI", - "version": "1.48.0", + "version": "1.49.0", "description": "Moonshot AI's coding assistant", "repository": "https://github.com/MoonshotAI/kimi-cli", "website": "https://moonshotai.github.io/kimi-cli/", @@ -880,39 +880,44 @@ "distribution": { "binary": { "darwin-aarch64": { - "archive": "https://github.com/MoonshotAI/kimi-cli/releases/download/1.48.0/kimi-1.48.0-aarch64-apple-darwin.tar.gz", + "archive": "https://github.com/MoonshotAI/kimi-cli/releases/download/1.49.0/kimi-1.49.0-aarch64-apple-darwin.tar.gz", "cmd": "./kimi", "args": [ "acp" - ] + ], + "sha256": "15018b20b203aee09658fdc64840c4846fc17c108d8dba1a19a95581d3ce2921" }, "linux-aarch64": { - "archive": "https://github.com/MoonshotAI/kimi-cli/releases/download/1.48.0/kimi-1.48.0-aarch64-unknown-linux-gnu.tar.gz", + "archive": "https://github.com/MoonshotAI/kimi-cli/releases/download/1.49.0/kimi-1.49.0-aarch64-unknown-linux-gnu.tar.gz", "cmd": "./kimi", "args": [ "acp" - ] + ], + "sha256": "5ac54cabce16ede27b9d2069b9b88edee25528646e7bb5befa9980a1ca71febb" }, "linux-x86_64": { - "archive": "https://github.com/MoonshotAI/kimi-cli/releases/download/1.48.0/kimi-1.48.0-x86_64-unknown-linux-gnu.tar.gz", + "archive": "https://github.com/MoonshotAI/kimi-cli/releases/download/1.49.0/kimi-1.49.0-x86_64-unknown-linux-gnu.tar.gz", "cmd": "./kimi", "args": [ "acp" - ] + ], + "sha256": "6ce0b83f583c45a64cc9f51ffe7e1a8e03ee79acda69945fcf8c23341b9d892f" }, "windows-aarch64": { - "archive": "https://github.com/MoonshotAI/kimi-cli/releases/download/1.48.0/kimi-1.48.0-aarch64-pc-windows-msvc.zip", + "archive": "https://github.com/MoonshotAI/kimi-cli/releases/download/1.49.0/kimi-1.49.0-aarch64-pc-windows-msvc.zip", "cmd": "./kimi.exe", "args": [ "acp" - ] + ], + "sha256": "3ac8f05c7bd18d902a324c6c03a71084cfbe785b9669bbd556c071ee1d8f2f26" }, "windows-x86_64": { - "archive": "https://github.com/MoonshotAI/kimi-cli/releases/download/1.48.0/kimi-1.48.0-x86_64-pc-windows-msvc.zip", + "archive": "https://github.com/MoonshotAI/kimi-cli/releases/download/1.49.0/kimi-1.49.0-x86_64-pc-windows-msvc.zip", "cmd": "./kimi.exe", "args": [ "acp" - ] + ], + "sha256": "2acbbc7ca8c8ac4b03dab1d970f53a292bd226168151b423499feab9fc203ddd" } } }, @@ -1004,7 +1009,7 @@ { "id": "opencode", "name": "OpenCode", - "version": "1.18.2", + "version": "1.18.3", "description": "The open source coding agent", "repository": "https://github.com/anomalyco/opencode", "website": "https://opencode.ai", @@ -1016,52 +1021,52 @@ "distribution": { "binary": { "darwin-aarch64": { - "archive": "https://github.com/anomalyco/opencode/releases/download/v1.18.2/opencode-darwin-arm64.zip", + "archive": "https://github.com/anomalyco/opencode/releases/download/v1.18.3/opencode-darwin-arm64.zip", "cmd": "./opencode", "args": [ "acp" ], - "sha256": "2cb1eb3301a73598890364dfeb45c535155a3855b37ed1d190172821e74e462c" + "sha256": "946f62b155638b911144b7bef520ee4a6442f696297907873463bca3524e40ef" }, "darwin-x86_64": { - "archive": "https://github.com/anomalyco/opencode/releases/download/v1.18.2/opencode-darwin-x64.zip", + "archive": "https://github.com/anomalyco/opencode/releases/download/v1.18.3/opencode-darwin-x64.zip", "cmd": "./opencode", "args": [ "acp" ], - "sha256": "3672491ae6383468be40e54ee5c6fcbac354eb5cfb7c6afbd0ee3ae0c6af4b2e" + "sha256": "4ea147867ba19e4ec03559df557811f1674f40788aea4d10326dc563b7667c6d" }, "linux-aarch64": { - "archive": "https://github.com/anomalyco/opencode/releases/download/v1.18.2/opencode-linux-arm64.tar.gz", + "archive": "https://github.com/anomalyco/opencode/releases/download/v1.18.3/opencode-linux-arm64.tar.gz", "cmd": "./opencode", "args": [ "acp" ], - "sha256": "93352b30d37d8da2e5c226085f1afbf37cf57cfcecedc813520ff2d0f8581540" + "sha256": "da0a631174eba380b2a1d51f9d364fa3812da433e72743c72471d4b5da59c69d" }, "linux-x86_64": { - "archive": "https://github.com/anomalyco/opencode/releases/download/v1.18.2/opencode-linux-x64.tar.gz", + "archive": "https://github.com/anomalyco/opencode/releases/download/v1.18.3/opencode-linux-x64.tar.gz", "cmd": "./opencode", "args": [ "acp" ], - "sha256": "97c95e004bb73d2039f957ea33be0635ea4e22b8dceaedf8f0983765950cf1b6" + "sha256": "60f27b2679f00a511b6539f97e02448afaf58d9c66e2448285ea0c517ca84583" }, "windows-aarch64": { - "archive": "https://github.com/anomalyco/opencode/releases/download/v1.18.2/opencode-windows-arm64.zip", + "archive": "https://github.com/anomalyco/opencode/releases/download/v1.18.3/opencode-windows-arm64.zip", "cmd": "./opencode", "args": [ "acp" ], - "sha256": "7eb407240101a0ebca9acb99ed819a09a4211fdafa381161af6ca49f85736e4b" + "sha256": "a549fb2e9041db9438bcd9b77bfa0a4b2476caf2d550f37479aabfec1b079bfb" }, "windows-x86_64": { - "archive": "https://github.com/anomalyco/opencode/releases/download/v1.18.2/opencode-windows-x64.zip", + "archive": "https://github.com/anomalyco/opencode/releases/download/v1.18.3/opencode-windows-x64.zip", "cmd": "./opencode.exe", "args": [ "acp" ], - "sha256": "aca23b18d03639c6bd576a2dd23cebfa6dab80d31358f17cfa03fd1f2a5c41fd" + "sha256": "68bc62930f6cb5755e0409aa9de0bb270a66ed2b8c9cf0c029e9f2287ed5486e" } } } @@ -1165,7 +1170,7 @@ { "id": "qwen-code", "name": "Qwen Code", - "version": "0.19.10", + "version": "0.19.11", "description": "Alibaba's Qwen coding assistant", "repository": "https://github.com/QwenLM/qwen-code", "website": "https://qwenlm.github.io/qwen-code-docs/en/users/overview", @@ -1175,7 +1180,7 @@ "license": "Apache-2.0", "distribution": { "npx": { - "package": "@qwen-code/qwen-code@0.19.10", + "package": "@qwen-code/qwen-code@0.19.11", "args": [ "--acp", "--experimental-skills" diff --git a/resources/model-db/providers.json b/resources/model-db/providers.json index 29764373e1..0370d123a8 100644 --- a/resources/model-db/providers.json +++ b/resources/model-db/providers.json @@ -493,9 +493,9 @@ "release_date": "2026-05-29", "last_updated": "2026-05-29", "cost": { - "input": 0.2, - "output": 1.15, - "cache_read": 0.04, + "input": 0.19, + "output": 1.14, + "cache_read": 0.03, "cache_write": 0 }, "type": "chat" @@ -614,9 +614,9 @@ "release_date": "2026-06-12", "last_updated": "2026-06-12", "cost": { - "input": 0.719, - "output": 3.49, - "cache_read": 0.149, + "input": 0.75, + "output": 3.5, + "cache_read": 0.16, "cache_write": 0 }, "type": "chat" @@ -37554,6 +37554,42 @@ }, "type": "chat" }, + { + "id": "k3", + "name": "Kimi K3", + "display_name": "Kimi K3", + "modalities": { + "input": [ + "text", + "image", + "video" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1048576, + "output": 131072 + }, + "temperature": true, + "tool_call": true, + "reasoning": { + "supported": true, + "default": true + }, + "attachment": false, + "open_weights": true, + "release_date": "2026-07-16", + "last_updated": "2026-07-16", + "cost": { + "input": 0, + "output": 0, + "cache_read": 0, + "cache_write": 0 + }, + "type": "chat" + }, { "id": "k2p6", "name": "Kimi K2.6", @@ -40434,6 +40470,43 @@ }, "type": "chat" }, + { + "id": "zai-org/GLM-5.2", + "name": "GLM-5.2", + "display_name": "GLM-5.2", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1048576, + "output": 131072 + }, + "temperature": true, + "tool_call": true, + "reasoning": { + "supported": true, + "default": true + }, + "extra_capabilities": { + "reasoning": { + "supported": true + } + }, + "attachment": false, + "open_weights": true, + "release_date": "2026-06-13", + "last_updated": "2026-06-13", + "cost": { + "input": 1.4375, + "output": 5.75 + }, + "type": "chat" + }, { "id": "mistralai/Mistral-Medium-3.5-128B", "name": "Mistral Medium 3.5", @@ -75663,6 +75736,47 @@ }, "type": "chat" }, + { + "id": "kimi-k3", + "name": "Kimi K3", + "display_name": "Kimi K3", + "modalities": { + "input": [ + "text", + "image", + "video" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1048576, + "output": 131072 + }, + "temperature": true, + "tool_call": true, + "reasoning": { + "supported": true, + "default": true + }, + "extra_capabilities": { + "reasoning": { + "supported": true, + "interleaved": true, + "summaries": true, + "visibility": "summary", + "continuation": [ + "thinking_blocks" + ] + } + }, + "attachment": true, + "open_weights": true, + "release_date": "2026-07-16", + "last_updated": "2026-07-16", + "type": "chat" + }, { "id": "kimi-k2.7-code-highspeed", "name": "Kimi K2.7 Code Highspeed", @@ -120641,6 +120755,62 @@ } ] }, + "thinkingmachines": { + "id": "thinkingmachines", + "name": "Thinking Machines", + "display_name": "Thinking Machines", + "api": "https://tinker.thinkingmachines.dev/services/tinker-prod/oai/api/v1", + "doc": "https://tinker-docs.thinkingmachines.ai/tinker/compatible-apis/openai/", + "models": [ + { + "id": "inkling", + "name": "Inkling", + "display_name": "Inkling", + "modalities": { + "input": [ + "text", + "image", + "audio", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 256000, + "output": 256000 + }, + "temperature": true, + "tool_call": true, + "reasoning": { + "supported": true, + "default": true + }, + "extra_capabilities": { + "reasoning": { + "supported": true, + "interleaved": true, + "summaries": true, + "visibility": "summary", + "continuation": [ + "thinking_blocks" + ] + } + }, + "attachment": true, + "open_weights": true, + "release_date": "2026-07-15", + "last_updated": "2026-07-15", + "cost": { + "input": 3.74, + "output": 9.36, + "cache_read": 0.748 + }, + "type": "chat" + } + ] + }, "nebius": { "id": "nebius", "name": "Nebius Token Factory", @@ -129318,54 +129488,9 @@ "type": "chat" }, { - "id": "deepseek-v4-flash", - "name": "DeepSeek V4 Flash", - "display_name": "DeepSeek V4 Flash", - "modalities": { - "input": [ - "text" - ], - "output": [ - "text" - ] - }, - "limit": { - "context": 1000000, - "output": 384000 - }, - "temperature": true, - "tool_call": true, - "reasoning": { - "supported": true, - "default": true - }, - "extra_capabilities": { - "reasoning": { - "supported": true, - "interleaved": true, - "summaries": true, - "visibility": "summary", - "continuation": [ - "thinking_blocks" - ] - } - }, - "attachment": false, - "open_weights": true, - "knowledge": "2025-05", - "release_date": "2026-04-24", - "last_updated": "2026-04-24", - "cost": { - "input": 0.14, - "output": 0.28, - "cache_read": 0.0028 - }, - "type": "chat" - }, - { - "id": "qwen3.5-plus", - "name": "Qwen3.5 Plus", - "display_name": "Qwen3.5 Plus", + "id": "kimi-k3", + "name": "Kimi K3", + "display_name": "Kimi K3", "modalities": { "input": [ "text", @@ -129377,8 +129502,8 @@ ] }, "limit": { - "context": 262144, - "output": 65536 + "context": 1048576, + "output": 131072 }, "temperature": true, "tool_call": true, @@ -129398,22 +129523,20 @@ } }, "attachment": true, - "open_weights": false, - "knowledge": "2025-04", - "release_date": "2026-02-16", - "last_updated": "2026-02-16", + "open_weights": true, + "release_date": "2026-07-16", + "last_updated": "2026-07-16", "cost": { - "input": 0.2, - "output": 1.2, - "cache_read": 0.02, - "cache_write": 0.25 + "input": 3, + "output": 15, + "cache_read": 0.3 }, "type": "chat" }, { - "id": "mimo-v2-pro", - "name": "MiMo V2 Pro", - "display_name": "MiMo V2 Pro", + "id": "deepseek-v4-flash", + "name": "DeepSeek V4 Flash", + "display_name": "DeepSeek V4 Flash", "modalities": { "input": [ "text" @@ -129423,8 +129546,8 @@ ] }, "limit": { - "context": 1048576, - "output": 128000 + "context": 1000000, + "output": 384000 }, "temperature": true, "tool_call": true, @@ -129443,38 +129566,186 @@ ] } }, - "attachment": true, + "attachment": false, "open_weights": true, - "knowledge": "2024-12", - "release_date": "2026-03-18", - "last_updated": "2026-03-18", + "knowledge": "2025-05", + "release_date": "2026-04-24", + "last_updated": "2026-04-24", "cost": { - "input": 1, - "output": 3, - "cache_read": 0.2, - "tiers": [ - { - "input": 2, - "output": 6, - "cache_read": 0.4, - "tier": { - "type": "context", - "size": 256000 - } - } - ], - "context_over_200k": { - "input": 2, - "output": 6, - "cache_read": 0.4 - } + "input": 0.14, + "output": 0.28, + "cache_read": 0.0028 }, "type": "chat" }, { - "id": "kimi-k2.6", - "name": "Kimi K2.6", - "display_name": "Kimi K2.6", + "id": "qwen3.5-plus", + "name": "Qwen3.5 Plus", + "display_name": "Qwen3.5 Plus", + "modalities": { + "input": [ + "text", + "image", + "video" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 262144, + "output": 65536 + }, + "temperature": true, + "tool_call": true, + "reasoning": { + "supported": true, + "default": true + }, + "extra_capabilities": { + "reasoning": { + "supported": true, + "interleaved": true, + "summaries": true, + "visibility": "summary", + "continuation": [ + "thinking_blocks" + ] + } + }, + "attachment": true, + "open_weights": false, + "knowledge": "2025-04", + "release_date": "2026-02-16", + "last_updated": "2026-02-16", + "cost": { + "input": 0.2, + "output": 1.2, + "cache_read": 0.02, + "cache_write": 0.25 + }, + "type": "chat" + }, + { + "id": "grok-4.5", + "name": "Grok 4.5", + "display_name": "Grok 4.5", + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 500000, + "output": 500000 + }, + "temperature": true, + "tool_call": true, + "reasoning": { + "supported": true, + "default": true + }, + "extra_capabilities": { + "reasoning": { + "supported": true + } + }, + "attachment": true, + "open_weights": false, + "release_date": "2026-07-08", + "last_updated": "2026-07-08", + "cost": { + "input": 2, + "output": 6, + "cache_read": 0.5, + "tiers": [ + { + "input": 4, + "output": 12, + "cache_read": 1, + "tier": { + "type": "context", + "size": 200000 + } + } + ], + "context_over_200k": { + "input": 4, + "output": 12, + "cache_read": 1 + } + }, + "type": "chat" + }, + { + "id": "mimo-v2-pro", + "name": "MiMo V2 Pro", + "display_name": "MiMo V2 Pro", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1048576, + "output": 128000 + }, + "temperature": true, + "tool_call": true, + "reasoning": { + "supported": true, + "default": true + }, + "extra_capabilities": { + "reasoning": { + "supported": true, + "interleaved": true, + "summaries": true, + "visibility": "summary", + "continuation": [ + "thinking_blocks" + ] + } + }, + "attachment": true, + "open_weights": true, + "knowledge": "2024-12", + "release_date": "2026-03-18", + "last_updated": "2026-03-18", + "cost": { + "input": 1, + "output": 3, + "cache_read": 0.2, + "tiers": [ + { + "input": 2, + "output": 6, + "cache_read": 0.4, + "tier": { + "type": "context", + "size": 256000 + } + } + ], + "context_over_200k": { + "input": 2, + "output": 6, + "cache_read": 0.4 + } + }, + "type": "chat" + }, + { + "id": "kimi-k2.6", + "name": "Kimi K2.6", + "display_name": "Kimi K2.6", "modalities": { "input": [ "text", @@ -156277,6 +156548,47 @@ }, "type": "chat" }, + { + "id": "kimi-k3", + "name": "Kimi K3", + "display_name": "Kimi K3", + "modalities": { + "input": [ + "text", + "image", + "video" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1048576, + "output": 131072 + }, + "temperature": true, + "tool_call": true, + "reasoning": { + "supported": true, + "default": true + }, + "extra_capabilities": { + "reasoning": { + "supported": true, + "interleaved": true, + "summaries": true, + "visibility": "summary", + "continuation": [ + "thinking_blocks" + ] + } + }, + "attachment": true, + "open_weights": true, + "release_date": "2026-07-16", + "last_updated": "2026-07-16", + "type": "chat" + }, { "id": "kimi-k2-thinking", "name": "Kimi K2 Thinking", @@ -158404,8 +158716,8 @@ ] }, "limit": { - "context": 1048576, - "output": 1048576 + "context": 256000, + "output": 256000 }, "temperature": true, "tool_call": true, @@ -175414,8 +175726,7 @@ "display_name": "MiMo V2.5 Pro", "modalities": { "input": [ - "text", - "pdf" + "text" ], "output": [ "text" @@ -176649,36 +176960,44 @@ "type": "chat" }, { - "id": "recraft/recraft-v4.1-utility", - "name": "Recraft V4.1 Utility", - "display_name": "Recraft V4.1 Utility", + "id": "thinkingmachines/inkling", + "name": "Inkling", + "display_name": "Inkling", "modalities": { "input": [ - "text" + "text", + "image", + "pdf" ], "output": [ - "image" + "text" ] }, "limit": { - "context": 8192, - "output": 8192 + "context": 256000, + "output": 256000 }, "temperature": true, - "tool_call": false, + "tool_call": true, "reasoning": { - "supported": false + "supported": true, + "default": true + }, + "attachment": true, + "open_weights": true, + "release_date": "2026-07-15", + "last_updated": "2026-07-15", + "cost": { + "input": 1, + "output": 4.05, + "cache_read": 0.17 }, - "attachment": false, - "open_weights": false, - "release_date": "2026-05-14", - "last_updated": "2026-05-14", "type": "chat" }, { - "id": "recraft/recraft-v4", - "name": "Recraft V4", - "display_name": "Recraft V4", + "id": "recraft/recraft-v4.1-utility", + "name": "Recraft V4.1 Utility", + "display_name": "Recraft V4.1 Utility", "modalities": { "input": [ "text" @@ -176698,14 +177017,14 @@ }, "attachment": false, "open_weights": false, - "release_date": "2026-02-17", - "last_updated": "2026-02-17", + "release_date": "2026-05-14", + "last_updated": "2026-05-14", "type": "chat" }, { - "id": "recraft/recraft-v4.1-utility-pro", - "name": "Recraft V4.1 Utility Pro", - "display_name": "Recraft V4.1 Utility Pro", + "id": "recraft/recraft-v4", + "name": "Recraft V4", + "display_name": "Recraft V4", "modalities": { "input": [ "text" @@ -176725,14 +177044,41 @@ }, "attachment": false, "open_weights": false, - "release_date": "2026-05-14", - "last_updated": "2026-05-14", + "release_date": "2026-02-17", + "last_updated": "2026-02-17", "type": "chat" }, { - "id": "recraft/recraft-v4.1-pro", - "name": "Recraft V4.1 Pro", - "display_name": "Recraft V4.1 Pro", + "id": "recraft/recraft-v4.1-utility-pro", + "name": "Recraft V4.1 Utility Pro", + "display_name": "Recraft V4.1 Utility Pro", + "modalities": { + "input": [ + "text" + ], + "output": [ + "image" + ] + }, + "limit": { + "context": 8192, + "output": 8192 + }, + "temperature": true, + "tool_call": false, + "reasoning": { + "supported": false + }, + "attachment": false, + "open_weights": false, + "release_date": "2026-05-14", + "last_updated": "2026-05-14", + "type": "chat" + }, + { + "id": "recraft/recraft-v4.1-pro", + "name": "Recraft V4.1 Pro", + "display_name": "Recraft V4.1 Pro", "modalities": { "input": [ "text" @@ -177673,6 +178019,69 @@ }, "type": "chat" }, + { + "id": "anthropic/claude-opus-4.8-fast", + "name": "Claude Opus 4.8 (Fast)", + "display_name": "Claude Opus 4.8 (Fast)", + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1000000, + "output": 128000 + }, + "temperature": true, + "tool_call": true, + "reasoning": { + "supported": true, + "default": false + }, + "extra_capabilities": { + "reasoning": { + "supported": true, + "default_enabled": false, + "mode": "effort", + "effort": "high", + "effort_options": [ + "low", + "medium", + "high", + "xhigh", + "max" + ], + "interleaved": true, + "summaries": true, + "visibility": "omitted", + "continuation": [ + "thinking_blocks" + ], + "notes": [ + "Claude Opus 4.7 and newer Opus models require thinking.type = \"adaptive\" to enable thinking explicitly.", + "Manual budget_tokens requests return 400 on Claude Opus 4.7 and newer adaptive-only Opus models.", + "task_budget is separate from thinking control and should not be treated as a thinking budget." + ] + } + }, + "attachment": true, + "open_weights": false, + "knowledge": "2026-01", + "release_date": "2026-05-28", + "last_updated": "2026-05-28", + "cost": { + "input": 10, + "output": 50, + "cache_read": 1, + "cache_write": 12.5 + }, + "type": "chat" + }, { "id": "anthropic/claude-opus-4.8", "name": "Claude Opus 4.8", @@ -178202,6 +178611,69 @@ }, "type": "chat" }, + { + "id": "anthropic/claude-opus-4.7-fast", + "name": "Claude Opus 4.7 (Fast)", + "display_name": "Claude Opus 4.7 (Fast)", + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1000000, + "output": 128000 + }, + "temperature": true, + "tool_call": true, + "reasoning": { + "supported": true, + "default": false + }, + "extra_capabilities": { + "reasoning": { + "supported": true, + "default_enabled": false, + "mode": "effort", + "effort": "high", + "effort_options": [ + "low", + "medium", + "high", + "xhigh", + "max" + ], + "interleaved": true, + "summaries": true, + "visibility": "omitted", + "continuation": [ + "thinking_blocks" + ], + "notes": [ + "Claude Opus 4.7 and newer Opus models require thinking.type = \"adaptive\" to enable thinking explicitly.", + "Manual budget_tokens requests return 400 on Claude Opus 4.7 and newer adaptive-only Opus models.", + "task_budget is separate from thinking control and should not be treated as a thinking budget." + ] + } + }, + "attachment": true, + "open_weights": false, + "knowledge": "2026-01-31", + "release_date": "2026-04-16", + "last_updated": "2026-04-16", + "cost": { + "input": 30, + "output": 150, + "cache_read": 3, + "cache_write": 37.5 + }, + "type": "chat" + }, { "id": "anthropic/claude-opus-4.1", "name": "Claude Opus 4.1", @@ -179021,6 +179493,46 @@ }, "type": "chat" }, + { + "id": "moonshotai/kimi-k3", + "name": "Kimi K3", + "display_name": "Kimi K3", + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1000000, + "output": 131072 + }, + "temperature": true, + "tool_call": true, + "reasoning": { + "supported": true, + "default": true + }, + "extra_capabilities": { + "reasoning": { + "supported": true + } + }, + "attachment": true, + "open_weights": false, + "release_date": "2026-07-16", + "last_updated": "2026-07-16", + "cost": { + "input": 3, + "output": 15, + "cache_read": 0.3 + }, + "type": "chat" + }, { "id": "moonshotai/kimi-k2-thinking", "name": "Kimi K2 Thinking", @@ -183597,6 +184109,33 @@ "last_updated": "2024-01-25", "type": "embedding" }, + { + "id": "openai/gpt-realtime-whisper", + "name": "gpt-realtime-whisper", + "display_name": "gpt-realtime-whisper", + "modalities": { + "input": [ + "audio" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 8192, + "output": 8192 + }, + "temperature": true, + "tool_call": false, + "reasoning": { + "supported": false + }, + "attachment": false, + "open_weights": false, + "release_date": "2026-05-07", + "last_updated": "2026-05-07", + "type": "chat" + }, { "id": "openai/gpt-5.1-thinking", "name": "GPT 5.1 Thinking", @@ -226933,6 +227472,25 @@ "name": "PPInfra", "display_name": "PPInfra", "models": [ + { + "id": "moonshotai/kimi-k3", + "name": "Kimi K3", + "display_name": "Kimi K3", + "limit": { + "context": 1048576 + }, + "tool_call": true, + "reasoning": { + "supported": true, + "default": true + }, + "extra_capabilities": { + "reasoning": { + "supported": true + } + }, + "type": "chat" + }, { "id": "zai-org/glm-5.2", "name": "GLM 5.2", @@ -234348,6 +234906,38 @@ }, "type": "chat" }, + { + "id": "kimi-k3", + "name": "kimi-k3", + "display_name": "kimi-k3", + "modalities": { + "input": [ + "text", + "image", + "video" + ] + }, + "limit": { + "context": 1048576, + "output": 1048576 + }, + "tool_call": true, + "reasoning": { + "supported": true, + "default": true + }, + "extra_capabilities": { + "reasoning": { + "supported": true + } + }, + "cost": { + "input": 3, + "output": 15, + "cache_read": 0.3 + }, + "type": "chat" + }, { "id": "claude-sonnet-5", "name": "claude-sonnet-5", @@ -234835,6 +235425,38 @@ }, "type": "chat" }, + { + "id": "coding-kimi-k3", + "name": "coding-kimi-k3", + "display_name": "coding-kimi-k3", + "modalities": { + "input": [ + "text", + "image", + "video" + ] + }, + "limit": { + "context": 1048576, + "output": 1048576 + }, + "tool_call": true, + "reasoning": { + "supported": true, + "default": true + }, + "extra_capabilities": { + "reasoning": { + "supported": true + } + }, + "cost": { + "input": 0.12, + "output": 0.439999, + "cache_read": 0.012 + }, + "type": "chat" + }, { "id": "coding-glm-5.2-free", "name": "coding-glm-5.2-free", @@ -234866,20 +235488,21 @@ "type": "chat" }, { - "id": "gemini-3.1-flash-image", - "name": "gemini-3.1-flash-image", - "display_name": "gemini-3.1-flash-image", + "id": "coding-kimi-k3-free", + "name": "coding-kimi-k3-free", + "display_name": "coding-kimi-k3-free", "modalities": { "input": [ "text", - "image" + "image", + "video" ] }, "limit": { - "context": 8192, - "output": 8192 + "context": 1048576, + "output": 1048576 }, - "tool_call": false, + "tool_call": true, "reasoning": { "supported": true, "default": true @@ -234890,11 +235513,11 @@ } }, "cost": { - "input": 0.5, - "output": 3, - "cache_read": 0.5 + "input": 0, + "output": 0, + "cache_read": 0 }, - "type": "imageGeneration" + "type": "chat" }, { "id": "kimi-k2.7-code", @@ -234961,34 +235584,9 @@ "type": "chat" }, { - "id": "kling-v3-omni", - "name": "kling-v3-omni", - "display_name": "kling-v3-omni", - "modalities": { - "input": [ - "text", - "image" - ] - }, - "limit": { - "context": 8192, - "output": 8192 - }, - "tool_call": false, - "reasoning": { - "supported": false - }, - "cost": { - "input": 2, - "output": 0, - "cache_read": 0 - }, - "type": "chat" - }, - { - "id": "kling-video-o1", - "name": "kling-video-o1", - "display_name": "kling-video-o1", + "id": "gemini-3.1-flash-image", + "name": "gemini-3.1-flash-image", + "display_name": "gemini-3.1-flash-image", "modalities": { "input": [ "text", @@ -235000,30 +235598,6 @@ "output": 8192 }, "tool_call": false, - "reasoning": { - "supported": false - }, - "cost": { - "input": 2, - "output": 0, - "cache_read": 0 - }, - "type": "chat" - }, - { - "id": "longcat-2.0", - "name": "longcat-2.0", - "display_name": "longcat-2.0", - "modalities": { - "input": [ - "text" - ] - }, - "limit": { - "context": 8192, - "output": 8192 - }, - "tool_call": true, "reasoning": { "supported": true, "default": true @@ -235034,11 +235608,11 @@ } }, "cost": { - "input": 0.7746, - "output": 3.0984, - "cache_read": 0.015492 + "input": 0.5, + "output": 3, + "cache_read": 0.5 }, - "type": "chat" + "type": "imageGeneration" }, { "id": "gemini-3-pro-image", @@ -235145,6 +235719,86 @@ }, "type": "chat" }, + { + "id": "kling-v3-omni", + "name": "kling-v3-omni", + "display_name": "kling-v3-omni", + "modalities": { + "input": [ + "text", + "image" + ] + }, + "limit": { + "context": 8192, + "output": 8192 + }, + "tool_call": false, + "reasoning": { + "supported": false + }, + "cost": { + "input": 2, + "output": 0, + "cache_read": 0 + }, + "type": "chat" + }, + { + "id": "kling-video-o1", + "name": "kling-video-o1", + "display_name": "kling-video-o1", + "modalities": { + "input": [ + "text", + "image" + ] + }, + "limit": { + "context": 8192, + "output": 8192 + }, + "tool_call": false, + "reasoning": { + "supported": false + }, + "cost": { + "input": 2, + "output": 0, + "cache_read": 0 + }, + "type": "chat" + }, + { + "id": "longcat-2.0", + "name": "longcat-2.0", + "display_name": "longcat-2.0", + "modalities": { + "input": [ + "text" + ] + }, + "limit": { + "context": 8192, + "output": 8192 + }, + "tool_call": true, + "reasoning": { + "supported": true, + "default": true + }, + "extra_capabilities": { + "reasoning": { + "supported": true + } + }, + "cost": { + "input": 0.7746, + "output": 3.0984, + "cache_read": 0.015492 + }, + "type": "chat" + }, { "id": "hy3-preview", "name": "hy3-preview", @@ -235463,18 +236117,17 @@ "type": "chat" }, { - "id": "grok-4.3", - "name": "grok-4.3", - "display_name": "grok-4.3", + "id": "coding-glm-5.2", + "name": "coding-glm-5.2", + "display_name": "coding-glm-5.2", "modalities": { "input": [ - "text", - "image" + "text" ] }, "limit": { - "context": 1000000, - "output": 1000000 + "context": 8192, + "output": 8192 }, "tool_call": true, "reasoning": { @@ -235487,24 +236140,24 @@ } }, "cost": { - "input": 1.25, - "output": 2.5, - "cache_read": 0.2 + "input": 0.06, + "output": 0.22 }, "type": "chat" }, { - "id": "coding-glm-5.2", - "name": "coding-glm-5.2", - "display_name": "coding-glm-5.2", + "id": "grok-4.3", + "name": "grok-4.3", + "display_name": "grok-4.3", "modalities": { "input": [ - "text" + "text", + "image" ] }, "limit": { - "context": 8192, - "output": 8192 + "context": 1000000, + "output": 1000000 }, "tool_call": true, "reasoning": { @@ -235517,8 +236170,9 @@ } }, "cost": { - "input": 0.06, - "output": 0.22 + "input": 1.25, + "output": 2.5, + "cache_read": 0.2 }, "type": "chat" }, @@ -241877,13 +242531,12 @@ "type": "chat" }, { - "id": "wan2.2-i2v-plus", - "name": "wan2.2-i2v-plus", - "display_name": "wan2.2-i2v-plus", + "id": "wan2.5-t2v-preview", + "name": "wan2.5-t2v-preview", + "display_name": "wan2.5-t2v-preview", "modalities": { "input": [ - "text", - "image" + "text" ] }, "limit": { @@ -241901,12 +242554,13 @@ "type": "chat" }, { - "id": "wan2.5-t2v-preview", - "name": "wan2.5-t2v-preview", - "display_name": "wan2.5-t2v-preview", + "id": "wan2.5-i2v-preview", + "name": "wan2.5-i2v-preview", + "display_name": "wan2.5-i2v-preview", "modalities": { "input": [ - "text" + "text", + "image" ] }, "limit": { @@ -241924,9 +242578,9 @@ "type": "chat" }, { - "id": "wan2.5-i2v-preview", - "name": "wan2.5-i2v-preview", - "display_name": "wan2.5-i2v-preview", + "id": "wan2.2-i2v-plus", + "name": "wan2.2-i2v-plus", + "display_name": "wan2.2-i2v-plus", "modalities": { "input": [ "text", @@ -247299,24 +247953,6 @@ }, "type": "chat" }, - { - "id": "tencent/Hunyuan-A13B-Instruct", - "name": "tencent/Hunyuan-A13B-Instruct", - "display_name": "tencent/Hunyuan-A13B-Instruct", - "limit": { - "context": 8192, - "output": 8192 - }, - "tool_call": false, - "reasoning": { - "supported": false - }, - "cost": { - "input": 0.14, - "output": 0.56 - }, - "type": "chat" - }, { "id": "MiniMaxAI/MiniMax-M1-80k", "name": "MiniMaxAI/MiniMax-M1-80k", @@ -247451,6 +248087,24 @@ }, "type": "chat" }, + { + "id": "tencent/Hunyuan-A13B-Instruct", + "name": "tencent/Hunyuan-A13B-Instruct", + "display_name": "tencent/Hunyuan-A13B-Instruct", + "limit": { + "context": 8192, + "output": 8192 + }, + "tool_call": false, + "reasoning": { + "supported": false + }, + "cost": { + "input": 0.14, + "output": 0.56 + }, + "type": "chat" + }, { "id": "unsloth/gemma-3-27b-it", "name": "unsloth/gemma-3-27b-it", @@ -247644,24 +248298,6 @@ }, "type": "chat" }, - { - "id": "tencent/Hunyuan-MT-7B", - "name": "tencent/Hunyuan-MT-7B", - "display_name": "tencent/Hunyuan-MT-7B", - "limit": { - "context": 8192, - "output": 8192 - }, - "tool_call": false, - "reasoning": { - "supported": false - }, - "cost": { - "input": 0.2, - "output": 0.2 - }, - "type": "chat" - }, { "id": "BAAI/bge-large-en-v1.5", "name": "BAAI/bge-large-en-v1.5", @@ -247734,6 +248370,24 @@ }, "type": "rerank" }, + { + "id": "tencent/Hunyuan-MT-7B", + "name": "tencent/Hunyuan-MT-7B", + "display_name": "tencent/Hunyuan-MT-7B", + "limit": { + "context": 8192, + "output": 8192 + }, + "tool_call": false, + "reasoning": { + "supported": false + }, + "cost": { + "input": 0.2, + "output": 0.2 + }, + "type": "chat" + }, { "id": "V3", "name": "V3", @@ -253994,9 +254648,14 @@ "type": "chat" }, { - "id": "text-ada-001", - "name": "text-ada-001", - "display_name": "text-ada-001", + "id": "tts-1-hd-1106", + "name": "tts-1-hd-1106", + "display_name": "tts-1-hd-1106", + "modalities": { + "input": [ + "audio" + ] + }, "limit": { "context": 8192, "output": 8192 @@ -254006,15 +254665,14 @@ "supported": false }, "cost": { - "input": 0.4, - "output": 0.4 - }, - "type": "chat" + "input": 30, + "output": 30 + } }, { - "id": "tts-1", - "name": "tts-1", - "display_name": "tts-1", + "id": "tts-1-hd", + "name": "tts-1-hd", + "display_name": "tts-1-hd", "modalities": { "input": [ "audio" @@ -254029,14 +254687,19 @@ "supported": false }, "cost": { - "input": 15, - "output": 15 + "input": 30, + "output": 30 } }, { - "id": "text-search-ada-doc-001", - "name": "text-search-ada-doc-001", - "display_name": "text-search-ada-doc-001", + "id": "tts-1-1106", + "name": "tts-1-1106", + "display_name": "tts-1-1106", + "modalities": { + "input": [ + "audio" + ] + }, "limit": { "context": 8192, "output": 8192 @@ -254046,15 +254709,14 @@ "supported": false }, "cost": { - "input": 20, - "output": 20 - }, - "type": "chat" + "input": 15, + "output": 15 + } }, { - "id": "tts-1-hd-1106", - "name": "tts-1-hd-1106", - "display_name": "tts-1-hd-1106", + "id": "tts-1", + "name": "tts-1", + "display_name": "tts-1", "modalities": { "input": [ "audio" @@ -254069,14 +254731,14 @@ "supported": false }, "cost": { - "input": 30, - "output": 30 + "input": 15, + "output": 15 } }, { - "id": "text-moderation-stable", - "name": "text-moderation-stable", - "display_name": "text-moderation-stable", + "id": "text-search-ada-doc-001", + "name": "text-search-ada-doc-001", + "display_name": "text-search-ada-doc-001", "limit": { "context": 8192, "output": 8192 @@ -254086,15 +254748,15 @@ "supported": false }, "cost": { - "input": 0.2, - "output": 0.2 + "input": 20, + "output": 20 }, "type": "chat" }, { - "id": "text-moderation-latest", - "name": "text-moderation-latest", - "display_name": "text-moderation-latest", + "id": "text-moderation-007", + "name": "text-moderation-007", + "display_name": "text-moderation-007", "limit": { "context": 8192, "output": 8192 @@ -254110,9 +254772,14 @@ "type": "chat" }, { - "id": "text-moderation-007", - "name": "text-moderation-007", - "display_name": "text-moderation-007", + "id": "text-embedding-ada-002", + "name": "text-embedding-ada-002", + "display_name": "text-embedding-ada-002", + "modalities": { + "input": [ + "text" + ] + }, "limit": { "context": 8192, "output": 8192 @@ -254122,18 +254789,18 @@ "supported": false }, "cost": { - "input": 0.2, - "output": 0.2 + "input": 0.1, + "output": 0.1 }, - "type": "chat" + "type": "embedding" }, { - "id": "tts-1-hd", - "name": "tts-1-hd", - "display_name": "tts-1-hd", + "id": "text-embedding-3-small", + "name": "text-embedding-3-small", + "display_name": "text-embedding-3-small", "modalities": { "input": [ - "audio" + "text" ] }, "limit": { @@ -254145,14 +254812,15 @@ "supported": false }, "cost": { - "input": 30, - "output": 30 - } + "input": 0.02, + "output": 0.02 + }, + "type": "embedding" }, { - "id": "text-embedding-v1", - "name": "text-embedding-v1", - "display_name": "text-embedding-v1", + "id": "text-embedding-3-large", + "name": "text-embedding-3-large", + "display_name": "text-embedding-3-large", "modalities": { "input": [ "text" @@ -254167,20 +254835,15 @@ "supported": false }, "cost": { - "input": 0.1, - "output": 0.1 + "input": 0.13, + "output": 0.13 }, "type": "embedding" }, { - "id": "text-embedding-ada-002", - "name": "text-embedding-ada-002", - "display_name": "text-embedding-ada-002", - "modalities": { - "input": [ - "text" - ] - }, + "id": "text-moderation-stable", + "name": "text-moderation-stable", + "display_name": "text-moderation-stable", "limit": { "context": 8192, "output": 8192 @@ -254190,20 +254853,15 @@ "supported": false }, "cost": { - "input": 0.1, - "output": 0.1 + "input": 0.2, + "output": 0.2 }, - "type": "embedding" + "type": "chat" }, { - "id": "text-embedding-3-small", - "name": "text-embedding-3-small", - "display_name": "text-embedding-3-small", - "modalities": { - "input": [ - "text" - ] - }, + "id": "text-davinci-edit-001", + "name": "text-davinci-edit-001", + "display_name": "text-davinci-edit-001", "limit": { "context": 8192, "output": 8192 @@ -254213,10 +254871,10 @@ "supported": false }, "cost": { - "input": 0.02, - "output": 0.02 + "input": 20, + "output": 20 }, - "type": "embedding" + "type": "chat" }, { "id": "whisper-1", @@ -254288,14 +254946,9 @@ "type": "chat" }, { - "id": "text-embedding-3-large", - "name": "text-embedding-3-large", - "display_name": "text-embedding-3-large", - "modalities": { - "input": [ - "text" - ] - }, + "id": "text-davinci-003", + "name": "text-davinci-003", + "display_name": "text-davinci-003", "limit": { "context": 8192, "output": 8192 @@ -254305,20 +254958,15 @@ "supported": false }, "cost": { - "input": 0.13, - "output": 0.13 + "input": 20, + "output": 20 }, - "type": "embedding" + "type": "chat" }, { - "id": "tts-1-1106", - "name": "tts-1-1106", - "display_name": "tts-1-1106", - "modalities": { - "input": [ - "audio" - ] - }, + "id": "text-davinci-002", + "name": "text-davinci-002", + "display_name": "text-davinci-002", "limit": { "context": 8192, "output": 8192 @@ -254328,14 +254976,15 @@ "supported": false }, "cost": { - "input": 15, - "output": 15 - } + "input": 20, + "output": 20 + }, + "type": "chat" }, { - "id": "text-davinci-edit-001", - "name": "text-davinci-edit-001", - "display_name": "text-davinci-edit-001", + "id": "text-curie-001", + "name": "text-curie-001", + "display_name": "text-curie-001", "limit": { "context": 8192, "output": 8192 @@ -254345,15 +254994,15 @@ "supported": false }, "cost": { - "input": 20, - "output": 20 + "input": 2, + "output": 2 }, "type": "chat" }, { - "id": "text-davinci-003", - "name": "text-davinci-003", - "display_name": "text-davinci-003", + "id": "text-babbage-001", + "name": "text-babbage-001", + "display_name": "text-babbage-001", "limit": { "context": 8192, "output": 8192 @@ -254363,15 +255012,15 @@ "supported": false }, "cost": { - "input": 20, - "output": 20 + "input": 0.5, + "output": 0.5 }, "type": "chat" }, { - "id": "text-davinci-002", - "name": "text-davinci-002", - "display_name": "text-davinci-002", + "id": "text-ada-001", + "name": "text-ada-001", + "display_name": "text-ada-001", "limit": { "context": 8192, "output": 8192 @@ -254381,15 +255030,15 @@ "supported": false }, "cost": { - "input": 20, - "output": 20 + "input": 0.4, + "output": 0.4 }, "type": "chat" }, { - "id": "text-curie-001", - "name": "text-curie-001", - "display_name": "text-curie-001", + "id": "text-moderation-latest", + "name": "text-moderation-latest", + "display_name": "text-moderation-latest", "limit": { "context": 8192, "output": 8192 @@ -254399,8 +255048,8 @@ "supported": false }, "cost": { - "input": 2, - "output": 2 + "input": 0.2, + "output": 0.2 }, "type": "chat" }, @@ -254513,9 +255162,14 @@ "type": "chat" }, { - "id": "text-babbage-001", - "name": "text-babbage-001", - "display_name": "text-babbage-001", + "id": "text-embedding-v1", + "name": "text-embedding-v1", + "display_name": "text-embedding-v1", + "modalities": { + "input": [ + "text" + ] + }, "limit": { "context": 8192, "output": 8192 @@ -254525,10 +255179,10 @@ "supported": false }, "cost": { - "input": 0.5, - "output": 0.5 + "input": 0.1, + "output": 0.1 }, - "type": "chat" + "type": "embedding" }, { "id": "meta-llama-3-70b", @@ -257185,8 +257839,8 @@ ] }, "limit": { - "context": 131072, - "output": 131072 + "context": 110000, + "output": 110000 }, "tool_call": true, "reasoning": { @@ -257782,8 +258436,8 @@ ] }, "limit": { - "context": 131072, - "output": 131072 + "context": 80000, + "output": 80000 }, "tool_call": false, "reasoning": { @@ -257926,6 +258580,33 @@ }, "type": "imageGeneration" }, + { + "id": "meta/muse-spark-1.1", + "name": "Meta: Muse Spark 1.1", + "display_name": "Meta: Muse Spark 1.1", + "modalities": { + "input": [ + "text", + "image", + "video", + "audio" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1048576, + "output": 1048576 + }, + "tool_call": true, + "reasoning": { + "supported": true, + "default": true + }, + "attachment": true, + "type": "imageGeneration" + }, { "id": "microsoft/phi-4", "name": "Microsoft: Phi 4", @@ -258139,7 +258820,7 @@ ] }, "limit": { - "context": 204800, + "context": 196608, "output": 131072 }, "temperature": true, @@ -258821,6 +259502,35 @@ }, "type": "imageGeneration" }, + { + "id": "moonshotai/kimi-k3", + "name": "MoonshotAI: Kimi K3", + "display_name": "MoonshotAI: Kimi K3", + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1048576, + "output": 1048576 + }, + "tool_call": true, + "reasoning": { + "supported": true, + "default": true + }, + "extra_capabilities": { + "reasoning": { + "supported": true + } + }, + "type": "imageGeneration" + }, { "id": "morph/morph-v3-fast", "name": "Morph: Morph V3 Fast", @@ -264548,6 +265258,36 @@ }, "type": "chat" }, + { + "id": "moonshotai/kimi-k3", + "name": "Kimi K3", + "display_name": "Kimi K3", + "modalities": { + "input": [ + "text", + "image", + "video" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1048576, + "output": 1048576 + }, + "tool_call": true, + "reasoning": { + "supported": true, + "default": true + }, + "extra_capabilities": { + "reasoning": { + "supported": true + } + }, + "type": "chat" + }, { "id": "gpt-5.6-terra", "name": "gpt-5.6-terra", @@ -272599,9 +273339,9 @@ "release_date": "2026-06-12", "last_updated": "2026-06-12", "cost": { - "input": 0.719, - "output": 3.49, - "cache_read": 0.149, + "input": 0.75, + "output": 3.5, + "cache_read": 0.16, "cache_write": 0 }, "type": "chat" @@ -272652,6 +273392,46 @@ }, "type": "chat" }, + { + "id": "moonshotai/kimi-k3", + "name": "MoonshotAI: Kimi K3", + "display_name": "MoonshotAI: Kimi K3", + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "limit": { + "context": 1000000, + "output": 131072 + }, + "temperature": true, + "tool_call": true, + "reasoning": { + "supported": true, + "default": true + }, + "extra_capabilities": { + "reasoning": { + "supported": true + } + }, + "attachment": true, + "open_weights": false, + "release_date": "2026-07-16", + "last_updated": "2026-07-16", + "cost": { + "input": 3, + "output": 15, + "cache_read": 0.3 + }, + "type": "chat" + }, { "id": "openai/chat-latest", "name": "OpenAI: Chat Latest (GPT-5.5 Instant)", @@ -274976,9 +275756,9 @@ "release_date": "2026-05-29", "last_updated": "2026-05-29", "cost": { - "input": 0.2, - "output": 1.15, - "cache_read": 0.04, + "input": 0.19, + "output": 1.14, + "cache_read": 0.03, "cache_write": 0 }, "type": "chat" @@ -275013,9 +275793,9 @@ "release_date": "2026-05-29", "last_updated": "2026-05-29", "cost": { - "input": 0.2, - "output": 1.15, - "cache_read": 0.04, + "input": 0.19, + "output": 1.14, + "cache_read": 0.03, "cache_write": 0 }, "type": "chat" diff --git a/scripts/generate-icon-collections.mjs b/scripts/generate-icon-collections.mjs index 1b1db8a462..36b269334f 100644 --- a/scripts/generate-icon-collections.mjs +++ b/scripts/generate-icon-collections.mjs @@ -33,7 +33,6 @@ const rendererRoots = [ 'src/shared', 'src/renderer/src', 'src/renderer/settings', - 'src/renderer/browser', 'src/renderer/floating', 'src/renderer/splash' ].map((sourcePath) => path.join(repoRoot, sourcePath)) diff --git a/src/renderer/src/lib/icons/icon-collections.generated.ts b/src/renderer/src/lib/icons/icon-collections.generated.ts index 5e88a411ca..5823ab93a7 100644 --- a/src/renderer/src/lib/icons/icon-collections.generated.ts +++ b/src/renderer/src/lib/icons/icon-collections.generated.ts @@ -395,9 +395,6 @@ export const lucideIconCollection = { 'list-tree': { body: '' }, - loader: { - body: '' - }, 'loader-circle': { body: '' }, @@ -724,9 +721,6 @@ export const lucideIconCollection = { 'help-circle': { parent: 'circle-question-mark' }, - 'loader-2': { - parent: 'loader-circle' - }, 'message-circle-question': { parent: 'message-circle-question-mark' }, @@ -746,7 +740,7 @@ export const lucideIconCollection = { parent: 'circle-x' } }, - lastModified: 1782727192, + lastModified: 1783920389, width: 24, height: 24 } as const @@ -807,7 +801,7 @@ export const vscodeIconCollection = { } }, aliases: {}, - lastModified: 1782727390, + lastModified: 1783920591, width: 32, height: 32 } as const diff --git a/src/renderer/src/lib/icons/icon-whitelist.generated.ts b/src/renderer/src/lib/icons/icon-whitelist.generated.ts index e6e57a70ea..f8ee59e1ba 100644 --- a/src/renderer/src/lib/icons/icon-whitelist.generated.ts +++ b/src/renderer/src/lib/icons/icon-whitelist.generated.ts @@ -138,8 +138,6 @@ export const GENERATED_ICON_WHITELIST: Record Date: Fri, 17 Jul 2026 18:14:04 +0800 Subject: [PATCH 03/12] fix(renderer): address startup review feedback --- .../src/apps/chat-main/ChatMainApp.vue | 17 ++++++++++------ test/renderer/components/ChatTabView.test.ts | 20 ++++++++++++++++++- 2 files changed, 30 insertions(+), 7 deletions(-) diff --git a/src/renderer/src/apps/chat-main/ChatMainApp.vue b/src/renderer/src/apps/chat-main/ChatMainApp.vue index 0aea96f702..e4703eb9a7 100644 --- a/src/renderer/src/apps/chat-main/ChatMainApp.vue +++ b/src/renderer/src/apps/chat-main/ChatMainApp.vue @@ -156,8 +156,8 @@ const displayError = (error: { id: string; title: string; message: string; type: variant: 'destructive', onOpenChange: (open) => { if (!open) { - // Also show next error when user manually closes - handleErrorClosed() + // Also show the next error when the current toast closes. + handleErrorClosed(error.id) } } }) @@ -168,14 +168,19 @@ const displayError = (error: { id: string; title: string; message: string; type: } errorDisplayTimer = window.setTimeout(() => { - // Handle logic after error is closed + // Dismissal invokes onOpenChange(false). Call the handler as a fallback for + // environments where it does not, while its ID guard prevents double advancement. dismiss() - handleErrorClosed() + handleErrorClosed(error.id) }, 3000) } -// Handle logic after error is closed -const handleErrorClosed = () => { +// Handle logic after an error toast closes +const handleErrorClosed = (errorId: string) => { + if (currentErrorId.value !== errorId) { + return + } + // Clear current error ID currentErrorId.value = null diff --git a/test/renderer/components/ChatTabView.test.ts b/test/renderer/components/ChatTabView.test.ts index 65d5db601e..65c0be0559 100644 --- a/test/renderer/components/ChatTabView.test.ts +++ b/test/renderer/components/ChatTabView.test.ts @@ -101,7 +101,10 @@ const setup = async (options: SetupOptions = {}) => { return { startupRunId: 'run-1', - activeSessionId: options.bootstrapActiveSessionId ?? sessionStore.activeSessionId, + activeSessionId: + options.bootstrapActiveSessionId === undefined + ? sessionStore.activeSessionId + : options.bootstrapActiveSessionId, activeSession: options.bootstrapActiveSessionId === undefined ? sessionStore.activeSession @@ -258,6 +261,21 @@ describe('ChatTabView startup and routing', () => { expect(sessionStore.activeSessionId).toBe('bootstrap-session') }) + it('preserves an explicit null bootstrap session id', async () => { + const { sessionStore } = await setup({ + currentRoute: 'chat', + activeSessionId: 'session-1', + bootstrapActiveSessionId: null + }) + + expect(sessionStore.applyBootstrapShell).toHaveBeenCalledWith( + expect.objectContaining({ + activeSessionId: null, + activeSession: null + }) + ) + }) + it('hydrates the route from the session store state and keeps provider warmup on demand', async () => { const { pageRouter, agentStore, projectStore, sessionStore, markStartupInteractive } = await setup({ From 04171d5d17d2ae2e8378c53ae9825069c3463a99 Mon Sep 17 00:00:00 2001 From: zhangmo8 Date: Fri, 17 Jul 2026 18:50:59 +0800 Subject: [PATCH 04/12] refactor(renderer): localize chat page feature --- .../chatpage-decomposition/spec.md | 15 +- .../chatpage-decomposition/tasks.md | 7 +- .../renderer-application-boundaries/plan.md | 3 +- .../renderer-application-boundaries/tasks.md | 6 +- docs/guides/getting-started.md | 2 +- scripts/agent-cleanup-guard.mjs | 4 +- scripts/generate-architecture-baseline.mjs | 2 +- .../chat-page}/ChatPage.vue | 146 +++---------- .../chat-page/composables}/useChatSearch.ts | 0 .../composables}/useComposerSubmit.ts | 0 .../composables}/useDisplayMessages.ts | 0 .../chat-page/composables}/useListGestures.ts | 0 .../composables}/useMessageVirtualization.ts | 0 .../composables}/usePlanFloatLifecycle.ts | 0 .../composables}/useSessionRestore.ts | 0 .../chat-page/composables/useVoiceInput.ts | 174 ++++++++++++++++ src/renderer/src/views/ChatTabView.vue | 2 +- test/renderer/components/ChatPage.test.ts | 2 +- test/renderer/components/ChatTabView.test.ts | 2 +- .../composables/useVoiceInput.test.ts | 194 ++++++++++++++++++ 20 files changed, 423 insertions(+), 136 deletions(-) rename src/renderer/src/{pages => features/chat-page}/ChatPage.vue (93%) rename src/renderer/src/{pages/chat-page => features/chat-page/composables}/useChatSearch.ts (100%) rename src/renderer/src/{pages/chat-page => features/chat-page/composables}/useComposerSubmit.ts (100%) rename src/renderer/src/{pages/chat-page => features/chat-page/composables}/useDisplayMessages.ts (100%) rename src/renderer/src/{pages/chat-page => features/chat-page/composables}/useListGestures.ts (100%) rename src/renderer/src/{pages/chat-page => features/chat-page/composables}/useMessageVirtualization.ts (100%) rename src/renderer/src/{pages/chat-page => features/chat-page/composables}/usePlanFloatLifecycle.ts (100%) rename src/renderer/src/{pages/chat-page => features/chat-page/composables}/useSessionRestore.ts (100%) create mode 100644 src/renderer/src/features/chat-page/composables/useVoiceInput.ts create mode 100644 test/renderer/features/chat-page/composables/useVoiceInput.test.ts diff --git a/docs/architecture/chatpage-decomposition/spec.md b/docs/architecture/chatpage-decomposition/spec.md index b4d9f444e1..d418b969a0 100644 --- a/docs/architecture/chatpage-decomposition/spec.md +++ b/docs/architecture/chatpage-decomposition/spec.md @@ -2,7 +2,8 @@ ## 背景 -`src/renderer/src/pages/ChatPage.vue` 已膨胀到 3050 行,单个 ` diff --git a/src/renderer/src/features/chat-page/ChatPage.vue b/src/renderer/src/features/chat-page/ChatPage.vue index ec2b177a4d..6460bc8a04 100644 --- a/src/renderer/src/features/chat-page/ChatPage.vue +++ b/src/renderer/src/features/chat-page/ChatPage.vue @@ -247,7 +247,7 @@