mirror of
https://github.com/aphrody-labs/opentui.git
synced 2026-10-13 21:08:53 +00:00
core: keep parseKeypress from changing the caller's Buffer (#1653)
`parseKeypress` decoded a single byte above 127 (the legacy 8-bit meta encoding) by subtracting 128 from the caller's Buffer in place. Parsing the same Buffer again returned the key without meta, and a caller that logs, retries, or forwards the raw bytes saw changed data. The renderer was not affected, because the stdin parser passes a fresh one-byte Buffer, so only direct callers of the exported function saw it. It now decodes the byte with `String.fromCharCode(byte - 128)` and leaves the Buffer alone. The returned key is the same as before for every input; no other code in `parseKeypress` or its callers writes into an input Buffer. The Buffer tests in `parse.keypress.test.ts` are now one table that checks the key and that the Buffer is byte for byte unchanged after the call: ASCII, a two-byte character, 127, 128, the high-bit meta bytes 160, 225, and 255, a Kitty sequence, and an SGR mouse report. Restoring the in-place write, decoding the wrong byte, or moving the bounds of the single-byte check each fail it. Fixes #1638
This commit is contained in:
1 parent
ae3193f169
commit
399fcac46c
2 files changed
+25
-32
No files matched your search
@@ -887,36 +887,30 @@ test("parseKeypress - backspace key with modifiers (Kitty keyboard protocol)", (
|
||||
expect(shiftBackspace.source).toBe("kitty")
|
||||
})
|
||||
|
||||
test("parseKeypress - Buffer input", () => {
|
||||
const buf = Buffer.from("a")
|
||||
expect(parseKeypress(buf)).toEqual({
|
||||
eventType: "press",
|
||||
name: "a",
|
||||
ctrl: false,
|
||||
meta: false,
|
||||
shift: false,
|
||||
option: false,
|
||||
number: false,
|
||||
sequence: "a",
|
||||
raw: "a",
|
||||
source: "raw",
|
||||
})
|
||||
})
|
||||
|
||||
test("parseKeypress - high byte buffer handling", () => {
|
||||
const buf = Buffer.from([160]) // 128 + 32, should become \x1b + " "
|
||||
expect(parseKeypress(buf)).toEqual({
|
||||
eventType: "press",
|
||||
name: "space",
|
||||
ctrl: false,
|
||||
meta: true,
|
||||
shift: false,
|
||||
option: false,
|
||||
number: false,
|
||||
sequence: "\x1b ",
|
||||
raw: "\x1b ",
|
||||
source: "raw",
|
||||
})
|
||||
test("parseKeypress - Buffer input leaves the buffer unchanged", () => {
|
||||
const press = { ctrl: false, shift: false, option: false, number: false, eventType: "press", source: "raw" } as const
|
||||
const key = (name: string, sequence: string, meta = false) => ({ ...press, name, meta, sequence, raw: sequence })
|
||||
const kittyFlags = { super: false, hyper: false, capsLock: false, numLock: false }
|
||||
const cases: Array<[Buffer, boolean, ParsedKey | null]> = [
|
||||
[Buffer.from("a"), false, key("a", "a")],
|
||||
[Buffer.from("é"), false, key("é", "é")], // two bytes, the first above 127
|
||||
[Buffer.from([127]), false, key("backspace", "\x7f")],
|
||||
[Buffer.from([128]), false, { ...key("space", "\x1b\x00", true), ctrl: true }],
|
||||
[Buffer.from([160]), false, key("space", "\x1b ", true)], // 128 + 32
|
||||
[Buffer.from([225]), false, key("a", "\x1ba", true)],
|
||||
[Buffer.from([255]), false, key("backspace", "\x1b\x7f", true)],
|
||||
[
|
||||
Buffer.from("\x1b[97;5u"),
|
||||
true,
|
||||
{ ...key("a", "a"), ...kittyFlags, ctrl: true, raw: "\x1b[97;5u", source: "kitty" },
|
||||
],
|
||||
[Buffer.from("\x1b[<0;1;1M"), false, null],
|
||||
]
|
||||
for (const [buf, useKittyKeyboard, expected] of cases) {
|
||||
const before = Buffer.from(buf)
|
||||
expect(parseKeypress(buf, { useKittyKeyboard })).toEqual(expected)
|
||||
expect(buf).toEqual(before)
|
||||
}
|
||||
})
|
||||
|
||||
test("parseKeypress - empty input", () => {
|
||||
|
||||
@@ -216,8 +216,7 @@ export const parseKeypress = (s: Buffer | string = "", options: ParseKeypressOpt
|
||||
|
||||
if (Buffer.isBuffer(s)) {
|
||||
if (s[0]! > 127 && s[1] === undefined) {
|
||||
;(s[0] as unknown as number) -= 128
|
||||
s = "\x1b" + String(s)
|
||||
s = "\x1b" + String.fromCharCode(s[0]! - 128)
|
||||
} else {
|
||||
s = String(s)
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user