Compare commits

..

1 Commits

4 changed files with 70 additions and 43 deletions

View File

@@ -57,7 +57,7 @@
"axios": "^1.13.2", "axios": "^1.13.2",
"dayjs": "1.11.19", "dayjs": "1.11.19",
"foxact": "^0.2.49", "foxact": "^0.2.49",
"i18next": "^25.6.0", "i18next": "^25.6.1",
"js-yaml": "^4.1.0", "js-yaml": "^4.1.0",
"json-schema": "^0.4.0", "json-schema": "^0.4.0",
"lodash-es": "^4.17.21", "lodash-es": "^4.17.21",

16
pnpm-lock.yaml generated
View File

@@ -78,8 +78,8 @@ importers:
specifier: ^0.2.49 specifier: ^0.2.49
version: 0.2.49(react@19.2.0) version: 0.2.49(react@19.2.0)
i18next: i18next:
specifier: ^25.6.0 specifier: ^25.6.1
version: 25.6.0(typescript@5.9.3) version: 25.6.1(typescript@5.9.3)
js-yaml: js-yaml:
specifier: ^4.1.0 specifier: ^4.1.0
version: 4.1.0 version: 4.1.0
@@ -112,7 +112,7 @@ importers:
version: 7.66.0(react@19.2.0) version: 7.66.0(react@19.2.0)
react-i18next: react-i18next:
specifier: 16.2.4 specifier: 16.2.4
version: 16.2.4(i18next@25.6.0(typescript@5.9.3))(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(typescript@5.9.3) version: 16.2.4(i18next@25.6.1(typescript@5.9.3))(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(typescript@5.9.3)
react-markdown: react-markdown:
specifier: 10.1.0 specifier: 10.1.0
version: 10.1.0(@types/react@19.2.2)(react@19.2.0) version: 10.1.0(@types/react@19.2.2)(react@19.2.0)
@@ -2943,8 +2943,8 @@ packages:
engines: {node: '>=18'} engines: {node: '>=18'}
hasBin: true hasBin: true
i18next@25.6.0: i18next@25.6.1:
resolution: {integrity: sha512-tTn8fLrwBYtnclpL5aPXK/tAYBLWVvoHM1zdfXoRNLcI+RvtMsoZRV98ePlaW3khHYKuNh/Q65W/+NVFUeIwVw==} resolution: {integrity: sha512-yUWvdXtalZztmKrKw3yz/AvSP3yKyqIkVPx/wyvoYy9lkLmwzItLxp0iHZLG5hfVQ539Jor4XLO+U+NHIXg7pw==}
peerDependencies: peerDependencies:
typescript: ^5 typescript: ^5
peerDependenciesMeta: peerDependenciesMeta:
@@ -7359,7 +7359,7 @@ snapshots:
husky@9.1.7: {} husky@9.1.7: {}
i18next@25.6.0(typescript@5.9.3): i18next@25.6.1(typescript@5.9.3):
dependencies: dependencies:
'@babel/runtime': 7.28.4 '@babel/runtime': 7.28.4
optionalDependencies: optionalDependencies:
@@ -8183,11 +8183,11 @@ snapshots:
dependencies: dependencies:
react: 19.2.0 react: 19.2.0
react-i18next@16.2.4(i18next@25.6.0(typescript@5.9.3))(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(typescript@5.9.3): react-i18next@16.2.4(i18next@25.6.1(typescript@5.9.3))(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(typescript@5.9.3):
dependencies: dependencies:
'@babel/runtime': 7.28.4 '@babel/runtime': 7.28.4
html-parse-stringify: 3.0.1 html-parse-stringify: 3.0.1
i18next: 25.6.0(typescript@5.9.3) i18next: 25.6.1(typescript@5.9.3)
react: 19.2.0 react: 19.2.0
use-sync-external-store: 1.6.0(react@19.2.0) use-sync-external-store: 1.6.0(react@19.2.0)
optionalDependencies: optionalDependencies:

View File

@@ -211,18 +211,28 @@ impl Hotkey {
let is_quit = matches!(function, HotkeyFunction::Quit); let is_quit = matches!(function, HotkeyFunction::Quit);
manager.on_shortcut(hotkey, move |_app_handle, hotkey_event, event| { manager.on_shortcut(hotkey, move |_app_handle, hotkey_event, event| {
if event.state == ShortcutState::Pressed { let hotkey_event_owned = *hotkey_event;
logging!(debug, Type::Hotkey, "Hotkey pressed: {:?}", hotkey_event); let event_owned = event;
let hotkey = hotkey_event.key; let function_owned = function;
if hotkey == Code::KeyQ && is_quit { let is_quit_owned = is_quit;
if let Some(window) = handle::Handle::get_window()
&& window.is_focused().unwrap_or(false) AsyncHandler::spawn(move || async move {
{ if event_owned.state == ShortcutState::Pressed {
logging!(debug, Type::Hotkey, "Executing quit function"); logging!(
Self::execute_function(function); debug,
} Type::Hotkey,
} else { "Hotkey pressed: {:?}",
AsyncHandler::spawn(move || async move { hotkey_event_owned
);
if hotkey_event_owned.key == Code::KeyQ && is_quit_owned {
if let Some(window) = handle::Handle::get_window()
&& window.is_focused().unwrap_or(false)
{
logging!(debug, Type::Hotkey, "Executing quit function");
Self::execute_function(function_owned);
}
} else {
logging!(debug, Type::Hotkey, "Executing function directly"); logging!(debug, Type::Hotkey, "Executing function directly");
let is_enable_global_hotkey = Config::verge() let is_enable_global_hotkey = Config::verge()
@@ -232,19 +242,19 @@ impl Hotkey {
.unwrap_or(true); .unwrap_or(true);
if is_enable_global_hotkey { if is_enable_global_hotkey {
Self::execute_function(function); Self::execute_function(function_owned);
} else { } else {
use crate::utils::window_manager::WindowManager; use crate::utils::window_manager::WindowManager;
let is_visible = WindowManager::is_main_window_visible(); let is_visible = WindowManager::is_main_window_visible();
let is_focused = WindowManager::is_main_window_focused(); let is_focused = WindowManager::is_main_window_focused();
if is_focused && is_visible { if is_focused && is_visible {
Self::execute_function(function); Self::execute_function(function_owned);
} }
} }
}); }
} }
} });
})?; })?;
logging!( logging!(

View File

@@ -24,6 +24,8 @@ use futures::future::join_all;
use parking_lot::Mutex; use parking_lot::Mutex;
use smartstring::alias::String; use smartstring::alias::String;
use std::collections::HashMap; use std::collections::HashMap;
use std::future::Future;
use std::pin::Pin;
use std::sync::Arc; use std::sync::Arc;
use std::{ use std::{
sync::atomic::{AtomicBool, Ordering}, sync::atomic::{AtomicBool, Ordering},
@@ -548,34 +550,49 @@ impl Tray {
let tray = builder.build(app_handle)?; let tray = builder.build(app_handle)?;
tray.on_tray_icon_event(|_app_handle, event| { tray.on_tray_icon_event(|_app_handle, event| {
if let TrayIconEvent::Click { // 忽略移动、进入和离开等无需处理的事件,避免不必要的刷新
button: MouseButton::Left, match event {
button_state: MouseButtonState::Down, TrayIconEvent::Move { .. }
.. | TrayIconEvent::Enter { .. }
} = event | TrayIconEvent::Leave { .. } => {
{ return;
AsyncHandler::spawn(|| async move { }
let tray_event = { Config::verge().await.latest_arc().tray_event.clone() }; _ => {}
let tray_event: String = tray_event.unwrap_or_else(|| "main_window".into()); }
logging!(debug, Type::Tray, "tray event: {tray_event:?}");
AsyncHandler::spawn(|| async move {
let tray_event = { Config::verge().await.latest_arc().tray_event.clone() };
let tray_event: String = tray_event.unwrap_or_else(|| "main_window".into());
logging!(debug, Type::Tray, "tray event: {tray_event:?}");
if let TrayIconEvent::Click {
button: MouseButton::Left,
button_state: MouseButtonState::Down,
..
} = event
{
// 添加防抖检查,防止快速连击 // 添加防抖检查,防止快速连击
if !should_handle_tray_click() { if !should_handle_tray_click() {
return; return;
} }
match tray_event.as_str() { let fut: Pin<Box<dyn Future<Output = ()> + Send>> = match tray_event.as_str() {
"system_proxy" => feat::toggle_system_proxy().await, "system_proxy" => Box::pin(async move {
"tun_mode" => feat::toggle_tun_mode(None).await, feat::toggle_system_proxy().await;
"main_window" => { }),
"tun_mode" => Box::pin(async move {
feat::toggle_tun_mode(None).await;
}),
"main_window" => Box::pin(async move {
if !lightweight::exit_lightweight_mode().await { if !lightweight::exit_lightweight_mode().await {
WindowManager::show_main_window().await; WindowManager::show_main_window().await;
}; };
} }),
_ => {} _ => Box::pin(async move {}),
}; };
}); fut.await;
} }
});
}); });
tray.on_menu_event(on_menu_event); tray.on_menu_event(on_menu_event);
Ok(()) Ok(())