@flyingrobots/bijou-tui 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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Flying Robots
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,78 @@
1
+ # @flyingrobots/bijou-tui
2
+
3
+ TEA runtime for terminal UIs — model/update/view with keyboard input, alt screen, and layout helpers.
4
+
5
+ Inspired by [Bubble Tea](https://github.com/charmbracelet/bubbletea) (Go), bijou-tui brings The Elm Architecture to TypeScript terminals.
6
+
7
+ ## Install
8
+
9
+ ```bash
10
+ npm install @flyingrobots/bijou @flyingrobots/bijou-tui
11
+ ```
12
+
13
+ ## Quick Start
14
+
15
+ ```typescript
16
+ import { run, quit, tick, type App, type KeyMsg } from '@flyingrobots/bijou-tui';
17
+
18
+ type Model = { count: number };
19
+
20
+ const app: App<Model> = {
21
+ init: () => [{ count: 0 }, tick(1000)],
22
+
23
+ update: (msg, model) => {
24
+ if (msg.type === 'key') {
25
+ if (msg.key === 'q') return [model, quit()];
26
+ if (msg.key === '+') return [{ count: model.count + 1 }, null];
27
+ if (msg.key === '-') return [{ count: model.count - 1 }, null];
28
+ }
29
+ if (msg.type === 'tick') {
30
+ return [model, tick(1000)];
31
+ }
32
+ return [model, null];
33
+ },
34
+
35
+ view: (model) => `Count: ${model.count}\n\nPress +/- to change, q to quit`,
36
+ };
37
+
38
+ run(app);
39
+ ```
40
+
41
+ ## API
42
+
43
+ ### Core Types
44
+
45
+ - **`App<M>`** — defines `init`, `update(msg, model)`, and `view(model)` functions
46
+ - **`KeyMsg`** — keyboard input message with `key`, `ctrl`, `shift`, `alt` fields
47
+ - **`Cmd`** — side-effect commands returned from `update`
48
+ - **`QUIT`** — sentinel value to signal app termination
49
+
50
+ ### Commands
51
+
52
+ - **`quit()`** — exit the app
53
+ - **`tick(ms)`** — schedule a tick after `ms` milliseconds
54
+ - **`batch(...cmds)`** — combine multiple commands
55
+
56
+ ### Screen Control
57
+
58
+ - **`enterScreen()` / `exitScreen()`** — alt screen buffer management
59
+ - **`clearAndHome()`** — clear screen and move cursor to top-left
60
+ - **`renderFrame(content)`** — efficient frame rendering
61
+
62
+ ### Layout
63
+
64
+ - **`vstack(...lines)`** — vertical stack (join with newlines)
65
+ - **`hstack(...cols)`** — horizontal stack (side-by-side columns)
66
+
67
+ ### Key Parsing
68
+
69
+ - **`parseKey(data)`** — parse raw stdin bytes into a `KeyMsg`
70
+
71
+ ## Related Packages
72
+
73
+ - [`@flyingrobots/bijou`](https://www.npmjs.com/package/@flyingrobots/bijou) — Zero-dependency core with all components and theme engine
74
+ - [`@flyingrobots/bijou-node`](https://www.npmjs.com/package/@flyingrobots/bijou-node) — Node.js runtime adapter (chalk, readline, process)
75
+
76
+ ## License
77
+
78
+ MIT
@@ -0,0 +1,8 @@
1
+ import { type Cmd } from './types.js';
2
+ /** Command that signals the runtime to quit. */
3
+ export declare function quit<M>(): Cmd<M>;
4
+ /** Command that delivers a message after a delay (one-shot). */
5
+ export declare function tick<M>(ms: number, msg: M): Cmd<M>;
6
+ /** Convenience: group multiple commands into an array. */
7
+ export declare function batch<M>(...cmds: Cmd<M>[]): Cmd<M>[];
8
+ //# sourceMappingURL=commands.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"commands.d.ts","sourceRoot":"","sources":["../src/commands.ts"],"names":[],"mappings":"AAAA,OAAO,EAAQ,KAAK,GAAG,EAAmB,MAAM,YAAY,CAAC;AAE7D,gDAAgD;AAChD,wBAAgB,IAAI,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,CAEhC;AAED,gEAAgE;AAChE,wBAAgB,IAAI,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,CAIlD;AAED,0DAA0D;AAC1D,wBAAgB,KAAK,CAAC,CAAC,EAAE,GAAG,IAAI,EAAE,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG,GAAG,CAAC,CAAC,CAAC,EAAE,CAEpD"}
@@ -0,0 +1,16 @@
1
+ import { QUIT } from './types.js';
2
+ /** Command that signals the runtime to quit. */
3
+ export function quit() {
4
+ return () => Promise.resolve(QUIT);
5
+ }
6
+ /** Command that delivers a message after a delay (one-shot). */
7
+ export function tick(ms, msg) {
8
+ return () => new Promise((resolve) => {
9
+ setTimeout(() => resolve(msg), ms);
10
+ });
11
+ }
12
+ /** Convenience: group multiple commands into an array. */
13
+ export function batch(...cmds) {
14
+ return cmds;
15
+ }
16
+ //# sourceMappingURL=commands.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"commands.js","sourceRoot":"","sources":["../src/commands.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAA6B,MAAM,YAAY,CAAC;AAE7D,gDAAgD;AAChD,MAAM,UAAU,IAAI;IAClB,OAAO,GAAG,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC,IAAkB,CAAC,CAAC;AACnD,CAAC;AAED,gEAAgE;AAChE,MAAM,UAAU,IAAI,CAAI,EAAU,EAAE,GAAM;IACxC,OAAO,GAAG,EAAE,CAAC,IAAI,OAAO,CAAI,CAAC,OAAO,EAAE,EAAE;QACtC,UAAU,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC,CAAC;IACrC,CAAC,CAAC,CAAC;AACL,CAAC;AAED,0DAA0D;AAC1D,MAAM,UAAU,KAAK,CAAI,GAAG,IAAc;IACxC,OAAO,IAAI,CAAC;AACd,CAAC"}
@@ -0,0 +1,8 @@
1
+ export type { App, Cmd, KeyMsg, QuitSignal, RunOptions } from './types.js';
2
+ export { QUIT } from './types.js';
3
+ export { parseKey } from './keys.js';
4
+ export { enterScreen, exitScreen, clearAndHome, renderFrame, ENTER_ALT_SCREEN, EXIT_ALT_SCREEN, HIDE_CURSOR, SHOW_CURSOR, CLEAR_SCREEN, CLEAR_TO_END, CLEAR_LINE, HOME, } from './screen.js';
5
+ export { quit, tick, batch } from './commands.js';
6
+ export { run } from './runtime.js';
7
+ export { vstack, hstack } from './layout.js';
8
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,YAAY,EAAE,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAC3E,OAAO,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAGlC,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAGrC,OAAO,EACL,WAAW,EACX,UAAU,EACV,YAAY,EACZ,WAAW,EACX,gBAAgB,EAChB,eAAe,EACf,WAAW,EACX,WAAW,EACX,YAAY,EACZ,YAAY,EACZ,UAAU,EACV,IAAI,GACL,MAAM,aAAa,CAAC;AAGrB,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AAGlD,OAAO,EAAE,GAAG,EAAE,MAAM,cAAc,CAAC;AAGnC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,12 @@
1
+ export { QUIT } from './types.js';
2
+ // Key parsing
3
+ export { parseKey } from './keys.js';
4
+ // Screen control
5
+ export { enterScreen, exitScreen, clearAndHome, renderFrame, ENTER_ALT_SCREEN, EXIT_ALT_SCREEN, HIDE_CURSOR, SHOW_CURSOR, CLEAR_SCREEN, CLEAR_TO_END, CLEAR_LINE, HOME, } from './screen.js';
6
+ // Commands
7
+ export { quit, tick, batch } from './commands.js';
8
+ // Runtime
9
+ export { run } from './runtime.js';
10
+ // Layout
11
+ export { vstack, hstack } from './layout.js';
12
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAElC,cAAc;AACd,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAErC,iBAAiB;AACjB,OAAO,EACL,WAAW,EACX,UAAU,EACV,YAAY,EACZ,WAAW,EACX,gBAAgB,EAChB,eAAe,EACf,WAAW,EACX,WAAW,EACX,YAAY,EACZ,YAAY,EACZ,UAAU,EACV,IAAI,GACL,MAAM,aAAa,CAAC;AAErB,WAAW;AACX,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AAElD,UAAU;AACV,OAAO,EAAE,GAAG,EAAE,MAAM,cAAc,CAAC;AAEnC,SAAS;AACT,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC"}
package/dist/keys.d.ts ADDED
@@ -0,0 +1,7 @@
1
+ import type { KeyMsg } from './types.js';
2
+ /**
3
+ * Parse a raw ANSI byte string from rawInput() into a structured KeyMsg.
4
+ * Unrecognized sequences produce { key: 'unknown' }.
5
+ */
6
+ export declare function parseKey(raw: string): KeyMsg;
7
+ //# sourceMappingURL=keys.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"keys.d.ts","sourceRoot":"","sources":["../src/keys.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AAMzC;;;GAGG;AACH,wBAAgB,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CA2C5C"}
package/dist/keys.js ADDED
@@ -0,0 +1,62 @@
1
+ function keyMsg(key, ctrl = false, alt = false, shift = false) {
2
+ return { type: 'key', key, ctrl, alt, shift };
3
+ }
4
+ /**
5
+ * Parse a raw ANSI byte string from rawInput() into a structured KeyMsg.
6
+ * Unrecognized sequences produce { key: 'unknown' }.
7
+ */
8
+ export function parseKey(raw) {
9
+ // Arrow keys and other CSI sequences
10
+ if (raw === '\x1b[A')
11
+ return keyMsg('up');
12
+ if (raw === '\x1b[B')
13
+ return keyMsg('down');
14
+ if (raw === '\x1b[C')
15
+ return keyMsg('right');
16
+ if (raw === '\x1b[D')
17
+ return keyMsg('left');
18
+ if (raw === '\x1b[H')
19
+ return keyMsg('home');
20
+ if (raw === '\x1b[F')
21
+ return keyMsg('end');
22
+ if (raw === '\x1b[3~')
23
+ return keyMsg('delete');
24
+ if (raw === '\x1b[5~')
25
+ return keyMsg('pageup');
26
+ if (raw === '\x1b[6~')
27
+ return keyMsg('pagedown');
28
+ // Enter, tab, backspace, space, escape
29
+ if (raw === '\r' || raw === '\n')
30
+ return keyMsg('enter');
31
+ if (raw === '\t')
32
+ return keyMsg('tab');
33
+ if (raw === '\x1b[Z')
34
+ return keyMsg('tab', false, false, true); // shift-tab
35
+ if (raw === '\x7f')
36
+ return keyMsg('backspace');
37
+ if (raw === ' ')
38
+ return keyMsg('space');
39
+ // Escape (alone)
40
+ if (raw === '\x1b')
41
+ return keyMsg('escape');
42
+ // Ctrl+C
43
+ if (raw === '\x03')
44
+ return keyMsg('c', true);
45
+ // Ctrl+A through Ctrl+Z (0x01-0x1a), excluding already-handled cases
46
+ if (raw.length === 1) {
47
+ const code = raw.charCodeAt(0);
48
+ if (code >= 0x01 && code <= 0x1a) {
49
+ const letter = String.fromCharCode(code + 0x60); // 0x01 → 'a', 0x02 → 'b', etc.
50
+ return keyMsg(letter, true);
51
+ }
52
+ }
53
+ // Printable single characters
54
+ if (raw.length === 1) {
55
+ const code = raw.charCodeAt(0);
56
+ if (code >= 0x20 && code <= 0x7e) {
57
+ return keyMsg(raw);
58
+ }
59
+ }
60
+ return keyMsg('unknown');
61
+ }
62
+ //# sourceMappingURL=keys.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"keys.js","sourceRoot":"","sources":["../src/keys.ts"],"names":[],"mappings":"AAEA,SAAS,MAAM,CAAC,GAAW,EAAE,IAAI,GAAG,KAAK,EAAE,GAAG,GAAG,KAAK,EAAE,KAAK,GAAG,KAAK;IACnE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,EAAE,KAAK,EAAE,CAAC;AAChD,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,QAAQ,CAAC,GAAW;IAClC,qCAAqC;IACrC,IAAI,GAAG,KAAK,QAAQ;QAAE,OAAO,MAAM,CAAC,IAAI,CAAC,CAAC;IAC1C,IAAI,GAAG,KAAK,QAAQ;QAAE,OAAO,MAAM,CAAC,MAAM,CAAC,CAAC;IAC5C,IAAI,GAAG,KAAK,QAAQ;QAAE,OAAO,MAAM,CAAC,OAAO,CAAC,CAAC;IAC7C,IAAI,GAAG,KAAK,QAAQ;QAAE,OAAO,MAAM,CAAC,MAAM,CAAC,CAAC;IAC5C,IAAI,GAAG,KAAK,QAAQ;QAAE,OAAO,MAAM,CAAC,MAAM,CAAC,CAAC;IAC5C,IAAI,GAAG,KAAK,QAAQ;QAAE,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC;IAC3C,IAAI,GAAG,KAAK,SAAS;QAAE,OAAO,MAAM,CAAC,QAAQ,CAAC,CAAC;IAC/C,IAAI,GAAG,KAAK,SAAS;QAAE,OAAO,MAAM,CAAC,QAAQ,CAAC,CAAC;IAC/C,IAAI,GAAG,KAAK,SAAS;QAAE,OAAO,MAAM,CAAC,UAAU,CAAC,CAAC;IAEjD,uCAAuC;IACvC,IAAI,GAAG,KAAK,IAAI,IAAI,GAAG,KAAK,IAAI;QAAE,OAAO,MAAM,CAAC,OAAO,CAAC,CAAC;IACzD,IAAI,GAAG,KAAK,IAAI;QAAE,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC;IACvC,IAAI,GAAG,KAAK,QAAQ;QAAE,OAAO,MAAM,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,CAAC,CAAC,CAAC,YAAY;IAC5E,IAAI,GAAG,KAAK,MAAM;QAAE,OAAO,MAAM,CAAC,WAAW,CAAC,CAAC;IAC/C,IAAI,GAAG,KAAK,GAAG;QAAE,OAAO,MAAM,CAAC,OAAO,CAAC,CAAC;IAExC,iBAAiB;IACjB,IAAI,GAAG,KAAK,MAAM;QAAE,OAAO,MAAM,CAAC,QAAQ,CAAC,CAAC;IAE5C,SAAS;IACT,IAAI,GAAG,KAAK,MAAM;QAAE,OAAO,MAAM,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;IAE7C,qEAAqE;IACrE,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACrB,MAAM,IAAI,GAAG,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;QAC/B,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,EAAE,CAAC;YACjC,MAAM,MAAM,GAAG,MAAM,CAAC,YAAY,CAAC,IAAI,GAAG,IAAI,CAAC,CAAC,CAAC,+BAA+B;YAChF,OAAO,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;QAC9B,CAAC;IACH,CAAC;IAED,8BAA8B;IAC9B,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACrB,MAAM,IAAI,GAAG,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;QAC/B,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,EAAE,CAAC;YACjC,OAAO,MAAM,CAAC,GAAG,CAAC,CAAC;QACrB,CAAC;IACH,CAAC;IAED,OAAO,MAAM,CAAC,SAAS,CAAC,CAAC;AAC3B,CAAC"}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Vertical stack — join blocks with newlines.
3
+ * Filters out empty strings.
4
+ */
5
+ export declare function vstack(...blocks: string[]): string;
6
+ /**
7
+ * Horizontal stack — place blocks side by side with a gap between columns.
8
+ * Pads shorter blocks with empty lines to align rows.
9
+ */
10
+ export declare function hstack(gap: number, ...blocks: string[]): string;
11
+ //# sourceMappingURL=layout.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"layout.d.ts","sourceRoot":"","sources":["../src/layout.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,wBAAgB,MAAM,CAAC,GAAG,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,CAElD;AAED;;;GAGG;AACH,wBAAgB,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,CAwB/D"}
package/dist/layout.js ADDED
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Vertical stack — join blocks with newlines.
3
+ * Filters out empty strings.
4
+ */
5
+ export function vstack(...blocks) {
6
+ return blocks.filter((b) => b !== '').join('\n');
7
+ }
8
+ /**
9
+ * Horizontal stack — place blocks side by side with a gap between columns.
10
+ * Pads shorter blocks with empty lines to align rows.
11
+ */
12
+ export function hstack(gap, ...blocks) {
13
+ if (blocks.length === 0)
14
+ return '';
15
+ if (blocks.length === 1)
16
+ return blocks[0];
17
+ const split = blocks.map((b) => b.split('\n'));
18
+ const maxRows = Math.max(...split.map((lines) => lines.length));
19
+ const widths = split.map((lines) => Math.max(...lines.map(visualWidth)));
20
+ const spacer = ' '.repeat(gap);
21
+ const rows = [];
22
+ for (let r = 0; r < maxRows; r++) {
23
+ const parts = [];
24
+ for (let c = 0; c < split.length; c++) {
25
+ const line = split[c][r] ?? '';
26
+ // Last column doesn't need right-padding
27
+ if (c < split.length - 1) {
28
+ parts.push(line + ' '.repeat(widths[c] - visualWidth(line)));
29
+ }
30
+ else {
31
+ parts.push(line);
32
+ }
33
+ }
34
+ rows.push(parts.join(spacer).trimEnd());
35
+ }
36
+ return rows.join('\n');
37
+ }
38
+ /** Width of a string with ANSI escapes stripped. */
39
+ function visualWidth(s) {
40
+ // Strip ANSI escape sequences
41
+ // eslint-disable-next-line no-control-regex
42
+ return s.replace(/\x1b\[[0-9;]*m/g, '').length;
43
+ }
44
+ //# sourceMappingURL=layout.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"layout.js","sourceRoot":"","sources":["../src/layout.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,UAAU,MAAM,CAAC,GAAG,MAAgB;IACxC,OAAO,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACnD,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,MAAM,CAAC,GAAW,EAAE,GAAG,MAAgB;IACrD,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IACnC,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,MAAM,CAAC,CAAC,CAAE,CAAC;IAE3C,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC;IAC/C,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC;IAChE,MAAM,MAAM,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC;IACzE,MAAM,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IAE/B,MAAM,IAAI,GAAa,EAAE,CAAC;IAC1B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,OAAO,EAAE,CAAC,EAAE,EAAE,CAAC;QACjC,MAAM,KAAK,GAAa,EAAE,CAAC;QAC3B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YACtC,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;YAChC,yCAAyC;YACzC,IAAI,CAAC,GAAG,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBACzB,KAAK,CAAC,IAAI,CAAC,IAAI,GAAG,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAE,GAAG,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YAChE,CAAC;iBAAM,CAAC;gBACN,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACnB,CAAC;QACH,CAAC;QACD,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC;IAC1C,CAAC;IACD,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACzB,CAAC;AAED,oDAAoD;AACpD,SAAS,WAAW,CAAC,CAAS;IAC5B,8BAA8B;IAC9B,4CAA4C;IAC5C,OAAO,CAAC,CAAC,OAAO,CAAC,iBAAiB,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC;AACjD,CAAC"}
@@ -0,0 +1,10 @@
1
+ import type { App, RunOptions } from './types.js';
2
+ /**
3
+ * Run a TEA application.
4
+ *
5
+ * In non-interactive modes (pipe/static/accessible), renders the initial view
6
+ * once and exits. In interactive mode, enters the alt screen, starts the
7
+ * keyboard event loop, and drives the model/update/view cycle.
8
+ */
9
+ export declare function run<Model, M>(app: App<Model, M>, options?: RunOptions): Promise<void>;
10
+ //# sourceMappingURL=runtime.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,GAAG,EAAe,UAAU,EAAE,MAAM,YAAY,CAAC;AAW/D;;;;;;GAMG;AACH,wBAAsB,GAAG,CAAC,KAAK,EAAE,CAAC,EAChC,GAAG,EAAE,GAAG,CAAC,KAAK,EAAE,CAAC,CAAC,EAClB,OAAO,CAAC,EAAE,UAAU,GACnB,OAAO,CAAC,IAAI,CAAC,CAsGf"}
@@ -0,0 +1,110 @@
1
+ import { getDefaultContext } from '@flyingrobots/bijou';
2
+ import { QUIT } from './types.js';
3
+ import { parseKey } from './keys.js';
4
+ import { enterScreen, exitScreen, renderFrame } from './screen.js';
5
+ /**
6
+ * Disable mouse reporting sequences that terminals may send.
7
+ * Some terminals auto-enable mouse tracking in alt screen mode.
8
+ */
9
+ const DISABLE_MOUSE = '\x1b[?1000l\x1b[?1002l\x1b[?1003l\x1b[?1006l';
10
+ /**
11
+ * Run a TEA application.
12
+ *
13
+ * In non-interactive modes (pipe/static/accessible), renders the initial view
14
+ * once and exits. In interactive mode, enters the alt screen, starts the
15
+ * keyboard event loop, and drives the model/update/view cycle.
16
+ */
17
+ export async function run(app, options) {
18
+ const ctx = options?.ctx ?? getDefaultContext();
19
+ const useAltScreen = options?.altScreen ?? true;
20
+ const useHideCursor = options?.hideCursor ?? true;
21
+ const [initModel, initCmds] = app.init();
22
+ // Non-interactive: render once and return
23
+ if (ctx.mode !== 'interactive') {
24
+ ctx.io.write(app.view(initModel));
25
+ return;
26
+ }
27
+ // Interactive mode
28
+ let model = initModel;
29
+ let running = true;
30
+ let lastCtrlC = 0;
31
+ let resolveQuit = null;
32
+ function shutdown() {
33
+ if (!running)
34
+ return;
35
+ running = false;
36
+ if (resolveQuit)
37
+ resolveQuit();
38
+ }
39
+ // Setup screen
40
+ if (useAltScreen || useHideCursor) {
41
+ enterScreen(ctx.io);
42
+ }
43
+ // Disable mouse reporting to avoid garbage from mouse events
44
+ ctx.io.write(DISABLE_MOUSE);
45
+ // Render helper
46
+ function render() {
47
+ if (!running)
48
+ return;
49
+ renderFrame(ctx.io, app.view(model));
50
+ }
51
+ // Message handler
52
+ function handleMsg(msg) {
53
+ if (!running)
54
+ return;
55
+ const [newModel, cmds] = app.update(msg, model);
56
+ model = newModel;
57
+ render();
58
+ executeCommands(cmds);
59
+ }
60
+ // Execute commands, feeding results back as messages
61
+ function executeCommands(cmds) {
62
+ for (const cmd of cmds) {
63
+ void cmd().then((result) => {
64
+ if (result === QUIT) {
65
+ shutdown();
66
+ return;
67
+ }
68
+ if (result !== undefined) {
69
+ handleMsg(result);
70
+ }
71
+ });
72
+ }
73
+ }
74
+ // Initial render
75
+ render();
76
+ // Execute startup commands
77
+ executeCommands(initCmds);
78
+ // Start keyboard listener
79
+ const inputHandle = ctx.io.rawInput((raw) => {
80
+ if (!running)
81
+ return;
82
+ const keyMsg = parseKey(raw);
83
+ // Skip unknown sequences (mouse events, etc.)
84
+ if (keyMsg.key === 'unknown')
85
+ return;
86
+ // Double Ctrl+C force-quit
87
+ if (keyMsg.key === 'c' && keyMsg.ctrl) {
88
+ const now = Date.now();
89
+ if (now - lastCtrlC < 1000) {
90
+ shutdown();
91
+ return;
92
+ }
93
+ lastCtrlC = now;
94
+ }
95
+ handleMsg(keyMsg);
96
+ });
97
+ // Wait for quit signal
98
+ await new Promise((resolve) => {
99
+ resolveQuit = resolve;
100
+ // In case shutdown was already called before we got here
101
+ if (!running)
102
+ resolve();
103
+ });
104
+ // Cleanup
105
+ inputHandle.dispose();
106
+ if (useAltScreen || useHideCursor) {
107
+ exitScreen(ctx.io);
108
+ }
109
+ }
110
+ //# sourceMappingURL=runtime.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime.js","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,21 @@
1
+ import type { IOPort } from '@flyingrobots/bijou';
2
+ export declare const ENTER_ALT_SCREEN = "\u001B[?1049h";
3
+ export declare const EXIT_ALT_SCREEN = "\u001B[?1049l";
4
+ export declare const HIDE_CURSOR = "\u001B[?25l";
5
+ export declare const SHOW_CURSOR = "\u001B[?25h";
6
+ export declare const CLEAR_SCREEN = "\u001B[2J";
7
+ export declare const CLEAR_TO_END = "\u001B[J";
8
+ export declare const HOME = "\u001B[H";
9
+ export declare const CLEAR_LINE = "\u001B[2K";
10
+ /** Enter alt screen buffer, hide cursor, and clear screen. */
11
+ export declare function enterScreen(io: IOPort): void;
12
+ /** Show cursor and exit alt screen buffer. */
13
+ export declare function exitScreen(io: IOPort): void;
14
+ /** Clear screen and move cursor to home position. */
15
+ export declare function clearAndHome(io: IOPort): void;
16
+ /**
17
+ * Flicker-free render: move cursor home, write content line-by-line
18
+ * (clearing each line first), then erase everything below.
19
+ */
20
+ export declare function renderFrame(io: IOPort, content: string): void;
21
+ //# sourceMappingURL=screen.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"screen.d.ts","sourceRoot":"","sources":["../src/screen.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAElD,eAAO,MAAM,gBAAgB,kBAAgB,CAAC;AAC9C,eAAO,MAAM,eAAe,kBAAgB,CAAC;AAC7C,eAAO,MAAM,WAAW,gBAAc,CAAC;AACvC,eAAO,MAAM,WAAW,gBAAc,CAAC;AACvC,eAAO,MAAM,YAAY,cAAY,CAAC;AACtC,eAAO,MAAM,YAAY,aAAW,CAAC;AACrC,eAAO,MAAM,IAAI,aAAW,CAAC;AAC7B,eAAO,MAAM,UAAU,cAAY,CAAC;AAEpC,8DAA8D;AAC9D,wBAAgB,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAE5C;AAED,8CAA8C;AAC9C,wBAAgB,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAE3C;AAED,qDAAqD;AACrD,wBAAgB,YAAY,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAE7C;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAQ7D"}
package/dist/screen.js ADDED
@@ -0,0 +1,34 @@
1
+ export const ENTER_ALT_SCREEN = '\x1b[?1049h';
2
+ export const EXIT_ALT_SCREEN = '\x1b[?1049l';
3
+ export const HIDE_CURSOR = '\x1b[?25l';
4
+ export const SHOW_CURSOR = '\x1b[?25h';
5
+ export const CLEAR_SCREEN = '\x1b[2J';
6
+ export const CLEAR_TO_END = '\x1b[J';
7
+ export const HOME = '\x1b[H';
8
+ export const CLEAR_LINE = '\x1b[2K';
9
+ /** Enter alt screen buffer, hide cursor, and clear screen. */
10
+ export function enterScreen(io) {
11
+ io.write(ENTER_ALT_SCREEN + HIDE_CURSOR + CLEAR_SCREEN + HOME);
12
+ }
13
+ /** Show cursor and exit alt screen buffer. */
14
+ export function exitScreen(io) {
15
+ io.write(SHOW_CURSOR + EXIT_ALT_SCREEN);
16
+ }
17
+ /** Clear screen and move cursor to home position. */
18
+ export function clearAndHome(io) {
19
+ io.write(CLEAR_SCREEN + HOME);
20
+ }
21
+ /**
22
+ * Flicker-free render: move cursor home, write content line-by-line
23
+ * (clearing each line first), then erase everything below.
24
+ */
25
+ export function renderFrame(io, content) {
26
+ const lines = content.split('\n');
27
+ let frame = HOME;
28
+ for (const line of lines) {
29
+ frame += CLEAR_LINE + line + '\n';
30
+ }
31
+ frame += CLEAR_TO_END;
32
+ io.write(frame);
33
+ }
34
+ //# sourceMappingURL=screen.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"screen.js","sourceRoot":"","sources":["../src/screen.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,gBAAgB,GAAG,aAAa,CAAC;AAC9C,MAAM,CAAC,MAAM,eAAe,GAAG,aAAa,CAAC;AAC7C,MAAM,CAAC,MAAM,WAAW,GAAG,WAAW,CAAC;AACvC,MAAM,CAAC,MAAM,WAAW,GAAG,WAAW,CAAC;AACvC,MAAM,CAAC,MAAM,YAAY,GAAG,SAAS,CAAC;AACtC,MAAM,CAAC,MAAM,YAAY,GAAG,QAAQ,CAAC;AACrC,MAAM,CAAC,MAAM,IAAI,GAAG,QAAQ,CAAC;AAC7B,MAAM,CAAC,MAAM,UAAU,GAAG,SAAS,CAAC;AAEpC,8DAA8D;AAC9D,MAAM,UAAU,WAAW,CAAC,EAAU;IACpC,EAAE,CAAC,KAAK,CAAC,gBAAgB,GAAG,WAAW,GAAG,YAAY,GAAG,IAAI,CAAC,CAAC;AACjE,CAAC;AAED,8CAA8C;AAC9C,MAAM,UAAU,UAAU,CAAC,EAAU;IACnC,EAAE,CAAC,KAAK,CAAC,WAAW,GAAG,eAAe,CAAC,CAAC;AAC1C,CAAC;AAED,qDAAqD;AACrD,MAAM,UAAU,YAAY,CAAC,EAAU;IACrC,EAAE,CAAC,KAAK,CAAC,YAAY,GAAG,IAAI,CAAC,CAAC;AAChC,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,WAAW,CAAC,EAAU,EAAE,OAAe;IACrD,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAClC,IAAI,KAAK,GAAG,IAAI,CAAC;IACjB,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,KAAK,IAAI,UAAU,GAAG,IAAI,GAAG,IAAI,CAAC;IACpC,CAAC;IACD,KAAK,IAAI,YAAY,CAAC;IACtB,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;AAClB,CAAC"}
@@ -0,0 +1,22 @@
1
+ import type { BijouContext } from '@flyingrobots/bijou';
2
+ export interface KeyMsg {
3
+ readonly type: 'key';
4
+ readonly key: string;
5
+ readonly ctrl: boolean;
6
+ readonly alt: boolean;
7
+ readonly shift: boolean;
8
+ }
9
+ export declare const QUIT: unique symbol;
10
+ export type QuitSignal = typeof QUIT;
11
+ export type Cmd<M> = () => Promise<M | QuitSignal | void>;
12
+ export interface App<Model, M = never> {
13
+ init(): [Model, Cmd<M>[]];
14
+ update(msg: KeyMsg | M, model: Model): [Model, Cmd<M>[]];
15
+ view(model: Model): string;
16
+ }
17
+ export interface RunOptions {
18
+ altScreen?: boolean;
19
+ hideCursor?: boolean;
20
+ ctx?: BijouContext;
21
+ }
22
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAIxD,MAAM,WAAW,MAAM;IACrB,QAAQ,CAAC,IAAI,EAAE,KAAK,CAAC;IACrB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IACvB,QAAQ,CAAC,GAAG,EAAE,OAAO,CAAC;IACtB,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;CACzB;AAID,eAAO,MAAM,IAAI,EAAE,OAAO,MAAuB,CAAC;AAClD,MAAM,MAAM,UAAU,GAAG,OAAO,IAAI,CAAC;AAErC,MAAM,MAAM,GAAG,CAAC,CAAC,IAAI,MAAM,OAAO,CAAC,CAAC,GAAG,UAAU,GAAG,IAAI,CAAC,CAAC;AAI1D,MAAM,WAAW,GAAG,CAAC,KAAK,EAAE,CAAC,GAAG,KAAK;IACnC,IAAI,IAAI,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IAC1B,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,CAAC,EAAE,KAAK,EAAE,KAAK,GAAG,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IACzD,IAAI,CAAC,KAAK,EAAE,KAAK,GAAG,MAAM,CAAC;CAC5B;AAID,MAAM,WAAW,UAAU;IACzB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,GAAG,CAAC,EAAE,YAAY,CAAC;CACpB"}
package/dist/types.js ADDED
@@ -0,0 +1,3 @@
1
+ // --- Commands ---
2
+ export const QUIT = Symbol('QUIT');
3
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAYA,mBAAmB;AAEnB,MAAM,CAAC,MAAM,IAAI,GAAkB,MAAM,CAAC,MAAM,CAAC,CAAC"}
package/package.json ADDED
@@ -0,0 +1,59 @@
1
+ {
2
+ "name": "@flyingrobots/bijou-tui",
3
+ "version": "0.1.0",
4
+ "description": "TEA runtime for terminal UIs — model/update/view with keyboard input, alt screen, and layout helpers.",
5
+ "type": "module",
6
+ "sideEffects": false,
7
+ "main": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "default": "./dist/index.js"
13
+ },
14
+ "./package.json": "./package.json"
15
+ },
16
+ "files": [
17
+ "dist",
18
+ "README.md",
19
+ "LICENSE"
20
+ ],
21
+ "scripts": {
22
+ "build": "tsc -b",
23
+ "prepack": "tsc -b",
24
+ "lint": "tsc --noEmit"
25
+ },
26
+ "dependencies": {
27
+ "@flyingrobots/bijou": "0.1.0"
28
+ },
29
+ "devDependencies": {
30
+ "@types/node": "^22.0.0",
31
+ "typescript": "^5.9.3",
32
+ "vitest": "^4.0.18"
33
+ },
34
+ "keywords": [
35
+ "cli",
36
+ "terminal",
37
+ "tui",
38
+ "bijou",
39
+ "tea",
40
+ "elm-architecture",
41
+ "interactive"
42
+ ],
43
+ "author": "James Ross <james@flyingrobots.dev>",
44
+ "license": "MIT",
45
+ "repository": {
46
+ "type": "git",
47
+ "url": "git+https://github.com/flyingrobots/bijou.git",
48
+ "directory": "packages/bijou-tui"
49
+ },
50
+ "homepage": "https://github.com/flyingrobots/bijou",
51
+ "bugs": "https://github.com/flyingrobots/bijou/issues",
52
+ "engines": {
53
+ "node": ">=18"
54
+ },
55
+ "publishConfig": {
56
+ "access": "public",
57
+ "provenance": true
58
+ }
59
+ }