fix: prevent duplicate widget windows on Windows/Linux (single instance)

Add tauri-plugin-single-instance (Windows/Linux only) registered as the
first plugin. A second launch — autostart, deep link, or re-open — now
focuses the running app instead of spawning another process with its own
floating widget. macOS enforces a single instance natively, which is why
the bug never appeared there. The deep-link feature forwards auth URLs
from a blocked second instance to the running app.
This commit is contained in:
David Perov 2026-06-24 11:03:55 +03:00
parent 9eeb48a2c0
commit bbfe9982d1
3 changed files with 56 additions and 1 deletions

17
src-tauri/Cargo.lock generated
View file

@ -4958,6 +4958,7 @@ dependencies = [
"tauri-plugin-opener",
"tauri-plugin-process",
"tauri-plugin-shell",
"tauri-plugin-single-instance",
"tauri-plugin-store",
"tauri-plugin-updater",
"tokio",
@ -5332,6 +5333,22 @@ dependencies = [
"tokio",
]
[[package]]
name = "tauri-plugin-single-instance"
version = "2.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a33a5b7d78f0dec4406b003ea87c40bf928d801b6fd9323a556172c91d8712c1"
dependencies = [
"serde",
"serde_json",
"tauri",
"tauri-plugin-deep-link",
"thiserror 2.0.18",
"tracing",
"windows-sys 0.60.2",
"zbus",
]
[[package]]
name = "tauri-plugin-store"
version = "2.4.2"

View file

@ -54,6 +54,11 @@ objc2-foundation = "0.3"
[target.'cfg(any(target_os = "macos", windows, target_os = "linux"))'.dependencies]
tauri-plugin-autostart = "2.5.1"
# macOS enforces a single app instance natively; Windows/Linux need this plugin
# so a second launch focuses the running app instead of spawning another widget.
[target.'cfg(any(windows, target_os = "linux"))'.dependencies]
tauri-plugin-single-instance = { version = "2", features = ["deep-link"] }
[target.'cfg(target_os = "linux")'.dependencies]
pipewire = "0.10.0"
webkit2gtk = { version = "2.0.2", features = ["v2_8"] }

View file

@ -22,8 +22,41 @@ use tauri::{Emitter, Manager};
use tauri_plugin_deep_link::DeepLinkExt;
#[cfg_attr(mobile, tauri::mobile_entry_point)]
/// Bring the already-running instance to front when a second launch is blocked
/// by the single-instance plugin (Windows/Linux only).
#[cfg(any(windows, target_os = "linux"))]
fn focus_existing_instance(app: &tauri::AppHandle) {
use tauri::Manager;
if let Some(win) = app.get_webview_window("settings") {
let _ = win.unminimize();
let _ = win.show();
let _ = win.set_focus();
return;
}
if let Some(win) = app.get_webview_window("widget") {
let _ = win.show();
let _ = win.set_focus();
}
}
pub fn run() {
tauri::Builder::default()
#[allow(unused_mut)]
let mut builder = tauri::Builder::default();
// The single-instance plugin must be registered first. macOS enforces one
// app instance natively (so the duplicate-widget bug never happens there);
// on Windows/Linux a second launch — autostart, deep link, or re-open —
// would otherwise start another process with its own floating widget.
#[cfg(any(windows, target_os = "linux"))]
{
builder = builder.plugin(tauri_plugin_single_instance::init(
|app, _argv, _cwd| focus_existing_instance(app),
));
}
builder
.plugin(tauri_plugin_deep_link::init())
.plugin(tauri_plugin_dialog::init())
.plugin(tauri_plugin_opener::init())