@promptctl/rich-js 0.24.0 → 0.24.1
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/THIRD-PARTY-NOTICES +29 -1
- package/dist/core/animation.d.ts +78 -0
- package/dist/core/animation.js +107 -0
- package/dist/core/cell-widths.d.ts +9 -0
- package/dist/core/cell-widths.js +140 -0
- package/dist/core/cells.d.ts +17 -11
- package/dist/core/cells.js +100 -25
- package/dist/core/console.js +5 -5
- package/dist/core/easing.d.ts +110 -10
- package/dist/core/easing.js +269 -36
- package/dist/core/json.js +2 -1
- package/dist/core/measure.js +2 -2
- package/dist/core/place.js +2 -1
- package/dist/core/pretty.js +2 -2
- package/dist/core/protocol.d.ts +73 -19
- package/dist/core/protocol.js +56 -15
- package/dist/core/render.js +2 -1
- package/dist/core/segment.d.ts +18 -1
- package/dist/core/segment.js +45 -44
- package/dist/core/strip.d.ts +3 -2
- package/dist/core/strip.js +11 -7
- package/dist/core/style.js +17 -6
- package/dist/core/text.js +22 -14
- package/dist/core/timeline.d.ts +84 -0
- package/dist/core/timeline.js +144 -0
- package/dist/core/tween.d.ts +49 -0
- package/dist/core/tween.js +69 -0
- package/dist/host/app.js +2 -2
- package/dist/index.d.ts +10 -7
- package/dist/index.js +13 -4
- package/dist/renderables/align.d.ts +3 -2
- package/dist/renderables/align.js +3 -0
- package/dist/renderables/columns.d.ts +3 -2
- package/dist/renderables/columns.js +7 -2
- package/dist/renderables/constrain.d.ts +3 -2
- package/dist/renderables/constrain.js +5 -1
- package/dist/renderables/effect.d.ts +43 -58
- package/dist/renderables/effect.js +58 -64
- package/dist/renderables/effects.d.ts +16 -14
- package/dist/renderables/effects.js +64 -45
- package/dist/renderables/embed.js +3 -2
- package/dist/renderables/flexStrip.d.ts +3 -2
- package/dist/renderables/flexStrip.js +6 -1
- package/dist/renderables/frames.d.ts +40 -19
- package/dist/renderables/frames.js +48 -21
- package/dist/renderables/group.d.ts +3 -2
- package/dist/renderables/group.js +5 -2
- package/dist/renderables/html-entities.d.ts +8 -0
- package/dist/renderables/html-entities.js +386 -0
- package/dist/renderables/layout.d.ts +3 -2
- package/dist/renderables/layout.js +15 -2
- package/dist/renderables/list-view.d.ts +3 -2
- package/dist/renderables/list-view.js +12 -5
- package/dist/renderables/live.js +2 -2
- package/dist/renderables/markdown-parse.d.ts +109 -0
- package/dist/renderables/markdown-parse.js +1421 -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 +8 -20
- package/dist/renderables/padding.js +32 -50
- package/dist/renderables/panel.d.ts +26 -26
- package/dist/renderables/panel.js +67 -103
- package/dist/renderables/progress.d.ts +4 -2
- package/dist/renderables/progress.js +11 -2
- package/dist/renderables/progressBar.js +2 -2
- package/dist/renderables/python-space.d.ts +13 -0
- package/dist/renderables/python-space.js +24 -0
- package/dist/renderables/spinner.d.ts +26 -14
- package/dist/renderables/spinner.js +49 -37
- package/dist/renderables/syntax.js +2 -1
- package/dist/renderables/table.d.ts +19 -11
- package/dist/renderables/table.js +140 -156
- package/dist/renderables/traceback.d.ts +1 -1
- package/dist/renderables/traceback.js +6 -5
- package/dist/renderables/tree.d.ts +6 -5
- package/dist/renderables/tree.js +11 -7
- package/dist/renderables/viewport.d.ts +13 -5
- package/dist/renderables/viewport.js +41 -20
- package/dist/template-bindings/animation-funcs.js +2 -2
- package/dist/template-bindings/index.js +2 -1
- package/dist/themes/ramp.d.ts +18 -3
- package/dist/themes/ramp.js +23 -2
- package/dist/widgets/focus-manager.d.ts +21 -14
- package/dist/widgets/focus-manager.js +28 -29
- package/dist/widgets/index.d.ts +1 -1
- package/dist/widgets/static-item.d.ts +4 -3
- package/dist/widgets/static-item.js +9 -7
- package/dist/widgets/text-input.d.ts +28 -18
- package/dist/widgets/text-input.js +144 -101
- package/dist/widgets/widget-app.d.ts +19 -3
- package/dist/widgets/widget-app.js +39 -21
- package/dist/widgets/widget-base.js +0 -3
- package/package.json +2 -5
|
@@ -0,0 +1,84 @@
|
|
|
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
|
+
* [LAW:no-ambient-temporal-coupling] Nothing here reads a clock. The host
|
|
36
|
+
* time is an argument everywhere, and a timeline is built from one the host
|
|
37
|
+
* hands in, so the same host time maps to the same animation time at one
|
|
38
|
+
* sample a second or sixty.
|
|
39
|
+
*/
|
|
40
|
+
import type { Animation } from "./animation.js";
|
|
41
|
+
/**
|
|
42
|
+
* A map from host time to animation time. Playing, it is `time` at `origin`
|
|
43
|
+
* moving `rate` animation seconds a host second; paused, it is `time` at
|
|
44
|
+
* every host time, with the rate it resumes at.
|
|
45
|
+
*/
|
|
46
|
+
export type Timeline = {
|
|
47
|
+
readonly playing: true;
|
|
48
|
+
/** The host time, in seconds, at which the animation time is `time`. */
|
|
49
|
+
readonly origin: number;
|
|
50
|
+
/** The animation time, in seconds, at `origin`. */
|
|
51
|
+
readonly time: number;
|
|
52
|
+
/** Animation seconds per host second; negative runs the animation backwards. Never zero: a held timeline is paused. */
|
|
53
|
+
readonly rate: number;
|
|
54
|
+
} | {
|
|
55
|
+
readonly playing: false;
|
|
56
|
+
/** The animation time, in seconds, held at every host time. */
|
|
57
|
+
readonly time: number;
|
|
58
|
+
/** The rate the timeline resumes at. */
|
|
59
|
+
readonly rate: number;
|
|
60
|
+
};
|
|
61
|
+
export declare const Timeline: {
|
|
62
|
+
/** Playing from host time `origin`, at which the animation time is 0, at `rate` animation seconds a host second. */
|
|
63
|
+
play(origin: number, rate?: number): Timeline;
|
|
64
|
+
/** The animation time at host time `host`. */
|
|
65
|
+
at(timeline: Timeline, host: number): number;
|
|
66
|
+
/** `timeline` held at the animation time it maps `host` to. Pausing a paused timeline holds the same time. */
|
|
67
|
+
pause(timeline: Timeline, host: number): Timeline;
|
|
68
|
+
/** `timeline` playing from `host` at the animation time it maps `host` to, at its rate. Resuming a playing timeline is the same map. */
|
|
69
|
+
resume(timeline: Timeline, host: number): Timeline;
|
|
70
|
+
/** `timeline` with the animation at `time` at host time `host`, playing or held as it was. */
|
|
71
|
+
seek(timeline: Timeline, host: number, time: number): Timeline;
|
|
72
|
+
/** `timeline` at `rate` from host time `host` on, with no jump: the animation time at `host` is what it was. */
|
|
73
|
+
withRate(timeline: Timeline, host: number, rate: number): Timeline;
|
|
74
|
+
/**
|
|
75
|
+
* `animation` on host time: its value at the time `timeline` maps each
|
|
76
|
+
* host time to, changing at the host moment the mapped time reaches the
|
|
77
|
+
* animation's own answer — backwards, when it drops under where the value
|
|
78
|
+
* began. Paused, the value never changes and never did. The value is the
|
|
79
|
+
* animation's own, on no clock: what a drawn value's cells say about when
|
|
80
|
+
* they change is stamped by whoever draws it at a host time
|
|
81
|
+
* (`../renderables/frames.ts`, `frameAt`), never carried in from here.
|
|
82
|
+
*/
|
|
83
|
+
run<T>(timeline: Timeline, animation: Animation<T>): Animation<T>;
|
|
84
|
+
};
|
|
@@ -0,0 +1,144 @@
|
|
|
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
|
+
* [LAW:no-ambient-temporal-coupling] Nothing here reads a clock. The host
|
|
36
|
+
* time is an argument everywhere, and a timeline is built from one the host
|
|
37
|
+
* hands in, so the same host time maps to the same animation time at one
|
|
38
|
+
* sample a second or sixty.
|
|
39
|
+
*/
|
|
40
|
+
import { firstReaching } from "./easing.js";
|
|
41
|
+
/** Throws, naming `what`, unless `seconds` is a finite number of seconds. */
|
|
42
|
+
function finiteSeconds(what, seconds) {
|
|
43
|
+
if (!Number.isFinite(seconds))
|
|
44
|
+
throw new RangeError(`${what} must be a finite number of seconds, got ${seconds}`);
|
|
45
|
+
return seconds;
|
|
46
|
+
}
|
|
47
|
+
/** [LAW:single-enforcer] A host time is a finite number of seconds, refused once, at every operation that takes one. */
|
|
48
|
+
const hostTime = (host) => finiteSeconds("Timeline host time", host);
|
|
49
|
+
/**
|
|
50
|
+
* [LAW:parse-dont-validate] A rate is a finite number other than zero,
|
|
51
|
+
* settled where it is given: a rate of zero would be a second way to hold a
|
|
52
|
+
* timeline still, and `pause` is the one.
|
|
53
|
+
*/
|
|
54
|
+
function playbackRate(rate) {
|
|
55
|
+
if (!Number.isFinite(rate) || rate === 0)
|
|
56
|
+
throw new RangeError(`Timeline rate must be finite and not zero, got ${rate}`);
|
|
57
|
+
return rate;
|
|
58
|
+
}
|
|
59
|
+
/** `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. */
|
|
60
|
+
function anchored(timeline, host, time, rate) {
|
|
61
|
+
const origin = hostTime(host);
|
|
62
|
+
return timeline.playing ? { playing: true, origin, time, rate } : { playing: false, time, rate };
|
|
63
|
+
}
|
|
64
|
+
/** `animation` asked at the time `playing` maps each host time to, saying on host time when it changes. */
|
|
65
|
+
function played(animation, { origin, time, rate }) {
|
|
66
|
+
const mapped = (host) => time + rate * (host - origin);
|
|
67
|
+
const forward = rate > 0;
|
|
68
|
+
// Forwards, the host next sees the animation's own next change, and the
|
|
69
|
+
// value began when the animation says it did; backwards, it next sees a
|
|
70
|
+
// change where the value began, and the value began where the animation
|
|
71
|
+
// says it ends. A hold is `[lastChange, nextChange)`, so the far side of
|
|
72
|
+
// an edge is at it going up and strictly under it coming down.
|
|
73
|
+
const ahead = forward ? (t) => animation.nextChange(t) : (t) => animation.lastChange(t);
|
|
74
|
+
const behind = forward ? (t) => animation.lastChange(t) : (t) => animation.nextChange(t);
|
|
75
|
+
const past = forward ? (t, edge) => t >= edge : (t, edge) => t < edge;
|
|
76
|
+
// The first host moment the map carries past `edge`, an animation time.
|
|
77
|
+
// An edge at infinity, or one so far off at this rate that no host float
|
|
78
|
+
// reaches it, is never reached, forwards or backwards: the sum's own sign
|
|
79
|
+
// says which, as a host moment past either end of the host's clock.
|
|
80
|
+
const hostOf = (edge) => {
|
|
81
|
+
const sum = origin + (edge - time) / rate;
|
|
82
|
+
return Number.isFinite(sum) ? firstReaching(sum, (host) => past(mapped(host), edge)) : sum;
|
|
83
|
+
};
|
|
84
|
+
return {
|
|
85
|
+
at: (host) => animation.at(mapped(hostTime(host))),
|
|
86
|
+
nextChange(host) {
|
|
87
|
+
const h = hostTime(host);
|
|
88
|
+
const t = mapped(h);
|
|
89
|
+
const edge = ahead(t);
|
|
90
|
+
return edge === t ? h : Math.max(h, hostOf(edge));
|
|
91
|
+
},
|
|
92
|
+
lastChange(host) {
|
|
93
|
+
const h = hostTime(host);
|
|
94
|
+
const t = mapped(h);
|
|
95
|
+
const edge = behind(t);
|
|
96
|
+
return edge === t ? h : Math.min(h, hostOf(edge));
|
|
97
|
+
},
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
export const Timeline = {
|
|
101
|
+
/** Playing from host time `origin`, at which the animation time is 0, at `rate` animation seconds a host second. */
|
|
102
|
+
play(origin, rate = 1) {
|
|
103
|
+
return { playing: true, origin: finiteSeconds("Timeline.play origin", origin), time: 0, rate: playbackRate(rate) };
|
|
104
|
+
},
|
|
105
|
+
/** The animation time at host time `host`. */
|
|
106
|
+
at(timeline, host) {
|
|
107
|
+
const h = hostTime(host);
|
|
108
|
+
return timeline.playing ? timeline.time + timeline.rate * (h - timeline.origin) : timeline.time;
|
|
109
|
+
},
|
|
110
|
+
/** `timeline` held at the animation time it maps `host` to. Pausing a paused timeline holds the same time. */
|
|
111
|
+
pause(timeline, host) {
|
|
112
|
+
return { playing: false, time: Timeline.at(timeline, host), rate: timeline.rate };
|
|
113
|
+
},
|
|
114
|
+
/** `timeline` playing from `host` at the animation time it maps `host` to, at its rate. Resuming a playing timeline is the same map. */
|
|
115
|
+
resume(timeline, host) {
|
|
116
|
+
return { playing: true, origin: hostTime(host), time: Timeline.at(timeline, host), rate: timeline.rate };
|
|
117
|
+
},
|
|
118
|
+
/** `timeline` with the animation at `time` at host time `host`, playing or held as it was. */
|
|
119
|
+
seek(timeline, host, time) {
|
|
120
|
+
return anchored(timeline, host, finiteSeconds("Timeline.seek time", time), timeline.rate);
|
|
121
|
+
},
|
|
122
|
+
/** `timeline` at `rate` from host time `host` on, with no jump: the animation time at `host` is what it was. */
|
|
123
|
+
withRate(timeline, host, rate) {
|
|
124
|
+
return anchored(timeline, host, Timeline.at(timeline, host), playbackRate(rate));
|
|
125
|
+
},
|
|
126
|
+
/**
|
|
127
|
+
* `animation` on host time: its value at the time `timeline` maps each
|
|
128
|
+
* host time to, changing at the host moment the mapped time reaches the
|
|
129
|
+
* animation's own answer — backwards, when it drops under where the value
|
|
130
|
+
* began. Paused, the value never changes and never did. The value is the
|
|
131
|
+
* animation's own, on no clock: what a drawn value's cells say about when
|
|
132
|
+
* they change is stamped by whoever draws it at a host time
|
|
133
|
+
* (`../renderables/frames.ts`, `frameAt`), never carried in from here.
|
|
134
|
+
*/
|
|
135
|
+
run(timeline, animation) {
|
|
136
|
+
if (timeline.playing)
|
|
137
|
+
return played(animation, timeline);
|
|
138
|
+
return {
|
|
139
|
+
at: (host) => animation.at(Timeline.at(timeline, host)),
|
|
140
|
+
nextChange: (host) => (hostTime(host), Number.POSITIVE_INFINITY),
|
|
141
|
+
lastChange: (host) => (hostTime(host), Number.NEGATIVE_INFINITY),
|
|
142
|
+
};
|
|
143
|
+
},
|
|
144
|
+
};
|
|
@@ -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 2 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,69 @@
|
|
|
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 2 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 {
|
|
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
|
+
};
|
|
69
|
+
}
|
package/dist/host/app.js
CHANGED
|
@@ -34,7 +34,7 @@ import { frameRate, systemClock } from "../core/clock.js";
|
|
|
34
34
|
import { Console } from "../core/console.js";
|
|
35
35
|
import { Segment } from "../core/segment.js";
|
|
36
36
|
import { SurfacePainter } from "../core/paint.js";
|
|
37
|
-
import { fitHeight } from "../core/protocol.js";
|
|
37
|
+
import { fitHeight, render } from "../core/protocol.js";
|
|
38
38
|
import { hostEnvironment } from "./host-environment.js";
|
|
39
39
|
import { InputParser, keyName, parseKeyName } from "./input.js";
|
|
40
40
|
// [LAW:one-source-of-truth] How a frame draws — its glyphs, the style names
|
|
@@ -381,7 +381,7 @@ export class App {
|
|
|
381
381
|
const height = this.painter.height(options.height.rows);
|
|
382
382
|
// [LAW:no-ambient-temporal-coupling] The frame owner reads the clock,
|
|
383
383
|
// once a frame; the view is handed the time as data.
|
|
384
|
-
const lines = Segment.splitLines(this.view(this.clock.now())
|
|
384
|
+
const lines = Segment.splitLines(render(this.view(this.clock.now()), { ...options, height }));
|
|
385
385
|
// The app set the budget, so the app shapes what comes back: no deeper
|
|
386
386
|
// than the terminal — a taller frame would scroll the rows the painter
|
|
387
387
|
// goes back over — and, as a region, exactly that deep.
|
package/dist/index.d.ts
CHANGED
|
@@ -15,8 +15,11 @@ export { transposePalette, themeKeyForRoot, isAnchored, ANCHORED_ROOTS, } from "
|
|
|
15
15
|
export { lighten, darken, relativeLuminance, contrastRatio, contrastFor, ensureContrast, ensureDrawn, drawnColour, } from "./themes/colorMath.js";
|
|
16
16
|
export { ColorRamp } from "./themes/ramp.js";
|
|
17
17
|
export type { ColorStop } from "./themes/ramp.js";
|
|
18
|
-
export { EASES, Phase, beat, cubicBezier, parseEase, steps } from "./core/easing.js";
|
|
19
|
-
export type { Beat, Ease, StepPosition } from "./core/easing.js";
|
|
18
|
+
export { EASES, Phase, beat, cubicBezier, parseEase, spring, steps } from "./core/easing.js";
|
|
19
|
+
export type { Beat, Ease, SpringOptions, StepPosition, Timing } from "./core/easing.js";
|
|
20
|
+
export { Animation } from "./core/animation.js";
|
|
21
|
+
export { tween } from "./core/tween.js";
|
|
22
|
+
export { Timeline } from "./core/timeline.js";
|
|
20
23
|
export { frameRate, systemClock } from "./core/clock.js";
|
|
21
24
|
export type { Clock, FrameRate } from "./core/clock.js";
|
|
22
25
|
export { Style, StyleSyntaxError, StyleStack, Theme, NULL_STYLE, DEFAULT_STYLES, } from "./core/style.js";
|
|
@@ -27,8 +30,8 @@ export type { Anchor } from "./core/anchor.js";
|
|
|
27
30
|
export { Box } from "./core/box.js";
|
|
28
31
|
export type { EdgeChars, ContentChars, RowLevel, SubstituteOptions } from "./core/box.js";
|
|
29
32
|
export { ASCII, ASCII2, ASCII_DOUBLE_HEAD, SQUARE, SQUARE_DOUBLE_HEAD, MINIMAL, MINIMAL_HEAVY_HEAD, MINIMAL_DOUBLE_HEAD, SIMPLE, SIMPLE_HEAD, SIMPLE_HEAVY, HORIZONTALS, ROUNDED, HEAVY, HEAVY_EDGE, HEAVY_HEAD, DOUBLE, DOUBLE_EDGE, MARKDOWN, } from "./core/box.js";
|
|
30
|
-
export { isRenderable, isMeasurable, withCellWidth, withBoundedWidth, insetHeight, stackedHeight, regionRows, fitHeight, getStyle, } from "./core/protocol.js";
|
|
31
|
-
export type { RenderOptions, OverflowMethod, Height, Renderable, Measurable, StyleErrorHandler, } from "./core/protocol.js";
|
|
33
|
+
export { render, isRenderable, isMeasurable, withCellWidth, withBoundedWidth, insetHeight, stackedHeight, regionRows, fitHeight, getStyle, } from "./core/protocol.js";
|
|
34
|
+
export type { RenderOptions, OverflowMethod, Height, Renderable, Measurable, Composite, Presence, StyleErrorHandler, } from "./core/protocol.js";
|
|
32
35
|
export type { Unsubscribe } from "./core/subscription.js";
|
|
33
36
|
export { Measurement, measureRenderables } from "./core/measure.js";
|
|
34
37
|
export { Span, RichText } from "./core/text.js";
|
|
@@ -55,8 +58,8 @@ export { Console } from "./core/console.js";
|
|
|
55
58
|
export type { ConsoleOptions, ConsoleSink, ConsoleStream, ConsoleEnvironment, LiveRegion, PrintOptions, } from "./core/console.js";
|
|
56
59
|
export type { Env } from "./core/env.js";
|
|
57
60
|
export { Constrain } from "./renderables/constrain.js";
|
|
58
|
-
export { Effected, confined, drawnOf,
|
|
59
|
-
export type { CellColors, Drawn, Effect, EffectCell, EffectedOptions, Pair, Placed } from "./renderables/effect.js";
|
|
61
|
+
export { Effected, confined, drawnOf, onBeat } from "./renderables/effect.js";
|
|
62
|
+
export type { CellColors, Drawn, Effect, EffectCell, EffectedOptions, EffectFrame, Pair, Placed } from "./renderables/effect.js";
|
|
60
63
|
export { EFFECT_CATALOGUE, EFFECT_CURVES, EFFECT_LIGHTS, dissolveOut, fadeIn, onColors, pulse, shimmer, sparkle, steppedInk, wheel, } from "./renderables/effects.js";
|
|
61
64
|
export type { ColorStep, Curve, EffectTarget, Field, Loop, LoopName, Touch, Transition, TransitionName } from "./renderables/effects.js";
|
|
62
65
|
export { Align } from "./renderables/align.js";
|
|
@@ -77,7 +80,7 @@ export { ProgressBar } from "./renderables/progressBar.js";
|
|
|
77
80
|
export type { ProgressBarOptions } from "./renderables/progressBar.js";
|
|
78
81
|
export { Spinner } from "./renderables/spinner.js";
|
|
79
82
|
export type { SpinnerOptions } from "./renderables/spinner.js";
|
|
80
|
-
export { FrameSequence, Frames } from "./renderables/frames.js";
|
|
83
|
+
export { FrameSequence, Frames, frameAt } from "./renderables/frames.js";
|
|
81
84
|
export type { Frame, FramesOptions } from "./renderables/frames.js";
|
|
82
85
|
export { Table, Column } from "./renderables/table.js";
|
|
83
86
|
export type { TableOptions, ColumnOptions } from "./renderables/table.js";
|
package/dist/index.js
CHANGED
|
@@ -29,7 +29,16 @@ export { lighten, darken, relativeLuminance, contrastRatio, contrastFor, ensureC
|
|
|
29
29
|
export { ColorRamp } from "./themes/ramp.js";
|
|
30
30
|
// Progress in [0, 1]: phases turn seconds into it, eases reshape it. One
|
|
31
31
|
// vocabulary for ramps and time-based effects alike, CSS's names throughout.
|
|
32
|
-
export { EASES, Phase, beat, cubicBezier, parseEase, steps } from "./core/easing.js";
|
|
32
|
+
export { EASES, Phase, beat, cubicBezier, parseEase, spring, steps } from "./core/easing.js";
|
|
33
|
+
// A value for a time, and when it next changes: the one type every kind of
|
|
34
|
+
// motion is an instance of — beats, frame sequences, spinners, effects.
|
|
35
|
+
export { Animation } from "./core/animation.js";
|
|
36
|
+
// A number eased from one value to another over seconds, then held: the
|
|
37
|
+
// value animation a fill, a counter or a width moves by instead of jumping.
|
|
38
|
+
export { tween } from "./core/tween.js";
|
|
39
|
+
// The map from the host's clock to the time an animation is asked for:
|
|
40
|
+
// pause, reverse, scrub and speed, once, above every kind of motion.
|
|
41
|
+
export { Timeline } from "./core/timeline.js";
|
|
33
42
|
// The time source and timer whoever draws frames is handed, and the one
|
|
34
43
|
// parser of the rate it ticks at.
|
|
35
44
|
export { frameRate, systemClock } from "./core/clock.js";
|
|
@@ -38,7 +47,7 @@ export { Segment, ControlType, } from "./core/segment.js";
|
|
|
38
47
|
export { Box } from "./core/box.js";
|
|
39
48
|
export { ASCII, ASCII2, ASCII_DOUBLE_HEAD, SQUARE, SQUARE_DOUBLE_HEAD, MINIMAL, MINIMAL_HEAVY_HEAD, MINIMAL_DOUBLE_HEAD, SIMPLE, SIMPLE_HEAD, SIMPLE_HEAVY, HORIZONTALS, ROUNDED, HEAVY, HEAVY_EDGE, HEAVY_HEAD, DOUBLE, DOUBLE_EDGE, MARKDOWN, } from "./core/box.js";
|
|
40
49
|
// Protocol
|
|
41
|
-
export { isRenderable, isMeasurable, withCellWidth, withBoundedWidth, insetHeight, stackedHeight, regionRows, fitHeight, getStyle, } from "./core/protocol.js";
|
|
50
|
+
export { render, isRenderable, isMeasurable, withCellWidth, withBoundedWidth, insetHeight, stackedHeight, regionRows, fitHeight, getStyle, } from "./core/protocol.js";
|
|
42
51
|
// Measurement
|
|
43
52
|
export { Measurement, measureRenderables } from "./core/measure.js";
|
|
44
53
|
// Text
|
|
@@ -69,7 +78,7 @@ export { SPINNERS, DEFAULT_SPINNER } from "./core/spinnerData.js";
|
|
|
69
78
|
export { Console } from "./core/console.js";
|
|
70
79
|
// Renderables
|
|
71
80
|
export { Constrain } from "./renderables/constrain.js";
|
|
72
|
-
export { Effected, confined, drawnOf,
|
|
81
|
+
export { Effected, confined, drawnOf, onBeat } from "./renderables/effect.js";
|
|
73
82
|
export { EFFECT_CATALOGUE, EFFECT_CURVES, EFFECT_LIGHTS, dissolveOut, fadeIn, onColors, pulse, shimmer, sparkle, steppedInk, wheel, } from "./renderables/effects.js";
|
|
74
83
|
export { Align } from "./renderables/align.js";
|
|
75
84
|
export { Padding } from "./renderables/padding.js";
|
|
@@ -81,7 +90,7 @@ export { Panel } from "./renderables/panel.js";
|
|
|
81
90
|
export { Group } from "./renderables/group.js";
|
|
82
91
|
export { ProgressBar } from "./renderables/progressBar.js";
|
|
83
92
|
export { Spinner } from "./renderables/spinner.js";
|
|
84
|
-
export { FrameSequence, Frames } from "./renderables/frames.js";
|
|
93
|
+
export { FrameSequence, Frames, frameAt } from "./renderables/frames.js";
|
|
85
94
|
export { Table, Column } from "./renderables/table.js";
|
|
86
95
|
export { Tree } from "./renderables/tree.js";
|
|
87
96
|
export { Columns } from "./renderables/columns.js";
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
* Align — wraps a renderable and places its output horizontally, as one block.
|
|
3
3
|
*/
|
|
4
4
|
import { Segment } from "../core/segment.js";
|
|
5
|
-
import type { Renderable, Measurable, RenderOptions } from "../core/protocol.js";
|
|
5
|
+
import type { Composite, Renderable, Measurable, RenderOptions } from "../core/protocol.js";
|
|
6
6
|
import { type Alignment } from "../core/place.js";
|
|
7
|
-
export declare class Align implements Renderable, Measurable {
|
|
7
|
+
export declare class Align implements Renderable, Measurable, Composite {
|
|
8
8
|
readonly renderable: Renderable;
|
|
9
9
|
readonly align: Alignment;
|
|
10
10
|
constructor(renderable: Renderable, align?: Alignment);
|
|
@@ -13,4 +13,5 @@ export declare class Align implements Renderable, Measurable {
|
|
|
13
13
|
minimum: number;
|
|
14
14
|
maximum: number;
|
|
15
15
|
};
|
|
16
|
+
contents(): Iterable<Renderable>;
|
|
16
17
|
}
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
*/
|
|
11
11
|
import type { Segment } from "../core/segment.js";
|
|
12
12
|
import type { PaddingDimensions } from "./padding.js";
|
|
13
|
-
import type { Renderable, Measurable, RenderOptions } from "../core/protocol.js";
|
|
13
|
+
import type { Composite, Renderable, Measurable, RenderOptions } from "../core/protocol.js";
|
|
14
14
|
export interface ColumnsOptions {
|
|
15
15
|
expand?: boolean;
|
|
16
16
|
equal?: boolean;
|
|
@@ -18,7 +18,7 @@ export interface ColumnsOptions {
|
|
|
18
18
|
padding?: PaddingDimensions;
|
|
19
19
|
columnFirst?: boolean;
|
|
20
20
|
}
|
|
21
|
-
export declare class Columns implements Renderable, Measurable {
|
|
21
|
+
export declare class Columns implements Renderable, Measurable, Composite {
|
|
22
22
|
renderables: (Renderable & Partial<Measurable>)[];
|
|
23
23
|
readonly expand: boolean;
|
|
24
24
|
readonly equal: boolean;
|
|
@@ -27,6 +27,7 @@ export declare class Columns implements Renderable, Measurable {
|
|
|
27
27
|
private readonly padding;
|
|
28
28
|
constructor(items?: Iterable<unknown>, options?: ColumnsOptions);
|
|
29
29
|
render(rawOptions: RenderOptions): Iterable<Segment>;
|
|
30
|
+
contents(): Iterable<Renderable>;
|
|
30
31
|
measure(rawOptions: RenderOptions): {
|
|
31
32
|
minimum: number;
|
|
32
33
|
maximum: number;
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
*/
|
|
11
11
|
import { Measurement } from "../core/measure.js";
|
|
12
12
|
import { normalizePadding } from "./padding.js";
|
|
13
|
-
import { withBoundedWidth, withCellWidth } from "../core/protocol.js";
|
|
13
|
+
import { render, withBoundedWidth, withCellWidth } from "../core/protocol.js";
|
|
14
14
|
import { cellCount } from "../core/cells.js";
|
|
15
15
|
import { EmbeddedText, embed } from "./embed.js";
|
|
16
16
|
import { Constrain } from "./constrain.js";
|
|
@@ -68,7 +68,12 @@ export class Columns {
|
|
|
68
68
|
// single column is laid out. An unbounded width reaches the same place by
|
|
69
69
|
// the same route, which is why the parse here is the bounded one.
|
|
70
70
|
const options = withBoundedWidth(rawOptions, this);
|
|
71
|
-
yield* this._grid(options)
|
|
71
|
+
yield* render(this._grid(options), options);
|
|
72
|
+
}
|
|
73
|
+
// The items, in the order they were given. Laid out column-first, the grid
|
|
74
|
+
// draws them in an order its width decides, and handed no column it has none.
|
|
75
|
+
contents() {
|
|
76
|
+
return this.renderables;
|
|
72
77
|
}
|
|
73
78
|
measure(rawOptions) {
|
|
74
79
|
// The maximum is the grid's, which reports its width before `expand`: a
|
|
@@ -2,12 +2,13 @@
|
|
|
2
2
|
* Constrain — wraps a renderable and constrains its maximum width.
|
|
3
3
|
*/
|
|
4
4
|
import { Segment } from "../core/segment.js";
|
|
5
|
-
import type { Renderable, Measurable, RenderOptions } from "../core/protocol.js";
|
|
6
|
-
export declare class Constrain implements Renderable, Measurable {
|
|
5
|
+
import type { Composite, Renderable, Measurable, RenderOptions } from "../core/protocol.js";
|
|
6
|
+
export declare class Constrain implements Renderable, Measurable, Composite {
|
|
7
7
|
readonly renderable: Renderable;
|
|
8
8
|
readonly width: number | undefined;
|
|
9
9
|
constructor(renderable: Renderable, width?: number);
|
|
10
10
|
render(options: RenderOptions): Iterable<Segment>;
|
|
11
|
+
contents(): Iterable<Renderable>;
|
|
11
12
|
measure(options: RenderOptions): {
|
|
12
13
|
minimum: number;
|
|
13
14
|
maximum: number;
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
* Constrain — wraps a renderable and constrains its maximum width.
|
|
3
3
|
*/
|
|
4
4
|
import { Measurement } from "../core/measure.js";
|
|
5
|
+
import { render } from "../core/protocol.js";
|
|
5
6
|
export class Constrain {
|
|
6
7
|
renderable;
|
|
7
8
|
width;
|
|
@@ -18,7 +19,10 @@ export class Constrain {
|
|
|
18
19
|
...options,
|
|
19
20
|
maxWidth: constrainedWidth,
|
|
20
21
|
};
|
|
21
|
-
yield* this.renderable
|
|
22
|
+
yield* render(this.renderable, innerOptions);
|
|
23
|
+
}
|
|
24
|
+
*contents() {
|
|
25
|
+
yield this.renderable;
|
|
22
26
|
}
|
|
23
27
|
measure(options) {
|
|
24
28
|
// Measured at the width it will be drawn at, as Rich's `update_width` does;
|