forked from orbit-oss/chalk
Add RGB (256/Truecolor) support (#140)
This commit is contained in:
parent
dbae68d623
commit
cb3f2308e1
4 changed files with 155 additions and 36 deletions
68
index.js
68
index.js
|
|
@ -6,9 +6,14 @@ var supportsColor = require('supports-color');
|
|||
var defineProps = Object.defineProperties;
|
||||
var isSimpleWindowsTerm = process.platform === 'win32' && !/^xterm/i.test(process.env.TERM);
|
||||
|
||||
// supportsColor.level -> ansiStyles.color[name] mapping
|
||||
var levelMapping = ['ansi', 'ansi', 'ansi256', 'ansi16m'];
|
||||
// color-convert models to exclude from the Chalk API due to conflicts and such.
|
||||
var skipModels = ['gray'];
|
||||
|
||||
function Chalk(options) {
|
||||
// detect mode if not set manually
|
||||
this.enabled = !options || options.enabled === undefined ? supportsColor : options.enabled;
|
||||
// detect level if not set manually
|
||||
this.level = !options || options.level === undefined ? supportsColor.level : options.level;
|
||||
}
|
||||
|
||||
// use bright blue on Windows as the normal blue color is illegible
|
||||
|
|
@ -23,7 +28,45 @@ Object.keys(ansiStyles).forEach(function (key) {
|
|||
|
||||
styles[key] = {
|
||||
get: function () {
|
||||
return build.call(this, this._styles ? this._styles.concat(key) : [key]);
|
||||
var codes = ansiStyles[key];
|
||||
return build.call(this, this._styles ? this._styles.concat(codes) : [codes], key);
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
ansiStyles.color.closeRe = new RegExp(escapeStringRegexp(ansiStyles.color.close), 'g');
|
||||
Object.keys(ansiStyles.color.ansi).forEach(function (model) {
|
||||
if (skipModels.indexOf(model) !== -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
styles[model] = {
|
||||
get: function () {
|
||||
var level = this.level;
|
||||
return function () {
|
||||
var open = ansiStyles.color[levelMapping[level]][model].apply(null, arguments);
|
||||
var codes = {open: open, close: ansiStyles.color.close, closeRe: ansiStyles.color.closeRe};
|
||||
return build.call(this, this._styles ? this._styles.concat(codes) : [codes], model);
|
||||
};
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
ansiStyles.bgColor.closeRe = new RegExp(escapeStringRegexp(ansiStyles.bgColor.close), 'g');
|
||||
Object.keys(ansiStyles.bgColor.ansi).forEach(function (model) {
|
||||
if (skipModels.indexOf(model) !== -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
var bgModel = 'bg' + model.charAt(0).toUpperCase() + model.substring(1);
|
||||
styles[bgModel] = {
|
||||
get: function () {
|
||||
var level = this.level;
|
||||
return function () {
|
||||
var open = ansiStyles.bgColor[levelMapping[level]][model].apply(null, arguments);
|
||||
var codes = {open: open, close: ansiStyles.bgColor.close, closeRe: ansiStyles.bgColor.closeRe};
|
||||
return build.call(this, this._styles ? this._styles.concat(codes) : [codes], model);
|
||||
};
|
||||
}
|
||||
};
|
||||
});
|
||||
|
|
@ -31,7 +74,7 @@ Object.keys(ansiStyles).forEach(function (key) {
|
|||
// eslint-disable-next-line func-names
|
||||
var proto = defineProps(function chalk() {}, styles);
|
||||
|
||||
function build(_styles) {
|
||||
function build(_styles, key) {
|
||||
var builder = function () {
|
||||
return applyStyle.apply(builder, arguments);
|
||||
};
|
||||
|
|
@ -40,16 +83,19 @@ function build(_styles) {
|
|||
|
||||
builder._styles = _styles;
|
||||
|
||||
Object.defineProperty(builder, 'enabled', {
|
||||
Object.defineProperty(builder, 'level', {
|
||||
enumerable: true,
|
||||
get: function () {
|
||||
return self.enabled;
|
||||
return self.level;
|
||||
},
|
||||
set: function (v) {
|
||||
self.enabled = v;
|
||||
set: function (level) {
|
||||
self.level = level;
|
||||
}
|
||||
});
|
||||
|
||||
// see below for fix regarding invisible grey/dim combination on windows.
|
||||
builder.hasGrey = this.hasGrey || key === 'gray' || key === 'grey';
|
||||
|
||||
// __proto__ is used because we must return a function, but there is
|
||||
// no way to create a function with a different prototype.
|
||||
/* eslint-disable no-proto */
|
||||
|
|
@ -71,7 +117,7 @@ function applyStyle() {
|
|||
}
|
||||
}
|
||||
|
||||
if (!this.enabled || !str) {
|
||||
if (!this.level || !str) {
|
||||
return str;
|
||||
}
|
||||
|
||||
|
|
@ -82,12 +128,12 @@ function applyStyle() {
|
|||
// see https://github.com/chalk/chalk/issues/58
|
||||
// If we're on Windows and we're dealing with a gray color, temporarily make 'dim' a noop.
|
||||
var originalDim = ansiStyles.dim.open;
|
||||
if (isSimpleWindowsTerm && (nestedStyles.indexOf('gray') !== -1 || nestedStyles.indexOf('grey') !== -1)) {
|
||||
if (isSimpleWindowsTerm && this.hasGrey) {
|
||||
ansiStyles.dim.open = '';
|
||||
}
|
||||
|
||||
while (i--) {
|
||||
var code = ansiStyles[nestedStyles[i]];
|
||||
var code = nestedStyles[i];
|
||||
|
||||
// Replace any instances already present with a re-opening code
|
||||
// otherwise only the part of the string until said closing code
|
||||
|
|
|
|||
|
|
@ -44,9 +44,9 @@
|
|||
"text"
|
||||
],
|
||||
"dependencies": {
|
||||
"ansi-styles": "^2.1.0",
|
||||
"ansi-styles": "^3.0.0",
|
||||
"escape-string-regexp": "^1.0.2",
|
||||
"supports-color": "^3.1.2"
|
||||
"supports-color": "^3.2.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"coveralls": "^2.11.2",
|
||||
|
|
|
|||
59
readme.md
59
readme.md
|
|
@ -76,14 +76,23 @@ CPU: ${chalk.red('90%')}
|
|||
RAM: ${chalk.green('40%')}
|
||||
DISK: ${chalk.yellow('70%')}
|
||||
`);
|
||||
|
||||
// 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!'));
|
||||
```
|
||||
|
||||
Easily define your own themes.
|
||||
|
||||
```js
|
||||
const chalk = require('chalk');
|
||||
|
||||
const error = chalk.bold.red;
|
||||
const warning = chalk.keyword('orange');
|
||||
|
||||
console.log(error('Error!'));
|
||||
console.log(warning('Warning!'));
|
||||
```
|
||||
|
||||
Take advantage of console.log [string substitution](http://nodejs.org/docs/latest/api/console.html#console_console_log_data).
|
||||
|
|
@ -105,16 +114,23 @@ Chain [styles](#styles) and call the last one as a method with a string argument
|
|||
|
||||
Multiple arguments will be separated by space.
|
||||
|
||||
### chalk.enabled
|
||||
### chalk.level
|
||||
|
||||
Color support is automatically detected, but you can override it by setting the `enabled` property. You should however only do this in your own code as it applies globally to all chalk consumers.
|
||||
Color support is automatically detected, but you can override it by setting the `level` property. You should however only do this in your own code as it applies globally to all chalk consumers.
|
||||
|
||||
If you need to change this in a reusable module create a new instance:
|
||||
|
||||
```js
|
||||
const ctx = new chalk.constructor({enabled: false});
|
||||
const ctx = new chalk.constructor({level: 0});
|
||||
```
|
||||
|
||||
Levels are as follows:
|
||||
|
||||
0. All colors disabled
|
||||
1. Basic color support (16 colors)
|
||||
2. 256 color support
|
||||
3. RGB/Truecolor support (16 million colors)
|
||||
|
||||
### chalk.supportsColor
|
||||
|
||||
Detect whether the terminal [supports color](https://github.com/chalk/supports-color). Used internally and handled for you, but exposed for convenience.
|
||||
|
|
@ -174,10 +190,42 @@ console.log(chalk.styles.red.open + 'Hello' + chalk.styles.red.close);
|
|||
- `bgWhite`
|
||||
|
||||
|
||||
## 256-colors
|
||||
## 256/16 million (Truecolor) color support
|
||||
|
||||
Chalk does not support anything other than the base eight colors, which guarantees it will work on all terminals and systems. Some terminals, specifically `xterm` compliant ones, will support the full range of 8-bit colors. For this the lower level [ansi-256-colors](https://github.com/jbnicolai/ansi-256-colors) package can be used.
|
||||
Chalk supports 256 colors and, when manually specified, [Truecolor (16 million colors)](https://gist.github.com/XVilka/8346728) on all supported terminal emulators.
|
||||
|
||||
Colors are downsampled from 16 million RGB values to an ANSI color format that is supported by the terminal emulator (or by specifying {level: n} as a chalk option). For example, Chalk configured to run at level 1 (basic color support) will downsample an RGB value of #FF0000 (red) to 31 (ANSI escape for red).
|
||||
|
||||
Some 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).
|
||||
|
||||
- `chalk.bgHex('#DEADED').underline('Hello, world!')`
|
||||
- `chalk.bgKeyword('orange')('Some orange text')`
|
||||
- `chalk.bgRgb(15, 100, 204).inverse('Hello!')`
|
||||
|
||||
As of this writing, these are the supported color models that are exposed in Chalk:
|
||||
|
||||
- `rgb` - e.g. `chalk.rgb(255, 136, 0).bold('Orange!')`
|
||||
- `hex` - e.g. `chalk.hex('#ff8800').bold('Orange!')`
|
||||
- `keyword` (CSS keywords) - e.g. `chalk.keyword('orange').bold('Orange!')`
|
||||
- `hsl` - e.g. `chalk.hsl(32, 100, 50).bold('Orange!')`
|
||||
- `hsv`
|
||||
- `hwb`
|
||||
- `cmyk`
|
||||
- `xyz`
|
||||
- `lab`
|
||||
- `lch`
|
||||
- `ansi16`
|
||||
- `ansi256`
|
||||
- `hcg`
|
||||
- `apple` (see [qix-/color-convert#30](https://github.com/Qix-/color-convert/issues/30))
|
||||
|
||||
For a complete list of color models, see [`color-convert`'s list of conversions](https://github.com/Qix-/color-convert/blob/master/conversions.js).
|
||||
|
||||
## Windows
|
||||
|
||||
|
|
@ -194,6 +242,7 @@ If you're on Windows, do yourself a favor and use [`cmder`](http://cmder.net/) i
|
|||
- [ansi-regex](https://github.com/chalk/ansi-regex) - Regular expression for matching ANSI escape codes
|
||||
- [wrap-ansi](https://github.com/chalk/wrap-ansi) - Wordwrap a string with ANSI escape codes
|
||||
- [slice-ansi](https://github.com/chalk/slice-ansi) - Slice a string with ANSI escape codes
|
||||
- [color-convert](https://github.com/qix-/color-convert) - Converts colors between different models
|
||||
|
||||
|
||||
## License
|
||||
|
|
|
|||
60
test.js
60
test.js
|
|
@ -72,6 +72,26 @@ describe('chalk', function () {
|
|||
it('line breaks should open and close colors', function () {
|
||||
assert.equal(chalk.grey('hello\nworld'), '\u001b[90mhello\u001b[39m\n\u001b[90mworld\u001b[39m');
|
||||
});
|
||||
|
||||
it('should properly convert RGB to 16 colors on basic color terminals', function () {
|
||||
assert.equal(new chalk.constructor({level: 1}).hex('#FF0000')('hello'), '\u001b[91mhello\u001b[39m');
|
||||
assert.equal(new chalk.constructor({level: 1}).bgHex('#FF0000')('hello'), '\u001b[101mhello\u001b[49m');
|
||||
});
|
||||
|
||||
it('should properly convert RGB to 256 colors on basic color terminals', function () {
|
||||
assert.equal(new chalk.constructor({level: 2}).hex('#FF0000')('hello'), '\u001b[38;5;196mhello\u001b[39m');
|
||||
assert.equal(new chalk.constructor({level: 2}).bgHex('#FF0000')('hello'), '\u001b[48;5;196mhello\u001b[49m');
|
||||
});
|
||||
|
||||
it('should properly convert RGB to 256 colors on basic color terminals', function () {
|
||||
assert.equal(new chalk.constructor({level: 3}).hex('#FF0000')('hello'), '\u001b[38;2;255;0;0mhello\u001b[39m');
|
||||
assert.equal(new chalk.constructor({level: 3}).bgHex('#FF0000')('hello'), '\u001b[48;2;255;0;0mhello\u001b[49m');
|
||||
});
|
||||
|
||||
it('should not emit RGB codes if level is 0', function () {
|
||||
assert.equal(new chalk.constructor({level: 0}).hex('#FF0000')('hello'), 'hello');
|
||||
assert.equal(new chalk.constructor({level: 0}).bgHex('#FF0000')('hello'), 'hello');
|
||||
});
|
||||
});
|
||||
|
||||
describe('chalk on windows', function () {
|
||||
|
|
@ -132,39 +152,43 @@ describe('chalk on windows', function () {
|
|||
});
|
||||
});
|
||||
|
||||
describe('chalk.enabled', function () {
|
||||
describe('chalk.level', function () {
|
||||
it('should not output colors when manually disabled', function () {
|
||||
chalk.enabled = false;
|
||||
var oldLevel = chalk.level;
|
||||
chalk.level = 0;
|
||||
assert.equal(chalk.red('foo'), 'foo');
|
||||
chalk.enabled = true;
|
||||
chalk.level = oldLevel;
|
||||
});
|
||||
|
||||
it('should enable/disable colors based on overall chalk enabled property, not individual instances', function () {
|
||||
chalk.enabled = true;
|
||||
var oldLevel = chalk.level;
|
||||
chalk.level = 1;
|
||||
var red = chalk.red;
|
||||
assert.equal(red.enabled, true);
|
||||
chalk.enabled = false;
|
||||
assert.equal(red.enabled, chalk.enabled);
|
||||
chalk.enabled = true;
|
||||
assert.equal(red.level, 1);
|
||||
chalk.level = 0;
|
||||
assert.equal(red.level, chalk.level);
|
||||
chalk.level = oldLevel;
|
||||
});
|
||||
|
||||
it('should propagate enable/disable changes from child colors', function () {
|
||||
chalk.enabled = true;
|
||||
var oldLevel = chalk.level;
|
||||
chalk.level = 1;
|
||||
var red = chalk.red;
|
||||
assert.equal(red.enabled, true);
|
||||
assert.equal(chalk.enabled, true);
|
||||
red.enabled = false;
|
||||
assert.equal(red.enabled, false);
|
||||
assert.equal(chalk.enabled, false);
|
||||
chalk.enabled = true;
|
||||
assert.equal(red.enabled, true);
|
||||
assert.equal(chalk.enabled, true);
|
||||
assert.equal(red.level, 1);
|
||||
assert.equal(chalk.level, 1);
|
||||
red.level = 0;
|
||||
assert.equal(red.level, 0);
|
||||
assert.equal(chalk.level, 0);
|
||||
chalk.level = 1;
|
||||
assert.equal(red.level, 1);
|
||||
assert.equal(chalk.level, 1);
|
||||
chalk.level = oldLevel;
|
||||
});
|
||||
});
|
||||
|
||||
describe('chalk.constructor', function () {
|
||||
it('should create a isolated context where colors can be disabled', function () {
|
||||
var ctx = new chalk.constructor({enabled: false});
|
||||
var ctx = new chalk.constructor({level: 0});
|
||||
assert.equal(ctx.red('foo'), 'foo');
|
||||
assert.equal(chalk.red('foo'), '\u001b[31mfoo\u001b[39m');
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue