@idle-screens/schema 3.9.1 → 3.10.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/FORMAT.md +128 -6
- package/dist/index.d.ts +216 -9
- package/dist/index.js +1068 -169
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/saver-spec.schema.json +63 -2
package/FORMAT.md
CHANGED
|
@@ -42,7 +42,10 @@ the `fade` transition, `role` on text sprites, and `text: 'crossfade'` on
|
|
|
42
42
|
`morph` (2026-09-08 — ambient presentations); `background: { type: 'field' }`
|
|
43
43
|
(seeded, warped noise quantised into bands), the static per-entity
|
|
44
44
|
`rotate`, and the print `finish` (grain + dither screen, spec- and
|
|
45
|
-
sequence-level) (2026-09-10 — the print look)
|
|
45
|
+
sequence-level) (2026-09-10 — the print look); `timeline` (keyframes on the
|
|
46
|
+
scene's own clock), paint-level layer `opacity` and `transform`,
|
|
47
|
+
`position.dx` / `dy`, `wrapMorph` on the sequence envelope and
|
|
48
|
+
`text: 'dip'` on `morph` (2026-09-25 — timed scenes).
|
|
46
49
|
|
|
47
50
|
## Safety invariants
|
|
48
51
|
|
|
@@ -107,7 +110,8 @@ must exist, have `count: 1`, and not themselves orbit a layer.
|
|
|
107
110
|
"ghosting": 0.9, // optional 0..0.95; frame-persistence smear
|
|
108
111
|
"finish": { "grain": 0.5, "dither": 0.3 }, // optional; print screen over the finished frame
|
|
109
112
|
"background": { ... }, // optional; defaults to black
|
|
110
|
-
"layers": [ { ... }, ... ]
|
|
113
|
+
"layers": [ { ... }, ... ], // 1..36, rendered back-to-front
|
|
114
|
+
"timeline": { ... } // optional; authored keyframes — see **Timeline**
|
|
111
115
|
}
|
|
112
116
|
```
|
|
113
117
|
|
|
@@ -283,7 +287,9 @@ own `finish` is not steerable; steer a segment's, or republish.
|
|
|
283
287
|
| `emit` | `{every ≥ 1000, life ≥ 500, jitter?, grow?}` (`life ≤ every`) | always lit | **sparse events**: each entity is dark except a `life`-ms window every `every` ms, fading in fast and out slow; `jitter` 0 staggers entities evenly (one event at a time while `life ≤ every / count`), 1 scatters the offsets (a fixed sequence, not seeded — declaring `emit` disturbs no other draw); `grow: [from, to]` scales size across the window — expansion rather than travel |
|
|
284
288
|
| `clock` | `{phase?, rate?}` | seeded phases | **phase-lock**: `pulse`, `grow` and `cycle` share one phase (turns, 0..1) and run at `rate` × time; two layers with the same clock breathe in step. Clocked periods must satisfy `period / rate ≥ 1000` |
|
|
285
289
|
| `key` | string | none | addressable name → `setParam("key.count", …)` for a layer field, `setParam("key.sprite.color", …)` for a sprite field (the path mirrors the JSON: sprite fields sit under `sprite`) |
|
|
286
|
-
| `position` | `{x, y}` 0..1 | none | exact placement; **requires `count: 1`** — except with a `list`/`table` layout, where it anchors the block's top-left; overrides `region` |
|
|
290
|
+
| `position` | `{x, y, dx?, dy?}` — `x`/`y` 0..1, `dx`/`dy` ±2 (±17280 px under `units: "px"`) | none | exact placement; **requires `count: 1`** — except with a `list`/`table` layout, where it anchors the block's top-left; overrides `region`. `dx`/`dy` offset the point in **`min(w, h)` units** (px under `units: "px"`): `x`/`y` are fractions of width and height while every size is a fraction of `min(w, h)`, so parts placed at computed fractions only register at the aspect they were computed for — anchor a compound form's parts on one shared fraction (the centre is the only aspect-safe one) and offset each with `dx`/`dy`. Placement (structural). **Native:** tvOS ignores the offset |
|
|
291
|
+
| `opacity` | number 0..1 | `1` | **paint-level** layer opacity: multiplies every entity's `alpha` and the `life` envelope at draw time. Unlike `alpha` (a range baked into each entity), it is outside the structural signature, so `setParam`, a `timeline` key or a `morph` **glides** it instead of re-seeding the layer. `0` skips the layer. **Native:** ignored ⇒ 1 |
|
|
292
|
+
| `transform` | `{x?, y?, scale?, scaleX?, rotate?}` | identity | **paint-level** transform of the whole layer about the viewport centre: translate `x`/`y` (±2 in `min(w, h)` units; px under `units: "px"`, ±17280), `rotate` (degrees, ±3600), `scale` (0..8) and `scaleX` (±8, × `scale` horizontally — toward 0 reads as a turn about the vertical axis, negative mirrors). Outside the structural signature: a camera for a timed piece, a slow drift for an ambient one, and it glides under steering. Sprites are not re-rasterised, so a large `scale` enlarges pixels of soft sprites, not detail. **Native:** ignored ⇒ identity |
|
|
287
293
|
|
|
288
294
|
`links`: `{ k: 1..8, maxDist, color?, alpha?, width?, mode?, falloff?, closed? }`.
|
|
289
295
|
`mode: "nearest"` (default) wires each entity to its k nearest neighbors within
|
|
@@ -663,6 +669,91 @@ control-track (`step` | `linear` | `smooth`). Placement/motion changes trigger
|
|
|
663
669
|
a deterministic rebuild (same seed → same stream). See `@idle-screens/core`
|
|
664
670
|
for `ControlTrack` and the idlescreens.com MCP `setParam` tool.
|
|
665
671
|
|
|
672
|
+
Paths that are **paint** (colours, `alpha` inside a sprite's paint, layer
|
|
673
|
+
`opacity` / `transform`, `textBlock.opacity`, `reveal.progress`, polygon
|
|
674
|
+
`points`) glide; paths that are **structure** (`count`, `position`, `motion`,
|
|
675
|
+
`size`, `layer.alpha`, …) rebuild — a deterministic re-seed, which reads as a
|
|
676
|
+
pop. Prefer `opacity` over `alpha` and `transform` over `position` for
|
|
677
|
+
anything that should move smoothly.
|
|
678
|
+
|
|
679
|
+
## Timeline — keyframes on the scene's clock
|
|
680
|
+
|
|
681
|
+
A SaverSpec can carry authored change on its **own** clock — the same clock
|
|
682
|
+
its drift, orbit and pulse run on (from mount; a sequence segment's `localT`):
|
|
683
|
+
|
|
684
|
+
```jsonc
|
|
685
|
+
"timeline": {
|
|
686
|
+
"loop": true, // optional; repeat every `duration` ms
|
|
687
|
+
"duration": 16000, // required with loop; > the latest key's t
|
|
688
|
+
"keys": [ // 1..256
|
|
689
|
+
{ "t": 400, "path": "sun.transform.y", "value": 0, "dur": 5200 },
|
|
690
|
+
{ "t": 6000, "path": "title.opacity", "value": 1, "dur": 1400, "ease": "linear" },
|
|
691
|
+
{ "t": 11000, "path": "title.opacity", "value": 0, "dur": 1200 }
|
|
692
|
+
]
|
|
693
|
+
}
|
|
694
|
+
```
|
|
695
|
+
|
|
696
|
+
- A key glides `path` from wherever it is at `t` to `value` over `dur` ms
|
|
697
|
+
(default 1000, the live-steer default; `0` is a cut), eased `smooth`
|
|
698
|
+
(default), `linear` or `step` (holds, then switches at the glide's end).
|
|
699
|
+
Keys address the same dot-paths as `setParam` — index or `key` form — and a
|
|
700
|
+
path must already exist on the spec (a key cannot add a field). The spec must
|
|
701
|
+
still validate with each key's value applied.
|
|
702
|
+
- Overlapping keys on one path chain: the later glide starts from the earlier
|
|
703
|
+
one's in-flight value.
|
|
704
|
+
- Without `loop`, a path holds the base spec's value before its first key and
|
|
705
|
+
its last key's value after it. With `loop`, the value depends only on
|
|
706
|
+
`t mod duration`, and **before a path's first key it has its last key's
|
|
707
|
+
value** — each lap is one closed cycle, lap 7 looks exactly like lap 1.
|
|
708
|
+
- **Flash safety:** distinct key times on one path sit ≥ 200 ms apart
|
|
709
|
+
(across the loop wrap too). Keys on different paths may share a beat.
|
|
710
|
+
Keys are also checked **together**: two keys that are each valid can
|
|
711
|
+
combine past a floor (a `clock.rate` and a `pulse.period` share one), so
|
|
712
|
+
the validator resolves the scene at every key's start, end and glide
|
|
713
|
+
quarter-points and rejects it there.
|
|
714
|
+
- A key on a **structural** path rebuilds the scene at that key — a pop, and
|
|
715
|
+
the validator warns `timeline-structural-key`. Animate paint: colours,
|
|
716
|
+
`opacity`, `transform`, polygon `points` (a whole equal-length array
|
|
717
|
+
lerps point by point), `reveal.progress`. A glide that runs past a loop's
|
|
718
|
+
lap warns `timeline-glide-overruns-lap`.
|
|
719
|
+
- A timeline is **frame-addressable**: the scene at `t` is a pure function of
|
|
720
|
+
`(spec, t)`, so seeks, ghosting warm-ups and perception all resolve it at
|
|
721
|
+
their own time. `resolveTimelineAt(spec, t)` returns the plain spec at `t`
|
|
722
|
+
(the same object when there is no timeline).
|
|
723
|
+
|
|
724
|
+
**Steering a scene with a timeline** — live steering layers on top, and the
|
|
725
|
+
rule falls out of the scene itself, no mode flag:
|
|
726
|
+
|
|
727
|
+
- a steer on a path **no key touches** is a sticky override — exactly today's
|
|
728
|
+
behaviour on an ambient scene;
|
|
729
|
+
- a steer on a path the **timeline animates** holds until that path's next
|
|
730
|
+
key, then glides back to the timeline over that key's `dur` — from
|
|
731
|
+
wherever the steer's own glide had reached, if the key arrives mid-glide.
|
|
732
|
+
|
|
733
|
+
A steer is rejected, as on any scene, when the scene it makes is invalid —
|
|
734
|
+
judged with every live steer applied, now and at each key still ahead (a
|
|
735
|
+
sticky steer outlives the keys).
|
|
736
|
+
|
|
737
|
+
A channel's control track is not a timeline: its `t` is a wall-clock stamp the
|
|
738
|
+
server writes, so it is never read as a schedule. Authored time lives here.
|
|
739
|
+
|
|
740
|
+
Each steer is applied once per server stamp, so a host re-sending the whole
|
|
741
|
+
track (idle-server does, on every steer) never re-arms a hold the timeline has
|
|
742
|
+
taken back. A viewer that mounts later replays the stored track at its own
|
|
743
|
+
mount: a steer on an animated path shows there until that path's next key
|
|
744
|
+
after the mount, while screens that saw it live have already moved on. Until
|
|
745
|
+
viewers share a clock (`sync: 'epoch'`, still unwired on the site), screens
|
|
746
|
+
mounted at different times can briefly disagree on such a path.
|
|
747
|
+
|
|
748
|
+
**One format for both use cases.** An ambient scene is a SaverSpec without a
|
|
749
|
+
timeline — nothing about it changes. A timed piece (an ident, a film, a
|
|
750
|
+
pre-roll) is a scene whose evolution is a timeline, and a sequence only where
|
|
751
|
+
it genuinely cuts to a different set of layers.
|
|
752
|
+
|
|
753
|
+
**Native:** tvOS ignores `timeline` and shows the base spec — so **author the
|
|
754
|
+
base as the piece's rest state** (its end card), and a timed piece degrades
|
|
755
|
+
to a valid ambient scene.
|
|
756
|
+
|
|
666
757
|
## Seeing without eyes — the perception API
|
|
667
758
|
|
|
668
759
|
`src/perceive.ts` translates a spec into modalities a **non-vision agent** can
|
|
@@ -677,6 +768,11 @@ simplified (`screen` ≈ `lighter`), wrapped link segments use straight
|
|
|
677
768
|
interpolation, and background drift is sampled at rest. These are documented
|
|
678
769
|
trade-offs for a zero-dependency, renderer-free analysis tool.
|
|
679
770
|
|
|
771
|
+
A `timeline` is resolved at the sample time `t` before anything is measured,
|
|
772
|
+
so every output below describes the scene as it is at that moment of the
|
|
773
|
+
piece. Layer `opacity` scales a layer's ink and weight; a layer `transform`
|
|
774
|
+
moves and scales its positions and sizes (sprite rotation is not modelled).
|
|
775
|
+
|
|
680
776
|
- `perceiveScene(spec, {t?, viewport?, seed?})` — one-call bundle: everything below.
|
|
681
777
|
**`t` is in milliseconds** (the MCP `previewScene` tool takes seconds and
|
|
682
778
|
converts; passing seconds here reads a self-typing block as one that never
|
|
@@ -797,7 +893,7 @@ the presenter never freezes. A durationless final segment holds regardless of
|
|
|
797
893
|
`advance`.
|
|
798
894
|
|
|
799
895
|
**Transitions:** `{ type: 'cut' }` (default) performs a hard switch.
|
|
800
|
-
`{ type: 'morph', dur: number, text?: 'step' | 'crossfade' }` interpolates
|
|
896
|
+
`{ type: 'morph', dur: number, text?: 'step' | 'crossfade' | 'dip' }` interpolates
|
|
801
897
|
**numbers and hex colours** (colour, alpha, pulse, `reveal.progress`, …) over
|
|
802
898
|
`dur` ms when crossing into the next segment; **every other value — strings,
|
|
803
899
|
and `textBlock.text` above all — switches on the first morph frame** under
|
|
@@ -811,7 +907,11 @@ lerped frame's paint (colour, `opacity` and layer alpha glide as usual). Only
|
|
|
811
907
|
the differing text layers cost a second draw, and only for `dur`; layers
|
|
812
908
|
whose words match, and every non-text layer, are untouched. It is an
|
|
813
909
|
internal paint pass, not a spec field — no `opacity` appears on the `text`
|
|
814
|
-
sprite.
|
|
910
|
+
sprite. **`text: 'dip'`** draws the same two passes one after the other
|
|
911
|
+
instead: the outgoing words fade out over the first half of the morph
|
|
912
|
+
(alpha `1 − 2k`) and the incoming fade in over the second (`2k − 1`), so two
|
|
913
|
+
captions never overlap — the right choice when the words sit in the same
|
|
914
|
+
place. `text` is a morph option only (a `fade` already cross-fades whole
|
|
815
915
|
frames; a `cut` has no window) and the validator rejects it elsewhere.
|
|
816
916
|
**Default `step` is today's behaviour byte for byte** — the sequence
|
|
817
917
|
baseline pins it — and a flip to `crossfade` by default would be a major
|
|
@@ -825,7 +925,7 @@ warning. When the segments are structural twins whose only differences are
|
|
|
825
925
|
values morph steps (a text-only change), the morph runs but every frame of it
|
|
826
926
|
shows the incoming segment — it reads as a cut — and the validator emits a
|
|
827
927
|
`morph-nothing-morphable` warning (never an error; stored sequences stay
|
|
828
|
-
valid) — unless the transition declared `text: 'crossfade'` and the words
|
|
928
|
+
valid) — unless the transition declared `text: 'crossfade'` or `'dip'` and the words
|
|
829
929
|
differ, in which case the words are the thing that morphs and the warning is
|
|
830
930
|
withheld. During a morph, entity placement inherits the outgoing segment's
|
|
831
931
|
seed — the incoming segment's own seed is unused. `dur` must be between 200
|
|
@@ -853,6 +953,28 @@ cross-fades on every segment change using the transition's `dur`, so `fade`
|
|
|
853
953
|
matches the native player rather than diverging from it; native reads
|
|
854
954
|
`fade.dur` where it reads the morph `dur` today.
|
|
855
955
|
|
|
956
|
+
**Wrap morph:** under `loop: true` a morph declared on the **last** segment
|
|
957
|
+
is ignored at the wrap — the lap cuts back to segment 0, and every stored
|
|
958
|
+
sequence expects that. `wrapMorph: true` on the envelope honours it: the lap
|
|
959
|
+
morphs back into segment 0 (from the second lap on; the first mount at
|
|
960
|
+
`T = 0` is not a wrap). It takes effect only when the whole lap is **one
|
|
961
|
+
morph chain** — every segment morphs into the next and the last is a
|
|
962
|
+
structural twin of the first — so every lap keeps the chain root's seed and
|
|
963
|
+
entity placement; otherwise it is still a cut and the validator warns
|
|
964
|
+
`wrap-morph-inactive` with the reason. With it, a looping ident is a closed
|
|
965
|
+
cycle of poses. A clicker jump to segment 0 (a `sequence.segment` steer)
|
|
966
|
+
still cuts — it arrives from wherever the presenter was, not from the last
|
|
967
|
+
segment. **Native:** tvOS cross-fades the wrap like any segment change.
|
|
968
|
+
|
|
969
|
+
**Timelines in segments:** a segment's scene may carry a `timeline`; it runs
|
|
970
|
+
on the segment's `localT`. A morph resolves each side on its own clock — the
|
|
971
|
+
outgoing segment at `duration + localT`, the incoming at `localT` — and lerps
|
|
972
|
+
the results, so a looping timeline keeps cycling through the morph. The
|
|
973
|
+
retained steers follow the steering rule on both ends (a steer the outgoing
|
|
974
|
+
timeline has already taken back stays taken back), and a layer `opacity` or
|
|
975
|
+
`transform` present on only one end glides from its identity (opacity 1,
|
|
976
|
+
no transform) instead of stepping.
|
|
977
|
+
|
|
856
978
|
**Time mapping:** global clock `T` maps to `(segmentIndex, localT)` via prefix
|
|
857
979
|
sums of durations. Half-open segments: `[start, start+duration)`. With
|
|
858
980
|
`loop: true`, `T` wraps at the sum of all durations (loop is incompatible with
|
package/dist/index.d.ts
CHANGED
|
@@ -46,6 +46,12 @@ interface SaverSpec {
|
|
|
46
46
|
* Absent ⇒ the frame is presented exactly as drawn.
|
|
47
47
|
*/
|
|
48
48
|
finish?: FinishSpec;
|
|
49
|
+
/**
|
|
50
|
+
* Authored keyframes on the scene's own clock — see `Timeline`. Absent ⇒
|
|
51
|
+
* the scene is exactly what it was before the field existed. **Native:**
|
|
52
|
+
* ignored ⇒ the base spec (author it as the piece's rest state).
|
|
53
|
+
*/
|
|
54
|
+
timeline?: Timeline;
|
|
49
55
|
/** Dimensional unit system. 'viewport' (default) = all sizes/speeds/distances are fractions of min(w,h). */
|
|
50
56
|
units?: 'viewport' | 'px';
|
|
51
57
|
/**
|
|
@@ -139,6 +145,51 @@ interface BackgroundDrift {
|
|
|
139
145
|
/** How far stops shift (fraction of the 0..1 range). Default 0.15, max 0.3. */
|
|
140
146
|
amount?: number;
|
|
141
147
|
}
|
|
148
|
+
/**
|
|
149
|
+
* A paint-level transform applied to a whole layer at draw time, about the
|
|
150
|
+
* viewport centre: translate by (`x`, `y`) in `min(w, h)` units, rotate by
|
|
151
|
+
* `rotate` degrees, scale by `scale` (× `scaleX` horizontally — `scaleX`
|
|
152
|
+
* toward 0 reads as a turn about the vertical axis). Kept out of the
|
|
153
|
+
* structural signature, so it glides under steering, a `timeline` key or a
|
|
154
|
+
* `morph` — a camera for timed pieces, a slow drift for ambient ones. Every
|
|
155
|
+
* field optional; an empty transform is the identity. **Native:** ignored ⇒
|
|
156
|
+
* identity.
|
|
157
|
+
*/
|
|
158
|
+
interface LayerTransform {
|
|
159
|
+
x?: number;
|
|
160
|
+
y?: number;
|
|
161
|
+
scale?: number;
|
|
162
|
+
scaleX?: number;
|
|
163
|
+
rotate?: number;
|
|
164
|
+
}
|
|
165
|
+
/** One authored key of a `Timeline`: at scene time `t` (ms), glide `path` to `value` over `dur` ms. */
|
|
166
|
+
interface TimelineKey {
|
|
167
|
+
/** Scene time in ms (the instance's own clock — a sequence segment's `localT`). */
|
|
168
|
+
t: number;
|
|
169
|
+
/** A steering path (`layers.0.sprite.color`, `key.sprite.color`, `background.stops.1.color`, …). It must already exist on the spec, as for `setParam`. */
|
|
170
|
+
path: string;
|
|
171
|
+
/** The target value; the key must still validate when applied. */
|
|
172
|
+
value: unknown;
|
|
173
|
+
/** Default `smooth`. */
|
|
174
|
+
ease?: 'step' | 'linear' | 'smooth';
|
|
175
|
+
/** Glide length in ms, starting at `t`. Default 1000 (as live steering); 0 = a cut. */
|
|
176
|
+
dur?: number;
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Authored change on the scene's own clock — keyframes. A scene without a
|
|
180
|
+
* timeline is unchanged (ambient, today's behaviour); with one, each key
|
|
181
|
+
* glides its path at its time, and the scene can loop. Distinct from the
|
|
182
|
+
* channel's live `track`, whose `t` is a wall-clock stamp: live steering still
|
|
183
|
+
* layers on top, and a steer on a path the timeline animates holds until that
|
|
184
|
+
* path's next key.
|
|
185
|
+
*/
|
|
186
|
+
interface Timeline {
|
|
187
|
+
/** Repeat every `duration` ms. A looping path's value before its first key is its last key's value (the loop wraps). */
|
|
188
|
+
loop?: boolean;
|
|
189
|
+
/** Loop length in ms; required with `loop`, and at least the latest key's `t`. */
|
|
190
|
+
duration?: number;
|
|
191
|
+
keys: TimelineKey[];
|
|
192
|
+
}
|
|
142
193
|
interface LayerSpec {
|
|
143
194
|
count: number;
|
|
144
195
|
sprite: SpriteSpec;
|
|
@@ -227,7 +278,28 @@ interface LayerSpec {
|
|
|
227
278
|
position?: {
|
|
228
279
|
x: number;
|
|
229
280
|
y: number;
|
|
281
|
+
/**
|
|
282
|
+
* An offset from `x`/`y` in `min(w, h)` units (px under `units: 'px'`).
|
|
283
|
+
* `x`/`y` are fractions of width and height while every size is a fraction
|
|
284
|
+
* of `min(w, h)`, so parts placed at computed fractions only register at
|
|
285
|
+
* the aspect they were computed for; anchor on a shared fraction (the
|
|
286
|
+
* centre is the only aspect-safe one) and offset in size units instead.
|
|
287
|
+
* Placement, like `x`/`y` (structural). Absent ⇒ 0. **Native:** tvOS
|
|
288
|
+
* ignores the offset until it reads the field.
|
|
289
|
+
*/
|
|
290
|
+
dx?: number;
|
|
291
|
+
dy?: number;
|
|
230
292
|
};
|
|
293
|
+
/**
|
|
294
|
+
* Paint-level layer opacity, 0..1 (default 1): multiplies the per-entity
|
|
295
|
+
* `alpha` and the `life` envelope at draw time. Unlike `alpha` (a range baked
|
|
296
|
+
* into every entity at build), it is **paint** — kept out of the structural
|
|
297
|
+
* signature — so `setParam`, a `timeline` key or a sequence `morph` glides it
|
|
298
|
+
* instead of re-seeding the layer. **Native:** ignored ⇒ 1.
|
|
299
|
+
*/
|
|
300
|
+
opacity?: number;
|
|
301
|
+
/** Paint-level transform of the whole layer — see `LayerTransform`. */
|
|
302
|
+
transform?: LayerTransform;
|
|
231
303
|
/**
|
|
232
304
|
* Inter-entity links. `mode` picks the wiring:
|
|
233
305
|
* - 'nearest' (default): each entity's k nearest neighbors within maxDist.
|
|
@@ -755,6 +827,14 @@ declare const LIMITS: {
|
|
|
755
827
|
readonly maxTransitionDur: 5000;
|
|
756
828
|
readonly minTransitionDur: 200;
|
|
757
829
|
readonly minEmitEvery: 1000;
|
|
830
|
+
readonly maxTimelineKeys: 256;
|
|
831
|
+
readonly minTimelineKeyInterval: 200;
|
|
832
|
+
readonly maxTimelineKeyDur: 30000;
|
|
833
|
+
readonly maxTimelineDuration: 86400000;
|
|
834
|
+
readonly maxLayerTransformOffset: 2;
|
|
835
|
+
readonly maxLayerTransformOffsetPx: 17280;
|
|
836
|
+
readonly maxLayerTransformScale: 8;
|
|
837
|
+
readonly maxLayerTransformRotate: 3600;
|
|
758
838
|
readonly maxEmitEvery: 600000;
|
|
759
839
|
readonly minEmitLife: 500;
|
|
760
840
|
readonly maxEmitGrow: 8;
|
|
@@ -777,13 +857,16 @@ type SequenceTransition = {
|
|
|
777
857
|
* identical) segment over `dur` ms. Strings step on the first frame —
|
|
778
858
|
* unless `text: 'crossfade'`, which draws each `text` / `textBlock` layer
|
|
779
859
|
* whose string(s) differ twice for the window: the outgoing string at
|
|
780
|
-
* `1 − k`, the incoming at `k` (k = the morph's eased progress).
|
|
781
|
-
*
|
|
860
|
+
* `1 − k`, the incoming at `k` (k = the morph's eased progress). `'dip'`
|
|
861
|
+
* draws the same two passes one after the other instead — the outgoing
|
|
862
|
+
* words fade out over the first half, the incoming fade in over the second —
|
|
863
|
+
* so the two strings never overlap. Default `'step'`, today's behaviour,
|
|
864
|
+
* byte for byte.
|
|
782
865
|
*/
|
|
783
866
|
| {
|
|
784
867
|
type: 'morph';
|
|
785
868
|
dur: number;
|
|
786
|
-
text?: 'step' | 'crossfade';
|
|
869
|
+
text?: 'step' | 'crossfade' | 'dip';
|
|
787
870
|
}
|
|
788
871
|
/**
|
|
789
872
|
* Cross-fade into the next segment over `dur` ms: the outgoing segment
|
|
@@ -821,6 +904,16 @@ interface IdleSequence {
|
|
|
821
904
|
* lands mid-loop. `advance: 'input'` holds stay armed under either mode.
|
|
822
905
|
*/
|
|
823
906
|
sync?: 'mount' | 'epoch';
|
|
907
|
+
/**
|
|
908
|
+
* Honour a `morph` declared on the last segment at the loop wrap (the lap
|
|
909
|
+
* morphs back into segment 0 instead of cutting). Opt-in: without it a
|
|
910
|
+
* last-segment morph stays a cut at the wrap, as every stored sequence
|
|
911
|
+
* expects. Takes effect only when the whole loop is one morph chain (every
|
|
912
|
+
* segment morphs into the next, and the last is a structural twin of the
|
|
913
|
+
* first) — so every lap reuses the same child and seed; otherwise it is a
|
|
914
|
+
* cut and the validator says why. Needs `loop: true`.
|
|
915
|
+
*/
|
|
916
|
+
wrapMorph?: boolean;
|
|
824
917
|
/**
|
|
825
918
|
* A scene drawn under every segment on the sequence's **global** clock —
|
|
826
919
|
* the ground that does not reset at a boundary or under a `sequence.segment`
|
|
@@ -850,6 +943,18 @@ declare function validateSpec(spec: unknown): ValidationResult;
|
|
|
850
943
|
* `validateSpec` it never normalises colours into the caller's spec.
|
|
851
944
|
*/
|
|
852
945
|
declare function ignoredPropertyPaths(spec: unknown): string[];
|
|
946
|
+
/**
|
|
947
|
+
* Validity of only the parts of a spec that `paths` (index-form dot-paths,
|
|
948
|
+
* as `canonicalSpecPath` spells them) live in: each touched layer, the
|
|
949
|
+
* background, the finish — plus the cross-layer entity cap. A layer's rules
|
|
950
|
+
* (the `clock`/`pulse`/`grow` flash floors, `emit`, orbit parents) are all
|
|
951
|
+
* judged inside that layer, so a change confined to layer N can only break
|
|
952
|
+
* layer N or the total. Any other top-level path falls back to the full
|
|
953
|
+
* `validateSpec`. For the timeline's composed-state checks, which run at every
|
|
954
|
+
* key boundary and on every live steer: the whole-spec check costs a
|
|
955
|
+
* full validation per sample, this one a layer or two.
|
|
956
|
+
*/
|
|
957
|
+
declare function validateSpecPaths(spec: SaverSpec, paths: Iterable<string>): boolean;
|
|
853
958
|
/** Narrowing helper: validate + cast. Throws with a joined message when invalid. */
|
|
854
959
|
declare function assertValidSpec(spec: unknown): SaverSpec;
|
|
855
960
|
/**
|
|
@@ -1206,6 +1311,15 @@ interface PathTarget {
|
|
|
1206
1311
|
}
|
|
1207
1312
|
/** Resolve a dot-path (key-aware) to its parent + final key; null if absent. */
|
|
1208
1313
|
declare function resolveSpecPath(spec: unknown, path: string): PathTarget | null;
|
|
1314
|
+
/**
|
|
1315
|
+
* The index form of a key-aware dot-path (`fireflies.sprite.color` →
|
|
1316
|
+
* `layers.3.sprite.color`), or null when it does not resolve. Two paths that
|
|
1317
|
+
* address the same field compare equal after this — how a live steer finds
|
|
1318
|
+
* out whether a `timeline` key animates the field it touches.
|
|
1319
|
+
*/
|
|
1320
|
+
declare function canonicalSpecPath(spec: unknown, path: string): string | null;
|
|
1321
|
+
/** Read the value at a dot-path, or undefined when it does not resolve. */
|
|
1322
|
+
declare function readSpecPath(spec: unknown, path: string): unknown;
|
|
1209
1323
|
/** A steering delta as carried on a channel control-track. */
|
|
1210
1324
|
interface SteerDelta {
|
|
1211
1325
|
t: number;
|
|
@@ -1222,6 +1336,14 @@ declare function applyDeltasToSpec(spec: SaverSpec, deltas: SteerDelta[]): Saver
|
|
|
1222
1336
|
* target immediately (k > 0). Layer counts round to integers.
|
|
1223
1337
|
*/
|
|
1224
1338
|
declare function lerpSpec(from: SaverSpec, to: SaverSpec, k: number): SaverSpec;
|
|
1339
|
+
/**
|
|
1340
|
+
* The walk behind `lerpSpec`, for any value: numbers lerp, hex colours lerp
|
|
1341
|
+
* per channel, equal-length arrays and objects recurse, everything else steps
|
|
1342
|
+
* to the target at k > 0 (`count` rounds). At a spec's root a `timeline` steps
|
|
1343
|
+
* too — two timelines' key times must never blend. No existing spec carries a
|
|
1344
|
+
* timeline, so every stored morph and glide is unchanged.
|
|
1345
|
+
*/
|
|
1346
|
+
declare function lerpValue(from: unknown, to: unknown, k: number, specRoot?: boolean, rootKey?: string): unknown;
|
|
1225
1347
|
/**
|
|
1226
1348
|
* The string(s) a text layer paints — `strings` of a `text` sprite, `text`
|
|
1227
1349
|
* of a `textBlock` — or null for every other sprite.
|
|
@@ -1472,10 +1594,11 @@ interface LuminanceGridOptions extends PerceiveOptions {
|
|
|
1472
1594
|
* of (seed, t), `ghosting` is a decayed sum of past-frame splats (weight g^m
|
|
1473
1595
|
* for ink m frames old, mirroring the renderer's warm-up replay) and `trail`
|
|
1474
1596
|
* re-uses drawTrail's sampling — past positions with decaying alpha and
|
|
1475
|
-
* shrinking radius.
|
|
1476
|
-
*
|
|
1477
|
-
*
|
|
1478
|
-
*
|
|
1597
|
+
* shrinking radius. Polygons larger than POLYGON_OUTLINE_CELLS are scanned by
|
|
1598
|
+
* their real (rotated) outline; smaller ones splat as a fill-weighted disc.
|
|
1599
|
+
* Remaining approximations (documented, deliberate): soft circles use a linear
|
|
1600
|
+
* falloff, background drift is sampled at its rest position. Good enough to
|
|
1601
|
+
* perceive composition, focus, balance — and now smear.
|
|
1479
1602
|
*/
|
|
1480
1603
|
declare function luminanceGrid(spec: SaverSpec, opts?: LuminanceGridOptions): LuminanceGrid;
|
|
1481
1604
|
/**
|
|
@@ -1549,7 +1672,15 @@ interface LayerMotionStats {
|
|
|
1549
1672
|
maxSpeed: number;
|
|
1550
1673
|
moving: boolean;
|
|
1551
1674
|
}
|
|
1552
|
-
/**
|
|
1675
|
+
/**
|
|
1676
|
+
* Per-layer displacement between t and t+dt — choreography as numbers.
|
|
1677
|
+
*
|
|
1678
|
+
* Samples the scene at both ends of the window separately (not just each
|
|
1679
|
+
* entity's own position within one scene) so a `timeline`-driven layer
|
|
1680
|
+
* `transform` — a camera pan or zoom with every entity otherwise static —
|
|
1681
|
+
* registers as motion instead of vanishing between two reads of the same
|
|
1682
|
+
* frozen transform.
|
|
1683
|
+
*/
|
|
1553
1684
|
declare function motionStats(spec: SaverSpec, opts?: PerceiveOptions & {
|
|
1554
1685
|
dt?: number;
|
|
1555
1686
|
}): LayerMotionStats[];
|
|
@@ -1890,6 +2021,15 @@ declare function canMorph(seq: IdleSequence, from: number): boolean;
|
|
|
1890
2021
|
* never its own index's.
|
|
1891
2022
|
*/
|
|
1892
2023
|
declare function morphChainRoot(seq: IdleSequence, index: number): number;
|
|
2024
|
+
/**
|
|
2025
|
+
* Whether a looping sequence's wrap (last segment → segment 0) plays the last
|
|
2026
|
+
* segment's `morph`. Opt-in via `wrapMorph: true`, and only when the whole lap
|
|
2027
|
+
* is one morph chain — every boundary morphs and the last segment is a
|
|
2028
|
+
* structural twin of segment 0 — so the wrap keeps the chain root's seed and
|
|
2029
|
+
* entity placement and nothing pops. Otherwise the wrap is the hard cut it has
|
|
2030
|
+
* always been (the validator warns when `wrapMorph` asks for one it can't do).
|
|
2031
|
+
*/
|
|
2032
|
+
declare function canWrapMorph(seq: IdleSequence): boolean;
|
|
1893
2033
|
/** `SpecInstance`'s seed normalization: 0 is falsy, so a valid `seed: 0` lands on 1. */
|
|
1894
2034
|
declare function normalizeSeed(seed: number): number;
|
|
1895
2035
|
/**
|
|
@@ -1959,6 +2099,73 @@ declare function sequenceWantsFinish(seq: IdleSequence): boolean;
|
|
|
1959
2099
|
*/
|
|
1960
2100
|
declare function sequenceSwapCompatible(prev: IdleSequence, next: IdleSequence): boolean;
|
|
1961
2101
|
|
|
2102
|
+
/**
|
|
2103
|
+
* `timeline` — authored keyframes on a scene's own clock.
|
|
2104
|
+
*
|
|
2105
|
+
* Everything here is a pure function of `(spec, t)`: resolving a scene at a
|
|
2106
|
+
* time never depends on what was rendered before, so a timeline scene is as
|
|
2107
|
+
* seekable and frame-addressable as any other (ghosting warm-up replays call
|
|
2108
|
+
* `resolveTimelineAt` at each replay frame's own time). A spec without a
|
|
2109
|
+
* `timeline` is returned untouched, which is what keeps every existing scene
|
|
2110
|
+
* byte-identical.
|
|
2111
|
+
*
|
|
2112
|
+
* Semantics:
|
|
2113
|
+
* - Keys address the same dot-paths as live steering; a path must already
|
|
2114
|
+
* exist on the spec (a key cannot create a field).
|
|
2115
|
+
* - A key glides from wherever its path is at `t` to `value` over `dur` ms
|
|
2116
|
+
* (default 1000, the live-steering default; 0 is a cut), eased `smooth`
|
|
2117
|
+
* unless it says otherwise. Overlapping glides are fine: the later one
|
|
2118
|
+
* starts from the earlier one's in-flight value.
|
|
2119
|
+
* - Without `loop`, a path holds the base spec's value before its first key.
|
|
2120
|
+
* - With `loop`, time wraps every `duration` ms and a path's value before its
|
|
2121
|
+
* first key is its LAST key's value — the lap is one closed cycle, and lap 1
|
|
2122
|
+
* looks exactly like lap 7 (the value depends only on `t mod duration`).
|
|
2123
|
+
*/
|
|
2124
|
+
|
|
2125
|
+
/** Default glide length for a key without `dur` — the same as a live steer's. */
|
|
2126
|
+
declare const DEFAULT_KEY_DUR = 1000;
|
|
2127
|
+
type Tracks = Map<string, TimelineKey[]>;
|
|
2128
|
+
/** The spec without its `timeline` (a shallow copy — the layers are shared, never mutated). */
|
|
2129
|
+
declare function withoutTimeline(spec: SaverSpec): SaverSpec;
|
|
2130
|
+
/**
|
|
2131
|
+
* The spec's keys grouped by the field they animate (index-form path), each
|
|
2132
|
+
* group sorted by time (stable, so equal times keep authored order and the
|
|
2133
|
+
* later one wins). Keys whose path does not resolve are dropped — the
|
|
2134
|
+
* validator rejects them, and the runtime stays lenient like steering.
|
|
2135
|
+
*/
|
|
2136
|
+
declare function timelineTracks(spec: SaverSpec): Tracks;
|
|
2137
|
+
/** Scene time folded into the timeline's own clock: `t mod duration` under `loop`, else `t`. */
|
|
2138
|
+
declare function timelineLocalTime(tl: Timeline, t: number): number;
|
|
2139
|
+
/**
|
|
2140
|
+
* The scene at time `t` with every key applied — and without its `timeline`,
|
|
2141
|
+
* so the result can be rendered, perceived, lerped or steered like any plain
|
|
2142
|
+
* spec. A spec without a timeline comes back as the same object.
|
|
2143
|
+
*/
|
|
2144
|
+
declare function resolveTimelineAt(spec: SaverSpec, t: number): SaverSpec;
|
|
2145
|
+
/**
|
|
2146
|
+
* When the timeline next takes a path back: the scene time of that path's
|
|
2147
|
+
* next key after `t`, and that key's glide length — or null when no key will
|
|
2148
|
+
* ever touch it again (no key on the path, or a non-looping timeline past its
|
|
2149
|
+
* last key). A live steer on the path holds until `at`, then glides back to
|
|
2150
|
+
* the timeline over `dur`.
|
|
2151
|
+
*/
|
|
2152
|
+
declare function nextKeyAfter(spec: SaverSpec, path: string, t: number): {
|
|
2153
|
+
at: number;
|
|
2154
|
+
dur: number;
|
|
2155
|
+
} | null;
|
|
2156
|
+
/**
|
|
2157
|
+
* Scene times (timeline-local; within one lap under `loop`) where the keys'
|
|
2158
|
+
* combined state is worth checking: time 0, and every key's start, end and
|
|
2159
|
+
* glide quarter-points. Two keys can each be valid alone and invalid together
|
|
2160
|
+
* (a `clock.rate` and a `pulse.period` the flash floor bounds as a ratio), so
|
|
2161
|
+
* validation samples the composed scene here. `dense: false` keeps only the
|
|
2162
|
+
* starts and ends, for the per-steer check a live viewer runs. Capped (evenly
|
|
2163
|
+
* subsampled) so a 256-key timeline stays cheap to check.
|
|
2164
|
+
*/
|
|
2165
|
+
declare function timelineSampleTimes(spec: SaverSpec, dense?: boolean, cap?: number, only?: (path: string) => boolean): number[];
|
|
2166
|
+
/** The absolute scene times at or after `now` where `timelineSampleTimes` fall — the next lap's occurrences under `loop`. */
|
|
2167
|
+
declare function timelineSampleTimesAfter(spec: SaverSpec, now: number, dense?: boolean, only?: (path: string) => boolean): number[];
|
|
2168
|
+
|
|
1962
2169
|
/**
|
|
1963
2170
|
* Reproduces the hand-coded Fish Aquarium saver as pure DATA — the discriminating test
|
|
1964
2171
|
* that the schema is actually expressive: two layers (fish drifting both ways at varied
|
|
@@ -2334,4 +2541,4 @@ declare const EXAMPLE_SPECS: SaverSpec[];
|
|
|
2334
2541
|
/** Lookup by saver id. */
|
|
2335
2542
|
declare const EXAMPLE_BY_ID: Readonly<Record<string, SaverSpec>>;
|
|
2336
2543
|
|
|
2337
|
-
export { AQUARIUM_SPEC, AURORA_SPEC, type BackgroundDrift, type BackgroundSpec, type BandSpec, COHESION_T, COMETS_SPEC, CONSTELLATION_SPEC, type CapabilityTier, type CohesionRead, type CycleSpec, DASHBOARD_SPEC, DITHER_ALPHA, DITHER_TILE, type DominanceEntry, EXAMPLE_BY_ID, EXAMPLE_SEQUENCES, EXAMPLE_SPECS, type Entity, FACETS_SPEC, FIELD_BUCKET_MS, FIELD_DRIFT_DEFAULT_AMOUNT, FIELD_RASTER_SHORT_SIDE, FIELD_RASTER_SHORT_SIDE_LOW, FINISH_ANIMATE_HZ, type FieldBackground, type FieldDrift, type FinishPass, type FinishSpec, GRAIN_ALPHA, GRAIN_TILE, type GradientStop, HAIKU_SPEC, type IdleSequence, LANTERNS_SPEC, LIMITS, LOBBY_TALK_SPEC, type LayerCohesion, type LayerMotionStats, type LayerSnapshot, type LayerSpec, type LinkEdge, type LuminanceGrid, type LuminanceGridOptions, MATRIX_RAIN_SPEC, MURMUR_SPEC, type MotionEase, type MotionSpec, NOSTALGHIA_CANDLE_SPEC, ORRERY_SPEC, PHASE_DUET_SPEC, PINGS_SPEC, POLYGONS_SPEC, PROCESSION_SPEC, type PerceiveOptions, type PerceiveSequenceOptions, type Placed, RAIN_SPEC, RELAY_BOARD_SPEC, type ResolveOptions, type ResolvedSegment, type RevealState, SAKURA_SPEC, SCHEMA_EXAMPLES, SCHEMA_VERSION, SHARD_FALL_SPEC, SIGNAL_BOARD_SPEC, SNOWFALL_SPEC, SPARSE_NIGHT_SPEC, type SaverSpec, type SceneDescription, type SceneDiff, type ScenePerception, type SceneSnapshot, type SchemaExample, type ScreenOp, type SequenceExample, type SequenceFramePerception, type SequenceMountContext, type SequenceSaverInstance, type SequenceSaverPlugin, type SequenceSegment, type SequenceTransition, type SpecError, type SpecMountContext, type SpecWarning, type SpriteSpec, type SteerDelta, THERMAL_FIELD_SPEC, THREE_MOVEMENTS_SEQUENCE, TYPED_CUE_SPEC, type TextBlockAnchor, type TextBlockLine, type TextMetricsClass, type TextRevealInput, type TextRevealSpec, type TextRole, type TextSpriteInfo, type ValidationResult, WARP_MAX_SCALE, WARP_NEAR, WARP_TUNNEL_SPEC, WEB_WORK_SPEC, type WanderOsc, type WarningBox, adviseSequence, adviseSpec, alphaAt, applyDeltasToSpec, assertValidSequence, assertValidSpec, bayerMatrix, bayerTilePixels, bedRenderSeed, breakTextBlock, buildEntities, canMorph, cohesionOf, compileSaver, compileSequence, createFinishPass, createSceneCanvas, describeScene, diffScenes, dominanceRanking, easeSmooth, emitEnvelope, emitWindow, fieldAt, fieldOctaves, fieldRgb, fieldRgbAt, fieldSampleTime, finishStrength, grainOffset, grainTilePixels, graphemeClusters, hasHotSwapSequence, headingAt, hexToRgb255, ignoredPropertyPaths, latticeHash, layerCohesion, lerpSpec, lifeAlphaAt, linkEdges, linkPairs, luminanceGrid, manifestFor, morphChainRoot, morphNothingMorphable, motionStats, normalizeSeed, perceiveScene, perceiveSequenceFrame, pickScreenOp, positionAt, presentWithFinish, renderBrailleMap, renderDensityMap, resolveSegment, resolveSpecPath, revealState, rgb255Luma, rotationAt, seamCauseOf, seamsWorthWarning, segmentRenderSeed, segmentStart, sequenceSteerablePaths, sequenceSwapCompatible, sequenceWantsFinish, sizeAt, spriteIndexAt, spriteVariants, steerablePaths, structuralSignature, textBlockAnchorOffset, textMetricsClassFor, textSprites, textStringsDiffer, textStringsOf, textWidthEm, validateSequence, validateSpec };
|
|
2544
|
+
export { AQUARIUM_SPEC, AURORA_SPEC, type BackgroundDrift, type BackgroundSpec, type BandSpec, COHESION_T, COMETS_SPEC, CONSTELLATION_SPEC, type CapabilityTier, type CohesionRead, type CycleSpec, DASHBOARD_SPEC, DEFAULT_KEY_DUR, DITHER_ALPHA, DITHER_TILE, type DominanceEntry, EXAMPLE_BY_ID, EXAMPLE_SEQUENCES, EXAMPLE_SPECS, type Entity, FACETS_SPEC, FIELD_BUCKET_MS, FIELD_DRIFT_DEFAULT_AMOUNT, FIELD_RASTER_SHORT_SIDE, FIELD_RASTER_SHORT_SIDE_LOW, FINISH_ANIMATE_HZ, type FieldBackground, type FieldDrift, type FinishPass, type FinishSpec, GRAIN_ALPHA, GRAIN_TILE, type GradientStop, HAIKU_SPEC, type IdleSequence, LANTERNS_SPEC, LIMITS, LOBBY_TALK_SPEC, type LayerCohesion, type LayerMotionStats, type LayerSnapshot, type LayerSpec, type LayerTransform, type LinkEdge, type LuminanceGrid, type LuminanceGridOptions, MATRIX_RAIN_SPEC, MURMUR_SPEC, type MotionEase, type MotionSpec, NOSTALGHIA_CANDLE_SPEC, ORRERY_SPEC, PHASE_DUET_SPEC, PINGS_SPEC, POLYGONS_SPEC, PROCESSION_SPEC, type PerceiveOptions, type PerceiveSequenceOptions, type Placed, RAIN_SPEC, RELAY_BOARD_SPEC, type ResolveOptions, type ResolvedSegment, type RevealState, SAKURA_SPEC, SCHEMA_EXAMPLES, SCHEMA_VERSION, SHARD_FALL_SPEC, SIGNAL_BOARD_SPEC, SNOWFALL_SPEC, SPARSE_NIGHT_SPEC, type SaverSpec, type SceneDescription, type SceneDiff, type ScenePerception, type SceneSnapshot, type SchemaExample, type ScreenOp, type SequenceExample, type SequenceFramePerception, type SequenceMountContext, type SequenceSaverInstance, type SequenceSaverPlugin, type SequenceSegment, type SequenceTransition, type SpecError, type SpecMountContext, type SpecWarning, type SpriteSpec, type SteerDelta, THERMAL_FIELD_SPEC, THREE_MOVEMENTS_SEQUENCE, TYPED_CUE_SPEC, type TextBlockAnchor, type TextBlockLine, type TextMetricsClass, type TextRevealInput, type TextRevealSpec, type TextRole, type TextSpriteInfo, type Timeline, type TimelineKey, type ValidationResult, WARP_MAX_SCALE, WARP_NEAR, WARP_TUNNEL_SPEC, WEB_WORK_SPEC, type WanderOsc, type WarningBox, adviseSequence, adviseSpec, alphaAt, applyDeltasToSpec, assertValidSequence, assertValidSpec, bayerMatrix, bayerTilePixels, bedRenderSeed, breakTextBlock, buildEntities, canMorph, canWrapMorph, canonicalSpecPath, cohesionOf, compileSaver, compileSequence, createFinishPass, createSceneCanvas, describeScene, diffScenes, dominanceRanking, easeSmooth, emitEnvelope, emitWindow, fieldAt, fieldOctaves, fieldRgb, fieldRgbAt, fieldSampleTime, finishStrength, grainOffset, grainTilePixels, graphemeClusters, hasHotSwapSequence, headingAt, hexToRgb255, ignoredPropertyPaths, latticeHash, layerCohesion, lerpSpec, lerpValue, lifeAlphaAt, linkEdges, linkPairs, luminanceGrid, manifestFor, morphChainRoot, morphNothingMorphable, motionStats, nextKeyAfter, normalizeSeed, perceiveScene, perceiveSequenceFrame, pickScreenOp, positionAt, presentWithFinish, readSpecPath, renderBrailleMap, renderDensityMap, resolveSegment, resolveSpecPath, resolveTimelineAt, revealState, rgb255Luma, rotationAt, seamCauseOf, seamsWorthWarning, segmentRenderSeed, segmentStart, sequenceSteerablePaths, sequenceSwapCompatible, sequenceWantsFinish, sizeAt, spriteIndexAt, spriteVariants, steerablePaths, structuralSignature, textBlockAnchorOffset, textMetricsClassFor, textSprites, textStringsDiffer, textStringsOf, textWidthEm, timelineLocalTime, timelineSampleTimes, timelineSampleTimesAfter, timelineTracks, validateSequence, validateSpec, validateSpecPaths, withoutTimeline };
|