Skip to content

Fix(terminal): Resolved numpad keys reporting 'Unidentified' in TUI apps (Chromium 142 regression)#299408

Open
Ammar2123 wants to merge 2 commits intomicrosoft:mainfrom
Ammar2123:main
Open

Fix(terminal): Resolved numpad keys reporting 'Unidentified' in TUI apps (Chromium 142 regression)#299408
Ammar2123 wants to merge 2 commits intomicrosoft:mainfrom
Ammar2123:main

Conversation

@Ammar2123
Copy link
Copy Markdown

Fixes #299374

Summary

Numpad keys (0–9, Enter, Decimal, and operators) are completely ignored by
terminal TUI applications that activate the Kitty keyboard protocol (CSI u /
enhanced keyboard mode) — such as OpenCode, bubbletea-based, and opentui-based
apps — when running inside VS Code 1.110.0's integrated terminal.

The regression was introduced by Electron 39 / Chromium 142 (chromium issue
405793116), which causes numpad KeyboardEvent objects to report
key: "Unidentified" instead of the correct key value (e.g. "5", "Enter",
"+").

Root Cause

Two-path keyboard handling in xterm.js:

Scenario Path Why numpad worked before
Normal shell (no enhanced mode) keydown → no match → keypress/input event fallback sends text Browser's native fallback still delivers the character
Kitty protocol active (TUI app) keydown → xterm.js must produce a CSI u escape sequence xterm.js reads ev.key for identity — gets "Unidentified" → drops the event

