@bomb.sh/tty 0.0.0-register.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,227 @@
1
+ # @bomb.sh/tty
2
+
3
+ A low-level, platform-independent terminal renderer and event parser for
4
+ JavaScript. You can use `@bomb.sh/tty` directly, or as the foundation for your
5
+ own framework.
6
+
7
+ ## Features
8
+
9
+ **Declarative terminal UI** — Build terminal interfaces the same way you'd build
10
+ a web page. `@bomb.sh/tty` uses [Clay](https://github.com/nicbarker/clay) under
11
+ the hood, giving you flexbox-like layout, pointer detection, and scroll
12
+ containers — all rendered to the terminal as box-drawing characters and ANSI
13
+ escape sequences.
14
+
15
+ **Zero I/O** — `@bomb.sh/tty` never reads stdin or writes stdout. You feed it
16
+ bytes and get bytes back. This makes it trivially embeddable in any framework,
17
+ any runtime, any event loop. There are no opinions about how you do I/O, just
18
+ pure computation.
19
+
20
+ **Runs everywhere** — The entire engine is compiled to WebAssembly, so
21
+ `@bomb.sh/tty` will run anywhere JavaScript runs with no native dependencies,
22
+ and no build step for consumers.
23
+
24
+ ### Examples
25
+
26
+ See this keyboard example and more in the [examples folder](examples/README.md).
27
+ This demo uses `@bomb.sh/tty` for all layout and input parsing.
28
+
29
+ #### Keyboard Events
30
+
31
+ The input parser decodes raw terminal bytes into structured events. Here you can
32
+ see each key event as the string "hello world" is typed.
33
+
34
+ ![Keyboard events demo](examples/keyboard/keyboard-key-events.gif)
35
+
36
+ #### Pointer Events
37
+
38
+ Here we see hover styles applied to UI elements in response to the pointer
39
+ state. Clay drives the hit testing; no manual coordinate math required.
40
+
41
+ ![Pointer events demo](examples/keyboard/keyboard-pointer-events.gif)
42
+
43
+ ## Architecture
44
+
45
+ `@bomb.sh/tty` does not do any I/O itself. On the ouput side, it converts UI
46
+ elements into a raw sequence of bytes and pointer events, and on the input side,
47
+ it converts a stream of raw bytes into structured events.
48
+
49
+ ### Output
50
+
51
+ With every frame, the entire UI tree is packed into a flat byte array and sent
52
+ to WASM in a single call. On the C side, Clay runs layout, render commands are
53
+ walked into a cell buffer, and the buffer is diffed against the previous frame.
54
+ Only the cells that actually changed produce output. The result is an ANSI
55
+ escape sequence that can be written directly to stdout. One trip to WASM per
56
+ frame, double buffered, and only the bytes that need to change hit the output
57
+ stream.
58
+
59
+ Because the WASM module is pure computation with no I/O, it runs anywhere
60
+ WebAssembly does: Deno, Node, Bun, browsers, or any other runtime.
61
+
62
+ ```
63
+ TypeScript WASM (C)
64
+ +---------------+ +---------------------------+
65
+ | | Uint32Array | |
66
+ | UI ops... | =============> | Clay layout |
67
+ | | | -> render commands |
68
+ +---------------+ | -> cell buffer (back) |
69
+ | -> diff against (front) |
70
+ | -> escape bytes |
71
+ +---------------+ | |
72
+ | | ANSI byte array| |
73
+ | stdout.write | <============= | |
74
+ | | | |
75
+ +---------------+ +---------------------------+
76
+ ```
77
+
78
+ ### Input
79
+
80
+ Raw bytes from stdin are fed into a WASM-based parser that recognizes VT/ANSI
81
+ escape sequences, UTF-8 codepoints, and mouse protocols (VT200, SGR, urxvt). The
82
+ parser maintains its own internal buffer so partial sequences that arrive across
83
+ read boundaries are reassembled automatically. A lone ESC byte is held for a
84
+ configurable latency window (default 25ms) before being emitted, giving
85
+ multi-byte sequences time to arrive.
86
+
87
+ ```
88
+ TypeScript WASM (C)
89
+ +---------------+ +---------------------------+
90
+ | | raw byte array| |
91
+ | stdin.read | =============> | trie match (keys/seqs) |
92
+ | | | -> mouse protocol |
93
+ | | | -> UTF-8 decode |
94
+ +---------------+ | -> ESC codes |
95
+ | |
96
+ +---------------+ | |
97
+ | | events[] | |
98
+ | KeyEvent | <============= | |
99
+ | MouseDownEvent| | |
100
+ | MouseUpEvent | +---------------------------+
101
+ | MouseMoveEvent|
102
+ | WheelEvent |
103
+ | ResizeEvent |
104
+ +---------------+
105
+ ```
106
+
107
+ ## Usage
108
+
109
+ ### Rendering
110
+
111
+ To render this:
112
+
113
+ ```
114
+ ╭───────────────╮
115
+ │ Hello, World! │
116
+ ╰───────────────╯
117
+ ```
118
+
119
+ ```typescript
120
+ import { close, createTerm, grow, open, rgba, text } from "@bomb.sh/tty";
121
+
122
+ let term = await createTerm({ width: 80, height: 24 });
123
+
124
+ let { output } = term.render([
125
+ open("root", {
126
+ layout: { width: grow(), height: grow(), direction: "ttb" },
127
+ }),
128
+ open("greeting", {
129
+ layout: { padding: { left: 1, right: 1 } },
130
+ border: {
131
+ color: rgba(0, 255, 0),
132
+ left: 1,
133
+ right: 1,
134
+ top: 1,
135
+ bottom: 1,
136
+ },
137
+ cornerRadius: { tl: 1, tr: 1, bl: 1, br: 1 },
138
+ }),
139
+ text("Hello, World!"),
140
+ close(),
141
+ close(),
142
+ ]);
143
+
144
+ process.stdout.write(output);
145
+ ```
146
+
147
+ ### Pointer detection
148
+
149
+ Pass pointer state to `render()` to have `@bomb.sh/tty` do hit detection and
150
+ return pointer events in addition to the byte sequence.
151
+
152
+ ```typescript
153
+ let { output, events } = term.render(
154
+ [
155
+ open("root", {
156
+ layout: { width: grow(), height: grow(), direction: "ltr" },
157
+ }),
158
+ open("sidebar", {
159
+ layout: { width: fixed(20), height: grow() },
160
+ bg: rgba(30, 30, 40),
161
+ }),
162
+ text("Sidebar"),
163
+ close(),
164
+ open("main", {
165
+ layout: { width: grow(), height: grow() },
166
+ }),
167
+ text("Main content"),
168
+ close(),
169
+ close(),
170
+ ],
171
+ {
172
+ pointer: { x: mouseX, y: mouseY, down: mouseDown },
173
+ },
174
+ );
175
+
176
+ for (let event of events) {
177
+ // { type: "pointerenter", id: "sidebar" }
178
+ // { type: "pointerleave", id: "sidebar" }
179
+ // { type: "pointerclick", id: "main" }
180
+ console.log(event);
181
+ }
182
+
183
+ process.stdout.write(output);
184
+ ```
185
+
186
+ ### Input parsing
187
+
188
+ ```typescript
189
+ import { createInput } from "@bomb.sh/tty/input";
190
+
191
+ let input = await createInput({ escLatency: 25 });
192
+
193
+ process.stdin.setRawMode(true);
194
+ let timer: ReturnType<typeof setTimeout> | undefined;
195
+
196
+ process.stdin.on("data", (buf) => {
197
+ clearTimeout(timer);
198
+
199
+ let { events, pending } = input.scan(new Uint8Array(buf));
200
+
201
+ for (let event of events) {
202
+ dispatch(event);
203
+ }
204
+
205
+ // if a lone ESC is pending, wait and re-scan to flush it
206
+ if (pending) {
207
+ timer = setTimeout(() => {
208
+ let flush = input.scan();
209
+ for (let event of flush.events) {
210
+ dispatch(event);
211
+ }
212
+ }, pending.delay);
213
+ }
214
+ });
215
+ ```
216
+
217
+ ## Development
218
+
219
+ For local source builds, toolchain setup, and `clay` submodule instructions, see
220
+ [BUILD.md](BUILD.md).
221
+
222
+ Quick local validation:
223
+
224
+ ```sh
225
+ make
226
+ deno task test
227
+ ```
@@ -0,0 +1,102 @@
1
+ export declare const EVENT_KEY = 1;
2
+ export declare const EVENT_MOUSE = 2;
3
+ export declare const EVENT_RESIZE = 3;
4
+ export declare const EVENT_CURSOR = 4;
5
+ export declare const MOD_ALT = 1;
6
+ export declare const MOD_CTRL = 2;
7
+ export declare const MOD_SHIFT = 4;
8
+ export declare const MOD_MOTION = 8;
9
+ export declare const MOD_RELEASE = 16;
10
+ export declare const KEY_F1 = 65535;
11
+ export declare const KEY_F2 = 65534;
12
+ export declare const KEY_F3 = 65533;
13
+ export declare const KEY_F4 = 65532;
14
+ export declare const KEY_F5 = 65531;
15
+ export declare const KEY_F6 = 65530;
16
+ export declare const KEY_F7 = 65529;
17
+ export declare const KEY_F8 = 65528;
18
+ export declare const KEY_F9 = 65527;
19
+ export declare const KEY_F10 = 65526;
20
+ export declare const KEY_F11 = 65525;
21
+ export declare const KEY_F12 = 65524;
22
+ export declare const KEY_ARROW_UP = 65523;
23
+ export declare const KEY_ARROW_DOWN = 65522;
24
+ export declare const KEY_ARROW_LEFT = 65521;
25
+ export declare const KEY_ARROW_RIGHT = 65520;
26
+ export declare const KEY_HOME = 65519;
27
+ export declare const KEY_END = 65518;
28
+ export declare const KEY_INSERT = 65517;
29
+ export declare const KEY_DELETE = 65516;
30
+ export declare const KEY_PGUP = 65515;
31
+ export declare const KEY_PGDN = 65514;
32
+ export declare const KEY_BACKTAB = 65513;
33
+ export declare const KEY_NUMPAD_0 = 65506;
34
+ export declare const KEY_NUMPAD_1 = 65505;
35
+ export declare const KEY_NUMPAD_2 = 65504;
36
+ export declare const KEY_NUMPAD_3 = 65503;
37
+ export declare const KEY_NUMPAD_4 = 65502;
38
+ export declare const KEY_NUMPAD_5 = 65501;
39
+ export declare const KEY_NUMPAD_6 = 65500;
40
+ export declare const KEY_NUMPAD_7 = 65499;
41
+ export declare const KEY_NUMPAD_8 = 65498;
42
+ export declare const KEY_NUMPAD_9 = 65497;
43
+ export declare const KEY_NUMPAD_DECIMAL = 65496;
44
+ export declare const KEY_NUMPAD_DIVIDE = 65495;
45
+ export declare const KEY_NUMPAD_MULTIPLY = 65494;
46
+ export declare const KEY_NUMPAD_SUBTRACT = 65493;
47
+ export declare const KEY_NUMPAD_ADD = 65492;
48
+ export declare const KEY_NUMPAD_ENTER = 65491;
49
+ export declare const KEY_NUMPAD_EQUAL = 65490;
50
+ export declare const KEY_SHIFT_LEFT = 65489;
51
+ export declare const KEY_SHIFT_RIGHT = 65488;
52
+ export declare const KEY_CONTROL_LEFT = 65487;
53
+ export declare const KEY_CONTROL_RIGHT = 65486;
54
+ export declare const KEY_ALT_LEFT = 65485;
55
+ export declare const KEY_ALT_RIGHT = 65484;
56
+ export declare const KEY_SUPER_LEFT = 65483;
57
+ export declare const KEY_SUPER_RIGHT = 65482;
58
+ export declare const KEY_HYPER_LEFT = 65481;
59
+ export declare const KEY_HYPER_RIGHT = 65480;
60
+ export declare const KEY_META_LEFT = 65479;
61
+ export declare const KEY_META_RIGHT = 65478;
62
+ export declare const KEY_CAPS_LOCK = 65477;
63
+ export declare const KEY_NUM_LOCK = 65476;
64
+ export declare const KEY_SCROLL_LOCK = 65475;
65
+ export declare const KEY_MOUSE_LEFT = 65512;
66
+ export declare const KEY_MOUSE_RIGHT = 65511;
67
+ export declare const KEY_MOUSE_MIDDLE = 65510;
68
+ export declare const KEY_MOUSE_RELEASE = 65509;
69
+ export declare const KEY_MOUSE_WHEEL_UP = 65508;
70
+ export declare const KEY_MOUSE_WHEEL_DOWN = 65507;
71
+ export declare const KEY_ESC = 27;
72
+ export declare const KEY_ENTER = 13;
73
+ export declare const KEY_TAB = 9;
74
+ export declare const KEY_BACKSPACE = 127;
75
+ export declare const KEY_SPACE = 32;
76
+ export interface NativeInputEvent {
77
+ type: number;
78
+ mod: number;
79
+ key: number;
80
+ ch: number;
81
+ x: number;
82
+ y: number;
83
+ w: number;
84
+ h: number;
85
+ action: number;
86
+ shifted: number;
87
+ base: number;
88
+ text: number[];
89
+ }
90
+ export declare function readEvent(view: DataView, ptr: number): NativeInputEvent;
91
+ export interface InputNative {
92
+ memory: WebAssembly.Memory;
93
+ state: number;
94
+ buffer: number;
95
+ scan(st: number, buf: number, len: number, now: number): number;
96
+ count(st: number): number;
97
+ event(st: number, index: number): number;
98
+ delay(st: number): number;
99
+ }
100
+ export declare function createInputNative(escLatency: number): Promise<InputNative>;
101
+ export declare const MAX_TERMINFO = 32768;
102
+ export declare const SCAN_BUFFER_SIZE = 4096;
@@ -0,0 +1,150 @@
1
+ export const EVENT_KEY = 1;
2
+ export const EVENT_MOUSE = 2;
3
+ export const EVENT_RESIZE = 3;
4
+ export const EVENT_CURSOR = 4;
5
+ export const MOD_ALT = 1;
6
+ export const MOD_CTRL = 2;
7
+ export const MOD_SHIFT = 4;
8
+ export const MOD_MOTION = 8;
9
+ export const MOD_RELEASE = 16;
10
+ export const KEY_F1 = 0xFFFF;
11
+ export const KEY_F2 = 0xFFFE;
12
+ export const KEY_F3 = 0xFFFD;
13
+ export const KEY_F4 = 0xFFFC;
14
+ export const KEY_F5 = 0xFFFB;
15
+ export const KEY_F6 = 0xFFFA;
16
+ export const KEY_F7 = 0xFFF9;
17
+ export const KEY_F8 = 0xFFF8;
18
+ export const KEY_F9 = 0xFFF7;
19
+ export const KEY_F10 = 0xFFF6;
20
+ export const KEY_F11 = 0xFFF5;
21
+ export const KEY_F12 = 0xFFF4;
22
+ export const KEY_ARROW_UP = 0xFFF3;
23
+ export const KEY_ARROW_DOWN = 0xFFF2;
24
+ export const KEY_ARROW_LEFT = 0xFFF1;
25
+ export const KEY_ARROW_RIGHT = 0xFFF0;
26
+ export const KEY_HOME = 0xFFEF;
27
+ export const KEY_END = 0xFFEE;
28
+ export const KEY_INSERT = 0xFFED;
29
+ export const KEY_DELETE = 0xFFEC;
30
+ export const KEY_PGUP = 0xFFEB;
31
+ export const KEY_PGDN = 0xFFEA;
32
+ export const KEY_BACKTAB = 0xFFE9;
33
+ export const KEY_NUMPAD_0 = 0xFFE2;
34
+ export const KEY_NUMPAD_1 = 0xFFE1;
35
+ export const KEY_NUMPAD_2 = 0xFFE0;
36
+ export const KEY_NUMPAD_3 = 0xFFDF;
37
+ export const KEY_NUMPAD_4 = 0xFFDE;
38
+ export const KEY_NUMPAD_5 = 0xFFDD;
39
+ export const KEY_NUMPAD_6 = 0xFFDC;
40
+ export const KEY_NUMPAD_7 = 0xFFDB;
41
+ export const KEY_NUMPAD_8 = 0xFFDA;
42
+ export const KEY_NUMPAD_9 = 0xFFD9;
43
+ export const KEY_NUMPAD_DECIMAL = 0xFFD8;
44
+ export const KEY_NUMPAD_DIVIDE = 0xFFD7;
45
+ export const KEY_NUMPAD_MULTIPLY = 0xFFD6;
46
+ export const KEY_NUMPAD_SUBTRACT = 0xFFD5;
47
+ export const KEY_NUMPAD_ADD = 0xFFD4;
48
+ export const KEY_NUMPAD_ENTER = 0xFFD3;
49
+ export const KEY_NUMPAD_EQUAL = 0xFFD2;
50
+ export const KEY_SHIFT_LEFT = 0xFFD1;
51
+ export const KEY_SHIFT_RIGHT = 0xFFD0;
52
+ export const KEY_CONTROL_LEFT = 0xFFCF;
53
+ export const KEY_CONTROL_RIGHT = 0xFFCE;
54
+ export const KEY_ALT_LEFT = 0xFFCD;
55
+ export const KEY_ALT_RIGHT = 0xFFCC;
56
+ export const KEY_SUPER_LEFT = 0xFFCB;
57
+ export const KEY_SUPER_RIGHT = 0xFFCA;
58
+ export const KEY_HYPER_LEFT = 0xFFC9;
59
+ export const KEY_HYPER_RIGHT = 0xFFC8;
60
+ export const KEY_META_LEFT = 0xFFC7;
61
+ export const KEY_META_RIGHT = 0xFFC6;
62
+ export const KEY_CAPS_LOCK = 0xFFC5;
63
+ export const KEY_NUM_LOCK = 0xFFC4;
64
+ export const KEY_SCROLL_LOCK = 0xFFC3;
65
+ export const KEY_MOUSE_LEFT = 0xFFE8;
66
+ export const KEY_MOUSE_RIGHT = 0xFFE7;
67
+ export const KEY_MOUSE_MIDDLE = 0xFFE6;
68
+ export const KEY_MOUSE_RELEASE = 0xFFE5;
69
+ export const KEY_MOUSE_WHEEL_UP = 0xFFE4;
70
+ export const KEY_MOUSE_WHEEL_DOWN = 0xFFE3;
71
+ export const KEY_ESC = 0x1B;
72
+ export const KEY_ENTER = 0x0D;
73
+ export const KEY_TAB = 0x09;
74
+ export const KEY_BACKSPACE = 0x7F;
75
+ export const KEY_SPACE = 0x20;
76
+ import { array, int32, offsets, struct, uint16, uint32, uint8, } from "./typedef.js";
77
+ const MAX_TEXT_CODEPOINTS = 8;
78
+ const InputEventLayout = struct({
79
+ type: uint8(),
80
+ mod: uint8(),
81
+ key: uint16(),
82
+ ch: uint32(),
83
+ x: int32(),
84
+ y: int32(),
85
+ w: int32(),
86
+ h: int32(),
87
+ action: uint8(),
88
+ shifted: uint32(),
89
+ base: uint32(),
90
+ text: array(uint32(), MAX_TEXT_CODEPOINTS),
91
+ text_len: uint8(),
92
+ });
93
+ const { type: OFFSET_TYPE, mod: OFFSET_MOD, key: OFFSET_KEY, ch: OFFSET_CH, x: OFFSET_X, y: OFFSET_Y, w: OFFSET_W, h: OFFSET_H, action: OFFSET_ACTION, text_len: OFFSET_TEXT_LEN, shifted: OFFSET_SHIFTED, base: OFFSET_BASE, text: OFFSET_TEXT, } = offsets(InputEventLayout);
94
+ export function readEvent(view, ptr) {
95
+ let len = view.getUint8(ptr + OFFSET_TEXT_LEN);
96
+ let text = [];
97
+ for (let i = 0; i < len && i < MAX_TEXT_CODEPOINTS; i++) {
98
+ text.push(view.getUint32(ptr + OFFSET_TEXT + i * 4, true));
99
+ }
100
+ return {
101
+ type: view.getUint8(ptr + OFFSET_TYPE),
102
+ mod: view.getUint8(ptr + OFFSET_MOD),
103
+ key: view.getUint16(ptr + OFFSET_KEY, true),
104
+ ch: view.getUint32(ptr + OFFSET_CH, true),
105
+ x: view.getInt32(ptr + OFFSET_X, true),
106
+ y: view.getInt32(ptr + OFFSET_Y, true),
107
+ w: view.getInt32(ptr + OFFSET_W, true),
108
+ h: view.getInt32(ptr + OFFSET_H, true),
109
+ action: view.getUint8(ptr + OFFSET_ACTION),
110
+ shifted: view.getUint32(ptr + OFFSET_SHIFTED, true),
111
+ base: view.getUint32(ptr + OFFSET_BASE, true),
112
+ text,
113
+ };
114
+ }
115
+ import { compiled } from "./wasm.js";
116
+ export async function createInputNative(escLatency) {
117
+ let memory = new WebAssembly.Memory({ initial: 4 });
118
+ let instance = await WebAssembly.instantiate(compiled, {
119
+ env: { memory },
120
+ clay: {
121
+ measureTextFunction() { },
122
+ queryScrollOffsetFunction(ret) {
123
+ let v = new DataView(memory.buffer);
124
+ v.setFloat32(ret, 0, true);
125
+ v.setFloat32(ret + 4, 0, true);
126
+ },
127
+ },
128
+ });
129
+ let exports = instance.exports;
130
+ let heap = exports.__heap_base.value;
131
+ let size = exports.input_size();
132
+ let state = exports.input_init(heap, escLatency);
133
+ let buffer = (heap + size + 7) & ~7;
134
+ return {
135
+ memory,
136
+ state,
137
+ buffer,
138
+ scan: exports.input_scan,
139
+ count: exports.input_count,
140
+ event: exports.input_event,
141
+ delay: exports.input_delay,
142
+ };
143
+ }
144
+ // Compiled terminfo entries are limited to 4096 bytes (legacy) or 32768
145
+ // bytes (extended ncurses format). We use the extended limit as our upper
146
+ // bound. See https://man7.org/linux/man-pages/man5/term.5.html
147
+ export const MAX_TERMINFO = 32768;
148
+ // Must match SCAN_BUFFER_SIZE in input.c — the maximum bytes input_scan()
149
+ // can accept in a single call.
150
+ export const SCAN_BUFFER_SIZE = 4096;
package/esm/input.d.ts ADDED
@@ -0,0 +1,226 @@
1
+ /**
2
+ * Terminal input parser.
3
+ *
4
+ * Thin wrapper around WASM `input_*` functions that decode raw VT/ANSI
5
+ * escape sequences into structured events. All parsing logic, state
6
+ * management, and buffering lives in WASM.
7
+ */
8
+ /**
9
+ * Modifier keys held during a key or mouse event.
10
+ */
11
+ export interface KeyModifiers {
12
+ alt?: true;
13
+ ctrl?: true;
14
+ shift?: true;
15
+ }
16
+ /**
17
+ * Physical key identity on a US PC-101 layout.
18
+ */
19
+ 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";
20
+ /**
21
+ * Shared key information present on all keyboard events.
22
+ */
23
+ export interface KeyInfo extends KeyModifiers {
24
+ key: string;
25
+ code: KeyCode;
26
+ }
27
+ /**
28
+ * A key was pressed. Emitted at all enhancement levels.
29
+ * On legacy terminals, this is the only keyboard event type.
30
+ */
31
+ export interface KeyDown extends KeyInfo {
32
+ type: "keydown";
33
+ shifted?: string;
34
+ text?: string;
35
+ }
36
+ /**
37
+ * A key is being held down (auto-repeat). Only emitted with
38
+ * Kitty enhancement level 2+ (report event types).
39
+ */
40
+ export interface KeyRepeat extends KeyInfo {
41
+ type: "keyrepeat";
42
+ shifted?: string;
43
+ text?: string;
44
+ }
45
+ /**
46
+ * A key was released. Only emitted with Kitty enhancement
47
+ * level 2+ (report event types). Does not carry text,
48
+ * shifted, or base fields.
49
+ */
50
+ export interface KeyUp extends KeyInfo {
51
+ type: "keyup";
52
+ }
53
+ export type KeyEvent = KeyDown | KeyRepeat | KeyUp;
54
+ /**
55
+ * A mouse button was pressed.
56
+ */
57
+ export interface MouseDownEvent extends KeyModifiers {
58
+ type: "mousedown";
59
+ /**
60
+ * Which mouse button triggered this event.
61
+ */
62
+ button: "left" | "right" | "middle";
63
+ /**
64
+ * `x` coordinate of this event
65
+ */
66
+ x: number;
67
+ /**
68
+ * `y` coordinate of this event
69
+ */
70
+ y: number;
71
+ }
72
+ /**
73
+ * A mouse button was released.
74
+ */
75
+ export interface MouseUpEvent extends KeyModifiers {
76
+ type: "mouseup";
77
+ /**
78
+ * Which mouse button triggered this event.
79
+ *
80
+ * Note: "release" is not technically a button, but is used by
81
+ * `VT200` and `urxvt` protocols where the terminal reports a button
82
+ * release without indicating which button.
83
+ */
84
+ button: "left" | "right" | "middle" | "release";
85
+ /**
86
+ * `x` coordinate of this event
87
+ */
88
+ x: number;
89
+ /**
90
+ * `y` coordinate of this event
91
+ */
92
+ y: number;
93
+ }
94
+ /**
95
+ * Mouse movement while a button is held.
96
+ */
97
+ export interface MouseMoveEvent extends KeyModifiers {
98
+ type: "mousemove";
99
+ /**
100
+ * Which mouse button is being held during the drag.
101
+ */
102
+ button: "left" | "right" | "middle";
103
+ /**
104
+ * Cursor column (0-based).
105
+ */
106
+ x: number;
107
+ /**
108
+ * Cursor row (0-based).
109
+ */
110
+ y: number;
111
+ }
112
+ /**
113
+ * A scroll wheel tick.
114
+ */
115
+ export interface WheelEvent extends KeyModifiers {
116
+ type: "wheel";
117
+ /**
118
+ * Did the wheel move up or down
119
+ */
120
+ direction: "up" | "down";
121
+ /**
122
+ * Cursor column at the time of the scroll (0-based).
123
+ */
124
+ x: number;
125
+ /**
126
+ * Cursor row at the time of the scroll (0-based).
127
+ */
128
+ y: number;
129
+ }
130
+ /**
131
+ * Terminal resize notification.
132
+ */
133
+ export interface ResizeEvent {
134
+ type: "resize";
135
+ /**
136
+ * New terminal width in columns.
137
+ */
138
+ width: number;
139
+ /**
140
+ * New terminal height in rows.
141
+ */
142
+ height: number;
143
+ }
144
+ /**
145
+ * Cursor position report (DSR response).
146
+ *
147
+ * Emitted when the terminal responds to a Device Status Report
148
+ * query (`\x1b[6n`) with the current cursor position.
149
+ */
150
+ export interface CursorEvent {
151
+ type: "cursor";
152
+ /**
153
+ * Cursor row (1-based). Matches ECMA-48 DSR native format.
154
+ */
155
+ row: number;
156
+ /**
157
+ * Cursor column (1-based). Matches ECMA-48 DSR native format.
158
+ */
159
+ column: number;
160
+ }
161
+ import type { PointerEvent } from "./term.js";
162
+ export type InputEvent = KeyEvent | MouseDownEvent | MouseUpEvent | MouseMoveEvent | WheelEvent | ResizeEvent | CursorEvent | PointerEvent;
163
+ /**
164
+ * Result of a single scan() call.
165
+ *
166
+ * When `pending` is present, a lone ESC is buffered and the caller should
167
+ * re-call scan() with an empty buffer after `pending.delay` milliseconds.
168
+ */
169
+ export interface ScanResult {
170
+ events: InputEvent[];
171
+ pending?: {
172
+ delay: number;
173
+ };
174
+ }
175
+ export interface Input {
176
+ /**
177
+ * Feed raw bytes from stdin into the parser and return any events
178
+ * produced. Call with no arguments to flush a pending ESC after the
179
+ * latency period has elapsed.
180
+ *
181
+ * @example
182
+ * ```ts
183
+ * let { events, pending } = input.scan(bytes);
184
+ * for (let event of events) {
185
+ * dispatch(event);
186
+ * }
187
+ * if (pending) {
188
+ * // there is a pending ESC event. wait for the delay
189
+ * await sleep(pending.delay);
190
+ *
191
+ * // re-scan
192
+ * let flush = input.scan();
193
+ *
194
+ * //dispatch the flushed ESC
195
+ * for (let event of flush.events) {
196
+ * dispatch(event)
197
+ * }
198
+ * }
199
+ * ```
200
+ */
201
+ scan(bytes?: Uint8Array): ScanResult;
202
+ }
203
+ export interface InputOptions {
204
+ /**
205
+ * Milliseconds to wait before resolving a lone ESC byte as the Escape
206
+ * key rather than the start of an escape sequence. Lower values feel
207
+ * snappier but risk misinterpreting sequences on slow connections.
208
+ *
209
+ * For reference, Vim's `ttimeoutlen` defaults to 100ms and ncurses
210
+ * `ESCDELAY` defaults to 1000ms. The default of 25ms is tuned for
211
+ * local terminals where escape sequences arrive within microseconds.
212
+ *
213
+ * @default 25
214
+ */
215
+ escLatency?: number;
216
+ /**
217
+ * Compiled terminfo binary to load terminal-specific escape sequences.
218
+ *
219
+ * This is the format used by files like /usr/lib/terminfo/78/xterm-256color
220
+ * and they can be directly loaded from disk into this option.
221
+ *
222
+ * If no terminfo is provided it will use xterm capabilities as the default
223
+ */
224
+ terminfo?: Uint8Array;
225
+ }
226
+ export declare function createInput(options?: InputOptions): Promise<Input>;