diff --git a/src-tauri/src/core/clash.rs b/src-tauri/src/core/clash.rs
index d73c5748fcec1b5e491d533ccc254a78d4d4824c..5d88aaf5d850fd24e125921b1e8ab89945d3591a 100644
--- a/src-tauri/src/core/clash.rs
+++ b/src-tauri/src/core/clash.rs
@@ -16,9 +16,6 @@ pub struct ClashInfo {
 
   /// clash secret
   pub secret: Option<String>,
-
-  /// mode
-  pub mode: Option<String>,
 }
 
 impl ClashInfo {
@@ -29,7 +26,6 @@ impl ClashInfo {
     let key_port_2 = Value::from("mixed-port");
     let key_server = Value::from("external-controller");
     let key_secret = Value::from("secret");
-    let key_mode = Value::from("mode");
 
     let port = match config.get(&key_port_1) {
       Some(value) => match value {
@@ -76,20 +72,11 @@ impl ClashInfo {
       _ => None,
     };
 
-    let mode = match config.get(&key_mode) {
-      Some(value) => match value {
-        Value::String(val_str) => Some(val_str.clone()),
-        _ => None,
-      },
-      _ => None,
-    };
-
     ClashInfo {
       status: "init".into(),
       port,
       server,
       secret,
-      mode,
     }
   }
 }
diff --git a/src-tauri/src/core/mod.rs b/src-tauri/src/core/mod.rs
index b6cb1d84e67a9a311086f1c2e2a0e6e008b692c5..7adc356a354ad3cecbba20a8634f6b81240e16de 100644
--- a/src-tauri/src/core/mod.rs
+++ b/src-tauri/src/core/mod.rs
@@ -3,7 +3,7 @@ use self::sysopt::Sysopt;
 use self::timer::Timer;
 use crate::core::enhance::PrfEnhancedResult;
 use crate::log_if_err;
-use crate::utils::{dirs, help};
+use crate::utils::help;
 use anyhow::{bail, Result};
 use parking_lot::Mutex;
 use serde_yaml::Mapping;
@@ -112,6 +112,7 @@ impl Core {
     log_if_err!(sysopt.init_launch(auto_launch));
 
     log_if_err!(self.update_systray(&app_handle));
+    log_if_err!(self.update_systray_clash(&app_handle));
 
     // wait the window setup during resolve app
     let core = self.clone();
@@ -191,7 +192,7 @@ impl Core {
     }
 
     if changed_mode {
-      self.update_systray(app_handle)?;
+      self.update_systray_clash(app_handle)?;
     }
 
     Ok(())
@@ -240,7 +241,7 @@ impl Core {
 
     #[cfg(target_os = "windows")]
     if tun_mode.is_some() && *tun_mode.as_ref().unwrap_or(&false) {
-      let wintun_dll = dirs::app_home_dir().join("wintun.dll");
+      let wintun_dll = crate::utils::dirs::app_home_dir().join("wintun.dll");
       if !wintun_dll.exists() {
         bail!("failed to enable TUN for missing `wintun.dll`");
       }
@@ -262,30 +263,38 @@ impl Core {
     Ok(())
   }
 
-  /// update the system tray state
-  pub fn update_systray(&self, app_handle: &AppHandle) -> Result<()> {
+  // update system tray state (clash config)
+  pub fn update_systray_clash(&self, app_handle: &AppHandle) -> Result<()> {
     let clash = self.clash.lock();
-    let info = clash.info.clone();
-    let mode = info.mode.as_ref();
+    let mode = clash
+      .config
+      .get(&Value::from("mode"))
+      .map(|val| val.as_str().unwrap_or("rule"))
+      .unwrap_or("rule");
 
-    let verge = self.verge.lock();
     let tray = app_handle.tray_handle();
 
-    let system_proxy = verge.enable_system_proxy.as_ref();
-    let tun_mode = verge.enable_tun_mode.as_ref();
-
-    tray
-      .get_item("rule_mode")
-      .set_selected((*mode.unwrap()).eq("rule"))?;
+    tray.get_item("rule_mode").set_selected(mode == "rule")?;
     tray
       .get_item("global_mode")
-      .set_selected((*mode.unwrap()).eq("global"))?;
+      .set_selected(mode == "global")?;
     tray
       .get_item("direct_mode")
-      .set_selected((*mode.unwrap()).eq("direct"))?;
+      .set_selected(mode == "direct")?;
     tray
       .get_item("script_mode")
-      .set_selected((*mode.unwrap()).eq("script"))?;
+      .set_selected(mode == "script")?;
+
+    Ok(())
+  }
+
+  /// update the system tray state (verge config)
+  pub fn update_systray(&self, app_handle: &AppHandle) -> Result<()> {
+    let verge = self.verge.lock();
+    let tray = app_handle.tray_handle();
+
+    let system_proxy = verge.enable_system_proxy.as_ref();
+    let tun_mode = verge.enable_tun_mode.as_ref();
 
     tray
       .get_item("system_proxy")
@@ -302,6 +311,33 @@ impl Core {
     Ok(())
   }
 
+  // update rule/global/direct/script mode
+  pub fn update_mode(&self, app_handle: &AppHandle, mode: &str) -> Result<()> {
+    // save config to file
+    let mut clash = self.clash.lock();
+    clash.config.insert(Value::from("mode"), Value::from(mode));
+    clash.save_config()?;
+
+    let info = clash.info.clone();
+    drop(clash);
+
+    let notice = {
+      let window = self.window.lock();
+      Notice::from(window.clone())
+    };
+
+    let mut mapping = Mapping::new();
+    mapping.insert(Value::from("mode"), Value::from(mode));
+
+    let service = self.service.lock();
+    service.patch_config(info, mapping, notice)?;
+
+    // update tray
+    self.update_systray_clash(app_handle)?;
+
+    Ok(())
+  }
+
   /// activate the profile
   /// auto activate enhanced profile
   pub fn activate(&self) -> Result<()> {
@@ -425,31 +461,6 @@ impl Core {
 
     Ok(())
   }
-
-  // update rule/global/direct/script mode
-  pub fn update_mode(&self, app_handle: &AppHandle, mode: &str) -> Result<()> {
-    let mut mapping = Mapping::new();
-    mapping.insert(Value::from("mode"), Value::from(mode));
-
-    self.patch_clash(mapping, app_handle);
-
-    let (config, info) = {
-      let clash = self.clash.lock();
-      let config = clash.config.clone();
-      let info = clash.info.clone();
-      (config, info)
-    };
-
-    let notice = {
-      let window = self.window.lock();
-      Notice::from(window.clone())
-    };
-
-    let service = self.service.lock();
-    service.patch_config(info, config, notice);
-
-    Ok(())
-  }
 }
 
 impl Core {
diff --git a/src-tauri/src/core/service.rs b/src-tauri/src/core/service.rs
index 8a9c1ba542376982563b6b28732ce5dbbe2d7c05..8d91d8039511dc446fb461916fa20b6b9350885c 100644
--- a/src-tauri/src/core/service.rs
+++ b/src-tauri/src/core/service.rs
@@ -1,10 +1,8 @@
-use super::Clash;
 use super::{notice::Notice, ClashInfo};
 use crate::log_if_err;
 use crate::utils::{config, dirs};
 use anyhow::{bail, Result};
 use reqwest::header::HeaderMap;
-use serde::{Deserialize, Serialize};
 use serde_yaml::Mapping;
 use std::{collections::HashMap, time::Duration};
 use tauri::api::process::{Command, CommandChild, CommandEvent};
@@ -139,24 +137,7 @@ impl Service {
     let temp_path = dirs::profiles_temp_path();
     config::save_yaml(temp_path.clone(), &config, Some("# Clash Verge Temp File"))?;
 
-    if info.server.is_none() {
-      if info.port.is_none() {
-        bail!("failed to parse config.yaml file");
-      } else {
-        bail!("failed to parse the server");
-      }
-    }
-
-    let server = info.server.unwrap();
-    let server = format!("http://{server}/configs");
-
-    let mut headers = HeaderMap::new();
-    headers.insert("Content-Type", "application/json".parse().unwrap());
-
-    if let Some(secret) = info.secret.as_ref() {
-      let secret = format!("Bearer {}", secret.clone()).parse().unwrap();
-      headers.insert("Authorization", secret);
-    }
+    let (server, headers) = Self::clash_client_info(info)?;
 
     tauri::async_runtime::spawn(async move {
       let mut data = HashMap::new();
@@ -197,6 +178,24 @@ impl Service {
       bail!("did not start sidecar");
     }
 
+    let (server, headers) = Self::clash_client_info(info)?;
+
+    tauri::async_runtime::spawn(async move {
+      if let Ok(client) = reqwest::ClientBuilder::new().no_proxy().build() {
+        let builder = client.patch(&server).headers(headers.clone()).json(&config);
+
+        match builder.send().await {
+          Ok(_) => notice.refresh_clash(),
+          Err(err) => log::error!("{err}"),
+        }
+      }
+    });
+
+    Ok(())
+  }
+
+  /// get clash client url and headers from clash info
+  fn clash_client_info(info: ClashInfo) -> Result<(String, HeaderMap)> {
     if info.server.is_none() {
       if info.port.is_none() {
         bail!("failed to parse config.yaml file");
@@ -216,34 +215,7 @@ impl Service {
       headers.insert("Authorization", secret);
     }
 
-    tauri::async_runtime::spawn(async move {
-      // retry 5 times
-      for _ in 0..5 {
-        match reqwest::ClientBuilder::new().no_proxy().build() {
-          Ok(client) => {
-            let builder = client.patch(&server).headers(headers.clone()).json(&config);
-
-            match builder.send().await {
-              Ok(resp) => {
-                if resp.status() != 204 {
-                  log::error!("failed to activate clash with status \"{}\"", resp.status());
-                }
-
-                notice.refresh_clash();
-
-                // do not retry
-                break;
-              }
-              Err(err) => log::error!("failed to activate for `{err}`"),
-            }
-          }
-          Err(err) => log::error!("failed to activate for `{err}`"),
-        }
-        sleep(Duration::from_millis(500)).await;
-      }
-    });
-
-    Ok(())
+    Ok((server, headers))
   }
 }
 
diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs
index ea4cf9eb37f4ba7de7900ecb6a2f017cd6c6f178..4010509ff7a95051eee7ae9436c42f6cc4a392be 100644
--- a/src-tauri/src/main.rs
+++ b/src-tauri/src/main.rs
@@ -51,21 +51,10 @@ fn main() -> std::io::Result<()> {
         "open_window" => {
           resolve::create_window(app_handle);
         }
-        "rule_mode" => {
+        mode @ ("rule_mode" | "global_mode" | "direct_mode" | "script_mode") => {
+          let mode = &mode[0..mode.len() - 5];
           let core = app_handle.state::<core::Core>();
-          crate::log_if_err!(core.update_mode(app_handle, "rule"));
-        }
-        "global_mode" => {
-          let core = app_handle.state::<core::Core>();
-          crate::log_if_err!(core.update_mode(app_handle, "global"));
-        }
-        "direct_mode" => {
-          let core = app_handle.state::<core::Core>();
-          crate::log_if_err!(core.update_mode(app_handle, "direct"));
-        }
-        "script_mode" => {
-          let core = app_handle.state::<core::Core>();
-          crate::log_if_err!(core.update_mode(app_handle, "script"));
+          crate::log_if_err!(core.update_mode(app_handle, mode));
         }
         "system_proxy" => {
           let core = app_handle.state::<core::Core>();