@promptctl/rich-js 0.18.1 → 0.20.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 (169) hide show
  1. package/README.md +11 -8
  2. package/dist/core/box.d.ts +3 -1
  3. package/dist/core/box.js +6 -13
  4. package/dist/core/cells.d.ts +61 -28
  5. package/dist/core/cells.js +171 -124
  6. package/dist/core/clock.d.ts +48 -0
  7. package/dist/core/clock.js +42 -0
  8. package/dist/core/color.d.ts +99 -18
  9. package/dist/core/color.js +334 -146
  10. package/dist/core/console.d.ts +38 -7
  11. package/dist/core/console.js +159 -90
  12. package/dist/core/easing.d.ts +86 -0
  13. package/dist/core/easing.js +171 -0
  14. package/dist/core/env.d.ts +16 -0
  15. package/dist/core/env.js +16 -0
  16. package/dist/core/export-html.js +1 -4
  17. package/dist/core/export-lines.d.ts +22 -13
  18. package/dist/core/export-lines.js +37 -19
  19. package/dist/core/export-svg.d.ts +49 -0
  20. package/dist/core/export-svg.js +213 -0
  21. package/dist/core/fnv1a.d.ts +10 -0
  22. package/dist/core/fnv1a.js +17 -0
  23. package/dist/core/highlighter.d.ts +2 -2
  24. package/dist/core/highlighter.js +113 -47
  25. package/dist/core/json.js +1 -1
  26. package/dist/core/markup.d.ts +28 -7
  27. package/dist/core/markup.js +235 -312
  28. package/dist/core/measure.d.ts +3 -3
  29. package/dist/core/measure.js +12 -7
  30. package/dist/core/memo.d.ts +19 -0
  31. package/dist/core/memo.js +34 -0
  32. package/dist/core/noise.d.ts +31 -0
  33. package/dist/core/noise.js +94 -0
  34. package/dist/core/osc8.d.ts +6 -6
  35. package/dist/core/osc8.js +9 -21
  36. package/dist/core/paint.d.ts +107 -0
  37. package/dist/core/paint.js +161 -0
  38. package/dist/core/place.d.ts +1 -1
  39. package/dist/core/place.js +4 -6
  40. package/dist/core/pretty.d.ts +126 -2
  41. package/dist/core/pretty.js +571 -105
  42. package/dist/core/protocol.d.ts +43 -3
  43. package/dist/core/protocol.js +23 -2
  44. package/dist/core/render.d.ts +33 -30
  45. package/dist/core/render.js +43 -83
  46. package/dist/core/segment.d.ts +6 -5
  47. package/dist/core/segment.js +11 -15
  48. package/dist/core/selection.d.ts +12 -0
  49. package/dist/core/selection.js +14 -0
  50. package/dist/core/strip.d.ts +116 -74
  51. package/dist/core/strip.js +266 -217
  52. package/dist/core/style.d.ts +27 -7
  53. package/dist/core/style.js +106 -41
  54. package/dist/core/text.d.ts +43 -16
  55. package/dist/core/text.js +180 -59
  56. package/dist/core/wrap.d.ts +5 -0
  57. package/dist/core/wrap.js +16 -3
  58. package/dist/host/app.d.ts +95 -17
  59. package/dist/host/app.js +201 -61
  60. package/dist/host/index.d.ts +4 -1
  61. package/dist/host/index.js +1 -0
  62. package/dist/host/input.d.ts +84 -0
  63. package/dist/host/input.js +498 -0
  64. package/dist/host/terminal-host.d.ts +15 -2
  65. package/dist/host/terminal-host.js +2 -0
  66. package/dist/index.d.ts +26 -14
  67. package/dist/index.js +17 -7
  68. package/dist/node/prompt.d.ts +13 -0
  69. package/dist/node/prompt.js +30 -9
  70. package/dist/node/save.d.ts +4 -3
  71. package/dist/node/save.js +6 -3
  72. package/dist/node/terminal-host.d.ts +10 -2
  73. package/dist/node/terminal-host.js +7 -0
  74. package/dist/node/traceback.d.ts +2 -1
  75. package/dist/node/traceback.js +3 -39
  76. package/dist/renderables/align.js +2 -9
  77. package/dist/renderables/columns.d.ts +45 -29
  78. package/dist/renderables/columns.js +142 -103
  79. package/dist/renderables/constrain.js +6 -12
  80. package/dist/renderables/effect.d.ts +113 -0
  81. package/dist/renderables/effect.js +182 -0
  82. package/dist/renderables/effects.d.ts +252 -0
  83. package/dist/renderables/effects.js +565 -0
  84. package/dist/renderables/embed.d.ts +94 -31
  85. package/dist/renderables/embed.js +141 -45
  86. package/dist/renderables/group.d.ts +6 -3
  87. package/dist/renderables/group.js +5 -0
  88. package/dist/renderables/layout.d.ts +6 -23
  89. package/dist/renderables/layout.js +24 -113
  90. package/dist/renderables/list-view.d.ts +42 -0
  91. package/dist/renderables/list-view.js +78 -0
  92. package/dist/renderables/live.d.ts +40 -16
  93. package/dist/renderables/live.js +96 -83
  94. package/dist/renderables/markdown.d.ts +16 -2
  95. package/dist/renderables/markdown.js +521 -118
  96. package/dist/renderables/padding.d.ts +22 -1
  97. package/dist/renderables/padding.js +30 -24
  98. package/dist/renderables/panel.d.ts +31 -33
  99. package/dist/renderables/panel.js +137 -141
  100. package/dist/renderables/progress.d.ts +110 -16
  101. package/dist/renderables/progress.js +212 -81
  102. package/dist/renderables/progressBar.d.ts +24 -4
  103. package/dist/renderables/progressBar.js +78 -23
  104. package/dist/renderables/prompt.d.ts +37 -15
  105. package/dist/renderables/prompt.js +125 -85
  106. package/dist/renderables/ratio.d.ts +123 -0
  107. package/dist/renderables/ratio.js +213 -0
  108. package/dist/renderables/rule.d.ts +3 -2
  109. package/dist/renderables/rule.js +50 -52
  110. package/dist/renderables/spinner.d.ts +28 -2
  111. package/dist/renderables/spinner.js +52 -19
  112. package/dist/renderables/status.d.ts +13 -7
  113. package/dist/renderables/status.js +15 -29
  114. package/dist/renderables/syntax.d.ts +37 -7
  115. package/dist/renderables/syntax.js +134 -83
  116. package/dist/renderables/table.d.ts +58 -20
  117. package/dist/renderables/table.js +373 -197
  118. package/dist/renderables/traceback.d.ts +5 -4
  119. package/dist/renderables/traceback.js +239 -68
  120. package/dist/renderables/tree.d.ts +10 -2
  121. package/dist/renderables/tree.js +61 -34
  122. package/dist/renderables/viewport.d.ts +30 -0
  123. package/dist/renderables/viewport.js +61 -7
  124. package/dist/template-bindings/color-funcs.d.ts +31 -9
  125. package/dist/template-bindings/color-funcs.js +40 -14
  126. package/dist/template-bindings/helpers.d.ts +4 -2
  127. package/dist/template-bindings/helpers.js +5 -20
  128. package/dist/template-bindings/index.d.ts +29 -17
  129. package/dist/template-bindings/index.js +36 -20
  130. package/dist/template-bindings/palette-funcs.d.ts +3 -1
  131. package/dist/template-bindings/palette-funcs.js +11 -7
  132. package/dist/template-bindings/style-funcs.js +4 -2
  133. package/dist/themes/buildPalette.d.ts +24 -3
  134. package/dist/themes/buildPalette.js +52 -8
  135. package/dist/themes/colorMath.d.ts +29 -17
  136. package/dist/themes/colorMath.js +74 -48
  137. package/dist/themes/colorRef.d.ts +2 -17
  138. package/dist/themes/colorRef.js +13 -31
  139. package/dist/themes/palette.d.ts +24 -0
  140. package/dist/themes/palette.js +46 -3
  141. package/dist/themes/ramp.d.ts +21 -34
  142. package/dist/themes/ramp.js +23 -42
  143. package/dist/themes/registry.js +39 -28
  144. package/dist/widgets/button.d.ts +2 -6
  145. package/dist/widgets/button.js +9 -40
  146. package/dist/widgets/checkbox.d.ts +2 -5
  147. package/dist/widgets/checkbox.js +9 -31
  148. package/dist/widgets/dropdown.d.ts +2 -5
  149. package/dist/widgets/dropdown.js +17 -51
  150. package/dist/widgets/event-router.d.ts +31 -40
  151. package/dist/widgets/event-router.js +52 -483
  152. package/dist/widgets/index.d.ts +2 -1
  153. package/dist/widgets/ink.d.ts +22 -0
  154. package/dist/widgets/ink.js +31 -0
  155. package/dist/widgets/slider.d.ts +2 -5
  156. package/dist/widgets/slider.js +11 -34
  157. package/dist/widgets/text-input.d.ts +19 -10
  158. package/dist/widgets/text-input.js +156 -147
  159. package/dist/widgets/themed-widget.d.ts +20 -0
  160. package/dist/widgets/themed-widget.js +89 -0
  161. package/dist/widgets/toggle.d.ts +2 -5
  162. package/dist/widgets/toggle.js +8 -31
  163. package/dist/widgets/types.d.ts +8 -25
  164. package/dist/widgets/types.js +8 -0
  165. package/dist/widgets/widget-app.d.ts +24 -7
  166. package/dist/widgets/widget-app.js +35 -18
  167. package/dist/widgets/widget-base.d.ts +2 -0
  168. package/dist/widgets/widget-base.js +9 -2
  169. package/package.json +31 -17
package/dist/index.js CHANGED
@@ -1,13 +1,13 @@
1
1
  // Core primitives
2
- export { cellLen, setCellSize, splitText, chopCells, cellFit, asCellCol, asCodePoint, } from "./core/cells.js";
3
- export { ColorRgba, ColorTable, ColorDepth, ColorSpec, ColorParseError, TerminalTheme, parseRgbHex, parseRgbaHex, blendRgb, resolveColorSystem, detectColorSystem, resolveDestination, STANDARD_TABLE, EIGHT_BIT_TABLE, WINDOWS_TABLE, ANSI_COLOR_NAMES, } from "./core/color.js";
2
+ export { cellLen, setCellSize, splitText, chopCells, cellFit, graphemes, asCellCol, asCodePoint, } from "./core/cells.js";
3
+ export { ColorRgba, ColorTable, ColorDepth, ColorSpec, ColorParseError, TerminalTheme, parseRgbHex, parseRgbaHex, parseHexColor, blendRgb, resolveColorSystem, detectColorSystem, resolveDestination, STANDARD_TABLE, EIGHT_BIT_TABLE, WINDOWS_TABLE, ANSI_COLOR_NAMES, } from "./core/color.js";
4
4
  // Perceptually-uniform color space (manipulation, transposition).
5
5
  export { Oklch, IDENTITY, INVERT_LIGHTNESS, isIdentityKey, } from "./core/oklch.js";
