diff --git a/CHANGELOG.md b/CHANGELOG.md index cba52d0..468a312 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,33 @@ 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.17] — 2026-07-17 + +Fixes the actual root cause behind the "hotkey doesn't work" reports: the +capture UI itself could persist a corrupted shortcut. + +### Fixed +- **Rebinding the hotkey to an Option/Alt combo could silently corrupt it.** + Live log evidence: `Global shortcut registration failed ... error=Found + empty token while parsing hotkey: Alt+\u{a0}`. The General pane's capture + UI built the accelerator from `KeyboardEvent.key` — the *composed* + character — but macOS recomposes many keys under Option into a different + Unicode character than what's printed on the keycap: Option+Space's `key` + is a non-breaking space (U+00A0), not a plain `" "`, and Option+letters + compose accented characters (Option+C → "ç"). The capture UI showed + "Alt+Space" (the *display* used the physical key label) but silently + persisted `"Alt+"` (built from the composed character) to + `settings.json` — invisible until the next boot's registration attempt + rejected it outright. The 0.1.15/0.1.16 self-heal recovered the hotkey + back to the default afterward, but the underlying capture bug meant + rebinding to *any* Option combo reproduced it every time. + `toAccelerator` now resolves letters/digits/Space from + `KeyboardEvent.code` (the physical key, unaffected by modifier + composition) instead of `key`. Verified live: simulated the exact + Option+Space event macOS actually sends (`code: "Space", key: "\u00A0"`) + and confirmed it now captures as `Alt+Space`; same for Option+C + (`key: "ç"`) capturing as `Alt+C`. + ## [0.1.16] — 2026-07-17 Fixes the hotkey going completely silent instead of just "unreliable." @@ -431,7 +458,8 @@ First cross-platform release — macOS, Linux, and Windows. --- -[Unreleased]: https://github.com/archdex-art/SuperSearch/compare/v0.1.16...HEAD +[Unreleased]: https://github.com/archdex-art/SuperSearch/compare/v0.1.17...HEAD +[0.1.17]: https://github.com/archdex-art/SuperSearch/releases/tag/v0.1.17 [0.1.16]: https://github.com/archdex-art/SuperSearch/releases/tag/v0.1.16 [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 diff --git a/Cargo.lock b/Cargo.lock index aff4917..84091dd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4327,7 +4327,7 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "supersearch-app" -version = "0.1.15" +version = "0.1.16" dependencies = [ "objc2", "parking_lot", diff --git a/react-command-palette/settings/panes/General.tsx b/react-command-palette/settings/panes/General.tsx index cc4638b..ee6b4e5 100644 --- a/react-command-palette/settings/panes/General.tsx +++ b/react-command-palette/settings/panes/General.tsx @@ -22,13 +22,37 @@ function toAccelerator(e: KeyboardEvent): string | null { if (e.altKey) mods.push("Alt"); if (e.shiftKey) mods.push("Shift"); if (mods.length === 0) return null; // require at least one modifier - let key = e.key; - if (key === " ") key = "Space"; - else if (key.length === 1) key = key.toUpperCase(); - else if (!/^[A-Za-z0-9]/.test(key)) return null; // Escape, Tab, etc. — not bindable + const key = codeToAcceleratorKey(e.code, e.key); + if (key == null) return null; return [...mods, key].join("+"); } +/** Resolve the pressed key to Tauri accelerator syntax from `code` (the + * *physical* key) wherever possible, not `key` (the *composed* character). + * + * macOS recomposes many keys under Option/Alt into a different Unicode + * character than what's physically printed on the keycap — Option+Space's + * `key` is a non-breaking space (U+00A0), not `" "`; Option+letters compose + * accented characters ("Option+C" → "ç"). Since Alt is one of the four + * modifiers this exact capture UI expects users to hold, keying off `key` + * silently persisted a corrupted accelerator like `"Alt+\u00A0"` straight + * to `settings.json` — invisible in the UI (looked like a normal capture), + * but rejected outright by the OS accelerator parser at registration time + * ("Found empty token while parsing hotkey"), leaving the hotkey broken + * until the boot-time self-heal fell back to the default. `code` names the + * physical key regardless of what a modifier composes it into. */ +function codeToAcceleratorKey(code: string, fallbackKey: string): string | null { + if (code === "Space") return "Space"; + if (/^Key[A-Z]$/.test(code)) return code.slice(3); + if (/^Digit[0-9]$/.test(code)) return code.slice(5); + // Unrecognized physical key (arrows, Escape, function keys, punctuation, + // …) — `key` is stable for these regardless of modifiers, since Option + // only recomposes *printable* characters. + if (fallbackKey.length === 1) return fallbackKey.toUpperCase(); + if (/^[A-Za-z0-9]/.test(fallbackKey)) return fallbackKey; + return null; // Tab, Escape's siblings that aren't bindable, etc. +} + export function GeneralPane({ settings, onChange, diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 5b480b4..4d0a6a8 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "supersearch-app" -version = "0.1.16" +version = "0.1.17" edition = "2021" description = "SuperSearch — AI-Native Productivity Layer Desktop App" diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index a48f6aa..6afea46 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.16", + "version": "0.1.17", "identifier": "com.supersearch.desktop", "build": { "frontendDist": "../react-command-palette/dist",