@profullstack/hqtui 0.1.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 (98) hide show
  1. package/README.md +144 -0
  2. package/dist/ansi.d.ts +42 -0
  3. package/dist/ansi.d.ts.map +1 -0
  4. package/dist/ansi.js +66 -0
  5. package/dist/ansi.js.map +1 -0
  6. package/dist/app.d.ts +128 -0
  7. package/dist/app.d.ts.map +1 -0
  8. package/dist/app.js +296 -0
  9. package/dist/app.js.map +1 -0
  10. package/dist/buffer.d.ts +50 -0
  11. package/dist/buffer.d.ts.map +1 -0
  12. package/dist/buffer.js +184 -0
  13. package/dist/buffer.js.map +1 -0
  14. package/dist/capabilities.d.ts +36 -0
  15. package/dist/capabilities.d.ts.map +1 -0
  16. package/dist/capabilities.js +93 -0
  17. package/dist/capabilities.js.map +1 -0
  18. package/dist/color.d.ts +47 -0
  19. package/dist/color.d.ts.map +1 -0
  20. package/dist/color.js +176 -0
  21. package/dist/color.js.map +1 -0
  22. package/dist/diff.d.ts +39 -0
  23. package/dist/diff.d.ts.map +1 -0
  24. package/dist/diff.js +203 -0
  25. package/dist/diff.js.map +1 -0
  26. package/dist/graphics/blocks.d.ts +26 -0
  27. package/dist/graphics/blocks.d.ts.map +1 -0
  28. package/dist/graphics/blocks.js +49 -0
  29. package/dist/graphics/blocks.js.map +1 -0
  30. package/dist/graphics/braille.d.ts +33 -0
  31. package/dist/graphics/braille.d.ts.map +1 -0
  32. package/dist/graphics/braille.js +166 -0
  33. package/dist/graphics/braille.js.map +1 -0
  34. package/dist/graphics/index.d.ts +4 -0
  35. package/dist/graphics/index.d.ts.map +1 -0
  36. package/dist/graphics/index.js +4 -0
  37. package/dist/graphics/index.js.map +1 -0
  38. package/dist/graphics/plot.d.ts +91 -0
  39. package/dist/graphics/plot.d.ts.map +1 -0
  40. package/dist/graphics/plot.js +291 -0
  41. package/dist/graphics/plot.js.map +1 -0
  42. package/dist/index.d.ts +34 -0
  43. package/dist/index.d.ts.map +1 -0
  44. package/dist/index.js +42 -0
  45. package/dist/index.js.map +1 -0
  46. package/dist/input.d.ts +62 -0
  47. package/dist/input.d.ts.map +1 -0
  48. package/dist/input.js +245 -0
  49. package/dist/input.js.map +1 -0
  50. package/dist/layout.d.ts +51 -0
  51. package/dist/layout.d.ts.map +1 -0
  52. package/dist/layout.js +163 -0
  53. package/dist/layout.js.map +1 -0
  54. package/dist/surface.d.ts +81 -0
  55. package/dist/surface.d.ts.map +1 -0
  56. package/dist/surface.js +181 -0
  57. package/dist/surface.js.map +1 -0
  58. package/dist/terminal.d.ts +63 -0
  59. package/dist/terminal.d.ts.map +1 -0
  60. package/dist/terminal.js +189 -0
  61. package/dist/terminal.js.map +1 -0
  62. package/dist/testing.d.ts +71 -0
  63. package/dist/testing.d.ts.map +1 -0
  64. package/dist/testing.js +171 -0
  65. package/dist/testing.js.map +1 -0
  66. package/dist/theme.d.ts +58 -0
  67. package/dist/theme.d.ts.map +1 -0
  68. package/dist/theme.js +238 -0
  69. package/dist/theme.js.map +1 -0
  70. package/dist/ui.d.ts +194 -0
  71. package/dist/ui.d.ts.map +1 -0
  72. package/dist/ui.js +439 -0
  73. package/dist/ui.js.map +1 -0
  74. package/dist/unicode.d.ts +36 -0
  75. package/dist/unicode.d.ts.map +1 -0
  76. package/dist/unicode.js +221 -0
  77. package/dist/unicode.js.map +1 -0
  78. package/dist/widgets/controls.d.ts +95 -0
  79. package/dist/widgets/controls.d.ts.map +1 -0
  80. package/dist/widgets/controls.js +231 -0
  81. package/dist/widgets/controls.js.map +1 -0
  82. package/dist/widgets/index.d.ts +5 -0
  83. package/dist/widgets/index.d.ts.map +1 -0
  84. package/dist/widgets/index.js +5 -0
  85. package/dist/widgets/index.js.map +1 -0
  86. package/dist/widgets/meters.d.ts +100 -0
  87. package/dist/widgets/meters.d.ts.map +1 -0
  88. package/dist/widgets/meters.js +209 -0
  89. package/dist/widgets/meters.js.map +1 -0
  90. package/dist/widgets/table.d.ts +92 -0
  91. package/dist/widgets/table.d.ts.map +1 -0
  92. package/dist/widgets/table.js +228 -0
  93. package/dist/widgets/table.js.map +1 -0
  94. package/dist/widgets/text.d.ts +64 -0
  95. package/dist/widgets/text.d.ts.map +1 -0
  96. package/dist/widgets/text.js +123 -0
  97. package/dist/widgets/text.js.map +1 -0
  98. package/package.json +56 -0
