From 86f0cf2f25dc3bad008933b9348f6521d4cf8c15 Mon Sep 17 00:00:00 2001 From: "zhenxing.shen" Date: Tue, 30 Jun 2026 14:47:26 +0800 Subject: [PATCH 1/2] fix(term): correctly map Ctrl+[ to ESC and other Ctrl+symbol combos On some Linux keyboard layouts, xterm.js may misidentify Ctrl+symbol key combinations (e.g. reporting Ctrl+] when Ctrl+[ is pressed), because event.key reports the wrong character when Ctrl is held. - Extend keyboardEventToASCII to handle all single-char Ctrl combos using the standard charCode & 0x1F formula (not just A-Z) - Add event.code-based fallback for bracket/backslash keys to correctly identify physical keys regardless of keyboard layout - Add fallback handler in handleTerminalKeydown to send correct ASCII control codes for Ctrl combos not already handled by app keybindings Fixes #3334 --- frontend/app/view/term/term-model.ts | 11 ++++++++ frontend/util/keyutil.ts | 40 +++++++++++++++++++++------- 2 files changed, 41 insertions(+), 10 deletions(-) diff --git a/frontend/app/view/term/term-model.ts b/frontend/app/view/term/term-model.ts index a256929e7d..9ab105ccf5 100644 --- a/frontend/app/view/term/term-model.ts +++ b/frontend/app/view/term/term-model.ts @@ -776,6 +776,17 @@ export class TermViewModel implements ViewModel { event.stopPropagation(); return false; } + // Fallback: send ASCII control character for Ctrl+key combos that xterm.js + // may mishandle on non-US keyboard layouts (e.g. Ctrl+[ should send ESC). + if (waveEvent.control && !waveEvent.alt && !waveEvent.meta) { + const ascii = keyutil.keyboardEventToASCII(waveEvent); + if (ascii.length > 0) { + this.sendDataToController(ascii); + event.preventDefault(); + event.stopPropagation(); + return false; + } + } return true; } diff --git a/frontend/util/keyutil.ts b/frontend/util/keyutil.ts index 867dfcb4e2..1d81ec79b0 100644 --- a/frontend/util/keyutil.ts +++ b/frontend/util/keyutil.ts @@ -288,9 +288,18 @@ const keyMap = { PageDown: "\x1b[6~", }; +// Maps physical key codes to base characters for Ctrl combinations (fallback for non-US keyboard layouts) +const ctrlCodeMap: Record = { + BracketLeft: "[", + Backslash: "\\", + BracketRight: "]", + Space: " ", +}; + +/** + * Converts a WaveKeyboardEvent to its ASCII control sequence for terminal input. + */ function keyboardEventToASCII(event: WaveKeyboardEvent): string { - // check modifiers - // if no modifiers are set, just send the key if (!event.alt && !event.control && !event.meta) { if (event.key == null || event.key == "") { return ""; @@ -304,18 +313,29 @@ function keyboardEventToASCII(event: WaveKeyboardEvent): string { console.log("not sending keyboard event", event.key, event); } } - // if meta or alt is set, there is no ASCII representation if (event.meta || event.alt) { return ""; } - // if ctrl is set, if it is a letter, subtract 64 from the uppercase value to get the ASCII value if (event.control) { - if ( - (event.key.length === 1 && event.key >= "A" && event.key <= "Z") || - (event.key >= "a" && event.key <= "z") - ) { - const key = event.key.toUpperCase(); - return String.fromCharCode(key.charCodeAt(0) - 64); + if (event.key === " " || event.code === "Space") { + return "\x00"; + } + if (event.key === "?") { + return "\x7f"; + } + let ctrlChar: string | null = null; + if (ctrlCodeMap[event.code] != null) { + ctrlChar = ctrlCodeMap[event.code]; + } else if (event.key != null && event.key.length === 1) { + ctrlChar = event.key; + } + if (ctrlChar != null) { + const upperChar = ctrlChar.toUpperCase(); + const code = upperChar.charCodeAt(0); + if (code >= 64 && code <= 95) { + return String.fromCharCode(code - 64); + } + return String.fromCharCode(ctrlChar.charCodeAt(0) & 0x1f); } } return ""; From 8038d250c247abc603d6c23a149bab66c9f0357b Mon Sep 17 00:00:00 2001 From: "zhenxing.shen" Date: Tue, 30 Jun 2026 23:54:53 +0800 Subject: [PATCH 2/2] fix(term): add Slash to ctrlCodeMap for non-US keyboard layouts CodeRabbit review identified that the ctrlCodeMap was missing the Slash physical key, which would cause non-US layouts that surface a localized event.key for the slash key to compute the wrong Ctrl byte. --- frontend/util/keyutil.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/frontend/util/keyutil.ts b/frontend/util/keyutil.ts index 1d81ec79b0..930386d0ff 100644 --- a/frontend/util/keyutil.ts +++ b/frontend/util/keyutil.ts @@ -293,6 +293,7 @@ const ctrlCodeMap: Record = { BracketLeft: "[", Backslash: "\\", BracketRight: "]", + Slash: "/", Space: " ", };