@bomb.sh/tty 0.0.0-register.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,61 @@
1
+ import { ALTSCREEN, CSI, ESC, HIDECURSOR, MAINSCREEN, SHOWCURSOR, } from "./termcodes.js";
2
+ export function settings(...sequence) {
3
+ return {
4
+ apply: concat(sequence.map((s) => s.apply)),
5
+ revert: concat(sequence.map((s) => s.revert).reverse()),
6
+ };
7
+ }
8
+ export function alternateBuffer(options) {
9
+ return {
10
+ apply: ALTSCREEN(options),
11
+ revert: MAINSCREEN(),
12
+ };
13
+ }
14
+ export function cursor(visible) {
15
+ if (visible) {
16
+ return {
17
+ apply: SHOWCURSOR(),
18
+ revert: HIDECURSOR(),
19
+ };
20
+ }
21
+ else {
22
+ return {
23
+ apply: HIDECURSOR(),
24
+ revert: SHOWCURSOR(),
25
+ };
26
+ }
27
+ }
28
+ /**
29
+ * Save and restore cursor position using DECSC (`ESC 7`) / DECRC (`ESC 8`).
30
+ *
31
+ * @see {@link https://vt100.net/docs/vt510-rm/DECSC.html | VT510 DECSC}
32
+ * @see {@link https://vt100.net/docs/vt510-rm/DECRC.html | VT510 DECRC}
33
+ */
34
+ export function saveCursorPosition() {
35
+ return {
36
+ apply: ESC("7"),
37
+ revert: ESC("8"),
38
+ };
39
+ }
40
+ export function progressiveInput(level) {
41
+ return {
42
+ apply: CSI(`>${level}u`),
43
+ revert: CSI("<u"),
44
+ };
45
+ }
46
+ export function mouseTracking() {
47
+ return {
48
+ apply: concat([CSI("?1003h"), CSI("?1006h")]),
49
+ revert: concat([CSI("?1006l"), CSI("?1003l")]),
50
+ };
51
+ }
52
+ function concat(arrays) {
53
+ let length = arrays.reduce((sum, a) => sum + a.length, 0);
54
+ let result = new Uint8Array(length);
55
+ let offset = 0;
56
+ for (let a of arrays) {
57
+ result.set(a, offset);
58
+ offset += a.length;
59
+ }
60
+ return result;
61
+ }
@@ -0,0 +1,21 @@
1
+ export interface BoundingBox {
2
+ x: number;
3
+ y: number;
4
+ width: number;
5
+ height: number;
6
+ }
7
+ export interface Native {
8
+ memory: WebAssembly.Memory;
9
+ statePtr: number;
10
+ opsBuf: number;
11
+ reduce(ct: number, buf: number, len: number, mode: number, row: number): void;
12
+ output(ct: number): number;
13
+ length(ct: number): number;
14
+ setPointer(x: number, y: number, down: boolean): void;
15
+ getPointerOverIds(): string[];
16
+ getElementBounds(id: string): BoundingBox | undefined;
17
+ errorCount(ct: number): number;
18
+ errorType(ct: number, index: number): number;
19
+ errorMessage(ct: number, index: number): string;
20
+ }
21
+ export declare function createTermNative(w: number, h: number): Promise<Native>;
@@ -0,0 +1,97 @@
1
+ import { f32, offsets, struct } from "./typedef.js";
2
+ const BoundingBoxStruct = struct({
3
+ x: f32(),
4
+ y: f32(),
5
+ width: f32(),
6
+ height: f32(),
7
+ });
8
+ const BOUNDING_BOX = offsets(BoundingBoxStruct);
9
+ import { compiled } from "./wasm.js";
10
+ export async function createTermNative(w, h) {
11
+ let memory = new WebAssembly.Memory({ initial: 2 });
12
+ let exports = {};
13
+ let instance = await WebAssembly.instantiate(compiled, {
14
+ env: { memory },
15
+ clay: {
16
+ measureTextFunction(ret, text, _config, _userData) {
17
+ exports.measure(ret, text);
18
+ },
19
+ queryScrollOffsetFunction(ret, _elementId, _userData) {
20
+ let view = new DataView(memory.buffer);
21
+ view.setFloat32(ret, 0, true);
22
+ view.setFloat32(ret + 4, 0, true);
23
+ },
24
+ },
25
+ });
26
+ Object.assign(exports, instance.exports);
27
+ let ct = exports;
28
+ let heap = ct.__heap_base.value;
29
+ let size = ct.clayterm_size(w, h);
30
+ // grow memory to fit heap + state + ops buffer (1MB headroom for ops)
31
+ let needed = heap + size + 1024 * 1024;
32
+ let pages = Math.ceil(needed / 65536);
33
+ let current = memory.buffer.byteLength / 65536;
34
+ if (pages > current) {
35
+ memory.grow(pages - current);
36
+ }
37
+ let statePtr = ct.init(heap, w, h);
38
+ let opsBuf = (heap + size + 3) & ~3;
39
+ return {
40
+ memory,
41
+ statePtr,
42
+ opsBuf,
43
+ reduce: ct.reduce,
44
+ output: ct.output,
45
+ length: ct.length,
46
+ setPointer(x, y, down) {
47
+ let view = new DataView(memory.buffer);
48
+ view.setFloat32(opsBuf, x, true);
49
+ view.setFloat32(opsBuf + 4, y, true);
50
+ ct.Clay_SetPointerState(opsBuf, down ? 1 : 0);
51
+ },
52
+ getPointerOverIds() {
53
+ let decoder = new TextDecoder();
54
+ let count = ct.pointer_over_count();
55
+ let ids = [];
56
+ for (let i = 0; i < count; i++) {
57
+ let len = ct.pointer_over_id_string_length(i);
58
+ if (len === 0)
59
+ continue;
60
+ let ptr = ct.pointer_over_id_string_ptr(i);
61
+ ids.push(decoder.decode(new Uint8Array(memory.buffer, ptr, len)));
62
+ }
63
+ return ids;
64
+ },
65
+ getElementBounds(id) {
66
+ let enc = new TextEncoder();
67
+ let bytes = enc.encode(id);
68
+ new Uint8Array(memory.buffer).set(bytes, opsBuf);
69
+ let out = opsBuf + 256;
70
+ let found = ct.get_element_bounds(opsBuf, bytes.length, out);
71
+ if (!found) {
72
+ return undefined;
73
+ }
74
+ let view = new DataView(memory.buffer);
75
+ return {
76
+ x: view.getFloat32(out + BOUNDING_BOX.x, true),
77
+ y: view.getFloat32(out + BOUNDING_BOX.y, true),
78
+ width: view.getFloat32(out + BOUNDING_BOX.width, true),
79
+ height: view.getFloat32(out + BOUNDING_BOX.height, true),
80
+ };
81
+ },
82
+ errorCount(ptr) {
83
+ return ct.error_count(ptr);
84
+ },
85
+ errorType(ptr, index) {
86
+ return ct.error_type(ptr, index);
87
+ },
88
+ errorMessage(ptr, index) {
89
+ let len = ct.error_message_length(ptr, index);
90
+ if (len === 0)
91
+ return "";
92
+ let p = ct.error_message_ptr(ptr, index);
93
+ let decoder = new TextDecoder();
94
+ return decoder.decode(new Uint8Array(memory.buffer, p, len));
95
+ },
96
+ };
97
+ }
package/esm/term.d.ts ADDED
@@ -0,0 +1,55 @@
1
+ import { type Op } from "./ops.js";
2
+ import { type BoundingBox } from "./term-native.js";
3
+ export interface TermOptions {
4
+ height: number;
5
+ width: number;
6
+ }
7
+ export interface RenderOptions {
8
+ mode?: "line";
9
+ /**
10
+ * Row where to begin rendering. This should only be used when
11
+ * rendering into a region as part of the CLI main screen. For
12
+ * interfaces that use the entire screen, leave unset which will
13
+ * default to 0. This is 1-based which which is the DSR native
14
+ * format.
15
+ *
16
+ * https://www.ecma-international.org/publications-and-standards/standards/ecma-48/
17
+ */
18
+ row?: number;
19
+ pointer?: {
20
+ x: number;
21
+ y: number;
22
+ down: boolean;
23
+ };
24
+ }
25
+ export type PointerEvent = {
26
+ type: "pointerenter";
27
+ id: string;
28
+ } | {
29
+ type: "pointerleave";
30
+ id: string;
31
+ } | {
32
+ type: "pointerclick";
33
+ id: string;
34
+ };
35
+ export type { BoundingBox };
36
+ export interface ElementInfo {
37
+ bounds: BoundingBox;
38
+ }
39
+ export interface ClayError {
40
+ type: string;
41
+ message: string;
42
+ }
43
+ export interface RenderInfo {
44
+ get(id: string): ElementInfo | undefined;
45
+ }
46
+ export interface RenderResult {
47
+ output: Uint8Array;
48
+ events: PointerEvent[];
49
+ info: RenderInfo;
50
+ errors: ClayError[];
51
+ }
52
+ export interface Term {
53
+ render(ops: Op[], options?: RenderOptions): RenderResult;
54
+ }
55
+ export declare function createTerm(options: TermOptions): Promise<Term>;
package/esm/term.js ADDED
@@ -0,0 +1,81 @@
1
+ import { pack } from "./ops.js";
2
+ import { createTermNative } from "./term-native.js";
3
+ const ERROR_TYPES = [
4
+ "TEXT_MEASUREMENT_FUNCTION_NOT_PROVIDED",
5
+ "ARENA_CAPACITY_EXCEEDED",
6
+ "ELEMENTS_CAPACITY_EXCEEDED",
7
+ "TEXT_MEASUREMENT_CAPACITY_EXCEEDED",
8
+ "DUPLICATE_ID",
9
+ "FLOATING_CONTAINER_PARENT_NOT_FOUND",
10
+ "PERCENTAGE_OVER_1",
11
+ "INTERNAL_ERROR",
12
+ "UNBALANCED_OPEN_CLOSE",
13
+ ];
14
+ export async function createTerm(options) {
15
+ let { width, height } = options;
16
+ let native = await createTermNative(width, height);
17
+ let { memory, statePtr, opsBuf } = native;
18
+ let prev = new Set();
19
+ let pressed = new Set();
20
+ let wasDown = false;
21
+ return {
22
+ render(ops, options) {
23
+ let len = pack(ops, memory.buffer, opsBuf, memory.buffer.byteLength);
24
+ let mode = options?.mode === "line" ? 1 : 0;
25
+ let row = options?.row ?? 1;
26
+ native.reduce(statePtr, opsBuf, len, mode, row);
27
+ if (options?.pointer) {
28
+ let { x, y, down } = options.pointer;
29
+ native.setPointer(x, y, down);
30
+ }
31
+ let output = new Uint8Array(memory.buffer, native.output(statePtr), native.length(statePtr));
32
+ let current = new Set(options?.pointer ? native.getPointerOverIds() : []);
33
+ let down = options?.pointer?.down ?? false;
34
+ let events = [];
35
+ for (let id of current) {
36
+ if (!prev.has(id)) {
37
+ events.push({ type: "pointerenter", id });
38
+ }
39
+ }
40
+ for (let id of prev) {
41
+ if (!current.has(id)) {
42
+ events.push({ type: "pointerleave", id });
43
+ }
44
+ }
45
+ if (wasDown && !down) {
46
+ for (let id of pressed) {
47
+ if (current.has(id)) {
48
+ events.push({ type: "pointerclick", id });
49
+ }
50
+ }
51
+ }
52
+ if (down && !wasDown) {
53
+ pressed = new Set(current);
54
+ }
55
+ else if (!down) {
56
+ pressed.clear();
57
+ }
58
+ prev = current;
59
+ wasDown = down;
60
+ let info = {
61
+ get(id) {
62
+ let bounds = native.getElementBounds(id);
63
+ if (bounds) {
64
+ return { bounds };
65
+ }
66
+ return undefined;
67
+ },
68
+ };
69
+ let errors = [];
70
+ let count = native.errorCount(statePtr);
71
+ for (let i = 0; i < count; i++) {
72
+ let code = native.errorType(statePtr, i);
73
+ errors.push({
74
+ type: ERROR_TYPES[code] ?? `UNKNOWN_${code}`,
75
+ message: native.errorMessage(statePtr, i),
76
+ });
77
+ }
78
+ return { output, events, info, errors };
79
+ },
80
+ };
81
+ }
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Encode a plain escape sequence.
3
+ *
4
+ * Prepends `ESC` (`\x1b`) to the given string and returns the result as bytes.
5
+ *
6
+ * @see {@link https://www.ecma-international.org/publications-and-standards/standards/ecma-48/ | ECMA-48}
7
+ */
8
+ export declare function ESC(str: string): Uint8Array;
9
+ /**
10
+ * Encode a Control Sequence Introducer (CSI) command.
11
+ *
12
+ * Prepends `ESC[` to the given string and returns the result as bytes.
13
+ *
14
+ * @see {@link https://www.ecma-international.org/publications-and-standards/standards/ecma-48/ | ECMA-48}
15
+ */
16
+ export declare function CSI(str: string): Uint8Array;
17
+ /**
18
+ * Request the cursor position via Device Status Report (DSR).
19
+ *
20
+ * Sends `CSI 6n`. The terminal responds with a Cursor Position Report
21
+ * (`CSI row ; column R`) where row and column are 1-based.
22
+ *
23
+ * @see {@link https://www.ecma-international.org/publications-and-standards/standards/ecma-48/ | ECMA-48}
24
+ */
25
+ export declare function DSR(): Uint8Array;
26
+ /**
27
+ * Show the cursor (DECTCEM set).
28
+ *
29
+ * DEC private mode 25. Not part of ECMA-48; originates from the VT220.
30
+ *
31
+ * @see {@link https://vt100.net/docs/vt510-rm/DECTCEM.html | VT510 DECTCEM}
32
+ */
33
+ export declare function SHOWCURSOR(): Uint8Array;
34
+ /**
35
+ * Hide the cursor (DECTCEM reset).
36
+ *
37
+ * DEC private mode 25. Not part of ECMA-48; originates from the VT220.
38
+ *
39
+ * @see {@link https://vt100.net/docs/vt510-rm/DECTCEM.html | VT510 DECTCEM}
40
+ */
41
+ export declare function HIDECURSOR(): Uint8Array;
42
+ /**
43
+ * Switch to the alternate screen buffer.
44
+ *
45
+ * Saves the cursor and switches to the alternate screen. When `clear` is
46
+ * `true` (the default), the alternate buffer is cleared on entry. When
47
+ * `false`, the existing contents are preserved.
48
+ *
49
+ * Use {@link MAINSCREEN} to switch back.
50
+ *
51
+ * @see {@link https://invisible-island.net/xterm/ctlseqs/ctlseqs.html | xterm control sequences}
52
+ */
53
+ export declare function ALTSCREEN(options?: {
54
+ clear?: boolean;
55
+ }): Uint8Array;
56
+ /**
57
+ * Switch back to the main screen buffer.
58
+ *
59
+ * Restores the cursor and returns to the main screen with scrollback intact.
60
+ *
61
+ * @see {@link https://invisible-island.net/xterm/ctlseqs/ctlseqs.html | xterm control sequences}
62
+ */
63
+ export declare function MAINSCREEN(): Uint8Array;
@@ -0,0 +1,85 @@
1
+ /**
2
+ * Encode a plain escape sequence.
3
+ *
4
+ * Prepends `ESC` (`\x1b`) to the given string and returns the result as bytes.
5
+ *
6
+ * @see {@link https://www.ecma-international.org/publications-and-standards/standards/ecma-48/ | ECMA-48}
7
+ */
8
+ export function ESC(str) {
9
+ return encode(`\x1b${str}`);
10
+ }
11
+ /**
12
+ * Encode a Control Sequence Introducer (CSI) command.
13
+ *
14
+ * Prepends `ESC[` to the given string and returns the result as bytes.
15
+ *
16
+ * @see {@link https://www.ecma-international.org/publications-and-standards/standards/ecma-48/ | ECMA-48}
17
+ */
18
+ export function CSI(str) {
19
+ return ESC(`[${str}`);
20
+ }
21
+ /**
22
+ * Request the cursor position via Device Status Report (DSR).
23
+ *
24
+ * Sends `CSI 6n`. The terminal responds with a Cursor Position Report
25
+ * (`CSI row ; column R`) where row and column are 1-based.
26
+ *
27
+ * @see {@link https://www.ecma-international.org/publications-and-standards/standards/ecma-48/ | ECMA-48}
28
+ */
29
+ export function DSR() {
30
+ return CSI("6n");
31
+ }
32
+ /**
33
+ * Show the cursor (DECTCEM set).
34
+ *
35
+ * DEC private mode 25. Not part of ECMA-48; originates from the VT220.
36
+ *
37
+ * @see {@link https://vt100.net/docs/vt510-rm/DECTCEM.html | VT510 DECTCEM}
38
+ */
39
+ export function SHOWCURSOR() {
40
+ return CSI("?25h");
41
+ }
42
+ /**
43
+ * Hide the cursor (DECTCEM reset).
44
+ *
45
+ * DEC private mode 25. Not part of ECMA-48; originates from the VT220.
46
+ *
47
+ * @see {@link https://vt100.net/docs/vt510-rm/DECTCEM.html | VT510 DECTCEM}
48
+ */
49
+ export function HIDECURSOR() {
50
+ return CSI("?25l");
51
+ }
52
+ /**
53
+ * Switch to the alternate screen buffer.
54
+ *
55
+ * Saves the cursor and switches to the alternate screen. When `clear` is
56
+ * `true` (the default), the alternate buffer is cleared on entry. When
57
+ * `false`, the existing contents are preserved.
58
+ *
59
+ * Use {@link MAINSCREEN} to switch back.
60
+ *
61
+ * @see {@link https://invisible-island.net/xterm/ctlseqs/ctlseqs.html | xterm control sequences}
62
+ */
63
+ export function ALTSCREEN(options) {
64
+ let { clear = true } = options ?? {};
65
+ if (clear) {
66
+ return CSI("?1049h");
67
+ }
68
+ else {
69
+ return CSI("?47h");
70
+ }
71
+ }
72
+ /**
73
+ * Switch back to the main screen buffer.
74
+ *
75
+ * Restores the cursor and returns to the main screen with scrollback intact.
76
+ *
77
+ * @see {@link https://invisible-island.net/xterm/ctlseqs/ctlseqs.html | xterm control sequences}
78
+ */
79
+ export function MAINSCREEN() {
80
+ return CSI("?1049l");
81
+ }
82
+ const encoder = new TextEncoder();
83
+ function encode(str) {
84
+ return encoder.encode(str);
85
+ }
@@ -0,0 +1,44 @@
1
+ export type Attrs<T> = {
2
+ [K in keyof T]: TypeDef<T[K]>;
3
+ };
4
+ export type Alignment = 1 | 2 | 4 | 8;
5
+ export type LayoutElement<T> = {
6
+ type: "padding";
7
+ byteLength: number;
8
+ } | {
9
+ type: "field";
10
+ name: string;
11
+ offset: number;
12
+ typedef: Attrs<T>[keyof Attrs<T>];
13
+ };
14
+ export type Struct<T> = {
15
+ type: "struct";
16
+ byteLength: number;
17
+ byteAlign: Alignment;
18
+ layout: LayoutElement<T>[];
19
+ };
20
+ export type Num<T> = {
21
+ type: "i32" | "f32" | "f64" | "uint8" | "uint16" | "uint32" | "int16" | "int32";
22
+ byteAlign: Alignment;
23
+ byteLength: number;
24
+ T?: T;
25
+ };
26
+ export type Arr<T> = {
27
+ type: "array";
28
+ element: TypeDef<T>;
29
+ length: number;
30
+ byteLength: number;
31
+ byteAlign: Alignment;
32
+ };
33
+ export type TypeDef<T> = Num<T> | Struct<T> | Arr<T>;
34
+ export declare function array<T>(element: TypeDef<T>, length: number): Arr<T[]>;
35
+ export declare const f32: () => TypeDef<number>;
36
+ export declare const int32: () => TypeDef<number>;
37
+ export declare const uint8: () => TypeDef<number>;
38
+ export declare const uint16: () => TypeDef<number>;
39
+ export declare const uint32: () => TypeDef<number>;
40
+ export declare function struct<T extends object>(attrs: Attrs<T>): Struct<T>;
41
+ export declare function offsets<T extends object>(def: Struct<T>): {
42
+ [K in keyof T]: number;
43
+ };
44
+ export declare function pad(offset: number, alignment: number): number;
package/esm/typedef.js ADDED
@@ -0,0 +1,84 @@
1
+ export function array(element, length) {
2
+ return {
3
+ type: "array",
4
+ element: element,
5
+ length,
6
+ byteLength: element.byteLength * length,
7
+ byteAlign: element.byteAlign,
8
+ };
9
+ }
10
+ export const f32 = () => ({
11
+ type: "f32",
12
+ byteLength: 4,
13
+ byteAlign: 4,
14
+ });
15
+ export const int32 = () => ({
16
+ type: "int32",
17
+ byteLength: 4,
18
+ byteAlign: 4,
19
+ });
20
+ export const uint8 = () => ({
21
+ type: "uint8",
22
+ byteLength: 1,
23
+ byteAlign: 1,
24
+ });
25
+ export const uint16 = () => ({
26
+ type: "uint16",
27
+ byteLength: 2,
28
+ byteAlign: 2,
29
+ });
30
+ export const uint32 = () => ({
31
+ type: "uint32",
32
+ byteAlign: 4,
33
+ byteLength: 4,
34
+ });
35
+ export function struct(attrs) {
36
+ let entries = Object.entries(attrs);
37
+ let acc = {
38
+ layout: [],
39
+ offset: 0,
40
+ };
41
+ let byteAlign = Math.max(...entries.map(([, typedef]) => typedef.byteAlign));
42
+ for (let [name, typedef] of entries) {
43
+ let padding = pad(acc.offset, typedef.byteAlign);
44
+ if (padding > 0) {
45
+ acc.layout.push({ type: "padding", byteLength: padding });
46
+ acc.offset += padding;
47
+ }
48
+ acc.layout.push({
49
+ type: "field",
50
+ name: name,
51
+ offset: acc.offset,
52
+ typedef,
53
+ });
54
+ acc.offset += typedef.byteLength;
55
+ }
56
+ let padding = pad(acc.offset, byteAlign);
57
+ if (padding > 0) {
58
+ acc.layout.push({ type: "padding", byteLength: padding });
59
+ acc.offset += padding;
60
+ }
61
+ return {
62
+ type: "struct",
63
+ layout: acc.layout,
64
+ byteLength: acc.offset,
65
+ byteAlign,
66
+ };
67
+ }
68
+ export function offsets(def) {
69
+ let result = {};
70
+ for (let element of def.layout) {
71
+ if (element.type === "field") {
72
+ result[element.name] = element.offset;
73
+ }
74
+ }
75
+ return result;
76
+ }
77
+ export function pad(offset, alignment) {
78
+ if ((offset % alignment) !== 0) {
79
+ return alignment - (offset % alignment);
80
+ }
81
+ else {
82
+ return 0;
83
+ }
84
+ }
package/esm/wasm.d.ts ADDED
@@ -0,0 +1 @@
1
+ export declare const compiled: any;