@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.
- package/README.md +14 -27
- 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 -229
- package/dist/core/color.js +0 -992
- package/dist/core/console.d.ts +0 -142
- package/dist/core/console.js +0 -477
- 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 -69
- package/dist/core/render.js +0 -140
- 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 -139
- package/dist/core/strip.js +0 -302
- package/dist/core/style.d.ts +0 -149
- package/dist/core/style.js +0 -741
- 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 -542
- 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
package/dist/core/protocol.js
DELETED
|
@@ -1,101 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Rendering protocol interfaces — Renderable, Measurable, RenderOptions.
|
|
3
|
-
* [LAW:one-source-of-truth] These interfaces are the single authority for the rendering contract.
|
|
4
|
-
*/
|
|
5
|
-
import { cellCount } from "./cells.js";
|
|
6
|
-
import { DEFAULT_THEME } from "./style.js";
|
|
7
|
-
/**
|
|
8
|
-
* The style a `string | Style` stands for in this render.
|
|
9
|
-
*
|
|
10
|
-
* [LAW:single-enforcer] Every renderable resolves a style name here, at render
|
|
11
|
-
* time, because only the render knows whose theme it is drawing for. Resolved
|
|
12
|
-
* when a renderable was built instead, a name was fixed to the built-in
|
|
13
|
-
* defaults before any `Console` could offer its theme, and `new Console({
|
|
14
|
-
* theme })` changed nothing.
|
|
15
|
-
*/
|
|
16
|
-
export function getStyle(options, style) {
|
|
17
|
-
return (options.theme ?? DEFAULT_THEME).resolve(style);
|
|
18
|
-
}
|
|
19
|
-
/**
|
|
20
|
-
* The options a renderable should work from: the caller's, with `maxWidth`
|
|
21
|
-
* parsed into an actual count of cells.
|
|
22
|
-
*
|
|
23
|
-
* [LAW:parse-dont-validate] The stamp has to travel, which is the whole reason
|
|
24
|
-
* this returns options rather than a number. Parsing `options.maxWidth` into a
|
|
25
|
-
* local leaves the caller's raw value sitting in `options` for whatever the
|
|
26
|
-
* renderable forwards it to — and every renderable forwards it, to a child's
|
|
27
|
-
* `render`, to `Measurement.get`, to a nested layout. `Columns` was fixed that
|
|
28
|
-
* way first and still threw `Invalid array length` on a NaN width, because it
|
|
29
|
-
* handed the unparsed original to `Measurement.get` and got a NaN column count
|
|
30
|
-
* back. Replace the field, and nothing downstream can see the number the
|
|
31
|
-
* caller actually wrote.
|
|
32
|
-
*
|
|
33
|
-
* [LAW:single-enforcer] This is the width checkpoint for the render contract.
|
|
34
|
-
* There is no second one: a renderable that re-derives its own answer is how
|
|
35
|
-
* `Panel`, `Table`, `Tree`, `Columns` and `Layout` came to disagree about what
|
|
36
|
-
* a NaN width means — one collapsed to a cell of garbage, two threw, one
|
|
37
|
-
* ignored the request and emitted its full natural width.
|
|
38
|
-
*/
|
|
39
|
-
export function withCellWidth(options) {
|
|
40
|
-
return { ...options, maxWidth: cellCount(options.maxWidth) };
|
|
41
|
-
}
|
|
42
|
-
/**
|
|
43
|
-
* The options a renderable should *lay out against*: `withCellWidth`, with an
|
|
44
|
-
* unbounded offer resolved to the renderable's own natural width.
|
|
45
|
-
*
|
|
46
|
-
* [LAW:parse-dont-validate] `cellCount` cannot finish the job alone. It floors
|
|
47
|
-
* a negative width and NaN to zero from the number alone, but `Infinity` is not
|
|
48
|
-
* a quantity it can floor — the only finite answer is "as wide as this
|
|
49
|
-
* renderable's content wants", which is a question about the renderable and not
|
|
50
|
-
* about the number. So the parse is completed here, where the renderable is in
|
|
51
|
-
* hand, and the stamped options travel exactly as `withCellWidth`'s do.
|
|
52
|
-
*
|
|
53
|
-
* [LAW:single-enforcer] One checkpoint for the whole rule, not one per
|
|
54
|
-
* renderable. `Panel`, `Padding`, `Columns` and `Layout` each expand into the
|
|
55
|
-
* width they are offered, and each independently reached `" ".repeat(Infinity)`
|
|
56
|
-
* — Columns twice over, since its column *count* is derived from the width too
|
|
57
|
-
* and `new Array(Infinity)` throws a different error again.
|
|
58
|
-
*
|
|
59
|
-
* The natural width comes from `measure`, which every one of them already
|
|
60
|
-
* implements, and the recursion terminates because `measure` reports content
|
|
61
|
-
* rather than the offer: `Panel.measure({maxWidth: Infinity})` is `{9, 9}` for
|
|
62
|
-
* nine cells of content and frame, not `{9, Infinity}`.
|
|
63
|
-
*
|
|
64
|
-
* So this belongs at the top of `render` and never at the top of `measure`:
|
|
65
|
-
* `measure` is the method being asked, and asking it through here would ask it
|
|
66
|
-
* with itself. `measure` parses with `withCellWidth` and reports a natural
|
|
67
|
-
* width of its own — that is the half of the contract that makes this half work.
|
|
68
|
-
*
|
|
69
|
-
* A `maximum` of `Infinity` means the renderable genuinely cannot answer. Two
|
|
70
|
-
* ways in: it wraps a `Renderable` with no `measure`, so nothing in the tree
|
|
71
|
-
* knows how wide the content wants to be; or something inside it asked for an
|
|
72
|
-
* unbounded width of its own, as a `Table` column declared
|
|
73
|
-
* `{ ratio: Infinity }` does. [LAW:no-silent-failure] Both are unanswerable
|
|
74
|
-
* rather than zero, and saying so names the cause — where `String.repeat` and
|
|
75
|
-
* `new Array` only ever name their own argument.
|
|
76
|
-
*/
|
|
77
|
-
export function withBoundedWidth(options, self) {
|
|
78
|
-
const parsed = withCellWidth(options);
|
|
79
|
-
if (Number.isFinite(parsed.maxWidth))
|
|
80
|
-
return parsed;
|
|
81
|
-
const natural = self.measure(parsed).maximum;
|
|
82
|
-
if (!Number.isFinite(natural)) {
|
|
83
|
-
throw new RangeError("maxWidth is unbounded and this renderable has no natural width to fall " +
|
|
84
|
-
"back on: its content does not implement measure(), or something inside " +
|
|
85
|
-
"it asked for an unbounded width of its own. Render it at a finite " +
|
|
86
|
-
"width, or give the content a measure() that reports a finite maximum.");
|
|
87
|
-
}
|
|
88
|
-
return { ...parsed, maxWidth: cellCount(natural) };
|
|
89
|
-
}
|
|
90
|
-
export function isRenderable(obj) {
|
|
91
|
-
return (typeof obj === "object" &&
|
|
92
|
-
obj !== null &&
|
|
93
|
-
"render" in obj &&
|
|
94
|
-
typeof obj.render === "function");
|
|
95
|
-
}
|
|
96
|
-
export function isMeasurable(obj) {
|
|
97
|
-
return (typeof obj === "object" &&
|
|
98
|
-
obj !== null &&
|
|
99
|
-
"measure" in obj &&
|
|
100
|
-
typeof obj.measure === "function");
|
|
101
|
-
}
|
package/dist/core/render.d.ts
DELETED
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* `renderToString` — stateless one-shot emission of a `Renderable` to a string
|
|
3
|
-
* of ANSI-encoded text. Pure function: same inputs produce byte-identical
|
|
4
|
-
* output. Does not write to `process.stdout` and does not require a `Console`
|
|
5
|
-
* instance.
|
|
6
|
-
*
|
|
7
|
-
* Note on `colorSystem: "auto"`: the `"auto"` spec resolves via
|
|
8
|
-
* `detectColorSystem`, which reads `process.env` and `process.stdout?.isTTY`
|
|
9
|
-
* by default. Callers that want a fully deterministic render must either pass
|
|
10
|
-
* an explicit `ColorDepth` enum / non-`"auto"` spec, or supply `env` and
|
|
11
|
-
* `isTTY` in the options so detection does not consult ambient process state.
|
|
12
|
-
*
|
|
13
|
-
* [LAW:single-enforcer] The Segment-to-ANSI conversion lives in `segmentsToString`
|
|
14
|
-
* and is the single way segments become wire bytes. `Console._writeSegments`,
|
|
15
|
-
* `Live.refresh`, and `segmentToString` all delegate here, so terminal output,
|
|
16
|
-
* live frames, string export, and single-segment encoding agree by construction.
|
|
17
|
-
*
|
|
18
|
-
* [LAW:dataflow-not-control-flow] The same pipeline runs every render: collect
|
|
19
|
-
* non-control non-empty pieces, partition by SGR-codes (SGR-runs), partition
|
|
20
|
-
* each run by link (link-runs), emit one SGR open/close per run with link
|
|
21
|
-
* open/close pairs sitting inside. `colorSystem === null` is data: every piece
|
|
22
|
-
* is collected with empty SGR-codes *and* no link, so the same pipeline emits
|
|
23
|
-
* plain text — no SGR wraps and no OSC 8 hyperlink wraps. NO_COLOR therefore
|
|
24
|
-
* strips *all* ANSI escape emission, not just colors.
|
|
25
|
-
*
|
|
26
|
-
* [LAW:types-are-the-program] Adjacent same-style segments share an SGR wrap
|
|
27
|
-
* because the SGR-codes string is the same group key for both — the
|
|
28
|
-
* partitioning shape (data) encodes the byte structure; the emit walk is a
|
|
29
|
-
* mechanical fold over it.
|
|
30
|
-
*/
|
|
31
|
-
import { ColorDepth } from "./color.js";
|
|
32
|
-
import type { Segment } from "./segment.js";
|
|
33
|
-
import type { Renderable } from "./protocol.js";
|
|
34
|
-
export interface RenderToStringOptions {
|
|
35
|
-
/** Cell width to render into. Default 80. */
|
|
36
|
-
width?: number;
|
|
37
|
-
/**
|
|
38
|
-
* Color encoding to emit. Accepts a string spec (`"auto"`, `"truecolor"`,
|
|
39
|
-
* `"256"`, `"ansi"`, `"none"`), a `ColorDepth` enum value, or `null` to
|
|
40
|
-
* strip *all* ANSI escape emission — SGR colors/attributes *and* OSC 8
|
|
41
|
-
* hyperlinks. Default truecolor.
|
|
42
|
-
*/
|
|
43
|
-
colorSystem?: string | ColorDepth | null;
|
|
44
|
-
/**
|
|
45
|
-
* Environment to consult when `colorSystem` is `"auto"`. Defaults to
|
|
46
|
-
* `process.env`. Pass an explicit value to keep rendering deterministic.
|
|
47
|
-
*/
|
|
48
|
-
env?: NodeJS.ProcessEnv;
|
|
49
|
-
/**
|
|
50
|
-
* Whether output is going to a TTY when `colorSystem` is `"auto"`. Defaults
|
|
51
|
-
* to `process.stdout?.isTTY`. Pass an explicit value to keep rendering
|
|
52
|
-
* deterministic.
|
|
53
|
-
*/
|
|
54
|
-
isTTY?: boolean;
|
|
55
|
-
/** When true, forces `colorSystem` to `null` regardless of the explicit value. */
|
|
56
|
-
noColor?: boolean;
|
|
57
|
-
}
|
|
58
|
-
/**
|
|
59
|
-
* Encodes a single segment as ANSI bytes. Equivalent to
|
|
60
|
-
* `segmentsToString([segment], colorSystem)` — same SGR / OSC 8 layout.
|
|
61
|
-
*/
|
|
62
|
-
export declare function segmentToString(segment: Segment, colorSystem: ColorDepth | null): string;
|
|
63
|
-
/**
|
|
64
|
-
* Encodes a sequence of segments as ANSI bytes, coalescing adjacent
|
|
65
|
-
* same-SGR segments under a single SGR open/close pair, with OSC 8 link
|
|
66
|
-
* pairs nested inside per same-link sub-run.
|
|
67
|
-
*/
|
|
68
|
-
export declare function segmentsToString(segments: Iterable<Segment>, colorSystem: ColorDepth | null): string;
|
|
69
|
-
export declare function renderToString(renderable: Renderable, options?: RenderToStringOptions): string;
|
package/dist/core/render.js
DELETED
|
@@ -1,140 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* `renderToString` — stateless one-shot emission of a `Renderable` to a string
|
|
3
|
-
* of ANSI-encoded text. Pure function: same inputs produce byte-identical
|
|
4
|
-
* output. Does not write to `process.stdout` and does not require a `Console`
|
|
5
|
-
* instance.
|
|
6
|
-
*
|
|
7
|
-
* Note on `colorSystem: "auto"`: the `"auto"` spec resolves via
|
|
8
|
-
* `detectColorSystem`, which reads `process.env` and `process.stdout?.isTTY`
|
|
9
|
-
* by default. Callers that want a fully deterministic render must either pass
|
|
10
|
-
* an explicit `ColorDepth` enum / non-`"auto"` spec, or supply `env` and
|
|
11
|
-
* `isTTY` in the options so detection does not consult ambient process state.
|
|
12
|
-
*
|
|
13
|
-
* [LAW:single-enforcer] The Segment-to-ANSI conversion lives in `segmentsToString`
|
|
14
|
-
* and is the single way segments become wire bytes. `Console._writeSegments`,
|
|
15
|
-
* `Live.refresh`, and `segmentToString` all delegate here, so terminal output,
|
|
16
|
-
* live frames, string export, and single-segment encoding agree by construction.
|
|
17
|
-
*
|
|
18
|
-
* [LAW:dataflow-not-control-flow] The same pipeline runs every render: collect
|
|
19
|
-
* non-control non-empty pieces, partition by SGR-codes (SGR-runs), partition
|
|
20
|
-
* each run by link (link-runs), emit one SGR open/close per run with link
|
|
21
|
-
* open/close pairs sitting inside. `colorSystem === null` is data: every piece
|
|
22
|
-
* is collected with empty SGR-codes *and* no link, so the same pipeline emits
|
|
23
|
-
* plain text — no SGR wraps and no OSC 8 hyperlink wraps. NO_COLOR therefore
|
|
24
|
-
* strips *all* ANSI escape emission, not just colors.
|
|
25
|
-
*
|
|
26
|
-
* [LAW:types-are-the-program] Adjacent same-style segments share an SGR wrap
|
|
27
|
-
* because the SGR-codes string is the same group key for both — the
|
|
28
|
-
* partitioning shape (data) encodes the byte structure; the emit walk is a
|
|
29
|
-
* mechanical fold over it.
|
|
30
|
-
*/
|
|
31
|
-
import { ColorDepth, resolveColorSystem } from "./color.js";
|
|
32
|
-
import { OSC8_CLOSE, osc8Open } from "./osc8.js";
|
|
33
|
-
const DEFAULT_WIDTH = 80;
|
|
34
|
-
function segmentToPiece(segment, colorSystem) {
|
|
35
|
-
if (segment.isControl)
|
|
36
|
-
return undefined;
|
|
37
|
-
if (segment.text.length === 0)
|
|
38
|
-
return undefined;
|
|
39
|
-
const style = segment.style;
|
|
40
|
-
if (!style || style.isNull || colorSystem === null) {
|
|
41
|
-
return { text: segment.text, sgrCodes: "", link: undefined };
|
|
42
|
-
}
|
|
43
|
-
return {
|
|
44
|
-
text: segment.text,
|
|
45
|
-
sgrCodes: style.toSgrCodes(colorSystem),
|
|
46
|
-
link: style.link,
|
|
47
|
-
};
|
|
48
|
-
}
|
|
49
|
-
/**
|
|
50
|
-
* Encodes a single segment as ANSI bytes. Equivalent to
|
|
51
|
-
* `segmentsToString([segment], colorSystem)` — same SGR / OSC 8 layout.
|
|
52
|
-
*/
|
|
53
|
-
export function segmentToString(segment, colorSystem) {
|
|
54
|
-
return segmentsToString([segment], colorSystem);
|
|
55
|
-
}
|
|
56
|
-
/**
|
|
57
|
-
* Encodes a sequence of segments as ANSI bytes, coalescing adjacent
|
|
58
|
-
* same-SGR segments under a single SGR open/close pair, with OSC 8 link
|
|
59
|
-
* pairs nested inside per same-link sub-run.
|
|
60
|
-
*/
|
|
61
|
-
export function segmentsToString(segments, colorSystem) {
|
|
62
|
-
const pieces = [];
|
|
63
|
-
for (const s of segments) {
|
|
64
|
-
const p = segmentToPiece(s, colorSystem);
|
|
65
|
-
if (p)
|
|
66
|
-
pieces.push(p);
|
|
67
|
-
}
|
|
68
|
-
if (pieces.length === 0)
|
|
69
|
-
return "";
|
|
70
|
-
// [LAW:dataflow-not-control-flow] One linear chunk accumulator for the
|
|
71
|
-
// entire output; SGR / OSC 8 boundaries and piece texts all push into it
|
|
72
|
-
// in order. No per-link-run intermediate string, no quadratic `+=` chains.
|
|
73
|
-
const parts = [];
|
|
74
|
-
let i = 0;
|
|
75
|
-
while (i < pieces.length) {
|
|
76
|
-
const sgr = pieces[i].sgrCodes;
|
|
77
|
-
let j = i + 1;
|
|
78
|
-
while (j < pieces.length && pieces[j].sgrCodes === sgr)
|
|
79
|
-
j++;
|
|
80
|
-
if (sgr.length > 0)
|
|
81
|
-
parts.push(`\x1b[${sgr}m`);
|
|
82
|
-
let k = i;
|
|
83
|
-
while (k < j) {
|
|
84
|
-
const link = pieces[k].link;
|
|
85
|
-
let l = k + 1;
|
|
86
|
-
while (l < j && pieces[l].link === link)
|
|
87
|
-
l++;
|
|
88
|
-
if (link) {
|
|
89
|
-
parts.push(osc8Open(link));
|
|
90
|
-
for (let m = k; m < l; m++)
|
|
91
|
-
parts.push(pieces[m].text);
|
|
92
|
-
parts.push(OSC8_CLOSE);
|
|
93
|
-
}
|
|
94
|
-
else {
|
|
95
|
-
for (let m = k; m < l; m++)
|
|
96
|
-
parts.push(pieces[m].text);
|
|
97
|
-
}
|
|
98
|
-
k = l;
|
|
99
|
-
}
|
|
100
|
-
if (sgr.length > 0)
|
|
101
|
-
parts.push("\x1b[0m");
|
|
102
|
-
i = j;
|
|
103
|
-
}
|
|
104
|
-
return parts.join("");
|
|
105
|
-
}
|
|
106
|
-
export function renderToString(renderable, options) {
|
|
107
|
-
const width = options?.width ?? DEFAULT_WIDTH;
|
|
108
|
-
// [LAW:dataflow-not-control-flow] Distinguish "explicit null" from the
|
|
109
|
-
// defaulted case. `??` would collapse `null` into the default; `in` would
|
|
110
|
-
// accept an explicit `undefined` value as authoritative. Only an explicit
|
|
111
|
-
// `null` (or `noColor: true`) strips color; everything else — absent field,
|
|
112
|
-
// explicit `undefined` — falls back to truecolor.
|
|
113
|
-
// [LAW:single-enforcer] String specs route through `resolveColorSystem`;
|
|
114
|
-
// enum/null pass through unchanged.
|
|
115
|
-
const rawSpec = options?.colorSystem;
|
|
116
|
-
// [LAW:dataflow-not-control-flow] Build the detect options unconditionally;
|
|
117
|
-
// `resolveColorSystem` ignores them for non-`"auto"` specs.
|
|
118
|
-
const detectOptions = {};
|
|
119
|
-
if (options?.env !== undefined)
|
|
120
|
-
detectOptions.env = options.env;
|
|
121
|
-
if (options?.isTTY !== undefined)
|
|
122
|
-
detectOptions.isTTY = options.isTTY;
|
|
123
|
-
const colorSystem = options?.noColor
|
|
124
|
-
? null
|
|
125
|
-
: rawSpec === undefined
|
|
126
|
-
? ColorDepth.TRUECOLOR
|
|
127
|
-
: typeof rawSpec === "string"
|
|
128
|
-
? resolveColorSystem(rawSpec, detectOptions)
|
|
129
|
-
: rawSpec;
|
|
130
|
-
const renderOptions = {
|
|
131
|
-
maxWidth: width,
|
|
132
|
-
isTerminal: false,
|
|
133
|
-
encoding: "utf-8",
|
|
134
|
-
asciiOnly: false,
|
|
135
|
-
// [LAW:one-source-of-truth] The depth the segments below are encoded at,
|
|
136
|
-
// so a renderable measures what this very call will draw.
|
|
137
|
-
colorSystem,
|
|
138
|
-
};
|
|
139
|
-
return segmentsToString(renderable.render(renderOptions), colorSystem);
|
|
140
|
-
}
|
package/dist/core/segment.d.ts
DELETED
|
@@ -1,142 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Segment — the atomic rendering unit. Every piece of styled terminal output
|
|
3
|
-
* is represented as a Segment: (text, style?, control?).
|
|
4
|
-
*/
|
|
5
|
-
import { type CellCol } from "./cells.js";
|
|
6
|
-
import { Style } from "./style.js";
|
|
7
|
-
export declare enum ControlType {
|
|
8
|
-
BELL = "bell",
|
|
9
|
-
CARRIAGE_RETURN = "carriage_return",
|
|
10
|
-
HOME = "home",
|
|
11
|
-
CLEAR = "clear",
|
|
12
|
-
SHOW_CURSOR = "show_cursor",
|
|
13
|
-
HIDE_CURSOR = "hide_cursor",
|
|
14
|
-
ENABLE_ALT_SCREEN = "enable_alt_screen",
|
|
15
|
-
DISABLE_ALT_SCREEN = "disable_alt_screen",
|
|
16
|
-
CURSOR_UP = "cursor_up",
|
|
17
|
-
CURSOR_DOWN = "cursor_down",
|
|
18
|
-
CURSOR_FORWARD = "cursor_forward",
|
|
19
|
-
CURSOR_BACKWARD = "cursor_backward",
|
|
20
|
-
CURSOR_MOVE_TO_COLUMN = "cursor_move_to_column",
|
|
21
|
-
CURSOR_MOVE_TO = "cursor_move_to",
|
|
22
|
-
ERASE_IN_LINE = "erase_in_line",
|
|
23
|
-
SET_WINDOW_TITLE = "set_window_title"
|
|
24
|
-
}
|
|
25
|
-
export type ControlCode = [ControlType, ...unknown[]];
|
|
26
|
-
export declare class Segment {
|
|
27
|
-
readonly text: string;
|
|
28
|
-
readonly style: Style | undefined;
|
|
29
|
-
readonly control: ControlCode[] | undefined;
|
|
30
|
-
constructor(text: string, style?: Style, control?: ControlCode[]);
|
|
31
|
-
get cellLength(): number;
|
|
32
|
-
get hasText(): boolean;
|
|
33
|
-
get isControl(): boolean;
|
|
34
|
-
/**
|
|
35
|
-
* Splits at a cell position. Returns [left, right].
|
|
36
|
-
*/
|
|
37
|
-
splitCells(position: CellCol): [Segment, Segment];
|
|
38
|
-
private static _line;
|
|
39
|
-
static line(): Segment;
|
|
40
|
-
/**
|
|
41
|
-
* Yields segments with combined styles.
|
|
42
|
-
*/
|
|
43
|
-
static applyStyle(segments: Iterable<Segment>, style?: Style, postStyle?: Style): Iterable<Segment>;
|
|
44
|
-
/**
|
|
45
|
-
* Filters segments by control status.
|
|
46
|
-
*/
|
|
47
|
-
static filterControl(segments: Iterable<Segment>, isControl: boolean): Iterable<Segment>;
|
|
48
|
-
/**
|
|
49
|
-
* Splits segments at newlines. Yields arrays of segments per line.
|
|
50
|
-
*/
|
|
51
|
-
static splitLines(segments: Iterable<Segment>): Segment[][];
|
|
52
|
-
/**
|
|
53
|
-
* Adjusts a line of segments to exactly `width` cells.
|
|
54
|
-
*/
|
|
55
|
-
static adjustLineLength(line: Segment[], width: number, style?: Style, pad?: boolean): Segment[];
|
|
56
|
-
/**
|
|
57
|
-
* Crops a renderable's output so no line exceeds `width`, leaving short lines
|
|
58
|
-
* alone and the line structure exactly as it arrived.
|
|
59
|
-
*
|
|
60
|
-
* [LAW:single-enforcer] `adjustLineLength` is where a width is enforced, but
|
|
61
|
-
* it takes one line and a container holding arbitrary content has a stream of
|
|
62
|
-
* them. Without this step the container has to trust content to honour the
|
|
63
|
-
* offer, and content that ignores it overflows the region — which is how a
|
|
64
|
-
* `Layout` leaf and a `Tree` label came to emit forty cells into a one-cell
|
|
65
|
-
* offer while `Panel` and `Padding`, which split and adjust, did not.
|
|
66
|
-
*
|
|
67
|
-
* One segment in, one segment out, shortening text and nothing else — so the
|
|
68
|
-
* line structure that arrives is the line structure that leaves. Two earlier
|
|
69
|
-
* shapes of this both decided how many lines there were and both were wrong:
|
|
70
|
-
* built on `splitLines`, which cannot tell `"abc"` from `"abc\n"`, it invented
|
|
71
|
-
* a trailing newline and dropped the row of an empty `Tree` label entirely,
|
|
72
|
-
* running its guides into the next row; rebuilt to accumulate lines, it
|
|
73
|
-
* dropped the zero-width-but-present line `RichText` emits at an offer of 0,
|
|
74
|
-
* and a row split rendered nothing at all. A crop shortens; it does not count.
|
|
75
|
-
*
|
|
76
|
-
* A wide glyph the edge cuts through leaves a space in the cell it would have
|
|
77
|
-
* half-filled, so a cropped line still reaches the edge — the reference's
|
|
78
|
-
* `set_cell_size`, which `splitText` already is. An unbounded `width` crops
|
|
79
|
-
* nothing, and a segment the crop did not change leaves as itself.
|
|
80
|
-
*/
|
|
81
|
-
static cropLines(segments: Iterable<Segment>, width: number): Iterable<Segment>;
|
|
82
|
-
/**
|
|
83
|
-
* Returns total cell width of a line. Ignores control segments.
|
|
84
|
-
*/
|
|
85
|
-
static getLineLength(line: Segment[]): number;
|
|
86
|
-
/**
|
|
87
|
-
* Returns [width, height] of a set of lines.
|
|
88
|
-
*/
|
|
89
|
-
static getShape(lines: Segment[][]): [number, number];
|
|
90
|
-
/**
|
|
91
|
-
* Composes cells side by side into full lines. Each cell contributes its
|
|
92
|
-
* own lines cropped/padded to its declared width; a cell shorter than the
|
|
93
|
-
* tallest is filled with blank space so the grid stays aligned. Adjacent
|
|
94
|
-
* cells are separated by a gutter of spaces, and each merged row ends with a
|
|
95
|
-
* line break. This is the single side-by-side merge used by every horizontal
|
|
96
|
-
* layout (Columns grid rows, Layout row splits).
|
|
97
|
-
*/
|
|
98
|
-
static mergeHorizontal(cells: readonly {
|
|
99
|
-
readonly lines: Segment[][];
|
|
100
|
-
readonly width: number;
|
|
101
|
-
}[], gutter?: number): Iterable<Segment>;
|
|
102
|
-
/**
|
|
103
|
-
* Merges contiguous segments with the same style.
|
|
104
|
-
*/
|
|
105
|
-
static simplify(segments: Iterable<Segment>): Iterable<Segment>;
|
|
106
|
-
/**
|
|
107
|
-
* Yields segments with links removed.
|
|
108
|
-
*/
|
|
109
|
-
static stripLinks(segments: Iterable<Segment>): Iterable<Segment>;
|
|
110
|
-
/**
|
|
111
|
-
* Yields segments with all styles removed.
|
|
112
|
-
*/
|
|
113
|
-
static stripStyles(segments: Iterable<Segment>): Iterable<Segment>;
|
|
114
|
-
/**
|
|
115
|
-
* Yields segments with colors removed but attributes preserved.
|
|
116
|
-
*/
|
|
117
|
-
static removeColor(segments: Iterable<Segment>): Iterable<Segment>;
|
|
118
|
-
/**
|
|
119
|
-
* Divides segments at cell positions. Yields arrays of segments for each section.
|
|
120
|
-
*/
|
|
121
|
-
static divide(segments: Segment[], cuts: number[]): Segment[][];
|
|
122
|
-
/**
|
|
123
|
-
* Pads below to fill height.
|
|
124
|
-
*/
|
|
125
|
-
static alignTop(lines: Segment[][], width: number, height: number, style: Style): Segment[][];
|
|
126
|
-
/**
|
|
127
|
-
* Pads above to fill height. Content at bottom.
|
|
128
|
-
*/
|
|
129
|
-
static alignBottom(lines: Segment[][], width: number, height: number, style: Style): Segment[][];
|
|
130
|
-
/**
|
|
131
|
-
* Pads above and below. Content in middle.
|
|
132
|
-
*/
|
|
133
|
-
static alignMiddle(lines: Segment[][], width: number, height: number, style: Style): Segment[][];
|
|
134
|
-
/**
|
|
135
|
-
* Forces lines to exactly width x height.
|
|
136
|
-
*/
|
|
137
|
-
static setShape(lines: Segment[][], width: number, height: number, style?: Style): Segment[][];
|
|
138
|
-
/**
|
|
139
|
-
* Splits segments into lines and adjusts each to exactly `width` cells.
|
|
140
|
-
*/
|
|
141
|
-
static splitAndCropLines(segments: Iterable<Segment>, width: number, pad?: boolean, includeNewLines?: boolean, style?: Style): Segment[][];
|
|
142
|
-
}
|