How to Define Global Keyboard Shortcuts in OpenSuperWhisper

Yes, OpenSuperWhisper supports global keyboard shortcuts through the KeyboardShortcuts Swift package, allowing you to define custom hotkeys for starting and stopping transcription, single modifier keys, or even mouse buttons.

OpenSuperWhisper by Starmel provides a flexible system for defining global keyboard shortcuts that trigger voice recording and transcription. The application leverages the third-party KeyboardShortcuts library to register system-wide hotkeys that work even when the app is not in focus. Whether you need a traditional key combination, a single modifier key, or a mouse button trigger, the architecture in OpenSuperWhisper/ShortcutManager.swift makes it straightforward to customize your transcription workflow.

Understanding the Shortcut Architecture

The global keyboard shortcut system in OpenSuperWhisper consists of three interconnected components that handle declaration, registration, and user customization.

KeyboardShortcuts.Name Extension

The foundation of the shortcut system lies in the KeyboardShortcuts.Name extension declared in OpenSuperWhisper/ShortcutManager.swift. This extension defines the identifiers that the app recognizes, each with an optional default key combination.

extension KeyboardShortcuts.Name {
    static let toggleRecord = Self("toggleRecord",
                                   default: .init(.backtick, modifiers: .option))
    static let escape = Self("escape", default: .init(.escape))
}

The default toggleRecord shortcut uses Option + Backtick (`), but you can define additional identifiers with custom defaults or leave them unbound for user configuration.

ShortcutManager Registration

The ShortcutManager class handles the actual registration of global hotkeys and forwards events to the UI layer. In the setupKeyboardShortcuts() method, the manager binds key-down and key-up events to specific actions using the KeyboardShortcuts API.

private func setupKeyboardShortcuts() {
    KeyboardShortcuts.onKeyDown(for: .toggleRecord) { [weak self] in
        self?.handleKeyDown()
    }
    KeyboardShortcuts.onKeyUp(for: .toggleRecord) { [weak self] in
        self?.handleKeyUp()
    }
    // Escape shortcut dismisses the indicator window
    KeyboardShortcuts.onKeyUp(for: .escape) { [weak self] in
        Task { @MainActor in
            if self?.activeVm != nil {
                IndicatorWindowManager.shared.stopForce()
                self?.activeVm = nil
            }
        }
    }
    KeyboardShortcuts.disable(.escape)  // Hide from UI, internal use only
}

When the user presses the registered hotkey, handleKeyDown() starts the recording process, while handleKeyUp() stops it and initiates transcription.

Settings Persistence and UI

User preferences for shortcuts are stored in AppPreferences and managed through the SettingsViewModel in OpenSuperWhisper/Settings.swift. The view model exposes properties like modifierOnlyHotkey and mouseButtonHotkey, and posts .hotkeySettingsChanged notifications when values change.

The Settings UI allows users to select between three trigger modes:

  1. Key Combination – Traditional hotkey using modifiers and letter keys
  2. Single Modifier Key – Uses ModifierKeyMonitor.swift to detect standalone Command, Option, or Control presses
  3. Mouse Button – Uses MouseButtonMonitor.swift to detect middle or side mouse button clicks

Step-by-Step Guide to Adding Custom Shortcuts

Follow these steps to implement a new global keyboard shortcut in OpenSuperWhisper:

  1. Declare the shortcut identifier by extending KeyboardShortcuts.Name in OpenSuperWhisper/ShortcutManager.swift with a unique name and optional default key combination.

  2. Register event handlers in ShortcutManager.setupKeyboardShortcuts() using KeyboardShortcuts.onKeyDown and KeyboardShortcuts.onKeyUp to bind your shortcut to existing logic like handleKeyDown().

  3. Add UI controls in OpenSuperWhisper/Settings.swift by creating a picker or segmented control that updates the corresponding view model property.

  4. Persist the selection by storing the value in AppPreferences and posting a .hotkeySettingsChanged notification so ShortcutManager can reconfigure the active monitors.

Practical Code Examples

Example 1: Declaring a New Shortcut

Add a custom "Start/Stop" shortcut named customToggle with a default of Control + F13:

// In OpenSuperWhisper/ShortcutManager.swift
extension KeyboardShortcuts.Name {
    static let customToggle = Self("customToggle",
                                   default: .init(.f13, modifiers: .control))
}

Example 2: Registering Callbacks

Reuse the existing recording logic by registering your new shortcut alongside the defaults:

// In ShortcutManager.setupKeyboardShortcuts()
private func setupKeyboardShortcuts() {
    // Existing toggleRecord handlers...
    
    KeyboardShortcuts.onKeyDown(for: .customToggle) { [weak self] in
        self?.handleKeyDown()
    }
    KeyboardShortcuts.onKeyUp(for: .customToggle) { [weak self] in
        self?.handleKeyUp()
    }
}

Example 3: Adding UI Controls

Expose the new shortcut in the settings interface using a picker:

// In OpenSuperWhisper/Settings.swift
Form {
    Picker("Recording Shortcut", selection: $viewModel.selectedShortcut) {
        Text("Option + Backtick").tag(KeyboardShortcuts.Name.toggleRecord)
        Text("Control + F13").tag(KeyboardShortcuts.Name.customToggle)
    }
    .pickerStyle(.menu)
}

Example 4: Persisting User Selection

Store the selected shortcut in AppPreferences and notify the system:

// In SettingsViewModel
@Published var selectedShortcut: KeyboardShortcuts.Name {
    didSet {
        AppPreferences.shared.selectedShortcut = selectedShortcut.rawValue
        NotificationCenter.default.post(name: .hotkeySettingsChanged, object: nil)
    }
}

The ShortcutManager automatically picks up this change and reconfigures the active shortcuts via setupRecordingTrigger().

Summary

  • OpenSuperWhisper uses the KeyboardShortcuts Swift package to manage system-wide hotkeys for transcription control.
  • Define new shortcuts by extending KeyboardShortcuts.Name in OpenSuperWhisper/ShortcutManager.swift and registering handlers in setupKeyboardShortcuts().
  • The app supports three trigger modes: traditional key combinations, single modifier keys via ModifierKeyMonitor.swift, and mouse buttons via MouseButtonMonitor.swift.
  • Persist user preferences in AppPreferences and broadcast changes via .hotkeySettingsChanged to trigger runtime updates without app restarts.
  • Default shortcuts can be disabled or hidden from the UI using KeyboardShortcuts.disable() while remaining active internally.

Frequently Asked Questions

What is the default global keyboard shortcut for OpenSuperWhisper?

The default global shortcut is Option + Backtick (`), defined in OpenSuperWhisper/ShortcutManager.swift as the toggleRecord identifier. This toggles recording on and off system-wide, even when the application is not focused.

Can I use a mouse button instead of a keyboard shortcut?

Yes. OpenSuperWhisper supports mouse button triggers through MouseButtonMonitor.swift. Users can select this mode in the Settings UI, which stores the preference in SettingsViewModel.mouseButtonHotkey and activates the monitor via ShortcutManager.setupRecordingTrigger().

Where are custom shortcut preferences stored?

Custom shortcut configurations are persisted in AppPreferences.swift using standard UserDefaults. The SettingsViewModel reads these values on initialization and writes changes back when the user selects a new trigger mode or key combination.

How do I disable the default Option+Backtick shortcut?

The default shortcut is automatically disabled when you select an alternative trigger mode (single modifier or mouse button) in the Settings UI. Programmatically, you can call KeyboardShortcuts.disable(.toggleRecord) in ShortcutManager, though this is typically handled automatically by setupRecordingTrigger() when switching modes.

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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →