Skip to content

Fix logical-character shortcuts on international keyboard layouts - #17892

Open
jfaltermeier wants to merge 10 commits into
masterfrom
issues/13874_keybindings
Open

Fix logical-character shortcuts on international keyboard layouts#17892
jfaltermeier wants to merge 10 commits into
masterfrom
issues/13874_keybindings

Conversation

@jfaltermeier

@jfaltermeier jfaltermeier commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

What it does

Fixes #13874.

Problem

Printable shortcuts previously mixed up command modifiers with modifiers needed by the active keyboard layout to produce a character.

On a German layout, [ is produced by AltGr+8. This caused two related failures:

  • Recording Ctrl+[ saved Ctrl+8.
  • Manually authoring ctrl+[ displayed and triggered as Ctrl+Ü, following the US BracketLeft position instead of the logical [ character.

As a concrete walkthrough, assign ctrl+[ to chat:open-claude-code-memory. This command has no default keybinding and is easy to use/verify.

Solution

The new flow keeps authored command modifiers (Ctrl, Alt, Shift, and Meta) separate from layout-production modifiers (Shift, AltGr, or both):

  1. KeyCode parsing preserves the authored logical character. Authored serialization remains separate from the stable runtime dispatchString() identity. [ScanCode] and [char:...] provide explicit physical and reserved-character forms.
  2. KeyboardLayoutService resolves German [ to physical Digit8 on the AltGr layout layer and derives platform-specific interpretations of normalized keyboard input.
  3. KeybindingRegistry resolves and caches bindings, matches normalized interpretations by runtime identity, prioritizes intentional command-modifier interpretations where necessary, and executes the selected command.
  4. The Keyboard Shortcuts editor records and displays ctrl+[ / Ctrl+[, while its tooltip exposes Ctrl+AltGr+8 as the physical realization.
Concept Before After
Authored binding ctrl+[ ctrl+[
Logical character Mixed up with US BracketLeft [
German physical realization Ctrl+Ü or recorded Ctrl+8 Digit8 + AltGr, with authored Ctrl
Display Ctrl+Ü / Ctrl+8 Ctrl+[
Runtime event Unreliable Ctrl+AltGr+8 matches the stable dispatch identity
Other affected areas

The logical/physical distinction now consistently applies to:

  • choosing the correct shortcut when one key press could match several shortcuts;
  • updating shortcuts when the keyboard layout or related settings change;
  • showing shortcuts that are not available on the current keyboard layout as inactive;
  • recording shortcuts and showing which physical keys must be pressed;
  • showing shortcuts correctly in browser and plugin menus;
  • showing only shortcuts supported by Electron in native menus;
  • handling shortcuts correctly in the Monaco editor;
  • passing keyboard input correctly from plugin webviews, including AltGr;
  • running shortcuts from code.
Compatibility and migration

Existing keymaps.json files and keybinding strings remain parseable. No file-format migration is required.

Printable tokens now follow logical characters. Position-dependent bindings must use scan-code syntax such as ctrl+[BracketLeft]. Reserved logical characters use character tokens such as ctrl+[char:0x2B].

Logical characters unavailable on the active layout remain visible but inactive instead of falling back to an unrelated US key position. Users who require global positional behavior can set:

"keyboard.dispatch": "keyCode"

Downstream adopters must:

  • migrate KeyboardLayout.key2KeyCode consumers to candidatesByCharacter or candidatesByFoldedCharacter;
  • move overrides of the removed transformKeyCode() and getCharacterIndex() extension points to resolveKeyCode() and candidate lookup;
  • pass normalized input to KeyboardLayoutService.validateKeyCode;
  • review KeyCode equality, modifier-only checks, authored serialization, and runtime dispatchString() usage;
  • update matchKeybinding() consumers from match.binding to match.runtime.binding;
  • adapt protected chord and keybinding-tree consumers to candidate sequences and runtime records;
  • pass an explicit logical or physical form to AcceleratorSource.getAccelerator.

See packages/keymaps/README.md for the complete keybinding grammar and doc/Migration.md for adopter details.

How to test

I guess we have to test setting up and using keyboard shortcuts on different OSes with various keyboard layouts and throughout the application. Maybe testing via next builds would be suitable.

Follow-ups

Breaking changes

  • This PR introduces breaking changes and requires careful review. If yes, the breaking changes section in the changelog has been updated.

Attribution

Review checklist

Reminder for reviewers

* normalize native and transported keyboard input for direct keybinding
dispatch while preserving AltGraph, legacy key identifiers, composition
handling, and dispatch identity
* add a toggleable developer command that logs interpreted input,
normalized key codes, keyboard layout source, matched bindings, and
skipped events
* expose keyboard layout metadata through an RPC-safe, optional source
provider for browser and Electron, with an unknown fallback
* separate runtime dispatch identity from persisted keybinding
serialization and add coverage for normalized input, diagnostics, layout
sources, and composition filtering
* separate authored command modifiers from physical Shift and AltGraph
production layers while preserving serialization and stable runtime
dispatch identity
* resolve printable bindings through ranked layout candidates, keep
unavailable characters configured but inactive, and refresh matching
when the layout or dispatch mode changes
* interpret platform-specific command and production candidates so
Windows AltGraph, macOS Option, shifted characters, and keyCode dispatch
remain symmetric
* show logical shortcut labels with deduplicated physical realizations
in keymap tooltips and explain unavailable bindings
* improve layout detection with raw AltGraph evidence and cover BÉPO
collisions, production priority, inactive bindings, platform fallbacks,
and provenance
* retain competing command and production prefixes across chords,
prioritize command matches, and reject equal-priority ambiguity
* track canonical and layout-derived runtime forms with provenance and
report interpretation shadowing separately from ordinary collisions
* canonicalize recorded shortcuts to stable logical characters, preserve
required physical Shift for unsupported characters, and ignore
modifier-only strokes
* surface command-modifier shadowing in keymap tooltips and explain
logical AltGr persistence to Windows users
* dispatch commands through normalized production-aware input while
preserving pre-cancelled DOM events for capture listeners without
duplicate execution
* transport explicit AltGraph, composition, and location data from
webviews through direct keybinding matching
* register only active, Monaco-representable bindings while handling
production Shift and platform-specific Ctrl/Cmd encoding
* use logical shortcut labels in Monaco and add focused coverage for
dispatch, transport, and adapter behavior
* expose physical formatting for resolved keybindings while preserving
logical browser-menu labels and suppressing inactive physical forms
* request logical accelerators in renderer menus and physical
realizations for Electron, including plugin-contributed menus
* validate Electron representability and omit unsupported AltGr,
non-ASCII, malformed, and chord accelerators
* preserve native roles and platform-specific accelerator registration
behavior
* carry accelerator metadata through preload and centralize native
conversion with focused core, Electron, and plugin-menu coverage
* add logical-character, Unicode escape, and physical scan-code
authoring with canonical recorder serialization and compatibility for
existing keybinding strings
* resolve printable bindings across Shift and AltGraph layers while
preserving stable dispatch, inactive logical bindings, physical
shortcuts, and integrations across supported execution paths
* document migration and API changes, and extend coverage for layout
layers, recorder persistence, scan codes, reserved characters, logical
plus, inactive labels, and JSDOM compatibility
* unify dead-key filtering and layout-modifier interpretation across
platforms
* simplify runtime matching, accelerator formatting, and Electron menu
metadata
* standardize keybinding terminology, serialization, inactive state, and
layout-source APIs
* harden ambiguous dispatch and webview handling with focused regression
coverage
* document API changes, migration guidance, and the positional dispatch
escape hatch
@github-project-automation github-project-automation Bot moved this to Waiting on reviewers in PR Backlog Aug 4, 2026
@jfaltermeier
jfaltermeier force-pushed the issues/13874_keybindings branch from 63e26e4 to cb975a8 Compare August 4, 2026 10:11
* detect the compiled keybinding test filename regardless of path
separator when toggling JSDOM
* derive Monaco control-key labels from its native platform formatter
instead of stubbing Theia OS detection
* preserve logical punctuation and letter label coverage on macOS and
non-macOS platforms
* preserve authored Ctrl and Alt modifiers when UI Events reports
AltGraph without the legacy pair
* resolve Ctrl+Alt-emulated AltGraph through command and layout
interpretations
* preserve resolved key codes during command dispatch so AltGraph
bindings round-trip
* cover dispatch and recorder behavior and clarify Windows AltGraph
documentation
@jfaltermeier
jfaltermeier marked this pull request as ready for review August 4, 2026 13:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Waiting on reviewers

Development

Successfully merging this pull request may close these issues.

Keybinding not mapped correctly depending on Keyboard Layout

1 participant