diff --git a/src-tauri/src/cmds.rs b/src-tauri/src/cmds.rs
index f3aa877bf0c2b2026f4a33c19e9e9c522a62bb30..a39bfc42842713069b8e05585a55542d2d76f6c5 100644
--- a/src-tauri/src/cmds.rs
+++ b/src-tauri/src/cmds.rs
@@ -178,8 +178,12 @@ pub fn get_clash_info(core: State<'_, Core>) -> CmdResult<ClashInfo> {
 /// after putting the change to the clash core
 /// then we should save the latest config
 #[tauri::command]
-pub fn patch_clash_config(payload: Mapping, core: State<'_, Core>) -> CmdResult {
-  wrap_err!(core.patch_clash(payload))
+pub fn patch_clash_config(
+  payload: Mapping,
+  app_handle: tauri::AppHandle,
+  core: State<'_, Core>,
+) -> CmdResult {
+  wrap_err!(core.patch_clash(payload, &app_handle))
 }
 
 /// get the verge config
diff --git a/src-tauri/src/core/clash.rs b/src-tauri/src/core/clash.rs
index 4d2cc0a5b7cb1c2d5348d54b208a9475bdc8373a..d73c5748fcec1b5e491d533ccc254a78d4d4824c 100644
--- a/src-tauri/src/core/clash.rs
+++ b/src-tauri/src/core/clash.rs
@@ -16,6 +16,9 @@ pub struct ClashInfo {
 
   /// clash secret
   pub secret: Option<String>,
+
+  /// mode
+  pub mode: Option<String>,
 }
 
 impl ClashInfo {
@@ -26,6 +29,7 @@ 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 {
@@ -72,11 +76,20 @@ 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,
     }
   }
 }
@@ -113,20 +126,26 @@ impl Clash {
 
   /// patch update the clash config
   /// if the port is changed then return true
-  pub fn patch_config(&mut self, patch: Mapping) -> Result<bool> {
+  pub fn patch_config(&mut self, patch: Mapping) -> Result<(bool, bool)> {
     let port_key = Value::from("mixed-port");
     let server_key = Value::from("external-controller");
     let secret_key = Value::from("secret");
+    let mode_key = Value::from("mode");
 
     let mut change_port = false;
     let mut change_info = false;
+    let mut change_mode = false;
 
     for (key, value) in patch.into_iter() {
       if key == port_key {
         change_port = true;
       }
 
-      if key == port_key || key == server_key || key == secret_key {
+      if key == mode_key {
+        change_mode = true;
+      }
+
+      if key == port_key || key == server_key || key == secret_key || key == mode_key {
         change_info = true;
       }
 
@@ -139,7 +158,7 @@ impl Clash {
 
     self.save_config()?;
 
-    Ok(change_port)
+    Ok((change_port, change_mode))
   }
 
   /// revise the `tun` and `dns` config
diff --git a/src-tauri/src/core/mod.rs b/src-tauri/src/core/mod.rs
index f9abfe910f8f4f4de3bd90727a9738833b4f7763..b6cb1d84e67a9a311086f1c2e2a0e6e008b692c5 100644
--- a/src-tauri/src/core/mod.rs
+++ b/src-tauri/src/core/mod.rs
@@ -7,6 +7,7 @@ use crate::utils::{dirs, help};
 use anyhow::{bail, Result};
 use parking_lot::Mutex;
 use serde_yaml::Mapping;
+use serde_yaml::Value;
 use std::sync::Arc;
 use std::time::Duration;
 use tauri::{AppHandle, Manager, Window};
@@ -168,15 +169,15 @@ impl Core {
 
   /// Patch Clash
   /// handle the clash config changed
-  pub fn patch_clash(&self, patch: Mapping) -> Result<()> {
-    let (changed, port) = {
+  pub fn patch_clash(&self, patch: Mapping, app_handle: &AppHandle) -> Result<()> {
+    let ((changed_port, changed_mode), port) = {
       let mut clash = self.clash.lock();
       (clash.patch_config(patch)?, clash.info.port.clone())
     };
 
     // todo: port check
 
-    if changed {
+    if changed_port {
       let mut service = self.service.lock();
       service.restart()?;
       drop(service);
@@ -189,6 +190,10 @@ impl Core {
       sysopt.init_sysproxy(port, &verge);
     }
 
+    if changed_mode {
+      self.update_systray(app_handle)?;
+    }
+
     Ok(())
   }
 
@@ -259,12 +264,29 @@ impl Core {
 
   /// update the system tray state
   pub fn update_systray(&self, app_handle: &AppHandle) -> Result<()> {
+    let clash = self.clash.lock();
+    let info = clash.info.clone();
+    let mode = info.mode.as_ref();
+
     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("global_mode")
+      .set_selected((*mode.unwrap()).eq("global"))?;
+    tray
+      .get_item("direct_mode")
+      .set_selected((*mode.unwrap()).eq("direct"))?;
+    tray
+      .get_item("script_mode")
+      .set_selected((*mode.unwrap()).eq("script"))?;
+
     tray
       .get_item("system_proxy")
       .set_selected(*system_proxy.unwrap_or(&false))?;
@@ -403,6 +425,31 @@ 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 8dbd27110bcc1151d2771101f793d426f6a02c12..8a9c1ba542376982563b6b28732ce5dbbe2d7c05 100644
--- a/src-tauri/src/core/service.rs
+++ b/src-tauri/src/core/service.rs
@@ -1,3 +1,4 @@
+use super::Clash;
 use super::{notice::Notice, ClashInfo};
 use crate::log_if_err;
 use crate::utils::{config, dirs};
@@ -189,6 +190,61 @@ impl Service {
 
     Ok(())
   }
+
+  /// patch clash config
+  pub fn patch_config(&self, info: ClashInfo, config: Mapping, notice: Notice) -> Result<()> {
+    if !self.service_mode && self.sidecar.is_none() {
+      bail!("did not start sidecar");
+    }
+
+    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);
+    }
+
+    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(())
+  }
 }
 
 impl Drop for Service {
diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs
index 10ae550267c94fff4ec659a46bcf42e14c7ba362..d40fffe14aa020ced8e9d25f294227f63afedb22 100644
--- a/src-tauri/src/main.rs
+++ b/src-tauri/src/main.rs
@@ -11,6 +11,7 @@ use crate::{
   core::Verge,
   utils::{resolve, server},
 };
+use serde_yaml::{Mapping, Value};
 use tauri::{
   api, CustomMenuItem, Manager, SystemTray, SystemTrayEvent, SystemTrayMenu, SystemTrayMenuItem,
 };
@@ -30,6 +31,10 @@ fn main() -> std::io::Result<()> {
 
   let tray_menu = SystemTrayMenu::new()
     .add_item(CustomMenuItem::new("open_window", "Show"))
+    .add_item(CustomMenuItem::new("rule_mode", "Rule Mode"))
+    .add_item(CustomMenuItem::new("global_mode", "Global Mode"))
+    .add_item(CustomMenuItem::new("direct_mode", "Direct Mode"))
+    .add_item(CustomMenuItem::new("script_mode", "Script Mode"))
     .add_item(CustomMenuItem::new("system_proxy", "System Proxy"))
     .add_item(CustomMenuItem::new("tun_mode", "Tun Mode"))
     .add_item(CustomMenuItem::new("restart_clash", "Restart Clash"))
@@ -45,6 +50,22 @@ fn main() -> std::io::Result<()> {
         "open_window" => {
           resolve::create_window(app_handle);
         }
+        "rule_mode" => {
+          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"));
+        }
         "system_proxy" => {
           let core = app_handle.state::<core::Core>();