From 4cf2e40e0739d4f5a3f1cd235b0e05e979bc5f34 Mon Sep 17 00:00:00 2001 From: Richie Bendall Date: Thu, 22 Apr 2021 15:54:42 +1200 Subject: [PATCH] Add `overline` style and remove `keyword`, `hsl`, `hsv`, `hwb` and `ansi` color spaces (#433) --- examples/rainbow.js | 14 +++--- examples/screenshot.js | 2 +- index.d.ts | 99 ++++++---------------------------------- index.test-d.ts | 15 ++---- package.json | 7 ++- readme.md | 14 ++---- source/index.js | 26 +++++++++-- test/template-literal.js | 10 ++-- 8 files changed, 61 insertions(+), 126 deletions(-) diff --git a/examples/rainbow.js b/examples/rainbow.js index 67dd236..9ea1d72 100644 --- a/examples/rainbow.js +++ b/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; @@ -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; } } @@ -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 } } diff --git a/examples/screenshot.js b/examples/screenshot.js index ea61e0c..6d5ed15 100644 --- a/examples/screenshot.js +++ b/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)) { diff --git a/index.d.ts b/index.d.ts index a1d2857..a21fa89 100644 --- a/index.d.ts +++ b/index.d.ts @@ -61,6 +61,7 @@ export type Modifiers = | 'dim' | 'italic' | 'underline' + | 'overline' | 'inverse' | 'hidden' | 'strikethrough' @@ -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. @@ -178,51 +184,14 @@ 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. + Use an [8-bit unsigned number](https://en.wikipedia.org/wiki/ANSI_escape_code#8-bit) 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. - - 30 <= code && code < 38 || 90 <= code && code < 98 - For example, 31 for red, 91 for redBright. - */ - ansi: (code: number) => this; + ansi256: (index: number) => this; /** - Use a [8-bit unsigned number](https://en.wikipedia.org/wiki/ANSI_escape_code#8-bit) to set text color. + Use RGB values to set background color. */ - ansi256: (index: number) => this; + bgRgb: (red: number, green: number, blue: number) => this; /** Use HEX value to set background color. @@ -238,49 +207,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. - - 30 <= code && code < 38 || 90 <= code && code < 98 - For example, 31 for red, 91 for redBright. - Use the foreground code, not the background code (for example, not 41, nor 101). - */ - bgAnsi: (code: number) => this; - /** Use a [8-bit unsigned number](https://en.wikipedia.org/wiki/ANSI_escape_code#8-bit) to set background color. */ @@ -311,6 +237,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. */ diff --git a/index.test-d.ts b/index.test-d.ts index 3e7e2d6..45299d6 100644 --- a/index.test-d.ts +++ b/index.test-d.ts @@ -41,21 +41,11 @@ expectType(chalk`Hello {bold.red ${name}}`); expectType(chalk`Works with numbers {bold.red ${1}}`); // -- Color methods -- -expectAssignable(chalk.hex('#DEADED')); -expectAssignable(chalk.keyword('orange')); expectAssignable(chalk.rgb(0, 0, 0)); -expectAssignable(chalk.hsl(0, 0, 0)); -expectAssignable(chalk.hsv(0, 0, 0)); -expectAssignable(chalk.hwb(0, 0, 0)); -expectAssignable(chalk.ansi(30)); +expectAssignable(chalk.hex('#DEADED')); expectAssignable(chalk.ansi256(0)); -expectAssignable(chalk.bgHex('#DEADED')); -expectAssignable(chalk.bgKeyword('orange')); expectAssignable(chalk.bgRgb(0, 0, 0)); -expectAssignable(chalk.bgHsl(0, 0, 0)); -expectAssignable(chalk.bgHsv(0, 0, 0)); -expectAssignable(chalk.bgHwb(0, 0, 0)); -expectAssignable(chalk.bgAnsi(30)); +expectAssignable(chalk.bgHex('#DEADED')); expectAssignable(chalk.bgAnsi256(0)); // -- Modifiers -- @@ -64,6 +54,7 @@ expectType(chalk.bold('foo')); expectType(chalk.dim('foo')); expectType(chalk.italic('foo')); expectType(chalk.underline('foo')); +expectType(chalk.overline('foo')); expectType(chalk.inverse('foo')); expectType(chalk.hidden('foo')); expectType(chalk.strikethrough('foo')); diff --git a/package.json b/package.json index 9e6f031..2509b62 100644 --- a/package.json +++ b/package.json @@ -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": { diff --git a/readme.md b/readme.md index 45e4be7..6d92165 100644 --- a/readme.md +++ b/readme.md @@ -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!')); ``` @@ -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!')); @@ -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. @@ -288,24 +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')` ## Browser support diff --git a/source/index.js b/source/index.js index 8db69d2..70f7a5d 100644 --- a/source/index.js +++ b/source/index.js @@ -74,14 +74,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']; 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); }; } @@ -92,7 +112,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); }; } diff --git a/test/template-literal.js b/test/template-literal.js index 773e097..e8d5950 100644 --- a/test/template-literal.js +++ b/test/template-literal.js @@ -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\')'); } });