@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/README.md
CHANGED
|
@@ -388,6 +388,13 @@ npm run demo
|
|
|
388
388
|
| `npm run themes-and-color-studio` | themes-and-color-studio | A printed tour of colours, palettes, bundled themes, and contrast. Prints once and exits; set any of `EXPORT_HTML=out.html`, `EXPORT_SVG=out.svg` and `EXPORT_TEXT=out.txt` to also save it to each file named. |
|
|
389
389
|
| `npm run strip` | rich-strip | Every built-in `Joiner`, printed side by side. Prints once and exits. |
|
|
390
390
|
| `npm run effects-feel` | effects-feel | Shimmer, a gentle pulse on chosen elements, sparkle, a hue wheel, fade-in and a dissolve-out that eddies back before it goes, on a powerline strip and a run of text, at a colour depth you choose, with keys to step the frame rate, the demo's pace and the theme while it runs. A demo for agreeing how the effects feel; `npm run effects-feel -- --help` lists the flags and keys. |
|
|
391
|
+
| `npm run glyph-effects` | glyph-effects | Text typed, scrambled until it resolves, crossed by a wave of capitals, and rained and snowed on, each glyph effect in the catalogue and a few made by hand, for twenty seconds. |
|
|
392
|
+
| `npm run attribute-effects` | attribute-effects | A line that blinks, pulses bold, and blinks its underline and its italics on beats of its own: the attribute effects in the catalogue and two made by hand from `blink`, for twenty seconds. |
|
|
393
|
+
| `npm run gradient-effects` | gradient-effects | A colour ramp laid across three rules and three panel borders, one scrolling, one turning its hues, one swaying and settling a quarter turn round, while the titles and the words inside keep their own colour, for twenty seconds. |
|
|
394
|
+
| `npm run transitions` | transitions | A build status changing state every three seconds, crossfaded in one panel and wiped in the other, each cell passing through the terminal's ground and nothing beside it moving while it changes, for twenty seconds. |
|
|
395
|
+
| `npm run lifecycle` | lifecycle | A build log whose lines type themselves in as they are appended and leave four seconds later, dissolved, backspaced or scrambled away in turn, each dropped from the log once it is gone, for twenty seconds. |
|
|
396
|
+
| `npm run scroll` | scroll | A marquee crossing a fixed width, a log tail stepping a row a second, and a panel sliding in from the left and holding: one window moved three ways, for twenty seconds. |
|
|
397
|
+
| `npm run animation-playground` | animation-playground | Every kind of motion — tweens, moving ramps, a marching border, glyph and attribute effects, transitions, a list that comes and goes, a marquee, staggers and a blinking caret — on one timeline, with buttons to pause, reverse, scrub, change speed, reduce motion, drop to sixteen colours or none, and paint at one frame a second or sixty. |
|
|
391
398
|
| `npm run markup-plugins` | rich-markup-plugins | Custom markup tags registered through `MarkupRegistry`. Prints once and exits. |
|
|
392
399
|
| `npm run viewport` | rich-viewport | A selection you move through a list taller than its window with the arrow keys, `j`/`k`, PgUp/PgDn and Home/End, kept in view by `Viewport.ensureVisible`; the mouse wheel scrolls the window, and `q` quits. |
|
|
393
400
|
| `npm run terminal-host` | terminal-host | Each key you press printed as the bytes the terminal sends for it, and each resize as the new size — a program written against `TerminalHost` alone. `examples/terminal-host/embed.ts` runs the same program on an xterm.js terminal in your own page. |
|
package/dist/core/anchor.d.ts
CHANGED
|
@@ -48,3 +48,8 @@ export declare function shiftAnchor(anchor: Anchor, cells: number): Anchor;
|
|
|
48
48
|
* nested in its output.
|
|
49
49
|
*/
|
|
50
50
|
export declare function drawnBy(anchor: Anchor | undefined, owner: object): boolean;
|
|
51
|
+
/**
|
|
52
|
+
* The level of `anchor` that `owner` stamped: the cell's place in `owner`'s
|
|
53
|
+
* output, with what `owner`'s own content stamped inside it.
|
|
54
|
+
*/
|
|
55
|
+
export declare function levelOf(anchor: Anchor | undefined, owner: object): Anchor | undefined;
|
package/dist/core/anchor.js
CHANGED
|
@@ -42,8 +42,15 @@ export function shiftAnchor(anchor, cells) {
|
|
|
42
42
|
* nested in its output.
|
|
43
43
|
*/
|
|
44
44
|
export function drawnBy(anchor, owner) {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
45
|
+
return levelOf(anchor, owner) !== undefined;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* The level of `anchor` that `owner` stamped: the cell's place in `owner`'s
|
|
49
|
+
* output, with what `owner`'s own content stamped inside it.
|
|
50
|
+
*/
|
|
51
|
+
export function levelOf(anchor, owner) {
|
|
52
|
+
let level = anchor;
|
|
53
|
+
while (level !== undefined && level.owner !== owner)
|
|
54
|
+
level = level.inner;
|
|
55
|
+
return level;
|
|
49
56
|
}
|
package/dist/core/animation.d.ts
CHANGED
|
@@ -10,9 +10,10 @@
|
|
|
10
10
|
* answer costs a host a frame that draws what the last one did; a late one
|
|
11
11
|
* skips a change. So an animation that cannot say answers `t`, as
|
|
12
12
|
* `continuous` does. A value drawn as cells carries this answer on every
|
|
13
|
-
* cell (`Style.
|
|
14
|
-
* there (`Segment.nextChange`); a value that is
|
|
15
|
-
* colour — has `nextChange` as its only way to say
|
|
13
|
+
* cell (`Style.hold`, both ends of the hold below), so a host holding
|
|
14
|
+
* the composed frame reads it there (`Segment.nextChange`); a value that is
|
|
15
|
+
* not drawn — a number, a colour — has `nextChange` as its only way to say
|
|
16
|
+
* when to look again.
|
|
16
17
|
*
|
|
17
18
|
* `lastChange(t)` is the other end of the same hold: the last moment, never
|
|
18
19
|
* after `t`, at which the value may have become `at(t)` — `-Infinity` if it
|
|
@@ -22,12 +23,39 @@
|
|
|
22
23
|
* (`./timeline.ts`): run down the timeline, the value next changes where it
|
|
23
24
|
* began. An animation that cannot say answers `t` here too.
|
|
24
25
|
*
|
|
26
|
+
* `reduced()` is the same animation with its motion taken out: what it shows,
|
|
27
|
+
* and when it starts and stops showing it, kept; every moment between two
|
|
28
|
+
* values skipped. A loop holds its first value, a one-shot cuts to its end
|
|
29
|
+
* when it starts, a transition to its new state, a stagger has everyone arrived, and an element is
|
|
30
|
+
* there from when it entered until it is told to leave. Terminals have no
|
|
31
|
+
* `prefers-reduced-motion`, so a host is told (`reducedMotion`,
|
|
32
|
+
* `./protocol.ts`), and under it draws every animation's reduced form, which
|
|
33
|
+
* wakes it only where content changes. It is the one place the preference is
|
|
34
|
+
* honoured for every kind at once, because every kind is this type, and each
|
|
35
|
+
* combinator says its reduced form so a built animation answers it too.
|
|
36
|
+
* [LAW:types-are-the-program] It is an animation and not a value because
|
|
37
|
+
* which content shows is a fact about time that motion is not: a value with
|
|
38
|
+
* no time could only show a scheduled exit as already gone.
|
|
39
|
+
*
|
|
25
40
|
* The instances already in the tree: a `Beat` is an animation of which hold
|
|
26
41
|
* is in progress (`./easing.ts`), a `FrameSequence` of the frame shown and a
|
|
27
42
|
* `Spinner` of the spinner drawn (`../renderables/`), and an `Effect` is, for
|
|
28
43
|
* what an element draws in a frame, an animation of how its cells' colours
|
|
29
44
|
* move (`../renderables/effect.ts`). Every kind of motion is one more.
|
|
30
45
|
*
|
|
46
|
+
* An animation of renderables is a renderable — its face, `placedRender`:
|
|
47
|
+
* placed as the child of any container — a `Panel`, a `Table` cell, a
|
|
48
|
+
* `Group`, a container a user wrote — it draws at the time of the frame it
|
|
49
|
+
* is drawn in (`RenderOptions.frame`; 0, its own start, when printed) and
|
|
50
|
+
* stamps its cells with its hold there, under whatever its value's cells
|
|
51
|
+
* already say, so a frame's hold is the intersection of its cells' holds.
|
|
52
|
+
* [LAW:composability] No time is threaded through the code that places it,
|
|
53
|
+
* and no container knows animations exist. An animation placed inside
|
|
54
|
+
* another's value draws at the frame's time too: a combinator remaps the
|
|
55
|
+
* time it hands its parts, never a value's contents, so content that should
|
|
56
|
+
* follow a part's clock is composed into the part (`map`, `all`) and the
|
|
57
|
+
* result placed. [LAW:one-source-of-truth]
|
|
58
|
+
*
|
|
31
59
|
* [LAW:no-ambient-temporal-coupling] Pure: `t` is an argument and never a
|
|
32
60
|
* clock read (`./clock.ts`), the same `t` gives the same value, nothing is
|
|
33
61
|
* kept between calls, and a call costs the same at `t = 0` and `t = 10⁶`, so
|
|
@@ -36,43 +64,180 @@
|
|
|
36
64
|
*
|
|
37
65
|
* [LAW:one-type-per-behavior] The combinators here are the ones every kind
|
|
38
66
|
* of motion is built from — a constant, a value moving every moment, a map
|
|
39
|
-
* over the value, several at once,
|
|
67
|
+
* over the value, several at once, one animation after another, and one
|
|
68
|
+
* played again every period — and each says how
|
|
40
69
|
* `nextChange` composes, so an animation built from them answers it without
|
|
41
70
|
* its author doing anything.
|
|
71
|
+
*
|
|
72
|
+
* Tier 5 of `src/core/`: `./easing.ts`, `./measure.ts`, `./protocol.ts`,
|
|
73
|
+
* `./segment.ts` and `./style.ts`, nothing else. [LAW:one-way-deps]
|
|
42
74
|
*/
|
|
43
|
-
|
|
44
|
-
|
|
75
|
+
import { type Beat } from "./easing.js";
|
|
76
|
+
import { type Measurable, type Motion, type Renderable, type RenderOptions } from "./protocol.js";
|
|
77
|
+
import { Segment } from "./segment.js";
|
|
78
|
+
import { Style, type Hold } from "./style.js";
|
|
79
|
+
/** A value of `T` at a time in seconds, and the hold it is in: what every animation answers, whatever its values. */
|
|
80
|
+
export interface Timed<T> {
|
|
45
81
|
/** The value at `t` seconds. */
|
|
46
82
|
at(t: number): T;
|
|
47
83
|
/** The first moment, never before `t`, at which the value may stop being `at(t)`: `t` while it moves, `Infinity` once it never will. */
|
|
48
84
|
nextChange(t: number): number;
|
|
49
85
|
/** The last moment, never after `t`, at which the value may have become `at(t)`: `t` while it moves, `-Infinity` if it always was. */
|
|
50
86
|
lastChange(t: number): number;
|
|
87
|
+
/** This animation with its motion taken out — a loop at its first value, a one-shot cut to its end — drawn in its place under reduced motion. */
|
|
88
|
+
reduced(): Animation<T>;
|
|
51
89
|
}
|
|
90
|
+
/**
|
|
91
|
+
* A value of `T` at a time, and — when its values are renderables — a
|
|
92
|
+
* renderable itself, drawn wherever a renderable goes at the time of the
|
|
93
|
+
* frame it is drawn in (`placedRender`). [LAW:types-are-the-program] The
|
|
94
|
+
* face is on the type exactly when the values can be drawn: a tween of a
|
|
95
|
+
* number is no renderable and a `Panel` refuses it; a tween mapped to a
|
|
96
|
+
* `RichText` is one, and a `Panel` holds it.
|
|
97
|
+
*/
|
|
98
|
+
export type Animation<T> = Timed<T> & ([T] extends [Renderable] ? Renderable & Measurable : unknown);
|
|
99
|
+
/** What a frame drawn at a time shows of an animation, and what that says. */
|
|
100
|
+
export interface Shown<T> {
|
|
101
|
+
readonly value: T;
|
|
102
|
+
/** The first moment, never before the time drawn at, the value may stop being this. */
|
|
103
|
+
readonly nextChange: number;
|
|
104
|
+
/** The animation the value is drawn from: the one handed in, or under reduced motion its reduced form. */
|
|
105
|
+
readonly drawn: Timed<T>;
|
|
106
|
+
/** Whether it is the animation reduced: drawn under reduced motion, changing only where content does. */
|
|
107
|
+
readonly reduced: boolean;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* What a frame drawn at `t` shows of `animation` under `motion`, and when
|
|
111
|
+
* that changes: the animation's, or under reduced motion its reduced form's
|
|
112
|
+
* (`reduced`). [LAW:single-enforcer] The one place the preference is read:
|
|
113
|
+
* every placed animation (`placedRender`), every effect, a spinner and
|
|
114
|
+
* the template functions choose here, so a host told once
|
|
115
|
+
* reduces every one.
|
|
116
|
+
*/
|
|
117
|
+
export declare function shownAt<T>(animation: Timed<T>, t: number, motion: Motion): Shown<T>;
|
|
118
|
+
/** The hold `shown`, drawn at `t`, is in: its animation's two answers there. */
|
|
119
|
+
export declare function holdOf(shown: Shown<unknown>, t: number): Hold;
|
|
120
|
+
/** The style saying a cell is in `hold`. */
|
|
121
|
+
export declare function stamp(hold: Hold): Style;
|
|
122
|
+
/**
|
|
123
|
+
* `segments`, every one carrying `hold` under whatever it already said: the
|
|
124
|
+
* later `lastChange` and the sooner `nextChange` win (`Style.add`), so a
|
|
125
|
+
* frame's hold is the intersection of its cells', narrower than the truth
|
|
126
|
+
* and never wider. [LAW:single-enforcer] The one way a time is put on drawn
|
|
127
|
+
* cells, by whatever draws for a time it knows.
|
|
128
|
+
*/
|
|
129
|
+
export declare function stampFrame(segments: Iterable<Segment>, hold: Hold): Iterable<Segment>;
|
|
130
|
+
/**
|
|
131
|
+
* The moment a frame drawn under `options` is drawn for: the frame's time,
|
|
132
|
+
* which the host writes once a frame (`RenderOptions.frame`), or with no
|
|
133
|
+
* frame — a `print` — 0, the start of an animation's own clock, the one
|
|
134
|
+
* moment it can name without reading one. [LAW:one-source-of-truth] Nothing
|
|
135
|
+
* else hands a placed animation its time, so an animation inside another's
|
|
136
|
+
* value draws at the frame's time too, never at a time a combinator mapped.
|
|
137
|
+
*/
|
|
138
|
+
export declare function frameTimeOf(options: RenderOptions): number;
|
|
139
|
+
/**
|
|
140
|
+
* An animation's face, `render`: drawn at the time of the frame it is drawn
|
|
141
|
+
* in (`frameTimeOf`), the value `shownAt` picks, every segment stamped with
|
|
142
|
+
* its hold there under whatever its own cells say, and the hold told to the
|
|
143
|
+
* frame (`RenderOptions.onHold`), because a frame of no cells — an element
|
|
144
|
+
* not yet entered — may have cells later, and must say when without taking a
|
|
145
|
+
* row to say it in. A container — the library's or a user's — that draws its
|
|
146
|
+
* children through `render` draws an animation among them with no word about
|
|
147
|
+
* time: the hold goes back the way the time came in. [LAW:composability]
|
|
148
|
+
*/
|
|
149
|
+
export declare function placedRender(animation: Timed<unknown>, options: RenderOptions): Iterable<Segment>;
|
|
150
|
+
/**
|
|
151
|
+
* An animation's face, `measure`: what it shows at the frame's time, measured,
|
|
152
|
+
* and its hold there told (`RenderOptions.onHold`), because a container lays
|
|
153
|
+
* out from the measurement and must lay out again when it changes.
|
|
154
|
+
*/
|
|
155
|
+
export declare function placedMeasure(animation: Timed<unknown>, options: RenderOptions): {
|
|
156
|
+
minimum: number;
|
|
157
|
+
maximum: number;
|
|
158
|
+
};
|
|
159
|
+
/** What `Animation.of` makes of `X`: `X`, with the face its values call for. */
|
|
160
|
+
export type Faced<X extends Timed<unknown>> = X & Animation<X extends Timed<infer T> ? T : never>;
|
|
52
161
|
export declare const Animation: {
|
|
162
|
+
/**
|
|
163
|
+
* `timed` as an animation: every member it carries kept (`membersOf`),
|
|
164
|
+
* with the face its values call for — `render` and `measure`,
|
|
165
|
+
* drawing it at the frame's time. Every animation made from a literal is
|
|
166
|
+
* made here, the library's and a user's alike. [LAW:single-enforcer] The
|
|
167
|
+
* one cast in the type: a literal's `T` is not known at runtime, so every
|
|
168
|
+
* animation made here has the face, and one whose values are not
|
|
169
|
+
* renderables throws when it is drawn.
|
|
170
|
+
*/
|
|
171
|
+
of<X extends Timed<unknown>>(timed: X): Faced<NoInfer<X>>;
|
|
53
172
|
/** `value` at every time, never changing. */
|
|
54
173
|
constant<T>(value: T): Animation<T>;
|
|
174
|
+
/**
|
|
175
|
+
* `before` until 0, then `after`: an instant change at 0 — a cut, what a
|
|
176
|
+
* one-shot is with its motion taken out, its start kept and its end at once.
|
|
177
|
+
*/
|
|
178
|
+
cut<T>(before: T, after: T): Animation<T>;
|
|
55
179
|
/**
|
|
56
180
|
* The value `at` gives, taken to move at every moment: what an animation
|
|
57
181
|
* that cannot say when it holds still is, so a host draws it every frame.
|
|
182
|
+
* Reduced, it holds its first value, as a loop does; a one-shot built on
|
|
183
|
+
* it says its own end (`transition`, `../renderables/effects.ts`).
|
|
58
184
|
*/
|
|
59
185
|
continuous<T>(at: (t: number) => T): Animation<T>;
|
|
186
|
+
/**
|
|
187
|
+
* The time itself, in seconds, moving at every moment: what a clock, a
|
|
188
|
+
* counter or anything else that reports the time is mapped from. It is
|
|
189
|
+
* information and not motion, so reduced it is itself, and a clock shown
|
|
190
|
+
* to a reader who wants no motion still keeps the time; motion computed
|
|
191
|
+
* from the time is `continuous`, which reduced holds still.
|
|
192
|
+
* [LAW:types-are-the-program]
|
|
193
|
+
*/
|
|
194
|
+
time(): Animation<number>;
|
|
60
195
|
/**
|
|
61
196
|
* `animation`'s value through `f`, changing when it does. Two values `f`
|
|
62
197
|
* maps alike make an early answer, which the contract allows.
|
|
63
198
|
*/
|
|
64
|
-
map<T, U>(animation:
|
|
199
|
+
map<T, U>(animation: Timed<T>, f: (value: T) => U): Animation<U>;
|
|
65
200
|
/**
|
|
66
201
|
* Every one of `animations` at once, their values in order, changing when
|
|
67
202
|
* the soonest of them does — so a stack of effects drawn together holds
|
|
68
|
-
* still exactly while each of them does
|
|
203
|
+
* still exactly while each of them does, and reduced each reduced — a
|
|
204
|
+
* stagger with every child arrived. None never changes.
|
|
205
|
+
*/
|
|
206
|
+
all<const T extends readonly unknown[]>(animations: { readonly [K in keyof T]: Timed<T[K]>; }): Animation<T>;
|
|
207
|
+
/**
|
|
208
|
+
* `animation` sampled once a hold of `beat`: within a hold, its value at
|
|
209
|
+
* the hold's start, and at each hold's start it moves on. It changes at the
|
|
210
|
+
* first hold's start the sampled animation has moved by: the end of this
|
|
211
|
+
* hold for one that moves every moment, the hold its next change falls in
|
|
212
|
+
* for one that holds longer, and never for one that never changes again.
|
|
213
|
+
* The answer is read off what the animation says, never a comparison of
|
|
214
|
+
* values, so one that is back on the same value by the next hold still
|
|
215
|
+
* turns the beat there, an early answer the contract allows.
|
|
216
|
+
* [LAW:one-source-of-truth] The one stepping rule, for an effect's frames
|
|
217
|
+
* and a transition's schedule alike.
|
|
69
218
|
*/
|
|
70
|
-
|
|
219
|
+
onBeat<T>(animation: Timed<T>, beat: Beat): Animation<T>;
|
|
71
220
|
/**
|
|
72
221
|
* `first` until `seconds`, then `then`, handed the time since it began —
|
|
73
222
|
* each part on its own clock from 0, as every effect is. Before the switch
|
|
74
223
|
* the value next changes when `first`'s does or at the switch, whichever is
|
|
75
224
|
* sooner; after it, when `then`'s does, carried back onto this timeline.
|
|
225
|
+
* Reduced, each part is reduced and the switch kept, so what either shows
|
|
226
|
+
* shows on time; a one-shot that is the motion into what follows says its
|
|
227
|
+
* own end (`transition`, `../renderables/effects.ts`).
|
|
228
|
+
*/
|
|
229
|
+
sequence<T>(first: Timed<T>, seconds: number, then: Timed<T>): Animation<T>;
|
|
230
|
+
/**
|
|
231
|
+
* `nth(n)` for every time in the `n`th turn, `[n·period, (n+1)·period)`,
|
|
232
|
+
* handed the time since that turn began — a one-shot played again every
|
|
233
|
+
* `period` seconds, a different one each turn if `nth` says so. `n` is any
|
|
234
|
+
* integer, so a timeline run back past 0 is in turn −1. Within a turn the
|
|
235
|
+
* value next changes when the turn's does or at the next turn, whichever is
|
|
236
|
+
* sooner, and became what it is when the turn's did or at this turn's start,
|
|
237
|
+
* whichever is later: run backwards, a host wakes where the turn began.
|
|
238
|
+
* Reduced, each turn is reduced and the turns kept, so a turn showing
|
|
239
|
+
* something new still shows it on time and a one-shot inside cuts to its
|
|
240
|
+
* end there. Nothing is walked through: a call reads only its turn.
|
|
76
241
|
*/
|
|
77
|
-
|
|
242
|
+
repeat<T>(period: number, nth: (n: number) => Timed<T>): Animation<T>;
|
|
78
243
|
};
|
package/dist/core/animation.js
CHANGED
|
@@ -10,9 +10,10 @@
|
|
|
10
10
|
* answer costs a host a frame that draws what the last one did; a late one
|
|
11
11
|
* skips a change. So an animation that cannot say answers `t`, as
|
|
12
12
|
* `continuous` does. A value drawn as cells carries this answer on every
|
|
13
|
-
* cell (`Style.
|
|
14
|
-
* there (`Segment.nextChange`); a value that is
|
|
15
|
-
* colour — has `nextChange` as its only way to say
|
|
13
|
+
* cell (`Style.hold`, both ends of the hold below), so a host holding
|
|
14
|
+
* the composed frame reads it there (`Segment.nextChange`); a value that is
|
|
15
|
+
* not drawn — a number, a colour — has `nextChange` as its only way to say
|
|
16
|
+
* when to look again.
|
|
16
17
|
*
|
|
17
18
|
* `lastChange(t)` is the other end of the same hold: the last moment, never
|
|
18
19
|
* after `t`, at which the value may have become `at(t)` — `-Infinity` if it
|
|
@@ -22,12 +23,39 @@
|
|
|
22
23
|
* (`./timeline.ts`): run down the timeline, the value next changes where it
|
|
23
24
|
* began. An animation that cannot say answers `t` here too.
|
|
24
25
|
*
|
|
26
|
+
* `reduced()` is the same animation with its motion taken out: what it shows,
|
|
27
|
+
* and when it starts and stops showing it, kept; every moment between two
|
|
28
|
+
* values skipped. A loop holds its first value, a one-shot cuts to its end
|
|
29
|
+
* when it starts, a transition to its new state, a stagger has everyone arrived, and an element is
|
|
30
|
+
* there from when it entered until it is told to leave. Terminals have no
|
|
31
|
+
* `prefers-reduced-motion`, so a host is told (`reducedMotion`,
|
|
32
|
+
* `./protocol.ts`), and under it draws every animation's reduced form, which
|
|
33
|
+
* wakes it only where content changes. It is the one place the preference is
|
|
34
|
+
* honoured for every kind at once, because every kind is this type, and each
|
|
35
|
+
* combinator says its reduced form so a built animation answers it too.
|
|
36
|
+
* [LAW:types-are-the-program] It is an animation and not a value because
|
|
37
|
+
* which content shows is a fact about time that motion is not: a value with
|
|
38
|
+
* no time could only show a scheduled exit as already gone.
|
|
39
|
+
*
|
|
25
40
|
* The instances already in the tree: a `Beat` is an animation of which hold
|
|
26
41
|
* is in progress (`./easing.ts`), a `FrameSequence` of the frame shown and a
|
|
27
42
|
* `Spinner` of the spinner drawn (`../renderables/`), and an `Effect` is, for
|
|
28
43
|
* what an element draws in a frame, an animation of how its cells' colours
|
|
29
44
|
* move (`../renderables/effect.ts`). Every kind of motion is one more.
|
|
30
45
|
*
|
|
46
|
+
* An animation of renderables is a renderable — its face, `placedRender`:
|
|
47
|
+
* placed as the child of any container — a `Panel`, a `Table` cell, a
|
|
48
|
+
* `Group`, a container a user wrote — it draws at the time of the frame it
|
|
49
|
+
* is drawn in (`RenderOptions.frame`; 0, its own start, when printed) and
|
|
50
|
+
* stamps its cells with its hold there, under whatever its value's cells
|
|
51
|
+
* already say, so a frame's hold is the intersection of its cells' holds.
|
|
52
|
+
* [LAW:composability] No time is threaded through the code that places it,
|
|
53
|
+
* and no container knows animations exist. An animation placed inside
|
|
54
|
+
* another's value draws at the frame's time too: a combinator remaps the
|
|
55
|
+
* time it hands its parts, never a value's contents, so content that should
|
|
56
|
+
* follow a part's clock is composed into the part (`map`, `all`) and the
|
|
57
|
+
* result placed. [LAW:one-source-of-truth]
|
|
58
|
+
*
|
|
31
59
|
* [LAW:no-ambient-temporal-coupling] Pure: `t` is an argument and never a
|
|
32
60
|
* clock read (`./clock.ts`), the same `t` gives the same value, nothing is
|
|
33
61
|
* kept between calls, and a call costs the same at `t = 0` and `t = 10⁶`, so
|
|
@@ -36,47 +64,241 @@
|
|
|
36
64
|
*
|
|
37
65
|
* [LAW:one-type-per-behavior] The combinators here are the ones every kind
|
|
38
66
|
* of motion is built from — a constant, a value moving every moment, a map
|
|
39
|
-
* over the value, several at once,
|
|
67
|
+
* over the value, several at once, one animation after another, and one
|
|
68
|
+
* played again every period — and each says how
|
|
40
69
|
* `nextChange` composes, so an animation built from them answers it without
|
|
41
70
|
* its author doing anything.
|
|
71
|
+
*
|
|
72
|
+
* Tier 5 of `src/core/`: `./easing.ts`, `./measure.ts`, `./protocol.ts`,
|
|
73
|
+
* `./segment.ts` and `./style.ts`, nothing else. [LAW:one-way-deps]
|
|
74
|
+
*/
|
|
75
|
+
import { beat, firstReaching, passAt, positiveSeconds } from "./easing.js";
|
|
76
|
+
import { Measurement } from "./measure.js";
|
|
77
|
+
import { isRenderable, render } from "./protocol.js";
|
|
78
|
+
import { Segment } from "./segment.js";
|
|
79
|
+
import { Style } from "./style.js";
|
|
80
|
+
/**
|
|
81
|
+
* What a frame drawn at `t` shows of `animation` under `motion`, and when
|
|
82
|
+
* that changes: the animation's, or under reduced motion its reduced form's
|
|
83
|
+
* (`reduced`). [LAW:single-enforcer] The one place the preference is read:
|
|
84
|
+
* every placed animation (`placedRender`), every effect, a spinner and
|
|
85
|
+
* the template functions choose here, so a host told once
|
|
86
|
+
* reduces every one.
|
|
42
87
|
*/
|
|
43
|
-
|
|
88
|
+
export function shownAt(animation, t, motion) {
|
|
89
|
+
const reduced = motion.reducedMotion === true;
|
|
90
|
+
const drawn = reduced ? animation.reduced() : animation;
|
|
91
|
+
return { value: drawn.at(t), nextChange: drawn.nextChange(t), drawn, reduced };
|
|
92
|
+
}
|
|
93
|
+
/** The hold `shown`, drawn at `t`, is in: its animation's two answers there. */
|
|
94
|
+
export function holdOf(shown, t) {
|
|
95
|
+
return { lastChange: shown.drawn.lastChange(t), nextChange: shown.nextChange };
|
|
96
|
+
}
|
|
97
|
+
/** The style saying a cell is in `hold`. */
|
|
98
|
+
export function stamp(hold) {
|
|
99
|
+
return new Style({ hold });
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* `segments`, every one carrying `hold` under whatever it already said: the
|
|
103
|
+
* later `lastChange` and the sooner `nextChange` win (`Style.add`), so a
|
|
104
|
+
* frame's hold is the intersection of its cells', narrower than the truth
|
|
105
|
+
* and never wider. [LAW:single-enforcer] The one way a time is put on drawn
|
|
106
|
+
* cells, by whatever draws for a time it knows.
|
|
107
|
+
*/
|
|
108
|
+
export function stampFrame(segments, hold) {
|
|
109
|
+
return Segment.applyStyle(segments, stamp(hold));
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* The moment a frame drawn under `options` is drawn for: the frame's time,
|
|
113
|
+
* which the host writes once a frame (`RenderOptions.frame`), or with no
|
|
114
|
+
* frame — a `print` — 0, the start of an animation's own clock, the one
|
|
115
|
+
* moment it can name without reading one. [LAW:one-source-of-truth] Nothing
|
|
116
|
+
* else hands a placed animation its time, so an animation inside another's
|
|
117
|
+
* value draws at the frame's time too, never at a time a combinator mapped.
|
|
118
|
+
*/
|
|
119
|
+
export function frameTimeOf(options) {
|
|
120
|
+
return options.frame?.time ?? 0;
|
|
121
|
+
}
|
|
122
|
+
/** `value` as what an animation's face draws. [LAW:no-silent-failure] An animation of numbers made through `Animation.of` has a face at runtime and nothing to draw. */
|
|
123
|
+
function drawable(value) {
|
|
124
|
+
if (!isRenderable(value))
|
|
125
|
+
throw new TypeError("an Animation is drawn only when its values are renderables; place what it maps to (Animation.map)");
|
|
126
|
+
return value;
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* An animation's face, `render`: drawn at the time of the frame it is drawn
|
|
130
|
+
* in (`frameTimeOf`), the value `shownAt` picks, every segment stamped with
|
|
131
|
+
* its hold there under whatever its own cells say, and the hold told to the
|
|
132
|
+
* frame (`RenderOptions.onHold`), because a frame of no cells — an element
|
|
133
|
+
* not yet entered — may have cells later, and must say when without taking a
|
|
134
|
+
* row to say it in. A container — the library's or a user's — that draws its
|
|
135
|
+
* children through `render` draws an animation among them with no word about
|
|
136
|
+
* time: the hold goes back the way the time came in. [LAW:composability]
|
|
137
|
+
*/
|
|
138
|
+
export function* placedRender(animation, options) {
|
|
139
|
+
const t = frameTimeOf(options);
|
|
140
|
+
const shown = shownAt(animation, t, options);
|
|
141
|
+
const value = drawable(shown.value);
|
|
142
|
+
const hold = holdOf(shown, t);
|
|
143
|
+
options.onHold?.(hold);
|
|
144
|
+
yield* stampFrame(render(value, options), hold);
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* An animation's face, `measure`: what it shows at the frame's time, measured,
|
|
148
|
+
* and its hold there told (`RenderOptions.onHold`), because a container lays
|
|
149
|
+
* out from the measurement and must lay out again when it changes.
|
|
150
|
+
*/
|
|
151
|
+
export function placedMeasure(animation, options) {
|
|
152
|
+
const t = frameTimeOf(options);
|
|
153
|
+
const shown = shownAt(animation, t, options);
|
|
154
|
+
options.onHold?.(holdOf(shown, t));
|
|
155
|
+
return Measurement.get(options, drawable(shown.value));
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Every member of `source`, its own and its class's, as own properties of a
|
|
159
|
+
* new object: a method bound to `source`, a getter read off it live, a field
|
|
160
|
+
* as it is. So a class instance keeps its methods, and a spread of the copy
|
|
161
|
+
* is whole.
|
|
162
|
+
*/
|
|
163
|
+
function membersOf(source) {
|
|
164
|
+
const copy = {};
|
|
165
|
+
for (let layer = source; layer !== null && layer !== Object.prototype; layer = Object.getPrototypeOf(layer)) {
|
|
166
|
+
for (const key of Reflect.ownKeys(layer)) {
|
|
167
|
+
if (key === "constructor" || Object.hasOwn(copy, key))
|
|
168
|
+
continue;
|
|
169
|
+
const member = Object.getOwnPropertyDescriptor(layer, key);
|
|
170
|
+
Object.defineProperty(copy, key, member.get !== undefined
|
|
171
|
+
? { enumerable: true, configurable: true, get: () => Reflect.get(source, key) }
|
|
172
|
+
: { enumerable: true, configurable: true, writable: true, value: typeof member.value === "function" ? member.value.bind(source) : member.value });
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
return copy;
|
|
176
|
+
}
|
|
44
177
|
export const Animation = {
|
|
178
|
+
/**
|
|
179
|
+
* `timed` as an animation: every member it carries kept (`membersOf`),
|
|
180
|
+
* with the face its values call for — `render` and `measure`,
|
|
181
|
+
* drawing it at the frame's time. Every animation made from a literal is
|
|
182
|
+
* made here, the library's and a user's alike. [LAW:single-enforcer] The
|
|
183
|
+
* one cast in the type: a literal's `T` is not known at runtime, so every
|
|
184
|
+
* animation made here has the face, and one whose values are not
|
|
185
|
+
* renderables throws when it is drawn.
|
|
186
|
+
*/
|
|
187
|
+
of(timed) {
|
|
188
|
+
const faced = Object.assign(membersOf(timed), {
|
|
189
|
+
render: (options) => placedRender(faced, options),
|
|
190
|
+
measure: (options) => placedMeasure(faced, options),
|
|
191
|
+
});
|
|
192
|
+
return faced;
|
|
193
|
+
},
|
|
45
194
|
/** `value` at every time, never changing. */
|
|
46
195
|
constant(value) {
|
|
47
|
-
|
|
196
|
+
const still = Animation.of({ at: () => value, nextChange: () => Number.POSITIVE_INFINITY, lastChange: () => Number.NEGATIVE_INFINITY, reduced: () => still });
|
|
197
|
+
return still;
|
|
198
|
+
},
|
|
199
|
+
/**
|
|
200
|
+
* `before` until 0, then `after`: an instant change at 0 — a cut, what a
|
|
201
|
+
* one-shot is with its motion taken out, its start kept and its end at once.
|
|
202
|
+
*/
|
|
203
|
+
cut(before, after) {
|
|
204
|
+
const cut = Animation.of({
|
|
205
|
+
at: (t) => (t < 0 ? before : after),
|
|
206
|
+
nextChange: (t) => (t < 0 ? 0 : Number.POSITIVE_INFINITY),
|
|
207
|
+
lastChange: (t) => (t < 0 ? Number.NEGATIVE_INFINITY : 0),
|
|
208
|
+
reduced: () => cut,
|
|
209
|
+
});
|
|
210
|
+
return cut;
|
|
48
211
|
},
|
|
49
212
|
/**
|
|
50
213
|
* The value `at` gives, taken to move at every moment: what an animation
|
|
51
214
|
* that cannot say when it holds still is, so a host draws it every frame.
|
|
215
|
+
* Reduced, it holds its first value, as a loop does; a one-shot built on
|
|
216
|
+
* it says its own end (`transition`, `../renderables/effects.ts`).
|
|
52
217
|
*/
|
|
53
218
|
continuous(at) {
|
|
54
|
-
return { at, nextChange: (t) => t, lastChange: (t) => t };
|
|
219
|
+
return Animation.of({ at, nextChange: (t) => t, lastChange: (t) => t, reduced: () => Animation.constant(at(0)) });
|
|
220
|
+
},
|
|
221
|
+
/**
|
|
222
|
+
* The time itself, in seconds, moving at every moment: what a clock, a
|
|
223
|
+
* counter or anything else that reports the time is mapped from. It is
|
|
224
|
+
* information and not motion, so reduced it is itself, and a clock shown
|
|
225
|
+
* to a reader who wants no motion still keeps the time; motion computed
|
|
226
|
+
* from the time is `continuous`, which reduced holds still.
|
|
227
|
+
* [LAW:types-are-the-program]
|
|
228
|
+
*/
|
|
229
|
+
time() {
|
|
230
|
+
const time = Animation.of({ at: (t) => t, nextChange: (t) => t, lastChange: (t) => t, reduced: () => time });
|
|
231
|
+
return time;
|
|
55
232
|
},
|
|
56
233
|
/**
|
|
57
234
|
* `animation`'s value through `f`, changing when it does. Two values `f`
|
|
58
235
|
* maps alike make an early answer, which the contract allows.
|
|
59
236
|
*/
|
|
60
237
|
map(animation, f) {
|
|
61
|
-
return
|
|
238
|
+
return Animation.of({
|
|
239
|
+
at: (t) => f(animation.at(t)),
|
|
240
|
+
nextChange: (t) => animation.nextChange(t),
|
|
241
|
+
lastChange: (t) => animation.lastChange(t),
|
|
242
|
+
reduced: () => Animation.map(animation.reduced(), f),
|
|
243
|
+
});
|
|
62
244
|
},
|
|
63
245
|
/**
|
|
64
246
|
* Every one of `animations` at once, their values in order, changing when
|
|
65
247
|
* the soonest of them does — so a stack of effects drawn together holds
|
|
66
|
-
* still exactly while each of them does
|
|
248
|
+
* still exactly while each of them does, and reduced each reduced — a
|
|
249
|
+
* stagger with every child arrived. None never changes.
|
|
67
250
|
*/
|
|
68
251
|
all(animations) {
|
|
69
|
-
return {
|
|
252
|
+
return Animation.of({
|
|
253
|
+
// [LAW:types-are-the-program] Each value at its own place in the tuple,
|
|
254
|
+
// which `map` cannot say of its array.
|
|
70
255
|
at: (t) => animations.map((animation) => animation.at(t)),
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
256
|
+
// A fold, not a spread: a stagger over a table's rows is a list as long
|
|
257
|
+
// as the table, past what an engine takes as arguments.
|
|
258
|
+
nextChange: (t) => animations.reduce((soonest, animation) => Math.min(soonest, animation.nextChange(t)), Number.POSITIVE_INFINITY),
|
|
259
|
+
lastChange: (t) => animations.reduce((latest, animation) => Math.max(latest, animation.lastChange(t)), Number.NEGATIVE_INFINITY),
|
|
260
|
+
reduced: () => Animation.all(animations.map((animation) => animation.reduced())),
|
|
261
|
+
});
|
|
262
|
+
},
|
|
263
|
+
/**
|
|
264
|
+
* `animation` sampled once a hold of `beat`: within a hold, its value at
|
|
265
|
+
* the hold's start, and at each hold's start it moves on. It changes at the
|
|
266
|
+
* first hold's start the sampled animation has moved by: the end of this
|
|
267
|
+
* hold for one that moves every moment, the hold its next change falls in
|
|
268
|
+
* for one that holds longer, and never for one that never changes again.
|
|
269
|
+
* The answer is read off what the animation says, never a comparison of
|
|
270
|
+
* values, so one that is back on the same value by the next hold still
|
|
271
|
+
* turns the beat there, an early answer the contract allows.
|
|
272
|
+
* [LAW:one-source-of-truth] The one stepping rule, for an effect's frames
|
|
273
|
+
* and a transition's schedule alike.
|
|
274
|
+
*/
|
|
275
|
+
onBeat(animation, beat) {
|
|
276
|
+
return Animation.of({
|
|
277
|
+
at: (t) => animation.at(beat.lastChange(t)),
|
|
278
|
+
nextChange(t) {
|
|
279
|
+
const moved = animation.nextChange(beat.lastChange(t));
|
|
280
|
+
if (moved === Number.POSITIVE_INFINITY)
|
|
281
|
+
return moved;
|
|
282
|
+
const end = beat.nextChange(t);
|
|
283
|
+
// The hold boundaries after `t` are `end`, then each hold's end in turn;
|
|
284
|
+
// the value turns at the first one the animation has moved by.
|
|
285
|
+
return moved <= end ? end : beat.lastChange(moved) === moved ? moved : beat.nextChange(moved);
|
|
286
|
+
},
|
|
287
|
+
// The value held now began at this hold's start at the latest: an early
|
|
288
|
+
// answer where the animation had not moved by it, which the contract allows.
|
|
289
|
+
lastChange: (t) => beat.lastChange(t),
|
|
290
|
+
// Reduced, nothing moves between beats, so there is nothing to sample on them.
|
|
291
|
+
reduced: () => animation.reduced(),
|
|
292
|
+
});
|
|
74
293
|
},
|
|
75
294
|
/**
|
|
76
295
|
* `first` until `seconds`, then `then`, handed the time since it began —
|
|
77
296
|
* each part on its own clock from 0, as every effect is. Before the switch
|
|
78
297
|
* the value next changes when `first`'s does or at the switch, whichever is
|
|
79
298
|
* sooner; after it, when `then`'s does, carried back onto this timeline.
|
|
299
|
+
* Reduced, each part is reduced and the switch kept, so what either shows
|
|
300
|
+
* shows on time; a one-shot that is the motion into what follows says its
|
|
301
|
+
* own end (`transition`, `../renderables/effects.ts`).
|
|
80
302
|
*/
|
|
81
303
|
sequence(first, seconds, then) {
|
|
82
304
|
positiveSeconds("Animation.sequence seconds", seconds);
|
|
@@ -86,7 +308,7 @@ export const Animation = {
|
|
|
86
308
|
// under it, where `at` still shows the old value, or over it, a float
|
|
87
309
|
// after the change showed.
|
|
88
310
|
const carried = (local) => (Number.isFinite(local) ? firstReaching(local + seconds, (x) => x - seconds >= local) : local);
|
|
89
|
-
return {
|
|
311
|
+
return Animation.of({
|
|
90
312
|
at: (t) => (t - seconds >= 0 ? then.at(t - seconds) : first.at(t)),
|
|
91
313
|
nextChange(t) {
|
|
92
314
|
const into = t - seconds;
|
|
@@ -102,6 +324,52 @@ export const Animation = {
|
|
|
102
324
|
return first.lastChange(t);
|
|
103
325
|
return Math.min(t, Math.max(seconds, carried(then.lastChange(into))));
|
|
104
326
|
},
|
|
105
|
-
|
|
327
|
+
reduced: () => Animation.sequence(first.reduced(), seconds, then.reduced()),
|
|
328
|
+
});
|
|
329
|
+
},
|
|
330
|
+
/**
|
|
331
|
+
* `nth(n)` for every time in the `n`th turn, `[n·period, (n+1)·period)`,
|
|
332
|
+
* handed the time since that turn began — a one-shot played again every
|
|
333
|
+
* `period` seconds, a different one each turn if `nth` says so. `n` is any
|
|
334
|
+
* integer, so a timeline run back past 0 is in turn −1. Within a turn the
|
|
335
|
+
* value next changes when the turn's does or at the next turn, whichever is
|
|
336
|
+
* sooner, and became what it is when the turn's did or at this turn's start,
|
|
337
|
+
* whichever is later: run backwards, a host wakes where the turn began.
|
|
338
|
+
* Reduced, each turn is reduced and the turns kept, so a turn showing
|
|
339
|
+
* something new still shows it on time and a one-shot inside cuts to its
|
|
340
|
+
* end there. Nothing is walked through: a call reads only its turn.
|
|
341
|
+
*/
|
|
342
|
+
repeat(period, nth) {
|
|
343
|
+
positiveSeconds("Animation.repeat period", period);
|
|
344
|
+
// [LAW:single-enforcer] Which turn a time is in, and how far into it, is
|
|
345
|
+
// `passAt`, the wrap a `beat` and `Phase.loop` read time through, so a
|
|
346
|
+
// repeat beside a loop of the same period turns on the same float; where
|
|
347
|
+
// a turn begins and the next begins are a one-hold beat's two ends.
|
|
348
|
+
const turns = beat([period]);
|
|
349
|
+
// The turn's moment `local`, on its own clock, carried onto this one: the
|
|
350
|
+
// first float the wrap places at or past `local` in turn `n`, or in a
|
|
351
|
+
// later turn, found as `sequence` carries its second part's.
|
|
352
|
+
const carried = (n, began, local) => Number.isFinite(local)
|
|
353
|
+
? firstReaching(began + local, (x) => {
|
|
354
|
+
const { pass, into } = passAt("Animation.repeat", x, period);
|
|
355
|
+
return pass > n || (pass === n && into >= local);
|
|
356
|
+
})
|
|
357
|
+
: local;
|
|
358
|
+
return Animation.of({
|
|
359
|
+
at: (t) => {
|
|
360
|
+
const { pass, into } = passAt("Animation.repeat", t, period);
|
|
361
|
+
return nth(pass).at(into);
|
|
362
|
+
},
|
|
363
|
+
nextChange(t) {
|
|
364
|
+
const { pass, into } = passAt("Animation.repeat", t, period);
|
|
365
|
+
return Math.max(t, Math.min(turns.nextChange(t), carried(pass, turns.lastChange(t), nth(pass).nextChange(into))));
|
|
366
|
+
},
|
|
367
|
+
lastChange(t) {
|
|
368
|
+
const { pass, into } = passAt("Animation.repeat", t, period);
|
|
369
|
+
const began = turns.lastChange(t);
|
|
370
|
+
return Math.min(t, Math.max(began, carried(pass, began, nth(pass).lastChange(into))));
|
|
371
|
+
},
|
|
372
|
+
reduced: () => Animation.repeat(period, (n) => nth(n).reduced()),
|
|
373
|
+
});
|
|
106
374
|
},
|
|
107
375
|
};
|