config: fix physical -> unicode keybinds for alternate keyboard layouts (#9469)

Dvorak input (and presumably others) on MacOS causes certain keys to not
work as expected: `[` `]` and `=`.

### Related

This fixes https://github.com/ghostty-org/ghostty/discussions/8743 as
well as an unmentioned problem where bracket navigation and equalize
panes are also broken.

This is similar to https://github.com/ghostty-org/ghostty/pull/8759 but
fixes more of the combos. Switching the `+` binding was not enough to
fix the problem for me since the right bracket physical keys is where
equals should be and overrides the combo.

### What this PR does

Switches several default keybindings from physical key codes to support
alternative keyboard layouts like Dvorak and keyboards with dedicated
plus keys. Effectively:

```diff
-  .physical = .equal // or .bracket_left or .bracket_right
+  .unicode = '=' // or '[' or ']'
```

### Details

In testing, I found that all of these bindings need to be fixed
otherwise the bracket physical keys overshadows the dvorak plus key.

This seems like the right solution for the same reason that we don't use
any physical letter or number keys. They move around with different
layouts and `=`, `[`, and `]` are no different than other keys like `-`
and `0` which use unicode in other default keybinds.

With this fix, tab and pane navigation (cmd+[], cmd+shift+[]), as well
as increase font size (cmd+shift+equals and cmd+equals) and equalize
panes (ctrl+cmd+=) now work as expected on dvoark layout on MacOS.

Note, I switch between dvorak virtual layout on the laptop and a
physical dvorak keyboard (passed through qwerty input) so my combos
would need to change depending on which keyboard I was using if we used
physical keys only.

I consulted Claude Code to help try to understand what order and
precedence was being applied in this change, but I wrote and tested the
code myself (however, this is my first `zig` code so take that with a
grain of salt).
This commit is contained in:
Mitchell Hashimoto
2026-01-20 10:03:32 -08:00
committed by GitHub

View File

@@ -6107,7 +6107,7 @@ pub const Keybinds = struct {
// set the expected keybind for the menu.
try self.set.put(
alloc,
.{ .key = .{ .physical = .equal }, .mods = inputpkg.ctrlOrSuper(.{}) },
.{ .key = .{ .unicode = '=' }, .mods = inputpkg.ctrlOrSuper(.{}) },
.{ .increase_font_size = 1 },
);
try self.set.put(
@@ -6275,13 +6275,13 @@ pub const Keybinds = struct {
);
try self.set.putFlags(
alloc,
.{ .key = .{ .physical = .bracket_left }, .mods = .{ .ctrl = true, .super = true } },
.{ .key = .{ .unicode = '[' }, .mods = .{ .ctrl = true, .super = true } },
.{ .goto_split = .previous },
.{ .performable = true },
);
try self.set.putFlags(
alloc,
.{ .key = .{ .physical = .bracket_right }, .mods = .{ .ctrl = true, .super = true } },
.{ .key = .{ .unicode = ']' }, .mods = .{ .ctrl = true, .super = true } },
.{ .goto_split = .next },
.{ .performable = true },
);
@@ -6607,12 +6607,12 @@ pub const Keybinds = struct {
);
try self.set.put(
alloc,
.{ .key = .{ .physical = .bracket_left }, .mods = .{ .super = true, .shift = true } },
.{ .key = .{ .unicode = '[' }, .mods = .{ .super = true, .shift = true } },
.{ .previous_tab = {} },
);
try self.set.put(
alloc,
.{ .key = .{ .physical = .bracket_right }, .mods = .{ .super = true, .shift = true } },
.{ .key = .{ .unicode = ']' }, .mods = .{ .super = true, .shift = true } },
.{ .next_tab = {} },
);
try self.set.put(
@@ -6627,12 +6627,12 @@ pub const Keybinds = struct {
);
try self.set.put(
alloc,
.{ .key = .{ .physical = .bracket_left }, .mods = .{ .super = true } },
.{ .key = .{ .unicode = '[' }, .mods = .{ .super = true } },
.{ .goto_split = .previous },
);
try self.set.put(
alloc,
.{ .key = .{ .physical = .bracket_right }, .mods = .{ .super = true } },
.{ .key = .{ .unicode = ']' }, .mods = .{ .super = true } },
.{ .goto_split = .next },
);
try self.set.put(
@@ -6677,7 +6677,7 @@ pub const Keybinds = struct {
);
try self.set.put(
alloc,
.{ .key = .{ .physical = .equal }, .mods = .{ .super = true, .ctrl = true } },
.{ .key = .{ .unicode = '=' }, .mods = .{ .super = true, .ctrl = true } },
.{ .equalize_splits = {} },
);