@peteanderson/ansi 0.3.2 → 1.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.
Files changed (182) hide show
  1. package/dist/ansi.d.ts +28 -37
  2. package/dist/ansi.d.ts.map +1 -0
  3. package/dist/ansi.js +29 -52
  4. package/dist/ansi.js.map +1 -0
  5. package/dist/caret.d.ts +5 -3
  6. package/dist/caret.d.ts.map +1 -0
  7. package/dist/caret.js +6 -8
  8. package/dist/caret.js.map +1 -0
  9. package/dist/erase.d.ts +5 -1
  10. package/dist/erase.d.ts.map +1 -0
  11. package/dist/erase.js +4 -2
  12. package/dist/erase.js.map +1 -0
  13. package/dist/features/build.d.ts +7 -11
  14. package/dist/features/build.d.ts.map +1 -0
  15. package/dist/features/build.js +12 -25
  16. package/dist/features/build.js.map +1 -0
  17. package/dist/features/detect.d.ts +4 -4
  18. package/dist/features/detect.d.ts.map +1 -0
  19. package/dist/features/detect.js +5 -6
  20. package/dist/features/detect.js.map +1 -0
  21. package/dist/features/features.d.ts +4 -4
  22. package/dist/features/features.d.ts.map +1 -0
  23. package/dist/features/features.js +24 -34
  24. package/dist/features/features.js.map +1 -0
  25. package/dist/features/index.d.ts +2 -2
  26. package/dist/features/index.d.ts.map +1 -0
  27. package/dist/features/index.js +17 -1
  28. package/dist/features/index.js.map +1 -0
  29. package/dist/features/types.d.ts +10 -0
  30. package/dist/features/types.d.ts.map +1 -0
  31. package/dist/features/types.js +3 -0
  32. package/dist/features/types.js.map +1 -0
  33. package/dist/index.d.ts +3 -81
  34. package/dist/index.d.ts.map +1 -0
  35. package/dist/index.js +23 -1
  36. package/dist/index.js.map +1 -0
  37. package/dist/pad.d.ts +3 -12
  38. package/dist/pad.d.ts.map +1 -0
  39. package/dist/pad.js +8 -14
  40. package/dist/pad.js.map +1 -0
  41. package/dist/patterns.d.ts +4 -3
  42. package/dist/patterns.d.ts.map +1 -0
  43. package/dist/patterns.js +6 -4
  44. package/dist/patterns.js.map +1 -0
  45. package/dist/scroll.d.ts +5 -3
  46. package/dist/scroll.d.ts.map +1 -0
  47. package/dist/scroll.js +6 -6
  48. package/dist/scroll.js.map +1 -0
  49. package/dist/sgr/color/color.d.ts +5 -9
  50. package/dist/sgr/color/color.d.ts.map +1 -0
  51. package/dist/sgr/color/color.js +22 -29
  52. package/dist/sgr/color/color.js.map +1 -0
  53. package/dist/sgr/color/index.d.ts +2 -2
  54. package/dist/sgr/color/index.d.ts.map +1 -0
  55. package/dist/sgr/color/index.js +17 -1
  56. package/dist/sgr/color/index.js.map +1 -0
  57. package/dist/sgr/color/test-rgb.d.ts +3 -0
  58. package/dist/sgr/color/test-rgb.d.ts.map +1 -0
  59. package/dist/sgr/color/test-rgb.js +149 -0
  60. package/dist/sgr/color/test-rgb.js.map +1 -0
  61. package/dist/sgr/color/types.d.ts +30 -0
  62. package/dist/sgr/color/types.d.ts.map +1 -0
  63. package/dist/sgr/color/types.js +3 -0
  64. package/dist/sgr/color/types.js.map +1 -0
  65. package/dist/sgr/color/utils.d.ts +7 -27
  66. package/dist/sgr/color/utils.d.ts.map +1 -0
  67. package/dist/sgr/color/utils.js +17 -32
  68. package/dist/sgr/color/utils.js.map +1 -0
  69. package/dist/sgr/index.d.ts +4 -9
  70. package/dist/sgr/index.d.ts.map +1 -0
  71. package/dist/sgr/index.js +18 -4
  72. package/dist/sgr/index.js.map +1 -0
  73. package/dist/sgr/reset.d.ts +2 -1
  74. package/dist/sgr/reset.d.ts.map +1 -0
  75. package/dist/sgr/reset.js +4 -1
  76. package/dist/sgr/reset.js.map +1 -0
  77. package/dist/sgr/sgr.d.ts +4 -3
  78. package/dist/sgr/sgr.d.ts.map +1 -0
  79. package/dist/sgr/sgr.js +8 -22
  80. package/dist/sgr/sgr.js.map +1 -0
  81. package/dist/sgr/style.d.ts +3 -1
  82. package/dist/sgr/style.d.ts.map +1 -0
  83. package/dist/sgr/style.js +5 -3
  84. package/dist/sgr/style.js.map +1 -0
  85. package/dist/sgr/utils.d.ts +3 -2
  86. package/dist/sgr/utils.d.ts.map +1 -0
  87. package/dist/sgr/utils.js +3 -3
  88. package/dist/sgr/utils.js.map +1 -0
  89. package/dist/simplify/atom.d.ts +4 -3
  90. package/dist/simplify/atom.d.ts.map +1 -0
  91. package/dist/simplify/atom.js +12 -9
  92. package/dist/simplify/atom.js.map +1 -0
  93. package/dist/simplify/index.d.ts +2 -2
  94. package/dist/simplify/index.d.ts.map +1 -0
  95. package/dist/simplify/index.js +17 -1
  96. package/dist/simplify/index.js.map +1 -0
  97. package/dist/simplify/lookup.d.ts +3 -5
  98. package/dist/simplify/lookup.d.ts.map +1 -0
  99. package/dist/simplify/lookup.js +9 -13
  100. package/dist/simplify/lookup.js.map +1 -0
  101. package/dist/simplify/simplify.d.ts +2 -1
  102. package/dist/simplify/simplify.d.ts.map +1 -0
  103. package/dist/simplify/simplify.js +7 -7
  104. package/dist/simplify/simplify.js.map +1 -0
  105. package/dist/simplify/state.d.ts +3 -8
  106. package/dist/simplify/state.d.ts.map +1 -0
  107. package/dist/simplify/state.js +9 -13
  108. package/dist/simplify/state.js.map +1 -0
  109. package/dist/simplify/test.d.ts +2 -0
  110. package/dist/simplify/test.d.ts.map +1 -0
  111. package/dist/simplify/test.js +64 -0
  112. package/dist/simplify/test.js.map +1 -0
  113. package/dist/simplify/types.d.ts +9 -0
  114. package/dist/simplify/types.d.ts.map +1 -0
  115. package/dist/simplify/types.js +3 -0
  116. package/dist/simplify/types.js.map +1 -0
  117. package/dist/simplify/utils.d.ts +3 -2
  118. package/dist/simplify/utils.d.ts.map +1 -0
  119. package/dist/simplify/utils.js +4 -3
  120. package/dist/simplify/utils.js.map +1 -0
  121. package/dist/slice.d.ts +21 -1
  122. package/dist/slice.d.ts.map +1 -0
  123. package/dist/slice.js +10 -22
  124. package/dist/slice.js.map +1 -0
  125. package/dist/strip.d.ts +4 -15
  126. package/dist/strip.d.ts.map +1 -0
  127. package/dist/strip.js +10 -17
  128. package/dist/strip.js.map +1 -0
  129. package/dist/terminal.d.ts +5 -1
  130. package/dist/terminal.d.ts.map +1 -0
  131. package/dist/terminal.js +4 -2
  132. package/dist/terminal.js.map +1 -0
  133. package/package.json +9 -22
  134. package/src/ansi.ts +71 -0
  135. package/src/{caret.js → caret.ts} +9 -15
  136. package/src/{erase.js → erase.ts} +7 -10
  137. package/src/features/build.ts +52 -0
  138. package/src/features/detect.ts +33 -0
  139. package/src/features/features.ts +48 -0
  140. package/src/features/index.ts +1 -0
  141. package/src/features/{types.d.ts → types.ts} +1 -1
  142. package/src/index.ts +2 -0
  143. package/src/pad.ts +7 -0
  144. package/src/{patterns.js → patterns.ts} +3 -5
  145. package/src/scroll.ts +13 -0
  146. package/src/sgr/color/{color.js → color.ts} +26 -35
  147. package/src/sgr/color/index.ts +1 -0
  148. package/src/sgr/color/{types.d.ts → types.ts} +6 -2
  149. package/src/sgr/color/utils.ts +58 -0
  150. package/src/sgr/index.ts +3 -0
  151. package/src/sgr/reset.ts +1 -0
  152. package/src/sgr/sgr.ts +68 -0
  153. package/src/sgr/{style.js → style.ts} +3 -5
  154. package/src/sgr/{utils.js → utils.ts} +3 -6
  155. package/src/simplify/{atom.js → atom.ts} +11 -13
  156. package/src/simplify/index.ts +1 -0
  157. package/src/simplify/{lookup.js → lookup.ts} +5 -14
  158. package/src/simplify/{simplify.js → simplify.ts} +4 -10
  159. package/src/simplify/state.ts +63 -0
  160. package/src/simplify/{types.d.ts → types.ts} +5 -5
  161. package/src/simplify/{utils.js → utils.ts} +4 -10
  162. package/src/{slice.js → slice.ts} +12 -26
  163. package/src/strip.ts +5 -0
  164. package/src/{terminal.js → terminal.ts} +4 -7
  165. package/LICENSE +0 -7
  166. package/README.md +0 -174
  167. package/src/ansi.js +0 -70
  168. package/src/features/build.js +0 -68
  169. package/src/features/detect.js +0 -40
  170. package/src/features/features.js +0 -53
  171. package/src/features/index.js +0 -1
  172. package/src/index.js +0 -1
  173. package/src/pad.js +0 -21
  174. package/src/scroll.js +0 -18
  175. package/src/sgr/color/index.js +0 -1
  176. package/src/sgr/color/utils.js +0 -81
  177. package/src/sgr/index.js +0 -5
  178. package/src/sgr/reset.js +0 -3
  179. package/src/sgr/sgr.js +0 -77
  180. package/src/simplify/index.js +0 -1
  181. package/src/simplify/state.js +0 -73
  182. package/src/strip.js +0 -23
