diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 1baef020b..058a09b2c 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -8,9 +8,65 @@ use core::{ }; use reqwest::Client; use std::{collections::HashMap, sync::Arc}; -use tauri::{Emitter, Manager}; - +use tauri::{Emitter, Manager, RunEvent}; use tokio::sync::Mutex; +// Cleanup function that handles both async and sync cleanup using Tokio +fn on_exit(app: &tauri::AppHandle, need_exiting: bool) { + let app_handle = app.clone(); + + // Use tokio::task::block_in_place to run async cleanup synchronously + tokio::task::block_in_place(|| { + tauri::async_runtime::block_on(async { + // Get the app state for cleanup + let state = app_handle.state::(); + + // Hide window first + if let Some(window) = app_handle.get_webview_window("main") { + let _ = window.hide(); + } + + // Emit event to kill MCP servers (but don't wait for response) + if let Some(window) = app_handle.get_webview_window("main") { + let _ = window.emit("kill-mcp-servers", ()); + } + + // Add a small delay to let the frontend handle the event + tokio::time::sleep(tokio::time::Duration::from_millis(100)).await; + + // Force cleanup processes directly (don't rely on frontend) + cleanup_processes(state).await; + + // Stop the HTTP server with timeout + let client = Client::new(); + let url = "http://127.0.0.1:39291/processManager/destroy"; + + // Use timeout to prevent hanging + match tokio::time::timeout( + tokio::time::Duration::from_secs(5), + client.delete(url).send(), + ) + .await + { + Ok(_) => { + log::info!("HTTP server stopped successfully"); + } + Err(_) => { + log::warn!("HTTP server stop request timed out"); + } + } + + // Close the window after cleanup + if let Some(win) = app_handle.get_webview_window("main") { + let _ = win.close(); + } + }); + }); + + // Exit the app if needed + if need_exiting { + app.exit(0); + } +} #[cfg_attr(mobile, tauri::mobile_entry_point)] pub fn run() { @@ -22,7 +78,8 @@ pub fn run() { // when defining deep link schemes at runtime, you must also check `argv` here })); } - builder + + let app = builder .plugin(tauri_plugin_os::init()) .plugin(tauri_plugin_deep_link::init()) .plugin(tauri_plugin_dialog::init()) @@ -136,19 +193,45 @@ pub fn run() { .on_window_event(|window, event| match event { tauri::WindowEvent::CloseRequested { .. } => { if window.label() == "main" { - window.emit("kill-mcp-servers", ()).unwrap(); - let state = window.app_handle().state::(); - - tauri::async_runtime::block_on(async { - cleanup_processes(state).await; - }); + // Run cleanup and exit + on_exit(&window.app_handle(), true); } - let client = Client::new(); - let url = "http://127.0.0.1:39291/processManager/destroy"; - let _ = client.delete(url).send(); } _ => {} }) - .run(tauri::generate_context!()) + .build(tauri::generate_context!()) .expect("error while running tauri application"); + + // Handle app lifecycle events + app.run(|app, event| match event { + RunEvent::Exit => { + // This is called when the app is actually exiting (e.g., macOS dock quit) + // We can't prevent this, so run cleanup quickly + let app_handle = app.clone(); + tokio::task::block_in_place(|| { + tauri::async_runtime::block_on(async { + let state = app_handle.state::(); + + // Hide window immediately + if let Some(window) = app_handle.get_webview_window("main") { + let _ = window.hide(); + let _ = window.emit("kill-mcp-servers", ()); + } + + // Quick cleanup with shorter timeout + cleanup_processes(state).await; + + // Stop HTTP server with shorter timeout + let client = Client::new(); + let url = "http://127.0.0.1:39291/processManager/destroy"; + let _ = tokio::time::timeout( + tokio::time::Duration::from_secs(2), + client.delete(url).send(), + ) + .await; + }); + }); + } + _ => {} + }); }