@promptctl/rich-js 0.24.1 → 0.25.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 +7 -0
- package/dist/core/anchor.d.ts +5 -0
- package/dist/core/anchor.js +11 -4
- package/dist/core/animation.d.ts +175 -10
- package/dist/core/animation.js +283 -15
- package/dist/core/ansi.js +2 -2
- package/dist/core/box.d.ts +7 -7
- package/dist/core/cells.d.ts +23 -13
- package/dist/core/cells.js +58 -48
- package/dist/core/clock.d.ts +2 -2
- package/dist/core/clock.js +6 -4
- package/dist/core/color.d.ts +27 -10
- package/dist/core/color.js +21 -29
- package/dist/core/console.d.ts +28 -17
- package/dist/core/console.js +44 -39
- package/dist/core/easing.d.ts +30 -0
- package/dist/core/easing.js +65 -20
- package/dist/core/emoji.d.ts +1 -1
- package/dist/core/emoji.js +1 -2
- package/dist/core/export-html.js +2 -1
- package/dist/core/export-lines.js +3 -3
- package/dist/core/highlighter.d.ts +9 -8
- package/dist/core/highlighter.js +3 -2
- package/dist/core/json.js +2 -2
- package/dist/core/markup.js +16 -12
- package/dist/core/measure.js +2 -2
- package/dist/core/memo.d.ts +11 -2
- package/dist/core/memo.js +16 -0
- package/dist/core/oklch.js +17 -6
- package/dist/core/osc8.d.ts +1 -1
- package/dist/core/osc8.js +4 -5
- package/dist/core/paint.d.ts +2 -1
- package/dist/core/paint.js +4 -3
- package/dist/core/pattern.d.ts +21 -0
- package/dist/core/pattern.js +21 -0
- package/dist/core/pretty.js +9 -10
- package/dist/core/protocol.d.ts +100 -39
- package/dist/core/protocol.js +167 -21
- package/dist/core/render.d.ts +15 -2
- package/dist/core/render.js +25 -5
- package/dist/core/segment.d.ts +13 -5
- package/dist/core/segment.js +23 -9
- package/dist/core/spinnerData.d.ts +1 -1
- package/dist/core/state.d.ts +53 -0
- package/dist/core/state.js +82 -0
- package/dist/core/strip.d.ts +13 -16
- package/dist/core/strip.js +119 -110
- package/dist/core/style.d.ts +47 -14
- package/dist/core/style.js +57 -28
- package/dist/core/text.d.ts +5 -8
- package/dist/core/text.js +116 -124
- package/dist/core/timeline.d.ts +30 -5
- package/dist/core/timeline.js +43 -7
- package/dist/core/tween.d.ts +1 -1
- package/dist/core/tween.js +5 -3
- package/dist/core/view.d.ts +92 -0
- package/dist/core/view.js +83 -0
- package/dist/host/app.d.ts +49 -21
- package/dist/host/app.js +71 -37
- package/dist/host/input.js +3 -4
- package/dist/index.d.ts +24 -12
- package/dist/index.js +9 -4
- package/dist/node/traceback.js +4 -3
- package/dist/renderables/align.d.ts +2 -3
- package/dist/renderables/align.js +0 -3
- package/dist/renderables/change.d.ts +42 -0
- package/dist/renderables/change.js +112 -0
- package/dist/renderables/columns.d.ts +3 -4
- package/dist/renderables/columns.js +2 -7
- package/dist/renderables/constrain.d.ts +2 -3
- package/dist/renderables/constrain.js +0 -3
- package/dist/renderables/effect.d.ts +132 -54
- package/dist/renderables/effect.js +188 -115
- package/dist/renderables/effects.d.ts +122 -11
- package/dist/renderables/effects.js +238 -94
- package/dist/renderables/embed.d.ts +1 -1
- package/dist/renderables/embed.js +16 -8
- package/dist/renderables/flexStrip.d.ts +2 -3
- package/dist/renderables/flexStrip.js +0 -4
- package/dist/renderables/frames.d.ts +35 -54
- package/dist/renderables/frames.js +43 -58
- package/dist/renderables/glyph-effect.d.ts +158 -0
- package/dist/renderables/glyph-effect.js +237 -0
- package/dist/renderables/glyph-effects.d.ts +147 -0
- package/dist/renderables/glyph-effects.js +283 -0
- package/dist/renderables/group.d.ts +3 -4
- package/dist/renderables/group.js +0 -3
- package/dist/renderables/layout.d.ts +12 -12
- package/dist/renderables/layout.js +40 -46
- package/dist/renderables/lifecycle.d.ts +77 -0
- package/dist/renderables/lifecycle.js +132 -0
- package/dist/renderables/list-view.d.ts +4 -5
- package/dist/renderables/list-view.js +9 -13
- package/dist/renderables/live.d.ts +36 -21
- package/dist/renderables/live.js +88 -41
- package/dist/renderables/markdown-parse.d.ts +4 -4
- package/dist/renderables/markdown-parse.js +52 -46
- package/dist/renderables/markdown-url.js +11 -11
- package/dist/renderables/padding.d.ts +4 -5
- package/dist/renderables/padding.js +0 -3
- package/dist/renderables/panel.d.ts +14 -16
- package/dist/renderables/panel.js +6 -14
- package/dist/renderables/progress.d.ts +83 -34
- package/dist/renderables/progress.js +182 -95
- package/dist/renderables/progressBar.d.ts +22 -12
- package/dist/renderables/progressBar.js +32 -9
- package/dist/renderables/prompt.js +3 -2
- package/dist/renderables/python-space.js +2 -1
- package/dist/renderables/scroll.d.ts +50 -0
- package/dist/renderables/scroll.js +106 -0
- package/dist/renderables/spinner.d.ts +35 -14
- package/dist/renderables/spinner.js +65 -25
- package/dist/renderables/status.js +1 -1
- package/dist/renderables/syntax.d.ts +10 -9
- package/dist/renderables/syntax.js +3 -2
- package/dist/renderables/table.d.ts +34 -40
- package/dist/renderables/table.js +41 -63
- package/dist/renderables/traceback.d.ts +2 -2
- package/dist/renderables/traceback.js +6 -5
- package/dist/renderables/tree.d.ts +27 -25
- package/dist/renderables/tree.js +104 -58
- package/dist/renderables/viewport.d.ts +75 -44
- package/dist/renderables/viewport.js +145 -82
- package/dist/template-bindings/animation-funcs.d.ts +78 -9
- package/dist/template-bindings/animation-funcs.js +132 -14
- package/dist/template-bindings/index.d.ts +5 -5
- package/dist/template-bindings/index.js +5 -5
- package/dist/themes/ramp.d.ts +46 -3
- package/dist/themes/ramp.js +61 -4
- package/dist/themes/registry.js +5 -7
- package/dist/widgets/button.js +3 -2
- package/dist/widgets/checkbox.js +4 -3
- package/dist/widgets/dropdown.d.ts +3 -1
- package/dist/widgets/dropdown.js +20 -15
- package/dist/widgets/event-router.js +4 -2
- package/dist/widgets/focus-manager.d.ts +2 -0
- package/dist/widgets/focus-manager.js +6 -4
- package/dist/widgets/hit.d.ts +6 -0
- package/dist/widgets/hit.js +8 -1
- package/dist/widgets/observable.d.ts +23 -0
- package/dist/widgets/observable.js +43 -0
- package/dist/widgets/slider.d.ts +1 -1
- package/dist/widgets/slider.js +12 -3
- package/dist/widgets/static-item.d.ts +2 -3
- package/dist/widgets/static-item.js +0 -3
- package/dist/widgets/text-input.d.ts +28 -32
- package/dist/widgets/text-input.js +360 -346
- package/dist/widgets/themed-widget.js +2 -1
- package/dist/widgets/toggle.js +4 -3
- package/dist/widgets/types.d.ts +10 -4
- package/dist/widgets/types.js +8 -2
- package/dist/widgets/widget-app.d.ts +14 -4
- package/dist/widgets/widget-app.js +36 -7
- package/dist/widgets/widget-base.d.ts +2 -2
- package/dist/widgets/widget-base.js +29 -18
- package/package.json +8 -1
package/dist/core/memo.d.ts
CHANGED
|
@@ -12,8 +12,17 @@ export declare const MEMO_MAX = 4096;
|
|
|
12
12
|
* be a pure function of `key`, so a value recomputed after a clear is the one
|
|
13
13
|
* that was dropped.
|
|
14
14
|
*/
|
|
15
|
-
export declare class Memo<V extends NonNullable<unknown
|
|
15
|
+
export declare class Memo<V extends NonNullable<unknown>, K extends string = string> {
|
|
16
16
|
private readonly entries;
|
|
17
17
|
get size(): number;
|
|
18
|
-
get(key:
|
|
18
|
+
get(key: K, compute: (key: K) => V): V;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* `Memo`'s contract for a key that is an object rather than a string: a
|
|
22
|
+
* value computed once per key, for as long as the key lives. Held weakly, so
|
|
23
|
+
* it needs no bound — a key a host stops holding takes its entry with it.
|
|
24
|
+
*/
|
|
25
|
+
export declare class WeakMemo<V extends NonNullable<unknown>, K extends object> {
|
|
26
|
+
private readonly entries;
|
|
27
|
+
get(key: K, compute: (key: K) => V): V;
|
|
19
28
|
}
|
package/dist/core/memo.js
CHANGED
|
@@ -32,3 +32,19 @@ export class Memo {
|
|
|
32
32
|
return value;
|
|
33
33
|
}
|
|
34
34
|
}
|
|
35
|
+
/**
|
|
36
|
+
* `Memo`'s contract for a key that is an object rather than a string: a
|
|
37
|
+
* value computed once per key, for as long as the key lives. Held weakly, so
|
|
38
|
+
* it needs no bound — a key a host stops holding takes its entry with it.
|
|
39
|
+
*/
|
|
40
|
+
export class WeakMemo {
|
|
41
|
+
entries = new WeakMap();
|
|
42
|
+
get(key, compute) {
|
|
43
|
+
const remembered = this.entries.get(key);
|
|
44
|
+
if (remembered !== undefined)
|
|
45
|
+
return remembered;
|
|
46
|
+
const value = compute(key);
|
|
47
|
+
this.entries.set(key, value);
|
|
48
|
+
return value;
|
|
49
|
+
}
|
|
50
|
+
}
|
package/dist/core/oklch.js
CHANGED
|
@@ -96,6 +96,11 @@ function inGamut(r, g, b) {
|
|
|
96
96
|
g >= -GAMUT_EPS && g <= 1 + GAMUT_EPS &&
|
|
97
97
|
b >= -GAMUT_EPS && b <= 1 + GAMUT_EPS);
|
|
98
98
|
}
|
|
99
|
+
/** Polar (C, h°) → OKLab (a, b): the one spelling `deltaE` measures by and `toRgba` draws by. */
|
|
100
|
+
function labOf(c, h) {
|
|
101
|
+
const hRad = h * (Math.PI / 180);
|
|
102
|
+
return [c * Math.cos(hRad), c * Math.sin(hRad)];
|
|
103
|
+
}
|
|
99
104
|
// ---------------------------------------------------------------------------
|
|
100
105
|
// Oklch — the value type.
|
|
101
106
|
// ---------------------------------------------------------------------------
|
|
@@ -130,6 +135,7 @@ export class Oklch {
|
|
|
130
135
|
if (!Number.isFinite(alpha) || alpha < 0 || alpha > 1) {
|
|
131
136
|
throw new RangeError(`Oklch.alpha must be a finite number in [0,1]; got ${alpha}`);
|
|
132
137
|
}
|
|
138
|
+
this.#lab = labOf(c, h);
|
|
133
139
|
}
|
|
134
140
|
/** sRGB → linear → OKLab → polar. Pure; no I/O, no allocation beyond the result. */
|
|
135
141
|
static fromRgba(color) {
|
|
@@ -265,16 +271,21 @@ export class Oklch {
|
|
|
265
271
|
* Symmetric and pure.
|
|
266
272
|
*/
|
|
267
273
|
deltaE(other) {
|
|
268
|
-
const a =
|
|
269
|
-
const
|
|
270
|
-
return Math.hypot(this.l - other.l,
|
|
274
|
+
const [a, b] = this.#lab;
|
|
275
|
+
const [otherA, otherB] = other.#lab;
|
|
276
|
+
return Math.hypot(this.l - other.l, a - otherA, b - otherB);
|
|
271
277
|
}
|
|
278
|
+
/**
|
|
279
|
+
* This colour's OKLab a and b, kept with it: a colour matched against a
|
|
280
|
+
* palette is measured against every entry, and each entry against every
|
|
281
|
+
* colour it is matched with, so the trigonometry is paid once a colour
|
|
282
|
+
* rather than once a comparison.
|
|
283
|
+
*/
|
|
284
|
+
#lab;
|
|
272
285
|
/** Linear-sRGB coordinates for an explicit (l, C, h). Pure; `toRgba` passes
|
|
273
286
|
* already-normalized values so this never sees out-of-range inputs. */
|
|
274
287
|
toLinearRgb(l, C, h) {
|
|
275
|
-
const
|
|
276
|
-
const aLab = C * Math.cos(hRad);
|
|
277
|
-
const bLab = C * Math.sin(hRad);
|
|
288
|
+
const [aLab, bLab] = labOf(C, h);
|
|
278
289
|
const lCube = l + 0.3963377774 * aLab + 0.2158037573 * bLab;
|
|
279
290
|
const mCube = l - 0.1055613458 * aLab - 0.0638541728 * bLab;
|
|
280
291
|
const sCube = l - 0.0894841775 * aLab - 1.2914855480 * bLab;
|
package/dist/core/osc8.d.ts
CHANGED
|
@@ -49,7 +49,7 @@ export declare const OSC8_CLOSE = "\u001B]8;;\u001B\\";
|
|
|
49
49
|
* on a close. To read bytes back use `osc8Sequences`; this pattern is exported
|
|
50
50
|
* for composing into a larger one (a width measure's zero-width alternation).
|
|
51
51
|
*/
|
|
52
|
-
export declare const OSC8:
|
|
52
|
+
export declare const OSC8: import("./pattern.js").Pattern;
|
|
53
53
|
/** One OSC 8 sequence found in rendered bytes. A close has `uri === ""`. */
|
|
54
54
|
export interface Osc8Sequence {
|
|
55
55
|
/** Offset of the sequence in the scanned string. */
|
package/dist/core/osc8.js
CHANGED
|
@@ -31,15 +31,15 @@
|
|
|
31
31
|
* exact, not approximate.
|
|
32
32
|
*/
|
|
33
33
|
import { fnv1a } from "./fnv1a.js";
|
|
34
|
+
import { pattern } from "./pattern.js";
|
|
34
35
|
/**
|
|
35
36
|
* The bytes that prematurely terminate an OSC 8 sequence: ESC (`\x1b`, which
|
|
36
37
|
* begins ST `ESC \`), BEL (`\x07`), and the 8-bit ST (`\x9c`).
|
|
37
38
|
*/
|
|
38
39
|
const TERMINATORS = "\\x1b\\x07\\x9c";
|
|
39
|
-
const TERMINATOR_RE = new RegExp(`[${TERMINATORS}]`, "g");
|
|
40
40
|
/** Remove every byte that could break a URL out of its OSC 8 wrap. */
|
|
41
41
|
export function stripOscTerminators(url) {
|
|
42
|
-
return url.replace(
|
|
42
|
+
return url.replace(new RegExp(`[${TERMINATORS}]`, "g"), "");
|
|
43
43
|
}
|
|
44
44
|
/**
|
|
45
45
|
* The bytes that open a hyperlink to `url`.
|
|
@@ -62,14 +62,13 @@ export const OSC8_CLOSE = "\x1b]8;;\x1b\\";
|
|
|
62
62
|
* on a close. To read bytes back use `osc8Sequences`; this pattern is exported
|
|
63
63
|
* for composing into a larger one (a width measure's zero-width alternation).
|
|
64
64
|
*/
|
|
65
|
-
export const OSC8 = new RegExp(`\\x1b\\]8;([^;${TERMINATORS}]*);([^${TERMINATORS}]*)(?:\\x1b\\\\|\\x07|\\x9c)`);
|
|
66
|
-
const OSC8_SCAN = new RegExp(OSC8.source, "g");
|
|
65
|
+
export const OSC8 = pattern(new RegExp(`\\x1b\\]8;([^;${TERMINATORS}]*);([^${TERMINATORS}]*)(?:\\x1b\\\\|\\x07|\\x9c)`));
|
|
67
66
|
/**
|
|
68
67
|
* Every OSC 8 sequence in `text`, in order — the typed way to read rendered
|
|
69
68
|
* bytes back.
|
|
70
69
|
*/
|
|
71
70
|
export function osc8Sequences(text) {
|
|
72
|
-
return [...text.matchAll(
|
|
71
|
+
return [...text.matchAll(new RegExp(OSC8.source, "g"))].map((m) => ({
|
|
73
72
|
index: m.index,
|
|
74
73
|
length: m[0].length,
|
|
75
74
|
params: m[1],
|
package/dist/core/paint.d.ts
CHANGED
|
@@ -71,7 +71,8 @@ export declare class SurfacePainter implements Painter {
|
|
|
71
71
|
* Paint `frame` over the last frame, leaving the cursor on its last row,
|
|
72
72
|
* and return it as painted. No row is wider than the screen, since one that
|
|
73
73
|
* soft-wrapped would push every row below it down a row the next paint does
|
|
74
|
-
* not go back over.
|
|
74
|
+
* not go back over. The rows returned are what `destination` shows
|
|
75
|
+
* (`shownOn`): under reduced motion, nothing in them blinks.
|
|
75
76
|
*/
|
|
76
77
|
paint(frame: FrameSource, screen: Screen, destination: Destination): Segment[][];
|
|
77
78
|
/**
|
package/dist/core/paint.js
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
* the last row, and the newline that ends it is written once, when the frame
|
|
14
14
|
* is handed back.
|
|
15
15
|
*/
|
|
16
|
-
import { segmentsToString } from "./render.js";
|
|
16
|
+
import { segmentsToString, shownOn } from "./render.js";
|
|
17
17
|
import { Segment } from "./segment.js";
|
|
18
18
|
const GEOMETRY = {
|
|
19
19
|
alternate: {
|
|
@@ -84,10 +84,11 @@ export class SurfacePainter {
|
|
|
84
84
|
* Paint `frame` over the last frame, leaving the cursor on its last row,
|
|
85
85
|
* and return it as painted. No row is wider than the screen, since one that
|
|
86
86
|
* soft-wrapped would push every row below it down a row the next paint does
|
|
87
|
-
* not go back over.
|
|
87
|
+
* not go back over. The rows returned are what `destination` shows
|
|
88
|
+
* (`shownOn`): under reduced motion, nothing in them blinks.
|
|
88
89
|
*/
|
|
89
90
|
paint(frame, screen, destination) {
|
|
90
|
-
const rows = fitRows(frame(), screen);
|
|
91
|
+
const rows = fitRows(frame(), screen).map((row) => row.map((segment) => shownOn(segment, destination)));
|
|
91
92
|
this.write(SYNC_START + this.over(rows, screen, destination) + SYNC_END);
|
|
92
93
|
return rows;
|
|
93
94
|
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* pattern — `Pattern`: a regular expression held where every caller reaches
|
|
3
|
+
* it — module scope, a class static, a renderable's field — with no cursor
|
|
4
|
+
* for them to share.
|
|
5
|
+
*
|
|
6
|
+
* A `RegExp` carries `lastIndex`, and the `exec`, `test` and `replace` of a
|
|
7
|
+
* global or sticky one write it, so a global regex held at module scope is
|
|
8
|
+
* state that every caller moves. A regex with neither flag has a `lastIndex`
|
|
9
|
+
* that `exec`, `test`, `match`, `replace` and `split` never write. `pattern`
|
|
10
|
+
* is where that is checked: it refuses `g` and `y`, and hands back only
|
|
11
|
+
* those, with `source` and `flags` to compose it into another. A cursor — a
|
|
12
|
+
* sticky scan, a global replace — is made in the function that runs it, so
|
|
13
|
+
* each call owns its own.
|
|
14
|
+
*/
|
|
15
|
+
declare const PATTERN: unique symbol;
|
|
16
|
+
/** A regex with no cursor: only `pattern` makes one. */
|
|
17
|
+
export type Pattern = Pick<RegExp, "exec" | "test" | "source" | "flags" | typeof Symbol.match | typeof Symbol.replace | typeof Symbol.split> & {
|
|
18
|
+
readonly [PATTERN]: true;
|
|
19
|
+
};
|
|
20
|
+
export declare function pattern(re: RegExp): Pattern;
|
|
21
|
+
export {};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* pattern — `Pattern`: a regular expression held where every caller reaches
|
|
3
|
+
* it — module scope, a class static, a renderable's field — with no cursor
|
|
4
|
+
* for them to share.
|
|
5
|
+
*
|
|
6
|
+
* A `RegExp` carries `lastIndex`, and the `exec`, `test` and `replace` of a
|
|
7
|
+
* global or sticky one write it, so a global regex held at module scope is
|
|
8
|
+
* state that every caller moves. A regex with neither flag has a `lastIndex`
|
|
9
|
+
* that `exec`, `test`, `match`, `replace` and `split` never write. `pattern`
|
|
10
|
+
* is where that is checked: it refuses `g` and `y`, and hands back only
|
|
11
|
+
* those, with `source` and `flags` to compose it into another. A cursor — a
|
|
12
|
+
* sticky scan, a global replace — is made in the function that runs it, so
|
|
13
|
+
* each call owns its own.
|
|
14
|
+
*/
|
|
15
|
+
export function pattern(re) {
|
|
16
|
+
// [LAW:no-silent-failure] A held cursor is refused where it is made, not found later as a match that began mid-string.
|
|
17
|
+
if (re.global || re.sticky) {
|
|
18
|
+
throw new TypeError(`/${re.source}/${re.flags} keeps a cursor in lastIndex; make it in the function that runs it`);
|
|
19
|
+
}
|
|
20
|
+
return re;
|
|
21
|
+
}
|
package/dist/core/pretty.js
CHANGED
|
@@ -18,10 +18,12 @@
|
|
|
18
18
|
*/
|
|
19
19
|
import { asCellCol, cellColToCodeUnitOffset, cellLen, graphemes, splitAtCells } from "./cells.js";
|
|
20
20
|
import { divideLine } from "./wrap.js";
|
|
21
|
+
import { Memo } from "./memo.js";
|
|
21
22
|
import { Segment } from "./segment.js";
|
|
22
23
|
import { RichText, stripControlChars } from "./text.js";
|
|
23
24
|
import { ReprHighlighter } from "./highlighter.js";
|
|
24
25
|
import { drawable, render } from "./protocol.js";
|
|
26
|
+
import { pattern } from "./pattern.js";
|
|
25
27
|
const reprHighlighter = new ReprHighlighter();
|
|
26
28
|
/**
|
|
27
29
|
* What a value is bounded by when whoever formats it has not seen it:
|
|
@@ -453,7 +455,7 @@ const elided = (dropped) => dropped > 0 ? [{ head: `... +${dropped}`, join: "",
|
|
|
453
455
|
* An index is at most 15 digits: every such literal is a safe integer and
|
|
454
456
|
* names itself, where `99999999999999999999` would name `1e20`'s key.
|
|
455
457
|
*/
|
|
456
|
-
const BARE_KEY = /^(?![^]*\p{Default_Ignorable_Code_Point})(?:[\p{ID_Start}$_][\p{ID_Continue}$]*|0|[1-9]\d{0,14})$/u;
|
|
458
|
+
const BARE_KEY = pattern(/^(?![^]*\p{Default_Ignorable_Code_Point})(?:[\p{ID_Start}$_][\p{ID_Continue}$]*|0|[1-9]\d{0,14})$/u);
|
|
457
459
|
/**
|
|
458
460
|
* What Python's `str.isprintable` refuses \u2014 control, format, private-use,
|
|
459
461
|
* unassigned, and every separator but the space \u2014 which Rich's repr escapes
|
|
@@ -461,9 +463,9 @@ const BARE_KEY = /^(?![^]*\p{Default_Ignorable_Code_Point})(?:[\p{ID_Start}$_][\
|
|
|
461
463
|
* byte reaches the terminal as a control, and a format character hides or
|
|
462
464
|
* reorders what is around it.
|
|
463
465
|
*/
|
|
464
|
-
const
|
|
466
|
+
const unprintable = () => /(?! )[\p{Cc}\p{Cf}\p{Co}\p{Cn}\p{Zl}\p{Zp}\p{Zs}]/gu;
|
|
465
467
|
/** `value` in double quotes, every character that does not print escaped. */
|
|
466
|
-
const quoted = (value) => JSON.stringify(value).replace(
|
|
468
|
+
const quoted = (value) => JSON.stringify(value).replace(unprintable(), (c) => {
|
|
467
469
|
const hex = c.codePointAt(0).toString(16).padStart(4, "0");
|
|
468
470
|
return hex.length > 4 ? `\\u{${hex}}` : `\\u${hex}`;
|
|
469
471
|
});
|
|
@@ -477,7 +479,7 @@ export class Pretty {
|
|
|
477
479
|
indentGuides;
|
|
478
480
|
highlighter;
|
|
479
481
|
// [LAW:no-shared-mutable-globals] A private memo, written only by `_cutString`.
|
|
480
|
-
cutStrings = new
|
|
482
|
+
cutStrings = new Memo();
|
|
481
483
|
constructor(data, options) {
|
|
482
484
|
this.data = data;
|
|
483
485
|
this.indent = options?.indent ?? 4;
|
|
@@ -595,15 +597,12 @@ export class Pretty {
|
|
|
595
597
|
* memoised per value.
|
|
596
598
|
*/
|
|
597
599
|
_cutString(value, max) {
|
|
598
|
-
|
|
599
|
-
if (cut === undefined) {
|
|
600
|
+
return this.cutStrings.get(value, () => {
|
|
600
601
|
const clusters = graphemes(value);
|
|
601
|
-
|
|
602
|
+
return clusters.length <= max
|
|
602
603
|
? quoted(value)
|
|
603
604
|
: quoted(clusters.slice(0, max).join("")) + `+${clusters.length - max}`;
|
|
604
|
-
|
|
605
|
-
}
|
|
606
|
-
return cut;
|
|
605
|
+
});
|
|
607
606
|
}
|
|
608
607
|
/**
|
|
609
608
|
* A container, or the text standing in for one that is empty or past the
|
package/dist/core/protocol.d.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* [LAW:one-source-of-truth] These interfaces are the single authority for the rendering contract.
|
|
4
4
|
*/
|
|
5
5
|
import type { Segment } from "./segment.js";
|
|
6
|
-
import { Style, type StyleSyntaxError, type Theme } from "./style.js";
|
|
6
|
+
import { Style, type Hold, type StyleSyntaxError, type Theme } from "./style.js";
|
|
7
7
|
import { ColorDepth } from "./color.js";
|
|
8
8
|
import type { Highlighter } from "./highlighter.js";
|
|
9
9
|
/**
|
|
@@ -17,6 +17,15 @@ import type { Highlighter } from "./highlighter.js";
|
|
|
17
17
|
* wrapping and leaves the edge, so a line too wide is cut by the method here.
|
|
18
18
|
*/
|
|
19
19
|
export type OverflowMethod = "fold" | "crop" | "ellipsis" | "ignore";
|
|
20
|
+
/**
|
|
21
|
+
* What draws frames — a `Live`, an `App` — as what draws in them sees it:
|
|
22
|
+
* the time a frame drawn now is drawn for, the host's clock read through its
|
|
23
|
+
* timeline for a view on it (`frameTime`, `./view.ts`). Not a `Clock`: under
|
|
24
|
+
* a paused timeline it holds, and under a reversed one it runs backwards.
|
|
25
|
+
*/
|
|
26
|
+
export interface FrameHost {
|
|
27
|
+
readonly frameTime: number;
|
|
28
|
+
}
|
|
20
29
|
export interface RenderOptions {
|
|
21
30
|
/**
|
|
22
31
|
* The cells this renderable may occupy. A count of cells, so a non-negative
|
|
@@ -68,8 +77,8 @@ export interface RenderOptions {
|
|
|
68
77
|
highlighter?: Highlighter;
|
|
69
78
|
/**
|
|
70
79
|
* The depth the output will be encoded at, `null` when it carries no colour.
|
|
71
|
-
* A renderable
|
|
72
|
-
*
|
|
80
|
+
* A renderable that measures what it draws — the strip's joiner, holding a
|
|
81
|
+
* seam's ink off its ground — measures the colours the terminal will draw at
|
|
73
82
|
* this depth, which at 256 colours or fewer are not the colours it was
|
|
74
83
|
* handed. Absent means truecolor.
|
|
75
84
|
*/
|
|
@@ -86,17 +95,52 @@ export interface RenderOptions {
|
|
|
86
95
|
* the render strict: the error leaves `render`.
|
|
87
96
|
*/
|
|
88
97
|
onStyleError?: StyleErrorHandler;
|
|
98
|
+
/**
|
|
99
|
+
* The reader asked for reduced motion — CSS's `prefers-reduced-motion`,
|
|
100
|
+
* which a terminal has no way to report, so a host is told. A renderable
|
|
101
|
+
* built for the time the host draws at draws every animation reduced, its
|
|
102
|
+
* motion taken out and its content kept on time (`shownAt`,
|
|
103
|
+
* `./view.ts`; `Animation.reduced`, `./animation.ts`). Absent,
|
|
104
|
+
* everything moves. Nothing here reads it from the environment.
|
|
105
|
+
*/
|
|
106
|
+
reducedMotion?: boolean;
|
|
107
|
+
/**
|
|
108
|
+
* The frame being drawn: `time`, the moment it is drawn for, in seconds, on
|
|
109
|
+
* the clock its cells' `nextChange` stamps are read on, and `host`, the
|
|
110
|
+
* host drawing it, whose `frameTime` that moment was. It is how a
|
|
111
|
+
* renderable that lives across frames — a widget, a `Progress` — draws for
|
|
112
|
+
* the time without being rebuilt each frame with one, and without reading
|
|
113
|
+
* a clock. Absent, the frame is drawn for no moment — a `print` — and
|
|
114
|
+
* nothing drawn from it moves.
|
|
115
|
+
*/
|
|
116
|
+
frame?: {
|
|
117
|
+
readonly time: number;
|
|
118
|
+
readonly host: FrameHost;
|
|
119
|
+
};
|
|
89
120
|
/**
|
|
90
121
|
* Told of every renderable `render` is asked for, as it is asked, so the
|
|
91
|
-
* calls arrive in document order: a container before what it holds,
|
|
92
|
-
*
|
|
122
|
+
* calls arrive in document order: a container before what it holds, what
|
|
123
|
+
* it holds in the order it renders it. `"drawn"` is a renderable drawn at
|
|
93
124
|
* the width it was handed; `"held"` is one handed no column, which is still
|
|
94
125
|
* in the view but has nothing drawn for it (`render`). A renderable rendered
|
|
95
126
|
* twice is told twice. A render made to measure is not drawing and is not
|
|
96
127
|
* told (`measuring`).
|
|
97
128
|
*/
|
|
98
129
|
onPresent?: (renderable: Renderable, presence: Presence) => void;
|
|
130
|
+
/**
|
|
131
|
+
* Told of a hold the frame is in that no cell of it may carry: an animation
|
|
132
|
+
* placed in the view reports its own, whether or not it drew a cell — one
|
|
133
|
+
* not yet entered draws none — and one a container crops away is still
|
|
134
|
+
* reported. It reaches whatever `frame` reaches, so a container that hands
|
|
135
|
+
* its child the frame's time hands back the child's hold with no word about
|
|
136
|
+
* either. [LAW:composability] Absent, nobody is listening. A measurement
|
|
137
|
+
* (`measuring`) and a held render (`holding`) tell too: what they find
|
|
138
|
+
* decides the frame's layout, so when it changes, the frame may.
|
|
139
|
+
*/
|
|
140
|
+
onHold?: (hold: Hold) => void;
|
|
99
141
|
}
|
|
142
|
+
/** The one field of the render options that says whether the reader wants motion. */
|
|
143
|
+
export type Motion = Pick<RenderOptions, "reducedMotion">;
|
|
100
144
|
/**
|
|
101
145
|
* Whether a renderable the view holds was drawn, or held: in the view, handed
|
|
102
146
|
* no column, and so not drawn (`render`).
|
|
@@ -251,6 +295,39 @@ export declare function withCellWidth(options: RenderOptions): RenderOptions;
|
|
|
251
295
|
export declare function drawsNothing(options: {
|
|
252
296
|
readonly maxWidth: number;
|
|
253
297
|
}): boolean;
|
|
298
|
+
/**
|
|
299
|
+
* One render pass: the outermost `render` or `measure` call — a host's frame,
|
|
300
|
+
* a print — and every `next()` of what it returned, at any depth. Opaque: it
|
|
301
|
+
* is only an identity, which a `State` compares to say whether this pass made
|
|
302
|
+
* it.
|
|
303
|
+
*/
|
|
304
|
+
export interface Pass {
|
|
305
|
+
readonly __pass: unique symbol;
|
|
306
|
+
}
|
|
307
|
+
/**
|
|
308
|
+
* `handler` run as if between passes: a callback a render makes to user code to
|
|
309
|
+
* report something it met, such as `onStyleError`, is an event handler, and may
|
|
310
|
+
* write what one may — print to a console, set a `State` — as React lets an
|
|
311
|
+
* event handler set state.
|
|
312
|
+
*/
|
|
313
|
+
export declare function betweenPasses<T>(handler: () => T): T;
|
|
314
|
+
/** The pass a render or measure in progress belongs to, or `undefined` between passes. */
|
|
315
|
+
export declare function activePass(): Pass | undefined;
|
|
316
|
+
/**
|
|
317
|
+
* `effect` run once the render pass in progress has drawn its last segment,
|
|
318
|
+
* between passes, where it may write what an event handler may: React's commit
|
|
319
|
+
* phase. A render that learns something only drawing can tell it — the lines
|
|
320
|
+
* content shown through a `Viewport` runs to at the width it was handed —
|
|
321
|
+
* draws from what it learned and hands the commit here, rather than writing it
|
|
322
|
+
* as it draws.
|
|
323
|
+
*
|
|
324
|
+
* Effects run in the order they were handed over, which is document order. A
|
|
325
|
+
* measurement commits nothing, and neither does a pass that throws or whose
|
|
326
|
+
* caller stops iterating it early: nobody saw what it drew. Outside a pass — a
|
|
327
|
+
* renderable's `render` called directly — there is nothing to wait for, and
|
|
328
|
+
* `effect` runs now.
|
|
329
|
+
*/
|
|
330
|
+
export declare function afterPass(effect: () => void): void;
|
|
254
331
|
/**
|
|
255
332
|
* `renderable` drawn under `options`: Rich's `Console.render`, which every
|
|
256
333
|
* renderable there is drawn through, so under one column nothing is.
|
|
@@ -262,25 +339,30 @@ export declare function drawsNothing(options: {
|
|
|
262
339
|
* this, each container asked `drawsNothing` itself, and the ones that forgot
|
|
263
340
|
* drew a child handed no column anyway.
|
|
264
341
|
*
|
|
342
|
+
* It is where a render pass is entered (`Pass`), so it is the one place a
|
|
343
|
+
* write during render is refused at: a `State` set in a pass that did not make
|
|
344
|
+
* it throws (`./state.ts`).
|
|
345
|
+
*
|
|
265
346
|
* It is also the one reporter of what the view holds (`onPresent`). Under one
|
|
266
|
-
* column a renderable is held,
|
|
267
|
-
*
|
|
268
|
-
*
|
|
269
|
-
*
|
|
270
|
-
*
|
|
271
|
-
* instead, and dropping what it drew, did commit that state.
|
|
347
|
+
* column a renderable is held: in the view, with nothing of it on the frame.
|
|
348
|
+
* It is drawn all the same and what it drew dropped (`holding`), so what a held
|
|
349
|
+
* container is reported holding is what it draws, under the options it hands
|
|
350
|
+
* each child — one description of a container's traversal, not a second
|
|
351
|
+
* kept beside it.
|
|
272
352
|
*/
|
|
273
353
|
export declare function render(renderable: Renderable, options: RenderOptions): Iterable<Segment>;
|
|
274
354
|
/**
|
|
275
|
-
* `
|
|
276
|
-
*
|
|
277
|
-
* one that did would put a widget in the view at the moment a container
|
|
278
|
-
* measured it, and keep one the frame then cropped away.
|
|
355
|
+
* What `measurable` reports under `options`: asked in a render pass, as
|
|
356
|
+
* `render` draws in one, with `measuring`'s options.
|
|
279
357
|
*
|
|
280
|
-
* [LAW:single-enforcer] Every
|
|
281
|
-
*
|
|
358
|
+
* [LAW:single-enforcer] Every call of a `Measurable`'s `measure` in `src/`
|
|
359
|
+
* goes through here, as every `render` goes through `render`;
|
|
360
|
+
* `test/seam/render-calls.test.ts` holds both.
|
|
282
361
|
*/
|
|
283
|
-
export declare function
|
|
362
|
+
export declare function measure(measurable: Measurable, options: RenderOptions): {
|
|
363
|
+
minimum: number;
|
|
364
|
+
maximum: number;
|
|
365
|
+
};
|
|
284
366
|
/**
|
|
285
367
|
* The options a renderable should *lay out against*: `withCellWidth`, with an
|
|
286
368
|
* unbounded offer resolved to the renderable's own natural width.
|
|
@@ -326,26 +408,6 @@ export interface Measurable {
|
|
|
326
408
|
maximum: number;
|
|
327
409
|
};
|
|
328
410
|
}
|
|
329
|
-
/**
|
|
330
|
-
* A container: a renderable that draws other renderables. `contents` is what
|
|
331
|
-
* its `render` hands to `render`, in that order where the order is not a
|
|
332
|
-
* width's, decided as the render decides it — and so it is how a container
|
|
333
|
-
* handed no column still reports what it holds (`render`). It takes no
|
|
334
|
-
* options: what a container holds may depend on its state (a hidden `Layout`
|
|
335
|
-
* holds nothing) but never on a width, because under one column there is none.
|
|
336
|
-
* It commits no state, and yields only renderables: text a container builds
|
|
337
|
-
* from a string it holds holds nothing.
|
|
338
|
-
*
|
|
339
|
-
* [LAW:one-source-of-truth] A second description of each container's
|
|
340
|
-
* traversal, so `test/renderables/contents.test.ts` holds every exported
|
|
341
|
-
* renderable's `contents` to what its `render` reports.
|
|
342
|
-
*
|
|
343
|
-
* A consumer's container that does not implement it reports nothing it holds
|
|
344
|
-
* while it is handed no column.
|
|
345
|
-
*/
|
|
346
|
-
export interface Composite {
|
|
347
|
-
contents(): Iterable<Renderable>;
|
|
348
|
-
}
|
|
349
411
|
/**
|
|
350
412
|
* What the wheel scrolls: an owner of anchors on the frame (`./anchor.ts`)
|
|
351
413
|
* that moves by lines. `canScrollBy` says whether `scrollBy` with the same
|
|
@@ -358,4 +420,3 @@ export interface Scrollable {
|
|
|
358
420
|
export declare function isRenderable(obj: unknown): obj is Renderable;
|
|
359
421
|
export declare function isMeasurable(obj: unknown): obj is Measurable;
|
|
360
422
|
export declare function isScrollable(obj: object): obj is Scrollable;
|
|
361
|
-
export declare function isComposite(obj: object): obj is Composite;
|