@promptctl/rich-js 0.24.0 → 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/THIRD-PARTY-NOTICES +29 -1
- package/dist/core/anchor.d.ts +5 -0
- package/dist/core/anchor.js +11 -4
- package/dist/core/animation.d.ts +243 -0
- package/dist/core/animation.js +375 -0
- package/dist/core/ansi.js +2 -2
- package/dist/core/box.d.ts +7 -7
- package/dist/core/cell-widths.d.ts +9 -0
- package/dist/core/cell-widths.js +140 -0
- package/dist/core/cells.d.ts +40 -24
- package/dist/core/cells.js +151 -66
- 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 +48 -43
- package/dist/core/easing.d.ts +140 -10
- package/dist/core/easing.js +328 -50
- 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 +3 -2
- package/dist/core/markup.js +16 -12
- package/dist/core/measure.js +3 -3
- 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/place.js +2 -1
- package/dist/core/pretty.js +11 -12
- package/dist/core/protocol.d.ts +136 -21
- package/dist/core/protocol.js +203 -16
- package/dist/core/render.d.ts +15 -2
- package/dist/core/render.js +27 -6
- package/dist/core/segment.d.ts +31 -6
- package/dist/core/segment.js +68 -53
- 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 +11 -13
- package/dist/core/strip.js +126 -113
- package/dist/core/style.d.ts +47 -14
- package/dist/core/style.js +74 -34
- package/dist/core/text.d.ts +5 -8
- package/dist/core/text.js +133 -133
- package/dist/core/timeline.d.ts +109 -0
- package/dist/core/timeline.js +180 -0
- package/dist/core/tween.d.ts +49 -0
- package/dist/core/tween.js +71 -0
- 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 +72 -38
- package/dist/host/input.js +3 -4
- package/dist/index.d.ts +30 -15
- package/dist/index.js +20 -6
- package/dist/node/traceback.js +4 -3
- package/dist/renderables/change.d.ts +42 -0
- package/dist/renderables/change.js +112 -0
- package/dist/renderables/columns.d.ts +1 -1
- package/dist/renderables/columns.js +3 -3
- package/dist/renderables/constrain.js +2 -1
- package/dist/renderables/effect.d.ts +155 -92
- package/dist/renderables/effect.js +203 -136
- package/dist/renderables/effects.d.ts +137 -24
- package/dist/renderables/effects.js +276 -113
- package/dist/renderables/embed.d.ts +1 -1
- package/dist/renderables/embed.js +18 -9
- package/dist/renderables/flexStrip.js +2 -1
- package/dist/renderables/frames.d.ts +45 -43
- package/dist/renderables/frames.js +57 -45
- 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 +1 -1
- package/dist/renderables/group.js +2 -2
- package/dist/renderables/html-entities.d.ts +8 -0
- package/dist/renderables/html-entities.js +386 -0
- package/dist/renderables/layout.d.ts +10 -9
- package/dist/renderables/layout.js +42 -35
- package/dist/renderables/lifecycle.d.ts +77 -0
- package/dist/renderables/lifecycle.js +132 -0
- package/dist/renderables/list-view.d.ts +2 -2
- package/dist/renderables/list-view.js +11 -8
- package/dist/renderables/live.d.ts +36 -21
- package/dist/renderables/live.js +89 -42
- package/dist/renderables/markdown-parse.d.ts +109 -0
- package/dist/renderables/markdown-parse.js +1427 -0
- package/dist/renderables/markdown-url.d.ts +19 -0
- package/dist/renderables/markdown-url.js +314 -0
- package/dist/renderables/markdown.d.ts +5 -7
- package/dist/renderables/markdown.js +356 -683
- package/dist/renderables/padding.d.ts +7 -20
- package/dist/renderables/padding.js +29 -50
- package/dist/renderables/panel.d.ts +30 -32
- package/dist/renderables/panel.js +65 -109
- package/dist/renderables/progress.d.ts +83 -32
- package/dist/renderables/progress.js +186 -90
- package/dist/renderables/progressBar.d.ts +22 -12
- package/dist/renderables/progressBar.js +33 -10
- package/dist/renderables/prompt.js +3 -2
- package/dist/renderables/python-space.d.ts +13 -0
- package/dist/renderables/python-space.js +25 -0
- package/dist/renderables/scroll.d.ts +50 -0
- package/dist/renderables/scroll.js +106 -0
- package/dist/renderables/spinner.d.ts +57 -24
- package/dist/renderables/spinner.js +101 -49
- package/dist/renderables/status.js +1 -1
- package/dist/renderables/syntax.d.ts +10 -9
- package/dist/renderables/syntax.js +5 -3
- package/dist/renderables/table.d.ts +48 -46
- package/dist/renderables/table.js +170 -208
- package/dist/renderables/traceback.d.ts +3 -3
- package/dist/renderables/traceback.js +12 -10
- package/dist/renderables/tree.d.ts +28 -25
- package/dist/renderables/tree.js +108 -58
- package/dist/renderables/viewport.d.ts +77 -38
- package/dist/renderables/viewport.js +156 -72
- 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 +7 -6
- package/dist/themes/ramp.d.ts +62 -4
- package/dist/themes/ramp.js +82 -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 +23 -14
- package/dist/widgets/focus-manager.js +34 -33
- package/dist/widgets/hit.d.ts +6 -0
- package/dist/widgets/hit.js +8 -1
- package/dist/widgets/index.d.ts +1 -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 +1 -1
- package/dist/widgets/static-item.js +6 -7
- package/dist/widgets/text-input.d.ts +53 -47
- package/dist/widgets/text-input.js +456 -399
- 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 +32 -6
- package/dist/widgets/widget-app.js +74 -27
- package/dist/widgets/widget-base.d.ts +2 -2
- package/dist/widgets/widget-base.js +29 -21
- package/package.json +9 -5
|
@@ -23,6 +23,8 @@
|
|
|
23
23
|
*/
|
|
24
24
|
import { cellCount, cellLen, setCellSize } from "../core/cells.js";
|
|
25
25
|
import { activeHighlighter, readStr } from "../core/markup.js";
|
|
26
|
+
import { Memo } from "../core/memo.js";
|
|
27
|
+
import { measure, render } from "../core/protocol.js";
|
|
26
28
|
import { Segment } from "../core/segment.js";
|
|
27
29
|
import { RichText } from "../core/text.js";
|
|
28
30
|
/**
|
|
@@ -38,8 +40,10 @@ export class EmbeddedText {
|
|
|
38
40
|
// A string's reading under each markup setting, made the first time one is
|
|
39
41
|
// asked for. Parsing is the costly half of drawing a string, and a table
|
|
40
42
|
// measures every cell more than once per print; read on every call, a
|
|
41
|
-
// 500-row table of markup printed 75% slower.
|
|
42
|
-
|
|
43
|
+
// 500-row table of markup printed 75% slower. Kept as characters and spans,
|
|
44
|
+
// not as the `RichText`, whose state a memo may not hold: a memo is "same
|
|
45
|
+
// key, same value", and each call builds a text of its own from it.
|
|
46
|
+
_readings = new Memo();
|
|
43
47
|
constructor(content) {
|
|
44
48
|
if (typeof content === "string") {
|
|
45
49
|
this._source = content;
|
|
@@ -60,10 +64,15 @@ export class EmbeddedText {
|
|
|
60
64
|
text(options, base) {
|
|
61
65
|
if (typeof this._source !== "string")
|
|
62
66
|
return this._source.copy();
|
|
67
|
+
const source = this._source;
|
|
63
68
|
const markup = options.markup !== false;
|
|
64
|
-
const reading = this._readings.get(markup
|
|
65
|
-
|
|
66
|
-
|
|
69
|
+
const reading = this._readings.get(markup ? "markup" : "plain", () => {
|
|
70
|
+
const read = readStr(source, markup);
|
|
71
|
+
return { plain: read.plain, spans: [...read.spans] };
|
|
72
|
+
});
|
|
73
|
+
const text = new RichText(reading.plain);
|
|
74
|
+
for (const span of reading.spans)
|
|
75
|
+
text.stylize(span.style, span.start, span.end);
|
|
67
76
|
const highlighter = activeHighlighter(options);
|
|
68
77
|
if (base !== undefined && highlighter === undefined)
|
|
69
78
|
text.style = base;
|
|
@@ -71,7 +80,7 @@ export class EmbeddedText {
|
|
|
71
80
|
return text;
|
|
72
81
|
}
|
|
73
82
|
render(options) {
|
|
74
|
-
return this.text(options)
|
|
83
|
+
return render(this.text(options), options);
|
|
75
84
|
}
|
|
76
85
|
/**
|
|
77
86
|
* Measured unhighlighted, as Rich's `Measurement.get` reads a string with
|
|
@@ -80,7 +89,7 @@ export class EmbeddedText {
|
|
|
80
89
|
* measures, and no highlighter runs just to be thrown away.
|
|
81
90
|
*/
|
|
82
91
|
measure(options) {
|
|
83
|
-
return this.text({ ...options, highlight: false })
|
|
92
|
+
return measure(this.text({ ...options, highlight: false }), options);
|
|
84
93
|
}
|
|
85
94
|
}
|
|
86
95
|
/**
|
|
@@ -124,7 +133,7 @@ export class InlineLabel {
|
|
|
124
133
|
* measures it, so a cut to a border and the width a label asks for count
|
|
125
134
|
* the cells it will draw. [LAW:single-enforcer] The one reading of "set into
|
|
126
135
|
* a line", for a label read through the crossing above and for text a
|
|
127
|
-
* caller hands over already read (`
|
|
136
|
+
* caller hands over already read (`FrameSequence`).
|
|
128
137
|
*/
|
|
129
138
|
export function setIntoLine(text) {
|
|
130
139
|
text.end = "";
|
|
@@ -163,7 +172,7 @@ export function present(content) {
|
|
|
163
172
|
*/
|
|
164
173
|
export function drawLabel(text, options, base) {
|
|
165
174
|
text.overflow = "ignore";
|
|
166
|
-
return [...Segment.applyStyle(
|
|
175
|
+
return [...Segment.applyStyle(render(text, options), base)];
|
|
167
176
|
}
|
|
168
177
|
/**
|
|
169
178
|
* Caller content as a label, or no label at all: content that is not
|
|
@@ -16,9 +16,10 @@
|
|
|
16
16
|
* FlexStrip reuses it verbatim. There is no second concept of "join."
|
|
17
17
|
*/
|
|
18
18
|
import { Segment } from "../core/segment.js";
|
|
19
|
+
import { render } from "../core/protocol.js";
|
|
19
20
|
import { drawItem, drawJoin } from "../core/strip.js";
|
|
20
21
|
function renderToBlock(r, options) {
|
|
21
|
-
const segments = [...
|
|
22
|
+
const segments = [...render(r, options)];
|
|
22
23
|
let width = 0;
|
|
23
24
|
for (const s of segments)
|
|
24
25
|
width += s.cellLength;
|
|
@@ -7,32 +7,33 @@
|
|
|
7
7
|
* (`./effect.ts`: "It changes how cells look, never which cells exist or who
|
|
8
8
|
* drew them"). A frame is the other kind of motion — what the element shows
|
|
9
9
|
* changes — so it is its own primitive and not a wider `Effect`, and the two
|
|
10
|
-
* compose by nesting: `new Effected(
|
|
11
|
-
*
|
|
10
|
+
* compose by nesting: `new Effected(sequence, …)` lights whichever frame is
|
|
11
|
+
* up.
|
|
12
12
|
*
|
|
13
|
-
* [LAW:no-ambient-temporal-coupling] `t` is
|
|
14
|
-
*
|
|
15
|
-
*
|
|
13
|
+
* [LAW:no-ambient-temporal-coupling] `t` is the frame's time, never a
|
|
14
|
+
* clock read: whoever draws frames hands each one its time
|
|
15
|
+
* (`RenderOptions.frame`), so the same `t` draws the same bytes at one
|
|
16
16
|
* redraw a second or sixty. The host's rate is the host's. At sixty a frame
|
|
17
17
|
* shows for exactly its hold; at one a second a hold shorter than a second is
|
|
18
18
|
* skipped and a period of whole seconds freezes on one frame, and nothing
|
|
19
19
|
* here adapts to that — the beat is `core/easing.ts`'s, the one timing
|
|
20
20
|
* vocabulary, and a draw costs the same at `t = 0` and `t = 10⁶`.
|
|
21
21
|
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
* over one. The sequence is a value the host keeps, not a memo here: nothing
|
|
29
|
-
* is looked up by key.
|
|
22
|
+
* A `FrameSequence` is the frames prepared once — the beat, the one width,
|
|
23
|
+
* each frame set into a line and padded — and an `Animation` of the frame
|
|
24
|
+
* shown (`core/animation.ts`), placed like any other: drawn, its work is one
|
|
25
|
+
* frame's — finding which, and copying it — so a draw costs the same over
|
|
26
|
+
* ninety frames as over one. The sequence is a value its holder keeps, as
|
|
27
|
+
* `Spinner` holds its two, not a memo here: nothing is looked up by key.
|
|
30
28
|
*
|
|
31
|
-
* The frame drawn is drawn until the next begins, and its cells say so:
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
29
|
+
* The frame drawn is drawn until the next begins, and its cells say so: a
|
|
30
|
+
* placed sequence stamps them with its hold (`Style.hold`,
|
|
31
|
+
* `placedRender` in `core/animation.ts`), which rides up
|
|
32
|
+
* through every container to whoever holds the composed frame
|
|
33
|
+
* (`Segment.nextChange`), so a host drawing at `t` knows when to draw again,
|
|
34
|
+
* and one drawing slower than the frames turn can see that it is skipping
|
|
35
|
+
* them. Nothing an animation's `at` returns carries a stamp of its own: the
|
|
36
|
+
* stamp is put on where the frame's time is known.
|
|
36
37
|
*
|
|
37
38
|
* Every frame is drawn at one width, the widest frame's, each padded on the
|
|
38
39
|
* right to it, so whatever sits beside the frames never moves as they turn;
|
|
@@ -43,17 +44,18 @@
|
|
|
43
44
|
* binding has no console for, so the frame arrives read, as a `Group`'s
|
|
44
45
|
* children do.
|
|
45
46
|
*/
|
|
47
|
+
import { Animation } from "../core/animation.js";
|
|
46
48
|
import { type Beat } from "../core/easing.js";
|
|
47
|
-
import type {
|
|
49
|
+
import type { Motion, RenderOptions } from "../core/protocol.js";
|
|
48
50
|
import type { Segment } from "../core/segment.js";
|
|
49
|
-
import
|
|
51
|
+
import { RichText } from "../core/text.js";
|
|
50
52
|
/** One frame: what it shows, and how long it is held, in seconds. */
|
|
51
53
|
export interface Frame {
|
|
52
54
|
readonly text: RichText;
|
|
53
55
|
readonly seconds: number;
|
|
54
56
|
}
|
|
55
57
|
/** A sequence of frames prepared to be drawn: built once, asked for the frame at a time. */
|
|
56
|
-
export declare class FrameSequence {
|
|
58
|
+
export declare class FrameSequence implements Animation<RichText> {
|
|
57
59
|
/** Which frame is in progress when: the holds are the frames' seconds, in order. */
|
|
58
60
|
readonly beat: Beat;
|
|
59
61
|
/** The width every frame draws at: the widest frame's, in cells. */
|
|
@@ -62,34 +64,34 @@ export declare class FrameSequence {
|
|
|
62
64
|
constructor(frames: readonly Frame[]);
|
|
63
65
|
/**
|
|
64
66
|
* The frame in progress at `t`, at `width` cells, ending no line: a copy,
|
|
65
|
-
* the caller's to change,
|
|
66
|
-
*
|
|
67
|
-
*
|
|
68
|
-
* answer to when the frame changes, read off the cells
|
|
69
|
-
* (`Segment.nextChange`): a sequence of nothing has no cell to carry one,
|
|
70
|
-
* and a field saying a time would disagree with cells saying never.
|
|
71
|
-
* `beat.at` refuses a `t` that is not finite.
|
|
67
|
+
* the caller's to change, carrying no word on when it changes — that is
|
|
68
|
+
* stamped where it is placed, at the frame's time. `beat.at`
|
|
69
|
+
* refuses a `t` that is not finite.
|
|
72
70
|
*/
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
/**
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
export declare class Frames implements Renderable, Measurable {
|
|
81
|
-
readonly sequence: FrameSequence;
|
|
82
|
-
readonly t: number;
|
|
83
|
-
private readonly _shown;
|
|
84
|
-
constructor(sequence: FrameSequence, options: FramesOptions);
|
|
71
|
+
at(t: number): RichText;
|
|
72
|
+
/** When the frame in progress at `t` gives way to the next: the beat's answer, its holds being the frames'. */
|
|
73
|
+
nextChange(t: number): number;
|
|
74
|
+
/** When the frame in progress at `t` began: the beat's answer. */
|
|
75
|
+
lastChange(t: number): number;
|
|
76
|
+
/** The frames with their motion taken out: the beat's first frame, held, a copy as `at`'s is. */
|
|
77
|
+
reduced(): Animation<RichText>;
|
|
85
78
|
render(options: RenderOptions): Iterable<Segment>;
|
|
86
79
|
/**
|
|
87
|
-
* One width both ways
|
|
88
|
-
*
|
|
89
|
-
* `Measurement.get`'s, as for every measurable.
|
|
80
|
+
* One width both ways, the same for every frame and reduced or not: a frame
|
|
81
|
+
* is one picture, never wrapped apart at a space or narrowed as the frames
|
|
82
|
+
* turn. Fitting it to the room is `Measurement.get`'s, as for every measurable.
|
|
90
83
|
*/
|
|
91
84
|
measure(): {
|
|
92
85
|
minimum: number;
|
|
93
86
|
maximum: number;
|
|
94
87
|
};
|
|
95
88
|
}
|
|
89
|
+
/**
|
|
90
|
+
* The text frame `animation` shows at host time `t` under `motion`
|
|
91
|
+
* (`shownAt`), copied and stamped with the hold it is in there: what the
|
|
92
|
+
* template `frames` and `glyphs` functions, which know the reader's
|
|
93
|
+
* preference before any render, splice into a line. The stamp is on the
|
|
94
|
+
* base style, which an empty text still draws as its one piece of no width,
|
|
95
|
+
* and on a copy, since an animation may hand back the one `RichText` it holds.
|
|
96
|
+
*/
|
|
97
|
+
export declare function textFrameAt(animation: Animation<RichText>, t: number, motion: Motion): RichText;
|
|
@@ -7,32 +7,33 @@
|
|
|
7
7
|
* (`./effect.ts`: "It changes how cells look, never which cells exist or who
|
|
8
8
|
* drew them"). A frame is the other kind of motion — what the element shows
|
|
9
9
|
* changes — so it is its own primitive and not a wider `Effect`, and the two
|
|
10
|
-
* compose by nesting: `new Effected(
|
|
11
|
-
*
|
|
10
|
+
* compose by nesting: `new Effected(sequence, …)` lights whichever frame is
|
|
11
|
+
* up.
|
|
12
12
|
*
|
|
13
|
-
* [LAW:no-ambient-temporal-coupling] `t` is
|
|
14
|
-
*
|
|
15
|
-
*
|
|
13
|
+
* [LAW:no-ambient-temporal-coupling] `t` is the frame's time, never a
|
|
14
|
+
* clock read: whoever draws frames hands each one its time
|
|
15
|
+
* (`RenderOptions.frame`), so the same `t` draws the same bytes at one
|
|
16
16
|
* redraw a second or sixty. The host's rate is the host's. At sixty a frame
|
|
17
17
|
* shows for exactly its hold; at one a second a hold shorter than a second is
|
|
18
18
|
* skipped and a period of whole seconds freezes on one frame, and nothing
|
|
19
19
|
* here adapts to that — the beat is `core/easing.ts`'s, the one timing
|
|
20
20
|
* vocabulary, and a draw costs the same at `t = 0` and `t = 10⁶`.
|
|
21
21
|
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
* over one. The sequence is a value the host keeps, not a memo here: nothing
|
|
29
|
-
* is looked up by key.
|
|
22
|
+
* A `FrameSequence` is the frames prepared once — the beat, the one width,
|
|
23
|
+
* each frame set into a line and padded — and an `Animation` of the frame
|
|
24
|
+
* shown (`core/animation.ts`), placed like any other: drawn, its work is one
|
|
25
|
+
* frame's — finding which, and copying it — so a draw costs the same over
|
|
26
|
+
* ninety frames as over one. The sequence is a value its holder keeps, as
|
|
27
|
+
* `Spinner` holds its two, not a memo here: nothing is looked up by key.
|
|
30
28
|
*
|
|
31
|
-
* The frame drawn is drawn until the next begins, and its cells say so:
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
29
|
+
* The frame drawn is drawn until the next begins, and its cells say so: a
|
|
30
|
+
* placed sequence stamps them with its hold (`Style.hold`,
|
|
31
|
+
* `placedRender` in `core/animation.ts`), which rides up
|
|
32
|
+
* through every container to whoever holds the composed frame
|
|
33
|
+
* (`Segment.nextChange`), so a host drawing at `t` knows when to draw again,
|
|
34
|
+
* and one drawing slower than the frames turn can see that it is skipping
|
|
35
|
+
* them. Nothing an animation's `at` returns carries a stamp of its own: the
|
|
36
|
+
* stamp is put on where the frame's time is known.
|
|
36
37
|
*
|
|
37
38
|
* Every frame is drawn at one width, the widest frame's, each padded on the
|
|
38
39
|
* right to it, so whatever sits beside the frames never moves as they turn;
|
|
@@ -43,8 +44,9 @@
|
|
|
43
44
|
* binding has no console for, so the frame arrives read, as a `Group`'s
|
|
44
45
|
* children do.
|
|
45
46
|
*/
|
|
47
|
+
import { Animation, holdOf, placedRender, shownAt, stamp } from "../core/animation.js";
|
|
46
48
|
import { beat } from "../core/easing.js";
|
|
47
|
-
import {
|
|
49
|
+
import { layerStyle } from "../core/style.js";
|
|
48
50
|
import { setIntoLine } from "./embed.js";
|
|
49
51
|
/** A sequence of frames prepared to be drawn: built once, asked for the frame at a time. */
|
|
50
52
|
export class FrameSequence {
|
|
@@ -61,7 +63,7 @@ export class FrameSequence {
|
|
|
61
63
|
this.width = Math.max(...widths);
|
|
62
64
|
this._shown = texts.map((text, i) => {
|
|
63
65
|
// One picture: a slot narrower than the frame crops it, never folds
|
|
64
|
-
// or wraps it onto a second line, which `
|
|
66
|
+
// or wraps it onto a second line, which `measure` says it cannot take.
|
|
65
67
|
text.noWrap = true;
|
|
66
68
|
text.overflow = "crop";
|
|
67
69
|
return text.padRight(this.width - widths[i]);
|
|
@@ -69,38 +71,48 @@ export class FrameSequence {
|
|
|
69
71
|
}
|
|
70
72
|
/**
|
|
71
73
|
* The frame in progress at `t`, at `width` cells, ending no line: a copy,
|
|
72
|
-
* the caller's to change,
|
|
73
|
-
*
|
|
74
|
-
*
|
|
75
|
-
* answer to when the frame changes, read off the cells
|
|
76
|
-
* (`Segment.nextChange`): a sequence of nothing has no cell to carry one,
|
|
77
|
-
* and a field saying a time would disagree with cells saying never.
|
|
78
|
-
* `beat.at` refuses a `t` that is not finite.
|
|
74
|
+
* the caller's to change, carrying no word on when it changes — that is
|
|
75
|
+
* stamped where it is placed, at the frame's time. `beat.at`
|
|
76
|
+
* refuses a `t` that is not finite.
|
|
79
77
|
*/
|
|
80
|
-
|
|
81
|
-
return this._shown[this.beat.at(t)].copy()
|
|
78
|
+
at(t) {
|
|
79
|
+
return this._shown[this.beat.at(t)].copy();
|
|
82
80
|
}
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
t
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
this.
|
|
81
|
+
/** When the frame in progress at `t` gives way to the next: the beat's answer, its holds being the frames'. */
|
|
82
|
+
nextChange(t) {
|
|
83
|
+
return this.beat.nextChange(t);
|
|
84
|
+
}
|
|
85
|
+
/** When the frame in progress at `t` began: the beat's answer. */
|
|
86
|
+
lastChange(t) {
|
|
87
|
+
return this.beat.lastChange(t);
|
|
88
|
+
}
|
|
89
|
+
/** The frames with their motion taken out: the beat's first frame, held, a copy as `at`'s is. */
|
|
90
|
+
reduced() {
|
|
91
|
+
return Animation.map(this.beat.reduced(), (i) => this._shown[i].copy());
|
|
94
92
|
}
|
|
95
93
|
render(options) {
|
|
96
|
-
return this
|
|
94
|
+
return placedRender(this, options);
|
|
97
95
|
}
|
|
98
96
|
/**
|
|
99
|
-
* One width both ways
|
|
100
|
-
*
|
|
101
|
-
* `Measurement.get`'s, as for every measurable.
|
|
97
|
+
* One width both ways, the same for every frame and reduced or not: a frame
|
|
98
|
+
* is one picture, never wrapped apart at a space or narrowed as the frames
|
|
99
|
+
* turn. Fitting it to the room is `Measurement.get`'s, as for every measurable.
|
|
102
100
|
*/
|
|
103
101
|
measure() {
|
|
104
|
-
return { minimum: this.
|
|
102
|
+
return { minimum: this.width, maximum: this.width };
|
|
105
103
|
}
|
|
106
104
|
}
|
|
105
|
+
/**
|
|
106
|
+
* The text frame `animation` shows at host time `t` under `motion`
|
|
107
|
+
* (`shownAt`), copied and stamped with the hold it is in there: what the
|
|
108
|
+
* template `frames` and `glyphs` functions, which know the reader's
|
|
109
|
+
* preference before any render, splice into a line. The stamp is on the
|
|
110
|
+
* base style, which an empty text still draws as its one piece of no width,
|
|
111
|
+
* and on a copy, since an animation may hand back the one `RichText` it holds.
|
|
112
|
+
*/
|
|
113
|
+
export function textFrameAt(animation, t, motion) {
|
|
114
|
+
const shown = shownAt(animation, t, motion);
|
|
115
|
+
const stamped = shown.value.copy();
|
|
116
|
+
stamped.style = layerStyle(stamped.style, stamp(holdOf(shown, t)));
|
|
117
|
+
return stamped;
|
|
118
|
+
}
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GlyphEffected — a renderable whose finished cells' glyphs pass through a
|
|
3
|
+
* glyph effect at a moment `t`: the glyph half of `Effected` (`./effect.ts`),
|
|
4
|
+
* built to its contract.
|
|
5
|
+
*
|
|
6
|
+
* A glyph effect is the colour effect's two-stage shape with a glyph where
|
|
7
|
+
* the colours were: for the cells an element draws in a frame, an
|
|
8
|
+
* `Animation` (`../core/animation.ts`) of what is done to them. The first
|
|
9
|
+
* stage is handed every cell once a frame (`GlyphCell`), blanks included —
|
|
10
|
+
* rain and stars live there — and whether the output draws ASCII alone
|
|
11
|
+
* (`DrawnGlyphs`), and works out what holds for the whole frame:
|
|
12
|
+
* how many cells are typed, where a band is. The second, the function
|
|
13
|
+
* `at(t)` returns, maps each cell to the text it shows. A typewriter, a
|
|
14
|
+
* scramble, a wave of case changes and particles on blank cells are all this
|
|
15
|
+
* one shape (`./glyph-effects.ts`) [LAW:one-type-per-behavior].
|
|
16
|
+
*
|
|
17
|
+
* Colour and glyph are not two kinds of animation: each is an `Animation` of
|
|
18
|
+
* a per-cell map, and they differ only in what a cell is mapped to. The
|
|
19
|
+
* wrappers are two because the stages are handed different facts. A colour
|
|
20
|
+
* effect is settled against the depth, the terminal and the colours of the
|
|
21
|
+
* words (`Drawn`), and with no colour output there is nothing for it to
|
|
22
|
+
* change. A glyph effect needs none of that, and is written at every depth,
|
|
23
|
+
* with no colour too. Over one element the two compose by nesting:
|
|
24
|
+
* `new Effected(new GlyphEffected(child, glyphs, …), colours, …)` moves the
|
|
25
|
+
* colours of the glyphs the glyph effect left, since each wrapper measures
|
|
26
|
+
* what its child drew.
|
|
27
|
+
*
|
|
28
|
+
* A `GlyphEffected` is the element under its glyph effect over time, an
|
|
29
|
+
* `Animation<Renderable>` placed as an `Effected` is, its value at `t` the
|
|
30
|
+
* element through the effect at `t` (`Reglyphed`).
|
|
31
|
+
* [LAW:no-ambient-temporal-coupling] The time is the frame's, never a clock
|
|
32
|
+
* read, so the same time draws the same bytes at any frame rate.
|
|
33
|
+
*
|
|
34
|
+
* What a glyph effect may not do is the contract this module holds:
|
|
35
|
+
*
|
|
36
|
+
* - It changes what a cell shows, never which cells exist, where they are or
|
|
37
|
+
* who drew them. The text it returns for a cell is exactly as many cells
|
|
38
|
+
* wide (`cellLen`), carries no line break or control character, and does
|
|
39
|
+
* not join the cell beside it into one grapheme cluster, or the render
|
|
40
|
+
* throws naming the cell [LAW:single-enforcer]. So what sits beside the
|
|
41
|
+
* element never moves, and a wide glyph is changed whole — blanked as two
|
|
42
|
+
* spaces, scrambled as two one-cell glyphs — never cut.
|
|
43
|
+
* - A segment whose every glyph comes back as it was is yielded as it was,
|
|
44
|
+
* so the identity effect is byte-identical. A changed segment keeps its
|
|
45
|
+
* style whole, link, meta and anchor included; no cell has moved, so the
|
|
46
|
+
* anchor's offsets are still true and hit testing still finds the owner.
|
|
47
|
+
* - Placed as a `GlyphEffected`, every cell carries the hold the frame is in
|
|
48
|
+
* (`Style.hold`), under whatever the child said,
|
|
49
|
+
* as under `Effected`; drawn as a `Reglyphed`, a value, none.
|
|
50
|
+
*
|
|
51
|
+
* Positions are the child's own: row and column count from the top-left of
|
|
52
|
+
* what the child drew.
|
|
53
|
+
*/
|
|
54
|
+
import { Animation } from "../core/animation.js";
|
|
55
|
+
import type { Measurable, Renderable, RenderOptions } from "../core/protocol.js";
|
|
56
|
+
import { Segment } from "../core/segment.js";
|
|
57
|
+
import { type RichText } from "../core/text.js";
|
|
58
|
+
import { type Moved, type Placed } from "./effect.js";
|
|
59
|
+
/**
|
|
60
|
+
* A cell of the element a glyph effect wraps: the glyph it shows, where it
|
|
61
|
+
* sits and who drew it, its place in reading order among the cells handed
|
|
62
|
+
* (`DrawnGlyphs.cells[index]` is this cell), and its seed, a number in [0, 1) that is the
|
|
63
|
+
* same for that cell on every frame, as an `EffectCell`'s is.
|
|
64
|
+
*/
|
|
65
|
+
export interface GlyphCell extends Placed {
|
|
66
|
+
readonly glyph: string;
|
|
67
|
+
readonly index: number;
|
|
68
|
+
readonly seed: number;
|
|
69
|
+
}
|
|
70
|
+
/** What a glyph effect does to the cells of one frame: a cell in, the text it shows out, as many cells wide. Return `cell.glyph` to leave it be. */
|
|
71
|
+
export type GlyphFrame = (cell: GlyphCell) => string;
|
|
72
|
+
/**
|
|
73
|
+
* What the element a glyph effect wraps draws in one frame, and on what: the
|
|
74
|
+
* glyph half of `Drawn`.
|
|
75
|
+
*/
|
|
76
|
+
export interface DrawnGlyphs {
|
|
77
|
+
/** Every cell, in reading order; a line break is no cell. */
|
|
78
|
+
readonly cells: readonly GlyphCell[];
|
|
79
|
+
/**
|
|
80
|
+
* Whether the output draws ASCII alone (`RenderOptions.asciiOnly`). A glyph
|
|
81
|
+
* the effect chooses itself, rather than one the element drew, goes through
|
|
82
|
+
* `drawable` against it, as every glyph the library chooses does.
|
|
83
|
+
*/
|
|
84
|
+
readonly asciiOnly: boolean;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* For an element drawing `drawn`, the animation of what is done to its
|
|
88
|
+
* glyphs. Its `nextChange` is when the frame next changes, as an `Effect`'s is.
|
|
89
|
+
*/
|
|
90
|
+
export type GlyphEffect = (drawn: DrawnGlyphs) => Animation<GlyphFrame>;
|
|
91
|
+
export interface GlyphEffectedOptions {
|
|
92
|
+
/** Names the element for its seeds, as `EffectedOptions.key` does; under both wrappers, one key gives one seed a cell. */
|
|
93
|
+
readonly key: string;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* A `GlyphEffected` at `t`, or an element through a glyph effect at a time an
|
|
97
|
+
* animation's `at` was handed: `renderable` with its cells' glyphs passed
|
|
98
|
+
* through `effect` at `t`, saying nothing of when that changes, as a
|
|
99
|
+
* `Recolored` is (`./effect.ts`). [LAW:one-type-per-behavior]
|
|
100
|
+
*/
|
|
101
|
+
export declare class Reglyphed implements Renderable, Measurable {
|
|
102
|
+
readonly renderable: Renderable;
|
|
103
|
+
readonly effect: GlyphEffect;
|
|
104
|
+
readonly t: number;
|
|
105
|
+
readonly key: string;
|
|
106
|
+
constructor(renderable: Renderable, effect: GlyphEffect, options: GlyphEffectedOptions & {
|
|
107
|
+
readonly t: number;
|
|
108
|
+
});
|
|
109
|
+
render(options: RenderOptions): Iterable<Segment>;
|
|
110
|
+
/** The frame the effect draws at `t`, and the hold it says that is in. */
|
|
111
|
+
moved(options: RenderOptions): Moved;
|
|
112
|
+
measure(options: RenderOptions): {
|
|
113
|
+
minimum: number;
|
|
114
|
+
maximum: number;
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* `renderable` under a glyph effect over time, an `Animation<Renderable>`
|
|
119
|
+
* as an `Effected` is (`./effect.ts`): its own answer is `t`, and placed,
|
|
120
|
+
* its cells carry the glyph effect's exact hold at the frame's time and the
|
|
121
|
+
* output's `asciiOnly`, under whatever the element already said.
|
|
122
|
+
*/
|
|
123
|
+
export declare class GlyphEffected implements Animation<Renderable> {
|
|
124
|
+
readonly renderable: Renderable;
|
|
125
|
+
readonly effect: GlyphEffect;
|
|
126
|
+
readonly key: string;
|
|
127
|
+
constructor(renderable: Renderable, effect: GlyphEffect, options: GlyphEffectedOptions);
|
|
128
|
+
/** The element through the glyph effect at `t`, unstamped. */
|
|
129
|
+
at(t: number): Reglyphed;
|
|
130
|
+
nextChange(t: number): number;
|
|
131
|
+
lastChange(t: number): number;
|
|
132
|
+
/** The element under the glyph effect with its motion taken out. */
|
|
133
|
+
reduced(): GlyphEffected;
|
|
134
|
+
/** Drawn at the frame's time, its cells stamped with the glyph effect's exact hold there. */
|
|
135
|
+
render(options: RenderOptions): Iterable<Segment>;
|
|
136
|
+
measure(options: RenderOptions): {
|
|
137
|
+
minimum: number;
|
|
138
|
+
maximum: number;
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* `text` with its glyphs passed through `effect`, before any render: the
|
|
143
|
+
* glyph half of a template fragment (`../template-bindings/animation-funcs.ts`),
|
|
144
|
+
* which is spliced into a line and drawn under a theme it does not know yet,
|
|
145
|
+
* so it cannot be rendered to cells first as `Reglyphed` renders its child.
|
|
146
|
+
* The cells are the text's own lines, at its own columns; the contract is
|
|
147
|
+
* the one `Reglyphed` is held to, through the same checks, over the text's
|
|
148
|
+
* own glyphs: a neighbour spliced beside it later, a combining mark leading
|
|
149
|
+
* the next fragment say, is outside what either check can see. The text keeps
|
|
150
|
+
* its every style and span, each moved to where its glyphs now sit, and a
|
|
151
|
+
* frame that changes nothing hands the text back as it was. What it says of
|
|
152
|
+
* when it changes is the effect's, unstamped: whoever draws it at a time
|
|
153
|
+
* stamps it (`textFrameAt`).
|
|
154
|
+
*/
|
|
155
|
+
export declare function reglyphText(text: RichText, effect: GlyphEffect, options: {
|
|
156
|
+
readonly key: string;
|
|
157
|
+
readonly asciiOnly: boolean;
|
|
158
|
+
}): Animation<RichText>;
|