@promptctl/rich-js 0.17.0 → 0.18.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.
- package/README.md +13 -26
- package/package.json +7 -3
- package/dist/core/box.d.ts +0 -144
- package/dist/core/box.js +0 -391
- package/dist/core/cells.d.ts +0 -148
- package/dist/core/cells.js +0 -266
- package/dist/core/color.d.ts +0 -250
- package/dist/core/color.js +0 -1029
- package/dist/core/console.d.ts +0 -156
- package/dist/core/console.js +0 -472
- package/dist/core/emoji.d.ts +0 -24
- package/dist/core/emoji.js +0 -391
- package/dist/core/export-html.d.ts +0 -24
- package/dist/core/export-html.js +0 -106
- package/dist/core/export-lines.d.ts +0 -121
- package/dist/core/export-lines.js +0 -141
- package/dist/core/highlighter.d.ts +0 -33
- package/dist/core/highlighter.js +0 -124
- package/dist/core/json.d.ts +0 -27
- package/dist/core/json.js +0 -53
- package/dist/core/markup.d.ts +0 -97
- package/dist/core/markup.js +0 -619
- package/dist/core/measure.d.ts +0 -27
- package/dist/core/measure.js +0 -61
- package/dist/core/oklch.d.ts +0 -161
- package/dist/core/oklch.js +0 -312
- package/dist/core/osc8.d.ts +0 -68
- package/dist/core/osc8.js +0 -90
- package/dist/core/pretty.d.ts +0 -141
- package/dist/core/pretty.js +0 -472
- package/dist/core/protocol.d.ts +0 -137
- package/dist/core/protocol.js +0 -101
- package/dist/core/render.d.ts +0 -84
- package/dist/core/render.js +0 -145
- package/dist/core/segment.d.ts +0 -142
- package/dist/core/segment.js +0 -433
- package/dist/core/spinnerData.d.ts +0 -10
- package/dist/core/spinnerData.js +0 -115
- package/dist/core/strip.d.ts +0 -145
- package/dist/core/strip.js +0 -308
- package/dist/core/style.d.ts +0 -144
- package/dist/core/style.js +0 -723
- package/dist/core/subscription.d.ts +0 -12
- package/dist/core/subscription.js +0 -12
- package/dist/core/text.d.ts +0 -255
- package/dist/core/text.js +0 -990
- package/dist/core/wrap.d.ts +0 -41
- package/dist/core/wrap.js +0 -127
- package/dist/host/host-stream.d.ts +0 -30
- package/dist/host/host-stream.js +0 -35
- package/dist/host/index.d.ts +0 -3
- package/dist/host/index.js +0 -2
- package/dist/host/terminal-host.d.ts +0 -143
- package/dist/host/terminal-host.js +0 -132
- package/dist/index.d.ts +0 -87
- package/dist/index.js +0 -104
- package/dist/node/prompt.d.ts +0 -21
- package/dist/node/prompt.js +0 -30
- package/dist/node/save.d.ts +0 -17
- package/dist/node/save.js +0 -21
- package/dist/node/terminal-host.d.ts +0 -84
- package/dist/node/terminal-host.js +0 -160
- package/dist/node/traceback.d.ts +0 -27
- package/dist/node/traceback.js +0 -112
- package/dist/renderables/align.d.ts +0 -16
- package/dist/renderables/align.js +0 -46
- package/dist/renderables/columns.d.ts +0 -61
- package/dist/renderables/columns.js +0 -168
- package/dist/renderables/constrain.d.ts +0 -15
- package/dist/renderables/constrain.js +0 -37
- package/dist/renderables/flexStrip.d.ts +0 -41
- package/dist/renderables/flexStrip.js +0 -200
- package/dist/renderables/group.d.ts +0 -11
- package/dist/renderables/group.js +0 -15
- package/dist/renderables/layout.d.ts +0 -92
- package/dist/renderables/layout.js +0 -315
- package/dist/renderables/live.d.ts +0 -47
- package/dist/renderables/live.js +0 -131
- package/dist/renderables/markdown.d.ts +0 -24
- package/dist/renderables/markdown.js +0 -182
- package/dist/renderables/padding.d.ts +0 -38
- package/dist/renderables/padding.js +0 -140
- package/dist/renderables/panel.d.ts +0 -116
- package/dist/renderables/panel.js +0 -322
- package/dist/renderables/progress.d.ts +0 -90
- package/dist/renderables/progress.js +0 -241
- package/dist/renderables/progressBar.d.ts +0 -31
- package/dist/renderables/progressBar.js +0 -52
- package/dist/renderables/prompt.d.ts +0 -49
- package/dist/renderables/prompt.js +0 -125
- package/dist/renderables/rule.d.ts +0 -24
- package/dist/renderables/rule.js +0 -90
- package/dist/renderables/spinner.d.ts +0 -29
- package/dist/renderables/spinner.js +0 -64
- package/dist/renderables/status.d.ts +0 -23
- package/dist/renderables/status.js +0 -60
- package/dist/renderables/syntax.d.ts +0 -35
- package/dist/renderables/syntax.js +0 -114
- package/dist/renderables/table.d.ts +0 -182
- package/dist/renderables/table.js +0 -697
- package/dist/renderables/traceback.d.ts +0 -31
- package/dist/renderables/traceback.js +0 -109
- package/dist/renderables/tree.d.ts +0 -54
- package/dist/renderables/tree.js +0 -159
- package/dist/template-bindings/color-funcs.d.ts +0 -87
- package/dist/template-bindings/color-funcs.js +0 -219
- package/dist/template-bindings/helpers.d.ts +0 -24
- package/dist/template-bindings/helpers.js +0 -48
- package/dist/template-bindings/index.d.ts +0 -107
- package/dist/template-bindings/index.js +0 -144
- package/dist/template-bindings/palette-funcs.d.ts +0 -94
- package/dist/template-bindings/palette-funcs.js +0 -151
- package/dist/template-bindings/style-funcs.d.ts +0 -28
- package/dist/template-bindings/style-funcs.js +0 -151
- package/dist/themes/buildPalette.d.ts +0 -36
- package/dist/themes/buildPalette.js +0 -56
- package/dist/themes/colorMath.d.ts +0 -82
- package/dist/themes/colorMath.js +0 -254
- package/dist/themes/colorRef.d.ts +0 -51
- package/dist/themes/colorRef.js +0 -96
- package/dist/themes/data/atom-one-dark.d.ts +0 -3
- package/dist/themes/data/atom-one-dark.js +0 -158
- package/dist/themes/data/atom-one-light.d.ts +0 -3
- package/dist/themes/data/atom-one-light.js +0 -158
- package/dist/themes/data/catppuccin-frappe.d.ts +0 -3
- package/dist/themes/data/catppuccin-frappe.js +0 -158
- package/dist/themes/data/catppuccin-latte.d.ts +0 -3
- package/dist/themes/data/catppuccin-latte.js +0 -158
- package/dist/themes/data/catppuccin-macchiato.d.ts +0 -3
- package/dist/themes/data/catppuccin-macchiato.js +0 -158
- package/dist/themes/data/catppuccin-mocha.d.ts +0 -3
- package/dist/themes/data/catppuccin-mocha.js +0 -158
- package/dist/themes/data/cyberpunk.d.ts +0 -17
- package/dist/themes/data/cyberpunk.js +0 -191
- package/dist/themes/data/default.d.ts +0 -3
- package/dist/themes/data/default.js +0 -158
- package/dist/themes/data/dracula.d.ts +0 -3
- package/dist/themes/data/dracula.js +0 -158
- package/dist/themes/data/flexoki.d.ts +0 -3
- package/dist/themes/data/flexoki.js +0 -158
- package/dist/themes/data/gruvbox.d.ts +0 -3
- package/dist/themes/data/gruvbox.js +0 -158
- package/dist/themes/data/index.d.ts +0 -35
- package/dist/themes/data/index.js +0 -64
- package/dist/themes/data/monokai.d.ts +0 -3
- package/dist/themes/data/monokai.js +0 -158
- package/dist/themes/data/nord.d.ts +0 -3
- package/dist/themes/data/nord.js +0 -158
- package/dist/themes/data/rose-pine-dawn.d.ts +0 -3
- package/dist/themes/data/rose-pine-dawn.js +0 -158
- package/dist/themes/data/rose-pine-moon.d.ts +0 -3
- package/dist/themes/data/rose-pine-moon.js +0 -158
- package/dist/themes/data/rose-pine.d.ts +0 -3
- package/dist/themes/data/rose-pine.js +0 -158
- package/dist/themes/data/solarized-dark.d.ts +0 -3
- package/dist/themes/data/solarized-dark.js +0 -158
- package/dist/themes/data/solarized-light.d.ts +0 -3
- package/dist/themes/data/solarized-light.js +0 -158
- package/dist/themes/data/svg-export.d.ts +0 -3
- package/dist/themes/data/svg-export.js +0 -158
- package/dist/themes/data/textual-ansi.d.ts +0 -3
- package/dist/themes/data/textual-ansi.js +0 -158
- package/dist/themes/data/textual-dark.d.ts +0 -3
- package/dist/themes/data/textual-dark.js +0 -158
- package/dist/themes/data/textual-light.d.ts +0 -3
- package/dist/themes/data/textual-light.js +0 -158
- package/dist/themes/data/tokyo-night.d.ts +0 -3
- package/dist/themes/data/tokyo-night.js +0 -158
- package/dist/themes/data/types.d.ts +0 -15
- package/dist/themes/data/types.js +0 -1
- package/dist/themes/palette.d.ts +0 -21
- package/dist/themes/palette.js +0 -28
- package/dist/themes/ramp.d.ts +0 -83
- package/dist/themes/ramp.js +0 -122
- package/dist/themes/registry.d.ts +0 -43
- package/dist/themes/registry.js +0 -82
- package/dist/themes/terminalThemes.d.ts +0 -54
- package/dist/themes/terminalThemes.js +0 -68
- package/dist/themes/transpose.d.ts +0 -87
- package/dist/themes/transpose.js +0 -138
- package/dist/widgets/button.d.ts +0 -49
- package/dist/widgets/button.js +0 -206
- package/dist/widgets/checkbox.d.ts +0 -41
- package/dist/widgets/checkbox.js +0 -155
- package/dist/widgets/dropdown.d.ts +0 -67
- package/dist/widgets/dropdown.js +0 -453
- package/dist/widgets/event-router.d.ts +0 -78
- package/dist/widgets/event-router.js +0 -622
- package/dist/widgets/focus-manager.d.ts +0 -26
- package/dist/widgets/focus-manager.js +0 -193
- package/dist/widgets/index.d.ts +0 -22
- package/dist/widgets/index.js +0 -15
- package/dist/widgets/screen.d.ts +0 -98
- package/dist/widgets/screen.js +0 -530
- package/dist/widgets/slider.d.ts +0 -57
- package/dist/widgets/slider.js +0 -270
- package/dist/widgets/static-item.d.ts +0 -40
- package/dist/widgets/static-item.js +0 -60
- package/dist/widgets/text-input.d.ts +0 -274
- package/dist/widgets/text-input.js +0 -1162
- package/dist/widgets/toggle.d.ts +0 -44
- package/dist/widgets/toggle.js +0 -179
- package/dist/widgets/types.d.ts +0 -108
- package/dist/widgets/types.js +0 -31
- package/dist/widgets/widget-base.d.ts +0 -42
- package/dist/widgets/widget-base.js +0 -173
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* ProgressBar — a visual progress bar rendered with block characters.
|
|
3
|
-
*/
|
|
4
|
-
import { Segment } from "../core/segment.js";
|
|
5
|
-
import { NULL_STYLE } from "../core/style.js";
|
|
6
|
-
import { getStyle } from "../core/protocol.js";
|
|
7
|
-
const FULL_BLOCK = "━";
|
|
8
|
-
const EMPTY_BLOCK = "━";
|
|
9
|
-
export class ProgressBar {
|
|
10
|
-
total;
|
|
11
|
-
completed;
|
|
12
|
-
width;
|
|
13
|
-
pulse;
|
|
14
|
-
style;
|
|
15
|
-
completeStyle;
|
|
16
|
-
finishedStyle;
|
|
17
|
-
constructor(options) {
|
|
18
|
-
this.total = options?.total ?? 100;
|
|
19
|
-
this.completed = options?.completed ?? 0;
|
|
20
|
-
this.width = options?.width;
|
|
21
|
-
this.pulse = options?.pulse ?? false;
|
|
22
|
-
this.style = options?.style ?? NULL_STYLE;
|
|
23
|
-
this.completeStyle = options?.completeStyle ?? "bar.complete";
|
|
24
|
-
this.finishedStyle = options?.finishedStyle ?? "bar.finished";
|
|
25
|
-
}
|
|
26
|
-
get percentComplete() {
|
|
27
|
-
if (this.total <= 0)
|
|
28
|
-
return 0;
|
|
29
|
-
return Math.min(1, Math.max(0, this.completed / this.total));
|
|
30
|
-
}
|
|
31
|
-
*render(options) {
|
|
32
|
-
const barWidth = this.width ?? Math.min(40, options.maxWidth);
|
|
33
|
-
const percent = this.percentComplete;
|
|
34
|
-
const isFinished = percent >= 1;
|
|
35
|
-
const filledWidth = Math.round(barWidth * percent);
|
|
36
|
-
const emptyWidth = barWidth - filledWidth;
|
|
37
|
-
const fill = getStyle(options, isFinished ? this.finishedStyle : this.completeStyle);
|
|
38
|
-
const back = getStyle(options, this.style);
|
|
39
|
-
const fillStyle = fill.isNull ? undefined : fill;
|
|
40
|
-
const bgStyle = back.isNull ? undefined : back;
|
|
41
|
-
if (filledWidth > 0) {
|
|
42
|
-
yield new Segment(FULL_BLOCK.repeat(filledWidth), fillStyle);
|
|
43
|
-
}
|
|
44
|
-
if (emptyWidth > 0) {
|
|
45
|
-
yield new Segment(EMPTY_BLOCK.repeat(emptyWidth), bgStyle);
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
measure(_options) {
|
|
49
|
-
const w = this.width ?? 40;
|
|
50
|
-
return { minimum: 4, maximum: w };
|
|
51
|
-
}
|
|
52
|
-
}
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Prompt — interactive prompts for user input.
|
|
3
|
-
*
|
|
4
|
-
* [LAW:locality-or-seam] The renderable owns prompt logic (display, choice
|
|
5
|
-
* validation, default fallback, retry loop) — but not where the answer comes
|
|
6
|
-
* from. The input source is a required `PromptInput` capability passed at the
|
|
7
|
-
* call site. Node consumers pass `nodeAsk` from
|
|
8
|
-
* `@promptctl/rich-js/node/prompt`; tests pass a fake; the browser bundle
|
|
9
|
-
* gets the classes without dragging `node:readline` into the main barrel.
|
|
10
|
-
*
|
|
11
|
-
* [LAW:types-are-the-program] The `input: PromptInput` parameter is
|
|
12
|
-
* positional and required on every `*.ask()` static — not an optional in
|
|
13
|
-
* `PromptOptions`. The previous shape allowed `Prompt.ask("name?")` at the
|
|
14
|
-
* type level and threw at runtime; the new shape makes the missing-capability
|
|
15
|
-
* state unrepresentable to TS callers.
|
|
16
|
-
*
|
|
17
|
-
* A trust-boundary `typeof === "function"` check remains because the public
|
|
18
|
-
* API surface is reachable from JS (no compile-time types) and from
|
|
19
|
-
* `any`-typed TS callers. The check makes the failure *diagnostic* (points
|
|
20
|
-
* the caller at the node helper), not gatekeep-against-bugs — TS users
|
|
21
|
-
* never see it because the type already forbids the bad state.
|
|
22
|
-
*/
|
|
23
|
-
/**
|
|
24
|
-
* Input capability: receives the rendered prompt string (always with a
|
|
25
|
-
* trailing space appended by the renderable, so implementations should not
|
|
26
|
-
* add their own), resolves with the raw user response. Implementations
|
|
27
|
-
* decide where the input comes from (stdin readline, network, in-memory
|
|
28
|
-
* queue, etc.).
|
|
29
|
-
*/
|
|
30
|
-
export type PromptInput = (prompt: string) => Promise<string>;
|
|
31
|
-
export interface PromptOptions<T> {
|
|
32
|
-
default?: T;
|
|
33
|
-
choices?: string[];
|
|
34
|
-
caseSensitive?: boolean;
|
|
35
|
-
showChoices?: boolean;
|
|
36
|
-
showDefault?: boolean;
|
|
37
|
-
}
|
|
38
|
-
export declare class Prompt {
|
|
39
|
-
static ask(promptText: string, input: PromptInput, options?: PromptOptions<string>): Promise<string>;
|
|
40
|
-
}
|
|
41
|
-
export declare class IntPrompt {
|
|
42
|
-
static ask(promptText: string, input: PromptInput, options?: PromptOptions<number>): Promise<number>;
|
|
43
|
-
}
|
|
44
|
-
export declare class FloatPrompt {
|
|
45
|
-
static ask(promptText: string, input: PromptInput, options?: PromptOptions<number>): Promise<number>;
|
|
46
|
-
}
|
|
47
|
-
export declare class Confirm {
|
|
48
|
-
static ask(promptText: string, input: PromptInput, options?: PromptOptions<boolean>): Promise<boolean>;
|
|
49
|
-
}
|
|
@@ -1,125 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Prompt — interactive prompts for user input.
|
|
3
|
-
*
|
|
4
|
-
* [LAW:locality-or-seam] The renderable owns prompt logic (display, choice
|
|
5
|
-
* validation, default fallback, retry loop) — but not where the answer comes
|
|
6
|
-
* from. The input source is a required `PromptInput` capability passed at the
|
|
7
|
-
* call site. Node consumers pass `nodeAsk` from
|
|
8
|
-
* `@promptctl/rich-js/node/prompt`; tests pass a fake; the browser bundle
|
|
9
|
-
* gets the classes without dragging `node:readline` into the main barrel.
|
|
10
|
-
*
|
|
11
|
-
* [LAW:types-are-the-program] The `input: PromptInput` parameter is
|
|
12
|
-
* positional and required on every `*.ask()` static — not an optional in
|
|
13
|
-
* `PromptOptions`. The previous shape allowed `Prompt.ask("name?")` at the
|
|
14
|
-
* type level and threw at runtime; the new shape makes the missing-capability
|
|
15
|
-
* state unrepresentable to TS callers.
|
|
16
|
-
*
|
|
17
|
-
* A trust-boundary `typeof === "function"` check remains because the public
|
|
18
|
-
* API surface is reachable from JS (no compile-time types) and from
|
|
19
|
-
* `any`-typed TS callers. The check makes the failure *diagnostic* (points
|
|
20
|
-
* the caller at the node helper), not gatekeep-against-bugs — TS users
|
|
21
|
-
* never see it because the type already forbids the bad state.
|
|
22
|
-
*/
|
|
23
|
-
import { renderMarkup } from "../core/markup.js";
|
|
24
|
-
// --- Base ---
|
|
25
|
-
function ask(promptText, input) {
|
|
26
|
-
// [LAW:single-enforcer] Trust-boundary validation for non-TS callers
|
|
27
|
-
// (JS, or TS with `any` laundering). TS callers can't reach this branch
|
|
28
|
-
// because `PromptInput` is required at every static `.ask`. The message
|
|
29
|
-
// points at the node helper rather than letting the call site die with
|
|
30
|
-
// a generic `TypeError: input is not a function`.
|
|
31
|
-
if (typeof input !== "function") {
|
|
32
|
-
throw new TypeError("Prompt: `input` must be a `PromptInput` function. Pass `nodeAsk` from " +
|
|
33
|
-
"`@promptctl/rich-js/node/prompt` for Node, or supply a custom " +
|
|
34
|
-
"`PromptInput` for tests/browsers.");
|
|
35
|
-
}
|
|
36
|
-
const rendered = renderMarkup(promptText);
|
|
37
|
-
return input(rendered.plain + " ");
|
|
38
|
-
}
|
|
39
|
-
// --- Prompt ---
|
|
40
|
-
export class Prompt {
|
|
41
|
-
static async ask(promptText, input, options) {
|
|
42
|
-
const showDefault = options?.showDefault !== false;
|
|
43
|
-
const showChoices = options?.showChoices !== false;
|
|
44
|
-
let display = promptText;
|
|
45
|
-
if (showChoices && options?.choices) {
|
|
46
|
-
display += ` [${options.choices.join("/")}]`;
|
|
47
|
-
}
|
|
48
|
-
if (showDefault && options?.default !== undefined) {
|
|
49
|
-
display += ` (${options.default})`;
|
|
50
|
-
}
|
|
51
|
-
display += ":";
|
|
52
|
-
while (true) {
|
|
53
|
-
const answer = await ask(display, input);
|
|
54
|
-
const value = answer.trim();
|
|
55
|
-
if (value === "" && options?.default !== undefined) {
|
|
56
|
-
return options.default;
|
|
57
|
-
}
|
|
58
|
-
if (options?.choices) {
|
|
59
|
-
const caseSensitive = options.caseSensitive !== false;
|
|
60
|
-
const match = options.choices.find((c) => caseSensitive ? c === value : c.toLowerCase() === value.toLowerCase());
|
|
61
|
-
if (match)
|
|
62
|
-
return match;
|
|
63
|
-
continue;
|
|
64
|
-
}
|
|
65
|
-
return value;
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
export class IntPrompt {
|
|
70
|
-
static async ask(promptText, input, options) {
|
|
71
|
-
const showDefault = options?.showDefault !== false;
|
|
72
|
-
let display = promptText;
|
|
73
|
-
if (showDefault && options?.default !== undefined) {
|
|
74
|
-
display += ` (${options.default})`;
|
|
75
|
-
}
|
|
76
|
-
display += ":";
|
|
77
|
-
while (true) {
|
|
78
|
-
const answer = await ask(display, input);
|
|
79
|
-
const value = answer.trim();
|
|
80
|
-
if (value === "" && options?.default !== undefined) {
|
|
81
|
-
return options.default;
|
|
82
|
-
}
|
|
83
|
-
const num = parseInt(value, 10);
|
|
84
|
-
if (!isNaN(num) && String(num) === value)
|
|
85
|
-
return num;
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
export class FloatPrompt {
|
|
90
|
-
static async ask(promptText, input, options) {
|
|
91
|
-
const showDefault = options?.showDefault !== false;
|
|
92
|
-
let display = promptText;
|
|
93
|
-
if (showDefault && options?.default !== undefined) {
|
|
94
|
-
display += ` (${options.default})`;
|
|
95
|
-
}
|
|
96
|
-
display += ":";
|
|
97
|
-
while (true) {
|
|
98
|
-
const answer = await ask(display, input);
|
|
99
|
-
const value = answer.trim();
|
|
100
|
-
if (value === "" && options?.default !== undefined) {
|
|
101
|
-
return options.default;
|
|
102
|
-
}
|
|
103
|
-
const num = parseFloat(value);
|
|
104
|
-
if (!isNaN(num))
|
|
105
|
-
return num;
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
export class Confirm {
|
|
110
|
-
static async ask(promptText, input, options) {
|
|
111
|
-
const defaultVal = options?.default;
|
|
112
|
-
const yesNo = defaultVal === true ? "Y/n" : defaultVal === false ? "y/N" : "y/n";
|
|
113
|
-
const display = `${promptText} [${yesNo}]:`;
|
|
114
|
-
while (true) {
|
|
115
|
-
const answer = await ask(display, input);
|
|
116
|
-
const value = answer.trim().toLowerCase();
|
|
117
|
-
if (value === "" && defaultVal !== undefined)
|
|
118
|
-
return defaultVal;
|
|
119
|
-
if (value === "y" || value === "yes")
|
|
120
|
-
return true;
|
|
121
|
-
if (value === "n" || value === "no")
|
|
122
|
-
return false;
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
}
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Rule — a horizontal divider line, optionally with a centered title.
|
|
3
|
-
*/
|
|
4
|
-
import { Segment } from "../core/segment.js";
|
|
5
|
-
import { Style } from "../core/style.js";
|
|
6
|
-
import type { Renderable, Measurable, RenderOptions } from "../core/protocol.js";
|
|
7
|
-
export type RuleAlign = "left" | "center" | "right";
|
|
8
|
-
export interface RuleOptions {
|
|
9
|
-
characters?: string;
|
|
10
|
-
align?: RuleAlign;
|
|
11
|
-
style?: string | Style;
|
|
12
|
-
}
|
|
13
|
-
export declare class Rule implements Renderable, Measurable {
|
|
14
|
-
readonly title: string | undefined;
|
|
15
|
-
readonly characters: string;
|
|
16
|
-
readonly align: RuleAlign;
|
|
17
|
-
readonly style: string | Style;
|
|
18
|
-
constructor(title?: string, options?: RuleOptions);
|
|
19
|
-
render(options: RenderOptions): Iterable<Segment>;
|
|
20
|
-
measure(_options: RenderOptions): {
|
|
21
|
-
minimum: number;
|
|
22
|
-
maximum: number;
|
|
23
|
-
};
|
|
24
|
-
}
|
package/dist/renderables/rule.js
DELETED
|
@@ -1,90 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Rule — a horizontal divider line, optionally with a centered title.
|
|
3
|
-
*/
|
|
4
|
-
import { cellLen } from "../core/cells.js";
|
|
5
|
-
import { Segment } from "../core/segment.js";
|
|
6
|
-
import { NULL_STYLE } from "../core/style.js";
|
|
7
|
-
import { getStyle } from "../core/protocol.js";
|
|
8
|
-
const ASCII_RULE_CHAR = "-";
|
|
9
|
-
const DEFAULT_RULE_CHAR = "\u2500"; // ─
|
|
10
|
-
export class Rule {
|
|
11
|
-
title;
|
|
12
|
-
characters;
|
|
13
|
-
align;
|
|
14
|
-
style;
|
|
15
|
-
constructor(title, options) {
|
|
16
|
-
const chars = options?.characters ?? DEFAULT_RULE_CHAR;
|
|
17
|
-
if (chars.length === 0) {
|
|
18
|
-
throw new Error("Rule characters must not be empty");
|
|
19
|
-
}
|
|
20
|
-
const align = options?.align;
|
|
21
|
-
if (align !== undefined && align !== "left" && align !== "center" && align !== "right") {
|
|
22
|
-
throw new Error(`Invalid align value: "${align}"`);
|
|
23
|
-
}
|
|
24
|
-
this.title = title;
|
|
25
|
-
this.characters = chars;
|
|
26
|
-
this.align = align ?? "center";
|
|
27
|
-
this.style = options?.style ?? NULL_STYLE;
|
|
28
|
-
}
|
|
29
|
-
*render(options) {
|
|
30
|
-
const maxWidth = options.maxWidth;
|
|
31
|
-
const ruleChar = options.asciiOnly ? ASCII_RULE_CHAR : this.characters;
|
|
32
|
-
const style = getStyle(options, this.style);
|
|
33
|
-
const ruleStyle = style.isNull ? undefined : style;
|
|
34
|
-
if (!this.title) {
|
|
35
|
-
// No title — just a line of repeated characters
|
|
36
|
-
yield new Segment(repeatToWidth(ruleChar, maxWidth), ruleStyle);
|
|
37
|
-
yield Segment.line();
|
|
38
|
-
return;
|
|
39
|
-
}
|
|
40
|
-
// With title
|
|
41
|
-
const titleText = ` ${this.title} `;
|
|
42
|
-
const titleWidth = cellLen(titleText);
|
|
43
|
-
if (titleWidth >= maxWidth) {
|
|
44
|
-
// Title fills the whole width
|
|
45
|
-
yield new Segment(titleText.slice(0, maxWidth), ruleStyle);
|
|
46
|
-
yield Segment.line();
|
|
47
|
-
return;
|
|
48
|
-
}
|
|
49
|
-
const remaining = maxWidth - titleWidth;
|
|
50
|
-
// [LAW:dataflow-not-control-flow] Always compute both sides; alignment determines distribution
|
|
51
|
-
const leftWidth = this.align === "right"
|
|
52
|
-
? remaining
|
|
53
|
-
: this.align === "center"
|
|
54
|
-
? Math.floor(remaining / 2)
|
|
55
|
-
: 0;
|
|
56
|
-
const rightWidth = remaining - leftWidth;
|
|
57
|
-
if (leftWidth > 0) {
|
|
58
|
-
yield new Segment(repeatToWidth(ruleChar, leftWidth), ruleStyle);
|
|
59
|
-
}
|
|
60
|
-
yield new Segment(titleText, ruleStyle);
|
|
61
|
-
if (rightWidth > 0) {
|
|
62
|
-
yield new Segment(repeatToWidth(ruleChar, rightWidth), ruleStyle);
|
|
63
|
-
}
|
|
64
|
-
yield Segment.line();
|
|
65
|
-
}
|
|
66
|
-
measure(_options) {
|
|
67
|
-
return { minimum: 1, maximum: _options.maxWidth };
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
function repeatToWidth(char, width) {
|
|
71
|
-
const charWidth = cellLen(char);
|
|
72
|
-
if (charWidth === 0)
|
|
73
|
-
return " ".repeat(width);
|
|
74
|
-
const repeats = Math.ceil(width / charWidth);
|
|
75
|
-
const full = char.repeat(repeats);
|
|
76
|
-
// Trim to exact width
|
|
77
|
-
let w = 0;
|
|
78
|
-
let i = 0;
|
|
79
|
-
for (const c of full) {
|
|
80
|
-
const cw = cellLen(c);
|
|
81
|
-
if (w + cw > width)
|
|
82
|
-
break;
|
|
83
|
-
w += cw;
|
|
84
|
-
i += c.length;
|
|
85
|
-
}
|
|
86
|
-
const result = full.slice(0, i);
|
|
87
|
-
// Pad if needed (wide char couldn't fill exact width)
|
|
88
|
-
const gap = width - w;
|
|
89
|
-
return gap > 0 ? result + " ".repeat(gap) : result;
|
|
90
|
-
}
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Spinner — animated terminal spinner with optional text label.
|
|
3
|
-
*/
|
|
4
|
-
import { Segment } from "../core/segment.js";
|
|
5
|
-
import { Style } from "../core/style.js";
|
|
6
|
-
import type { Renderable, Measurable, RenderOptions } from "../core/protocol.js";
|
|
7
|
-
export interface SpinnerOptions {
|
|
8
|
-
speed?: number;
|
|
9
|
-
style?: string | Style;
|
|
10
|
-
}
|
|
11
|
-
export declare class Spinner implements Renderable, Measurable {
|
|
12
|
-
readonly name: string;
|
|
13
|
-
readonly text: string | undefined;
|
|
14
|
-
readonly speed: number;
|
|
15
|
-
readonly style: string | Style;
|
|
16
|
-
private readonly _data;
|
|
17
|
-
private _frameIndex;
|
|
18
|
-
private _lastUpdate;
|
|
19
|
-
constructor(name?: string, text?: string, options?: SpinnerOptions);
|
|
20
|
-
get frames(): readonly string[];
|
|
21
|
-
get interval(): number;
|
|
22
|
-
/** Advance frame based on elapsed time and return current frame. */
|
|
23
|
-
private _currentFrame;
|
|
24
|
-
render(options: RenderOptions): Iterable<Segment>;
|
|
25
|
-
measure(_options: RenderOptions): {
|
|
26
|
-
minimum: number;
|
|
27
|
-
maximum: number;
|
|
28
|
-
};
|
|
29
|
-
}
|
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Spinner — animated terminal spinner with optional text label.
|
|
3
|
-
*/
|
|
4
|
-
import { cellLen } from "../core/cells.js";
|
|
5
|
-
import { Segment } from "../core/segment.js";
|
|
6
|
-
import { NULL_STYLE } from "../core/style.js";
|
|
7
|
-
import { SPINNERS, DEFAULT_SPINNER } from "../core/spinnerData.js";
|
|
8
|
-
import { getStyle } from "../core/protocol.js";
|
|
9
|
-
export class Spinner {
|
|
10
|
-
name;
|
|
11
|
-
text;
|
|
12
|
-
speed;
|
|
13
|
-
style;
|
|
14
|
-
_data;
|
|
15
|
-
_frameIndex;
|
|
16
|
-
_lastUpdate;
|
|
17
|
-
constructor(name, text, options) {
|
|
18
|
-
const spinnerName = name ?? DEFAULT_SPINNER;
|
|
19
|
-
const data = SPINNERS[spinnerName];
|
|
20
|
-
if (!data) {
|
|
21
|
-
throw new Error(`Unknown spinner: "${spinnerName}"`);
|
|
22
|
-
}
|
|
23
|
-
this.name = spinnerName;
|
|
24
|
-
this.text = text;
|
|
25
|
-
this.speed = options?.speed ?? 1;
|
|
26
|
-
this.style = options?.style ?? NULL_STYLE;
|
|
27
|
-
this._data = data;
|
|
28
|
-
this._frameIndex = 0;
|
|
29
|
-
this._lastUpdate = Date.now();
|
|
30
|
-
}
|
|
31
|
-
get frames() {
|
|
32
|
-
return this._data.frames;
|
|
33
|
-
}
|
|
34
|
-
get interval() {
|
|
35
|
-
return this._data.interval;
|
|
36
|
-
}
|
|
37
|
-
/** Advance frame based on elapsed time and return current frame. */
|
|
38
|
-
_currentFrame() {
|
|
39
|
-
const now = Date.now();
|
|
40
|
-
const elapsed = now - this._lastUpdate;
|
|
41
|
-
const effectiveInterval = this.interval / this.speed;
|
|
42
|
-
if (elapsed >= effectiveInterval) {
|
|
43
|
-
const steps = Math.floor(elapsed / effectiveInterval);
|
|
44
|
-
this._frameIndex = (this._frameIndex + steps) % this._data.frames.length;
|
|
45
|
-
this._lastUpdate = now;
|
|
46
|
-
}
|
|
47
|
-
return this._data.frames[this._frameIndex];
|
|
48
|
-
}
|
|
49
|
-
*render(options) {
|
|
50
|
-
const frame = this._currentFrame();
|
|
51
|
-
const style = getStyle(options, this.style);
|
|
52
|
-
const spinStyle = style.isNull ? undefined : style;
|
|
53
|
-
yield new Segment(frame, spinStyle);
|
|
54
|
-
if (this.text) {
|
|
55
|
-
yield new Segment(` ${this.text}`);
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
measure(_options) {
|
|
59
|
-
const frameWidth = Math.max(...this._data.frames.map((f) => cellLen(f)));
|
|
60
|
-
const textWidth = this.text ? cellLen(this.text) + 1 : 0;
|
|
61
|
-
const total = frameWidth + textWidth;
|
|
62
|
-
return { minimum: frameWidth, maximum: total };
|
|
63
|
-
}
|
|
64
|
-
}
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Status — displays a spinner animation with a status message.
|
|
3
|
-
*/
|
|
4
|
-
import { Style } from "../core/style.js";
|
|
5
|
-
import { Console } from "../core/console.js";
|
|
6
|
-
export interface StatusOptions {
|
|
7
|
-
spinner?: string;
|
|
8
|
-
speed?: number;
|
|
9
|
-
style?: string | Style;
|
|
10
|
-
console?: Console;
|
|
11
|
-
}
|
|
12
|
-
export declare class Status {
|
|
13
|
-
private _live;
|
|
14
|
-
private _renderable;
|
|
15
|
-
private _console;
|
|
16
|
-
constructor(message: string, options?: StatusOptions);
|
|
17
|
-
get console(): Console;
|
|
18
|
-
get message(): string;
|
|
19
|
-
set message(value: string);
|
|
20
|
-
start(): void;
|
|
21
|
-
stop(): void;
|
|
22
|
-
update(message: string): void;
|
|
23
|
-
}
|
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Status — displays a spinner animation with a status message.
|
|
3
|
-
*/
|
|
4
|
-
import { Segment } from "../core/segment.js";
|
|
5
|
-
import { NULL_STYLE } from "../core/style.js";
|
|
6
|
-
import { Console } from "../core/console.js";
|
|
7
|
-
import { Spinner } from "./spinner.js";
|
|
8
|
-
import { Live } from "./live.js";
|
|
9
|
-
import { getStyle } from "../core/protocol.js";
|
|
10
|
-
/** A renderable that shows spinner + message */
|
|
11
|
-
class StatusRenderable {
|
|
12
|
-
message;
|
|
13
|
-
_spinner;
|
|
14
|
-
_style;
|
|
15
|
-
constructor(message, spinner, style) {
|
|
16
|
-
this.message = message;
|
|
17
|
-
this._spinner = spinner;
|
|
18
|
-
this._style = style;
|
|
19
|
-
}
|
|
20
|
-
*render(options) {
|
|
21
|
-
yield* this._spinner.render(options);
|
|
22
|
-
const style = getStyle(options, this._style);
|
|
23
|
-
const msgStyle = style.isNull ? undefined : style;
|
|
24
|
-
yield new Segment(` ${this.message}`, msgStyle);
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
export class Status {
|
|
28
|
-
_live;
|
|
29
|
-
_renderable;
|
|
30
|
-
_console;
|
|
31
|
-
constructor(message, options) {
|
|
32
|
-
this._console = options?.console ?? new Console({ forceTerminal: true });
|
|
33
|
-
const spinner = new Spinner(options?.spinner ?? "dots", undefined, { speed: options?.speed });
|
|
34
|
-
this._renderable = new StatusRenderable(message, spinner, options?.style ?? NULL_STYLE);
|
|
35
|
-
this._live = new Live(this._renderable, {
|
|
36
|
-
console: this._console,
|
|
37
|
-
transient: true,
|
|
38
|
-
refreshPerSecond: 12.5,
|
|
39
|
-
});
|
|
40
|
-
}
|
|
41
|
-
get console() {
|
|
42
|
-
return this._console;
|
|
43
|
-
}
|
|
44
|
-
get message() {
|
|
45
|
-
return this._renderable.message;
|
|
46
|
-
}
|
|
47
|
-
set message(value) {
|
|
48
|
-
this._renderable.message = value;
|
|
49
|
-
}
|
|
50
|
-
start() {
|
|
51
|
-
this._live.start();
|
|
52
|
-
}
|
|
53
|
-
stop() {
|
|
54
|
-
this._live.stop();
|
|
55
|
-
}
|
|
56
|
-
update(message) {
|
|
57
|
-
this._renderable.message = message;
|
|
58
|
-
this._live.update(this._renderable, { refresh: true });
|
|
59
|
-
}
|
|
60
|
-
}
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Syntax — renders source code with basic syntax highlighting.
|
|
3
|
-
* Uses built-in tokenization (no external dependency).
|
|
4
|
-
*/
|
|
5
|
-
import { Segment } from "../core/segment.js";
|
|
6
|
-
import type { PaddingDimensions } from "./padding.js";
|
|
7
|
-
import type { Renderable, Measurable, RenderOptions } from "../core/protocol.js";
|
|
8
|
-
export interface SyntaxOptions {
|
|
9
|
-
lineNumbers?: boolean;
|
|
10
|
-
theme?: string;
|
|
11
|
-
startLine?: number;
|
|
12
|
-
lineRange?: [number, number];
|
|
13
|
-
highlightLines?: Set<number>;
|
|
14
|
-
wordWrap?: boolean;
|
|
15
|
-
tabSize?: number;
|
|
16
|
-
padding?: PaddingDimensions;
|
|
17
|
-
}
|
|
18
|
-
export declare class Syntax implements Renderable, Measurable {
|
|
19
|
-
readonly code: string;
|
|
20
|
-
readonly language: string;
|
|
21
|
-
readonly lineNumbers: boolean;
|
|
22
|
-
readonly startLine: number;
|
|
23
|
-
readonly lineRange: [number, number] | undefined;
|
|
24
|
-
readonly highlightLines: Set<number>;
|
|
25
|
-
readonly wordWrap: boolean;
|
|
26
|
-
readonly tabSize: number;
|
|
27
|
-
constructor(code: string, language?: string, options?: SyntaxOptions);
|
|
28
|
-
render(options: RenderOptions): Iterable<Segment>;
|
|
29
|
-
measure(options: RenderOptions): {
|
|
30
|
-
minimum: number;
|
|
31
|
-
maximum: number;
|
|
32
|
-
};
|
|
33
|
-
private _highlight;
|
|
34
|
-
static fromPath(_filePath: string, _options?: SyntaxOptions): Syntax;
|
|
35
|
-
}
|
|
@@ -1,114 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Syntax — renders source code with basic syntax highlighting.
|
|
3
|
-
* Uses built-in tokenization (no external dependency).
|
|
4
|
-
*/
|
|
5
|
-
import { cellLen } from "../core/cells.js";
|
|
6
|
-
import { Segment } from "../core/segment.js";
|
|
7
|
-
import { Style } from "../core/style.js";
|
|
8
|
-
import { RichText } from "../core/text.js";
|
|
9
|
-
// Built-in token patterns for common languages
|
|
10
|
-
const TOKEN_PATTERNS = {
|
|
11
|
-
_default: [
|
|
12
|
-
[/\/\/.*$/gm, "comment"],
|
|
13
|
-
[/\/\*[\s\S]*?\*\//g, "comment"],
|
|
14
|
-
[/#.*$/gm, "comment"],
|
|
15
|
-
[/"(?:[^"\\]|\\.)*"/g, "string"],
|
|
16
|
-
[/'(?:[^'\\]|\\.)*'/g, "string"],
|
|
17
|
-
[/`(?:[^`\\]|\\.)*`/g, "string"],
|
|
18
|
-
[/\b(?:true|false|null|undefined|NaN|Infinity)\b/g, "keyword.constant"],
|
|
19
|
-
[/\b(?:function|class|const|let|var|return|if|else|for|while|do|switch|case|break|continue|new|throw|try|catch|finally|import|export|from|default|async|await|yield|of|in|typeof|instanceof|void|delete)\b/g, "keyword"],
|
|
20
|
-
[/\b(?:def|lambda|with|as|pass|raise|elif|except|print|self|cls|None|True|False)\b/g, "keyword"],
|
|
21
|
-
[/\b\d+(?:\.\d+)?(?:[eE][+-]?\d+)?\b/g, "number"],
|
|
22
|
-
[/\b0x[0-9a-fA-F]+\b/g, "number"],
|
|
23
|
-
],
|
|
24
|
-
};
|
|
25
|
-
const TOKEN_STYLES = {
|
|
26
|
-
"keyword": Style.parse("bold magenta"),
|
|
27
|
-
"keyword.constant": Style.parse("italic bright_magenta"),
|
|
28
|
-
"string": Style.parse("green"),
|
|
29
|
-
"number": Style.parse("cyan"),
|
|
30
|
-
"comment": Style.parse("dim italic"),
|
|
31
|
-
};
|
|
32
|
-
export class Syntax {
|
|
33
|
-
code;
|
|
34
|
-
language;
|
|
35
|
-
lineNumbers;
|
|
36
|
-
startLine;
|
|
37
|
-
lineRange;
|
|
38
|
-
highlightLines;
|
|
39
|
-
wordWrap;
|
|
40
|
-
tabSize;
|
|
41
|
-
constructor(code, language, options) {
|
|
42
|
-
this.code = code;
|
|
43
|
-
this.language = language ?? "text";
|
|
44
|
-
this.lineNumbers = options?.lineNumbers ?? false;
|
|
45
|
-
this.startLine = options?.startLine ?? 1;
|
|
46
|
-
this.lineRange = options?.lineRange;
|
|
47
|
-
this.highlightLines = options?.highlightLines ?? new Set();
|
|
48
|
-
this.wordWrap = options?.wordWrap ?? false;
|
|
49
|
-
this.tabSize = options?.tabSize ?? 4;
|
|
50
|
-
}
|
|
51
|
-
*render(options) {
|
|
52
|
-
const expandedCode = this.code.replace(/\t/g, " ".repeat(this.tabSize));
|
|
53
|
-
let lines = expandedCode.split("\n");
|
|
54
|
-
// Apply line range
|
|
55
|
-
if (this.lineRange) {
|
|
56
|
-
const [start, end] = this.lineRange;
|
|
57
|
-
lines = lines.slice(start - 1, end);
|
|
58
|
-
}
|
|
59
|
-
const lineNumWidth = this.lineNumbers
|
|
60
|
-
? String(this.startLine + lines.length - 1).length + 1
|
|
61
|
-
: 0;
|
|
62
|
-
// Tokenize the full text for highlighting
|
|
63
|
-
const text = new RichText(lines.join("\n"), { end: "" });
|
|
64
|
-
this._highlight(text);
|
|
65
|
-
const textLines = text.split("\n");
|
|
66
|
-
for (let i = 0; i < textLines.length; i++) {
|
|
67
|
-
const lineNo = this.startLine + i;
|
|
68
|
-
const isHighlighted = this.highlightLines.has(lineNo);
|
|
69
|
-
if (this.lineNumbers) {
|
|
70
|
-
const numStr = String(lineNo).padStart(lineNumWidth - 1) + " ";
|
|
71
|
-
const numStyle = isHighlighted
|
|
72
|
-
? Style.parse("bold on grey27")
|
|
73
|
-
: Style.parse("dim");
|
|
74
|
-
yield new Segment(numStr, numStyle);
|
|
75
|
-
yield new Segment("│ ", Style.parse("dim"));
|
|
76
|
-
}
|
|
77
|
-
const line = textLines[i];
|
|
78
|
-
const segs = [...line.render({ ...options, maxWidth: options.maxWidth - lineNumWidth - 2 })];
|
|
79
|
-
// Filter out trailing newline from render
|
|
80
|
-
for (const seg of segs) {
|
|
81
|
-
if (seg.text !== "\n")
|
|
82
|
-
yield seg;
|
|
83
|
-
}
|
|
84
|
-
yield Segment.line();
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
measure(options) {
|
|
88
|
-
const lines = this.code.split("\n");
|
|
89
|
-
let maxLine = 0;
|
|
90
|
-
for (const line of lines) {
|
|
91
|
-
maxLine = Math.max(maxLine, cellLen(line));
|
|
92
|
-
}
|
|
93
|
-
const lineNumWidth = this.lineNumbers
|
|
94
|
-
? String(this.startLine + lines.length - 1).length + 3
|
|
95
|
-
: 0;
|
|
96
|
-
return {
|
|
97
|
-
minimum: 10,
|
|
98
|
-
maximum: Math.min(maxLine + lineNumWidth, options.maxWidth),
|
|
99
|
-
};
|
|
100
|
-
}
|
|
101
|
-
_highlight(text) {
|
|
102
|
-
const patterns = TOKEN_PATTERNS["_default"] ?? [];
|
|
103
|
-
for (const [pattern, tokenType] of patterns) {
|
|
104
|
-
const style = TOKEN_STYLES[tokenType];
|
|
105
|
-
if (style) {
|
|
106
|
-
text.highlightRegex(pattern, style);
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
static fromPath(_filePath, _options) {
|
|
111
|
-
// In a real implementation, this would read the file and detect language
|
|
112
|
-
throw new Error("Syntax.fromPath requires file system access — use the constructor with code string instead");
|
|
113
|
-
}
|
|
114
|
-
}
|