diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock
index 7bcb55c..c8b8f84 100644
--- a/src-tauri/Cargo.lock
+++ b/src-tauri/Cargo.lock
@@ -291,6 +291,17 @@ version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0"
+[[package]]
+name = "auto-launch"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1f012b8cc0c850f34117ec8252a44418f2e34a2cf501de89e29b241ae5f79471"
+dependencies = [
+ "dirs 4.0.0",
+ "thiserror 1.0.69",
+ "winreg 0.10.1",
+]
+
[[package]]
name = "autocfg"
version = "1.5.0"
@@ -598,6 +609,7 @@ name = "cc-switch"
version = "3.7.0"
dependencies = [
"anyhow",
+ "auto-launch",
"chrono",
"dirs 5.0.1",
"futures",
@@ -982,6 +994,15 @@ dependencies = [
"subtle",
]
+[[package]]
+name = "dirs"
+version = "4.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ca3aa72a6f96ea37bbc5aa912f6788242832f75369bdfdadcb0e38423f100059"
+dependencies = [
+ "dirs-sys 0.3.7",
+]
+
[[package]]
name = "dirs"
version = "5.0.1"
@@ -1000,6 +1021,17 @@ dependencies = [
"dirs-sys 0.5.0",
]
+[[package]]
+name = "dirs-sys"
+version = "0.3.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6"
+dependencies = [
+ "libc",
+ "redox_users 0.4.6",
+ "winapi",
+]
+
[[package]]
name = "dirs-sys"
version = "0.4.1"
@@ -6397,6 +6429,15 @@ dependencies = [
"memchr",
]
+[[package]]
+name = "winreg"
+version = "0.10.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d"
+dependencies = [
+ "winapi",
+]
+
[[package]]
name = "winreg"
version = "0.52.0"
diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml
index 2a2dd53..530d258 100644
--- a/src-tauri/Cargo.toml
+++ b/src-tauri/Cargo.toml
@@ -34,6 +34,7 @@ tauri-plugin-updater = "2"
tauri-plugin-dialog = "2"
tauri-plugin-store = "2"
tauri-plugin-deep-link = "2"
+auto-launch = "0.5"
dirs = "5.0"
toml = "0.8"
toml_edit = "0.22"
diff --git a/src-tauri/src/auto_launch.rs b/src-tauri/src/auto_launch.rs
new file mode 100644
index 0000000..43bef82
--- /dev/null
+++ b/src-tauri/src/auto_launch.rs
@@ -0,0 +1,39 @@
+use crate::error::AppError;
+use auto_launch::AutoLaunch;
+
+/// 初始化 AutoLaunch 实例
+fn get_auto_launch() -> Result {
+ let app_name = "CC Switch";
+ let app_path = std::env::current_exe().map_err(AppError::AutoLaunchPathError)?;
+
+ let auto_launch = AutoLaunch::new(app_name, &app_path.to_string_lossy(), false, &[] as &[&str]);
+ Ok(auto_launch)
+}
+
+/// 启用开机自启
+pub fn enable_auto_launch() -> Result<(), AppError> {
+ let auto_launch = get_auto_launch()?;
+ auto_launch
+ .enable()
+ .map_err(|e| AppError::AutoLaunchEnableError(e.to_string()))?;
+ log::info!("Auto-launch enabled");
+ Ok(())
+}
+
+/// 禁用开机自启
+pub fn disable_auto_launch() -> Result<(), AppError> {
+ let auto_launch = get_auto_launch()?;
+ auto_launch
+ .disable()
+ .map_err(|e| AppError::AutoLaunchDisableError(e.to_string()))?;
+ log::info!("Auto-launch disabled");
+ Ok(())
+}
+
+/// 检查是否已启用开机自启
+pub fn is_auto_launch_enabled() -> Result {
+ let auto_launch = get_auto_launch()?;
+ auto_launch
+ .is_enabled()
+ .map_err(|e| AppError::AutoLaunchCheckError(e.to_string()))
+}
diff --git a/src-tauri/src/commands/settings.rs b/src-tauri/src/commands/settings.rs
index ee76526..eeca097 100644
--- a/src-tauri/src/commands/settings.rs
+++ b/src-tauri/src/commands/settings.rs
@@ -37,3 +37,20 @@ pub async fn set_app_config_dir_override(
crate::app_store::set_app_config_dir_to_store(&app, path.as_deref())?;
Ok(true)
}
+
+/// 设置开机自启
+#[tauri::command]
+pub async fn set_auto_launch(enabled: bool) -> Result {
+ if enabled {
+ crate::auto_launch::enable_auto_launch().map_err(|e| e.to_string())?;
+ } else {
+ crate::auto_launch::disable_auto_launch().map_err(|e| e.to_string())?;
+ }
+ Ok(true)
+}
+
+/// 获取开机自启状态
+#[tauri::command]
+pub async fn get_auto_launch_status() -> Result {
+ crate::auto_launch::is_auto_launch_enabled().map_err(|e| e.to_string())
+}
diff --git a/src-tauri/src/error.rs b/src-tauri/src/error.rs
index d9b0262..2912d8e 100644
--- a/src-tauri/src/error.rs
+++ b/src-tauri/src/error.rs
@@ -50,6 +50,14 @@ pub enum AppError {
zh: String,
en: String,
},
+ #[error("Failed to get application path for auto-launch: {0}")]
+ AutoLaunchPathError(#[source] std::io::Error),
+ #[error("Failed to enable auto-launch: {0}")]
+ AutoLaunchEnableError(String),
+ #[error("Failed to disable auto-launch: {0}")]
+ AutoLaunchDisableError(String),
+ #[error("Failed to check auto-launch status: {0}")]
+ AutoLaunchCheckError(String),
}
impl AppError {
diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs
index 0ed21ab..5ac537e 100644
--- a/src-tauri/src/lib.rs
+++ b/src-tauri/src/lib.rs
@@ -1,5 +1,6 @@
mod app_config;
mod app_store;
+mod auto_launch;
mod claude_mcp;
mod claude_plugin;
mod codex_config;
@@ -559,6 +560,30 @@ pub fn run() {
// 启动阶段不再无条件保存,避免意外覆盖用户配置。
+ // 同步开机自启状态:以 settings.json 为准,保持系统项一致
+ {
+ let settings = crate::settings::get_settings();
+ let system_enabled = crate::auto_launch::is_auto_launch_enabled().unwrap_or(false);
+
+ if settings.launch_on_startup != system_enabled {
+ log::info!(
+ "开机自启状态不一致:settings={}, system={},以 settings 为准",
+ settings.launch_on_startup,
+ system_enabled
+ );
+
+ let sync_result = if settings.launch_on_startup {
+ crate::auto_launch::enable_auto_launch()
+ } else {
+ crate::auto_launch::disable_auto_launch()
+ };
+
+ if let Err(e) = sync_result {
+ log::warn!("同步开机自启状态失败: {}", e);
+ }
+ }
+ }
+
// 注册 deep-link URL 处理器(使用正确的 DeepLinkExt API)
log::info!("=== Registering deep-link URL handler ===");
@@ -653,6 +678,8 @@ pub fn run() {
commands::get_settings,
commands::save_settings,
commands::restart_app,
+ commands::set_auto_launch,
+ commands::get_auto_launch_status,
commands::check_for_updates,
commands::is_portable_mode,
commands::get_claude_plugin_status,
diff --git a/src-tauri/src/settings.rs b/src-tauri/src/settings.rs
index 75b4c3c..518e1cb 100644
--- a/src-tauri/src/settings.rs
+++ b/src-tauri/src/settings.rs
@@ -49,6 +49,9 @@ pub struct AppSettings {
pub gemini_config_dir: Option,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub language: Option,
+ /// 是否开机自启
+ #[serde(default)]
+ pub launch_on_startup: bool,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub security: Option,
/// Claude 自定义端点列表
@@ -77,6 +80,7 @@ impl Default for AppSettings {
codex_config_dir: None,
gemini_config_dir: None,
language: None,
+ launch_on_startup: false,
security: None,
custom_endpoints_claude: HashMap::new(),
custom_endpoints_codex: HashMap::new(),
diff --git a/src/components/settings/WindowSettings.tsx b/src/components/settings/WindowSettings.tsx
index 94ec79a..06f0d39 100644
--- a/src/components/settings/WindowSettings.tsx
+++ b/src/components/settings/WindowSettings.tsx
@@ -19,6 +19,13 @@ export function WindowSettings({ settings, onChange }: WindowSettingsProps) {
+ onChange({ launchOnStartup: value })}
+ />
+
{
+ return await invoke("set_auto_launch", { enabled });
+ },
+
+ async getAutoLaunchStatus(): Promise {
+ return await invoke("get_auto_launch_status");
+ },
};
diff --git a/src/types.ts b/src/types.ts
index b4cb57f..f94a737 100644
--- a/src/types.ts
+++ b/src/types.ts
@@ -101,6 +101,8 @@ export interface Settings {
geminiConfigDir?: string;
// 首选语言(可选,默认中文)
language?: "en" | "zh";
+ // 是否开机自启
+ launchOnStartup?: boolean;
// Claude 自定义端点列表
customEndpointsClaude?: Record;
// Codex 自定义端点列表