diff --git a/package.json b/package.json
index 7577bcd..8fdf7fb 100644
--- a/package.json
+++ b/package.json
@@ -37,6 +37,7 @@
"@tauri-apps/api": "^2.8.0",
"@tauri-apps/plugin-dialog": "^2.4.0",
"@tauri-apps/plugin-process": "^2.0.0",
+ "@tauri-apps/plugin-store": "^2.0.0",
"@tauri-apps/plugin-updater": "^2.0.0",
"codemirror": "^6.0.2",
"i18next": "^25.5.2",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 590f7e1..55138f8 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -38,6 +38,9 @@ importers:
'@tauri-apps/plugin-process':
specifier: ^2.0.0
version: 2.3.0
+ '@tauri-apps/plugin-store':
+ specifier: ^2.0.0
+ version: 2.4.0
'@tauri-apps/plugin-updater':
specifier: ^2.0.0
version: 2.9.0
@@ -689,6 +692,9 @@ packages:
'@tauri-apps/plugin-process@2.3.0':
resolution: {integrity: sha512-0DNj6u+9csODiV4seSxxRbnLpeGYdojlcctCuLOCgpH9X3+ckVZIEj6H7tRQ7zqWr7kSTEWnrxtAdBb0FbtrmQ==}
+ '@tauri-apps/plugin-store@2.4.0':
+ resolution: {integrity: sha512-PjBnlnH6jyI71MGhrPaxUUCsOzc7WO1mbc4gRhME0m2oxLgCqbksw6JyeKQimuzv4ysdpNO3YbmaY2haf82a3A==}
+
'@tauri-apps/plugin-updater@2.9.0':
resolution: {integrity: sha512-j++sgY8XpeDvzImTrzWA08OqqGqgkNyxczLD7FjNJJx/uXxMZFz5nDcfkyoI/rCjYuj2101Tci/r/HFmOmoxCg==}
@@ -1561,6 +1567,10 @@ snapshots:
dependencies:
'@tauri-apps/api': 2.8.0
+ '@tauri-apps/plugin-store@2.4.0':
+ dependencies:
+ '@tauri-apps/api': 2.8.0
+
'@tauri-apps/plugin-updater@2.9.0':
dependencies:
'@tauri-apps/api': 2.8.0
@@ -1894,4 +1904,4 @@ snapshots:
yallist@3.1.1: {}
- yallist@5.0.0: {}
+ yallist@5.0.0: {}
\ No newline at end of file
diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock
index 27e0a9b..b1f90cf 100644
--- a/src-tauri/Cargo.lock
+++ b/src-tauri/Cargo.lock
@@ -583,6 +583,7 @@ dependencies = [
"tauri-plugin-opener",
"tauri-plugin-process",
"tauri-plugin-single-instance",
+ "tauri-plugin-store",
"tauri-plugin-updater",
"tokio",
"toml 0.8.2",
@@ -4542,6 +4543,22 @@ dependencies = [
"zbus",
]
+[[package]]
+name = "tauri-plugin-store"
+version = "2.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d85dd80d60a76ee2c2fdce09e9ef30877b239c2a6bb76e6d7d03708aa5f13a19"
+dependencies = [
+ "dunce",
+ "serde",
+ "serde_json",
+ "tauri",
+ "tauri-plugin",
+ "thiserror 2.0.17",
+ "tokio",
+ "tracing",
+]
+
[[package]]
name = "tauri-plugin-updater"
version = "2.9.0"
diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml
index 0b2a9ae..47333a9 100644
--- a/src-tauri/Cargo.toml
+++ b/src-tauri/Cargo.toml
@@ -28,6 +28,7 @@ tauri-plugin-opener = "2"
tauri-plugin-process = "2"
tauri-plugin-updater = "2"
tauri-plugin-dialog = "2"
+tauri-plugin-store = "2"
dirs = "5.0"
toml = "0.8"
reqwest = { version = "0.12", default-features = false, features = ["rustls-tls", "json"] }
diff --git a/src-tauri/src/app_store.rs b/src-tauri/src/app_store.rs
new file mode 100644
index 0000000..e08d3ba
--- /dev/null
+++ b/src-tauri/src/app_store.rs
@@ -0,0 +1,137 @@
+use serde_json::Value;
+use std::path::PathBuf;
+use std::sync::{OnceLock, RwLock};
+use tauri_plugin_store::StoreExt;
+
+/// Store 中的键名
+const STORE_KEY_APP_CONFIG_DIR: &str = "app_config_dir_override";
+
+/// 全局缓存的 AppHandle (在应用启动时设置)
+static APP_HANDLE: OnceLock>> = OnceLock::new();
+
+/// 设置全局 AppHandle
+pub fn set_app_handle(handle: tauri::AppHandle) {
+ let store = APP_HANDLE.get_or_init(|| RwLock::new(None));
+ if let Ok(mut guard) = store.write() {
+ *guard = Some(handle);
+ }
+}
+
+/// 获取全局 AppHandle
+fn get_app_handle() -> Option {
+ let store = APP_HANDLE.get()?;
+ let guard = store.read().ok()?;
+ guard.as_ref().cloned()
+}
+
+/// 从 Tauri Store 读取 app_config_dir 覆盖配置 (无需 AppHandle 版本)
+pub fn get_app_config_dir_override() -> Option {
+ let app = get_app_handle()?;
+ get_app_config_dir_from_store(&app)
+}
+
+/// 从 Tauri Store 读取 app_config_dir 覆盖配置(公开函数)
+pub fn get_app_config_dir_from_store(app: &tauri::AppHandle) -> Option {
+ let store = app.store_builder("app_paths.json").build();
+
+ if let Err(e) = &store {
+ log::warn!("无法创建 Store: {}", e);
+ return None;
+ }
+
+ let store = store.unwrap();
+
+ match store.get(STORE_KEY_APP_CONFIG_DIR) {
+ Some(Value::String(path_str)) => {
+ let path_str = path_str.trim();
+ if path_str.is_empty() {
+ return None;
+ }
+
+ let path = resolve_path(path_str);
+
+ // 验证路径是否存在
+ if !path.exists() {
+ log::warn!(
+ "Store 中配置的 app_config_dir 不存在: {:?}\n\
+ 将使用默认路径。",
+ path
+ );
+ return None;
+ }
+
+ log::info!("使用 Store 中的 app_config_dir: {:?}", path);
+ Some(path)
+ }
+ Some(_) => {
+ log::warn!("Store 中的 {} 类型不正确,应为字符串", STORE_KEY_APP_CONFIG_DIR);
+ None
+ }
+ None => None,
+ }
+}
+
+/// 写入 app_config_dir 到 Tauri Store
+pub fn set_app_config_dir_to_store(
+ app: &tauri::AppHandle,
+ path: Option<&str>,
+) -> Result<(), String> {
+ let store = app
+ .store_builder("app_paths.json")
+ .build()
+ .map_err(|e| format!("创建 Store 失败: {}", e))?;
+
+ match path {
+ Some(p) => {
+ let trimmed = p.trim();
+ if !trimmed.is_empty() {
+ store.set(STORE_KEY_APP_CONFIG_DIR, Value::String(trimmed.to_string()));
+ log::info!("已将 app_config_dir 写入 Store: {}", trimmed);
+ } else {
+ // 空字符串 = 删除配置
+ store.delete(STORE_KEY_APP_CONFIG_DIR);
+ log::info!("已从 Store 中删除 app_config_dir 配置");
+ }
+ }
+ None => {
+ // None = 删除配置
+ store.delete(STORE_KEY_APP_CONFIG_DIR);
+ log::info!("已从 Store 中删除 app_config_dir 配置");
+ }
+ }
+
+ store.save().map_err(|e| format!("保存 Store 失败: {}", e))?;
+
+ Ok(())
+}
+
+/// 解析路径,支持 ~ 开头的相对路径
+fn resolve_path(raw: &str) -> PathBuf {
+ if raw == "~" {
+ if let Some(home) = dirs::home_dir() {
+ return home;
+ }
+ } else if let Some(stripped) = raw.strip_prefix("~/") {
+ if let Some(home) = dirs::home_dir() {
+ return home.join(stripped);
+ }
+ } else if let Some(stripped) = raw.strip_prefix("~\\") {
+ if let Some(home) = dirs::home_dir() {
+ return home.join(stripped);
+ }
+ }
+
+ PathBuf::from(raw)
+}
+
+/// 从旧的 settings.json 迁移 app_config_dir 到 Store
+pub fn migrate_app_config_dir_from_settings(app: &tauri::AppHandle) -> Result<(), String> {
+ // app_config_dir 已从 settings.json 移除,此函数保留但不再执行迁移
+ // 如果用户在旧版本设置过 app_config_dir,需要在 Store 中手动配置
+ log::info!("app_config_dir 迁移功能已移除,请在设置中重新配置");
+
+ // 确保 Store 初始化正常
+ let _ = get_app_config_dir_from_store(app);
+
+ Ok(())
+}
\ No newline at end of file
diff --git a/src-tauri/src/commands.rs b/src-tauri/src/commands.rs
index 5a3ec2a..9f1cbd6 100644
--- a/src-tauri/src/commands.rs
+++ b/src-tauri/src/commands.rs
@@ -1222,6 +1222,13 @@ pub async fn save_settings(settings: crate::settings::AppSettings) -> Result Result {
+ // 使用 tauri-plugin-process 重启应用
+ app.restart();
+}
+
/// 检查更新
#[tauri::command]
pub async fn check_for_updates(handle: tauri::AppHandle) -> Result {
@@ -1469,3 +1476,20 @@ pub async fn update_endpoint_last_used(
state.save()?;
Ok(())
}
+
+/// 获取 app_config_dir 覆盖配置 (从 Store)
+#[tauri::command]
+pub async fn get_app_config_dir_override(app: tauri::AppHandle) -> Result
+
+
+
+ {t("settings.appConfigDirDescription")}
+
+
+ setAppConfigDir(e.target.value)}
+ placeholder={t("settings.browsePlaceholderApp")}
+ className="flex-1 px-3 py-2 text-xs font-mono bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500/40"
+ />
+
+
+
+
+
);
-}
+}
\ No newline at end of file
diff --git a/src/i18n/locales/en.json b/src/i18n/locales/en.json
index 4701b22..f2263b4 100644
--- a/src/i18n/locales/en.json
+++ b/src/i18n/locales/en.json
@@ -109,6 +109,9 @@
"openFolder": "Open Folder",
"configDirectoryOverride": "Configuration Directory Override (Advanced)",
"configDirectoryDescription": "When using Claude Code or Codex in environments like WSL, you can manually specify the configuration directory in WSL to keep provider data consistent with the main environment.",
+ "appConfigDir": "CC-Switch Configuration Directory",
+ "appConfigDirDescription": "Customize the storage location for CC-Switch configuration files (config.json, etc.)",
+ "browsePlaceholderApp": "e.g., C:\\Users\\Administrator\\.cc-switch",
"claudeConfigDir": "Claude Code Configuration Directory",
"codexConfigDir": "Codex Configuration Directory",
"browsePlaceholderClaude": "e.g., /home/
/.claude",
@@ -123,7 +126,12 @@
"releaseNotes": "Release Notes",
"viewReleaseNotes": "View release notes for this version",
"viewCurrentReleaseNotes": "View current version release notes",
- "exportFailedError": "Export config failed:"
+ "exportFailedError": "Export config failed:",
+ "restartRequired": "Restart Required",
+ "restartRequiredMessage": "Modifying the CC-Switch configuration directory requires restarting the application to take effect. Restart now?",
+ "restartNow": "Restart Now",
+ "restartLater": "Restart Later",
+ "devModeRestartHint": "Dev Mode: Configuration saved. Please manually restart the application for changes to take effect"
},
"apps": {
"claude": "Claude Code",
diff --git a/src/i18n/locales/zh.json b/src/i18n/locales/zh.json
index 5ed5204..28a80d2 100644
--- a/src/i18n/locales/zh.json
+++ b/src/i18n/locales/zh.json
@@ -109,6 +109,9 @@
"openFolder": "打开文件夹",
"configDirectoryOverride": "配置目录覆盖(高级)",
"configDirectoryDescription": "在 WSL 等环境使用 Claude Code 或 Codex 的时候,可手动指定 WSL 里的配置目录,供应商数据与主环境保持一致。",
+ "appConfigDir": "CC-Switch 配置目录",
+ "appConfigDirDescription": "自定义 CC-Switch 的配置存储位置(config.json 等文件)",
+ "browsePlaceholderApp": "例如:C:\\Users\\Administrator\\.cc-switch",
"claudeConfigDir": "Claude Code 配置目录",
"codexConfigDir": "Codex 配置目录",
"browsePlaceholderClaude": "例如:/home/<你的用户名>/.claude",
@@ -123,7 +126,12 @@
"releaseNotes": "更新日志",
"viewReleaseNotes": "查看该版本更新日志",
"viewCurrentReleaseNotes": "查看当前版本更新日志",
- "exportFailedError": "导出配置失败:"
+ "exportFailedError": "导出配置失败:",
+ "restartRequired": "需要重启应用",
+ "restartRequiredMessage": "修改 CC-Switch 配置目录后需要重启应用才能生效,是否立即重启?",
+ "restartNow": "立即重启",
+ "restartLater": "稍后重启",
+ "devModeRestartHint": "开发模式:配置已保存,请手动重启应用以使新配置生效"
},
"apps": {
"claude": "Claude Code",
diff --git a/src/lib/tauri-api.ts b/src/lib/tauri-api.ts
index 7590147..973b2c2 100644
--- a/src/lib/tauri-api.ts
+++ b/src/lib/tauri-api.ts
@@ -209,6 +209,16 @@ export const tauriAPI = {
}
},
+ // 重启应用程序
+ restartApp: async (): Promise => {
+ try {
+ return await invoke("restart_app");
+ } catch (error) {
+ console.error("重启应用失败:", error);
+ return false;
+ }
+ },
+
// 检查更新
checkForUpdates: async (): Promise => {
try {
@@ -653,6 +663,26 @@ export const tauriAPI = {
});
return unlisten;
},
+
+ // 获取 app_config_dir 覆盖配置(从 Store)
+ getAppConfigDirOverride: async (): Promise => {
+ try {
+ return await invoke("get_app_config_dir_override");
+ } catch (error) {
+ console.error("获取 app_config_dir 覆盖配置失败:", error);
+ return null;
+ }
+ },
+
+ // 设置 app_config_dir 覆盖配置(到 Store)
+ setAppConfigDirOverride: async (path: string | null): Promise => {
+ try {
+ return await invoke("set_app_config_dir_override", { path });
+ } catch (error) {
+ console.error("设置 app_config_dir 覆盖配置失败:", error);
+ throw error;
+ }
+ },
};
// 创建全局 API 对象,兼容现有代码
@@ -662,4 +692,4 @@ if (typeof window !== "undefined") {
(window as any).api = tauriAPI;
}
-export default tauriAPI;
+export default tauriAPI;
\ No newline at end of file
diff --git a/src/vite-env.d.ts b/src/vite-env.d.ts
index 4a0356c..8587079 100644
--- a/src/vite-env.d.ts
+++ b/src/vite-env.d.ts
@@ -58,6 +58,7 @@ declare global {
) => Promise;
getSettings: () => Promise;
saveSettings: (settings: Settings) => Promise;
+ restartApp: () => Promise;
checkForUpdates: () => Promise;
isPortable: () => Promise;
getAppConfigPath: () => Promise;