Currently if you pass decimal values to rgb or bgRgb, it will generate output with those decimals, which iTerm silently ignores. Not sure if this is the ideal fix, since when rgb values are from 0-255 it's *commonly* assumed that they're integers. Maybe chalk should just throw an error instead. But this is a bug I came across when converting from 0-1 r,g,b, which is common, so there should be at least some indication.
219 lines
5.1 KiB
JavaScript
219 lines
5.1 KiB
JavaScript
const ANSI_BACKGROUND_OFFSET = 10;
|
|
|
|
const wrapAnsi16 = (offset = 0) => code => `\u001B[${code + offset}m`;
|
|
|
|
const wrapAnsi256 = (offset = 0) => code => `\u001B[${38 + offset};5;${code}m`;
|
|
|
|
const wrapAnsi16m = (offset = 0) => (red, green, blue) => `\u001B[${38 + offset};2;${Math.round(red)};${Math.round(green)};${Math.round(blue)}m`;
|
|
|
|
function assembleStyles() {
|
|
const codes = new Map();
|
|
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],
|
|
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: [90, 39],
|
|
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: [100, 49],
|
|
bgRedBright: [101, 49],
|
|
bgGreenBright: [102, 49],
|
|
bgYellowBright: [103, 49],
|
|
bgBlueBright: [104, 49],
|
|
bgMagentaBright: [105, 49],
|
|
bgCyanBright: [106, 49],
|
|
bgWhiteBright: [107, 49],
|
|
},
|
|
};
|
|
|
|
// Alias bright black as gray (and grey)
|
|
styles.color.gray = styles.color.blackBright;
|
|
styles.bgColor.bgGray = styles.bgColor.bgBlackBright;
|
|
styles.color.grey = styles.color.blackBright;
|
|
styles.bgColor.bgGrey = styles.bgColor.bgBlackBright;
|
|
|
|
for (const [groupName, group] of Object.entries(styles)) {
|
|
for (const [styleName, style] of Object.entries(group)) {
|
|
styles[styleName] = {
|
|
open: `\u001B[${style[0]}m`,
|
|
close: `\u001B[${style[1]}m`,
|
|
};
|
|
|
|
group[styleName] = styles[styleName];
|
|
|
|
codes.set(style[0], style[1]);
|
|
}
|
|
|
|
Object.defineProperty(styles, groupName, {
|
|
value: group,
|
|
enumerable: false,
|
|
});
|
|
}
|
|
|
|
Object.defineProperty(styles, 'codes', {
|
|
value: codes,
|
|
enumerable: false,
|
|
});
|
|
|
|
styles.color.close = '\u001B[39m';
|
|
styles.bgColor.close = '\u001B[49m';
|
|
|
|
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);
|
|
|
|
// 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 = /(?<colorString>[a-f\d]{6}|[a-f\d]{3})/i.exec(hex.toString(16));
|
|
if (!matches) {
|
|
return [0, 0, 0];
|
|
}
|
|
|
|
let {colorString} = matches.groups;
|
|
|
|
if (colorString.length === 3) {
|
|
colorString = [...colorString].map(character => character + character).join('');
|
|
}
|
|
|
|
const integer = Number.parseInt(colorString, 16);
|
|
|
|
return [
|
|
/* eslint-disable no-bitwise */
|
|
(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;
|