[Bug]: Change appearance shortcut fails on macOS AZERTY (Option+Shift+Cmd+A)#15954

openedOct 5, 2026byNnbros

Before submitting

  • I searched existing issues and did not find a duplicate.
  • I included enough detail to reproduce or investigate the problem.

Area

apps/web

Steps to reproduce

  1. Use a MacBook Pro with a French AZERTY keyboard layout.
  2. Open T3 Code and keep focus outside the integrated terminal.
  3. Open the command palette and search for "Change appearance". It advertises Option+Shift+Cmd+A (⌥⇧⌘A).
  4. Close the palette and press that combination using the key labeled A on the AZERTY keyboard.
  5. Press it several times to distinguish a missed shortcut from a transition between System and the same effective light/dark mode.

Expected behavior

The advertised shortcut should cycle System → Light → Dark → System, using the A key from the active keyboard layout.

Actual behavior

The advertised shortcut does not appear to change the appearance on my MacBook Pro with an AZERTY keyboard.

Suspected cause (source inspection)

In apps/web/src/keybindings.ts, resolveEventKeys falls back to the letter in KeyboardEvent.code when event.key is not a Latin a–z character. Option can turn a letter into a symbol. On AZERTY, the key labeled A has code KeyQ, so this fallback can resolve it as q rather than a and miss the default mod+alt+shift+a binding.

For example, a synthetic event with key: "Æ", code: "KeyQ", metaKey: true, altKey: true and shiftKey: true exercises this mismatch. This is a unit-test input, not a captured keyboard-event log from my machine.

  • #2261: Option-modified letter shortcuts failing on macOS (closed).
  • #4434 / #4469: Latin layout letters take precedence over physical positions, but the physical fallback remains for Option-generated symbols.
  • #13229: the recorder displays US key positions for non-US punctuation (open); related layout handling, but a different symptom.

I did not find an existing report specifically covering Option-modified A on AZERTY for appearance.cycle.

Impact

Minor bug or occasional failure

Version or commit

Personal build based on upstream v0.0.45; shortcut matching inspected in the upstream source as well.

Environment

macOS, MacBook Pro, French AZERTY keyboard. macOS 26.7.1

Logs or stack traces

shell
1

Screenshots, recordings, or supporting files

No response

Workaround

Use the command palette → Change appearance and select a mode manually, or assign a shortcut without Option in Settings → Keybindings.

Note

Grok responding on behalf of Julius.

Triage

Thanks @nbros for the careful report, and for working out which input would trigger it. Current main matches what you describe: on a French AZERTY layout, shortcut matching reads that key as q.

What I found

  • The default binding for appearance.cycle is mod+alt+shift+a (packages/shared/src/keybindings.ts), and it runs from the command palette's keydown listener.
  • resolveEventKeys in apps/web/src/keybindings.ts uses a Latin event.key as is. When Option turns the key into a symbol, it falls back to the US letter from event.code. On AZERTY, the key labeled A is KeyQ and Option+Shift+A produces Æ, so the event resolves to æ and q and never matches a.
  • Change theme (mod+alt+a) misses for the same reason. Option shortcuts on letters that sit in the same place as on QWERTY (B, N, T) still match. On AZERTY the affected letters are A, Q, Z, and W.
  • #4469 kept this physical fallback for Option-generated symbols when it stopped double-matching unmodified Latin letters. The issue here is separate from #2261, #4434, and #13229.

Likely fix area

One option is to keep the #4469 rule for unmodified Latin letters and resolve an Option-generated symbol to the letter the active layout prints on that key, rather than the US letter in event.code. Until then, your workarounds are good: use the command palette, or bind the command without Option in Settings → Keybindings.

A maintainer will decide on the fix direction.

Synced from GitHub

