@promptctl/rich-js 0.19.0 → 0.21.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.
Files changed (58) hide show
  1. package/README.md +3 -2
  2. package/dist/core/anchor.d.ts +5 -0
  3. package/dist/core/anchor.js +10 -0
  4. package/dist/core/color.d.ts +1 -0
  5. package/dist/core/color.js +14 -3
  6. package/dist/core/noise.d.ts +31 -0
  7. package/dist/core/noise.js +94 -0
  8. package/dist/core/paint.js +13 -2
  9. package/dist/core/pretty.d.ts +23 -2
  10. package/dist/core/pretty.js +54 -18
  11. package/dist/core/protocol.d.ts +2 -0
  12. package/dist/core/protocol.js +3 -2
  13. package/dist/core/selection.d.ts +12 -0
  14. package/dist/core/selection.js +14 -0
  15. package/dist/core/strip.d.ts +159 -76
  16. package/dist/core/strip.js +367 -215
  17. package/dist/core/style.js +10 -7
  18. package/dist/host/app.d.ts +68 -2
  19. package/dist/host/app.js +125 -15
  20. package/dist/host/index.d.ts +3 -1
  21. package/dist/host/index.js +1 -0
  22. package/dist/host/input.d.ts +87 -0
  23. package/dist/host/input.js +517 -0
  24. package/dist/host/terminal-host.d.ts +12 -0
  25. package/dist/host/terminal-host.js +2 -0
  26. package/dist/index.d.ts +11 -6
  27. package/dist/index.js +7 -4
  28. package/dist/node/terminal-host.d.ts +7 -0
  29. package/dist/node/terminal-host.js +7 -0
  30. package/dist/renderables/effect.d.ts +89 -16
  31. package/dist/renderables/effect.js +115 -42
  32. package/dist/renderables/effects.d.ts +260 -0
  33. package/dist/renderables/effects.js +596 -0
  34. package/dist/renderables/embed.d.ts +11 -5
  35. package/dist/renderables/embed.js +13 -6
  36. package/dist/renderables/flexStrip.d.ts +1 -1
  37. package/dist/renderables/flexStrip.js +10 -9
  38. package/dist/renderables/list-view.d.ts +42 -0
  39. package/dist/renderables/list-view.js +78 -0
  40. package/dist/renderables/progress.d.ts +6 -0
  41. package/dist/renderables/progress.js +11 -4
  42. package/dist/renderables/progressBar.d.ts +22 -2
  43. package/dist/renderables/progressBar.js +52 -7
  44. package/dist/renderables/rule.d.ts +2 -2
  45. package/dist/renderables/rule.js +12 -10
  46. package/dist/renderables/table.js +5 -7
  47. package/dist/renderables/viewport.d.ts +30 -0
  48. package/dist/renderables/viewport.js +59 -3
  49. package/dist/widgets/dropdown.js +3 -3
  50. package/dist/widgets/event-router.d.ts +31 -40
  51. package/dist/widgets/event-router.js +52 -483
  52. package/dist/widgets/hit.js +2 -4
  53. package/dist/widgets/index.d.ts +2 -1
  54. package/dist/widgets/types.d.ts +8 -25
  55. package/dist/widgets/types.js +8 -0
  56. package/dist/widgets/widget-app.d.ts +22 -7
  57. package/dist/widgets/widget-app.js +22 -12
  58. package/package.json +26 -18
