Skip to content

Commit 2ceace1

Browse files
RichienbQix-
andauthored
Add support for ansi colors and add .ansi256ToAnsi(), .rgbToAnsi() and .hexToAnsi() (#71)
Co-authored-by: Qix <[email protected]>
1 parent b566b5b commit 2ceace1

File tree

4 files changed

+99
-3
lines changed

4 files changed

+99
-3
lines changed

index.d.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ export interface ColorBase {
1616
*/
1717
readonly close: string;
1818

19+
ansi(code: number): string;
20+
1921
ansi256(code: number): string;
2022

2123
ansi16m(red: number, green: number, blue: number): string;
@@ -153,6 +155,29 @@ export interface ConvertColor {
153155
@param hex - A hexadecimal string containing RGB data.
154156
*/
155157
hexToAnsi256(hex: string): number;
158+
159+
/**
160+
Convert from the ANSI 256 color space to the ANSI 16 color space.
161+
162+
@param code - A number representing the ANSI 256 color.
163+
*/
164+
ansi256ToAnsi(code: number): number;
165+
166+
/**
167+
Convert from the RGB color space to the ANSI 16 color space.
168+
169+
@param red - (`0...255`)
170+
@param green - (`0...255`)
171+
@param blue - (`0...255`)
172+
*/
173+
rgbToAnsi(red: number, green: number, blue: number): number;
174+
175+
/**
176+
Convert from the RGB HEX color space to the ANSI 16 color space.
177+
178+
@param hex - A hexadecimal string containing RGB data.
179+
*/
180+
hexToAnsi(hex: string): number;
156181
}
157182

158183
declare const ansiStyles: {

index.js

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
const ANSI_BACKGROUND_OFFSET = 10;
22

3+
const wrapAnsi16 = (offset = 0) => code => `\u001B[${code + offset}m`;
4+
35
const wrapAnsi256 = (offset = 0) => code => `\u001B[${38 + offset};5;${code}m`;
46

57
const wrapAnsi16m = (offset = 0) => (red, green, blue) => `\u001B[${38 + offset};2;${red};${green};${blue}m`;
@@ -93,8 +95,10 @@ function assembleStyles() {
9395
styles.color.close = '\u001B[39m';
9496
styles.bgColor.close = '\u001B[49m';
9597

98+
styles.color.ansi = wrapAnsi16();
9699
styles.color.ansi256 = wrapAnsi256();
97100
styles.color.ansi16m = wrapAnsi16m();
101+
styles.bgColor.ansi = wrapAnsi16(ANSI_BACKGROUND_OFFSET);
98102
styles.bgColor.ansi256 = wrapAnsi256(ANSI_BACKGROUND_OFFSET);
99103
styles.bgColor.ansi16m = wrapAnsi16m(ANSI_BACKGROUND_OFFSET);
100104

@@ -149,6 +153,58 @@ function assembleStyles() {
149153
hexToAnsi256: {
150154
value: hex => styles.rgbToAnsi256(...styles.hexToRgb(hex)),
151155
enumerable: false
156+
},
157+
ansi256ToAnsi: {
158+
value: code => {
159+
if (code < 8) {
160+
return 30 + code;
161+
}
162+
163+
if (code < 16) {
164+
return 90 + (code - 8);
165+
}
166+
167+
let red;
168+
let green;
169+
let blue;
170+
171+
if (code >= 232) {
172+
red = (((code - 232) * 10) + 8) / 255;
173+
green = red;
174+
blue = red;
175+
} else {
176+
code -= 16;
177+
178+
const remainder = code % 36;
179+
180+
red = Math.floor(code / 36) / 5;
181+
green = Math.floor(remainder / 6) / 5;
182+
blue = (remainder % 6) / 5;
183+
}
184+
185+
const value = Math.max(red, green, blue) * 2;
186+
187+
if (value === 0) {
188+
return 30;
189+
}
190+
191+
let result = 30 + ((Math.round(blue) << 2) | (Math.round(green) << 1) | Math.round(red));
192+
193+
if (value === 2) {
194+
result += 60;
195+
}
196+
197+
return result;
198+
},
199+
enumerable: false
200+
},
201+
rgbToAnsi: {
202+
value: (red, green, blue) => styles.ansi256ToAnsi(styles.rgbToAnsi256(red, green, blue)),
203+
enumerable: false
204+
},
205+
hexToAnsi: {
206+
value: hex => styles.ansi256ToAnsi(styles.hexToAnsi256(hex)),
207+
enumerable: false
152208
}
153209
});
154210

readme.md

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ console.log(`${styles.green.open}Hello world!${styles.green.close}`);
2525
// may be degraded to fit the new color palette. This means terminals
2626
// that do not support 16 million colors will best-match the
2727
// original color.
28+
console.log(`${styles.color.ansi(styles.rgbToAnsi(199, 20, 250))}Hello World${styles.color.close}`)
2829
console.log(`${styles.color.ansi256(styles.rgbToAnsi256(199, 20, 250))}Hello World${styles.color.close}`)
2930
console.log(`${styles.color.ansi16m(...styles.hexToRgb('#abcdef'))}Hello World${styles.color.close}`)
3031
```
@@ -112,21 +113,25 @@ console.log(styles.codes.get(36));
112113
//=> 39
113114
```
114115

115-
## [256 / 16 million (TrueColor) support](https://gist.github.com/XVilka/8346728)
116+
## 16 / 256 / 16 million (TrueColor) support
116117

117-
`ansi-styles` allows converting between various color formats and ANSI escapes, with support for 256 and 16 million colors.
118+
`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).
118119

119-
The following color spaces from `color-convert` are supported:
120+
The following color spaces are supported:
120121

121122
- `rgb`
122123
- `hex`
123124
- `ansi256`
125+
- `ansi`
124126

125127
To use these, call the associated conversion function with the intended output, for example:
126128

127129
```js
128130
import styles from 'ansi-styles';
129131

132+
styles.color.ansi(styles.rgbToAnsi(100, 200, 15)); // RGB to 16 color ansi foreground code
133+
styles.bgColor.ansi(styles.hexToAnsi('#C0FFEE')); // HEX to 16 color ansi foreground code
134+
130135
styles.color.ansi256(styles.rgbToAnsi256(100, 200, 15)); // RGB to 256 color ansi foreground code
131136
styles.bgColor.ansi256(styles.hexToAnsi256('#C0FFEE')); // HEX to 256 color ansi foreground code
132137

test/test.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,16 @@ test('groups should not be enumerable', t => {
1919
t.false(Object.keys(ansiStyles).includes('modifier'));
2020
});
2121

22+
test('support conversion to ansi (16 colors)', t => {
23+
t.is(ansiStyles.color.ansi(ansiStyles.rgbToAnsi(255, 255, 255)), '\u001B[97m');
24+
t.is(ansiStyles.color.ansi(ansiStyles.hexToAnsi('#990099')), '\u001B[35m');
25+
t.is(ansiStyles.color.ansi(ansiStyles.hexToAnsi('#FF00FF')), '\u001B[95m');
26+
27+
t.is(ansiStyles.bgColor.ansi(ansiStyles.rgbToAnsi(255, 255, 255)), '\u001B[107m');
28+
t.is(ansiStyles.bgColor.ansi(ansiStyles.hexToAnsi('#990099')), '\u001B[45m');
29+
t.is(ansiStyles.bgColor.ansi(ansiStyles.hexToAnsi('#FF00FF')), '\u001B[105m');
30+
});
31+
2232
test('support conversion to ansi (256 colors)', t => {
2333
t.is(ansiStyles.color.ansi256(ansiStyles.rgbToAnsi256(255, 255, 255)), '\u001B[38;5;231m');
2434
t.is(ansiStyles.color.ansi256(ansiStyles.hexToAnsi256('#990099')), '\u001B[38;5;127m');

0 commit comments

Comments
 (0)