@promptctl/rich-js 0.18.1 → 0.19.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 +9 -7
- package/dist/core/box.d.ts +3 -1
- package/dist/core/box.js +6 -13
- package/dist/core/cells.d.ts +61 -28
- package/dist/core/cells.js +171 -124
- package/dist/core/clock.d.ts +48 -0
- package/dist/core/clock.js +42 -0
- package/dist/core/color.d.ts +98 -18
- package/dist/core/color.js +331 -144
- package/dist/core/console.d.ts +38 -7
- package/dist/core/console.js +159 -90
- package/dist/core/easing.d.ts +86 -0
- package/dist/core/easing.js +171 -0
- package/dist/core/env.d.ts +16 -0
- package/dist/core/env.js +16 -0
- package/dist/core/export-html.js +1 -4
- package/dist/core/export-lines.d.ts +22 -13
- package/dist/core/export-lines.js +37 -19
- package/dist/core/export-svg.d.ts +49 -0
- package/dist/core/export-svg.js +213 -0
- package/dist/core/fnv1a.d.ts +10 -0
- package/dist/core/fnv1a.js +17 -0
- package/dist/core/highlighter.d.ts +2 -2
- package/dist/core/highlighter.js +113 -47
- package/dist/core/json.js +1 -1
- package/dist/core/markup.d.ts +28 -7
- package/dist/core/markup.js +235 -312
- package/dist/core/measure.d.ts +3 -3
- package/dist/core/measure.js +12 -7
- package/dist/core/memo.d.ts +19 -0
- package/dist/core/memo.js +34 -0
- package/dist/core/osc8.d.ts +6 -6
- package/dist/core/osc8.js +9 -21
- package/dist/core/paint.d.ts +107 -0
- package/dist/core/paint.js +150 -0
- package/dist/core/place.d.ts +1 -1
- package/dist/core/place.js +4 -6
- package/dist/core/pretty.d.ts +105 -2
- package/dist/core/pretty.js +527 -97
- package/dist/core/protocol.d.ts +41 -3
- package/dist/core/protocol.js +20 -0
- package/dist/core/render.d.ts +33 -30
- package/dist/core/render.js +43 -83
- package/dist/core/segment.d.ts +6 -5
- package/dist/core/segment.js +11 -15
- package/dist/core/strip.d.ts +13 -2
- package/dist/core/strip.js +44 -23
- package/dist/core/style.d.ts +27 -7
- package/dist/core/style.js +97 -34
- package/dist/core/text.d.ts +43 -16
- package/dist/core/text.js +180 -59
- package/dist/core/wrap.d.ts +5 -0
- package/dist/core/wrap.js +16 -3
- package/dist/host/app.d.ts +31 -19
- package/dist/host/app.js +90 -60
- package/dist/host/index.d.ts +2 -1
- package/dist/host/terminal-host.d.ts +3 -2
- package/dist/index.d.ts +17 -10
- package/dist/index.js +11 -4
- package/dist/node/prompt.d.ts +13 -0
- package/dist/node/prompt.js +30 -9
- package/dist/node/save.d.ts +4 -3
- package/dist/node/save.js +6 -3
- package/dist/node/terminal-host.d.ts +3 -2
- package/dist/node/traceback.d.ts +2 -1
- package/dist/node/traceback.js +3 -39
- package/dist/renderables/align.js +2 -9
- package/dist/renderables/columns.d.ts +45 -29
- package/dist/renderables/columns.js +142 -103
- package/dist/renderables/constrain.js +6 -12
- package/dist/renderables/effect.d.ts +98 -0
- package/dist/renderables/effect.js +160 -0
- package/dist/renderables/embed.d.ts +87 -30
- package/dist/renderables/embed.js +134 -45
- package/dist/renderables/group.d.ts +6 -3
- package/dist/renderables/group.js +5 -0
- package/dist/renderables/layout.d.ts +6 -23
- package/dist/renderables/layout.js +24 -113
- package/dist/renderables/live.d.ts +40 -16
- package/dist/renderables/live.js +96 -83
- package/dist/renderables/markdown.d.ts +16 -2
- package/dist/renderables/markdown.js +521 -118
- package/dist/renderables/padding.d.ts +22 -1
- package/dist/renderables/padding.js +30 -24
- package/dist/renderables/panel.d.ts +31 -33
- package/dist/renderables/panel.js +137 -141
- package/dist/renderables/progress.d.ts +104 -16
- package/dist/renderables/progress.js +205 -81
- package/dist/renderables/progressBar.d.ts +2 -2
- package/dist/renderables/progressBar.js +30 -19
- package/dist/renderables/prompt.d.ts +37 -15
- package/dist/renderables/prompt.js +125 -85
- package/dist/renderables/ratio.d.ts +123 -0
- package/dist/renderables/ratio.js +213 -0
- package/dist/renderables/rule.d.ts +1 -0
- package/dist/renderables/rule.js +44 -48
- package/dist/renderables/spinner.d.ts +28 -2
- package/dist/renderables/spinner.js +52 -19
- package/dist/renderables/status.d.ts +13 -7
- package/dist/renderables/status.js +15 -29
- package/dist/renderables/syntax.d.ts +37 -7
- package/dist/renderables/syntax.js +134 -83
- package/dist/renderables/table.d.ts +58 -20
- package/dist/renderables/table.js +370 -192
- package/dist/renderables/traceback.d.ts +5 -4
- package/dist/renderables/traceback.js +239 -68
- package/dist/renderables/tree.d.ts +10 -2
- package/dist/renderables/tree.js +61 -34
- package/dist/renderables/viewport.js +2 -4
- package/dist/template-bindings/color-funcs.d.ts +31 -9
- package/dist/template-bindings/color-funcs.js +40 -14
- package/dist/template-bindings/helpers.d.ts +4 -2
- package/dist/template-bindings/helpers.js +5 -20
- package/dist/template-bindings/index.d.ts +29 -17
- package/dist/template-bindings/index.js +36 -20
- package/dist/template-bindings/palette-funcs.d.ts +3 -1
- package/dist/template-bindings/palette-funcs.js +11 -7
- package/dist/template-bindings/style-funcs.js +4 -2
- package/dist/themes/buildPalette.d.ts +24 -3
- package/dist/themes/buildPalette.js +52 -8
- package/dist/themes/colorMath.d.ts +29 -17
- package/dist/themes/colorMath.js +74 -48
- package/dist/themes/colorRef.d.ts +2 -17
- package/dist/themes/colorRef.js +13 -31
- package/dist/themes/palette.d.ts +24 -0
- package/dist/themes/palette.js +46 -3
- package/dist/themes/ramp.d.ts +21 -34
- package/dist/themes/ramp.js +23 -42
- package/dist/themes/registry.js +39 -28
- package/dist/widgets/button.d.ts +2 -6
- package/dist/widgets/button.js +9 -40
- package/dist/widgets/checkbox.d.ts +2 -5
- package/dist/widgets/checkbox.js +9 -31
- package/dist/widgets/dropdown.d.ts +2 -5
- package/dist/widgets/dropdown.js +14 -48
- package/dist/widgets/ink.d.ts +22 -0
- package/dist/widgets/ink.js +31 -0
- package/dist/widgets/slider.d.ts +2 -5
- package/dist/widgets/slider.js +11 -34
- package/dist/widgets/text-input.d.ts +19 -10
- package/dist/widgets/text-input.js +156 -147
- package/dist/widgets/themed-widget.d.ts +20 -0
- package/dist/widgets/themed-widget.js +89 -0
- package/dist/widgets/toggle.d.ts +2 -5
- package/dist/widgets/toggle.js +8 -31
- package/dist/widgets/widget-app.d.ts +2 -0
- package/dist/widgets/widget-app.js +13 -6
- package/dist/widgets/widget-base.d.ts +2 -0
- package/dist/widgets/widget-base.js +9 -2
- package/package.json +11 -1
package/dist/core/protocol.d.ts
CHANGED
|
@@ -4,7 +4,8 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import type { Segment } from "./segment.js";
|
|
6
6
|
import { type Style, type StyleSyntaxError, type Theme } from "./style.js";
|
|
7
|
-
import
|
|
7
|
+
import { ColorDepth } from "./color.js";
|
|
8
|
+
import type { Highlighter } from "./highlighter.js";
|
|
8
9
|
/**
|
|
9
10
|
* What becomes of a line still wider than its width once wrapping is done.
|
|
10
11
|
* `"fold"` breaks it across lines, `"crop"` cuts it at the edge, `"ellipsis"`
|
|
@@ -41,8 +42,30 @@ export interface RenderOptions {
|
|
|
41
42
|
justify?: "left" | "center" | "right" | "full";
|
|
42
43
|
overflow?: OverflowMethod;
|
|
43
44
|
noWrap?: boolean;
|
|
44
|
-
|
|
45
|
-
|
|
45
|
+
/**
|
|
46
|
+
* Whether a string handed to a renderable — a cell, a label, a pane — is
|
|
47
|
+
* read as markup when it is drawn. Absent, it is, as a `Console`'s is unless
|
|
48
|
+
* told otherwise. A `Console` passes its own setting, and `print`'s.
|
|
49
|
+
*/
|
|
50
|
+
markup?: boolean;
|
|
51
|
+
/**
|
|
52
|
+
* Whether a string handed to a renderable is highlighted when it is drawn,
|
|
53
|
+
* by `highlighter`. Absent, it is, as a `Console`'s is unless told
|
|
54
|
+
* otherwise. A `Console` passes its own setting, and `print`'s; a renderable
|
|
55
|
+
* that draws its strings plain, as Rich's `Panel`, `Table` and `Tree` do by
|
|
56
|
+
* default, sets it false for what it renders — Rich's
|
|
57
|
+
* `options.update(highlight=False)` — and a table column's `highlight` sets
|
|
58
|
+
* it either way. `activeHighlighter` is the one reading of the pair.
|
|
59
|
+
*/
|
|
60
|
+
highlight?: boolean;
|
|
61
|
+
/**
|
|
62
|
+
* What highlights a string handed to a renderable, while `highlight` says
|
|
63
|
+
* to. A `Console` passes its highlighter whether or not its own `highlight`
|
|
64
|
+
* is on, as Rich's `render_str` reaches `console.highlighter`, so content
|
|
65
|
+
* that turns highlighting on finds the console's highlighter there. Absent,
|
|
66
|
+
* nothing highlights.
|
|
67
|
+
*/
|
|
68
|
+
highlighter?: Highlighter;
|
|
46
69
|
/**
|
|
47
70
|
* The depth the output will be encoded at, `null` when it carries no colour.
|
|
48
71
|
* A renderable choosing between two ways of drawing something — the strip's
|
|
@@ -177,6 +200,21 @@ export type StyleErrorHandler = (error: StyleSyntaxError, style: string) => void
|
|
|
177
200
|
* theme })` changed nothing.
|
|
178
201
|
*/
|
|
179
202
|
export declare function getStyle(options: RenderOptions, style: string | Style): Style;
|
|
203
|
+
/**
|
|
204
|
+
* `style` as the depth the render is encoded at can draw it. A style that
|
|
205
|
+
* paints its own ground in fixed colours chose its ink and ground as a pair;
|
|
206
|
+
* at sixteen colours the writer rounds a fixed colour to an ANSI slot, which
|
|
207
|
+
* the terminal paints in its own theme's colour, and no pair of slots reads in
|
|
208
|
+
* every bundled theme. So where the depth would turn a fixed colour of the
|
|
209
|
+
* pair into a slot, the pair is dropped and the run is drawn on the
|
|
210
|
+
* terminal's own foreground and background, which its theme does make
|
|
211
|
+
* readable; its attributes still set it apart. A style whose colours are
|
|
212
|
+
* slots already (`white on blue`), or that paints no ground (`bold magenta`),
|
|
213
|
+
* was written for the terminal's own colours and is drawn as written. Rich
|
|
214
|
+
* draws a highlighted line number the same way below 256 colours
|
|
215
|
+
* (`Syntax._get_number_styles`).
|
|
216
|
+
*/
|
|
217
|
+
export declare function groundedStyle(options: RenderOptions, style: Style): Style;
|
|
180
218
|
/**
|
|
181
219
|
* The options a renderable should work from: the caller's, with `maxWidth`
|
|
182
220
|
* parsed into an actual count of cells.
|
package/dist/core/protocol.js
CHANGED
|
@@ -60,6 +60,26 @@ export function fitHeight(lines, height) {
|
|
|
60
60
|
export function getStyle(options, style) {
|
|
61
61
|
return (options.theme ?? DEFAULT_THEME).resolve(style);
|
|
62
62
|
}
|
|
63
|
+
/**
|
|
64
|
+
* `style` as the depth the render is encoded at can draw it. A style that
|
|
65
|
+
* paints its own ground in fixed colours chose its ink and ground as a pair;
|
|
66
|
+
* at sixteen colours the writer rounds a fixed colour to an ANSI slot, which
|
|
67
|
+
* the terminal paints in its own theme's colour, and no pair of slots reads in
|
|
68
|
+
* every bundled theme. So where the depth would turn a fixed colour of the
|
|
69
|
+
* pair into a slot, the pair is dropped and the run is drawn on the
|
|
70
|
+
* terminal's own foreground and background, which its theme does make
|
|
71
|
+
* readable; its attributes still set it apart. A style whose colours are
|
|
72
|
+
* slots already (`white on blue`), or that paints no ground (`bold magenta`),
|
|
73
|
+
* was written for the terminal's own colours and is drawn as written. Rich
|
|
74
|
+
* draws a highlighted line number the same way below 256 colours
|
|
75
|
+
* (`Syntax._get_number_styles`).
|
|
76
|
+
*/
|
|
77
|
+
export function groundedStyle(options, style) {
|
|
78
|
+
const drawn = style.drawnColors(options.colorSystem ?? undefined);
|
|
79
|
+
const lost = (written, shown) => written?.fixedValue !== undefined && shown?.fixedValue === undefined;
|
|
80
|
+
const pairLost = style.bgcolor !== undefined && (lost(style.color, drawn.color) || lost(style.bgcolor, drawn.bgcolor));
|
|
81
|
+
return pairLost ? style.withoutColor : style;
|
|
82
|
+
}
|
|
63
83
|
/**
|
|
64
84
|
* The options a renderable should work from: the caller's, with `maxWidth`
|
|
65
85
|
* parsed into an actual count of cells.
|
package/dist/core/render.d.ts
CHANGED
|
@@ -10,50 +10,57 @@
|
|
|
10
10
|
* an explicit `ColorDepth` enum / non-`"auto"` spec, or supply `env` and
|
|
11
11
|
* `isTTY` in the options so detection does not consult ambient process state.
|
|
12
12
|
*
|
|
13
|
-
* [LAW:single-enforcer] The Segment-to-ANSI conversion lives in `
|
|
14
|
-
* and is the single way
|
|
15
|
-
* `
|
|
16
|
-
* `
|
|
17
|
-
*
|
|
13
|
+
* [LAW:single-enforcer] The Segment-to-ANSI conversion lives in `segmentToString`
|
|
14
|
+
* and is the single way a segment becomes wire bytes. `segmentsToString` is
|
|
15
|
+
* that encoding joined, and `Console._writeSegments`, `Painter` (every `Live`
|
|
16
|
+
* and `App` frame) and `renderToString` all go through it, so terminal output,
|
|
17
|
+
* live and app frames and string export agree by construction.
|
|
18
18
|
*
|
|
19
19
|
* [LAW:one-source-of-truth] What the encoding needs from where it writes
|
|
20
20
|
* arrives as one `Destination` value. Each caller resolves its destination
|
|
21
21
|
* once — spec, detection, and its own overrides — and hands it through whole,
|
|
22
22
|
* so a depth is never paired with a link setting taken from somewhere else.
|
|
23
23
|
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
24
|
+
* Every segment is written alone, exactly as Python Rich 9d8f9a3 writes it
|
|
25
|
+
* (`Console._render_buffer` calls `Style.render` per segment): its SGR codes
|
|
26
|
+
* open, its text, a reset, and an OSC 8 pair around all three when it carries
|
|
27
|
+
* a link. Two adjacent segments with equal codes are two runs, not one. That
|
|
28
|
+
* costs a reset and a reopen the terminal draws identically, and it is what
|
|
29
|
+
* lets a fixture generated from the reference hold any sequence of segments,
|
|
30
|
+
* rather than only those in which no two neighbours happen to share a style
|
|
31
|
+
* (rich-render-g5g8). The price of that is that where a renderable cuts its
|
|
32
|
+
* segments is now in the bytes, so a renderable pinned against the reference
|
|
33
|
+
* has to cut where the reference cuts, as `Box`'s rules and `Table`'s blank
|
|
34
|
+
* cell lines do. A link split across segments still hovers as one link,
|
|
35
|
+
* because every pair it becomes carries the id `osc8Open` derives from the URL.
|
|
32
36
|
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
+
* Colour and hyperlinks are two facts about the destination:
|
|
38
|
+
* `colorSystem === null` empties every segment's SGR codes, and
|
|
39
|
+
* `hyperlinks === false` drops every segment's link. A colour depth never
|
|
40
|
+
* removes a link — a hyperlink is not a colour, and a NO_COLOR terminal still
|
|
41
|
+
* follows OSC 8. This is where the port departs from the reference, whose
|
|
42
|
+
* `Style.render` returns bare text for a null colour system.
|
|
37
43
|
*/
|
|
38
44
|
import { ColorDepth } from "./color.js";
|
|
39
|
-
import type { Destination } from "./color.js";
|
|
45
|
+
import type { ColorSystemName, Destination } from "./color.js";
|
|
46
|
+
import type { Env } from "./env.js";
|
|
40
47
|
import type { Segment } from "./segment.js";
|
|
41
48
|
import type { Renderable, RenderOptions } from "./protocol.js";
|
|
42
|
-
export interface RenderToStringOptions extends Pick<RenderOptions, "asciiOnly" | "onStyleError"> {
|
|
49
|
+
export interface RenderToStringOptions extends Pick<RenderOptions, "asciiOnly" | "theme" | "onStyleError" | "highlight" | "highlighter"> {
|
|
43
50
|
/** Cell width to render into. Default 80. */
|
|
44
51
|
width?: number;
|
|
45
52
|
/**
|
|
46
|
-
* Color encoding to emit. Accepts a
|
|
47
|
-
* `"256"`, `"ansi"`, `"none"`), a `ColorDepth` enum value, or `null` to
|
|
53
|
+
* Color encoding to emit. Accepts a `ColorSystemName` (`"auto"`,
|
|
54
|
+
* `"truecolor"`, `"256"`, `"ansi"`, `"none"`), a `ColorDepth` enum value, or `null` to
|
|
48
55
|
* strip SGR colors/attributes. Hyperlinks are `hyperlinks`' concern. Default
|
|
49
56
|
* truecolor.
|
|
50
57
|
*/
|
|
51
|
-
colorSystem?:
|
|
58
|
+
colorSystem?: ColorSystemName | ColorDepth | null;
|
|
52
59
|
/**
|
|
53
60
|
* Environment to consult when `colorSystem` is `"auto"`. Defaults to
|
|
54
61
|
* `process.env`. Pass an explicit value to keep rendering deterministic.
|
|
55
62
|
*/
|
|
56
|
-
env?:
|
|
63
|
+
env?: Env;
|
|
57
64
|
/**
|
|
58
65
|
* Whether output is going to a TTY when `colorSystem` is `"auto"`. Defaults
|
|
59
66
|
* to `process.stdout?.isTTY`. Pass an explicit value to keep rendering
|
|
@@ -71,14 +78,10 @@ export interface RenderToStringOptions extends Pick<RenderOptions, "asciiOnly" |
|
|
|
71
78
|
hyperlinks?: boolean;
|
|
72
79
|
}
|
|
73
80
|
/**
|
|
74
|
-
* Encodes
|
|
75
|
-
* `
|
|
81
|
+
* Encodes one segment as ANSI bytes for `destination`: the reference's
|
|
82
|
+
* `Style.render` for that segment, and nothing for a control segment.
|
|
76
83
|
*/
|
|
77
84
|
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
|
-
*/
|
|
85
|
+
/** Encodes a sequence of segments as ANSI bytes for `destination`, one segment at a time. */
|
|
83
86
|
export declare function segmentsToString(segments: Iterable<Segment>, destination: Destination): string;
|
|
84
87
|
export declare function renderToString(renderable: Renderable, options?: RenderToStringOptions): string;
|
package/dist/core/render.js
CHANGED
|
@@ -10,106 +10,63 @@
|
|
|
10
10
|
* an explicit `ColorDepth` enum / non-`"auto"` spec, or supply `env` and
|
|
11
11
|
* `isTTY` in the options so detection does not consult ambient process state.
|
|
12
12
|
*
|
|
13
|
-
* [LAW:single-enforcer] The Segment-to-ANSI conversion lives in `
|
|
14
|
-
* and is the single way
|
|
15
|
-
* `
|
|
16
|
-
* `
|
|
17
|
-
*
|
|
13
|
+
* [LAW:single-enforcer] The Segment-to-ANSI conversion lives in `segmentToString`
|
|
14
|
+
* and is the single way a segment becomes wire bytes. `segmentsToString` is
|
|
15
|
+
* that encoding joined, and `Console._writeSegments`, `Painter` (every `Live`
|
|
16
|
+
* and `App` frame) and `renderToString` all go through it, so terminal output,
|
|
17
|
+
* live and app frames and string export agree by construction.
|
|
18
18
|
*
|
|
19
19
|
* [LAW:one-source-of-truth] What the encoding needs from where it writes
|
|
20
20
|
* arrives as one `Destination` value. Each caller resolves its destination
|
|
21
21
|
* once — spec, detection, and its own overrides — and hands it through whole,
|
|
22
22
|
* so a depth is never paired with a link setting taken from somewhere else.
|
|
23
23
|
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
24
|
+
* Every segment is written alone, exactly as Python Rich 9d8f9a3 writes it
|
|
25
|
+
* (`Console._render_buffer` calls `Style.render` per segment): its SGR codes
|
|
26
|
+
* open, its text, a reset, and an OSC 8 pair around all three when it carries
|
|
27
|
+
* a link. Two adjacent segments with equal codes are two runs, not one. That
|
|
28
|
+
* costs a reset and a reopen the terminal draws identically, and it is what
|
|
29
|
+
* lets a fixture generated from the reference hold any sequence of segments,
|
|
30
|
+
* rather than only those in which no two neighbours happen to share a style
|
|
31
|
+
* (rich-render-g5g8). The price of that is that where a renderable cuts its
|
|
32
|
+
* segments is now in the bytes, so a renderable pinned against the reference
|
|
33
|
+
* has to cut where the reference cuts, as `Box`'s rules and `Table`'s blank
|
|
34
|
+
* cell lines do. A link split across segments still hovers as one link,
|
|
35
|
+
* because every pair it becomes carries the id `osc8Open` derives from the URL.
|
|
32
36
|
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
+
* Colour and hyperlinks are two facts about the destination:
|
|
38
|
+
* `colorSystem === null` empties every segment's SGR codes, and
|
|
39
|
+
* `hyperlinks === false` drops every segment's link. A colour depth never
|
|
40
|
+
* removes a link — a hyperlink is not a colour, and a NO_COLOR terminal still
|
|
41
|
+
* follows OSC 8. This is where the port departs from the reference, whose
|
|
42
|
+
* `Style.render` returns bare text for a null colour system.
|
|
37
43
|
*/
|
|
38
44
|
import { ColorDepth, resolveDestination } from "./color.js";
|
|
39
45
|
import { OSC8_CLOSE, osc8Open } from "./osc8.js";
|
|
40
46
|
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
47
|
/**
|
|
59
|
-
* Encodes
|
|
60
|
-
* `
|
|
48
|
+
* Encodes one segment as ANSI bytes for `destination`: the reference's
|
|
49
|
+
* `Style.render` for that segment, and nothing for a control segment.
|
|
61
50
|
*/
|
|
62
51
|
export function segmentToString(segment, destination) {
|
|
63
|
-
|
|
52
|
+
if (segment.isControl)
|
|
53
|
+
return "";
|
|
54
|
+
const { text, style } = segment;
|
|
55
|
+
// As the reference's `if not text` and `if style:` — no pair around nothing.
|
|
56
|
+
if (text.length === 0 || style === undefined)
|
|
57
|
+
return text;
|
|
58
|
+
// [LAW:one-type-per-behavior] Colour depth governs SGR only; hyperlinks are
|
|
59
|
+
// their own fact. Coupling them let a colour setting delete every control.
|
|
60
|
+
const codes = destination.colorSystem === null ? "" : style.toSgrCodes(destination.colorSystem);
|
|
61
|
+
const styled = codes.length > 0 ? `\x1b[${codes}m${text}\x1b[0m` : text;
|
|
62
|
+
const link = destination.hyperlinks ? style.link : undefined;
|
|
63
|
+
return link === undefined ? styled : `${osc8Open(link)}${styled}${OSC8_CLOSE}`;
|
|
64
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
|
-
*/
|
|
65
|
+
/** Encodes a sequence of segments as ANSI bytes for `destination`, one segment at a time. */
|
|
70
66
|
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
67
|
const parts = [];
|
|
83
|
-
|
|
84
|
-
|
|
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
|
-
}
|
|
68
|
+
for (const segment of segments)
|
|
69
|
+
parts.push(segmentToString(segment, destination));
|
|
113
70
|
return parts.join("");
|
|
114
71
|
}
|
|
115
72
|
export function renderToString(renderable, options) {
|
|
@@ -136,7 +93,10 @@ export function renderToString(renderable, options) {
|
|
|
136
93
|
maxWidth: width,
|
|
137
94
|
isTerminal: false,
|
|
138
95
|
asciiOnly: options?.asciiOnly ?? false,
|
|
96
|
+
theme: options?.theme,
|
|
139
97
|
onStyleError: options?.onStyleError,
|
|
98
|
+
highlight: options?.highlight,
|
|
99
|
+
highlighter: options?.highlighter,
|
|
140
100
|
// [LAW:one-source-of-truth] The depth the segments below are encoded at,
|
|
141
101
|
// so a renderable measures what this very call will draw.
|
|
142
102
|
colorSystem: destination.colorSystem,
|
package/dist/core/segment.d.ts
CHANGED
|
@@ -33,12 +33,13 @@ export declare class Segment {
|
|
|
33
33
|
get hasText(): boolean;
|
|
34
34
|
get isControl(): boolean;
|
|
35
35
|
/**
|
|
36
|
-
* Splits at a cell position. Returns [left, right]
|
|
36
|
+
* Splits at a cell position. Returns [left, right]: the first `position`
|
|
37
|
+
* cells and the rest, so the two halves are exactly as wide as this one —
|
|
38
|
+
* `cutCells` blanks a wide glyph the cut goes through rather than keep it
|
|
39
|
+
* whole on a side it would widen by a cell.
|
|
37
40
|
*
|
|
38
|
-
* [LAW:single-enforcer] The right half starts
|
|
39
|
-
* whatever drew it
|
|
40
|
-
* so; see `./anchor.ts`. That is not `leftText`'s width: a cut through a
|
|
41
|
-
* wide glyph pads the left half and keeps the whole glyph on the right.
|
|
41
|
+
* [LAW:single-enforcer] The right half starts `position` cells further into
|
|
42
|
+
* whatever drew it, and its anchor says so; see `./anchor.ts`.
|
|
42
43
|
*/
|
|
43
44
|
splitCells(position: CellCol): [Segment, Segment];
|
|
44
45
|
private static _line;
|
package/dist/core/segment.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Segment — the atomic rendering unit. Every piece of styled terminal output
|
|
3
3
|
* is represented as a Segment: (text, style?, control?).
|
|
4
4
|
*/
|
|
5
|
-
import { cellLen, splitText, asCellCol } from "./cells.js";
|
|
5
|
+
import { cellLen, cutCells, splitText, asCellCol } from "./cells.js";
|
|
6
6
|
import { Style } from "./style.js";
|
|
7
7
|
import { shiftAnchor } from "./anchor.js";
|
|
8
8
|
// --- ControlType ---
|
|
@@ -45,12 +45,13 @@ export class Segment {
|
|
|
45
45
|
return this.control !== undefined;
|
|
46
46
|
}
|
|
47
47
|
/**
|
|
48
|
-
* Splits at a cell position. Returns [left, right]
|
|
48
|
+
* Splits at a cell position. Returns [left, right]: the first `position`
|
|
49
|
+
* cells and the rest, so the two halves are exactly as wide as this one —
|
|
50
|
+
* `cutCells` blanks a wide glyph the cut goes through rather than keep it
|
|
51
|
+
* whole on a side it would widen by a cell.
|
|
49
52
|
*
|
|
50
|
-
* [LAW:single-enforcer] The right half starts
|
|
51
|
-
* whatever drew it
|
|
52
|
-
* so; see `./anchor.ts`. That is not `leftText`'s width: a cut through a
|
|
53
|
-
* wide glyph pads the left half and keeps the whole glyph on the right.
|
|
53
|
+
* [LAW:single-enforcer] The right half starts `position` cells further into
|
|
54
|
+
* whatever drew it, and its anchor says so; see `./anchor.ts`.
|
|
54
55
|
*/
|
|
55
56
|
splitCells(position) {
|
|
56
57
|
const len = this.cellLength;
|
|
@@ -58,10 +59,10 @@ export class Segment {
|
|
|
58
59
|
return [this, new Segment("")];
|
|
59
60
|
if (position <= 0)
|
|
60
61
|
return [new Segment(""), this];
|
|
61
|
-
const [leftText, rightText] =
|
|
62
|
+
const [leftText, rightText] = cutCells(this.text, position);
|
|
62
63
|
return [
|
|
63
64
|
new Segment(leftText, this.style),
|
|
64
|
-
new Segment(rightText, this.style?.shiftedBy(
|
|
65
|
+
new Segment(rightText, this.style?.shiftedBy(position)),
|
|
65
66
|
];
|
|
66
67
|
}
|
|
67
68
|
// --- Static factories ---
|
|
@@ -394,13 +395,8 @@ export class Segment {
|
|
|
394
395
|
for (const cut of cuts) {
|
|
395
396
|
const section = [];
|
|
396
397
|
while (currentSegment && cellOffset + currentSegment.cellLength <= cut) {
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
}
|
|
400
|
-
else {
|
|
401
|
-
section.push(currentSegment);
|
|
402
|
-
cellOffset += currentSegment.cellLength;
|
|
403
|
-
}
|
|
398
|
+
section.push(currentSegment);
|
|
399
|
+
cellOffset += currentSegment.cellLength;
|
|
404
400
|
segmentIndex++;
|
|
405
401
|
currentSegment = segments[segmentIndex];
|
|
406
402
|
}
|
package/dist/core/strip.d.ts
CHANGED
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
*/
|
|
28
28
|
import { Segment } from "./segment.js";
|
|
29
29
|
import { Style } from "./style.js";
|
|
30
|
-
import { type Renderable, type RenderOptions } from "./protocol.js";
|
|
30
|
+
import { type Measurable, type Renderable, type RenderOptions } from "./protocol.js";
|
|
31
31
|
/**
|
|
32
32
|
* Items in a `Strip` expose their per-edge style so joiners can paint the
|
|
33
33
|
* transition glyph at each boundary.
|
|
@@ -52,11 +52,22 @@ export interface Joiner<T extends StyledRenderable = StyledRenderable> {
|
|
|
52
52
|
*/
|
|
53
53
|
join(left: T | null, right: T | null): Renderable;
|
|
54
54
|
}
|
|
55
|
-
export declare class Strip<T extends StyledRenderable = StyledRenderable> implements Renderable {
|
|
55
|
+
export declare class Strip<T extends StyledRenderable = StyledRenderable> implements Renderable, Measurable {
|
|
56
56
|
readonly items: readonly T[];
|
|
57
57
|
readonly joiner: Joiner<T>;
|
|
58
58
|
constructor(items: readonly T[], joiner: Joiner<T>);
|
|
59
59
|
render(options: RenderOptions): Iterable<Segment>;
|
|
60
|
+
/**
|
|
61
|
+
* [LAW:one-source-of-truth] The width of the one row it draws, and that is
|
|
62
|
+
* both bounds: the row never wraps and no item is offered less than the whole
|
|
63
|
+
* width, so there is no narrower strip to report as a minimum. Read off the
|
|
64
|
+
* drawing rather than summed from the items' measurements, which describe
|
|
65
|
+
* how an item could be drawn, not how the walk draws it.
|
|
66
|
+
*/
|
|
67
|
+
measure(options: RenderOptions): {
|
|
68
|
+
minimum: number;
|
|
69
|
+
maximum: number;
|
|
70
|
+
};
|
|
60
71
|
}
|
|
61
72
|
/**
|
|
62
73
|
* The least ΔE_OK two neighbouring backgrounds must differ by for the powerline
|
package/dist/core/strip.js
CHANGED
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
*/
|
|
28
28
|
import { Segment } from "./segment.js";
|
|
29
29
|
import { Style } from "./style.js";
|
|
30
|
-
import { ColorDepth, ColorSpec,
|
|
30
|
+
import { ColorDepth, ColorSpec, SURFACE_BLACK } from "./color.js";
|
|
31
31
|
import { Oklch } from "./oklch.js";
|
|
32
32
|
import { drawable } from "./protocol.js";
|
|
33
33
|
// --- Strip ---
|
|
@@ -46,12 +46,17 @@ export class Strip {
|
|
|
46
46
|
// start-cap, item, mid-join, item, ..., item, end-cap. Variability lives
|
|
47
47
|
// in `items` and in what the joiner emits at each position — never in
|
|
48
48
|
// whether a join runs.
|
|
49
|
-
|
|
49
|
+
//
|
|
50
|
+
// The row sits its items side by side, so none is handed a justify: a
|
|
51
|
+
// `left` fills the canvas, and the first item would pad over the rest of
|
|
52
|
+
// the row. Where the row sits in the width is its container's to say.
|
|
53
|
+
const row = { ...options, justify: undefined };
|
|
54
|
+
yield* this.joiner.join(null, items[0]).render(row);
|
|
50
55
|
for (let i = 0; i < items.length; i++) {
|
|
51
56
|
const item = items[i];
|
|
52
|
-
yield* item.render(
|
|
57
|
+
yield* item.render(row);
|
|
53
58
|
const next = i + 1 < items.length ? items[i + 1] : null;
|
|
54
|
-
yield* this.joiner.join(item, next).render(
|
|
59
|
+
yield* this.joiner.join(item, next).render(row);
|
|
55
60
|
}
|
|
56
61
|
// [LAW:one-source-of-truth] `Strip`'s sibling `FlexStrip` ends every line
|
|
57
62
|
// it emits, including the last, and the other block-level renderables
|
|
@@ -61,11 +66,23 @@ export class Strip {
|
|
|
61
66
|
// under composition: printed alone, Console's own "close whatever the
|
|
62
67
|
// renderable left open" step (console.ts) papered over it, and a `Group`
|
|
63
68
|
// of a Strip and a RichText ran the RichText onto the Strip's line
|
|
64
|
-
// instead of below it.
|
|
65
|
-
//
|
|
66
|
-
// rich-flexstrip-5kf.4cq for the open question of formalizing that split.)
|
|
69
|
+
// instead of below it. `LINE_ENDS` in test/seam/line-ends.ts is the list
|
|
70
|
+
// of which renderables end their own line and which are fragments.
|
|
67
71
|
yield Segment.line();
|
|
68
72
|
}
|
|
73
|
+
/**
|
|
74
|
+
* [LAW:one-source-of-truth] The width of the one row it draws, and that is
|
|
75
|
+
* both bounds: the row never wraps and no item is offered less than the whole
|
|
76
|
+
* width, so there is no narrower strip to report as a minimum. Read off the
|
|
77
|
+
* drawing rather than summed from the items' measurements, which describe
|
|
78
|
+
* how an item could be drawn, not how the walk draws it.
|
|
79
|
+
*/
|
|
80
|
+
measure(options) {
|
|
81
|
+
let width = 0;
|
|
82
|
+
for (const segment of this.render(options))
|
|
83
|
+
width += segment.cellLength;
|
|
84
|
+
return { minimum: width, maximum: width };
|
|
85
|
+
}
|
|
69
86
|
}
|
|
70
87
|
// --- Helpers ---
|
|
71
88
|
const EMPTY = {
|
|
@@ -91,11 +108,11 @@ function bgAsFg(edge) {
|
|
|
91
108
|
function paintableBg(bg) {
|
|
92
109
|
return bg !== undefined && !bg.isDefault ? bg : undefined;
|
|
93
110
|
}
|
|
94
|
-
// A cap or
|
|
95
|
-
// it
|
|
96
|
-
// enters.
|
|
111
|
+
// A cap, arrow or gradient is its cell continuing: that cell's ground as the
|
|
112
|
+
// writer draws it (`Style.drawnColors`, onto the terminal's black), so a
|
|
113
|
+
// translucent ground is not composited a second time over whatever it enters.
|
|
97
114
|
function drawnGround(bg) {
|
|
98
|
-
return
|
|
115
|
+
return bg.flattenAlpha(SURFACE_BLACK);
|
|
99
116
|
}
|
|
100
117
|
// A cap is its glyph in its cell's ground, and "" is a flat end: no glyph, so
|
|
101
118
|
// no segment — the same output as a join with nothing to paint.
|
|
@@ -117,10 +134,12 @@ export const SEAM_MIN_DELTA_E = 0.04;
|
|
|
117
134
|
// encodes: the arrow's colour flattened onto its ground and the ground onto
|
|
118
135
|
// the terminal's black, both downgraded to the depth the render encodes at,
|
|
119
136
|
// so two grounds 256 colours round to one cube entry are the one entry they
|
|
120
|
-
// render as. A colour whose RGB is the terminal theme's own (ANSI 0–15
|
|
121
|
-
// value here, so two of them are the same only
|
|
122
|
-
//
|
|
123
|
-
// "color(1)", an EIGHT_BIT spec
|
|
137
|
+
// render as. A colour whose RGB is the terminal theme's own (ANSI 0–15, a
|
|
138
|
+
// blend reaching one) has no value here, so two of them are the same only
|
|
139
|
+
// when they are one expression: the same palette slot — its `number`, never
|
|
140
|
+
// the name or the depth that spells it ("red", "color(1)", an EIGHT_BIT spec
|
|
141
|
+
// on 0–15 are one slot) — or the same blend, whose name spells each end by its
|
|
142
|
+
// slot for the same reason.
|
|
124
143
|
function vanishes(arrow, colorSystem) {
|
|
125
144
|
// No colour emitted draws nothing to tell apart; measure what was handed.
|
|
126
145
|
const { color, bgcolor } = arrow.drawnColors(colorSystem ?? ColorDepth.TRUECOLOR);
|
|
@@ -130,7 +149,7 @@ function vanishes(arrow, colorSystem) {
|
|
|
130
149
|
const bv = bgcolor.fixedValue;
|
|
131
150
|
return av !== undefined && bv !== undefined
|
|
132
151
|
? Oklch.fromRgba(av).deltaE(Oklch.fromRgba(bv)) < SEAM_MIN_DELTA_E
|
|
133
|
-
: color.number === bgcolor.number;
|
|
152
|
+
: color.number !== undefined ? color.number === bgcolor.number : color.name === bgcolor.name;
|
|
134
153
|
}
|
|
135
154
|
/**
|
|
136
155
|
* The powerline set: U+E0B0 (right-arrow) divided by U+E0B1 (thin right-arrow),
|
|
@@ -275,17 +294,17 @@ export class GradientJoiner {
|
|
|
275
294
|
join(left, right) {
|
|
276
295
|
// [LAW:dataflow-not-control-flow] Endpoints have no opposite anchor to
|
|
277
296
|
// interpolate toward — the data (a missing neighbor) makes the gradient
|
|
278
|
-
// empty. Same for edges
|
|
297
|
+
// empty. Same for edges with nothing to paint: nothing to blend between.
|
|
279
298
|
if (left === null || right === null)
|
|
280
299
|
return EMPTY;
|
|
281
300
|
const steps = this._steps;
|
|
282
301
|
return deferred(function* (options) {
|
|
283
|
-
const lbg = left.edgeStyle("right", options).bgcolor;
|
|
284
|
-
const rbg = right.edgeStyle("left", options).bgcolor;
|
|
302
|
+
const lbg = paintableBg(left.edgeStyle("right", options).bgcolor);
|
|
303
|
+
const rbg = paintableBg(right.edgeStyle("left", options).bgcolor);
|
|
285
304
|
if (!lbg || !rbg)
|
|
286
305
|
return;
|
|
287
|
-
const
|
|
288
|
-
const
|
|
306
|
+
const from = drawnGround(lbg);
|
|
307
|
+
const to = drawnGround(rbg);
|
|
289
308
|
// An ASCII cell carries one sample, the background: the gradient keeps
|
|
290
309
|
// its colours at half the resolution.
|
|
291
310
|
const half = drawable(options, HALF_BLOCK, " ");
|
|
@@ -296,8 +315,10 @@ export class GradientJoiner {
|
|
|
296
315
|
for (let i = 0; i < steps; i++) {
|
|
297
316
|
const tLeft = (2 * i + 0.5) / samples;
|
|
298
317
|
const tRight = (2 * i + 1.5) / samples;
|
|
299
|
-
|
|
300
|
-
|
|
318
|
+
// Each sample stays a mix of the two grounds, so a theme that draws
|
|
319
|
+
// `blue` in its own shade draws the ramp from that shade.
|
|
320
|
+
const fg = ColorSpec.blend(from, to, tLeft);
|
|
321
|
+
const bg = ColorSpec.blend(from, to, tRight);
|
|
301
322
|
yield new Segment(half, new Style({ color: fg, bgcolor: bg }));
|
|
302
323
|
}
|
|
303
324
|
});
|