@textui/terminal 0.2.0 → 0.3.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/dist/index.d.ts CHANGED
@@ -2,6 +2,8 @@ export * as ansi from './ansi.js';
2
2
  export { Writer, createWriter } from './writer.js';
3
3
  export { captureBuffer } from './capture.js';
4
4
  export type { CaptureOptions } from './capture.js';
5
+ export { renderStill } from './still.js';
6
+ export type { Still, StillOptions } from './still.js';
5
7
  export { bufferToSvg } from './svg.js';
6
8
  export type { SvgOptions } from './svg.js';
7
9
  export { detectCapabilities, detectColorDepth, detectUnicode, applyOverrides, describeEnvironment, } from './capabilities.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,YAAY,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AACnD,OAAO,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AACvC,YAAY,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAC3C,OAAO,EACL,kBAAkB,EAAE,gBAAgB,EAAE,aAAa,EACnD,cAAc,EAAE,mBAAmB,GACpC,MAAM,mBAAmB,CAAC;AAC3B,YAAY,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AACzD,YAAY,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AACjD,OAAO,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAC;AACpE,YAAY,EAAE,kBAAkB,EAAE,aAAa,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AACnG,OAAO,EAAE,sBAAsB,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAC;AAC7E,YAAY,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,YAAY,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AACnD,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACzC,YAAY,EAAE,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AACtD,OAAO,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AACvC,YAAY,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAC3C,OAAO,EACL,kBAAkB,EAAE,gBAAgB,EAAE,aAAa,EACnD,cAAc,EAAE,mBAAmB,GACpC,MAAM,mBAAmB,CAAC;AAC3B,YAAY,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AACzD,YAAY,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AACjD,OAAO,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAC;AACpE,YAAY,EAAE,kBAAkB,EAAE,aAAa,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AACnG,OAAO,EAAE,sBAAsB,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAC;AAC7E,YAAY,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAC"}
package/dist/index.js CHANGED
@@ -1,6 +1,7 @@
1
1
  export * as ansi from './ansi.js';
2
2
  export { Writer, createWriter } from './writer.js';
3
3
  export { captureBuffer } from './capture.js';
4
+ export { renderStill } from './still.js';
4
5
  export { bufferToSvg } from './svg.js';
5
6
  export { detectCapabilities, detectColorDepth, detectUnicode, applyOverrides, describeEnvironment, } from './capabilities.js';
6
7
  export { InputDecoder, createDecoder } from './input.js';
@@ -0,0 +1,71 @@
1
+ import type { CapabilityOverrides, CellBuffer, CreateAppOptions, TextUIApp } from '@textui/core';
2
+ import type { CaptureOptions } from './capture.js';
3
+ /**
4
+ * One frame of an application, as text.
5
+ *
6
+ * The same application, not a second rendering path: it is mounted against a
7
+ * terminal that is a size and nothing else, rendered until it stops changing,
8
+ * and every cell it painted is what comes back. Which is what a program does
9
+ * when its output is a pipe rather than a screen - there is no frame after
10
+ * this one to correct it, so it has to be the finished picture.
11
+ *
12
+ * Every example in this repository had written this out by hand, and all of
13
+ * them ended the same way: a sleep loop of four milliseconds times a number
14
+ * somebody had tried until the picture looked right. Eight, mostly - four in
15
+ * one, twelve in another - for the same eight lines of setup either side. The
16
+ * number was the tell. Nobody knew it, a small one silently writes a
17
+ * half-drawn frame, and `TextUIApp.settled` is the answer they were all
18
+ * approximating.
19
+ */
20
+ export interface StillOptions extends Omit<CreateAppOptions, 'terminal' | 'session'> {
21
+ /** Columns. The terminal is only a size, so this is the whole of it. */
22
+ width?: number;
23
+ height?: number;
24
+ /** What the terminal should claim to be able to do. */
25
+ capabilities?: CapabilityOverrides;
26
+ /**
27
+ * Drive it before the frame is taken.
28
+ *
29
+ * A still of an application in its opening state is the least interesting
30
+ * one. This is where a screen is pushed, a message sent, or a scripted host
31
+ * pumped to the point worth photographing - and it may be async, because
32
+ * most of those are.
33
+ */
34
+ before?(app: TextUIApp): void | Promise<void>;
35
+ /**
36
+ * The frame is drawn and the application is still alive.
37
+ *
38
+ * For what a string cannot carry and `stop` would take away: the buffer, to
39
+ * crop it to the rows that were used, and the theme, whose two colours are
40
+ * the honest background for a picture of *this* screen rather than the
41
+ * exporter's guess. It runs before the capture, so a buffer changed here is
42
+ * the buffer that comes back as `text`.
43
+ */
44
+ after?(app: TextUIApp): void | Promise<void>;
45
+ /** Plain text or SGR, and at what depth. Defaults to what the terminal claims. */
46
+ capture?: CaptureOptions;
47
+ /** Settle passes before giving up. See `TextUIApp.settled`. */
48
+ settleLimit?: number;
49
+ }
50
+ export interface Still {
51
+ /** Every cell it painted, rows separated by newlines. */
52
+ text: string;
53
+ /**
54
+ * The cells themselves, for anything text cannot carry - `bufferToSvg`, a
55
+ * pixel diff, an assertion about one cell's colour. Valid after the
56
+ * application has stopped: nothing writes to it again.
57
+ */
58
+ buffer: CellBuffer;
59
+ /**
60
+ * Whether it went quiet, or the limit ran out first.
61
+ *
62
+ * An application that animates settles between its frames, so this is
63
+ * `true` for one of those and the picture is a photograph of something
64
+ * moving. `false` means the passes never stopped producing work - a render
65
+ * loop that does not converge - and the frame was taken anyway, because a
66
+ * still is better evidence of that than nothing is.
67
+ */
68
+ settled: boolean;
69
+ }
70
+ export declare function renderStill(options?: StillOptions): Promise<Still>;
71
+ //# sourceMappingURL=still.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"still.d.ts","sourceRoot":"","sources":["../src/still.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,mBAAmB,EAAE,UAAU,EAAE,gBAAgB,EAAE,SAAS,EAC7D,MAAM,cAAc,CAAC;AAKtB,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAEnD;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,WAAW,YAAa,SAAQ,IAAI,CAAC,gBAAgB,EAAE,UAAU,GAAG,SAAS,CAAC;IAClF,wEAAwE;IACxE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,uDAAuD;IACvD,YAAY,CAAC,EAAE,mBAAmB,CAAC;IACnC;;;;;;;OAOG;IACH,MAAM,CAAC,CAAC,GAAG,EAAE,SAAS,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9C;;;;;;;;OAQG;IACH,KAAK,CAAC,CAAC,GAAG,EAAE,SAAS,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7C,kFAAkF;IAClF,OAAO,CAAC,EAAE,cAAc,CAAC;IACzB,+DAA+D;IAC/D,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,KAAK;IACpB,yDAAyD;IACzD,IAAI,EAAE,MAAM,CAAC;IACb;;;;OAIG;IACH,MAAM,EAAE,UAAU,CAAC;IACnB;;;;;;;;OAQG;IACH,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,wBAAsB,WAAW,CAAC,OAAO,GAAE,YAAiB,GAAG,OAAO,CAAC,KAAK,CAAC,CAgC5E"}
package/dist/still.js ADDED
@@ -0,0 +1,29 @@
1
+ import { WRITER_KEY, createApp } from '@textui/core';
2
+ import { createVirtualTerminal } from './virtual.js';
3
+ import { createWriter } from './writer.js';
4
+ import { captureBuffer } from './capture.js';
5
+ export async function renderStill(options = {}) {
6
+ const { width = 80, height = 24, capabilities, before, after, capture, settleLimit, ...app } = options;
7
+ const terminal = createVirtualTerminal({
8
+ width,
9
+ height,
10
+ ...(capabilities ? { capabilities } : {}),
11
+ });
12
+ const created = createApp({ ...app, terminal });
13
+ // The writer is provided even though the buffer is what is read: a virtual
14
+ // terminal records what was written to it, and a still that quietly stopped
15
+ // producing that would break anything reading the bytes rather than the
16
+ // cells.
17
+ created.services.provide(WRITER_KEY, createWriter(terminal.capabilities()));
18
+ await created.start();
19
+ await before?.(created);
20
+ const settled = await created.settled(settleLimit === undefined ? {} : { limit: settleLimit });
21
+ // Even when it did not settle: the limit means "stop waiting", not "give up
22
+ // on the frame", and a still of a moving thing is still a still.
23
+ created.flush();
24
+ await after?.(created);
25
+ const text = captureBuffer(created.buffer(), terminal.capabilities(), capture ?? {});
26
+ const buffer = created.buffer();
27
+ await created.stop();
28
+ return { text, buffer, settled };
29
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@textui/terminal",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "Terminal adapters, capability detection, ANSI writing and input decoding for TextUI",
5
5
  "keywords": [
6
6
  "terminal",
@@ -54,7 +54,7 @@
54
54
  "test": "vitest run"
55
55
  },
56
56
  "dependencies": {
57
- "@textui/core": "^0.2.0"
57
+ "@textui/core": "^0.3.0"
58
58
  },
59
59
  "publishConfig": {
60
60
  "access": "public"
package/src/index.ts CHANGED
@@ -2,6 +2,8 @@ export * as ansi from './ansi.js';
2
2
  export { Writer, createWriter } from './writer.js';
3
3
  export { captureBuffer } from './capture.js';
4
4
  export type { CaptureOptions } from './capture.js';
5
+ export { renderStill } from './still.js';
6
+ export type { Still, StillOptions } from './still.js';
5
7
  export { bufferToSvg } from './svg.js';
6
8
  export type { SvgOptions } from './svg.js';
7
9
  export {
package/src/still.ts ADDED
@@ -0,0 +1,111 @@
1
+ import type {
2
+ CapabilityOverrides, CellBuffer, CreateAppOptions, TextUIApp,
3
+ } from '@textui/core';
4
+ import { WRITER_KEY, createApp } from '@textui/core';
5
+ import { createVirtualTerminal } from './virtual.js';
6
+ import { createWriter } from './writer.js';
7
+ import { captureBuffer } from './capture.js';
8
+ import type { CaptureOptions } from './capture.js';
9
+
10
+ /**
11
+ * One frame of an application, as text.
12
+ *
13
+ * The same application, not a second rendering path: it is mounted against a
14
+ * terminal that is a size and nothing else, rendered until it stops changing,
15
+ * and every cell it painted is what comes back. Which is what a program does
16
+ * when its output is a pipe rather than a screen - there is no frame after
17
+ * this one to correct it, so it has to be the finished picture.
18
+ *
19
+ * Every example in this repository had written this out by hand, and all of
20
+ * them ended the same way: a sleep loop of four milliseconds times a number
21
+ * somebody had tried until the picture looked right. Eight, mostly - four in
22
+ * one, twelve in another - for the same eight lines of setup either side. The
23
+ * number was the tell. Nobody knew it, a small one silently writes a
24
+ * half-drawn frame, and `TextUIApp.settled` is the answer they were all
25
+ * approximating.
26
+ */
27
+ export interface StillOptions extends Omit<CreateAppOptions, 'terminal' | 'session'> {
28
+ /** Columns. The terminal is only a size, so this is the whole of it. */
29
+ width?: number;
30
+ height?: number;
31
+ /** What the terminal should claim to be able to do. */
32
+ capabilities?: CapabilityOverrides;
33
+ /**
34
+ * Drive it before the frame is taken.
35
+ *
36
+ * A still of an application in its opening state is the least interesting
37
+ * one. This is where a screen is pushed, a message sent, or a scripted host
38
+ * pumped to the point worth photographing - and it may be async, because
39
+ * most of those are.
40
+ */
41
+ before?(app: TextUIApp): void | Promise<void>;
42
+ /**
43
+ * The frame is drawn and the application is still alive.
44
+ *
45
+ * For what a string cannot carry and `stop` would take away: the buffer, to
46
+ * crop it to the rows that were used, and the theme, whose two colours are
47
+ * the honest background for a picture of *this* screen rather than the
48
+ * exporter's guess. It runs before the capture, so a buffer changed here is
49
+ * the buffer that comes back as `text`.
50
+ */
51
+ after?(app: TextUIApp): void | Promise<void>;
52
+ /** Plain text or SGR, and at what depth. Defaults to what the terminal claims. */
53
+ capture?: CaptureOptions;
54
+ /** Settle passes before giving up. See `TextUIApp.settled`. */
55
+ settleLimit?: number;
56
+ }
57
+
58
+ export interface Still {
59
+ /** Every cell it painted, rows separated by newlines. */
60
+ text: string;
61
+ /**
62
+ * The cells themselves, for anything text cannot carry - `bufferToSvg`, a
63
+ * pixel diff, an assertion about one cell's colour. Valid after the
64
+ * application has stopped: nothing writes to it again.
65
+ */
66
+ buffer: CellBuffer;
67
+ /**
68
+ * Whether it went quiet, or the limit ran out first.
69
+ *
70
+ * An application that animates settles between its frames, so this is
71
+ * `true` for one of those and the picture is a photograph of something
72
+ * moving. `false` means the passes never stopped producing work - a render
73
+ * loop that does not converge - and the frame was taken anyway, because a
74
+ * still is better evidence of that than nothing is.
75
+ */
76
+ settled: boolean;
77
+ }
78
+
79
+ export async function renderStill(options: StillOptions = {}): Promise<Still> {
80
+ const {
81
+ width = 80, height = 24, capabilities, before, after, capture, settleLimit, ...app
82
+ } = options;
83
+
84
+ const terminal = createVirtualTerminal({
85
+ width,
86
+ height,
87
+ ...(capabilities ? { capabilities } : {}),
88
+ });
89
+
90
+ const created = createApp({ ...app, terminal });
91
+ // The writer is provided even though the buffer is what is read: a virtual
92
+ // terminal records what was written to it, and a still that quietly stopped
93
+ // producing that would break anything reading the bytes rather than the
94
+ // cells.
95
+ created.services.provide(WRITER_KEY, createWriter(terminal.capabilities()));
96
+
97
+ await created.start();
98
+ await before?.(created);
99
+
100
+ const settled = await created.settled(settleLimit === undefined ? {} : { limit: settleLimit });
101
+ // Even when it did not settle: the limit means "stop waiting", not "give up
102
+ // on the frame", and a still of a moving thing is still a still.
103
+ created.flush();
104
+ await after?.(created);
105
+
106
+ const text = captureBuffer(created.buffer(), terminal.capabilities(), capture ?? {});
107
+ const buffer = created.buffer();
108
+ await created.stop();
109
+
110
+ return { text, buffer, settled };
111
+ }