• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1const ANSI_BACKGROUND_OFFSET = 10;
2
3const wrapAnsi16 = (offset = 0) => code => `\u001B[${code + offset}m`;
4
5const wrapAnsi256 = (offset = 0) => code => `\u001B[${38 + offset};5;${code}m`;
6
7const wrapAnsi16m = (offset = 0) => (red, green, blue) => `\u001B[${38 + offset};2;${red};${green};${blue}m`;
8
9const styles = {
10	modifier: {
11		reset: [0, 0],
12		// 21 isn't widely supported and 22 does the same thing
13		bold: [1, 22],
14		dim: [2, 22],
15		italic: [3, 23],
16		underline: [4, 24],
17		overline: [53, 55],
18		inverse: [7, 27],
19		hidden: [8, 28],
20		strikethrough: [9, 29],
21	},
22	color: {
23		black: [30, 39],
24		red: [31, 39],
25		green: [32, 39],
26		yellow: [33, 39],
27		blue: [34, 39],
28		magenta: [35, 39],
29		cyan: [36, 39],
30		white: [37, 39],
31
32		// Bright color
33		blackBright: [90, 39],
34		gray: [90, 39], // Alias of `blackBright`
35		grey: [90, 39], // Alias of `blackBright`
36		redBright: [91, 39],
37		greenBright: [92, 39],
38		yellowBright: [93, 39],
39		blueBright: [94, 39],
40		magentaBright: [95, 39],
41		cyanBright: [96, 39],
42		whiteBright: [97, 39],
43	},
44	bgColor: {
45		bgBlack: [40, 49],
46		bgRed: [41, 49],
47		bgGreen: [42, 49],
48		bgYellow: [43, 49],
49		bgBlue: [44, 49],
50		bgMagenta: [45, 49],
51		bgCyan: [46, 49],
52		bgWhite: [47, 49],
53
54		// Bright color
55		bgBlackBright: [100, 49],
56		bgGray: [100, 49], // Alias of `bgBlackBright`
57		bgGrey: [100, 49], // Alias of `bgBlackBright`
58		bgRedBright: [101, 49],
59		bgGreenBright: [102, 49],
60		bgYellowBright: [103, 49],
61		bgBlueBright: [104, 49],
62		bgMagentaBright: [105, 49],
63		bgCyanBright: [106, 49],
64		bgWhiteBright: [107, 49],
65	},
66};
67
68export const modifierNames = Object.keys(styles.modifier);
69export const foregroundColorNames = Object.keys(styles.color);
70export const backgroundColorNames = Object.keys(styles.bgColor);
71export const colorNames = [...foregroundColorNames, ...backgroundColorNames];
72
73function assembleStyles() {
74	const codes = new Map();
75
76	for (const [groupName, group] of Object.entries(styles)) {
77		for (const [styleName, style] of Object.entries(group)) {
78			styles[styleName] = {
79				open: `\u001B[${style[0]}m`,
80				close: `\u001B[${style[1]}m`,
81			};
82
83			group[styleName] = styles[styleName];
84
85			codes.set(style[0], style[1]);
86		}
87
88		Object.defineProperty(styles, groupName, {
89			value: group,
90			enumerable: false,
91		});
92	}
93
94	Object.defineProperty(styles, 'codes', {
95		value: codes,
96		enumerable: false,
97	});
98
99	styles.color.close = '\u001B[39m';
100	styles.bgColor.close = '\u001B[49m';
101
102	styles.color.ansi = wrapAnsi16();
103	styles.color.ansi256 = wrapAnsi256();
104	styles.color.ansi16m = wrapAnsi16m();
105	styles.bgColor.ansi = wrapAnsi16(ANSI_BACKGROUND_OFFSET);
106	styles.bgColor.ansi256 = wrapAnsi256(ANSI_BACKGROUND_OFFSET);
107	styles.bgColor.ansi16m = wrapAnsi16m(ANSI_BACKGROUND_OFFSET);
108
109	// From https://github.com/Qix-/color-convert/blob/3f0e0d4e92e235796ccb17f6e85c72094a651f49/conversions.js
110	Object.defineProperties(styles, {
111		rgbToAnsi256: {
112			value: (red, green, blue) => {
113				// We use the extended greyscale palette here, with the exception of
114				// black and white. normal palette only has 4 greyscale shades.
115				if (red === green && green === blue) {
116					if (red < 8) {
117						return 16;
118					}
119
120					if (red > 248) {
121						return 231;
122					}
123
124					return Math.round(((red - 8) / 247) * 24) + 232;
125				}
126
127				return 16
128					+ (36 * Math.round(red / 255 * 5))
129					+ (6 * Math.round(green / 255 * 5))
130					+ Math.round(blue / 255 * 5);
131			},
132			enumerable: false,
133		},
134		hexToRgb: {
135			value: hex => {
136				const matches = /[a-f\d]{6}|[a-f\d]{3}/i.exec(hex.toString(16));
137				if (!matches) {
138					return [0, 0, 0];
139				}
140
141				let [colorString] = matches;
142
143				if (colorString.length === 3) {
144					colorString = [...colorString].map(character => character + character).join('');
145				}
146
147				const integer = Number.parseInt(colorString, 16);
148
149				return [
150					/* eslint-disable no-bitwise */
151					(integer >> 16) & 0xFF,
152					(integer >> 8) & 0xFF,
153					integer & 0xFF,
154					/* eslint-enable no-bitwise */
155				];
156			},
157			enumerable: false,
158		},
159		hexToAnsi256: {
160			value: hex => styles.rgbToAnsi256(...styles.hexToRgb(hex)),
161			enumerable: false,
162		},
163		ansi256ToAnsi: {
164			value: code => {
165				if (code < 8) {
166					return 30 + code;
167				}
168
169				if (code < 16) {
170					return 90 + (code - 8);
171				}
172
173				let red;
174				let green;
175				let blue;
176
177				if (code >= 232) {
178					red = (((code - 232) * 10) + 8) / 255;
179					green = red;
180					blue = red;
181				} else {
182					code -= 16;
183
184					const remainder = code % 36;
185
186					red = Math.floor(code / 36) / 5;
187					green = Math.floor(remainder / 6) / 5;
188					blue = (remainder % 6) / 5;
189				}
190
191				const value = Math.max(red, green, blue) * 2;
192
193				if (value === 0) {
194					return 30;
195				}
196
197				// eslint-disable-next-line no-bitwise
198				let result = 30 + ((Math.round(blue) << 2) | (Math.round(green) << 1) | Math.round(red));
199
200				if (value === 2) {
201					result += 60;
202				}
203
204				return result;
205			},
206			enumerable: false,
207		},
208		rgbToAnsi: {
209			value: (red, green, blue) => styles.ansi256ToAnsi(styles.rgbToAnsi256(red, green, blue)),
210			enumerable: false,
211		},
212		hexToAnsi: {
213			value: hex => styles.ansi256ToAnsi(styles.hexToAnsi256(hex)),
214			enumerable: false,
215		},
216	});
217
218	return styles;
219}
220
221const ansiStyles = assembleStyles();
222
223export default ansiStyles;
224