mirror of
https://github.com/wavetermdev/xterm.js.git
synced 2026-08-05 13:43:48 -07:00
Fix some edge cases
This commit is contained in:
@@ -1118,8 +1118,9 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal {
|
||||
}
|
||||
|
||||
// HACK: Process A-Z in the keypress event to fix an issue with macOS IMEs where lower case
|
||||
// letters cannot be input while caps lock is on.
|
||||
if (event.key && !event.ctrlKey && !event.altKey && !event.metaKey && event.key.length === 1) {
|
||||
// letters cannot be input while caps lock is on. Skip this hack when using kitty protocol
|
||||
// as it needs to send proper CSI u sequences for all key events.
|
||||
if (!useKitty && event.key && !event.ctrlKey && !event.altKey && !event.metaKey && event.key.length === 1) {
|
||||
if (event.key.charCodeAt(0) >= 65 && event.key.charCodeAt(0) <= 90) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -547,115 +547,42 @@ describe('KittyKeyboard', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('legacy ctrl mapping', () => {
|
||||
it('ctrl+a → 0x01', () => {
|
||||
const result = evaluateKeyboardEventKitty(createEvent({ key: 'a', ctrlKey: true }), 0);
|
||||
assert.strictEqual(result.key, '\x01');
|
||||
describe('REPORT_ALTERNATE_KEYS flag', () => {
|
||||
const flags = KittyKeyboardFlags.REPORT_ALL_KEYS_AS_ESCAPE_CODES | KittyKeyboardFlags.REPORT_ALTERNATE_KEYS;
|
||||
|
||||
it('Shift+a includes shifted key → CSI 97:65 ; 2 u', () => {
|
||||
const result = evaluateKeyboardEventKitty(createEvent({ key: 'A', shiftKey: true, code: 'KeyA' }), flags);
|
||||
assert.strictEqual(result.key, '\x1b[97:65;2u');
|
||||
});
|
||||
|
||||
it('ctrl+b → 0x02', () => {
|
||||
const result = evaluateKeyboardEventKitty(createEvent({ key: 'b', ctrlKey: true }), 0);
|
||||
assert.strictEqual(result.key, '\x02');
|
||||
it('unshifted key has no alternate', () => {
|
||||
const result = evaluateKeyboardEventKitty(createEvent({ key: 'a', code: 'KeyA' }), flags);
|
||||
assert.strictEqual(result.key, '\x1b[97u');
|
||||
});
|
||||
|
||||
it('ctrl+c → 0x03', () => {
|
||||
const result = evaluateKeyboardEventKitty(createEvent({ key: 'c', ctrlKey: true }), 0);
|
||||
assert.strictEqual(result.key, '\x03');
|
||||
it('Shift+5 includes shifted key → CSI 53:37 ; 2 u', () => {
|
||||
const result = evaluateKeyboardEventKitty(createEvent({ key: '%', shiftKey: true, code: 'Digit5' }), flags);
|
||||
assert.strictEqual(result.key, '\x1b[53:37;2u');
|
||||
});
|
||||
|
||||
it('ctrl+z → 0x1A', () => {
|
||||
const result = evaluateKeyboardEventKitty(createEvent({ key: 'z', ctrlKey: true }), 0);
|
||||
assert.strictEqual(result.key, '\x1a');
|
||||
it('functional keys have no shifted alternate', () => {
|
||||
const result = evaluateKeyboardEventKitty(createEvent({ key: 'Escape', shiftKey: true }), flags);
|
||||
assert.strictEqual(result.key, '\x1b[27;2u');
|
||||
});
|
||||
});
|
||||
|
||||
describe('REPORT_ALTERNATE_KEYS with REPORT_ASSOCIATED_TEXT', () => {
|
||||
const flags = KittyKeyboardFlags.REPORT_ALL_KEYS_AS_ESCAPE_CODES | KittyKeyboardFlags.REPORT_ALTERNATE_KEYS | KittyKeyboardFlags.REPORT_ASSOCIATED_TEXT;
|
||||
|
||||
it('Shift+a → CSI 97:65 ; 2 ; 65 u', () => {
|
||||
const result = evaluateKeyboardEventKitty(createEvent({ key: 'A', shiftKey: true, code: 'KeyA' }), flags);
|
||||
assert.strictEqual(result.key, '\x1b[97:65;2;65u');
|
||||
});
|
||||
|
||||
it('ctrl+space → 0x00', () => {
|
||||
const result = evaluateKeyboardEventKitty(createEvent({ key: ' ', ctrlKey: true }), 0);
|
||||
assert.strictEqual(result.key, '\x00');
|
||||
});
|
||||
|
||||
it('ctrl+[ → 0x1B (ESC)', () => {
|
||||
const result = evaluateKeyboardEventKitty(createEvent({ key: '[', ctrlKey: true }), 0);
|
||||
assert.strictEqual(result.key, '\x1b');
|
||||
});
|
||||
|
||||
it('ctrl+\\ → 0x1C', () => {
|
||||
const result = evaluateKeyboardEventKitty(createEvent({ key: '\\', ctrlKey: true }), 0);
|
||||
assert.strictEqual(result.key, '\x1c');
|
||||
});
|
||||
|
||||
it('ctrl+] → 0x1D', () => {
|
||||
const result = evaluateKeyboardEventKitty(createEvent({ key: ']', ctrlKey: true }), 0);
|
||||
assert.strictEqual(result.key, '\x1d');
|
||||
});
|
||||
|
||||
it('ctrl+^ → 0x1E', () => {
|
||||
const result = evaluateKeyboardEventKitty(createEvent({ key: '^', ctrlKey: true }), 0);
|
||||
assert.strictEqual(result.key, '\x1e');
|
||||
});
|
||||
|
||||
it('ctrl+_ → 0x1F', () => {
|
||||
const result = evaluateKeyboardEventKitty(createEvent({ key: '_', ctrlKey: true }), 0);
|
||||
assert.strictEqual(result.key, '\x1f');
|
||||
});
|
||||
|
||||
it('ctrl+2 → 0x00', () => {
|
||||
const result = evaluateKeyboardEventKitty(createEvent({ key: '2', ctrlKey: true }), 0);
|
||||
assert.strictEqual(result.key, '\x00');
|
||||
});
|
||||
|
||||
it('ctrl+3 → 0x1B', () => {
|
||||
const result = evaluateKeyboardEventKitty(createEvent({ key: '3', ctrlKey: true }), 0);
|
||||
assert.strictEqual(result.key, '\x1b');
|
||||
});
|
||||
|
||||
it('ctrl+4 → 0x1C', () => {
|
||||
const result = evaluateKeyboardEventKitty(createEvent({ key: '4', ctrlKey: true }), 0);
|
||||
assert.strictEqual(result.key, '\x1c');
|
||||
});
|
||||
|
||||
it('ctrl+5 → 0x1D', () => {
|
||||
const result = evaluateKeyboardEventKitty(createEvent({ key: '5', ctrlKey: true }), 0);
|
||||
assert.strictEqual(result.key, '\x1d');
|
||||
});
|
||||
|
||||
it('ctrl+6 → 0x1E', () => {
|
||||
const result = evaluateKeyboardEventKitty(createEvent({ key: '6', ctrlKey: true }), 0);
|
||||
assert.strictEqual(result.key, '\x1e');
|
||||
});
|
||||
|
||||
it('ctrl+7 → 0x1F', () => {
|
||||
const result = evaluateKeyboardEventKitty(createEvent({ key: '7', ctrlKey: true }), 0);
|
||||
assert.strictEqual(result.key, '\x1f');
|
||||
});
|
||||
|
||||
it('ctrl+8 → 0x7F', () => {
|
||||
const result = evaluateKeyboardEventKitty(createEvent({ key: '8', ctrlKey: true }), 0);
|
||||
assert.strictEqual(result.key, '\x7f');
|
||||
});
|
||||
|
||||
it('ctrl+/ → 0x1F', () => {
|
||||
const result = evaluateKeyboardEventKitty(createEvent({ key: '/', ctrlKey: true }), 0);
|
||||
assert.strictEqual(result.key, '\x1f');
|
||||
});
|
||||
|
||||
it('ctrl+? → 0x7F', () => {
|
||||
const result = evaluateKeyboardEventKitty(createEvent({ key: '?', ctrlKey: true }), 0);
|
||||
assert.strictEqual(result.key, '\x7f');
|
||||
});
|
||||
|
||||
it('alt+a → ESC a', () => {
|
||||
const result = evaluateKeyboardEventKitty(createEvent({ key: 'a', altKey: true }), 0);
|
||||
assert.strictEqual(result.key, '\x1ba');
|
||||
});
|
||||
|
||||
it('alt+A → ESC A', () => {
|
||||
const result = evaluateKeyboardEventKitty(createEvent({ key: 'A', altKey: true, shiftKey: true }), 0);
|
||||
assert.strictEqual(result.key, '\x1bA');
|
||||
});
|
||||
|
||||
it('ctrl+alt+a → ESC ctrl+a', () => {
|
||||
const result = evaluateKeyboardEventKitty(createEvent({ key: 'a', ctrlKey: true, altKey: true }), 0);
|
||||
assert.strictEqual(result.key, '\x1b\x01');
|
||||
it('Shift+a release → CSI 97:65 ; 2:3 u (no text)', () => {
|
||||
const flagsWithEvents = flags | KittyKeyboardFlags.REPORT_EVENT_TYPES;
|
||||
const result = evaluateKeyboardEventKitty(createEvent({ key: 'A', shiftKey: true, code: 'KeyA' }), flagsWithEvents, KittyKeyboardEventType.RELEASE);
|
||||
assert.strictEqual(result.key, '\x1b[97:65;2:3u');
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -59,35 +59,13 @@ const FUNCTIONAL_KEY_CODES: { [key: string]: number } = {
|
||||
'Enter': 13,
|
||||
'Tab': 9,
|
||||
'Backspace': 127,
|
||||
'Insert': 2,
|
||||
'Delete': 3,
|
||||
'ArrowLeft': 57419,
|
||||
'ArrowRight': 57421,
|
||||
'ArrowUp': 57417,
|
||||
'ArrowDown': 57420,
|
||||
'PageUp': 57423,
|
||||
'PageDown': 57424,
|
||||
'Home': 57416,
|
||||
'End': 57418,
|
||||
'CapsLock': 57358,
|
||||
'ScrollLock': 57359,
|
||||
'NumLock': 57360,
|
||||
'PrintScreen': 57361,
|
||||
'Pause': 57362,
|
||||
'ContextMenu': 57363,
|
||||
// F1-F35
|
||||
'F1': 57364,
|
||||
'F2': 57365,
|
||||
'F3': 57366,
|
||||
'F4': 57367,
|
||||
'F5': 57368,
|
||||
'F6': 57369,
|
||||
'F7': 57370,
|
||||
'F8': 57371,
|
||||
'F9': 57372,
|
||||
'F10': 57373,
|
||||
'F11': 57374,
|
||||
'F12': 57375,
|
||||
// F13-F35 (F1-F12 use legacy encoding)
|
||||
'F13': 57376,
|
||||
'F14': 57377,
|
||||
'F15': 57378,
|
||||
@@ -138,6 +116,46 @@ const FUNCTIONAL_KEY_CODES: { [key: string]: number } = {
|
||||
'AudioVolumeMute': 57440
|
||||
};
|
||||
|
||||
/**
|
||||
* Keys that use CSI ~ encoding with a number parameter.
|
||||
*/
|
||||
const CSI_TILDE_KEYS: { [key: string]: number } = {
|
||||
'Insert': 2,
|
||||
'Delete': 3,
|
||||
'PageUp': 5,
|
||||
'PageDown': 6,
|
||||
'F5': 15,
|
||||
'F6': 17,
|
||||
'F7': 18,
|
||||
'F8': 19,
|
||||
'F9': 20,
|
||||
'F10': 21,
|
||||
'F11': 23,
|
||||
'F12': 24
|
||||
};
|
||||
|
||||
/**
|
||||
* Keys that use CSI letter encoding (arrows, Home, End).
|
||||
*/
|
||||
const CSI_LETTER_KEYS: { [key: string]: string } = {
|
||||
'ArrowUp': 'A',
|
||||
'ArrowDown': 'B',
|
||||
'ArrowRight': 'C',
|
||||
'ArrowLeft': 'D',
|
||||
'Home': 'H',
|
||||
'End': 'F'
|
||||
};
|
||||
|
||||
/**
|
||||
* Function keys F1-F4 use SS3 encoding without modifiers.
|
||||
*/
|
||||
const SS3_FUNCTION_KEYS: { [key: string]: string } = {
|
||||
'F1': 'P',
|
||||
'F2': 'Q',
|
||||
'F3': 'R',
|
||||
'F4': 'S'
|
||||
};
|
||||
|
||||
/**
|
||||
* Map browser key codes to Kitty numpad codes.
|
||||
*/
|
||||
@@ -194,6 +212,8 @@ function encodeModifiers(ev: IKeyboardEvent): number {
|
||||
|
||||
/**
|
||||
* Get the unicode key code for a keyboard event.
|
||||
* Returns the lowercase codepoint for letters.
|
||||
* For shifted keys, uses the code property to get the base key.
|
||||
*/
|
||||
function getKeyCode(ev: IKeyboardEvent): number | undefined {
|
||||
// Check for numpad first
|
||||
@@ -214,9 +234,31 @@ function getKeyCode(ev: IKeyboardEvent): number | undefined {
|
||||
return funcCode;
|
||||
}
|
||||
|
||||
// For shifted keys, use code property to get base key
|
||||
if (ev.shiftKey && ev.code) {
|
||||
// Handle Digit0-Digit9
|
||||
if (ev.code.startsWith('Digit') && ev.code.length === 6) {
|
||||
const digit = ev.code.charAt(5);
|
||||
if (digit >= '0' && digit <= '9') {
|
||||
return digit.charCodeAt(0);
|
||||
}
|
||||
}
|
||||
// Handle KeyA-KeyZ
|
||||
if (ev.code.startsWith('Key') && ev.code.length === 4) {
|
||||
const letter = ev.code.charAt(3).toLowerCase();
|
||||
return letter.charCodeAt(0);
|
||||
}
|
||||
}
|
||||
|
||||
// For regular keys, use the key character's codepoint
|
||||
// Always use lowercase for letters (per spec)
|
||||
if (ev.key.length === 1) {
|
||||
return ev.key.codePointAt(0);
|
||||
const code = ev.key.codePointAt(0)!;
|
||||
// Convert uppercase A-Z to lowercase a-z
|
||||
if (code >= 65 && code <= 90) {
|
||||
return code + 32;
|
||||
}
|
||||
return code;
|
||||
}
|
||||
|
||||
return undefined;
|
||||
@@ -248,36 +290,64 @@ export function evaluateKeyboardEventKitty(
|
||||
key: undefined
|
||||
};
|
||||
|
||||
// Get the key code
|
||||
const modifiers = encodeModifiers(ev);
|
||||
const isMod = isModifierKey(ev);
|
||||
const reportEventTypes = !!(flags & KittyKeyboardFlags.REPORT_EVENT_TYPES);
|
||||
|
||||
// Don't report release events unless flag is set
|
||||
if (!reportEventTypes && eventType === KittyKeyboardEventType.RELEASE) {
|
||||
return result;
|
||||
}
|
||||
|
||||
// Modifier-only keys require REPORT_ALL_KEYS_AS_ESCAPE_CODES or REPORT_EVENT_TYPES
|
||||
if (isMod && !(flags & KittyKeyboardFlags.REPORT_ALL_KEYS_AS_ESCAPE_CODES) && !reportEventTypes) {
|
||||
return result;
|
||||
}
|
||||
|
||||
// Check for CSI letter keys (arrows, Home, End)
|
||||
const csiLetter = CSI_LETTER_KEYS[ev.key];
|
||||
if (csiLetter) {
|
||||
result.key = buildCsiLetterSequence(csiLetter, modifiers, eventType, reportEventTypes);
|
||||
result.cancel = true;
|
||||
return result;
|
||||
}
|
||||
|
||||
// Check for SS3/CSI function keys (F1-F4)
|
||||
const ss3Letter = SS3_FUNCTION_KEYS[ev.key];
|
||||
if (ss3Letter) {
|
||||
result.key = buildSs3Sequence(ss3Letter, modifiers, eventType, reportEventTypes);
|
||||
result.cancel = true;
|
||||
return result;
|
||||
}
|
||||
|
||||
// Check for CSI ~ keys (Insert, Delete, PageUp/Down, F5-F12)
|
||||
const tildeCode = CSI_TILDE_KEYS[ev.key];
|
||||
if (tildeCode !== undefined) {
|
||||
result.key = buildCsiTildeSequence(tildeCode, modifiers, eventType, reportEventTypes);
|
||||
result.cancel = true;
|
||||
return result;
|
||||
}
|
||||
|
||||
// Get the key code for CSI u encoding
|
||||
const keyCode = getKeyCode(ev);
|
||||
if (keyCode === undefined) {
|
||||
return result;
|
||||
}
|
||||
|
||||
const modifiers = encodeModifiers(ev);
|
||||
const isFunc = FUNCTIONAL_KEY_CODES[ev.key] !== undefined || getNumpadKeyCode(ev) !== undefined;
|
||||
const isMod = isModifierKey(ev);
|
||||
|
||||
// Determine if we should use CSI u encoding or can use legacy
|
||||
// Determine if we should use CSI u encoding
|
||||
let useCsiU = false;
|
||||
|
||||
if (flags & KittyKeyboardFlags.REPORT_ALL_KEYS_AS_ESCAPE_CODES) {
|
||||
// All keys use CSI u
|
||||
useCsiU = true;
|
||||
} else if (flags & KittyKeyboardFlags.REPORT_EVENT_TYPES) {
|
||||
// When reporting event types, use CSI u for all keys
|
||||
} else if (reportEventTypes) {
|
||||
useCsiU = true;
|
||||
} else if (flags & KittyKeyboardFlags.DISAMBIGUATE_ESCAPE_CODES) {
|
||||
// Modifier-only keys are never reported without REPORT_EVENT_TYPES
|
||||
if (isMod) {
|
||||
return result;
|
||||
}
|
||||
// Modifier-only keys already handled above
|
||||
// Use CSI u for keys that would be ambiguous in legacy encoding
|
||||
if (keyCode === 27 || keyCode === 127 || keyCode === 13) {
|
||||
// Escape, Backspace, Enter
|
||||
useCsiU = true;
|
||||
} else if (keyCode === 9 && ev.shiftKey) {
|
||||
// Shift+Tab
|
||||
if (keyCode === 27 || keyCode === 127 || keyCode === 13 || keyCode === 9 || keyCode === 32) {
|
||||
// Escape, Backspace, Enter, Tab, Space
|
||||
useCsiU = true;
|
||||
} else if (isFunc) {
|
||||
useCsiU = true;
|
||||
@@ -287,58 +357,154 @@ export function evaluateKeyboardEventKitty(
|
||||
}
|
||||
}
|
||||
|
||||
// Determine if we should report this event type
|
||||
const reportEventTypes = !!(flags & KittyKeyboardFlags.REPORT_EVENT_TYPES);
|
||||
if (!reportEventTypes && eventType === KittyKeyboardEventType.RELEASE) {
|
||||
// Don't report release events unless flag is set
|
||||
return result;
|
||||
}
|
||||
|
||||
if (useCsiU) {
|
||||
// Build CSI u sequence: CSI keycode ; modifiers:event-type u
|
||||
// Format: CSI <keycode>[:<shifted>][:<base>] ; <modifiers>[:<event>] u
|
||||
let seq = C0.ESC + '[' + keyCode;
|
||||
|
||||
// Check if we need associated text (press and repeat events, not release)
|
||||
const reportAssociatedText = !!(flags & KittyKeyboardFlags.REPORT_ASSOCIATED_TEXT) &&
|
||||
eventType !== KittyKeyboardEventType.RELEASE && ev.key.length === 1 && !isFunc && !isMod;
|
||||
const textCode = reportAssociatedText ? ev.key.codePointAt(0) : undefined;
|
||||
|
||||
// When text is present, don't include event type marker (even for repeat)
|
||||
// Release events always need event type marker
|
||||
const needsEventType = reportEventTypes && eventType !== KittyKeyboardEventType.PRESS && textCode === undefined;
|
||||
if (modifiers > 0 || needsEventType || textCode !== undefined) {
|
||||
seq += ';';
|
||||
// Use 1 as base when event type needed but no modifiers (kitty format: 1 + modifier_bits)
|
||||
if (modifiers > 0) {
|
||||
seq += modifiers;
|
||||
} else if (needsEventType) {
|
||||
seq += '1';
|
||||
}
|
||||
if (needsEventType) {
|
||||
seq += ':' + eventType;
|
||||
}
|
||||
}
|
||||
|
||||
// Add associated text if requested
|
||||
if (textCode !== undefined) {
|
||||
seq += ';' + textCode;
|
||||
}
|
||||
|
||||
seq += 'u';
|
||||
result.key = seq;
|
||||
result.key = buildCsiUSequence(ev, keyCode, modifiers, eventType, flags, isFunc, isMod);
|
||||
result.cancel = true;
|
||||
} else {
|
||||
// Legacy-compatible encoding for text keys without modifiers
|
||||
if (ev.key.length === 1 && !ev.ctrlKey && !ev.altKey && !ev.metaKey) {
|
||||
result.key = ev.key;
|
||||
}
|
||||
// Otherwise no key sequence (will fall through to legacy handling)
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build CSI letter sequence for arrow keys, Home, End.
|
||||
* Format: CSI [1;mod] letter
|
||||
*/
|
||||
function buildCsiLetterSequence(
|
||||
letter: string,
|
||||
modifiers: number,
|
||||
eventType: KittyKeyboardEventType,
|
||||
reportEventTypes: boolean
|
||||
): string {
|
||||
const needsEventType = reportEventTypes && eventType !== KittyKeyboardEventType.PRESS;
|
||||
|
||||
if (modifiers > 0 || needsEventType) {
|
||||
let seq = C0.ESC + '[1;' + (modifiers > 0 ? modifiers : '1');
|
||||
if (needsEventType) {
|
||||
seq += ':' + eventType;
|
||||
}
|
||||
seq += letter;
|
||||
return seq;
|
||||
}
|
||||
return C0.ESC + '[' + letter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build SS3 sequence for F1-F4.
|
||||
* Without modifiers: SS3 letter
|
||||
* With modifiers: CSI 1;mod letter
|
||||
*/
|
||||
function buildSs3Sequence(
|
||||
letter: string,
|
||||
modifiers: number,
|
||||
eventType: KittyKeyboardEventType,
|
||||
reportEventTypes: boolean
|
||||
): string {
|
||||
const needsEventType = reportEventTypes && eventType !== KittyKeyboardEventType.PRESS;
|
||||
|
||||
if (modifiers > 0 || needsEventType) {
|
||||
let seq = C0.ESC + '[1;' + (modifiers > 0 ? modifiers : '1');
|
||||
if (needsEventType) {
|
||||
seq += ':' + eventType;
|
||||
}
|
||||
seq += letter;
|
||||
return seq;
|
||||
}
|
||||
return C0.ESC + 'O' + letter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build CSI ~ sequence for Insert, Delete, PageUp/Down, F5-F12.
|
||||
* Format: CSI number [;mod[:event]] ~
|
||||
*/
|
||||
function buildCsiTildeSequence(
|
||||
number: number,
|
||||
modifiers: number,
|
||||
eventType: KittyKeyboardEventType,
|
||||
reportEventTypes: boolean
|
||||
): string {
|
||||
const needsEventType = reportEventTypes && eventType !== KittyKeyboardEventType.PRESS;
|
||||
|
||||
let seq = C0.ESC + '[' + number;
|
||||
if (modifiers > 0 || needsEventType) {
|
||||
seq += ';' + (modifiers > 0 ? modifiers : '1');
|
||||
if (needsEventType) {
|
||||
seq += ':' + eventType;
|
||||
}
|
||||
}
|
||||
seq += '~';
|
||||
return seq;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build CSI u sequence.
|
||||
* Format: CSI keycode[:shifted[:base]] [;mod[:event][;text]] u
|
||||
*/
|
||||
function buildCsiUSequence(
|
||||
ev: IKeyboardEvent,
|
||||
keyCode: number,
|
||||
modifiers: number,
|
||||
eventType: KittyKeyboardEventType,
|
||||
flags: number,
|
||||
isFunc: boolean,
|
||||
isMod: boolean
|
||||
): string {
|
||||
const reportEventTypes = !!(flags & KittyKeyboardFlags.REPORT_EVENT_TYPES);
|
||||
const reportAlternateKeys = !!(flags & KittyKeyboardFlags.REPORT_ALTERNATE_KEYS);
|
||||
|
||||
let seq = C0.ESC + '[' + keyCode;
|
||||
|
||||
// Add shifted key alternate if REPORT_ALTERNATE_KEYS is set and shift is pressed
|
||||
// Only for text-producing keys (not functional or modifier keys)
|
||||
let shiftedKey: number | undefined;
|
||||
if (reportAlternateKeys && ev.shiftKey && ev.key.length === 1 && !isFunc && !isMod) {
|
||||
shiftedKey = ev.key.codePointAt(0);
|
||||
seq += ':' + shiftedKey;
|
||||
}
|
||||
|
||||
// Check if we need associated text (press and repeat events, not release)
|
||||
// Only for text-producing keys (not functional or modifier keys)
|
||||
// Also don't include text when ctrl is pressed (produces control code)
|
||||
const reportAssociatedText = !!(flags & KittyKeyboardFlags.REPORT_ASSOCIATED_TEXT) &&
|
||||
eventType !== KittyKeyboardEventType.RELEASE &&
|
||||
ev.key.length === 1 &&
|
||||
!isFunc &&
|
||||
!isMod &&
|
||||
!ev.ctrlKey;
|
||||
const textCode = reportAssociatedText ? ev.key.codePointAt(0) : undefined;
|
||||
|
||||
// Determine if we need event type suffix
|
||||
// For repeat: only include :2 when there's no text (text implies it's still useful input)
|
||||
// For release: always include :3
|
||||
const needsEventType = reportEventTypes &&
|
||||
eventType !== KittyKeyboardEventType.PRESS &&
|
||||
(eventType === KittyKeyboardEventType.RELEASE || textCode === undefined);
|
||||
|
||||
if (modifiers > 0 || needsEventType || textCode !== undefined) {
|
||||
seq += ';';
|
||||
if (modifiers > 0) {
|
||||
seq += modifiers;
|
||||
} else if (needsEventType) {
|
||||
seq += '1';
|
||||
}
|
||||
if (needsEventType) {
|
||||
seq += ':' + eventType;
|
||||
}
|
||||
}
|
||||
|
||||
// Add associated text if requested
|
||||
if (textCode !== undefined) {
|
||||
seq += ';' + textCode;
|
||||
}
|
||||
|
||||
seq += 'u';
|
||||
return seq;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a keyboard event should be handled by Kitty protocol.
|
||||
* Returns true if Kitty flags are active and the event should use Kitty encoding.
|
||||
|
||||
Reference in New Issue
Block a user