How to Handle Shortcut Conflicts in OpenSuperWhisper: Priority System and Resolution
OpenSuperWhisper resolves shortcut conflicts through a strict priority hierarchy where mouse-button hotkeys take precedence over modifier-only keys, which in turn override regular keyboard shortcuts, enforced by mutual exclusion in ShortcutManager.swift.
OpenSuperWhisper, the open-source macOS dictation tool by Starmel, allows users to trigger recordings through three distinct input mechanisms. When multiple hotkey types are configured, the application must prevent simultaneous activation to avoid duplicate recordings or conflicting states. The conflict resolution system is centralized in ShortcutManager.swift, which implements a priority-based mutex ensuring only one recording trigger remains active at any time.
Understanding the Three Hotkey Modes
OpenSuperWhisper supports three activation methods, each managed by separate monitoring components. The application handles shortcut conflicts by strictly prioritizing these modes to prevent overlapping triggers.
Regular Keyboard Shortcuts
The default activation method uses the KeyboardShortcuts library with the identifier KeyboardShortcuts.Name.toggleRecord. Typically bound to ⌥ + \, this mode relies on standard key press and release combinations. When either a modifier-only or mouse-button hotkey is active, this regular shortcut is automatically disabled to prevent conflicts.
Modifier-Only Hotkeys
Users can configure modifier-only hotkeys (such as Left ⌘ or Right ⌥) through the custom ModifierKeyMonitor class. These triggers activate when a specific modifier key is pressed and released without accompanying character keys. This mode takes precedence over regular keyboard shortcuts but yields to mouse-button hotkeys when both are configured.
Mouse-Button Hotkeys
Mouse-button hotkeys provide the highest priority activation method, monitored by MouseButtonMonitor. When configured (for example, Right-click or Middle-click), this mode automatically disables both the regular keyboard shortcut and any modifier-only hotkeys. This ensures that deliberate mouse-based triggering isn't interrupted by accidental keyboard modifier presses.
Conflict Resolution Logic in ShortcutManager.swift
The core conflict resolution implementation resides in OpenSuperWhisper/ShortcutManager.swift. The manager evaluates user preferences and enforces mutual exclusion whenever hotkey settings change.
Preference Lookup and Monitoring
The manager first reads the current configuration from AppPreferences to determine which modes should be active. According to lines 75-77 of ShortcutManager.swift, the initialization checks both modifier and mouse button settings:
let modifierKey = ModifierKey(rawValue: AppPreferences.shared.modifierOnlyHotkey) ?? .none
let mouseButton = MouseButton(rawValue: AppPreferences.shared.mouseButtonHotkey) ?? .none
The setupRecordingTrigger() method (lines 84-119) then evaluates these values to determine which monitoring system to activate.
Mutual Exclusion Enforcement
Before enabling any hotkey mode, the manager explicitly stops all other monitors to guarantee single-mode operation. As implemented in lines 78-83, the system tears down existing monitors before initializing new ones:
ModifierKeyMonitor.shared.stop()
MouseButtonMonitor.shared.stop()
This teardown prevents race conditions where a modifier key release might trigger a recording while a mouse button hotkey is being configured.
Priority Order Handling
The decision hierarchy in setupRecordingTrigger() follows strict precedence rules:
- Mouse-button hotkey – If
mouseButton != .none, the manager installs the mouse monitor, disables the regular shortcut, and logs the configuration change. - Modifier-only hotkey – If no mouse button is selected but a modifier key is configured, the manager starts
ModifierKeyMonitorand disables the regular shortcut. - Regular shortcut – Only when both specialized modes are set to "none" does the
KeyboardShortcutslibrary handle the default toggle.
Both handleKeyDown() and handleKeyUp() methods maintain consistent recording logic regardless of which trigger system is active, ensuring uniform behavior across all input modes.
Dynamic Reconfiguration
The system supports live switching without application restarts. When users modify hotkey preferences in the Settings UI, ShortcutManager observes the .hotkeySettingsChanged notification (registered in lines 30-35) and re-runs setupRecordingTrigger() to re-evaluate the priority hierarchy immediately.
Configuring Hotkeys Without Conflicts
You can programmatically configure hotkey preferences while respecting the conflict resolution system. Changes automatically trigger the priority evaluation in ShortcutManager.
Enabling Mouse-Button Hotkeys
To set a mouse button as the primary trigger (which automatically disables other modes):
// In Settings.swift – when the user selects a mouse button
AppPreferences.shared.mouseButtonHotkey = MouseButton.right.rawValue // “right” is defined in MouseButton enum
// ShortcutManager will automatically re‑configure on the next notification
Setting Modifier-Only Keys
To configure a modifier-only hotkey (which disables the regular shortcut but defers to mouse buttons):
// In Settings.swift – user selects Left ⌘ as the hot‑key
AppPreferences.shared.modifierOnlyHotkey = ModifierKey.leftCommand.rawValue
// ShortcutManager disables the regular shortcut and starts ModifierKeyMonitor
Resetting to Default Shortcuts
To restore the standard keyboard shortcut and disable specialized modes:
AppPreferences.shared.mouseButtonHotkey = "none"
AppPreferences.shared.modifierOnlyHotkey = "none"
// ShortcutManager falls back to KeyboardShortcuts.toggleRecord
Observing Configuration Changes
Internally, components listen for preference changes to update UI state accordingly:
NotificationCenter.default.addObserver(
self,
selector: #selector(hotkeySettingsChanged),
name: .hotkeySettingsChanged,
object: nil)
This observer pattern ensures that ContentView.swift and other UI components reflect the current active trigger mode without requiring manual refresh.
Summary
- Priority hierarchy: Mouse-button hotkeys take precedence over modifier-only keys, which override regular keyboard shortcuts.
- Mutual exclusion:
ShortcutManager.swiftstops all monitors before activating a new mode to prevent double-triggering. - Dynamic updates: The system re-evaluates conflicts immediately when preferences change via
.hotkeySettingsChangednotifications. - Consistent handling:
handleKeyDown()andhandleKeyUp()provide uniform recording logic regardless of the active input method. - Configuration storage:
AppPreferences.swiftpersists settings formodifierOnlyHotkeyandmouseButtonHotkey, whichShortcutManagerreads to resolve conflicts.
Frequently Asked Questions
What happens if I configure both a mouse button and a modifier key shortcut?
The mouse-button hotkey takes absolute precedence. When AppPreferences.shared.mouseButtonHotkey is set to any value other than "none", ShortcutManager automatically disables both the regular keyboard shortcut and the modifier-only monitor. This prevents conflicts where pressing a mouse button might coincide with modifier key presses.
Why does my regular keyboard shortcut stop working when I enable a modifier-only hotkey?
This is the intended behavior according to the conflict resolution logic in ShortcutManager.swift. When a modifier-only hotkey is active, the system disables the standard KeyboardShortcuts.toggleRecord to prevent both triggers from firing simultaneously. The modifier-only key becomes the exclusive recording trigger until you either disable it or configure a mouse-button hotkey (which would then take precedence).
How do I switch back to the default keyboard shortcut after using specialized hotkeys?
Set both preference values to "none" to restore the default behavior:
AppPreferences.shared.mouseButtonHotkey = "none"
AppPreferences.shared.modifierOnlyHotkey = "none"
Once both are reset to "none", ShortcutManager re-enables the regular KeyboardShortcuts library handler for toggle recording.
Can I use multiple hotkeys simultaneously in OpenSuperWhisper?
No, the architecture specifically prevents simultaneous activation. The setupRecordingTrigger() method in ShortcutManager.swift enforces mutual exclusion by stopping all monitors before starting a new one. This design prevents race conditions and ensures that a single physical action cannot trigger multiple recording sessions.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →