@peteanderson/ansi 1.0.1 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +23 -3
- package/dist/ansi.d.ts +79 -41
- package/dist/ansi.d.ts.map +1 -1
- package/dist/ansi.js +9 -6
- package/dist/ansi.js.map +1 -1
- package/dist/features/features.d.ts +1 -3
- package/dist/features/features.d.ts.map +1 -1
- package/dist/features/features.js.map +1 -1
- package/dist/features/index.d.ts +1 -0
- package/dist/features/index.d.ts.map +1 -1
- package/dist/features/types.d.ts +2 -0
- package/dist/features/types.d.ts.map +1 -1
- package/dist/pad.d.ts +2 -0
- package/dist/pad.d.ts.map +1 -1
- package/dist/sgr/color/channel.d.ts +4 -0
- package/dist/sgr/color/channel.d.ts.map +1 -0
- package/dist/sgr/color/channel.js +33 -0
- package/dist/sgr/color/channel.js.map +1 -0
- package/dist/sgr/color/color.d.ts +3 -4
- package/dist/sgr/color/color.d.ts.map +1 -1
- package/dist/sgr/color/color.js +16 -65
- package/dist/sgr/color/color.js.map +1 -1
- package/dist/sgr/color/context.d.ts +13 -0
- package/dist/sgr/color/context.d.ts.map +1 -0
- package/dist/sgr/color/context.js +24 -0
- package/dist/sgr/color/context.js.map +1 -0
- package/dist/sgr/color/rgb.d.ts +5 -0
- package/dist/sgr/color/rgb.d.ts.map +1 -0
- package/dist/sgr/color/rgb.js +32 -0
- package/dist/sgr/color/rgb.js.map +1 -0
- package/dist/sgr/color/utils.d.ts.map +1 -1
- package/dist/sgr/color/utils.js.map +1 -1
- package/dist/sgr/combine.d.ts +3 -0
- package/dist/sgr/combine.d.ts.map +1 -0
- package/dist/sgr/combine.js +14 -0
- package/dist/sgr/combine.js.map +1 -0
- package/dist/sgr/format-builder.d.ts +3 -0
- package/dist/sgr/format-builder.d.ts.map +1 -0
- package/dist/sgr/format-builder.js +33 -0
- package/dist/sgr/format-builder.js.map +1 -0
- package/dist/sgr/index.d.ts +2 -3
- package/dist/sgr/index.d.ts.map +1 -1
- package/dist/sgr/index.js +1 -3
- package/dist/sgr/index.js.map +1 -1
- package/dist/sgr/lazy.d.ts +6 -0
- package/dist/sgr/lazy.d.ts.map +1 -0
- package/dist/sgr/lazy.js +17 -0
- package/dist/sgr/lazy.js.map +1 -0
- package/dist/sgr/sgr.d.ts +3 -14
- package/dist/sgr/sgr.d.ts.map +1 -1
- package/dist/sgr/sgr.js +45 -39
- package/dist/sgr/sgr.js.map +1 -1
- package/dist/sgr/style.d.ts +2 -14
- package/dist/sgr/style.d.ts.map +1 -1
- package/dist/sgr/style.js +27 -16
- package/dist/sgr/style.js.map +1 -1
- package/dist/sgr/types.d.ts +59 -0
- package/dist/sgr/types.d.ts.map +1 -0
- package/dist/sgr/{color/types.js.map → types.js.map} +1 -1
- package/dist/simplify/simplify.d.ts +1 -0
- package/dist/simplify/simplify.d.ts.map +1 -1
- package/dist/slice.d.ts +1 -0
- package/dist/slice.d.ts.map +1 -1
- package/dist/strip.d.ts +3 -0
- package/dist/strip.d.ts.map +1 -1
- package/dist/typed-object.d.ts +16 -0
- package/dist/typed-object.d.ts.map +1 -0
- package/dist/typed-object.js +36 -0
- package/dist/typed-object.js.map +1 -0
- package/dist/types.d.ts +25 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +3 -0
- package/dist/types.js.map +1 -0
- package/package.json +3 -2
- package/src/ansi.ts +11 -36
- package/src/features/features.ts +1 -8
- package/src/features/index.ts +3 -1
- package/src/features/types.ts +10 -1
- package/src/pad.ts +4 -1
- package/src/sgr/color/channel.ts +44 -0
- package/src/sgr/color/color.ts +30 -95
- package/src/sgr/color/context.ts +44 -0
- package/src/sgr/color/rgb.ts +48 -0
- package/src/sgr/color/utils.ts +2 -3
- package/src/sgr/combine.ts +13 -0
- package/src/sgr/format-builder.ts +47 -0
- package/src/sgr/index.ts +3 -3
- package/src/sgr/lazy.ts +21 -0
- package/src/sgr/sgr.ts +61 -54
- package/src/sgr/style.ts +35 -18
- package/src/sgr/types.ts +91 -0
- package/src/simplify/simplify.ts +3 -1
- package/src/slice.ts +3 -1
- package/src/strip.ts +5 -1
- package/src/typed-object.ts +85 -0
- package/src/types.ts +28 -0
- package/src/utility-types.d.ts +18 -0
- package/dist/sgr/color/types.d.ts +0 -30
- package/dist/sgr/color/types.d.ts.map +0 -1
- package/dist/sgr/utils.d.ts +0 -3
- package/dist/sgr/utils.d.ts.map +0 -1
- package/dist/sgr/utils.js +0 -16
- package/dist/sgr/utils.js.map +0 -1
- package/src/sgr/color/types.ts +0 -32
- package/src/sgr/utils.ts +0 -17
- /package/dist/sgr/{color/types.js → types.js} +0 -0
package/src/ansi.ts
CHANGED
|
@@ -3,54 +3,25 @@ import {makeErase} from "./erase";
|
|
|
3
3
|
import {getFeatures} from "./features";
|
|
4
4
|
import {padEnd, padStart} from "./pad";
|
|
5
5
|
import {makeScroll} from "./scroll";
|
|
6
|
-
import {
|
|
6
|
+
import {makeSGR} from "./sgr";
|
|
7
7
|
import {simplify} from "./simplify";
|
|
8
8
|
import {slice} from "./slice";
|
|
9
9
|
import {sanitize, strip, visibleLength} from "./strip";
|
|
10
10
|
import {makeTerminal} from "./terminal";
|
|
11
11
|
|
|
12
|
-
import type {Args}
|
|
13
|
-
import type {
|
|
14
|
-
import type {Erase} from "./erase";
|
|
15
|
-
import type {Features} from "./features/types";
|
|
16
|
-
import type {Scroll} from "./scroll";
|
|
17
|
-
import type {Color, Style} from "./sgr";
|
|
18
|
-
import type {Terminal} from "./terminal";
|
|
12
|
+
import type {Args} from "./features";
|
|
13
|
+
import type {Ansi} from "./types";
|
|
19
14
|
|
|
20
|
-
|
|
21
|
-
caret : Caret;
|
|
22
|
-
erase : Erase;
|
|
23
|
-
scroll : Scroll;
|
|
24
|
-
|
|
25
|
-
reset : string;
|
|
26
|
-
style : Style;
|
|
27
|
-
|
|
28
|
-
terminal : Terminal;
|
|
29
|
-
|
|
30
|
-
padEnd : typeof padEnd;
|
|
31
|
-
padStart : typeof padStart;
|
|
32
|
-
|
|
33
|
-
strip : typeof strip;
|
|
34
|
-
visibleLength : typeof visibleLength;
|
|
35
|
-
sanitize : typeof sanitize;
|
|
36
|
-
slice : typeof slice;
|
|
37
|
-
simplify : typeof simplify;
|
|
38
|
-
|
|
39
|
-
features : Features;
|
|
40
|
-
};
|
|
41
|
-
|
|
42
|
-
export function makeAnsi(...args: Args): Ansi {
|
|
15
|
+
function makeAnsi(...args: Args): Ansi {
|
|
43
16
|
const features = getFeatures(...args);
|
|
44
17
|
|
|
45
18
|
return {
|
|
19
|
+
...makeSGR(features),
|
|
20
|
+
|
|
46
21
|
caret : makeCaret(features.caret),
|
|
47
22
|
erase : makeErase(features.erase),
|
|
48
23
|
scroll : makeScroll(features.scroll),
|
|
49
24
|
|
|
50
|
-
reset : makeReset(features.colorDepth > 1),
|
|
51
|
-
style : makeStyle(features.style),
|
|
52
|
-
...makeColor(features.colorDepth),
|
|
53
|
-
|
|
54
25
|
terminal: makeTerminal(features.terminal),
|
|
55
26
|
|
|
56
27
|
padEnd,
|
|
@@ -66,6 +37,10 @@ export function makeAnsi(...args: Args): Ansi {
|
|
|
66
37
|
};
|
|
67
38
|
}
|
|
68
39
|
|
|
69
|
-
const ansi = Object.assign(makeAnsi, makeAnsi());
|
|
40
|
+
export const ansi = Object.assign(makeAnsi, makeAnsi());
|
|
41
|
+
|
|
42
|
+
export const {fg, bg, style, reset, caret, erase, scroll, terminal} = ansi;
|
|
43
|
+
|
|
44
|
+
export {padEnd, padStart, strip, visibleLength}
|
|
70
45
|
|
|
71
46
|
export default ansi;
|
package/src/features/features.ts
CHANGED
|
@@ -3,14 +3,7 @@ import {WriteStream} from "node:tty";
|
|
|
3
3
|
import {boolean, build, number, pipe, tty} from "./build";
|
|
4
4
|
import {getColorDepth} from "./detect";
|
|
5
5
|
|
|
6
|
-
import type {
|
|
7
|
-
|
|
8
|
-
export type Args =
|
|
9
|
-
| [enabled : boolean, stream?: NodeJS.WriteStream]
|
|
10
|
-
| [colorDepth : ColorDepth, stream?: NodeJS.WriteStream]
|
|
11
|
-
| [features : Partial<Features>, stream?: NodeJS.WriteStream]
|
|
12
|
-
| [socket : Socket]
|
|
13
|
-
| [stream? : NodeJS.WriteStream];
|
|
6
|
+
import type {Args, Features} from "./types";
|
|
14
7
|
|
|
15
8
|
export function getFeatures(...args: Args): Features {
|
|
16
9
|
let [features, stream] = args;
|
package/src/features/index.ts
CHANGED
package/src/features/types.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import type {Socket} from "node:net";
|
|
2
|
+
|
|
1
3
|
export type ColorDepth = 1 | 3 | 4 | 8 | 24;
|
|
2
4
|
|
|
3
5
|
export interface Features {
|
|
@@ -7,4 +9,11 @@ export interface Features {
|
|
|
7
9
|
erase : boolean;
|
|
8
10
|
scroll : boolean;
|
|
9
11
|
terminal : boolean;
|
|
10
|
-
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export type Args =
|
|
15
|
+
| [enabled : boolean, stream?: NodeJS.WriteStream]
|
|
16
|
+
| [colorDepth : ColorDepth, stream?: NodeJS.WriteStream]
|
|
17
|
+
| [features : Partial<Features>, stream?: NodeJS.WriteStream]
|
|
18
|
+
| [socket : Socket]
|
|
19
|
+
| [stream? : NodeJS.WriteStream];
|
package/src/pad.ts
CHANGED
|
@@ -12,4 +12,7 @@ export const padEnd = (text: string, targetLength: number, fillString = " "): st
|
|
|
12
12
|
* without counting ANSI escape codes.
|
|
13
13
|
*/
|
|
14
14
|
export const padStart = (text: string, targetLength: number, fillString = " "): string =>
|
|
15
|
-
fillString.repeat(Math.max(0, targetLength - visibleLength(text)) / fillString.length) + text;
|
|
15
|
+
fillString.repeat(Math.max(0, targetLength - visibleLength(text)) / fillString.length) + text;
|
|
16
|
+
|
|
17
|
+
export type PadEnd = typeof padEnd;
|
|
18
|
+
export type PadStart = typeof padStart;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import {lazy} from "../lazy";
|
|
2
|
+
import {buildContext} from "./context";
|
|
3
|
+
import {makeRGB, makeX256} from "./rgb";
|
|
4
|
+
|
|
5
|
+
import type {ColorDepth} from "../../features";
|
|
6
|
+
import type {ChainKey, Channel, ColorKey, FormatBuilder, Style} from "../types";
|
|
7
|
+
|
|
8
|
+
const colors = [
|
|
9
|
+
"black",
|
|
10
|
+
"red",
|
|
11
|
+
"green",
|
|
12
|
+
"yellow",
|
|
13
|
+
"blue",
|
|
14
|
+
"magenta",
|
|
15
|
+
"cyan",
|
|
16
|
+
"white",
|
|
17
|
+
] as const;
|
|
18
|
+
|
|
19
|
+
export function makeChannel(
|
|
20
|
+
keys : ReadonlySet<ChainKey>,
|
|
21
|
+
makeFormatter : FormatBuilder,
|
|
22
|
+
style : Style,
|
|
23
|
+
colorDepth : ColorDepth,
|
|
24
|
+
channel : ColorKey,
|
|
25
|
+
): Channel {
|
|
26
|
+
const ctx = buildContext(keys, makeFormatter, style, colorDepth, channel);
|
|
27
|
+
const {build, bright, open, close} = ctx;
|
|
28
|
+
|
|
29
|
+
const rtn = {
|
|
30
|
+
rgb : makeRGB(ctx),
|
|
31
|
+
x256 : makeX256(ctx),
|
|
32
|
+
default : colorDepth > 1 ? `\x1b[${close}m` : "",
|
|
33
|
+
} as Channel;
|
|
34
|
+
|
|
35
|
+
for (let i = 0; i < colors.length; i++) {
|
|
36
|
+
const color = colors[i];
|
|
37
|
+
const brightColor = `bright${color[0].toUpperCase()}${color.slice(1)}`;
|
|
38
|
+
|
|
39
|
+
lazy.add(rtn, color, () => build(open + i));
|
|
40
|
+
lazy.add(rtn, brightColor, () => bright(i));
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return rtn;
|
|
44
|
+
}
|
package/src/sgr/color/color.ts
CHANGED
|
@@ -1,95 +1,30 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
import type {
|
|
7
|
-
import type {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
code => code >= 60 ? sgr(open + code - 60) : sgr(open + code);
|
|
32
|
-
|
|
33
|
-
return {
|
|
34
|
-
black : sgr(open + 0),
|
|
35
|
-
red : sgr(open + 1),
|
|
36
|
-
green : sgr(open + 2),
|
|
37
|
-
yellow : sgr(open + 3),
|
|
38
|
-
blue : sgr(open + 4),
|
|
39
|
-
magenta : sgr(open + 5),
|
|
40
|
-
cyan : sgr(open + 6),
|
|
41
|
-
white : sgr(open + 7),
|
|
42
|
-
|
|
43
|
-
brightBlack : bright(0),
|
|
44
|
-
brightRed : bright(1),
|
|
45
|
-
brightGreen : bright(2),
|
|
46
|
-
brightYellow : bright(3),
|
|
47
|
-
brightBlue : bright(4),
|
|
48
|
-
brightMagenta : bright(5),
|
|
49
|
-
brightCyan : bright(6),
|
|
50
|
-
brightWhite : bright(7),
|
|
51
|
-
|
|
52
|
-
rgb:
|
|
53
|
-
colorDepth >= 24 ? (r, g, b) => sgr([open + 8, 2, ...clip(r, g, b)]) :
|
|
54
|
-
colorDepth >= 8 ? (r, g, b) => sgr([open + 8, 5, rgbToX256(...clip(r, g, b))]) :
|
|
55
|
-
colorDepth >= 4 ? (r, g, b) => sgr(open + rgbToX16(...clip(r, g, b))) :
|
|
56
|
-
colorDepth === 3 ?
|
|
57
|
-
open === fgOpen ?
|
|
58
|
-
(r, g, b) => x16ToX8(rgbToX16(...clip(r, g, b))) :
|
|
59
|
-
(r, g, b) => sgr(open + rgbToX8(...clip(r, g, b))) :
|
|
60
|
-
() => sgr(0),
|
|
61
|
-
|
|
62
|
-
x256:
|
|
63
|
-
colorDepth >= 8 ?
|
|
64
|
-
(...args: [number] | [number, number, number]): SGR => sgr(
|
|
65
|
-
[open + 8, 5, args.length === 3 ? rgbToX256(...clip(...args)) : clip(args[0])],
|
|
66
|
-
) :
|
|
67
|
-
colorDepth >= 4 ?
|
|
68
|
-
(...args: [number] | [number, number, number]): SGR => sgr(
|
|
69
|
-
open +
|
|
70
|
-
rgbToX16(...(args.length === 3 ? clip(...args) : x256ToRgb(clip(args[0])))),
|
|
71
|
-
) :
|
|
72
|
-
colorDepth === 3 ?
|
|
73
|
-
open === fgOpen ?
|
|
74
|
-
(...args: [number] | [number, number, number]): SGR => x16ToX8(
|
|
75
|
-
args.length === 3
|
|
76
|
-
? rgbToX16(...clip(...args))
|
|
77
|
-
: rgbToX16(...x256ToRgb(clip(args[0]))),
|
|
78
|
-
) :
|
|
79
|
-
(...args: [number] | [number, number, number]): SGR => sgr(
|
|
80
|
-
open +
|
|
81
|
-
rgbToX8(...args.length === 3 ? clip(...args) : x256ToRgb(clip(args[0]))),
|
|
82
|
-
) :
|
|
83
|
-
(): SGR => sgr(0),
|
|
84
|
-
|
|
85
|
-
default: colorDepth > 1 ? `\x1b[${close}m` : "",
|
|
86
|
-
};
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
const cache: Partial<Record<ColorDepth, Color>> = {};
|
|
90
|
-
|
|
91
|
-
export const makeColor = (colorDepth: ColorDepth = 4): Color =>
|
|
92
|
-
cache[colorDepth] ?? (cache[colorDepth] = {
|
|
93
|
-
fg : makeChannel(colorDepth, fgOpen, fgClose),
|
|
94
|
-
bg : makeChannel(colorDepth, bgOpen, bgClose),
|
|
95
|
-
});
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
import {lazy} from "../lazy";
|
|
4
|
+
import {makeChannel} from "./channel";
|
|
5
|
+
|
|
6
|
+
import type {ColorDepth} from "../../features";
|
|
7
|
+
import type {ChainKey, Color, FormatBuilder, Style} from "../types";
|
|
8
|
+
|
|
9
|
+
const channels = ["fg", "bg"] as const;
|
|
10
|
+
|
|
11
|
+
export function makeColor(
|
|
12
|
+
keys : ReadonlySet<ChainKey>,
|
|
13
|
+
makeFormatter : FormatBuilder,
|
|
14
|
+
style : Style,
|
|
15
|
+
colorDepth : ColorDepth,
|
|
16
|
+
): Color<any> {
|
|
17
|
+
const rtn = {} as Color<any>;
|
|
18
|
+
|
|
19
|
+
for (const channel of channels) {
|
|
20
|
+
if (keys.has(channel)) {
|
|
21
|
+
lazy.add(rtn, channel, () => {
|
|
22
|
+
const newKeys = new Set(keys);
|
|
23
|
+
newKeys.delete(channel);
|
|
24
|
+
return makeChannel(newKeys, makeFormatter, style, colorDepth, channel);
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return rtn;
|
|
30
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import {combineCodes} from "../combine";
|
|
2
|
+
|
|
3
|
+
import type {ColorDepth} from "../../features";
|
|
4
|
+
import type {ChainKey, Code, ColorKey, Format, FormatBase, FormatBuilder, Style} from "../types";
|
|
5
|
+
|
|
6
|
+
const baseCodes = {
|
|
7
|
+
fg: {open: 30, close: 39},
|
|
8
|
+
bg: {open: 40, close: 49},
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export function buildContext(
|
|
12
|
+
keys : ReadonlySet<ChainKey>,
|
|
13
|
+
makeFormatter : FormatBuilder,
|
|
14
|
+
style : Style,
|
|
15
|
+
colorDepth : ColorDepth,
|
|
16
|
+
channel : ColorKey
|
|
17
|
+
) {
|
|
18
|
+
const {open, close} = baseCodes[channel];
|
|
19
|
+
|
|
20
|
+
const build = (open: Code): Format => makeFormatter(keys, open, close);
|
|
21
|
+
|
|
22
|
+
const combine = (base: FormatBase, style: FormatBase): Format => makeFormatter(
|
|
23
|
+
keys,
|
|
24
|
+
combineCodes(base.codes.open, style.codes.open),
|
|
25
|
+
combineCodes(base.codes.close, style.codes.close),
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
const bright: (index: number) => Format =
|
|
29
|
+
colorDepth >= 4 ? index => build(open + 60 + index) :
|
|
30
|
+
colorDepth === 3 ?
|
|
31
|
+
channel === "fg" ?
|
|
32
|
+
index => combine(style.bold, build(open + index)) :
|
|
33
|
+
index => build(open + index) :
|
|
34
|
+
() => build(0);
|
|
35
|
+
|
|
36
|
+
const x16ToX8: (code: number) => Format =
|
|
37
|
+
channel === "fg" ?
|
|
38
|
+
code => code >= 60 ? combine(style.bold, build(open + code - 60)) : build(open + code) :
|
|
39
|
+
code => code >= 60 ? build(open + code - 60) : build(open + code);
|
|
40
|
+
|
|
41
|
+
return {colorDepth, channel, open, close, build, bright, x16ToX8};
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export type Context = ReturnType<typeof buildContext>;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import {clip, rgbToX256, rgbToX16, rgbToX8, x256ToRgb} from "./utils";
|
|
2
|
+
|
|
3
|
+
import type {Channel, Format} from "../types";
|
|
4
|
+
import type {Context} from "./context";
|
|
5
|
+
|
|
6
|
+
export function makeRGB(ctx: Context): Channel["rgb"] {
|
|
7
|
+
const {colorDepth, channel, build, open, x16ToX8} = ctx;
|
|
8
|
+
|
|
9
|
+
return (
|
|
10
|
+
colorDepth >= 24 ? (r, g, b) => build([open + 8, 2, ...clip(r, g, b)]) :
|
|
11
|
+
colorDepth >= 8 ? (r, g, b) => build([open + 8, 5, rgbToX256(...clip(r, g, b))]) :
|
|
12
|
+
colorDepth >= 4 ? (r, g, b) => build(open + rgbToX16(...clip(r, g, b))) :
|
|
13
|
+
colorDepth === 3 ?
|
|
14
|
+
channel === "fg" ?
|
|
15
|
+
(r, g, b) => x16ToX8(rgbToX16(...clip(r, g, b))) :
|
|
16
|
+
(r, g, b) => build(open + rgbToX8(...clip(r, g, b))) :
|
|
17
|
+
() => build(0)
|
|
18
|
+
);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function makeX256(ctx: Context): (...args: [number] | [number, number, number]) => Format {
|
|
22
|
+
const {colorDepth, channel, build, open, x16ToX8} = ctx;
|
|
23
|
+
|
|
24
|
+
return (
|
|
25
|
+
colorDepth >= 8 ?
|
|
26
|
+
(...args) => build(
|
|
27
|
+
[open + 8, 5, args.length === 3 ? rgbToX256(...clip(...args)) : clip(args[0])],
|
|
28
|
+
) :
|
|
29
|
+
colorDepth >= 4 ?
|
|
30
|
+
(...args) => build(
|
|
31
|
+
open +
|
|
32
|
+
rgbToX16(...(args.length === 3 ? clip(...args) : x256ToRgb(clip(args[0])))),
|
|
33
|
+
) :
|
|
34
|
+
colorDepth === 3 ?
|
|
35
|
+
channel === "fg" ?
|
|
36
|
+
(...args) => x16ToX8(
|
|
37
|
+
args.length === 3 ?
|
|
38
|
+
rgbToX16(...clip(...args)) :
|
|
39
|
+
rgbToX16(...x256ToRgb(clip(args[0]))),
|
|
40
|
+
) :
|
|
41
|
+
(...args) => build(
|
|
42
|
+
open + rgbToX8(
|
|
43
|
+
...args.length === 3 ? clip(...args) : x256ToRgb(clip(args[0])),
|
|
44
|
+
),
|
|
45
|
+
) :
|
|
46
|
+
() => build(0)
|
|
47
|
+
);
|
|
48
|
+
}
|
package/src/sgr/color/utils.ts
CHANGED
|
@@ -29,6 +29,7 @@ export function x256ToRgb(code: number): [number, number, number] {
|
|
|
29
29
|
|
|
30
30
|
export function rgbToX16(r: number, g: number, b: number): number {
|
|
31
31
|
// First calculate it as a 4x4x4 cube.
|
|
32
|
+
|
|
32
33
|
r = Math.min(2, Math.floor(r / 85));
|
|
33
34
|
g = Math.min(2, Math.floor(g / 85));
|
|
34
35
|
b = Math.min(2, Math.floor(b / 85));
|
|
@@ -55,9 +56,7 @@ export function rgbToX8(r: number, g: number, b: number): number {
|
|
|
55
56
|
|
|
56
57
|
export function clip(x: number): number;
|
|
57
58
|
export function clip(r: number, g: number, b: number): [number, number, number];
|
|
58
|
-
export function clip(
|
|
59
|
-
...args: [number] | [number, number, number]
|
|
60
|
-
): number | [number, number, number] {
|
|
59
|
+
export function clip(...args: [number] | [number, number, number]): number | [number, number, number] {
|
|
61
60
|
return args.length === 1 ?
|
|
62
61
|
Math.max(0, Math.min(255, Math.floor(args[0]))) :
|
|
63
62
|
[
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type {Code} from "./types";
|
|
2
|
+
|
|
3
|
+
export function combineCodes(base: Code, code: Code): Code {
|
|
4
|
+
const set = new Set(Array.isArray(base) ? [...base] : [base]);
|
|
5
|
+
|
|
6
|
+
if (Array.isArray(code)) {
|
|
7
|
+
for (const c of code)
|
|
8
|
+
set.add(c);
|
|
9
|
+
} else
|
|
10
|
+
set.add(code);
|
|
11
|
+
|
|
12
|
+
return [...set];
|
|
13
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import {define} from "@peteanderson/props";
|
|
2
|
+
import {lazy} from "./lazy";
|
|
3
|
+
|
|
4
|
+
import type {ChainBuilder, ChainKey, Code, Format, FormatBase, FormatBuilder} from "./types";
|
|
5
|
+
|
|
6
|
+
const codeSequence = (code: Code): string | number => Array.isArray(code) ? code.join(';') : code;
|
|
7
|
+
|
|
8
|
+
const chain = <Keys extends ChainKey>(
|
|
9
|
+
makeChain : ChainBuilder,
|
|
10
|
+
keys : ReadonlySet<Keys>,
|
|
11
|
+
baseFormat : FormatBase,
|
|
12
|
+
): Format<Keys> => !keys.size
|
|
13
|
+
? baseFormat as Format<Keys>
|
|
14
|
+
: lazy.add(baseFormat, "and", () => makeChain(keys, baseFormat));
|
|
15
|
+
|
|
16
|
+
export function makeFormatBuilder(makeChain: ChainBuilder, enabled = true): FormatBuilder {
|
|
17
|
+
if (enabled) {
|
|
18
|
+
return (keys, open, close, reset) => {
|
|
19
|
+
const openCode = codeSequence(open);
|
|
20
|
+
const closeCode = codeSequence(close);
|
|
21
|
+
const openSequence = `\x1b[${openCode}m`;
|
|
22
|
+
const closeSequence = `\x1b[${closeCode}m`;
|
|
23
|
+
const reopenCode = reset ? `${closeCode};${openCode}` : openCode;
|
|
24
|
+
|
|
25
|
+
const rxClose = new RegExp(
|
|
26
|
+
`(?<start>\\x1b\\[(?:\\d+;)*)${closeCode}(?<end>(?:;\\d+)*m)`,
|
|
27
|
+
"g"
|
|
28
|
+
);
|
|
29
|
+
const replace = `$<start>${reopenCode}$<end>`;
|
|
30
|
+
|
|
31
|
+
return chain(makeChain, keys, define(
|
|
32
|
+
(s: string) => s ? openSequence + s.replace(rxClose, replace) + closeSequence : s,
|
|
33
|
+
{
|
|
34
|
+
open : {value: openSequence, enumerable: true},
|
|
35
|
+
close : {value: closeSequence, enumerable: true},
|
|
36
|
+
codes : {value: {open, close}, enumerable: true},
|
|
37
|
+
},
|
|
38
|
+
));
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return keys => chain(makeChain, keys, define((s: string) => s, {
|
|
43
|
+
open : {value: "", enumerable: true},
|
|
44
|
+
close : {value: "", enumerable: true},
|
|
45
|
+
codes : {value: {open: [], close: []}, enumerable: true},
|
|
46
|
+
}));
|
|
47
|
+
}
|
package/src/sgr/index.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export * from "./
|
|
2
|
-
|
|
3
|
-
export
|
|
1
|
+
export * from "./sgr";
|
|
2
|
+
|
|
3
|
+
export {SGR} from "./types";
|
package/src/sgr/lazy.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import {define} from "@peteanderson/props";
|
|
2
|
+
|
|
3
|
+
import type {GetterProperty} from "@peteanderson/props";
|
|
4
|
+
|
|
5
|
+
export function lazy<K extends keyof any, V>(key: K, get: () => V): GetterProperty<V> {
|
|
6
|
+
return {
|
|
7
|
+
get() {
|
|
8
|
+
const value = get();
|
|
9
|
+
define(this, key, {value, enumerable: true});
|
|
10
|
+
return value;
|
|
11
|
+
},
|
|
12
|
+
enumerable : true,
|
|
13
|
+
configurable : true,
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
lazy.add = <T extends object, K extends keyof any, V>(
|
|
18
|
+
obj : T,
|
|
19
|
+
key : K,
|
|
20
|
+
get : () => V,
|
|
21
|
+
): T & {[k in K]: V} => define(obj, key, lazy(key, get));
|
package/src/sgr/sgr.ts
CHANGED
|
@@ -1,65 +1,72 @@
|
|
|
1
|
-
|
|
1
|
+
import {TypedObject} from "../typed-object";
|
|
2
|
+
import {makeColor} from "./color";
|
|
3
|
+
import {combineCodes} from "./combine";
|
|
4
|
+
import {makeFormatBuilder} from "./format-builder";
|
|
5
|
+
import {makeReset} from "./reset";
|
|
6
|
+
import {makeStyle} from "./style";
|
|
2
7
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
open : string;
|
|
6
|
-
close : string;
|
|
7
|
-
codes : {open: Code; close: Code};
|
|
8
|
-
combine : (...styles: SGR[]) => SGR;
|
|
9
|
-
}
|
|
8
|
+
import type {ColorDepth, Features} from "../features";
|
|
9
|
+
import type {Chain, ChainKey, FormatBase, FormatBuilder, SGR} from "./types";
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
11
|
+
const keyMap: Record<ChainKey, undefined> = {
|
|
12
|
+
fg : undefined,
|
|
13
|
+
bg : undefined,
|
|
14
|
+
bold : undefined,
|
|
15
|
+
dim : undefined,
|
|
16
|
+
italic : undefined,
|
|
17
|
+
underline : undefined,
|
|
18
|
+
inverse : undefined,
|
|
19
|
+
hidden : undefined,
|
|
20
|
+
strikethrough : undefined,
|
|
21
|
+
doubleUnderline : undefined,
|
|
22
|
+
frame : undefined,
|
|
23
|
+
encircle : undefined,
|
|
24
|
+
overline : undefined,
|
|
25
|
+
};
|
|
14
26
|
|
|
15
|
-
|
|
27
|
+
const allKeys: ReadonlySet<ChainKey> = new Set(TypedObject.keys(keyMap));
|
|
16
28
|
|
|
17
|
-
|
|
18
|
-
if (Array.isArray(c)) {
|
|
19
|
-
for (const code of c)
|
|
20
|
-
set.add(code);
|
|
21
|
-
} else
|
|
22
|
-
set.add(c);
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
return Array.from(set);
|
|
26
|
-
}
|
|
29
|
+
const cache: Partial<Record<`${ColorDepth}.${boolean}`, SGR>> = {};
|
|
27
30
|
|
|
28
|
-
|
|
31
|
+
export function makeSGR(features: Features): SGR {
|
|
32
|
+
const cacheKey = `${features.colorDepth}.${features.style}` as const;
|
|
33
|
+
if (cache[cacheKey])
|
|
34
|
+
return cache[cacheKey];
|
|
29
35
|
|
|
30
|
-
|
|
31
|
-
const
|
|
32
|
-
const
|
|
33
|
-
const openSequence = `\x1b[${openCode}m`;
|
|
34
|
-
const closeSequence = `\x1b[${closeCode}m`;
|
|
35
|
-
const reopenCode = reset ? `${closeCode};${openCode}` : openCode;
|
|
36
|
+
const colorFormat = makeFormatBuilder(makeChain, features.colorDepth > 1);
|
|
37
|
+
const styleFormat = makeFormatBuilder(makeChain, features.style);
|
|
38
|
+
const style = makeStyle(allKeys, styleFormat, features.style);
|
|
36
39
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
)
|
|
41
|
-
|
|
40
|
+
function makeChain<Keys extends ChainKey>(
|
|
41
|
+
keys : ReadonlySet<Keys>,
|
|
42
|
+
baseFormat : FormatBase,
|
|
43
|
+
): Chain<Keys> {
|
|
44
|
+
const chainedColorFormat: FormatBuilder = (keys, open, close, reset) => colorFormat(
|
|
45
|
+
keys,
|
|
46
|
+
combineCodes(baseFormat.codes.open, open),
|
|
47
|
+
combineCodes(baseFormat.codes.close, close),
|
|
48
|
+
reset,
|
|
49
|
+
);
|
|
50
|
+
const chainedStyleFormat: FormatBuilder = (keys, open, close, reset) => styleFormat(
|
|
51
|
+
keys,
|
|
52
|
+
combineCodes(baseFormat.codes.open, open),
|
|
53
|
+
combineCodes(baseFormat.codes.close, close),
|
|
54
|
+
reset,
|
|
55
|
+
);
|
|
42
56
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
close : closeSequence,
|
|
49
|
-
combine : (...styles: SGR[]) => sgr(
|
|
50
|
-
combine(open, styles.map(s => s.codes.open)),
|
|
51
|
-
combine(close, styles.map(s => s.codes.close)),
|
|
52
|
-
),
|
|
53
|
-
});
|
|
54
|
-
}
|
|
57
|
+
return /** @type {Chain<any>} */({
|
|
58
|
+
...makeColor(keys, chainedColorFormat, style, features.colorDepth),
|
|
59
|
+
...makeStyle(keys, chainedStyleFormat, features.style),
|
|
60
|
+
});
|
|
61
|
+
}
|
|
55
62
|
|
|
56
|
-
const
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
});
|
|
63
|
+
const rtn: SGR = {
|
|
64
|
+
...makeColor(allKeys, colorFormat, style, features.colorDepth),
|
|
65
|
+
style,
|
|
66
|
+
reset: makeReset(features.colorDepth > 1 || features.style),
|
|
67
|
+
};
|
|
62
68
|
|
|
63
|
-
|
|
69
|
+
cache[cacheKey] = rtn;
|
|
64
70
|
|
|
65
|
-
|
|
71
|
+
return rtn;
|
|
72
|
+
}
|