@travetto/terminal 3.4.0-rc.0 → 4.0.0-rc.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.
@@ -1,160 +0,0 @@
1
- export const NAMED_COLORS = {
2
- // Name: RGB
3
- stdBlack: 0x000000,
4
- stdRed: 0xcd0000,
5
- stdGreen: 0x00cd00,
6
- stdYellow: 0xcdcd00,
7
- stdBlue: 0x0000ee,
8
- stdMagenta: 0xcd00cd,
9
- stdCyan: 0x00cdcd,
10
- stdWhite: 0xe5e5e5,
11
- brightBlack: 0x7f7f7f,
12
- brightRed: 0xff0000,
13
- brightGreen: 0x00fc00,
14
- brightYellow: 0xffff00,
15
- brightBlue: 0x0000fc,
16
- brightMagenta: 0xff00ff,
17
- brightCyan: 0x00ffff,
18
- brightWhite: 0xffffff,
19
-
20
- aliceBlue: 0xf0f8ff,
21
- antiqueWhite: 0xfaebd7,
22
- aqua: 0x00ffff,
23
- aquamarine: 0x7fffd4,
24
- azure: 0xf0ffff,
25
- beige: 0xf5f5dc,
26
- bisque: 0xffe4c4,
27
- black: 0x000000,
28
- blanchedAlmond: 0xffebcd,
29
- blue: 0x0000ff,
30
- blueViolet: 0x8a2be2,
31
- brown: 0xa52a2a,
32
- burlyWood: 0xdeb887,
33
- cadetBlue: 0x5f9ea0,
34
- chartreuse: 0x7fff00,
35
- chocolate: 0xd2691e,
36
- coral: 0xff7f50,
37
- cornflowerBlue: 0x6495ed,
38
- cornSilk: 0xfff8dc,
39
- crimson: 0xdc143c,
40
- cyan: 0x00ffff,
41
- darkBlue: 0x00008b,
42
- darkCyan: 0x008b8b,
43
- darkGoldenrod: 0xb8860b,
44
- darkGray: 0xa9a9a9,
45
- darkGreen: 0x006400,
46
- darkKhaki: 0xbdb76b,
47
- darkMagenta: 0x8b008b,
48
- darkOliveGreen: 0x556b2f,
49
- darkOrange: 0xff8c00,
50
- darkOrchid: 0x9932cc,
51
- darkRed: 0x8b0000,
52
- darkSalmon: 0xe9967a,
53
- darkSeaGreen: 0x8fbc8f,
54
- darkSlateBlue: 0x483d8b,
55
- darkSlateGray: 0x2f4f4f,
56
- darkTurquoise: 0x00ced1,
57
- darkViolet: 0x9400d3,
58
- deepPink: 0xff1493,
59
- deepSkyBlue: 0x00bfff,
60
- dimGray: 0x696969,
61
- dodgerBlue: 0x1e90ff,
62
- firebrick: 0xb22222,
63
- floralWhite: 0xfffaf0,
64
- forestGreen: 0x228b22,
65
- fuchsia: 0xff00ff,
66
- gainsboro: 0xdcdcdc,
67
- ghostWhite: 0xf8f8ff,
68
- gold: 0xffd700,
69
- goldenrod: 0xdaa520,
70
- gray: 0x808080,
71
- green: 0x008000,
72
- greenYellow: 0xadff2f,
73
- honeydew: 0xf0fff0,
74
- hotPink: 0xff69b4,
75
- indianRed: 0xcd5c5c,
76
- indigo: 0x4b0082,
77
- ivory: 0xfffff0,
78
- khaki: 0xf0e68c,
79
- lavender: 0xe6e6fa,
80
- lavenderBlush: 0xfff0f5,
81
- lawnGreen: 0x7cfc00,
82
- lemonChiffon: 0xfffacd,
83
- lightBlue: 0xadd8e6,
84
- lightCoral: 0xf08080,
85
- lightCyan: 0xe0ffff,
86
- lightGoldenRodYellow: 0xfafad2,
87
- lightGray: 0xd3d3d3,
88
- lightGreen: 0x90ee90,
89
- lightPink: 0xffb6c1,
90
- lightSalmon: 0xffa07a,
91
- lightSeaGreen: 0x20b2aa,
92
- lightSkyBlue: 0x87cefa,
93
- lightSlateGray: 0x778899,
94
- lightSteelBlue: 0xb0c4de,
95
- lightYellow: 0xffffe0,
96
- lime: 0x00ff00,
97
- limeGreen: 0x32cd32,
98
- linen: 0xfaf0e6,
99
- magenta: 0xff00ff,
100
- maroon: 0x800000,
101
- mediumAquamarine: 0x66cdaa,
102
- mediumBlue: 0x0000cd,
103
- mediumOrchid: 0xba55d3,
104
- mediumPurple: 0x9370db,
105
- mediumSeaGreen: 0x3cb371,
106
- mediumSlateBlue: 0x7b68ee,
107
- mediumSpringGreen: 0x00fa9a,
108
- mediumTurquoise: 0x48d1cc,
109
- mediumVioletRed: 0xc71585,
110
- midnightBlue: 0x191970,
111
- mintCream: 0xf5fffa,
112
- mistyRose: 0xffe4e1,
113
- moccasin: 0xffe4b5,
114
- navajoWhite: 0xffdead,
115
- navy: 0x000080,
116
- oldLace: 0xfdf5e6,
117
- olive: 0x808000,
118
- oliveDrab: 0x6b8e23,
119
- orange: 0xffa500,
120
- orangeRed: 0xff4500,
121
- orchid: 0xda70d6,
122
- paleGoldenrod: 0xeee8aa,
123
- paleGreen: 0x98fb98,
124
- paleTurquoise: 0xafeeee,
125
- paleVioletRed: 0xdb7093,
126
- papayaWhip: 0xffefd5,
127
- peachPuff: 0xffdab9,
128
- peru: 0xcd853f,
129
- pink: 0xffc0cb,
130
- plum: 0xdda0dd,
131
- powderBlue: 0xb0e0e6,
132
- purple: 0x800080,
133
- red: 0xff0000,
134
- rosyBrown: 0xbc8f8f,
135
- royalBlue: 0x4169e1,
136
- saddleBrown: 0x8b4513,
137
- salmon: 0xfa8072,
138
- sandyBrown: 0xf4a460,
139
- seaGreen: 0x2e8b57,
140
- seashell: 0xfff5ee,
141
- sienna: 0xa0522d,
142
- silver: 0xc0c0c0,
143
- skyBlue: 0x87ceeb,
144
- slateBlue: 0x6a5acd,
145
- slateGray: 0x708090,
146
- snow: 0xfffafa,
147
- springGreen: 0x00ff7f,
148
- steelBlue: 0x4682b4,
149
- tan: 0xd2b48c,
150
- teal: 0x008080,
151
- thistle: 0xd8bfd8,
152
- tomato: 0xff6347,
153
- turquoise: 0x40e0d0,
154
- violet: 0xee82ee,
155
- wheat: 0xf5deb3,
156
- white: 0xffffff,
157
- whitesmoke: 0xf5f5f5,
158
- yellow: 0xffff00,
159
- yellowGreen: 0x9acd32,
160
- } as const;
package/src/operation.ts DELETED
@@ -1,158 +0,0 @@
1
- import { IterableUtil } from './iterable';
2
- import { TerminalWriter } from './writer';
3
- import { Indexed, TerminalProgressRender, TerminalStreamingConfig, TerminalWaitingConfig, TermState } from './types';
4
- import { ColorOutputUtil, TermStyleInput } from './color-output';
5
-
6
- const STD_WAIT_STATES = '⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏'.split('');
7
-
8
- /**
9
- * Standard Terminal Operations
10
- */
11
- export class TerminalOperation {
12
-
13
- static truncateIfNeeded(term: TermState, text: string, suffix = '...'): string {
14
- if (text.length > term.width) {
15
- return `${text.substring(0, term.width - suffix.length)}${suffix}`;
16
- }
17
- return text;
18
- }
19
-
20
- /**
21
- * Allows for writing at top, bottom, or current position while new text is added
22
- */
23
- static async streamToPosition(term: TermState, source: AsyncIterable<string>, config: TerminalStreamingConfig = {}): Promise<void> {
24
- const curPos = config.at ?? { ...await term.getCursorPosition() };
25
- const pos = config.position ?? 'inline';
26
-
27
- const writePos = pos === 'inline' ?
28
- { ...curPos, x: 0 } :
29
- { x: 0, y: pos === 'top' ? 0 : -1 };
30
-
31
- try {
32
- const batch = TerminalWriter.for(term).hideCursor();
33
- if (pos !== 'inline') {
34
- batch.storePosition().scrollRange(pos === 'top' ? { start: 2 } : { end: -2 }).restorePosition();
35
- if (pos === 'top' && curPos.y === 0) {
36
- batch.changePosition({ y: 1 }).write('');
37
- } else if (pos === 'bottom' && curPos.y === term.height - 1) {
38
- batch.changePosition({ y: -1 }).write('\n');
39
- }
40
- } else {
41
- batch.write('\n'); // Move past line
42
- }
43
- await batch.commit();
44
-
45
- let start = Date.now();
46
- const minDelay = config.minDelay ?? 0;
47
-
48
- let line: string = '';
49
- for await (const text of source) {
50
- line = text;
51
- if ((Date.now() - start) >= minDelay) {
52
- start = Date.now();
53
- await TerminalWriter.for(term).setPosition(writePos).write(line).clearLine(1).commit(true);
54
- line = '';
55
- }
56
- }
57
-
58
- if (line) {
59
- await TerminalWriter.for(term).setPosition(writePos).write(line).clearLine(1).commit(true);
60
- }
61
-
62
- if (config.clearOnFinish ?? true) {
63
- await TerminalWriter.for(term).setPosition(writePos).clearLine().commit(true);
64
- }
65
- } finally {
66
- const finalCursor = await term.getCursorPosition();
67
- await TerminalWriter.for(term).scrollRangeClear().setPosition(finalCursor).showCursor().commit();
68
- }
69
- }
70
-
71
- /**
72
- * Consumes a stream, of events, tied to specific list indices, and updates in place
73
- */
74
- static async streamList(term: TermState, source: AsyncIterable<Indexed & { text: string }>): Promise<void> {
75
- let max = 0;
76
- try {
77
- await TerminalWriter.for(term).hideCursor().commit();
78
- for await (const { idx, text } of source) {
79
- max = Math.max(idx, max);
80
- await TerminalWriter.for(term).write('\n'.repeat(idx)).rewriteLine(text).clearLine(1).changePosition({ y: -idx }).commit();
81
- }
82
- } finally {
83
- await TerminalWriter.for(term).changePosition({ y: max + 1 }).writeLine('\n').showCursor().commit();
84
- }
85
- }
86
-
87
- /**
88
- * Waiting indicator, streamed to a specific position, can be canceled
89
- */
90
- static streamWaiting(term: TermState, message: string, config: TerminalWaitingConfig = {}): () => Promise<void> {
91
- const { stop, stream } = IterableUtil.cycle(STD_WAIT_STATES);
92
- const indicator = IterableUtil.map(
93
- stream,
94
- IterableUtil.DELAY(config),
95
- (ch, i) => config.end ? `${message} ${ch}` : `${ch} ${message}`
96
- );
97
-
98
- const final = this.streamToPosition(term, indicator, config);
99
- return async () => { stop(); return final; };
100
- }
101
-
102
- /**
103
- * Build progress par formatter for terminal progress events
104
- */
105
- static buildProgressBar(term: TermState, style: TermStyleInput): TerminalProgressRender {
106
- const color = ColorOutputUtil.colorer(term, style);
107
- return ({ total, idx, text }): string => {
108
- text ||= total ? '%idx/%total' : '%idx';
109
-
110
- const totalStr = `${total ?? ''}`;
111
- const idxStr = `${idx}`.padStart(totalStr.length);
112
- const pct = total === undefined ? 0 : (idx / total);
113
- const line = text
114
- .replace(/%idx/, idxStr)
115
- .replace(/%total/, totalStr)
116
- .replace(/%pct/, `${Math.trunc(pct * 100)}`);
117
- const full = this.truncateIfNeeded(term, ` ${line}`.padEnd(term.width));
118
- const mid = Math.trunc(pct * term.width);
119
- const [l, r] = [full.substring(0, mid), full.substring(mid)];
120
- return `${color(l)}${r}`;
121
- };
122
- }
123
-
124
- /**
125
- * Stream lines with a waiting indicator
126
- */
127
- static async streamLinesWithWaiting(term: TermState, lines: AsyncIterable<string | undefined>, cfg: TerminalWaitingConfig = {}): Promise<void> {
128
- let writer: (() => Promise<unknown>) | undefined;
129
- let line: string | undefined;
130
-
131
- let pos = await term.getCursorPosition();
132
-
133
-
134
- const commitLine = async (): Promise<void> => {
135
- await writer?.();
136
- if (line) {
137
- const msg = cfg.committedPrefix ? `${cfg.committedPrefix} ${line}` : line;
138
- if (cfg.position === 'inline') {
139
- await TerminalWriter.for(term).setPosition(pos).write(msg).commit(true);
140
- } else {
141
- await TerminalWriter.for(term).writeLine(msg).commit();
142
- }
143
- line = undefined;
144
- }
145
- };
146
-
147
- for await (let msg of lines) {
148
- await commitLine();
149
- if (msg !== undefined) {
150
- msg = msg.replace(/\n$/, '');
151
- pos = await term.getCursorPosition();
152
- writer = this.streamWaiting(term, this.truncateIfNeeded(term, msg), { ...cfg, at: pos, clearOnFinish: false });
153
- line = msg;
154
- }
155
- }
156
- await commitLine();
157
- }
158
- }
package/src/query.ts DELETED
@@ -1,94 +0,0 @@
1
- import type tty from 'tty';
2
- import { spawn } from 'child_process';
3
-
4
- import { ANSICodes } from './codes';
5
- import { IterableUtil } from './iterable';
6
- import { RGB, TermCoord, TermQuery } from './types';
7
-
8
- const to256 = (x: string): number => Math.trunc(parseInt(x, 16) / (16 ** (x.length - 2)));
9
- const COLOR_RESPONSE = /(?<r>][0-9a-f]+)[/](?<g>[0-9a-f]+)[/](?<b>[0-9a-f]+)[/]?(?<a>[0-9a-f]+)?/i;
10
-
11
- const queryScript = (function (...bytes: number[]): void {
12
- const i = process.stdin;
13
- i.setRawMode(true);
14
- i.resume();
15
- i.once('readable', function () {
16
- const inp = i.read();
17
- /* @ts-expect-error */
18
- process.send(Buffer.isBuffer(inp) ? inp.toString('utf8') : inp);
19
- i.setRawMode(false);
20
- });
21
- process.stdout.write(String.fromCharCode(...bytes));
22
- });
23
-
24
- const runQuery = async (input: tty.ReadStream, output: tty.WriteStream, code: string): Promise<Buffer> => {
25
- const script = queryScript.toString().replaceAll('\'', '"').replaceAll('\n', '');
26
- const fullScript = `(${script})(${code.split('').map(x => x.charCodeAt(0))})`;
27
- const proc = spawn(process.argv0, ['-e', fullScript], { stdio: [input, output, 2, 'ipc'], detached: true });
28
- const text = await new Promise<string>((res, rej) => {
29
- proc.once('message', res);
30
- proc.on('error', rej);
31
- });
32
- return Buffer.from(text, 'utf8');
33
- };
34
-
35
- const ANSIQueries = {
36
- /** Parse xterm color response */
37
- color: (field: 'background' | 'foreground'): TermQuery<RGB | undefined> => ({
38
- query: (): string => ANSICodes.OSC_QUERY(`${field}Color`),
39
- response: (response: Buffer): RGB | undefined => {
40
- const groups = response.toString('utf8').match(COLOR_RESPONSE)?.groups ?? {};
41
- return 'r' in groups ? [to256(groups.r), to256(groups.g), to256(groups.b)] : undefined;
42
- }
43
- }),
44
- /** Parse cursor query response into {x,y} */
45
- cursorPosition: {
46
- query: (): string => ANSICodes.DEVICE_STATUS_REPORT('cursorPosition'),
47
- response: (response: Buffer): TermCoord => {
48
- const groups = response.toString('utf8').match(/(?<r>\d*);(?<c>\d*)/)?.groups ?? {};
49
- return 'c' in groups ? { x: +(groups.c) - 1, y: +(groups.r) - 1 } : { x: 0, y: 0 };
50
- }
51
- }
52
- };
53
-
54
- /**
55
- * Terminal query support with centralized queuing for multiple writes to the same stream
56
- */
57
- export class TerminalQuerier {
58
-
59
- static #cache = new Map<tty.ReadStream, TerminalQuerier>();
60
-
61
- static for(input: tty.ReadStream, output: tty.WriteStream): TerminalQuerier {
62
- if (!this.#cache.has(input)) {
63
- this.#cache.set(input, new TerminalQuerier(input, output));
64
- }
65
- return this.#cache.get(input)!;
66
- }
67
-
68
- #queue = IterableUtil.simpleQueue();
69
- #output: tty.WriteStream;
70
- #input: tty.ReadStream;
71
- #restore?: () => void;
72
-
73
- constructor(input: tty.ReadStream, output: tty.WriteStream) {
74
- this.#input = input;
75
- this.#output = output;
76
- }
77
-
78
- query<T>(q: TermQuery<T>): Promise<T> {
79
- return this.#queue.add(() => runQuery(this.#input, this.#output, q.query()).then(q.response));
80
- }
81
-
82
- cursorPosition(): Promise<TermCoord> {
83
- return this.query(ANSIQueries.cursorPosition);
84
- }
85
-
86
- backgroundColor(): Promise<RGB | undefined> {
87
- return this.query(ANSIQueries.color('background'));
88
- }
89
-
90
- close(): void {
91
- this.#restore?.();
92
- this.#queue.close();
93
- }
94
- }
package/src/types.ts DELETED
@@ -1,33 +0,0 @@
1
- import tty from 'tty';
2
-
3
- export type Indexed = { idx: number };
4
- export type DelayedConfig = { initialDelay?: number, cycleDelay?: number };
5
-
6
- type I = number;
7
- export type RGB = [r: I, g: I, b: I] | (readonly [r: I, g: I, b: I]);
8
- export type TermCoord = { x: number, y: number };
9
- export type TermLinePosition = 'top' | 'bottom' | 'inline';
10
- export type TermColorField = 'foregroundColor' | 'backgroundColor';
11
-
12
- export type TerminalTableEvent = { idx: number, text: string, done?: boolean };
13
- export type TerminalTableConfig = { header?: string[], forceNonInteractiveOrder?: boolean };
14
- export type TerminalProgressEvent = { idx: number, total?: number, text?: string };
15
- export type TerminalProgressRender = (ev: TerminalProgressEvent) => string;
16
- export type TerminalStreamingConfig = { position?: TermLinePosition, clearOnFinish?: boolean, at?: TermCoord, minDelay?: number };
17
- export type TerminalWaitingConfig = { end?: boolean, committedPrefix?: string } & TerminalStreamingConfig & DelayedConfig;
18
-
19
- export type TermColorLevel = 0 | 1 | 2 | 3;
20
- export type TermColorScheme = 'dark' | 'light';
21
-
22
- export type TermState = {
23
- interactive: boolean;
24
- height: number;
25
- width: number;
26
- input: tty.ReadStream;
27
- output: tty.WriteStream;
28
- colorLevel: TermColorLevel;
29
- backgroundScheme: TermColorScheme;
30
- getCursorPosition(): Promise<TermCoord>;
31
- };
32
-
33
- export type TermQuery<T> = { query: () => string, response: (inp: Buffer) => T };