Extras
The macOS shortcuts Serenity's firmware expects, config for the apps around it, and a guide to Vim-style editing without Vim.
macOS setup
Most keys on the Serenity QMK OS, Hardware and Action layers send shortcuts macOS already knows. The ones below have no default, so they do nothing until you set them. Tick them off as you go; the ticks stay in this browser.
Application config
Ghostty
Add this to your Ghostty config, at ~/.config/ghostty/config by default.
# sends ESC d, which Zsh/Bash typically interpret as kill-word (delete next word).
keybind = alt+delete=esc:diTerm2
Key mappings for moving with the arrow keys and modifiers. Import the preset under Settings → Keys → Key Bindings → Presets… → Import…
{"Key Mappings":{"0xf702-0x320000-0x7b":{"Version":0,"Action":47,"Text":"2","Label":""},"0xf703-0x280000-0x0":{"Version":0,"Action":10,"Text":"f","Label":""},"0x7a-0x100000-0x6":{"Version":0,"Action":11,"Text":"0x1f","Label":""},"0xf703-0x2a0000-0x7c":{"Version":0,"Action":46,"Text":"1","Label":""},"0x5a-0x120000-0x6":{"Version":0,"Action":11,"Text":"0x18 0x1f","Label":""},"0xf700-0x300000-0x0":{"Version":0,"Action":7,"Text":"","Label":""},"0xf702-0x2a0000-0x7b":{"Version":0,"Action":47,"Text":"1","Label":""},"0x9-0x40000-0x0":{"Version":0,"Action":25,"Text":"Select Next Tab
Select Next Tab","Label":""},"0x7f-0x100000-0x33":{"Version":0,"Action":11,"Text":"0x15","Label":""},"0xf72d-0x100000-0x0":{"Version":0,"Action":8,"Text":"","Label":""},"0xf702-0x280000-0x0":{"Version":0,"Action":10,"Text":"b","Label":""},"0xf72b-0x100000-0x0":{"Version":0,"Action":4,"Text":"","Label":""},"0x74-0x100000-0x0":{"Version":0,"Action":25,"Text":"New Tab with Current Profile","Label":""},"0x54-0x120000-0x0":{"Version":0,"Action":25,"Text":"Split Vertically with Current Profile
Split Vertically with Current Profile","Label":""},"0xf703-0x300000-0x0":{"Version":0,"Action":11,"Text":"0x05","Label":""},"0x19-0x60000-0x0":{"Version":0,"Action":25,"Text":"Select Previous Tab
Select Previous Tab","Label":""},"0xf703-0x320000-0x0":{"Version":0,"Action":46,"Text":"2","Label":""},"0xf72c-0x20000-0x0":{"Version":0,"Action":9,"Text":"","Label":""},"0xf72d-0x20000-0x0":{"Version":0,"Action":8,"Text":"","Label":""},"0xf703-0x220000-0x7c":{"Version":0,"Action":46,"Text":"0","Label":""},"0x7f-0x80000-0x33":{"Version":0,"Action":11,"Text":"0x17","Label":""},"0xf729-0x100000-0x0":{"Version":0,"Action":5,"Text":"","Label":""},"0xf72c-0x100000-0x0":{"Version":0,"Action":9,"Text":"","Label":""},"0xf702-0x300000-0x7b":{"Version":0,"Action":11,"Text":"0x01","Label":""},"0xf701-0x300000-0x0":{"Version":0,"Action":6,"Text":"","Label":""},"0xf728-0x80000-0x75":{"Version":0,"Action":11,"Text":"0x1b 0x64","Label":""},"0xf702-0x220000-0x7b":{"Version":0,"Action":47,"Text":"0","Label":""}},"Touch Bar Items":{}}Hammerspoon
Lets Karabiner-Elements know when the screen is locked. Add it to your Hammerspoon config, at ~/.hammerspoon/init.lua by default.
local karabinerLog = hs.logger.new("karabinerSync", "info")
local KARABINER_CLI = "/Library/Application Support/org.pqrs/Karabiner-Elements/bin/karabiner_cli"
-- Push a variable into Karabiner-Elements so complex modification rules
-- can react to it via variable_if / variable_unless conditions.
local function setKarabinerVariable(name, value)
local json = string.format('{"%s": %d}', name, value)
local task = hs.task.new(KARABINER_CLI, function(exitCode, _, stdErr)
if exitCode ~= 0 then
karabinerLog.ef("karabiner_cli failed (%d): %s", exitCode, stdErr)
end
end, { "--set-variables-from-stdin" })
task:setInput(json)
task:start()
end
-- Keep Karabiner's `is_lock_screen` variable in sync with the real lock state,
-- since Karabiner has no native way to detect this itself.
local screenWatcher = hs.caffeinate.watcher.new(function(eventType)
if eventType == hs.caffeinate.watcher.screensDidLock then
setKarabinerVariable("is_lock_screen", 1)
elseif eventType == hs.caffeinate.watcher.screensDidUnlock then
setKarabinerVariable("is_lock_screen", 0)
end
end)
screenWatcher:start()Automator
An AppleScript Quick Action that toggles Dark Mode, if you'd rather not use the Shortcuts app version in macOS setup above. Create it in Automator, then give it a shortcut in System Settings → Keyboard → Keyboard Shortcuts… →Services: ⌃⌥⇧⌘D matches the Serenity QMK Hardware layer.
on run {input, parameters}
tell application "System Events"
tell appearance preferences
set dark mode to not dark mode
end tell
end tell
return input
end runVim-less
Thinking of quitting Vim? These are the common Vim motions and commands done with standard Mac shortcuts. Most work in any app; the ones tagged code editors are VS Code's defaults, and other editors have close equivalents. They're comfortable on Serenity because the firmware puts modifiers and arrows on the home row, with paging and clipboard keys right below them. They won't replace a modal editor for everything, but they'll keep you in Insert mode far more often if you can't quit cold turkey.
Move
- h, j, k, l
- Move
- Press: ← ↓ ↑ →
- b
- Move to start of word
- Press: ⌥ ←
- e
- Move to end of word
- Press: ⌥ →
- ^
- Move to first character of linecode editors
- Press: ⌘ ← or Home
- 0
- Move to start of line
- Press: ⌘ ← (twice in code editors, or Home twice)
- $
- Move to end of line
- Press: ⌘ → (or End in code editors)
- gg
- Jump to beginning of document
- Press: ⌘ ↑
- G
- Jump to end of document
- Press: ⌘ ↓
- ⌃ b
- Page up
- Press: Page Up key (Num/Nav layer)
- ⌃ f
- Page down
- Press: Page Down key (Num/Nav layer)
- :42
- Go to linecode editors
- Press: ⌃ G
- %
- Jump to matching bracketcode editors
- Press: ⇧ ⌘ \
Delete
- X
- Delete previous character
- Press: ⌫
- x
- Delete character
- Press: ⌦
- db
- Delete to start of word
- Press: ⌥ ⌫
- de
- Delete to end of word
- Press: ⌥ ⌦
- d0
- Delete to start of line
- Press: ⌘ ⌫
- D
- Delete to end of line
- Press: ⌘ ⌦ or ⌃ K
- xp
- Swap two characters
- Press: ⌃ T
Clipboard and undo
- u
- Undo
- Press: Undo key (Num/Nav layer)
- ⌃ r
- Redo
- Press: Redo key (Num/Nav layer)
- d
- Cut
- Press: Cut key (Num/Nav layer)
- y
- Copy
- Press: Copy key (Num/Nav layer)
- p
- Paste
- Press: Paste key (Num/Nav layer)
- dd, yy
- Cut or copy linecode editors
- Press: Cut or Copy key with nothing selected
Select
- v
- Selection (Visual mode)
- Press: ⇧ with any movement above, to grow or shrink the selection
- viw
- Select cursor word
- Press: Word Select key (Num/Nav layer); tap again to add the next word
- V
- Select cursor line
- Press: Word Select key with Shift; tap again to add the next line
- gn
- Add the next match to the selectioncode editors
- Press: ⌘ D
Lines
- >>
- Indentcode editors
- Press: ⌘ ], or ⇥ (with lines selected)
- <<
- Un-indentcode editors
- Press: ⌘ [ or ⇤
- o
- Open line belowcode editors
- Press: ⌘ ↵
- O
- Open line abovecode editors
- Press: ⇧ ⌘ ↵
- ddp
- Move line downcode editors
- Press: ⌥ ↓
- ddkP
- Move line upcode editors
- Press: ⌥ ↑
- yyp
- Duplicate linecode editors
- Press: ⇧ ⌥ ↓
- J
- Join linescode editors
- Press: ⌃ J
Search
- /
- Find
- Press: ⌘ F
- n
- Next match
- Press: ⌘ G
- N
- Previous match
- Press: ⇧ ⌘ G
- *
- Find word under cursor
- Press: Word Select key, then ⌘ E, then ⌘ G
- :%s
- Find and replacecode editors
- Press: ⌥ ⌘ F
Files
- :w
- Save
- Press: ⌘ S
- :q
- Close, or quit the app
- Press: ⌘ W, ⌘ Q
Native Mac text fields also take Emacs-style keys: ⌃ A and ⌃ E for the start and end of the line, ⌃ K to cut to the end of the line, and ⌃ Y to paste it back.
What's left, like text objects (ciw), the dot repeat and macros, is where a modal editor still earns its keep, though a modern editor covers a lot of it with extensions or built-in commands. These are two VS Code extensions I maintain to fill such gaps.