package/README.md ADDED
@@ -0,0 +1,144 @@
1
+ <h1 align="center">HQTUI</h1>
2
+ <p align="center"><strong>High Quality Terminal UI for TypeScript</strong><br>
3
+ btop-grade dashboards with a one-import API, dark by default, zero runtime dependencies.</p>
4
+ <p align="center">
5
+ <a href="https://hqtui.com">hqtui.com</a> ·
6
+ <a href="https://www.npmjs.com/package/@profullstack/hqtui">npm</a> ·
7
+ <a href="./docs">docs</a>
8
+ </p>
9
+
10
+ ![HQTUI dashboard](https://raw.githubusercontent.com/profullstack/hqtui/main/assets/hqtui-dashboard.png)
11
+
12
+ ---
13
+
14
+ ## Why
15
+
16
+ Terminal apps do not have to look like 1990s ncurses software. HQTUI owns the terminal
17
+ directly — ANSI/VT sequences, a typed-array framebuffer, differential rendering, Braille
18
+ graphics and truecolor — so a dashboard written in TypeScript can look and feel like a
19
+ modern desktop app while starting instantly and running fine over SSH.
20
+
21
+ No ncurses. No browser DOM. No React. No native addon. No network access. Ever.
22
+
23
+ ## Install
24
+
25
+ ```bash
26
+ bun add @profullstack/hqtui # Bun is the default runtime
27
+ npm add @profullstack/hqtui # Node 22.6+ works too
28
+ ```
29
+
30
+ ## Hello, terminal
31
+
32
+ ```ts
33
+ import { createApp } from "@profullstack/hqtui";
34
+
35
+ const app = await createApp();
36
+
37
+ app.render(({ ui }) => {
38
+ ui.panel({ title: "Hello" }, (panel) => {
39
+ panel.text("Hello, terminal.");
40
+ });
41
+ });
42
+
43
+ await app.start();
44
+ ```
45
+
46
+ That is the whole API surface you need to start. `createApp()` already gives you a dark
47
+ theme, truecolor with automatic 256/16-colour fallback, mouse tracking, the alternate
48
+ screen, resize handling, 30fps adaptive rendering (15 over SSH), and a terminal that is
49
+ restored no matter how the process dies — Ctrl+C, SIGTERM, or an uncaught exception.
50
+
51
+ ## A real dashboard
52
+
53
+ ```ts
54
+ import { createApp } from "@profullstack/hqtui";
55
+
56
+ const app = await createApp({ fps: 30 });
57
+
58
+ app.render(({ ui, theme }) => {
59
+ ui.grid({ columns: ["2fr", "1fr"], rows: [14, "1fr"], gap: 1 }, (grid) => {
60
+ grid.panel({ title: "CPU" }, (p) => {
61
+ p.graph({ values: cpuHistory, min: 0, max: 100, fill: true });
62
+ p.meters(cores.map((value, i) => ({ label: `P${i}`, value })), { columns: 2 });
63
+ });
64
+
65
+ grid.panel({ title: "Memory" }, (p) => {
66
+ p.meter({ label: "Used", value: 0.42, text: "6.7 GiB" });
67
+ p.keyValues([{ label: "Cached", value: "4.0 GiB" }]);
68
+ });
69
+
70
+ grid.panel({ title: "Processes", colSpan: 2 }, (p) => {
71
+ p.table({
72
+ rows: processes,
73
+ columns: [
74
+ { key: "pid", title: "PID", width: 7, align: "right" },
75
+ { key: "name", title: "Name" },
76
+ { key: "cpu", title: "CPU%", width: 6, align: "right" },
77
+ ],
78
+ });
79
+ });
80
+ });
81
+ });
82
+
83
+ await app.start();
84
+ ```
85
+
86
+ ## See it running
87
+
88
+ ```bash
89
+ bunx @profullstack/hqtui-demo # your real machine
90
+ bunx @profullstack/hqtui-demo --sim # deterministic simulation
91
+ ```
92
+
93
+ Six screens: dashboard, components, graphics, themes, input visualizer, stress test.
94
+ Real metrics are collected on Linux, macOS and Windows with no native dependencies.
95
+
96
+ ## What is in the box
97
+
98
+ | | |
99
+ |---|---|
100
+ | **Layout** | rows, columns, grid with spans, `"40%"`, `"2fr"`, `auto`, min/max, padding, gaps, clipping, responsive breakpoints |
101
+ | **Widgets** | panel, table, tree, list, log viewer, key/values, meter, gauge, donut, progress, sparkline, line/area/multi-series graph, histogram, heat bar, tabs, status bar, button, checkbox, toggle, radio, select, text input, modal, command palette, tooltip, badge, divider |
102
+ | **Graphics** | Braille canvas (2×4 pixels per cell), block/half-block/quadrant/ASCII modes, gradients, software alpha blending |
103
+ | **Color** | 24-bit truecolor, automatic 256 and 16-colour quantization, `NO_COLOR`, monochrome and high-contrast modes |
104
+ | **Themes** | dark (default), dracula, nord, tokyo night, gruvbox, matrix, monochrome, high contrast, light — plus `defineTheme()` |
105
+ | **Input** | normalized keys with modifiers, SGR mouse (click, drag, scroll, move), bracketed paste, focus events, Tab focus traversal |
106
+ | **Testing** | headless renderer: `renderToText`, `renderToScreen`, `renderToAnsi`, `renderToHtml` — no TTY required |
107
+
108
+ ## Testing your TUI
109
+
110
+ Terminal apps are usually untestable. Here they are not:
111
+
112
+ ```ts
113
+ import { renderToScreen } from "@profullstack/hqtui";
114
+
115
+ const screen = renderToScreen(({ ui }) => ui.panel({ title: "CPU" }, (p) => p.text("72%")), {
116
+ width: 40,
117
+ height: 6,
118
+ });
119
+
120
+ expect(screen.contains("72%")).toBe(true);
121
+ expect(screen.cell(2, 0).fg).toBe(theme.title);
122
+ ```
123
+
124
+ ## Performance
125
+
126
+ The screen is one grid of cells in four typed arrays — no object is allocated per cell.
127
+ Each frame is diffed against the previous one and only the changed runs are written,
128
+ with a model of the terminal's pen so no redundant escape sequence is emitted.
129
+
130
+ Changing `CPU 72%` to `CPU 73%` writes a single character, not a screen.
131
+
132
+ ```bash
133
+ bun run bench
134
+ ```
135
+
136
+ ## Runtimes
137
+
138
+ Bun is the default. Node 22.6+ runs everything unchanged (it strips TypeScript natively).
139
+ Deno support is best-effort. Tested on Linux, macOS and Windows Terminal; degrades
140
+ gracefully on limited terminals (no mouse, quantized color, ASCII instead of Braille).
141
+
142
+ ## License
143
+
144
+ MIT.
package/dist/ansi.d.ts ADDED
@@ -0,0 +1,42 @@
1
+ /** Raw VT/ANSI control sequences. Nothing above this layer writes an escape by hand. */
2
+ export declare const ESC = "\u001B";
3
+ export declare const CSI = "\u001B[";
4
+ export declare const ansi: {
5
+ readonly reset: "\u001B[0m";
6
+ readonly alternateScreenOn: "\u001B[?1049h";
7
+ readonly alternateScreenOff: "\u001B[?1049l";
8
+ readonly cursorHide: "\u001B[?25l";
9
+ readonly cursorShow: "\u001B[?25h";
10
+ readonly cursorHome: "\u001B[H";
11
+ readonly cursorSave: "\u001B7";
12
+ readonly cursorRestore: "\u001B8";
13
+ readonly clearScreen: "\u001B[2J";
14
+ readonly clearScrollback: "\u001B[3J";
15
+ readonly clearLine: "\u001B[2K";
16
+ readonly clearToEnd: "\u001B[0J";
17
+ readonly mouseOn: "\u001B[?1000h\u001B[?1002h\u001B[?1003h\u001B[?1006h";
18
+ readonly mouseOff: "\u001B[?1006l\u001B[?1003l\u001B[?1002l\u001B[?1000l";
19
+ readonly bracketedPasteOn: "\u001B[?2004h";
20
+ readonly bracketedPasteOff: "\u001B[?2004l";
21
+ readonly focusOn: "\u001B[?1004h";
22
+ readonly focusOff: "\u001B[?1004l";
23
+ /** Atomic frame: the terminal shows nothing until endSync. Kills tearing. */
24
+ readonly beginSync: "\u001B[?2026h";
25
+ readonly endSync: "\u001B[?2026l";
26
+ readonly softReset: "\u001B[!p";
27
+ };
28
+ export declare function moveTo(x: number, y: number): string;
29
+ export declare function moveRight(n: number): string;
30
+ export declare function moveToColumn(x: number): string;
31
+ export declare function setTitle(title: string): string;
32
+ export declare function fgTrue(r: number, g: number, b: number): string;
33
+ export declare function bgTrue(r: number, g: number, b: number): string;
34
+ export declare function fg256(i: number): string;
35
+ export declare function bg256(i: number): string;
36
+ export declare function fg16(i: number): string;
37
+ export declare function bg16(i: number): string;
38
+ export declare const fgDefault = "\u001B[39m";
39
+ export declare const bgDefault = "\u001B[49m";
40
+ /** Strip escape sequences — used by the headless renderer and by tests. */
41
+ export declare function stripAnsi(text: string): string;
42
+ //# sourceMappingURL=ansi.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ansi.d.ts","sourceRoot":"","sources":["../src/ansi.ts"],"names":[],"mappings":"AAAA,wFAAwF;AAExF,eAAO,MAAM,GAAG,WAAS,CAAC;AAC1B,eAAO,MAAM,GAAG,YAAU,CAAC;AAE3B,eAAO,MAAM,IAAI;;;;;;;;;;;;;;;;;;;IA2Bf,6EAA6E;;;;CAKrE,CAAC;AAEX,wBAAgB,MAAM,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAEnD;AAED,wBAAgB,SAAS,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAE3C;AAED,wBAAgB,YAAY,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAE9C;AAED,wBAAgB,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAE9C;AAED,wBAAgB,MAAM,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAE9D;AAED,wBAAgB,MAAM,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAE9D;AAED,wBAAgB,KAAK,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAEvC;AAED,wBAAgB,KAAK,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAEvC;AAED,wBAAgB,IAAI,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAEtC;AAED,wBAAgB,IAAI,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAEtC;AAED,eAAO,MAAM,SAAS,eAAc,CAAC;AACrC,eAAO,MAAM,SAAS,eAAc,CAAC;AAErC,2EAA2E;AAC3E,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAG9C"}
package/dist/ansi.js ADDED
@@ -0,0 +1,66 @@
1
+ /** Raw VT/ANSI control sequences. Nothing above this layer writes an escape by hand. */
2
+ export const ESC = "\x1b";
3
+ export const CSI = "\x1b[";
4
+ export const ansi = {
5
+ reset: `${CSI}0m`,
6
+ alternateScreenOn: `${CSI}?1049h`,
7
+ alternateScreenOff: `${CSI}?1049l`,
8
+ cursorHide: `${CSI}?25l`,
9
+ cursorShow: `${CSI}?25h`,
10
+ cursorHome: `${CSI}H`,
11
+ cursorSave: `${ESC}7`,
12
+ cursorRestore: `${ESC}8`,
13
+ clearScreen: `${CSI}2J`,
14
+ clearScrollback: `${CSI}3J`,
15
+ clearLine: `${CSI}2K`,
16
+ clearToEnd: `${CSI}0J`,
17
+ // 1000 = clicks, 1002 = drag, 1003 = any motion, 1006 = SGR extended coords.
18
+ mouseOn: `${CSI}?1000h${CSI}?1002h${CSI}?1003h${CSI}?1006h`,
19
+ mouseOff: `${CSI}?1006l${CSI}?1003l${CSI}?1002l${CSI}?1000l`,
20
+ bracketedPasteOn: `${CSI}?2004h`,
21
+ bracketedPasteOff: `${CSI}?2004l`,
22
+ focusOn: `${CSI}?1004h`,
23
+ focusOff: `${CSI}?1004l`,
24
+ /** Atomic frame: the terminal shows nothing until endSync. Kills tearing. */
25
+ beginSync: `${CSI}?2026h`,
26
+ endSync: `${CSI}?2026l`,
27
+ softReset: `${CSI}!p`,
28
+ };
29
+ export function moveTo(x, y) {
30
+ return `${CSI}${y + 1};${x + 1}H`;
31
+ }
32
+ export function moveRight(n) {
33
+ return n === 1 ? `${CSI}C` : `${CSI}${n}C`;
34
+ }
35
+ export function moveToColumn(x) {
36
+ return `${CSI}${x + 1}G`;
37
+ }
38
+ export function setTitle(title) {
39
+ return `${ESC}]0;${title.replace(/[\x00-\x1f]/g, "")}\x07`;
40
+ }
41
+ export function fgTrue(r, g, b) {
42
+ return `${CSI}38;2;${r};${g};${b}m`;
43
+ }
44
+ export function bgTrue(r, g, b) {
45
+ return `${CSI}48;2;${r};${g};${b}m`;
46
+ }
47
+ export function fg256(i) {
48
+ return `${CSI}38;5;${i}m`;
49
+ }
50
+ export function bg256(i) {
51
+ return `${CSI}48;5;${i}m`;
52
+ }
53
+ export function fg16(i) {
54
+ return i < 8 ? `${CSI}${30 + i}m` : `${CSI}${90 + i - 8}m`;
55
+ }
56
+ export function bg16(i) {
57
+ return i < 8 ? `${CSI}${40 + i}m` : `${CSI}${100 + i - 8}m`;
58
+ }
59
+ export const fgDefault = `${CSI}39m`;
60
+ export const bgDefault = `${CSI}49m`;
61
+ /** Strip escape sequences — used by the headless renderer and by tests. */
62
+ export function stripAnsi(text) {
63
+ // eslint-disable-next-line no-control-regex
64
+ return text.replace(/\x1b\[[0-9;?]*[A-Za-z]|\x1b\][^\x07\x1b]*(\x07|\x1b\\)|\x1b[@-Z\\-_]/g, "");
65
+ }
66
+ //# sourceMappingURL=ansi.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ansi.js","sourceRoot":"","sources":["../src/ansi.ts"],"names":[],"mappings":"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"}
package/dist/app.d.ts ADDED
@@ -0,0 +1,128 @@
1
+ import { Terminal, type TerminalOptions, emergencyRestore } from "./terminal.ts";
2
+ import type { Capabilities } from "./capabilities.ts";
3
+ import { type Theme, type ThemeName, themes } from "./theme.ts";
4
+ import { Container } from "./ui.ts";
5
+ import type { KeyEvent, MouseEvent, PasteEvent, FocusEvent } from "./input.ts";
6
+ export interface AppOptions extends TerminalOptions {
7
+ /** Theme object or built-in name. Defaults to the dark theme. */
8
+ theme?: Theme | ThemeName | string;
9
+ /** Cap on frames per second. Default 30, or 15 over SSH. */
10
+ fps?: number;
11
+ /** Frame cap when an SSH session is detected. Default 15. */
12
+ remoteFps?: number;
13
+ /** Redraw every tick instead of only when invalidated. Default false. */
14
+ alwaysRender?: boolean;
15
+ /** Keys that quit. Default ctrl+c and q. Pass [] to handle quitting yourself. */
16
+ quitKeys?: string[];
17
+ /** Tab/Shift+Tab move focus. Default true. */
18
+ focusNavigation?: boolean;
19
+ /** Paint the theme background across the whole screen. Default true. */
20
+ paintBackground?: boolean;
21
+ /** Drain color, for accessibility or NO_COLOR. */
22
+ monochrome?: boolean;
23
+ /** Skip animation-driven redraws. */
24
+ reducedMotion?: boolean;
25
+ }
26
+ export interface RenderArgs {
27
+ ui: Container;
28
+ theme: Theme;
29
+ capabilities: Capabilities;
30
+ width: number;
31
+ height: number;
32
+ frame: number;
33
+ /** Milliseconds since start. */
34
+ elapsed: number;
35
+ focus: number;
36
+ app: App;
37
+ }
38
+ export type RenderFn = (args: RenderArgs) => void;
39
+ export interface FrameStats {
40
+ frame: number;
41
+ /** Time spent building + diffing + writing, in milliseconds. */
42
+ renderMs: number;
43
+ changedCells: number;
44
+ dirtyRows: number;
45
+ bytes: number;
46
+ fps: number;
47
+ }
48
+ type EventMap = {
49
+ key: KeyEvent;
50
+ mouse: MouseEvent;
51
+ paste: PasteEvent;
52
+ focus: FocusEvent;
53
+ resize: {
54
+ width: number;
55
+ height: number;
56
+ };
57
+ frame: FrameStats;
58
+ exit: void;
59
+ };
60
+ /**
61
+ * The application: owns the terminal, both framebuffers, the scheduler and the
62
+ * event loop. Everything else in the library is reachable from here.
63
+ */
64
+ export declare class App {
65
+ readonly terminal: Terminal;
66
+ readonly capabilities: Capabilities;
67
+ theme: Theme;
68
+ private current;
69
+ private previous;
70
+ private encoder;
71
+ private renderFn;
72
+ private options;
73
+ private listeners;
74
+ private running;
75
+ private dirty;
76
+ private forceRepaint;
77
+ private timer;
78
+ private startedAt;
79
+ private frameCount;
80
+ private lastFrameAt;
81
+ private exitResolve;
82
+ private focusIndex;
83
+ private focusCount;
84
+ private focusActions;
85
+ private hits;
86
+ private overlays;
87
+ private lastStats;
88
+ constructor(options?: AppOptions);
89
+ get width(): number;
90
+ get height(): number;
91
+ /** Stats for the most recent frame. */
92
+ get stats(): FrameStats;
93
+ /** Register the view. Called on every frame; keep it pure and cheap. */
94
+ render(fn: RenderFn): this;
95
+ /** Ask for a redraw. The scheduler coalesces repeated calls into one frame. */
96
+ invalidate(): void;
97
+ /** Force a full repaint, e.g. after another process wrote to the terminal. */
98
+ redraw(): void;
99
+ setTheme(theme: Theme | ThemeName | string): this;
100
+ on<K extends keyof EventMap>(event: K, listener: (value: EventMap[K]) => void): () => void;
101
+ private emit;
102
+ /** Move keyboard focus. Wraps around. */
103
+ focusNext(delta?: number): void;
104
+ /** Activate the focused control, as Enter does. */
105
+ activateFocused(): void;
106
+ /** Start the loop. Resolves when the app exits. */
107
+ start(): Promise<void>;
108
+ private targetFps;
109
+ /** Stop the loop and restore the terminal. */
110
+ stop(): void;
111
+ /** Alias for `stop()`, matching what users type in their key handlers. */
112
+ quit(): void;
113
+ private handleInput;
114
+ private dispatchMouse;
115
+ /** Build one frame and push the difference to the terminal. */
116
+ frame(): FrameStats;
117
+ }
118
+ /**
119
+ * Create an app. Every option has a sensible default: dark theme, mouse on,
120
+ * 30fps, alternate screen, terminal restored no matter how the process dies.
121
+ *
122
+ * const app = await createApp();
123
+ * app.render(({ ui }) => ui.panel({ title: "Hello" }, p => p.text("Hi")));
124
+ * await app.start();
125
+ */
126
+ export declare function createApp(options?: AppOptions): Promise<App>;
127
+ export { emergencyRestore, themes };
128
+ //# sourceMappingURL=app.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"app.d.ts","sourceRoot":"","sources":["../src/app.ts"],"names":[],"mappings":"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"}