I implemented the fix direction suggested in the triage in my fork, and it works on macOS with a French AZERTY layout: ⌥⇧⌘A cycles the appearance, and A no longer also matches Q.

  • Base letter from the client's layout. At startup, the web client reads navigator.keyboard.getLayoutMap(), so on AZERTY KeyQ gives a. It refreshes the map on window focus. When the Keyboard API is unavailable (some browsers, insecure remote origins), it falls back to the letters seen during ordinary typing without modifiers: the same physical key types a before Option turns it into æ.
  • Shared matching. packages/shared/src/keybindings.ts exposes setKeyboardLayoutLetterResolver(resolver), which the web client registers. In shortcutKeyFromEvent, a Latin event.key still wins. Otherwise the layout's base letter comes before the QWERTY letter from event.code. resolveEventKeys no longer adds the event.code letter alongside, since adding both made AZERTY A also trigger Q.
  • Pitfall. In Chromium (Electron), navigator.keyboard has getLayoutMap() but is not an EventTarget, so addEventListener("layoutchange", …) throws. That stopped the app from loading ("T3 Code could not load"). Subscribe only when the method exists; the focus refresh already picks up layout switches.

Because the resolver runs in the client, shortcuts follow the keyboard of the machine in front of you, including when connected to a remote server. The fork covers this with unit tests for AZERTY (A vs Q, Option symbols, the fallback without the Keyboard API, and the missing layoutchange).

Checked upstream main b5a226e49c with vp run dev, isolated copied thread data, and Linux Chromium 154. Opened the command palette, confirmed Change appearance advertises Ctrl+Alt+Shift+A, closed it with focus outside the terminal, and pressed the shortcut three times. The stored appearance changed light → dark → system; each event had key: "a", code: "KeyA", ctrlKey: true, altKey: true, shiftKey: true, metaKey: false.

This does not verify the macOS French AZERTY path. This environment has no macOS input source, and the Æ/KeyQ example in the report is explicitly synthetic. Please confirm the failure on a current upstream build without the fork fix and provide the native keydown values (key, code, metaKey, ctrlKey, altKey, shiftKey) for ⌥⇧⌘A, plus the exact French input-source name and whether this is Electron or a browser (and its version). Keeping this open pending that platform check.

Synced from GitHub

Confirmed on the current official nightly, without the fork fix: ⌥⇧⌘A still doesn't cycle the appearance.

  • Build: T3 Code (Nightly) 0.1.2-nightly.20261021.3473 (built from 1688f965), Electron app, Electron 44.9.3 / Chromium 152.0.7977.167, macOS 26.7.1 (25G241)
  • Input source: com.apple.keylayout.French ("French", not "French – PC")
  • Native keydown for ⌥⇧⌘A (the key labeled A, captured in the app's DevTools):
    json
    1{"key":"Æ","code":"KeyQ","metaKey":true,"ctrlKey":false,"altKey":true,"shiftKey":true}

Æ/KeyQ reflects real Electron output and isn't synthetic. With Cmd held, Chromium on macOS still applies Option+Shift to event.key. For reference, macOS's UCKeyTranslate on this input source gives, for KeyQ: a / A with ⇧ / æ with ⌥ / Æ with ⌥⇧.

shortcutKeyFromEvent / resolveEventKeys are unchanged on current main (8acbefefc5): æ isn't a Latin letter, so they fall back to KeyQ → q and resolve the event to {æ, q}. That never matches mod+alt+shift+a.

Your Linux/QWERTY check (key: "a", code: "KeyA") takes the Latin event.key path, so it can't reproduce this.

The native event confirms the French macOS path. On upstream defb7e6e6b, I ran the web dev app with isolated copied state on Linux Chromium, set the client platform to MacIntel, and replayed the captured Æ/KeyQ event outside the terminal. The palette advertises ⌥⇧⌘A; three presses left appearance unchanged. The a/KeyA control changed it to light. A regression test with the French browser layout map also fails: expected appearance.cycle, received null. Native macOS/Electron input remains unverified on this VM.