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