@heinrichb/console-toolkit 1.0.5 → 1.0.6

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/progress.js DELETED
@@ -1,43 +0,0 @@
1
- /**
2
- * Creates a StyledLine representing a progress bar.
3
- */
4
- export function createProgressBar(options) {
5
- const { progress, width = 20, style, bracketStyle, startStyle, endStyle, barStyle, fillStyle, emptyStyle, percentageStyle, startChar = "[", endChar = "]", fillChar = "█", emptyChar = "░", showPercentage = true, formatPercentage } = options;
6
- // Clamp progress
7
- const p = Math.max(0, Math.min(1, progress));
8
- // Calculate filled width
9
- const filledWidth = Math.round(p * width);
10
- const emptyWidth = width - filledWidth;
11
- // Resolve styles
12
- const baseStyle = style ?? [];
13
- const resolvedBracketStyle = bracketStyle ?? baseStyle;
14
- const resolvedStartStyle = startStyle ?? resolvedBracketStyle;
15
- const resolvedEndStyle = endStyle ?? resolvedBracketStyle;
16
- const resolvedBarStyle = barStyle ?? baseStyle;
17
- const resolvedFillStyle = fillStyle ?? resolvedBarStyle;
18
- const resolvedEmptyStyle = emptyStyle ?? resolvedBarStyle;
19
- const resolvedPercentageStyle = percentageStyle ?? baseStyle;
20
- const segments = [];
21
- // Start Bracket
22
- if (startChar) {
23
- segments.push({ text: startChar, style: resolvedStartStyle });
24
- }
25
- // Filled Part
26
- if (filledWidth > 0) {
27
- segments.push({ text: fillChar.repeat(filledWidth), style: resolvedFillStyle });
28
- }
29
- // Empty Part
30
- if (emptyWidth > 0) {
31
- segments.push({ text: emptyChar.repeat(emptyWidth), style: resolvedEmptyStyle });
32
- }
33
- // End Bracket
34
- if (endChar) {
35
- segments.push({ text: endChar, style: resolvedEndStyle });
36
- }
37
- // Percentage
38
- if (showPercentage) {
39
- const percentageText = formatPercentage ? formatPercentage(p) : ` ${Math.round(p * 100)}%`;
40
- segments.push({ text: percentageText, style: resolvedPercentageStyle });
41
- }
42
- return { segments };
43
- }
@@ -1 +0,0 @@
1
- export {};
@@ -1,101 +0,0 @@
1
- import { expect, test, describe } from "bun:test";
2
- import { createProgressBar } from "./progress";
3
- function getText(line) {
4
- return line.segments.map((s) => s.text).join("");
5
- }
6
- describe("createProgressBar", () => {
7
- test("generates a default progress bar at 0%", () => {
8
- const line = createProgressBar({ progress: 0 });
9
- const text = getText(line);
10
- expect(text).toContain("[");
11
- expect(text).toContain("]");
12
- expect(text).toContain("0%");
13
- expect(text).toContain("░");
14
- });
15
- test("generates a default progress bar at 50%", () => {
16
- const line = createProgressBar({ progress: 0.5 });
17
- const text = getText(line);
18
- expect(text).toContain("50%");
19
- expect(text).toContain("█");
20
- expect(text).toContain("░");
21
- });
22
- test("generates a default progress bar at 100%", () => {
23
- const line = createProgressBar({ progress: 1.0 });
24
- const text = getText(line);
25
- expect(text).toContain("100%");
26
- expect(text).not.toContain("░");
27
- });
28
- test("allows custom width", () => {
29
- const width = 10;
30
- const line = createProgressBar({ progress: 0.5, width });
31
- const segments = line.segments;
32
- const filled = segments.find((s) => s.text.includes("█"));
33
- const empty = segments.find((s) => s.text.includes("░"));
34
- expect(filled?.text.length).toBe(5);
35
- expect(empty?.text.length).toBe(5);
36
- });
37
- test("applies styles correctly", () => {
38
- const line = createProgressBar({
39
- progress: 0.5,
40
- style: "blue",
41
- bracketStyle: "red",
42
- barStyle: "green",
43
- percentageStyle: "yellow"
44
- });
45
- const start = line.segments.find((s) => s.text === "[");
46
- const filled = line.segments.find((s) => s.text.includes("█"));
47
- const end = line.segments.find((s) => s.text === "]");
48
- const percentage = line.segments.find((s) => s.text.includes("%"));
49
- expect(start?.style).toBe("red");
50
- expect(filled?.style).toBe("green");
51
- expect(end?.style).toBe("red");
52
- expect(percentage?.style).toBe("yellow");
53
- });
54
- test("cascades styles (general style -> specific)", () => {
55
- const line = createProgressBar({
56
- progress: 0.5,
57
- style: "blue"
58
- });
59
- line.segments.forEach((s) => {
60
- if (s.text.trim().length > 0) {
61
- expect(s.style).toBe("blue");
62
- }
63
- });
64
- });
65
- test("allows custom characters", () => {
66
- const line = createProgressBar({
67
- progress: 0.5,
68
- startChar: "<",
69
- endChar: ">",
70
- fillChar: "=",
71
- emptyChar: "-"
72
- });
73
- const text = getText(line);
74
- expect(text).toContain("<");
75
- expect(text).toContain(">");
76
- expect(text).toContain("=");
77
- expect(text).toContain("-");
78
- });
79
- test("hides percentage", () => {
80
- const line = createProgressBar({
81
- progress: 0.5,
82
- showPercentage: false
83
- });
84
- const text = getText(line);
85
- expect(text).not.toContain("%");
86
- });
87
- test("formats percentage custom", () => {
88
- const line = createProgressBar({
89
- progress: 0.5,
90
- formatPercentage: (p) => `${p * 10}/10`
91
- });
92
- const text = getText(line);
93
- expect(text).toContain("5/10");
94
- });
95
- test("clamping progress", () => {
96
- const lineLow = createProgressBar({ progress: -0.1 });
97
- expect(getText(lineLow)).toContain("0%");
98
- const lineHigh = createProgressBar({ progress: 1.1 });
99
- expect(getText(lineHigh)).toContain("100%");
100
- });
101
- });
package/dist/spinner.d.ts DELETED
@@ -1,36 +0,0 @@
1
- export type SpinnerFrames = string[];
2
- export interface SpinnerOptions {
3
- /**
4
- * The array of frames to cycle through.
5
- */
6
- frames: SpinnerFrames;
7
- /**
8
- * The interval in milliseconds between frames.
9
- * Defaults to 80ms.
10
- */
11
- interval?: number;
12
- }
13
- /**
14
- * Common spinner frame presets.
15
- */
16
- export declare const SPINNERS: {
17
- dots: string[];
18
- lines: string[];
19
- arrows: string[];
20
- circle: string[];
21
- square: string[];
22
- };
23
- /**
24
- * A stateful spinner that calculates the current frame based on elapsed time.
25
- * Designed to be used within a render loop (e.g. Printer.print loop).
26
- */
27
- export declare class Spinner {
28
- private frames;
29
- private interval;
30
- private startTime;
31
- constructor(options: SpinnerOptions);
32
- /**
33
- * Returns the current frame based on the elapsed time.
34
- */
35
- getFrame(): string;
36
- }
package/dist/spinner.js DELETED
@@ -1,33 +0,0 @@
1
- /**
2
- * Common spinner frame presets.
3
- */
4
- export const SPINNERS = {
5
- dots: ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"],
6
- lines: ["-", "\\", "|", "/"],
7
- arrows: ["←", "↖", "↑", "↗", "→", "↘", "↓", "↙"],
8
- circle: ["◐", "◓", "◑", "◒"],
9
- square: ["▖", "▘", "▝", "▗"]
10
- };
11
- /**
12
- * A stateful spinner that calculates the current frame based on elapsed time.
13
- * Designed to be used within a render loop (e.g. Printer.print loop).
14
- */
15
- export class Spinner {
16
- frames;
17
- interval;
18
- startTime;
19
- constructor(options) {
20
- this.frames = options.frames;
21
- this.interval = options.interval ?? 80;
22
- this.startTime = Date.now();
23
- }
24
- /**
25
- * Returns the current frame based on the elapsed time.
26
- */
27
- getFrame() {
28
- const now = Date.now();
29
- const elapsed = now - this.startTime;
30
- const index = Math.floor(elapsed / this.interval) % this.frames.length;
31
- return this.frames[index];
32
- }
33
- }
@@ -1 +0,0 @@
1
- export {};
@@ -1,53 +0,0 @@
1
- import { expect, test, describe, spyOn, afterEach, beforeEach } from "bun:test";
2
- import { Spinner, SPINNERS } from "./spinner";
3
- describe("Spinner Class", () => {
4
- let now = 1000;
5
- // Mock Date.now() to control time
6
- const dateSpy = spyOn(Date, "now").mockImplementation(() => now);
7
- beforeEach(() => {
8
- now = 1000;
9
- dateSpy.mockClear();
10
- dateSpy.mockImplementation(() => now);
11
- });
12
- afterEach(() => {
13
- dateSpy.mockClear();
14
- });
15
- test("initializes with correct defaults", () => {
16
- const spinner = new Spinner({ frames: ["a", "b"] });
17
- expect(spinner.getFrame()).toBe("a");
18
- });
19
- test("advances frames over time", () => {
20
- const spinner = new Spinner({ frames: ["a", "b", "c"], interval: 100 });
21
- // t=0 (1000)
22
- expect(spinner.getFrame()).toBe("a");
23
- // t=50 (1050) -> still frame 0
24
- now = 1050;
25
- expect(spinner.getFrame()).toBe("a");
26
- // t=100 (1100) -> frame 1
27
- now = 1100;
28
- expect(spinner.getFrame()).toBe("b");
29
- // t=200 (1200) -> frame 2
30
- now = 1200;
31
- expect(spinner.getFrame()).toBe("c");
32
- // t=300 (1300) -> frame 0 (loop)
33
- now = 1300;
34
- expect(spinner.getFrame()).toBe("a");
35
- });
36
- test("uses custom interval", () => {
37
- const spinner = new Spinner({ frames: ["a", "b"], interval: 50 });
38
- // t=0
39
- expect(spinner.getFrame()).toBe("a");
40
- // t=50 -> frame 1
41
- now = 1050;
42
- expect(spinner.getFrame()).toBe("b");
43
- });
44
- });
45
- describe("Spinner Presets", () => {
46
- test("dots preset exists and has frames", () => {
47
- expect(SPINNERS.dots).toBeDefined();
48
- expect(SPINNERS.dots.length).toBeGreaterThan(0);
49
- });
50
- test("lines preset exists", () => {
51
- expect(SPINNERS.lines).toBeDefined();
52
- });
53
- });