System-wide Vim keybindings for macOS. A menu bar app that brings modal editing — Normal and Visual modes, operators, text objects — to almost every place you type on your Mac: Notion, Slack, Apple Notes, Mail, Safari, and more. Free and open source.
If you live in Vim, every Notion page and Slack message costs you a context switch back to arrow keys and the mouse. VimAction intercepts keys at the system level and replays your Vim muscle memory as native editing commands in whatever app has focus.
It is not a full Vim emulator. It is a curated subset of motions and edits — enough to cover most text navigation and everyday editing, chosen to work reliably across apps.
brew install --cask pilyang/tap/vimactionDownload the latest DMG from Releases and drag VimAction into Applications.
Either way you get the same build: Developer ID-signed, notarized, and self-updating via Sparkle (update checks ask for your consent on first run).
Requires macOS 14 or later. Development and day-to-day testing happen on macOS 26; earlier versions are currently covered by build checks only, with light smoke testing in VMs planned. If anything misbehaves on a version below macOS 26, please report it in Issues — those reports are what harden support there.
- Launch VimAction. It runs as a menu bar app — no Dock icon.
- Grant Accessibility permission. The settings window opens on first launch; click Grant Permission and enable VimAction under System Settings → Privacy & Security → Accessibility. The app detects the grant within a second — no restart needed.
- Press
Escin any text field (orCtrl-[) — you are in Normal mode.iputs you back in Insert.
VimAction starts in Insert mode: your typing is completely untouched until you explicitly press Esc. Unmapped Cmd/Opt shortcuts (Spotlight, Raycast, …) pass through and drop you back to Insert, so typing right after them just works.
The menu bar icon shows the current mode and state:
If anything ever misbehaves, Ctrl-Option-Cmd-Esc is the kill switch — it turns interception off instantly. The menu bar toggle re-enables it.
A taste of what works today:
| Keys | What they do |
|---|---|
h j k l · w b e · 0 ^ $ · gg G |
Motions — all accept counts (3w) |
i a I A o O |
Enter Insert mode |
x · D C · p P · u · Ctrl-r |
Delete, change, paste, undo, redo |
d c y + motion / text object |
Operators — dw, 3dd, diw, ci", dG, … |
v / V |
Visual / Visual Line selection |
Ctrl-d Ctrl-u / Ctrl-f Ctrl-b |
Scroll half / full page |
Esc or Ctrl-[ |
Back to Normal mode |
The full vocabulary — including what is planned and what is deliberately out of scope — lives in docs/KEYBINDINGS.md.
Clipboard note. As in Vim, delete and change (
x,dd,ciw, …) cut through the system clipboard, which acts as Vim's unnamed register —ppastes what you last deleted.
Configuration is plain YAML in ~/.config/vim-action/ — seeded with commented defaults on first launch, and never overwritten after that. The files are yours (dotfiles-friendly).
config.yaml— per-app on/off map. Terminals (Terminal, iTerm2, Ghostty) and editors with their own Vim plugins (VS Code, Cursor, Windsurf) are off by default: double interpretation breaks both sides.profiles/<bundle-id>.yaml— optional per-app tuning: remap or disable individual motions and actions, or adjust scroll distances. The bundled Slack and Notion profiles double as annotated examples. Most apps should not need a profile.
The classic case is Slack, where Return sends the message — so o/O would post a half-written message instead of opening a line. The bundled profile swaps in Shift-Return as the newline key, and o/O just work:
# profiles/com.tinyspeck.slackmacgap.yaml
name: Slack
actions:
open_line: [shift-return]The menu bar menu covers the common flows without hand-editing: toggle VimAction for the frontmost app, copy its bundle id, open or create its profile, and Reload Config to apply your edits without restarting.
Every configurable field — motion and action names, key token notation, scroll distances, and how errors are handled — is documented in docs/CONFIGURATION.md.
An app that intercepts every keystroke has to earn trust. VimAction is built accordingly:
- Accessibility permission only. Input Monitoring is never requested.
- Keystrokes never leave your Mac. No telemetry, no analytics; the only network traffic is Sparkle checking GitHub Releases for updates.
- Secure-input aware. When macOS engages Secure Input (password fields), keys stop reaching VimAction, so nothing is intercepted.
- Hard kill switch.
Ctrl-Option-Cmd-Escruns on its own event tap and dedicated thread, so it works even if the app stalls — and it also aborts any synthetic key burst mid-flight. - Fails off, not weird. Repeated execution failures automatically disable interception instead of letting a broken state keep grabbing keys.
- Open source and auditable. MIT-licensed; every release is built, signed, and notarized by a public GitHub Actions pipeline.
Keys enter through a single CGEventTap and are normalized into layout-independent key values. A pure Swift mode engine (Packages/VimActionCore, no macOS dependency) interprets them into abstract Vim actions — it knows nothing about how they execute. A dispatcher then performs each action in the focused app by synthesizing that app's native editing commands (e.g. w → Option-Right), reading the Accessibility API to compute exact offsets where an app exposes them and falling back to plain key synthesis where it doesn't. Every synthetic event carries a marker so the tap never re-interprets its own output.
Both sides of the translation, caught by a keystroke visualizer — the key you press, then what the focused app receives.
Where this is heading. Reading is only half of it. Performing the edits through the Accessibility API too — rather than synthesizing keys for them — is implemented and gets closer to Vim-exact results in apps that support it, but it stays off by default. Automatic per-app detection is the next step, and until it ships the manual switch stays undocumented: an app that doesn't expose its focused element would simply stop responding to motions, with nothing on screen to say why.
- The Unicode Hex Input keyboard layout breaks word motions. That input source reserves
Optionfor hex code entry, soOption-based combinations don't exist in it at all — not even typed by hand. VimAction reaches word-level motions through them, sow,b,e,iw,^, andvbdo nothing while it is the active input source. This is a macOS limitation rather than something VimAction can detect or work around. Workaround: switch to a standard layout (ABC, US, or any non-hex layout) while using VimAction. - Some apps don't expose their text to the Accessibility API — Slack and VS Code among them. VimAction still works there, but without exact offsets to read a few edits stay approximate rather than Vim-exact; for example
xat the end of a line joins it with the next instead of stopping. - Keystroke visualizers show VimAction's output too. Tools like KeyCastr watch the same event stream that apps receive, so alongside the key you press they also display the shortcuts VimAction synthesizes from it —
wshows up aswfollowed by⌥→. The synthesized events are real keyboard events by design; that is exactly what makes them work everywhere.
# Engine tests — pure Swift, no macOS APIs needed
swift test --package-path Packages/VimActionCore
# App unit tests
xcodebuild test -project VimAction.xcodeproj -scheme VimAction \
-destination 'platform=macOS' -only-testing:VimActionTests
# Build (CI-equivalent, unsigned)
xcodebuild build -project VimAction.xcodeproj -scheme VimAction \
-destination 'platform=macOS' CODE_SIGNING_ALLOWED=NOVimAction/ # App: menu bar, event tap, adapters, settings UI
Packages/VimActionCore/ # Pure Swift engine + config layer (swift test, no macOS)
VimActionTests/ # App unit tests
docs/ # User documentation (keybinding vocabulary)
Bug reports and feature requests are welcome in Issues; for pull requests, please open an issue first — CONTRIBUTING.md explains the workflow. VimAction is maintained by a solo developer working with an AI coding agent (Claude Code); a human reviews, tests, and decides everything that ships.
If VimAction isn't quite what you need, these explore the same idea:
- kindaVim — a polished app that brings Vim moves to macOS system-wide.
- SketchyVim — an open-source project adding Vim moves and modes to macOS text fields.
VimAction owes its vocabulary — and its reason to exist — to Vim and Neovim. It also ships on open source: Sparkle powers updates, and Yams parses configuration.


