console-toolkit 1.0.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.
Files changed (78) hide show
  1. package/LICENSE +34 -0
  2. package/README.md +92 -0
  3. package/package.json +59 -0
  4. package/src/alphanumeric/arrows.js +31 -0
  5. package/src/alphanumeric/fractions.js +82 -0
  6. package/src/alphanumeric/number-formatters.js +131 -0
  7. package/src/alphanumeric/roman.js +86 -0
  8. package/src/alphanumeric/unicode-cultural-numbers.js +68 -0
  9. package/src/alphanumeric/unicode-letters.js +63 -0
  10. package/src/alphanumeric/unicode-numbers.js +75 -0
  11. package/src/alphanumeric/utils.js +44 -0
  12. package/src/ansi/csi.js +67 -0
  13. package/src/ansi/index.js +20 -0
  14. package/src/ansi/sgr-constants.js +99 -0
  15. package/src/ansi/sgr-state.js +398 -0
  16. package/src/ansi/sgr.js +214 -0
  17. package/src/box.js +253 -0
  18. package/src/charts/bars/block-frac-grouped.js +6 -0
  19. package/src/charts/bars/block-frac.js +36 -0
  20. package/src/charts/bars/block-grouped.js +6 -0
  21. package/src/charts/bars/block.js +43 -0
  22. package/src/charts/bars/draw-grouped.js +39 -0
  23. package/src/charts/bars/draw-stacked.js +24 -0
  24. package/src/charts/bars/frac-grouped.js +33 -0
  25. package/src/charts/bars/plain-grouped.js +6 -0
  26. package/src/charts/bars/plain.js +63 -0
  27. package/src/charts/columns/block-frac-grouped.js +6 -0
  28. package/src/charts/columns/block-frac.js +30 -0
  29. package/src/charts/columns/block-grouped.js +6 -0
  30. package/src/charts/columns/block.js +37 -0
  31. package/src/charts/columns/draw-grouped.js +48 -0
  32. package/src/charts/columns/draw-stacked.js +31 -0
  33. package/src/charts/columns/frac-grouped.js +27 -0
  34. package/src/charts/columns/plain-grouped.js +6 -0
  35. package/src/charts/columns/plain.js +39 -0
  36. package/src/charts/themes/default.js +12 -0
  37. package/src/charts/themes/rainbow-reversed.js +5 -0
  38. package/src/charts/themes/rainbow.js +8 -0
  39. package/src/charts/utils.js +75 -0
  40. package/src/draw-block-frac.js +33 -0
  41. package/src/draw-block.js +55 -0
  42. package/src/meta.js +41 -0
  43. package/src/output/show.js +40 -0
  44. package/src/output/updater.js +82 -0
  45. package/src/output/writer.js +131 -0
  46. package/src/panel.js +748 -0
  47. package/src/plot/bitmap.js +108 -0
  48. package/src/plot/draw-line.js +26 -0
  49. package/src/plot/draw-rect.js +216 -0
  50. package/src/plot/index.js +24 -0
  51. package/src/plot/to-quads.js +32 -0
  52. package/src/spinner/index.js +8 -0
  53. package/src/spinner/spin.js +51 -0
  54. package/src/spinner/spinner.js +75 -0
  55. package/src/spinner/spinners.js +65 -0
  56. package/src/strings.js +72 -0
  57. package/src/style.js +620 -0
  58. package/src/symbols.js +131 -0
  59. package/src/table/draw-borders.js +87 -0
  60. package/src/table/index.js +7 -0
  61. package/src/table/table.js +330 -0
  62. package/src/themes/blocks/unicode-half.js +9 -0
  63. package/src/themes/blocks/unicode-thin.js +9 -0
  64. package/src/themes/lines/ascii-compact.js +11 -0
  65. package/src/themes/lines/ascii-dots.js +9 -0
  66. package/src/themes/lines/ascii-girder.js +9 -0
  67. package/src/themes/lines/ascii-github.js +11 -0
  68. package/src/themes/lines/ascii-reddit.js +11 -0
  69. package/src/themes/lines/ascii-rounded.js +11 -0
  70. package/src/themes/lines/ascii.js +11 -0
  71. package/src/themes/lines/unicode-bold.js +15 -0
  72. package/src/themes/lines/unicode-rounded.js +15 -0
  73. package/src/themes/lines/unicode.js +15 -0
  74. package/src/themes/utils.js +38 -0
  75. package/src/turtle/draw-line-art.js +46 -0
  76. package/src/turtle/draw-unicode.js +33 -0
  77. package/src/turtle/index.js +12 -0
  78. package/src/turtle/turtle.js +286 -0
