emit-keypress
Version:
Drop-dead simple keypress event emitter for Node.js. Create powerful CLI applications and experiences with ease.
1,459 lines (1,451 loc) • 44.9 kB
JavaScript
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// index.ts
var emit_keypress_exports = {};
__export(emit_keypress_exports, {
NON_PRINTABLE_CHAR_REGEX: () => NON_PRINTABLE_CHAR_REGEX,
PRINTABLE_CHAR_REGEX: () => PRINTABLE_CHAR_REGEX,
createShortcut: () => createShortcut,
cursor: () => cursor,
default: () => emit_keypress_default,
disableMouse: () => disableMouse,
disablePaste: () => disablePaste,
emitKeypress: () => emitKeypress,
emitKeypressEvents: () => emitKeypressEvents,
enableMouse: () => enableMouse,
enablePaste: () => enablePaste,
isMousepress: () => isMousepress,
isPrintableCharacter: () => isPrintableCharacter,
keycodes: () => keycodes,
metaKeys: () => metaKeys,
modifierKeys: () => modifierKeys,
mousepress: () => mousepress,
parsePosition: () => parsePosition,
prioritizeKeymap: () => prioritizeKeymap,
sortKeys: () => sortKeys,
sortModifiers: () => sortModifiers,
sortShortcutModifier: () => sortShortcutModifier,
sortShortcutModifiers: () => sortShortcutModifiers
});
module.exports = __toCommonJS(emit_keypress_exports);
var import_node_process = require("process");
// src/emit-keypress.ts
var import_node_string_decoder = require("string_decoder");
var import_node_timers = require("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 import_node_string_decoder.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) {
(0, import_node_timers.clearTimeout)(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 = (0, import_node_timers.setTimeout)(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 = import_node_process.stdin,
output = import_node_process.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;
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
NON_PRINTABLE_CHAR_REGEX,
PRINTABLE_CHAR_REGEX,
createShortcut,
cursor,
disableMouse,
disablePaste,
emitKeypress,
emitKeypressEvents,
enableMouse,
enablePaste,
isMousepress,
isPrintableCharacter,
keycodes,
metaKeys,
modifierKeys,
mousepress,
parsePosition,
prioritizeKeymap,
sortKeys,
sortModifiers,
sortShortcutModifier,
sortShortcutModifiers
});
//# sourceMappingURL=index.js.map