Ghostty, Zellij and a Keychron: fixing the macOS Option key
Zellij wants Option as Alt, macOS wants it for @ and ~. Ghostty can split the two Option keys, and a LaunchAgent gives a Keychron K3 a right Option key.
https://stratinator.com/blog/ghostty-zellij-keychron-option-key/I set up my remote development workflow on the Mac with Ghostty as the terminal and Zellij for sessions. Then I hit the Option key problem.
The problem
Zellij, like tmux, uses Alt for its bindings. Mine include:
Alt + nfor a new tabAlt + h/j/k/lto move between panesAlt + vfor a split
On macOS, Option also types special characters. On my layout, Option + n gives ~ and Option + l gives @.
So with macos-option-as-alt = true in Ghostty, Zellij works and I can’t type an email address. Without it, I can type, and Zellij ignores my shortcuts.
The fix in Ghostty
Ghostty can treat the two Option keys differently. Left Option becomes Alt for commands, right Option stays a normal Option key for symbols.
In .config/ghostty/config:
# Left Option for Zellij, Right Option for symbols (@, ~)
macos-option-as-alt = left
Now my left thumb does LeftOpt + n for a new tab and my right thumb does RightOpt + n for a tilde. It took some retraining, but it works.
The Keychron problem
My Keychron K3 has no right Option key. The key to the right of the space bar is right Control.
A Reddit thread had the fix: remap right Control to right Option for the whole system with hidutil, from a LaunchAgent so it runs when you log in.
Create ~/Library/LaunchAgents/com.local.KeyRemapping.plist:
<!--
Put this file in ~/Library/LaunchAgents/com.local.KeyRemapping.plist to
automatically remap your keys when macOS starts.
See https://developer.apple.com/library/archive/technotes/tn2450/_index.html for
the key "usage IDs". Take the usage ID and add 0x700000000 to it before putting it
into a source or destination (HIDKeyboardModifierMappingSrc and
HIDKeyboardModifierMappingDst respectively).
The remapping makes the right control behave as a right option.
-->
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.dgrcode.KeyRemapping</string>
<key>ProgramArguments</key>
<array>
<string>/usr/bin/hidutil</string>
<string>property</string>
<string>--set</string>
<string>{"UserKeyMapping":[
{
"HIDKeyboardModifierMappingSrc": 0x7000000E4,
"HIDKeyboardModifierMappingDst": 0x7000000E6
}
]}</string>
</array>
<key>RunAtLoad</key>
<true/>
</dict>
</plist>
0x7000000E4 is right Control and 0x7000000E6 is right Option. With that in place, the Keychron’s right Control key acts as right Option, and I can type symbols again.