Skip to content

Commit 9a33e24

Browse files
committed
Make multi-window rock steady
Found some critical race conditions that might've led to app crashes in the past.
1 parent 2c805ef commit 9a33e24

4 files changed

Lines changed: 30 additions & 9 deletions

File tree

apps/desktop/src-tauri/src/lib.rs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -323,7 +323,8 @@ pub fn run() {
323323
serde_json::json!({ "action": "sortOrder", "value": order }),
324324
);
325325
} else if id == VIEWER_WORD_WRAP_ID {
326-
// Find the focused viewer window and emit toggle event to it
326+
// Toggle word wrap on the focused viewer window (if any).
327+
// Safe: unwrap_or(false) handles destroyed windows, `let _ =` ignores emit errors.
327328
for (label, window) in app.webview_windows() {
328329
if label.starts_with("viewer-") && window.is_focused().unwrap_or(false) {
329330
let _ = app.emit_to(&label, "viewer-word-wrap-toggled", ());
@@ -598,8 +599,10 @@ pub fn run() {
598599
network::mdns_discovery::stop_discovery();
599600
window.app_handle().exit(0);
600601
}
601-
// Also handle window destruction for cleanup
602-
if let tauri::WindowEvent::Destroyed = event {
602+
// Clean up app-wide resources only when the main window is destroyed
603+
if let tauri::WindowEvent::Destroyed = event
604+
&& window.label() == "main"
605+
{
603606
ai::manager::shutdown();
604607
#[cfg(target_os = "macos")]
605608
network::mdns_discovery::stop_discovery();

apps/desktop/src-tauri/src/mcp/executor.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -543,7 +543,9 @@ fn execute_dialog_close<R: Runtime>(app: &AppHandle<R>, dialog_type: &str, path:
543543

544544
match dialog_type {
545545
"settings" => {
546-
app.emit_to("settings", "mcp-settings-close", ())?;
546+
if app.webview_windows().contains_key("settings") {
547+
app.emit_to("settings", "mcp-settings-close", ())?;
548+
}
547549
Ok(json!("OK: Closed settings"))
548550
}
549551
"file-viewer" => {

apps/desktop/src/routes/(main)/+page.svelte

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -197,14 +197,24 @@
197197
await emitToFileViewers('mcp-viewer-close', { path })
198198
} else {
199199
// Close the most recent viewer directly
200-
await viewers[0].close()
200+
try {
201+
await viewers[0].close()
202+
} catch {
203+
// Window may already be closed
204+
}
201205
}
202206
}
203207
204-
/** Close all file viewer windows */
208+
/** Close all file viewer windows sequentially to avoid concurrent destruction races */
205209
async function closeAllFileViewers() {
206210
const viewers = await getFileViewerWindows()
207-
await Promise.all(viewers.map((v) => v.close()))
211+
for (const viewer of viewers) {
212+
try {
213+
await viewer.close()
214+
} catch {
215+
// Window may already be closed
216+
}
217+
}
208218
}
209219
210220
/** Focus a file viewer window. If path is provided, focuses the viewer with that path. Otherwise focuses the most recent. */
@@ -216,8 +226,11 @@
216226
// Emit event with path - the viewer with that path will focus itself
217227
await emitToFileViewers('mcp-viewer-focus', { path })
218228
} else {
219-
// Focus the most recent viewer directly
220-
await viewers[0].setFocus()
229+
try {
230+
await viewers[0].setFocus()
231+
} catch {
232+
// Window may already be closed
233+
}
221234
}
222235
}
223236

apps/desktop/src/routes/viewer/+page.svelte

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@
9696
// Window lifecycle state: prevents closing before WebKit is fully initialized
9797
let windowReady = $state(false)
9898
let closeRequested = $state(false)
99+
let closing = false
99100
100101
// Event listener cleanup functions
101102
let unlistenMcpClose: UnlistenFn | undefined
@@ -437,12 +438,14 @@
437438
})
438439
439440
function closeWindow() {
441+
if (closing) return
440442
// If window isn't ready yet, queue the close for when it is
441443
if (!windowReady) {
442444
feLog('[viewer] closeWindow: window not ready, queueing close')
443445
closeRequested = true
444446
return
445447
}
448+
closing = true
446449
447450
const start = performance.now()
448451
feLog('[viewer] closeWindow: starting')

0 commit comments

Comments
 (0)