@promptctl/rich-js 0.16.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.
Files changed (206) hide show
  1. package/README.md +14 -27
  2. package/package.json +7 -3
  3. package/dist/core/box.d.ts +0 -144
  4. package/dist/core/box.js +0 -391
  5. package/dist/core/cells.d.ts +0 -148
  6. package/dist/core/cells.js +0 -266
  7. package/dist/core/color.d.ts +0 -229
  8. package/dist/core/color.js +0 -992
  9. package/dist/core/console.d.ts +0 -142
  10. package/dist/core/console.js +0 -477
  11. package/dist/core/emoji.d.ts +0 -24
  12. package/dist/core/emoji.js +0 -391
  13. package/dist/core/export-html.d.ts +0 -24
  14. package/dist/core/export-html.js +0 -106
  15. package/dist/core/export-lines.d.ts +0 -121
  16. package/dist/core/export-lines.js +0 -141
  17. package/dist/core/highlighter.d.ts +0 -33
  18. package/dist/core/highlighter.js +0 -124
  19. package/dist/core/json.d.ts +0 -27
  20. package/dist/core/json.js +0 -53
  21. package/dist/core/markup.d.ts +0 -97
  22. package/dist/core/markup.js +0 -619
  23. package/dist/core/measure.d.ts +0 -27
  24. package/dist/core/measure.js +0 -61
  25. package/dist/core/oklch.d.ts +0 -161
  26. package/dist/core/oklch.js +0 -312
  27. package/dist/core/osc8.d.ts +0 -68
  28. package/dist/core/osc8.js +0 -90
  29. package/dist/core/pretty.d.ts +0 -141
  30. package/dist/core/pretty.js +0 -472
  31. package/dist/core/protocol.d.ts +0 -137
  32. package/dist/core/protocol.js +0 -101
  33. package/dist/core/render.d.ts +0 -69
  34. package/dist/core/render.js +0 -140
  35. package/dist/core/segment.d.ts +0 -142
  36. package/dist/core/segment.js +0 -433
  37. package/dist/core/spinnerData.d.ts +0 -10
  38. package/dist/core/spinnerData.js +0 -115
  39. package/dist/core/strip.d.ts +0 -139
  40. package/dist/core/strip.js +0 -302
  41. package/dist/core/style.d.ts +0 -149
  42. package/dist/core/style.js +0 -741
  43. package/dist/core/subscription.d.ts +0 -12
  44. package/dist/core/subscription.js +0 -12
  45. package/dist/core/text.d.ts +0 -255
  46. package/dist/core/text.js +0 -990
  47. package/dist/core/wrap.d.ts +0 -41
  48. package/dist/core/wrap.js +0 -127
  49. package/dist/host/host-stream.d.ts +0 -30
  50. package/dist/host/host-stream.js +0 -35
  51. package/dist/host/index.d.ts +0 -3
  52. package/dist/host/index.js +0 -2
  53. package/dist/host/terminal-host.d.ts +0 -143
  54. package/dist/host/terminal-host.js +0 -132
  55. package/dist/index.d.ts +0 -87
  56. package/dist/index.js +0 -104
  57. package/dist/node/prompt.d.ts +0 -21
  58. package/dist/node/prompt.js +0 -30
  59. package/dist/node/save.d.ts +0 -17
  60. package/dist/node/save.js +0 -21
  61. package/dist/node/terminal-host.d.ts +0 -84
  62. package/dist/node/terminal-host.js +0 -160
  63. package/dist/node/traceback.d.ts +0 -27
  64. package/dist/node/traceback.js +0 -112
  65. package/dist/renderables/align.d.ts +0 -16
  66. package/dist/renderables/align.js +0 -46
  67. package/dist/renderables/columns.d.ts +0 -61
  68. package/dist/renderables/columns.js +0 -168
  69. package/dist/renderables/constrain.d.ts +0 -15
  70. package/dist/renderables/constrain.js +0 -37
  71. package/dist/renderables/flexStrip.d.ts +0 -41
  72. package/dist/renderables/flexStrip.js +0 -200
  73. package/dist/renderables/group.d.ts +0 -11
  74. package/dist/renderables/group.js +0 -15
  75. package/dist/renderables/layout.d.ts +0 -92
  76. package/dist/renderables/layout.js +0 -315
  77. package/dist/renderables/live.d.ts +0 -47
  78. package/dist/renderables/live.js +0 -131
  79. package/dist/renderables/markdown.d.ts +0 -24
  80. package/dist/renderables/markdown.js +0 -182
  81. package/dist/renderables/padding.d.ts +0 -38
  82. package/dist/renderables/padding.js +0 -140
  83. package/dist/renderables/panel.d.ts +0 -116
  84. package/dist/renderables/panel.js +0 -322
  85. package/dist/renderables/progress.d.ts +0 -90
  86. package/dist/renderables/progress.js +0 -241
  87. package/dist/renderables/progressBar.d.ts +0 -31
  88. package/dist/renderables/progressBar.js +0 -52
  89. package/dist/renderables/prompt.d.ts +0 -49
  90. package/dist/renderables/prompt.js +0 -125
  91. package/dist/renderables/rule.d.ts +0 -24
  92. package/dist/renderables/rule.js +0 -90
  93. package/dist/renderables/spinner.d.ts +0 -29
  94. package/dist/renderables/spinner.js +0 -64
  95. package/dist/renderables/status.d.ts +0 -23
  96. package/dist/renderables/status.js +0 -60
  97. package/dist/renderables/syntax.d.ts +0 -35
  98. package/dist/renderables/syntax.js +0 -114
  99. package/dist/renderables/table.d.ts +0 -182
  100. package/dist/renderables/table.js +0 -697
  101. package/dist/renderables/traceback.d.ts +0 -31
  102. package/dist/renderables/traceback.js +0 -109
  103. package/dist/renderables/tree.d.ts +0 -54
  104. package/dist/renderables/tree.js +0 -159
  105. package/dist/template-bindings/color-funcs.d.ts +0 -87
  106. package/dist/template-bindings/color-funcs.js +0 -219
  107. package/dist/template-bindings/helpers.d.ts +0 -24
  108. package/dist/template-bindings/helpers.js +0 -48
  109. package/dist/template-bindings/index.d.ts +0 -107
  110. package/dist/template-bindings/index.js +0 -144
  111. package/dist/template-bindings/palette-funcs.d.ts +0 -94
  112. package/dist/template-bindings/palette-funcs.js +0 -151
  113. package/dist/template-bindings/style-funcs.d.ts +0 -28
  114. package/dist/template-bindings/style-funcs.js +0 -151
  115. package/dist/themes/buildPalette.d.ts +0 -36
  116. package/dist/themes/buildPalette.js +0 -56
  117. package/dist/themes/colorMath.d.ts +0 -82
  118. package/dist/themes/colorMath.js +0 -254
  119. package/dist/themes/colorRef.d.ts +0 -51
  120. package/dist/themes/colorRef.js +0 -96
  121. package/dist/themes/data/atom-one-dark.d.ts +0 -3
  122. package/dist/themes/data/atom-one-dark.js +0 -158
  123. package/dist/themes/data/atom-one-light.d.ts +0 -3
  124. package/dist/themes/data/atom-one-light.js +0 -158
  125. package/dist/themes/data/catppuccin-frappe.d.ts +0 -3
  126. package/dist/themes/data/catppuccin-frappe.js +0 -158
  127. package/dist/themes/data/catppuccin-latte.d.ts +0 -3
  128. package/dist/themes/data/catppuccin-latte.js +0 -158
  129. package/dist/themes/data/catppuccin-macchiato.d.ts +0 -3
  130. package/dist/themes/data/catppuccin-macchiato.js +0 -158
  131. package/dist/themes/data/catppuccin-mocha.d.ts +0 -3
  132. package/dist/themes/data/catppuccin-mocha.js +0 -158
  133. package/dist/themes/data/cyberpunk.d.ts +0 -17
  134. package/dist/themes/data/cyberpunk.js +0 -191
  135. package/dist/themes/data/default.d.ts +0 -3
  136. package/dist/themes/data/default.js +0 -158
  137. package/dist/themes/data/dracula.d.ts +0 -3
  138. package/dist/themes/data/dracula.js +0 -158
  139. package/dist/themes/data/flexoki.d.ts +0 -3
  140. package/dist/themes/data/flexoki.js +0 -158
  141. package/dist/themes/data/gruvbox.d.ts +0 -3
  142. package/dist/themes/data/gruvbox.js +0 -158
  143. package/dist/themes/data/index.d.ts +0 -35
  144. package/dist/themes/data/index.js +0 -64
  145. package/dist/themes/data/monokai.d.ts +0 -3
  146. package/dist/themes/data/monokai.js +0 -158
  147. package/dist/themes/data/nord.d.ts +0 -3
  148. package/dist/themes/data/nord.js +0 -158
  149. package/dist/themes/data/rose-pine-dawn.d.ts +0 -3
  150. package/dist/themes/data/rose-pine-dawn.js +0 -158
  151. package/dist/themes/data/rose-pine-moon.d.ts +0 -3
  152. package/dist/themes/data/rose-pine-moon.js +0 -158
  153. package/dist/themes/data/rose-pine.d.ts +0 -3
  154. package/dist/themes/data/rose-pine.js +0 -158
  155. package/dist/themes/data/solarized-dark.d.ts +0 -3
  156. package/dist/themes/data/solarized-dark.js +0 -158
  157. package/dist/themes/data/solarized-light.d.ts +0 -3
  158. package/dist/themes/data/solarized-light.js +0 -158
  159. package/dist/themes/data/svg-export.d.ts +0 -3
  160. package/dist/themes/data/svg-export.js +0 -158
  161. package/dist/themes/data/textual-ansi.d.ts +0 -3
  162. package/dist/themes/data/textual-ansi.js +0 -158
  163. package/dist/themes/data/textual-dark.d.ts +0 -3
  164. package/dist/themes/data/textual-dark.js +0 -158
  165. package/dist/themes/data/textual-light.d.ts +0 -3
  166. package/dist/themes/data/textual-light.js +0 -158
  167. package/dist/themes/data/tokyo-night.d.ts +0 -3
  168. package/dist/themes/data/tokyo-night.js +0 -158
  169. package/dist/themes/data/types.d.ts +0 -15
  170. package/dist/themes/data/types.js +0 -1
  171. package/dist/themes/palette.d.ts +0 -21
  172. package/dist/themes/palette.js +0 -28
  173. package/dist/themes/ramp.d.ts +0 -83
  174. package/dist/themes/ramp.js +0 -122
  175. package/dist/themes/registry.d.ts +0 -43
  176. package/dist/themes/registry.js +0 -82
  177. package/dist/themes/terminalThemes.d.ts +0 -54
  178. package/dist/themes/terminalThemes.js +0 -68
  179. package/dist/themes/transpose.d.ts +0 -87
  180. package/dist/themes/transpose.js +0 -138
  181. package/dist/widgets/button.d.ts +0 -49
  182. package/dist/widgets/button.js +0 -206
  183. package/dist/widgets/checkbox.d.ts +0 -41
  184. package/dist/widgets/checkbox.js +0 -155
  185. package/dist/widgets/dropdown.d.ts +0 -67
  186. package/dist/widgets/dropdown.js +0 -453
  187. package/dist/widgets/event-router.d.ts +0 -78
  188. package/dist/widgets/event-router.js +0 -622
  189. package/dist/widgets/focus-manager.d.ts +0 -26
  190. package/dist/widgets/focus-manager.js +0 -193
  191. package/dist/widgets/index.d.ts +0 -22
  192. package/dist/widgets/index.js +0 -15
  193. package/dist/widgets/screen.d.ts +0 -98
  194. package/dist/widgets/screen.js +0 -542
  195. package/dist/widgets/slider.d.ts +0 -57
  196. package/dist/widgets/slider.js +0 -270
  197. package/dist/widgets/static-item.d.ts +0 -40
  198. package/dist/widgets/static-item.js +0 -60
  199. package/dist/widgets/text-input.d.ts +0 -274
  200. package/dist/widgets/text-input.js +0 -1162
  201. package/dist/widgets/toggle.d.ts +0 -44
  202. package/dist/widgets/toggle.js +0 -179
  203. package/dist/widgets/types.d.ts +0 -108
  204. package/dist/widgets/types.js +0 -31
  205. package/dist/widgets/widget-base.d.ts +0 -42
  206. 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
- }
@@ -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
- }