@heinrichb/console-toolkit 1.0.7 → 1.0.9

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.
@@ -1,108 +0,0 @@
1
- import { expect, test, describe } from "bun:test";
2
- import { hexToRgb, rgbToAnsi, interpolateColor, resolveStyle, resolveColorToAnsi, resolveModifiersToAnsi, getGradientColor, mergeStyles } from "./style";
3
- const ESC = "\x1b";
4
- describe("Color Utilities", () => {
5
- test("hexToRgb converts correctly", () => {
6
- expect(hexToRgb("#FFFFFF")).toEqual({ r: 255, g: 255, b: 255 });
7
- expect(hexToRgb("#000000")).toEqual({ r: 0, g: 0, b: 0 });
8
- });
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
12
- });
13
- test("rgbToAnsi converts correctly", () => {
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`);
16
- });
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
- });
21
- test("interpolateColor returns hex string for hex inputs", () => {
22
- const start = "#000000";
23
- const end = "#ffffff";
24
- expect(interpolateColor(start, end, 0.5)).toBe("#808080");
25
- });
26
- test("interpolateColor handles standard color names", () => {
27
- expect(interpolateColor("black", "white", 0.5)).toBe("#808080");
28
- });
29
- test("interpolateColor clamps factors", () => {
30
- expect(interpolateColor("#000000", "#ffffff", -1)).toBe("#000000");
31
- expect(interpolateColor("#000000", "#ffffff", 2)).toBe("#ffffff");
32
- });
33
- });
34
- describe("Style Resolution", () => {
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
55
- });
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
60
- });
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");
88
- });
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");
94
- });
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");
101
- });
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({});
107
- });
108
- });
@@ -1,4 +0,0 @@
1
- // -----------------
2
- // Style Types
3
- // -----------------
4
- export {};
@@ -1,36 +0,0 @@
1
- /**
2
- * Gets the plain text length of a PrintLine (ignoring ANSI codes).
3
- *
4
- * @param line - The PrintLine to measure.
5
- * @returns The length of the text content.
6
- */
7
- export function getLineLength(line) {
8
- return line.segments.reduce((acc, seg) => acc + seg.text.length, 0);
9
- }
10
- /**
11
- * Computes the maximum width among an array of PrintLines.
12
- * Useful for aligning columns.
13
- *
14
- * @param lines - Array of PrintLines.
15
- * @returns The maximum line length found.
16
- */
17
- export function computeMaxWidth(lines) {
18
- return lines.length > 0 ? Math.max(...lines.map(getLineLength)) : 0;
19
- }
20
- /**
21
- * Pads a PrintLine to a target width by adding an empty segment at the end.
22
- *
23
- * @param line - The line to pad.
24
- * @param targetWidth - The desired minimum width.
25
- * @param padStyle - The style to apply to the padding spaces.
26
- * @returns A new PrintLine with padding added if necessary.
27
- */
28
- export function padLine(line, targetWidth, padStyle) {
29
- const currentLength = getLineLength(line);
30
- if (currentLength < targetWidth) {
31
- return {
32
- segments: [...line.segments, { text: " ".repeat(targetWidth - currentLength), style: padStyle }]
33
- };
34
- }
35
- return line;
36
- }
@@ -1 +0,0 @@
1
- export {};
@@ -1,25 +0,0 @@
1
- import { expect, test, describe } from "bun:test";
2
- import { getLineLength, computeMaxWidth, padLine } from "./utils";
3
- const lineA = { segments: [{ text: "Hello", style: {} }] };
4
- const lineB = { segments: [{ text: "World!!", style: {} }] };
5
- describe("Line Utilities", () => {
6
- test("getLineLength calculates correctly", () => {
7
- expect(getLineLength(lineA)).toBe(5);
8
- expect(getLineLength(lineB)).toBe(7);
9
- });
10
- test("computeMaxWidth finds the longest line", () => {
11
- expect(computeMaxWidth([lineA, lineB])).toBe(7);
12
- expect(computeMaxWidth([])).toBe(0);
13
- });
14
- test("padLine adds padding when needed", () => {
15
- const padded = padLine(lineA, 10, { color: "red" });
16
- expect(getLineLength(padded)).toBe(10);
17
- expect(padded.segments[1].style).toEqual({ color: "red" });
18
- expect(padded.segments[1].text).toBe(" ");
19
- });
20
- test("padLine does nothing if line is already wide enough", () => {
21
- const ignored = padLine(lineB, 5, { color: "red" });
22
- expect(getLineLength(ignored)).toBe(7);
23
- expect(ignored.segments.length).toBe(1);
24
- });
25
- });
package/dist/demo.d.ts DELETED
@@ -1 +0,0 @@
1
- export declare function runDemo(): Promise<void>;
package/dist/demo.js DELETED
@@ -1,145 +0,0 @@
1
- import { printColumns, getDragon, Printer, interpolateColor, createProgressBar, Spinner, SPINNERS } from "./index";
2
- import pkg from "../package.json";
3
- /**
4
- * Run this demo to visually verify terminal output:
5
- * bun run src/demo.ts
6
- */
7
- function getProgressBarColor(factor) {
8
- // Multi-stop gradient: Blue -> Cyan -> Green
9
- if (factor < 0.5) {
10
- return interpolateColor("#3B82F6", "#06B6D4", factor * 2);
11
- }
12
- else {
13
- return interpolateColor("#06B6D4", "#10B981", (factor - 0.5) * 2);
14
- }
15
- }
16
- export async function runDemo() {
17
- console.clear();
18
- const staticPrinter = new Printer();
19
- // 1. Static Dual Column Print
20
- console.log("--- Static Dual Column Demo ---");
21
- const purple = { color: "#A78BFA" };
22
- const blue = { color: "#60A5FA" };
23
- const green = { color: "#34D399" };
24
- const yellow = { color: "#FBBF24" };
25
- const leftContent = [
26
- { segments: [{ text: "Package:", style: purple }] },
27
- { segments: [{ text: "Version:", style: purple }] },
28
- { segments: [{ text: "Status:", style: purple }] }
29
- ];
30
- const rightContent = [
31
- { segments: [{ text: pkg.name, style: blue }] },
32
- { segments: [{ text: pkg.version, style: green }] },
33
- { segments: [{ text: "Testing live output...", style: yellow }] }
34
- ];
35
- printColumns([leftContent, rightContent], { separator: " => " });
36
- console.log("\n");
37
- // 2. Block Vertical Gradient Demo
38
- console.log("--- Block Vertical Gradient Demo ---");
39
- const gradientBlockLines = Array.from({ length: 10 }, (_, i) => ({
40
- segments: [{ text: `Line ${i + 1} - Inherits Gradient` }]
41
- }));
42
- staticPrinter.print({
43
- style: { color: ["#EF4444", "#3B82F6"] },
44
- lines: gradientBlockLines
45
- });
46
- console.log("\n");
47
- // 3. Dragon Gradient Preset
48
- console.log("--- Dragon Gradient Preset ---");
49
- const dragon = getDragon("#EF4444", "#FDE047");
50
- staticPrinter.print({ lines: dragon });
51
- console.log("\n");
52
- // 4. Interactive Progress Bar Demo
53
- console.log("--- Live Progress Bar Demo ---");
54
- const livePrinter = new Printer({ live: true });
55
- const gray = { color: "#4B5563" };
56
- for (let i = 0; i <= 100; i += 2) {
57
- const factor = i / 100;
58
- const progressColorHex = interpolateColor("#3B82F6", "#10B981", factor);
59
- const progressColor = { color: progressColorHex };
60
- // Standard Bar
61
- const progressLine = createProgressBar({
62
- progress: factor,
63
- width: 30,
64
- startChar: "[",
65
- endChar: "]",
66
- startStyle: progressColor,
67
- endStyle: progressColor,
68
- fillStyle: progressColor,
69
- emptyStyle: gray,
70
- percentageStyle: progressColor
71
- });
72
- // Gradient Bar
73
- const gradientHex = getProgressBarColor(factor);
74
- const gradientStyle = { color: gradientHex };
75
- const complexGradientBar = createProgressBar({
76
- progress: factor,
77
- width: 40,
78
- startChar: "▕",
79
- endChar: "▏",
80
- fillChar: "█",
81
- emptyChar: "░",
82
- startStyle: gradientStyle,
83
- endStyle: gradientStyle,
84
- fillStyle: { color: ["#3B82F6", "#EC4899"] },
85
- emptyStyle: { color: "gray" },
86
- percentageStyle: { modifiers: ["bold"], color: gradientHex }
87
- });
88
- livePrinter.print({
89
- lines: [
90
- progressLine,
91
- { segments: [] },
92
- { segments: [{ text: "Horizontal Gradient on Bar Segment:", style: { modifiers: ["bold"] } }] },
93
- complexGradientBar
94
- ]
95
- });
96
- await new Promise((resolve) => setTimeout(resolve, 30));
97
- }
98
- console.log("\n");
99
- // 5. Spinners Demo
100
- console.log("--- Spinners Demo ---");
101
- const spinnerTypes = Object.keys(SPINNERS);
102
- const spinners = spinnerTypes.map((type) => ({
103
- type,
104
- instance: new Spinner({ frames: SPINNERS[type], interval: 80 })
105
- }));
106
- const spinnerPrinter = new Printer({ live: true });
107
- const spinnerStart = Date.now();
108
- while (Date.now() - spinnerStart < 3000) {
109
- const lines = spinners.map((s) => ({
110
- segments: [
111
- { text: `${s.type.charAt(0).toUpperCase() + s.type.slice(1)}:`.padEnd(10), style: { modifiers: ["dim"] } },
112
- { text: s.instance.getFrame(), style: { color: "cyan" } }
113
- ]
114
- }));
115
- spinnerPrinter.print({ lines });
116
- await new Promise((resolve) => setTimeout(resolve, 50));
117
- }
118
- console.log("\n");
119
- // 6. Style Codes Demo
120
- console.log("--- Style Codes Demo ---");
121
- const styles = [
122
- { name: "Default" },
123
- { name: "Bold + Red", style: { modifiers: ["bold"], color: "red" } },
124
- { name: "Bold", style: { modifiers: ["bold"] } },
125
- { name: "Dim", style: { modifiers: ["dim"] } },
126
- { name: "Italic", style: { modifiers: ["italic"] } },
127
- { name: "Underline", style: { modifiers: ["underline"] } },
128
- { name: "Strikethrough", style: { modifiers: ["strikethrough"] } },
129
- { name: "Inverse", style: { modifiers: ["inverse"] } },
130
- { name: "Hidden", style: { modifiers: ["hidden"] } },
131
- { name: "Red", style: { color: "red" } },
132
- { name: "Green", style: { color: "green" } },
133
- { name: "Yellow", style: { color: "yellow" } },
134
- { name: "Blue", style: { color: "blue" } },
135
- { name: "Magenta", style: { color: "magenta" } },
136
- { name: "Cyan", style: { color: "cyan" } },
137
- { name: "White", style: { color: "white" } },
138
- { name: "Gray", style: { color: "gray" } }
139
- ];
140
- const styleLines = styles.map((s) => ({
141
- segments: [{ text: s.name.padEnd(20) }, { text: "Sample Text", style: s.style }]
142
- }));
143
- staticPrinter.print({ lines: styleLines });
144
- console.log("\n✨ Demo Complete!");
145
- }
@@ -1 +0,0 @@
1
- export {};
package/dist/demo.test.js DELETED
@@ -1,28 +0,0 @@
1
- import { expect, test, describe, spyOn, afterEach, beforeEach } from "bun:test";
2
- import { runDemo } from "./demo";
3
- describe("Demo Script", () => {
4
- const logSpy = spyOn(console, "log").mockImplementation(() => undefined);
5
- const clearSpy = spyOn(console, "clear").mockImplementation(() => undefined);
6
- const stdoutSpy = spyOn(process.stdout, "write").mockImplementation(() => true);
7
- const originalTimeout = global.setTimeout;
8
- beforeEach(() => {
9
- // Mocking setTimeout requires unknown casting due to return type mismatch in Bun (void vs Timer)
10
- global.setTimeout = ((fn) => {
11
- fn();
12
- });
13
- });
14
- afterEach(() => {
15
- logSpy.mockClear();
16
- clearSpy.mockClear();
17
- stdoutSpy.mockClear();
18
- global.setTimeout = originalTimeout;
19
- });
20
- test("runDemo executes correctly", async () => {
21
- // Just await the function call; we expect it not to throw.
22
- await runDemo();
23
- expect(clearSpy).toHaveBeenCalled();
24
- expect(logSpy).toHaveBeenCalledWith(expect.stringContaining("Static Dual Column Demo"));
25
- expect(logSpy).toHaveBeenCalledWith(expect.stringContaining("Demo Complete!"));
26
- expect(stdoutSpy).toHaveBeenCalled();
27
- });
28
- });
@@ -1,35 +0,0 @@
1
- import { interpolateColor } from "../core/style";
2
- // -----------------
3
- // Presets
4
- // -----------------
5
- /**
6
- * Returns the classic Dragon ASCII art as PrintLines with a vertical color gradient.
7
- */
8
- export function getDragon(startColor = "#EF4444", endColor = "#F59E0B") {
9
- const rawDragon = [
10
- " ^ ^",
11
- " / \\ //\\",
12
- " |\\___/| / \\// .\\",
13
- " /O O \\__ / // | \\ \\",
14
- "/ / \\_/_/ // | \\ \\",
15
- "@___@' \\_// // | \\ \\ ",
16
- " | \\_// // | \\ \\ ",
17
- " | \\/// | \\ \\ ",
18
- " _|_ / ) // | \\ _\\",
19
- " '/,_ _ _/ ( ; -. | _ _\\.-~ .-~~~^-.",
20
- " ,-{ _ `-.|.-~-. .~ `.",
21
- " '/\\ / ~-. _ .-~ .-~^-. \\",
22
- " `. { } / \\ \\",
23
- " .----~-\\. \\-' .~ \\ `. \\^-.",
24
- " ///.----..> c \\ _ -~ `. ^-` ^-_",
25
- " ///-._ _ _ _ _ _ _}^ - - - - ~ ~--, .-~",
26
- " /.-'"
27
- ];
28
- // Note: We could use PrintBlock's vertical gradient feature instead of manual interpolation here.
29
- // But to keep logic similar and return lines directly, we manual interp.
30
- return rawDragon.map((text, i) => {
31
- const factor = rawDragon.length <= 1 ? 0 : i / (rawDragon.length - 1);
32
- const colorStyle = interpolateColor(startColor, endColor, factor);
33
- return { segments: [{ text, style: { color: colorStyle } }] };
34
- });
35
- }
@@ -1 +0,0 @@
1
- export {};
@@ -1,21 +0,0 @@
1
- import { expect, test, describe } from "bun:test";
2
- import { getDragon } from "./ascii";
3
- describe("Presets", () => {
4
- test("getDragon returns valid array", () => {
5
- const lines = getDragon();
6
- expect(lines.length).toBeGreaterThan(0);
7
- const firstSegmentStyle = lines[0].segments[0].style;
8
- expect(firstSegmentStyle).toBeDefined();
9
- if (firstSegmentStyle) {
10
- // Check that color is defined and is hex
11
- expect(firstSegmentStyle.color).toBeDefined();
12
- if (typeof firstSegmentStyle.color === "string") {
13
- expect(firstSegmentStyle.color.startsWith("#")).toBe(true);
14
- }
15
- else {
16
- // Fail if it's not a string (e.g. array)
17
- expect(typeof firstSegmentStyle.color).toBe("string");
18
- }
19
- }
20
- });
21
- });