Support command/ctrl/alt backspace correctly (#17175)

This commit is contained in:
Tommaso Sciortino
2026-01-21 10:13:26 -08:00
committed by GitHub
parent 7e4adfb2fd
commit 8d5c8a6fad
27 changed files with 487 additions and 298 deletions
@@ -251,9 +251,10 @@ function bufferPaste(keypressHandler: KeypressHandler): KeypressHandler {
if (buffer.length > 0) {
keypressHandler({
name: 'paste',
ctrl: false,
meta: false,
shift: false,
alt: false,
ctrl: false,
cmd: false,
insertable: true,
sequence: buffer,
});
@@ -300,9 +301,10 @@ function* emitKeys(
let escaped = false;
let name = undefined;
let ctrl = false;
let meta = false;
let shift = false;
let alt = false;
let ctrl = false;
let cmd = false;
let code = undefined;
let insertable = false;
@@ -353,9 +355,10 @@ function* emitKeys(
const decoded = Buffer.from(base64Data, 'base64').toString('utf-8');
keypressHandler({
name: 'paste',
ctrl: false,
meta: false,
shift: false,
alt: false,
ctrl: false,
cmd: false,
insertable: true,
sequence: decoded,
});
@@ -490,9 +493,10 @@ function* emitKeys(
}
// Parse the key modifier
ctrl = !!(modifier & 4);
meta = !!(modifier & 10); // use 10 to catch both alt (2) and meta (8).
shift = !!(modifier & 1);
alt = !!(modifier & 2);
ctrl = !!(modifier & 4);
cmd = !!(modifier & 8);
const keyInfo = KEY_INFO_MAP[code];
if (keyInfo) {
@@ -503,13 +507,16 @@ function* emitKeys(
if (keyInfo.ctrl) {
ctrl = true;
}
if (name === 'space' && !ctrl && !meta) {
if (name === 'space' && !ctrl && !cmd && !alt) {
sequence = ' ';
insertable = true;
}
} else {
name = 'undefined';
if ((ctrl || meta) && (code.endsWith('u') || code.endsWith('~'))) {
if (
(ctrl || cmd || alt) &&
(code.endsWith('u') || code.endsWith('~'))
) {
// CSI-u or tilde-coded functional keys: ESC [ <code> ; <mods> (u|~)
const codeNumber = parseInt(code.slice(1, -1), 10);
if (
@@ -523,26 +530,26 @@ function* emitKeys(
} else if (ch === '\r') {
// carriage return
name = 'return';
meta = escaped;
alt = escaped;
} else if (escaped && ch === '\n') {
// Alt+Enter (linefeed), should be consistent with carriage return
name = 'return';
meta = escaped;
alt = escaped;
} else if (ch === '\t') {
// tab
name = 'tab';
meta = escaped;
alt = escaped;
} else if (ch === '\b' || ch === '\x7f') {
// backspace or ctrl+h
name = 'backspace';
meta = escaped;
alt = escaped;
} else if (ch === ESC) {
// escape key
name = 'escape';
meta = escaped;
alt = escaped;
} else if (ch === ' ') {
name = 'space';
meta = escaped;
alt = escaped;
insertable = true;
} else if (!escaped && ch <= '\x1a') {
// ctrl+letter
@@ -552,29 +559,30 @@ function* emitKeys(
// Letter, number, shift+letter
name = ch.toLowerCase();
shift = /^[A-Z]$/.exec(ch) !== null;
meta = escaped;
alt = escaped;
insertable = true;
} else if (MAC_ALT_KEY_CHARACTER_MAP[ch] && process.platform === 'darwin') {
name = MAC_ALT_KEY_CHARACTER_MAP[ch];
meta = true;
alt = true;
} else if (sequence === `${ESC}${ESC}`) {
// Double escape
name = 'escape';
meta = true;
alt = true;
// Emit first escape key here, then continue processing
keypressHandler({
name: 'escape',
ctrl,
meta,
shift,
alt,
ctrl,
cmd,
insertable: false,
sequence: ESC,
});
} else if (escaped) {
// Escape sequence timeout
name = ch.length ? undefined : 'escape';
meta = true;
alt = true;
} else {
// Any other character is considered printable.
insertable = true;
@@ -586,9 +594,10 @@ function* emitKeys(
) {
keypressHandler({
name: name || '',
ctrl,
meta,
shift,
alt,
ctrl,
cmd,
insertable,
sequence,
});
@@ -599,9 +608,10 @@ function* emitKeys(
export interface Key {
name: string;
ctrl: boolean;
meta: boolean;
shift: boolean;
alt: boolean;
ctrl: boolean;
cmd: boolean; // Command/Windows/Super key
insertable: boolean;
sequence: string;
}