Address edge-case with kitty protocol in wezterm

packages/tui/src/stdin-buffer.ts — in extractCompleteSequences, after finding \x1b\x1b as "complete" (legacy meta-key), check if the next character in the buffer would start a new escape sequence ([, ], O, P, _). If so, emit only the first \x1b and let the second ESC begin a new parse iteration.

packages/tui/test/stdin-buffer.test.ts — three new cases in the Kitty section:
- \x1b\x1b[27;129:3u (num_lock) splits into ["\x1b", "\x1b[27;129:3u"]
- \x1b\x1b[27;1:3u (no num_lock) splits into ["\x1b", "\x1b[27;1:3u"]
- \x1b\x1b alone (no following CSI) still emits as ["\x1b\x1b"] — preserves ctrl+alt+[
This commit is contained in:
Mikhail f. Shiryaev
2026-05-13 18:42:05 +02:00
parent a5cca409d8
commit 7f30fb6136
2 changed files with 43 additions and 0 deletions

View File

@@ -198,6 +198,26 @@ describe("StdinBuffer", () => {
assert.deepStrictEqual(emittedSequences, ["\x1b[3;1:3~"]);
});
it("should split ESC+ESC+CSI into standalone ESC and the CSI sequence (WezTerm Escape key regression)", () => {
// WezTerm with enable_kitty_keyboard sends Escape key press as raw \x1b
// and the release as a full Kitty CSI-u sequence, concatenated.
// The buffer must not treat \x1b\x1b as a complete meta-key when the
// following byte starts a new escape sequence.
processInput("\x1b\x1b[27;129:3u");
assert.deepStrictEqual(emittedSequences, ["\x1b", "\x1b[27;129:3u"]);
});
it("should split ESC+ESC+CSI with no modifier (no num_lock)", () => {
processInput("\x1b\x1b[27;1:3u");
assert.deepStrictEqual(emittedSequences, ["\x1b", "\x1b[27;1:3u"]);
});
it("should still emit ESC+ESC as a single sequence when not followed by a new escape", () => {
// \x1b\x1b alone (no following CSI) stays as-is — e.g. ctrl+alt+[
processInput("\x1b\x1b");
assert.deepStrictEqual(emittedSequences, ["\x1b\x1b"]);
});
it("should handle plain characters mixed with Kitty sequences", () => {
// Plain 'a' followed by Kitty release
processInput("a\x1b[97;1:3u");