Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add overline style and remove keyword, hsl, hsv, hwb and ansi color spaces #433

Merged
merged 11 commits into from Apr 22, 2021
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
14 changes: 6 additions & 8 deletions examples/rainbow.js
@@ -1,11 +1,10 @@
import chalk from '../index.js';
import chalk from '../source/index.js';
import convertColor from 'color-convert';
import updateLog from 'log-update';
import delay from 'yoctodelay';

const ignoreChars = /[^!-~]/g;

const delay = milliseconds => new Promise(resolve => {
setTimeout(resolve, milliseconds);
});

function rainbow(string, offset) {
if (!string || string.length === 0) {
return string;
Expand All @@ -19,7 +18,7 @@ function rainbow(string, offset) {
if (ignoreChars.test(character)) {
characters.push(character);
} else {
characters.push(chalk.hsl(hue, 100, 50)(character));
characters.push(chalk.hex(convertColor.hsl.hex(hue, 100, 50))(character));
hue = (hue + hueStep) % 360;
}
}
Expand All @@ -28,9 +27,8 @@ function rainbow(string, offset) {
}

async function animateString(string) {
console.log();
for (let index = 0; index < 360 * 5; index++) {
console.log('\u001B[1F\u001B[G', rainbow(string, index));
updateLog(rainbow(string, index));
await delay(2); // eslint-disable-line no-await-in-loop
}
}
Expand Down
2 changes: 1 addition & 1 deletion examples/screenshot.js
@@ -1,5 +1,5 @@
import styles from 'ansi-styles';
import chalk from '../index.js';
import chalk from '../source/index.js';

// Generates screenshot
for (const key of Object.keys(styles)) {
Expand Down
84 changes: 16 additions & 68 deletions index.d.ts
Expand Up @@ -61,6 +61,7 @@ export type Modifiers =
| 'dim'
| 'italic'
| 'underline'
| 'overline'
| 'inverse'
| 'hidden'
| 'strikethrough'
Expand Down Expand Up @@ -163,6 +164,11 @@ export interface ChalkInstance extends ChalkFunction {
*/
level: ColorSupportLevel;

/**
Use RGB values to set text color.
*/
rgb: (red: number, green: number, blue: number) => this;

/**
Use HEX value to set text color.

Expand All @@ -177,40 +183,6 @@ export interface ChalkInstance extends ChalkFunction {
*/
hex: (color: string) => this;

/**
Use keyword color value to set text color.

@param color - Keyword value representing the desired color.

@example
```
import chalk from 'chalk';

chalk.keyword('orange');
```
*/
keyword: (color: string) => this;

/**
Use RGB values to set text color.
*/
rgb: (red: number, green: number, blue: number) => this;

/**
Use HSL values to set text color.
*/
hsl: (hue: number, saturation: number, lightness: number) => this;

/**
Use HSV values to set text color.
*/
hsv: (hue: number, saturation: number, value: number) => this;

/**
Use HWB values to set text color.
*/
hwb: (hue: number, whiteness: number, blackness: number) => this;

/**
Use a [Select/Set Graphic Rendition](https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_parameters) (SGR) [color code number](https://en.wikipedia.org/wiki/ANSI_escape_code#3/4_bit) to set text color.

Expand All @@ -224,6 +196,11 @@ export interface ChalkInstance extends ChalkFunction {
*/
ansi256: (index: number) => this;

/**
Use RGB values to set background color.
*/
bgRgb: (red: number, green: number, blue: number) => this;

/**
Use HEX value to set background color.

Expand All @@ -238,40 +215,6 @@ export interface ChalkInstance extends ChalkFunction {
*/
bgHex: (color: string) => this;

/**
Use keyword color value to set background color.

@param color - Keyword value representing the desired color.

@example
```
import chalk from 'chalk';

chalk.bgKeyword('orange');
```
*/
bgKeyword: (color: string) => this;

/**
Use RGB values to set background color.
*/
bgRgb: (red: number, green: number, blue: number) => this;

/**
Use HSL values to set background color.
*/
bgHsl: (hue: number, saturation: number, lightness: number) => this;

/**
Use HSV values to set background color.
*/
bgHsv: (hue: number, saturation: number, value: number) => this;

/**
Use HWB values to set background color.
*/
bgHwb: (hue: number, whiteness: number, blackness: number) => this;

/**
Use a [Select/Set Graphic Rendition](https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_parameters) (SGR) [color code number](https://en.wikipedia.org/wiki/ANSI_escape_code#3/4_bit) to set background color.

Expand Down Expand Up @@ -311,6 +254,11 @@ export interface ChalkInstance extends ChalkFunction {
*/
readonly underline: this;

/**
Modifier: Make text overline. (Not widely supported)
*/
readonly overline: this;

/**
Modifier: Inverse background and foreground colors.
*/
Expand Down
13 changes: 3 additions & 10 deletions index.test-d.ts
Expand Up @@ -41,20 +41,12 @@ expectType<string>(chalk`Hello {bold.red ${name}}`);
expectType<string>(chalk`Works with numbers {bold.red ${1}}`);

// -- Color methods --
expectAssignable<colorReturn>(chalk.hex('#DEADED'));
expectAssignable<colorReturn>(chalk.keyword('orange'));
expectAssignable<colorReturn>(chalk.rgb(0, 0, 0));
expectAssignable<colorReturn>(chalk.hsl(0, 0, 0));
expectAssignable<colorReturn>(chalk.hsv(0, 0, 0));
expectAssignable<colorReturn>(chalk.hwb(0, 0, 0));
expectAssignable<colorReturn>(chalk.hex('#DEADED'));
expectAssignable<colorReturn>(chalk.ansi(30));
expectAssignable<colorReturn>(chalk.ansi256(0));
expectAssignable<colorReturn>(chalk.bgHex('#DEADED'));
expectAssignable<colorReturn>(chalk.bgKeyword('orange'));
expectAssignable<colorReturn>(chalk.bgRgb(0, 0, 0));
expectAssignable<colorReturn>(chalk.bgHsl(0, 0, 0));
expectAssignable<colorReturn>(chalk.bgHsv(0, 0, 0));
expectAssignable<colorReturn>(chalk.bgHwb(0, 0, 0));
expectAssignable<colorReturn>(chalk.bgHex('#DEADED'));
expectAssignable<colorReturn>(chalk.bgAnsi(30));
expectAssignable<colorReturn>(chalk.bgAnsi256(0));

Expand All @@ -64,6 +56,7 @@ expectType<string>(chalk.bold('foo'));
expectType<string>(chalk.dim('foo'));
expectType<string>(chalk.italic('foo'));
expectType<string>(chalk.underline('foo'));
expectType<string>(chalk.overline('foo'));
expectType<string>(chalk.inverse('foo'));
expectType<string>(chalk.hidden('foo'));
expectType<string>(chalk.strikethrough('foo'));
Expand Down
7 changes: 5 additions & 2 deletions package.json
Expand Up @@ -42,17 +42,20 @@
"text"
],
"dependencies": {
"ansi-styles": "^4.1.0",
"ansi-styles": "^6.1.0",
"supports-color": "^9.0.0"
},
"devDependencies": {
"ava": "^3.15.0",
"color-convert": "^2.0.1",
"coveralls": "^3.1.0",
"execa": "^5.0.0",
"log-update": "^4.0.0",
"matcha": "^0.7.0",
"nyc": "^15.1.0",
"tsd": "^0.14.0",
"xo": "^0.38.2"
"xo": "^0.38.2",
"yoctodelay": "^1.2.0"
},
"xo": {
"rules": {
Expand Down
13 changes: 3 additions & 10 deletions readme.md
Expand Up @@ -125,7 +125,6 @@ DISK: {rgb(255,131,0) ${disk.used / disk.total * 100}%}
`);

// Use RGB colors in terminal emulators that support it.
log(chalk.keyword('orange')('Yay for orange colored text!'));
log(chalk.rgb(123, 45, 67).underline('Underlined reddish color'));
log(chalk.hex('#DEADED').bold('Bold gray!'));
```
Expand All @@ -136,7 +135,7 @@ Easily define your own themes:
import chalk from 'chalk';

const error = chalk.bold.red;
const warning = chalk.keyword('orange');
const warning = chalk.hex('#FFA500'); // Orange color

console.log(error('Error!'));
console.log(warning('Warning!'));
Expand Down Expand Up @@ -275,7 +274,7 @@ console.log(chalk.bold.rgb(10, 100, 200)`Hello!`);
console.log(chalk`{bold.rgb(10,100,200) Hello!}`);
```

Note that function styles (`rgb()`, `hsl()`, `keyword()`, etc.) may not contain spaces between parameters.
Note that function styles (`rgb()`, `hex()`, etc.) may not contain spaces between parameters.

All interpolated values (`` chalk`${foo}` ``) are converted to strings via the `.toString()` method. All curly braces (`{` and `}`) in interpolated value strings are escaped.

Expand All @@ -288,23 +287,17 @@ Colors are downsampled from 16 million RGB values to an ANSI color format that i
Examples:

- `chalk.hex('#DEADED').underline('Hello, world!')`
- `chalk.keyword('orange')('Some orange text')`
- `chalk.rgb(15, 100, 204).inverse('Hello!')`

Background versions of these models are prefixed with `bg` and the first level of the module capitalized (e.g. `keyword` for foreground colors and `bgKeyword` for background colors).
Background versions of these models are prefixed with `bg` and the first level of the module capitalized (e.g. `hex` for foreground colors and `bgHex` for background colors).

- `chalk.bgHex('#DEADED').underline('Hello, world!')`
- `chalk.bgKeyword('orange')('Some orange text')`
- `chalk.bgRgb(15, 100, 204).inverse('Hello!')`

The following color models can be used:

- [`rgb`](https://en.wikipedia.org/wiki/RGB_color_model) - Example: `chalk.rgb(255, 136, 0).bold('Orange!')`
- [`hex`](https://en.wikipedia.org/wiki/Web_colors#Hex_triplet) - Example: `chalk.hex('#FF8800').bold('Orange!')`
- [`keyword`](https://www.w3.org/wiki/CSS/Properties/color/keywords) (CSS keywords) - Example: `chalk.keyword('orange').bold('Orange!')`
- [`hsl`](https://en.wikipedia.org/wiki/HSL_and_HSV) - Example: `chalk.hsl(32, 100, 50).bold('Orange!')`
- [`hsv`](https://en.wikipedia.org/wiki/HSL_and_HSV) - Example: `chalk.hsv(32, 100, 100).bold('Orange!')`
- [`hwb`](https://en.wikipedia.org/wiki/HWB_color_model) - Example: `chalk.hwb(32, 0, 50).bold('Orange!')`
- [`ansi`](https://en.wikipedia.org/wiki/ANSI_escape_code#3/4_bit) - Example: `chalk.ansi(31).bgAnsi(93)('red on yellowBright')`
- [`ansi256`](https://en.wikipedia.org/wiki/ANSI_escape_code#8-bit) - Example: `chalk.bgAnsi256(194)('Honeydew, more or less')`

Expand Down
26 changes: 23 additions & 3 deletions source/index.js
Expand Up @@ -72,14 +72,34 @@ styles.visible = {
}
};

const usedModels = ['rgb', 'hex', 'keyword', 'hsl', 'hsv', 'hwb', 'ansi', 'ansi256'];
const getModelAnsi = (model, level, type, ...arguments_) => {
if (model === 'rgb') {
if (level === 'ansi16m') {
return ansiStyles[type].ansi16m(...arguments_);
}

if (level === 'ansi256') {
return ansiStyles[type].ansi256(ansiStyles.rgbToAnsi256(...arguments_));
}

return ansiStyles[type].ansi(ansiStyles.rgbToAnsi(...arguments_));
}

if (model === 'hex') {
return getModelAnsi('rgb', level, type, ...ansiStyles.hexToRgb(...arguments_));
}

return ansiStyles[type][model](...arguments_);
};

const usedModels = ['rgb', 'hex', 'ansi256', 'ansi'];

for (const model of usedModels) {
styles[model] = {
get() {
const {level} = this;
return function (...arguments_) {
const styler = createStyler(ansiStyles.color[levelMapping[level]][model](...arguments_), ansiStyles.color.close, this._styler);
const styler = createStyler(getModelAnsi(model, levelMapping[level], 'color', ...arguments_), ansiStyles.color.close, this._styler);
return createBuilder(this, styler, this._isEmpty);
};
}
Expand All @@ -90,7 +110,7 @@ for (const model of usedModels) {
get() {
const {level} = this;
return function (...arguments_) {
const styler = createStyler(ansiStyles.bgColor[levelMapping[level]][model](...arguments_), ansiStyles.bgColor.close, this._styler);
const styler = createStyler(getModelAnsi(model, levelMapping[level], 'bgColor', ...arguments_), ansiStyles.bgColor.close, this._styler);
return createBuilder(this, styler, this._isEmpty);
};
}
Expand Down
10 changes: 5 additions & 5 deletions test/template-literal.js
Expand Up @@ -150,18 +150,18 @@ test('correctly parses unicode/hex escapes', t => {

test('correctly parses string arguments', t => {
const instance = new Chalk({level: 3});
t.is(instance`{keyword('black').bold can haz cheezburger}`, '\u001B[38;2;0;0;0m\u001B[1mcan haz cheezburger\u001B[22m\u001B[39m');
t.is(instance`{keyword('blac\x6B').bold can haz cheezburger}`, '\u001B[38;2;0;0;0m\u001B[1mcan haz cheezburger\u001B[22m\u001B[39m');
t.is(instance`{keyword('blac\u006B').bold can haz cheezburger}`, '\u001B[38;2;0;0;0m\u001B[1mcan haz cheezburger\u001B[22m\u001B[39m');
t.is(instance`{hex('#000000').bold can haz cheezburger}`, '\u001B[38;2;0;0;0m\u001B[1mcan haz cheezburger\u001B[22m\u001B[39m');
t.is(instance`{hex('#00000\x30').bold can haz cheezburger}`, '\u001B[38;2;0;0;0m\u001B[1mcan haz cheezburger\u001B[22m\u001B[39m');
t.is(instance`{hex('#00000\u0030').bold can haz cheezburger}`, '\u001B[38;2;0;0;0m\u001B[1mcan haz cheezburger\u001B[22m\u001B[39m');
});

test('throws if a bad argument is encountered', t => {
const instance = new Chalk({level: 3}); // Keep level at least 1 in case we optimize for disabled chalk instances
try {
console.log(instance`{keyword(????) hi}`);
console.log(instance`{hex(????) hi}`);
t.fail();
} catch (error) {
t.is(error.message, 'Invalid Chalk template style argument: ???? (in style \'keyword\')');
t.is(error.message, 'Invalid Chalk template style argument: ???? (in style \'hex\')');
}
});

Expand Down