Thanks for your interest in improving HotkeyClash. It is a free, open-source macOS menu bar app that detects keyboard shortcut conflicts across running apps, automation tools, and system shortcuts. Issues and pull requests are welcome.
By contributing you agree that your contributions are licensed under the project's GPL-2.0 license.
- Report a bug or unexpected conflict result (open an issue)
- Add support for a new shortcut source (see "Adding a scanner" below)
- Improve detection accuracy, key mappings, or the UI
- Improve documentation
HotkeyClash is deliberately small. Please do not propose features in these areas, as they define the boundary of the product:
- Editing or reassigning shortcuts (that is System Settings / KeyCue territory)
- A shortcut cheat-sheet or viewer mode
- Cloud sync, accounts, telemetry, or analytics
- AI features
- Subscriptions, in-app purchases, or any payment
- Network access (other than an optional update check)
- Third-party Swift Package Manager dependencies (Apple frameworks only)
Requirements:
- macOS 14 (Sonoma) or newer
- Xcode 16 or newer (Swift 6.2+)
Clone and open:
git clone https://github.com/Wunderlandmedia/HotkeyClash.git
cd HotkeyClash
open HotkeyClash.xcodeprojBuild and run from Xcode with Cmd+R, or from the command line:
xcodebuild -scheme HotkeyClash -configuration Debug buildThe app runs as a menu bar item (LSUIElement, no Dock icon). To scan running apps you must grant Accessibility permission in System Settings > Privacy & Security > Accessibility. Config file and system shortcut scanning work without it.
Unit tests live in HotkeyClashTests/ and cover the pure logic: conflict
classification (real conflict vs app overlap), conflict detection and ranking, and the
Accessibility keycode/modifier mapping. Run them from Xcode with Cmd+U, or:
xcodebuild test -scheme HotkeyClash -destination 'platform=macOS'CI runs the same command on every push and pull request. The tests are a host-based
target (they @testable import HotkeyClash), so the app builds and launches to host
them; no Accessibility permission or network access is required.
A scan is orchestrated by ShortcutScanner (Services/ShortcutScanner.swift), which
runs three scanners in sequence and merges their output:
SystemShortcutScannerreads thecom.apple.symbolichotkeysplistConfigFileScannerreads Karabiner-Elements and skhd config filesMenuBarScannertraverses running apps' menu bars via the Accessibility API
Every scanner returns [HotkeyBinding]. A HotkeyBinding
(Models/HotkeyBinding.swift) represents one shortcut registration:
| Field | Notes |
|---|---|
keyCode |
UInt16 virtual keycode |
modifiers |
NSEvent.ModifierFlags |
ownerName |
Display name of the owning app or tool |
ownerBundleID |
Optional; lets the UI show the app icon |
action |
Human-readable description of what the shortcut does |
source |
.menuBar, .configFile, or .systemShortcut |
All bindings are passed to ConflictDetector, which groups them by
(keyCode, normalizedModifiers) and classifies each group's severity. Results render
in the master-detail split view (Views/ConflictListView.swift).
Key combos are represented as (keyCode: UInt16, modifiers: NSEvent.ModifierFlags)
throughout. ShortcutFormatter (in Services/HotKeyManager.swift) is the single
source of truth for mapping keycodes to display names. Use it rather than hand-rolling
a mapping.
The most common contribution is supporting a new automation tool. There are two cases.
If the tool stores its shortcuts in a file under the user's home directory, add a parser
to Services/ConfigFileScanner.swift:
- Add a private
scanYourTool() -> [HotkeyBinding]method, modeled onscanKarabiner()orscanSkhd(). - Resolve the path with
expandingTildeInPath, andreturn [](do not throw) when the file is missing, too large, or fails to parse. Reuse themaxConfigBytessize guard. - Map the tool's key names to virtual keycodes. Reuse or extend an existing key map if the format overlaps.
- Produce
HotkeyBindingvalues withsource: .configFile, a clearownerName, and anactiondescribing the shortcut. - Call your method from
scan()and append the results. - Log a summary with the module
logger(counts only, never file contents).
If the source is not a file under home (for example a SQLite store or a new system API),
add a dedicated service in Services/ that exposes a scan() returning
[HotkeyBinding], then wire it into ShortcutScanner.runScan() alongside the existing
scanners. Keep it @MainActor and follow the conventions below.
The planned-but-unimplemented sources (Keyboard Maestro, BetterTouchTool, Hammerspoon, Alfred, Raycast) are good first contributions and are tracked in the roadmap.
- One component per file; keep files focused
- All services are
@MainActor(they touch AppKit/UI) - Use Swift concurrency (
Task,async/await), not GCD (DispatchQueue) - Prefer Apple frameworks over third-party packages, always
- Use modern SwiftUI API:
Tab(nottabItem),foregroundStyle(notforegroundColor),ContentUnavailableViewfor empty states SettingsManageris@Observable @MainActor; bind withBindable(settings).property- No em dashes in user-facing strings
- No emojis in code or UI
- Fail gracefully: a scanner that hits a missing or malformed source should return an empty result and log, never crash the scan
- Do not log file contents or anything that could leak user data; HotkeyClash collects nothing and reaches no network
- Fork the repo and create a topic branch off
main. - Keep commits focused with clear messages.
- Make sure the project builds cleanly before opening a PR:
xcodebuild -scheme HotkeyClash -configuration Debug build
- Manually verify your change in the running app. If you added a scanner, confirm its bindings appear in a scan and that conflicts are detected as expected.
- Open a pull request describing what changed and why. Reference any related issue.
There is no automated test suite yet, so describe how you verified your change. Pushing
a v* tag triggers the release workflow (.github/workflows/release.yml); regular PRs
do not.
Open an issue and include:
- macOS version and Mac model
- HotkeyClash version
- What you expected vs. what happened
- If it is a detection issue: the apps or tools involved and the key combo
HotkeyClash is licensed under GPL-2.0. All contributions are accepted under the same license.