diff --git a/index.d.ts b/index.d.ts index c31f21b..951bb95 100644 --- a/index.d.ts +++ b/index.d.ts @@ -16,6 +16,8 @@ export interface ColorBase { */ readonly close: string; + ansi(code: number): string; + ansi256(code: number): string; ansi16m(red: number, green: number, blue: number): string; @@ -153,6 +155,29 @@ export interface ConvertColor { @param hex - A hexadecimal string containing RGB data. */ hexToAnsi256(hex: string): number; + + /** + Convert from the ANSI 256 color space to the ANSI 16 color space. + + @param code - A number representing the ANSI 256 color. + */ + ansi256ToAnsi(code: number): number; + + /** + Convert from the RGB color space to the ANSI 16 color space. + + @param red - (`0...255`) + @param green - (`0...255`) + @param blue - (`0...255`) + */ + rgbToAnsi(red: number, green: number, blue: number): number; + + /** + Convert from the RGB HEX color space to the ANSI 16 color space. + + @param hex - A hexadecimal string containing RGB data. + */ + hexToAnsi(hex: string): number; } declare const ansiStyles: { diff --git a/index.js b/index.js index 5e16f63..e732c06 100644 --- a/index.js +++ b/index.js @@ -1,5 +1,7 @@ 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;${red};${green};${blue}m`; @@ -93,8 +95,10 @@ function assembleStyles() { 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); @@ -149,6 +153,58 @@ function assembleStyles() { 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; + } + + 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 } }); diff --git a/readme.md b/readme.md index 401f49d..4b07dbb 100644 --- a/readme.md +++ b/readme.md @@ -25,6 +25,7 @@ console.log(`${styles.green.open}Hello world!${styles.green.close}`); // may be degraded to fit the new color palette. This means terminals // that do not support 16 million colors will best-match the // original color. +console.log(`${styles.color.ansi(styles.rgbToAnsi(199, 20, 250))}Hello World${styles.color.close}`) console.log(`${styles.color.ansi256(styles.rgbToAnsi256(199, 20, 250))}Hello World${styles.color.close}`) console.log(`${styles.color.ansi16m(...styles.hexToRgb('#abcdef'))}Hello World${styles.color.close}`) ``` @@ -112,21 +113,25 @@ console.log(styles.codes.get(36)); //=> 39 ``` -## [256 / 16 million (TrueColor) support](https://gist.github.com/XVilka/8346728) +## 16 / 256 / 16 million (TrueColor) support -`ansi-styles` allows converting between various color formats and ANSI escapes, with support for 256 and 16 million colors. +`ansi-styles` allows converting between various color formats and ANSI escapes, with support for 16, 256 and [16 million colors](https://gist.github.com/XVilka/8346728). -The following color spaces from `color-convert` are supported: +The following color spaces are supported: - `rgb` - `hex` - `ansi256` +- `ansi` To use these, call the associated conversion function with the intended output, for example: ```js import styles from 'ansi-styles'; +styles.color.ansi(styles.rgbToAnsi(100, 200, 15)); // RGB to 16 color ansi foreground code +styles.bgColor.ansi(styles.hexToAnsi('#C0FFEE')); // HEX to 16 color ansi foreground code + styles.color.ansi256(styles.rgbToAnsi256(100, 200, 15)); // RGB to 256 color ansi foreground code styles.bgColor.ansi256(styles.hexToAnsi256('#C0FFEE')); // HEX to 256 color ansi foreground code diff --git a/test/test.js b/test/test.js index 930da22..ee11468 100644 --- a/test/test.js +++ b/test/test.js @@ -19,6 +19,16 @@ test('groups should not be enumerable', t => { t.false(Object.keys(ansiStyles).includes('modifier')); }); +test('support conversion to ansi (16 colors)', t => { + t.is(ansiStyles.color.ansi(ansiStyles.rgbToAnsi(255, 255, 255)), '\u001B[97m'); + t.is(ansiStyles.color.ansi(ansiStyles.hexToAnsi('#990099')), '\u001B[35m'); + t.is(ansiStyles.color.ansi(ansiStyles.hexToAnsi('#FF00FF')), '\u001B[95m'); + + t.is(ansiStyles.bgColor.ansi(ansiStyles.rgbToAnsi(255, 255, 255)), '\u001B[107m'); + t.is(ansiStyles.bgColor.ansi(ansiStyles.hexToAnsi('#990099')), '\u001B[45m'); + t.is(ansiStyles.bgColor.ansi(ansiStyles.hexToAnsi('#FF00FF')), '\u001B[105m'); +}); + test('support conversion to ansi (256 colors)', t => { t.is(ansiStyles.color.ansi256(ansiStyles.rgbToAnsi256(255, 255, 255)), '\u001B[38;5;231m'); t.is(ansiStyles.color.ansi256(ansiStyles.hexToAnsi256('#990099')), '\u001B[38;5;127m');