diff --git a/src-tauri/src/settings.rs b/src-tauri/src/settings.rs index 88f087283..01de0af27 100644 --- a/src-tauri/src/settings.rs +++ b/src-tauri/src/settings.rs @@ -16,6 +16,35 @@ pub struct CustomEndpoint { pub last_used: Option, } +fn default_true() -> bool { + true +} + +/// 主页面显示的应用配置 +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct VisibleApps { + #[serde(default = "default_true")] + pub claude: bool, + #[serde(default = "default_true")] + pub codex: bool, + #[serde(default = "default_true")] + pub gemini: bool, + #[serde(default = "default_true")] + pub opencode: bool, +} + +impl Default for VisibleApps { + fn default() -> Self { + Self { + claude: true, + codex: true, + gemini: true, + opencode: true, + } + } +} + /// 应用设置结构 /// /// 存储设备级别设置,保存在本地 `~/.cc-switch/settings.json`,不随数据库同步。 @@ -40,6 +69,10 @@ pub struct AppSettings { #[serde(default, skip_serializing_if = "Option::is_none")] pub language: Option, + // ===== 主页面显示的应用 ===== + #[serde(default, skip_serializing_if = "Option::is_none")] + pub visible_apps: Option, + // ===== 设备级目录覆盖 ===== #[serde(default, skip_serializing_if = "Option::is_none")] pub claude_config_dir: Option, @@ -73,10 +106,6 @@ fn default_minimize_to_tray_on_close() -> bool { true } -fn default_true() -> bool { - true -} - impl Default for AppSettings { fn default() -> Self { Self { @@ -86,6 +115,7 @@ impl Default for AppSettings { skip_claude_onboarding: true, launch_on_startup: false, language: None, + visible_apps: None, claude_config_dir: None, codex_config_dir: None, gemini_config_dir: None, diff --git a/src/App.tsx b/src/App.tsx index 20c31968d..7b92c8fd4 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -17,9 +17,9 @@ import { Download, BarChart2, } from "lucide-react"; -import type { Provider } from "@/types"; +import type { Provider, VisibleApps } from "@/types"; import type { EnvConflict } from "@/types/env"; -import { useProvidersQuery } from "@/lib/query"; +import { useProvidersQuery, useSettingsQuery } from "@/lib/query"; import { providersApi, settingsApi, @@ -78,6 +78,31 @@ function App() { const [settingsDefaultTab, setSettingsDefaultTab] = useState("general"); const [isAddOpen, setIsAddOpen] = useState(false); + // Get settings for visibleApps + const { data: settingsData } = useSettingsQuery(); + const visibleApps: VisibleApps = settingsData?.visibleApps ?? { + claude: true, + codex: true, + gemini: true, + opencode: true, + }; + + // Get first visible app for fallback + const getFirstVisibleApp = (): AppId => { + if (visibleApps.claude) return "claude"; + if (visibleApps.codex) return "codex"; + if (visibleApps.gemini) return "gemini"; + if (visibleApps.opencode) return "opencode"; + return "claude"; // fallback + }; + + // If current active app is hidden, switch to first visible app + useEffect(() => { + if (!visibleApps[activeApp]) { + setActiveApp(getFirstVisibleApp()); + } + }, [visibleApps, activeApp]); + const [editingProvider, setEditingProvider] = useState(null); const [usageProvider, setUsageProvider] = useState(null); // Confirm action state: 'remove' = remove from live config, 'delete' = delete from database @@ -864,7 +889,7 @@ function App() { )} - +
- - - - - - + {appsToShow.map((app) => ( + + ))}
); } diff --git a/src/components/settings/SettingsPage.tsx b/src/components/settings/SettingsPage.tsx index 3f12a471d..2eaf48089 100644 --- a/src/components/settings/SettingsPage.tsx +++ b/src/components/settings/SettingsPage.tsx @@ -34,6 +34,7 @@ import { settingsApi } from "@/lib/api"; import { LanguageSettings } from "@/components/settings/LanguageSettings"; import { ThemeSettings } from "@/components/settings/ThemeSettings"; import { WindowSettings } from "@/components/settings/WindowSettings"; +import { AppVisibilitySettings } from "@/components/settings/AppVisibilitySettings"; import { DirectorySettings } from "@/components/settings/DirectorySettings"; import { ImportExportSection } from "@/components/settings/ImportExportSection"; import { AboutSection } from "@/components/settings/AboutSection"; @@ -240,6 +241,10 @@ export function SettingsPage({ onChange={(lang) => handleAutoSave({ language: lang })} /> + ); } - -interface ToggleRowProps { - icon: React.ReactNode; - title: string; - description?: string; - checked: boolean; - onCheckedChange: (value: boolean) => void; -} - -function ToggleRow({ - icon, - title, - description, - checked, - onCheckedChange, -}: ToggleRowProps) { - return ( -
-
-
- {icon} -
-
-

{title}

- {description ? ( -

{description}

- ) : null} -
-
- -
- ); -} diff --git a/src/components/ui/toggle-row.tsx b/src/components/ui/toggle-row.tsx new file mode 100644 index 000000000..6df8a37e3 --- /dev/null +++ b/src/components/ui/toggle-row.tsx @@ -0,0 +1,41 @@ +import { Switch } from "@/components/ui/switch"; + +export interface ToggleRowProps { + icon: React.ReactNode; + title: string; + description?: string; + checked: boolean; + onCheckedChange: (value: boolean) => void; + disabled?: boolean; +} + +export function ToggleRow({ + icon, + title, + description, + checked, + onCheckedChange, + disabled, +}: ToggleRowProps) { + return ( +
+
+
+ {icon} +
+
+

{title}

+ {description ? ( +

{description}

+ ) : null} +
+
+ +
+ ); +} diff --git a/src/i18n/locales/en.json b/src/i18n/locales/en.json index 25b07f0a3..bfccd61cc 100644 --- a/src/i18n/locales/en.json +++ b/src/i18n/locales/en.json @@ -272,6 +272,14 @@ "enableClaudePluginIntegrationDescription": "When enabled, the VS Code Claude Code extension provider will switch with this app", "skipClaudeOnboarding": "Skip Claude Code first-run confirmation", "skipClaudeOnboardingDescription": "When enabled, Claude Code will skip the first-run confirmation", + "appVisibility": { + "title": "Homepage Display", + "description": "Choose which apps to show on the homepage", + "claudeDesc": "Anthropic Claude Code CLI", + "codexDesc": "OpenAI Codex CLI", + "geminiDesc": "Google Gemini CLI", + "opencodeDesc": "OpenCode CLI" + }, "configDirectoryOverride": "Configuration Directory Override (Advanced)", "configDirectoryDescription": "When using Claude Code or Codex in environments like WSL, you can manually specify the configuration directory to the one in WSL to keep provider data consistent with the main environment.", "appConfigDir": "CC Switch Configuration Directory", @@ -333,7 +341,7 @@ } }, "apps": { - "claude": "Claude Code", + "claude": "Claude", "codex": "Codex", "gemini": "Gemini", "opencode": "OpenCode" diff --git a/src/i18n/locales/ja.json b/src/i18n/locales/ja.json index 1d16de8cf..7784a9104 100644 --- a/src/i18n/locales/ja.json +++ b/src/i18n/locales/ja.json @@ -272,6 +272,14 @@ "enableClaudePluginIntegrationDescription": "オンにすると VS Code の Claude Code 拡張のプロバイダーも同期します", "skipClaudeOnboarding": "Claude Code の初回確認をスキップ", "skipClaudeOnboardingDescription": "オンにすると Claude Code の初回インストール確認をスキップします", + "appVisibility": { + "title": "ホームページ表示", + "description": "ホームページに表示するアプリを選択", + "claudeDesc": "Anthropic Claude Code CLI", + "codexDesc": "OpenAI Codex CLI", + "geminiDesc": "Google Gemini CLI", + "opencodeDesc": "OpenCode CLI" + }, "configDirectoryOverride": "設定ディレクトリの上書き(詳細)", "configDirectoryDescription": "WSL などで Claude Code や Codex を使う場合、ここで設定ディレクトリを WSL 側に合わせるとデータを揃えられます。", "appConfigDir": "CC Switch 設定ディレクトリ", @@ -333,7 +341,7 @@ } }, "apps": { - "claude": "Claude Code", + "claude": "Claude", "codex": "Codex", "gemini": "Gemini", "opencode": "OpenCode" diff --git a/src/i18n/locales/zh.json b/src/i18n/locales/zh.json index 5462b38e2..d36f1638a 100644 --- a/src/i18n/locales/zh.json +++ b/src/i18n/locales/zh.json @@ -272,6 +272,14 @@ "enableClaudePluginIntegrationDescription": "开启后 Vscode Claude Code 插件的供应商将随本软件切换", "skipClaudeOnboarding": "跳过 Claude Code 初次安装确认", "skipClaudeOnboardingDescription": "开启后跳过 Claude Code 初次安装确认", + "appVisibility": { + "title": "主页面显示", + "description": "选择在主页面显示的应用", + "claudeDesc": "Anthropic Claude Code CLI", + "codexDesc": "OpenAI Codex CLI", + "geminiDesc": "Google Gemini CLI", + "opencodeDesc": "OpenCode CLI" + }, "configDirectoryOverride": "配置目录覆盖(高级)", "configDirectoryDescription": "在 WSL 等环境使用 Claude Code 或 Codex 的时候,可手动指定为 WSL 里的配置目录,供应商数据与主环境保持一致。", "appConfigDir": "CC Switch 配置目录", @@ -333,7 +341,7 @@ } }, "apps": { - "claude": "Claude Code", + "claude": "Claude", "codex": "Codex", "gemini": "Gemini", "opencode": "OpenCode" diff --git a/src/types.ts b/src/types.ts index 9fd73774a..007674302 100644 --- a/src/types.ts +++ b/src/types.ts @@ -137,6 +137,14 @@ export interface ProviderMeta { proxyConfig?: ProviderProxyConfig; } +// 主页面显示的应用配置 +export interface VisibleApps { + claude: boolean; + codex: boolean; + gemini: boolean; + opencode: boolean; +} + // 应用设置类型(用于设置对话框与 Tauri API) // 存储在本地 ~/.cc-switch/settings.json,不随数据库同步 export interface Settings { @@ -154,6 +162,9 @@ export interface Settings { // 首选语言(可选,默认中文) language?: "en" | "zh" | "ja"; + // 主页面显示的应用(默认全部显示) + visibleApps?: VisibleApps; + // ===== 设备级目录覆盖 ===== // 覆盖 Claude Code 配置目录(可选) claudeConfigDir?: string;