diff --git a/src-tauri/src/cmd/app.rs b/src-tauri/src/cmd/app.rs index 4677b0d6..76cf9829 100644 --- a/src-tauri/src/cmd/app.rs +++ b/src-tauri/src/cmd/app.rs @@ -222,3 +222,11 @@ pub fn notify_ui_ready() -> CmdResult<()> { crate::utils::resolve::mark_ui_ready(); Ok(()) } + +/// 重置UI就绪状态 +#[tauri::command] +pub fn reset_ui_ready_state() -> CmdResult<()> { + log::info!(target: "app", "重置UI就绪状态"); + crate::utils::resolve::reset_ui_ready(); + Ok(()) +} diff --git a/src-tauri/src/cmd/lightweight.rs b/src-tauri/src/cmd/lightweight.rs index ac71ad37..7afbe0ef 100644 --- a/src-tauri/src/cmd/lightweight.rs +++ b/src-tauri/src/cmd/lightweight.rs @@ -7,3 +7,9 @@ pub async fn entry_lightweight_mode() -> CmdResult { lightweight::entry_lightweight_mode(); Ok(()) } + +#[tauri::command] +pub async fn exit_lightweight_mode() -> CmdResult { + lightweight::exit_lightweight_mode(); + Ok(()) +} diff --git a/src-tauri/src/core/tray/mod.rs b/src-tauri/src/core/tray/mod.rs index 274ba672..37d813e1 100644 --- a/src-tauri/src/core/tray/mod.rs +++ b/src-tauri/src/core/tray/mod.rs @@ -206,7 +206,14 @@ impl Tray { match tray_event.as_str() { "system_proxy" => feat::toggle_system_proxy(), "tun_mode" => feat::toggle_tun_mode(None), - "main_window" => resolve::create_window(true), + "main_window" => { + // 如果在轻量模式中,先退出轻量模式 + if crate::module::lightweight::is_in_lightweight_mode() { + crate::module::lightweight::exit_lightweight_mode(); + } + // 然后创建窗口 + resolve::create_window(true) + } _ => {} } } @@ -680,7 +687,14 @@ fn on_menu_event(_: &AppHandle, event: MenuEvent) { println!("change mode to: {}", mode); feat::change_clash_mode(mode.into()); } - "open_window" => resolve::create_window(true), + "open_window" => { + // 如果在轻量模式中,先退出轻量模式 + if crate::module::lightweight::is_in_lightweight_mode() { + crate::module::lightweight::exit_lightweight_mode(); + } + // 然后创建窗口 + resolve::create_window(true) + } "system_proxy" => feat::toggle_system_proxy(), "tun_mode" => feat::toggle_tun_mode(None), "copy_env" => feat::copy_clash_env(), diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 01a22da7..85a04ae2 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -160,10 +160,14 @@ pub fn run() { cmd::restart_app, // 添加新的命令 cmd::notify_ui_ready, + cmd::reset_ui_ready_state, cmd::get_running_mode, cmd::get_app_uptime, cmd::get_auto_launch_status, cmd::is_admin, + // 添加轻量模式相关命令 + cmd::entry_lightweight_mode, + cmd::exit_lightweight_mode, // service 管理 cmd::install_service, cmd::uninstall_service, diff --git a/src-tauri/src/module/lightweight.rs b/src-tauri/src/module/lightweight.rs index 4f55b494..17849275 100644 --- a/src-tauri/src/module/lightweight.rs +++ b/src-tauri/src/module/lightweight.rs @@ -9,12 +9,33 @@ use crate::{ use anyhow::{Context, Result}; use delay_timer::prelude::TaskBuilder; use once_cell::sync::OnceCell; +use parking_lot::RwLock; +use std::sync::Arc; use tauri::{Listener, Manager}; pub static AUTO_LIGHT_WEIGHT_MODE_INIT: OnceCell<()> = OnceCell::new(); const LIGHT_WEIGHT_TASK_UID: &str = "light_weight_task"; +// 轻量模式状态标志 +static IS_LIGHTWEIGHT_MODE: OnceCell>> = OnceCell::new(); + +fn get_lightweight_mode() -> &'static Arc> { + IS_LIGHTWEIGHT_MODE.get_or_init(|| Arc::new(RwLock::new(false))) +} + +// 检查是否处于轻量模式 +pub fn is_in_lightweight_mode() -> bool { + *get_lightweight_mode().read() +} + +// 设置轻量模式状态 +fn set_lightweight_mode(value: bool) { + let mut mode = get_lightweight_mode().write(); + *mode = value; + logging!(info, Type::Lightweight, true, "轻量模式状态: {}", value); +} + pub fn enable_auto_light_weight_mode() { Timer::global().init().unwrap(); logging!(info, Type::Lightweight, true, "开启自动轻量模式"); @@ -40,9 +61,21 @@ pub fn entry_lightweight_mode() { AppHandleManager::global().set_activation_policy_accessory(); logging!(info, Type::Lightweight, true, "轻量模式已开启"); } + // 标记已进入轻量模式 + set_lightweight_mode(true); let _ = cancel_light_weight_timer(); } +// 从轻量模式恢复 +pub fn exit_lightweight_mode() { + // 标记退出轻量模式 + set_lightweight_mode(false); + logging!(info, Type::Lightweight, true, "正在退出轻量模式"); + + // 重置UI就绪状态 + crate::utils::resolve::reset_ui_ready(); +} + pub fn add_light_weight_timer() { logging_error!(Type::Lightweight, setup_light_weight_timer()); } diff --git a/src-tauri/src/utils/resolve.rs b/src-tauri/src/utils/resolve.rs index dc1a7c5a..fa166e05 100644 --- a/src-tauri/src/utils/resolve.rs +++ b/src-tauri/src/utils/resolve.rs @@ -16,7 +16,7 @@ use percent_encoding::percent_decode_str; use serde_json; use serde_yaml::Mapping; use std::{net::TcpListener, sync::Arc}; -use tauri::{App, Manager}; +use tauri::{App, Emitter, Manager}; use tauri::Url; //#[cfg(not(target_os = "linux"))] @@ -41,6 +41,13 @@ pub fn mark_ui_ready() { *ready = true; } +// 重置UI就绪状态 +pub fn reset_ui_ready() { + let mut ready = get_ui_ready().write(); + *ready = false; + logging!(info, Type::Window, true, "UI就绪状态已重置"); +} + pub fn find_unused_port() -> Result { match TcpListener::bind("127.0.0.1:0") { Ok(listener) => { @@ -219,26 +226,30 @@ pub fn create_window(is_showup: bool) { #[cfg(target_os = "macos")] AppHandleManager::global().set_activation_policy_regular(); + // 检查是否从轻量模式恢复 + let from_lightweight = crate::module::lightweight::is_in_lightweight_mode(); + if from_lightweight { + logging!(info, Type::Window, true, "从轻量模式恢复窗口"); + crate::module::lightweight::exit_lightweight_mode(); + } + if let Some(window) = handle::Handle::global().get_window() { - logging!( - info, - Type::Window, - true, - "Found existing window, attempting to restore visibility" - ); + logging!(info, Type::Window, true, "Found existing window"); if window.is_minimized().unwrap_or(false) { - logging!( - info, - Type::Window, - true, - "Window is minimized, restoring window state" - ); let _ = window.unminimize(); } - let _ = window.show(); - let _ = window.set_focus(); - return; + + if from_lightweight { + // 从轻量模式恢复需要销毁旧窗口以重建 + logging!(info, Type::Window, true, "销毁旧窗口以重建新窗口"); + let _ = window.close(); + } else { + // 普通情况直接显示窗口 + let _ = window.show(); + let _ = window.set_focus(); + return; + } } // 定义默认窗口大小 @@ -346,83 +357,65 @@ pub fn create_window(is_showup: bool) { } } + // 创建异步任务处理UI就绪和显示窗口 + let was_from_lightweight = from_lightweight; AsyncHandler::spawn(move || async move { - use tauri::Emitter; - - logging!(info, Type::Window, true, "UI gets ready."); + // 处理启动完成 handle::Handle::global().mark_startup_completed(); if let Some(window) = app_handle_clone.get_webview_window("main") { - // 检查窗口大小 - match window.inner_size() { - Ok(size) => { - let width = size.width; - let height = size.height; - - let state_width = STATE_WIDTH.get().copied().unwrap_or(DEFAULT_WIDTH); - let state_height = - STATE_HEIGHT.get().copied().unwrap_or(DEFAULT_HEIGHT); - - logging!( - info, - Type::Window, - true, - "异步任务中窗口尺寸: {}x{}, 状态文件尺寸: {}x{}", - width, - height, - state_width, - state_height - ); - } - Err(e) => { - logging!( - error, - Type::Window, - true, - "Failed to get window size: {:?}", - e - ); - } - } - // 发送启动完成事件 let _ = window.emit("verge://startup-completed", ()); if is_showup { - // 启动一个任务等待UI准备就绪再显示窗口 let window_clone = window.clone(); - AsyncHandler::spawn(move || async move { - async fn wait_for_ui_ready() { - while !*get_ui_ready().read() { - tokio::time::sleep(std::time::Duration::from_millis(50)).await; - } - } - match tokio::time::timeout( - std::time::Duration::from_secs(5), - wait_for_ui_ready(), - ) - .await - { - Ok(_) => { - logging!(info, Type::Window, true, "UI准备就绪,显示窗口"); - } - Err(_) => { - logging!( - warn, - Type::Window, - true, - "等待UI准备就绪超时,强制显示窗口" - ); - } - } + // 从轻量模式恢复时使用较短的超时,避免卡死 + let timeout_seconds = if was_from_lightweight { + // 从轻量模式恢复只等待2秒,确保不会卡死 + 2 + } else { + 5 + }; - let _ = window_clone.show(); - let _ = window_clone.set_focus(); - }); + // 等待UI就绪 + async fn wait_for_ui_ready() { + while !*get_ui_ready().read() { + tokio::time::sleep(std::time::Duration::from_millis(100)).await; + } + } + + // 使用超时机制等待UI就绪 + match tokio::time::timeout( + std::time::Duration::from_secs(timeout_seconds), + wait_for_ui_ready(), + ) + .await + { + Ok(_) => { + logging!(info, Type::Window, true, "UI准备就绪,显示窗口"); + } + Err(_) => { + logging!( + warn, + Type::Window, + true, + "等待UI准备就绪超时({}秒),强制显示窗口", + timeout_seconds + ); + } + } + + // 无论是否超时,都显示窗口 + let _ = window_clone.show(); + let _ = window_clone.set_focus(); } + } else { + logging!(error, Type::Window, true, "无法获取主窗口"); } }); + + logging!(info, Type::Window, true, "异步任务已创建"); } Err(e) => { logging!( diff --git a/src/pages/_layout.tsx b/src/pages/_layout.tsx index ae8acacd..44acafd2 100644 --- a/src/pages/_layout.tsx +++ b/src/pages/_layout.tsx @@ -214,6 +214,8 @@ const Layout = () => { useEffect(() => { const notifyUiReady = async () => { try { + await invoke("reset_ui_ready_state"); + console.log("已重置UI就绪状态"); await new Promise(resolve => setTimeout(resolve, 200)); await invoke("notify_ui_ready"); console.log("已通知后端UI准备就绪"); @@ -224,12 +226,19 @@ const Layout = () => { // 监听后端发送的启动完成事件 const listenStartupCompleted = async () => { - const unlisten = await listen("verge://startup-completed", () => { - console.log("收到启动完成事件"); - }); - return unlisten; + try { + const unlisten = await listen("verge://startup-completed", () => { + console.log("收到启动完成事件,开始通知UI就绪"); + notifyUiReady(); + }); + return unlisten; + } catch (err) { + console.error("监听启动完成事件失败:", err); + return () => {}; + } }; + console.log("页面初始加载,通知UI就绪"); notifyUiReady(); const unlistenPromise = listenStartupCompleted();