UNPKG

4.22 kBJavaScriptView Raw
1import process from 'node:process';
2
3const ESC = '\u001B[';
4const OSC = '\u001B]';
5const BEL = '\u0007';
6const SEP = ';';
7
8/* global window */
9const isBrowser = typeof window !== 'undefined' && typeof window.document !== 'undefined';
10
11const isTerminalApp = !isBrowser && process.env.TERM_PROGRAM === 'Apple_Terminal';
12const isWindows = !isBrowser && process.platform === 'win32';
13const cwdFunction = isBrowser ? () => {
14 throw new Error('`process.cwd()` only works in Node.js, not the browser.');
15} : process.cwd;
16
17const ansiEscapes = {};
18
19ansiEscapes.cursorTo = (x, y) => {
20 if (typeof x !== 'number') {
21 throw new TypeError('The `x` argument is required');
22 }
23
24 if (typeof y !== 'number') {
25 return ESC + (x + 1) + 'G';
26 }
27
28 return ESC + (y + 1) + SEP + (x + 1) + 'H';
29};
30
31ansiEscapes.cursorMove = (x, y) => {
32 if (typeof x !== 'number') {
33 throw new TypeError('The `x` argument is required');
34 }
35
36 let returnValue = '';
37
38 if (x < 0) {
39 returnValue += ESC + (-x) + 'D';
40 } else if (x > 0) {
41 returnValue += ESC + x + 'C';
42 }
43
44 if (y < 0) {
45 returnValue += ESC + (-y) + 'A';
46 } else if (y > 0) {
47 returnValue += ESC + y + 'B';
48 }
49
50 return returnValue;
51};
52
53ansiEscapes.cursorUp = (count = 1) => ESC + count + 'A';
54ansiEscapes.cursorDown = (count = 1) => ESC + count + 'B';
55ansiEscapes.cursorForward = (count = 1) => ESC + count + 'C';
56ansiEscapes.cursorBackward = (count = 1) => ESC + count + 'D';
57
58ansiEscapes.cursorLeft = ESC + 'G';
59ansiEscapes.cursorSavePosition = isTerminalApp ? '\u001B7' : ESC + 's';
60ansiEscapes.cursorRestorePosition = isTerminalApp ? '\u001B8' : ESC + 'u';
61ansiEscapes.cursorGetPosition = ESC + '6n';
62ansiEscapes.cursorNextLine = ESC + 'E';
63ansiEscapes.cursorPrevLine = ESC + 'F';
64ansiEscapes.cursorHide = ESC + '?25l';
65ansiEscapes.cursorShow = ESC + '?25h';
66
67ansiEscapes.eraseLines = count => {
68 let clear = '';
69
70 for (let i = 0; i < count; i++) {
71 clear += ansiEscapes.eraseLine + (i < count - 1 ? ansiEscapes.cursorUp() : '');
72 }
73
74 if (count) {
75 clear += ansiEscapes.cursorLeft;
76 }
77
78 return clear;
79};
80
81ansiEscapes.eraseEndLine = ESC + 'K';
82ansiEscapes.eraseStartLine = ESC + '1K';
83ansiEscapes.eraseLine = ESC + '2K';
84ansiEscapes.eraseDown = ESC + 'J';
85ansiEscapes.eraseUp = ESC + '1J';
86ansiEscapes.eraseScreen = ESC + '2J';
87ansiEscapes.scrollUp = ESC + 'S';
88ansiEscapes.scrollDown = ESC + 'T';
89
90ansiEscapes.clearScreen = '\u001Bc';
91
92ansiEscapes.clearTerminal = isWindows
93 ? `${ansiEscapes.eraseScreen}${ESC}0f`
94 // 1. Erases the screen (Only done in case `2` is not supported)
95 // 2. Erases the whole screen including scrollback buffer
96 // 3. Moves cursor to the top-left position
97 // More info: https://www.real-world-systems.com/docs/ANSIcode.html
98 : `${ansiEscapes.eraseScreen}${ESC}3J${ESC}H`;
99
100ansiEscapes.enterAlternativeScreen = ESC + '?1049h';
101ansiEscapes.exitAlternativeScreen = ESC + '?1049l';
102
103ansiEscapes.beep = BEL;
104
105ansiEscapes.link = (text, url) => [
106 OSC,
107 '8',
108 SEP,
109 SEP,
110 url,
111 BEL,
112 text,
113 OSC,
114 '8',
115 SEP,
116 SEP,
117 BEL,
118].join('');
119
120ansiEscapes.image = (buffer, options = {}) => {
121 let returnValue = `${OSC}1337;File=inline=1`;
122
123 if (options.width) {
124 returnValue += `;width=${options.width}`;
125 }
126
127 if (options.height) {
128 returnValue += `;height=${options.height}`;
129 }
130
131 if (options.preserveAspectRatio === false) {
132 returnValue += ';preserveAspectRatio=0';
133 }
134
135 return returnValue + ':' + buffer.toString('base64') + BEL;
136};
137
138ansiEscapes.iTerm = {
139 setCwd: (cwd = cwdFunction()) => `${OSC}50;CurrentDir=${cwd}${BEL}`,
140
141 annotation(message, options = {}) {
142 let returnValue = `${OSC}1337;`;
143
144 const hasX = typeof options.x !== 'undefined';
145 const hasY = typeof options.y !== 'undefined';
146 if ((hasX || hasY) && !(hasX && hasY && typeof options.length !== 'undefined')) {
147 throw new Error('`x`, `y` and `length` must be defined when `x` or `y` is defined');
148 }
149
150 message = message.replace(/\|/g, '');
151
152 returnValue += options.isHidden ? 'AddHiddenAnnotation=' : 'AddAnnotation=';
153
154 if (options.length > 0) {
155 returnValue += (
156 hasX
157 ? [message, options.length, options.x, options.y]
158 : [options.length, message]
159 ).join('|');
160 } else {
161 returnValue += message;
162 }
163
164 return returnValue + BEL;
165 },
166};
167
168export default ansiEscapes;