@@ -0,0 +1,52 @@
1
+ import { getColorDepth, detectFeatureSupport } from "./detect";
2
+ import type { ColorDepth, Features } from "./types";
3
+
4
+ function normalizeColorDepth(colorDepth: number): ColorDepth {
5
+ colorDepth = Math.floor(colorDepth);
6
+ if (colorDepth >= 24) return 24;
7
+ if (colorDepth >= 8) return 8;
8
+ if (colorDepth >= 4) return 4;
9
+ if (colorDepth === 3) return 3;
10
+ return 1;
11
+ }
12
+
13
+ export function build(
14
+ isTTY : boolean,
15
+ colorDepth : ColorDepth,
16
+ style? : boolean,
17
+ caret? : boolean,
18
+ erase? : boolean,
19
+ scroll? : boolean,
20
+ terminal? : boolean,
21
+ ): Features {
22
+ if (
23
+ style !== undefined &&
24
+ caret !== undefined &&
25
+ erase !== undefined &&
26
+ scroll !== undefined &&
27
+ terminal !== undefined
28
+ )
29
+ return {colorDepth: normalizeColorDepth(colorDepth), style, caret, erase, scroll, terminal};
30
+
31
+ const support = detectFeatureSupport(isTTY, colorDepth);
32
+ return {
33
+ colorDepth : normalizeColorDepth(colorDepth),
34
+ style : style ?? support.style,
35
+ caret : caret ?? support.caret,
36
+ erase : erase ?? support.erase,
37
+ scroll : scroll ?? support.scroll,
38
+ terminal : terminal ?? support.terminal,
39
+ };
40
+ }
41
+
42
+ export const boolean = (isTTY: boolean, enabled: boolean): Features =>
43
+ build(isTTY, enabled ? 4 : 1);
44
+
45
+ export const number = (isTTY: boolean, colorDepth: ColorDepth): Features =>
46
+ build(isTTY, colorDepth);
47
+
48
+ export const tty = (stream: NodeJS.WriteStream): Features =>
49
+ build(true, getColorDepth(stream));
50
+
51
+ export const pipe = (): Features =>
52
+ build(false, getColorDepth());
@@ -0,0 +1,33 @@
1
+ import type { ColorDepth, Features } from "./types";
2
+
3
+ export function getColorDepth(stream?: NodeJS.WriteStream): ColorDepth {
4
+ if (stream?.isTTY)
5
+ return stream.getColorDepth() as ColorDepth;
6
+
7
+ if (process.env.NO_COLOR || process.env.NODE_DISABLE_COLORS)
8
+ return 1;
9
+
10
+ switch (process.env.FORCE_COLOR) {
11
+ case "":
12
+ case "true":
13
+ case "1":
14
+ return 4;
15
+ case "2":
16
+ return 8;
17
+ case "3":
18
+ return 24;
19
+ default:
20
+ return 1;
21
+ }
22
+ }
23
+
24
+ export function detectFeatureSupport(isTTY: boolean, colorDepth: ColorDepth): Omit<Features, "colorDepth"> {
25
+ const enabled = process.env.TERM !== "dumb" && isTTY;
26
+ return {
27
+ style : colorDepth > 1,
28
+ caret : enabled,
29
+ erase : enabled,
30
+ scroll : enabled,
31
+ terminal : enabled,
32
+ };
33
+ }
@@ -0,0 +1,48 @@
1
+ import { Socket } from "node:net";
2
+ import { WriteStream } from "node:tty";
3
+ import { build, boolean, number, tty, pipe } from "./build";
4
+ import { getColorDepth } from "./detect";
5
+ import type { ColorDepth, Features } from "./types";
6
+
7
+ export type Args =
8
+ | [enabled : boolean, stream?: NodeJS.WriteStream]
9
+ | [colorDepth : ColorDepth, stream?: NodeJS.WriteStream]
10
+ | [features : Partial<Features>, stream?: NodeJS.WriteStream]
11
+ | [socket : Socket]
12
+ | [stream? : NodeJS.WriteStream];
13
+
14
+ export function getFeatures(...args: Args): Features {
15
+ const [first, second] = args as [
16
+ boolean | ColorDepth | Partial<Features> | Socket | NodeJS.WriteStream | undefined,
17
+ NodeJS.WriteStream | undefined,
18
+ ];
19
+
20
+ if (typeof first === "boolean")
21
+ return boolean((second ?? process.stdout).isTTY, first);
22
+
23
+ if (typeof first === "number")
24
+ return number((second ?? process.stdout).isTTY, first);
25
+
26
+ let input: Partial<Features> | Socket | NodeJS.WriteStream =
27
+ !first || typeof first !== "object"
28
+ ? second ?? process.stdout
29
+ : first;
30
+
31
+ if (input instanceof WriteStream)
32
+ return tty(input);
33
+
34
+ if (input instanceof Socket)
35
+ return pipe();
36
+
37
+ const {colorDepth, style, caret, erase, scroll, terminal} = input as Partial<Features>;
38
+
39
+ const stream = second ?? process.stdout;
40
+
41
+ if (colorDepth)
42
+ build(stream.isTTY, colorDepth, style, caret, erase, scroll, terminal);
43
+
44
+ if (stream.isTTY)
45
+ return build(true, getColorDepth(stream), style, caret, erase, scroll, terminal);
46
+
47
+ return build(false, getColorDepth(), style, caret, erase, scroll, terminal);
48
+ }
@@ -0,0 +1 @@
1
+ export * from "./features";
@@ -7,4 +7,4 @@ export interface Features {
7
7
  erase : boolean;
8
8
  scroll : boolean;
9
9
  terminal : boolean;
10
- }
10
+ }
package/src/index.ts ADDED
@@ -0,0 +1,2 @@
1
+ export {default} from "./ansi";
2
+ export * from "./ansi";
package/src/pad.ts ADDED
@@ -0,0 +1,7 @@
1
+ import {visibleLength} from "./strip";
2
+
3
+ export const padEnd = (text: string, targetLength: number, fillString = " "): string =>
4
+ text + fillString.repeat(Math.max(0, targetLength - visibleLength(text)) / fillString.length);
5
+
6
+ export const padStart = (text: string, targetLength: number, fillString = " "): string =>
7
+ fillString.repeat(Math.max(0, targetLength - visibleLength(text)) / fillString.length) + text;
@@ -37,8 +37,6 @@ timeit(() => long .replace(new RegExp(rxSGR.source, "g"), ""), {description: "lo
37
37
  ```
38
38
  */
39
39
 
40
- const rxSGR = Object.freeze(/\x1b\[[\d;]*m/g);
41
- const rxCSI = Object.freeze(/\x1b\[[0-?]*[ -/]*[@-~]/g);
42
- const rxUnsafe = Object.freeze(/\x1b\[[0-?]*[ -/]*[@-ln-~]/g); // Everything except SGR codes (m).
43
-
44
- module.exports = {rxSGR, rxCSI, rxUnsafe};
40
+ export const rxSGR = Object.freeze(/\x1b\[[\d;]*m/g);
41
+ export const rxCSI = Object.freeze(/\x1b\[[0-?]*[ -/]*[@-~]/g);
42
+ export const rxUnsafe = Object.freeze(/\x1b\[[0-?]*[ -/]*[@-ln-~]/g); // Everything except SGR codes (m).
package/src/scroll.ts ADDED
@@ -0,0 +1,13 @@
1
+ const scroll = {
2
+ up : (lines?: number): string => `\x1b[${lines ?? 1}S`,
3
+ down : (lines?: number): string => `\x1b[${lines ?? 1}T`,
4
+ };
5
+
6
+ export type Scroll = typeof scroll;
7
+
8
+ const disabled: Scroll = {
9
+ up : () => "",
10
+ down : () => "",
11
+ };
12
+
13
+ export const makeScroll = (enabled = true): Scroll => enabled ? scroll : disabled;
@@ -1,25 +1,23 @@
1
- const {makeSGR} = require("../sgr");
2
- const {makeStyle} = require("../style");
3
- const {clip, rgbToX256, rgbToX16, rgbToX8, x256ToRgb} = require("./utils");
1
+ import {makeSGR} from "../sgr";
2
+ import {makeStyle} from "../style";
3
+ import {clip, rgbToX16, rgbToX256, rgbToX8, x256ToRgb} from "./utils";
4
4
 
5
- /** @typedef {import("../sgr").Code} Code */
6
- /** @typedef {import("../../features/types").ColorDepth} ColorDepth */
7
- /** @typedef {import("../sgr").SGR} SGR */
8
- /** @typedef {import("./types").Channel} Channel */
5
+ import type {ColorDepth} from "../../features/types";
6
+ import type {SGR} from "../sgr";
7
+ import type {Channel, Color} from "./types";
8
+
9
+ export type {Color} from "./types";
9
10
 
10
11
  const fgOpen = 30, fgClose = 39;
11
12
  const bgOpen = 40, bgClose = 49;
12
13
 
13
- /** @type {(colorDepth: ColorDepth, open: number, close: number) => Channel} */
14
- function makeChannel(colorDepth, open, close) {
14
+ function makeChannel(colorDepth: ColorDepth, open: number, close: number): Channel {
15
15
  const sgrBase = makeSGR(colorDepth > 1);
16
16
  const style = makeStyle(colorDepth > 1);
17
17
 
18
- /** @type {(code: Code) => SGR} */
19
- const sgr = code => sgrBase(code, close);
18
+ const sgr = (code: number | number[]): SGR => sgrBase(code, close);
20
19
 
21
- /** @type {(index: number) => SGR} */
22
- const bright =
20
+ const bright: (index: number) => SGR =
23
21
  colorDepth >= 4 ? index => sgr(open + 60 + index) :
24
22
  colorDepth === 3 ?
25
23
  open === fgOpen ?
@@ -27,11 +25,10 @@ function makeChannel(colorDepth, open, close) {
27
25
  index => sgr(open + index) :
28
26
  () => sgr(0);
29
27
 
30
- /** @type {(code: number) => SGR} */
31
- const x16ToX8 =
28
+ const x16ToX8: (code: number) => SGR =
32
29
  open === fgOpen ?
33
30
  code => code >= 60 ? style.bold.combine(sgr(open + code - 60)) : sgr(open + code) :
34
- code => code >= 60 ? sgr(open + code - 60) : sgr(open + code);
31
+ code => code >= 60 ? sgr(open + code - 60) : sgr(open + code);
35
32
 
36
33
  return {
37
34
  black : sgr(open + 0),
@@ -62,43 +59,37 @@ function makeChannel(colorDepth, open, close) {
62
59
  (r, g, b) => sgr(open + rgbToX8(...clip(r, g, b))) :
63
60
  () => sgr(0),
64
61
 
65
- /** @type {(...args: [number] | [number, number, number]) => SGR} */
66
62
  x256:
67
63
  colorDepth >= 8 ?
68
- (...args) => sgr(
64
+ (...args: [number] | [number, number, number]): SGR => sgr(
69
65
  [open + 8, 5, args.length === 3 ? rgbToX256(...clip(...args)) : clip(args[0])],
70
66
  ) :
71
67
  colorDepth >= 4 ?
72
- (...args) => sgr(
68
+ (...args: [number] | [number, number, number]): SGR => sgr(
73
69
  open +
74
70
  rgbToX16(...(args.length === 3 ? clip(...args) : x256ToRgb(clip(args[0])))),
75
71
  ) :
76
72
  colorDepth === 3 ?
77
73
  open === fgOpen ?
78
- (...args) => x16ToX8(
79
- args.length === 3 ?
80
- rgbToX16(...clip(...args)) :
81
- rgbToX16(...x256ToRgb(clip(args[0]))),
74
+ (...args: [number] | [number, number, number]): SGR => x16ToX8(
75
+ args.length === 3
76
+ ? rgbToX16(...clip(...args))
77
+ : rgbToX16(...x256ToRgb(clip(args[0]))),
82
78
  ) :
83
- (...args) => sgr(
84
- open + rgbToX8(
85
- ...args.length === 3 ? clip(...args) : x256ToRgb(clip(args[0])),
86
- ),
79
+ (...args: [number] | [number, number, number]): SGR => sgr(
80
+ open +
81
+ rgbToX8(...args.length === 3 ? clip(...args) : x256ToRgb(clip(args[0]))),
87
82
  ) :
88
- () => sgr(0),
83
+ (): SGR => sgr(0),
89
84
 
90
85
  default: colorDepth > 1 ? `\x1b[${close}m` : "",
91
86
  };
92
87
  }
93
88
 
94
- /** @type {Partial<Record<ColorDepth, {fg: Channel, bg: Channel}>>} */
95
- const cache = {};
89
+ const cache: Partial<Record<ColorDepth, Color>> = {};
96
90
 
97
- /** @type {(colorDepth: ColorDepth) => {fg: Channel, bg: Channel}} */
98
- const makeColor = (colorDepth = 4) =>
91
+ export const makeColor = (colorDepth: ColorDepth = 4): Color =>
99
92
  cache[colorDepth] ?? (cache[colorDepth] = {
100
93
  fg : makeChannel(colorDepth, fgOpen, fgClose),
101
94
  bg : makeChannel(colorDepth, bgOpen, bgClose),
102
- });
103
-
104
- module.exports = {makeColor};
95
+ });
@@ -0,0 +1 @@
1
+ export * from "./color";
@@ -1,3 +1,5 @@
1
+ import type {SGR} from "../sgr";
2
+
1
3
  export interface Channel {
2
4
  black : SGR;
3
5
  red : SGR;
@@ -24,5 +26,7 @@ export interface Channel {
24
26
  (r: number, g: number, b: number): SGR;
25
27
  };
26
28
 
27
- default: string,
28
- }
29
+ default: string;
30
+ }
31
+
32
+ export type Color = {fg: Channel; bg: Channel};
@@ -0,0 +1,58 @@
1
+ export function rgbToX256(r: number, g: number, b: number): number {
2
+ if (r === g && g === b)
3
+ return 232 + Math.floor(r * 24 / 256);
4
+
5
+ return (
6
+ 16 +
7
+ Math.floor(r * 6 / 256) * 36 +
8
+ Math.floor(g * 6 / 256) * 6 +
9
+ Math.floor(b * 6 / 256)
10
+ );
11
+ }
12
+
13
+ export function x256ToRgb(code: number): [number, number, number] {
14
+ if (code >= 232) {
15
+ const gray = Math.round((code - 232) * 255 / 24);
16
+ return [gray, gray, gray];
17
+ }
18
+
19
+ code -= 16;
20
+ const r = Math.floor(code / 36) * 51;
21
+ const g = Math.floor((code % 36) / 6) * 51;
22
+ const b = (code % 6) * 51;
23
+ return [r, g, b];
24
+ }
25
+
26
+ export function rgbToX16(r: number, g: number, b: number): number {
27
+ r = Math.min(2, Math.floor(r / 85));
28
+ g = Math.min(2, Math.floor(g / 85));
29
+ b = Math.min(2, Math.floor(b / 85));
30
+
31
+ const index = (r ? 1 : 0) | (g ? 2 : 0) | (b ? 4 : 0);
32
+
33
+ const twos = (r === 2 ? 1 : 0) + (g === 2 ? 1 : 0) + (b === 2 ? 1 : 0);
34
+ const ones = (r === 1 ? 1 : 0) + (g === 1 ? 1 : 0) + (b === 1 ? 1 : 0);
35
+ const bright = twos > 0 && twos >= ones;
36
+
37
+ return index + (bright ? 60 : 0);
38
+ }
39
+
40
+ export function rgbToX8(r: number, g: number, b: number): number {
41
+ r = r >= 128 ? 1 : 0;
42
+ g = g >= 128 ? 1 : 0;
43
+ b = b >= 128 ? 1 : 0;
44
+
45
+ return (r ? 1 : 0) | (g ? 2 : 0) | (b ? 4 : 0);
46
+ }
47
+
48
+ export function clip(x: number): number;
49
+ export function clip(r: number, g: number, b: number): [number, number, number];
50
+ export function clip(...args: [number] | [number, number, number]): number | [number, number, number] {
51
+ return args.length === 1
52
+ ? Math.max(0, Math.min(255, Math.floor(args[0])))
53
+ : [
54
+ Math.max(0, Math.min(255, Math.floor(args[0]))),
55
+ Math.max(0, Math.min(255, Math.floor(args[1]))),
56
+ Math.max(0, Math.min(255, Math.floor(args[2]))),
57
+ ];
58
+ }
@@ -0,0 +1,3 @@
1
+ export * from "./reset";
2
+ export * from "./style";
3
+ export * from "./color";
@@ -0,0 +1 @@
1
+ export const makeReset = (enabled = true): string => enabled ? "\x1b[0m" : "";
package/src/sgr/sgr.ts ADDED
@@ -0,0 +1,68 @@
1
+ export type Code = number | number[];
2
+
3
+ export interface SGR {
4
+ (text: string): string;
5
+ open : string;
6
+ close : string;
7
+ codes : {open: Code; close: Code};
8
+ combine : (...styles: SGR[]) => SGR;
9
+ }
10
+
11
+ function combine(code: Code, codes: Code[]): Code {
12
+ if (!codes.length)
13
+ return code;
14
+
15
+ const rtn = Array.isArray(code) ? [...code] : [code];
16
+
17
+ for (const c of codes) {
18
+ if (Array.isArray(c))
19
+ rtn.push(...c);
20
+ else
21
+ rtn.push(c);
22
+ }
23
+
24
+ return rtn;
25
+ }
26
+
27
+ const codeSequence = (code: Code): string | number => Array.isArray(code) ? code.join(";") : code;
28
+
29
+ function sgr(open: Code, close: Code, reset = false): SGR {
30
+ const openCode = codeSequence(open);
31
+ const closeCode = codeSequence(close);
32
+ const openSequence = `\x1b[${openCode}m`;
33
+ const closeSequence = `\x1b[${closeCode}m`;
34
+ const reopenCode = reset ? `${closeCode};${openCode}` : openCode;
35
+
36
+ const rxClose = new RegExp(
37
+ `(?<start>\\x1b\\[(?:\\d+;)*)${closeCode}(?<end>(?:;\\d+)*m)`,
38
+ "g"
39
+ );
40
+ const replace = `$<start>${reopenCode}$<end>`;
41
+
42
+ const func = (s: string) => s ? openSequence + s.replace(rxClose, replace) + closeSequence : s;
43
+
44
+ return Object.assign(func, {
45
+ codes : {open, close},
46
+ open : openSequence,
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
+ }
54
+
55
+ let disabledSGR = null as unknown as SGR;
56
+ disabledSGR = Object.assign(
57
+ (s: string) => s,
58
+ {
59
+ codes : {open: 0, close: 0},
60
+ open : "",
61
+ close : "",
62
+ combine : (): SGR => disabledSGR,
63
+ },
64
+ );
65
+
66
+ const disabled = (): SGR => disabledSGR;
67
+
68
+ export const makeSGR = (enabled = true): typeof sgr => enabled ? sgr : disabled;
@@ -1,8 +1,6 @@
1
- "use strict";
1
+ import {makeSGR} from "./sgr";
2
2
 
3
- const {makeSGR} = require("./sgr");
4
-
5
- function makeStyle(enabled = true) {
3
+ export function makeStyle(enabled = true) {
6
4
  const sgr = makeSGR(enabled);
7
5
 
8
6
  return {
@@ -20,4 +18,4 @@ function makeStyle(enabled = true) {
20
18
  };
21
19
  }
22
20
 
23
- module.exports = {makeStyle};
21
+ export type Style = ReturnType<typeof makeStyle>;
@@ -1,7 +1,6 @@
1
- /** @typedef {number | number[]} Code */
1
+ import type {Code} from "./sgr";
2
2
 
3
- /** @type {(code: Code, codes: Code[]) => Code} */
4
- function combine(code, codes, ) {
3
+ export function combine(code: Code, codes: Code[]): Code {
5
4
  if (!codes.length)
6
5
  return code;
7
6
 
@@ -15,6 +14,4 @@ function combine(code, codes, ) {
15
14
  }
16
15
 
17
16
  return rtn;
18
- }
19
-
20
- module.exports = {combine};
17
+ }
@@ -1,9 +1,6 @@
1
- "use strict";
1
+ import type {Attribute, BuildAtom} from "./types";
2
2
 
3
- /** @typedef {import("./types").BuildAtom} BuildAtom */
4
-
5
- /** @type {BuildAtom} */
6
- function buildExtendedColorAtom(attribute, codes, index) {
3
+ export const buildExtendedColorAtom: BuildAtom = (attribute, codes, index) => {
7
4
  switch (codes[index + 1]) {
8
5
  case 2:
9
6
  if (index + 5 >= codes.length)
@@ -16,19 +13,20 @@ function buildExtendedColorAtom(attribute, codes, index) {
16
13
  default:
17
14
  return {attribute, code: codes.slice(index, index + 2), skip: 1};
18
15
  }
19
- }
16
+ };
20
17
 
21
- /** @type {BuildAtom} */
22
- function buildIntensityAtom(attribute, codes, index, state) {
18
+ export const buildIntensityAtom: BuildAtom = (attribute, codes, index, state) => {
23
19
  const code = codes[index];
24
20
  if (code === 22)
25
21
  return {attribute, code: [code]};
26
22
 
27
- const current = state.get("intensity");
28
- if (!current || current.length == 1 && current[0] === code || current[0] === 22)
23
+ const current = state.get("intensity" as Attribute);
24
+ if (
25
+ !current ||
26
+ current[0] === 22 ||
27
+ current.length === 1 && current[0] === code
28
+ )
29
29
  return {attribute, code: [code]};
30
30
 
31
31
  return {attribute, code: [1, 2]};
32
- }
33
-
34
- module.exports = {buildExtendedColorAtom, buildIntensityAtom};
32
+ };
@@ -0,0 +1 @@
1
+ export * from "./simplify";
@@ -1,14 +1,8 @@
1
- "use strict";
1
+ import {buildExtendedColorAtom, buildIntensityAtom} from "./atom";
2
2
 
3
- const {buildExtendedColorAtom, buildIntensityAtom} = require("./atom");
3
+ import type {Atom, Attribute, AttributeMap, BuildAtom} from "./types";
4
4
 
5
- /** @typedef {import("./types").Atom} Atom */
6
- /** @typedef {import("./types").Attribute} Attribute */
7
- /** @typedef {import("./types").AttributeMap} AttributeMap */
8
- /** @typedef {import("./types").BuildAtom} BuildAtom */
9
-
10
- /** @type {Record<number, [attribute: Attribute, buildAtom?: BuildAtom]>} */
11
- const codeMap = {
5
+ const codeMap: Record<number, [attribute: Attribute, buildAtom?: BuildAtom]> = {
12
6
  0 : ["reset"],
13
7
  1 : ["intensity", buildIntensityAtom],
14
8
  2 : ["intensity", buildIntensityAtom],
@@ -67,8 +61,7 @@ const codeMap = {
67
61
  107 : ["bg"],
68
62
  };
69
63
 
70
- /** @type {(codes: number[], index: number, state: AttributeMap) => Atom} */
71
- function lookUpAtom(codes, index, state) {
64
+ export function lookUpAtom(codes: number[], index: number, state: AttributeMap): Atom {
72
65
  const code = codes[index];
73
66
  const entry = codeMap[code];
74
67
 
@@ -81,6 +74,4 @@ function lookUpAtom(codes, index, state) {
81
74
  return buildAtom(attribute, codes, index, state);
82
75
 
83
76
  return {attribute, code: [code]};
84
- }
85
-
86
- module.exports = {lookUpAtom};
77
+ }
@@ -1,10 +1,7 @@
1
- "use strict";
1
+ import {rxSGR} from "../patterns";
2
+ import {State} from "./state";
2
3
 
3
- const {rxSGR} = require("../patterns");
4
- const {State} = require("./state");
5
-
6
- /** @type {(text: string) => string} */
7
- function simplify(text) {
4
+ export function simplify(text: string): string {
8
5
  // `.split` doesn't modify `lastIndex`, so no clone is necessary here.
9
6
  const parts = text.split(rxSGR);
10
7
 
@@ -16,7 +13,6 @@ function simplify(text) {
16
13
  let chunk = parts[i++];
17
14
 
18
15
  while (!chunk && i < parts.length) {
19
- // Merge consecutive SGR sequences.
20
16
  codes.push(...parts[i++].split(";").map(Number));
21
17
  chunk = parts[i++];
22
18
  }
@@ -28,6 +24,4 @@ function simplify(text) {
28
24
  }
29
25
 
30
26
  return result;
31
- }
32
-
33
- module.exports = {simplify};
27
+ }
@@ -0,0 +1,63 @@
1
+ import {lookUpAtom} from "./lookup";
2
+ import {arraysEqual, transitionIntensity} from "./utils";
3
+
4
+ import type {Attribute, AttributeMap} from "./types";
5
+
6
+ const defaultCodes: Map<Attribute, number[]> = new Map([
7
+ ["intensity", [22]],
8
+ ["italic", [23]],
9
+ ["underline", [24]],
10
+ ["inverse", [27]],
11
+ ["hidden", [28]],
12
+ ["strikethrough", [29]],
13
+ ["fg", [39]],
14
+ ["bg", [49]],
15
+ ["frame", [54]],
16
+ ["overline", [55]],
17
+ ]);
18
+
19
+ export class State extends Map<Attribute, number[]> implements AttributeMap {
20
+ update(codes: number[]): number[] {
21
+ let snapshot: AttributeMap = new Map(this);
22
+
23
+ const attributes = new Set<Attribute>();
24
+
25
+ for (let i = 0; i < codes.length; i++) {
26
+ const {attribute, code, skip} = lookUpAtom(codes, i, this);
27
+
28
+ if (attribute === "reset") {
29
+ attributes.clear();
30
+ this.clear();
31
+ attributes.add("reset");
32
+ for (const [attr, defaultCode] of defaultCodes)
33
+ this.set(attr, defaultCode);
34
+
35
+ snapshot = new Map(this);
36
+ continue;
37
+ }
38
+
39
+ attributes.add(attribute);
40
+ this.set(attribute, code);
41
+
42
+ if (skip)
43
+ i += skip;
44
+ }
45
+
46
+ return Array.from(attributes).flatMap(attribute => {
47
+ if (attribute === "reset")
48
+ return [0];
49
+
50
+ const code = this.get(attribute)!;
51
+
52
+ if (arraysEqual(code, snapshot.get(attribute)))
53
+ return [];
54
+
55
+ if (attribute === "intensity") {
56
+ const previous = snapshot.get(attribute);
57
+ return transitionIntensity(previous, code);
58
+ }
59
+
60
+ return code;
61
+ });
62
+ }
63
+ }