@heinrichb/console-toolkit 1.0.5 → 1.0.7
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/dist/components/progress.d.ts +12 -12
- package/dist/components/progress.js +5 -6
- package/dist/components/progress.test.js +14 -10
- package/dist/core/layout.d.ts +6 -6
- package/dist/core/layout.js +9 -11
- package/dist/core/layout.test.js +16 -5
- package/dist/core/printer.d.ts +20 -7
- package/dist/core/printer.js +105 -17
- package/dist/core/printer.test.js +112 -24
- package/dist/core/style.d.ts +31 -29
- package/dist/core/style.js +114 -70
- package/dist/core/style.test.js +81 -26
- package/dist/core/types.d.ts +42 -12
- package/dist/core/utils.d.ts +10 -10
- package/dist/core/utils.js +6 -9
- package/dist/core/utils.test.js +5 -5
- package/dist/demo.js +79 -122
- package/dist/presets/ascii.d.ts +3 -3
- package/dist/presets/ascii.js +5 -3
- package/dist/presets/ascii.test.js +15 -4
- package/package.json +45 -46
- package/dist/index.test.d.ts +0 -1
- package/dist/index.test.js +0 -146
- package/dist/progress.d.ts +0 -75
- package/dist/progress.js +0 -43
- package/dist/progress.test.d.ts +0 -1
- package/dist/progress.test.js +0 -101
- package/dist/spinner.d.ts +0 -36
- package/dist/spinner.js +0 -33
- package/dist/spinner.test.d.ts +0 -1
- package/dist/spinner.test.js +0 -53
package/dist/core/style.d.ts
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
import { HexColor,
|
|
1
|
+
import { Color, HexColor, PrintStyle, StyleModifier } from "./types";
|
|
2
2
|
/**
|
|
3
3
|
* ANSI escape sequence to reset all styles.
|
|
4
4
|
*/
|
|
5
5
|
export declare const RESET = "\u001B[0m";
|
|
6
|
+
/**
|
|
7
|
+
* Converts any Color (hex or standard name) to a Hex string.
|
|
8
|
+
*/
|
|
9
|
+
export declare function colorToHex(color: Color): string;
|
|
6
10
|
/**
|
|
7
11
|
* Converts a hex color string to an RGB object.
|
|
8
|
-
* Validates the hex string format.
|
|
9
|
-
*
|
|
10
|
-
* @param hex - Hex color in the form "#RRGGBB".
|
|
11
|
-
* @returns Object with r, g, b components (0-255).
|
|
12
12
|
*/
|
|
13
13
|
export declare function hexToRgb(hex: string): {
|
|
14
14
|
r: number;
|
|
@@ -17,35 +17,37 @@ export declare function hexToRgb(hex: string): {
|
|
|
17
17
|
};
|
|
18
18
|
/**
|
|
19
19
|
* Converts RGB to a 24-bit ANSI foreground color escape sequence.
|
|
20
|
-
*
|
|
21
|
-
* @param r - Red component (0-255).
|
|
22
|
-
* @param g - Green component (0-255).
|
|
23
|
-
* @param b - Blue component (0-255).
|
|
24
|
-
* @returns ANSI escape sequence string.
|
|
25
20
|
*/
|
|
26
21
|
export declare function rgbToAnsi(r: number, g: number, b: number): string;
|
|
27
22
|
/**
|
|
28
|
-
* Converts
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
23
|
+
* Converts any Color to an ANSI escape sequence.
|
|
24
|
+
*/
|
|
25
|
+
export declare function resolveColorToAnsi(color: Color): string;
|
|
26
|
+
/**
|
|
27
|
+
* Converts a list of modifiers to an ANSI escape sequence.
|
|
28
|
+
*/
|
|
29
|
+
export declare function resolveModifiersToAnsi(modifiers?: StyleModifier[]): string;
|
|
30
|
+
/**
|
|
31
|
+
* Interpolates between two hex colors.
|
|
32
|
+
*/
|
|
33
|
+
export declare function interpolateHex(color1: string, color2: string, factor: number): string;
|
|
34
|
+
/**
|
|
35
|
+
* Public interpolation function that accepts any Color type.
|
|
36
|
+
*/
|
|
37
|
+
export declare function interpolateColor(color1: Color, color2: Color, factor: number): HexColor;
|
|
38
|
+
/**
|
|
39
|
+
* logic to get a specific color from a multi-stop gradient array at a specific factor (0-1).
|
|
32
40
|
*/
|
|
33
|
-
export declare function
|
|
41
|
+
export declare function getGradientColor(colors: Color[], factor: number): string;
|
|
34
42
|
/**
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
* @param color1 - Start color (hex).
|
|
39
|
-
* @param color2 - End color (hex).
|
|
40
|
-
* @param factor - Interpolation factor (0.0 to 1.0).
|
|
41
|
-
* @returns Interpolated Hex color.
|
|
43
|
+
* Merges a child style into a parent style.
|
|
44
|
+
* - Modifiers are combined (union).
|
|
45
|
+
* - Child color overrides parent color.
|
|
42
46
|
*/
|
|
43
|
-
export declare function
|
|
47
|
+
export declare function mergeStyles(parent?: PrintStyle, child?: PrintStyle): PrintStyle;
|
|
44
48
|
/**
|
|
45
|
-
* Resolves a
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
* @param style - The style or array of styles to resolve.
|
|
49
|
-
* @returns The resulting ANSI escape sequence string.
|
|
49
|
+
* Resolves a style object into an ANSI string.
|
|
50
|
+
* If the color is a gradient (array), it uses the provided factor (0-1) to pick the color.
|
|
51
|
+
* Defaults to factor 0 if not provided.
|
|
50
52
|
*/
|
|
51
|
-
export declare function resolveStyle(style?:
|
|
53
|
+
export declare function resolveStyle(style?: PrintStyle, gradientFactor?: number): string;
|
package/dist/core/style.js
CHANGED
|
@@ -6,17 +6,46 @@ const ESC = "\x1b";
|
|
|
6
6
|
* ANSI escape sequence to reset all styles.
|
|
7
7
|
*/
|
|
8
8
|
export const RESET = `${ESC}[0m`;
|
|
9
|
+
const STANDARD_COLORS = {
|
|
10
|
+
black: "#000000",
|
|
11
|
+
red: "#EF4444", // Tailwind Red-500
|
|
12
|
+
green: "#10B981", // Tailwind Emerald-500
|
|
13
|
+
yellow: "#F59E0B", // Tailwind Amber-500
|
|
14
|
+
blue: "#3B82F6", // Tailwind Blue-500
|
|
15
|
+
magenta: "#EC4899", // Tailwind Pink-500
|
|
16
|
+
cyan: "#06B6D4", // Tailwind Cyan-500
|
|
17
|
+
white: "#FFFFFF",
|
|
18
|
+
gray: "#6B7280", // Tailwind Gray-500
|
|
19
|
+
grey: "#6B7280"
|
|
20
|
+
};
|
|
21
|
+
const MODIFIER_CODES = {
|
|
22
|
+
default: "0",
|
|
23
|
+
bold: "1",
|
|
24
|
+
dim: "2",
|
|
25
|
+
italic: "3",
|
|
26
|
+
underline: "4",
|
|
27
|
+
inverse: "7",
|
|
28
|
+
hidden: "8",
|
|
29
|
+
strikethrough: "9"
|
|
30
|
+
};
|
|
31
|
+
/**
|
|
32
|
+
* Converts any Color (hex or standard name) to a Hex string.
|
|
33
|
+
*/
|
|
34
|
+
export function colorToHex(color) {
|
|
35
|
+
if (color.startsWith("#"))
|
|
36
|
+
return color;
|
|
37
|
+
const hex = STANDARD_COLORS[color.toLowerCase()];
|
|
38
|
+
if (!hex)
|
|
39
|
+
return "#FFFFFF"; // Fallback
|
|
40
|
+
return hex;
|
|
41
|
+
}
|
|
9
42
|
/**
|
|
10
43
|
* Converts a hex color string to an RGB object.
|
|
11
|
-
* Validates the hex string format.
|
|
12
|
-
*
|
|
13
|
-
* @param hex - Hex color in the form "#RRGGBB".
|
|
14
|
-
* @returns Object with r, g, b components (0-255).
|
|
15
44
|
*/
|
|
16
45
|
export function hexToRgb(hex) {
|
|
17
46
|
const h = hex.replace(/^#/, "");
|
|
18
47
|
if (h.length !== 6)
|
|
19
|
-
|
|
48
|
+
return { r: 255, g: 255, b: 255 }; // Fallback for invalid hex
|
|
20
49
|
return {
|
|
21
50
|
r: parseInt(h.substring(0, 2), 16),
|
|
22
51
|
g: parseInt(h.substring(2, 4), 16),
|
|
@@ -25,43 +54,42 @@ export function hexToRgb(hex) {
|
|
|
25
54
|
}
|
|
26
55
|
/**
|
|
27
56
|
* Converts RGB to a 24-bit ANSI foreground color escape sequence.
|
|
28
|
-
*
|
|
29
|
-
* @param r - Red component (0-255).
|
|
30
|
-
* @param g - Green component (0-255).
|
|
31
|
-
* @param b - Blue component (0-255).
|
|
32
|
-
* @returns ANSI escape sequence string.
|
|
33
57
|
*/
|
|
34
58
|
export function rgbToAnsi(r, g, b) {
|
|
35
59
|
return `${ESC}[38;2;${r};${g};${b}m`;
|
|
36
60
|
}
|
|
37
61
|
/**
|
|
38
|
-
* Converts
|
|
39
|
-
*
|
|
40
|
-
* @param hex - Hex color string.
|
|
41
|
-
* @returns ANSI escape sequence string.
|
|
62
|
+
* Converts any Color to an ANSI escape sequence.
|
|
42
63
|
*/
|
|
43
|
-
export function
|
|
64
|
+
export function resolveColorToAnsi(color) {
|
|
65
|
+
const hex = colorToHex(color);
|
|
44
66
|
const { r, g, b } = hexToRgb(hex);
|
|
45
67
|
return rgbToAnsi(r, g, b);
|
|
46
68
|
}
|
|
47
69
|
/**
|
|
48
|
-
* Converts a
|
|
49
|
-
|
|
70
|
+
* Converts a list of modifiers to an ANSI escape sequence.
|
|
71
|
+
*/
|
|
72
|
+
export function resolveModifiersToAnsi(modifiers) {
|
|
73
|
+
if (!modifiers || modifiers.length === 0)
|
|
74
|
+
return "";
|
|
75
|
+
return modifiers
|
|
76
|
+
.map((m) => {
|
|
77
|
+
const code = MODIFIER_CODES[m];
|
|
78
|
+
return code ? `${ESC}[${code}m` : "";
|
|
79
|
+
})
|
|
80
|
+
.join("");
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Helper to convert number to 2-digit hex.
|
|
50
84
|
*/
|
|
51
85
|
function toHex(c) {
|
|
52
86
|
const hex = Math.max(0, Math.min(255, Math.round(c))).toString(16);
|
|
53
87
|
return hex.length === 1 ? "0" + hex : hex;
|
|
54
88
|
}
|
|
55
89
|
/**
|
|
56
|
-
* Interpolates between two hex colors
|
|
57
|
-
* Used for gradients.
|
|
58
|
-
*
|
|
59
|
-
* @param color1 - Start color (hex).
|
|
60
|
-
* @param color2 - End color (hex).
|
|
61
|
-
* @param factor - Interpolation factor (0.0 to 1.0).
|
|
62
|
-
* @returns Interpolated Hex color.
|
|
90
|
+
* Interpolates between two hex colors.
|
|
63
91
|
*/
|
|
64
|
-
export function
|
|
92
|
+
export function interpolateHex(color1, color2, factor) {
|
|
65
93
|
const f = Math.max(0, Math.min(1, factor));
|
|
66
94
|
const c1 = hexToRgb(color1);
|
|
67
95
|
const c2 = hexToRgb(color2);
|
|
@@ -70,58 +98,74 @@ export function interpolateColor(color1, color2, factor) {
|
|
|
70
98
|
const b = c1.b + f * (c2.b - c1.b);
|
|
71
99
|
return `#${toHex(r)}${toHex(g)}${toHex(b)}`;
|
|
72
100
|
}
|
|
101
|
+
/**
|
|
102
|
+
* Public interpolation function that accepts any Color type.
|
|
103
|
+
*/
|
|
104
|
+
export function interpolateColor(color1, color2, factor) {
|
|
105
|
+
return interpolateHex(colorToHex(color1), colorToHex(color2), factor);
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* logic to get a specific color from a multi-stop gradient array at a specific factor (0-1).
|
|
109
|
+
*/
|
|
110
|
+
export function getGradientColor(colors, factor) {
|
|
111
|
+
if (colors.length === 0)
|
|
112
|
+
return "";
|
|
113
|
+
if (colors.length === 1)
|
|
114
|
+
return resolveColorToAnsi(colors[0]);
|
|
115
|
+
const f = Math.max(0, Math.min(1, factor));
|
|
116
|
+
// Map factor to segments between colors
|
|
117
|
+
// e.g. 3 colors: [0, 0.5, 1]. factor 0.25 is in first segment (0.5 of way through)
|
|
118
|
+
const segmentLength = 1 / (colors.length - 1);
|
|
119
|
+
const segmentIndex = Math.min(Math.floor(f / segmentLength), colors.length - 2);
|
|
120
|
+
const segmentFactor = (f - segmentIndex * segmentLength) / segmentLength;
|
|
121
|
+
const c1 = colors[segmentIndex];
|
|
122
|
+
const c2 = colors[segmentIndex + 1];
|
|
123
|
+
const hex = interpolateColor(c1, c2, segmentFactor);
|
|
124
|
+
return resolveColorToAnsi(hex);
|
|
125
|
+
}
|
|
73
126
|
// -----------------
|
|
74
|
-
// Style
|
|
127
|
+
// Style Merging
|
|
75
128
|
// -----------------
|
|
76
|
-
const STYLE_CODES = {
|
|
77
|
-
default: "0",
|
|
78
|
-
bold: "1",
|
|
79
|
-
dim: "2",
|
|
80
|
-
italic: "3",
|
|
81
|
-
underline: "4",
|
|
82
|
-
inverse: "7",
|
|
83
|
-
hidden: "8",
|
|
84
|
-
strikethrough: "9",
|
|
85
|
-
black: "30",
|
|
86
|
-
red: "31",
|
|
87
|
-
green: "32",
|
|
88
|
-
yellow: "33",
|
|
89
|
-
blue: "34",
|
|
90
|
-
magenta: "35",
|
|
91
|
-
cyan: "36",
|
|
92
|
-
white: "37",
|
|
93
|
-
gray: "90",
|
|
94
|
-
grey: "90"
|
|
95
|
-
};
|
|
96
129
|
/**
|
|
97
|
-
*
|
|
98
|
-
*
|
|
99
|
-
*
|
|
100
|
-
* @param style - The style or array of styles to resolve.
|
|
101
|
-
* @returns The resulting ANSI escape sequence string.
|
|
130
|
+
* Merges a child style into a parent style.
|
|
131
|
+
* - Modifiers are combined (union).
|
|
132
|
+
* - Child color overrides parent color.
|
|
102
133
|
*/
|
|
103
|
-
export function
|
|
104
|
-
if (
|
|
105
|
-
return
|
|
106
|
-
|
|
107
|
-
|
|
134
|
+
export function mergeStyles(parent, child) {
|
|
135
|
+
if (!parent && !child)
|
|
136
|
+
return {};
|
|
137
|
+
if (!parent)
|
|
138
|
+
return child ?? {};
|
|
139
|
+
if (!child)
|
|
140
|
+
return parent;
|
|
141
|
+
const mergedModifiers = Array.from(new Set([...(parent.modifiers ?? []), ...(child.modifiers ?? [])]));
|
|
142
|
+
return {
|
|
143
|
+
modifiers: mergedModifiers,
|
|
144
|
+
color: child.color ?? parent.color
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Resolves a style object into an ANSI string.
|
|
149
|
+
* If the color is a gradient (array), it uses the provided factor (0-1) to pick the color.
|
|
150
|
+
* Defaults to factor 0 if not provided.
|
|
151
|
+
*/
|
|
152
|
+
export function resolveStyle(style, gradientFactor = 0) {
|
|
153
|
+
if (!style)
|
|
108
154
|
return "";
|
|
155
|
+
let ansi = "";
|
|
156
|
+
if (style.modifiers) {
|
|
157
|
+
ansi += resolveModifiersToAnsi(style.modifiers);
|
|
109
158
|
}
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
159
|
+
if (style.color) {
|
|
160
|
+
if (Array.isArray(style.color)) {
|
|
161
|
+
// Gradient
|
|
162
|
+
const hex = getGradientColor(style.color, gradientFactor);
|
|
163
|
+
ansi += hex;
|
|
114
164
|
}
|
|
115
|
-
|
|
116
|
-
|
|
165
|
+
else {
|
|
166
|
+
// Solid
|
|
167
|
+
ansi += resolveColorToAnsi(style.color);
|
|
117
168
|
}
|
|
118
169
|
}
|
|
119
|
-
|
|
120
|
-
const code = STYLE_CODES[style.toLowerCase()];
|
|
121
|
-
if (code) {
|
|
122
|
-
return `${ESC}[${code}m`;
|
|
123
|
-
}
|
|
124
|
-
// Fallback: return as raw string if it looks like ANSI or just text
|
|
125
|
-
// Ideally we would validate ANSI here, but for flexibility we return it.
|
|
126
|
-
return style;
|
|
170
|
+
return ansi;
|
|
127
171
|
}
|
package/dist/core/style.test.js
CHANGED
|
@@ -1,53 +1,108 @@
|
|
|
1
1
|
import { expect, test, describe } from "bun:test";
|
|
2
|
-
import { hexToRgb, rgbToAnsi,
|
|
2
|
+
import { hexToRgb, rgbToAnsi, interpolateColor, resolveStyle, resolveColorToAnsi, resolveModifiersToAnsi, getGradientColor, mergeStyles } from "./style";
|
|
3
|
+
const ESC = "\x1b";
|
|
3
4
|
describe("Color Utilities", () => {
|
|
4
5
|
test("hexToRgb converts correctly", () => {
|
|
5
6
|
expect(hexToRgb("#FFFFFF")).toEqual({ r: 255, g: 255, b: 255 });
|
|
6
7
|
expect(hexToRgb("#000000")).toEqual({ r: 0, g: 0, b: 0 });
|
|
7
8
|
});
|
|
8
|
-
test("hexToRgb
|
|
9
|
-
expect(
|
|
10
|
-
expect(
|
|
9
|
+
test("hexToRgb handles invalid hex gracefully", () => {
|
|
10
|
+
expect(hexToRgb("invalid")).toEqual({ r: 255, g: 255, b: 255 }); // Fallback
|
|
11
|
+
expect(hexToRgb("#FFF")).toEqual({ r: 255, g: 255, b: 255 }); // Fallback
|
|
11
12
|
});
|
|
12
13
|
test("rgbToAnsi converts correctly", () => {
|
|
13
|
-
expect(rgbToAnsi(255, 255, 255)).toBe(
|
|
14
|
-
expect(rgbToAnsi(0, 0, 0)).toBe(
|
|
14
|
+
expect(rgbToAnsi(255, 255, 255)).toBe(`${ESC}[38;2;255;255;255m`);
|
|
15
|
+
expect(rgbToAnsi(0, 0, 0)).toBe(`${ESC}[38;2;0;0;0m`);
|
|
15
16
|
});
|
|
16
|
-
test("
|
|
17
|
-
expect(
|
|
18
|
-
expect(
|
|
19
|
-
expect(hexToAnsi("#FF0000")).toBe("\x1b[38;2;255;0;0m");
|
|
17
|
+
test("resolveColorToAnsi converts hex and standard colors", () => {
|
|
18
|
+
expect(resolveColorToAnsi("#FFFFFF")).toBe(`${ESC}[38;2;255;255;255m`);
|
|
19
|
+
expect(resolveColorToAnsi("red")).toBe(`${ESC}[38;2;239;68;68m`); // Updated to Tailwind value
|
|
20
20
|
});
|
|
21
|
-
test("interpolateColor returns hex string", () => {
|
|
21
|
+
test("interpolateColor returns hex string for hex inputs", () => {
|
|
22
22
|
const start = "#000000";
|
|
23
23
|
const end = "#ffffff";
|
|
24
24
|
expect(interpolateColor(start, end, 0.5)).toBe("#808080");
|
|
25
25
|
});
|
|
26
|
+
test("interpolateColor handles standard color names", () => {
|
|
27
|
+
expect(interpolateColor("black", "white", 0.5)).toBe("#808080");
|
|
28
|
+
});
|
|
26
29
|
test("interpolateColor clamps factors", () => {
|
|
27
30
|
expect(interpolateColor("#000000", "#ffffff", -1)).toBe("#000000");
|
|
28
31
|
expect(interpolateColor("#000000", "#ffffff", 2)).toBe("#ffffff");
|
|
29
32
|
});
|
|
30
33
|
});
|
|
31
34
|
describe("Style Resolution", () => {
|
|
32
|
-
test("
|
|
33
|
-
expect(
|
|
34
|
-
expect(
|
|
35
|
+
test("resolveModifiersToAnsi handles modifiers", () => {
|
|
36
|
+
expect(resolveModifiersToAnsi(["bold"])).toBe(`${ESC}[1m`);
|
|
37
|
+
expect(resolveModifiersToAnsi(["bold", "italic"])).toBe(`${ESC}[1m${ESC}[3m`);
|
|
38
|
+
expect(resolveModifiersToAnsi([])).toBe("");
|
|
39
|
+
});
|
|
40
|
+
test("resolveStyle handles modifiers only", () => {
|
|
41
|
+
expect(resolveStyle({ modifiers: ["bold"] })).toBe(`${ESC}[1m`);
|
|
42
|
+
});
|
|
43
|
+
test("resolveStyle handles solid color", () => {
|
|
44
|
+
const style = { color: "#FF0000" };
|
|
45
|
+
expect(resolveStyle(style)).toBe(`${ESC}[38;2;255;0;0m`);
|
|
46
|
+
});
|
|
47
|
+
test("resolveStyle handles modifiers and solid color", () => {
|
|
48
|
+
const style = { modifiers: ["bold"], color: "#FF0000" };
|
|
49
|
+
expect(resolveStyle(style)).toBe(`${ESC}[1m${ESC}[38;2;255;0;0m`);
|
|
50
|
+
});
|
|
51
|
+
test("resolveStyle handles gradient (uses factor 0 by default)", () => {
|
|
52
|
+
const colors = ["#000000", "#FFFFFF"];
|
|
53
|
+
const style = { color: colors };
|
|
54
|
+
expect(resolveStyle(style)).toBe(`${ESC}[38;2;0;0;0m`); // Factor 0 is black
|
|
35
55
|
});
|
|
36
|
-
test("resolveStyle handles
|
|
37
|
-
|
|
56
|
+
test("resolveStyle handles gradient with explicit factor", () => {
|
|
57
|
+
const colors = ["#000000", "#FFFFFF"];
|
|
58
|
+
const style = { color: colors };
|
|
59
|
+
expect(resolveStyle(style, 1)).toBe(`${ESC}[38;2;255;255;255m`); // Factor 1 is white
|
|
38
60
|
});
|
|
39
|
-
|
|
40
|
-
|
|
61
|
+
});
|
|
62
|
+
describe("Gradient Utilities", () => {
|
|
63
|
+
test("getGradientColor handles 2 colors", () => {
|
|
64
|
+
const colors = ["#000000", "#FFFFFF"];
|
|
65
|
+
expect(getGradientColor(colors, 0)).toBe(`${ESC}[38;2;0;0;0m`);
|
|
66
|
+
expect(getGradientColor(colors, 0.5)).toBe(`${ESC}[38;2;128;128;128m`);
|
|
67
|
+
expect(getGradientColor(colors, 1)).toBe(`${ESC}[38;2;255;255;255m`);
|
|
68
|
+
});
|
|
69
|
+
test("getGradientColor handles 3 colors", () => {
|
|
70
|
+
const colors = ["#000000", "#808080", "#FFFFFF"];
|
|
71
|
+
// 0 -> black
|
|
72
|
+
expect(getGradientColor(colors, 0)).toBe(`${ESC}[38;2;0;0;0m`);
|
|
73
|
+
// 0.5 -> middle color (gray)
|
|
74
|
+
expect(getGradientColor(colors, 0.5)).toBe(`${ESC}[38;2;128;128;128m`);
|
|
75
|
+
// 1 -> white
|
|
76
|
+
expect(getGradientColor(colors, 1)).toBe(`${ESC}[38;2;255;255;255m`);
|
|
77
|
+
// 0.25 -> between black and gray
|
|
78
|
+
expect(getGradientColor(colors, 0.25)).toBe(`${ESC}[38;2;64;64;64m`);
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
describe("Style Merging", () => {
|
|
82
|
+
test("mergeStyles combines modifiers", () => {
|
|
83
|
+
const p = { modifiers: ["bold"] };
|
|
84
|
+
const c = { modifiers: ["italic"] };
|
|
85
|
+
const merged = mergeStyles(p, c);
|
|
86
|
+
expect(merged.modifiers).toContain("bold");
|
|
87
|
+
expect(merged.modifiers).toContain("italic");
|
|
41
88
|
});
|
|
42
|
-
test("
|
|
43
|
-
|
|
89
|
+
test("mergeStyles overrides color (Child wins)", () => {
|
|
90
|
+
const p = { color: "red" };
|
|
91
|
+
const c = { color: "blue" };
|
|
92
|
+
const merged = mergeStyles(p, c);
|
|
93
|
+
expect(merged.color).toBe("blue");
|
|
44
94
|
});
|
|
45
|
-
test("
|
|
46
|
-
|
|
95
|
+
test("mergeStyles inherits parent color if child has none", () => {
|
|
96
|
+
const p = { color: "red" };
|
|
97
|
+
const c = { modifiers: ["bold"] };
|
|
98
|
+
const merged = mergeStyles(p, c);
|
|
99
|
+
expect(merged.color).toBe("red");
|
|
100
|
+
expect(merged.modifiers).toContain("bold");
|
|
47
101
|
});
|
|
48
|
-
test("
|
|
49
|
-
const
|
|
50
|
-
expect(
|
|
51
|
-
expect(
|
|
102
|
+
test("mergeStyles handles null/undefined inputs", () => {
|
|
103
|
+
const p = { color: "red" };
|
|
104
|
+
expect(mergeStyles(p, undefined)).toEqual(p);
|
|
105
|
+
expect(mergeStyles(undefined, p)).toEqual(p);
|
|
106
|
+
expect(mergeStyles(undefined, undefined)).toEqual({});
|
|
52
107
|
});
|
|
53
108
|
});
|
package/dist/core/types.d.ts
CHANGED
|
@@ -11,32 +11,62 @@ export type StyleModifier = "bold" | "dim" | "italic" | "underline" | "default"
|
|
|
11
11
|
*/
|
|
12
12
|
export type HexColor = `#${string}`;
|
|
13
13
|
/**
|
|
14
|
-
* A
|
|
15
|
-
* It can also be a raw ANSI string (though discouraged) for backward compatibility or special cases.
|
|
14
|
+
* A color can be a standard color name or a hex color string.
|
|
16
15
|
*/
|
|
17
|
-
export type
|
|
16
|
+
export type Color = StandardColor | HexColor;
|
|
17
|
+
/**
|
|
18
|
+
* Represents the style configuration for a text segment, line, or block.
|
|
19
|
+
*/
|
|
20
|
+
export interface PrintStyle {
|
|
21
|
+
/**
|
|
22
|
+
* The color to apply.
|
|
23
|
+
* - A single color (string) applies a solid color.
|
|
24
|
+
* - An array of colors applies a gradient.
|
|
25
|
+
*/
|
|
26
|
+
color?: Color | Color[];
|
|
27
|
+
/**
|
|
28
|
+
* A list of style modifiers (e.g., bold, italic) to apply.
|
|
29
|
+
*/
|
|
30
|
+
modifiers?: StyleModifier[];
|
|
31
|
+
}
|
|
18
32
|
/**
|
|
19
33
|
* Represents a segment of text with applied styles.
|
|
20
34
|
*/
|
|
21
|
-
export interface
|
|
35
|
+
export interface PrintSegment {
|
|
22
36
|
/** The text content of the segment. */
|
|
23
37
|
text: string;
|
|
24
|
-
/**
|
|
25
|
-
style?:
|
|
38
|
+
/** The style specific to this segment. Merges with parent styles. */
|
|
39
|
+
style?: PrintStyle;
|
|
26
40
|
}
|
|
27
41
|
/**
|
|
28
42
|
* Represents a line of text composed of multiple styled segments.
|
|
29
43
|
*/
|
|
30
|
-
export interface
|
|
44
|
+
export interface PrintLine {
|
|
31
45
|
/** Array of segments that make up the line. */
|
|
32
|
-
segments:
|
|
46
|
+
segments: PrintSegment[];
|
|
47
|
+
/** The style specific to this line. Merges with parent block style and applies to all children. */
|
|
48
|
+
style?: PrintStyle;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Represents a block of lines managed by the printer.
|
|
52
|
+
*/
|
|
53
|
+
export interface PrintBlock {
|
|
54
|
+
/** Array of lines that make up the block. */
|
|
55
|
+
lines: PrintLine[];
|
|
56
|
+
/** The style specific to this block. Applies to all children. */
|
|
57
|
+
style?: PrintStyle;
|
|
33
58
|
}
|
|
34
59
|
/**
|
|
35
60
|
* Configuration for the Printer engine.
|
|
36
61
|
*/
|
|
37
62
|
export interface PrinterOptions {
|
|
38
|
-
/**
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
63
|
+
/**
|
|
64
|
+
* If true, the printer will overwrite previous output on subsequent print calls.
|
|
65
|
+
* Useful for live-updating displays (spinners, progress bars).
|
|
66
|
+
*/
|
|
67
|
+
live?: boolean;
|
|
68
|
+
/**
|
|
69
|
+
* Initial data to load into the printer.
|
|
70
|
+
*/
|
|
71
|
+
data?: PrintBlock;
|
|
42
72
|
}
|
package/dist/core/utils.d.ts
CHANGED
|
@@ -1,25 +1,25 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { PrintLine, PrintStyle } from "./types";
|
|
2
2
|
/**
|
|
3
|
-
* Gets the plain text length of a
|
|
3
|
+
* Gets the plain text length of a PrintLine (ignoring ANSI codes).
|
|
4
4
|
*
|
|
5
|
-
* @param line - The
|
|
5
|
+
* @param line - The PrintLine to measure.
|
|
6
6
|
* @returns The length of the text content.
|
|
7
7
|
*/
|
|
8
|
-
export declare function getLineLength(line:
|
|
8
|
+
export declare function getLineLength(line: PrintLine): number;
|
|
9
9
|
/**
|
|
10
|
-
* Computes the maximum width among an array of
|
|
10
|
+
* Computes the maximum width among an array of PrintLines.
|
|
11
11
|
* Useful for aligning columns.
|
|
12
12
|
*
|
|
13
|
-
* @param lines - Array of
|
|
13
|
+
* @param lines - Array of PrintLines.
|
|
14
14
|
* @returns The maximum line length found.
|
|
15
15
|
*/
|
|
16
|
-
export declare function computeMaxWidth(lines:
|
|
16
|
+
export declare function computeMaxWidth(lines: PrintLine[]): number;
|
|
17
17
|
/**
|
|
18
|
-
* Pads a
|
|
18
|
+
* Pads a PrintLine to a target width by adding an empty segment at the end.
|
|
19
19
|
*
|
|
20
20
|
* @param line - The line to pad.
|
|
21
21
|
* @param targetWidth - The desired minimum width.
|
|
22
22
|
* @param padStyle - The style to apply to the padding spaces.
|
|
23
|
-
* @returns A new
|
|
23
|
+
* @returns A new PrintLine with padding added if necessary.
|
|
24
24
|
*/
|
|
25
|
-
export declare function padLine(line:
|
|
25
|
+
export declare function padLine(line: PrintLine, targetWidth: number, padStyle?: PrintStyle): PrintLine;
|
package/dist/core/utils.js
CHANGED
|
@@ -1,32 +1,29 @@
|
|
|
1
|
-
// -----------------
|
|
2
|
-
// Line Manipulation Helpers
|
|
3
|
-
// -----------------
|
|
4
1
|
/**
|
|
5
|
-
* Gets the plain text length of a
|
|
2
|
+
* Gets the plain text length of a PrintLine (ignoring ANSI codes).
|
|
6
3
|
*
|
|
7
|
-
* @param line - The
|
|
4
|
+
* @param line - The PrintLine to measure.
|
|
8
5
|
* @returns The length of the text content.
|
|
9
6
|
*/
|
|
10
7
|
export function getLineLength(line) {
|
|
11
8
|
return line.segments.reduce((acc, seg) => acc + seg.text.length, 0);
|
|
12
9
|
}
|
|
13
10
|
/**
|
|
14
|
-
* Computes the maximum width among an array of
|
|
11
|
+
* Computes the maximum width among an array of PrintLines.
|
|
15
12
|
* Useful for aligning columns.
|
|
16
13
|
*
|
|
17
|
-
* @param lines - Array of
|
|
14
|
+
* @param lines - Array of PrintLines.
|
|
18
15
|
* @returns The maximum line length found.
|
|
19
16
|
*/
|
|
20
17
|
export function computeMaxWidth(lines) {
|
|
21
18
|
return lines.length > 0 ? Math.max(...lines.map(getLineLength)) : 0;
|
|
22
19
|
}
|
|
23
20
|
/**
|
|
24
|
-
* Pads a
|
|
21
|
+
* Pads a PrintLine to a target width by adding an empty segment at the end.
|
|
25
22
|
*
|
|
26
23
|
* @param line - The line to pad.
|
|
27
24
|
* @param targetWidth - The desired minimum width.
|
|
28
25
|
* @param padStyle - The style to apply to the padding spaces.
|
|
29
|
-
* @returns A new
|
|
26
|
+
* @returns A new PrintLine with padding added if necessary.
|
|
30
27
|
*/
|
|
31
28
|
export function padLine(line, targetWidth, padStyle) {
|
|
32
29
|
const currentLength = getLineLength(line);
|
package/dist/core/utils.test.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { expect, test, describe } from "bun:test";
|
|
2
2
|
import { getLineLength, computeMaxWidth, padLine } from "./utils";
|
|
3
|
-
const lineA = { segments: [{ text: "Hello", style:
|
|
4
|
-
const lineB = { segments: [{ text: "World!!", style:
|
|
3
|
+
const lineA = { segments: [{ text: "Hello", style: {} }] };
|
|
4
|
+
const lineB = { segments: [{ text: "World!!", style: {} }] };
|
|
5
5
|
describe("Line Utilities", () => {
|
|
6
6
|
test("getLineLength calculates correctly", () => {
|
|
7
7
|
expect(getLineLength(lineA)).toBe(5);
|
|
@@ -12,13 +12,13 @@ describe("Line Utilities", () => {
|
|
|
12
12
|
expect(computeMaxWidth([])).toBe(0);
|
|
13
13
|
});
|
|
14
14
|
test("padLine adds padding when needed", () => {
|
|
15
|
-
const padded = padLine(lineA, 10, "red");
|
|
15
|
+
const padded = padLine(lineA, 10, { color: "red" });
|
|
16
16
|
expect(getLineLength(padded)).toBe(10);
|
|
17
|
-
expect(padded.segments[1].style).
|
|
17
|
+
expect(padded.segments[1].style).toEqual({ color: "red" });
|
|
18
18
|
expect(padded.segments[1].text).toBe(" ");
|
|
19
19
|
});
|
|
20
20
|
test("padLine does nothing if line is already wide enough", () => {
|
|
21
|
-
const ignored = padLine(lineB, 5, "red");
|
|
21
|
+
const ignored = padLine(lineB, 5, { color: "red" });
|
|
22
22
|
expect(getLineLength(ignored)).toBe(7);
|
|
23
23
|
expect(ignored.segments.length).toBe(1);
|
|
24
24
|
});
|