@heinrichb/console-toolkit 1.0.3 → 1.0.5

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 (41) hide show
  1. package/README.md +162 -2
  2. package/dist/components/progress.d.ts +75 -0
  3. package/dist/components/progress.js +43 -0
  4. package/dist/components/progress.test.d.ts +1 -0
  5. package/dist/components/progress.test.js +101 -0
  6. package/dist/components/spinner.d.ts +36 -0
  7. package/dist/components/spinner.js +33 -0
  8. package/dist/components/spinner.test.d.ts +1 -0
  9. package/dist/components/spinner.test.js +53 -0
  10. package/dist/core/layout.d.ts +25 -0
  11. package/dist/core/layout.js +57 -0
  12. package/dist/core/layout.test.d.ts +1 -0
  13. package/dist/core/layout.test.js +41 -0
  14. package/dist/core/printer.d.ts +20 -0
  15. package/dist/core/printer.js +41 -0
  16. package/dist/core/printer.test.d.ts +1 -0
  17. package/dist/core/printer.test.js +36 -0
  18. package/dist/core/style.d.ts +51 -0
  19. package/dist/core/style.js +127 -0
  20. package/dist/core/style.test.d.ts +1 -0
  21. package/dist/core/style.test.js +53 -0
  22. package/dist/core/types.d.ts +42 -0
  23. package/dist/core/types.js +4 -0
  24. package/dist/core/utils.d.ts +25 -0
  25. package/dist/core/utils.js +39 -0
  26. package/dist/core/utils.test.d.ts +1 -0
  27. package/dist/core/utils.test.js +25 -0
  28. package/dist/demo.d.ts +0 -4
  29. package/dist/demo.js +119 -15
  30. package/dist/index.d.ts +8 -99
  31. package/dist/index.js +14 -234
  32. package/dist/index.test.js +26 -6
  33. package/dist/presets/ascii.d.ts +5 -0
  34. package/dist/presets/ascii.js +33 -0
  35. package/dist/presets/ascii.test.d.ts +1 -0
  36. package/dist/presets/ascii.test.js +10 -0
  37. package/dist/spinner.d.ts +36 -0
  38. package/dist/spinner.js +33 -0
  39. package/dist/spinner.test.d.ts +1 -0
  40. package/dist/spinner.test.js +53 -0
  41. package/package.json +8 -6
