diff --git a/packages/desktop/src-tauri/Cargo.lock b/packages/desktop/src-tauri/Cargo.lock index 3ebc0e49..f04ab7a8 100644 --- a/packages/desktop/src-tauri/Cargo.lock +++ b/packages/desktop/src-tauri/Cargo.lock @@ -2847,7 +2847,7 @@ dependencies = [ [[package]] name = "openchamber-desktop" -version = "1.0.2" +version = "1.0.3" dependencies = [ "anyhow", "axum", @@ -2875,6 +2875,7 @@ dependencies = [ "tauri-plugin-fs", "tauri-plugin-log", "tauri-plugin-notification", + "tauri-plugin-process", "tauri-plugin-shell", "tauri-plugin-updater", "tokio", @@ -4721,6 +4722,16 @@ dependencies = [ "url", ] +[[package]] +name = "tauri-plugin-process" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d55511a7bf6cd70c8767b02c97bf8134fa434daf3926cfc1be0a0f94132d165a" +dependencies = [ + "tauri", + "tauri-plugin", +] + [[package]] name = "tauri-plugin-shell" version = "2.3.3" diff --git a/packages/desktop/src-tauri/Cargo.toml b/packages/desktop/src-tauri/Cargo.toml index 79322af4..a1b2437a 100644 --- a/packages/desktop/src-tauri/Cargo.toml +++ b/packages/desktop/src-tauri/Cargo.toml @@ -44,6 +44,7 @@ uuid = { version = "1.18.1", features = ["v4"] } tokio-util = { version = "0.7", features = ["io"] } tauri-plugin-notification = "2.3.3" tauri-plugin-updater = "2" +tauri-plugin-process = "2" [build-dependencies] tauri-build = { version = "2.5.3", features = [] } diff --git a/packages/desktop/src-tauri/capabilities/default.json b/packages/desktop/src-tauri/capabilities/default.json index 63edb380..8469c243 100644 --- a/packages/desktop/src-tauri/capabilities/default.json +++ b/packages/desktop/src-tauri/capabilities/default.json @@ -38,6 +38,6 @@ "updater:default", "updater:allow-check", "updater:allow-download-and-install", - "process:allow-relaunch" + "process:allow-restart" ] } diff --git a/packages/desktop/src-tauri/src/main.rs b/packages/desktop/src-tauri/src/main.rs index 9172e40b..651e5aa0 100644 --- a/packages/desktop/src-tauri/src/main.rs +++ b/packages/desktop/src-tauri/src/main.rs @@ -79,23 +79,11 @@ pub(crate) struct DesktopRuntime { } impl DesktopRuntime { - async fn initialize() -> Result { + fn initialize_sync() -> Result { let settings = Arc::new(SettingsStore::new()?); - - // Read lastDirectory from settings before starting OpenCode - let initial_dir = settings.last_directory().await.ok().flatten(); - + let initial_dir = tauri::async_runtime::block_on(settings.last_directory()).ok().flatten(); let opencode = Arc::new(OpenCodeManager::new_with_directory(initial_dir.clone())); - // Try to start OpenCode if CLI is available - if opencode.is_cli_available() { - if let Err(e) = opencode.ensure_running().await { - warn!("[desktop] Failed to start OpenCode: {}", e); - } - } else { - info!("[desktop] OpenCode CLI not available - running in limited mode"); - } - let client = Client::builder().build()?; let (shutdown_tx, shutdown_rx) = broadcast::channel(2); @@ -119,6 +107,16 @@ impl DesktopRuntime { }) } + async fn start_opencode(&self) { + if self.opencode.is_cli_available() { + if let Err(e) = self.opencode.ensure_running().await { + warn!("[desktop] Failed to start OpenCode: {}", e); + } + } else { + info!("[desktop] OpenCode CLI not available - running in limited mode"); + } + } + async fn shutdown(&self) { let _ = self.shutdown_tx.send(()); let _ = self.opencode.shutdown().await; @@ -257,18 +255,17 @@ fn main() { .plugin(fs_plugin()) .plugin(notification_plugin()) .plugin(tauri_plugin_updater::Builder::new().build()) + .plugin(tauri_plugin_process::init()) .plugin(log_builder.build()) .setup(|app| { #[cfg(target_os = "macos")] prevent_app_nap(); - let runtime = tauri::async_runtime::block_on(DesktopRuntime::initialize())?; - app.manage(runtime.clone()); + app.manage(TerminalState::new()); - // Background assistant completion notifications (SSE) independent of the webview lifecycle - let app_handle = app.app_handle(); - spawn_assistant_notifications(app_handle.clone(), runtime.clone()); - spawn_session_activity_tracker(app_handle.clone(), runtime.clone()); + let stored_state = tauri::async_runtime::block_on(load_window_state()).unwrap_or(None); + let manager = WindowStateManager::new(stored_state.clone().unwrap_or_default()); + app.manage(manager.clone()); if let Some(window) = app.get_webview_window("main") { #[cfg(target_os = "macos")] @@ -281,30 +278,32 @@ fn main() { info!("[desktop:vibrancy] Applied macOS Sidebar vibrancy to main window"); } } - } - - app.manage(TerminalState::new()); - let stored_state = tauri::async_runtime::block_on(load_window_state()).unwrap_or(None); - let manager = WindowStateManager::new(stored_state.clone().unwrap_or_default()); - app.manage(manager.clone()); - - if let Some(saved) = stored_state { - if let Some(window) = app.get_webview_window("main") { - let _ = window_state::apply_window_state(&window, &saved); + if let Some(saved) = &stored_state { + let _ = window_state::apply_window_state(&window, saved); } + + let _ = window.show(); } - // Restore bookmarks on startup (macOS security-scoped access) - // We'll do this synchronously within the setup to avoid lifetime issues - tauri::async_runtime::block_on(async { - if let Err(e) = - restore_bookmarks_on_startup(app.state::().clone()).await - { + let runtime = DesktopRuntime::initialize_sync()?; + app.manage(runtime.clone()); + + let app_handle = app.app_handle().clone(); + let runtime_clone = runtime.clone(); + tauri::async_runtime::spawn(async move { + runtime_clone.start_opencode().await; + + if let Err(e) = restore_bookmarks_on_startup(app_handle.state::().clone()).await { warn!("Failed to restore bookmarks on startup: {}", e); } + + let _ = app_handle.emit("openchamber:runtime-ready", ()); }); + spawn_assistant_notifications(app.app_handle().clone(), runtime.clone()); + spawn_session_activity_tracker(app.app_handle().clone(), runtime.clone()); + Ok(()) }) .invoke_handler(tauri::generate_handler![ diff --git a/packages/desktop/src-tauri/tauri.conf.json b/packages/desktop/src-tauri/tauri.conf.json index 04b2c254..38008ac2 100644 --- a/packages/desktop/src-tauri/tauri.conf.json +++ b/packages/desktop/src-tauri/tauri.conf.json @@ -26,7 +26,7 @@ "x": 17, "y": 26 }, - "visible": true, + "visible": false, "backgroundThrottling": "disabled" } ], diff --git a/packages/desktop/src/main.tsx b/packages/desktop/src/main.tsx index c27e1344..c2d7e55c 100644 --- a/packages/desktop/src/main.tsx +++ b/packages/desktop/src/main.tsx @@ -237,6 +237,15 @@ if (typeof window !== 'undefined') { }); } +const runtimeReady = new Promise((resolve) => { + listen('openchamber:runtime-ready', () => { + console.info('[main] Runtime ready event received'); + resolve(); + }); +}); + +await runtimeReady; + try { await import('@openchamber/ui/main'); } catch (error) {