# How to Implement Custom Keyboard Shortcuts in OpenSuperWhisper

> Learn to implement custom keyboard shortcuts in OpenSuperWhisper using the Swift KeyboardShortcuts package. Customize global hotkeys and settings for a personalized workflow. Get started now!

- Repository: [Starmel/OpenSuperWhisper](https://github.com/Starmel/OpenSuperWhisper)
- Tags: how-to-guide
- Published: 2026-07-05

---

**OpenSuperWhisper leverages the Swift `KeyboardShortcuts` package to register global hotkeys, with [`ShortcutManager.swift`](https://github.com/Starmel/OpenSuperWhisper/blob/main/ShortcutManager.swift) handling runtime wiring, [`AppPreferences.swift`](https://github.com/Starmel/OpenSuperWhisper/blob/main/AppPreferences.swift) persisting user choices, and [`Settings.swift`](https://github.com/Starmel/OpenSuperWhisper/blob/main/Settings.swift) providing the configuration interface.**

OpenSuperWhisper is a macOS transcription application built in Swift that requires reliable global keyboard shortcuts for hands-free recording control. Implementing custom shortcuts in this codebase involves extending the existing abstraction layer that supports three distinct trigger modes: traditional key combinations, single-modifier hotkeys, and mouse button events. This guide walks through the exact implementation pattern used in the repository to add new global shortcuts without modifying low-level event-tap code.

## Understanding the Keyboard Shortcut Architecture

The shortcut system follows a pipeline architecture that separates definition, configuration, and execution concerns across specific source files.

### Core Components

The implementation relies on four primary components working in concert:

- **[`ShortcutManager.swift`](https://github.com/Starmel/OpenSuperWhisper/blob/main/ShortcutManager.swift)** – Located at [`OpenSuperWhisper/ShortcutManager.swift`](https://github.com/Starmel/OpenSuperWhisper/blob/main/OpenSuperWhisper/ShortcutManager.swift), this singleton defines default shortcuts in the `KeyboardShortcuts.Name` extension (lines 9-12) and manages runtime registration through `setupKeyboardShortcuts()` and `setupRecordingTrigger()` (lines 54-82).

- **[`AppPreferences.swift`](https://github.com/Starmel/OpenSuperWhisper/blob/main/AppPreferences.swift)** – Found at [`OpenSuperWhisper/Utils/AppPreferences.swift`](https://github.com/Starmel/OpenSuperWhisper/blob/main/OpenSuperWhisper/Utils/AppPreferences.swift), this `UserDefaults` wrapper stores user selections including `modifierOnlyHotkey` and `mouseButtonHotkey` (lines 105-112).

- **Monitor Classes** – [`ModifierKeyMonitor.swift`](https://github.com/Starmel/OpenSuperWhisper/blob/main/ModifierKeyMonitor.swift) creates low-level event taps using `CGEvent.tapCreate` for single-modifier detection, while [`MouseButtonMonitor.swift`](https://github.com/Starmel/OpenSuperWhisper/blob/main/MouseButtonMonitor.swift) captures `otherMouseDown` and `otherMouseUp` events for mouse-based triggers.

- **[`Settings.swift`](https://github.com/Starmel/OpenSuperWhisper/blob/main/Settings.swift)** – The SwiftUI view at [`OpenSuperWhisper/Settings.swift`](https://github.com/Starmel/OpenSuperWhisper/blob/main/OpenSuperWhisper/Settings.swift) (lines 290-340) exposes the configuration UI through `SettingsView`.

### The Three Trigger Modes

OpenSuperWhisper abstracts three input methods through a unified interface:

1. **Key Combination** – Standard modifier-plus-key shortcuts (e.g., ⌘+`) handled via `KeyboardShortcuts.onKeyDown` and `onKeyUp` callbacks.

2. **Single Modifier Key** – Standalone modifier detection (e.g., just the Fn key) monitored by `ModifierKeyMonitor` watching OS-level flag-change events.

3. **Mouse Button** – Low-level button capture for peripherals, implemented in `MouseButtonMonitor` using `NSEvent` monitoring.

## Step-by-Step Implementation Guide

To add a new global shortcut—such as "Toggle Transcription"—follow this exact pattern derived from the source code.

### Step 1: Define the Shortcut Identifier

Extend `KeyboardShortcuts.Name` in [`OpenSuperWhisper/ShortcutManager.swift`](https://github.com/Starmel/OpenSuperWhisper/blob/main/OpenSuperWhisper/ShortcutManager.swift) with a static constant and sensible default:

```swift
// OpenSuperWhisper/ShortcutManager.swift
extension KeyboardShortcuts.Name {
    static let toggleTranscription = Self(
        "toggleTranscription",
        default: .init(.t, modifiers: [.command, .option])   // Cmd-Option-T
    )
}

```

This declaration associates a string identifier with a default key combination that users can later override through the Settings UI.

### Step 2: Register Callbacks in ShortcutManager

Inside the `setupKeyboardShortcuts()` method, register event listeners for your new shortcut alongside existing registrations:

```swift
private func setupKeyboardShortcuts() {
    // Existing shortcuts
    KeyboardShortcuts.onKeyDown(for: .toggleRecord) { [weak self] in
        self?.handleKeyDown()
    }
    KeyboardShortcuts.onKeyUp(for: .toggleRecord) { [weak self] in
        self?.handleKeyUp()
    }

    // New shortcut registration
    KeyboardShortcuts.onKeyDown(for: .toggleTranscription) { [weak self] in
        self?.toggleTranscription()
    }
}

```

The `KeyboardShortcuts` library automatically manages the global event monitor when these callbacks are registered.

### Step 3: Create the Action Handler

Implement the handler method that executes when the shortcut fires. Place this in [`ShortcutManager.swift`](https://github.com/Starmel/OpenSuperWhisper/blob/main/ShortcutManager.swift) or delegate to a service class:

```swift
private func toggleTranscription() {
    if TranscriptionService.shared.isRunning {
        TranscriptionService.shared.stop()
    } else {
        TranscriptionService.shared.start()
    }
}

```

This pattern mirrors the existing `handleKeyDown()` and `handleKeyUp()` methods (lines 22-68 in [`ShortcutManager.swift`](https://github.com/Starmel/OpenSuperWhisper/blob/main/ShortcutManager.swift)) that control the recording indicator via `IndicatorWindowManager`.

### Step 4: Add UI Controls in SettingsView

Expose the configuration option in the Settings interface. In [`OpenSuperWhisper/Settings.swift`](https://github.com/Starmel/OpenSuperWhisper/blob/main/OpenSuperWhisper/Settings.swift), add a control within the shortcut settings section:

```swift
// Inside SettingsView.shortcutSettings
Form {
    // Existing UI elements...
    
    VStack(alignment: .leading, spacing: 8) {
        Text("Toggle Transcription")
            .font(.subheadline)
        
        KeyboardShortcuts.Name.toggleTranscription
            .editor()  // Provided by KeyboardShortcuts library
            .frame(maxWidth: 250)
    }
    .padding(.vertical, 4)
}

```

The `.editor()` view modifier automatically renders the current key combination and provides a capture interface for user customization.

### Step 5: Persist User Preferences

While the `KeyboardShortcuts` package automatically persists key combinations, custom modifier-only or mouse button triggers require explicit storage. Add a `@Published` property to `SettingsViewModel`:

```swift
@Published var customModifier: ModifierKey = .function

```

Bind this property to a picker in `SettingsView`, and `ShortcutManager` will read the value via `AppPreferences.shared.modifierOnlyHotkey` during `setupRecordingTrigger()` initialization.

## Key Source Files Reference

The following files constitute the complete shortcut infrastructure in OpenSuperWhisper:

| File | Role |
|------|------|
| [`OpenSuperWhisper/ShortcutManager.swift`](https://github.com/Starmel/OpenSuperWhisper/blob/main/OpenSuperWhisper/ShortcutManager.swift) | Central hotkey manager; defines default shortcuts, registers callbacks, and selects active trigger mode. |
| [`OpenSuperWhisper/Utils/AppPreferences.swift`](https://github.com/Starmel/OpenSuperWhisper/blob/main/OpenSuperWhisper/Utils/AppPreferences.swift) | `UserDefaults` wrapper storing selected hotkey options. |
| [`OpenSuperWhisper/ModifierKeyMonitor.swift`](https://github.com/Starmel/OpenSuperWhisper/blob/main/OpenSuperWhisper/ModifierKeyMonitor.swift) | Low-level monitor for single-modifier hotkeys using `CGEvent.tapCreate`. |
| [`OpenSuperWhisper/MouseButtonMonitor.swift`](https://github.com/Starmel/OpenSuperWhisper/blob/main/OpenSuperWhisper/MouseButtonMonitor.swift) | Low-level monitor for mouse-button triggers capturing `otherMouseDown/Up` events. |
| [`OpenSuperWhisper/Settings.swift`](https://github.com/Starmel/OpenSuperWhisper/blob/main/OpenSuperWhisper/Settings.swift) | SwiftUI view presenting the Shortcuts tab and binding selections to `SettingsViewModel`. |
| [`OpenSuperWhisper/Utils/KeyboardLayoutProvider.swift`](https://github.com/Starmel/OpenSuperWhisper/blob/main/OpenSuperWhisper/Utils/KeyboardLayoutProvider.swift) | Layout-aware key-code conversion utility. |

## Advanced Customization Options

### Implementing Modifier-Only Shortcuts

For shortcuts that trigger on a single modifier key (like Fn or Control), leverage the existing `ModifierKeyMonitor` infrastructure. Add your modifier preference to [`AppPreferences.swift`](https://github.com/Starmel/OpenSuperWhisper/blob/main/AppPreferences.swift), then extend `setupRecordingTrigger()` in [`ShortcutManager.swift`](https://github.com/Starmel/OpenSuperWhisper/blob/main/ShortcutManager.swift) to initialize a `ModifierKeyMonitor` instance that calls your custom handler when the specific modifier flags change.

### Adding Mouse Button Triggers

Mouse button support follows the same pattern using `MouseButtonMonitor`. Define the button constant in `AppPreferences`, then instantiate `MouseButtonMonitor` with a callback closure in `setupRecordingTrigger()`. The monitor automatically handles the low-level `NSEvent` tap creation and cleanup.

## Summary

- **OpenSuperWhisper** uses the `KeyboardShortcuts` Swift package for cross-application global hotkey registration.
- **ShortcutManager.swift** serves as the central coordinator, defining shortcuts in the `KeyboardShortcuts.Name` extension and wiring callbacks in `setupKeyboardShortcuts()`.
- **Three trigger modes** are supported: traditional key combinations (via `KeyboardShortcuts`), single modifiers (via `ModifierKeyMonitor`), and mouse buttons (via `MouseButtonMonitor`).
- **Persistence** is handled through [`AppPreferences.swift`](https://github.com/Starmel/OpenSuperWhisper/blob/main/AppPreferences.swift) for non-standard inputs, while the `KeyboardShortcuts` library automatically saves key combinations.
- **UI exposure** requires adding a `.editor()` view to [`SettingsView.swift`](https://github.com/Starmel/OpenSuperWhisper/blob/main/SettingsView.swift) and binding it to the corresponding `KeyboardShortcuts.Name` constant.

## Frequently Asked Questions

### How do I change the default key combination for existing shortcuts?

Modify the `default` parameter in the `KeyboardShortcuts.Name` extension within [`OpenSuperWhisper/ShortcutManager.swift`](https://github.com/Starmel/OpenSuperWhisper/blob/main/OpenSuperWhisper/ShortcutManager.swift). For example, change `.init(.graveAccent, modifiers: [.command])` to `.init(.r, modifiers: [.command, .shift])` to switch from Command-` to Command-Shift-R. Users can still override this through the Settings UI, but your default will apply on fresh installations.

### Why does my custom shortcut work in development but not in release builds?

Release builds require specific entitlements for global event monitoring. Ensure your app has the `Input Monitoring` permission granted in System Settings > Security & Privacy > Privacy > Input Monitoring. Additionally, verify that [`ModifierKeyMonitor.swift`](https://github.com/Starmel/OpenSuperWhisper/blob/main/ModifierKeyMonitor.swift) and [`MouseButtonMonitor.swift`](https://github.com/Starmel/OpenSuperWhisper/blob/main/MouseButtonMonitor.swift) are not stripped by dead-code elimination if using aggressive compiler optimizations, as they may be instantiated dynamically via string reflection.

### Can I implement a double-tap modifier key shortcut?

The current architecture in [`ShortcutManager.swift`](https://github.com/Starmel/OpenSuperWhisper/blob/main/ShortcutManager.swift) does not support double-tap detection natively. You would need to extend [`ModifierKeyMonitor.swift`](https://github.com/Starmel/OpenSuperWhisper/blob/main/ModifierKeyMonitor.swift) to implement a timing threshold mechanism that tracks `timestamp` values from `CGEvent` objects. Store the last modifier-down timestamp in an instance variable, and if the next event occurs within 300 milliseconds, trigger your custom action instead of the standard behavior.

### Where are the keyboard shortcut preferences stored on disk?

Standard `KeyboardShortcuts` preferences are stored in `~/Library/Preferences/com.starmel.OpenSuperWhisper.plist` under keys prefixed with `KeyboardShortcuts_`. Custom modifier and mouse button selections are stored in the same plist file but managed through [`AppPreferences.swift`](https://github.com/Starmel/OpenSuperWhisper/blob/main/AppPreferences.swift) using `@AppStorage` property wrappers. You can inspect these values using the `defaults read com.starmel.OpenSuperWhisper` command in Terminal.