feat(profiles): include Claude Desktop provider in project profiles

Claude Desktop's only dimension managed by cc-switch is its provider
(MCP/Skills are hardcoded unsupported and prompts have no live file),
so snapshots capture just the current desktop provider while the empty
MCP/Skills sets and None prompt make apply a natural no-op for the
other dimensions - no per-dimension special casing needed.

- Add claude-desktop slot to PROFILE_APPS and PerApp (serde key uses
  the hyphenated app id); old payloads without the key deserialize to
  None and leave Claude Desktop untouched on apply
- Gate the tray Projects submenu by iterating PROFILE_APPS instead of
  hardcoding Claude/Codex visibility
- Show the profile switcher on the Claude Desktop tab, mirror the
  PerApp type, invalidate the claude-desktop providers cache on apply,
  and extend the switcher tooltip in all four locales
- Extend the roundtrip integration test with the desktop provider
  dimension; the desktop switch is cfg-gated to macOS/Windows because
  desktop live writes error on Linux where CI runs cargo test
This commit is contained in:
Jason
2026-07-04 18:07:43 +08:00
parent 6179c18805
commit 65a5464fcc
10 changed files with 93 additions and 15 deletions
+4 -4
View File
@@ -42,7 +42,7 @@ import {
import { ProfileManageDialog } from "./ProfileManageDialog";
/** 后端 services/profile.rs 的 PROFILE_APPS 前端镜像,扩展支持范围时两处同步 */
const PROFILE_SUPPORTED_APPS: AppId[] = ["claude", "codex"];
const PROFILE_SUPPORTED_APPS: AppId[] = ["claude", "claude-desktop", "codex"];
interface ProfileSwitcherProps {
activeApp: AppId;
@@ -51,8 +51,8 @@ interface ProfileSwitcherProps {
/**
* 项目 Profile 切换器(header 左侧入口)
*
* Profile 是跨应用的配置快照(Claude Code + Codex 的供应商/MCP/Skills/记忆文件
* 与右侧 AppSwitcher(仅切换查看的应用)语义不同。
* Profile 是跨应用的配置快照(Claude Code + Codex 的供应商/MCP/Skills/记忆文件,
* 以及 Claude Desktop 的供应商),与右侧 AppSwitcher(仅切换查看的应用)语义不同。
*/
export function ProfileSwitcher({ activeApp }: ProfileSwitcherProps) {
const { t } = useTranslation();
@@ -66,7 +66,7 @@ export function ProfileSwitcher({ activeApp }: ProfileSwitcherProps) {
const clearMutation = useClearProfileMutation();
const createMutation = useCreateProfileMutation();
// Profile 仅作用于 Claude Code + Codex——在其他应用的标签页展示会误导用户
// Profile 仅作用于受支持的应用——在其他应用的标签页展示会误导用户
// 以为当前应用也被切换了,因此只在受支持应用的页面渲染
if (!PROFILE_SUPPORTED_APPS.includes(activeApp)) {
return null;
+1 -1
View File
@@ -1867,7 +1867,7 @@
}
},
"profiles": {
"switcherTooltip": "Projects: switch providers / MCP / Skills / memory files for Claude Code and Codex in one click",
"switcherTooltip": "Projects: switch providers / MCP / Skills / memory files for Claude Code and Codex, plus the Claude Desktop provider, in one click",
"none": "No project",
"searchPlaceholder": "Search projects",
"empty": "No projects yet",
+1 -1
View File
@@ -1867,7 +1867,7 @@
}
},
"profiles": {
"switcherTooltip": "プロジェクト:Claude Code と Codex のプロバイダー / MCP / Skills / メモリファイルをワンクリックで切り替え",
"switcherTooltip": "プロジェクト:Claude Code と Codex のプロバイダー / MCP / Skills / メモリファイル、および Claude Desktop のプロバイダーをワンクリックで切り替え",
"none": "プロジェクトを使用しない",
"searchPlaceholder": "プロジェクトを検索",
"empty": "プロジェクトはまだありません",
+1 -1
View File
@@ -1839,7 +1839,7 @@
}
},
"profiles": {
"switcherTooltip": "專案:一鍵切換 Claude Code 與 Codex 的供應商 / MCP / Skills / 記憶檔案",
"switcherTooltip": "專案:一鍵切換 Claude Code 與 Codex 的供應商 / MCP / Skills / 記憶檔案,及 Claude Desktop 的供應商",
"none": "不使用專案",
"searchPlaceholder": "搜尋專案",
"empty": "尚無專案",
+1 -1
View File
@@ -1867,7 +1867,7 @@
}
},
"profiles": {
"switcherTooltip": "项目:一键切换 Claude Code 与 Codex 的供应商 / MCP / Skills / 记忆文件",
"switcherTooltip": "项目:一键切换 Claude Code 与 Codex 的供应商 / MCP / Skills / 记忆文件,及 Claude Desktop 的供应商",
"none": "不使用项目",
"searchPlaceholder": "搜索项目",
"empty": "暂无项目",
+1
View File
@@ -5,6 +5,7 @@ import { invoke } from "@tauri-apps/api/core";
*/
export interface PerApp<T> {
claude: T;
"claude-desktop": T;
codex: T;
}
+3
View File
@@ -118,6 +118,9 @@ export const useApplyProfileMutation = () => {
await queryClient.invalidateQueries({
queryKey: ["providers", "claude"],
});
await queryClient.invalidateQueries({
queryKey: ["providers", "claude-desktop"],
});
await queryClient.invalidateQueries({ queryKey: ["providers", "codex"] });
await queryClient.invalidateQueries({ queryKey: ["mcp", "all"] });
await queryClient.invalidateQueries({ queryKey: ["skills"] });