@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
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Timeline — the map from the host's clock to the time an animation is
|
|
3
|
+
* asked for: playback control, and nothing else.
|
|
4
|
+
*
|
|
5
|
+
* Every animation is a pure function of its time (`./animation.ts`), so
|
|
6
|
+
* pausing, reversing, scrubbing and speeding one up are not things done to
|
|
7
|
+
* it but a change in which time it is asked for. A timeline is that map, as
|
|
8
|
+
* a value: an animation time at a host time, a rate — animation seconds per
|
|
9
|
+
* host second, negative running it backwards — and whether it is held. Each
|
|
10
|
+
* operation is a value in and a value out, so a paused timeline resumed at a
|
|
11
|
+
* new host time is one that maps that host time to the time it froze at, and
|
|
12
|
+
* two hosts holding equal timelines ask every animation for the same time.
|
|
13
|
+
*
|
|
14
|
+
* A paused timeline carries no host anchor: it maps every host time to the
|
|
15
|
+
* one time it holds, so there is no origin for it to be wrong about.
|
|
16
|
+
* [LAW:types-are-the-program]
|
|
17
|
+
*
|
|
18
|
+
* `run` is an animation on host time. Its `nextChange` is the host moment
|
|
19
|
+
* the mapped time reaches the animation's own answer, which is what
|
|
20
|
+
* `Spinner`'s speed scaled its holds for before this existed: an answer a
|
|
21
|
+
* host reads is on the host's timeline, and a beat over scaled time answers
|
|
22
|
+
* on the scaled one. Run backwards, the host next sees a change when the
|
|
23
|
+
* mapped time drops under where the value began — the animation's
|
|
24
|
+
* `lastChange` — so a reversed loop turns exactly at its hold boundaries,
|
|
25
|
+
* and a reversed one-shot changes at its switch and never before its start.
|
|
26
|
+
* Paused, nothing changes: never, and never did. The host moment is the
|
|
27
|
+
* first host float the map carries past the edge, searched out from the sum
|
|
28
|
+
* `origin + (edge - time) / rate` (`firstReaching`): a host float is a step
|
|
29
|
+
* of `rate` on the animation's clock, so the floats between that sum and
|
|
30
|
+
* the first one past the edge are as many as the two clocks' resolutions
|
|
31
|
+
* are apart — unbounded, far from the origin or at a slow rate — which is
|
|
32
|
+
* why it is a search and not the ulp walk a sum on one clock takes. A value
|
|
33
|
+
* moving at the mapped time changes at the host time itself.
|
|
34
|
+
*
|
|
35
|
+
* A stagger — a Group's children pulsing in sequence, a light walking down
|
|
36
|
+
* a Table's rows — is the same map once per child: `stagger` runs each
|
|
37
|
+
* child's animation on a timeline playing from its index times the delay,
|
|
38
|
+
* and the group changes when the soonest child does, each child's answer
|
|
39
|
+
* carried onto the group's clock by `run`. Before its turn a child is asked
|
|
40
|
+
* for a time before 0, which is its own business: a loop is shifted in
|
|
41
|
+
* phase. Tree, Table, Group and Layout take the children's frames as they
|
|
42
|
+
* take any renderables, through `Animation.map`, so none of them knows a
|
|
43
|
+
* stagger exists. [LAW:composability] A lifecycle is not staggered here: it
|
|
44
|
+
* carries its turn as `entered`, on the host's clock its `leave` and `gone`
|
|
45
|
+
* take, so moving it onto a shifted clock would lose them
|
|
46
|
+
* ([LAW:one-source-of-truth] for when it arrives).
|
|
47
|
+
*
|
|
48
|
+
* [LAW:no-ambient-temporal-coupling] Nothing here reads a clock. The host
|
|
49
|
+
* time is an argument everywhere, and a timeline is built from one the host
|
|
50
|
+
* hands in, so the same host time maps to the same animation time at one
|
|
51
|
+
* sample a second or sixty.
|
|
52
|
+
*/
|
|
53
|
+
import { Animation } from "./animation.js";
|
|
54
|
+
import { firstReaching } from "./easing.js";
|
|
55
|
+
/** Throws, naming `what`, unless `seconds` is a finite number of seconds. */
|
|
56
|
+
function finiteSeconds(what, seconds) {
|
|
57
|
+
if (!Number.isFinite(seconds))
|
|
58
|
+
throw new RangeError(`${what} must be a finite number of seconds, got ${seconds}`);
|
|
59
|
+
return seconds;
|
|
60
|
+
}
|
|
61
|
+
/** [LAW:single-enforcer] A host time is a finite number of seconds, refused once, at every operation that takes one. */
|
|
62
|
+
const hostTime = (host) => finiteSeconds("Timeline host time", host);
|
|
63
|
+
/**
|
|
64
|
+
* [LAW:parse-dont-validate] A rate is a finite number other than zero,
|
|
65
|
+
* settled where it is given: a rate of zero would be a second way to hold a
|
|
66
|
+
* timeline still, and `pause` is the one.
|
|
67
|
+
*/
|
|
68
|
+
function playbackRate(rate) {
|
|
69
|
+
if (!Number.isFinite(rate) || rate === 0)
|
|
70
|
+
throw new RangeError(`Timeline rate must be finite and not zero, got ${rate}`);
|
|
71
|
+
return rate;
|
|
72
|
+
}
|
|
73
|
+
/** `timeline` with `time` at host time `host`, at `rate`, held as it was. A paused timeline keeps no origin, and refuses a host time it cannot use all the same. */
|
|
74
|
+
function anchored(timeline, host, time, rate) {
|
|
75
|
+
const origin = hostTime(host);
|
|
76
|
+
return timeline.playing ? { playing: true, origin, time, rate } : { playing: false, time, rate };
|
|
77
|
+
}
|
|
78
|
+
/** `animation` asked at the time `playing` maps each host time to, saying on host time when it changes. */
|
|
79
|
+
function played(animation, { origin, time, rate }) {
|
|
80
|
+
const mapped = (host) => time + rate * (host - origin);
|
|
81
|
+
const forward = rate > 0;
|
|
82
|
+
// Forwards, the host next sees the animation's own next change, and the
|
|
83
|
+
// value began when the animation says it did; backwards, it next sees a
|
|
84
|
+
// change where the value began, and the value began where the animation
|
|
85
|
+
// says it ends. A hold is `[lastChange, nextChange)`, so the far side of
|
|
86
|
+
// an edge is at it going up and strictly under it coming down.
|
|
87
|
+
const ahead = forward ? (t) => animation.nextChange(t) : (t) => animation.lastChange(t);
|
|
88
|
+
const behind = forward ? (t) => animation.lastChange(t) : (t) => animation.nextChange(t);
|
|
89
|
+
const past = forward ? (t, edge) => t >= edge : (t, edge) => t < edge;
|
|
90
|
+
// The first host moment the map carries past `edge`, an animation time.
|
|
91
|
+
// An edge at infinity, or one so far off at this rate that no host float
|
|
92
|
+
// reaches it, is never reached, forwards or backwards: the sum's own sign
|
|
93
|
+
// says which, as a host moment past either end of the host's clock.
|
|
94
|
+
const hostOf = (edge) => {
|
|
95
|
+
const sum = origin + (edge - time) / rate;
|
|
96
|
+
return Number.isFinite(sum) ? firstReaching(sum, (host) => past(mapped(host), edge)) : sum;
|
|
97
|
+
};
|
|
98
|
+
return Animation.of({
|
|
99
|
+
at: (host) => animation.at(mapped(hostTime(host))),
|
|
100
|
+
nextChange(host) {
|
|
101
|
+
const h = hostTime(host);
|
|
102
|
+
const t = mapped(h);
|
|
103
|
+
const edge = ahead(t);
|
|
104
|
+
return edge === t ? h : Math.max(h, hostOf(edge));
|
|
105
|
+
},
|
|
106
|
+
lastChange(host) {
|
|
107
|
+
const h = hostTime(host);
|
|
108
|
+
const t = mapped(h);
|
|
109
|
+
const edge = behind(t);
|
|
110
|
+
return edge === t ? h : Math.min(h, hostOf(edge));
|
|
111
|
+
},
|
|
112
|
+
reduced: () => played(animation.reduced(), { playing: true, origin, time, rate }),
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
export const Timeline = {
|
|
116
|
+
/** Playing from host time `origin`, at which the animation time is 0, at `rate` animation seconds a host second. */
|
|
117
|
+
play(origin, rate = 1) {
|
|
118
|
+
return { playing: true, origin: finiteSeconds("Timeline.play origin", origin), time: 0, rate: playbackRate(rate) };
|
|
119
|
+
},
|
|
120
|
+
/** The animation time at host time `host`. */
|
|
121
|
+
at(timeline, host) {
|
|
122
|
+
const h = hostTime(host);
|
|
123
|
+
return timeline.playing ? timeline.time + timeline.rate * (h - timeline.origin) : timeline.time;
|
|
124
|
+
},
|
|
125
|
+
/** `timeline` held at the animation time it maps `host` to. Pausing a paused timeline holds the same time. */
|
|
126
|
+
pause(timeline, host) {
|
|
127
|
+
return { playing: false, time: Timeline.at(timeline, host), rate: timeline.rate };
|
|
128
|
+
},
|
|
129
|
+
/** `timeline` playing from `host` at the animation time it maps `host` to, at its rate. Resuming a playing timeline is the same map. */
|
|
130
|
+
resume(timeline, host) {
|
|
131
|
+
return { playing: true, origin: hostTime(host), time: Timeline.at(timeline, host), rate: timeline.rate };
|
|
132
|
+
},
|
|
133
|
+
/** `timeline` with the animation at `time` at host time `host`, playing or held as it was. */
|
|
134
|
+
seek(timeline, host, time) {
|
|
135
|
+
return anchored(timeline, host, finiteSeconds("Timeline.seek time", time), timeline.rate);
|
|
136
|
+
},
|
|
137
|
+
/** `timeline` at `rate` from host time `host` on, with no jump: the animation time at `host` is what it was. */
|
|
138
|
+
withRate(timeline, host, rate) {
|
|
139
|
+
return anchored(timeline, host, Timeline.at(timeline, host), playbackRate(rate));
|
|
140
|
+
},
|
|
141
|
+
/**
|
|
142
|
+
* `animation` on host time: its value at the time `timeline` maps each
|
|
143
|
+
* host time to, changing at the host moment the mapped time reaches the
|
|
144
|
+
* animation's own answer — backwards, when it drops under where the value
|
|
145
|
+
* began. Paused, the value never changes and never did. The value is the
|
|
146
|
+
* animation's own, on no clock: placed, it is stamped at the frame's time
|
|
147
|
+
* (`placedRender`, `./animation.ts`) with this animation's answers, so a
|
|
148
|
+
* value's contents follow the frame's clock and not this map.
|
|
149
|
+
* Reduced, it is the animation reduced on the same timeline: what shows
|
|
150
|
+
* still follows the timeline, and only the motion is gone.
|
|
151
|
+
*/
|
|
152
|
+
run(timeline, animation) {
|
|
153
|
+
if (timeline.playing)
|
|
154
|
+
return played(animation, timeline);
|
|
155
|
+
return Animation.of({
|
|
156
|
+
at: (host) => animation.at(Timeline.at(timeline, host)),
|
|
157
|
+
nextChange: (host) => (hostTime(host), Number.POSITIVE_INFINITY),
|
|
158
|
+
lastChange: (host) => (hostTime(host), Number.NEGATIVE_INFINITY),
|
|
159
|
+
reduced: () => Timeline.run(timeline, animation.reduced()),
|
|
160
|
+
});
|
|
161
|
+
},
|
|
162
|
+
/**
|
|
163
|
+
* Every one of `animations` at once, each on a timeline playing from its
|
|
164
|
+
* index times `delay` seconds after the earliest, their values in order, on
|
|
165
|
+
* a clock of the stagger's own from 0. It changes when the soonest of them
|
|
166
|
+
* does, on that clock (`Animation.all` of `run`s). A delay of 0 starts them
|
|
167
|
+
* together and a negative one starts the last at 0 and the first last.
|
|
168
|
+
* Reduced, the offsets are the cascade's choreography, which is motion: every
|
|
169
|
+
* child reduced at once, so from 0 everyone has arrived.
|
|
170
|
+
*/
|
|
171
|
+
stagger(animations, delay) {
|
|
172
|
+
finiteSeconds("Timeline.stagger delay", delay);
|
|
173
|
+
const span = finiteSeconds("Timeline.stagger delay times the last index", Math.max(0, animations.length - 1) * delay);
|
|
174
|
+
// [LAW:dataflow-not-control-flow] Whichever way the cascade runs, every
|
|
175
|
+
// origin is shifted by the lowest of them, so the earliest child plays from 0.
|
|
176
|
+
const earliest = Math.min(0, span);
|
|
177
|
+
const cascade = Animation.all(animations.map((animation, i) => Timeline.run(Timeline.play(i * delay - earliest), animation)));
|
|
178
|
+
return Animation.of({ ...cascade, reduced: () => Animation.all(animations.map((animation) => animation.reduced())) });
|
|
179
|
+
},
|
|
180
|
+
};
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tween — a value eased from one number to another over seconds, then held:
|
|
3
|
+
* the animation a progress bar's fill, a counter or a width is handed so it
|
|
4
|
+
* moves to its new value instead of jumping. The first `Animation` of a
|
|
5
|
+
* value that is never drawn as cells, so its `nextChange` is its only way of
|
|
6
|
+
* saying when to look again (`./animation.ts`).
|
|
7
|
+
*
|
|
8
|
+
* The value at `t` is the `Timing`'s ease of the way from `from` to `to`
|
|
9
|
+
* through `Phase.once(0, seconds)`: `from` at `t ≤ 0` whenever the ease
|
|
10
|
+
* starts at 0, as every built-in does but `step-start` and the steps that
|
|
11
|
+
* jump at the start of their interval, which show their first jump before
|
|
12
|
+
* the tween begins, as `ColorRamp.at` does; `to` from `seconds` on. It is
|
|
13
|
+
* the one-shot phase every effect transition reads time
|
|
14
|
+
* through, so there is no second rule about when a one-shot begins: it
|
|
15
|
+
* begins at 0 on its own clock, and starting it later is `Animation.sequence`'s
|
|
16
|
+
* or a `Timeline`'s job. [LAW:single-enforcer] An ease that overshoots is
|
|
17
|
+
* shown as it is — a count eased by `back-out` passes its target and comes
|
|
18
|
+
* back, which is what the curve is for — and lands exactly on `to`, because
|
|
19
|
+
* every built-in ease is exactly 1 at 1 and the blend is `(1 − p)·from +
|
|
20
|
+
* p·to`, exact at both ends where `from + (to − from)·p` is not.
|
|
21
|
+
*
|
|
22
|
+
* A tween keeps nothing between calls, so retargeting is not a mutation: a
|
|
23
|
+
* tween whose target changes at `t₁` is the old tween until `t₁`, then a new
|
|
24
|
+
* one from the old one's value there — `Animation.sequence(old, t₁,
|
|
25
|
+
* tween(old.at(t₁), to, timing))` — and starts from where it is because
|
|
26
|
+
* that is where it was built from. [LAW:no-ambient-temporal-coupling]
|
|
27
|
+
*
|
|
28
|
+
* Over colours it is a `ColorRamp` read over time (`../themes/ramp.ts`,
|
|
29
|
+
* `over`): the ramp is already the one place two colours are blended, in
|
|
30
|
+
* OKLCH, and a colour tween is a two-stop ramp over its seconds, not a
|
|
31
|
+
* second blender. [LAW:one-source-of-truth]
|
|
32
|
+
*
|
|
33
|
+
* Tier 6 of `src/core/`: `./easing.ts` and `./animation.ts`, nothing else.
|
|
34
|
+
* [LAW:one-way-deps]
|
|
35
|
+
*/
|
|
36
|
+
import { Animation } from "./animation.js";
|
|
37
|
+
import { type Timing } from "./easing.js";
|
|
38
|
+
/**
|
|
39
|
+
* `from` eased to `to` over `timing`, as an `Animation<number>`: moving from
|
|
40
|
+
* 0 to `timing.seconds` on its own clock, the ease of 0 before (`from`, for
|
|
41
|
+
* every ease that starts at 0), `to` after, and so
|
|
42
|
+
* seen at its end, never mid-way, by a host whose frames are further apart
|
|
43
|
+
* than it is long. `from` and `to` must be finite; the seconds are checked
|
|
44
|
+
* where every one-shot's are, in `Phase.once`. A tween to where it already
|
|
45
|
+
* is is `Animation.constant(from)`, decided here once: the blend would land
|
|
46
|
+
* one ulp off `from` at some `p`, and a value that never moves is one that
|
|
47
|
+
* never changes, so a host is told not to look again. [LAW:parse-dont-validate]
|
|
48
|
+
*/
|
|
49
|
+
export declare function tween(from: number, to: number, timing: Timing): Animation<number>;
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tween — a value eased from one number to another over seconds, then held:
|
|
3
|
+
* the animation a progress bar's fill, a counter or a width is handed so it
|
|
4
|
+
* moves to its new value instead of jumping. The first `Animation` of a
|
|
5
|
+
* value that is never drawn as cells, so its `nextChange` is its only way of
|
|
6
|
+
* saying when to look again (`./animation.ts`).
|
|
7
|
+
*
|
|
8
|
+
* The value at `t` is the `Timing`'s ease of the way from `from` to `to`
|
|
9
|
+
* through `Phase.once(0, seconds)`: `from` at `t ≤ 0` whenever the ease
|
|
10
|
+
* starts at 0, as every built-in does but `step-start` and the steps that
|
|
11
|
+
* jump at the start of their interval, which show their first jump before
|
|
12
|
+
* the tween begins, as `ColorRamp.at` does; `to` from `seconds` on. It is
|
|
13
|
+
* the one-shot phase every effect transition reads time
|
|
14
|
+
* through, so there is no second rule about when a one-shot begins: it
|
|
15
|
+
* begins at 0 on its own clock, and starting it later is `Animation.sequence`'s
|
|
16
|
+
* or a `Timeline`'s job. [LAW:single-enforcer] An ease that overshoots is
|
|
17
|
+
* shown as it is — a count eased by `back-out` passes its target and comes
|
|
18
|
+
* back, which is what the curve is for — and lands exactly on `to`, because
|
|
19
|
+
* every built-in ease is exactly 1 at 1 and the blend is `(1 − p)·from +
|
|
20
|
+
* p·to`, exact at both ends where `from + (to − from)·p` is not.
|
|
21
|
+
*
|
|
22
|
+
* A tween keeps nothing between calls, so retargeting is not a mutation: a
|
|
23
|
+
* tween whose target changes at `t₁` is the old tween until `t₁`, then a new
|
|
24
|
+
* one from the old one's value there — `Animation.sequence(old, t₁,
|
|
25
|
+
* tween(old.at(t₁), to, timing))` — and starts from where it is because
|
|
26
|
+
* that is where it was built from. [LAW:no-ambient-temporal-coupling]
|
|
27
|
+
*
|
|
28
|
+
* Over colours it is a `ColorRamp` read over time (`../themes/ramp.ts`,
|
|
29
|
+
* `over`): the ramp is already the one place two colours are blended, in
|
|
30
|
+
* OKLCH, and a colour tween is a two-stop ramp over its seconds, not a
|
|
31
|
+
* second blender. [LAW:one-source-of-truth]
|
|
32
|
+
*
|
|
33
|
+
* Tier 6 of `src/core/`: `./easing.ts` and `./animation.ts`, nothing else.
|
|
34
|
+
* [LAW:one-way-deps]
|
|
35
|
+
*/
|
|
36
|
+
import { Animation } from "./animation.js";
|
|
37
|
+
import { Phase } from "./easing.js";
|
|
38
|
+
/**
|
|
39
|
+
* `from` eased to `to` over `timing`, as an `Animation<number>`: moving from
|
|
40
|
+
* 0 to `timing.seconds` on its own clock, the ease of 0 before (`from`, for
|
|
41
|
+
* every ease that starts at 0), `to` after, and so
|
|
42
|
+
* seen at its end, never mid-way, by a host whose frames are further apart
|
|
43
|
+
* than it is long. `from` and `to` must be finite; the seconds are checked
|
|
44
|
+
* where every one-shot's are, in `Phase.once`. A tween to where it already
|
|
45
|
+
* is is `Animation.constant(from)`, decided here once: the blend would land
|
|
46
|
+
* one ulp off `from` at some `p`, and a value that never moves is one that
|
|
47
|
+
* never changes, so a host is told not to look again. [LAW:parse-dont-validate]
|
|
48
|
+
*/
|
|
49
|
+
export function tween(from, to, timing) {
|
|
50
|
+
for (const [name, v] of [["from", from], ["to", to]]) {
|
|
51
|
+
if (!Number.isFinite(v))
|
|
52
|
+
throw new RangeError(`tween ${name} must be finite, got ${v}`);
|
|
53
|
+
}
|
|
54
|
+
const { ease, seconds } = timing;
|
|
55
|
+
const phase = Phase.once(0, seconds);
|
|
56
|
+
if (from === to)
|
|
57
|
+
return Animation.constant(from);
|
|
58
|
+
return Animation.of({
|
|
59
|
+
at(t) {
|
|
60
|
+
const p = ease(phase(t));
|
|
61
|
+
return (1 - p) * from + p * to;
|
|
62
|
+
},
|
|
63
|
+
// Before it begins the value is held until 0; while it moves, every
|
|
64
|
+
// frame; from `seconds` on, never again — and it became what it is at
|
|
65
|
+
// `seconds`, or always was before it began.
|
|
66
|
+
nextChange: (t) => (t < 0 ? 0 : t < seconds ? t : Number.POSITIVE_INFINITY),
|
|
67
|
+
lastChange: (t) => (t < 0 ? Number.NEGATIVE_INFINITY : t < seconds ? t : seconds),
|
|
68
|
+
// A one-shot, reduced, cuts from where it starts to where it arrives.
|
|
69
|
+
reduced: () => Animation.cut(from, to),
|
|
70
|
+
});
|
|
71
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* View — what a host draws, and the frame it draws from it at a host time on
|
|
3
|
+
* its timeline: the lines, and what they say about when they change.
|
|
4
|
+
*
|
|
5
|
+
* A host — a `Live`, an `App` — reads its clock once a frame and draws its
|
|
6
|
+
* view for that moment. The view is a renderable, drawn as it is now (a
|
|
7
|
+
* table mutated in place between frames), at the time the host's `Timeline`
|
|
8
|
+
* (`./timeline.ts`) maps its clock to, written on the frame
|
|
9
|
+
* (`RenderOptions.frame`): every animation placed anywhere inside it draws
|
|
10
|
+
* at that time and stamps its cells with when it changes, on that clock
|
|
11
|
+
* (`placedRender`, `./animation.ts`). A program may hand a function for one
|
|
12
|
+
* instead, asked each frame so a view of its state is built from the state
|
|
13
|
+
* as it is then. It is handed no time, and `hostView` makes it the
|
|
14
|
+
* renderable it stands for, so a host holds one shape of view and draws it
|
|
15
|
+
* one way. [LAW:one-type-per-behavior]
|
|
16
|
+
*
|
|
17
|
+
* So a view says when it changes only through what it draws: what moves in
|
|
18
|
+
* it is an animation placed there — `Animation.continuous` for a value
|
|
19
|
+
* computed from the time — and never a time handed to the program.
|
|
20
|
+
* [LAW:one-source-of-truth] Pausing, reversing, scrubbing and speeding up
|
|
21
|
+
* what a host shows are changes to one value the host holds and not to
|
|
22
|
+
* anything it draws. What the drawn frame says about when it changes is on
|
|
23
|
+
* the timeline's clock, and `drawView` carries it back onto the host's by
|
|
24
|
+
* running the frame through the same timeline: the frame is a sample held
|
|
25
|
+
* for the intersection of its cells' holds (`Segment.lastChange`,
|
|
26
|
+
* `Segment.nextChange`) and the holds it was told that no cell carries
|
|
27
|
+
* (`RenderOptions.onHold`), and `Timeline.run` is the one place a hold is
|
|
28
|
+
* mapped onto host time, forwards, backwards or paused.
|
|
29
|
+
*
|
|
30
|
+
* What a host does with that is its own policy, and it reads it off the
|
|
31
|
+
* `DrawnView`. A view may also change without saying so — it reads state
|
|
32
|
+
* the host cannot see — so a host that polls for such a change, a `Live`'s
|
|
33
|
+
* auto-refresh, polls every frame.
|
|
34
|
+
*
|
|
35
|
+
* Tier 7 of `src/core/`: `./animation.ts`'s `Animation`, `./timeline.ts`,
|
|
36
|
+
* `./segment.ts`, `./protocol.ts` and `./style.ts`'s `Hold`, nothing else.
|
|
37
|
+
* [LAW:one-way-deps]
|
|
38
|
+
*/
|
|
39
|
+
import { type FrameHost, type Renderable, type RenderOptions } from "./protocol.js";
|
|
40
|
+
import { Segment } from "./segment.js";
|
|
41
|
+
import { Timeline } from "./timeline.js";
|
|
42
|
+
/**
|
|
43
|
+
* What a host draws: a renderable — an animation of renderables among them,
|
|
44
|
+
* or anything holding one — or a function asked for one each frame.
|
|
45
|
+
*/
|
|
46
|
+
export type View = Renderable | (() => Renderable);
|
|
47
|
+
/** `view` as the renderable a host holds: a function is asked each time it is drawn. */
|
|
48
|
+
export declare function hostView(view: View): Renderable;
|
|
49
|
+
/**
|
|
50
|
+
* A host's time, its clock read once: `now` on that clock; `epoch`, the
|
|
51
|
+
* moment on it the host began; and `timeline`, the map every view is drawn
|
|
52
|
+
* through. Handed none, a host's timeline starts as `Timeline.play(epoch)`,
|
|
53
|
+
* so a frame is drawn for the seconds since `epoch` until the timeline is
|
|
54
|
+
* paused, sought, re-rated or replaced.
|
|
55
|
+
*/
|
|
56
|
+
export interface HostTime {
|
|
57
|
+
readonly now: number;
|
|
58
|
+
readonly epoch: number;
|
|
59
|
+
readonly timeline: Timeline;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* The time a frame drawn at `time` is drawn for, on the clock its cells are
|
|
63
|
+
* stamped on: the time the timeline maps `now` to. [LAW:single-enforcer]
|
|
64
|
+
* `drawView` draws at it and hands it to the frame as
|
|
65
|
+
* `RenderOptions.frame`, and a host reads it here for anything heard between
|
|
66
|
+
* frames — a key — so a moment a widget keeps and the frame it is compared
|
|
67
|
+
* against are on one clock.
|
|
68
|
+
*/
|
|
69
|
+
export declare function frameTime(time: HostTime): number;
|
|
70
|
+
/** What a frame drawn at host time `t` says about when it changes. */
|
|
71
|
+
export interface FrameSays {
|
|
72
|
+
/** The host time the frame was drawn at. */
|
|
73
|
+
readonly t: number;
|
|
74
|
+
/** The first host moment, never before `t`, at which the frame may stop showing this: `t` while it moves, `Infinity` once it never will. */
|
|
75
|
+
readonly nextChange: number;
|
|
76
|
+
}
|
|
77
|
+
/** A frame drawn from a view: its lines, and what it says. */
|
|
78
|
+
export interface DrawnView extends FrameSays {
|
|
79
|
+
readonly lines: Segment[][];
|
|
80
|
+
}
|
|
81
|
+
/** What a frame not yet drawn at host time `t` says: nothing. */
|
|
82
|
+
export declare const undrawn: (t: number) => FrameSays;
|
|
83
|
+
/**
|
|
84
|
+
* `view` drawn by `drawnBy` at `time` under `options`, its lines shaped by
|
|
85
|
+
* `draw` — the host's own render under those options, with its height and
|
|
86
|
+
* overflow — and when the frame changes on the host's clock: the hold its
|
|
87
|
+
* cells say, carried back through the timeline. [LAW:one-source-of-truth]
|
|
88
|
+
* `options` is the one place the frame reads the reader's preference for
|
|
89
|
+
* motion, handed on to `draw` with the frame: the time it is drawn for
|
|
90
|
+
* (`frameTime`) and `drawnBy`, the host whose time that is.
|
|
91
|
+
*/
|
|
92
|
+
export declare function drawView(view: Renderable, time: HostTime, drawnBy: FrameHost, options: RenderOptions, draw: (renderable: Renderable, options: RenderOptions) => Segment[][]): DrawnView;
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* View — what a host draws, and the frame it draws from it at a host time on
|
|
3
|
+
* its timeline: the lines, and what they say about when they change.
|
|
4
|
+
*
|
|
5
|
+
* A host — a `Live`, an `App` — reads its clock once a frame and draws its
|
|
6
|
+
* view for that moment. The view is a renderable, drawn as it is now (a
|
|
7
|
+
* table mutated in place between frames), at the time the host's `Timeline`
|
|
8
|
+
* (`./timeline.ts`) maps its clock to, written on the frame
|
|
9
|
+
* (`RenderOptions.frame`): every animation placed anywhere inside it draws
|
|
10
|
+
* at that time and stamps its cells with when it changes, on that clock
|
|
11
|
+
* (`placedRender`, `./animation.ts`). A program may hand a function for one
|
|
12
|
+
* instead, asked each frame so a view of its state is built from the state
|
|
13
|
+
* as it is then. It is handed no time, and `hostView` makes it the
|
|
14
|
+
* renderable it stands for, so a host holds one shape of view and draws it
|
|
15
|
+
* one way. [LAW:one-type-per-behavior]
|
|
16
|
+
*
|
|
17
|
+
* So a view says when it changes only through what it draws: what moves in
|
|
18
|
+
* it is an animation placed there — `Animation.continuous` for a value
|
|
19
|
+
* computed from the time — and never a time handed to the program.
|
|
20
|
+
* [LAW:one-source-of-truth] Pausing, reversing, scrubbing and speeding up
|
|
21
|
+
* what a host shows are changes to one value the host holds and not to
|
|
22
|
+
* anything it draws. What the drawn frame says about when it changes is on
|
|
23
|
+
* the timeline's clock, and `drawView` carries it back onto the host's by
|
|
24
|
+
* running the frame through the same timeline: the frame is a sample held
|
|
25
|
+
* for the intersection of its cells' holds (`Segment.lastChange`,
|
|
26
|
+
* `Segment.nextChange`) and the holds it was told that no cell carries
|
|
27
|
+
* (`RenderOptions.onHold`), and `Timeline.run` is the one place a hold is
|
|
28
|
+
* mapped onto host time, forwards, backwards or paused.
|
|
29
|
+
*
|
|
30
|
+
* What a host does with that is its own policy, and it reads it off the
|
|
31
|
+
* `DrawnView`. A view may also change without saying so — it reads state
|
|
32
|
+
* the host cannot see — so a host that polls for such a change, a `Live`'s
|
|
33
|
+
* auto-refresh, polls every frame.
|
|
34
|
+
*
|
|
35
|
+
* Tier 7 of `src/core/`: `./animation.ts`'s `Animation`, `./timeline.ts`,
|
|
36
|
+
* `./segment.ts`, `./protocol.ts` and `./style.ts`'s `Hold`, nothing else.
|
|
37
|
+
* [LAW:one-way-deps]
|
|
38
|
+
*/
|
|
39
|
+
import { render } from "./protocol.js";
|
|
40
|
+
import { Segment } from "./segment.js";
|
|
41
|
+
import { Timeline } from "./timeline.js";
|
|
42
|
+
/** `view` as the renderable a host holds: a function is asked each time it is drawn. */
|
|
43
|
+
export function hostView(view) {
|
|
44
|
+
return typeof view === "function" ? { render: (options) => render(view(), options) } : view;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* The time a frame drawn at `time` is drawn for, on the clock its cells are
|
|
48
|
+
* stamped on: the time the timeline maps `now` to. [LAW:single-enforcer]
|
|
49
|
+
* `drawView` draws at it and hands it to the frame as
|
|
50
|
+
* `RenderOptions.frame`, and a host reads it here for anything heard between
|
|
51
|
+
* frames — a key — so a moment a widget keeps and the frame it is compared
|
|
52
|
+
* against are on one clock.
|
|
53
|
+
*/
|
|
54
|
+
export function frameTime(time) {
|
|
55
|
+
return Timeline.at(time.timeline, time.now);
|
|
56
|
+
}
|
|
57
|
+
/** What a frame not yet drawn at host time `t` says: nothing. */
|
|
58
|
+
export const undrawn = (t) => ({ t, nextChange: Number.POSITIVE_INFINITY });
|
|
59
|
+
/** The hold of the frame `lines` draw, told `told` besides: the intersection of its cells' holds and it. */
|
|
60
|
+
const frameHold = (lines, told) => lines.reduce((hold, row) => ({ lastChange: Math.max(hold.lastChange, Segment.lastChange(row)), nextChange: Math.min(hold.nextChange, Segment.nextChange(row)) }), told);
|
|
61
|
+
/**
|
|
62
|
+
* `view` drawn by `drawnBy` at `time` under `options`, its lines shaped by
|
|
63
|
+
* `draw` — the host's own render under those options, with its height and
|
|
64
|
+
* overflow — and when the frame changes on the host's clock: the hold its
|
|
65
|
+
* cells say, carried back through the timeline. [LAW:one-source-of-truth]
|
|
66
|
+
* `options` is the one place the frame reads the reader's preference for
|
|
67
|
+
* motion, handed on to `draw` with the frame: the time it is drawn for
|
|
68
|
+
* (`frameTime`) and `drawnBy`, the host whose time that is.
|
|
69
|
+
*/
|
|
70
|
+
export function drawView(view, time, drawnBy, options, draw) {
|
|
71
|
+
const { now: host, timeline } = time;
|
|
72
|
+
let told = { lastChange: Number.NEGATIVE_INFINITY, nextChange: Number.POSITIVE_INFINITY };
|
|
73
|
+
const onHold = (hold) => {
|
|
74
|
+
told = { lastChange: Math.max(told.lastChange, hold.lastChange), nextChange: Math.min(told.nextChange, hold.nextChange) };
|
|
75
|
+
};
|
|
76
|
+
const lines = draw(view, { ...options, frame: { time: frameTime(time), host: drawnBy }, onHold });
|
|
77
|
+
const hold = frameHold(lines, told);
|
|
78
|
+
// The frame as the sample of the view it is, on the timeline's clock; the
|
|
79
|
+
// timeline says when the host's clock leaves its hold.
|
|
80
|
+
const sample = { at: () => lines, nextChange: () => hold.nextChange, lastChange: () => hold.lastChange, reduced: () => sample };
|
|
81
|
+
const nextChange = Timeline.run(timeline, sample).nextChange(host);
|
|
82
|
+
return { t: host, lines, nextChange };
|
|
83
|
+
}
|
package/dist/host/app.d.ts
CHANGED
|
@@ -34,8 +34,10 @@ import { type Clock, type FrameRate } from "../core/clock.js";
|
|
|
34
34
|
import { type ConsoleOptions } from "../core/console.js";
|
|
35
35
|
import { Segment } from "../core/segment.js";
|
|
36
36
|
import { type Surface } from "../core/paint.js";
|
|
37
|
-
import { type
|
|
37
|
+
import { type FrameHost } from "../core/protocol.js";
|
|
38
38
|
import type { Unsubscribe } from "../core/subscription.js";
|
|
39
|
+
import { Timeline } from "../core/timeline.js";
|
|
40
|
+
import { type View } from "../core/view.js";
|
|
39
41
|
import { type KeyPress, type ScreenMouseEvent } from "./input.js";
|
|
40
42
|
import type { TerminalHost } from "./terminal-host.js";
|
|
41
43
|
/**
|
|
@@ -44,7 +46,7 @@ import type { TerminalHost } from "./terminal-host.js";
|
|
|
44
46
|
* the program; `stopped` has handed it back for good.
|
|
45
47
|
*/
|
|
46
48
|
export type AppPhase = "idle" | "running" | "suspended" | "stopped";
|
|
47
|
-
declare const DRAW_OPTIONS: readonly ["asciiOnly", "theme", "onStyleError", "markup", "highlight", "highlighter"];
|
|
49
|
+
declare const DRAW_OPTIONS: readonly ["asciiOnly", "reducedMotion", "theme", "onStyleError", "markup", "highlight", "highlighter"];
|
|
48
50
|
type DrawOption = (typeof DRAW_OPTIONS)[number];
|
|
49
51
|
type DrawOptions = Readonly<Pick<ConsoleOptions, DrawOption>>;
|
|
50
52
|
export interface AppOptions extends DrawOptions {
|
|
@@ -52,15 +54,28 @@ export interface AppOptions extends DrawOptions {
|
|
|
52
54
|
readonly host: TerminalHost;
|
|
53
55
|
readonly surface: Surface;
|
|
54
56
|
/**
|
|
55
|
-
*
|
|
56
|
-
* app's state is drawn from the state as it is at
|
|
57
|
-
* frame
|
|
58
|
-
*
|
|
57
|
+
* What the app draws: a renderable, or a function asked for one once per
|
|
58
|
+
* frame — so a view of the app's state is drawn from the state as it is at
|
|
59
|
+
* that frame. What moves in it is an `Animation` placed there, drawn at the
|
|
60
|
+
* frame's time on the app's `timeline`, which says when it changes, so the
|
|
61
|
+
* app knows when to paint again.
|
|
59
62
|
*/
|
|
60
|
-
readonly view:
|
|
63
|
+
readonly view: View;
|
|
61
64
|
/** What the app reads the time from and ticks on. The platform's, unless given. */
|
|
62
65
|
readonly clock?: Clock;
|
|
63
|
-
/**
|
|
66
|
+
/**
|
|
67
|
+
* The map from the clock to the time the view is drawn for (`Timeline`).
|
|
68
|
+
* Playing from 0 when the host is made, at the clock's pace, unless given
|
|
69
|
+
* — so a tween in the view starts when the host is made, not when the clock
|
|
70
|
+
* began; `app.timeline` reads and replaces it.
|
|
71
|
+
*/
|
|
72
|
+
readonly timeline?: Timeline;
|
|
73
|
+
/**
|
|
74
|
+
* How often the app paints while what it shows moves every moment — a
|
|
75
|
+
* frame that says it moves. A frame held until a
|
|
76
|
+
* moment is painted at that moment, whatever the rate. 30 frames a second,
|
|
77
|
+
* unless given.
|
|
78
|
+
*/
|
|
64
79
|
readonly rate?: FrameRate;
|
|
65
80
|
/**
|
|
66
81
|
* The keys that stop the app, by `keyName`, heard before any `onKey`
|
|
@@ -87,14 +102,17 @@ type WheelTurn = Extract<ScreenMouseEvent, {
|
|
|
87
102
|
}>;
|
|
88
103
|
/** A pointer event `onMouse` hears: every one but the vertical wheel, which is `onWheel`'s. */
|
|
89
104
|
export type AppMouseEvent = Exclude<ScreenMouseEvent, WheelTurn>;
|
|
90
|
-
export declare class App {
|
|
105
|
+
export declare class App implements FrameHost {
|
|
91
106
|
private readonly host;
|
|
92
107
|
private readonly surface;
|
|
93
108
|
private readonly view;
|
|
94
|
-
private readonly
|
|
109
|
+
private readonly _clock;
|
|
110
|
+
private readonly _epoch;
|
|
111
|
+
private _timeline;
|
|
95
112
|
private _rate;
|
|
96
|
-
private readonly animations;
|
|
97
113
|
private ticking;
|
|
114
|
+
private said;
|
|
115
|
+
private waking;
|
|
98
116
|
private readonly console;
|
|
99
117
|
private _phase;
|
|
100
118
|
private _frame;
|
|
@@ -110,7 +128,18 @@ export declare class App {
|
|
|
110
128
|
private settle;
|
|
111
129
|
constructor(options: AppOptions);
|
|
112
130
|
get phase(): AppPhase;
|
|
113
|
-
/**
|
|
131
|
+
/** The clock the app reads the time from: where the host time a `Timeline` operation takes is read. */
|
|
132
|
+
get clock(): Clock;
|
|
133
|
+
/** The map from the clock to the time the view is drawn for. */
|
|
134
|
+
get timeline(): Timeline;
|
|
135
|
+
/**
|
|
136
|
+
* Replace the timeline and paint the frame for the time it now maps the
|
|
137
|
+
* clock's now to, so a seek shows where it sought, once. Paused, the frame
|
|
138
|
+
* then holds: nothing drawn for a held time changes, so the app waits on
|
|
139
|
+
* nothing until the timeline plays again, and paints when asked.
|
|
140
|
+
*/
|
|
141
|
+
set timeline(timeline: Timeline);
|
|
142
|
+
/** How often the app paints while what it shows moves every moment. */
|
|
114
143
|
get rate(): FrameRate;
|
|
115
144
|
/**
|
|
116
145
|
* Set how often the app paints: an animation under way goes on at the new
|
|
@@ -119,6 +148,14 @@ export declare class App {
|
|
|
119
148
|
set rate(rate: FrameRate);
|
|
120
149
|
/** The rows on screen now, as painted: what a pointer event lands on. */
|
|
121
150
|
get frame(): readonly (readonly Segment[])[];
|
|
151
|
+
/**
|
|
152
|
+
* The time a frame drawn now is drawn for (`RenderOptions.frame`): the
|
|
153
|
+
* clock's now, through the timeline. What a moment heard
|
|
154
|
+
* between frames — a key — is stamped with, so it is on the clock the
|
|
155
|
+
* frame is.
|
|
156
|
+
*/
|
|
157
|
+
get frameTime(): number;
|
|
158
|
+
private _time;
|
|
122
159
|
/**
|
|
123
160
|
* Take the terminal, paint the first frame, and resolve when the app stops
|
|
124
161
|
* — or reject with the error a frame threw or `fail` was given, the terminal
|
|
@@ -137,15 +174,6 @@ export declare class App {
|
|
|
137
174
|
* for as long as it ran.
|
|
138
175
|
*/
|
|
139
176
|
refresh(): void;
|
|
140
|
-
/**
|
|
141
|
-
* Paint at the app's `rate` until the returned function is called: what an
|
|
142
|
-
* effect sampled at the frame's time asks for while it moves. The app ticks
|
|
143
|
-
* while anything is animating and it holds the terminal, and stops when
|
|
144
|
-
* nothing is, so a still app paints only when asked. The animation's first
|
|
145
|
-
* frame does not wait out an interval: at one frame every two seconds, that
|
|
146
|
-
* would hold its start off screen for two.
|
|
147
|
-
*/
|
|
148
|
-
animate(): Unsubscribe;
|
|
149
177
|
/**
|
|
150
178
|
* Hand the terminal to whatever launched the program, and take it back and
|
|
151
179
|
* repaint when that resumes it. What sends the app here is the app's own —
|