(data: string)
| 1249 | } |
| 1250 | |
| 1251 | export function parseKey(data: string): string | undefined { |
| 1252 | const kitty = parseKittySequence(data); |
| 1253 | if (kitty) { |
| 1254 | return formatParsedKey(kitty.codepoint, kitty.modifier, kitty.baseLayoutKey); |
| 1255 | } |
| 1256 | |
| 1257 | const modifyOtherKeys = parseModifyOtherKeysSequence(data); |
| 1258 | if (modifyOtherKeys) { |
| 1259 | return formatParsedKey(modifyOtherKeys.codepoint, modifyOtherKeys.modifier); |
| 1260 | } |
| 1261 | |
| 1262 | // Mode-aware legacy sequences |
| 1263 | // When Kitty protocol is active, ambiguous sequences are interpreted as custom terminal mappings: |
| 1264 | // - \x1b\r = shift+enter (Kitty mapping), not alt+enter |
| 1265 | // - \n = shift+enter (Ghostty mapping) |
| 1266 | if (_kittyProtocolActive) { |
| 1267 | if (data === "\x1b\r" || data === "\n") return "shift+enter"; |
| 1268 | } |
| 1269 | |
| 1270 | const legacySequenceKeyId = LEGACY_SEQUENCE_KEY_IDS[data]; |
| 1271 | if (legacySequenceKeyId) return legacySequenceKeyId; |
| 1272 | |
| 1273 | // Legacy sequences (used when Kitty protocol is not active, or for unambiguous sequences) |
| 1274 | if (data === "\x1b") return "escape"; |
| 1275 | if (data === "\x1c") return "ctrl+\\"; |
| 1276 | if (data === "\x1d") return "ctrl+]"; |
| 1277 | if (data === "\x1f") return "ctrl+-"; |
| 1278 | if (data === "\x1b\x1b") return "ctrl+alt+["; |
| 1279 | if (data === "\x1b\x1c") return "ctrl+alt+\\"; |
| 1280 | if (data === "\x1b\x1d") return "ctrl+alt+]"; |
| 1281 | if (data === "\x1b\x1f") return "ctrl+alt+-"; |
| 1282 | if (data === "\t") return "tab"; |
| 1283 | if (data === "\r" || (!_kittyProtocolActive && data === "\n") || data === "\x1bOM") return "enter"; |
| 1284 | if (data === "\x00") return "ctrl+space"; |
| 1285 | if (data === " ") return "space"; |
| 1286 | if (data === "\x7f") return "backspace"; |
| 1287 | if (data === "\x08") return isWindowsTerminalSession() ? "ctrl+backspace" : "backspace"; |
| 1288 | if (data === "\x1b[Z") return "shift+tab"; |
| 1289 | if (!_kittyProtocolActive && data === "\x1b\r") return "alt+enter"; |
| 1290 | if (!_kittyProtocolActive && data === "\x1b ") return "alt+space"; |
| 1291 | if (data === "\x1b\x7f" || data === "\x1b\b") return "alt+backspace"; |
| 1292 | if (!_kittyProtocolActive && data === "\x1bB") return "alt+left"; |
| 1293 | if (!_kittyProtocolActive && data === "\x1bF") return "alt+right"; |
| 1294 | if (!_kittyProtocolActive && data.length === 2 && data[0] === "\x1b") { |
| 1295 | const code = data.charCodeAt(1); |
| 1296 | if (code >= 1 && code <= 26) { |
| 1297 | return `ctrl+alt+${String.fromCharCode(code + 96)}`; |
| 1298 | } |
| 1299 | // Legacy alt+letter/digit (ESC followed by the key) |
| 1300 | if ((code >= 97 && code <= 122) || (code >= 48 && code <= 57)) { |
| 1301 | return `alt+${String.fromCharCode(code)}`; |
| 1302 | } |
| 1303 | } |
| 1304 | if (data === "\x1b[A") return "up"; |
| 1305 | if (data === "\x1b[B") return "down"; |
| 1306 | if (data === "\x1b[C") return "right"; |
| 1307 | if (data === "\x1b[D") return "left"; |
| 1308 | if (data === "\x1b[H" || data === "\x1bOH") return "home"; |
no test coverage detected