6
6
  // Themes — semantic palettes (distinct from ColorTable quantization LUTs)
7
7
  export { Palette } from "./themes/palette.js";
8
8
  // The one checkpoint turning an author-written colour string — a palette
9
9
  // variable name or a `#RRGGBB` literal — into a colour. [LAW:parse-dont-validate]
10
- export { resolveColorRef, parseHexColor, ColorRefError, HEX_COLOR_RE, } from "./themes/colorRef.js";
10
+ export { resolveColorRef, ColorRefError } from "./themes/colorRef.js";
11
11
  export { buildPalette } from "./themes/buildPalette.js";
12
12
  export { DEFAULT_TERMINAL_THEME, MONOKAI, SVG_EXPORT_THEME, NORD, GRUVBOX, DRACULA, TOKYO_NIGHT, FLEXOKI, CYBERPUNK, CATPPUCCIN_MOCHA, CATPPUCCIN_LATTE, CATPPUCCIN_FRAPPE, CATPPUCCIN_MACCHIATO, SOLARIZED_DARK, SOLARIZED_LIGHT, ROSE_PINE, ROSE_PINE_MOON, ROSE_PINE_DAWN, ATOM_ONE_DARK, ATOM_ONE_LIGHT, TEXTUAL_DARK, TEXTUAL_LIGHT, TEXTUAL_ANSI, } from "./themes/terminalThemes.js";
13
13
  export { getThemePalette, listThemePalettes } from "./themes/registry.js";
@@ -24,9 +24,15 @@ export { transposePalette, themeKeyForRoot, isAnchored, ANCHORED_ROOTS, } from "
24
24
  export { lighten, darken, relativeLuminance, contrastRatio, contrastFor, ensureContrast, ensureDrawn, drawnColour, } from "./themes/colorMath.js";
25
25
  // A number → a color through ordered stops, interpolated in OKLCH — the one
26
26
  // color operation whose input is a measurement rather than a color, so a
27
- // gradient (or, with the `step` easing, a threshold cascade) stays inside the
27
+ // gradient (or, with the `step` ease, a threshold cascade) stays inside the
28
28
  // theme system instead of being computed beside it.