@@ -642,8 +642,11 @@ export const DEFAULT_STYLES = {
642
642
  "log.line_no": Style.parse("cyan"),
643
643
  "log.level": NULL_STYLE,
644
644
  // Rule
645
- "rule.line": Style.parse("green"),
646
- "rule.text": Style.parse("bold"),
645
+ "rule.line": Style.parse("bright_green"),
646
+ "rule.text": NULL_STYLE,
647
+ // Strip
648
+ "strip.separator": Style.parse("bright_black"),
649
+ "strip.rail": Style.parse("bright_black"),
647
650
  // JSON
648
651
  "json.brace": Style.parse("bold"),
649
652
  "json.bool_true": Style.parse("italic bright_green"),
@@ -677,9 +680,9 @@ export const DEFAULT_STYLES = {
677
680
  "markdown.link_url": Style.parse("blue"),
678
681
  // Progress
679
682
  "progress.description": NULL_STYLE,
680
- "progress.percentage": Style.parse("cyan"),
683
+ "progress.percentage": Style.parse("magenta"),
681
684
  "progress.remaining": Style.parse("cyan"),
682
- "progress.elapsed": Style.parse("cyan"),
685
+ "progress.elapsed": Style.parse("yellow"),
683
686
  "progress.spinner": Style.parse("green"),
684
687
  "progress.download": Style.parse("green"),
685
688
  "progress.filesize": Style.parse("green"),
@@ -687,9 +690,9 @@ export const DEFAULT_STYLES = {
687
690
  "progress.data.speed": Style.parse("red"),
688
691
  // Bar
689
692
  "bar.back": Style.parse("grey23"),
690
- "bar.complete": Style.parse("magenta"),
691
- "bar.finished": Style.parse("green"),
692
- "bar.pulse": Style.parse("magenta"),
693
+ "bar.complete": Style.parse("rgb(249,38,114)"),
694
+ "bar.finished": Style.parse("rgb(114,156,31)"),
695
+ "bar.pulse": Style.parse("rgb(249,38,114)"),
693
696
  // Scrollbar
694
697
  "scrollbar.thumb": NULL_STYLE,
695
698
  "scrollbar.track": Style.parse("grey37"),
@@ -23,6 +23,12 @@
23
23
  * frame's encoding. What differs per host — whether the program can end
24
24
  * under us, how it is suspended — is the `TerminalHost`'s, so the same app
25
25
  * runs in node and in a browser with no path of its own for either.
26
+ *
27
+ * [LAW:single-enforcer] Input has one reader: the app subscribes to its
28
+ * host's bytes once and parses them (`host/input`), so a program hears keys
29
+ * and the pointer as events and never decodes a byte. The quit keys are
30
+ * checked here, before any handler hears the key, so nothing a program binds
31
+ * — a focused field in a `WidgetApp` included — can swallow them.
26
32
  */
27
33
  import { type Clock, type FrameRate } from "../core/clock.js";
28
34
  import { type ConsoleOptions } from "../core/console.js";
@@ -30,6 +36,7 @@ import { Segment } from "../core/segment.js";
30
36
  import { type Surface } from "../core/paint.js";
31
37
  import { type Renderable } from "../core/protocol.js";
32
38
  import type { Unsubscribe } from "../core/subscription.js";
39
+ import { type KeyPress, type ScreenMouseEvent } from "./input.js";
33
40
  import type { TerminalHost } from "./terminal-host.js";
34
41
  /**
35
42
  * Where an app is in its life. `idle` has not run; `running` holds the
@@ -55,15 +62,39 @@ export interface AppOptions extends DrawOptions {
55
62
  readonly clock?: Clock;
56
63
  /** How often the app paints while something is animating. 30 frames a second, unless given. */
57
64
  readonly rate?: FrameRate;
65
+ /**
66
+ * The keys that stop the app, by `keyName`, heard before any `onKey`
67
+ * handler. `["ctrl+c"]` unless given; `[]` leaves every key to the program.
68
+ * A name no key goes by throws here.
69
+ */
70
+ readonly quitKeys?: readonly string[];
58
71
  }
72
+ /**
73
+ * A turn of the wheel, where the pointer was when it turned: `lines` is
74
+ * positive towards the end of the content, a line a notch.
75
+ */
76
+ export interface Wheel {
77
+ readonly x: number;
78
+ readonly y: number;
79
+ readonly lines: number;
80
+ }
81
+ declare const WHEEL_LINES: {
82
+ readonly scroll_up: -1;
83
+ readonly scroll_down: 1;
84
+ };
85
+ type WheelTurn = Extract<ScreenMouseEvent, {
86
+ type: keyof typeof WHEEL_LINES;
87
+ }>;
88
+ /** A pointer event `onMouse` hears: every one but the vertical wheel, which is `onWheel`'s. */
89
+ export type AppMouseEvent = Exclude<ScreenMouseEvent, WheelTurn>;
59
90
  export declare class App {
60
91
  private readonly host;
61
92
  private readonly surface;
62
93
  private readonly view;
63
94
  private readonly clock;
64
- private readonly rate;
95
+ private _rate;
65
96
  private readonly animations;
66
- private stopTicking;
97
+ private ticking;
67
98
  private readonly console;
68
99
  private _phase;
69
100
  private _frame;
@@ -71,9 +102,21 @@ export declare class App {
71
102
  private refreshQueued;
72
103
  private subscriptions;
73
104
  private readonly paintHandlers;
105
+ private readonly keyHandlers;
106
+ private readonly mouseHandlers;
107
+ private readonly wheelHandlers;
108
+ private readonly quitKeys;
109
+ private readonly input;
74
110
  private settle;
75
111
  constructor(options: AppOptions);
76
112
  get phase(): AppPhase;
113
+ /** How often the app paints while something is animating. */
114
+ get rate(): FrameRate;
115
+ /**
116
+ * Set how often the app paints: an animation under way goes on at the new
117
+ * rate from its next frame, without a frame lost or one painted twice.
118
+ */
119
+ set rate(rate: FrameRate);
77
120
  /** The rows on screen now, as painted: what a pointer event lands on. */
78
121
  get frame(): readonly (readonly Segment[])[];
79
122
  /**
@@ -114,6 +157,26 @@ export declare class App {
114
157
  * handler that throws fails the app as a frame that throws does.
115
158
  */
116
159
  onPaint(handler: (frame: readonly (readonly Segment[])[]) => void): Unsubscribe;
160
+ /**
161
+ * Hear every key the app does not quit on, by the `KeyPress` the terminal
162
+ * reported; `keyName` names it. A handler that throws fails the app.
163
+ */
164
+ onKey(handler: (press: KeyPress) => void): Unsubscribe;
165
+ /**
166
+ * Hear every pointer event but the vertical wheel, which is `onWheel`'s,
167
+ * in terminal cells. Only the alternate surface reports the pointer. A
168
+ * handler that throws fails the app.
169
+ */
170
+ onMouse(handler: (event: AppMouseEvent) => void): Unsubscribe;
171
+ /**
172
+ * Offer every wheel turn to `handler`, which returns whether it moved
173
+ * something. A turn no handler moves anything with is passed outward to the
174
+ * host (`TerminalHost.passWheel`), so the page or terminal around the app
175
+ * scrolls instead. Handlers are offered a turn in the order they subscribed,
176
+ * and the first that moves something takes it. A handler that throws fails
177
+ * the app.
178
+ */
179
+ onWheel(handler: (wheel: Wheel) => boolean): Unsubscribe;
117
180
  /** Hand the terminal back for good; `run` resolves. */
118
181
  stop(): void;
119
182
  /**
@@ -126,6 +189,9 @@ export declare class App {
126
189
  private enter;
127
190
  private leave;
128
191
  private end;
192
+ private key;
193
+ private mouse;
194
+ private hear;
129
195
  private tick;
130
196
  private paint;
131
197
  private draw;
package/dist/host/app.js CHANGED
@@ -23,6 +23,12 @@
23
23
  * frame's encoding. What differs per host — whether the program can end
24
24
  * under us, how it is suspended — is the `TerminalHost`'s, so the same app
25
25
  * runs in node and in a browser with no path of its own for either.
26
+ *
27
+ * [LAW:single-enforcer] Input has one reader: the app subscribes to its
28
+ * host's bytes once and parses them (`host/input`), so a program hears keys
29
+ * and the pointer as events and never decodes a byte. The quit keys are
30
+ * checked here, before any handler hears the key, so nothing a program binds
31
+ * — a focused field in a `WidgetApp` included — can swallow them.
26
32
  */
27
33
  import { frameRate, systemClock } from "../core/clock.js";
28
34
  import { Console } from "../core/console.js";
@@ -30,6 +36,7 @@ import { Segment } from "../core/segment.js";
30
36
  import { SurfacePainter } from "../core/paint.js";
31
37
  import { fitHeight } from "../core/protocol.js";
32
38
  import { hostEnvironment } from "./host-environment.js";
39
+ import { InputParser, keyName, parseKeyName } from "./input.js";
33
40
  // [LAW:one-source-of-truth] How a frame draws — its glyphs, the style names
34
41
  // it knows, what happens to a style it cannot resolve, markup and
35
42
  // highlighting — is the console's to say, so App takes those options as the
@@ -46,6 +53,7 @@ function drawOptions(options) {
46
53
  return picked;
47
54
  }
48
55
  const DEFAULT_RATE = frameRate(30);
56
+ const DEFAULT_QUIT_KEYS = ["ctrl+c"];
49
57
  // Button presses, motion and the wheel, in the SGR encoding: coordinates as
50
58
  // decimal numbers, so a terminal wider than 223 columns still reports them.
51
59
  // The terminal reports a pointer by its row on the screen, and an inline
@@ -55,15 +63,20 @@ const POINTER = {
55
63
  alternate: { on: "\x1b[?1006h\x1b[?1000h\x1b[?1003h", off: "\x1b[?1003l\x1b[?1000l\x1b[?1006l" },
56
64
  inline: { on: "", off: "" },
57
65
  };
66
+ // The sideways wheel is not here: nothing scrolls by columns, inside an app or
67
+ // around it.
68
+ const WHEEL_LINES = { scroll_up: -1, scroll_down: 1 };
69
+ const isWheelTurn = (event) => event.type in WHEEL_LINES;
58
70
  export class App {
59
71
  host;
60
72
  surface;
61
73
  view;
62
74
  clock;
63
- rate;
75
+ _rate;
64
76
  // What is animating now: one token per `animate()` not yet released.
65
77
  animations = new Set();
66
- stopTicking = null;
78
+ // The timer painting frames, and the rate it was started at.
79
+ ticking = null;
67
80
  // [LAW:one-source-of-truth] The host is the console's whole environment:
68
81
  // its size, its colours, where bytes go. Every frame renders with this
69
82
  // console's options and is encoded for its destination.
@@ -74,6 +87,14 @@ export class App {
74
87
  refreshQueued = false;
75
88
  subscriptions = [];
76
89
  paintHandlers = new Set();
90
+ keyHandlers = new Set();
91
+ mouseHandlers = new Set();
92
+ wheelHandlers = new Set();
93
+ quitKeys;
94
+ input = new InputParser({
95
+ key: (press) => this.key(press),
96
+ mouse: (event) => this.mouse(event),
97
+ });
77
98
  settle = () => { };
78
99
  constructor(options) {
79
100
  this.host = options.host;
@@ -81,12 +102,25 @@ export class App {
81
102
  this.painter = new SurfacePainter(options.surface, (bytes) => this.host.write(bytes));
82
103
  this.view = options.view;
83
104
  this.clock = options.clock ?? systemClock();
84
- this.rate = options.rate ?? DEFAULT_RATE;
105
+ this._rate = options.rate ?? DEFAULT_RATE;
106
+ this.quitKeys = new Set((options.quitKeys ?? DEFAULT_QUIT_KEYS).map(parseKeyName));
85
107
  this.console = new Console({ ...drawOptions(options), environment: hostEnvironment(options.host) });
86
108
  }
87
109
  get phase() {
88
110
  return this._phase;
89
111
  }
112
+ /** How often the app paints while something is animating. */
113
+ get rate() {
114
+ return this._rate;
115
+ }
116
+ /**
117
+ * Set how often the app paints: an animation under way goes on at the new
118
+ * rate from its next frame, without a frame lost or one painted twice.
119
+ */
120
+ set rate(rate) {
121
+ this._rate = rate;
122
+ this.tick();
123
+ }
90
124
  /** The rows on screen now, as painted: what a pointer event lands on. */
91
125
  get frame() {
92
126
  return this._frame;
@@ -118,6 +152,7 @@ export class App {
118
152
  this._phase = "running";
119
153
  this.host.start();
120
154
  this.subscriptions = [
155
+ this.host.onData((chunk) => this.input.feed(chunk)),
121
156
  this.host.onResize(() => this.refresh()),
122
157
  // The program is ending under the app — by a signal, a crash, or an
123
158
  // exit the app's own code never saw. Ending hands the terminal back,
@@ -201,6 +236,35 @@ export class App {
201
236
  this.paintHandlers.add(handler);
202
237
  return () => this.paintHandlers.delete(handler);
203
238
  }
239
+ /**
240
+ * Hear every key the app does not quit on, by the `KeyPress` the terminal
241
+ * reported; `keyName` names it. A handler that throws fails the app.
242
+ */
243
+ onKey(handler) {
244
+ this.keyHandlers.add(handler);
245
+ return () => this.keyHandlers.delete(handler);
246
+ }
247
+ /**
248
+ * Hear every pointer event but the vertical wheel, which is `onWheel`'s,
249
+ * in terminal cells. Only the alternate surface reports the pointer. A
250
+ * handler that throws fails the app.
251
+ */
252
+ onMouse(handler) {
253
+ this.mouseHandlers.add(handler);
254
+ return () => this.mouseHandlers.delete(handler);
255
+ }
256
+ /**
257
+ * Offer every wheel turn to `handler`, which returns whether it moved
258
+ * something. A turn no handler moves anything with is passed outward to the
259
+ * host (`TerminalHost.passWheel`), so the page or terminal around the app
260
+ * scrolls instead. Handlers are offered a turn in the order they subscribed,
261
+ * and the first that moves something takes it. A handler that throws fails
262
+ * the app.
263
+ */
264
+ onWheel(handler) {
265
+ this.wheelHandlers.add(handler);
266
+ return () => this.wheelHandlers.delete(handler);
267
+ }
204
268
  /** Hand the terminal back for good; `run` resolves. */
205
269
  stop() {
206
270
  this.end({ kind: "stopped" });
@@ -227,33 +291,79 @@ export class App {
227
291
  }
228
292
  end(outcome) {
229
293
  const phase = this._phase;
230
- if (phase === "idle" || phase === "stopped")
294
+ // [LAW:no-silent-failure] An error after the app ended has no `run` left
295
+ // to reject, so it is thrown to whoever raised it instead of dropped.
296
+ if (phase === "idle" || phase === "stopped") {
297
+ if (outcome.kind === "failed")
298
+ throw outcome.error;
231
299
  return;
300
+ }
232
301
  this._phase = "stopped";
233
302
  this.tick();
234
303
  for (const unsubscribe of this.subscriptions)
235
304
  unsubscribe();
236
305
  this.subscriptions = [];
306
+ this.input.reset();
237
307
  // A suspended app handed the terminal back when it suspended.
238
308
  if (phase === "running")
239
309
  this.leave();
240
310
  this.host.stop();
241
311
  this.settle(outcome);
242
312
  }
243
- // [LAW:single-enforcer] Whether the clock is ticking follows from the
244
- // phase and what is animating, and is brought in line with them here
245
- // alone, after every change to either — so a timer runs exactly while a
246
- // running app has something animating, and none outlives the app.
313
+ // --- input ---
314
+ key(press) {
315
+ if (this.quitKeys.has(keyName(press)))
316
+ return this.stop();
317
+ this.hear(this.keyHandlers, press);
318
+ }
319
+ // [LAW:dataflow-not-control-flow] One rule for every app: a wheel turn
320
+ // nothing in the app moved with goes outward. The vertical wheel reaches the
321
+ // program through `onWheel` alone, so moving something with it and claiming
322
+ // the turn are one act, and an app that reads no wheel passes every turn.
323
+ // [LAW:single-enforcer] Only a running app holds the terminal, so only a
324
+ // running app is offered a turn or passes one on.
325
+ mouse(event) {
326
+ if (!isWheelTurn(event))
327
+ return this.hear(this.mouseHandlers, event);
328
+ if (this._phase !== "running")
329
+ return;
330
+ const lines = WHEEL_LINES[event.type];
331
+ const wheel = { x: event.x, y: event.y, lines };
332
+ try {
333
+ const moved = [...this.wheelHandlers].some((handler) => this._phase === "running" && handler(wheel));
334
+ if (!moved && this._phase === "running")
335
+ this.host.passWheel(lines);
336
+ }
337
+ catch (error) {
338
+ this.fail(error);
339
+ }
340
+ }
341
+ // [LAW:no-silent-failure] The host calls these handlers, not the program,
342
+ // so one that throws ends the app with its error, as a frame that throws
343
+ // does. A handler that stops the app is the last to hear the event.
344
+ hear(handlers, event) {
345
+ try {
346
+ for (const handler of [...handlers])
347
+ if (this._phase !== "stopped")
348
+ handler(event);
349
+ }
350
+ catch (error) {
351
+ this.fail(error);
352
+ }
353
+ }
354
+ // [LAW:single-enforcer] Whether the clock is ticking, and at what rate,
355
+ // follows from the phase, what is animating and the rate, and is brought
356
+ // in line with them here alone, after every change to any — so a timer
357
+ // runs exactly while a running app has something animating, at the rate
358
+ // the app has now, and none outlives the app.
247
359
  tick() {
248
360
  const wanted = this._phase === "running" && this.animations.size > 0;
249
- const ticking = this.stopTicking;
250
- if (ticking === null) {
251
- if (wanted)
252
- this.stopTicking = this.clock.every(this.rate, () => this.paint());
361
+ if (this.ticking !== null && (!wanted || this.ticking.rate.perSecond !== this._rate.perSecond)) {
362
+ this.ticking.stop();
363
+ this.ticking = null;
253
364
  }
254
- else if (!wanted) {
255
- ticking();
256
- this.stopTicking = null;
365
+ if (wanted && this.ticking === null) {
366
+ this.ticking = { rate: this._rate, stop: this.clock.every(this._rate, () => this.paint()) };
257
367
  }
258
368
  }
259
369
  // [LAW:no-silent-failure] A frame that throws ends the app with that
@@ -2,5 +2,7 @@ export { BrowserTerminalHost } from "./terminal-host.js";
2
2
  export type { TerminalHost, TerminalSize, DataHandler, ResizeHandler, ExitHandler, BrowserTerminalHostOptions, XtermTerminal, XtermDisposable, XtermResizeEvent, } from "./terminal-host.js";
3
3
  export { hostEnvironment } from "./host-environment.js";
4
4
  export { App } from "./app.js";
5
- export type { AppOptions, AppPhase } from "./app.js";
5
+ export type { AppMouseEvent, AppOptions, AppPhase, Wheel } from "./app.js";
6
6
  export type { Surface } from "../core/paint.js";
7
+ export { keyName } from "./input.js";
8
+ export type { KeyPress, ScreenMouseEvent } from "./input.js";
@@ -1,3 +1,4 @@
1
1
  export { BrowserTerminalHost } from "./terminal-host.js";
2
2
  export { hostEnvironment } from "./host-environment.js";
3
3
  export { App } from "./app.js";
4
+ export { keyName } from "./input.js";
@@ -0,0 +1,87 @@
1
+ /**
2
+ * Terminal input: the bytes a terminal sends for keys and the pointer,
3
+ * parsed into `KeyPress` and `ScreenMouseEvent` values, and the one name
4
+ * each key goes by.
5
+ *
6
+ * [LAW:single-enforcer] Escape-sequence parsing lives only here. `App` owns
7
+ * the one parser over its host's input; a `WidgetApp` routes the events `App`
8
+ * hears, and no program reads its terminal's bytes itself.
9
+ *
10
+ * [LAW:dataflow-not-control-flow] The same parse pipeline runs for every
11
+ * input chunk: append → consume one event at a time until the buffer can't
12
+ * yield more. The byte's value, not a side-mode, decides which event is
13
+ * emitted. There is no "are we mid-sequence" mode flag — the buffer head
14
+ * carries all the state we need, so a UTF-8 character or an escape sequence
15
+ * split across two chunks is completed by the second.
16
+ *
17
+ * Lone-ESC handling: a bare `\x1b` is ambiguous (escape key vs. start of a
18
+ * CSI sequence). When the buffer is drained but ends with a lone ESC, the
19
+ * parser defers emission via setTimeout(0). A next chunk opening with `[` or
20
+ * `O` extends the sequence; any other next chunk emits the ESC as Escape
21
+ * first. `flush()` drains it synchronously.
22
+ */
23
+ /** A key as the terminal reported it: plain data, with no say over who hears it. */
24
+ export interface KeyPress {
25
+ readonly key: string;
26
+ /** The printable character the key types, or "" for a key that types none. */
27
+ readonly character: string;
28
+ readonly shift: boolean;
29
+ readonly ctrl: boolean;
30
+ readonly meta: boolean;
31
+ }
32
+ export type ScreenMouseEvent = {
33
+ [T in MouseEventType]: MouseEventOf<T>;
34
+ }[MouseEventType];
35
+ type MouseEventType = "mouse_down" | "mouse_up" | "mouse_move" | "scroll_up" | "scroll_down" | "scroll_left" | "scroll_right";
36
+ interface MouseEventOf<Type extends string> {
37
+ readonly type: Type;
38
+ readonly x: number;
39
+ readonly y: number;
40
+ readonly button: number;
41
+ readonly shift: boolean;
42
+ readonly ctrl: boolean;
43
+ }
44
+ /**
45
+ * The one name a key goes by — what a keymap is keyed by and what
46
+ * `quitKeys` lists. A key that types a character is named by it (`q`, `G`,
47
+ * `>`); any other is named by its key (`up`, `enter`, `space`, `f5`). Held
48
+ * modifiers come first, always in the order `ctrl+`, `meta+`, `shift+`:
49
+ * `ctrl+c`, `shift+tab`, `meta+left`. Shift is not named on a character,
50
+ * which already carries it: Shift+G is `G`.
51
+ */
52
+ export declare function keyName(press: KeyPress): string;
53
+ /** Where a parser delivers what it parses. */
54
+ export interface InputSink {
55
+ key(press: KeyPress): void;
56
+ mouse(event: ScreenMouseEvent): void;
57
+ }
58
+ /**
59
+ * `name`, if `keyName` can produce it: modifiers in its order, then a named
60
+ * key or one glyph, with no `shift+` on a glyph and, after `ctrl+`, only a
61
+ * glyph a control byte stands for.
62
+ * [LAW:parse-dont-validate] A name no key can produce throws here, at the
63
+ * one place a program hands names in, rather than binding a key that never fires.
64
+ */
65
+ export declare function parseKeyName(name: string): string;
66
+ export declare class InputParser {
67
+ private readonly sink;
68
+ private buffer;
69
+ private escTimer;
70
+ constructor(sink: InputSink);
71
+ /** Feed a chunk of bytes (or a string of them) into the parser. */
72
+ feed(chunk: Uint8Array | string): void;
73
+ /** Force any pending lone ESC out of the buffer. */
74
+ flush(): void;
75
+ /** Drop what is buffered and any pending lone ESC: the parser as constructed. */
76
+ reset(): void;
77
+ /** The parser's one hold: a lone ESC that may yet open a sequence. */
78
+ private holdingEscape;
79
+ private cancelEscape;
80
+ private drain;
81
+ private consumeOne;
82
+ private consumePrintable;
83
+ private consumeEscape;
84
+ private consumeSS3;
85
+ private consumeCSI;
86
+ }
87
+ export {};