/** * Terminal input parser. * * Thin wrapper around WASM `input_*` functions that decode raw VT/ANSI * escape sequences into structured events. All parsing logic, state * management, and buffering lives in WASM. */ import type { Rgb, TerminalInfo } from "./terminfo.js"; /** * Modifier keys held during a key or mouse event. */ export interface KeyModifiers { alt?: true; ctrl?: true; shift?: true; } /** * Physical key identity on a US PC-101 layout. */ export type KeyCode = "a" | "b" | "c" | "d" | "e" | "f" | "g" | "h" | "i" | "j" | "k" | "l" | "m" | "n" | "o" | "p" | "q" | "r" | "s" | "t" | "u" | "v" | "w" | "x" | "y" | "z" | "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" | "`" | "-" | "=" | "[" | "]" | "\\" | ";" | "'" | "," | "." | "/" | " " | "F1" | "F2" | "F3" | "F4" | "F5" | "F6" | "F7" | "F8" | "F9" | "F10" | "F11" | "F12" | "ArrowUp" | "ArrowDown" | "ArrowLeft" | "ArrowRight" | "Home" | "End" | "Insert" | "Delete" | "PageUp" | "PageDown" | "Backtab" | "Backspace" | "Tab" | "Enter" | "Escape" | "Numpad0" | "Numpad1" | "Numpad2" | "Numpad3" | "Numpad4" | "Numpad5" | "Numpad6" | "Numpad7" | "Numpad8" | "Numpad9" | "NumpadDecimal" | "NumpadDivide" | "NumpadMultiply" | "NumpadSubtract" | "NumpadAdd" | "NumpadEnter" | "NumpadEqual" | "ShiftLeft" | "ShiftRight" | "ControlLeft" | "ControlRight" | "AltLeft" | "AltRight" | "SuperLeft" | "SuperRight" | "HyperLeft" | "HyperRight" | "MetaLeft" | "MetaRight" | "CapsLock" | "NumLock" | "ScrollLock"; /** * Shared key information present on all keyboard events. */ export interface KeyInfo extends KeyModifiers { key: string; code: KeyCode; } /** * A key was pressed. Emitted at all enhancement levels. * On legacy terminals, this is the only keyboard event type. */ export interface KeyDown extends KeyInfo { type: "keydown"; shifted?: string; text?: string; } /** * A key is being held down (auto-repeat). Only emitted with * Kitty enhancement level 2+ (report event types). */ export interface KeyRepeat extends KeyInfo { type: "keyrepeat"; shifted?: string; text?: string; } /** * A key was released. Only emitted with Kitty enhancement * level 2+ (report event types). Does not carry text, * shifted, or base fields. */ export interface KeyUp extends KeyInfo { type: "keyup"; } export type KeyEvent = KeyDown | KeyRepeat | KeyUp; /** * A mouse button was pressed. */ export interface MouseDownEvent extends KeyModifiers { type: "mousedown"; /** * Which mouse button triggered this event. */ button: "left" | "right" | "middle"; /** * `x` coordinate of this event */ x: number; /** * `y` coordinate of this event */ y: number; } /** * A mouse button was released. */ export interface MouseUpEvent extends KeyModifiers { type: "mouseup"; /** * Which mouse button triggered this event. * * Note: "release" is not technically a button, but is used by * `VT200` and `urxvt` protocols where the terminal reports a button * release without indicating which button. */ button: "left" | "right" | "middle" | "release"; /** * `x` coordinate of this event */ x: number; /** * `y` coordinate of this event */ y: number; } /** * Mouse movement while a button is held. */ export interface MouseMoveEvent extends KeyModifiers { type: "mousemove"; /** * Which mouse button is being held during the drag. */ button: "left" | "right" | "middle"; /** * Cursor column (0-based). */ x: number; /** * Cursor row (0-based). */ y: number; } /** * A scroll wheel tick. */ export interface WheelEvent extends KeyModifiers { type: "wheel"; /** * Did the wheel move up or down */ direction: "up" | "down"; /** * Cursor column at the time of the scroll (0-based). */ x: number; /** * Cursor row at the time of the scroll (0-based). */ y: number; } /** * Terminal resize notification. */ export interface ResizeEvent { type: "resize"; /** * New terminal width in columns. */ width: number; /** * New terminal height in rows. */ height: number; } /** * Cursor position report (DSR response). * * Emitted when the terminal responds to a Device Status Report * query (`\x1b[6n`) with the current cursor position. */ export interface CursorEvent { type: "cursor"; /** * Cursor row (1-based). Matches ECMA-48 DSR native format. */ row: number; /** * Cursor column (1-based). Matches ECMA-48 DSR native format. */ column: number; } /** Color depth tier reported by the terminal's XTGETTCAP probe response. */ export type ColorDepth = "truecolor" | "256" | "16"; /** * A probe-response event emitted by scan() when the terminal answers one * of the capability queries in TerminalInfo.probe. Route to term.update(). */ export type CapabilityEvent = { readonly type: "capability"; readonly key: "foreground-color"; readonly value: Rgb; } | { readonly type: "capability"; readonly key: "background-color"; readonly value: Rgb; } | { readonly type: "capability"; readonly key: "cursor-color"; readonly value: Rgb; } | { readonly type: "capability"; readonly key: "colordepth"; readonly value: ColorDepth; } | { readonly type: "capability"; readonly key: "sync-output"; readonly value: boolean; } | { readonly type: "capability"; readonly key: "kitty-keyboard"; readonly value: boolean; } | { readonly type: "capability"; readonly key: "kitty-graphics"; readonly value: boolean; } | { readonly type: "capability"; readonly key: "pointer-shape"; readonly value: boolean; }; export type { Rgb }; import type { PointerEvent } from "./term.js"; export type InputEvent = KeyEvent | MouseDownEvent | MouseUpEvent | MouseMoveEvent | WheelEvent | ResizeEvent | CursorEvent | CapabilityEvent | PointerEvent; /** * Result of a single scan() call. * * When `pending` is present, a lone ESC is buffered and the caller should * re-call scan() with an empty buffer after `pending.delay` milliseconds. */ export interface ScanResult { events: InputEvent[]; pending?: { delay: number; }; } export interface Input { /** * Feed raw bytes from stdin into the parser and return any events * produced. Call with no arguments to flush a pending ESC after the * latency period has elapsed. * * @example * ```ts * let { events, pending } = input.scan(bytes); * for (let event of events) { * dispatch(event); * } * if (pending) { * // there is a pending ESC event. wait for the delay * await sleep(pending.delay); * * // re-scan * let flush = input.scan(); * * //dispatch the flushed ESC * for (let event of flush.events) { * dispatch(event) * } * } * ``` */ scan(bytes?: Uint8Array): ScanResult; } export interface InputOptions { /** * Milliseconds to wait before resolving a lone ESC byte as the Escape * key rather than the start of an escape sequence. * * @default 25 */ escLatency?: number; /** * Terminal detection from detectTerminal(). Seeds the key trie from * terminfo.keys and uses terminfo.capabilities.colors for * colordepth denial events. When omitted, the parser uses xterm * default key sequences and a 256-color baseline. */ terminfo?: TerminalInfo; } export declare function createInput(options?: InputOptions): Promise;