From fd8c043c8aa6a997d2d355cdc55c26940af5e3f2 Mon Sep 17 00:00:00 2001 From: archdex-art Date: Fri, 17 Jul 2026 13:53:51 +0530 Subject: [PATCH] Fix hotkey not reliably summoning the palette (v0.1.15) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Three fixes found while tracing 'hotkey doesn't trigger most of the time' back to real, factually-confirmed causes: - Two competing processes could run simultaneously (a stray /Applications install alongside a cargo tauri dev session), each with its own independently-cached SettingsStore and its own attempt at registering the same global hotkey — confirmed via ps aux showing both alive at once. Added tauri-plugin-single-instance so a second launch just re-summons the running instance's palette instead of starting a competing process. Verified live: launching the built binary twice leaves exactly one process alive, confirmed via ps that the second exits (defunct, status 0). - The palette runs under ActivationPolicy::Accessory (no Dock icon) — the one case where WebviewWindow::set_focus() alone is unreliable on macOS. It calls makeKeyAndOrderFront: on the window but never activates the process; summoned via a background hotkey while another app holds focus, the window could end up shown but not actually key, silently swallowing keyboard input. Confirmed via grep that this codebase never called NSApplication activateIgnoringOtherApps: anywhere. Added it to show_palette. - closingRef (lets the toggle-hotkey listener tell 'idle' apart from 'mid-close-animation') was mirrored from React state via a useEffect, which only runs after React commits — a hotkey press landing in that gap read a stale value and silently no-op'd instead of cancelling the close and reopening. Now updated synchronously at the same call site as setClosing. Bump to v0.1.15, changelog entry. --- CHANGELOG.md | 44 +++- Cargo.lock | 373 ++++++++++++++++++++++++++++++++++ react-command-palette/App.tsx | 28 ++- src-tauri/Cargo.toml | 3 +- src-tauri/src/lib.rs | 57 ++++++ src-tauri/tauri.conf.json | 2 +- 6 files changed, 494 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 32f409c..8997f67 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,47 @@ follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/); versions correspond to [GitHub Releases](https://github.com/archdex-art/SuperSearch/releases) and their published installers. +## [0.1.15] — 2026-07-17 + +Three fixes for "the hotkey doesn't reliably summon the palette," found while +chasing a settings-persistence report back to its actual root cause. + +### Fixed +- **Two competing processes could run at once, each with its own cached + settings.** A second launch (a stray `/Applications` install left running + alongside a `cargo tauri dev` session, or an overlapping dev restart) + started a fully independent process — its own `SettingsStore` loaded once + from disk at its own boot time, its own attempt at registering the same + global hotkey. Two processes racing for one hotkey meant summoning "the + palette" could nondeterministically show either one's window, and each had + its own stale view of settings (the `rev` guard added in 0.1.14 only + covers races *within* one process — it can't help across two with no + shared memory). Added `tauri-plugin-single-instance`, registered first per + Tauri's requirement: a second launch now just re-summons the one running + instance's palette instead of starting a competing process. Verified live + — launched the binary twice and confirmed via `ps` that the second + process exits (defunct, status 0) instead of staying alive. +- **The hotkey could show the window without it ever actually taking + keyboard focus.** The palette runs under `ActivationPolicy::Accessory` (no + Dock icon) — the one case where `WebviewWindow::set_focus()` alone is + unreliable on macOS. `set_focus()` calls `makeKeyAndOrderFront:` on the + *window*; it doesn't activate the *process*. Summoned from a background + global hotkey while a different app holds focus, an accessory app's window + can end up visually shown but not actually key, so it silently never + receives keyboard input — indistinguishable from "the hotkey did nothing." + `show_palette` now also calls `activateIgnoringOtherApps:` on + `NSApplication`, which `set_focus()` never did. +- **A hotkey press during the close animation could get silently dropped.** + `closingRef` (read by the toggle-hotkey listener to tell "genuinely idle" + apart from "still animating closed") was mirrored from React state via a + `useEffect`, which only runs *after* React commits the next render. A + hotkey re-summon landing in that gap — e.g. right after `hide_on_blur` + starts a close — read the *previous* value of `closingRef.current` and hit + the wrong branch: it called `hide()` again (a no-op, already closing) + instead of cancelling the close and reopening. `closingRef` is now updated + synchronously at the same call site as `setClosing`, closing that window + entirely rather than narrowing it. + ## [0.1.14] — 2026-07-17 Fixes an accent-color persistence race: a color chosen in Settings could @@ -369,7 +410,8 @@ First cross-platform release — macOS, Linux, and Windows. --- -[Unreleased]: https://github.com/archdex-art/SuperSearch/compare/v0.1.14...HEAD +[Unreleased]: https://github.com/archdex-art/SuperSearch/compare/v0.1.15...HEAD +[0.1.15]: https://github.com/archdex-art/SuperSearch/releases/tag/v0.1.15 [0.1.14]: https://github.com/archdex-art/SuperSearch/releases/tag/v0.1.14 [0.1.13]: https://github.com/archdex-art/SuperSearch/releases/tag/v0.1.13 [0.1.12]: https://github.com/archdex-art/SuperSearch/releases/tag/v0.1.12 diff --git a/Cargo.lock b/Cargo.lock index 8d87c54..df26b5d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -110,6 +110,126 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" +[[package]] +name = "async-broadcast" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "435a87a52755b8f27fcf321ac4f04b2802e337c8c4872923137471ec39c37532" +dependencies = [ + "event-listener", + "event-listener-strategy", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-channel" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "924ed96dd52d1b75e9c1a3e6275715fd320f5f9439fb5a4a11fa51f4221158d2" +dependencies = [ + "concurrent-queue", + "event-listener-strategy", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-executor" +version = "1.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c96bf972d85afc50bf5ab8fe2d54d1586b4e0b46c97c50a0c9e71e2f7bcd812a" +dependencies = [ + "async-task", + "concurrent-queue", + "fastrand", + "futures-lite", + "pin-project-lite", + "slab", +] + +[[package]] +name = "async-io" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "456b8a8feb6f42d237746d4b3e9a178494627745c3c56c6ea55d92ba50d026fc" +dependencies = [ + "autocfg", + "cfg-if", + "concurrent-queue", + "futures-io", + "futures-lite", + "parking", + "polling", + "rustix 1.1.4", + "slab", + "windows-sys 0.61.2", +] + +[[package]] +name = "async-lock" +version = "3.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290f7f2596bd5b78a9fec8088ccd89180d7f9f55b94b0576823bbbdc72ee8311" +dependencies = [ + "event-listener", + "event-listener-strategy", + "pin-project-lite", +] + +[[package]] +name = "async-process" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc50921ec0055cdd8a16de48773bfeec5c972598674347252c0399676be7da75" +dependencies = [ + "async-channel", + "async-io", + "async-lock", + "async-signal", + "async-task", + "blocking", + "cfg-if", + "event-listener", + "futures-lite", + "rustix 1.1.4", +] + +[[package]] +name = "async-recursion" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "async-signal" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52b5aaafa020cf5053a01f2a60e8ff5dccf550f0f77ec54a4e47285ac2bab485" +dependencies = [ + "async-io", + "async-lock", + "atomic-waker", + "cfg-if", + "futures-core", + "futures-io", + "rustix 1.1.4", + "signal-hook-registry", + "slab", + "windows-sys 0.61.2", +] + +[[package]] +name = "async-task" +version = "4.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" + [[package]] name = "async-trait" version = "0.1.89" @@ -239,6 +359,19 @@ dependencies = [ "objc2", ] +[[package]] +name = "blocking" +version = "1.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e83f8d02be6967315521be875afa792a316e28d57b5a2d401897e2a7921b7f21" +dependencies = [ + "async-channel", + "async-task", + "futures-io", + "futures-lite", + "piper", +] + [[package]] name = "brotli" version = "8.0.3" @@ -517,6 +650,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "concurrent-queue" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" +dependencies = [ + "crossbeam-utils", +] + [[package]] name = "constant_time_eq" version = "0.4.2" @@ -1188,6 +1330,33 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "endi" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66b7e2430c6dff6a955451e2cfc438f09cea1965a9d6f87f7e3b90decc014099" + +[[package]] +name = "enumflags2" +version = "0.7.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1027f7680c853e056ebcec683615fb6fbbc07dbaa13b4d5d9442b146ded4ecef" +dependencies = [ + "enumflags2_derive", + "serde", +] + +[[package]] +name = "enumflags2_derive" +version = "0.7.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67c78a4d8fdf9953a5c9d458f9efe940fd97a0cab0941c075a813ac594733827" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + [[package]] name = "equivalent" version = "1.0.2" @@ -1215,6 +1384,27 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "event-listener" +version = "5.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13b66accf52311f30a0db42147dadea9850cb48cd070028831ae5f5d4b856ab" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite", +] + +[[package]] +name = "event-listener-strategy" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93" +dependencies = [ + "event-listener", + "pin-project-lite", +] + [[package]] name = "fallible-iterator" version = "0.3.0" @@ -1379,6 +1569,19 @@ version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" +[[package]] +name = "futures-lite" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f78e10609fe0e0b3f4157ffab1876319b5b0db102a2c60dc4626306dc46b44ad" +dependencies = [ + "fastrand", + "futures-core", + "futures-io", + "parking", + "pin-project-lite", +] + [[package]] name = "futures-macro" version = "0.3.32" @@ -2899,6 +3102,16 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" +[[package]] +name = "ordered-stream" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aa2b01e1d916879f73a53d01d1d6cee68adbb31d6d9177a8cfce093cced1d50" +dependencies = [ + "futures-core", + "pin-project-lite", +] + [[package]] name = "osakit" version = "0.3.1" @@ -2938,6 +3151,12 @@ dependencies = [ "system-deps", ] +[[package]] +name = "parking" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" + [[package]] name = "parking_lot" version = "0.12.5" @@ -3042,6 +3261,17 @@ version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" +[[package]] +name = "piper" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c835479a4443ded371d6c535cbfd8d31ad92c5d23ae9770a61bc155e4992a3c1" +dependencies = [ + "atomic-waker", + "fastrand", + "futures-io", +] + [[package]] name = "pkg-config" version = "0.3.33" @@ -3115,6 +3345,20 @@ dependencies = [ "miniz_oxide", ] +[[package]] +name = "polling" +version = "3.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d0e4f59085d47d8241c88ead0f274e8a0cb551f3625263c05eb8dd897c34218" +dependencies = [ + "cfg-if", + "concurrent-queue", + "hermit-abi", + "pin-project-lite", + "rustix 1.1.4", + "windows-sys 0.61.2", +] + [[package]] name = "postcard" version = "1.1.3" @@ -4094,6 +4338,7 @@ dependencies = [ "tauri-build", "tauri-plugin-dialog", "tauri-plugin-global-shortcut", + "tauri-plugin-single-instance", "tauri-plugin-updater", "tempfile", "tokio", @@ -4453,6 +4698,22 @@ dependencies = [ "thiserror 2.0.18", ] +[[package]] +name = "tauri-plugin-single-instance" +version = "2.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3214becf9ef5783c0ae99a3bb25adf5353a7a16ebf53e74b909e29205735c6c" +dependencies = [ + "serde", + "serde_json", + "tauri", + "thiserror 2.0.18", + "tokio", + "tracing", + "windows-sys 0.60.2", + "zbus", +] + [[package]] name = "tauri-plugin-updater" version = "2.10.1" @@ -5092,6 +5353,17 @@ version = "1.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20" +[[package]] +name = "uds_windows" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f6fb2847f6742cd76af783a2a2c49e9375d0a111c7bef6f71cd9e738c72d6e" +dependencies = [ + "memoffset", + "tempfile", + "windows-sys 0.61.2", +] + [[package]] name = "unic-char-property" version = "0.9.0" @@ -6567,6 +6839,67 @@ dependencies = [ "synstructure", ] +[[package]] +name = "zbus" +version = "5.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a28b97f866896a4be7aefd2b5a8e01bb6773d19a775d54ab28b4d094b9a4480e" +dependencies = [ + "async-broadcast", + "async-executor", + "async-io", + "async-lock", + "async-process", + "async-recursion", + "async-task", + "async-trait", + "blocking", + "enumflags2", + "event-listener", + "futures-core", + "futures-lite", + "hex", + "libc", + "ordered-stream", + "rustix 1.1.4", + "serde", + "serde_repr", + "tracing", + "uds_windows", + "uuid", + "windows-sys 0.61.2", + "winnow 1.0.3", + "zbus_macros", + "zbus_names", + "zvariant", +] + +[[package]] +name = "zbus_macros" +version = "5.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e05ad887425eecf5e8384dc2406a4a9313eb73468712fc1cdea362eb4fe0469" +dependencies = [ + "proc-macro-crate 3.5.0", + "proc-macro2", + "quote", + "syn 2.0.117", + "zbus_names", + "zvariant", + "zvariant_utils", +] + +[[package]] +name = "zbus_names" +version = "4.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1039ca249fee9559680f3a9f05b55e0761fee51af4f6c1e7d8c1f31e549721d2" +dependencies = [ + "serde", + "winnow 1.0.3", + "zvariant", +] + [[package]] name = "zerocopy" version = "0.8.50" @@ -6692,3 +7025,43 @@ dependencies = [ "cc", "pkg-config", ] + +[[package]] +name = "zvariant" +version = "5.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cf057bb00bf5c9ad77abb6147b0ca4818236a1858416e9d988e40d6322fefa7" +dependencies = [ + "endi", + "enumflags2", + "serde", + "winnow 1.0.3", + "zvariant_derive", + "zvariant_utils", +] + +[[package]] +name = "zvariant_derive" +version = "5.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8118ca6bda77bfc0ab51d660db0c955f2505eef854c9a449435bccb616933b31" +dependencies = [ + "proc-macro-crate 3.5.0", + "proc-macro2", + "quote", + "syn 2.0.117", + "zvariant_utils", +] + +[[package]] +name = "zvariant_utils" +version = "3.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90cb9383f9b45290407a1258b202d3f8f01db719eb60b4e4055c6375af4fc7c7" +dependencies = [ + "proc-macro2", + "quote", + "serde", + "syn 2.0.117", + "winnow 1.0.3", +] diff --git a/react-command-palette/App.tsx b/react-command-palette/App.tsx index c807dff..f9dd7a9 100644 --- a/react-command-palette/App.tsx +++ b/react-command-palette/App.tsx @@ -50,24 +50,32 @@ export default function App() { const inputRef = useRef(null); const listRef = useRef(null); const baseId = useId(); - // Mirrors `closing` for the toggle-hotkey listener below, which is - // registered once and would otherwise read a stale value out of a closure - // captured on first mount. + // Mirrors `closing` for the toggle-hotkey listener below (registered once + // on mount, so a plain closure over `closing` would read a stale value). + // Updated synchronously by `setClosingBoth`, NOT via a `useEffect` mirror — + // an effect only runs after React commits the next render, which leaves a + // real gap: a hotkey re-summon landing in that gap during the ~150ms exit + // animation would read the *previous* value of `closingRef.current` and + // silently drop the summon (see the `toggle-request` listener) instead of + // cancelling the close and reopening. This is user-visible as "the hotkey + // doesn't do anything" whenever a press lands while the panel is still + // finishing a close. const closingRef = useRef(false); - useEffect(() => { - closingRef.current = closing; - }, [closing]); + const setClosingBoth = useCallback((value: boolean) => { + closingRef.current = value; + setClosing(value); + }, []); - const hide = useCallback(() => setClosing(true), []); + const hide = useCallback(() => setClosingBoth(true), [setClosingBoth]); /** Fires when the panel's `animate` variant finishes transitioning. Only * the "exit" variant should ever trigger the real window hide. */ const onPanelAnimationComplete = useCallback((definition: unknown) => { if (definition === "exit") { void invoke("hide_window"); - setClosing(false); + setClosingBoth(false); } - }, []); + }, [setClosingBoth]); // Debounced server-side search (native results + enabled extensions). useEffect(() => { @@ -229,7 +237,7 @@ export default function App() { let cancelled = false; listen("supersearch://toggle-request", () => { if (closingRef.current) { - setClosing(false); + setClosingBoth(false); requestAnimationFrame(() => inputRef.current?.focus()); } else { hide(); diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index fc1efcf..de9bcca 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "supersearch-app" -version = "0.1.14" +version = "0.1.15" edition = "2021" description = "SuperSearch — AI-Native Productivity Layer Desktop App" @@ -8,6 +8,7 @@ description = "SuperSearch — AI-Native Productivity Layer Desktop App" supersearch-runtime = { path = "../crates/supersearch-runtime" } tauri = { version = "2", features = ["macos-private-api"] } tauri-plugin-global-shortcut = "2" +tauri-plugin-single-instance = "2" tauri-plugin-dialog = "2" # Opt-in: auto-update requires signing keys (plugins.updater.pubkey), so it is # off by default and enabled for release builds via `--features updater`. diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 8eccf66..1013d66 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -171,12 +171,44 @@ fn request_accessibility() { #[cfg(not(target_os = "macos"))] fn enable_fullscreen_overlay(_window: &tauri::WebviewWindow) {} +/// Bring the whole app to the front, not just the window. +/// +/// The palette runs under `ActivationPolicy::Accessory` (no Dock icon), and +/// accessory apps are the one case where `WebviewWindow::set_focus()` alone +/// is unreliable on macOS: `set_focus()` calls `makeKeyAndOrderFront:` on the +/// *window*, but doesn't activate the *process* — when a background global +/// hotkey summons an accessory app while a different app currently holds +/// focus, AppKit can leave the window visually shown yet not actually key, +/// so it never receives keyboard input (looks exactly like "the hotkey +/// didn't do anything"). `activateIgnoringOtherApps:` is the process-level +/// activation `set_focus()` doesn't do on its own. +#[cfg(target_os = "macos")] +fn activate_app() { + use objc2::msg_send; + use objc2::runtime::AnyObject; + + // SAFETY: `sharedApplication` returns the process-wide singleton + // `NSApplication` instance, always valid once AppKit is initialized + // (guaranteed by the time `setup`/hotkey handlers run). `true` is a + // plain `BOOL` argument; this call has no other side effects besides + // the documented app-activation behavior. + unsafe { + let cls = objc2::class!(NSApplication); + let app: *mut AnyObject = msg_send![cls, sharedApplication]; + let _: () = msg_send![app, activateIgnoringOtherApps: true]; + } +} + +#[cfg(not(target_os = "macos"))] +fn activate_app() {} + /// Show, center, and focus the palette, then tell the UI to reset its input. fn show_palette(window: &tauri::WebviewWindow) { // Re-assert the overlay collection-behavior + high window level every time: // macOS can drop them when the window is ordered out and back across // Spaces, which would make the palette fall behind a full-screen app. enable_fullscreen_overlay(window); + activate_app(); let _ = window.center(); let _ = window.show(); let _ = window.set_focus(); @@ -368,7 +400,32 @@ pub fn run() { let app_state = AppState::from_kernel(&kernel, boot_ms); // 4. Build the Tauri app. + // + // `single_instance` MUST be the first plugin registered (Tauri's own + // recommendation) and is the actual fix for a very real class of bug: a + // second `SuperSearch.app`/`cargo tauri dev` launch used to start a + // *second, fully independent process* — its own `SettingsStore` loaded + // once from disk at its own boot time, its own attempt at registering + // the same global hotkey. With two processes racing for one hotkey and + // each caching its own now-divergent settings snapshot in memory, + // picking a new accent color in one process's Settings window persisted + // correctly to disk, but the *other*, still-running process never + // re-read it — so summoning "the palette" could nondeterministically + // show either process's window depending on which one's hotkey + // registration the OS honored that time, and interacting with the stale + // one (e.g. toggling an unrelated setting) would spread its own outdated + // `accent_color` right back over the correct value on disk. This isn't + // a race *within* one process (see `SettingsStore::set`'s `rev` guard + // for that one) — it's two independent processes with no shared memory + // at all, which no amount of in-process ordering can fix. A second + // launch now just re-summons the one real instance's palette instead of + // starting a competing process. let builder = tauri::Builder::default() + .plugin(tauri_plugin_single_instance::init(|app, _args, _cwd| { + if let Some(window) = app.get_webview_window("main") { + show_palette(&window); + } + })) .plugin(tauri_plugin_global_shortcut::Builder::new().build()) .plugin(tauri_plugin_dialog::init()) .manage(app_state) diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index 4920a20..ceb68c1 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -1,7 +1,7 @@ { "$schema": "https://raw.githubusercontent.com/tauri-apps/tauri/dev/crates/tauri-config-schema/schema.json", "productName": "SuperSearch", - "version": "0.1.14", + "version": "0.1.15", "identifier": "com.supersearch.desktop", "build": { "frontendDist": "../react-command-palette/dist",