@travetto/terminal 3.0.3 → 3.1.0-rc.1
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 +4 -4
- package/package.json +1 -1
- package/src/color-output.ts +2 -2
- package/src/operation.ts +27 -3
- package/src/query.ts +32 -33
- package/src/terminal.ts +4 -4
- package/src/types.ts +1 -1
- package/src/writer.ts +11 -5
package/README.md
CHANGED
|
@@ -38,12 +38,12 @@ const tplFn = GlobalTerminal.templateFunction({
|
|
|
38
38
|
path: 'teal',
|
|
39
39
|
success: 'green',
|
|
40
40
|
failure: 'red',
|
|
41
|
-
param: 'yellow',
|
|
41
|
+
param: ['yellow', 'goldenrod'],
|
|
42
42
|
type: 'cyan',
|
|
43
|
-
description: 'white',
|
|
44
|
-
title: 'brightWhite',
|
|
43
|
+
description: ['white', 'gray'],
|
|
44
|
+
title: ['brightWhite', 'black'],
|
|
45
45
|
identifier: 'dodgerBlue',
|
|
46
|
-
subtitle: 'lightGray',
|
|
46
|
+
subtitle: ['lightGray', 'darkGray'],
|
|
47
47
|
subsubtitle: 'darkGray'
|
|
48
48
|
});
|
|
49
49
|
|
package/package.json
CHANGED
package/src/color-output.ts
CHANGED
|
@@ -9,7 +9,7 @@ export type TermStyle =
|
|
|
9
9
|
|
|
10
10
|
export type TermStyleInput = TermStyle | RGBInput;
|
|
11
11
|
export type Prim = string | number | boolean | Date | RegExp;
|
|
12
|
-
export type TermColorPaletteInput = Record<string, TermStyleInput | [TermStyleInput, TermStyleInput]>;
|
|
12
|
+
export type TermColorPaletteInput = Record<string, TermStyleInput | [dark: TermStyleInput, light: TermStyleInput]>;
|
|
13
13
|
export type TermColorFn = (text: Prim) => string;
|
|
14
14
|
export type TermColorPalette<T> = Record<keyof T, TermColorFn>;
|
|
15
15
|
|
|
@@ -92,7 +92,7 @@ export class ColorOutputUtil {
|
|
|
92
92
|
/**
|
|
93
93
|
* Make a simple primitive colorer
|
|
94
94
|
*/
|
|
95
|
-
static colorer(term: TermState, style: TermStyleInput | [
|
|
95
|
+
static colorer(term: TermState, style: TermStyleInput | [dark: TermStyleInput, light: TermStyleInput]): TermColorFn {
|
|
96
96
|
const schemes = {
|
|
97
97
|
light: this.getStyledLevels(Array.isArray(style) ? style[1] ?? style[0] : style),
|
|
98
98
|
dark: this.getStyledLevels(Array.isArray(style) ? style[0] : style),
|
package/src/operation.ts
CHANGED
|
@@ -5,8 +5,18 @@ import { ColorOutputUtil, TermStyleInput } from './color-output';
|
|
|
5
5
|
|
|
6
6
|
const STD_WAIT_STATES = '⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏'.split('');
|
|
7
7
|
|
|
8
|
+
/**
|
|
9
|
+
* Standard Terminal Operations
|
|
10
|
+
*/
|
|
8
11
|
export class TerminalOperation {
|
|
9
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
|
+
|
|
10
20
|
/**
|
|
11
21
|
* Allows for writing at top, bottom, or current position while new text is added
|
|
12
22
|
*/
|
|
@@ -32,9 +42,23 @@ export class TerminalOperation {
|
|
|
32
42
|
}
|
|
33
43
|
await batch.commit();
|
|
34
44
|
|
|
45
|
+
let start = Date.now();
|
|
46
|
+
const minDelay = config.minDelay ?? 0;
|
|
47
|
+
|
|
48
|
+
let line: string = '';
|
|
35
49
|
for await (const text of source) {
|
|
36
|
-
|
|
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
|
+
}
|
|
37
56
|
}
|
|
57
|
+
|
|
58
|
+
if (line) {
|
|
59
|
+
await TerminalWriter.for(term).setPosition(writePos).write(line).clearLine(1).commit(true);
|
|
60
|
+
}
|
|
61
|
+
|
|
38
62
|
if (config.clearOnFinish ?? true) {
|
|
39
63
|
await TerminalWriter.for(term).setPosition(writePos).clearLine().commit(true);
|
|
40
64
|
}
|
|
@@ -90,7 +114,7 @@ export class TerminalOperation {
|
|
|
90
114
|
.replace(/%idx/, idxStr)
|
|
91
115
|
.replace(/%total/, totalStr)
|
|
92
116
|
.replace(/%pct/, `${Math.trunc(pct * 100)}`);
|
|
93
|
-
const full = ` ${line}`.padEnd(term.width);
|
|
117
|
+
const full = this.truncateIfNeeded(term, ` ${line}`.padEnd(term.width));
|
|
94
118
|
const mid = Math.trunc(pct * term.width);
|
|
95
119
|
const [l, r] = [full.substring(0, mid), full.substring(mid)];
|
|
96
120
|
return `${color(l)}${r}`;
|
|
@@ -125,7 +149,7 @@ export class TerminalOperation {
|
|
|
125
149
|
if (msg !== undefined) {
|
|
126
150
|
msg = msg.replace(/\n$/, '');
|
|
127
151
|
pos = await term.getCursorPosition();
|
|
128
|
-
writer = this.streamWaiting(term, msg, { ...cfg, at: pos, clearOnFinish: false });
|
|
152
|
+
writer = this.streamWaiting(term, this.truncateIfNeeded(term, msg), { ...cfg, at: pos, clearOnFinish: false });
|
|
129
153
|
line = msg;
|
|
130
154
|
}
|
|
131
155
|
}
|
package/src/query.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import tty from 'tty';
|
|
1
|
+
import type tty from 'tty';
|
|
2
|
+
import { spawn } from 'child_process';
|
|
2
3
|
|
|
3
4
|
import { ANSICodes } from './codes';
|
|
4
5
|
import { IterableUtil } from './iterable';
|
|
@@ -7,6 +8,31 @@ import { RGB, TermCoord, TermQuery } from './types';
|
|
|
7
8
|
const to256 = (x: string): number => Math.trunc(parseInt(x, 16) / (16 ** (x.length - 2)));
|
|
8
9
|
const COLOR_RESPONSE = /(?<r>][0-9a-f]+)[/](?<g>[0-9a-f]+)[/](?<b>[0-9a-f]+)[/]?(?<a>[0-9a-f]+)?/i;
|
|
9
10
|
|
|
11
|
+
// @ts-expect-error
|
|
12
|
+
const queryScript = (function (...bytes) {
|
|
13
|
+
const i = process.stdin;
|
|
14
|
+
i.setRawMode(true);
|
|
15
|
+
i.resume();
|
|
16
|
+
i.once('readable', function () {
|
|
17
|
+
const inp = i.read();
|
|
18
|
+
/* @ts-expect-error */
|
|
19
|
+
process.send(Buffer.isBuffer(inp) ? inp.toString('utf8') : inp);
|
|
20
|
+
i.setRawMode(false);
|
|
21
|
+
});
|
|
22
|
+
process.stdout.write(String.fromCharCode(...bytes));
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
const runQuery = async (input: tty.ReadStream, output: tty.WriteStream, code: string): Promise<Buffer> => {
|
|
26
|
+
const script = queryScript.toString().replaceAll('\'', '"').replaceAll('\n', '');
|
|
27
|
+
const fullScript = `(${script})(${code.split('').map(x => x.charCodeAt(0))})`
|
|
28
|
+
const proc = spawn(process.argv0, ['-e', fullScript], { stdio: [input, output, 2, 'ipc'], detached: true });
|
|
29
|
+
const text = await new Promise<string>((res, rej) => {
|
|
30
|
+
proc.once('message', res);
|
|
31
|
+
proc.on('error', rej);
|
|
32
|
+
});
|
|
33
|
+
return Buffer.from(text, 'utf8');
|
|
34
|
+
};
|
|
35
|
+
|
|
10
36
|
const ANSIQueries = {
|
|
11
37
|
/** Parse xterm color response */
|
|
12
38
|
color: (field: 'background' | 'foreground'): TermQuery<RGB | undefined> => ({
|
|
@@ -43,43 +69,15 @@ export class TerminalQuerier {
|
|
|
43
69
|
#queue = IterableUtil.simpleQueue();
|
|
44
70
|
#output: tty.WriteStream;
|
|
45
71
|
#input: tty.ReadStream;
|
|
72
|
+
#restore?: () => void;
|
|
46
73
|
|
|
47
74
|
constructor(input: tty.ReadStream, output: tty.WriteStream) {
|
|
48
75
|
this.#input = input;
|
|
49
76
|
this.#output = output;
|
|
50
77
|
}
|
|
51
78
|
|
|
52
|
-
async #readInput(query: string): Promise<Buffer> {
|
|
53
|
-
const isRaw = this.#input.isRaw;
|
|
54
|
-
const isPaused = this.#input.isPaused();
|
|
55
|
-
const data = this.#input.listeners('data');
|
|
56
|
-
try {
|
|
57
|
-
this.#input.removeAllListeners('data');
|
|
58
|
-
|
|
59
|
-
if (isPaused) {
|
|
60
|
-
this.#input.resume();
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
this.#input.setRawMode(true);
|
|
64
|
-
// Send data, but do not wait on it
|
|
65
|
-
this.#output.write(query);
|
|
66
|
-
await new Promise(res => this.#input.once('readable', res));
|
|
67
|
-
const val: Buffer | string = this.#input.read();
|
|
68
|
-
return typeof val === 'string' ? Buffer.from(val, 'utf8') : val;
|
|
69
|
-
} finally {
|
|
70
|
-
if (isPaused) {
|
|
71
|
-
this.#input.pause();
|
|
72
|
-
}
|
|
73
|
-
this.#input.setRawMode(isRaw);
|
|
74
|
-
for (const fn of data) {
|
|
75
|
-
// @ts-ignore
|
|
76
|
-
this.#input.on('data', fn);
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
|
|
81
79
|
query<T>(q: TermQuery<T>): Promise<T> {
|
|
82
|
-
return this.#queue.add(() => this.#
|
|
80
|
+
return this.#queue.add(() => runQuery(this.#input, this.#output, q.query()).then(q.response));
|
|
83
81
|
}
|
|
84
82
|
|
|
85
83
|
cursorPosition(): Promise<TermCoord> {
|
|
@@ -90,7 +88,8 @@ export class TerminalQuerier {
|
|
|
90
88
|
return this.query(ANSIQueries.color('background'));
|
|
91
89
|
}
|
|
92
90
|
|
|
93
|
-
close():
|
|
94
|
-
|
|
91
|
+
close(): void {
|
|
92
|
+
this.#restore?.();
|
|
93
|
+
this.#queue.close();
|
|
95
94
|
}
|
|
96
95
|
}
|
package/src/terminal.ts
CHANGED
|
@@ -13,6 +13,7 @@ import { ColorOutputUtil, Prim, TermColorFn, TermColorPalette, TermColorPaletteI
|
|
|
13
13
|
type TerminalStreamPositionConfig = {
|
|
14
14
|
position?: TermLinePosition;
|
|
15
15
|
staticMessage?: string;
|
|
16
|
+
minDelay?: number;
|
|
16
17
|
};
|
|
17
18
|
|
|
18
19
|
type TerminalProgressConfig = TerminalStreamPositionConfig & {
|
|
@@ -98,12 +99,11 @@ export class Terminal implements TermState {
|
|
|
98
99
|
return this.#init;
|
|
99
100
|
}
|
|
100
101
|
|
|
101
|
-
|
|
102
|
-
|
|
102
|
+
reset(): void {
|
|
103
|
+
this.#query.close();
|
|
103
104
|
if (this.interactive) {
|
|
104
|
-
|
|
105
|
+
this.#output.write(this.writer().resetCommands());
|
|
105
106
|
}
|
|
106
|
-
return;
|
|
107
107
|
}
|
|
108
108
|
|
|
109
109
|
getCursorPosition(): Promise<TermCoord> {
|
package/src/types.ts
CHANGED
|
@@ -13,7 +13,7 @@ export type TerminalTableEvent = { idx: number, text: string, done?: boolean };
|
|
|
13
13
|
export type TerminalTableConfig = { header?: string[], forceNonInteractiveOrder?: boolean };
|
|
14
14
|
export type TerminalProgressEvent = { idx: number, total?: number, text?: string };
|
|
15
15
|
export type TerminalProgressRender = (ev: TerminalProgressEvent) => string;
|
|
16
|
-
export type TerminalStreamingConfig = { position?: TermLinePosition, clearOnFinish?: boolean, at?: TermCoord };
|
|
16
|
+
export type TerminalStreamingConfig = { position?: TermLinePosition, clearOnFinish?: boolean, at?: TermCoord, minDelay?: number };
|
|
17
17
|
export type TerminalWaitingConfig = { end?: boolean, committedPrefix?: string } & TerminalStreamingConfig & DelayedConfig;
|
|
18
18
|
|
|
19
19
|
export type TermColorLevel = 0 | 1 | 2 | 3;
|
package/src/writer.ts
CHANGED
|
@@ -137,10 +137,16 @@ export class TerminalWriter {
|
|
|
137
137
|
|
|
138
138
|
/** Reset */
|
|
139
139
|
reset(): this {
|
|
140
|
-
return this
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
140
|
+
return this.write(this.resetCommands());
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/** Reset Commands */
|
|
144
|
+
resetCommands(): string {
|
|
145
|
+
return [
|
|
146
|
+
ANSICodes.POSITION_SAVE(),
|
|
147
|
+
ANSICodes.SHOW_CURSOR(),
|
|
148
|
+
ANSICodes.SCROLL_RANGE_CLEAR(),
|
|
149
|
+
ANSICodes.POSITION_RESTORE()
|
|
150
|
+
].join('');
|
|
145
151
|
}
|
|
146
152
|
}
|