@travetto/terminal 3.0.0-rc.9 → 3.0.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 CHANGED
@@ -6,4 +6,84 @@
6
6
  **Install: @travetto/terminal**
7
7
  ```bash
8
8
  npm install @travetto/terminal
9
+
10
+ # or
11
+
12
+ yarn add @travetto/terminal
9
13
  ```
14
+
15
+ This module provides basic support for interacting with the terminal, and provides the basis for output colorization and the basic command line interactions. The functionality can be broken down into:
16
+
17
+
18
+ * Output Colorization
19
+ * Terminal Interactions
20
+
21
+ ## Output Colorization
22
+
23
+ Oddly enough, colorizing output in a terminal is a fairly complex process. The standards are somewhat inconsistent and detection can be a tricky process. For terminals, [Node](https://nodejs.org) supports 4 different levels of coloring:
24
+
25
+ * 0 - One color, essentially uncolored output
26
+ * 1 - Basic color support, 16 colors
27
+ * 2 - Enhanced color support, 225 colors, providing a fair representation of most colors
28
+ * 3 - True color, 24bit color with R, G, B each getting 8-bits. Can represent any color needed
29
+
30
+ This module provides the ability to define color palettes using RGB or [named colors](https://github.com/travetto/travetto/tree/main/module/terminal/src/named-colors.ts#L1) modeled after the standard HTML color names. The module also provides the ability to specify palettes based on a dark or light background for a given terminal. Support for this is widespread, but when it fails, it will gracefully assume a dark background.
31
+
32
+ These palettes then are usable at runtime, with the module determine light or dark palettes, as well as falling back to the closest color value based on what the existing terminal supports. This means a color like 'olivegreen', will get the proper output in 24bit color support, a close approximation in enhanced color support, fall back to green in basic color support, and will be color less at level 0.
33
+
34
+ **Code: CLI Color Palette**
35
+ ```typescript
36
+ import { Util } from '@travetto/base';
37
+ import { GlobalTerminal } from '@travetto/terminal';
38
+
39
+ const tplFn = GlobalTerminal.templateFunction({
40
+ input: 'oliveDrab',
41
+ output: 'pink',
42
+ path: 'teal',
43
+ success: 'green',
44
+ failure: 'red',
45
+ param: 'yellow',
46
+ type: 'cyan',
47
+ description: 'white',
48
+ title: 'brightWhite',
49
+ identifier: 'dodgerBlue',
50
+ subtitle: 'lightGray',
51
+ subsubtitle: 'darkGray'
52
+ });
53
+
54
+ export const cliTpl = Util.makeTemplate(tplFn);
55
+ ```
56
+
57
+ When the color palette is combined with [Base](https://github.com/travetto/travetto/tree/main/module/base#readme "Environment config and common utilities for travetto applications.")'s Util.makeTemplate, you produce a string template function that will automatically colorize:
58
+
59
+ **Code: Sample Template Usage**
60
+ ```typescript
61
+ cliTpl`Build finished: status=${{success: "complete"}}, output=${{path: "/build.zip"}}`
62
+ ```
63
+
64
+ This would then produce colorized output based on the palette, and the terminal capabilities.
65
+
66
+ This module follows the pattern [Node](https://nodejs.org) follows with respect to the environment variables: `NO_COLOR`, `FORCE_COLOR` and `NODE_DISABLE_COLORS`
67
+
68
+ **Terminal: Node help on colors**
69
+ ```bash
70
+ $ /usr/bin/node -h | grep -i color
71
+
72
+ FORCE_COLOR when set to 'true', 1, 2, 3, or an
73
+ empty string causes NO_COLOR and
74
+ NODE_DISABLE_COLORS to be ignored.
75
+ NO_COLOR Alias for NODE_DISABLE_COLORS
76
+ NODE_DISABLE_COLORS set to 1 to disable colors in the REPL
77
+ ```
78
+
79
+ ## Terminal Interactions
80
+ Within the [Travetto](https://travetto.dev) framework, there are plenty of command line interactions that are enhanced with additional interactivity. This mainly revolves around indicating progress while a program is executing. The module provides support for:
81
+
82
+
83
+ * Progress Bars
84
+ * Waiting Indicators
85
+ * Streaming Content
86
+
87
+ This is generally meant for use within the framework, and so is highly tailored to the specific needs and scenarios. You can see this pattern play out in the [Compiler](https://github.com/travetto/travetto/tree/main/module/compiler#readme "The compiler infrastructure for the Travetto framework") progress output, or in [Pack](https://github.com/travetto/travetto/tree/main/module/pack#readme "Code packing utilities")'s output.
88
+
89
+ In these scenarios, the dynamic behaviors are dependent on having an interactive TTY. When running without access to a proper stdin, the output will default to basic line printing. This dynamic behavior can also be disabled using the environment variable `TRV_QUIET`. When set to `1` will provide a minimal text-based experience.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@travetto/terminal",
3
- "version": "3.0.0-rc.9",
3
+ "version": "3.0.1",
4
4
  "description": "General terminal support",
5
5
  "keywords": [
6
6
  "terminal",
package/src/operation.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { IterableUtil } from './iterable';
2
2
  import { TerminalWriter } from './writer';
3
- import { Indexed, TermCoord, TerminalProgressRender, TerminalWaitingConfig, TermLinePosition, TermState } from './types';
3
+ import { Indexed, TerminalProgressRender, TerminalStreamingConfig, TerminalWaitingConfig, TermState } from './types';
4
4
  import { ColorOutputUtil, TermStyleInput } from './color-output';
5
5
 
6
6
  const STD_WAIT_STATES = '⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏'.split('');
@@ -10,8 +10,10 @@ export class TerminalOperation {
10
10
  /**
11
11
  * Allows for writing at top, bottom, or current position while new text is added
12
12
  */
13
- static async streamToPosition(term: TermState, source: AsyncIterable<string>, pos: TermLinePosition = 'inline', curPos?: TermCoord): Promise<void> {
14
- curPos ??= { ...await term.getCursorPosition() };
13
+ static async streamToPosition(term: TermState, source: AsyncIterable<string>, config: TerminalStreamingConfig = {}): Promise<void> {
14
+ const curPos = config.at ?? { ...await term.getCursorPosition() };
15
+ const pos = config.position ?? 'inline';
16
+
15
17
  const writePos = pos === 'inline' ?
16
18
  { ...curPos, x: 0 } :
17
19
  { x: 0, y: pos === 'top' ? 0 : -1 };
@@ -33,7 +35,9 @@ export class TerminalOperation {
33
35
  for await (const text of source) {
34
36
  await TerminalWriter.for(term).setPosition(writePos).write(text).clearLine(1).commit(true);
35
37
  }
36
- await TerminalWriter.for(term).setPosition(writePos).clearLine().commit(true);
38
+ if (config.clearOnFinish ?? true) {
39
+ await TerminalWriter.for(term).setPosition(writePos).clearLine().commit(true);
40
+ }
37
41
  } finally {
38
42
  const finalCursor = await term.getCursorPosition();
39
43
  await TerminalWriter.for(term).scrollRangeClear().setPosition(finalCursor).showCursor().commit();
@@ -59,15 +63,15 @@ export class TerminalOperation {
59
63
  /**
60
64
  * Waiting indicator, streamed to a specific position, can be canceled
61
65
  */
62
- static streamWaiting(term: TermState, message: string, config: TerminalWaitingConfig = {}, curPos?: TermCoord): () => Promise<void> {
66
+ static streamWaiting(term: TermState, message: string, config: TerminalWaitingConfig = {}): () => Promise<void> {
63
67
  const { stop, stream } = IterableUtil.cycle(STD_WAIT_STATES);
64
68
  const indicator = IterableUtil.map(
65
69
  stream,
66
70
  IterableUtil.DELAY(config),
67
- (ch, i) => config.end ? `${message} ${ch}` : (i === 0 ? `${ch} ${message}` : ch)
71
+ (ch, i) => config.end ? `${message} ${ch}` : `${ch} ${message}`
68
72
  );
69
73
 
70
- const final = this.streamToPosition(term, indicator, config.position ?? 'inline', curPos);
74
+ const final = this.streamToPosition(term, indicator, config);
71
75
  return async () => { stop(); return final; };
72
76
  }
73
77
 
@@ -100,12 +104,15 @@ export class TerminalOperation {
100
104
  let writer: (() => Promise<unknown>) | undefined;
101
105
  let line: string | undefined;
102
106
 
107
+ let pos = await term.getCursorPosition();
108
+
109
+
103
110
  const commitLine = async (): Promise<void> => {
104
111
  await writer?.();
105
112
  if (line) {
106
113
  const msg = cfg.committedPrefix ? `${cfg.committedPrefix} ${line}` : line;
107
114
  if (cfg.position === 'inline') {
108
- await TerminalWriter.for(term).setPosition({ x: 0 }).changePosition({ y: -1 }).writeLine(msg).commit();
115
+ await TerminalWriter.for(term).setPosition(pos).write(msg).commit(true);
109
116
  } else {
110
117
  await TerminalWriter.for(term).writeLine(msg).commit();
111
118
  }
@@ -113,13 +120,12 @@ export class TerminalOperation {
113
120
  }
114
121
  };
115
122
 
116
- const pos = await term.getCursorPosition();
117
-
118
123
  for await (let msg of lines) {
119
124
  await commitLine();
120
125
  if (msg !== undefined) {
121
126
  msg = msg.replace(/\n$/, '');
122
- writer = this.streamWaiting(term, msg, cfg, pos);
127
+ pos = await term.getCursorPosition();
128
+ writer = this.streamWaiting(term, msg, { ...cfg, at: pos, clearOnFinish: false });
123
129
  line = msg;
124
130
  }
125
131
  }
package/src/terminal.ts CHANGED
@@ -93,6 +93,7 @@ export class Terminal implements TermState {
93
93
  () => this.interactive ? this.#query.backgroundColor() : undefined
94
94
  );
95
95
  })();
96
+ process.on('exit', () => this.reset());
96
97
  }
97
98
  return this.#init;
98
99
  }
@@ -174,7 +175,7 @@ export class Terminal implements TermState {
174
175
  await IterableUtil.drain(source);
175
176
  return;
176
177
  }
177
- return TerminalOperation.streamToPosition(this, IterableUtil.map(source, resolve), config?.position);
178
+ return TerminalOperation.streamToPosition(this, IterableUtil.map(source, resolve), config);
178
179
  }
179
180
 
180
181
  /**
@@ -203,7 +204,4 @@ export class Terminal implements TermState {
203
204
  }
204
205
  }
205
206
 
206
- export const GlobalTerminal = new Terminal({ output: process.stdout });
207
-
208
- // Trigger
209
- GlobalTerminal.init();
207
+ export const GlobalTerminal = new Terminal({ output: process.stdout });
package/src/types.ts CHANGED
@@ -13,7 +13,8 @@ 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 TerminalWaitingConfig = { position?: TermLinePosition, end?: boolean, committedPrefix?: string } & DelayedConfig;
16
+ export type TerminalStreamingConfig = { position?: TermLinePosition, clearOnFinish?: boolean, at?: TermCoord };
17
+ export type TerminalWaitingConfig = { end?: boolean, committedPrefix?: string } & TerminalStreamingConfig & DelayedConfig;
17
18
 
18
19
  export type TermColorLevel = 0 | 1 | 2 | 3;
19
20
  export type TermColorScheme = 'dark' | 'light';