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/chat-display-model-boundary/plan.md b/docs/architecture/chat-display-model-boundary/plan.md
new file mode 100644
index 0000000000..bce0141698
--- /dev/null
+++ b/docs/architecture/chat-display-model-boundary/plan.md
@@ -0,0 +1,34 @@
+# 实施计划
+
+## 1. 建立 feature model
+
+在 `src/renderer/src/features/chat-page/model/displayMessage.ts` 原样迁移
+`messageListItems.ts` 的所有 type 和 pure helper:
+
+- `DisplayMessage` 家族与 `MessageListItem`;
+- assistant block 的 internal tool、filter、presence 策略;
+- compaction 判定。
+
+新模块只保留对 `@shared/types/*` 的 type/value 依赖,因此可以被 feature、component、store 和
+纯 UI helper 使用而不引入运行时分层循环。
+
+## 2. 原子切换消费者
+
+把 renderer source、renderer tests 和 fixtures 的 import 改为
+`@/features/chat-page/model/displayMessage`。特别保留 component 对 feature model 的单向读取:
+组件不会导入 ChatPage、feature composable 或 store。
+
+删除旧的 `components/chat/messageListItems.ts`,不建立过渡 re-export;这样新增引用无法继续落在
+错误的 component 目录。
+
+## 3. 验证
+
+1. 搜索确认旧模块 import 为零。
+2. 执行 formatter;随后执行 i18n、lint 与 renderer type check。
+3. 运行直接覆盖消息 list/window、message block 和 chat feature composable 的 targeted suites;如
+ repository runner 支持,再执行完整 renderer test suite。
+
+## 兼容性与回滚
+
+模块导出名和实现不变,调用方数据格式与运行时行为不变。若出现解析或架构问题,单次提交即可将
+imports 和文件位置恢复,无需迁移数据或 IPC contract。
diff --git a/docs/architecture/chat-display-model-boundary/spec.md b/docs/architecture/chat-display-model-boundary/spec.md
new file mode 100644
index 0000000000..91fb983ab0
--- /dev/null
+++ b/docs/architecture/chat-display-model-boundary/spec.md
@@ -0,0 +1,35 @@
+# Chat 展示模型边界收敛
+
+## 背景
+
+`DisplayMessage`、其 block/usage 契约,以及 assistant block 的可渲染性策略目前定义在
+`src/renderer/src/components/chat/messageListItems.ts`。但记录到展示消息的转换、流式尾部拼装、
+是否显示空 assistant 消息的决策都由 `features/chat-page` 持有。feature 因而反向依赖 UI 目录以
+取得自己的领域展示模型,模型所有权与实际业务所有权不一致。
+
+这些契约同时被 chat list、message block 组件、message store、chat-only composable 与测试使用。
+它们不是可复用通用 UI 的模型,而是 chat-page feature 的输入/输出契约。
+
+## 目标
+
+1. 将 `DisplayMessage` 家族、`MessageListItem` 与 assistant block 可渲染性策略迁入
+ `src/renderer/src/features/chat-page/model/`。
+2. 所有消费者直接引用 feature model,不保留 `components/chat/messageListItems.ts` 作为第二个
+ 定义或兼容 re-export。
+3. 保持类型形状、过滤条件与 compaction 判定完全不变;这是纯模块边界调整。
+4. 记录 chat 组件可以依赖该 feature 的**纯 model contract**:model 只依赖 shared types,
+ 不依赖 Vue、Pinia、IPC、component 或 composable,且不得反向导入消费者。
+
+## 非目标
+
+- 不调整消息转换、流式缓存、虚拟化或 Markdown 渲染逻辑。
+- 不变更 `MessageList` / `MessageListRow` 的 props、events 或 template。
+- 不迁移通用 renderer foundation,也不改变 IPC/store 数据格式。
+- 不在本切片拆分 `ChatStatusBar` 或继续提取 ChatPage viewport 生命周期。
+
+## 验收标准
+
+1. `components/chat/messageListItems.ts` 不再存在,且 renderer 与测试中没有对其的 import。
+2. 所有原有导出的 type 和 helper 在 feature model 有同名导出,运行行为保持一致。
+3. feature model 不导入 Vue、Pinia、renderer store、renderer API 或 component。
+4. `pnpm run format`、`pnpm run i18n`、`pnpm run lint` 和相关 renderer type/test 校验通过。
diff --git a/docs/architecture/chat-display-model-boundary/tasks.md b/docs/architecture/chat-display-model-boundary/tasks.md
new file mode 100644
index 0000000000..4c91772991
--- /dev/null
+++ b/docs/architecture/chat-display-model-boundary/tasks.md
@@ -0,0 +1,7 @@
+# 任务清单
+
+- [x] 审计展示模型、renderability policy 与全部 source/test 消费者。
+- [x] 在 `features/chat-page/model/` 建立纯展示模型模块。
+- [x] 原子更新 renderer source、tests 与 fixtures 的 import。
+- [x] 删除旧 component-local 模块并确认无残留引用。
+- [x] 执行 format、i18n、lint、typecheck 与 targeted renderer tests。
diff --git a/docs/architecture/chatpage-decomposition/spec.md b/docs/architecture/chatpage-decomposition/spec.md
index b4d9f444e1..59fe8759aa 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 行,单个 `
-
-
-
-
-
-
-
-
-
-
-
-
-
{
- if (!open) modelCheckStore.closeDialog()
- }
- "
- />
-
+
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..eee16723d3
--- /dev/null
+++ b/src/renderer/src/apps/chat-main/ChatMainApp.vue
@@ -0,0 +1,646 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
{
+ if (!open) modelCheckStore.closeDialog()
+ }
+ "
+ />
+
+
diff --git a/src/renderer/src/components/chat/ChatToolInteractionOverlay.vue b/src/renderer/src/components/chat/ChatToolInteractionOverlay.vue
index 2548204813..0b0d3b4f2a 100644
--- a/src/renderer/src/components/chat/ChatToolInteractionOverlay.vue
+++ b/src/renderer/src/components/chat/ChatToolInteractionOverlay.vue
@@ -98,7 +98,7 @@ import { useI18n } from 'vue-i18n'
import { Button } from '@shadcn/components/ui/button'
import { Icon } from '@iconify/vue'
import type { ToolInteractionResponse } from '@shared/types/agent-interface'
-import type { DisplayAssistantMessageBlock } from '@/components/chat/messageListItems'
+import type { DisplayAssistantMessageBlock } from '@/features/chat-page/model/displayMessage'
type PendingInteractionView = {
messageId: string
diff --git a/src/renderer/src/components/chat/MessageList.vue b/src/renderer/src/components/chat/MessageList.vue
index 77fc5939ed..87067c4358 100644
--- a/src/renderer/src/components/chat/MessageList.vue
+++ b/src/renderer/src/components/chat/MessageList.vue
@@ -51,7 +51,7 @@ import {
type DisplayAssistantMessageBlock,
type DisplayMessage,
type MessageListItem
-} from './messageListItems'
+} from '@/features/chat-page/model/displayMessage'
import MessageListRow from './MessageListRow.vue'
const props = withDefaults(
diff --git a/src/renderer/src/components/chat/MessageListRow.vue b/src/renderer/src/components/chat/MessageListRow.vue
index b1b5aedcf8..49b854fa40 100644
--- a/src/renderer/src/components/chat/MessageListRow.vue
+++ b/src/renderer/src/components/chat/MessageListRow.vue
@@ -59,7 +59,7 @@ import {
isCompactionMessageItem,
type DisplayUserMessage,
type MessageListItem
-} from './messageListItems'
+} from '@/features/chat-page/model/displayMessage'
const props = withDefaults(
defineProps<{
diff --git a/src/renderer/src/components/message/MessageBlockAction.vue b/src/renderer/src/components/message/MessageBlockAction.vue
index 9fd3705b98..365ba02cea 100644
--- a/src/renderer/src/components/message/MessageBlockAction.vue
+++ b/src/renderer/src/components/message/MessageBlockAction.vue
@@ -48,7 +48,7 @@ import { useI18n } from 'vue-i18n'
import { Icon } from '@iconify/vue'
import { Button } from '@shadcn/components/ui/button'
import { computed, ref, onMounted, onUnmounted } from 'vue'
-import type { DisplayAssistantMessageBlock } from '@/components/chat/messageListItems'
+import type { DisplayAssistantMessageBlock } from '@/features/chat-page/model/displayMessage'
const { t } = useI18n()
diff --git a/src/renderer/src/components/message/MessageBlockActivityGroup.vue b/src/renderer/src/components/message/MessageBlockActivityGroup.vue
index 1754396209..bb9d4f8604 100644
--- a/src/renderer/src/components/message/MessageBlockActivityGroup.vue
+++ b/src/renderer/src/components/message/MessageBlockActivityGroup.vue
@@ -63,7 +63,7 @@ import { useI18n } from 'vue-i18n'
import type {
DisplayAssistantMessageBlock,
DisplayMessageUsage
-} from '@/components/chat/messageListItems'
+} from '@/features/chat-page/model/displayMessage'
import { formatActivityDuration } from './messageActivityGroups'
import MessageBlockThink from './MessageBlockThink.vue'
import MessageBlockToolCall from './MessageBlockToolCall.vue'
diff --git a/src/renderer/src/components/message/MessageBlockAudio.vue b/src/renderer/src/components/message/MessageBlockAudio.vue
index fa6405fcb0..19c9886751 100644
--- a/src/renderer/src/components/message/MessageBlockAudio.vue
+++ b/src/renderer/src/components/message/MessageBlockAudio.vue
@@ -35,7 +35,7 @@ import { computed, ref } from 'vue'
import { Icon } from '@iconify/vue'
import { useI18n } from 'vue-i18n'
import { Spinner } from '@shadcn/components/ui/spinner'
-import type { DisplayAssistantMessageBlock } from '@/components/chat/messageListItems'
+import type { DisplayAssistantMessageBlock } from '@/features/chat-page/model/displayMessage'
const keyMap = {
'mcp.sampling.contentType.audio': 'Audio',
diff --git a/src/renderer/src/components/message/MessageBlockContent.vue b/src/renderer/src/components/message/MessageBlockContent.vue
index f68920164c..4180c3761b 100644
--- a/src/renderer/src/components/message/MessageBlockContent.vue
+++ b/src/renderer/src/components/message/MessageBlockContent.vue
@@ -47,7 +47,7 @@ import ToolCallPreview from '../artifacts/ToolCallPreview.vue'
import { useBlockContent, type ProcessedPart } from '@/composables/useArtifacts'
import { useArtifactStore } from '@/stores/artifact'
import MarkdownRenderer from '@/components/markdown/MarkdownRenderer.vue'
-import type { DisplayAssistantMessageBlock } from '@/components/chat/messageListItems'
+import type { DisplayAssistantMessageBlock } from '@/features/chat-page/model/displayMessage'
const artifactStore = useArtifactStore()
const props = defineProps<{
diff --git a/src/renderer/src/components/message/MessageBlockError.vue b/src/renderer/src/components/message/MessageBlockError.vue
index 6131759034..66ae84cd9f 100644
--- a/src/renderer/src/components/message/MessageBlockError.vue
+++ b/src/renderer/src/components/message/MessageBlockError.vue
@@ -47,7 +47,7 @@
import { useI18n } from 'vue-i18n'
import { Icon } from '@iconify/vue'
import { computed, ref, useId } from 'vue'
-import type { DisplayAssistantMessageBlock } from '@/components/chat/messageListItems'
+import type { DisplayAssistantMessageBlock } from '@/features/chat-page/model/displayMessage'
const { t } = useI18n()
const props = defineProps<{
diff --git a/src/renderer/src/components/message/MessageBlockImage.vue b/src/renderer/src/components/message/MessageBlockImage.vue
index fbdebad37b..b02a6517be 100644
--- a/src/renderer/src/components/message/MessageBlockImage.vue
+++ b/src/renderer/src/components/message/MessageBlockImage.vue
@@ -76,7 +76,7 @@ import { Button } from '@shadcn/components/ui/button'
import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@shadcn/components/ui/dialog'
import { Spinner } from '@shadcn/components/ui/spinner'
import { Tooltip, TooltipContent, TooltipTrigger } from '@shadcn/components/ui/tooltip'
-import type { DisplayAssistantMessageBlock } from '@/components/chat/messageListItems'
+import type { DisplayAssistantMessageBlock } from '@/features/chat-page/model/displayMessage'
import ImageActionContextMenu from './ImageActionContextMenu.vue'
import { useImageActions } from '@/composables/useImageActions'
diff --git a/src/renderer/src/components/message/MessageBlockQuestionRequest.vue b/src/renderer/src/components/message/MessageBlockQuestionRequest.vue
index 6468dd3a53..2b2d988faa 100644
--- a/src/renderer/src/components/message/MessageBlockQuestionRequest.vue
+++ b/src/renderer/src/components/message/MessageBlockQuestionRequest.vue
@@ -28,7 +28,7 @@