29
- export { ColorRamp, RAMP_EASING_NAMES, parseRampEasing } from "./themes/ramp.js";
29
+ export { ColorRamp } from "./themes/ramp.js";
30
+ // Progress in [0, 1]: phases turn seconds into it, eases reshape it. One
31
+ // vocabulary for ramps and time-based effects alike, CSS's names throughout.
32
+ export { EASES, Phase, cubicBezier, parseEase, steps } from "./core/easing.js";
33
+ // The time source and timer whoever draws frames is handed, and the one
34
+ // parser of the rate it ticks at.
35
+ export { frameRate, systemClock } from "./core/clock.js";
30
36
  export { Style, StyleSyntaxError, StyleStack, Theme, NULL_STYLE, DEFAULT_STYLES, } from "./core/style.js";
31
37
  export { Segment, ControlType, } from "./core/segment.js";
32
38
  export { Box } from "./core/box.js";
@@ -38,7 +44,7 @@ export { Measurement, measureRenderables } from "./core/measure.js";
38
44
  // Text
39
45
  export { Span, RichText } from "./core/text.js";
40
46
  // Strip + Joiner
41
- export { Strip, PowerlineJoiner, SEAM_MIN_DELTA_E, POWERLINE_JOINER_GLYPHS, CapsuleJoiner, PlainJoiner, GradientJoiner, } from "./core/strip.js";
47
+ export { Strip, CellJoiner, SEAM_MIN_DELTA_E, POWERLINE_JOINER, CAPSULE_JOINER, PLAIN_JOINER, gradientJoiner, } from "./core/strip.js";
42
48
  // renderToString — stateless one-shot emission
43
49
  export { renderToString, segmentToString, segmentsToString, } from "./core/render.js";
44
50
  // OSC 8 hyperlink wire grammar — for consumers that read rendered bytes back
@@ -63,9 +69,13 @@ export { SPINNERS, DEFAULT_SPINNER } from "./core/spinnerData.js";
63
69
  export { Console } from "./core/console.js";
64
70
  // Renderables
65
71
  export { Constrain } from "./renderables/constrain.js";
72
+ export { Effected } from "./renderables/effect.js";
73
+ export { EFFECT_CURVES, EFFECT_LIGHTS, SHIMMER_WIDTH, dissolveOut, fadeIn, onColors, pulse, shares, shimmer, sparkle, wheel, } from "./renderables/effects.js";
66
74
  export { Align } from "./renderables/align.js";
67
75
  export { Padding } from "./renderables/padding.js";
68
- export { Viewport, SCROLLBAR } from "./renderables/viewport.js";
76
+ export { Viewport, ViewportPerKey, SCROLLBAR } from "./renderables/viewport.js";
77
+ export { ListView } from "./renderables/list-view.js";
78
+ export { moveSelection } from "./core/selection.js";
69
79
  export { Rule } from "./renderables/rule.js";
70
80
  export { Panel } from "./renderables/panel.js";
71
81
  export { Group } from "./renderables/group.js";
@@ -13,6 +13,19 @@
13
13
  * // or with options:
14
14
  * const choice = await Prompt.ask("Pick one", nodeAsk, { choices: ["a", "b"] });
15
15
  *
16
+ * The prompt is printed by the `Console` it was asked on — the app's, when it
17
+ * passed one as `console`, a new default one otherwise — and then a line is
18
+ * read, as Rich's `Console.input` prints its prompt and calls `input()`. So
19
+ * the prompt is coloured exactly as that console's own output would be and
20
+ * goes to its target: a console on stderr asks on stderr, beside the
21
+ * invalid-answer messages it prints. It goes as one logical line, never broken
22
+ * at the console's width: the terminal wraps it at whatever width it has.
23
+ *
24
+ * readline reads in non-terminal mode, the terminal's own cooked line
25
+ * discipline, as Python's `input()` does without its readline module: in
26
+ * terminal mode readline redraws the line itself, prompt included, and needs a
27
+ * whole `tty.WriteStream` to draw on, which a console's target need not be.
28
+ *
16
29
  * [LAW:single-enforcer] One readline interface per `nodeAsk` call —
17
30
  * created, asked, closed. No shared `rl` across prompts, no listener-leak
18
31
  * pitfalls when callers stack prompts in a loop.
@@ -13,18 +13,39 @@
13
13
  * // or with options:
