diff --git a/src-tauri/build.rs b/src-tauri/build.rs index d860e1e6a..12b5797b3 100644 --- a/src-tauri/build.rs +++ b/src-tauri/build.rs @@ -1,3 +1,18 @@ fn main() { - tauri_build::build() + tauri_build::build(); + + #[cfg(target_os = "windows")] + { + let manifest_path = std::path::PathBuf::from( + std::env::var("CARGO_MANIFEST_DIR").expect("missing CARGO_MANIFEST_DIR"), + ) + .join("common-controls.manifest"); + let manifest_arg = format!("/MANIFESTINPUT:{}", manifest_path.display()); + + println!("cargo:rustc-link-arg=/MANIFEST:EMBED"); + println!("cargo:rustc-link-arg={}", manifest_arg); + // Avoid duplicate manifest resources in binary builds. + println!("cargo:rustc-link-arg-bins=/MANIFEST:NO"); + println!("cargo:rerun-if-changed={}", manifest_path.display()); + } } diff --git a/src-tauri/common-controls.manifest b/src-tauri/common-controls.manifest new file mode 100644 index 000000000..8324d4bdd --- /dev/null +++ b/src-tauri/common-controls.manifest @@ -0,0 +1,13 @@ + + + + + + + + diff --git a/src-tauri/src/codex_config.rs b/src-tauri/src/codex_config.rs index 040e5f0c3..e57d79027 100644 --- a/src-tauri/src/codex_config.rs +++ b/src-tauri/src/codex_config.rs @@ -11,6 +11,13 @@ use std::path::Path; /// 获取用户主目录,带回退和日志 fn get_home_dir() -> PathBuf { + #[cfg(windows)] + if let Ok(home) = std::env::var("HOME") { + let trimmed = home.trim(); + if !trimmed.is_empty() { + return PathBuf::from(trimmed); + } + } dirs::home_dir().unwrap_or_else(|| { log::warn!("无法获取用户主目录,回退到当前目录"); PathBuf::from(".") diff --git a/src-tauri/src/config.rs b/src-tauri/src/config.rs index 6eae3a2cd..cabc0e878 100644 --- a/src-tauri/src/config.rs +++ b/src-tauri/src/config.rs @@ -7,6 +7,13 @@ use crate::error::AppError; /// 获取用户主目录,带回退和日志 fn get_home_dir() -> PathBuf { + #[cfg(windows)] + if let Ok(home) = std::env::var("HOME") { + let trimmed = home.trim(); + if !trimmed.is_empty() { + return PathBuf::from(trimmed); + } + } dirs::home_dir().unwrap_or_else(|| { log::warn!("无法获取用户主目录,回退到当前目录"); PathBuf::from(".") @@ -71,10 +78,7 @@ pub fn get_app_config_dir() -> PathBuf { if let Some(custom) = crate::app_store::get_app_config_dir_override() { return custom; } - - dirs::home_dir() - .expect("无法获取用户主目录") - .join(".cc-switch") + get_home_dir().join(".cc-switch") } /// 获取应用配置文件路径 diff --git a/src-tauri/src/gemini_config.rs b/src-tauri/src/gemini_config.rs index 6ea476662..c652d61d4 100644 --- a/src-tauri/src/gemini_config.rs +++ b/src-tauri/src/gemini_config.rs @@ -7,6 +7,13 @@ use std::path::PathBuf; /// 获取用户主目录,带回退和日志 fn get_home_dir() -> PathBuf { + #[cfg(windows)] + if let Ok(home) = std::env::var("HOME") { + let trimmed = home.trim(); + if !trimmed.is_empty() { + return PathBuf::from(trimmed); + } + } dirs::home_dir().unwrap_or_else(|| { log::warn!("无法获取用户主目录,回退到当前目录"); PathBuf::from(".") diff --git a/src-tauri/src/settings.rs b/src-tauri/src/settings.rs index 301151a27..2d3580739 100644 --- a/src-tauri/src/settings.rs +++ b/src-tauri/src/settings.rs @@ -154,6 +154,17 @@ impl Default for AppSettings { impl AppSettings { fn settings_path() -> Option { // settings.json 保留用于旧版本迁移和无数据库场景 + #[cfg(windows)] + if let Ok(home) = std::env::var("HOME") { + let trimmed = home.trim(); + if !trimmed.is_empty() { + return Some( + PathBuf::from(trimmed) + .join(".cc-switch") + .join("settings.json"), + ); + } + } dirs::home_dir().map(|h| h.join(".cc-switch").join("settings.json")) } diff --git a/src-tauri/tests/import_export_sync.rs b/src-tauri/tests/import_export_sync.rs index 50fd70f3e..24a1667fb 100644 --- a/src-tauri/tests/import_export_sync.rs +++ b/src-tauri/tests/import_export_sync.rs @@ -971,12 +971,18 @@ fn export_sql_returns_error_for_invalid_path() { let state = create_test_state().expect("create test state"); - // Try to export to an invalid path (parent directory doesn't exist) - let invalid_path = PathBuf::from("/nonexistent/directory/export.sql"); + // Try to export to an invalid path (nonexistent parent or invalid name on Windows) + let invalid_parent = if cfg!(windows) { + std::env::temp_dir().join("cc-switch-test-invalid<>dir") + } else { + PathBuf::from("/nonexistent/directory") + }; + let invalid_path = invalid_parent.join("export.sql"); let err = state .db .export_sql(&invalid_path) .expect_err("export to invalid path should fail"); + let invalid_prefix = invalid_parent.to_string_lossy(); // The error can be either IoContext or Io depending on where it fails match err { @@ -988,8 +994,8 @@ fn export_sql_returns_error_for_invalid_path() { } AppError::Io { path, .. } => { assert!( - path.starts_with("/nonexistent"), - "expected error for /nonexistent path, got: {path:?}" + path.starts_with(invalid_prefix.as_ref()), + "expected error for {invalid_parent:?}, got: {path:?}" ); } other => panic!("expected IoContext or Io error, got {other:?}"),