@promptctl/rich-js 0.17.0 → 0.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -26
- package/package.json +7 -3
- package/dist/core/box.d.ts +0 -144
- package/dist/core/box.js +0 -391
- package/dist/core/cells.d.ts +0 -148
- package/dist/core/cells.js +0 -266
- package/dist/core/color.d.ts +0 -250
- package/dist/core/color.js +0 -1029
- package/dist/core/console.d.ts +0 -156
- package/dist/core/console.js +0 -472
- package/dist/core/emoji.d.ts +0 -24
- package/dist/core/emoji.js +0 -391
- package/dist/core/export-html.d.ts +0 -24
- package/dist/core/export-html.js +0 -106
- package/dist/core/export-lines.d.ts +0 -121
- package/dist/core/export-lines.js +0 -141
- package/dist/core/highlighter.d.ts +0 -33
- package/dist/core/highlighter.js +0 -124
- package/dist/core/json.d.ts +0 -27
- package/dist/core/json.js +0 -53
- package/dist/core/markup.d.ts +0 -97
- package/dist/core/markup.js +0 -619
- package/dist/core/measure.d.ts +0 -27
- package/dist/core/measure.js +0 -61
- package/dist/core/oklch.d.ts +0 -161
- package/dist/core/oklch.js +0 -312
- package/dist/core/osc8.d.ts +0 -68
- package/dist/core/osc8.js +0 -90
- package/dist/core/pretty.d.ts +0 -141
- package/dist/core/pretty.js +0 -472
- package/dist/core/protocol.d.ts +0 -137
- package/dist/core/protocol.js +0 -101
- package/dist/core/render.d.ts +0 -84
- package/dist/core/render.js +0 -145
- package/dist/core/segment.d.ts +0 -142
- package/dist/core/segment.js +0 -433
- package/dist/core/spinnerData.d.ts +0 -10
- package/dist/core/spinnerData.js +0 -115
- package/dist/core/strip.d.ts +0 -145
- package/dist/core/strip.js +0 -308
- package/dist/core/style.d.ts +0 -144
- package/dist/core/style.js +0 -723
- package/dist/core/subscription.d.ts +0 -12
- package/dist/core/subscription.js +0 -12
- package/dist/core/text.d.ts +0 -255
- package/dist/core/text.js +0 -990
- package/dist/core/wrap.d.ts +0 -41
- package/dist/core/wrap.js +0 -127
- package/dist/host/host-stream.d.ts +0 -30
- package/dist/host/host-stream.js +0 -35
- package/dist/host/index.d.ts +0 -3
- package/dist/host/index.js +0 -2
- package/dist/host/terminal-host.d.ts +0 -143
- package/dist/host/terminal-host.js +0 -132
- package/dist/index.d.ts +0 -87
- package/dist/index.js +0 -104
- package/dist/node/prompt.d.ts +0 -21
- package/dist/node/prompt.js +0 -30
- package/dist/node/save.d.ts +0 -17
- package/dist/node/save.js +0 -21
- package/dist/node/terminal-host.d.ts +0 -84
- package/dist/node/terminal-host.js +0 -160
- package/dist/node/traceback.d.ts +0 -27
- package/dist/node/traceback.js +0 -112
- package/dist/renderables/align.d.ts +0 -16
- package/dist/renderables/align.js +0 -46
- package/dist/renderables/columns.d.ts +0 -61
- package/dist/renderables/columns.js +0 -168
- package/dist/renderables/constrain.d.ts +0 -15
- package/dist/renderables/constrain.js +0 -37
- package/dist/renderables/flexStrip.d.ts +0 -41
- package/dist/renderables/flexStrip.js +0 -200
- package/dist/renderables/group.d.ts +0 -11
- package/dist/renderables/group.js +0 -15
- package/dist/renderables/layout.d.ts +0 -92
- package/dist/renderables/layout.js +0 -315
- package/dist/renderables/live.d.ts +0 -47
- package/dist/renderables/live.js +0 -131
- package/dist/renderables/markdown.d.ts +0 -24
- package/dist/renderables/markdown.js +0 -182
- package/dist/renderables/padding.d.ts +0 -38
- package/dist/renderables/padding.js +0 -140
- package/dist/renderables/panel.d.ts +0 -116
- package/dist/renderables/panel.js +0 -322
- package/dist/renderables/progress.d.ts +0 -90
- package/dist/renderables/progress.js +0 -241
- package/dist/renderables/progressBar.d.ts +0 -31
- package/dist/renderables/progressBar.js +0 -52
- package/dist/renderables/prompt.d.ts +0 -49
- package/dist/renderables/prompt.js +0 -125
- package/dist/renderables/rule.d.ts +0 -24
- package/dist/renderables/rule.js +0 -90
- package/dist/renderables/spinner.d.ts +0 -29
- package/dist/renderables/spinner.js +0 -64
- package/dist/renderables/status.d.ts +0 -23
- package/dist/renderables/status.js +0 -60
- package/dist/renderables/syntax.d.ts +0 -35
- package/dist/renderables/syntax.js +0 -114
- package/dist/renderables/table.d.ts +0 -182
- package/dist/renderables/table.js +0 -697
- package/dist/renderables/traceback.d.ts +0 -31
- package/dist/renderables/traceback.js +0 -109
- package/dist/renderables/tree.d.ts +0 -54
- package/dist/renderables/tree.js +0 -159
- package/dist/template-bindings/color-funcs.d.ts +0 -87
- package/dist/template-bindings/color-funcs.js +0 -219
- package/dist/template-bindings/helpers.d.ts +0 -24
- package/dist/template-bindings/helpers.js +0 -48
- package/dist/template-bindings/index.d.ts +0 -107
- package/dist/template-bindings/index.js +0 -144
- package/dist/template-bindings/palette-funcs.d.ts +0 -94
- package/dist/template-bindings/palette-funcs.js +0 -151
- package/dist/template-bindings/style-funcs.d.ts +0 -28
- package/dist/template-bindings/style-funcs.js +0 -151
- package/dist/themes/buildPalette.d.ts +0 -36
- package/dist/themes/buildPalette.js +0 -56
- package/dist/themes/colorMath.d.ts +0 -82
- package/dist/themes/colorMath.js +0 -254
- package/dist/themes/colorRef.d.ts +0 -51
- package/dist/themes/colorRef.js +0 -96
- package/dist/themes/data/atom-one-dark.d.ts +0 -3
- package/dist/themes/data/atom-one-dark.js +0 -158
- package/dist/themes/data/atom-one-light.d.ts +0 -3
- package/dist/themes/data/atom-one-light.js +0 -158
- package/dist/themes/data/catppuccin-frappe.d.ts +0 -3
- package/dist/themes/data/catppuccin-frappe.js +0 -158
- package/dist/themes/data/catppuccin-latte.d.ts +0 -3
- package/dist/themes/data/catppuccin-latte.js +0 -158
- package/dist/themes/data/catppuccin-macchiato.d.ts +0 -3
- package/dist/themes/data/catppuccin-macchiato.js +0 -158
- package/dist/themes/data/catppuccin-mocha.d.ts +0 -3
- package/dist/themes/data/catppuccin-mocha.js +0 -158
- package/dist/themes/data/cyberpunk.d.ts +0 -17
- package/dist/themes/data/cyberpunk.js +0 -191
- package/dist/themes/data/default.d.ts +0 -3
- package/dist/themes/data/default.js +0 -158
- package/dist/themes/data/dracula.d.ts +0 -3
- package/dist/themes/data/dracula.js +0 -158
- package/dist/themes/data/flexoki.d.ts +0 -3
- package/dist/themes/data/flexoki.js +0 -158
- package/dist/themes/data/gruvbox.d.ts +0 -3
- package/dist/themes/data/gruvbox.js +0 -158
- package/dist/themes/data/index.d.ts +0 -35
- package/dist/themes/data/index.js +0 -64
- package/dist/themes/data/monokai.d.ts +0 -3
- package/dist/themes/data/monokai.js +0 -158
- package/dist/themes/data/nord.d.ts +0 -3
- package/dist/themes/data/nord.js +0 -158
- package/dist/themes/data/rose-pine-dawn.d.ts +0 -3
- package/dist/themes/data/rose-pine-dawn.js +0 -158
- package/dist/themes/data/rose-pine-moon.d.ts +0 -3
- package/dist/themes/data/rose-pine-moon.js +0 -158
- package/dist/themes/data/rose-pine.d.ts +0 -3
- package/dist/themes/data/rose-pine.js +0 -158
- package/dist/themes/data/solarized-dark.d.ts +0 -3
- package/dist/themes/data/solarized-dark.js +0 -158
- package/dist/themes/data/solarized-light.d.ts +0 -3
- package/dist/themes/data/solarized-light.js +0 -158
- package/dist/themes/data/svg-export.d.ts +0 -3
- package/dist/themes/data/svg-export.js +0 -158
- package/dist/themes/data/textual-ansi.d.ts +0 -3
- package/dist/themes/data/textual-ansi.js +0 -158
- package/dist/themes/data/textual-dark.d.ts +0 -3
- package/dist/themes/data/textual-dark.js +0 -158
- package/dist/themes/data/textual-light.d.ts +0 -3
- package/dist/themes/data/textual-light.js +0 -158
- package/dist/themes/data/tokyo-night.d.ts +0 -3
- package/dist/themes/data/tokyo-night.js +0 -158
- package/dist/themes/data/types.d.ts +0 -15
- package/dist/themes/data/types.js +0 -1
- package/dist/themes/palette.d.ts +0 -21
- package/dist/themes/palette.js +0 -28
- package/dist/themes/ramp.d.ts +0 -83
- package/dist/themes/ramp.js +0 -122
- package/dist/themes/registry.d.ts +0 -43
- package/dist/themes/registry.js +0 -82
- package/dist/themes/terminalThemes.d.ts +0 -54
- package/dist/themes/terminalThemes.js +0 -68
- package/dist/themes/transpose.d.ts +0 -87
- package/dist/themes/transpose.js +0 -138
- package/dist/widgets/button.d.ts +0 -49
- package/dist/widgets/button.js +0 -206
- package/dist/widgets/checkbox.d.ts +0 -41
- package/dist/widgets/checkbox.js +0 -155
- package/dist/widgets/dropdown.d.ts +0 -67
- package/dist/widgets/dropdown.js +0 -453
- package/dist/widgets/event-router.d.ts +0 -78
- package/dist/widgets/event-router.js +0 -622
- package/dist/widgets/focus-manager.d.ts +0 -26
- package/dist/widgets/focus-manager.js +0 -193
- package/dist/widgets/index.d.ts +0 -22
- package/dist/widgets/index.js +0 -15
- package/dist/widgets/screen.d.ts +0 -98
- package/dist/widgets/screen.js +0 -530
- package/dist/widgets/slider.d.ts +0 -57
- package/dist/widgets/slider.js +0 -270
- package/dist/widgets/static-item.d.ts +0 -40
- package/dist/widgets/static-item.js +0 -60
- package/dist/widgets/text-input.d.ts +0 -274
- package/dist/widgets/text-input.js +0 -1162
- package/dist/widgets/toggle.d.ts +0 -44
- package/dist/widgets/toggle.js +0 -179
- package/dist/widgets/types.d.ts +0 -108
- package/dist/widgets/types.js +0 -31
- package/dist/widgets/widget-base.d.ts +0 -42
- package/dist/widgets/widget-base.js +0 -173
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,84 +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`, `Screen`'s frame paint, `renderToString`, and
|
|
16
|
-
* `segmentToString` all delegate here, so terminal output, live frames, widget
|
|
17
|
-
* frames, string export, and single-segment encoding agree by construction.
|
|
18
|
-
*
|
|
19
|
-
* [LAW:one-source-of-truth] What the encoding needs from where it writes
|
|
20
|
-
* arrives as one `Destination` value. Each caller resolves its destination
|
|
21
|
-
* once — spec, detection, and its own overrides — and hands it through whole,
|
|
22
|
-
* so a depth is never paired with a link setting taken from somewhere else.
|
|
23
|
-
*
|
|
24
|
-
* [LAW:dataflow-not-control-flow] The same pipeline runs every render: collect
|
|
25
|
-
* non-control non-empty pieces, partition by SGR-codes (SGR-runs), partition
|
|
26
|
-
* each run by link (link-runs), emit one SGR open/close per run with link
|
|
27
|
-
* open/close pairs sitting inside. Colour and hyperlinks are two facts about
|
|
28
|
-
* the destination: `colorSystem === null` empties every piece's SGR-codes, and
|
|
29
|
-
* `hyperlinks === false` empties every piece's link. A colour depth never
|
|
30
|
-
* removes a link — a hyperlink is not a colour, and a NO_COLOR terminal still
|
|
31
|
-
* follows OSC 8.
|
|
32
|
-
*
|
|
33
|
-
* [LAW:types-are-the-program] Adjacent same-style segments share an SGR wrap
|
|
34
|
-
* because the SGR-codes string is the same group key for both — the
|
|
35
|
-
* partitioning shape (data) encodes the byte structure; the emit walk is a
|
|
36
|
-
* mechanical fold over it.
|
|
37
|
-
*/
|
|
38
|
-
import { ColorDepth } from "./color.js";
|
|
39
|
-
import type { Destination } from "./color.js";
|
|
40
|
-
import type { Segment } from "./segment.js";
|
|
41
|
-
import type { Renderable } from "./protocol.js";
|
|
42
|
-
export interface RenderToStringOptions {
|
|
43
|
-
/** Cell width to render into. Default 80. */
|
|
44
|
-
width?: number;
|
|
45
|
-
/**
|
|
46
|
-
* Color encoding to emit. Accepts a string spec (`"auto"`, `"truecolor"`,
|
|
47
|
-
* `"256"`, `"ansi"`, `"none"`), a `ColorDepth` enum value, or `null` to
|
|
48
|
-
* strip SGR colors/attributes. Hyperlinks are `hyperlinks`' concern. Default
|
|
49
|
-
* truecolor.
|
|
50
|
-
*/
|
|
51
|
-
colorSystem?: string | ColorDepth | null;
|
|
52
|
-
/**
|
|
53
|
-
* Environment to consult when `colorSystem` is `"auto"`. Defaults to
|
|
54
|
-
* `process.env`. Pass an explicit value to keep rendering deterministic.
|
|
55
|
-
*/
|
|
56
|
-
env?: NodeJS.ProcessEnv;
|
|
57
|
-
/**
|
|
58
|
-
* Whether output is going to a TTY when `colorSystem` is `"auto"`. Defaults
|
|
59
|
-
* to `process.stdout?.isTTY`. Pass an explicit value to keep rendering
|
|
60
|
-
* deterministic.
|
|
61
|
-
*/
|
|
62
|
-
isTTY?: boolean;
|
|
63
|
-
/** When true, forces `colorSystem` to `null` regardless of the explicit value. */
|
|
64
|
-
noColor?: boolean;
|
|
65
|
-
/**
|
|
66
|
-
* Whether OSC 8 hyperlinks are emitted. Independent of `colorSystem`: a
|
|
67
|
-
* null colour system strips SGR and keeps links. Default: what the
|
|
68
|
-
* destination takes — true for an explicit depth, detected under `"auto"`
|
|
69
|
-
* (no TTY or TERM=dumb: false). `false` with `colorSystem: null` is plain text.
|
|
70
|
-
*/
|
|
71
|
-
hyperlinks?: boolean;
|
|
72
|
-
}
|
|
73
|
-
/**
|
|
74
|
-
* Encodes a single segment as ANSI bytes for `destination`. Equivalent to
|
|
75
|
-
* `segmentsToString([segment], destination)` — same SGR / OSC 8 layout.
|
|
76
|
-
*/
|
|
77
|
-
export declare function segmentToString(segment: Segment, destination: Destination): string;
|
|
78
|
-
/**
|
|
79
|
-
* Encodes a sequence of segments as ANSI bytes for `destination`, coalescing
|
|
80
|
-
* adjacent same-SGR segments under a single SGR open/close pair, with OSC 8
|
|
81
|
-
* link pairs nested inside per same-link sub-run.
|
|
82
|
-
*/
|
|
83
|
-
export declare function segmentsToString(segments: Iterable<Segment>, destination: Destination): string;
|
|
84
|
-
export declare function renderToString(renderable: Renderable, options?: RenderToStringOptions): string;
|
package/dist/core/render.js
DELETED
|
@@ -1,145 +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`, `Screen`'s frame paint, `renderToString`, and
|
|
16
|
-
* `segmentToString` all delegate here, so terminal output, live frames, widget
|
|
17
|
-
* frames, string export, and single-segment encoding agree by construction.
|
|
18
|
-
*
|
|
19
|
-
* [LAW:one-source-of-truth] What the encoding needs from where it writes
|
|
20
|
-
* arrives as one `Destination` value. Each caller resolves its destination
|
|
21
|
-
* once — spec, detection, and its own overrides — and hands it through whole,
|
|
22
|
-
* so a depth is never paired with a link setting taken from somewhere else.
|
|
23
|
-
*
|
|
24
|
-
* [LAW:dataflow-not-control-flow] The same pipeline runs every render: collect
|
|
25
|
-
* non-control non-empty pieces, partition by SGR-codes (SGR-runs), partition
|
|
26
|
-
* each run by link (link-runs), emit one SGR open/close per run with link
|
|
27
|
-
* open/close pairs sitting inside. Colour and hyperlinks are two facts about
|
|
28
|
-
* the destination: `colorSystem === null` empties every piece's SGR-codes, and
|
|
29
|
-
* `hyperlinks === false` empties every piece's link. A colour depth never
|
|
30
|
-
* removes a link — a hyperlink is not a colour, and a NO_COLOR terminal still
|
|
31
|
-
* follows OSC 8.
|
|
32
|
-
*
|
|
33
|
-
* [LAW:types-are-the-program] Adjacent same-style segments share an SGR wrap
|
|
34
|
-
* because the SGR-codes string is the same group key for both — the
|
|
35
|
-
* partitioning shape (data) encodes the byte structure; the emit walk is a
|
|
36
|
-
* mechanical fold over it.
|
|
37
|
-
*/
|
|
38
|
-
import { ColorDepth, resolveDestination } from "./color.js";
|
|
39
|
-
import { OSC8_CLOSE, osc8Open } from "./osc8.js";
|
|
40
|
-
const DEFAULT_WIDTH = 80;
|
|
41
|
-
function segmentToPiece(segment, destination) {
|
|
42
|
-
if (segment.isControl)
|
|
43
|
-
return undefined;
|
|
44
|
-
if (segment.text.length === 0)
|
|
45
|
-
return undefined;
|
|
46
|
-
const style = segment.style;
|
|
47
|
-
if (!style || style.isNull) {
|
|
48
|
-
return { text: segment.text, sgrCodes: "", link: undefined };
|
|
49
|
-
}
|
|
50
|
-
// [LAW:one-type-per-behavior] Colour depth governs SGR only; hyperlinks are
|
|
51
|
-
// their own fact. Coupling them let a colour setting delete every control.
|
|
52
|
-
return {
|
|
53
|
-
text: segment.text,
|
|
54
|
-
sgrCodes: destination.colorSystem === null ? "" : style.toSgrCodes(destination.colorSystem),
|
|
55
|
-
link: destination.hyperlinks ? style.link : undefined,
|
|
56
|
-
};
|
|
57
|
-
}
|
|
58
|
-
/**
|
|
59
|
-
* Encodes a single segment as ANSI bytes for `destination`. Equivalent to
|
|
60
|
-
* `segmentsToString([segment], destination)` — same SGR / OSC 8 layout.
|
|
61
|
-
*/
|
|
62
|
-
export function segmentToString(segment, destination) {
|
|
63
|
-
return segmentsToString([segment], destination);
|
|
64
|
-
}
|
|
65
|
-
/**
|
|
66
|
-
* Encodes a sequence of segments as ANSI bytes for `destination`, coalescing
|
|
67
|
-
* adjacent same-SGR segments under a single SGR open/close pair, with OSC 8
|
|
68
|
-
* link pairs nested inside per same-link sub-run.
|
|
69
|
-
*/
|
|
70
|
-
export function segmentsToString(segments, destination) {
|
|
71
|
-
const pieces = [];
|
|
72
|
-
for (const s of segments) {
|
|
73
|
-
const p = segmentToPiece(s, destination);
|
|
74
|
-
if (p)
|
|
75
|
-
pieces.push(p);
|
|
76
|
-
}
|
|
77
|
-
if (pieces.length === 0)
|
|
78
|
-
return "";
|
|
79
|
-
// [LAW:dataflow-not-control-flow] One linear chunk accumulator for the
|
|
80
|
-
// entire output; SGR / OSC 8 boundaries and piece texts all push into it
|
|
81
|
-
// in order. No per-link-run intermediate string, no quadratic `+=` chains.
|
|
82
|
-
const parts = [];
|
|
83
|
-
let i = 0;
|
|
84
|
-
while (i < pieces.length) {
|
|
85
|
-
const sgr = pieces[i].sgrCodes;
|
|
86
|
-
let j = i + 1;
|
|
87
|
-
while (j < pieces.length && pieces[j].sgrCodes === sgr)
|
|
88
|
-
j++;
|
|
89
|
-
if (sgr.length > 0)
|
|
90
|
-
parts.push(`\x1b[${sgr}m`);
|
|
91
|
-
let k = i;
|
|
92
|
-
while (k < j) {
|
|
93
|
-
const link = pieces[k].link;
|
|
94
|
-
let l = k + 1;
|
|
95
|
-
while (l < j && pieces[l].link === link)
|
|
96
|
-
l++;
|
|
97
|
-
if (link) {
|
|
98
|
-
parts.push(osc8Open(link));
|
|
99
|
-
for (let m = k; m < l; m++)
|
|
100
|
-
parts.push(pieces[m].text);
|
|
101
|
-
parts.push(OSC8_CLOSE);
|
|
102
|
-
}
|
|
103
|
-
else {
|
|
104
|
-
for (let m = k; m < l; m++)
|
|
105
|
-
parts.push(pieces[m].text);
|
|
106
|
-
}
|
|
107
|
-
k = l;
|
|
108
|
-
}
|
|
109
|
-
if (sgr.length > 0)
|
|
110
|
-
parts.push("\x1b[0m");
|
|
111
|
-
i = j;
|
|
112
|
-
}
|
|
113
|
-
return parts.join("");
|
|
114
|
-
}
|
|
115
|
-
export function renderToString(renderable, options) {
|
|
116
|
-
const width = options?.width ?? DEFAULT_WIDTH;
|
|
117
|
-
// [LAW:dataflow-not-control-flow] An absent spec is truecolor; an explicit
|
|
118
|
-
// `null` stays null (`??` would collapse it).
|
|
119
|
-
// [LAW:single-enforcer] Specs resolve through `resolveDestination`.
|
|
120
|
-
const detectOptions = {};
|
|
121
|
-
if (options?.env !== undefined)
|
|
122
|
-
detectOptions.env = options.env;
|
|
123
|
-
if (options?.isTTY !== undefined)
|
|
124
|
-
detectOptions.isTTY = options.isTTY;
|
|
125
|
-
const rawSpec = options?.colorSystem;
|
|
126
|
-
const resolved = resolveDestination(rawSpec === undefined ? ColorDepth.TRUECOLOR : rawSpec, detectOptions);
|
|
127
|
-
// [LAW:single-enforcer] This call's overrides are applied here, once, to
|
|
128
|
-
// make the one destination everything below is drawn for. `noColor` is a
|
|
129
|
-
// colour choice, so it overrides the depth and leaves hyperlinks to the
|
|
130
|
-
// destination; an explicit `hyperlinks` overrides what the destination takes.
|
|
131
|
-
const destination = {
|
|
132
|
-
colorSystem: options?.noColor ? null : resolved.colorSystem,
|
|
133
|
-
hyperlinks: options?.hyperlinks ?? resolved.hyperlinks,
|
|
134
|
-
};
|
|
135
|
-
const renderOptions = {
|
|
136
|
-
maxWidth: width,
|
|
137
|
-
isTerminal: false,
|
|
138
|
-
encoding: "utf-8",
|
|
139
|
-
asciiOnly: false,
|
|
140
|
-
// [LAW:one-source-of-truth] The depth the segments below are encoded at,
|
|
141
|
-
// so a renderable measures what this very call will draw.
|
|
142
|
-
colorSystem: destination.colorSystem,
|
|
143
|
-
};
|
|
144
|
-
return segmentsToString(renderable.render(renderOptions), destination);
|
|
145
|
-
}
|
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
|
-
}
|