From 4c2881aaf09c4ceda96e23297f0717a9ea628b4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=A8=8B=E5=BA=8F=E5=91=98=E9=98=BF=E6=B1=9F=28Relakkes?= =?UTF-8?q?=29?= Date: Thu, 7 May 2026 16:27:49 +0800 Subject: [PATCH] Prevent duplicate desktop app instances Notification clicks and repeated launches can enter the native app through OS-level activation paths, so the desktop runtime now registers Tauri's single-instance guard before sidecars or other plugins start. A second launch restores and focuses the existing main window instead of creating another app process. Constraint: macOS and Windows launch paths must reuse the running desktop instance. Rejected: Handle only notification click callbacks | does not cover Dock, shortcut, installer, or Windows relaunch paths. Confidence: high Scope-risk: narrow Directive: Keep the single-instance plugin before sidecar startup so duplicate launches cannot create extra runtime processes. Tested: SKIP_INSTALL=1 ./desktop/scripts/build-macos-arm64.sh Tested: open -n built app twice; desktop_count stayed 1 with pid 69821 Tested: bun run check:native Tested: cargo test --manifest-path desktop/src-tauri/Cargo.toml Tested: cd desktop && bun run test -- desktopNotifications.test.ts Tested: cd desktop && bun run lint --- desktop/src-tauri/Cargo.lock | 16 ++++++++++++++++ desktop/src-tauri/Cargo.toml | 1 + desktop/src-tauri/src/lib.rs | 4 ++++ 3 files changed, 21 insertions(+) diff --git a/desktop/src-tauri/Cargo.lock b/desktop/src-tauri/Cargo.lock index e4ba4009..3b4237b2 100644 --- a/desktop/src-tauri/Cargo.lock +++ b/desktop/src-tauri/Cargo.lock @@ -485,6 +485,7 @@ dependencies = [ "tauri-plugin-notification", "tauri-plugin-process", "tauri-plugin-shell", + "tauri-plugin-single-instance", "tauri-plugin-updater", "tauri-runtime", "tauri-runtime-wry", @@ -4386,6 +4387,21 @@ dependencies = [ "tokio", ] +[[package]] +name = "tauri-plugin-single-instance" +version = "2.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c8f29386f5e9fdc699182388a33ee80a56de436d91b67459e86afef426282af" +dependencies = [ + "serde", + "serde_json", + "tauri", + "thiserror 2.0.18", + "tracing", + "windows-sys 0.60.2", + "zbus", +] + [[package]] name = "tauri-plugin-updater" version = "2.10.1" diff --git a/desktop/src-tauri/Cargo.toml b/desktop/src-tauri/Cargo.toml index 96e0dcd7..5af0ed59 100644 --- a/desktop/src-tauri/Cargo.toml +++ b/desktop/src-tauri/Cargo.toml @@ -23,3 +23,4 @@ serde_json = "1" anyhow = "1.0.102" portable-pty = "0.9.0" tauri-plugin-notification = "2" +tauri-plugin-single-instance = "2" diff --git a/desktop/src-tauri/src/lib.rs b/desktop/src-tauri/src/lib.rs index abc6a5ba..743c19c3 100644 --- a/desktop/src-tauri/src/lib.rs +++ b/desktop/src-tauri/src/lib.rs @@ -1487,6 +1487,10 @@ mod tests { #[cfg_attr(mobile, tauri::mobile_entry_point)] pub fn run() { let builder = tauri::Builder::default() + // Keep this first so duplicate launches are stopped before sidecars start. + .plugin(tauri_plugin_single_instance::init(|app, _args, _cwd| { + show_main_window(app); + })) .manage(ServerState::default()) .manage(AdapterState::default()) .manage(TerminalState::default())