14
14
  * const choice = await Prompt.ask("Pick one", nodeAsk, { choices: ["a", "b"] });
15
15
  *
16
+ * The prompt is printed by the `Console` it was asked on — the app's, when it
17
+ * passed one as `console`, a new default one otherwise — and then a line is
18
+ * read, as Rich's `Console.input` prints its prompt and calls `input()`. So
19
+ * the prompt is coloured exactly as that console's own output would be and
20
+ * goes to its target: a console on stderr asks on stderr, beside the
21
+ * invalid-answer messages it prints. It goes as one logical line, never broken
22
+ * at the console's width: the terminal wraps it at whatever width it has.
23
+ *
24
+ * readline reads in non-terminal mode, the terminal's own cooked line
25
+ * discipline, as Python's `input()` does without its readline module: in
26
+ * terminal mode readline redraws the line itself, prompt included, and needs a
27
+ * whole `tty.WriteStream` to draw on, which a console's target need not be.
28
+ *
16
29
  * [LAW:single-enforcer] One readline interface per `nodeAsk` call —
17
30
  * created, asked, closed. No shared `rl` across prompts, no listener-leak
18
31
  * pitfalls when callers stack prompts in a loop.
19
32
  */
20
33
  import * as readline from "node:readline";
21
- export const nodeAsk = (prompt) => new Promise((resolve) => {
22
- const rl = readline.createInterface({
23
- input: process.stdin,
24
- output: process.stdout,
25
- });
26
- rl.question(prompt, (answer) => {
27
- rl.close();
28
- resolve(answer);
34
+ export const nodeAsk = (prompt, console) => {
35
+ console.print(prompt, { end: "", softWrap: true });
36
+ return new Promise((resolve) => {
37
+ const rl = readline.createInterface({
38
+ input: process.stdin,
39
+ // [LAW:types-are-the-program] exception: readline types its output as a
40
+ // whole `NodeJS.WritableStream`, and a console's target is only a
41
+ // `ConsoleSink`. With `terminal: false` readline only ever calls its
42
+ // `write`, whatever its `isTTY` says.
43
+ output: console.file,
44
+ terminal: false,
45
+ });
46
+ rl.question("", (answer) => {
47
+ rl.close();
48
+ resolve(answer);
49
+ });
29
50
  });
30
- });
51
+ };
@@ -8,10 +8,11 @@
8
8
  * refuse to resolve `fs` if a consumer ever pulls it in by accident.
9
9
  *
10
10
  * [LAW:single-enforcer] One funnel per format: `saveText` consumes
11
- * `Console.exportText()` and `saveHtml` consumes `Console.exportHtml()`.
12
- * The encoding and recording logic stays on Console; this file is purely
13
- * the IO sink.
11
+ * `Console.exportText()`, `saveHtml` consumes `Console.exportHtml()`, and
12
+ * `saveSvg` consumes `Console.exportSvg()`. The encoding and recording logic
13
+ * stays on Console; this file is purely the IO sink.
14
14
  */
15
15
  import type { Console } from "../core/console.js";
16
16
  export declare function saveText(out: Console, path: string, options?: Parameters<Console["exportText"]>[0]): void;
17
17
  export declare function saveHtml(out: Console, path: string, options?: Parameters<Console["exportHtml"]>[0]): void;
18
+ export declare function saveSvg(out: Console, path: string, options?: Parameters<Console["exportSvg"]>[0]): void;
package/dist/node/save.js CHANGED
@@ -8,9 +8,9 @@
8
8
  * refuse to resolve `fs` if a consumer ever pulls it in by accident.
9
9
  *
10
10
  * [LAW:single-enforcer] One funnel per format: `saveText` consumes
11
- * `Console.exportText()` and `saveHtml` consumes `Console.exportHtml()`.
12
- * The encoding and recording logic stays on Console; this file is purely
13
- * the IO sink.
11
+ * `Console.exportText()`, `saveHtml` consumes `Console.exportHtml()`, and
12
+ * `saveSvg` consumes `Console.exportSvg()`. The encoding and recording logic
13
+ * stays on Console; this file is purely the IO sink.
14
14
  */
15
15
  import { writeFileSync } from "node:fs";
16
16
  export function saveText(out, path, options) {
@@ -19,3 +19,6 @@ export function saveText(out, path, options) {
19
19
  export function saveHtml(out, path, options) {
20
20
  writeFileSync(path, out.exportHtml(options), "utf-8");
21
21
  }
22
+ export function saveSvg(out, path, options) {
23
+ writeFileSync(path, out.exportSvg(options), "utf-8");
24
+ }
@@ -29,6 +29,7 @@
29
29
  */
30
30
  import type { DataHandler, ExitHandler, ResizeHandler, TerminalHost, TerminalSize } from "../host/terminal-host.js";
31
31
  import type { Unsubscribe } from "../core/subscription.js";
32
+ import type { Env } from "../core/env.js";
32
33
  /**
33
34
  * Subset of NodeJS stream API the node host actually depends on. Tests
34
35
  * construct a host over PassThrough / Writable mocks; production wires it
@@ -49,6 +50,12 @@ interface NodeWritable {
49
50
  readonly columns?: number;
50
51
  readonly rows?: number;
51
52
  readonly isTTY?: boolean;
53
+ /**
54
+ * Scroll whatever holds the terminal this stream writes to. Node's own
55
+ * stdout has none: its terminal is the outermost thing. A stand-in whose
56
+ * terminal sits in a page (the docs' example card) has one.
57
+ */
58
+ passWheel?(lines: number): void;
52
59
  }
53
60
  /**
54
61
  * Subset of node's `process` the host hears the program end through and
@@ -79,7 +86,7 @@ export interface NodeTerminalHostOptions {
79
86
  * pass a fixed map so what the host reports does not depend on the shell
80
87
  * that ran them.
81
88
  */
82
- env?: NodeJS.ProcessEnv;
89
+ env?: Env;
83
90
  /**
84
91
  * What the host hears the program end through and suspends it with.
85
92
  * Defaults to `process`; tests pass an emitter that records `kill`.
@@ -90,7 +97,7 @@ export declare class NodeTerminalHost implements TerminalHost {
90
97
  private readonly stdin;
91
98
  private readonly stdout;
92
99
  private readonly process;
93
- readonly env: NodeJS.ProcessEnv;
100
+ readonly env: Env;
94
101
  private readonly dataHandlers;
95
102
  private readonly resizeHandlers;
96
103
  private dataListener;
@@ -100,6 +107,7 @@ export declare class NodeTerminalHost implements TerminalHost {
100
107
  get isTTY(): boolean;
101
108
  get writesToTerminal(): boolean;
102
109
  write(data: Uint8Array | string): void;
110
+ passWheel(lines: number): void;
103
111
  size(): TerminalSize;
104
112
  setRawMode(raw: boolean): void;
105
113
  private applyRawMode;
@@ -65,6 +65,13 @@ export class NodeTerminalHost {
65
65
  write(data) {
66
66
  this.stdout.write(data);
67
67
  }
68
+ // A terminal that reports the wheel to the program has taken it from its
69
+ // own scrollback, and nothing here can hand it back: on node's stdout this
70
+ // does nothing. A stdout whose terminal sits in something that scrolls
71
+ // carries the way out itself.
72
+ passWheel(lines) {
73
+ this.stdout.passWheel?.(lines);
74
+ }
68
75
  size() {
69
76
  return {
70
77
  cols: this.stdout.columns ?? DEFAULT_COLS,
@@ -15,7 +15,8 @@
15
15
  * payload is trusted to be an `Error`: `@types/node` annotates
16
16
  * `uncaughtException` with `Error`, but `throw "boom"` delivers the raw
17
17
  * string, so the annotation is a claim about the common case rather than a
18
- * guarantee. `report` takes `unknown`.
18
+ * guarantee. `report` takes `unknown` and hands it to `Traceback` as it came,
19
+ * which reads a caught value of any shape.
19
20
  *
20
21
  * [LAW:no-ambient-temporal-coupling] The one channel is also what puts the
21
22
  * report after the terminal is handed back. A program holding the alternate
@@ -15,7 +15,8 @@
15
15
  * payload is trusted to be an `Error`: `@types/node` annotates
16
16
  * `uncaughtException` with `Error`, but `throw "boom"` delivers the raw
17
17
  * string, so the annotation is a claim about the common case rather than a
18
- * guarantee. `report` takes `unknown`.
18
+ * guarantee. `report` takes `unknown` and hands it to `Traceback` as it came,
19
+ * which reads a caught value of any shape.
19
20
  *
20
21
  * [LAW:no-ambient-temporal-coupling] The one channel is also what puts the
21
22
  * report after the terminal is handed back. A program holding the alternate
@@ -26,45 +27,8 @@
26
27
  * the terminal. Node's crash sequence owns the order, not the order in which
27
28
  * `installTraceback` and a host happened to add their listeners.
28
29
  */
29
- import { inspect, types } from "node:util";
30
30
  import { Console } from "../core/console.js";
31
31
  import { Traceback } from "../renderables/traceback.js";
32
- /** A payload's own stack, when it carries one, or `""` when it does not. */
33
- function stackOf(reason) {
34
- if (typeof reason !== "object" || reason === null)
35
- return "";
36
- if (!("stack" in reason))
37
- return "";
38
- return typeof reason.stack === "string" ? reason.stack : "";
39
- }
40
- /**
41
- * [LAW:parse-dont-validate] The checkpoint between a crash payload, which node
42
- * allows to be any value at all, and `Traceback`, which renders an `Error`.
43
- * The return type is the proof: nothing downstream re-asks whether the payload
44
- * was throwable, and no raw value can reach the renderable to have `.name` and
45
- * `.message` read off it as `undefined`.
46
- *
47
- * The gate is `isNativeError`, not `instanceof Error`. An error thrown inside a
48
- * `node:vm` context is a real error with real frames, but its prototype comes
49
- * from that realm, so `instanceof` answers false and the frames would be
50
- * discarded by the one component that exists to show them. `isNativeError`
51
- * reads V8's internal error slot and is realm-independent.
52
- *
53
- * Whatever survives that gate is not an error, so it has no call site of its
54
- * own; `stackOf` salvages a stack from a duck-typed thrower and otherwise
55
- * leaves it empty rather than reporting the frames of this function, which
56
- * point into rich-js and describe nothing about the fault. `inspect` renders
57
- * every payload shape, including circular objects, where `String(reason)`
58
- * would collapse them to `[object Object]`.
59
- */
60
- function toError(reason) {
61
- if (types.isNativeError(reason))
62
- return reason;
63
- const error = new Error(inspect(reason));
64
- error.name = "NonError";
65
- error.stack = stackOf(reason);
66
- return error;
67
- }
68
32
  /**
69
33
  * The handler currently registered, or a no-op standing in for "none yet".
70
34
  *
@@ -116,7 +80,7 @@ export function installTraceback(options) {
116
80
  // sink would report `isTTY === false` and silently strip colour.
117
81
  const out = new Console({ stderr: true });
118
82
  out.beginCapture();
119
- out.print(new Traceback(toError(reason), options));
83
+ out.print(new Traceback(reason, options));
120
84
  // [LAW:no-silent-failure] Exit only once the bytes have drained. When
121
85
  // stderr is a pipe (CI logs, `2>&1 | tee`), writes are asynchronous and
122
86
  // an immediate `process.exit(1)` truncates the traceback at the pipe
@@ -3,7 +3,7 @@
3
3
  */
4
4
  import { Segment } from "../core/segment.js";
5
5
  import { Measurement } from "../core/measure.js";
6
- import { isMeasurable, withBoundedWidth } from "../core/protocol.js";
6
+ import { withBoundedWidth } from "../core/protocol.js";
7
7
  import { placeBlock } from "../core/place.js";
8
8
  export class Align {
9
9
  renderable;
@@ -21,13 +21,6 @@ export class Align {
21
21
  }
22
22
  }
23
23
  measure(options) {
24
- if (isMeasurable(this.renderable)) {
25
- const measurement = Measurement.get(options, this.renderable);
26
- return {
27
- minimum: Math.max(1, measurement.minimum),
28
- maximum: measurement.maximum,
29
- };
30
- }
31
- return { minimum: 1, maximum: options.maxWidth };
24
+ return Measurement.get(options, this.renderable);
32
25
  }
33
26
  }
@@ -1,7 +1,14 @@
1
1
  /**
2
2
  * Columns — arranges renderables in a multi-column layout.
3
+ *
4
+ * A port of Rich's `Columns`. What it decides is how many columns, and which
5
+ * item goes in which cell. Everything after that — how wide each column is, how
6
+ * far apart, what `expand` does, how rows are separated — is the grid's: the
7
+ * items are laid out as a `Table.grid`, as the reference lays them out.
8
+ * [LAW:one-source-of-truth] A second copy of that geometry here is how the two
9
+ * drifted: an empty item's column was drawn with no width where the grid draws one cell.
3
10
  */
4
- import { Segment } from "../core/segment.js";
11
+ import type { Segment } from "../core/segment.js";
5
12
  import type { PaddingDimensions } from "./padding.js";
6
13
  import type { Renderable, Measurable, RenderOptions } from "../core/protocol.js";
7
14
  export interface ColumnsOptions {
@@ -17,45 +24,54 @@ export declare class Columns implements Renderable, Measurable {
17
24
  readonly equal: boolean;
18
25
  readonly colWidth: number | undefined;
19
26
  readonly columnFirst: boolean;
20
- readonly gutterWidth: number;
27
+ private readonly padding;
21
28
  constructor(items?: Iterable<unknown>, options?: ColumnsOptions);
22
29
  render(rawOptions: RenderOptions): Iterable<Segment>;
30
+ measure(rawOptions: RenderOptions): {
31
+ minimum: number;
32
+ maximum: number;
33
+ };
23
34
  /**
24
- * The requested width divided into columns, once.
35
+ * The items at the width offered, as the reference's grid: `_layout`'s
36
+ * cells in a `Table.grid`.
37
+ *
38
+ * [LAW:single-enforcer] `render` draws this and `measure` reports its width,
39
+ * so the two cannot disagree about how wide a Columns is.
25
40
  *
26
- * [LAW:single-enforcer] The three modes disagree only about how many columns
27
- * there are and how wide each one is, so they disagree in one place. `render`
28
- * lays out against this division and `_naturalWidth` is the width at which it
29
- * comes out as one row of every item.
41
+ * A declared `width` is each column's natural width rather than a Table
42
+ * column `width`, which this port never stretches: the reference's grid
43
+ * stretches a Columns' declared width under `expand` like any other.
44
+ * `equal` holds every item to the widest, Rich's `Constrain`; without it
45
+ * the cap is `undefined` and holds nothing.
30
46
  */
31
- private _divide;
47
+ private _grid;
32
48
  /**
33
- * The widest any one item wants to be.
49
+ * How many columns fit the width offered, and which item goes in each cell,
50
+ * decided the way Rich's `Columns` decides them.
34
51
  *
35
- * An item that cannot measure itself wants the offer, which is what `Panel`,
36
- * `Padding`, `Layout` and `Tree` all answer for the same case — and under an
37
- * unbounded offer that is `Infinity`, so `withBoundedWidth` throws and says
38
- * the request was unanswerable. Counting it as one cell instead reported a
39
- * natural width of 1, which resolved an unbounded offer to a single column and
40
- * cropped forty cells of content down to `"x"` with no error at all.
52
+ * Every item carries a width. It is what the item measures, or the declared
53
+ * `width` for all of them. `equal` counts every item as the widest when
54
+ * choosing how many columns fit, and the grid still sizes each column by
55
+ * what is in it — which is why an `equal` layout is not a grid of equal
56
+ * columns, in the reference or here.
57
+ *
58
+ * The count starts at one column per item and drops whenever the columns
59
+ * filled so far, and the gaps between them, pass the width offered. Two
60
+ * columns stand the wider of the padding's left and right sides apart, which
61
+ * is what the collapsed grid leaves between them. One departure from the
62
+ * reference: a declared `width` is searched the same way rather than by
63
+ * `max_width // (width + gap)`, which laid out empty trailing columns and
64
+ * charged the last column a gap it does not have.
41
65
  */
42
- private _itemWidth;
66
+ private _layout;
43
67
  /**
44
68
  * The declared column width, bounded by the width offered.
45
69
  *
46
- * [LAW:single-enforcer] A declared width is what a column asks for, not what
47
- * it takes — the contract `Table._outerWidth` keeps for a declared table
48
- * width. Read by `_divide` for render and by `_naturalWidth` for measure, so
49
- * the two answer from one number: laid out at the raw declared width, a
50
- * six-cell column offered three emitted six-cell lines while `measure`
51
- * reported three, and the terminal's soft wrap took the frame of everything
52
- * printed after it.
70
+ * A declared width is what a column asks for, not what it takes — the
71
+ * contract `Table._outerWidth` keeps for a declared table width. Laid out at
72
+ * the raw declared width, a six-cell column offered three emitted six-cell
73
+ * lines while `measure` reported three, and the terminal's soft wrap took the
74
+ * frame of everything printed after it.
53
75
  */
54
76
  private _declaredWidth;
55
- /** The width at which every item sits on one row: n columns and the gutters between them. */
56
- private _naturalWidth;
57
- measure(rawOptions: RenderOptions): {
58
- minimum: number;
59
- maximum: number;
60
- };
61
77
  }