feat: add project profiles for snapshot-based config switching

Add a profile feature that captures the current provider, MCP,
skills and prompt state for Claude Code and Codex as a named
snapshot, and re-applies it in one click from the header switcher
or the tray Projects submenu.

- New profiles table (schema v12) with current marker in settings
- ProfileService orchestrates the four existing switch primitives
  (provider first, then MCP diff, skills diff, prompt enable)
- Best-effort apply: dangling references become warnings, no rollback
- Header combobox switcher + snapshot-style manage dialog
- Tray Projects submenu shared with the UI apply/event pipeline
- i18n for zh/en/ja/zh-TW under the new profiles domain
- Integration tests covering roundtrip, dangling refs and clear
This commit is contained in:
Jason
2026-07-04 16:03:19 +08:00
parent b3e5e32c89
commit 8f018a2d45
22 changed files with 1955 additions and 3 deletions
+9
View File
@@ -58,6 +58,7 @@ import {
DRAG_REGION_STYLE,
} from "@/lib/platform";
import { AppSwitcher } from "@/components/AppSwitcher";
import { ProfileSwitcher } from "@/components/profiles/ProfileSwitcher";
import { ProviderList } from "@/components/providers/ProviderList";
import { AddProviderDialog } from "@/components/providers/AddProviderDialog";
import { EditProviderDialog } from "@/components/providers/EditProviderDialog";
@@ -381,6 +382,13 @@ function App() {
}
});
// 托盘应用项目后刷新相关缓存(providers 由既有 provider-switched 监听承接)
useTauriEvent("profile-applied", async () => {
await queryClient.invalidateQueries({ queryKey: ["profiles"] });
await queryClient.invalidateQueries({ queryKey: ["mcp", "all"] });
await queryClient.invalidateQueries({ queryKey: ["skills"] });
});
useTauriEvent<SyncStatusUpdatedPayload | null | undefined>(
"webdav-sync-status-updated",
async (payload) => {
@@ -1189,6 +1197,7 @@ function App() {
CC Switch
</a>
</div>
<ProfileSwitcher visibleApps={visibleApps} />
<Button
variant="ghost"
size="icon"