@idle-screens/schema 3.9.0 → 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 +243 -15
- package/dist/index.js +1121 -199
- 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`
|
|
@@ -843,6 +936,25 @@ interface IdleSequence {
|
|
|
843
936
|
}
|
|
844
937
|
|
|
845
938
|
declare function validateSpec(spec: unknown): ValidationResult;
|
|
939
|
+
/**
|
|
940
|
+
* Dot-paths (numeric layer indices, as `steerablePaths` spells them) of every
|
|
941
|
+
* property the validator flags as unknown or misplaced — i.e. fields the
|
|
942
|
+
* renderer never reads, so steering them does nothing. Pure: unlike
|
|
943
|
+
* `validateSpec` it never normalises colours into the caller's spec.
|
|
944
|
+
*/
|
|
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;
|
|
846
958
|
/** Narrowing helper: validate + cast. Throws with a joined message when invalid. */
|
|
847
959
|
declare function assertValidSpec(spec: unknown): SaverSpec;
|
|
848
960
|
/**
|
|
@@ -1199,6 +1311,15 @@ interface PathTarget {
|
|
|
1199
1311
|
}
|
|
1200
1312
|
/** Resolve a dot-path (key-aware) to its parent + final key; null if absent. */
|
|
1201
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;
|
|
1202
1323
|
/** A steering delta as carried on a channel control-track. */
|
|
1203
1324
|
interface SteerDelta {
|
|
1204
1325
|
t: number;
|
|
@@ -1215,6 +1336,14 @@ declare function applyDeltasToSpec(spec: SaverSpec, deltas: SteerDelta[]): Saver
|
|
|
1215
1336
|
* target immediately (k > 0). Layer counts round to integers.
|
|
1216
1337
|
*/
|
|
1217
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;
|
|
1218
1347
|
/**
|
|
1219
1348
|
* The string(s) a text layer paints — `strings` of a `text` sprite, `text`
|
|
1220
1349
|
* of a `textBlock` — or null for every other sprite.
|
|
@@ -1235,6 +1364,12 @@ declare function morphNothingMorphable(a: SaverSpec, b: SaverSpec, opts?: {
|
|
|
1235
1364
|
* (id, label, schemaVersion, seed, units, kind, type, key) are excluded —
|
|
1236
1365
|
* they describe structure, not tuneable values.
|
|
1237
1366
|
*
|
|
1367
|
+
* Properties the validator flags as unknown or misplaced (e.g. `angle` on a
|
|
1368
|
+
* gradient background, `blend` inside a sprite) are excluded too: the
|
|
1369
|
+
* renderer never reads them, so advertising them invites a steer that
|
|
1370
|
+
* silently does nothing. Same source of truth as validateSpec's
|
|
1371
|
+
* "will be ignored" warnings (`ignoredPropertyPaths`).
|
|
1372
|
+
*
|
|
1238
1373
|
* Layer keys are NOT substituted: paths always use numeric indices.
|
|
1239
1374
|
* Consumers can map to key-based paths via resolveSpecPath if needed.
|
|
1240
1375
|
*/
|
|
@@ -1459,10 +1594,11 @@ interface LuminanceGridOptions extends PerceiveOptions {
|
|
|
1459
1594
|
* of (seed, t), `ghosting` is a decayed sum of past-frame splats (weight g^m
|
|
1460
1595
|
* for ink m frames old, mirroring the renderer's warm-up replay) and `trail`
|
|
1461
1596
|
* re-uses drawTrail's sampling — past positions with decaying alpha and
|
|
1462
|
-
* shrinking radius.
|
|
1463
|
-
*
|
|
1464
|
-
*
|
|
1465
|
-
*
|
|
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.
|
|
1466
1602
|
*/
|
|
1467
1603
|
declare function luminanceGrid(spec: SaverSpec, opts?: LuminanceGridOptions): LuminanceGrid;
|
|
1468
1604
|
/**
|
|
@@ -1536,7 +1672,15 @@ interface LayerMotionStats {
|
|
|
1536
1672
|
maxSpeed: number;
|
|
1537
1673
|
moving: boolean;
|
|
1538
1674
|
}
|
|
1539
|
-
/**
|
|
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
|
+
*/
|
|
1540
1684
|
declare function motionStats(spec: SaverSpec, opts?: PerceiveOptions & {
|
|
1541
1685
|
dt?: number;
|
|
1542
1686
|
}): LayerMotionStats[];
|
|
@@ -1877,6 +2021,15 @@ declare function canMorph(seq: IdleSequence, from: number): boolean;
|
|
|
1877
2021
|
* never its own index's.
|
|
1878
2022
|
*/
|
|
1879
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;
|
|
1880
2033
|
/** `SpecInstance`'s seed normalization: 0 is falsy, so a valid `seed: 0` lands on 1. */
|
|
1881
2034
|
declare function normalizeSeed(seed: number): number;
|
|
1882
2035
|
/**
|
|
@@ -1919,10 +2072,14 @@ declare function sequenceWantsFinish(seq: IdleSequence): boolean;
|
|
|
1919
2072
|
* when present;
|
|
1920
2073
|
* - the same top-level `seq.seed`, compared normalized (like the mount-time
|
|
1921
2074
|
* seed itself) so a change of an exact multiple of 2^32 isn't flagged —
|
|
1922
|
-
*
|
|
1923
|
-
* `
|
|
1924
|
-
*
|
|
1925
|
-
*
|
|
2075
|
+
* but ONLY when either side has a sequence-level `finish`: that's the one
|
|
2076
|
+
* place `seq.seed` reaches rendering on its own (`frameFinishSeed` uses the
|
|
2077
|
+
* instance's mount-time seed for a sequence-level finish's grain, which a
|
|
2078
|
+
* swap cannot update). Without a sequence-level `finish`, every consumer of
|
|
2079
|
+
* `seq.seed` is a `segmentRenderSeed`/`bedRenderSeed` fallback, and those
|
|
2080
|
+
* are already compared per segment and for the bed below — so when every
|
|
2081
|
+
* segment and the bed carry their own seed, `seq.seed` is dead weight and
|
|
2082
|
+
* changing it is free;
|
|
1926
2083
|
* - the same finish presence (`sequenceWantsFinish`) — the presentation
|
|
1927
2084
|
* pass is allocated once at construction, so a swap that adds or removes
|
|
1928
2085
|
* every `finish` in the sequence cannot (de)allocate it;
|
|
@@ -1942,6 +2099,73 @@ declare function sequenceWantsFinish(seq: IdleSequence): boolean;
|
|
|
1942
2099
|
*/
|
|
1943
2100
|
declare function sequenceSwapCompatible(prev: IdleSequence, next: IdleSequence): boolean;
|
|
1944
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
|
+
|
|
1945
2169
|
/**
|
|
1946
2170
|
* Reproduces the hand-coded Fish Aquarium saver as pure DATA — the discriminating test
|
|
1947
2171
|
* that the schema is actually expressive: two layers (fish drifting both ways at varied
|
|
@@ -2263,8 +2487,12 @@ declare const MURMUR_SPEC: SaverSpec;
|
|
|
2263
2487
|
* hex value glides over 2500 ms, and because the words differ and the
|
|
2264
2488
|
* transition declares `text: 'crossfade'`, the outgoing caption fades under
|
|
2265
2489
|
* the incoming one instead of switching whole-frame. Without the crossfade
|
|
2266
|
-
* declaration
|
|
2267
|
-
*
|
|
2490
|
+
* declaration the morph would still glide — colour alone gives it a
|
|
2491
|
+
* `hasGlide` — just with the caption stepping on the first frame instead of
|
|
2492
|
+
* fading; the validator only says `morph-nothing-morphable` for a pair
|
|
2493
|
+
* whose differences are all values morph cannot glide — strings
|
|
2494
|
+
* (`textBlock.text` above all), booleans, mismatched arrays — which this
|
|
2495
|
+
* one isn't.
|
|
2268
2496
|
* - **fade.** Segments 2 → 3 have nothing structurally in common, so a morph
|
|
2269
2497
|
* is impossible and a `fade` is the honest transition: the outgoing scene
|
|
2270
2498
|
* keeps animating on its own canvas for 3000 ms while the incoming one
|
|
@@ -2313,4 +2541,4 @@ declare const EXAMPLE_SPECS: SaverSpec[];
|
|
|
2313
2541
|
/** Lookup by saver id. */
|
|
2314
2542
|
declare const EXAMPLE_BY_ID: Readonly<Record<string, SaverSpec>>;
|
|
2315
2543
|
|
|
2316
|
-
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, 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 };
|