@mariozechner/pi-tui 0.36.0 → 0.37.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/input.d.ts +1 -0
- package/dist/components/input.d.ts.map +1 -1
- package/dist/components/input.js +7 -0
- package/dist/components/input.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/keybindings.d.ts.map +1 -1
- package/dist/keybindings.js +1 -1
- package/dist/keybindings.js.map +1 -1
- package/dist/keys.d.ts +11 -0
- package/dist/keys.d.ts.map +1 -1
- package/dist/keys.js +52 -7
- package/dist/keys.js.map +1 -1
- package/dist/terminal.d.ts +12 -0
- package/dist/terminal.d.ts.map +1 -1
- package/dist/terminal.js +82 -9
- package/dist/terminal.js.map +1 -1
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +3 -2
- package/dist/utils.js.map +1 -1
- package/package.json +1 -1
package/dist/keys.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"keys.js","sourceRoot":"","sources":["../src/keys.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Keyboard input handling for terminal applications.\n *\n * Supports both legacy terminal sequences and Kitty keyboard protocol.\n * See: https://sw.kovidgoyal.net/kitty/keyboard-protocol/\n *\n * Symbol keys are also supported, however some ctrl+symbol combos\n * overlap with ASCII codes, e.g. ctrl+[ = ESC.\n * See: https://sw.kovidgoyal.net/kitty/keyboard-protocol/#legacy-ctrl-mapping-of-ascii-keys\n * Those can still be * used for ctrl+shift combos\n *\n * API:\n * - matchesKey(data, keyId) - Check if input matches a key identifier\n * - parseKey(data) - Parse input and return the key identifier\n * - Key - Helper object for creating typed key identifiers\n */\n\n// =============================================================================\n// Type-Safe Key Identifiers\n// =============================================================================\n\ntype Letter =\n\t| \"a\"\n\t| \"b\"\n\t| \"c\"\n\t| \"d\"\n\t| \"e\"\n\t| \"f\"\n\t| \"g\"\n\t| \"h\"\n\t| \"i\"\n\t| \"j\"\n\t| \"k\"\n\t| \"l\"\n\t| \"m\"\n\t| \"n\"\n\t| \"o\"\n\t| \"p\"\n\t| \"q\"\n\t| \"r\"\n\t| \"s\"\n\t| \"t\"\n\t| \"u\"\n\t| \"v\"\n\t| \"w\"\n\t| \"x\"\n\t| \"y\"\n\t| \"z\";\n\ntype SymbolKey =\n\t| \"`\"\n\t| \"-\"\n\t| \"=\"\n\t| \"[\"\n\t| \"]\"\n\t| \"\\\\\"\n\t| \";\"\n\t| \"'\"\n\t| \",\"\n\t| \".\"\n\t| \"/\"\n\t| \"!\"\n\t| \"@\"\n\t| \"#\"\n\t| \"$\"\n\t| \"%\"\n\t| \"^\"\n\t| \"&\"\n\t| \"*\"\n\t| \"(\"\n\t| \")\"\n\t| \"_\"\n\t| \"+\"\n\t| \"|\"\n\t| \"~\"\n\t| \"{\"\n\t| \"}\"\n\t| \":\"\n\t| \"<\"\n\t| \">\"\n\t| \"?\";\n\ntype SpecialKey =\n\t| \"escape\"\n\t| \"esc\"\n\t| \"enter\"\n\t| \"return\"\n\t| \"tab\"\n\t| \"space\"\n\t| \"backspace\"\n\t| \"delete\"\n\t| \"home\"\n\t| \"end\"\n\t| \"up\"\n\t| \"down\"\n\t| \"left\"\n\t| \"right\";\n\ntype BaseKey = Letter | SymbolKey | SpecialKey;\n\n/**\n * Union type of all valid key identifiers.\n * Provides autocomplete and catches typos at compile time.\n */\nexport type KeyId =\n\t| BaseKey\n\t| `ctrl+${BaseKey}`\n\t| `shift+${BaseKey}`\n\t| `alt+${BaseKey}`\n\t| `ctrl+shift+${BaseKey}`\n\t| `shift+ctrl+${BaseKey}`\n\t| `ctrl+alt+${BaseKey}`\n\t| `alt+ctrl+${BaseKey}`\n\t| `shift+alt+${BaseKey}`\n\t| `alt+shift+${BaseKey}`\n\t| `ctrl+shift+alt+${BaseKey}`\n\t| `ctrl+alt+shift+${BaseKey}`\n\t| `shift+ctrl+alt+${BaseKey}`\n\t| `shift+alt+ctrl+${BaseKey}`\n\t| `alt+ctrl+shift+${BaseKey}`\n\t| `alt+shift+ctrl+${BaseKey}`;\n\n/**\n * Helper object for creating typed key identifiers with autocomplete.\n *\n * Usage:\n * - Key.escape, Key.enter, Key.tab, etc. for special keys\n * - Key.backtick, Key.comma, Key.period, etc. for symbol keys\n * - Key.ctrl(\"c\"), Key.alt(\"x\") for single modifier\n * - Key.ctrlShift(\"p\"), Key.ctrlAlt(\"x\") for combined modifiers\n */\nexport const Key = {\n\t// Special keys\n\tescape: \"escape\" as const,\n\tesc: \"esc\" as const,\n\tenter: \"enter\" as const,\n\treturn: \"return\" as const,\n\ttab: \"tab\" as const,\n\tspace: \"space\" as const,\n\tbackspace: \"backspace\" as const,\n\tdelete: \"delete\" as const,\n\thome: \"home\" as const,\n\tend: \"end\" as const,\n\tup: \"up\" as const,\n\tdown: \"down\" as const,\n\tleft: \"left\" as const,\n\tright: \"right\" as const,\n\n\t// Symbol keys\n\tbacktick: \"`\" as const,\n\thyphen: \"-\" as const,\n\tequals: \"=\" as const,\n\tleftbracket: \"[\" as const,\n\trightbracket: \"]\" as const,\n\tbackslash: \"\\\\\" as const,\n\tsemicolon: \";\" as const,\n\tquote: \"'\" as const,\n\tcomma: \",\" as const,\n\tperiod: \".\" as const,\n\tslash: \"/\" as const,\n\texclamation: \"!\" as const,\n\tat: \"@\" as const,\n\thash: \"#\" as const,\n\tdollar: \"$\" as const,\n\tpercent: \"%\" as const,\n\tcaret: \"^\" as const,\n\tampersand: \"&\" as const,\n\tasterisk: \"*\" as const,\n\tleftparen: \"(\" as const,\n\trightparen: \")\" as const,\n\tunderscore: \"_\" as const,\n\tplus: \"+\" as const,\n\tpipe: \"|\" as const,\n\ttilde: \"~\" as const,\n\tleftbrace: \"{\" as const,\n\trightbrace: \"}\" as const,\n\tcolon: \":\" as const,\n\tlessthan: \"<\" as const,\n\tgreaterthan: \">\" as const,\n\tquestion: \"?\" as const,\n\n\t// Single modifiers\n\tctrl: <K extends BaseKey>(key: K): `ctrl+${K}` => `ctrl+${key}`,\n\tshift: <K extends BaseKey>(key: K): `shift+${K}` => `shift+${key}`,\n\talt: <K extends BaseKey>(key: K): `alt+${K}` => `alt+${key}`,\n\n\t// Combined modifiers\n\tctrlShift: <K extends BaseKey>(key: K): `ctrl+shift+${K}` => `ctrl+shift+${key}`,\n\tshiftCtrl: <K extends BaseKey>(key: K): `shift+ctrl+${K}` => `shift+ctrl+${key}`,\n\tctrlAlt: <K extends BaseKey>(key: K): `ctrl+alt+${K}` => `ctrl+alt+${key}`,\n\taltCtrl: <K extends BaseKey>(key: K): `alt+ctrl+${K}` => `alt+ctrl+${key}`,\n\tshiftAlt: <K extends BaseKey>(key: K): `shift+alt+${K}` => `shift+alt+${key}`,\n\taltShift: <K extends BaseKey>(key: K): `alt+shift+${K}` => `alt+shift+${key}`,\n\n\t// Triple modifiers\n\tctrlShiftAlt: <K extends BaseKey>(key: K): `ctrl+shift+alt+${K}` => `ctrl+shift+alt+${key}`,\n} as const;\n\n// =============================================================================\n// Constants\n// =============================================================================\n\nconst SYMBOL_KEYS = new Set([\n\t\"`\",\n\t\"-\",\n\t\"=\",\n\t\"[\",\n\t\"]\",\n\t\"\\\\\",\n\t\";\",\n\t\"'\",\n\t\",\",\n\t\".\",\n\t\"/\",\n\t\"!\",\n\t\"@\",\n\t\"#\",\n\t\"$\",\n\t\"%\",\n\t\"^\",\n\t\"&\",\n\t\"*\",\n\t\"(\",\n\t\")\",\n\t\"_\",\n\t\"+\",\n\t\"|\",\n\t\"~\",\n\t\"{\",\n\t\"}\",\n\t\":\",\n\t\"<\",\n\t\">\",\n\t\"?\",\n]);\n\nconst MODIFIERS = {\n\tshift: 1,\n\talt: 2,\n\tctrl: 4,\n} as const;\n\nconst LOCK_MASK = 64 + 128; // Caps Lock + Num Lock\n\nconst CODEPOINTS = {\n\tescape: 27,\n\ttab: 9,\n\tenter: 13,\n\tspace: 32,\n\tbackspace: 127,\n\tkpEnter: 57414, // Numpad Enter (Kitty protocol)\n} as const;\n\nconst ARROW_CODEPOINTS = {\n\tup: -1,\n\tdown: -2,\n\tright: -3,\n\tleft: -4,\n} as const;\n\nconst FUNCTIONAL_CODEPOINTS = {\n\tdelete: -10,\n\tinsert: -11,\n\tpageUp: -12,\n\tpageDown: -13,\n\thome: -14,\n\tend: -15,\n} as const;\n\n// =============================================================================\n// Kitty Protocol Parsing\n// =============================================================================\n\ninterface ParsedKittySequence {\n\tcodepoint: number;\n\tmodifier: number;\n}\n\nfunction parseKittySequence(data: string): ParsedKittySequence | null {\n\t// CSI u format: \\x1b[<num>u or \\x1b[<num>;<mod>u\n\tconst csiUMatch = data.match(/^\\x1b\\[(\\d+)(?:;(\\d+))?u$/);\n\tif (csiUMatch) {\n\t\tconst codepoint = parseInt(csiUMatch[1]!, 10);\n\t\tconst modValue = csiUMatch[2] ? parseInt(csiUMatch[2], 10) : 1;\n\t\treturn { codepoint, modifier: modValue - 1 };\n\t}\n\n\t// Arrow keys with modifier: \\x1b[1;<mod>A/B/C/D\n\tconst arrowMatch = data.match(/^\\x1b\\[1;(\\d+)([ABCD])$/);\n\tif (arrowMatch) {\n\t\tconst modValue = parseInt(arrowMatch[1]!, 10);\n\t\tconst arrowCodes: Record<string, number> = { A: -1, B: -2, C: -3, D: -4 };\n\t\treturn { codepoint: arrowCodes[arrowMatch[2]!]!, modifier: modValue - 1 };\n\t}\n\n\t// Functional keys: \\x1b[<num>~ or \\x1b[<num>;<mod>~\n\tconst funcMatch = data.match(/^\\x1b\\[(\\d+)(?:;(\\d+))?~$/);\n\tif (funcMatch) {\n\t\tconst keyNum = parseInt(funcMatch[1]!, 10);\n\t\tconst modValue = funcMatch[2] ? parseInt(funcMatch[2], 10) : 1;\n\t\tconst funcCodes: Record<number, number> = {\n\t\t\t2: FUNCTIONAL_CODEPOINTS.insert,\n\t\t\t3: FUNCTIONAL_CODEPOINTS.delete,\n\t\t\t5: FUNCTIONAL_CODEPOINTS.pageUp,\n\t\t\t6: FUNCTIONAL_CODEPOINTS.pageDown,\n\t\t\t7: FUNCTIONAL_CODEPOINTS.home,\n\t\t\t8: FUNCTIONAL_CODEPOINTS.end,\n\t\t};\n\t\tconst codepoint = funcCodes[keyNum];\n\t\tif (codepoint !== undefined) {\n\t\t\treturn { codepoint, modifier: modValue - 1 };\n\t\t}\n\t}\n\n\t// Home/End with modifier: \\x1b[1;<mod>H/F\n\tconst homeEndMatch = data.match(/^\\x1b\\[1;(\\d+)([HF])$/);\n\tif (homeEndMatch) {\n\t\tconst modValue = parseInt(homeEndMatch[1]!, 10);\n\t\tconst codepoint = homeEndMatch[2] === \"H\" ? FUNCTIONAL_CODEPOINTS.home : FUNCTIONAL_CODEPOINTS.end;\n\t\treturn { codepoint, modifier: modValue - 1 };\n\t}\n\n\treturn null;\n}\n\nfunction matchesKittySequence(data: string, expectedCodepoint: number, expectedModifier: number): boolean {\n\tconst parsed = parseKittySequence(data);\n\tif (!parsed) return false;\n\tconst actualMod = parsed.modifier & ~LOCK_MASK;\n\tconst expectedMod = expectedModifier & ~LOCK_MASK;\n\treturn parsed.codepoint === expectedCodepoint && actualMod === expectedMod;\n}\n\n// =============================================================================\n// Generic Key Matching\n// =============================================================================\n\nfunction rawCtrlChar(letter: string): string {\n\tconst code = letter.toLowerCase().charCodeAt(0) - 96;\n\treturn String.fromCharCode(code);\n}\n\nfunction parseKeyId(keyId: string): { key: string; ctrl: boolean; shift: boolean; alt: boolean } | null {\n\tconst parts = keyId.toLowerCase().split(\"+\");\n\tconst key = parts[parts.length - 1];\n\tif (!key) return null;\n\treturn {\n\t\tkey,\n\t\tctrl: parts.includes(\"ctrl\"),\n\t\tshift: parts.includes(\"shift\"),\n\t\talt: parts.includes(\"alt\"),\n\t};\n}\n\n/**\n * Match input data against a key identifier string.\n *\n * Supported key identifiers:\n * - Single keys: \"escape\", \"tab\", \"enter\", \"backspace\", \"delete\", \"home\", \"end\", \"space\"\n * - Arrow keys: \"up\", \"down\", \"left\", \"right\"\n * - Ctrl combinations: \"ctrl+c\", \"ctrl+z\", etc.\n * - Shift combinations: \"shift+tab\", \"shift+enter\"\n * - Alt combinations: \"alt+enter\", \"alt+backspace\"\n * - Combined modifiers: \"shift+ctrl+p\", \"ctrl+alt+x\"\n *\n * Use the Key helper for autocomplete: Key.ctrl(\"c\"), Key.escape, Key.ctrlShift(\"p\")\n *\n * @param data - Raw input data from terminal\n * @param keyId - Key identifier (e.g., \"ctrl+c\", \"escape\", Key.ctrl(\"c\"))\n */\nexport function matchesKey(data: string, keyId: KeyId): boolean {\n\tconst parsed = parseKeyId(keyId);\n\tif (!parsed) return false;\n\n\tconst { key, ctrl, shift, alt } = parsed;\n\tlet modifier = 0;\n\tif (shift) modifier |= MODIFIERS.shift;\n\tif (alt) modifier |= MODIFIERS.alt;\n\tif (ctrl) modifier |= MODIFIERS.ctrl;\n\n\tswitch (key) {\n\t\tcase \"escape\":\n\t\tcase \"esc\":\n\t\t\tif (modifier !== 0) return false;\n\t\t\treturn data === \"\\x1b\" || matchesKittySequence(data, CODEPOINTS.escape, 0);\n\n\t\tcase \"space\":\n\t\t\tif (modifier === 0) {\n\t\t\t\treturn data === \" \" || matchesKittySequence(data, CODEPOINTS.space, 0);\n\t\t\t}\n\t\t\treturn matchesKittySequence(data, CODEPOINTS.space, modifier);\n\n\t\tcase \"tab\":\n\t\t\tif (shift && !ctrl && !alt) {\n\t\t\t\treturn data === \"\\x1b[Z\" || matchesKittySequence(data, CODEPOINTS.tab, MODIFIERS.shift);\n\t\t\t}\n\t\t\tif (modifier === 0) {\n\t\t\t\treturn data === \"\\t\" || matchesKittySequence(data, CODEPOINTS.tab, 0);\n\t\t\t}\n\t\t\treturn matchesKittySequence(data, CODEPOINTS.tab, modifier);\n\n\t\tcase \"enter\":\n\t\tcase \"return\":\n\t\t\tif (shift && !ctrl && !alt) {\n\t\t\t\treturn (\n\t\t\t\t\tmatchesKittySequence(data, CODEPOINTS.enter, MODIFIERS.shift) ||\n\t\t\t\t\tmatchesKittySequence(data, CODEPOINTS.kpEnter, MODIFIERS.shift)\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (alt && !ctrl && !shift) {\n\t\t\t\treturn (\n\t\t\t\t\tdata === \"\\x1b\\r\" ||\n\t\t\t\t\tmatchesKittySequence(data, CODEPOINTS.enter, MODIFIERS.alt) ||\n\t\t\t\t\tmatchesKittySequence(data, CODEPOINTS.kpEnter, MODIFIERS.alt)\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (modifier === 0) {\n\t\t\t\treturn (\n\t\t\t\t\tdata === \"\\r\" ||\n\t\t\t\t\tdata === \"\\x1bOM\" || // SS3 M (numpad enter in some terminals)\n\t\t\t\t\tmatchesKittySequence(data, CODEPOINTS.enter, 0) ||\n\t\t\t\t\tmatchesKittySequence(data, CODEPOINTS.kpEnter, 0)\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn (\n\t\t\t\tmatchesKittySequence(data, CODEPOINTS.enter, modifier) ||\n\t\t\t\tmatchesKittySequence(data, CODEPOINTS.kpEnter, modifier)\n\t\t\t);\n\n\t\tcase \"backspace\":\n\t\t\tif (alt && !ctrl && !shift) {\n\t\t\t\treturn data === \"\\x1b\\x7f\" || matchesKittySequence(data, CODEPOINTS.backspace, MODIFIERS.alt);\n\t\t\t}\n\t\t\tif (modifier === 0) {\n\t\t\t\treturn data === \"\\x7f\" || data === \"\\x08\" || matchesKittySequence(data, CODEPOINTS.backspace, 0);\n\t\t\t}\n\t\t\treturn matchesKittySequence(data, CODEPOINTS.backspace, modifier);\n\n\t\tcase \"delete\":\n\t\t\tif (modifier === 0) {\n\t\t\t\treturn data === \"\\x1b[3~\" || matchesKittySequence(data, FUNCTIONAL_CODEPOINTS.delete, 0);\n\t\t\t}\n\t\t\treturn matchesKittySequence(data, FUNCTIONAL_CODEPOINTS.delete, modifier);\n\n\t\tcase \"home\":\n\t\t\tif (modifier === 0) {\n\t\t\t\treturn (\n\t\t\t\t\tdata === \"\\x1b[H\" ||\n\t\t\t\t\tdata === \"\\x1b[1~\" ||\n\t\t\t\t\tdata === \"\\x1b[7~\" ||\n\t\t\t\t\tmatchesKittySequence(data, FUNCTIONAL_CODEPOINTS.home, 0)\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn matchesKittySequence(data, FUNCTIONAL_CODEPOINTS.home, modifier);\n\n\t\tcase \"end\":\n\t\t\tif (modifier === 0) {\n\t\t\t\treturn (\n\t\t\t\t\tdata === \"\\x1b[F\" ||\n\t\t\t\t\tdata === \"\\x1b[4~\" ||\n\t\t\t\t\tdata === \"\\x1b[8~\" ||\n\t\t\t\t\tmatchesKittySequence(data, FUNCTIONAL_CODEPOINTS.end, 0)\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn matchesKittySequence(data, FUNCTIONAL_CODEPOINTS.end, modifier);\n\n\t\tcase \"up\":\n\t\t\tif (modifier === 0) {\n\t\t\t\treturn data === \"\\x1b[A\" || matchesKittySequence(data, ARROW_CODEPOINTS.up, 0);\n\t\t\t}\n\t\t\treturn matchesKittySequence(data, ARROW_CODEPOINTS.up, modifier);\n\n\t\tcase \"down\":\n\t\t\tif (modifier === 0) {\n\t\t\t\treturn data === \"\\x1b[B\" || matchesKittySequence(data, ARROW_CODEPOINTS.down, 0);\n\t\t\t}\n\t\t\treturn matchesKittySequence(data, ARROW_CODEPOINTS.down, modifier);\n\n\t\tcase \"left\":\n\t\t\tif (alt && !ctrl && !shift) {\n\t\t\t\treturn (\n\t\t\t\t\tdata === \"\\x1b[1;3D\" ||\n\t\t\t\t\tdata === \"\\x1bb\" ||\n\t\t\t\t\tmatchesKittySequence(data, ARROW_CODEPOINTS.left, MODIFIERS.alt)\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (ctrl && !alt && !shift) {\n\t\t\t\treturn data === \"\\x1b[1;5D\" || matchesKittySequence(data, ARROW_CODEPOINTS.left, MODIFIERS.ctrl);\n\t\t\t}\n\t\t\tif (modifier === 0) {\n\t\t\t\treturn data === \"\\x1b[D\" || matchesKittySequence(data, ARROW_CODEPOINTS.left, 0);\n\t\t\t}\n\t\t\treturn matchesKittySequence(data, ARROW_CODEPOINTS.left, modifier);\n\n\t\tcase \"right\":\n\t\t\tif (alt && !ctrl && !shift) {\n\t\t\t\treturn (\n\t\t\t\t\tdata === \"\\x1b[1;3C\" ||\n\t\t\t\t\tdata === \"\\x1bf\" ||\n\t\t\t\t\tmatchesKittySequence(data, ARROW_CODEPOINTS.right, MODIFIERS.alt)\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (ctrl && !alt && !shift) {\n\t\t\t\treturn data === \"\\x1b[1;5C\" || matchesKittySequence(data, ARROW_CODEPOINTS.right, MODIFIERS.ctrl);\n\t\t\t}\n\t\t\tif (modifier === 0) {\n\t\t\t\treturn data === \"\\x1b[C\" || matchesKittySequence(data, ARROW_CODEPOINTS.right, 0);\n\t\t\t}\n\t\t\treturn matchesKittySequence(data, ARROW_CODEPOINTS.right, modifier);\n\t}\n\n\t// Handle single letter keys (a-z) and some symbols\n\tif (key.length === 1 && ((key >= \"a\" && key <= \"z\") || SYMBOL_KEYS.has(key))) {\n\t\tconst codepoint = key.charCodeAt(0);\n\n\t\tif (ctrl && !shift && !alt) {\n\t\t\tconst raw = rawCtrlChar(key);\n\t\t\tif (data === raw) return true;\n\t\t\tif (data.length > 0 && data.charCodeAt(0) === raw.charCodeAt(0)) return true;\n\t\t\treturn matchesKittySequence(data, codepoint, MODIFIERS.ctrl);\n\t\t}\n\n\t\tif (ctrl && shift && !alt) {\n\t\t\treturn matchesKittySequence(data, codepoint, MODIFIERS.shift + MODIFIERS.ctrl);\n\t\t}\n\n\t\tif (shift && !ctrl && !alt) {\n\t\t\t// Legacy: shift+letter produces uppercase\n\t\t\tif (data === key.toUpperCase()) return true;\n\t\t\treturn matchesKittySequence(data, codepoint, MODIFIERS.shift);\n\t\t}\n\n\t\tif (modifier !== 0) {\n\t\t\treturn matchesKittySequence(data, codepoint, modifier);\n\t\t}\n\n\t\treturn data === key;\n\t}\n\n\treturn false;\n}\n\n/**\n * Parse input data and return the key identifier if recognized.\n *\n * @param data - Raw input data from terminal\n * @returns Key identifier string (e.g., \"ctrl+c\") or undefined\n */\nexport function parseKey(data: string): string | undefined {\n\tconst kitty = parseKittySequence(data);\n\tif (kitty) {\n\t\tconst { codepoint, modifier } = kitty;\n\t\tconst mods: string[] = [];\n\t\tconst effectiveMod = modifier & ~LOCK_MASK;\n\t\tif (effectiveMod & MODIFIERS.shift) mods.push(\"shift\");\n\t\tif (effectiveMod & MODIFIERS.ctrl) mods.push(\"ctrl\");\n\t\tif (effectiveMod & MODIFIERS.alt) mods.push(\"alt\");\n\n\t\tlet keyName: string | undefined;\n\t\tif (codepoint === CODEPOINTS.escape) keyName = \"escape\";\n\t\telse if (codepoint === CODEPOINTS.tab) keyName = \"tab\";\n\t\telse if (codepoint === CODEPOINTS.enter || codepoint === CODEPOINTS.kpEnter) keyName = \"enter\";\n\t\telse if (codepoint === CODEPOINTS.space) keyName = \"space\";\n\t\telse if (codepoint === CODEPOINTS.backspace) keyName = \"backspace\";\n\t\telse if (codepoint === FUNCTIONAL_CODEPOINTS.delete) keyName = \"delete\";\n\t\telse if (codepoint === FUNCTIONAL_CODEPOINTS.home) keyName = \"home\";\n\t\telse if (codepoint === FUNCTIONAL_CODEPOINTS.end) keyName = \"end\";\n\t\telse if (codepoint === ARROW_CODEPOINTS.up) keyName = \"up\";\n\t\telse if (codepoint === ARROW_CODEPOINTS.down) keyName = \"down\";\n\t\telse if (codepoint === ARROW_CODEPOINTS.left) keyName = \"left\";\n\t\telse if (codepoint === ARROW_CODEPOINTS.right) keyName = \"right\";\n\t\telse if (codepoint >= 97 && codepoint <= 122) keyName = String.fromCharCode(codepoint);\n\t\telse if (SYMBOL_KEYS.has(String.fromCharCode(codepoint))) keyName = String.fromCharCode(codepoint);\n\n\t\tif (keyName) {\n\t\t\treturn mods.length > 0 ? `${mods.join(\"+\")}+${keyName}` : keyName;\n\t\t}\n\t}\n\n\t// Legacy sequences\n\tif (data === \"\\x1b\") return \"escape\";\n\tif (data === \"\\t\") return \"tab\";\n\tif (data === \"\\r\" || data === \"\\x1bOM\") return \"enter\";\n\tif (data === \" \") return \"space\";\n\tif (data === \"\\x7f\" || data === \"\\x08\") return \"backspace\";\n\tif (data === \"\\x1b[Z\") return \"shift+tab\";\n\tif (data === \"\\x1b\\r\") return \"alt+enter\";\n\tif (data === \"\\x1b\\x7f\") return \"alt+backspace\";\n\tif (data === \"\\x1b[A\") return \"up\";\n\tif (data === \"\\x1b[B\") return \"down\";\n\tif (data === \"\\x1b[C\") return \"right\";\n\tif (data === \"\\x1b[D\") return \"left\";\n\tif (data === \"\\x1b[H\") return \"home\";\n\tif (data === \"\\x1b[F\") return \"end\";\n\tif (data === \"\\x1b[3~\") return \"delete\";\n\n\t// Raw Ctrl+letter\n\tif (data.length === 1) {\n\t\tconst code = data.charCodeAt(0);\n\t\tif (code >= 1 && code <= 26) {\n\t\t\treturn `ctrl+${String.fromCharCode(code + 96)}`;\n\t\t}\n\t\tif (code >= 32 && code <= 126) {\n\t\t\treturn data;\n\t\t}\n\t}\n\n\treturn undefined;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"keys.js","sourceRoot":"","sources":["../src/keys.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Keyboard input handling for terminal applications.\n *\n * Supports both legacy terminal sequences and Kitty keyboard protocol.\n * See: https://sw.kovidgoyal.net/kitty/keyboard-protocol/\n *\n * Symbol keys are also supported, however some ctrl+symbol combos\n * overlap with ASCII codes, e.g. ctrl+[ = ESC.\n * See: https://sw.kovidgoyal.net/kitty/keyboard-protocol/#legacy-ctrl-mapping-of-ascii-keys\n * Those can still be * used for ctrl+shift combos\n *\n * API:\n * - matchesKey(data, keyId) - Check if input matches a key identifier\n * - parseKey(data) - Parse input and return the key identifier\n * - Key - Helper object for creating typed key identifiers\n * - setKittyProtocolActive(active) - Set global Kitty protocol state\n * - isKittyProtocolActive() - Query global Kitty protocol state\n */\n\n// =============================================================================\n// Global Kitty Protocol State\n// =============================================================================\n\nlet _kittyProtocolActive = false;\n\n/**\n * Set the global Kitty keyboard protocol state.\n * Called by ProcessTerminal after detecting protocol support.\n */\nexport function setKittyProtocolActive(active: boolean): void {\n\t_kittyProtocolActive = active;\n}\n\n/**\n * Query whether Kitty keyboard protocol is currently active.\n */\nexport function isKittyProtocolActive(): boolean {\n\treturn _kittyProtocolActive;\n}\n\n// =============================================================================\n// Type-Safe Key Identifiers\n// =============================================================================\n\ntype Letter =\n\t| \"a\"\n\t| \"b\"\n\t| \"c\"\n\t| \"d\"\n\t| \"e\"\n\t| \"f\"\n\t| \"g\"\n\t| \"h\"\n\t| \"i\"\n\t| \"j\"\n\t| \"k\"\n\t| \"l\"\n\t| \"m\"\n\t| \"n\"\n\t| \"o\"\n\t| \"p\"\n\t| \"q\"\n\t| \"r\"\n\t| \"s\"\n\t| \"t\"\n\t| \"u\"\n\t| \"v\"\n\t| \"w\"\n\t| \"x\"\n\t| \"y\"\n\t| \"z\";\n\ntype SymbolKey =\n\t| \"`\"\n\t| \"-\"\n\t| \"=\"\n\t| \"[\"\n\t| \"]\"\n\t| \"\\\\\"\n\t| \";\"\n\t| \"'\"\n\t| \",\"\n\t| \".\"\n\t| \"/\"\n\t| \"!\"\n\t| \"@\"\n\t| \"#\"\n\t| \"$\"\n\t| \"%\"\n\t| \"^\"\n\t| \"&\"\n\t| \"*\"\n\t| \"(\"\n\t| \")\"\n\t| \"_\"\n\t| \"+\"\n\t| \"|\"\n\t| \"~\"\n\t| \"{\"\n\t| \"}\"\n\t| \":\"\n\t| \"<\"\n\t| \">\"\n\t| \"?\";\n\ntype SpecialKey =\n\t| \"escape\"\n\t| \"esc\"\n\t| \"enter\"\n\t| \"return\"\n\t| \"tab\"\n\t| \"space\"\n\t| \"backspace\"\n\t| \"delete\"\n\t| \"home\"\n\t| \"end\"\n\t| \"up\"\n\t| \"down\"\n\t| \"left\"\n\t| \"right\";\n\ntype BaseKey = Letter | SymbolKey | SpecialKey;\n\n/**\n * Union type of all valid key identifiers.\n * Provides autocomplete and catches typos at compile time.\n */\nexport type KeyId =\n\t| BaseKey\n\t| `ctrl+${BaseKey}`\n\t| `shift+${BaseKey}`\n\t| `alt+${BaseKey}`\n\t| `ctrl+shift+${BaseKey}`\n\t| `shift+ctrl+${BaseKey}`\n\t| `ctrl+alt+${BaseKey}`\n\t| `alt+ctrl+${BaseKey}`\n\t| `shift+alt+${BaseKey}`\n\t| `alt+shift+${BaseKey}`\n\t| `ctrl+shift+alt+${BaseKey}`\n\t| `ctrl+alt+shift+${BaseKey}`\n\t| `shift+ctrl+alt+${BaseKey}`\n\t| `shift+alt+ctrl+${BaseKey}`\n\t| `alt+ctrl+shift+${BaseKey}`\n\t| `alt+shift+ctrl+${BaseKey}`;\n\n/**\n * Helper object for creating typed key identifiers with autocomplete.\n *\n * Usage:\n * - Key.escape, Key.enter, Key.tab, etc. for special keys\n * - Key.backtick, Key.comma, Key.period, etc. for symbol keys\n * - Key.ctrl(\"c\"), Key.alt(\"x\") for single modifier\n * - Key.ctrlShift(\"p\"), Key.ctrlAlt(\"x\") for combined modifiers\n */\nexport const Key = {\n\t// Special keys\n\tescape: \"escape\" as const,\n\tesc: \"esc\" as const,\n\tenter: \"enter\" as const,\n\treturn: \"return\" as const,\n\ttab: \"tab\" as const,\n\tspace: \"space\" as const,\n\tbackspace: \"backspace\" as const,\n\tdelete: \"delete\" as const,\n\thome: \"home\" as const,\n\tend: \"end\" as const,\n\tup: \"up\" as const,\n\tdown: \"down\" as const,\n\tleft: \"left\" as const,\n\tright: \"right\" as const,\n\n\t// Symbol keys\n\tbacktick: \"`\" as const,\n\thyphen: \"-\" as const,\n\tequals: \"=\" as const,\n\tleftbracket: \"[\" as const,\n\trightbracket: \"]\" as const,\n\tbackslash: \"\\\\\" as const,\n\tsemicolon: \";\" as const,\n\tquote: \"'\" as const,\n\tcomma: \",\" as const,\n\tperiod: \".\" as const,\n\tslash: \"/\" as const,\n\texclamation: \"!\" as const,\n\tat: \"@\" as const,\n\thash: \"#\" as const,\n\tdollar: \"$\" as const,\n\tpercent: \"%\" as const,\n\tcaret: \"^\" as const,\n\tampersand: \"&\" as const,\n\tasterisk: \"*\" as const,\n\tleftparen: \"(\" as const,\n\trightparen: \")\" as const,\n\tunderscore: \"_\" as const,\n\tplus: \"+\" as const,\n\tpipe: \"|\" as const,\n\ttilde: \"~\" as const,\n\tleftbrace: \"{\" as const,\n\trightbrace: \"}\" as const,\n\tcolon: \":\" as const,\n\tlessthan: \"<\" as const,\n\tgreaterthan: \">\" as const,\n\tquestion: \"?\" as const,\n\n\t// Single modifiers\n\tctrl: <K extends BaseKey>(key: K): `ctrl+${K}` => `ctrl+${key}`,\n\tshift: <K extends BaseKey>(key: K): `shift+${K}` => `shift+${key}`,\n\talt: <K extends BaseKey>(key: K): `alt+${K}` => `alt+${key}`,\n\n\t// Combined modifiers\n\tctrlShift: <K extends BaseKey>(key: K): `ctrl+shift+${K}` => `ctrl+shift+${key}`,\n\tshiftCtrl: <K extends BaseKey>(key: K): `shift+ctrl+${K}` => `shift+ctrl+${key}`,\n\tctrlAlt: <K extends BaseKey>(key: K): `ctrl+alt+${K}` => `ctrl+alt+${key}`,\n\taltCtrl: <K extends BaseKey>(key: K): `alt+ctrl+${K}` => `alt+ctrl+${key}`,\n\tshiftAlt: <K extends BaseKey>(key: K): `shift+alt+${K}` => `shift+alt+${key}`,\n\taltShift: <K extends BaseKey>(key: K): `alt+shift+${K}` => `alt+shift+${key}`,\n\n\t// Triple modifiers\n\tctrlShiftAlt: <K extends BaseKey>(key: K): `ctrl+shift+alt+${K}` => `ctrl+shift+alt+${key}`,\n} as const;\n\n// =============================================================================\n// Constants\n// =============================================================================\n\nconst SYMBOL_KEYS = new Set([\n\t\"`\",\n\t\"-\",\n\t\"=\",\n\t\"[\",\n\t\"]\",\n\t\"\\\\\",\n\t\";\",\n\t\"'\",\n\t\",\",\n\t\".\",\n\t\"/\",\n\t\"!\",\n\t\"@\",\n\t\"#\",\n\t\"$\",\n\t\"%\",\n\t\"^\",\n\t\"&\",\n\t\"*\",\n\t\"(\",\n\t\")\",\n\t\"_\",\n\t\"+\",\n\t\"|\",\n\t\"~\",\n\t\"{\",\n\t\"}\",\n\t\":\",\n\t\"<\",\n\t\">\",\n\t\"?\",\n]);\n\nconst MODIFIERS = {\n\tshift: 1,\n\talt: 2,\n\tctrl: 4,\n} as const;\n\nconst LOCK_MASK = 64 + 128; // Caps Lock + Num Lock\n\nconst CODEPOINTS = {\n\tescape: 27,\n\ttab: 9,\n\tenter: 13,\n\tspace: 32,\n\tbackspace: 127,\n\tkpEnter: 57414, // Numpad Enter (Kitty protocol)\n} as const;\n\nconst ARROW_CODEPOINTS = {\n\tup: -1,\n\tdown: -2,\n\tright: -3,\n\tleft: -4,\n} as const;\n\nconst FUNCTIONAL_CODEPOINTS = {\n\tdelete: -10,\n\tinsert: -11,\n\tpageUp: -12,\n\tpageDown: -13,\n\thome: -14,\n\tend: -15,\n} as const;\n\n// =============================================================================\n// Kitty Protocol Parsing\n// =============================================================================\n\ninterface ParsedKittySequence {\n\tcodepoint: number;\n\tmodifier: number;\n}\n\nfunction parseKittySequence(data: string): ParsedKittySequence | null {\n\t// CSI u format: \\x1b[<num>u or \\x1b[<num>;<mod>u\n\tconst csiUMatch = data.match(/^\\x1b\\[(\\d+)(?:;(\\d+))?u$/);\n\tif (csiUMatch) {\n\t\tconst codepoint = parseInt(csiUMatch[1]!, 10);\n\t\tconst modValue = csiUMatch[2] ? parseInt(csiUMatch[2], 10) : 1;\n\t\treturn { codepoint, modifier: modValue - 1 };\n\t}\n\n\t// Arrow keys with modifier: \\x1b[1;<mod>A/B/C/D\n\tconst arrowMatch = data.match(/^\\x1b\\[1;(\\d+)([ABCD])$/);\n\tif (arrowMatch) {\n\t\tconst modValue = parseInt(arrowMatch[1]!, 10);\n\t\tconst arrowCodes: Record<string, number> = { A: -1, B: -2, C: -3, D: -4 };\n\t\treturn { codepoint: arrowCodes[arrowMatch[2]!]!, modifier: modValue - 1 };\n\t}\n\n\t// Functional keys: \\x1b[<num>~ or \\x1b[<num>;<mod>~\n\tconst funcMatch = data.match(/^\\x1b\\[(\\d+)(?:;(\\d+))?~$/);\n\tif (funcMatch) {\n\t\tconst keyNum = parseInt(funcMatch[1]!, 10);\n\t\tconst modValue = funcMatch[2] ? parseInt(funcMatch[2], 10) : 1;\n\t\tconst funcCodes: Record<number, number> = {\n\t\t\t2: FUNCTIONAL_CODEPOINTS.insert,\n\t\t\t3: FUNCTIONAL_CODEPOINTS.delete,\n\t\t\t5: FUNCTIONAL_CODEPOINTS.pageUp,\n\t\t\t6: FUNCTIONAL_CODEPOINTS.pageDown,\n\t\t\t7: FUNCTIONAL_CODEPOINTS.home,\n\t\t\t8: FUNCTIONAL_CODEPOINTS.end,\n\t\t};\n\t\tconst codepoint = funcCodes[keyNum];\n\t\tif (codepoint !== undefined) {\n\t\t\treturn { codepoint, modifier: modValue - 1 };\n\t\t}\n\t}\n\n\t// Home/End with modifier: \\x1b[1;<mod>H/F\n\tconst homeEndMatch = data.match(/^\\x1b\\[1;(\\d+)([HF])$/);\n\tif (homeEndMatch) {\n\t\tconst modValue = parseInt(homeEndMatch[1]!, 10);\n\t\tconst codepoint = homeEndMatch[2] === \"H\" ? FUNCTIONAL_CODEPOINTS.home : FUNCTIONAL_CODEPOINTS.end;\n\t\treturn { codepoint, modifier: modValue - 1 };\n\t}\n\n\treturn null;\n}\n\nfunction matchesKittySequence(data: string, expectedCodepoint: number, expectedModifier: number): boolean {\n\tconst parsed = parseKittySequence(data);\n\tif (!parsed) return false;\n\tconst actualMod = parsed.modifier & ~LOCK_MASK;\n\tconst expectedMod = expectedModifier & ~LOCK_MASK;\n\treturn parsed.codepoint === expectedCodepoint && actualMod === expectedMod;\n}\n\n// =============================================================================\n// Generic Key Matching\n// =============================================================================\n\nfunction rawCtrlChar(letter: string): string {\n\tconst code = letter.toLowerCase().charCodeAt(0) - 96;\n\treturn String.fromCharCode(code);\n}\n\nfunction parseKeyId(keyId: string): { key: string; ctrl: boolean; shift: boolean; alt: boolean } | null {\n\tconst parts = keyId.toLowerCase().split(\"+\");\n\tconst key = parts[parts.length - 1];\n\tif (!key) return null;\n\treturn {\n\t\tkey,\n\t\tctrl: parts.includes(\"ctrl\"),\n\t\tshift: parts.includes(\"shift\"),\n\t\talt: parts.includes(\"alt\"),\n\t};\n}\n\n/**\n * Match input data against a key identifier string.\n *\n * Supported key identifiers:\n * - Single keys: \"escape\", \"tab\", \"enter\", \"backspace\", \"delete\", \"home\", \"end\", \"space\"\n * - Arrow keys: \"up\", \"down\", \"left\", \"right\"\n * - Ctrl combinations: \"ctrl+c\", \"ctrl+z\", etc.\n * - Shift combinations: \"shift+tab\", \"shift+enter\"\n * - Alt combinations: \"alt+enter\", \"alt+backspace\"\n * - Combined modifiers: \"shift+ctrl+p\", \"ctrl+alt+x\"\n *\n * Use the Key helper for autocomplete: Key.ctrl(\"c\"), Key.escape, Key.ctrlShift(\"p\")\n *\n * @param data - Raw input data from terminal\n * @param keyId - Key identifier (e.g., \"ctrl+c\", \"escape\", Key.ctrl(\"c\"))\n */\nexport function matchesKey(data: string, keyId: KeyId): boolean {\n\tconst parsed = parseKeyId(keyId);\n\tif (!parsed) return false;\n\n\tconst { key, ctrl, shift, alt } = parsed;\n\tlet modifier = 0;\n\tif (shift) modifier |= MODIFIERS.shift;\n\tif (alt) modifier |= MODIFIERS.alt;\n\tif (ctrl) modifier |= MODIFIERS.ctrl;\n\n\tswitch (key) {\n\t\tcase \"escape\":\n\t\tcase \"esc\":\n\t\t\tif (modifier !== 0) return false;\n\t\t\treturn data === \"\\x1b\" || matchesKittySequence(data, CODEPOINTS.escape, 0);\n\n\t\tcase \"space\":\n\t\t\tif (modifier === 0) {\n\t\t\t\treturn data === \" \" || matchesKittySequence(data, CODEPOINTS.space, 0);\n\t\t\t}\n\t\t\treturn matchesKittySequence(data, CODEPOINTS.space, modifier);\n\n\t\tcase \"tab\":\n\t\t\tif (shift && !ctrl && !alt) {\n\t\t\t\treturn data === \"\\x1b[Z\" || matchesKittySequence(data, CODEPOINTS.tab, MODIFIERS.shift);\n\t\t\t}\n\t\t\tif (modifier === 0) {\n\t\t\t\treturn data === \"\\t\" || matchesKittySequence(data, CODEPOINTS.tab, 0);\n\t\t\t}\n\t\t\treturn matchesKittySequence(data, CODEPOINTS.tab, modifier);\n\n\t\tcase \"enter\":\n\t\tcase \"return\":\n\t\t\tif (shift && !ctrl && !alt) {\n\t\t\t\t// CSI u sequences (standard Kitty protocol)\n\t\t\t\tif (\n\t\t\t\t\tmatchesKittySequence(data, CODEPOINTS.enter, MODIFIERS.shift) ||\n\t\t\t\t\tmatchesKittySequence(data, CODEPOINTS.kpEnter, MODIFIERS.shift)\n\t\t\t\t) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t\t// When Kitty protocol is active, legacy sequences are custom terminal mappings\n\t\t\t\t// \\x1b\\r = Kitty's \"map shift+enter send_text all \\e\\r\"\n\t\t\t\t// \\n = Ghostty's \"keybind = shift+enter=text:\\n\"\n\t\t\t\tif (_kittyProtocolActive) {\n\t\t\t\t\treturn data === \"\\x1b\\r\" || data === \"\\n\";\n\t\t\t\t}\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tif (alt && !ctrl && !shift) {\n\t\t\t\t// CSI u sequences (standard Kitty protocol)\n\t\t\t\tif (\n\t\t\t\t\tmatchesKittySequence(data, CODEPOINTS.enter, MODIFIERS.alt) ||\n\t\t\t\t\tmatchesKittySequence(data, CODEPOINTS.kpEnter, MODIFIERS.alt)\n\t\t\t\t) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t\t// \\x1b\\r is alt+enter only in legacy mode (no Kitty protocol)\n\t\t\t\t// When Kitty protocol is active, alt+enter comes as CSI u sequence\n\t\t\t\tif (!_kittyProtocolActive) {\n\t\t\t\t\treturn data === \"\\x1b\\r\";\n\t\t\t\t}\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tif (modifier === 0) {\n\t\t\t\treturn (\n\t\t\t\t\tdata === \"\\r\" ||\n\t\t\t\t\tdata === \"\\x1bOM\" || // SS3 M (numpad enter in some terminals)\n\t\t\t\t\tmatchesKittySequence(data, CODEPOINTS.enter, 0) ||\n\t\t\t\t\tmatchesKittySequence(data, CODEPOINTS.kpEnter, 0)\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn (\n\t\t\t\tmatchesKittySequence(data, CODEPOINTS.enter, modifier) ||\n\t\t\t\tmatchesKittySequence(data, CODEPOINTS.kpEnter, modifier)\n\t\t\t);\n\n\t\tcase \"backspace\":\n\t\t\tif (alt && !ctrl && !shift) {\n\t\t\t\treturn data === \"\\x1b\\x7f\" || matchesKittySequence(data, CODEPOINTS.backspace, MODIFIERS.alt);\n\t\t\t}\n\t\t\tif (modifier === 0) {\n\t\t\t\treturn data === \"\\x7f\" || data === \"\\x08\" || matchesKittySequence(data, CODEPOINTS.backspace, 0);\n\t\t\t}\n\t\t\treturn matchesKittySequence(data, CODEPOINTS.backspace, modifier);\n\n\t\tcase \"delete\":\n\t\t\tif (modifier === 0) {\n\t\t\t\treturn data === \"\\x1b[3~\" || matchesKittySequence(data, FUNCTIONAL_CODEPOINTS.delete, 0);\n\t\t\t}\n\t\t\treturn matchesKittySequence(data, FUNCTIONAL_CODEPOINTS.delete, modifier);\n\n\t\tcase \"home\":\n\t\t\tif (modifier === 0) {\n\t\t\t\treturn (\n\t\t\t\t\tdata === \"\\x1b[H\" ||\n\t\t\t\t\tdata === \"\\x1b[1~\" ||\n\t\t\t\t\tdata === \"\\x1b[7~\" ||\n\t\t\t\t\tmatchesKittySequence(data, FUNCTIONAL_CODEPOINTS.home, 0)\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn matchesKittySequence(data, FUNCTIONAL_CODEPOINTS.home, modifier);\n\n\t\tcase \"end\":\n\t\t\tif (modifier === 0) {\n\t\t\t\treturn (\n\t\t\t\t\tdata === \"\\x1b[F\" ||\n\t\t\t\t\tdata === \"\\x1b[4~\" ||\n\t\t\t\t\tdata === \"\\x1b[8~\" ||\n\t\t\t\t\tmatchesKittySequence(data, FUNCTIONAL_CODEPOINTS.end, 0)\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn matchesKittySequence(data, FUNCTIONAL_CODEPOINTS.end, modifier);\n\n\t\tcase \"up\":\n\t\t\tif (modifier === 0) {\n\t\t\t\treturn data === \"\\x1b[A\" || matchesKittySequence(data, ARROW_CODEPOINTS.up, 0);\n\t\t\t}\n\t\t\treturn matchesKittySequence(data, ARROW_CODEPOINTS.up, modifier);\n\n\t\tcase \"down\":\n\t\t\tif (modifier === 0) {\n\t\t\t\treturn data === \"\\x1b[B\" || matchesKittySequence(data, ARROW_CODEPOINTS.down, 0);\n\t\t\t}\n\t\t\treturn matchesKittySequence(data, ARROW_CODEPOINTS.down, modifier);\n\n\t\tcase \"left\":\n\t\t\tif (alt && !ctrl && !shift) {\n\t\t\t\treturn (\n\t\t\t\t\tdata === \"\\x1b[1;3D\" ||\n\t\t\t\t\tdata === \"\\x1bb\" ||\n\t\t\t\t\tmatchesKittySequence(data, ARROW_CODEPOINTS.left, MODIFIERS.alt)\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (ctrl && !alt && !shift) {\n\t\t\t\treturn data === \"\\x1b[1;5D\" || matchesKittySequence(data, ARROW_CODEPOINTS.left, MODIFIERS.ctrl);\n\t\t\t}\n\t\t\tif (modifier === 0) {\n\t\t\t\treturn data === \"\\x1b[D\" || matchesKittySequence(data, ARROW_CODEPOINTS.left, 0);\n\t\t\t}\n\t\t\treturn matchesKittySequence(data, ARROW_CODEPOINTS.left, modifier);\n\n\t\tcase \"right\":\n\t\t\tif (alt && !ctrl && !shift) {\n\t\t\t\treturn (\n\t\t\t\t\tdata === \"\\x1b[1;3C\" ||\n\t\t\t\t\tdata === \"\\x1bf\" ||\n\t\t\t\t\tmatchesKittySequence(data, ARROW_CODEPOINTS.right, MODIFIERS.alt)\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (ctrl && !alt && !shift) {\n\t\t\t\treturn data === \"\\x1b[1;5C\" || matchesKittySequence(data, ARROW_CODEPOINTS.right, MODIFIERS.ctrl);\n\t\t\t}\n\t\t\tif (modifier === 0) {\n\t\t\t\treturn data === \"\\x1b[C\" || matchesKittySequence(data, ARROW_CODEPOINTS.right, 0);\n\t\t\t}\n\t\t\treturn matchesKittySequence(data, ARROW_CODEPOINTS.right, modifier);\n\t}\n\n\t// Handle single letter keys (a-z) and some symbols\n\tif (key.length === 1 && ((key >= \"a\" && key <= \"z\") || SYMBOL_KEYS.has(key))) {\n\t\tconst codepoint = key.charCodeAt(0);\n\n\t\tif (ctrl && !shift && !alt) {\n\t\t\tconst raw = rawCtrlChar(key);\n\t\t\tif (data === raw) return true;\n\t\t\tif (data.length > 0 && data.charCodeAt(0) === raw.charCodeAt(0)) return true;\n\t\t\treturn matchesKittySequence(data, codepoint, MODIFIERS.ctrl);\n\t\t}\n\n\t\tif (ctrl && shift && !alt) {\n\t\t\treturn matchesKittySequence(data, codepoint, MODIFIERS.shift + MODIFIERS.ctrl);\n\t\t}\n\n\t\tif (shift && !ctrl && !alt) {\n\t\t\t// Legacy: shift+letter produces uppercase\n\t\t\tif (data === key.toUpperCase()) return true;\n\t\t\treturn matchesKittySequence(data, codepoint, MODIFIERS.shift);\n\t\t}\n\n\t\tif (modifier !== 0) {\n\t\t\treturn matchesKittySequence(data, codepoint, modifier);\n\t\t}\n\n\t\treturn data === key;\n\t}\n\n\treturn false;\n}\n\n/**\n * Parse input data and return the key identifier if recognized.\n *\n * @param data - Raw input data from terminal\n * @returns Key identifier string (e.g., \"ctrl+c\") or undefined\n */\nexport function parseKey(data: string): string | undefined {\n\tconst kitty = parseKittySequence(data);\n\tif (kitty) {\n\t\tconst { codepoint, modifier } = kitty;\n\t\tconst mods: string[] = [];\n\t\tconst effectiveMod = modifier & ~LOCK_MASK;\n\t\tif (effectiveMod & MODIFIERS.shift) mods.push(\"shift\");\n\t\tif (effectiveMod & MODIFIERS.ctrl) mods.push(\"ctrl\");\n\t\tif (effectiveMod & MODIFIERS.alt) mods.push(\"alt\");\n\n\t\tlet keyName: string | undefined;\n\t\tif (codepoint === CODEPOINTS.escape) keyName = \"escape\";\n\t\telse if (codepoint === CODEPOINTS.tab) keyName = \"tab\";\n\t\telse if (codepoint === CODEPOINTS.enter || codepoint === CODEPOINTS.kpEnter) keyName = \"enter\";\n\t\telse if (codepoint === CODEPOINTS.space) keyName = \"space\";\n\t\telse if (codepoint === CODEPOINTS.backspace) keyName = \"backspace\";\n\t\telse if (codepoint === FUNCTIONAL_CODEPOINTS.delete) keyName = \"delete\";\n\t\telse if (codepoint === FUNCTIONAL_CODEPOINTS.home) keyName = \"home\";\n\t\telse if (codepoint === FUNCTIONAL_CODEPOINTS.end) keyName = \"end\";\n\t\telse if (codepoint === ARROW_CODEPOINTS.up) keyName = \"up\";\n\t\telse if (codepoint === ARROW_CODEPOINTS.down) keyName = \"down\";\n\t\telse if (codepoint === ARROW_CODEPOINTS.left) keyName = \"left\";\n\t\telse if (codepoint === ARROW_CODEPOINTS.right) keyName = \"right\";\n\t\telse if (codepoint >= 97 && codepoint <= 122) keyName = String.fromCharCode(codepoint);\n\t\telse if (SYMBOL_KEYS.has(String.fromCharCode(codepoint))) keyName = String.fromCharCode(codepoint);\n\n\t\tif (keyName) {\n\t\t\treturn mods.length > 0 ? `${mods.join(\"+\")}+${keyName}` : keyName;\n\t\t}\n\t}\n\n\t// Mode-aware legacy sequences\n\t// When Kitty protocol is active, ambiguous sequences are interpreted as custom terminal mappings:\n\t// - \\x1b\\r = shift+enter (Kitty mapping), not alt+enter\n\t// - \\n = shift+enter (Ghostty mapping)\n\tif (_kittyProtocolActive) {\n\t\tif (data === \"\\x1b\\r\" || data === \"\\n\") return \"shift+enter\";\n\t}\n\n\t// Legacy sequences (used when Kitty protocol is not active, or for unambiguous sequences)\n\tif (data === \"\\x1b\") return \"escape\";\n\tif (data === \"\\t\") return \"tab\";\n\tif (data === \"\\r\" || data === \"\\x1bOM\") return \"enter\";\n\tif (data === \" \") return \"space\";\n\tif (data === \"\\x7f\" || data === \"\\x08\") return \"backspace\";\n\tif (data === \"\\x1b[Z\") return \"shift+tab\";\n\tif (!_kittyProtocolActive && data === \"\\x1b\\r\") return \"alt+enter\";\n\tif (data === \"\\x1b\\x7f\") return \"alt+backspace\";\n\tif (data === \"\\x1b[A\") return \"up\";\n\tif (data === \"\\x1b[B\") return \"down\";\n\tif (data === \"\\x1b[C\") return \"right\";\n\tif (data === \"\\x1b[D\") return \"left\";\n\tif (data === \"\\x1b[H\") return \"home\";\n\tif (data === \"\\x1b[F\") return \"end\";\n\tif (data === \"\\x1b[3~\") return \"delete\";\n\n\t// Raw Ctrl+letter\n\tif (data.length === 1) {\n\t\tconst code = data.charCodeAt(0);\n\t\tif (code >= 1 && code <= 26) {\n\t\t\treturn `ctrl+${String.fromCharCode(code + 96)}`;\n\t\t}\n\t\tif (code >= 32 && code <= 126) {\n\t\t\treturn data;\n\t\t}\n\t}\n\n\treturn undefined;\n}\n"]}
|
package/dist/terminal.d.ts
CHANGED
|
@@ -7,6 +7,7 @@ export interface Terminal {
|
|
|
7
7
|
write(data: string): void;
|
|
8
8
|
get columns(): number;
|
|
9
9
|
get rows(): number;
|
|
10
|
+
get kittyProtocolActive(): boolean;
|
|
10
11
|
moveBy(lines: number): void;
|
|
11
12
|
hideCursor(): void;
|
|
12
13
|
showCursor(): void;
|
|
@@ -22,7 +23,18 @@ export declare class ProcessTerminal implements Terminal {
|
|
|
22
23
|
private wasRaw;
|
|
23
24
|
private inputHandler?;
|
|
24
25
|
private resizeHandler?;
|
|
26
|
+
private _kittyProtocolActive;
|
|
27
|
+
get kittyProtocolActive(): boolean;
|
|
25
28
|
start(onInput: (data: string) => void, onResize: () => void): void;
|
|
29
|
+
/**
|
|
30
|
+
* Query terminal for Kitty keyboard protocol support and enable if available.
|
|
31
|
+
*
|
|
32
|
+
* Sends CSI ? u to query current flags. If terminal responds with CSI ? <flags> u,
|
|
33
|
+
* it supports the protocol and we enable it with CSI > 1 u.
|
|
34
|
+
*
|
|
35
|
+
* Non-supporting terminals won't respond, so we use a timeout.
|
|
36
|
+
*/
|
|
37
|
+
private queryAndEnableKittyProtocol;
|
|
26
38
|
stop(): void;
|
|
27
39
|
write(data: string): void;
|
|
28
40
|
get columns(): number;
|
package/dist/terminal.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"terminal.d.ts","sourceRoot":"","sources":["../src/terminal.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"terminal.d.ts","sourceRoot":"","sources":["../src/terminal.ts"],"names":[],"mappings":"AAEA;;GAEG;AACH,MAAM,WAAW,QAAQ;IAExB,KAAK,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,EAAE,QAAQ,EAAE,MAAM,IAAI,GAAG,IAAI,CAAC;IAGnE,IAAI,IAAI,IAAI,CAAC;IAGb,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAG1B,IAAI,OAAO,IAAI,MAAM,CAAC;IACtB,IAAI,IAAI,IAAI,MAAM,CAAC;IAGnB,IAAI,mBAAmB,IAAI,OAAO,CAAC;IAGnC,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAG5B,UAAU,IAAI,IAAI,CAAC;IACnB,UAAU,IAAI,IAAI,CAAC;IAGnB,SAAS,IAAI,IAAI,CAAC;IAClB,eAAe,IAAI,IAAI,CAAC;IACxB,WAAW,IAAI,IAAI,CAAC;IAGpB,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B;AAED;;GAEG;AACH,qBAAa,eAAgB,YAAW,QAAQ;IAC/C,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,YAAY,CAAC,CAAyB;IAC9C,OAAO,CAAC,aAAa,CAAC,CAAa;IACnC,OAAO,CAAC,oBAAoB,CAAS;IAErC,IAAI,mBAAmB,IAAI,OAAO,CAEjC;IAED,KAAK,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,EAAE,QAAQ,EAAE,MAAM,IAAI,GAAG,IAAI,CAsBjE;IAED;;;;;;;OAOG;IACH,OAAO,CAAC,2BAA2B;IAuEnC,IAAI,IAAI,IAAI,CAyBX;IAED,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAExB;IAED,IAAI,OAAO,IAAI,MAAM,CAEpB;IAED,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAS1B;IAED,UAAU,IAAI,IAAI,CAEjB;IAED,UAAU,IAAI,IAAI,CAEjB;IAED,SAAS,IAAI,IAAI,CAEhB;IAED,eAAe,IAAI,IAAI,CAEtB;IAED,WAAW,IAAI,IAAI,CAElB;IAED,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAG5B;CACD","sourcesContent":["import { setKittyProtocolActive } from \"./keys.js\";\n\n/**\n * Minimal terminal interface for TUI\n */\nexport interface Terminal {\n\t// Start the terminal with input and resize handlers\n\tstart(onInput: (data: string) => void, onResize: () => void): void;\n\n\t// Stop the terminal and restore state\n\tstop(): void;\n\n\t// Write output to terminal\n\twrite(data: string): void;\n\n\t// Get terminal dimensions\n\tget columns(): number;\n\tget rows(): number;\n\n\t// Whether Kitty keyboard protocol is active\n\tget kittyProtocolActive(): boolean;\n\n\t// Cursor positioning (relative to current position)\n\tmoveBy(lines: number): void; // Move cursor up (negative) or down (positive) by N lines\n\n\t// Cursor visibility\n\thideCursor(): void; // Hide the cursor\n\tshowCursor(): void; // Show the cursor\n\n\t// Clear operations\n\tclearLine(): void; // Clear current line\n\tclearFromCursor(): void; // Clear from cursor to end of screen\n\tclearScreen(): void; // Clear entire screen and move cursor to (0,0)\n\n\t// Title operations\n\tsetTitle(title: string): void; // Set terminal window title\n}\n\n/**\n * Real terminal using process.stdin/stdout\n */\nexport class ProcessTerminal implements Terminal {\n\tprivate wasRaw = false;\n\tprivate inputHandler?: (data: string) => void;\n\tprivate resizeHandler?: () => void;\n\tprivate _kittyProtocolActive = false;\n\n\tget kittyProtocolActive(): boolean {\n\t\treturn this._kittyProtocolActive;\n\t}\n\n\tstart(onInput: (data: string) => void, onResize: () => void): void {\n\t\tthis.inputHandler = onInput;\n\t\tthis.resizeHandler = onResize;\n\n\t\t// Save previous state and enable raw mode\n\t\tthis.wasRaw = process.stdin.isRaw || false;\n\t\tif (process.stdin.setRawMode) {\n\t\t\tprocess.stdin.setRawMode(true);\n\t\t}\n\t\tprocess.stdin.setEncoding(\"utf8\");\n\t\tprocess.stdin.resume();\n\n\t\t// Enable bracketed paste mode - terminal will wrap pastes in \\x1b[200~ ... \\x1b[201~\n\t\tprocess.stdout.write(\"\\x1b[?2004h\");\n\n\t\t// Set up resize handler immediately\n\t\tprocess.stdout.on(\"resize\", this.resizeHandler);\n\n\t\t// Query and enable Kitty keyboard protocol\n\t\t// The query handler intercepts input temporarily, then installs the user's handler\n\t\t// See: https://sw.kovidgoyal.net/kitty/keyboard-protocol/\n\t\tthis.queryAndEnableKittyProtocol();\n\t}\n\n\t/**\n\t * Query terminal for Kitty keyboard protocol support and enable if available.\n\t *\n\t * Sends CSI ? u to query current flags. If terminal responds with CSI ? <flags> u,\n\t * it supports the protocol and we enable it with CSI > 1 u.\n\t *\n\t * Non-supporting terminals won't respond, so we use a timeout.\n\t */\n\tprivate queryAndEnableKittyProtocol(): void {\n\t\tconst QUERY_TIMEOUT_MS = 100;\n\t\tlet resolved = false;\n\t\tlet buffer = \"\";\n\n\t\t// Kitty protocol response pattern: \\x1b[?<flags>u\n\t\tconst kittyResponsePattern = /\\x1b\\[\\?(\\d+)u/;\n\n\t\tconst queryHandler = (data: string) => {\n\t\t\tif (resolved) {\n\t\t\t\t// Query phase done, forward to user handler\n\t\t\t\tif (this.inputHandler) {\n\t\t\t\t\tthis.inputHandler(data);\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tbuffer += data;\n\n\t\t\t// Check if we have a Kitty protocol response\n\t\t\tconst match = buffer.match(kittyResponsePattern);\n\t\t\tif (match) {\n\t\t\t\tresolved = true;\n\t\t\t\tthis._kittyProtocolActive = true;\n\t\t\t\tsetKittyProtocolActive(true);\n\n\t\t\t\t// Enable Kitty keyboard protocol (push flags)\n\t\t\t\t// Flag 1 = disambiguate escape codes\n\t\t\t\tprocess.stdout.write(\"\\x1b[>1u\");\n\n\t\t\t\t// Remove the response from buffer, forward any remaining input\n\t\t\t\tconst remaining = buffer.replace(kittyResponsePattern, \"\");\n\t\t\t\tif (remaining && this.inputHandler) {\n\t\t\t\t\tthis.inputHandler(remaining);\n\t\t\t\t}\n\n\t\t\t\t// Replace with user handler\n\t\t\t\tprocess.stdin.removeListener(\"data\", queryHandler);\n\t\t\t\tif (this.inputHandler) {\n\t\t\t\t\tprocess.stdin.on(\"data\", this.inputHandler);\n\t\t\t\t}\n\t\t\t}\n\t\t};\n\n\t\t// Temporarily intercept input for the query\n\t\tprocess.stdin.on(\"data\", queryHandler);\n\n\t\t// Send query\n\t\tprocess.stdout.write(\"\\x1b[?u\");\n\n\t\t// Timeout: if no response, terminal doesn't support Kitty protocol\n\t\tsetTimeout(() => {\n\t\t\tif (!resolved) {\n\t\t\t\tresolved = true;\n\t\t\t\tthis._kittyProtocolActive = false;\n\t\t\t\tsetKittyProtocolActive(false);\n\n\t\t\t\t// Forward any buffered input that wasn't a Kitty response\n\t\t\t\tif (buffer && this.inputHandler) {\n\t\t\t\t\tthis.inputHandler(buffer);\n\t\t\t\t}\n\n\t\t\t\t// Replace with user handler\n\t\t\t\tprocess.stdin.removeListener(\"data\", queryHandler);\n\t\t\t\tif (this.inputHandler) {\n\t\t\t\t\tprocess.stdin.on(\"data\", this.inputHandler);\n\t\t\t\t}\n\t\t\t}\n\t\t}, QUERY_TIMEOUT_MS);\n\t}\n\n\tstop(): void {\n\t\t// Disable bracketed paste mode\n\t\tprocess.stdout.write(\"\\x1b[?2004l\");\n\n\t\t// Disable Kitty keyboard protocol (pop the flags we pushed) - only if we enabled it\n\t\tif (this._kittyProtocolActive) {\n\t\t\tprocess.stdout.write(\"\\x1b[<u\");\n\t\t\tthis._kittyProtocolActive = false;\n\t\t\tsetKittyProtocolActive(false);\n\t\t}\n\n\t\t// Remove event handlers\n\t\tif (this.inputHandler) {\n\t\t\tprocess.stdin.removeListener(\"data\", this.inputHandler);\n\t\t\tthis.inputHandler = undefined;\n\t\t}\n\t\tif (this.resizeHandler) {\n\t\t\tprocess.stdout.removeListener(\"resize\", this.resizeHandler);\n\t\t\tthis.resizeHandler = undefined;\n\t\t}\n\n\t\t// Restore raw mode state\n\t\tif (process.stdin.setRawMode) {\n\t\t\tprocess.stdin.setRawMode(this.wasRaw);\n\t\t}\n\t}\n\n\twrite(data: string): void {\n\t\tprocess.stdout.write(data);\n\t}\n\n\tget columns(): number {\n\t\treturn process.stdout.columns || 80;\n\t}\n\n\tget rows(): number {\n\t\treturn process.stdout.rows || 24;\n\t}\n\n\tmoveBy(lines: number): void {\n\t\tif (lines > 0) {\n\t\t\t// Move down\n\t\t\tprocess.stdout.write(`\\x1b[${lines}B`);\n\t\t} else if (lines < 0) {\n\t\t\t// Move up\n\t\t\tprocess.stdout.write(`\\x1b[${-lines}A`);\n\t\t}\n\t\t// lines === 0: no movement\n\t}\n\n\thideCursor(): void {\n\t\tprocess.stdout.write(\"\\x1b[?25l\");\n\t}\n\n\tshowCursor(): void {\n\t\tprocess.stdout.write(\"\\x1b[?25h\");\n\t}\n\n\tclearLine(): void {\n\t\tprocess.stdout.write(\"\\x1b[K\");\n\t}\n\n\tclearFromCursor(): void {\n\t\tprocess.stdout.write(\"\\x1b[J\");\n\t}\n\n\tclearScreen(): void {\n\t\tprocess.stdout.write(\"\\x1b[2J\\x1b[H\"); // Clear screen and move to home (1,1)\n\t}\n\n\tsetTitle(title: string): void {\n\t\t// OSC 0;title BEL - set terminal window title\n\t\tprocess.stdout.write(`\\x1b]0;${title}\\x07`);\n\t}\n}\n"]}
|
package/dist/terminal.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { setKittyProtocolActive } from "./keys.js";
|
|
1
2
|
/**
|
|
2
3
|
* Real terminal using process.stdin/stdout
|
|
3
4
|
*/
|
|
@@ -5,6 +6,10 @@ export class ProcessTerminal {
|
|
|
5
6
|
wasRaw = false;
|
|
6
7
|
inputHandler;
|
|
7
8
|
resizeHandler;
|
|
9
|
+
_kittyProtocolActive = false;
|
|
10
|
+
get kittyProtocolActive() {
|
|
11
|
+
return this._kittyProtocolActive;
|
|
12
|
+
}
|
|
8
13
|
start(onInput, onResize) {
|
|
9
14
|
this.inputHandler = onInput;
|
|
10
15
|
this.resizeHandler = onResize;
|
|
@@ -17,20 +22,88 @@ export class ProcessTerminal {
|
|
|
17
22
|
process.stdin.resume();
|
|
18
23
|
// Enable bracketed paste mode - terminal will wrap pastes in \x1b[200~ ... \x1b[201~
|
|
19
24
|
process.stdout.write("\x1b[?2004h");
|
|
20
|
-
//
|
|
21
|
-
// This makes terminals like Ghostty, Kitty, WezTerm send enhanced key sequences
|
|
22
|
-
// e.g., Shift+Enter becomes \x1b[13;2u instead of just \r
|
|
23
|
-
// See: https://sw.kovidgoyal.net/kitty/keyboard-protocol/
|
|
24
|
-
process.stdout.write("\x1b[>1u");
|
|
25
|
-
// Set up event handlers
|
|
26
|
-
process.stdin.on("data", this.inputHandler);
|
|
25
|
+
// Set up resize handler immediately
|
|
27
26
|
process.stdout.on("resize", this.resizeHandler);
|
|
27
|
+
// Query and enable Kitty keyboard protocol
|
|
28
|
+
// The query handler intercepts input temporarily, then installs the user's handler
|
|
29
|
+
// See: https://sw.kovidgoyal.net/kitty/keyboard-protocol/
|
|
30
|
+
this.queryAndEnableKittyProtocol();
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Query terminal for Kitty keyboard protocol support and enable if available.
|
|
34
|
+
*
|
|
35
|
+
* Sends CSI ? u to query current flags. If terminal responds with CSI ? <flags> u,
|
|
36
|
+
* it supports the protocol and we enable it with CSI > 1 u.
|
|
37
|
+
*
|
|
38
|
+
* Non-supporting terminals won't respond, so we use a timeout.
|
|
39
|
+
*/
|
|
40
|
+
queryAndEnableKittyProtocol() {
|
|
41
|
+
const QUERY_TIMEOUT_MS = 100;
|
|
42
|
+
let resolved = false;
|
|
43
|
+
let buffer = "";
|
|
44
|
+
// Kitty protocol response pattern: \x1b[?<flags>u
|
|
45
|
+
const kittyResponsePattern = /\x1b\[\?(\d+)u/;
|
|
46
|
+
const queryHandler = (data) => {
|
|
47
|
+
if (resolved) {
|
|
48
|
+
// Query phase done, forward to user handler
|
|
49
|
+
if (this.inputHandler) {
|
|
50
|
+
this.inputHandler(data);
|
|
51
|
+
}
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
buffer += data;
|
|
55
|
+
// Check if we have a Kitty protocol response
|
|
56
|
+
const match = buffer.match(kittyResponsePattern);
|
|
57
|
+
if (match) {
|
|
58
|
+
resolved = true;
|
|
59
|
+
this._kittyProtocolActive = true;
|
|
60
|
+
setKittyProtocolActive(true);
|
|
61
|
+
// Enable Kitty keyboard protocol (push flags)
|
|
62
|
+
// Flag 1 = disambiguate escape codes
|
|
63
|
+
process.stdout.write("\x1b[>1u");
|
|
64
|
+
// Remove the response from buffer, forward any remaining input
|
|
65
|
+
const remaining = buffer.replace(kittyResponsePattern, "");
|
|
66
|
+
if (remaining && this.inputHandler) {
|
|
67
|
+
this.inputHandler(remaining);
|
|
68
|
+
}
|
|
69
|
+
// Replace with user handler
|
|
70
|
+
process.stdin.removeListener("data", queryHandler);
|
|
71
|
+
if (this.inputHandler) {
|
|
72
|
+
process.stdin.on("data", this.inputHandler);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
// Temporarily intercept input for the query
|
|
77
|
+
process.stdin.on("data", queryHandler);
|
|
78
|
+
// Send query
|
|
79
|
+
process.stdout.write("\x1b[?u");
|
|
80
|
+
// Timeout: if no response, terminal doesn't support Kitty protocol
|
|
81
|
+
setTimeout(() => {
|
|
82
|
+
if (!resolved) {
|
|
83
|
+
resolved = true;
|
|
84
|
+
this._kittyProtocolActive = false;
|
|
85
|
+
setKittyProtocolActive(false);
|
|
86
|
+
// Forward any buffered input that wasn't a Kitty response
|
|
87
|
+
if (buffer && this.inputHandler) {
|
|
88
|
+
this.inputHandler(buffer);
|
|
89
|
+
}
|
|
90
|
+
// Replace with user handler
|
|
91
|
+
process.stdin.removeListener("data", queryHandler);
|
|
92
|
+
if (this.inputHandler) {
|
|
93
|
+
process.stdin.on("data", this.inputHandler);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}, QUERY_TIMEOUT_MS);
|
|
28
97
|
}
|
|
29
98
|
stop() {
|
|
30
99
|
// Disable bracketed paste mode
|
|
31
100
|
process.stdout.write("\x1b[?2004l");
|
|
32
|
-
// Disable Kitty keyboard protocol (pop the flags we pushed)
|
|
33
|
-
|
|
101
|
+
// Disable Kitty keyboard protocol (pop the flags we pushed) - only if we enabled it
|
|
102
|
+
if (this._kittyProtocolActive) {
|
|
103
|
+
process.stdout.write("\x1b[<u");
|
|
104
|
+
this._kittyProtocolActive = false;
|
|
105
|
+
setKittyProtocolActive(false);
|
|
106
|
+
}
|
|
34
107
|
// Remove event handlers
|
|
35
108
|
if (this.inputHandler) {
|
|
36
109
|
process.stdin.removeListener("data", this.inputHandler);
|
package/dist/terminal.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"terminal.js","sourceRoot":"","sources":["../src/terminal.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Minimal terminal interface for TUI\n */\nexport interface Terminal {\n\t// Start the terminal with input and resize handlers\n\tstart(onInput: (data: string) => void, onResize: () => void): void;\n\n\t// Stop the terminal and restore state\n\tstop(): void;\n\n\t// Write output to terminal\n\twrite(data: string): void;\n\n\t// Get terminal dimensions\n\tget columns(): number;\n\tget rows(): number;\n\n\t// Cursor positioning (relative to current position)\n\tmoveBy(lines: number): void; // Move cursor up (negative) or down (positive) by N lines\n\n\t// Cursor visibility\n\thideCursor(): void; // Hide the cursor\n\tshowCursor(): void; // Show the cursor\n\n\t// Clear operations\n\tclearLine(): void; // Clear current line\n\tclearFromCursor(): void; // Clear from cursor to end of screen\n\tclearScreen(): void; // Clear entire screen and move cursor to (0,0)\n\n\t// Title operations\n\tsetTitle(title: string): void; // Set terminal window title\n}\n\n/**\n * Real terminal using process.stdin/stdout\n */\nexport class ProcessTerminal implements Terminal {\n\tprivate wasRaw = false;\n\tprivate inputHandler?: (data: string) => void;\n\tprivate resizeHandler?: () => void;\n\n\tstart(onInput: (data: string) => void, onResize: () => void): void {\n\t\tthis.inputHandler = onInput;\n\t\tthis.resizeHandler = onResize;\n\n\t\t// Save previous state and enable raw mode\n\t\tthis.wasRaw = process.stdin.isRaw || false;\n\t\tif (process.stdin.setRawMode) {\n\t\t\tprocess.stdin.setRawMode(true);\n\t\t}\n\t\tprocess.stdin.setEncoding(\"utf8\");\n\t\tprocess.stdin.resume();\n\n\t\t// Enable bracketed paste mode - terminal will wrap pastes in \\x1b[200~ ... \\x1b[201~\n\t\tprocess.stdout.write(\"\\x1b[?2004h\");\n\n\t\t// Enable Kitty keyboard protocol (disambiguate escape codes)\n\t\t// This makes terminals like Ghostty, Kitty, WezTerm send enhanced key sequences\n\t\t// e.g., Shift+Enter becomes \\x1b[13;2u instead of just \\r\n\t\t// See: https://sw.kovidgoyal.net/kitty/keyboard-protocol/\n\t\tprocess.stdout.write(\"\\x1b[>1u\");\n\n\t\t// Set up event handlers\n\t\tprocess.stdin.on(\"data\", this.inputHandler);\n\t\tprocess.stdout.on(\"resize\", this.resizeHandler);\n\t}\n\n\tstop(): void {\n\t\t// Disable bracketed paste mode\n\t\tprocess.stdout.write(\"\\x1b[?2004l\");\n\n\t\t// Disable Kitty keyboard protocol (pop the flags we pushed)\n\t\tprocess.stdout.write(\"\\x1b[<u\");\n\n\t\t// Remove event handlers\n\t\tif (this.inputHandler) {\n\t\t\tprocess.stdin.removeListener(\"data\", this.inputHandler);\n\t\t\tthis.inputHandler = undefined;\n\t\t}\n\t\tif (this.resizeHandler) {\n\t\t\tprocess.stdout.removeListener(\"resize\", this.resizeHandler);\n\t\t\tthis.resizeHandler = undefined;\n\t\t}\n\n\t\t// Restore raw mode state\n\t\tif (process.stdin.setRawMode) {\n\t\t\tprocess.stdin.setRawMode(this.wasRaw);\n\t\t}\n\t}\n\n\twrite(data: string): void {\n\t\tprocess.stdout.write(data);\n\t}\n\n\tget columns(): number {\n\t\treturn process.stdout.columns || 80;\n\t}\n\n\tget rows(): number {\n\t\treturn process.stdout.rows || 24;\n\t}\n\n\tmoveBy(lines: number): void {\n\t\tif (lines > 0) {\n\t\t\t// Move down\n\t\t\tprocess.stdout.write(`\\x1b[${lines}B`);\n\t\t} else if (lines < 0) {\n\t\t\t// Move up\n\t\t\tprocess.stdout.write(`\\x1b[${-lines}A`);\n\t\t}\n\t\t// lines === 0: no movement\n\t}\n\n\thideCursor(): void {\n\t\tprocess.stdout.write(\"\\x1b[?25l\");\n\t}\n\n\tshowCursor(): void {\n\t\tprocess.stdout.write(\"\\x1b[?25h\");\n\t}\n\n\tclearLine(): void {\n\t\tprocess.stdout.write(\"\\x1b[K\");\n\t}\n\n\tclearFromCursor(): void {\n\t\tprocess.stdout.write(\"\\x1b[J\");\n\t}\n\n\tclearScreen(): void {\n\t\tprocess.stdout.write(\"\\x1b[2J\\x1b[H\"); // Clear screen and move to home (1,1)\n\t}\n\n\tsetTitle(title: string): void {\n\t\t// OSC 0;title BEL - set terminal window title\n\t\tprocess.stdout.write(`\\x1b]0;${title}\\x07`);\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"terminal.js","sourceRoot":"","sources":["../src/terminal.ts"],"names":[],"mappings":"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","sourcesContent":["import { setKittyProtocolActive } from \"./keys.js\";\n\n/**\n * Minimal terminal interface for TUI\n */\nexport interface Terminal {\n\t// Start the terminal with input and resize handlers\n\tstart(onInput: (data: string) => void, onResize: () => void): void;\n\n\t// Stop the terminal and restore state\n\tstop(): void;\n\n\t// Write output to terminal\n\twrite(data: string): void;\n\n\t// Get terminal dimensions\n\tget columns(): number;\n\tget rows(): number;\n\n\t// Whether Kitty keyboard protocol is active\n\tget kittyProtocolActive(): boolean;\n\n\t// Cursor positioning (relative to current position)\n\tmoveBy(lines: number): void; // Move cursor up (negative) or down (positive) by N lines\n\n\t// Cursor visibility\n\thideCursor(): void; // Hide the cursor\n\tshowCursor(): void; // Show the cursor\n\n\t// Clear operations\n\tclearLine(): void; // Clear current line\n\tclearFromCursor(): void; // Clear from cursor to end of screen\n\tclearScreen(): void; // Clear entire screen and move cursor to (0,0)\n\n\t// Title operations\n\tsetTitle(title: string): void; // Set terminal window title\n}\n\n/**\n * Real terminal using process.stdin/stdout\n */\nexport class ProcessTerminal implements Terminal {\n\tprivate wasRaw = false;\n\tprivate inputHandler?: (data: string) => void;\n\tprivate resizeHandler?: () => void;\n\tprivate _kittyProtocolActive = false;\n\n\tget kittyProtocolActive(): boolean {\n\t\treturn this._kittyProtocolActive;\n\t}\n\n\tstart(onInput: (data: string) => void, onResize: () => void): void {\n\t\tthis.inputHandler = onInput;\n\t\tthis.resizeHandler = onResize;\n\n\t\t// Save previous state and enable raw mode\n\t\tthis.wasRaw = process.stdin.isRaw || false;\n\t\tif (process.stdin.setRawMode) {\n\t\t\tprocess.stdin.setRawMode(true);\n\t\t}\n\t\tprocess.stdin.setEncoding(\"utf8\");\n\t\tprocess.stdin.resume();\n\n\t\t// Enable bracketed paste mode - terminal will wrap pastes in \\x1b[200~ ... \\x1b[201~\n\t\tprocess.stdout.write(\"\\x1b[?2004h\");\n\n\t\t// Set up resize handler immediately\n\t\tprocess.stdout.on(\"resize\", this.resizeHandler);\n\n\t\t// Query and enable Kitty keyboard protocol\n\t\t// The query handler intercepts input temporarily, then installs the user's handler\n\t\t// See: https://sw.kovidgoyal.net/kitty/keyboard-protocol/\n\t\tthis.queryAndEnableKittyProtocol();\n\t}\n\n\t/**\n\t * Query terminal for Kitty keyboard protocol support and enable if available.\n\t *\n\t * Sends CSI ? u to query current flags. If terminal responds with CSI ? <flags> u,\n\t * it supports the protocol and we enable it with CSI > 1 u.\n\t *\n\t * Non-supporting terminals won't respond, so we use a timeout.\n\t */\n\tprivate queryAndEnableKittyProtocol(): void {\n\t\tconst QUERY_TIMEOUT_MS = 100;\n\t\tlet resolved = false;\n\t\tlet buffer = \"\";\n\n\t\t// Kitty protocol response pattern: \\x1b[?<flags>u\n\t\tconst kittyResponsePattern = /\\x1b\\[\\?(\\d+)u/;\n\n\t\tconst queryHandler = (data: string) => {\n\t\t\tif (resolved) {\n\t\t\t\t// Query phase done, forward to user handler\n\t\t\t\tif (this.inputHandler) {\n\t\t\t\t\tthis.inputHandler(data);\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tbuffer += data;\n\n\t\t\t// Check if we have a Kitty protocol response\n\t\t\tconst match = buffer.match(kittyResponsePattern);\n\t\t\tif (match) {\n\t\t\t\tresolved = true;\n\t\t\t\tthis._kittyProtocolActive = true;\n\t\t\t\tsetKittyProtocolActive(true);\n\n\t\t\t\t// Enable Kitty keyboard protocol (push flags)\n\t\t\t\t// Flag 1 = disambiguate escape codes\n\t\t\t\tprocess.stdout.write(\"\\x1b[>1u\");\n\n\t\t\t\t// Remove the response from buffer, forward any remaining input\n\t\t\t\tconst remaining = buffer.replace(kittyResponsePattern, \"\");\n\t\t\t\tif (remaining && this.inputHandler) {\n\t\t\t\t\tthis.inputHandler(remaining);\n\t\t\t\t}\n\n\t\t\t\t// Replace with user handler\n\t\t\t\tprocess.stdin.removeListener(\"data\", queryHandler);\n\t\t\t\tif (this.inputHandler) {\n\t\t\t\t\tprocess.stdin.on(\"data\", this.inputHandler);\n\t\t\t\t}\n\t\t\t}\n\t\t};\n\n\t\t// Temporarily intercept input for the query\n\t\tprocess.stdin.on(\"data\", queryHandler);\n\n\t\t// Send query\n\t\tprocess.stdout.write(\"\\x1b[?u\");\n\n\t\t// Timeout: if no response, terminal doesn't support Kitty protocol\n\t\tsetTimeout(() => {\n\t\t\tif (!resolved) {\n\t\t\t\tresolved = true;\n\t\t\t\tthis._kittyProtocolActive = false;\n\t\t\t\tsetKittyProtocolActive(false);\n\n\t\t\t\t// Forward any buffered input that wasn't a Kitty response\n\t\t\t\tif (buffer && this.inputHandler) {\n\t\t\t\t\tthis.inputHandler(buffer);\n\t\t\t\t}\n\n\t\t\t\t// Replace with user handler\n\t\t\t\tprocess.stdin.removeListener(\"data\", queryHandler);\n\t\t\t\tif (this.inputHandler) {\n\t\t\t\t\tprocess.stdin.on(\"data\", this.inputHandler);\n\t\t\t\t}\n\t\t\t}\n\t\t}, QUERY_TIMEOUT_MS);\n\t}\n\n\tstop(): void {\n\t\t// Disable bracketed paste mode\n\t\tprocess.stdout.write(\"\\x1b[?2004l\");\n\n\t\t// Disable Kitty keyboard protocol (pop the flags we pushed) - only if we enabled it\n\t\tif (this._kittyProtocolActive) {\n\t\t\tprocess.stdout.write(\"\\x1b[<u\");\n\t\t\tthis._kittyProtocolActive = false;\n\t\t\tsetKittyProtocolActive(false);\n\t\t}\n\n\t\t// Remove event handlers\n\t\tif (this.inputHandler) {\n\t\t\tprocess.stdin.removeListener(\"data\", this.inputHandler);\n\t\t\tthis.inputHandler = undefined;\n\t\t}\n\t\tif (this.resizeHandler) {\n\t\t\tprocess.stdout.removeListener(\"resize\", this.resizeHandler);\n\t\t\tthis.resizeHandler = undefined;\n\t\t}\n\n\t\t// Restore raw mode state\n\t\tif (process.stdin.setRawMode) {\n\t\t\tprocess.stdin.setRawMode(this.wasRaw);\n\t\t}\n\t}\n\n\twrite(data: string): void {\n\t\tprocess.stdout.write(data);\n\t}\n\n\tget columns(): number {\n\t\treturn process.stdout.columns || 80;\n\t}\n\n\tget rows(): number {\n\t\treturn process.stdout.rows || 24;\n\t}\n\n\tmoveBy(lines: number): void {\n\t\tif (lines > 0) {\n\t\t\t// Move down\n\t\t\tprocess.stdout.write(`\\x1b[${lines}B`);\n\t\t} else if (lines < 0) {\n\t\t\t// Move up\n\t\t\tprocess.stdout.write(`\\x1b[${-lines}A`);\n\t\t}\n\t\t// lines === 0: no movement\n\t}\n\n\thideCursor(): void {\n\t\tprocess.stdout.write(\"\\x1b[?25l\");\n\t}\n\n\tshowCursor(): void {\n\t\tprocess.stdout.write(\"\\x1b[?25h\");\n\t}\n\n\tclearLine(): void {\n\t\tprocess.stdout.write(\"\\x1b[K\");\n\t}\n\n\tclearFromCursor(): void {\n\t\tprocess.stdout.write(\"\\x1b[J\");\n\t}\n\n\tclearScreen(): void {\n\t\tprocess.stdout.write(\"\\x1b[2J\\x1b[H\"); // Clear screen and move to home (1,1)\n\t}\n\n\tsetTitle(title: string): void {\n\t\t// OSC 0;title BEL - set terminal window title\n\t\tprocess.stdout.write(`\\x1b]0;${title}\\x07`);\n\t}\n}\n"]}
|
package/dist/utils.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAKA;;GAEG;AACH,wBAAgB,YAAY,IAAI,IAAI,CAAC,SAAS,CAE7C;AAmED;;GAEG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAoDhD;AAiSD;;;;;;;;;;GAUG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAoBtE;AAkFD;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEtD;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEvD;AAuED;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,GAAG,MAAM,CASzG;AAED;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,GAAE,MAAc,GAAG,MAAM,CAkEhG","sourcesContent":["import { eastAsianWidth } from \"get-east-asian-width\";\n\n// Grapheme segmenter (shared instance)\nconst segmenter = new Intl.Segmenter(undefined, { granularity: \"grapheme\" });\n\n/**\n * Get the shared grapheme segmenter instance.\n */\nexport function getSegmenter(): Intl.Segmenter {\n\treturn segmenter;\n}\n\n/**\n * Check if a grapheme cluster (after segmentation) could possibly be an RGI emoji.\n * This is a fast heuristic to avoid the expensive rgiEmojiRegex test.\n * The tested Unicode blocks are deliberately broad to account for future\n * Unicode additions.\n */\nfunction couldBeEmoji(segment: string): boolean {\n\tconst cp = segment.codePointAt(0)!;\n\treturn (\n\t\t(cp >= 0x1f000 && cp <= 0x1fbff) || // Emoji and Pictograph\n\t\t(cp >= 0x2300 && cp <= 0x23ff) || // Misc technical\n\t\t(cp >= 0x2600 && cp <= 0x27bf) || // Misc symbols, dingbats\n\t\t(cp >= 0x2b50 && cp <= 0x2b55) || // Specific stars/circles\n\t\tsegment.includes(\"\\uFE0F\") || // Contains VS16 (emoji presentation selector)\n\t\tsegment.length > 2 // Multi-codepoint sequences (ZWJ, skin tones, etc.)\n\t);\n}\n\n// Regexes for character classification (same as string-width library)\nconst zeroWidthRegex = /^(?:\\p{Default_Ignorable_Code_Point}|\\p{Control}|\\p{Mark}|\\p{Surrogate})+$/v;\nconst leadingNonPrintingRegex = /^[\\p{Default_Ignorable_Code_Point}\\p{Control}\\p{Format}\\p{Mark}\\p{Surrogate}]+/v;\nconst rgiEmojiRegex = /^\\p{RGI_Emoji}$/v;\n\n// Cache for non-ASCII strings\nconst WIDTH_CACHE_SIZE = 512;\nconst widthCache = new Map<string, number>();\n\n/**\n * Calculate the terminal width of a single grapheme cluster.\n * Based on code from the string-width library, but includes a possible-emoji\n * check to avoid running the RGI_Emoji regex unnecessarily.\n */\nfunction graphemeWidth(segment: string): number {\n\t// Zero-width clusters\n\tif (zeroWidthRegex.test(segment)) {\n\t\treturn 0;\n\t}\n\n\t// Emoji check with pre-filter\n\tif (couldBeEmoji(segment) && rgiEmojiRegex.test(segment)) {\n\t\treturn 2;\n\t}\n\n\t// Get base visible codepoint\n\tconst base = segment.replace(leadingNonPrintingRegex, \"\");\n\tconst cp = base.codePointAt(0);\n\tif (cp === undefined) {\n\t\treturn 0;\n\t}\n\n\tlet width = eastAsianWidth(cp);\n\n\t// Trailing halfwidth/fullwidth forms\n\tif (segment.length > 1) {\n\t\tfor (const char of segment.slice(1)) {\n\t\t\tconst c = char.codePointAt(0)!;\n\t\t\tif (c >= 0xff00 && c <= 0xffef) {\n\t\t\t\twidth += eastAsianWidth(c);\n\t\t\t}\n\t\t}\n\t}\n\n\treturn width;\n}\n\n/**\n * Calculate the visible width of a string in terminal columns.\n */\nexport function visibleWidth(str: string): number {\n\tif (str.length === 0) {\n\t\treturn 0;\n\t}\n\n\t// Fast path: pure ASCII printable\n\tlet isPureAscii = true;\n\tfor (let i = 0; i < str.length; i++) {\n\t\tconst code = str.charCodeAt(i);\n\t\tif (code < 0x20 || code > 0x7e) {\n\t\t\tisPureAscii = false;\n\t\t\tbreak;\n\t\t}\n\t}\n\tif (isPureAscii) {\n\t\treturn str.length;\n\t}\n\n\t// Check cache\n\tconst cached = widthCache.get(str);\n\tif (cached !== undefined) {\n\t\treturn cached;\n\t}\n\n\t// Normalize: tabs to 3 spaces, strip ANSI escape codes\n\tlet clean = str;\n\tif (str.includes(\"\\t\")) {\n\t\tclean = clean.replace(/\\t/g, \" \");\n\t}\n\tif (clean.includes(\"\\x1b\")) {\n\t\t// Strip SGR codes (\\x1b[...m) and cursor codes (\\x1b[...G/K/H/J)\n\t\tclean = clean.replace(/\\x1b\\[[0-9;]*[mGKHJ]/g, \"\");\n\t\t// Strip OSC 8 hyperlinks: \\x1b]8;;URL\\x07 and \\x1b]8;;\\x07\n\t\tclean = clean.replace(/\\x1b\\]8;;[^\\x07]*\\x07/g, \"\");\n\t}\n\n\t// Calculate width\n\tlet width = 0;\n\tfor (const { segment } of segmenter.segment(clean)) {\n\t\twidth += graphemeWidth(segment);\n\t}\n\n\t// Cache result\n\tif (widthCache.size >= WIDTH_CACHE_SIZE) {\n\t\tconst firstKey = widthCache.keys().next().value;\n\t\tif (firstKey !== undefined) {\n\t\t\twidthCache.delete(firstKey);\n\t\t}\n\t}\n\twidthCache.set(str, width);\n\n\treturn width;\n}\n\n/**\n * Extract ANSI escape sequences from a string at the given position.\n */\nfunction extractAnsiCode(str: string, pos: number): { code: string; length: number } | null {\n\tif (pos >= str.length || str[pos] !== \"\\x1b\" || str[pos + 1] !== \"[\") {\n\t\treturn null;\n\t}\n\n\tlet j = pos + 2;\n\twhile (j < str.length && str[j] && !/[mGKHJ]/.test(str[j]!)) {\n\t\tj++;\n\t}\n\n\tif (j < str.length) {\n\t\treturn {\n\t\t\tcode: str.substring(pos, j + 1),\n\t\t\tlength: j + 1 - pos,\n\t\t};\n\t}\n\n\treturn null;\n}\n\n/**\n * Track active ANSI SGR codes to preserve styling across line breaks.\n */\nclass AnsiCodeTracker {\n\t// Track individual attributes separately so we can reset them specifically\n\tprivate bold = false;\n\tprivate dim = false;\n\tprivate italic = false;\n\tprivate underline = false;\n\tprivate blink = false;\n\tprivate inverse = false;\n\tprivate hidden = false;\n\tprivate strikethrough = false;\n\tprivate fgColor: string | null = null; // Stores the full code like \"31\" or \"38;5;240\"\n\tprivate bgColor: string | null = null; // Stores the full code like \"41\" or \"48;5;240\"\n\n\tprocess(ansiCode: string): void {\n\t\tif (!ansiCode.endsWith(\"m\")) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Extract the parameters between \\x1b[ and m\n\t\tconst match = ansiCode.match(/\\x1b\\[([\\d;]*)m/);\n\t\tif (!match) return;\n\n\t\tconst params = match[1];\n\t\tif (params === \"\" || params === \"0\") {\n\t\t\t// Full reset\n\t\t\tthis.reset();\n\t\t\treturn;\n\t\t}\n\n\t\t// Parse parameters (can be semicolon-separated)\n\t\tconst parts = params.split(\";\");\n\t\tlet i = 0;\n\t\twhile (i < parts.length) {\n\t\t\tconst code = Number.parseInt(parts[i], 10);\n\n\t\t\t// Handle 256-color and RGB codes which consume multiple parameters\n\t\t\tif (code === 38 || code === 48) {\n\t\t\t\t// 38;5;N (256 color fg) or 38;2;R;G;B (RGB fg)\n\t\t\t\t// 48;5;N (256 color bg) or 48;2;R;G;B (RGB bg)\n\t\t\t\tif (parts[i + 1] === \"5\" && parts[i + 2] !== undefined) {\n\t\t\t\t\t// 256 color: 38;5;N or 48;5;N\n\t\t\t\t\tconst colorCode = `${parts[i]};${parts[i + 1]};${parts[i + 2]}`;\n\t\t\t\t\tif (code === 38) {\n\t\t\t\t\t\tthis.fgColor = colorCode;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tthis.bgColor = colorCode;\n\t\t\t\t\t}\n\t\t\t\t\ti += 3;\n\t\t\t\t\tcontinue;\n\t\t\t\t} else if (parts[i + 1] === \"2\" && parts[i + 4] !== undefined) {\n\t\t\t\t\t// RGB color: 38;2;R;G;B or 48;2;R;G;B\n\t\t\t\t\tconst colorCode = `${parts[i]};${parts[i + 1]};${parts[i + 2]};${parts[i + 3]};${parts[i + 4]}`;\n\t\t\t\t\tif (code === 38) {\n\t\t\t\t\t\tthis.fgColor = colorCode;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tthis.bgColor = colorCode;\n\t\t\t\t\t}\n\t\t\t\t\ti += 5;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Standard SGR codes\n\t\t\tswitch (code) {\n\t\t\t\tcase 0:\n\t\t\t\t\tthis.reset();\n\t\t\t\t\tbreak;\n\t\t\t\tcase 1:\n\t\t\t\t\tthis.bold = true;\n\t\t\t\t\tbreak;\n\t\t\t\tcase 2:\n\t\t\t\t\tthis.dim = true;\n\t\t\t\t\tbreak;\n\t\t\t\tcase 3:\n\t\t\t\t\tthis.italic = true;\n\t\t\t\t\tbreak;\n\t\t\t\tcase 4:\n\t\t\t\t\tthis.underline = true;\n\t\t\t\t\tbreak;\n\t\t\t\tcase 5:\n\t\t\t\t\tthis.blink = true;\n\t\t\t\t\tbreak;\n\t\t\t\tcase 7:\n\t\t\t\t\tthis.inverse = true;\n\t\t\t\t\tbreak;\n\t\t\t\tcase 8:\n\t\t\t\t\tthis.hidden = true;\n\t\t\t\t\tbreak;\n\t\t\t\tcase 9:\n\t\t\t\t\tthis.strikethrough = true;\n\t\t\t\t\tbreak;\n\t\t\t\tcase 21:\n\t\t\t\t\tthis.bold = false;\n\t\t\t\t\tbreak; // Some terminals\n\t\t\t\tcase 22:\n\t\t\t\t\tthis.bold = false;\n\t\t\t\t\tthis.dim = false;\n\t\t\t\t\tbreak;\n\t\t\t\tcase 23:\n\t\t\t\t\tthis.italic = false;\n\t\t\t\t\tbreak;\n\t\t\t\tcase 24:\n\t\t\t\t\tthis.underline = false;\n\t\t\t\t\tbreak;\n\t\t\t\tcase 25:\n\t\t\t\t\tthis.blink = false;\n\t\t\t\t\tbreak;\n\t\t\t\tcase 27:\n\t\t\t\t\tthis.inverse = false;\n\t\t\t\t\tbreak;\n\t\t\t\tcase 28:\n\t\t\t\t\tthis.hidden = false;\n\t\t\t\t\tbreak;\n\t\t\t\tcase 29:\n\t\t\t\t\tthis.strikethrough = false;\n\t\t\t\t\tbreak;\n\t\t\t\tcase 39:\n\t\t\t\t\tthis.fgColor = null;\n\t\t\t\t\tbreak; // Default fg\n\t\t\t\tcase 49:\n\t\t\t\t\tthis.bgColor = null;\n\t\t\t\t\tbreak; // Default bg\n\t\t\t\tdefault:\n\t\t\t\t\t// Standard foreground colors 30-37, 90-97\n\t\t\t\t\tif ((code >= 30 && code <= 37) || (code >= 90 && code <= 97)) {\n\t\t\t\t\t\tthis.fgColor = String(code);\n\t\t\t\t\t}\n\t\t\t\t\t// Standard background colors 40-47, 100-107\n\t\t\t\t\telse if ((code >= 40 && code <= 47) || (code >= 100 && code <= 107)) {\n\t\t\t\t\t\tthis.bgColor = String(code);\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\ti++;\n\t\t}\n\t}\n\n\tprivate reset(): void {\n\t\tthis.bold = false;\n\t\tthis.dim = false;\n\t\tthis.italic = false;\n\t\tthis.underline = false;\n\t\tthis.blink = false;\n\t\tthis.inverse = false;\n\t\tthis.hidden = false;\n\t\tthis.strikethrough = false;\n\t\tthis.fgColor = null;\n\t\tthis.bgColor = null;\n\t}\n\n\tgetActiveCodes(): string {\n\t\tconst codes: string[] = [];\n\t\tif (this.bold) codes.push(\"1\");\n\t\tif (this.dim) codes.push(\"2\");\n\t\tif (this.italic) codes.push(\"3\");\n\t\tif (this.underline) codes.push(\"4\");\n\t\tif (this.blink) codes.push(\"5\");\n\t\tif (this.inverse) codes.push(\"7\");\n\t\tif (this.hidden) codes.push(\"8\");\n\t\tif (this.strikethrough) codes.push(\"9\");\n\t\tif (this.fgColor) codes.push(this.fgColor);\n\t\tif (this.bgColor) codes.push(this.bgColor);\n\n\t\tif (codes.length === 0) return \"\";\n\t\treturn `\\x1b[${codes.join(\";\")}m`;\n\t}\n\n\thasActiveCodes(): boolean {\n\t\treturn (\n\t\t\tthis.bold ||\n\t\t\tthis.dim ||\n\t\t\tthis.italic ||\n\t\t\tthis.underline ||\n\t\t\tthis.blink ||\n\t\t\tthis.inverse ||\n\t\t\tthis.hidden ||\n\t\t\tthis.strikethrough ||\n\t\t\tthis.fgColor !== null ||\n\t\t\tthis.bgColor !== null\n\t\t);\n\t}\n\n\t/**\n\t * Get reset codes for attributes that need to be turned off at line end,\n\t * specifically underline which bleeds into padding.\n\t * Returns empty string if no problematic attributes are active.\n\t */\n\tgetLineEndReset(): string {\n\t\t// Only underline causes visual bleeding into padding\n\t\t// Other attributes like colors don't visually bleed to padding\n\t\tif (this.underline) {\n\t\t\treturn \"\\x1b[24m\"; // Underline off only\n\t\t}\n\t\treturn \"\";\n\t}\n}\n\nfunction updateTrackerFromText(text: string, tracker: AnsiCodeTracker): void {\n\tlet i = 0;\n\twhile (i < text.length) {\n\t\tconst ansiResult = extractAnsiCode(text, i);\n\t\tif (ansiResult) {\n\t\t\ttracker.process(ansiResult.code);\n\t\t\ti += ansiResult.length;\n\t\t} else {\n\t\t\ti++;\n\t\t}\n\t}\n}\n\n/**\n * Split text into words while keeping ANSI codes attached.\n */\nfunction splitIntoTokensWithAnsi(text: string): string[] {\n\tconst tokens: string[] = [];\n\tlet current = \"\";\n\tlet pendingAnsi = \"\"; // ANSI codes waiting to be attached to next visible content\n\tlet inWhitespace = false;\n\tlet i = 0;\n\n\twhile (i < text.length) {\n\t\tconst ansiResult = extractAnsiCode(text, i);\n\t\tif (ansiResult) {\n\t\t\t// Hold ANSI codes separately - they'll be attached to the next visible char\n\t\t\tpendingAnsi += ansiResult.code;\n\t\t\ti += ansiResult.length;\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst char = text[i];\n\t\tconst charIsSpace = char === \" \";\n\n\t\tif (charIsSpace !== inWhitespace && current) {\n\t\t\t// Switching between whitespace and non-whitespace, push current token\n\t\t\ttokens.push(current);\n\t\t\tcurrent = \"\";\n\t\t}\n\n\t\t// Attach any pending ANSI codes to this visible character\n\t\tif (pendingAnsi) {\n\t\t\tcurrent += pendingAnsi;\n\t\t\tpendingAnsi = \"\";\n\t\t}\n\n\t\tinWhitespace = charIsSpace;\n\t\tcurrent += char;\n\t\ti++;\n\t}\n\n\t// Handle any remaining pending ANSI codes (attach to last token)\n\tif (pendingAnsi) {\n\t\tcurrent += pendingAnsi;\n\t}\n\n\tif (current) {\n\t\ttokens.push(current);\n\t}\n\n\treturn tokens;\n}\n\n/**\n * Wrap text with ANSI codes preserved.\n *\n * ONLY does word wrapping - NO padding, NO background colors.\n * Returns lines where each line is <= width visible chars.\n * Active ANSI codes are preserved across line breaks.\n *\n * @param text - Text to wrap (may contain ANSI codes and newlines)\n * @param width - Maximum visible width per line\n * @returns Array of wrapped lines (NOT padded to width)\n */\nexport function wrapTextWithAnsi(text: string, width: number): string[] {\n\tif (!text) {\n\t\treturn [\"\"];\n\t}\n\n\t// Handle newlines by processing each line separately\n\t// Track ANSI state across lines so styles carry over after literal newlines\n\tconst inputLines = text.split(\"\\n\");\n\tconst result: string[] = [];\n\tconst tracker = new AnsiCodeTracker();\n\n\tfor (const inputLine of inputLines) {\n\t\t// Prepend active ANSI codes from previous lines (except for first line)\n\t\tconst prefix = result.length > 0 ? tracker.getActiveCodes() : \"\";\n\t\tresult.push(...wrapSingleLine(prefix + inputLine, width));\n\t\t// Update tracker with codes from this line for next iteration\n\t\tupdateTrackerFromText(inputLine, tracker);\n\t}\n\n\treturn result.length > 0 ? result : [\"\"];\n}\n\nfunction wrapSingleLine(line: string, width: number): string[] {\n\tif (!line) {\n\t\treturn [\"\"];\n\t}\n\n\tconst visibleLength = visibleWidth(line);\n\tif (visibleLength <= width) {\n\t\treturn [line];\n\t}\n\n\tconst wrapped: string[] = [];\n\tconst tracker = new AnsiCodeTracker();\n\tconst tokens = splitIntoTokensWithAnsi(line);\n\n\tlet currentLine = \"\";\n\tlet currentVisibleLength = 0;\n\n\tfor (const token of tokens) {\n\t\tconst tokenVisibleLength = visibleWidth(token);\n\t\tconst isWhitespace = token.trim() === \"\";\n\n\t\t// Token itself is too long - break it character by character\n\t\tif (tokenVisibleLength > width && !isWhitespace) {\n\t\t\tif (currentLine) {\n\t\t\t\t// Add specific reset for underline only (preserves background)\n\t\t\t\tconst lineEndReset = tracker.getLineEndReset();\n\t\t\t\tif (lineEndReset) {\n\t\t\t\t\tcurrentLine += lineEndReset;\n\t\t\t\t}\n\t\t\t\twrapped.push(currentLine);\n\t\t\t\tcurrentLine = \"\";\n\t\t\t\tcurrentVisibleLength = 0;\n\t\t\t}\n\n\t\t\t// Break long token - breakLongWord handles its own resets\n\t\t\tconst broken = breakLongWord(token, width, tracker);\n\t\t\twrapped.push(...broken.slice(0, -1));\n\t\t\tcurrentLine = broken[broken.length - 1];\n\t\t\tcurrentVisibleLength = visibleWidth(currentLine);\n\t\t\tcontinue;\n\t\t}\n\n\t\t// Check if adding this token would exceed width\n\t\tconst totalNeeded = currentVisibleLength + tokenVisibleLength;\n\n\t\tif (totalNeeded > width && currentVisibleLength > 0) {\n\t\t\t// Add specific reset for underline only (preserves background)\n\t\t\tlet lineToWrap = currentLine.trimEnd();\n\t\t\tconst lineEndReset = tracker.getLineEndReset();\n\t\t\tif (lineEndReset) {\n\t\t\t\tlineToWrap += lineEndReset;\n\t\t\t}\n\t\t\twrapped.push(lineToWrap);\n\t\t\tif (isWhitespace) {\n\t\t\t\t// Don't start new line with whitespace\n\t\t\t\tcurrentLine = tracker.getActiveCodes();\n\t\t\t\tcurrentVisibleLength = 0;\n\t\t\t} else {\n\t\t\t\tcurrentLine = tracker.getActiveCodes() + token;\n\t\t\t\tcurrentVisibleLength = tokenVisibleLength;\n\t\t\t}\n\t\t} else {\n\t\t\t// Add to current line\n\t\t\tcurrentLine += token;\n\t\t\tcurrentVisibleLength += tokenVisibleLength;\n\t\t}\n\n\t\tupdateTrackerFromText(token, tracker);\n\t}\n\n\tif (currentLine) {\n\t\t// No reset at end of final line - let caller handle it\n\t\twrapped.push(currentLine);\n\t}\n\n\treturn wrapped.length > 0 ? wrapped : [\"\"];\n}\n\nconst PUNCTUATION_REGEX = /[(){}[\\]<>.,;:'\"!?+\\-=*/\\\\|&%^$#@~`]/;\n\n/**\n * Check if a character is whitespace.\n */\nexport function isWhitespaceChar(char: string): boolean {\n\treturn /\\s/.test(char);\n}\n\n/**\n * Check if a character is punctuation.\n */\nexport function isPunctuationChar(char: string): boolean {\n\treturn PUNCTUATION_REGEX.test(char);\n}\n\nfunction breakLongWord(word: string, width: number, tracker: AnsiCodeTracker): string[] {\n\tconst lines: string[] = [];\n\tlet currentLine = tracker.getActiveCodes();\n\tlet currentWidth = 0;\n\n\t// First, separate ANSI codes from visible content\n\t// We need to handle ANSI codes specially since they're not graphemes\n\tlet i = 0;\n\tconst segments: Array<{ type: \"ansi\" | \"grapheme\"; value: string }> = [];\n\n\twhile (i < word.length) {\n\t\tconst ansiResult = extractAnsiCode(word, i);\n\t\tif (ansiResult) {\n\t\t\tsegments.push({ type: \"ansi\", value: ansiResult.code });\n\t\t\ti += ansiResult.length;\n\t\t} else {\n\t\t\t// Find the next ANSI code or end of string\n\t\t\tlet end = i;\n\t\t\twhile (end < word.length) {\n\t\t\t\tconst nextAnsi = extractAnsiCode(word, end);\n\t\t\t\tif (nextAnsi) break;\n\t\t\t\tend++;\n\t\t\t}\n\t\t\t// Segment this non-ANSI portion into graphemes\n\t\t\tconst textPortion = word.slice(i, end);\n\t\t\tfor (const seg of segmenter.segment(textPortion)) {\n\t\t\t\tsegments.push({ type: \"grapheme\", value: seg.segment });\n\t\t\t}\n\t\t\ti = end;\n\t\t}\n\t}\n\n\t// Now process segments\n\tfor (const seg of segments) {\n\t\tif (seg.type === \"ansi\") {\n\t\t\tcurrentLine += seg.value;\n\t\t\ttracker.process(seg.value);\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst grapheme = seg.value;\n\t\t// Skip empty graphemes to avoid issues with string-width calculation\n\t\tif (!grapheme) continue;\n\n\t\tconst graphemeWidth = visibleWidth(grapheme);\n\n\t\tif (currentWidth + graphemeWidth > width) {\n\t\t\t// Add specific reset for underline only (preserves background)\n\t\t\tconst lineEndReset = tracker.getLineEndReset();\n\t\t\tif (lineEndReset) {\n\t\t\t\tcurrentLine += lineEndReset;\n\t\t\t}\n\t\t\tlines.push(currentLine);\n\t\t\tcurrentLine = tracker.getActiveCodes();\n\t\t\tcurrentWidth = 0;\n\t\t}\n\n\t\tcurrentLine += grapheme;\n\t\tcurrentWidth += graphemeWidth;\n\t}\n\n\tif (currentLine) {\n\t\t// No reset at end of final segment - caller handles continuation\n\t\tlines.push(currentLine);\n\t}\n\n\treturn lines.length > 0 ? lines : [\"\"];\n}\n\n/**\n * Apply background color to a line, padding to full width.\n *\n * @param line - Line of text (may contain ANSI codes)\n * @param width - Total width to pad to\n * @param bgFn - Background color function\n * @returns Line with background applied and padded to width\n */\nexport function applyBackgroundToLine(line: string, width: number, bgFn: (text: string) => string): string {\n\t// Calculate padding needed\n\tconst visibleLen = visibleWidth(line);\n\tconst paddingNeeded = Math.max(0, width - visibleLen);\n\tconst padding = \" \".repeat(paddingNeeded);\n\n\t// Apply background to content + padding\n\tconst withPadding = line + padding;\n\treturn bgFn(withPadding);\n}\n\n/**\n * Truncate text to fit within a maximum visible width, adding ellipsis if needed.\n * Properly handles ANSI escape codes (they don't count toward width).\n *\n * @param text - Text to truncate (may contain ANSI codes)\n * @param maxWidth - Maximum visible width\n * @param ellipsis - Ellipsis string to append when truncating (default: \"...\")\n * @returns Truncated text with ellipsis if it exceeded maxWidth\n */\nexport function truncateToWidth(text: string, maxWidth: number, ellipsis: string = \"...\"): string {\n\tconst textVisibleWidth = visibleWidth(text);\n\n\tif (textVisibleWidth <= maxWidth) {\n\t\treturn text;\n\t}\n\n\tconst ellipsisWidth = visibleWidth(ellipsis);\n\tconst targetWidth = maxWidth - ellipsisWidth;\n\n\tif (targetWidth <= 0) {\n\t\treturn ellipsis.substring(0, maxWidth);\n\t}\n\n\t// Separate ANSI codes from visible content using grapheme segmentation\n\tlet i = 0;\n\tconst segments: Array<{ type: \"ansi\" | \"grapheme\"; value: string }> = [];\n\n\twhile (i < text.length) {\n\t\tconst ansiResult = extractAnsiCode(text, i);\n\t\tif (ansiResult) {\n\t\t\tsegments.push({ type: \"ansi\", value: ansiResult.code });\n\t\t\ti += ansiResult.length;\n\t\t} else {\n\t\t\t// Find the next ANSI code or end of string\n\t\t\tlet end = i;\n\t\t\twhile (end < text.length) {\n\t\t\t\tconst nextAnsi = extractAnsiCode(text, end);\n\t\t\t\tif (nextAnsi) break;\n\t\t\t\tend++;\n\t\t\t}\n\t\t\t// Segment this non-ANSI portion into graphemes\n\t\t\tconst textPortion = text.slice(i, end);\n\t\t\tfor (const seg of segmenter.segment(textPortion)) {\n\t\t\t\tsegments.push({ type: \"grapheme\", value: seg.segment });\n\t\t\t}\n\t\t\ti = end;\n\t\t}\n\t}\n\n\t// Build truncated string from segments\n\tlet result = \"\";\n\tlet currentWidth = 0;\n\n\tfor (const seg of segments) {\n\t\tif (seg.type === \"ansi\") {\n\t\t\tresult += seg.value;\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst grapheme = seg.value;\n\t\t// Skip empty graphemes to avoid issues with string-width calculation\n\t\tif (!grapheme) continue;\n\n\t\tconst graphemeWidth = visibleWidth(grapheme);\n\n\t\tif (currentWidth + graphemeWidth > targetWidth) {\n\t\t\tbreak;\n\t\t}\n\n\t\tresult += grapheme;\n\t\tcurrentWidth += graphemeWidth;\n\t}\n\n\t// Add reset code before ellipsis to prevent styling leaking into it\n\treturn `${result}\\x1b[0m${ellipsis}`;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAKA;;GAEG;AACH,wBAAgB,YAAY,IAAI,IAAI,CAAC,SAAS,CAE7C;AAmED;;GAEG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAoDhD;AAiSD;;;;;;;;;;GAUG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAoBtE;AAmFD;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEtD;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEvD;AAuED;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,GAAG,MAAM,CASzG;AAED;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,GAAE,MAAc,GAAG,MAAM,CAkEhG","sourcesContent":["import { eastAsianWidth } from \"get-east-asian-width\";\n\n// Grapheme segmenter (shared instance)\nconst segmenter = new Intl.Segmenter(undefined, { granularity: \"grapheme\" });\n\n/**\n * Get the shared grapheme segmenter instance.\n */\nexport function getSegmenter(): Intl.Segmenter {\n\treturn segmenter;\n}\n\n/**\n * Check if a grapheme cluster (after segmentation) could possibly be an RGI emoji.\n * This is a fast heuristic to avoid the expensive rgiEmojiRegex test.\n * The tested Unicode blocks are deliberately broad to account for future\n * Unicode additions.\n */\nfunction couldBeEmoji(segment: string): boolean {\n\tconst cp = segment.codePointAt(0)!;\n\treturn (\n\t\t(cp >= 0x1f000 && cp <= 0x1fbff) || // Emoji and Pictograph\n\t\t(cp >= 0x2300 && cp <= 0x23ff) || // Misc technical\n\t\t(cp >= 0x2600 && cp <= 0x27bf) || // Misc symbols, dingbats\n\t\t(cp >= 0x2b50 && cp <= 0x2b55) || // Specific stars/circles\n\t\tsegment.includes(\"\\uFE0F\") || // Contains VS16 (emoji presentation selector)\n\t\tsegment.length > 2 // Multi-codepoint sequences (ZWJ, skin tones, etc.)\n\t);\n}\n\n// Regexes for character classification (same as string-width library)\nconst zeroWidthRegex = /^(?:\\p{Default_Ignorable_Code_Point}|\\p{Control}|\\p{Mark}|\\p{Surrogate})+$/v;\nconst leadingNonPrintingRegex = /^[\\p{Default_Ignorable_Code_Point}\\p{Control}\\p{Format}\\p{Mark}\\p{Surrogate}]+/v;\nconst rgiEmojiRegex = /^\\p{RGI_Emoji}$/v;\n\n// Cache for non-ASCII strings\nconst WIDTH_CACHE_SIZE = 512;\nconst widthCache = new Map<string, number>();\n\n/**\n * Calculate the terminal width of a single grapheme cluster.\n * Based on code from the string-width library, but includes a possible-emoji\n * check to avoid running the RGI_Emoji regex unnecessarily.\n */\nfunction graphemeWidth(segment: string): number {\n\t// Zero-width clusters\n\tif (zeroWidthRegex.test(segment)) {\n\t\treturn 0;\n\t}\n\n\t// Emoji check with pre-filter\n\tif (couldBeEmoji(segment) && rgiEmojiRegex.test(segment)) {\n\t\treturn 2;\n\t}\n\n\t// Get base visible codepoint\n\tconst base = segment.replace(leadingNonPrintingRegex, \"\");\n\tconst cp = base.codePointAt(0);\n\tif (cp === undefined) {\n\t\treturn 0;\n\t}\n\n\tlet width = eastAsianWidth(cp);\n\n\t// Trailing halfwidth/fullwidth forms\n\tif (segment.length > 1) {\n\t\tfor (const char of segment.slice(1)) {\n\t\t\tconst c = char.codePointAt(0)!;\n\t\t\tif (c >= 0xff00 && c <= 0xffef) {\n\t\t\t\twidth += eastAsianWidth(c);\n\t\t\t}\n\t\t}\n\t}\n\n\treturn width;\n}\n\n/**\n * Calculate the visible width of a string in terminal columns.\n */\nexport function visibleWidth(str: string): number {\n\tif (str.length === 0) {\n\t\treturn 0;\n\t}\n\n\t// Fast path: pure ASCII printable\n\tlet isPureAscii = true;\n\tfor (let i = 0; i < str.length; i++) {\n\t\tconst code = str.charCodeAt(i);\n\t\tif (code < 0x20 || code > 0x7e) {\n\t\t\tisPureAscii = false;\n\t\t\tbreak;\n\t\t}\n\t}\n\tif (isPureAscii) {\n\t\treturn str.length;\n\t}\n\n\t// Check cache\n\tconst cached = widthCache.get(str);\n\tif (cached !== undefined) {\n\t\treturn cached;\n\t}\n\n\t// Normalize: tabs to 3 spaces, strip ANSI escape codes\n\tlet clean = str;\n\tif (str.includes(\"\\t\")) {\n\t\tclean = clean.replace(/\\t/g, \" \");\n\t}\n\tif (clean.includes(\"\\x1b\")) {\n\t\t// Strip SGR codes (\\x1b[...m) and cursor codes (\\x1b[...G/K/H/J)\n\t\tclean = clean.replace(/\\x1b\\[[0-9;]*[mGKHJ]/g, \"\");\n\t\t// Strip OSC 8 hyperlinks: \\x1b]8;;URL\\x07 and \\x1b]8;;\\x07\n\t\tclean = clean.replace(/\\x1b\\]8;;[^\\x07]*\\x07/g, \"\");\n\t}\n\n\t// Calculate width\n\tlet width = 0;\n\tfor (const { segment } of segmenter.segment(clean)) {\n\t\twidth += graphemeWidth(segment);\n\t}\n\n\t// Cache result\n\tif (widthCache.size >= WIDTH_CACHE_SIZE) {\n\t\tconst firstKey = widthCache.keys().next().value;\n\t\tif (firstKey !== undefined) {\n\t\t\twidthCache.delete(firstKey);\n\t\t}\n\t}\n\twidthCache.set(str, width);\n\n\treturn width;\n}\n\n/**\n * Extract ANSI escape sequences from a string at the given position.\n */\nfunction extractAnsiCode(str: string, pos: number): { code: string; length: number } | null {\n\tif (pos >= str.length || str[pos] !== \"\\x1b\" || str[pos + 1] !== \"[\") {\n\t\treturn null;\n\t}\n\n\tlet j = pos + 2;\n\twhile (j < str.length && str[j] && !/[mGKHJ]/.test(str[j]!)) {\n\t\tj++;\n\t}\n\n\tif (j < str.length) {\n\t\treturn {\n\t\t\tcode: str.substring(pos, j + 1),\n\t\t\tlength: j + 1 - pos,\n\t\t};\n\t}\n\n\treturn null;\n}\n\n/**\n * Track active ANSI SGR codes to preserve styling across line breaks.\n */\nclass AnsiCodeTracker {\n\t// Track individual attributes separately so we can reset them specifically\n\tprivate bold = false;\n\tprivate dim = false;\n\tprivate italic = false;\n\tprivate underline = false;\n\tprivate blink = false;\n\tprivate inverse = false;\n\tprivate hidden = false;\n\tprivate strikethrough = false;\n\tprivate fgColor: string | null = null; // Stores the full code like \"31\" or \"38;5;240\"\n\tprivate bgColor: string | null = null; // Stores the full code like \"41\" or \"48;5;240\"\n\n\tprocess(ansiCode: string): void {\n\t\tif (!ansiCode.endsWith(\"m\")) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Extract the parameters between \\x1b[ and m\n\t\tconst match = ansiCode.match(/\\x1b\\[([\\d;]*)m/);\n\t\tif (!match) return;\n\n\t\tconst params = match[1];\n\t\tif (params === \"\" || params === \"0\") {\n\t\t\t// Full reset\n\t\t\tthis.reset();\n\t\t\treturn;\n\t\t}\n\n\t\t// Parse parameters (can be semicolon-separated)\n\t\tconst parts = params.split(\";\");\n\t\tlet i = 0;\n\t\twhile (i < parts.length) {\n\t\t\tconst code = Number.parseInt(parts[i], 10);\n\n\t\t\t// Handle 256-color and RGB codes which consume multiple parameters\n\t\t\tif (code === 38 || code === 48) {\n\t\t\t\t// 38;5;N (256 color fg) or 38;2;R;G;B (RGB fg)\n\t\t\t\t// 48;5;N (256 color bg) or 48;2;R;G;B (RGB bg)\n\t\t\t\tif (parts[i + 1] === \"5\" && parts[i + 2] !== undefined) {\n\t\t\t\t\t// 256 color: 38;5;N or 48;5;N\n\t\t\t\t\tconst colorCode = `${parts[i]};${parts[i + 1]};${parts[i + 2]}`;\n\t\t\t\t\tif (code === 38) {\n\t\t\t\t\t\tthis.fgColor = colorCode;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tthis.bgColor = colorCode;\n\t\t\t\t\t}\n\t\t\t\t\ti += 3;\n\t\t\t\t\tcontinue;\n\t\t\t\t} else if (parts[i + 1] === \"2\" && parts[i + 4] !== undefined) {\n\t\t\t\t\t// RGB color: 38;2;R;G;B or 48;2;R;G;B\n\t\t\t\t\tconst colorCode = `${parts[i]};${parts[i + 1]};${parts[i + 2]};${parts[i + 3]};${parts[i + 4]}`;\n\t\t\t\t\tif (code === 38) {\n\t\t\t\t\t\tthis.fgColor = colorCode;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tthis.bgColor = colorCode;\n\t\t\t\t\t}\n\t\t\t\t\ti += 5;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Standard SGR codes\n\t\t\tswitch (code) {\n\t\t\t\tcase 0:\n\t\t\t\t\tthis.reset();\n\t\t\t\t\tbreak;\n\t\t\t\tcase 1:\n\t\t\t\t\tthis.bold = true;\n\t\t\t\t\tbreak;\n\t\t\t\tcase 2:\n\t\t\t\t\tthis.dim = true;\n\t\t\t\t\tbreak;\n\t\t\t\tcase 3:\n\t\t\t\t\tthis.italic = true;\n\t\t\t\t\tbreak;\n\t\t\t\tcase 4:\n\t\t\t\t\tthis.underline = true;\n\t\t\t\t\tbreak;\n\t\t\t\tcase 5:\n\t\t\t\t\tthis.blink = true;\n\t\t\t\t\tbreak;\n\t\t\t\tcase 7:\n\t\t\t\t\tthis.inverse = true;\n\t\t\t\t\tbreak;\n\t\t\t\tcase 8:\n\t\t\t\t\tthis.hidden = true;\n\t\t\t\t\tbreak;\n\t\t\t\tcase 9:\n\t\t\t\t\tthis.strikethrough = true;\n\t\t\t\t\tbreak;\n\t\t\t\tcase 21:\n\t\t\t\t\tthis.bold = false;\n\t\t\t\t\tbreak; // Some terminals\n\t\t\t\tcase 22:\n\t\t\t\t\tthis.bold = false;\n\t\t\t\t\tthis.dim = false;\n\t\t\t\t\tbreak;\n\t\t\t\tcase 23:\n\t\t\t\t\tthis.italic = false;\n\t\t\t\t\tbreak;\n\t\t\t\tcase 24:\n\t\t\t\t\tthis.underline = false;\n\t\t\t\t\tbreak;\n\t\t\t\tcase 25:\n\t\t\t\t\tthis.blink = false;\n\t\t\t\t\tbreak;\n\t\t\t\tcase 27:\n\t\t\t\t\tthis.inverse = false;\n\t\t\t\t\tbreak;\n\t\t\t\tcase 28:\n\t\t\t\t\tthis.hidden = false;\n\t\t\t\t\tbreak;\n\t\t\t\tcase 29:\n\t\t\t\t\tthis.strikethrough = false;\n\t\t\t\t\tbreak;\n\t\t\t\tcase 39:\n\t\t\t\t\tthis.fgColor = null;\n\t\t\t\t\tbreak; // Default fg\n\t\t\t\tcase 49:\n\t\t\t\t\tthis.bgColor = null;\n\t\t\t\t\tbreak; // Default bg\n\t\t\t\tdefault:\n\t\t\t\t\t// Standard foreground colors 30-37, 90-97\n\t\t\t\t\tif ((code >= 30 && code <= 37) || (code >= 90 && code <= 97)) {\n\t\t\t\t\t\tthis.fgColor = String(code);\n\t\t\t\t\t}\n\t\t\t\t\t// Standard background colors 40-47, 100-107\n\t\t\t\t\telse if ((code >= 40 && code <= 47) || (code >= 100 && code <= 107)) {\n\t\t\t\t\t\tthis.bgColor = String(code);\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\ti++;\n\t\t}\n\t}\n\n\tprivate reset(): void {\n\t\tthis.bold = false;\n\t\tthis.dim = false;\n\t\tthis.italic = false;\n\t\tthis.underline = false;\n\t\tthis.blink = false;\n\t\tthis.inverse = false;\n\t\tthis.hidden = false;\n\t\tthis.strikethrough = false;\n\t\tthis.fgColor = null;\n\t\tthis.bgColor = null;\n\t}\n\n\tgetActiveCodes(): string {\n\t\tconst codes: string[] = [];\n\t\tif (this.bold) codes.push(\"1\");\n\t\tif (this.dim) codes.push(\"2\");\n\t\tif (this.italic) codes.push(\"3\");\n\t\tif (this.underline) codes.push(\"4\");\n\t\tif (this.blink) codes.push(\"5\");\n\t\tif (this.inverse) codes.push(\"7\");\n\t\tif (this.hidden) codes.push(\"8\");\n\t\tif (this.strikethrough) codes.push(\"9\");\n\t\tif (this.fgColor) codes.push(this.fgColor);\n\t\tif (this.bgColor) codes.push(this.bgColor);\n\n\t\tif (codes.length === 0) return \"\";\n\t\treturn `\\x1b[${codes.join(\";\")}m`;\n\t}\n\n\thasActiveCodes(): boolean {\n\t\treturn (\n\t\t\tthis.bold ||\n\t\t\tthis.dim ||\n\t\t\tthis.italic ||\n\t\t\tthis.underline ||\n\t\t\tthis.blink ||\n\t\t\tthis.inverse ||\n\t\t\tthis.hidden ||\n\t\t\tthis.strikethrough ||\n\t\t\tthis.fgColor !== null ||\n\t\t\tthis.bgColor !== null\n\t\t);\n\t}\n\n\t/**\n\t * Get reset codes for attributes that need to be turned off at line end,\n\t * specifically underline which bleeds into padding.\n\t * Returns empty string if no problematic attributes are active.\n\t */\n\tgetLineEndReset(): string {\n\t\t// Only underline causes visual bleeding into padding\n\t\t// Other attributes like colors don't visually bleed to padding\n\t\tif (this.underline) {\n\t\t\treturn \"\\x1b[24m\"; // Underline off only\n\t\t}\n\t\treturn \"\";\n\t}\n}\n\nfunction updateTrackerFromText(text: string, tracker: AnsiCodeTracker): void {\n\tlet i = 0;\n\twhile (i < text.length) {\n\t\tconst ansiResult = extractAnsiCode(text, i);\n\t\tif (ansiResult) {\n\t\t\ttracker.process(ansiResult.code);\n\t\t\ti += ansiResult.length;\n\t\t} else {\n\t\t\ti++;\n\t\t}\n\t}\n}\n\n/**\n * Split text into words while keeping ANSI codes attached.\n */\nfunction splitIntoTokensWithAnsi(text: string): string[] {\n\tconst tokens: string[] = [];\n\tlet current = \"\";\n\tlet pendingAnsi = \"\"; // ANSI codes waiting to be attached to next visible content\n\tlet inWhitespace = false;\n\tlet i = 0;\n\n\twhile (i < text.length) {\n\t\tconst ansiResult = extractAnsiCode(text, i);\n\t\tif (ansiResult) {\n\t\t\t// Hold ANSI codes separately - they'll be attached to the next visible char\n\t\t\tpendingAnsi += ansiResult.code;\n\t\t\ti += ansiResult.length;\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst char = text[i];\n\t\tconst charIsSpace = char === \" \";\n\n\t\tif (charIsSpace !== inWhitespace && current) {\n\t\t\t// Switching between whitespace and non-whitespace, push current token\n\t\t\ttokens.push(current);\n\t\t\tcurrent = \"\";\n\t\t}\n\n\t\t// Attach any pending ANSI codes to this visible character\n\t\tif (pendingAnsi) {\n\t\t\tcurrent += pendingAnsi;\n\t\t\tpendingAnsi = \"\";\n\t\t}\n\n\t\tinWhitespace = charIsSpace;\n\t\tcurrent += char;\n\t\ti++;\n\t}\n\n\t// Handle any remaining pending ANSI codes (attach to last token)\n\tif (pendingAnsi) {\n\t\tcurrent += pendingAnsi;\n\t}\n\n\tif (current) {\n\t\ttokens.push(current);\n\t}\n\n\treturn tokens;\n}\n\n/**\n * Wrap text with ANSI codes preserved.\n *\n * ONLY does word wrapping - NO padding, NO background colors.\n * Returns lines where each line is <= width visible chars.\n * Active ANSI codes are preserved across line breaks.\n *\n * @param text - Text to wrap (may contain ANSI codes and newlines)\n * @param width - Maximum visible width per line\n * @returns Array of wrapped lines (NOT padded to width)\n */\nexport function wrapTextWithAnsi(text: string, width: number): string[] {\n\tif (!text) {\n\t\treturn [\"\"];\n\t}\n\n\t// Handle newlines by processing each line separately\n\t// Track ANSI state across lines so styles carry over after literal newlines\n\tconst inputLines = text.split(\"\\n\");\n\tconst result: string[] = [];\n\tconst tracker = new AnsiCodeTracker();\n\n\tfor (const inputLine of inputLines) {\n\t\t// Prepend active ANSI codes from previous lines (except for first line)\n\t\tconst prefix = result.length > 0 ? tracker.getActiveCodes() : \"\";\n\t\tresult.push(...wrapSingleLine(prefix + inputLine, width));\n\t\t// Update tracker with codes from this line for next iteration\n\t\tupdateTrackerFromText(inputLine, tracker);\n\t}\n\n\treturn result.length > 0 ? result : [\"\"];\n}\n\nfunction wrapSingleLine(line: string, width: number): string[] {\n\tif (!line) {\n\t\treturn [\"\"];\n\t}\n\n\tconst visibleLength = visibleWidth(line);\n\tif (visibleLength <= width) {\n\t\treturn [line];\n\t}\n\n\tconst wrapped: string[] = [];\n\tconst tracker = new AnsiCodeTracker();\n\tconst tokens = splitIntoTokensWithAnsi(line);\n\n\tlet currentLine = \"\";\n\tlet currentVisibleLength = 0;\n\n\tfor (const token of tokens) {\n\t\tconst tokenVisibleLength = visibleWidth(token);\n\t\tconst isWhitespace = token.trim() === \"\";\n\n\t\t// Token itself is too long - break it character by character\n\t\tif (tokenVisibleLength > width && !isWhitespace) {\n\t\t\tif (currentLine) {\n\t\t\t\t// Add specific reset for underline only (preserves background)\n\t\t\t\tconst lineEndReset = tracker.getLineEndReset();\n\t\t\t\tif (lineEndReset) {\n\t\t\t\t\tcurrentLine += lineEndReset;\n\t\t\t\t}\n\t\t\t\twrapped.push(currentLine);\n\t\t\t\tcurrentLine = \"\";\n\t\t\t\tcurrentVisibleLength = 0;\n\t\t\t}\n\n\t\t\t// Break long token - breakLongWord handles its own resets\n\t\t\tconst broken = breakLongWord(token, width, tracker);\n\t\t\twrapped.push(...broken.slice(0, -1));\n\t\t\tcurrentLine = broken[broken.length - 1];\n\t\t\tcurrentVisibleLength = visibleWidth(currentLine);\n\t\t\tcontinue;\n\t\t}\n\n\t\t// Check if adding this token would exceed width\n\t\tconst totalNeeded = currentVisibleLength + tokenVisibleLength;\n\n\t\tif (totalNeeded > width && currentVisibleLength > 0) {\n\t\t\t// Trim trailing whitespace, then add underline reset (not full reset, to preserve background)\n\t\t\tlet lineToWrap = currentLine.trimEnd();\n\t\t\tconst lineEndReset = tracker.getLineEndReset();\n\t\t\tif (lineEndReset) {\n\t\t\t\tlineToWrap += lineEndReset;\n\t\t\t}\n\t\t\twrapped.push(lineToWrap);\n\t\t\tif (isWhitespace) {\n\t\t\t\t// Don't start new line with whitespace\n\t\t\t\tcurrentLine = tracker.getActiveCodes();\n\t\t\t\tcurrentVisibleLength = 0;\n\t\t\t} else {\n\t\t\t\tcurrentLine = tracker.getActiveCodes() + token;\n\t\t\t\tcurrentVisibleLength = tokenVisibleLength;\n\t\t\t}\n\t\t} else {\n\t\t\t// Add to current line\n\t\t\tcurrentLine += token;\n\t\t\tcurrentVisibleLength += tokenVisibleLength;\n\t\t}\n\n\t\tupdateTrackerFromText(token, tracker);\n\t}\n\n\tif (currentLine) {\n\t\t// No reset at end of final line - let caller handle it\n\t\twrapped.push(currentLine);\n\t}\n\n\t// Trailing whitespace can cause lines to exceed the requested width\n\treturn wrapped.length > 0 ? wrapped.map((line) => line.trimEnd()) : [\"\"];\n}\n\nconst PUNCTUATION_REGEX = /[(){}[\\]<>.,;:'\"!?+\\-=*/\\\\|&%^$#@~`]/;\n\n/**\n * Check if a character is whitespace.\n */\nexport function isWhitespaceChar(char: string): boolean {\n\treturn /\\s/.test(char);\n}\n\n/**\n * Check if a character is punctuation.\n */\nexport function isPunctuationChar(char: string): boolean {\n\treturn PUNCTUATION_REGEX.test(char);\n}\n\nfunction breakLongWord(word: string, width: number, tracker: AnsiCodeTracker): string[] {\n\tconst lines: string[] = [];\n\tlet currentLine = tracker.getActiveCodes();\n\tlet currentWidth = 0;\n\n\t// First, separate ANSI codes from visible content\n\t// We need to handle ANSI codes specially since they're not graphemes\n\tlet i = 0;\n\tconst segments: Array<{ type: \"ansi\" | \"grapheme\"; value: string }> = [];\n\n\twhile (i < word.length) {\n\t\tconst ansiResult = extractAnsiCode(word, i);\n\t\tif (ansiResult) {\n\t\t\tsegments.push({ type: \"ansi\", value: ansiResult.code });\n\t\t\ti += ansiResult.length;\n\t\t} else {\n\t\t\t// Find the next ANSI code or end of string\n\t\t\tlet end = i;\n\t\t\twhile (end < word.length) {\n\t\t\t\tconst nextAnsi = extractAnsiCode(word, end);\n\t\t\t\tif (nextAnsi) break;\n\t\t\t\tend++;\n\t\t\t}\n\t\t\t// Segment this non-ANSI portion into graphemes\n\t\t\tconst textPortion = word.slice(i, end);\n\t\t\tfor (const seg of segmenter.segment(textPortion)) {\n\t\t\t\tsegments.push({ type: \"grapheme\", value: seg.segment });\n\t\t\t}\n\t\t\ti = end;\n\t\t}\n\t}\n\n\t// Now process segments\n\tfor (const seg of segments) {\n\t\tif (seg.type === \"ansi\") {\n\t\t\tcurrentLine += seg.value;\n\t\t\ttracker.process(seg.value);\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst grapheme = seg.value;\n\t\t// Skip empty graphemes to avoid issues with string-width calculation\n\t\tif (!grapheme) continue;\n\n\t\tconst graphemeWidth = visibleWidth(grapheme);\n\n\t\tif (currentWidth + graphemeWidth > width) {\n\t\t\t// Add specific reset for underline only (preserves background)\n\t\t\tconst lineEndReset = tracker.getLineEndReset();\n\t\t\tif (lineEndReset) {\n\t\t\t\tcurrentLine += lineEndReset;\n\t\t\t}\n\t\t\tlines.push(currentLine);\n\t\t\tcurrentLine = tracker.getActiveCodes();\n\t\t\tcurrentWidth = 0;\n\t\t}\n\n\t\tcurrentLine += grapheme;\n\t\tcurrentWidth += graphemeWidth;\n\t}\n\n\tif (currentLine) {\n\t\t// No reset at end of final segment - caller handles continuation\n\t\tlines.push(currentLine);\n\t}\n\n\treturn lines.length > 0 ? lines : [\"\"];\n}\n\n/**\n * Apply background color to a line, padding to full width.\n *\n * @param line - Line of text (may contain ANSI codes)\n * @param width - Total width to pad to\n * @param bgFn - Background color function\n * @returns Line with background applied and padded to width\n */\nexport function applyBackgroundToLine(line: string, width: number, bgFn: (text: string) => string): string {\n\t// Calculate padding needed\n\tconst visibleLen = visibleWidth(line);\n\tconst paddingNeeded = Math.max(0, width - visibleLen);\n\tconst padding = \" \".repeat(paddingNeeded);\n\n\t// Apply background to content + padding\n\tconst withPadding = line + padding;\n\treturn bgFn(withPadding);\n}\n\n/**\n * Truncate text to fit within a maximum visible width, adding ellipsis if needed.\n * Properly handles ANSI escape codes (they don't count toward width).\n *\n * @param text - Text to truncate (may contain ANSI codes)\n * @param maxWidth - Maximum visible width\n * @param ellipsis - Ellipsis string to append when truncating (default: \"...\")\n * @returns Truncated text with ellipsis if it exceeded maxWidth\n */\nexport function truncateToWidth(text: string, maxWidth: number, ellipsis: string = \"...\"): string {\n\tconst textVisibleWidth = visibleWidth(text);\n\n\tif (textVisibleWidth <= maxWidth) {\n\t\treturn text;\n\t}\n\n\tconst ellipsisWidth = visibleWidth(ellipsis);\n\tconst targetWidth = maxWidth - ellipsisWidth;\n\n\tif (targetWidth <= 0) {\n\t\treturn ellipsis.substring(0, maxWidth);\n\t}\n\n\t// Separate ANSI codes from visible content using grapheme segmentation\n\tlet i = 0;\n\tconst segments: Array<{ type: \"ansi\" | \"grapheme\"; value: string }> = [];\n\n\twhile (i < text.length) {\n\t\tconst ansiResult = extractAnsiCode(text, i);\n\t\tif (ansiResult) {\n\t\t\tsegments.push({ type: \"ansi\", value: ansiResult.code });\n\t\t\ti += ansiResult.length;\n\t\t} else {\n\t\t\t// Find the next ANSI code or end of string\n\t\t\tlet end = i;\n\t\t\twhile (end < text.length) {\n\t\t\t\tconst nextAnsi = extractAnsiCode(text, end);\n\t\t\t\tif (nextAnsi) break;\n\t\t\t\tend++;\n\t\t\t}\n\t\t\t// Segment this non-ANSI portion into graphemes\n\t\t\tconst textPortion = text.slice(i, end);\n\t\t\tfor (const seg of segmenter.segment(textPortion)) {\n\t\t\t\tsegments.push({ type: \"grapheme\", value: seg.segment });\n\t\t\t}\n\t\t\ti = end;\n\t\t}\n\t}\n\n\t// Build truncated string from segments\n\tlet result = \"\";\n\tlet currentWidth = 0;\n\n\tfor (const seg of segments) {\n\t\tif (seg.type === \"ansi\") {\n\t\t\tresult += seg.value;\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst grapheme = seg.value;\n\t\t// Skip empty graphemes to avoid issues with string-width calculation\n\t\tif (!grapheme) continue;\n\n\t\tconst graphemeWidth = visibleWidth(grapheme);\n\n\t\tif (currentWidth + graphemeWidth > targetWidth) {\n\t\t\tbreak;\n\t\t}\n\n\t\tresult += grapheme;\n\t\tcurrentWidth += graphemeWidth;\n\t}\n\n\t// Add reset code before ellipsis to prevent styling leaking into it\n\treturn `${result}\\x1b[0m${ellipsis}`;\n}\n"]}
|
package/dist/utils.js
CHANGED
|
@@ -454,7 +454,7 @@ function wrapSingleLine(line, width) {
|
|
|
454
454
|
// Check if adding this token would exceed width
|
|
455
455
|
const totalNeeded = currentVisibleLength + tokenVisibleLength;
|
|
456
456
|
if (totalNeeded > width && currentVisibleLength > 0) {
|
|
457
|
-
//
|
|
457
|
+
// Trim trailing whitespace, then add underline reset (not full reset, to preserve background)
|
|
458
458
|
let lineToWrap = currentLine.trimEnd();
|
|
459
459
|
const lineEndReset = tracker.getLineEndReset();
|
|
460
460
|
if (lineEndReset) {
|
|
@@ -482,7 +482,8 @@ function wrapSingleLine(line, width) {
|
|
|
482
482
|
// No reset at end of final line - let caller handle it
|
|
483
483
|
wrapped.push(currentLine);
|
|
484
484
|
}
|
|
485
|
-
|
|
485
|
+
// Trailing whitespace can cause lines to exceed the requested width
|
|
486
|
+
return wrapped.length > 0 ? wrapped.map((line) => line.trimEnd()) : [""];
|
|
486
487
|
}
|
|
487
488
|
const PUNCTUATION_REGEX = /[(){}[\]<>.,;:'"!?+\-=*/\\|&%^$#@~`]/;
|
|
488
489
|
/**
|