@@ -0,0 +1,75 @@
1
+ import {SymbolRange, transcode as internalTranscode} from './utils.js';
2
+ import {minus, multiplication, superscriptPlus, superscriptMinus} from '../symbols.js';
3
+
4
+ export const transcodeTables = {
5
+ // Enclosed Alphanumeric
6
+ circled: new SymbolRange('①', 1, 20),
7
+ parens: new SymbolRange('⑴', 1, 20),
8
+ dots: new SymbolRange('⒈', 1, 20),
9
+ doubleCircled: new SymbolRange('⓵', 1, 10),
10
+
11
+ // Mathematical Alphanumeric Symbols
12
+ bold: new SymbolRange('𝟎'),
13
+ doubleStruck: new SymbolRange('𝟘'),
14
+ sansSerif: new SymbolRange('𝟢'),
15
+ sansSerifBold: new SymbolRange('𝟬'),
16
+ mono: new SymbolRange('𝟶'),
17
+
18
+ // Enclosed Alphanumeric Supplement
19
+ commas: new SymbolRange('🄁'),
20
+
21
+ // Number Forms
22
+ roman: new SymbolRange('Ⅰ', 1, 12),
23
+ romanLower: new SymbolRange('ⅰ', 1, 12),
24
+
25
+ // Dingbats
26
+ dingbatsCircledSansSerif: new SymbolRange('➀', 1, 10),
27
+ dingbatsNegativeCircled: new SymbolRange('❶', 1, 10),
28
+ dingbatsNegativeCircledSansSerif: new SymbolRange('➊', 1, 10),
29
+
30
+ // Superscripts and Subscripts
31
+ superscript: new SymbolRange('⁰'),
32
+ subscript: new SymbolRange('₀')
33
+ };
34
+
35
+ // patches
36
+
37
+ const circled_21_35 = new SymbolRange('\u{3251}', 21, 35),
38
+ circled_36_50 = new SymbolRange('\u{32B1}', 36, 50);
39
+ circled_36_50.overlay = {0: '\u{24EA}'};
40
+ circled_21_35.overlay = circled_36_50;
41
+ transcodeTables.circled.overlay = circled_21_35;
42
+
43
+ transcodeTables.dots.overlay = {0: '🄀'};
44
+
45
+ const negativeCircled_11_20 = new SymbolRange('\u{24EB}', 11, 20);
46
+ negativeCircled_11_20.overlay = {0: '\u{24FF}'};
47
+ transcodeTables.dingbatsNegativeCircled.overlay = negativeCircled_11_20;
48
+
49
+ transcodeTables.superscript.overlay = {1: '¹', 2: '²', 3: '³'};
50
+
51
+ // API
52
+
53
+ export const transcode = (s, name, options) => {
54
+ const table = typeof name == 'string' ? transcodeTables[name] : name;
55
+ if (!table) throw new Error(`There is no transcode table "${name}"`);
56
+ return internalTranscode(s, table, options);
57
+ };
58
+
59
+ export const numberPunctuation = (s, {addBefore = '', addAfter = ' '} = {}) =>
60
+ s.replace(
61
+ /(\d)([\,\.])/g,
62
+ (_, d, p) => addBefore + transcodeTables[p === '.' ? 'dots' : 'commas'].transcode(d) + addAfter
63
+ );
64
+
65
+ export const numberExponent = (s, {useSpecialMinus} = {}) => {
66
+ const r = /^([+-]?)([^e]+)e([+-]?)(.+)$/i.exec(s);
67
+ return r
68
+ ? (r[1] === '-' && useSpecialMinus ? minus : r[1]) +
69
+ r[2] +
70
+ multiplication +
71
+ '10' +
72
+ (r[3] === '+' ? superscriptPlus : r[3] === '-' ? superscriptMinus : r[3]) +
73
+ transcode(r[4], transcodeTables.superscript)
74
+ : s;
75
+ };
@@ -0,0 +1,44 @@
1
+ export class SymbolRange {
2
+ constructor(fromSymbol, from = 0, to = 9, inputBase = '0') {
3
+ this.from = from;
4
+ this.to = to;
5
+ this.base = fromSymbol.codePointAt(0) - from;
6
+ this.inputBase = inputBase.codePointAt(0);
7
+ this.overlay = null;
8
+ }
9
+ get(i) {
10
+ if (this.overlay) {
11
+ const result =
12
+ typeof this.overlay.get == 'function'
13
+ ? this.overlay.get(i)
14
+ : this.overlay[typeof i !== 'number' ? i : String.fromCodePoint(this.inputBase + i)];
15
+ if (result) return result;
16
+ }
17
+ if (typeof i !== 'number') {
18
+ i = String(i).codePointAt(0) - this.inputBase;
19
+ }
20
+ return this.from <= i && i <= this.to && String.fromCodePoint(this.base + i);
21
+ }
22
+ transcode(s, {missing} = {}) {
23
+ return s.replace(/./g, missing ? m => this.get(m) || missing : m => this.get(m) || m);
24
+ }
25
+ }
26
+
27
+ export const transcode = (s, tables, {missing} = {}) => {
28
+ let fn;
29
+ if (typeof tables == 'function') {
30
+ fn = tables;
31
+ } else if (!Array.isArray(tables)) {
32
+ if (typeof tables.transcode == 'function') return tables.transcode(s, {missing});
33
+ fn = m => tables[m];
34
+ } else {
35
+ fn = m => {
36
+ for (const table of tables) {
37
+ const result = typeof table.get == 'function' ? table.get(m) : table[m];
38
+ if (result) return result;
39
+ }
40
+ // return undefined;
41
+ };
42
+ }
43
+ return s.replace(/./g, missing ? m => fn(m) || missing : m => fn(m) || m);
44
+ };
@@ -0,0 +1,67 @@
1
+ // CSI (Control Sequence Introducer) commands definitions and helpers.
2
+ // CSI is a part of Fe Escape sequences.
3
+ // Type Fe is supported by C1 control codes.
4
+ // See https://en.wikipedia.org/wiki/ANSI_escape_code for more details.
5
+ // SGR is in a separate file. See sgr.js.
6
+
7
+ export * from './sgr.js';
8
+
9
+ // matcher
10
+ export const matchCsi = /\x1B\[([\x30-\x3F]*)([\x20-\x2F]*)([\x40-\x7E])/g;
11
+
12
+ const CSI = '\x1B[';
13
+
14
+ export const CURSOR_UP1 = CSI + 'A';
15
+ export const CURSOR_DOWN1 = CSI + 'B';
16
+ export const CURSOR_FORWARD1 = CSI + 'C';
17
+ export const CURSOR_BACK1 = CSI + 'D';
18
+ export const CURSOR_NEXT_LINE1 = CSI + 'E';
19
+ export const CURSOR_PREV_LINE1 = CSI + 'F';
20
+ export const CURSOR_COLUMN1 = CSI + 'G';
21
+ export const CURSOR_HOME = CSI + 'H';
22
+
23
+ export const CURSOR_GET_POS = CSI + '6n';
24
+ export const CURSOR_SAVE_POS = CSI + 's';
25
+ export const CURSOR_RESTORE_POS = CSI + 'u';
26
+ export const CURSOR_NORMAL = CSI + '?25h';
27
+ export const CURSOR_INVISIBLE = CSI + '?25l';
28
+
29
+ export const cursorUp = (n = 1) => CSI + (n > 1 ? n.toFixed() : '') + 'A';
30
+ export const cursorDown = (n = 1) => CSI + (n > 1 ? n.toFixed() : '') + 'B';
31
+ export const cursorForward = (n = 1) => CSI + (n > 1 ? n.toFixed() : '') + 'C';
32
+ export const cursorBack = (n = 1) => CSI + (n > 1 ? n.toFixed() : '') + 'D';
33
+ export const cursorNextLine = (n = 1) => CSI + (n > 1 ? n.toFixed() : '') + 'E';
34
+ export const cursorPrevLine = (n = 1) => CSI + (n > 1 ? n.toFixed() : '') + 'F';
35
+ export const cursorColumn = (n = 1) => CSI + (n > 1 ? n.toFixed() : '') + 'G';
36
+ export const cursorSetPos = (n, m) => CSI + (n > 1 ? n.toFixed() : '') + ';' + (m > 1 ? m.toFixed() : '') + 'H';
37
+ export const cursorSetPosAlt = (n, m) => CSI + (n > 1 ? n.toFixed() : '') + ';' + (m > 1 ? m.toFixed() : '') + 'f'; // HVP
38
+
39
+ export const CURSOR_RIGHT1 = CURSOR_FORWARD1;
40
+ export const CURSOR_LEFT1 = CURSOR_BACK1;
41
+ export const cursorRight = cursorForward;
42
+ export const cursorLeft = cursorBack;
43
+
44
+ export const CLEAR_EOS = CSI + 'J';
45
+ export const CLEAR_BOS = CSI + '1J';
46
+ export const CLEAR_SCREEN = CSI + '2J';
47
+ export const CLEAR_SCREEN_ALL = CSI + '3J'; // includes the scrollback buffer
48
+ export const CLEAR_EOL = CSI + 'K';
49
+ export const CLEAR_BOL = CSI + '1K';
50
+ export const CLEAR_LINE = CSI + '2K';
51
+
52
+ export const SCREEN_SAVE = CSI + '?47h';
53
+ export const SCREEN_RESTORE = CSI + '?47l';
54
+ export const SCREEN_ALT_ON = CSI + '?1049h'; // alternative screen buffer
55
+ export const SCREEN_ALT_OFF = CSI + '?1049l';
56
+ export const SCREEN_SCROLL_UP1 = CSI + 'S'
57
+ export const SCREEN_SCROLL_DOWN1 = CSI + 'T'
58
+ export const SCREEN_REPORT_FOCUS_ON = CSI + '?1004h';
59
+ export const SCREEN_REPORT_FOCUS_OFF = CSI + '?1004l';
60
+
61
+ export const screenScrollUp = (n = 1) => CSI + (n > 1 ? n.toFixed() : '') + 'S';
62
+ export const screenScrollDown = (n = 1) => CSI + (n > 1 ? n.toFixed() : '') + 'T';
63
+
64
+ export const WRAPPING_ON = CSI + '=7h';
65
+ export const WRAPPING_OFF = CSI + '=7l';
66
+ export const BRACKETED_PASTE_ON = CSI + '?2004h';
67
+ export const BRACKETED_PASTE_OFF = CSI + '?2004l';
@@ -0,0 +1,20 @@
1
+ // ANSI escape code. See https://en.wikipedia.org/wiki/ANSI_escape_code for more details.
2
+ // This is a collection of escape sequences that are not part of other files (e.g., CSI, SGR).
3
+
4
+ export * from './csi.js';
5
+
6
+ export const ESC = '\x1B';
7
+
8
+ export const CURSOR_DELETE = '\x7F';
9
+
10
+ export const CURSOR_GO_UP1 = ESC + 'M';
11
+
12
+ export const CURSOR_SAVE = ESC + '7';
13
+ export const CURSOR_RESTORE = ESC + '8';
14
+
15
+ // matchers
16
+ export const matchFe = /\x1B([\x40-\x5F])/g;
17
+ export const matchFs = /\x1B([\x60-\x7E])/g;
18
+ export const matchFp = /\x1B([\x30-\x3F])/g;
19
+ export const matchNf = /\x1B([\x20-\x2F]+)([\x30-\x7E])/g;
20
+ export const matchOsc = /\x1B\]([\x20-\x7E]*)([\x07\x9C]|\x1B\x5C)/g;
@@ -0,0 +1,99 @@
1
+ // SGR commands as constants: some people prefer it this way
2
+
3
+ import {Commands, Colors, setCommands, getColor, getBrightColor, getBgColor, getBgBrightColor} from './sgr.js';
4
+
5
+ // Commands
6
+
7
+ export const BOLD = setCommands(Commands.BOLD);
8
+ export const DIM = setCommands(Commands.DIM);
9
+ export const ITALIC = setCommands(Commands.ITALIC);
10
+ export const UNDERLINE = setCommands(Commands.UNDERLINE);
11
+ export const BLINK = setCommands(Commands.BLINK);
12
+ export const RAPID_BLINK = setCommands(Commands.RAPID_BLINK);
13
+ export const INVERSE = setCommands(Commands.INVERSE);
14
+ export const HIDDEN = setCommands(Commands.HIDDEN);
15
+ export const STRIKETHROUGH = setCommands(Commands.STRIKETHROUGH);
16
+ export const OVERLINE = setCommands(Commands.OVERLINE);
17
+
18
+ // Reset commands
19
+
20
+ export const RESET_ALL = setCommands('');
21
+ export const RESET = RESET_ALL;
22
+
23
+ export const RESET_BOLD = setCommands(Commands.RESET_BOLD);
24
+ export const RESET_DIM = setCommands(Commands.RESET_DIM);
25
+ export const RESET_ITALIC = setCommands(Commands.RESET_ITALIC);
26
+ export const RESET_UNDERLINE = setCommands(Commands.RESET_UNDERLINE);
27
+ export const RESET_BLINK = setCommands(Commands.RESET_BLINK);
28
+ export const RESET_RAPID_BLINK = setCommands(Commands.RESET_RAPID_BLINK);
29
+ export const RESET_INVERSE = setCommands(Commands.RESET_INVERSE);
30
+ export const RESET_HIDDEN = setCommands(Commands.RESET_HIDDEN);
31
+ export const RESET_STRIKETHROUGH = setCommands(Commands.RESET_STRIKETHROUGH);
32
+ export const RESET_OVERLINE = setCommands(Commands.RESET_OVERLINE);
33
+
34
+ export const RESET_COLOR = setCommands(Commands.RESET_COLOR);
35
+ export const RESET_BG_COLOR = setCommands(Commands.RESET_BG_COLOR);
36
+ export const RESET_FONT = setCommands(Commands.RESET_FONT);
37
+
38
+ // Colors
39
+
40
+ export const BLACK = setCommands(getColor(Colors.BLACK));
41
+ export const RED = setCommands(getColor(Colors.RED));
42
+ export const GREEN = setCommands(getColor(Colors.GREEN));
43
+ export const YELLOW = setCommands(getColor(Colors.YELLOW));
44
+ export const BLUE = setCommands(getColor(Colors.BLUE));
45
+ export const MAGENTA = setCommands(getColor(Colors.MAGENTA));
46
+ export const CYAN = setCommands(getColor(Colors.CYAN));
47
+ export const WHITE = setCommands(getColor(Colors.WHITE));
48
+
49
+ export const BRIGHT_BLACK = setCommands(getBrightColor(Colors.BLACK));
50
+ export const BRIGHT_RED = setCommands(getBrightColor(Colors.RED));
51
+ export const BRIGHT_GREEN = setCommands(getBrightColor(Colors.GREEN));
52
+ export const BRIGHT_YELLOW = setCommands(getBrightColor(Colors.YELLOW));
53
+ export const BRIGHT_BLUE = setCommands(getBrightColor(Colors.BLUE));
54
+ export const BRIGHT_MAGENTA = setCommands(getBrightColor(Colors.MAGENTA));
55
+ export const BRIGHT_CYAN = setCommands(getBrightColor(Colors.CYAN));
56
+ export const BRIGHT_WHITE = setCommands(getBrightColor(Colors.WHITE));
57
+
58
+ export const BG_BLACK = setCommands(getBgColor(Colors.BLACK));
59
+ export const BG_RED = setCommands(getBgColor(Colors.RED));
60
+ export const BG_GREEN = setCommands(getBgColor(Colors.GREEN));
61
+ export const BG_YELLOW = setCommands(getBgColor(Colors.YELLOW));
62
+ export const BG_BLUE = setCommands(getBgColor(Colors.BLUE));
63
+ export const BG_MAGENTA = setCommands(getBgColor(Colors.MAGENTA));
64
+ export const BG_CYAN = setCommands(getBgColor(Colors.CYAN));
65
+ export const BG_WHITE = setCommands(getBgColor(Colors.WHITE));
66
+
67
+ export const BG_BRIGHT_BLACK = setCommands(getBgBrightColor(Colors.BLACK));
68
+ export const BG_BRIGHT_RED = setCommands(getBgBrightColor(Colors.RED));
69
+ export const BG_BRIGHT_GREEN = setCommands(getBgBrightColor(Colors.GREEN));
70
+ export const BG_BRIGHT_YELLOW = setCommands(getBgBrightColor(Colors.YELLOW));
71
+ export const BG_BRIGHT_BLUE = setCommands(getBgBrightColor(Colors.BLUE));
72
+ export const BG_BRIGHT_MAGENTA = setCommands(getBgBrightColor(Colors.MAGENTA));
73
+ export const BG_BRIGHT_CYAN = setCommands(getBgBrightColor(Colors.CYAN));
74
+ export const BG_BRIGHT_WHITE = setCommands(getBgBrightColor(Colors.WHITE));
75
+
76
+ export const FG_BLACK = BLACK;
77
+ export const FG_RED = RED;
78
+ export const FG_GREEN = GREEN;
79
+ export const FG_YELLOW = YELLOW;
80
+ export const FG_BLUE = BLUE;
81
+ export const FG_MAGENTA = MAGENTA;
82
+ export const FG_CYAN = CYAN;
83
+ export const FG_WHITE = WHITE;
84
+
85
+ export const FG_BRIGHT_BLACK = BRIGHT_BLACK;
86
+ export const FG_BRIGHT_RED = BRIGHT_RED;
87
+ export const FG_BRIGHT_GREEN = BRIGHT_GREEN;
88
+ export const FG_BRIGHT_YELLOW = BRIGHT_YELLOW;
89
+ export const FG_BRIGHT_BLUE = BRIGHT_BLUE;
90
+ export const FG_BRIGHT_MAGENTA = BRIGHT_MAGENTA;
91
+ export const FG_BRIGHT_CYAN = BRIGHT_CYAN;
92
+ export const FG_BRIGHT_WHITE = BRIGHT_WHITE;
93
+
94
+ export const GRAY = BRIGHT_BLACK;
95
+ export const GREY = GRAY;
96
+ export const BG_GRAY = BG_BRIGHT_BLACK;
97
+ export const BG_GREY = BG_GRAY;
98
+ export const FG_GRAY = FG_BRIGHT_BLACK;
99
+ export const FG_GREY = FG_GRAY;
@@ -0,0 +1,398 @@
1
+ // Support for states based on SGR commands. See https://en.wikipedia.org/wiki/ANSI_escape_code for more details.
2
+
3
+ import {
4
+ Commands,
5
+ ColorFormatSize,
6
+ isFgColorCommand,
7
+ isBgColorCommand,
8
+ isFontCommand,
9
+ setCommands,
10
+ matchSgr
11
+ } from './sgr.js';
12
+
13
+ export const RESET_STATE = {
14
+ bold: null,
15
+ dim: null,
16
+ italic: null,
17
+ underline: null,
18
+ blink: null,
19
+ inverse: null,
20
+ hidden: null,
21
+ strikethrough: null,
22
+ overline: null,
23
+ foreground: null,
24
+ background: null,
25
+ decoration: null,
26
+ font: null
27
+ };
28
+
29
+ const defaultState = Symbol('defaultState');
30
+
31
+ let toState;
32
+
33
+ export const extractState = (s, initState = defaultState) => {
34
+ let state = toState(initState);
35
+ matchSgr.lastIndex = 0;
36
+ for (const match of s.matchAll(matchSgr)) state = addCommandsToState(state, match[1].split(';'));
37
+ return state;
38
+ };
39
+
40
+ toState = value => {
41
+ switch (typeof value) {
42
+ case 'object':
43
+ if (!value) return RESET_STATE;
44
+ if (typeof value.getState == 'function') return value.getState();
45
+ return value;
46
+ case 'string':
47
+ if (!value) break;
48
+ return extractState(value);
49
+ }
50
+ return {};
51
+ };
52
+ export {toState};
53
+
54
+ const TOTAL_RESETS = Array.from(Object.keys(RESET_STATE)).length;
55
+
56
+ const getStateResets = state => {
57
+ let resetCount = 0;
58
+
59
+ for (const name of Object.keys(RESET_STATE)) {
60
+ if (state[name] === null) ++resetCount;
61
+ }
62
+
63
+ return resetCount;
64
+ };
65
+
66
+ export const combineStates = (...states) => {
67
+ let state = {};
68
+ for (const s of states) {
69
+ const currentState = toState(s);
70
+ for (const [name, value] of Object.entries(currentState)) {
71
+ if (value !== undefined) state[name] = value;
72
+ }
73
+ }
74
+ return state;
75
+ };
76
+
77
+ export const commandsToState = commands => {
78
+ let state = {};
79
+ for (let i = 0; i < commands.length; ++i) {
80
+ const currentCommand = commands[i];
81
+ switch (currentCommand) {
82
+ case '': // reset
83
+ case Commands.RESET_ALL:
84
+ state = RESET_STATE;
85
+ continue;
86
+ case Commands.BOLD:
87
+ state.bold = currentCommand;
88
+ continue;
89
+ case Commands.DIM:
90
+ state.dim = currentCommand;
91
+ continue;
92
+ case Commands.ITALIC:
93
+ state.italic = currentCommand;
94
+ continue;
95
+ case Commands.UNDERLINE:
96
+ case Commands.DOUBLE_UNDERLINE:
97
+ case Commands.CURLY_UNDERLINE:
98
+ state.underline = currentCommand;
99
+ continue;
100
+ case Commands.BLINK:
101
+ case Commands.RAPID_BLINK:
102
+ state.blink = currentCommand;
103
+ continue;
104
+ case Commands.INVERSE:
105
+ state.inverse = currentCommand;
106
+ continue;
107
+ case Commands.HIDDEN:
108
+ state.hidden = currentCommand;
109
+ continue;
110
+ case Commands.STRIKETHROUGH:
111
+ state.strikethrough = currentCommand;
112
+ continue;
113
+ case Commands.OVERLINE:
114
+ state.overline = currentCommand;
115
+ continue;
116
+ case Commands.RESET_BOLD:
117
+ // case Commands.RESET_DIM:
118
+ state.bold = state.dim = null;
119
+ continue;
120
+ case Commands.RESET_ITALIC:
121
+ state.italic = null;
122
+ continue;
123
+ case Commands.RESET_UNDERLINE:
124
+ // case Commands.RESET_DOUBLE_UNDERLINE:
125
+ // case Commands.RESET_CURLY_UNDERLINE:
126
+ state.underline = null;
127
+ continue;
128
+ case Commands.RESET_BLINK:
129
+ // case RESET_RAPID_BLINK:
130
+ state.blink = null;
131
+ continue;
132
+ case Commands.RESET_INVERSE:
133
+ state.inverse = null;
134
+ continue;
135
+ case Commands.RESET_HIDDEN:
136
+ state.hidden = null;
137
+ continue;
138
+ case Commands.RESET_STRIKETHROUGH:
139
+ state.strikethrough = null;
140
+ continue;
141
+ case Commands.RESET_OVERLINE:
142
+ state.overline = null;
143
+ continue;
144
+ case Commands.RESET_DECORATION_COLOR:
145
+ state.decoration = null;
146
+ continue;
147
+ case Commands.EXTENDED_COLOR: {
148
+ const next = ColorFormatSize[commands[i + 1]],
149
+ color = commands.slice(i, i + next);
150
+ i += next - 1;
151
+ state.foreground = color;
152
+ continue;
153
+ }
154
+ case Commands.BG_EXTENDED_COLOR: {
155
+ const next = ColorFormatSize[commands[i + 1]],
156
+ color = commands.slice(i, i + next);
157
+ i += next - 1;
158
+ state.background = color;
159
+ continue;
160
+ }
161
+ case Commands.DECORATION_COLOR: {
162
+ const next = ColorFormatSize[commands[i + 1]],
163
+ color = commands.slice(i, i + next);
164
+ i += next - 1;
165
+ state.decoration = color;
166
+ continue;
167
+ }
168
+ case Commands.DEFAULT_COLOR:
169
+ state.foreground = null;
170
+ continue;
171
+ case Commands.BG_DEFAULT_COLOR:
172
+ state.background = null;
173
+ continue;
174
+ case Commands.DEFAULT_FONT:
175
+ state.font = null;
176
+ continue;
177
+ }
178
+ if (isFgColorCommand(currentCommand)) {
179
+ state.foreground = currentCommand;
180
+ continue;
181
+ }
182
+ if (isBgColorCommand(currentCommand)) {
183
+ state.background = currentCommand;
184
+ continue;
185
+ }
186
+ if (isFontCommand(currentCommand)) {
187
+ state.font = currentCommand;
188
+ continue;
189
+ }
190
+ }
191
+ return state;
192
+ };
193
+
194
+ export const addCommandsToState = (state, commands) => combineStates(state, commandsToState(commands));
195
+
196
+ const equalColors = (a, b) => {
197
+ if (a === b) return true;
198
+ if (Array.isArray(a) && Array.isArray(b))
199
+ return a.length === b.length && a.every((value, index) => value === b[index]);
200
+ return false;
201
+ };
202
+
203
+ const pushColor = (commands, color) => {
204
+ if (Array.isArray(color)) {
205
+ commands.push(...color);
206
+ } else {
207
+ commands.push(color);
208
+ }
209
+ return commands;
210
+ };
211
+
212
+ const resetColorProperties = {
213
+ foreground: Commands.RESET_COLOR,
214
+ background: Commands.RESET_BG_COLOR,
215
+ decoration: Commands.RESET_DECORATION_COLOR
216
+ };
217
+
218
+ const chainedStates = {bold: 1, dim: 1};
219
+
220
+ export const stateToCommands = state => {
221
+ state = toState(state);
222
+ const commands = [];
223
+ let resetCount = 0;
224
+
225
+ // process chained states separately
226
+ if (state.bold === null) {
227
+ commands.push(Commands.RESET_BOLD);
228
+ ++resetCount;
229
+ }
230
+ if (state.dim === null) {
231
+ commands.push(Commands.RESET_DIM);
232
+ ++resetCount;
233
+ }
234
+ state.bold && commands.push(state.bold);
235
+ state.dim && commands.push(state.dim);
236
+
237
+ for (const [name, value] of Object.entries(state)) {
238
+ if (chainedStates[name] === 1) continue; // skip chained states
239
+ if (resetColorProperties.hasOwnProperty(name)) {
240
+ // colors
241
+ if (value === null) {
242
+ commands.push(resetColorProperties[name]);
243
+ ++resetCount;
244
+ continue;
245
+ }
246
+ value && pushColor(commands, value);
247
+ continue;
248
+ }
249
+ if (value === null) {
250
+ commands.push(Commands['RESET_' + name.toUpperCase()]);
251
+ ++resetCount;
252
+ continue;
253
+ }
254
+ value && commands.push(value);
255
+ }
256
+
257
+ return resetCount === TOTAL_RESETS ? [''] : commands;
258
+ };
259
+
260
+ export const stateTransition = (prev, next) => {
261
+ prev = toState(prev);
262
+ next = toState(next);
263
+ const commands = [];
264
+ let resetCount = 0;
265
+
266
+ // process chained states separately
267
+ if (prev.bold !== next.bold) {
268
+ if (next.bold === null) {
269
+ commands.push(Commands.RESET_BOLD);
270
+ } else if (next.bold) {
271
+ if (next.dim === null && prev.dim !== null) commands.push(Commands.RESET_DIM);
272
+ commands.push(next.bold);
273
+ }
274
+ next.dim && commands.push(next.dim);
275
+ } else {
276
+ if (prev.dim !== next.dim) {
277
+ if (next.dim === null) {
278
+ commands.push(Commands.RESET_DIM);
279
+ next.bold && commands.push(next.bold);
280
+ } else {
281
+ next.dim && commands.push(next.dim);
282
+ }
283
+ }
284
+ }
285
+ if (next.bold === null) ++resetCount;
286
+ if (next.dim === null) ++resetCount;
287
+
288
+ for (const name of Object.keys(RESET_STATE)) {
289
+ if (chainedStates[name] === 1) continue; // skip chained states
290
+ const value = next[name];
291
+ if (resetColorProperties.hasOwnProperty(name)) {
292
+ // color
293
+ if (value === null) {
294
+ if (prev[name] !== null) commands.push(resetColorProperties[name]);
295
+ ++resetCount;
296
+ continue;
297
+ }
298
+ if (value) {
299
+ if (!equalColors(prev[name], value)) pushColor(commands, value);
300
+ }
301
+ continue;
302
+ }
303
+ if (value === null) {
304
+ if (prev[name] !== null) commands.push(Commands['RESET_' + name.toUpperCase()]);
305
+ ++resetCount;
306
+ continue;
307
+ }
308
+ if (value) {
309
+ if (prev[name] !== value) commands.push(value);
310
+ }
311
+ }
312
+
313
+ if (resetCount === TOTAL_RESETS) {
314
+ const prevResets = getStateResets(prev);
315
+ return prevResets === TOTAL_RESETS ? [] : [''];
316
+ }
317
+
318
+ return commands;
319
+ };
320
+
321
+ export const stateReverseTransition = (prev, next) => {
322
+ prev = toState(prev);
323
+ next = toState(next);
324
+ const commands = [];
325
+ let resetCount = 0;
326
+
327
+ // process chained states separately
328
+ if (next.bold !== prev.bold) {
329
+ if (!prev.bold) {
330
+ commands.push(Commands.RESET_BOLD);
331
+ } else {
332
+ if (!prev.dim && next.dim) commands.push(Commands.RESET_DIM);
333
+ commands.push(prev.bold);
334
+ }
335
+ prev.dim && commands.push(prev.dim);
336
+ } else {
337
+ if (next.dim !== prev.dim) {
338
+ if (!prev.dim) {
339
+ commands.push(Commands.RESET_DIM);
340
+ prev.bold && commands.push(prev.bold);
341
+ } else {
342
+ prev.dim && commands.push(prev.dim);
343
+ }
344
+ }
345
+ }
346
+ if (prev.bold === null) ++resetCount;
347
+ if (prev.dim === null) ++resetCount;
348
+
349
+ for (const name of Object.keys(RESET_STATE)) {
350
+ const value = prev[name];
351
+ if (resetColorProperties.hasOwnProperty(name)) {
352
+ // color
353
+ if (!value) {
354
+ if (next[name]) commands.push(resetColorProperties[name]);
355
+ if (value === null) ++resetCount;
356
+ continue;
357
+ }
358
+ if (!equalColors(next[name], value)) pushColor(commands, value);
359
+ continue;
360
+ }
361
+ if (!value) {
362
+ if (next[name]) commands.push(Commands['RESET_' + name.toUpperCase()]);
363
+ if (value === null) ++resetCount;
364
+ continue;
365
+ }
366
+ if (next[name] !== value) commands.push(value);
367
+ }
368
+
369
+ if (resetCount === TOTAL_RESETS) {
370
+ const nextResets = getStateResets(next);
371
+ return nextResets === TOTAL_RESETS ? [] : [''];
372
+ }
373
+
374
+ return commands;
375
+ };
376
+
377
+ export const stringifyCommands = commands => (commands?.length ? setCommands(commands) : '');
378
+
379
+ export const optimize = (s, initState = defaultState) => {
380
+ let state = toState(initState),
381
+ result = '',
382
+ start = 0;
383
+ matchSgr.lastIndex = 0;
384
+ for (const match of s.matchAll(matchSgr)) {
385
+ if (start < match.index) {
386
+ const commands = initState !== state ? stateTransition(initState, state) : [];
387
+ result += stringifyCommands(commands);
388
+ initState = state;
389
+ result += s.substring(start, match.index);
390
+ }
391
+ state = addCommandsToState(state, match[1].split(';'));
392
+ start = match.index + match[0].length;
393
+ }
394
+ const commands = initState !== state ? stateTransition(initState, state) : [];
395
+ result += stringifyCommands(commands);
396
+ if (start < s.length) result += s.substring(start);
397
+ return result;
398
+ };