@peteanderson/ansi 1.0.0 → 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/LICENSE +7 -0
- package/README.md +194 -0
- 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/build.d.ts.map +1 -1
- package/dist/features/build.js.map +1 -1
- package/dist/features/detect.d.ts.map +1 -1
- package/dist/features/detect.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 +12 -13
- 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 +10 -0
- package/dist/pad.d.ts.map +1 -1
- package/dist/pad.js +8 -0
- package/dist/pad.js.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 +12 -4
- 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 +44 -37
- 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/atom.d.ts.map +1 -1
- package/dist/simplify/atom.js +8 -1
- package/dist/simplify/atom.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/slice.js +1 -0
- package/dist/slice.js.map +1 -1
- package/dist/strip.d.ts +6 -0
- package/dist/strip.d.ts.map +1 -1
- package/dist/strip.js +3 -0
- package/dist/strip.js.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 +26 -10
- package/src/ansi.ts +11 -36
- package/src/features/build.ts +4 -3
- package/src/features/detect.ts +6 -3
- package/src/features/features.ts +19 -30
- package/src/features/index.ts +2 -0
- package/src/features/types.ts +9 -0
- package/src/pad.ts +12 -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/index.ts +1 -1
- package/src/sgr/color/rgb.ts +48 -0
- package/src/sgr/color/utils.ts +13 -4
- 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 -57
- package/src/sgr/style.ts +35 -18
- package/src/sgr/types.ts +91 -0
- package/src/simplify/atom.ts +8 -2
- package/src/simplify/simplify.ts +3 -1
- package/src/slice.ts +4 -1
- package/src/strip.ts +10 -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/test-rgb.d.ts +0 -3
- package/dist/sgr/color/test-rgb.d.ts.map +0 -1
- package/dist/sgr/color/test-rgb.js +0 -149
- package/dist/sgr/color/test-rgb.js.map +0 -1
- 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/dist/simplify/test.d.ts +0 -2
- package/dist/simplify/test.d.ts.map +0 -1
- package/dist/simplify/test.js +0 -64
- package/dist/simplify/test.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
|
@@ -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>;
|
package/src/sgr/color/index.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export * from "./color";
|
|
1
|
+
export * from "./color";
|
|
@@ -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
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
export function rgbToX256(r: number, g: number, b: number): number {
|
|
2
|
-
if (r === g && g === b)
|
|
2
|
+
if (r === g && g === b) {
|
|
3
|
+
// grayscale
|
|
3
4
|
return 232 + Math.floor(r * 24 / 256);
|
|
5
|
+
}
|
|
4
6
|
|
|
7
|
+
// 6x6x6 color cube
|
|
5
8
|
return (
|
|
6
9
|
16 +
|
|
7
10
|
Math.floor(r * 6 / 256) * 36 +
|
|
@@ -12,6 +15,7 @@ export function rgbToX256(r: number, g: number, b: number): number {
|
|
|
12
15
|
|
|
13
16
|
export function x256ToRgb(code: number): [number, number, number] {
|
|
14
17
|
if (code >= 232) {
|
|
18
|
+
// grayscale
|
|
15
19
|
const gray = Math.round((code - 232) * 255 / 24);
|
|
16
20
|
return [gray, gray, gray];
|
|
17
21
|
}
|
|
@@ -24,16 +28,21 @@ export function x256ToRgb(code: number): [number, number, number] {
|
|
|
24
28
|
}
|
|
25
29
|
|
|
26
30
|
export function rgbToX16(r: number, g: number, b: number): number {
|
|
31
|
+
// First calculate it as a 4x4x4 cube.
|
|
32
|
+
|
|
27
33
|
r = Math.min(2, Math.floor(r / 85));
|
|
28
34
|
g = Math.min(2, Math.floor(g / 85));
|
|
29
35
|
b = Math.min(2, Math.floor(b / 85));
|
|
30
36
|
|
|
37
|
+
// Which channels are "on" determines the hue (ANSI color index 0–7).
|
|
31
38
|
const index = (r ? 1 : 0) | (g ? 2 : 0) | (b ? 4 : 0);
|
|
32
39
|
|
|
40
|
+
// The bright bit is derived from the max channel level across all three.
|
|
33
41
|
const twos = (r === 2 ? 1 : 0) + (g === 2 ? 1 : 0) + (b === 2 ? 1 : 0);
|
|
34
42
|
const ones = (r === 1 ? 1 : 0) + (g === 1 ? 1 : 0) + (b === 1 ? 1 : 0);
|
|
35
43
|
const bright = twos > 0 && twos >= ones;
|
|
36
44
|
|
|
45
|
+
// Return value uses the SGR offset scheme: 0–7 for normal, 60–67 for bright.
|
|
37
46
|
return index + (bright ? 60 : 0);
|
|
38
47
|
}
|
|
39
48
|
|
|
@@ -48,9 +57,9 @@ export function rgbToX8(r: number, g: number, b: number): number {
|
|
|
48
57
|
export function clip(x: number): number;
|
|
49
58
|
export function clip(r: number, g: number, b: number): [number, number, number];
|
|
50
59
|
export function clip(...args: [number] | [number, number, number]): number | [number, number, number] {
|
|
51
|
-
return args.length === 1
|
|
52
|
-
|
|
53
|
-
|
|
60
|
+
return args.length === 1 ?
|
|
61
|
+
Math.max(0, Math.min(255, Math.floor(args[0]))) :
|
|
62
|
+
[
|
|
54
63
|
Math.max(0, Math.min(255, Math.floor(args[0]))),
|
|
55
64
|
Math.max(0, Math.min(255, Math.floor(args[1]))),
|
|
56
65
|
Math.max(0, Math.min(255, Math.floor(args[2]))),
|
|
@@ -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,68 +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
|
-
rtn.push(...c);
|
|
20
|
-
else
|
|
21
|
-
rtn.push(c);
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
return rtn;
|
|
25
|
-
}
|
|
29
|
+
const cache: Partial<Record<`${ColorDepth}.${boolean}`, SGR>> = {};
|
|
26
30
|
|
|
27
|
-
|
|
31
|
+
export function makeSGR(features: Features): SGR {
|
|
32
|
+
const cacheKey = `${features.colorDepth}.${features.style}` as const;
|
|
33
|
+
if (cache[cacheKey])
|
|
34
|
+
return cache[cacheKey];
|
|
28
35
|
|
|
29
|
-
|
|
30
|
-
const
|
|
31
|
-
const
|
|
32
|
-
const openSequence = `\x1b[${openCode}m`;
|
|
33
|
-
const closeSequence = `\x1b[${closeCode}m`;
|
|
34
|
-
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);
|
|
35
39
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
)
|
|
40
|
-
|
|
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
|
+
);
|
|
41
56
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
close : closeSequence,
|
|
48
|
-
combine : (...styles: SGR[]) => sgr(
|
|
49
|
-
combine(open, styles.map(s => s.codes.open)),
|
|
50
|
-
combine(close, styles.map(s => s.codes.close)),
|
|
51
|
-
),
|
|
52
|
-
});
|
|
53
|
-
}
|
|
57
|
+
return /** @type {Chain<any>} */({
|
|
58
|
+
...makeColor(keys, chainedColorFormat, style, features.colorDepth),
|
|
59
|
+
...makeStyle(keys, chainedStyleFormat, features.style),
|
|
60
|
+
});
|
|
61
|
+
}
|
|
54
62
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
open : "",
|
|
61
|
-
close : "",
|
|
62
|
-
combine : (): SGR => disabledSGR,
|
|
63
|
-
},
|
|
64
|
-
);
|
|
63
|
+
const rtn: SGR = {
|
|
64
|
+
...makeColor(allKeys, colorFormat, style, features.colorDepth),
|
|
65
|
+
style,
|
|
66
|
+
reset: makeReset(features.colorDepth > 1 || features.style),
|
|
67
|
+
};
|
|
65
68
|
|
|
66
|
-
|
|
69
|
+
cache[cacheKey] = rtn;
|
|
67
70
|
|
|
68
|
-
|
|
71
|
+
return rtn;
|
|
72
|
+
}
|
package/src/sgr/style.ts
CHANGED
|
@@ -1,21 +1,38 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {TypedObject} from "../typed-object";
|
|
2
|
+
import {lazy} from "./lazy";
|
|
2
3
|
|
|
3
|
-
|
|
4
|
-
const sgr = makeSGR(enabled);
|
|
4
|
+
import type {ChainKey, FormatBuilder, Style} from "./types";
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
}
|
|
6
|
+
const propParams = TypedObject.entries<Record<keyof Style, SkipFirst<Parameters<FormatBuilder>>>>({
|
|
7
|
+
bold : [ 1, 22, true],
|
|
8
|
+
dim : [ 2, 22, true],
|
|
9
|
+
italic : [ 3, 23],
|
|
10
|
+
underline : [ 4, 24],
|
|
11
|
+
inverse : [ 7, 27],
|
|
12
|
+
hidden : [ 8, 28],
|
|
13
|
+
strikethrough : [ 9, 29],
|
|
14
|
+
doubleUnderline : [21, 24],
|
|
15
|
+
frame : [51, 54],
|
|
16
|
+
encircle : [52, 54],
|
|
17
|
+
overline : [53, 55],
|
|
18
|
+
});
|
|
20
19
|
|
|
21
|
-
export
|
|
20
|
+
export function makeStyle(
|
|
21
|
+
keys : ReadonlySet<ChainKey>,
|
|
22
|
+
makeFormatter : FormatBuilder,
|
|
23
|
+
enabled : boolean,
|
|
24
|
+
): Style<any> {
|
|
25
|
+
const rtn = {} as Style<any>;
|
|
26
|
+
|
|
27
|
+
for (const [key, args] of propParams) {
|
|
28
|
+
if (keys.has(key)) {
|
|
29
|
+
lazy.add(rtn, key, () => {
|
|
30
|
+
const newKeys = new Set(keys);
|
|
31
|
+
newKeys.delete(key);
|
|
32
|
+
return enabled ? makeFormatter(newKeys, ...args) : makeFormatter(newKeys, 0, 0);
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return rtn;
|
|
38
|
+
}
|