Repository navigation
Description
Description
App keybinds are matched against event.key (the character produced by the active
layout) and only fall back to the physical event.code when Alt is held. On
non-Latin layouts (e.g. Russian) event.key is Cyrillic, so letter and punctuation
shortcuts silently do nothing unless Alt is involved.
Example: the session find bar added in #48470 is bound to mod+f. On an English
layout Ctrl+F opens it; on a Russian layout it does nothing. session.search is
registered with hidden: true, so the command palette cannot reach it either — the
shortcut is the only entry point, and on a Cyrillic layout it never fires.
Root cause — packages/app/src/shell/commands/command.tsx:
export function keyFromKeyboardEvent(event: KeyboardEvent) {
const key = normalizeKey(event.key)
if (!event.altKey || /^[a-z0-9]$/.test(key)) return key
if (!event.code.startsWith("Key") || event.code.length !== 4) return key
return event.code.slice(3).toLowerCase()
}matchKeybind compares the configured key ("f") with keyFromKeyboardEvent(event).
With a Russian layout, Ctrl+F arrives as event.key === "а", event.code === "KeyF",
event.altKey === false, so the function returns "а" and "f" !== "а" — no match.
Alt+F does match, so the event.code fallback exists but is gated on Alt (it was
added for macOS Option). Ctrl/Meta/Shift on a non-Latin layout are not covered.
The same applies to mod+p, mod+t, mod+w, mod+u, mod+shift+a/e/l/o/r/s/t/x,
mod+shift+d, and the punctuation binds mod+comma, mod+., mod+;, mod+',
mod+[, mod+], mod+\\. Digit binds (mod+1..9) are fine because the digit row
matches across these layouts.
Suggested fix: remove the !event.altKey gate so the physical-key fallback applies to
every modifier (this keeps the macOS Option behavior), and additionally map event.code
for the punctuation keys used in keybinds (Comma, Period, Semicolon, Quote,
BracketLeft, BracketRight, Backslash, …).
Related, but not the same:
- Keybind Ctrl + . does not work with non-US keyboard layouts (e.g., Russian) #40048 — "Keybind Ctrl + . does not work with non-US keyboard layouts (e.g., Russian)"
— identical root cause, auto-closed by the stale bot, not fixed. - Hotkeys don't work if the keyboard layout isn't english (or similar latin alphabet layout) #6364 — "Hotkeys don't work if the keyboard layout isn't english" — same, auto-closed.
- fix(app): match keybinds for dead keys via event.code #54708 — handles
Dead/Unidentifiedkeys; hereevent.keyis a valid character, so
that fallback does not trigger. - Title: Windows: TUI keybindings only work with the English keyboard layout #55371 — open, but the TUI (terminal input / Kitty protocol); the app path is separate.
Plugins
None
OpenCode version
2.0.29 (desktop app)
Steps to reproduce
- Open the desktop app on Windows and switch the keyboard layout to Russian (ЙЦУКЕН).
- Open a session and press
Ctrl+Fon the physical F key. → Nothing happens. - Switch to the English layout and press the same physical keys. → The find bar opens.
- Still in the Russian layout,
Alt+Fmatches whileCtrl+Fdoes not.
Screenshot and/or share link
N/A
Operating System
Windows 11 (10.0.22631)
Terminal
N/A — desktop app
Activity
opencode-agent commentedon Oct 15, 2026
Thanks for the detailed report. I can confirm this.
I couldn't run the Windows desktop app here, so I tested the app's own keybind matcher (
matchKeybind/parseKeybindinpackages/app/src/shell/commands/command.tsx) with the keyboard events a Russian layout produces:key: "f",code: "KeyF") matchesmod+f.key: "а",code: "KeyF") does not match.mod+p,mod+t,mod+w,mod+comma,mod+.andmod+[.event.codefallback.The results are the same on 2.0.29 and on the latest
dev(4397ee44). The test script below reproduces it.Related: #40048 (same problem on v1, closed by the stale bot), plus the open PRs #54573 and #54708, which change the same function for other keyboard cases.
Reproduction script (layout-keybind.test.ts)
Run it:
bun layout-keybind.test.tsZXLyyds100 commentedon Oct 15, 2026
I'd like to take this on. I'll cover Ctrl/Meta shortcuts whose key is a non-Latin character, including punctuation keys, while preserving Latin layout mappings and unmodified custom bindings. I'll extend the existing command matcher tests. I've checked #54573 and #54708; their Option and Dead/Unidentified cases are separate from this fix.
Delivery update, October 29: #55765 at
4f0cf1cb98d3c8abded6e5497dcbee010f447f96now normally merges currentdev@726cf096, preserving recording, bracket/Option behavior and the new declined-command contract. This is AI-assisted implementation/testing/self-review, not independent human review.Local verification: 63 focused command tests, 630 randomized repetitions, 971 unit + 251 browser tests, 20 focused Chrome component repetitions, app TypeScript, root check/normal push hook, changed-file lint and formatting passed. Current upstream first fails 13 command assertions and both focused browser regressions.
The PR describes the remaining baseline browser failures honestly: full components 205 passed / 2 failed; the selected built-mode keepers 3 passed / 4 failed, also reproduced on the same upstream production code. No assertions were skipped or weakened. Synthetic keyboard events on macOS/Chrome are not OS-level Russian-layout or hosted Linux/Windows verification. Maintainer review and fork-workflow approval are still separate gates.
fix(app): match shortcuts on non-Latin keyboard layouts