stratinator
  • Asia session 20:00 to 00:00 NY
  • London session 02:00 to 05:00 NY
  • New York AM session 08:30 to 11:00 NY
  • New York PM session 13:30 to 16:00 NY
  • NYSE cash 09:30 to 16:00 NY
  • CME index futures: Sunday 18:00 to Friday 17:00 NY, daily break 17:00 to 18:00
  • Crypto: always open

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.

2 min read René

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 + n for a new tab
  • Alt + h/j/k/l to move between panes
  • Alt + v for 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.