@@ -0,0 +1,41 @@
1
+ import { resolveStyle, RESET } from "./style";
2
+ const ESC = "\x1b";
3
+ // -----------------
4
+ // Printer Class
5
+ // -----------------
6
+ /**
7
+ * Handles rendering StyledLines to the terminal with support for interactive overwriting.
8
+ */
9
+ export class Printer {
10
+ linesRendered = 0;
11
+ isInteractive;
12
+ constructor(options = {}) {
13
+ this.isInteractive = options.interactive ?? false;
14
+ }
15
+ /**
16
+ * Generates the clear sequence to move cursor and clear previously rendered lines.
17
+ */
18
+ getClearSequence() {
19
+ if (!this.isInteractive || this.linesRendered === 0)
20
+ return "";
21
+ return `${ESC}[1A${ESC}[2K\r`.repeat(this.linesRendered);
22
+ }
23
+ /**
24
+ * Renders an array of StyledLines to the standard output.
25
+ * If interactive mode is enabled, it clears the previously printed lines first.
26
+ *
27
+ * @param lines - The lines to print.
28
+ */
29
+ print(lines) {
30
+ let output = this.getClearSequence();
31
+ lines.forEach((line) => {
32
+ line.segments.forEach((seg) => {
33
+ const ansiStyle = resolveStyle(seg.style);
34
+ output += `${ansiStyle}${seg.text}${RESET}`;
35
+ });
36
+ output += "\n";
37
+ });
38
+ process.stdout.write(output);
39
+ this.linesRendered = lines.length;
40
+ }
41
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,36 @@
1
+ import { expect, test, describe, spyOn, afterEach } from "bun:test";
2
+ import { Printer } from "./printer";
3
+ describe("Printer", () => {
4
+ const stdoutSpy = spyOn(process.stdout, "write").mockImplementation(() => true);
5
+ afterEach(() => {
6
+ stdoutSpy.mockClear();
7
+ });
8
+ test("Printer.print outputs to console with resolved styles", () => {
9
+ const printer = new Printer();
10
+ printer.print([{ segments: [{ text: "Test", style: "red" }] }]);
11
+ expect(stdoutSpy).toHaveBeenCalled();
12
+ const output = stdoutSpy.mock.calls[0][0];
13
+ expect(output).toContain("\x1b[31mTest");
14
+ });
15
+ test("Printer handles interactive clearing", () => {
16
+ const printer = new Printer({ interactive: true });
17
+ printer.print([{ segments: [{ text: "L1", style: "" }] }]);
18
+ printer.print([{ segments: [{ text: "L2", style: "" }] }]);
19
+ expect(stdoutSpy).toHaveBeenCalledWith(expect.stringContaining("\x1b[1A\x1b[2K\r"));
20
+ });
21
+ test("Printer optimizes clearing of multiple lines", () => {
22
+ const printer = new Printer({ interactive: true });
23
+ printer.print([
24
+ { segments: [{ text: "L1", style: "" }] },
25
+ { segments: [{ text: "L2", style: "" }] },
26
+ { segments: [{ text: "L3", style: "" }] }
27
+ ]);
28
+ stdoutSpy.mockClear();
29
+ printer.print([{ segments: [{ text: "New", style: "" }] }]);
30
+ const clearSeq = "\x1b[1A\x1b[2K\r";
31
+ const expectedClear = clearSeq.repeat(3);
32
+ expect(stdoutSpy).toHaveBeenCalledTimes(1);
33
+ const callArg = stdoutSpy.mock.calls[0][0];
34
+ expect(callArg.startsWith(expectedClear)).toBe(true);
35
+ });
36
+ });
@@ -0,0 +1,51 @@
1
+ import { HexColor, Style } from "./types";
2
+ /**
3
+ * ANSI escape sequence to reset all styles.
4
+ */
5
+ export declare const RESET = "\u001B[0m";
6
+ /**
7
+ * 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
+ */
13
+ export declare function hexToRgb(hex: string): {
14
+ r: number;
15
+ g: number;
16
+ b: number;
17
+ };
18
+ /**
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
+ */
26
+ export declare function rgbToAnsi(r: number, g: number, b: number): string;
27
+ /**
28
+ * Converts a hex color string directly to an ANSI escape sequence.
29
+ *
30
+ * @param hex - Hex color string.
31
+ * @returns ANSI escape sequence string.
32
+ */
33
+ export declare function hexToAnsi(hex: string): string;
34
+ /**
35
+ * Interpolates between two hex colors based on a factor (0 to 1) and returns a Hex color string.
36
+ * Used for gradients.
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.
42
+ */
43
+ export declare function interpolateColor(color1: string, color2: string, factor: number): HexColor;
44
+ /**
45
+ * Resolves a single Style or array of Styles into an ANSI escape sequence.
46
+ * Handles hex colors, standard colors, and modifiers.
47
+ *
48
+ * @param style - The style or array of styles to resolve.
49
+ * @returns The resulting ANSI escape sequence string.
50
+ */
51
+ export declare function resolveStyle(style?: Style | Style[]): string;
@@ -0,0 +1,127 @@
1
+ // -----------------
2
+ // Color Utilities
3
+ // -----------------
4
+ const ESC = "\x1b";
5
+ /**
6
+ * ANSI escape sequence to reset all styles.
7
+ */
8
+ export const RESET = `${ESC}[0m`;
9
+ /**
10
+ * 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
+ */
16
+ export function hexToRgb(hex) {
17
+ const h = hex.replace(/^#/, "");
18
+ if (h.length !== 6)
19
+ throw new Error("Invalid hex color.");
20
+ return {
21
+ r: parseInt(h.substring(0, 2), 16),
22
+ g: parseInt(h.substring(2, 4), 16),
23
+ b: parseInt(h.substring(4, 6), 16)
24
+ };
25
+ }
26
+ /**
27
+ * 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
+ */
34
+ export function rgbToAnsi(r, g, b) {
35
+ return `${ESC}[38;2;${r};${g};${b}m`;
36
+ }
37
+ /**
38
+ * Converts a hex color string directly to an ANSI escape sequence.
39
+ *
40
+ * @param hex - Hex color string.
41
+ * @returns ANSI escape sequence string.
42
+ */
43
+ export function hexToAnsi(hex) {
44
+ const { r, g, b } = hexToRgb(hex);
45
+ return rgbToAnsi(r, g, b);
46
+ }
47
+ /**
48
+ * Converts a single RGB component to a 2-digit hex string.
49
+ * Helper for interpolation.
50
+ */
51
+ function toHex(c) {
52
+ const hex = Math.max(0, Math.min(255, Math.round(c))).toString(16);
53
+ return hex.length === 1 ? "0" + hex : hex;
54
+ }
55
+ /**
56
+ * Interpolates between two hex colors based on a factor (0 to 1) and returns a Hex color string.
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.
63
+ */
64
+ export function interpolateColor(color1, color2, factor) {
65
+ const f = Math.max(0, Math.min(1, factor));
66
+ const c1 = hexToRgb(color1);
67
+ const c2 = hexToRgb(color2);
68
+ const r = c1.r + f * (c2.r - c1.r);
69
+ const g = c1.g + f * (c2.g - c1.g);
70
+ const b = c1.b + f * (c2.b - c1.b);
71
+ return `#${toHex(r)}${toHex(g)}${toHex(b)}`;
72
+ }
73
+ // -----------------
74
+ // Style Resolution
75
+ // -----------------
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
+ /**
97
+ * Resolves a single Style or array of Styles into an ANSI escape sequence.
98
+ * Handles hex colors, standard colors, and modifiers.
99
+ *
100
+ * @param style - The style or array of styles to resolve.
101
+ * @returns The resulting ANSI escape sequence string.
102
+ */
103
+ export function resolveStyle(style) {
104
+ if (Array.isArray(style)) {
105
+ return style.map(resolveStyle).join("");
106
+ }
107
+ if (typeof style !== "string") {
108
+ return "";
109
+ }
110
+ // Check for hex color
111
+ if (style.startsWith("#")) {
112
+ try {
113
+ return hexToAnsi(style);
114
+ }
115
+ catch {
116
+ return ""; // Invalid hex, ignore
117
+ }
118
+ }
119
+ // Check for standard styles/colors
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;
127
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,53 @@
1
+ import { expect, test, describe } from "bun:test";
2
+ import { hexToRgb, rgbToAnsi, hexToAnsi, interpolateColor, resolveStyle } from "./style";
3
+ describe("Color Utilities", () => {
4
+ test("hexToRgb converts correctly", () => {
5
+ expect(hexToRgb("#FFFFFF")).toEqual({ r: 255, g: 255, b: 255 });
6
+ expect(hexToRgb("#000000")).toEqual({ r: 0, g: 0, b: 0 });
7
+ });
8
+ test("hexToRgb throws on invalid hex", () => {
9
+ expect(() => hexToRgb("invalid")).toThrow("Invalid hex color.");
10
+ expect(() => hexToRgb("#FFF")).toThrow("Invalid hex color.");
11
+ });
12
+ test("rgbToAnsi converts correctly", () => {
13
+ expect(rgbToAnsi(255, 255, 255)).toBe("\x1b[38;2;255;255;255m");
14
+ expect(rgbToAnsi(0, 0, 0)).toBe("\x1b[38;2;0;0;0m");
15
+ });
16
+ test("hexToAnsi converts hex string directly to ANSI", () => {
17
+ expect(hexToAnsi("#FFFFFF")).toBe("\x1b[38;2;255;255;255m");
18
+ expect(hexToAnsi("#000000")).toBe("\x1b[38;2;0;0;0m");
19
+ expect(hexToAnsi("#FF0000")).toBe("\x1b[38;2;255;0;0m");
20
+ });
21
+ test("interpolateColor returns hex string", () => {
22
+ const start = "#000000";
23
+ const end = "#ffffff";
24
+ expect(interpolateColor(start, end, 0.5)).toBe("#808080");
25
+ });
26
+ test("interpolateColor clamps factors", () => {
27
+ expect(interpolateColor("#000000", "#ffffff", -1)).toBe("#000000");
28
+ expect(interpolateColor("#000000", "#ffffff", 2)).toBe("#ffffff");
29
+ });
30
+ });
31
+ describe("Style Resolution", () => {
32
+ test("resolveStyle handles standard colors", () => {
33
+ expect(resolveStyle("red")).toBe("\x1b[31m");
34
+ expect(resolveStyle("blue")).toBe("\x1b[34m");
35
+ });
36
+ test("resolveStyle handles modifiers", () => {
37
+ expect(resolveStyle("bold")).toBe("\x1b[1m");
38
+ });
39
+ test("resolveStyle handles hex colors", () => {
40
+ expect(resolveStyle("#FF0000")).toBe("\x1b[38;2;255;0;0m");
41
+ });
42
+ test("resolveStyle handles arrays of styles", () => {
43
+ expect(resolveStyle(["bold", "red"])).toBe("\x1b[1m\x1b[31m");
44
+ });
45
+ test("resolveStyle handles undefined style", () => {
46
+ expect(resolveStyle(undefined)).toBe("");
47
+ });
48
+ test("resolveStyle passes through raw strings", () => {
49
+ const raw = "\x1b[31m";
50
+ expect(resolveStyle(raw)).toBe(raw);
51
+ expect(resolveStyle("unknown")).toBe("unknown");
52
+ });
53
+ });
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Standard colors supported by most terminals.
3
+ */
4
+ export type StandardColor = "black" | "red" | "green" | "yellow" | "blue" | "magenta" | "cyan" | "white" | "gray" | "grey";
5
+ /**
6
+ * Text style modifiers.
7
+ */
8
+ export type StyleModifier = "bold" | "dim" | "italic" | "underline" | "default" | "hidden" | "inverse" | "strikethrough";
9
+ /**
10
+ * A valid Hex color string (e.g., "#FF0000").
11
+ */
12
+ export type HexColor = `#${string}`;
13
+ /**
14
+ * A style can be a standard color name, a hex color string, or a style modifier.
15
+ * It can also be a raw ANSI string (though discouraged) for backward compatibility or special cases.
16
+ */
17
+ export type Style = StandardColor | StyleModifier | HexColor | string;
18
+ /**
19
+ * Represents a segment of text with applied styles.
20
+ */
21
+ export interface StyledSegment {
22
+ /** The text content of the segment. */
23
+ text: string;
24
+ /** Style or array of styles to apply to the text. */
25
+ style?: Style | Style[];
26
+ }
27
+ /**
28
+ * Represents a line of text composed of multiple styled segments.
29
+ */
30
+ export interface StyledLine {
31
+ /** Array of segments that make up the line. */
32
+ segments: StyledSegment[];
33
+ }
34
+ /**
35
+ * Configuration for the Printer engine.
36
+ */
37
+ export interface PrinterOptions {
38
+ /** If true, the printer will overwrite previous lines instead of appending new ones. */
39
+ interactive?: boolean;
40
+ /** The default style to apply to padding or separators. */
41
+ defaultStyle?: Style | Style[];
42
+ }
@@ -0,0 +1,4 @@
1
+ // -----------------
2
+ // Style Types
3
+ // -----------------
4
+ export {};
@@ -0,0 +1,25 @@
1
+ import { Style, StyledLine } from "./types";
2
+ /**
3
+ * Gets the plain text length of a StyledLine (ignoring ANSI codes).
4
+ *
5
+ * @param line - The StyledLine to measure.
6
+ * @returns The length of the text content.
7
+ */
8
+ export declare function getLineLength(line: StyledLine): number;
9
+ /**
10
+ * Computes the maximum width among an array of StyledLines.
11
+ * Useful for aligning columns.
12
+ *
13
+ * @param lines - Array of StyledLines.
14
+ * @returns The maximum line length found.
15
+ */
16
+ export declare function computeMaxWidth(lines: StyledLine[]): number;
17
+ /**
18
+ * Pads a StyledLine to a target width by adding an empty segment at the end.
19
+ *
20
+ * @param line - The line to pad.
21
+ * @param targetWidth - The desired minimum width.
22
+ * @param padStyle - The style to apply to the padding spaces.
23
+ * @returns A new StyledLine with padding added if necessary.
24
+ */
25
+ export declare function padLine(line: StyledLine, targetWidth: number, padStyle: Style | Style[]): StyledLine;
@@ -0,0 +1,39 @@
1
+ // -----------------
2
+ // Line Manipulation Helpers
3
+ // -----------------
4
+ /**
5
+ * Gets the plain text length of a StyledLine (ignoring ANSI codes).
6
+ *
7
+ * @param line - The StyledLine to measure.
8
+ * @returns The length of the text content.
9
+ */
10
+ export function getLineLength(line) {
11
+ return line.segments.reduce((acc, seg) => acc + seg.text.length, 0);
12
+ }
13
+ /**
14
+ * Computes the maximum width among an array of StyledLines.
15
+ * Useful for aligning columns.
16
+ *
17
+ * @param lines - Array of StyledLines.
18
+ * @returns The maximum line length found.
19
+ */
20
+ export function computeMaxWidth(lines) {
21
+ return lines.length > 0 ? Math.max(...lines.map(getLineLength)) : 0;
22
+ }
23
+ /**
24
+ * Pads a StyledLine to a target width by adding an empty segment at the end.
25
+ *
26
+ * @param line - The line to pad.
27
+ * @param targetWidth - The desired minimum width.
28
+ * @param padStyle - The style to apply to the padding spaces.
29
+ * @returns A new StyledLine with padding added if necessary.
30
+ */
31
+ export function padLine(line, targetWidth, padStyle) {
32
+ const currentLength = getLineLength(line);
33
+ if (currentLength < targetWidth) {
34
+ return {
35
+ segments: [...line.segments, { text: " ".repeat(targetWidth - currentLength), style: padStyle }]
36
+ };
37
+ }
38
+ return line;
39
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,25 @@
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, "red");
16
+ expect(getLineLength(padded)).toBe(10);
17
+ expect(padded.segments[1].style).toBe("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, "red");
22
+ expect(getLineLength(ignored)).toBe(7);
23
+ expect(ignored.segments.length).toBe(1);
24
+ });
25
+ });
package/dist/demo.d.ts CHANGED
@@ -1,5 +1 @@
1
- /**
2
- * Run this demo to visually verify terminal output:
3
- * bun run src/demo.ts
4
- */
5
1
  export declare function runDemo(): Promise<void>;
