UNPKG

emit-keypress

Version:

Drop-dead simple keypress event emitter for Node.js. Create powerful CLI applications and experiences with ease.

1,415 lines (1,407 loc) 43 kB
var __defProp = Object.defineProperty; var __name = (target, value) => __defProp(target, "name", { value, configurable: true }); // index.ts import { stdin, stdout } from "node:process"; // src/emit-keypress.ts import { StringDecoder } from "node:string_decoder"; import { clearTimeout as clearTimeout2, setTimeout as setTimeout2 } from "node:timers"; // src/keypress.ts var kEscape = "\x1B"; var kSubstringSearch = Symbol("kSubstringSearch"); var kUTF16SurrogateThreshold = 65536; function CSI(strings, ...args) { let ret = `${kEscape}[`; for (let n = 0; n < strings.length; n++) { ret += strings[n]; if (n < args.length) { ret += args[n]; } } return ret; } __name(CSI, "CSI"); CSI.kEscape = kEscape; CSI.kClearToLineBeginning = CSI`1K`; CSI.kClearToLineEnd = CSI`0K`; CSI.kClearLine = CSI`2K`; CSI.kClearScreenDown = CSI`0J`; function charLengthAt(str, i) { if (str.length <= i) { return 1; } return str.codePointAt(i) >= kUTF16SurrogateThreshold ? 2 : 1; } __name(charLengthAt, "charLengthAt"); function* emitKeys(stream) { while (true) { let ch = yield; let s = ch; let escaped = false; const key = { sequence: null, name: void 0, ctrl: false, meta: false, shift: false, fn: false }; if (ch === kEscape) { escaped = true; s += ch = yield; if (ch === kEscape) { s += ch = yield; } } if (escaped && (ch === "O" || ch === "[")) { let code = ch; let modifier = 0; if (ch === "O") { s += ch = yield; if (ch >= "0" && ch <= "9") { modifier = (ch >> 0) - 1; s += ch = yield; } code += ch; } else if (ch === "[") { s += ch = yield; if (ch === "[") { code += ch; s += ch = yield; } const cmdStart = s.length - 1; if (ch >= "0" && ch <= "9") { s += ch = yield; if (ch >= "0" && ch <= "9") { s += ch = yield; if (ch >= "0" && ch <= "9") { s += ch = yield; } } } if (ch === ";") { s += ch = yield; if (ch >= "0" && ch <= "9") { s += yield; } } const cmd = s.slice(cmdStart); let match; if (match = /^(?:(\d\d?)(?:;(\d))?([~^$])|(\d{3}~))$/.exec(cmd)) { if (match[4]) { code += match[4]; } else { code += match[1] + match[3]; modifier = (match[2] || 1) - 1; } } else if (match = /^((\d;)?(\d))?([A-Za-z])$/.exec(cmd)) { code += match[4]; modifier = (match[3] || 1) - 1; } else { code += cmd; } } if (/\[(?:1[5-9]|2[0-2]);10/.test(code)) { s += ch = yield; } key.ctrl = Boolean(modifier & 4); key.meta = Boolean(modifier & 10); key.shift = Boolean(modifier & 1); key.code = code; if (!key.meta) { const parts = [...s]; if (parts[0] === "\x1B" && parts[1] === "\x1B") { key.meta = true; } } if (/\x1B\[1;[59][FH]/.test(s)) { key.fn = true; } switch (code) { case "[M": { key.name = "mouse"; s += ch = yield; s += ch = yield; s += ch = yield; break; } case "[P": key.name = "f1"; key.fn = true; break; case "[Q": key.name = "f2"; key.fn = true; break; case "[R": key.name = "f3"; key.fn = true; break; case "[S": key.name = "f4"; key.fn = true; break; case "OP": key.name = "f1"; key.fn = true; break; case "OQ": key.name = "f2"; key.fn = true; break; case "OR": key.name = "f3"; key.fn = true; break; case "OS": key.name = "f4"; key.fn = true; break; case "[11~": key.name = "f1"; key.fn = true; break; case "[12~": key.name = "f2"; key.fn = true; break; case "[13~": key.name = "f3"; key.fn = true; break; case "[14~": key.name = "f4"; key.fn = true; break; case "[200~": key.name = "paste-start"; break; case "[201~": key.name = "paste-end"; break; case "[[A": key.name = "f1"; key.fn = true; break; case "[[B": key.name = "f2"; key.fn = true; break; case "[[C": key.name = "f3"; key.fn = true; break; case "[[D": key.name = "f4"; key.fn = true; break; case "[[E": key.name = "f5"; key.fn = true; break; case "[15~": key.name = "f5"; key.fn = true; break; case "[17~": key.name = "f6"; key.fn = true; break; case "[18~": key.name = "f7"; key.fn = true; break; case "[19~": key.name = "f8"; key.fn = true; break; case "[20~": key.name = "f9"; key.fn = true; break; case "[21~": key.name = "f10"; key.fn = true; break; case "[23~": key.name = "f11"; key.fn = true; break; case "[24~": key.name = "f12"; key.fn = true; break; case "[15;10": key.name = "f5"; key.fn = true; key.shift = true; key.meta = true; break; case "[17;10": key.name = "f7"; key.fn = true; key.shift = true; key.meta = true; break; case "[18;10": key.name = "f8"; key.fn = true; key.shift = true; key.meta = true; break; case "[19;10": key.name = "f9"; key.fn = true; key.shift = true; key.meta = true; break; case "[20;10": key.name = "f10"; key.fn = true; key.shift = true; key.meta = true; break; case "[21;10": key.name = "f11"; key.fn = true; key.shift = true; key.meta = true; break; case "[22;10": key.name = "f12"; key.fn = true; key.shift = true; key.meta = true; break; case "[A": key.name = "up"; break; case "[B": key.name = "down"; break; case "[C": key.name = "right"; break; case "[D": key.name = "left"; break; case "[E": key.name = "clear"; break; case "[F": key.name = "end"; break; case "[H": key.name = "home"; break; case "OA": key.name = "up"; break; case "OB": key.name = "down"; break; case "OC": key.name = "right"; break; case "OD": key.name = "left"; break; case "OE": key.name = "clear"; break; case "OF": key.name = "end"; break; case "OH": key.name = "home"; break; case "[1~": key.name = "home"; break; case "[2~": key.name = "insert"; break; case "[3~": key.name = "delete"; key.shift = !s.includes("3;5~"); key.fn = /^\[3;[256]~$/.test(s.slice(1)); break; case "[4~": key.name = "end"; break; case "[5~": key.name = "pageup"; break; case "[6~": key.name = "pagedown"; break; case "[[5~": key.name = "pageup"; break; case "[[6~": key.name = "pagedown"; break; case "[7~": key.name = "home"; break; case "[8~": key.name = "end"; break; case "[a": key.name = "up"; key.shift = true; break; case "[b": key.name = "down"; key.shift = true; break; case "[c": key.name = "right"; key.shift = true; break; case "[d": key.name = "left"; key.shift = true; break; case "[e": key.name = "clear"; key.shift = true; break; case "[2$": key.name = "insert"; key.shift = true; break; case "[3$": key.name = "delete"; key.shift = true; break; case "[5$": key.name = "pageup"; key.shift = true; break; case "[6$": key.name = "pagedown"; key.shift = true; break; case "[7$": key.name = "home"; key.shift = true; break; case "[8$": key.name = "end"; key.shift = true; break; case "Oa": key.name = "up"; key.ctrl = true; break; case "Ob": key.name = "down"; key.ctrl = true; break; case "Oc": key.name = "right"; key.ctrl = true; break; case "Od": key.name = "left"; key.ctrl = true; break; case "Oe": key.name = "clear"; key.ctrl = true; break; case "[2^": key.name = "insert"; key.ctrl = true; break; case "[3^": key.name = "delete"; key.ctrl = true; break; case "[5^": key.name = "pageup"; key.ctrl = true; break; case "[6^": key.name = "pagedown"; key.ctrl = true; break; case "[7^": key.name = "home"; key.ctrl = true; break; case "[8^": key.name = "end"; key.ctrl = true; break; case "[3;5~": key.name = "delete"; key.ctrl = true; key.fn = true; break; case "[1;13": case "[1;14": key.shift = code === "[1;14"; key.ctrl = true; key.meta = true; if (ch = yield) { s += ch; switch (ch) { case "A": key.name = "up"; break; case "B": key.name = "down"; break; case "C": key.name = "right"; break; case "D": key.name = "left"; break; case "F": key.name = "right"; key.fn = true; break; case "H": key.name = "left"; key.fn = true; break; default: break; } } break; case "[3;10": if (ch = yield) { s += ch; key.name = "delete"; key.shift = true; key.meta = true; key.fn = true; } break; case "[3;13": case "[3;14": if (ch = yield) { s += ch; key.name = "delete"; key.ctrl = true; key.shift = code === "[3;14"; key.meta = true; key.fn = true; } break; case "[5;14": case "[6;14": if (ch = yield) { s += ch; key.name = code === "[5;14" ? "up" : "down"; key.ctrl = true; key.shift = true; key.meta = true; key.fn = true; } break; case "[1;10": if (ch = yield) { s += ch; } switch (ch) { case "A": key.name = "up"; break; case "B": key.name = "down"; break; case "C": key.name = "right"; break; case "D": key.name = "left"; break; case "F": key.name = "right"; key.fn = true; break; case "H": key.name = "left"; key.fn = true; break; case "P": key.name = "f1"; key.fn = true; break; case "Q": key.name = "f2"; key.fn = true; break; case "R": key.name = "f3"; key.fn = true; break; case "S": key.name = "f4"; key.fn = true; break; default: break; } key.shift = true; key.meta = true; break; case "[5;10": case "[6;10": if (ch = yield) { s += ch; key.name = code === "[5;10" ? "up" : "down"; key.shift = true; key.fn = true; } key.meta = true; break; case "[Z": key.name = "tab"; key.shift = true; break; default: key.name = void 0; break; } } else if (ch === "\r") { key.name = "return"; key.meta = escaped; } else if (ch === "\n") { key.name = "enter"; key.meta = escaped; } else if (ch === " ") { key.name = "tab"; key.meta = escaped; } else if (ch === "\b") { key.name = "backspace"; key.ctrl = true; key.meta = escaped; } else if (ch === "\x7F") { key.name = "backspace"; key.meta = escaped; } else if (ch === kEscape) { key.name = "escape"; key.meta = escaped; } else if (ch === " ") { key.name = "space"; key.meta = escaped; } else if (!escaped && ch <= "") { key.name = String.fromCharCode(ch.charCodeAt(0) + "a".charCodeAt(0) - 1); key.ctrl = true; } else if (/^[0-9A-Za-z]$/.exec(ch) !== null) { key.name = ch; key.shift = /^[A-Z]$/.exec(ch) !== null; key.meta = escaped; } else if (escaped) { key.name = ch.length ? void 0 : "escape"; key.meta = false; } key.sequence = s; if (s.length !== 0 && (key.name !== void 0 || escaped)) { stream.emit("keypress", escaped ? void 0 : s, key); } else if (charLengthAt(s, 0) === s.length) { stream.emit("keypress", s, key); } } } __name(emitKeys, "emitKeys"); // src/emit-keypress.ts var { kEscape: kEscape2 } = CSI; var KEYPRESS_DECODER = Symbol("keypress-decoder"); var ESCAPE_DECODER = Symbol("escape-decoder"); var kSawKeyPress = Symbol("saw-key-press"); var ESCAPE_CODE_TIMEOUT = 500; function emitKeypressEvents(stream, iface = {}) { if (stream[KEYPRESS_DECODER]) return; stream[KEYPRESS_DECODER] = new StringDecoder("utf8"); stream[ESCAPE_DECODER] = emitKeys(stream); stream[ESCAPE_DECODER].next(); const triggerEscape = /* @__PURE__ */ __name(() => stream[ESCAPE_DECODER].next(""), "triggerEscape"); const { escapeCodeTimeout = ESCAPE_CODE_TIMEOUT } = iface; let timeoutId; function onData(input) { if (stream.listenerCount("keypress") > 0) { const string = stream[KEYPRESS_DECODER].write(input); if (string) { clearTimeout2(timeoutId); iface[kSawKeyPress] = charLengthAt(string, 0) === string.length; iface.isCompletionEnabled = false; let length = 0; for (const character of string) { length += character.length; if (length === string.length) { iface.isCompletionEnabled = true; } try { stream[ESCAPE_DECODER].next(character); if (length === string.length && character === kEscape2) { timeoutId = setTimeout2(triggerEscape, escapeCodeTimeout); } } catch (err) { stream[ESCAPE_DECODER] = emitKeys(stream); stream[ESCAPE_DECODER].next(); throw err; } } } } else { stream.removeListener("data", onData); stream.on("newListener", onNewListener); } } __name(onData, "onData"); function onNewListener(event) { if (event === "keypress") { stream.on("data", onData); stream.removeListener("newListener", onNewListener); } } __name(onNewListener, "onNewListener"); if (stream.listenerCount("keypress") > 0) { stream.on("data", onData); } else { stream.on("newListener", onNewListener); } } __name(emitKeypressEvents, "emitKeypressEvents"); // src/mousepress.ts var mousepress = /* @__PURE__ */ __name((s, buf, state = {}) => { let parts; let b; let x; let y; let mod; let params; let down; let page; let button; const key = { name: void 0, ctrl: false, meta: false, shift: false }; if (Buffer.isBuffer(s)) { if (s[0] > 127 && s[1] === void 0) { s[0] -= 128; s = "\x1B" + s.toString("utf8"); } else { s = s.toString("utf8"); } } const bx = s.charCodeAt(4); const by = s.charCodeAt(5); if (buf[0] === 27 && buf[1] === 91 && buf[2] === 77 && (state.isVTE || bx >= 65533 || by >= 65533 || bx > 0 && bx < 32 || by > 0 && by < 32 || buf[4] > 223 && buf[4] < 248 && buf.length === 6 || buf[5] > 223 && buf[5] < 248 && buf.length === 6)) { b = buf[3]; x = buf[4]; y = buf[5]; if (x < 32) x += 255; if (y < 32) y += 255; if (x === 255) x = 255; if (y === 255) y = 255; s = "\x1B[M" + String.fromCharCode(b) + String.fromCharCode(x) + String.fromCharCode(y); } if (parts = /^\x1b\[M([\x00\u0020-\uffff]{3})/.exec(s)) { b = parts[1].charCodeAt(0); x = parts[1].charCodeAt(1); y = parts[1].charCodeAt(2); key.name = "mouse"; key.type = "X10"; key.raw = [b, x, y, parts[0]]; key.buf = buf; key.x = x - 32; key.y = y - 32; if (state.zero) { key.x--; key.y--; } if (x === 0) key.x = 255; if (y === 0) key.y = 255; mod = b >> 2; key.shift = Boolean(mod & 1); key.meta = Boolean(mod >> 1 & 1); key.ctrl = Boolean(mod >> 2 & 1); b -= 32; if (b >> 6 & 1) { key.action = b & 1 ? "wheeldown" : "wheelup"; key.button = "middle"; } else if (b === 3) { key.action = "mouseup"; key.button = state._lastButton || "unknown"; delete state._lastButton; } else { key.action = "mousedown"; button = b & 3; key.button = button === 0 ? "left" : button === 1 ? "middle" : button === 2 ? "right" : "unknown"; state._lastButton = key.button; } if (b === 35 || b === 39 || b === 51 || b === 43 || state.isVTE && (b === 32 || b === 36 || b === 48 || b === 40)) { delete key.button; key.action = "mousemove"; } key.sequence = s; return key; } if (parts = /^\x1b\[(\d+;\d+;\d+)M/.exec(s)) { params = parts[1].split(";"); b = Number(params[0]); x = Number(params[1]); y = Number(params[2]); key.name = "mouse"; key.type = "urxvt"; key.raw = [b, x, y, parts[0]]; key.buf = buf; key.x = x; key.y = y; if (state.zero) { key.x--; key.y--; } mod = b >> 2; key.shift = Boolean(mod & 1); key.meta = Boolean(mod >> 1 & 1); key.ctrl = Boolean(mod >> 2 & 1); if (b === 128 || b === 129) b = 67; b -= 32; if (b >> 6 & 1) { key.action = b & 1 ? "wheeldown" : "wheelup"; key.button = "middle"; } else if (b === 3) { key.action = "mouseup"; key.button = state._lastButton || "unknown"; delete state._lastButton; } else { key.action = "mousedown"; button = b & 3; key.button = button === 0 ? "left" : button === 1 ? "middle" : button === 2 ? "right" : "unknown"; state._lastButton = key.button; } if (b === 35 || b === 39 || b === 51 || b === 43 || state.isVTE && (b === 32 || b === 36 || b === 48 || b === 40)) { delete key.button; key.action = "mousemove"; } key.sequence = s; return key; } if (parts = /^\x1b\[<(\d+;\d+;\d+)([mM])/.exec(s)) { down = parts[2] === "M"; params = parts[1].split(";"); b = Number(params[0]); x = Number(params[1]); y = Number(params[2]); key.name = "mouse"; key.type = "sgr"; key.raw = [b, x, y, parts[0]]; key.buf = buf; key.x = x; key.y = y; if (state.zero) { key.x--; key.y--; } mod = b >> 2; key.shift = Boolean(mod & 1); key.meta = Boolean(mod >> 1 & 1); key.ctrl = Boolean(mod >> 2 & 1); if (b >> 6 & 1) { key.action = b & 1 ? "wheeldown" : "wheelup"; key.button = "middle"; } else { key.action = down ? "mousedown" : "mouseup"; button = b & 3; key.button = button === 0 ? "left" : button === 1 ? "middle" : button === 2 ? "right" : "unknown"; } if (b === 35 || b === 39 || b === 51 || b === 43 || state.isVTE && (b === 32 || b === 36 || b === 48 || b === 40)) { delete key.button; key.action = "mousemove"; } key.sequence = s; return key; } if (parts = /^\x1b\[<(\d+;\d+;\d+;\d+)&w/.exec(s)) { params = parts[1].split(";"); b = Number(params[0]); x = Number(params[1]); y = Number(params[2]); page = Number(params[3]); key.name = "mouse"; key.type = "dec"; key.raw = [b, x, y, parts[0]]; key.buf = buf; key.x = x; key.y = y; key.page = page; if (state.zero) { key.x--; key.y--; } key.action = b === 3 ? "mouseup" : "mousedown"; key.button = b === 2 ? "left" : b === 4 ? "middle" : b === 6 ? "right" : "unknown"; key.sequence = s; return key; } if (parts = /^\x1b\[24([0135])~\[(\d+),(\d+)\]\r/.exec(s)) { b = Number(parts[1]); x = Number(parts[2]); y = Number(parts[3]); key.name = "mouse"; key.type = "vt300"; key.raw = [b, x, y, parts[0]]; key.buf = buf; key.x = x; key.y = y; if (state.zero) { key.x--; key.y--; } key.action = "mousedown"; key.button = b === 1 ? "left" : b === 2 ? "middle" : b === 5 ? "right" : "unknown"; key.sequence = s; return key; } if (parts = /^\x1b\[(O|I)/.exec(s)) { key.action = parts[1] === "I" ? "focus" : "blur"; } key.x = s.charCodeAt(4) - 32; key.y = s.charCodeAt(5) - 32; key.scroll = 0; key.ctrl = Boolean(1 << 4 & b); key.meta = Boolean(1 << 3 & b); key.shift = Boolean(1 << 2 & b); key.release = (3 & b) === 3; if (1 << 6 & b) { key.scroll = 1 & b ? 1 : -1; } if (!key.release && !key.scroll) { key.button = b & 3; } key.sequence = s; return key; }, "mousepress"); // src/keycodes.ts var keycodes = [ { sequence: "\r", shortcut: "return" }, { sequence: "\x7F", shortcut: "backspace" }, { sequence: "\x1B", shortcut: "escape" }, { sequence: "\x1B[1~", shortcut: "home" }, { sequence: "\x1B\x1B[1~", shortcut: "home" }, { sequence: "\x1B[2~", shortcut: "insert" }, { sequence: "\x1B[3~", shortcut: "delete" }, { sequence: "\x1B[4~", shortcut: "end" }, { sequence: "\x1B[A", shortcut: "up" }, { sequence: "\x1B[B", shortcut: "down" }, { sequence: "\x1B[C", shortcut: "right" }, { sequence: "\x1B[D", shortcut: "left" }, // <shift> { sequence: "\x1B[2;2~", shortcut: "shift+insert" }, { sequence: "\x1B[5;2~", shortcut: "shift+pageup" }, { sequence: "\x1B[6;2~", shortcut: "shift+pagedown" }, { sequence: "\x1B[1;2A", shortcut: "shift+up" }, { sequence: "\x1B[1;2B", shortcut: "shift+down" }, { sequence: "\x1B[1;2C", shortcut: "shift+right" }, { sequence: "\x1B[1;2D", shortcut: "shift+left" }, { sequence: "\x1B[Z", shortcut: "shift+tab" }, // <meta> { sequence: "\x1Bf", shortcut: "meta+right" }, { sequence: "\x1Bb", shortcut: "meta+left" }, { sequence: "\x1B[1;9A", shortcut: "meta+up" }, { sequence: "\x1B[1;9B", shortcut: "meta+down" }, { sequence: "\x1B[1;9C", shortcut: "cmd+meta+right" }, { sequence: "\x1B[1;9D", shortcut: "cmd+meta+left" }, { sequence: "\x1B[2;3~", shortcut: "meta+insert" }, { sequence: "\x1B[3;3~", shortcut: "meta+delete" }, { sequence: "\x1B[5;3~", shortcut: "meta+pageup" }, { sequence: "\x1B[6;3~", shortcut: "meta+pagedown" }, { sequence: "\x1B\x7F", shortcut: "meta+backspace" }, // <shift+meta> { sequence: "\x1B[1;10A", shortcut: "shift+meta+up" }, { sequence: "\x1B[1;10B", shortcut: "shift+meta+down" }, { sequence: "\x1B[1;10C", shortcut: "shift+meta+right" }, { sequence: "\x1B[1;10D", shortcut: "shift+meta+left" }, { sequence: "\x1B[2;4~", shortcut: "shift+meta+insert" }, { sequence: "\x1B[3;4~", shortcut: "shift+meta+delete" }, { sequence: "\x1B[5;4~", shortcut: "shift+meta+pageup" }, { sequence: "\x1B[6;4~", shortcut: "shift+meta+pagedown" }, // <ctrl> { sequence: "\0", shortcut: "ctrl+`" }, { sequence: "\f", shortcut: "ctrl+4" }, // { sequence: '\x0d', shortcut: 'ctrl+5' }, { sequence: "", shortcut: "ctrl+6" }, { sequence: "", shortcut: "ctrl+7" }, { sequence: "", shortcut: "ctrl+a" }, { sequence: "", shortcut: "ctrl+b" }, { sequence: "", shortcut: "ctrl+c" }, { sequence: "", shortcut: "ctrl+d" }, { sequence: "", shortcut: "ctrl+e" }, { sequence: "", shortcut: "ctrl+f" }, { sequence: "\x07", shortcut: "ctrl+g" }, // { sequence: '\b', shortcut: 'ctrl+h' }, { sequence: " ", shortcut: "ctrl+i" }, { sequence: "\n", shortcut: "ctrl+j" }, { sequence: "\v", shortcut: "ctrl+k" }, { sequence: "\f", shortcut: "ctrl+l" }, { sequence: "", shortcut: "ctrl+n" }, { sequence: "", shortcut: "ctrl+o" }, { sequence: "", shortcut: "ctrl+p" }, { sequence: "", shortcut: "ctrl+q" }, { sequence: "", shortcut: "ctrl+r" }, { sequence: "", shortcut: "ctrl+s" }, { sequence: "", shortcut: "ctrl+t" }, { sequence: "", shortcut: "ctrl+u" }, { sequence: "", shortcut: "ctrl+v" }, { sequence: "", shortcut: "ctrl+w" }, { sequence: "", shortcut: "ctrl+x" }, { sequence: "", shortcut: "ctrl+y" }, { sequence: "", shortcut: "ctrl+z" }, { sequence: "", shortcut: "ctrl+-" }, { sequence: "", shortcut: "ctrl+|" }, { sequence: "", shortcut: "ctrl+]" }, { sequence: "\x1B[1;5A", shortcut: "ctrl+up", weight: -1 }, { sequence: "\x1B[1;5B", shortcut: "ctrl+down", weight: -1 }, { sequence: "\x1B[1;5C", shortcut: "ctrl+right", weight: -1 }, { sequence: "\x1B[1;5D", shortcut: "ctrl+left", weight: -1 }, { sequence: "\x1B[2;5~", shortcut: "ctrl+insert", weight: -1 }, { sequence: "\x1B[5;5~", shortcut: "ctrl+pageup", weight: -1 }, { sequence: "\x1B[6;5~", shortcut: "ctrl+pagedown", weight: -1 }, // <ctrl+shift> { sequence: "\x1B[1;6B", shortcut: "ctrl+shift+down" }, { sequence: "\x1B[1;6D", shortcut: "ctrl+shift+left" }, { sequence: "\x1B[1;6C", shortcut: "ctrl+shift+right" }, { sequence: "\x1B[1;6A", shortcut: "ctrl+shift+up" }, // <ctrl+shift+meta> { sequence: "\x1B[1;14B", shortcut: "ctrl+shift+meta+down" }, { sequence: "\x1B[1;14D", shortcut: "ctrl+shift+meta+left" }, { sequence: "\x1B[1;14C", shortcut: "ctrl+shift+meta+right" }, // <fn> { sequence: "\x1B[6~", shortcut: "fn+down", name: "end" }, { sequence: "\x1B[H", shortcut: "fn+left", name: "home" }, { sequence: "\x1B[F", shortcut: "fn+right", name: "pagedown" }, { sequence: "\x1B[5~", shortcut: "fn+up", name: "pageup" }, // <fn+ctrl> { sequence: "\x1B[1;5F", shortcut: "fn+ctrl+right" }, { sequence: "\x1B[1;5H", shortcut: "fn+ctrl+left" }, { sequence: "\x1B[3;5~", shortcut: "fn+ctrl+delete" }, // <fn+shift> { sequence: "\x1B[1;2F", shortcut: "fn+shift+right", name: "shift+home" }, { sequence: "\x1B[1;2H", shortcut: "fn+shift+left", name: "shift+end" }, { sequence: "\x1B[3;2~", shortcut: "fn+shift+delete" }, // <fn+meta> { sequence: "\x1B[1;9F", shortcut: "fn+meta+right" }, { sequence: "\x1B[1;9H", shortcut: "fn+meta+left" }, { sequence: "\x1B[5;9~", shortcut: "fn+meta+up" }, { sequence: "\x1B[6;9~", shortcut: "fn+meta+down" }, { sequence: "\x1B\x1B[5~", shortcut: "fn+meta+up" }, { sequence: "\x1B\x1B[6~", shortcut: "fn+meta+down" }, { sequence: "\x1Bd", shortcut: "fn+meta+delete", fn: true }, // <fn+ctrl+shift> { sequence: "\x1B[1;6F", shortcut: "fn+ctrl+shift+right" }, { sequence: "\x1B[1;6H", shortcut: "fn+ctrl+shift+left" }, { sequence: "\x1B[3;6~", shortcut: "fn+ctrl+shift+delete" }, { sequence: "\x1B[5;6~", shortcut: "fn+ctrl+shift+up" }, { sequence: "\x1B[6;6~", shortcut: "fn+ctrl+shift+down" }, // <fn+ctrl+meta> { sequence: "\x1B[1;13F", shortcut: "fn+ctrl+meta+right" }, { sequence: "\x1B[1;13H", shortcut: "fn+ctrl+meta+left" }, { sequence: "\x1B[3;13~", shortcut: "fn+ctrl+meta+delete" }, // <fn+shift+meta> { sequence: "\x1B[1;10F", shortcut: "fn+shift+meta+right" }, { sequence: "\x1B[1;10H", shortcut: "fn+shift+meta+left" }, { sequence: "\x1B[3;10~", shortcut: "fn+shift+meta+delete" }, { sequence: "\x1B[5;10~", shortcut: "fn+shift+meta+up" }, { sequence: "\x1B[6;10~", shortcut: "fn+shift+meta+down" }, // <fn+ctrl+shift+meta> { sequence: "\x1B[1;14F", shortcut: "fn+ctrl+shift+meta+right" }, { sequence: "\x1B[1;14H", shortcut: "fn+ctrl+shift+meta+left" }, { sequence: "\x1B[3;14~", shortcut: "fn+ctrl+shift+meta+delete" }, { sequence: "\x1B[5;14~", shortcut: "fn+ctrl+shift+meta+up" }, { sequence: "\x1B[6;14~", shortcut: "fn+ctrl+shift+meta+down" }, // <f1-f4> { sequence: "\x1BOP", shortcut: "f1" }, { sequence: "\x1BOQ", shortcut: "f2" }, { sequence: "\x1BOR", shortcut: "f3" }, { sequence: "\x1BOS", shortcut: "f4" }, // <f1-f20> { sequence: "\x1B[11~", shortcut: "f1" }, { sequence: "\x1B[12~", shortcut: "f2" }, { sequence: "\x1B[13~", shortcut: "f3" }, { sequence: "\x1B[14~", shortcut: "f4" }, { sequence: "\x1B[15~", shortcut: "f5" }, { sequence: "\x1B[17~", shortcut: "f6" }, { sequence: "\x1B[18~", shortcut: "f7" }, { sequence: "\x1B[19~", shortcut: "f8" }, { sequence: "\x1B[20~", shortcut: "f9" }, { sequence: "\x1B[21~", shortcut: "f10" }, { sequence: "\x1B[23~", shortcut: "f11" }, { sequence: "\x1B[24~", shortcut: "f12" }, { sequence: "\x1B[25~", shortcut: "f13" }, { sequence: "\x1B[26~", shortcut: "f14" }, { sequence: "\x1B[28~", shortcut: "f15" }, { sequence: "\x1B[29~", shortcut: "f16" }, { sequence: "\x1B[31~", shortcut: "f17" }, { sequence: "\x1B[32~", shortcut: "f18" }, { sequence: "\x1B[33~", shortcut: "f19" }, { sequence: "\x1B[34~", shortcut: "f20" }, // <shift+f1-f12> { sequence: "\x1B[1;2P", shortcut: "shift+f1" }, { sequence: "\x1B[1;2Q", shortcut: "shift+f2" }, { sequence: "\x1B[1;2R", shortcut: "shift+f3" }, { sequence: "\x1B[1;2S", shortcut: "shift+f4" }, { sequence: "\x1B[15;2~", shortcut: "shift+f5" }, { sequence: "\x1B[17;2~", shortcut: "shift+f6" }, { sequence: "\x1B[18;2~", shortcut: "shift+f7" }, { sequence: "\x1B[19;2~", shortcut: "shift+f8" }, { sequence: "\x1B[20;2~", shortcut: "shift+f9" }, { sequence: "\x1B[21;2~", shortcut: "shift+f10" }, { sequence: "\x1B[23;2~", shortcut: "shift+f11" }, { sequence: "\x1B[24;2~", shortcut: "shift+f12" }, // <num_key> { sequence: "\x1BOp", shortcut: "num_key_0" }, { sequence: "\x1BOq", shortcut: "num_key_1" }, { sequence: "\x1BOr", shortcut: "num_key_2" }, { sequence: "\x1BOs", shortcut: "num_key_3" }, { sequence: "\x1BOt", shortcut: "num_key_4" }, { sequence: "\x1BOu", shortcut: "num_key_5" }, { sequence: "\x1BOv", shortcut: "num_key_6" }, { sequence: "\x1BOw", shortcut: "num_key_7" }, { sequence: "\x1BOx", shortcut: "num_key_8" }, { sequence: "\x1BOy", shortcut: "num_key_9" }, { sequence: "\x1BOl", shortcut: "num_key_comma" }, { sequence: "\x1BOm", shortcut: "num_key_minus" }, { sequence: "\x1BOn", shortcut: "num_key_period" } ].map((key) => ({ ...key, weight: key.weight || 0 })); // src/utils.ts var PRINTABLE_CHAR_REGEX = /^(?!.*[\uFEFF])[\p{L}\p{N}\p{P}\p{S}\p{Z}\p{M}\s]+$/u; var NON_PRINTABLE_CHAR_REGEX = /[\p{Cc}\p{Cf}]/u; var metaKeys = /* @__PURE__ */ new Set(["alt", "meta", "option"]); var modifierKeys = ["fn", "ctrl", "shift", ...[...metaKeys], "cmd"]; function isMousepress(input, key) { if (key.code === "[M" || key.code === "[I" || key.code === "[O") { return true; } return /\x1b\[M/.test(input) || /\x1b\[M([\x00\u0020-\uffff]{3})/.test(input) || /\x1b\[(\d+;\d+;\d+)M/.test(input) || /\x1b\[<(\d+;\d+;\d+)([mM])/.test(input) || /\x1b\[<(\d+;\d+;\d+;\d+)&w/.test(input) || /\x1b\[24([0135])~\[(\d+),(\d+)\]\r/.test(input) || /\x1b\[(O|I)/.test(input); } __name(isMousepress, "isMousepress"); var parsePosition = /* @__PURE__ */ __name((input) => { if (!input) return null; const match = /^\x1B\[([0-9]+);([0-9]+)R/.exec(String(input)); if (match) { return { name: "position", position: { y: match[1], x: match[2] }, printable: false }; } return null; }, "parsePosition"); var order = [ "sequence", "name", "shortcut", "command", "ctrl", "shift", "meta", "fn", "printable", "pasted", "weight" ]; var sortKeys = /* @__PURE__ */ __name((obj, keys = order) => { const ordered = {}; for (const key of keys) { if (obj[key] !== void 0) { ordered[key] = obj[key]; } } for (const [k, v] of Object.entries(obj)) { if (!(k in ordered)) { ordered[k] = v; } } return ordered; }, "sortKeys"); var sortModifiers = /* @__PURE__ */ __name((names) => { const modifiers = []; const after = []; for (const name of modifierKeys) { if (names.includes(name)) { modifiers.push(name); } } for (const name of names) { if (!modifiers.includes(name)) { after.push(name); } } return modifiers.concat(after); }, "sortModifiers"); var createShortcut = /* @__PURE__ */ __name((key) => { const modifiers = /* @__PURE__ */ new Set(); if (key.fn) modifiers.add("fn"); if (key.ctrl) modifiers.add("ctrl"); if (key.shift) modifiers.add("shift"); if (key.alt) modifiers.add("meta"); if (key.option) modifiers.add("meta"); if (key.meta) modifiers.add("meta"); let keyName = isPrintableCharacter(key.sequence) ? key.sequence : key.name; if (keyName === "undefined") keyName = ""; const output = modifiers.size > 0 && keyName ? `${sortModifiers([...modifiers]).join("+")}+${keyName}` : keyName; return output.length > 1 ? output : ""; }, "createShortcut"); var sortShortcutModifier = /* @__PURE__ */ __name((shortcut) => { return sortModifiers(shortcut.split("+")).join("+"); }, "sortShortcutModifier"); var sortShortcutModifiers = /* @__PURE__ */ __name((keymap = []) => { for (const key of keymap) key.shortcut = sortShortcutModifier(key.shortcut); return keymap; }, "sortShortcutModifiers"); var prioritizeKeymap = /* @__PURE__ */ __name((keymap = []) => { const omit = keymap.filter((k) => k.shortcut?.startsWith("-")).map((k) => sortShortcutModifier(k.shortcut.slice(1))); const isBuiltIn = /* @__PURE__ */ __name((k) => !k.weight || k.weight <= 0, "isBuiltIn"); const bindings = sortShortcutModifiers(keymap).filter((k) => !isBuiltIn(k) || !omit.includes(k.shortcut)); bindings.sort((a, b) => { a.weight ||= 0; b.weight ||= 0; if (a.weight === b.weight) return 0; return a.weight > b.weight ? 1 : -1; }); return bindings; }, "prioritizeKeymap"); var isPrintableCharacter = /* @__PURE__ */ __name((s) => { return s ? PRINTABLE_CHAR_REGEX.test(s) && !NON_PRINTABLE_CHAR_REGEX.test(s) : false; }, "isPrintableCharacter"); // index.ts var ESC = "\x1B"; var isWindows = globalThis.process.platform === "win32"; var MAX_PASTE_BUFFER = 1024 * 1024; var ENABLE_PASTE_BRACKET_MODE = `${ESC}[?2004h`; var DISABLE_PASTE_BRACKET_MODE = `${ESC}[?2004l`; var enablePaste = /* @__PURE__ */ __name((stdout2) => { stdout2.write(ENABLE_PASTE_BRACKET_MODE); }, "enablePaste"); var disablePaste = /* @__PURE__ */ __name((stdout2) => { stdout2.write(DISABLE_PASTE_BRACKET_MODE); }, "disablePaste"); var enableMouse = /* @__PURE__ */ __name((stdout2) => { stdout2.write(`${ESC}[?1003h`); }, "enableMouse"); var disableMouse = /* @__PURE__ */ __name((stdout2) => { stdout2.write(`${ESC}[?1003l`); }, "disableMouse"); var cursor = { hide: /* @__PURE__ */ __name((stdout2) => { stdout2.write(`${ESC}[?25l`); }, "hide"), show: /* @__PURE__ */ __name((stdout2) => { stdout2.write(`${ESC}[?25h`); }, "show"), position: /* @__PURE__ */ __name((stdout2) => { stdout2.write(`${ESC}[6n`); }, "position") }; var hasMatchingModifiers = /* @__PURE__ */ __name((a, b) => { return a.ctrl === b.ctrl && a.shift === b.shift && a.meta === b.meta && a.fn === b.fn; }, "hasMatchingModifiers"); var hasModifier = /* @__PURE__ */ __name((key) => { return key.ctrl || key.shift || key.meta || key.fn; }, "hasModifier"); var onExitHandlers = globalThis.onExitHandlers ||= []; var onExitHandler = /* @__PURE__ */ __name(() => { for (const fn of onExitHandlers) { if (!fn.called) { fn.called = true; fn(); } } }, "onExitHandler"); if (!globalThis.exitHandlers) { globalThis.exitHandlers = onExitHandlers; process.once("uncaughtException", onExitHandler); process.once("SIGINT", onExitHandler); process.once("exit", onExitHandler); } var emitKeypress = /* @__PURE__ */ __name(({ input = stdin, output = stdout, keymap = [], onKeypress, onMousepress, onExit, escapeCodeTimeout = 500, handleClose = true, hideCursor = false, initialPosition = false, enablePasteMode = false, pasteModeTimeout = 100 }) => { if (!input || input !== process.stdin && !input.isTTY) { throw new Error("Invalid stream passed"); } const isRaw = input.isRaw; const sortedShorcuts = /* @__PURE__ */ new Set(); let closed = false; let pasting = false; let initial = true; let sorted = false; let buffer = ""; let pasteTimeout = null; const clearPasteState = /* @__PURE__ */ __name(() => { pasting = false; buffer = ""; if (pasteTimeout) { clearTimeout(pasteTimeout); pasteTimeout = null; } }, "clearPasteState"); async function handleKeypress(input2, key) { if (initialPosition && initial) { const parsed = parsePosition(key.sequence); if (parsed) { initial = false; onKeypress("", parsed, close); return; } } if (key.name === "paste-start" || /\x1B\[200~/.test(key.sequence)) { clearPasteState(); pasting = true; pasteTimeout = setTimeout(clearPasteState, pasteModeTimeout); return; } if (key.name === "paste-end" || /\x1B\[201~/.test(key.sequence)) { clearTimeout(pasteTimeout); pasteTimeout = null; if (pasting) { key.name = "paste"; key.sequence = buffer.replace(/\x1B\[201~/g, ""); key.ctrl = false; key.shift = false; key.meta = false; key.fn = false; key.printable = true; onKeypress(buffer, key, close); clearPasteState(); } return; } if (pasting) { if (buffer.length < MAX_PASTE_BUFFER) { buffer += key.sequence?.replace(/\r/g, "\n") || ""; } else { clearPasteState(); } return; } if (!buffer && isMousepress(key.sequence, key)) { const k = mousepress(key.sequence, Buffer.from(key.sequence)); k.mouse = true; onMousepress?.(k, close); } else { let addShortcut = false; if (typeof keymap === "function") { keymap = keymap(); } if (!sorted) { keymap = prioritizeKeymap(keymap); sorted = true; } const shortcut = key.shortcut ? sortShortcutModifier(key.shortcut) : createShortcut(key); if (!key.shortcut && hasModifier(key)) { key.shortcut = shortcut; } for (const mapping of keymap) { if (mapping.sequence) { if (key.sequence === mapping.sequence && hasMatchingModifiers(key, mapping)) { key = { ...key, ...mapping }; addShortcut = false; break; } continue; } if (mapping.shortcut && !sortedShorcuts.has(mapping.shortcut)) { sortedShorcuts.add(mapping.shortcut); mapping.shortcut = sortShortcutModifier(mapping.shortcut); } if (shortcut === mapping.shortcut || key.name && key.name === mapping.shortcut && hasMatchingModifiers(key, mapping)) { key = { ...key, ...mapping }; addShortcut = false; break; } } if (/^f[0-9]+$/.test(key.name)) { addShortcut = true; } if (addShortcut) { key.shortcut ||= shortcut; } key.printable ||= isPrintableCharacter(key.sequence); onKeypress(key.sequence, key, close); } } __name(handleKeypress, "handleKeypress"); function close() { if (closed) return; if (!isWindows && input.isTTY) input.setRawMode(isRaw); if (hideCursor) cursor.show(output); if (onMousepress) disableMouse(output); if (enablePasteMode) disablePaste(output); if (onKeypress) input.off("keypress", handleKeypress); if (pasteTimeout) clearTimeout(pasteTimeout); closed = true; input.pause(); onExit?.(); } __name(close, "close"); emitKeypressEvents(input, { escapeCodeTimeout }); if (onMousepress) { enableMouse(output); } if (enablePasteMode === true) { enablePaste(output); } if (!isWindows && input.isTTY) input.setRawMode(true); if (hideCursor) cursor.hide(output); if (onKeypress) input.on("keypress", handleKeypress); input.setEncoding("utf8"); input.once("pause", close); input.resume(); if (initialPosition) { cursor.position(output); } if (handleClose !== false) { onExitHandlers.push(close); } return close; }, "emitKeypress"); var emit_keypress_default = emitKeypress; export { NON_PRINTABLE_CHAR_REGEX, PRINTABLE_CHAR_REGEX, createShortcut, cursor, emit_keypress_default as default, disableMouse, disablePaste, emitKeypress, emitKeypressEvents, enableMouse, enablePaste, isMousepress, isPrintableCharacter, keycodes, metaKeys, modifierKeys, mousepress, parsePosition, prioritizeKeymap, sortKeys, sortModifiers, sortShortcutModifier, sortShortcutModifiers }; //# sourceMappingURL=index.mjs.map