(data: string)
| 585 | } |
| 586 | |
| 587 | function parseKittySequence(data: string): ParsedKittySequence | null { |
| 588 | // CSI u format with alternate keys (flag 4): |
| 589 | // \x1b[<codepoint>u |
| 590 | // \x1b[<codepoint>;<mod>u |
| 591 | // \x1b[<codepoint>;<mod>:<event>u |
| 592 | // \x1b[<codepoint>:<shifted>;<mod>u |
| 593 | // \x1b[<codepoint>:<shifted>:<base>;<mod>u |
| 594 | // \x1b[<codepoint>::<base>;<mod>u (no shifted key, only base) |
| 595 | // |
| 596 | // With flag 2, event type is appended after modifier colon: 1=press, 2=repeat, 3=release |
| 597 | // With flag 4, alternate keys are appended after codepoint with colons |
| 598 | const csiUMatch = data.match(/^\x1b\[(\d+)(?::(\d*))?(?::(\d+))?(?:;(\d+))?(?::(\d+))?u$/); |
| 599 | if (csiUMatch) { |
| 600 | const codepoint = parseInt(csiUMatch[1]!, 10); |
| 601 | const shiftedKey = csiUMatch[2] && csiUMatch[2].length > 0 ? parseInt(csiUMatch[2], 10) : undefined; |
| 602 | const baseLayoutKey = csiUMatch[3] ? parseInt(csiUMatch[3], 10) : undefined; |
| 603 | const modValue = csiUMatch[4] ? parseInt(csiUMatch[4], 10) : 1; |
| 604 | const eventType = parseEventType(csiUMatch[5]); |
| 605 | _lastEventType = eventType; |
| 606 | return { codepoint, shiftedKey, baseLayoutKey, modifier: modValue - 1, eventType }; |
| 607 | } |
| 608 | |
| 609 | // Arrow keys with modifier: \x1b[1;<mod>A/B/C/D or \x1b[1;<mod>:<event>A/B/C/D |
| 610 | const arrowMatch = data.match(/^\x1b\[1;(\d+)(?::(\d+))?([ABCD])$/); |
| 611 | if (arrowMatch) { |
| 612 | const modValue = parseInt(arrowMatch[1]!, 10); |
| 613 | const eventType = parseEventType(arrowMatch[2]); |
| 614 | const arrowCodes: Record<string, number> = { A: -1, B: -2, C: -3, D: -4 }; |
| 615 | _lastEventType = eventType; |
| 616 | return { codepoint: arrowCodes[arrowMatch[3]!]!, modifier: modValue - 1, eventType }; |
| 617 | } |
| 618 | |
| 619 | // Functional keys: \x1b[<num>~ or \x1b[<num>;<mod>~ or \x1b[<num>;<mod>:<event>~ |
| 620 | const funcMatch = data.match(/^\x1b\[(\d+)(?:;(\d+))?(?::(\d+))?~$/); |
| 621 | if (funcMatch) { |
| 622 | const keyNum = parseInt(funcMatch[1]!, 10); |
| 623 | const modValue = funcMatch[2] ? parseInt(funcMatch[2], 10) : 1; |
| 624 | const eventType = parseEventType(funcMatch[3]); |
| 625 | const funcCodes: Record<number, number> = { |
| 626 | 2: FUNCTIONAL_CODEPOINTS.insert, |
| 627 | 3: FUNCTIONAL_CODEPOINTS.delete, |
| 628 | 5: FUNCTIONAL_CODEPOINTS.pageUp, |
| 629 | 6: FUNCTIONAL_CODEPOINTS.pageDown, |
| 630 | 7: FUNCTIONAL_CODEPOINTS.home, |
| 631 | 8: FUNCTIONAL_CODEPOINTS.end, |
| 632 | }; |
| 633 | const codepoint = funcCodes[keyNum]; |
| 634 | if (codepoint !== undefined) { |
| 635 | _lastEventType = eventType; |
| 636 | return { codepoint, modifier: modValue - 1, eventType }; |
| 637 | } |
| 638 | } |
| 639 | |
| 640 | // Home/End with modifier: \x1b[1;<mod>H/F or \x1b[1;<mod>:<event>H/F |
| 641 | const homeEndMatch = data.match(/^\x1b\[1;(\d+)(?::(\d+))?([HF])$/); |
| 642 | if (homeEndMatch) { |
| 643 | const modValue = parseInt(homeEndMatch[1]!, 10); |
| 644 | const eventType = parseEventType(homeEndMatch[2]); |
no test coverage detected