emit-keypress
Version:
Drop-dead simple keypress event emitter for Node.js. Create powerful CLI applications and experiences with ease.
1,752 lines (1,745 loc) • 69.7 kB
JavaScript
var __defProp = Object.defineProperty;
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
// index.ts
import { stdin, stdout } from "process";
// node_modules/.pnpm/detect-terminal@3.0.0/node_modules/detect-terminal/dist/index.mjs
import path from "path";
import { execFileSync as nodeExecFileSync } from "child_process";
import { execFileSync as nodeExecFileSync2 } from "child_process";
var __defProp2 = Object.defineProperty;
var __name2 = /* @__PURE__ */ __name((target, value) => __defProp2(target, "name", { value, configurable: true }), "__name");
var TRUE_COLOR_PATTERN = /^(truecolor|24bits?)$/;
var has256Color = /* @__PURE__ */ __name2((value) => {
return Boolean(value?.match(/256/));
}, "has256Color");
var supportsTrueColor = /* @__PURE__ */ __name2((value) => {
return Boolean(value?.match(TRUE_COLOR_PATTERN));
}, "supportsTrueColor");
var getColorSupport = /* @__PURE__ */ __name2((env) => {
return {
supports256Color: has256Color(env.TERM) || has256Color(env.COLORTERM),
supportsTrueColor: supportsTrueColor(env.COLORTERM)
};
}, "getColorSupport");
var NO_BASIC_COLOR_TERMINALS = /* @__PURE__ */ new Set([
"dumb",
"linux_console",
"none",
"unknown",
"vt100"
]);
var getColorLevel = /* @__PURE__ */ __name2((env, terminal, colorSupport = getColorSupport(env)) => {
const { supports256Color, supportsTrueColor: supportsTrueColor2 } = colorSupport;
if (supportsTrueColor2) {
return 3;
}
if (supports256Color) {
return 2;
}
if (terminal && !NO_BASIC_COLOR_TERMINALS.has(terminal)) {
return 1;
}
if (env.TERM_PROGRAM || env.COLORTERM || env.TERMUX_VERSION) {
return 1;
}
return 0;
}, "getColorLevel");
var AMBIGUOUS_ENV_TERMINALS = /* @__PURE__ */ new Set([
"linux_console",
"truecolor_terminal",
"vt100",
"xterm",
"xterm_256color",
"xterm_truecolor"
]);
var SHELL_PROCESS_NAMES = /* @__PURE__ */ new Set([
"bash",
"csh",
"dash",
"fish",
"ksh",
"login",
"node",
"nodejs",
"nu",
"sh",
"sudo",
"tcsh",
"zsh"
]);
var normalizeIdentifier = /* @__PURE__ */ __name2((value) => {
return value.trim().toLowerCase().replace(/[^a-z0-9]+/g, "_");
}, "normalizeIdentifier");
var normalizeAppName = /* @__PURE__ */ __name2((value, platform) => {
const appName = platform === "darwin" ? path.parse(value).name : value;
return normalizeIdentifier(appName);
}, "normalizeAppName");
var isAmbiguousEnvResult = /* @__PURE__ */ __name2((terminal, options = {}) => {
if (!terminal) {
return true;
}
if (options.preferOuter && (terminal === "tmux" || terminal === "screen")) {
return true;
}
return AMBIGUOUS_ENV_TERMINALS.has(terminal);
}, "isAmbiguousEnvResult");
var hasKonsoleEnvironment = /* @__PURE__ */ __name2((env) => {
return Object.keys(env).some((envVar) => /konsole/i.test(envVar));
}, "hasKonsoleEnvironment");
var normalizeParentProcessName = /* @__PURE__ */ __name2((processName) => {
const normalized = normalizeIdentifier(path.parse(processName).name);
switch (normalized) {
case "alacritty":
case "aterm":
case "atomic_terminal":
case "eterm":
case "foot":
case "ghostty":
case "hyper":
case "kitty":
case "konsole":
case "kuake":
case "mate_terminal":
case "mrxvt":
case "powershell":
case "putty":
case "qterminal":
case "rio":
case "rxvt":
case "terminator":
case "terminology":
case "tilda":
case "tmux":
case "warp":
case "wezterm":
case "wterm":
return normalized;
case "apple_terminal":
case "terminal":
return "terminal";
case "cmd":
return "cmd";
case "code":
case "code_insiders":
return "vscode";
case "conhost":
return "conhost";
case "gnome":
case "gnome_terminal":
case "gnome_terminal_server":
case "guake":
return "gnome_terminal";
case "iterm":
case "iterm2":
return "iterm";
case "login":
case "linux":
return "linux_console";
case "pwsh":
return "powershell";
case "screen":
return "screen";
case "terminal_app":
return "terminal_app";
case "urxvt":
case "urxvt256c":
case "urxvt256c_ml":
case "urxvt_ml":
return "rxvt";
case "wt":
case "windows_terminal":
return "windows_terminal";
case "xfce":
case "xfce_terminal":
case "xfce4_terminal":
return "xfce4_terminal";
default: {
if (normalized.startsWith("gnome_terminal")) {
return "gnome_terminal";
}
if (normalized.startsWith("warp")) {
return "warp";
}
return null;
}
}
}, "normalizeParentProcessName");
var colorVariant = /* @__PURE__ */ __name2((terminal, supports256Color, supportsTrueColor2) => {
return supports256Color || supportsTrueColor2 ? `${terminal}_256color` : terminal;
}, "colorVariant");
var normalizeTermProgram = /* @__PURE__ */ __name2((termProgram) => {
switch (termProgram) {
case "apple_terminal":
return "terminal";
case "eterm":
return "eterm";
case "gnome_terminal":
case "gnome_terminal_server":
return "gnome_terminal";
case "hyper":
return "hyper";
case "iterm_app":
case "iterm":
case "iterm2":
return "iterm";
case "kitty":
return "kitty";
case "konsole":
return "konsole";
case "mate_terminal":
return "mate_terminal";
case "powershell":
return "powershell";
case "putty":
return "putty";
case "qterminal":
return "qterminal";
case "rio":
return "rio";
case "rxvt":
return "rxvt";
case "terminal_app":
return "terminal_app";
case "terminal":
return "terminal";
case "terminator":
return "terminator";
case "termux":
return "termux";
case "vscode":
return "vscode";
case "warp":
return "warp";
case "wezterm":
return "wezterm";
case "xfce4_terminal":
return "xfce4_terminal";
case "alacritty":
return "alacritty";
default:
return null;
}
}, "normalizeTermProgram");
var detectFromTerm = /* @__PURE__ */ __name2((term, env, platform) => {
const { supports256Color, supportsTrueColor: supportsTrueColor2 } = getColorSupport(env);
if (term === "xterm" || term === "xterm_256color") {
if (supportsTrueColor2) {
return "xterm_truecolor";
}
if (env.VTE_VERSION) {
const vteVersion = Number.parseInt(env.VTE_VERSION, 10);
if (vteVersion >= 3803) {
return colorVariant("gnome_terminal", supports256Color, supportsTrueColor2);
}
}
if (hasKonsoleEnvironment(env)) {
return colorVariant("konsole", supports256Color, supportsTrueColor2);
}
if (platform === "darwin") {
return "terminal";
}
return term;
}
switch (term) {
case "linux":
return "linux_console";
case "gnome":
case "gnome_256color":
case "gnome_terminal":
case "gnome_terminal_256color":
case "guake":
case "terminator":
return colorVariant("gnome_terminal", supports256Color, supportsTrueColor2);
case "konsole":
return colorVariant("konsole", supports256Color, supportsTrueColor2);
case "rxvt":
case "rxvt_xpm":
case "rxvt_unicode":
case "rxvt_unicode_256color":
case "urxvt":
case "urxvt_ml":
case "urxvt256c":
case "urxvt256c_ml":
return colorVariant("rxvt", term === "rxvt" || supports256Color, supportsTrueColor2);
case "xfce":
case "xfce_terminal":
case "xfce4_terminal":
return "xfce4_terminal";
case "eterm":
return colorVariant("eterm", supports256Color, supportsTrueColor2);
case "xterm_kitty":
case "kitty":
return "kitty";
case "screen":
case "screen_256color":
return "screen";
case "tmux":
case "tmux_256color":
return "tmux";
case "vt100":
return "vt100";
case "aterm":
case "atomic_terminal":
case "dopamine":
case "foot":
case "ghostty":
case "kuake":
case "rio":
case "terminology":
case "termux":
case "tilda":
case "wterm":
case "mrxvt":
return term;
default:
if (/screen/.test(term)) return "screen";
if (/tmux/.test(term)) return "tmux";
if (/rxvt/.test(term)) return "rxvt";
if (/vt100/.test(term)) return "vt100";
if (/linux/.test(term)) return "linux_console";
if (/alacritty/.test(term)) return "alacritty";
if (/dopamine/.test(term)) return "dopamine";
if (/kitty/.test(term)) return "kitty";
if (/ghostty/.test(term)) return "ghostty";
if (/^foot/.test(term)) return "foot";
if (/^rio/.test(term)) return "rio";
return term;
}
}, "detectFromTerm");
var detectFromEnvResult = /* @__PURE__ */ __name2((options = {}) => {
const env = options.env ?? process.env;
const platform = options.platform ?? process.platform;
const term = env.TERM ? normalizeIdentifier(env.TERM) : void 0;
if (platform === "android" && env.TERMUX_VERSION) {
return { terminal: "termux", source: "env", generic: term };
}
if (!options.preferOuter && term) {
if (term.startsWith("tmux")) {
return { terminal: "tmux", source: "env", generic: term };
}
if (term.startsWith("screen")) {
return { terminal: "screen", source: "env", generic: term };
}
}
if (env.KITTY_PID) {
return { terminal: "kitty", source: "env", generic: term };
}
if (env.GHOSTTY_RESOURCES_DIR) {
return { terminal: "ghostty", source: "env", generic: term };
}
if (env.ALACRITTY_SOCKET || env.ALACRITTY_LOG) {
return { terminal: "alacritty", source: "env", generic: term };
}
const termProgram = env.TERM_PROGRAM ? normalizeAppName(env.TERM_PROGRAM, platform) : void 0;
if (termProgram) {
return {
terminal: normalizeTermProgram(termProgram) ?? termProgram,
source: "env",
generic: term
};
}
if (typeof env.VSCODE_PID !== "undefined" || typeof env.TERM_PROGRAM_VERSION !== "undefined" && /vscode/i.test(env.TERM_PROGRAM_VERSION)) {
return { terminal: "vscode", source: "env", generic: term };
}
if (term && term !== "unknown") {
return {
terminal: detectFromTerm(term, env, platform),
source: "env",
generic: term
};
}
const colorTerm = env.COLORTERM ? normalizeIdentifier(env.COLORTERM) : void 0;
if (colorTerm === "truecolor" || colorTerm === "24bit" || colorTerm === "24bits") {
return { terminal: "truecolor_terminal", source: "env", generic: term };
}
if (colorTerm) {
return { terminal: colorTerm, source: "env", generic: term };
}
return null;
}, "detectFromEnvResult");
var runPs = /* @__PURE__ */ __name2((run, args) => {
return run("ps", args, {
encoding: "utf8",
timeout: 1e3,
stdio: ["ignore", "pipe", "ignore"]
});
}, "runPs");
var getParentProcess = /* @__PURE__ */ __name2((pid, options = {}) => {
const run = options.execFileSync ?? nodeExecFileSync;
const parentPid = Number.parseInt(
runPs(run, ["-o", "ppid=", "-p", String(pid)]).trim(),
10
);
if (!Number.isFinite(parentPid) || parentPid <= 1) {
return null;
}
const appName = runPs(run, ["-o", "comm=", "-p", String(parentPid)]).trim();
if (!appName) {
return null;
}
return {
pid: parentPid,
appName
};
}, "getParentProcess");
var colorVariant2 = /* @__PURE__ */ __name2((terminal, supports256Color, supportsTrueColor2) => {
return supports256Color || supportsTrueColor2 ? `${terminal}_256color` : terminal;
}, "colorVariant");
var applyParentColorVariant = /* @__PURE__ */ __name2((terminal, env) => {
const { supports256Color, supportsTrueColor: supportsTrueColor2 } = getColorSupport(env);
switch (terminal) {
case "eterm":
case "konsole":
case "rxvt":
return colorVariant2(terminal, supports256Color, supportsTrueColor2);
case "gnome_terminal":
case "terminator":
return colorVariant2("gnome_terminal", supports256Color, supportsTrueColor2);
default:
return terminal;
}
}, "applyParentColorVariant");
var detectFromParentProcessResult = /* @__PURE__ */ __name2((options = {}) => {
const env = options.env ?? process.env;
const platform = options.platform ?? process.platform;
if (platform === "win32" || env.SSH_CONNECTION) {
return null;
}
const seenPids = /* @__PURE__ */ new Set();
let pid = options.pid ?? process.pid;
for (let depth = 0; depth < 12; depth += 1) {
if (seenPids.has(pid)) {
break;
}
seenPids.add(pid);
try {
const parent = getParentProcess(pid, options);
if (!parent) {
break;
}
const normalized = normalizeParentProcessName(parent.appName);
if (normalized === "screen" || normalized === "tmux") {
if (!options.preferOuter) {
return {
terminal: normalized,
source: "parent",
appName: parent.appName,
pid: parent.pid
};
}
pid = parent.pid;
continue;
}
if (normalized) {
return {
terminal: applyParentColorVariant(normalized, env),
source: "parent",
appName: parent.appName,
pid: parent.pid
};
}
if (!SHELL_PROCESS_NAMES.has(normalizeIdentifier(parent.appName))) {
break;
}
pid = parent.pid;
} catch {
break;
}
}
return null;
}, "detectFromParentProcessResult");
var detectFromProcessTitleResult = /* @__PURE__ */ __name2((options = {}) => {
const processTitle = (options.processTitle ?? process.title)?.toLowerCase() ?? "";
if (/^alacritty/.test(processTitle)) {
return {
terminal: "alacritty",
source: "process_title"
};
}
if (/^kitty/.test(processTitle)) {
return {
terminal: "kitty",
source: "process_title"
};
}
if (/^wezterm/.test(processTitle)) {
return {
terminal: "wezterm",
source: "process_title"
};
}
if (/^hyper/.test(processTitle)) {
return {
terminal: "hyper",
source: "process_title"
};
}
if (/^foot/.test(processTitle)) {
return {
terminal: "foot",
source: "process_title"
};
}
if (/^rio/.test(processTitle)) {
return {
terminal: "rio",
source: "process_title"
};
}
if (/^ghostty/.test(processTitle)) {
return {
terminal: "ghostty",
source: "process_title"
};
}
if (/bash/.test(processTitle)) {
return {
terminal: "bash",
source: "process_title"
};
}
if (/zsh/.test(processTitle)) {
return {
terminal: "zsh",
source: "process_title"
};
}
if (/ksh/.test(processTitle)) {
return {
terminal: "ksh",
source: "process_title"
};
}
if (/fish/.test(processTitle)) {
return {
terminal: "fish",
source: "process_title"
};
}
if (/csh/.test(processTitle)) {
return {
terminal: "csh",
source: "process_title"
};
}
if (/tcsh/.test(processTitle)) {
return {
terminal: "tcsh",
source: "process_title"
};
}
if (/pwsh/.test(processTitle)) {
return {
terminal: "powershell",
source: "process_title"
};
}
if (/powershell/.test(processTitle)) {
return {
terminal: "powershell",
source: "process_title"
};
}
if (/cmd/.test(processTitle)) {
return {
terminal: "cmd",
source: "process_title"
};
}
if (/sh$/.test(processTitle)) {
return {
terminal: "sh",
source: "process_title"
};
}
if (/^node/.test(processTitle)) {
return {
terminal: "node",
source: "process_title"
};
}
return null;
}, "detectFromProcessTitleResult");
var runShellTerm = /* @__PURE__ */ __name2((run) => {
return run("sh", ["-c", 'printf %s "$TERM"'], {
encoding: "utf8",
timeout: 1e3,
stdio: ["ignore", "pipe", "ignore"]
});
}, "runShellTerm");
var detectFromShellResult = /* @__PURE__ */ __name2((options = {}) => {
const env = options.env ?? process.env;
const platform = options.platform ?? process.platform;
try {
if (platform === "win32") {
if (env.WT_SESSION) {
return {
terminal: "windows_terminal",
source: "shell"
};
}
const shell = env.COMSPEC?.toLowerCase() || "";
if (/powershell/i.test(shell)) {
return {
terminal: "powershell",
source: "shell"
};
}
if (/pwsh/i.test(shell)) {
return {
terminal: "powershell",
source: "shell"
};
}
if (/cmd\.exe/i.test(shell)) {
return {
terminal: "cmd",
source: "shell"
};
}
if (/wt\.exe/i.test(shell)) {
return {
terminal: "windows_terminal",
source: "shell"
};
}
if (/conhost\.exe/i.test(shell)) {
return {
terminal: "conhost",
source: "shell"
};
}
return {
terminal: "windows_cmd",
source: "shell"
};
}
const run = options.execFileSync ?? nodeExecFileSync2;
const terminal = normalizeIdentifier(runShellTerm(run));
if (!terminal) {
return null;
}
if (!options.preferOuter) {
if (terminal.startsWith("tmux")) {
return {
terminal: "tmux",
source: "shell"
};
}
if (terminal.startsWith("screen")) {
return {
terminal: "screen",
source: "shell"
};
}
}
return {
terminal,
source: "shell"
};
} catch {
return null;
}
}, "detectFromShellResult");
var getTerminalResult = /* @__PURE__ */ __name2((options) => {
const envTerminal = detectFromEnvResult(options);
let result = envTerminal;
if (options.resolveAmbiguousFromParent && isAmbiguousEnvResult(envTerminal?.terminal ?? null, options)) {
result = detectFromParentProcessResult(options) ?? result;
}
result ??= detectFromShellResult(options);
result ??= detectFromProcessTitleResult(options);
return result ?? {
terminal: envTerminal?.terminal ?? "unknown",
source: envTerminal ? envTerminal.source : "unknown",
generic: envTerminal?.generic
};
}, "getTerminalResult");
var detectTerminalDetails = /* @__PURE__ */ __name2((options = {}) => {
const env = options.env ?? process.env;
const stdout2 = options.stdout ?? process.stdout;
const isTTY = Boolean(stdout2.isTTY);
const isSSH = Boolean(env.SSH_CONNECTION);
const term = env.TERM?.trim();
const termProgram = env.TERM_PROGRAM?.trim();
const colorTerm = env.COLORTERM?.trim();
const colorSupport = getColorSupport(env);
if (!isTTY && !options.unpipe) {
return {
terminal: "none",
source: "none",
isTTY,
isSSH,
isAmbiguous: false,
supportsColor: false,
colorLevel: 0,
supports256Color: colorSupport.supports256Color,
supportsTrueColor: colorSupport.supportsTrueColor,
term,
termProgram,
colorTerm,
generic: "none"
};
}
const result = getTerminalResult(options);
const colorLevel = getColorLevel(env, result.terminal, colorSupport);
return {
terminal: result.terminal,
source: result.source,
isTTY,
isSSH,
isAmbiguous: result.source === "env" && isAmbiguousEnvResult(result.terminal, options),
colorLevel,
supportsColor: colorLevel > 0,
supports256Color: colorSupport.supports256Color,
supportsTrueColor: colorSupport.supportsTrueColor,
term,
termProgram,
colorTerm,
generic: result.generic ?? term?.toLowerCase() ?? "unknown",
pid: result.pid,
appName: result.appName
};
}, "detectTerminalDetails");
var detectTerminal = /* @__PURE__ */ __name2((options = {}) => {
return detectTerminalDetails(options).terminal;
}, "detectTerminal");
// src/emit-keypress.ts
import { StringDecoder } from "string_decoder";
import { clearTimeout as clearTimeout2, setTimeout as setTimeout2 } from "timers";
// src/keypress.ts
var kEscape = "\x1B";
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`;
var CSI_U_ENABLE = `${kEscape}[>4;1m`;
var CSI_U_DISABLE = `${kEscape}[>4;0m`;
var KITTY_ENABLE = `${kEscape}[>1u`;
var KITTY_DISABLE = `${kEscape}[<u`;
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;
const SPECIAL_NAMES = {
8: "backspace",
// BS (ctrl+h legacy)
9: "tab",
10: "enter",
// LF
13: "return",
// CR
27: "escape",
32: "space",
127: "backspace"
// DEL
};
if (match = /^(\d+);(\d+)u$/.exec(cmd)) {
const charCode = parseInt(match[1], 10);
modifier = parseInt(match[2], 10) - 1;
const char = String.fromCharCode(charCode);
key.name = SPECIAL_NAMES[charCode] || char.toLowerCase();
key.ctrl = Boolean(modifier & 4);
key.meta = Boolean(modifier & 10);
key.shift = Boolean(modifier & 1) || char !== char.toLowerCase();
key.code = `[${match[1]}u`;
key.sequence = s;
stream.emit("keypress", void 0, key);
continue;
}
if (match = /^27;(\d+);(\d+)~$/.exec(cmd)) {
modifier = parseInt(match[1], 10) - 1;
const charCode = parseInt(match[2], 10);
const char = String.fromCharCode(charCode);
key.name = SPECIAL_NAMES[charCode] || char.toLowerCase();
key.ctrl = Boolean(modifier & 4);
key.meta = Boolean(modifier & 10);
key.shift = Boolean(modifier & 1) || char !== char.toLowerCase();
key.code = `[27;${match[1]};${match[2]}~`;
key.sequence = s;
stream.emit("keypress", void 0, key);
continue;
}
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;
}
/* xterm/gnome ESC [ letter (with modifier) */
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;
/* xterm/gnome ESC O letter (without modifier) */
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;
/* xterm/rxvt ESC [ number ~ */
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;
/* paste bracket mode */
case "[200~":
key.name = "paste-start";
break;
case "[201~":
key.name = "paste-end";
break;
/* from Cygwin and used in libuv */
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;
/* common */
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;
/* common */
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;
/* xterm ESC [ letter */
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;
/* xterm/gnome ESC O letter */
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;
/* xterm/rxvt ESC [ number ~ */
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;
/* putty */
case "[[5~":
key.name = "pageup";
break;
case "[[6~":
key.name = "pagedown";
break;
/* rxvt */
case "[7~":
key.name = "home";
break;
case "[8~":
key.name = "end";
break;
/* rxvt keys with modifiers */
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;
/* misc. */
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 = /* @__PURE__ */ Symbol("keypress-decoder");
var ESCAPE_DECODER = /* @__PURE__ */ Symbol("escape-decoder");
var kSawKeyPress = /* @__PURE__ */ 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.off("data", onData);
stream.on("newListener", onNewListener);
}
}
__name(onData, "onData");
function onNewListener(event) {
if (event === "keypress") {
stream.on("data", onData);
stream.off("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", command: "enter" },
{ 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[H", shortcut: "fn+left", name: "home" },
{ sequence: "\x1B[F", shortcut: "fn+right", name: "end" },
{ sequence: "\x1B[5~", shortcut: "fn+up", name: "pageup" },
{ sequence: "\x1B[6~", shortcut: "fn+down", name: "pagedown" },
// <fn+ctrl>
{ sequence: "\x1B[1;5F", shortcut: "fn+ctrl+right" },
{ sequence: "\x1B[1;5H", shortcut: "fn+ctrl+left" },