When a TUI app activates the Kitty keyboard protocol, xterm.js's
KittyKeyboard.evaluate() must produce structured escape sequences (e.g.
\x1b[57404u for Numpad5). When ev.key === "Unidentified" the key cannot be
identified, result.key is left undefined, and the TUI app receives nothing.
The keypress fallback path that saves normal shell input is NOT used in Kitty
mode, so strokes are silently dropped.

The event.code property ("Numpad0"–"Numpad9", "NumpadAdd",
"NumpadEnter", etc.) is not affected by the Chromium regression and
reliably identifies numpad keys.

Fix

In terminalInstance.ts, inside the attachCustomKeyEventHandler callback
that runs before xterm.js evaluates any event:

  1. Detect the broken pattern: event.key === 'Unidentified' && event.code.startsWith('Numpad')
  2. Resolve the correct key via resolveNumpadKey(), respecting NumLock state
    and the active keyboard layout (via IKeyboardLayoutService)
  3. Patch the original event (Object.defineProperty) so xterm.js sees the
    corrected key when it processes the event after the handler returns
  4. Create a derived KeyboardEvent with the corrected key for local
    StandardKeyboardEvent dispatch, avoiding sole reliance on mutation

Key mapping (resolveNumpadKey)

  • NumLock ON: Numpad0Numpad9"0""9", NumpadDecimal → locale-aware
    (e.g. "." for US, "," for German — resolved via IKeyboardLayoutService.getRawKeyboardMapping())
  • NumLock OFF: Numpad0Numpad9 → navigation keys (Insert, End,
    ArrowDown, …), NumpadDecimal"Delete"
  • Operators (any NumLock state): NumpadAdd"+", NumpadSubtract
    "-", NumpadMultiply"*", NumpadDivide"/",
    NumpadEnter"Enter", NumpadEqual"="

Design decisions

  • IKeyboardLayoutService injection — needed for locale-aware NumpadDecimal
    resolution; getRawKeyboardMapping()?.['NumpadDecimal']?.value returns the
    layout-correct character with '.' as fallback via ??
  • Exported pure functionresolveNumpadKey(code, numLockOn, decimalValue?)
    is extracted as an exported function for direct unit testing without needing
    a full TerminalInstance
  • Dual patching — both Object.defineProperty on the original event (for
    xterm.js) and a new KeyboardEvent (for local StandardKeyboardEvent
    dispatch) to avoid relying on property mutation alone

Testing

  • TypeScript compilation: VS Code - Build watch task ✅ (0 errors)
  • Unit tests: 6 new tests in terminalInstance.test.ts ✅ (all passing)
    • Operator keys resolve regardless of NumLock state
    • Digit keys → numbers when NumLock ON
    • Digit keys → navigation keys when NumLock OFF
    • NumpadDecimal → locale-aware value when NumLock ON (US ., German ,)
    • NumpadDecimalDelete when NumLock OFF (locale does not affect this)
    • Unknown numpad codes → undefined
  • Hygiene: pre-commit hook passed ✅
  • Manual: numpad keys in Kitty-protocol TUI apps (OpenCode, bubbletea apps)
    correctly produce input in VS Code integrated terminal

Changed Files

File Changes
src/vs/workbench/contrib/terminal/browser/terminalInstance.ts +89 lines: import, DI injection, event handler patch, _resolveNumpadKey wrapper, exported resolveNumpadKey pure function
src/vs/workbench/contrib/terminal/test/browser/terminalInstance.test.ts +53 lines: 6 unit tests for resolveNumpadKey

Copilot AI review requested due to automatic review settings March 5, 2026 08:05
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes an Electron 39 / Chromium 142 regression where numpad KeyboardEvent.key becomes "Unidentified" in the integrated terminal, causing xterm.js Kitty keyboard protocol (CSI u) handling to drop numpad input for TUI apps.

Changes:

  • Add a terminal-level workaround that repairs "Unidentified" numpad KeyboardEvent.key values before xterm.js processes the event.
  • Introduce an exported resolveNumpadKey(code, numLockOn, decimalValue?) helper used to map numpad code → correct key.
  • Add unit tests covering the new resolveNumpadKey mapping behavior.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.

File Description
src/vs/workbench/contrib/terminal/browser/terminalInstance.ts Injects IKeyboardLayoutService, patches numpad KeyboardEvent.key, and adds exported resolveNumpadKey mapping utility.
src/vs/workbench/contrib/terminal/test/browser/terminalInstance.test.ts Adds unit tests validating numpad code→key resolution across NumLock/layout scenarios.

Workaround for Chromium 142+ regression (chromium issue 405793116) where
numpad KeyboardEvent.key is reported as "Unidentified", breaking xterm.js
Kitty keyboard protocol handling used by TUI apps.

Changes:
- Inject IKeyboardLayoutService into TerminalInstance for locale-aware
  NumpadDecimal resolution (e.g. ',' on German layouts)
- Add pre-xterm key event handler that detects 'Unidentified' numpad
  events and restores the correct key value from event.code
- Create a derived KeyboardEvent for local dispatch instead of relying
  solely on mutating the original event
- Patch the original event via Object.defineProperty so xterm.js also
  sees the corrected key after the handler returns
- Extract resolveNumpadKey() as an exported pure function for testability
- Add 6 unit tests covering operators, digits (NumLock on/off), locale-
  aware decimal, and unknown code handling
Workaround for Chromium 142+ regression (chromium issue 405793116) where
numpad KeyboardEvent.key is reported as "Unidentified", breaking xterm.js
Kitty keyboard protocol handling used by TUI apps.

Changes:
- Inject IKeyboardLayoutService into TerminalInstance for locale-aware
  NumpadDecimal resolution (e.g. ',' on German layouts)
- Add pre-xterm key event handler that detects 'Unidentified' numpad
  events and restores the correct key value from event.code
- Create a derived KeyboardEvent for local dispatch instead of relying
  solely on mutating the original event
- Patch the original event via Object.defineProperty so xterm.js also
  sees the corrected key after the handler returns
- Extract resolveNumpadKey() as an exported pure function for testability
- Add 6 unit tests covering operators, digits (NumLock on/off), locale-
  aware decimal, and unknown code handling
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Numpad keys not working in terminal TUI applications after 1.110 update (Electron 39 / Chromium 142 regression)

4 participants