@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.
- package/README.md +144 -0
- package/dist/ansi.d.ts +42 -0
- package/dist/ansi.d.ts.map +1 -0
- package/dist/ansi.js +66 -0
- package/dist/ansi.js.map +1 -0
- package/dist/app.d.ts +128 -0
- package/dist/app.d.ts.map +1 -0
- package/dist/app.js +296 -0
- package/dist/app.js.map +1 -0
- package/dist/buffer.d.ts +50 -0
- package/dist/buffer.d.ts.map +1 -0
- package/dist/buffer.js +184 -0
- package/dist/buffer.js.map +1 -0
- package/dist/capabilities.d.ts +36 -0
- package/dist/capabilities.d.ts.map +1 -0
- package/dist/capabilities.js +93 -0
- package/dist/capabilities.js.map +1 -0
- package/dist/color.d.ts +47 -0
- package/dist/color.d.ts.map +1 -0
- package/dist/color.js +176 -0
- package/dist/color.js.map +1 -0
- package/dist/diff.d.ts +39 -0
- package/dist/diff.d.ts.map +1 -0
- package/dist/diff.js +203 -0
- package/dist/diff.js.map +1 -0
- package/dist/graphics/blocks.d.ts +26 -0
- package/dist/graphics/blocks.d.ts.map +1 -0
- package/dist/graphics/blocks.js +49 -0
- package/dist/graphics/blocks.js.map +1 -0
- package/dist/graphics/braille.d.ts +33 -0
- package/dist/graphics/braille.d.ts.map +1 -0
- package/dist/graphics/braille.js +166 -0
- package/dist/graphics/braille.js.map +1 -0
- package/dist/graphics/index.d.ts +4 -0
- package/dist/graphics/index.d.ts.map +1 -0
- package/dist/graphics/index.js +4 -0
- package/dist/graphics/index.js.map +1 -0
- package/dist/graphics/plot.d.ts +91 -0
- package/dist/graphics/plot.d.ts.map +1 -0
- package/dist/graphics/plot.js +291 -0
- package/dist/graphics/plot.js.map +1 -0
- package/dist/index.d.ts +34 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +42 -0
- package/dist/index.js.map +1 -0
- package/dist/input.d.ts +62 -0
- package/dist/input.d.ts.map +1 -0
- package/dist/input.js +245 -0
- package/dist/input.js.map +1 -0
- package/dist/layout.d.ts +51 -0
- package/dist/layout.d.ts.map +1 -0
- package/dist/layout.js +163 -0
- package/dist/layout.js.map +1 -0
- package/dist/surface.d.ts +81 -0
- package/dist/surface.d.ts.map +1 -0
- package/dist/surface.js +181 -0
- package/dist/surface.js.map +1 -0
- package/dist/terminal.d.ts +63 -0
- package/dist/terminal.d.ts.map +1 -0
- package/dist/terminal.js +189 -0
- package/dist/terminal.js.map +1 -0
- package/dist/testing.d.ts +71 -0
- package/dist/testing.d.ts.map +1 -0
- package/dist/testing.js +171 -0
- package/dist/testing.js.map +1 -0
- package/dist/theme.d.ts +58 -0
- package/dist/theme.d.ts.map +1 -0
- package/dist/theme.js +238 -0
- package/dist/theme.js.map +1 -0
- package/dist/ui.d.ts +194 -0
- package/dist/ui.d.ts.map +1 -0
- package/dist/ui.js +439 -0
- package/dist/ui.js.map +1 -0
- package/dist/unicode.d.ts +36 -0
- package/dist/unicode.d.ts.map +1 -0
- package/dist/unicode.js +221 -0
- package/dist/unicode.js.map +1 -0
- package/dist/widgets/controls.d.ts +95 -0
- package/dist/widgets/controls.d.ts.map +1 -0
- package/dist/widgets/controls.js +231 -0
- package/dist/widgets/controls.js.map +1 -0
- package/dist/widgets/index.d.ts +5 -0
- package/dist/widgets/index.d.ts.map +1 -0
- package/dist/widgets/index.js +5 -0
- package/dist/widgets/index.js.map +1 -0
- package/dist/widgets/meters.d.ts +100 -0
- package/dist/widgets/meters.d.ts.map +1 -0
- package/dist/widgets/meters.js +209 -0
- package/dist/widgets/meters.js.map +1 -0
- package/dist/widgets/table.d.ts +92 -0
- package/dist/widgets/table.d.ts.map +1 -0
- package/dist/widgets/table.js +228 -0
- package/dist/widgets/table.js.map +1 -0
- package/dist/widgets/text.d.ts +64 -0
- package/dist/widgets/text.d.ts.map +1 -0
- package/dist/widgets/text.js +123 -0
- package/dist/widgets/text.js.map +1 -0
- 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
|
+

|
|
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
|
package/dist/ansi.js.map
ADDED
|
@@ -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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"}
|