UNPKG

ansi-styles

Version:

ANSI escape codes for styling strings in the terminal

265 lines (225 loc) 6.96 kB
const ANSI_BACKGROUND_OFFSET = 10; const ANSI_UNDERLINE_OFFSET = 20; const wrapAnsi16 = (offset = 0) => code => `\u{1B}[${code + offset}m`; const wrapAnsi256 = (offset = 0) => code => `\u{1B}[${38 + offset};5;${code}m`; const wrapAnsi16m = (offset = 0) => (red, green, blue) => `\u{1B}[${38 + offset};2;${red};${green};${blue}m`; // `SGR 58` has no basic 16-color form, so the basic color code is mapped to its palette index instead. const wrapUnderlineAnsi = code => `\u{1B}[58;5;${code < 90 ? code - 30 : code - 90 + 8}m`; const blackBright = [90, 39]; const bgBlackBright = [100, 49]; const styles = { modifier: { reset: [0, 0], // 21 isn't widely supported and 22 does the same thing bold: [1, 22], dim: [2, 22], italic: [3, 23], underline: [4, 24], // Extended underline styles (`SGR 4:x` sub-parameters). Not in upstream `ansi-styles`. underlineDouble: ['4:2', 24], underlineCurly: ['4:3', 24], underlineDotted: ['4:4', 24], underlineDashed: ['4:5', 24], overline: [53, 55], inverse: [7, 27], hidden: [8, 28], strikethrough: [9, 29], }, color: { black: [30, 39], red: [31, 39], green: [32, 39], yellow: [33, 39], blue: [34, 39], magenta: [35, 39], cyan: [36, 39], white: [37, 39], // Bright color blackBright, gray: blackBright, grey: blackBright, redBright: [91, 39], greenBright: [92, 39], yellowBright: [93, 39], blueBright: [94, 39], magentaBright: [95, 39], cyanBright: [96, 39], whiteBright: [97, 39], }, bgColor: { bgBlack: [40, 49], bgRed: [41, 49], bgGreen: [42, 49], bgYellow: [43, 49], bgBlue: [44, 49], bgMagenta: [45, 49], bgCyan: [46, 49], bgWhite: [47, 49], // Bright color bgBlackBright, bgGray: bgBlackBright, bgGrey: bgBlackBright, bgRedBright: [101, 49], bgGreenBright: [102, 49], bgYellowBright: [103, 49], bgBlueBright: [104, 49], bgMagentaBright: [105, 49], bgCyanBright: [106, 49], bgWhiteBright: [107, 49], }, // Underline color (`SGR 58`/`59`). Not in upstream `ansi-styles`. underlineColor: { underlineBlack: ['58;5;0', 59], underlineRed: ['58;5;1', 59], underlineGreen: ['58;5;2', 59], underlineYellow: ['58;5;3', 59], underlineBlue: ['58;5;4', 59], underlineMagenta: ['58;5;5', 59], underlineCyan: ['58;5;6', 59], underlineWhite: ['58;5;7', 59], // Bright color underlineBlackBright: ['58;5;8', 59], underlineGray: ['58;5;8', 59], // Alias of `underlineBlackBright` underlineGrey: ['58;5;8', 59], // Alias of `underlineBlackBright` underlineRedBright: ['58;5;9', 59], underlineGreenBright: ['58;5;10', 59], underlineYellowBright: ['58;5;11', 59], underlineBlueBright: ['58;5;12', 59], underlineMagentaBright: ['58;5;13', 59], underlineCyanBright: ['58;5;14', 59], underlineWhiteBright: ['58;5;15', 59], }, }; export const modifierNames = Object.keys(styles.modifier); export const foregroundColorNames = Object.keys(styles.color); export const backgroundColorNames = Object.keys(styles.bgColor); export const underlineColorNames = Object.keys(styles.underlineColor); export const colorNames = [...foregroundColorNames, ...backgroundColorNames]; function assembleStyles() { const codes = new Map(); for (const [groupName, group] of Object.entries(styles)) { for (const [styleName, style] of Object.entries(group)) { styles[styleName] = { open: `\u{1B}[${style[0]}m`, close: `\u{1B}[${style[1]}m`, }; group[styleName] = styles[styleName]; // Only the leading SGR parameter identifies a style, so `4:3` and `58;5;1` are keyed as `4` and `58`. codes.set(Number(String(style[0]).split(/[:;]/v, 1)[0]), style[1]); } Object.defineProperty(styles, groupName, { value: group, enumerable: false, }); } Object.defineProperty(styles, 'codes', { value: codes, enumerable: false, }); styles.color.close = '\u{1B}[39m'; styles.bgColor.close = '\u{1B}[49m'; styles.underlineColor.close = '\u{1B}[59m'; styles.color.ansi = wrapAnsi16(); styles.color.ansi256 = wrapAnsi256(); styles.color.ansi16m = wrapAnsi16m(); styles.bgColor.ansi = wrapAnsi16(ANSI_BACKGROUND_OFFSET); styles.bgColor.ansi256 = wrapAnsi256(ANSI_BACKGROUND_OFFSET); styles.bgColor.ansi16m = wrapAnsi16m(ANSI_BACKGROUND_OFFSET); styles.underlineColor.ansi = wrapUnderlineAnsi; styles.underlineColor.ansi256 = wrapAnsi256(ANSI_UNDERLINE_OFFSET); styles.underlineColor.ansi16m = wrapAnsi16m(ANSI_UNDERLINE_OFFSET); // From https://github.com/Qix-/color-convert/blob/3f0e0d4e92e235796ccb17f6e85c72094a651f49/conversions.js Object.defineProperties(styles, { rgbToAnsi256: { value(red, green, blue) { // We use the extended greyscale palette here, with the exception of // black and white. normal palette only has 4 greyscale shades. if (red === green && green === blue) { if (red < 8) { return 16; } if (red > 248) { return 231; } return Math.round(((red - 8) / 247) * 24) + 232; } return 16 + (36 * Math.round(red / 255 * 5)) + (6 * Math.round(green / 255 * 5)) + Math.round(blue / 255 * 5); }, enumerable: false, }, hexToRgb: { value(hex) { const matches = /[\da-f]{6}|[\da-f]{3}/iv.exec(hex.toString(16)); if (!matches) { return [0, 0, 0]; } let [colorString] = matches; if (colorString.length === 3) { colorString = [...colorString].map(character => character + character).join(''); } const integer = Number.parseInt(colorString, 16); return [ /* eslint-disable no-bitwise -- We need the speed */ (integer >> 16) & 0xFF, (integer >> 8) & 0xFF, integer & 0xFF, /* eslint-enable no-bitwise */ ]; }, enumerable: false, }, hexToAnsi256: { value: hex => styles.rgbToAnsi256(...styles.hexToRgb(hex)), enumerable: false, }, ansi256ToAnsi: { value(code) { if (code < 8) { return 30 + code; } if (code < 16) { return 90 + (code - 8); } let red; let green; let blue; if (code >= 232) { red = (((code - 232) * 10) + 8) / 255; green = red; blue = red; } else { code -= 16; const remainder = code % 36; red = Math.floor(code / 36) / 5; green = Math.floor(remainder / 6) / 5; blue = (remainder % 6) / 5; } const value = Math.max(red, green, blue) * 2; if (value === 0) { return 30; } // eslint-disable-next-line no-bitwise let result = 30 + ((Math.round(blue) << 2) | (Math.round(green) << 1) | Math.round(red)); if (value === 2) { result += 60; } return result; }, enumerable: false, }, rgbToAnsi: { value: (red, green, blue) => styles.ansi256ToAnsi(styles.rgbToAnsi256(red, green, blue)), enumerable: false, }, hexToAnsi: { value: hex => styles.ansi256ToAnsi(styles.hexToAnsi256(hex)), enumerable: false, }, }); return styles; } const ansiStyles = assembleStyles(); export default ansiStyles;