Skip to content

Commit

Permalink
Add support for ansi colors and add .ansi256ToAnsi(), `.rgbToAnsi…
Browse files Browse the repository at this point in the history
…()` and `.hexToAnsi()` (#71)

Co-authored-by: Qix <Qix-@users.noreply.github.com>
  • Loading branch information
Richienb and Qix- committed Apr 21, 2021
1 parent b566b5b commit 2ceace1
Show file tree
Hide file tree
Showing 4 changed files with 99 additions and 3 deletions.
25 changes: 25 additions & 0 deletions index.d.ts
Expand Up @@ -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;
Expand Down Expand Up @@ -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: {
Expand Down
56 changes: 56 additions & 0 deletions 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`;
Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -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
}
});

Expand Down
11 changes: 8 additions & 3 deletions readme.md
Expand Up @@ -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}`)
```
Expand Down Expand Up @@ -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

Expand Down
10 changes: 10 additions & 0 deletions test/test.js
Expand Up @@ -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');
Expand Down

0 comments on commit 2ceace1

Please sign in to comment.