package/dist/demo.js CHANGED
@@ -1,11 +1,21 @@
1
- import { printDualColumn, getDragonLines, Printer, interpolateColor, createProgressBar } from "./index";
1
+ import { printColumns, getDragonLines, Printer, interpolateColor, createProgressBar, Spinner, SPINNERS } from "./index";
2
2
  import pkg from "../package.json";
3
3
  /**
4
4
  * Run this demo to visually verify terminal output:
5
5
  * bun run src/demo.ts
6
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
+ }
7
16
  export async function runDemo() {
8
17
  console.clear();
18
+ const staticPrinter = new Printer();
9
19
  // 1. Test Static Dual Column Print
10
20
  console.log("--- Static Dual Column Demo ---");
11
21
  const purple = "#A78BFA";
@@ -22,17 +32,23 @@ export async function runDemo() {
22
32
  { segments: [{ text: pkg.version, style: green }] },
23
33
  { segments: [{ text: "Testing live output...", style: yellow }] }
24
34
  ];
25
- printDualColumn(leftContent, rightContent, { separator: " => " });
35
+ printColumns([leftContent, rightContent], { separator: " => " });
36
+ console.log("\n");
37
+ // 1b. Test 3-Column Print
38
+ console.log("--- Static 3-Column Demo ---");
39
+ const col1 = [{ segments: [{ text: "Column 1", style: "red" }] }];
40
+ const col2 = [{ segments: [{ text: "Column 2", style: "green" }] }];
41
+ const col3 = [{ segments: [{ text: "Column 3", style: "blue" }] }];
42
+ printColumns([col1, col2, col3], { separator: " | " });
26
43
  console.log("\n");
27
44
  // 2. Test the Dragon Gradient Preset
28
45
  console.log("--- Dragon Gradient Preset ---");
29
46
  const dragon = getDragonLines("#EF4444", "#FDE047");
30
- const printer = new Printer();
31
- printer.print(dragon);
47
+ staticPrinter.print(dragon);
32
48
  console.log("\n");
33
49
  // 3. Test Progress Bar
34
50
  console.log("--- Interactive Progress Bar Demo ---");
35
- const interactivePrinter = new Printer({ interactive: true });
51
+ const interactiveProgressPrinter = new Printer({ interactive: true });
36
52
  const gray = "#4B5563";
37
53
  for (let i = 0; i <= 100; i += 2) {
38
54
  const progressColor = interpolateColor("#3B82F6", "#10B981", i / 100);
@@ -47,14 +63,106 @@ export async function runDemo() {
47
63
  emptyStyle: gray,
48
64
  percentageStyle: progressColor
49
65
  });
50
- interactivePrinter.print([progressLine]);
51
- await new Promise((resolve) => setTimeout(resolve, 20));
66
+ const factor = i / 100;
67
+ const gradientColor = getProgressBarColor(factor);
68
+ const gradientBar = createProgressBar({
69
+ progress: factor,
70
+ width: 40,
71
+ startChar: "▕",
72
+ endChar: "▏",
73
+ fillChar: "█",
74
+ emptyChar: "░",
75
+ startStyle: gradientColor,
76
+ endStyle: gradientColor,
77
+ fillStyle: gradientColor,
78
+ emptyStyle: "gray",
79
+ percentageStyle: ["bold", gradientColor]
80
+ });
81
+ const sharpBar = createProgressBar({
82
+ progress: factor,
83
+ width: 40,
84
+ startChar: "[",
85
+ endChar: "]",
86
+ fillChar: "#",
87
+ emptyChar: "-",
88
+ style: "white",
89
+ fillStyle: "green",
90
+ emptyStyle: "dim"
91
+ });
92
+ const noStyleBar = createProgressBar({
93
+ progress: factor,
94
+ width: 40,
95
+ startChar: "<",
96
+ endChar: ">",
97
+ fillChar: "=",
98
+ emptyChar: " "
99
+ });
100
+ interactiveProgressPrinter.print([
101
+ progressLine,
102
+ { segments: [] },
103
+ { segments: [{ text: "Gradient: ", style: "bold" }] },
104
+ gradientBar,
105
+ { segments: [] },
106
+ { segments: [{ text: "Sharp: ", style: "bold" }] },
107
+ sharpBar,
108
+ { segments: [] },
109
+ { segments: [{ text: "Minimal: ", style: "bold" }] },
110
+ noStyleBar
111
+ ]);
112
+ await new Promise((resolve) => setTimeout(resolve, 30));
113
+ }
114
+ console.log("\n");
115
+ // 4. Spinners Demo
116
+ console.log("--- Spinners Demo ---");
117
+ const spinnerDots = new Spinner({ frames: SPINNERS.dots, interval: 80 });
118
+ const spinnerLines = new Spinner({ frames: SPINNERS.lines, interval: 100 });
119
+ const spinnerArrows = new Spinner({ frames: SPINNERS.arrows, interval: 120 });
120
+ const spinnerCircle = new Spinner({ frames: SPINNERS.circle, interval: 150 });
121
+ const spinnerSquare = new Spinner({ frames: SPINNERS.square, interval: 200 });
122
+ const interactiveSpinnerPrinter = new Printer({ interactive: true });
123
+ const startTime = Date.now();
124
+ while (Date.now() - startTime < 3000) {
125
+ const lines = [
126
+ {
127
+ segments: [
128
+ { text: "Dots: ", style: "dim" },
129
+ { text: spinnerDots.getFrame(), style: "cyan" }
130
+ ]
131
+ },
132
+ {
133
+ segments: [
134
+ { text: "Lines: ", style: "dim" },
135
+ { text: spinnerLines.getFrame(), style: "yellow" }
136
+ ]
137
+ },
138
+ {
139
+ segments: [
140
+ { text: "Arrows: ", style: "dim" },
141
+ { text: spinnerArrows.getFrame(), style: "green" }
142
+ ]
143
+ },
144
+ {
145
+ segments: [
146
+ { text: "Circle: ", style: "dim" },
147
+ { text: spinnerCircle.getFrame(), style: "magenta" }
148
+ ]
149
+ },
150
+ {
151
+ segments: [
152
+ { text: "Square: ", style: "dim" },
153
+ { text: spinnerSquare.getFrame(), style: "blue" }
154
+ ]
155
+ }
156
+ ];
157
+ interactiveSpinnerPrinter.print(lines);
158
+ await new Promise((resolve) => setTimeout(resolve, 50));
52
159
  }
53
160
  console.log("\n");
54
- // 4. Style Codes Demo
161
+ // 5. Style Codes Demo
55
162
  console.log("--- Style Codes Demo ---");
56
163
  const styles = [
57
- { name: "Default", style: "default" },
164
+ { name: "Default", style: undefined },
165
+ { name: "Bold + Red", style: ["bold", "red"] },
58
166
  { name: "Bold", style: "bold" },
59
167
  { name: "Dim", style: "dim" },
60
168
  { name: "Italic", style: "italic" },
@@ -73,12 +181,8 @@ export async function runDemo() {
73
181
  { name: "Gray", style: "gray" }
74
182
  ];
75
183
  const styleLines = styles.map((s) => ({
76
- segments: [
77
- { text: s.name.padEnd(15), style: "default" },
78
- { text: "Sample Text", style: s.style }
79
- ]
184
+ segments: [{ text: s.name.padEnd(15) }, { text: "Sample Text", style: s.style }]
80
185
  }));
81
- const stylePrinter = new Printer();
82
- stylePrinter.print(styleLines);
186
+ staticPrinter.print(styleLines);
83
187
  console.log("\n✨ Demo Complete!");
84
188
  }