From fa5f763072210dfcf1e6172915e3c4ffe39dbaf8 Mon Sep 17 00:00:00 2001 From: AI Assistant Date: Mon, 20 Apr 2026 12:45:28 -0400 Subject: [PATCH] fix(tauri): gracefully handle Alt+Space global shortcut conflicts --- .../TAURI-001-graceful-shortcut-conflicts.md | 53 +++++++++++++++++++ apps/tauri/src-tauri/src/main.rs | 21 ++++++-- 2 files changed, 69 insertions(+), 5 deletions(-) create mode 100644 .tasks/interface/TAURI-001-graceful-shortcut-conflicts.md diff --git a/.tasks/interface/TAURI-001-graceful-shortcut-conflicts.md b/.tasks/interface/TAURI-001-graceful-shortcut-conflicts.md new file mode 100644 index 000000000000..f4d6997b991d --- /dev/null +++ b/.tasks/interface/TAURI-001-graceful-shortcut-conflicts.md @@ -0,0 +1,53 @@ +--- +id: TAURI-001 +title: "Gracefully handle Alt+Space shortcut conflicts" +status: "Done" +assignee: "jamiepine" +priority: "Medium" +tags: ["interface", "tauri", "desktop"] +--- + +## Description + +The Spacedrive Tauri application maps `Alt+Space` as the default global shortcut to toggle the voice overlay. Previously, this shortcut was registered at builder-time during the app's initialization sequence. If another application (such as Windows PowerToys Run, macOS Spotlight, or Wox) already had `Alt+Space` bound, the `expect("failed to register Alt+Space global shortcut")` call would cause the entire Tauri application to panic and crash on startup. + +## The Why + +Global shortcut collisions are extremely common on user machines. A failure to register a non-critical global shortcut (like the voice overlay) should never result in a fatal crash of the main application. By moving the registration from builder-time to runtime, we can catch the error, log a graceful warning via `tracing`, and allow the app to boot normally. + +## The How (Implementation Steps) + +1. **Remove Builder-Time Registration**: + We removed the hardcoded `.with_shortcut("Alt+Space").expect(...)` from the `tauri_plugin_global_shortcut` builder initialization. +2. **Implement Runtime Registration**: + After the app is built and running, we use `app.handle().global_shortcut().register("Alt+Space")` to dynamically register the shortcut. +3. **Graceful Error Handling**: + We match the `Result` of the registration attempt. On success, we log an `info!` tracing event. On failure, we log a `warn!` event and continue execution. + +### Example Diff + +```diff +- .plugin( +- tauri_plugin_global_shortcut::Builder::new() +- .with_shortcut("Alt+Space") +- .expect("failed to register Alt+Space global shortcut") +- .with_handler(|app, _shortcut, event| { ++ // Registration moved to runtime: ++ #[cfg(not(target_os = "linux"))] ++ { ++ use tauri_plugin_global_shortcut::GlobalShortcutExt; ++ match app.handle().global_shortcut().register("Alt+Space") { ++ Ok(_) => tracing::info!("Registered Alt+Space global shortcut"), ++ Err(error) => tracing::warn!(?error, "Failed to register Alt+Space global shortcut, voice overlay disabled"), ++ } ++ } +``` + +> **Note**: The implementation uses `#[cfg(not(target_os = "linux"))]` because global shortcuts on Linux (especially under Wayland) are handled at the desktop environment level and are not reliably supported by the Tauri plugin architecture. + +## Acceptance Criteria +- Tauri app boots normally even if `Alt+Space` is bound by another program. +- A descriptive warning is printed to the daemon logs when a shortcut collision occurs. + +## Review Refinements +- **Neutral Log Messaging:** Updated the `tracing::warn!` message for `Alt+Space` registration failure to be error-agnostic and explicitly log the underlying `Err(e)` details, rather than assuming the shortcut was already in use. diff --git a/apps/tauri/src-tauri/src/main.rs b/apps/tauri/src-tauri/src/main.rs index 8153efe10fa4..b7bb37c2f5f4 100644 --- a/apps/tauri/src-tauri/src/main.rs +++ b/apps/tauri/src-tauri/src/main.rs @@ -16,7 +16,6 @@ use std::sync::Arc; use tauri::menu::MenuItem; use tauri::Emitter; use tauri::{AppHandle, Manager}; -use tauri_plugin_global_shortcut::ShortcutState; use tokio::sync::oneshot; use tokio::sync::RwLock; use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; @@ -1928,10 +1927,10 @@ fn main() { .plugin(tauri_plugin_updater::Builder::new().build()) .plugin( tauri_plugin_global_shortcut::Builder::new() - .with_shortcut("Alt+Space") - .expect("failed to register Alt+Space global shortcut") - .with_handler(|app, _shortcut, event| { - if event.state() == ShortcutState::Pressed { + .with_handler(|app, shortcut, event| { + if event.state() == tauri_plugin_global_shortcut::ShortcutState::Pressed + && shortcut.to_string() == "Alt+Space" + { if let Err(error) = windows::toggle_voice_overlay_internal(app.clone()) { tracing::warn!( ?error, @@ -2004,6 +2003,18 @@ fn main() { tracing::info!("Spacedrive Tauri app starting..."); + // Register Alt+Space global shortcut for the voice overlay. + // Done at runtime (not at builder time) so a conflict with another + // app (e.g. PowerToys Run) is handled gracefully instead of panicking. + #[cfg(not(target_os = "linux"))] + { + use tauri_plugin_global_shortcut::GlobalShortcutExt; + match app.handle().global_shortcut().register("Alt+Space") { + Ok(_) => tracing::info!("Registered Alt+Space global shortcut"), + Err(error) => tracing::warn!(?error, "Failed to register Alt+Space global shortcut, voice overlay disabled"), + } + } + // Apply Windows-specific window customizations (dark titlebar) #[cfg(target_os = "windows")] {