@idle-screens/schema 3.4.5 → 3.6.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 +153 -20
- package/dist/index.d.ts +368 -10
- package/dist/index.js +1204 -84
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
- package/saver-spec.schema.json +230 -35
package/dist/index.d.ts
CHANGED
|
@@ -22,6 +22,16 @@ interface SaverSpec {
|
|
|
22
22
|
background?: BackgroundSpec;
|
|
23
23
|
layers: LayerSpec[];
|
|
24
24
|
motionIntensity?: 'calm' | 'moderate' | 'energetic';
|
|
25
|
+
/**
|
|
26
|
+
* Declared density intent. `sparse` says the emptiness is the point (one
|
|
27
|
+
* mark on a dark ground, long silences): `adviseSpec` withholds
|
|
28
|
+
* `sparse-scene`, and scorers that gate on coverage should read this before
|
|
29
|
+
* calling a faithful scene broken. `dense` withholds `dense-scene` the same
|
|
30
|
+
* way. Either declaration is checked against the measured coverage and a
|
|
31
|
+
* `density-mismatch` advisory fires when the scene contradicts it. Omitted
|
|
32
|
+
* means `normal`. A hint only — it changes no pixel.
|
|
33
|
+
*/
|
|
34
|
+
density?: 'sparse' | 'normal' | 'dense';
|
|
25
35
|
/**
|
|
26
36
|
* Frame persistence (0..1, capped at LIMITS.maxGhosting). Instead of fully clearing,
|
|
27
37
|
* each frame the background is painted at reduced alpha so moving entities leave
|
|
@@ -137,7 +147,8 @@ interface LayerSpec {
|
|
|
137
147
|
key?: string;
|
|
138
148
|
/**
|
|
139
149
|
* Exact fractional position {x, y} for a single entity (0 = left/top, 1 = right/bottom).
|
|
140
|
-
* Only valid when `count` is 1
|
|
150
|
+
* Only valid when `count` is 1 — or, with a `list` / `table` layout, as the
|
|
151
|
+
* top-left anchor of the whole block. Overrides `region` scatter placement.
|
|
141
152
|
*/
|
|
142
153
|
position?: {
|
|
143
154
|
x: number;
|
|
@@ -185,6 +196,26 @@ interface LayerSpec {
|
|
|
185
196
|
x?: number;
|
|
186
197
|
y?: number;
|
|
187
198
|
};
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Data layouts — entities in READING ORDER, no scatter. `list` stacks them
|
|
202
|
+
* in one column `gap` apart (viewport units of min(w,h), default 0.06);
|
|
203
|
+
* `table` fills `columns` row-major with `gap` `{x, y}` (one number = both).
|
|
204
|
+
* Text and emoji sprites take `strings[i]` / `glyphs[i]` in order instead of
|
|
205
|
+
* a seeded pick, and palette `colors[i]` likewise, so N labels or N bars are
|
|
206
|
+
* ONE layer. `position` (with any count) is the block's top-left anchor;
|
|
207
|
+
* without it the block is centred in `region`.
|
|
208
|
+
*/
|
|
209
|
+
| {
|
|
210
|
+
type: 'list';
|
|
211
|
+
gap?: number;
|
|
212
|
+
} | {
|
|
213
|
+
type: 'table';
|
|
214
|
+
columns: number;
|
|
215
|
+
gap?: number | {
|
|
216
|
+
x?: number;
|
|
217
|
+
y?: number;
|
|
218
|
+
};
|
|
188
219
|
};
|
|
189
220
|
/**
|
|
190
221
|
* Layer lifecycle for act structure — a pure function of t, no state. Alpha is 0
|
|
@@ -196,6 +227,56 @@ interface LayerSpec {
|
|
|
196
227
|
exit?: number;
|
|
197
228
|
fade?: number;
|
|
198
229
|
};
|
|
230
|
+
/**
|
|
231
|
+
* Sparse events — marks that appear, expand and fade in place, one at a time,
|
|
232
|
+
* with long silences between (the "one ping" primitive). Each entity is dark
|
|
233
|
+
* except for a window of `life` ms that recurs every `every` ms at a
|
|
234
|
+
* per-entity offset: `jitter` 1 (default) scatters the offsets (a fixed
|
|
235
|
+
* low-discrepancy sequence — not seeded, so declaring `emit` disturbs no
|
|
236
|
+
* other draw and the same spec times its events identically under every
|
|
237
|
+
* seed), 0 spreads the entities evenly across the period — a metronome, one
|
|
238
|
+
* event at a time as long as `life ≤ every / count`.
|
|
239
|
+
* Inside a window the entity fades in over the first quarter and out over
|
|
240
|
+
* the rest; `grow` scales its size from `grow[0]` to `grow[1]` across the
|
|
241
|
+
* window — expansion rather than travel. A pure function of t. Flash safety:
|
|
242
|
+
* `every` ≥ 1000 ms and `life` ≥ 500 ms, so no entity fires more than once a
|
|
243
|
+
* second and never as a hard cut. Composes with `pulse`, `life`, `trail` and
|
|
244
|
+
* every motion; with `motion.ease` the eased travel restarts on every event.
|
|
245
|
+
*/
|
|
246
|
+
emit?: {
|
|
247
|
+
every: number;
|
|
248
|
+
life: number;
|
|
249
|
+
jitter?: number;
|
|
250
|
+
grow?: [number, number];
|
|
251
|
+
};
|
|
252
|
+
/**
|
|
253
|
+
* Phase-lock. Replaces the seeded per-entity phases of `pulse`, `grow` and
|
|
254
|
+
* `cycle` with one shared phase (`phase` in turns, 0..1, default 0) and runs
|
|
255
|
+
* those three at `rate` × scene time (default 1). Two layers declaring the
|
|
256
|
+
* same clock breathe in step; different phases give a fixed offset (call and
|
|
257
|
+
* response). `pulse.wave` still adds its position-derived phase on top, so a
|
|
258
|
+
* clocked wave stays a wave. Because a clocked layer moves in unison, its
|
|
259
|
+
* `pulse`/`grow` periods must satisfy `period / rate ≥ 1000 ms` (1 Hz).
|
|
260
|
+
*/
|
|
261
|
+
clock?: {
|
|
262
|
+
phase?: number;
|
|
263
|
+
rate?: number;
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
/**
|
|
267
|
+
* Closed-form velocity easing for `drift`, `rise` and `wander`. `settle`
|
|
268
|
+
* starts at the entity's speed and decelerates to rest with time constant
|
|
269
|
+
* `tau` ms — it travels `speed × tau` and stops, a mark flung and coming to
|
|
270
|
+
* rest. `buoyant` starts at rest and approaches the speed over `tau` — a
|
|
271
|
+
* bubble reaching terminal velocity. Position stays an analytic function of
|
|
272
|
+
* t (no integration state). With `emit`, the eased travel restarts from the
|
|
273
|
+
* spawn point on every event. For `wander` only the base velocity eases —
|
|
274
|
+
* the harmonic meander keeps breathing, so a settled wanderer hovers rather
|
|
275
|
+
* than freezes.
|
|
276
|
+
*/
|
|
277
|
+
interface MotionEase {
|
|
278
|
+
type: 'settle' | 'buoyant';
|
|
279
|
+
tau: number;
|
|
199
280
|
}
|
|
200
281
|
type SpriteSpec = {
|
|
201
282
|
kind: 'emoji';
|
|
@@ -248,6 +329,8 @@ type SpriteSpec = {
|
|
|
248
329
|
/**
|
|
249
330
|
* Axis-aligned rectangle (rotates with `spin`). `width` is the horizontal size
|
|
250
331
|
* range; `aspect` the height/width ratio range (default [1,1] = squares).
|
|
332
|
+
* `feather` (0..1) softens the edges: that fraction of the half-size fades
|
|
333
|
+
* out toward the border — Rothko's soft-edged block at 0.5–0.8.
|
|
251
334
|
* Mondrian blocks, confetti, city lights.
|
|
252
335
|
*/
|
|
253
336
|
| {
|
|
@@ -255,6 +338,63 @@ type SpriteSpec = {
|
|
|
255
338
|
width: [number, number];
|
|
256
339
|
aspect?: [number, number];
|
|
257
340
|
color: string;
|
|
341
|
+
feather?: number;
|
|
342
|
+
colors?: string[];
|
|
343
|
+
colorWeights?: number[];
|
|
344
|
+
}
|
|
345
|
+
/**
|
|
346
|
+
* A data bar. Entity i draws a bar `length × values[i] / max` long (viewport
|
|
347
|
+
* units of min(w,h)), `thickness` thick, growing from its position toward
|
|
348
|
+
* `direction` (default 'right'). `values` are PAINT — steer
|
|
349
|
+
* `layers.N.sprite.values` and the bars glide; `max` defaults to the largest
|
|
350
|
+
* value. Pair with a `list` layout: a chart is one layer, labels another.
|
|
351
|
+
*/
|
|
352
|
+
| {
|
|
353
|
+
kind: 'bar';
|
|
354
|
+
values: number[];
|
|
355
|
+
length: number;
|
|
356
|
+
thickness: number;
|
|
357
|
+
color: string;
|
|
358
|
+
max?: number;
|
|
359
|
+
direction?: 'right' | 'left' | 'up' | 'down';
|
|
360
|
+
colors?: string[];
|
|
361
|
+
colorWeights?: number[];
|
|
362
|
+
}
|
|
363
|
+
/**
|
|
364
|
+
* Regular or custom polygon. `sides` (3..12, default 6) draws a regular
|
|
365
|
+
* n-gon of the seeded `radius` (circumradius), point up; `points` (3..24
|
|
366
|
+
* unit coordinates in −1..1, scaled by the radius) draws any facet — a
|
|
367
|
+
* Picasso shard, a Kandinsky triangle, a Mondrian plane. `soft` feathers
|
|
368
|
+
* the fill from the centre like a soft circle. Rotates with `spin`.
|
|
369
|
+
*/
|
|
370
|
+
| {
|
|
371
|
+
kind: 'polygon';
|
|
372
|
+
radius: [number, number];
|
|
373
|
+
color: string;
|
|
374
|
+
sides?: number;
|
|
375
|
+
points?: Array<[number, number]>;
|
|
376
|
+
soft?: boolean;
|
|
377
|
+
colors?: string[];
|
|
378
|
+
colorWeights?: number[];
|
|
379
|
+
}
|
|
380
|
+
/**
|
|
381
|
+
* A freehand mark: a path through `points` (2..24 unit coordinates in
|
|
382
|
+
* −1..1, scaled by the seeded `length`) stroked `width` wide with round
|
|
383
|
+
* joins. `curve: 'smooth'` (default) runs a Catmull-Rom spline through the
|
|
384
|
+
* points, `'linear'` a polyline. `taper` thins the mark to almost nothing at
|
|
385
|
+
* both ends — a brush stroke rather than a line. `orient` turns the path's
|
|
386
|
+
* +x along the entity's heading (like `streak`). Rotates with `spin`. Van
|
|
387
|
+
* Gogh's stroke, Hokusai's contour, O'Keeffe's petal, Basquiat's scrawl.
|
|
388
|
+
*/
|
|
389
|
+
| {
|
|
390
|
+
kind: 'stroke';
|
|
391
|
+
length: [number, number];
|
|
392
|
+
points: Array<[number, number]>;
|
|
393
|
+
color: string;
|
|
394
|
+
width?: number;
|
|
395
|
+
curve?: 'smooth' | 'linear';
|
|
396
|
+
taper?: boolean;
|
|
397
|
+
orient?: boolean;
|
|
258
398
|
colors?: string[];
|
|
259
399
|
colorWeights?: number[];
|
|
260
400
|
}
|
|
@@ -292,8 +432,12 @@ interface TextRevealSpec {
|
|
|
292
432
|
* by `mode`. Steerable paint — glide it to animate typing or deleting.
|
|
293
433
|
*/
|
|
294
434
|
progress?: number;
|
|
295
|
-
/**
|
|
296
|
-
|
|
435
|
+
/**
|
|
436
|
+
* Reveal granularity: per-grapheme (default), per-word, per-line, or
|
|
437
|
+
* `glyphFade` — each glyph fades in over a staggered alpha ramp instead of
|
|
438
|
+
* appearing whole (the caption look). Still a pure function of `progress`.
|
|
439
|
+
*/
|
|
440
|
+
mode?: 'typewriter' | 'word' | 'line' | 'glyphFade';
|
|
297
441
|
/**
|
|
298
442
|
* Self-typing rate in graphemes/second from scene start (media time). The
|
|
299
443
|
* effective progress is `min(progress, speed·t/total)` — so a steered
|
|
@@ -309,6 +453,12 @@ interface TextRevealSpec {
|
|
|
309
453
|
blink?: number;
|
|
310
454
|
color?: string;
|
|
311
455
|
};
|
|
456
|
+
/**
|
|
457
|
+
* `glyphFade` only: each glyph's fade window as a fraction of progress
|
|
458
|
+
* (0 exclusive to 1, default 0.15). Small = soft typewriter; large = a
|
|
459
|
+
* wave of overlapping fades. Ignored by the other modes.
|
|
460
|
+
*/
|
|
461
|
+
fade?: number;
|
|
312
462
|
}
|
|
313
463
|
/** Rotate through sprite variants over time. Each entity offsets by its seeded phase. */
|
|
314
464
|
interface CycleSpec {
|
|
@@ -328,12 +478,14 @@ type MotionSpec =
|
|
|
328
478
|
angle?: number;
|
|
329
479
|
bidirectional?: boolean;
|
|
330
480
|
bob?: number;
|
|
481
|
+
ease?: MotionEase;
|
|
331
482
|
}
|
|
332
483
|
/** Rise upward (px/sec) with an optional horizontal sway amplitude (px) — bubbles. */
|
|
333
484
|
| {
|
|
334
485
|
type: 'rise';
|
|
335
486
|
speed: [number, number];
|
|
336
487
|
sway?: number;
|
|
488
|
+
ease?: MotionEase;
|
|
337
489
|
}
|
|
338
490
|
/** Bounce diagonally at a per-entity speed, reflecting off the edges (px/sec). */
|
|
339
491
|
| {
|
|
@@ -378,6 +530,7 @@ type MotionSpec =
|
|
|
378
530
|
angle?: number;
|
|
379
531
|
meander?: number;
|
|
380
532
|
coherence?: number;
|
|
533
|
+
ease?: MotionEase;
|
|
381
534
|
}
|
|
382
535
|
/**
|
|
383
536
|
* Perspective starfield: entities live on a depth axis and stream toward the
|
|
@@ -467,6 +620,20 @@ declare const LIMITS: {
|
|
|
467
620
|
readonly minSegmentDuration: 1000;
|
|
468
621
|
readonly maxTransitionDur: 5000;
|
|
469
622
|
readonly minTransitionDur: 200;
|
|
623
|
+
readonly minEmitEvery: 1000;
|
|
624
|
+
readonly maxEmitEvery: 600000;
|
|
625
|
+
readonly minEmitLife: 500;
|
|
626
|
+
readonly maxEmitGrow: 8;
|
|
627
|
+
readonly minClockedPeriod: 1000;
|
|
628
|
+
readonly minClockRate: 0.1;
|
|
629
|
+
readonly maxClockRate: 4;
|
|
630
|
+
readonly defaultListGap: 0.06;
|
|
631
|
+
readonly defaultListGapPx: 40;
|
|
632
|
+
readonly minPolygonSides: 3;
|
|
633
|
+
readonly maxPolygonSides: 12;
|
|
634
|
+
readonly maxShapePoints: 24;
|
|
635
|
+
readonly minEaseTau: 100;
|
|
636
|
+
readonly maxEaseTau: 120000;
|
|
470
637
|
};
|
|
471
638
|
type SequenceTransition = {
|
|
472
639
|
type: 'cut';
|
|
@@ -544,8 +711,12 @@ interface Entity {
|
|
|
544
711
|
orbitCy: number;
|
|
545
712
|
colorIndex: number;
|
|
546
713
|
cyclePeriod: number;
|
|
547
|
-
/** Rect sprite second dimension (height px). Only set for rect sprites. */
|
|
714
|
+
/** Rect sprite second dimension (height px), or bar thickness. Only set for rect / bar sprites. */
|
|
548
715
|
size2?: number;
|
|
716
|
+
/** Reading-order index into `bar.values`. Only set for bar sprites. */
|
|
717
|
+
barIndex?: number;
|
|
718
|
+
/** Placed by a list/table layout: variants cycle from the ordered base index, in step. Only set for data layouts. */
|
|
719
|
+
ordered?: true;
|
|
549
720
|
/** Orbit parent layer key (motion.center = { layer }). Center resolved at render time. */
|
|
550
721
|
orbitParent?: string;
|
|
551
722
|
/** Harmonic drift params. Only set for wander motion. */
|
|
@@ -564,6 +735,23 @@ interface Entity {
|
|
|
564
735
|
cx: number;
|
|
565
736
|
cy: number;
|
|
566
737
|
};
|
|
738
|
+
/** Sparse-event window. Only set when `layer.emit` is declared. `phase` ms offset. */
|
|
739
|
+
emit?: {
|
|
740
|
+
every: number;
|
|
741
|
+
life: number;
|
|
742
|
+
phase: number;
|
|
743
|
+
growFrom: number;
|
|
744
|
+
growTo: number;
|
|
745
|
+
};
|
|
746
|
+
/** Shared-clock time multiplier for pulse/grow/cycle. Only set when `layer.clock` is declared. */
|
|
747
|
+
clockRate?: number;
|
|
748
|
+
/** Shared cycle phase (radians). Only set when `layer.clock` is declared. */
|
|
749
|
+
cyclePhase?: number;
|
|
750
|
+
/** Velocity easing. Only set when the motion declares `ease`. */
|
|
751
|
+
ease?: {
|
|
752
|
+
type: 'settle' | 'buoyant';
|
|
753
|
+
tau: number;
|
|
754
|
+
};
|
|
567
755
|
/** Spline params (pts in px, shared per layer). Only set for path motion. */
|
|
568
756
|
path?: {
|
|
569
757
|
pts: Array<{
|
|
@@ -588,6 +776,14 @@ declare function spriteVariants(sprite: SpriteSpec): number;
|
|
|
588
776
|
* `scale` multiplies every dimensional draw (default 1). Used for viewport units.
|
|
589
777
|
* `countScale` multiplies entity count for density-aware scaling (default 1). */
|
|
590
778
|
declare function buildEntities(layer: LayerSpec, rng: Rng, w: number, h: number, scale?: number, countScale?: number): Entity[];
|
|
779
|
+
/**
|
|
780
|
+
* Where entity `e` sits in its current emit window at time `t`: 0..1 across
|
|
781
|
+
* the visible `life` ms, or null while dark (or when the layer has no `emit`).
|
|
782
|
+
* Pure — the window index is `floor((t - phase) / every)`, no state.
|
|
783
|
+
*/
|
|
784
|
+
declare function emitWindow(e: Entity, t: number): number | null;
|
|
785
|
+
/** Alpha envelope across an emit window: fast smooth attack, slow smooth decay. */
|
|
786
|
+
declare function emitEnvelope(u: number): number;
|
|
591
787
|
/** Analytic opacity of an entity at logical time `t` (ms). Pure, clamped to 0..1. */
|
|
592
788
|
declare function alphaAt(e: Entity, t: number): number;
|
|
593
789
|
/** Analytic size multiplier at time `t` (ms). Pure, clamped to > 0. */
|
|
@@ -637,6 +833,8 @@ declare function linkPairs(positions: Array<{
|
|
|
637
833
|
x: number;
|
|
638
834
|
y: number;
|
|
639
835
|
}>, k: number, maxDist: number, doWrap: boolean, w: number, h: number): Array<[number, number]>;
|
|
836
|
+
/** Em-width of a string under the same character-class table `breakTextBlock` uses. */
|
|
837
|
+
declare function textWidthEm(str: string): number;
|
|
640
838
|
interface TextBlockLine {
|
|
641
839
|
text: string;
|
|
642
840
|
widthEm: number;
|
|
@@ -656,8 +854,9 @@ declare function breakTextBlock(text: string, maxWidthEm: number): TextBlockLine
|
|
|
656
854
|
declare function graphemeClusters(text: string): string[];
|
|
657
855
|
interface TextRevealInput {
|
|
658
856
|
progress?: number;
|
|
659
|
-
mode?: 'typewriter' | 'word' | 'line';
|
|
857
|
+
mode?: 'typewriter' | 'word' | 'line' | 'glyphFade';
|
|
660
858
|
speed?: number;
|
|
859
|
+
fade?: number;
|
|
661
860
|
}
|
|
662
861
|
interface RevealState {
|
|
663
862
|
/** Effective 0..1 progress after combining authored progress and speed at t. */
|
|
@@ -669,6 +868,12 @@ interface RevealState {
|
|
|
669
868
|
/** Caret anchor: line index and the visible prefix on that line. */
|
|
670
869
|
caretLine: number;
|
|
671
870
|
caretPrefix: string;
|
|
871
|
+
/**
|
|
872
|
+
* `glyphFade` mode only: per-line per-grapheme alpha (0..1), indexed
|
|
873
|
+
* parallel to `graphemeClusters(lines[i].text)`. Alphas only decrease in
|
|
874
|
+
* reading order, so a renderer may stop at the first zero.
|
|
875
|
+
*/
|
|
876
|
+
glyphAlphas?: number[][];
|
|
672
877
|
}
|
|
673
878
|
/**
|
|
674
879
|
* Resolve which glyphs of a broken textBlock are visible at time `tMs`.
|
|
@@ -743,6 +948,16 @@ declare function structuralSignature(spec: SaverSpec): string;
|
|
|
743
948
|
declare function adviseSpec(spec: SaverSpec, viewport?: {
|
|
744
949
|
width: number;
|
|
745
950
|
height: number;
|
|
951
|
+
},
|
|
952
|
+
/**
|
|
953
|
+
* Sample point. `perceiveScene` forwards its own `t`/`seed` so the advisories
|
|
954
|
+
* describe the same scene its other channels do — without them a caller
|
|
955
|
+
* perceiving at t = 30 s would get `form` for that instant beside an
|
|
956
|
+
* `overlap-seams` advisory computed for a different one.
|
|
957
|
+
*/
|
|
958
|
+
opts?: {
|
|
959
|
+
t?: number;
|
|
960
|
+
seed?: number;
|
|
746
961
|
}): SpecWarning[];
|
|
747
962
|
/**
|
|
748
963
|
* Non-blocking advisory warnings for a validated sequence envelope.
|
|
@@ -753,6 +968,82 @@ declare function adviseSequence(seq: IdleSequence, viewport?: {
|
|
|
753
968
|
height: number;
|
|
754
969
|
}): SpecWarning[];
|
|
755
970
|
|
|
971
|
+
/**
|
|
972
|
+
* Layer cohesion — does a layer read as ONE form, or as N separate marks?
|
|
973
|
+
*
|
|
974
|
+
* The gap this closes: every other perception channel reports a heavily
|
|
975
|
+
* overlapping layer identically whether its sprites merge into a silhouette or
|
|
976
|
+
* stay legible as individual discs. Luminance, coverage, dominance and the
|
|
977
|
+
* braille map are all blind to it, because they measure ink, not edges. An
|
|
978
|
+
* agent drawing a *shape* out of sprites therefore cannot tell success from
|
|
979
|
+
* failure without publishing and looking at real pixels.
|
|
980
|
+
*
|
|
981
|
+
* Two independent facts decide it, so both are reported:
|
|
982
|
+
*
|
|
983
|
+
* - `overlap` — geometry. What fraction of a typical entity's outline is buried
|
|
984
|
+
* inside a sibling. Scattered fields sit near 0; a deliberate mass sits high.
|
|
985
|
+
* - `seamless` — paint. Whether an overlap between two of this layer's entities
|
|
986
|
+
* paints as an invisible join. Only a hard-edged, fully opaque, single-colour,
|
|
987
|
+
* un-blended layer merges; anything else shows every internal edge.
|
|
988
|
+
*
|
|
989
|
+
* Both are analytic, deterministic and renderer-free, like the rest of
|
|
990
|
+
* `perceive.ts`.
|
|
991
|
+
*/
|
|
992
|
+
|
|
993
|
+
/**
|
|
994
|
+
* Sample time for a spec-level cohesion read, matching `perceiveScene`'s own
|
|
995
|
+
* default so the advisory and the reported numbers agree on the usual call.
|
|
996
|
+
* Past all typical `life.enter` staging; `grow` has had time to breathe.
|
|
997
|
+
*/
|
|
998
|
+
declare const COHESION_T = 5000;
|
|
999
|
+
/** What a layer will read as on the wall. */
|
|
1000
|
+
type CohesionRead =
|
|
1001
|
+
/** Entities merge into a single silhouette — the layer draws a shape. */
|
|
1002
|
+
'mass'
|
|
1003
|
+
/** Entities overlap heavily but every overlap paints a visible edge. */
|
|
1004
|
+
| 'seamed'
|
|
1005
|
+
/** Entities stand alone — a field of marks, which most layers are. */
|
|
1006
|
+
| 'marks';
|
|
1007
|
+
interface LayerCohesion {
|
|
1008
|
+
layerIndex: number;
|
|
1009
|
+
key: string | undefined;
|
|
1010
|
+
/**
|
|
1011
|
+
* Mean fraction of an entity's outline covered by a same-layer sibling, 0..1.
|
|
1012
|
+
* `null` for sprite kinds where a merged silhouette is not a meaningful idea
|
|
1013
|
+
* (lines, glyphs and text: ring, streak, stroke, bar, emoji, text, textBlock).
|
|
1014
|
+
*/
|
|
1015
|
+
overlap: number | null;
|
|
1016
|
+
/** Do this layer's overlaps paint as invisible joins? */
|
|
1017
|
+
seamless: boolean;
|
|
1018
|
+
/** Why not, when not — the field to act on. `null` when seamless. */
|
|
1019
|
+
seamCause: string | null;
|
|
1020
|
+
/** The verdict. `null` when `overlap` is null. */
|
|
1021
|
+
reads: CohesionRead | null;
|
|
1022
|
+
}
|
|
1023
|
+
/**
|
|
1024
|
+
* Why this layer's overlaps paint a visible edge, or null when they vanish.
|
|
1025
|
+
*
|
|
1026
|
+
* Opacity is judged over the whole timeline, not this instant: a layer that
|
|
1027
|
+
* breathes with `pulse` is translucent at some point in every cycle, so its
|
|
1028
|
+
* seams appear even if the sampled frame happens to catch it at full alpha.
|
|
1029
|
+
*/
|
|
1030
|
+
declare function seamCauseOf(layer: LayerSpec): string | null;
|
|
1031
|
+
/**
|
|
1032
|
+
* Per-layer cohesion for an already-built scene. Takes entities rather than a
|
|
1033
|
+
* spec so `perceiveScene` and `adviseSpec` can each pass the scene they already
|
|
1034
|
+
* built instead of building a third one.
|
|
1035
|
+
*/
|
|
1036
|
+
declare function cohesionOf(layers: Array<{
|
|
1037
|
+
layer: LayerSpec;
|
|
1038
|
+
entities: Entity[];
|
|
1039
|
+
}>, t: number, w: number, h: number): LayerCohesion[];
|
|
1040
|
+
/**
|
|
1041
|
+
* True when a `seamed` layer is worth warning about: dense enough to be
|
|
1042
|
+
* deliberate, and opaque enough that it cannot be an atmospheric wash. Keeps
|
|
1043
|
+
* the advisory off the soft low-alpha glow fields the playbook recommends.
|
|
1044
|
+
*/
|
|
1045
|
+
declare function seamsWorthWarning(layer: LayerSpec, c: LayerCohesion, entityCount: number): boolean;
|
|
1046
|
+
|
|
756
1047
|
interface LayerSnapshot {
|
|
757
1048
|
key: string | undefined;
|
|
758
1049
|
count: number;
|
|
@@ -899,6 +1190,13 @@ interface DominanceEntry {
|
|
|
899
1190
|
* ribbons, so a comet layer is ranked by its comet, not just its head.
|
|
900
1191
|
*/
|
|
901
1192
|
declare function dominanceRanking(spec: SaverSpec, opts?: PerceiveOptions): DominanceEntry[];
|
|
1193
|
+
/**
|
|
1194
|
+
* Does each layer read as one merged form or as separate marks? The one thing
|
|
1195
|
+
* the luminance maps cannot answer — they measure ink, not edges, so a layer
|
|
1196
|
+
* that merged into a silhouette and one that stayed a pile of discs look
|
|
1197
|
+
* identical in every other channel. See `cohesion.ts`.
|
|
1198
|
+
*/
|
|
1199
|
+
declare function layerCohesion(spec: SaverSpec, opts?: PerceiveOptions): LayerCohesion[];
|
|
902
1200
|
interface LayerMotionStats {
|
|
903
1201
|
layerIndex: number;
|
|
904
1202
|
key: string | undefined;
|
|
@@ -976,6 +1274,8 @@ interface ScenePerception {
|
|
|
976
1274
|
colProfile: number[];
|
|
977
1275
|
dominance: DominanceEntry[];
|
|
978
1276
|
motion: LayerMotionStats[];
|
|
1277
|
+
/** Per layer: merged silhouette, seamed overlap, or separate marks. */
|
|
1278
|
+
form: LayerCohesion[];
|
|
979
1279
|
/** Literal strings + sizes of any text layers (glyphs don't show in the maps). */
|
|
980
1280
|
text: TextSpriteInfo[];
|
|
981
1281
|
advisories: ReturnType<typeof adviseSpec>;
|
|
@@ -993,18 +1293,44 @@ interface ResolvedSegment {
|
|
|
993
1293
|
localT: number;
|
|
994
1294
|
/** Cumulative start time of this segment in the global timeline. */
|
|
995
1295
|
startT: number;
|
|
1296
|
+
/**
|
|
1297
|
+
* Present (true) when the timeline has run past this segment's duration but
|
|
1298
|
+
* the segment has `advance: 'input'` and no input has released it yet — the
|
|
1299
|
+
* scene keeps animating (`localT` keeps growing) while the show waits for
|
|
1300
|
+
* the presenter.
|
|
1301
|
+
*/
|
|
1302
|
+
held?: true;
|
|
1303
|
+
}
|
|
1304
|
+
interface ResolveOptions {
|
|
1305
|
+
/**
|
|
1306
|
+
* Segments below this index have been released by input: a `sequence.segment`
|
|
1307
|
+
* steer to index `n` counts as the presenter clicking past every hold before
|
|
1308
|
+
* `n`. `SequenceInstance` maintains this; callers resolving a bare timeline
|
|
1309
|
+
* leave it at 0, so every `advance: 'input'` hold is honoured.
|
|
1310
|
+
*/
|
|
1311
|
+
releasedBelow?: number;
|
|
996
1312
|
}
|
|
1313
|
+
/** Global start time of segment `index` — the prefix sum of the durations before it. */
|
|
1314
|
+
declare function segmentStart(seq: IdleSequence, index: number): number;
|
|
997
1315
|
/**
|
|
998
1316
|
* Map global time T to (segmentIndex, localT) via prefix sums of durations.
|
|
999
1317
|
* Segments are half-open: [start, start+duration). The final segment may omit
|
|
1000
1318
|
* duration, in which case it holds indefinitely (T past the last timed segment
|
|
1001
1319
|
* always resolves to the final one).
|
|
1002
1320
|
*
|
|
1003
|
-
*
|
|
1004
|
-
*
|
|
1005
|
-
*
|
|
1321
|
+
* `advance: 'input'` makes a timed segment hold at its end instead of yielding
|
|
1322
|
+
* to the next one — the clicker. `auto` (default) and `either` advance on the
|
|
1323
|
+
* timer. A hold is released by a `sequence.segment` steer (see
|
|
1324
|
+
* `ResolveOptions.releasedBelow`); the held scene keeps its clock running, so
|
|
1325
|
+
* `localT` grows past `duration` rather than freezing.
|
|
1326
|
+
*
|
|
1327
|
+
* With `loop: true`, T wraps modulo the total duration of all segments — but
|
|
1328
|
+
* only once every hold on the current lap has been released; after a wrap
|
|
1329
|
+
* every hold is armed again. If the final segment has no duration, looping is
|
|
1330
|
+
* over the timed prefix only — the durationless tail is unreachable in loop
|
|
1331
|
+
* mode (validated elsewhere).
|
|
1006
1332
|
*/
|
|
1007
|
-
declare function resolveSegment(seq: IdleSequence, T: number): ResolvedSegment;
|
|
1333
|
+
declare function resolveSegment(seq: IdleSequence, T: number, opts?: ResolveOptions): ResolvedSegment;
|
|
1008
1334
|
|
|
1009
1335
|
/**
|
|
1010
1336
|
* Reproduces the hand-coded Fish Aquarium saver as pure DATA — the discriminating test
|
|
@@ -1112,6 +1438,38 @@ declare const MATRIX_RAIN_SPEC: SaverSpec;
|
|
|
1112
1438
|
*/
|
|
1113
1439
|
declare const NOSTALGHIA_CANDLE_SPEC: SaverSpec;
|
|
1114
1440
|
|
|
1441
|
+
/**
|
|
1442
|
+
* Pings — almost nothing, almost never.
|
|
1443
|
+
* A near-black ground and one teal hue. Three rings take turns: each one
|
|
1444
|
+
* appears, grows sixteenfold (0.15× → 2.4× its radius) and fades over four
|
|
1445
|
+
* seconds, then the frame is empty until the next — `emit` with `jitter: 0`
|
|
1446
|
+
* spaces them evenly, one event every four seconds, and `life` is kept at
|
|
1447
|
+
* the stagger so no two rings are ever lit together. Underneath, a few motes rise,
|
|
1448
|
+
* `settle` to rest and fade on their own, longer, seeded schedule. Nothing
|
|
1449
|
+
* travels; everything grows and goes. The house style Fathom describes this
|
|
1450
|
+
* exactly and, before `emit`, could not be authored.
|
|
1451
|
+
*/
|
|
1452
|
+
declare const PINGS_SPEC: SaverSpec;
|
|
1453
|
+
|
|
1454
|
+
/**
|
|
1455
|
+
* Facets — a composition in the manner of the early abstractionists: three
|
|
1456
|
+
* soft-edged colour planes (feathered `rect`) breathing slowly behind a field
|
|
1457
|
+
* of tumbling triangular facets (custom `polygon`), tapered brush arcs
|
|
1458
|
+
* (`stroke`) drifting across, and a few glowing hexagons (soft regular
|
|
1459
|
+
* `polygon`). Every glyph here is a path, not a disc — the shape family nine
|
|
1460
|
+
* of the fifteen style profiles asked for.
|
|
1461
|
+
*/
|
|
1462
|
+
declare const FACETS_SPEC: SaverSpec;
|
|
1463
|
+
|
|
1464
|
+
/**
|
|
1465
|
+
* Relay Board — a six-row status chart in five layers instead of twenty-five.
|
|
1466
|
+
* One `list` of labels, one `list` of `bar`s whose lengths are the numbers,
|
|
1467
|
+
* one `list` of readouts, a title block, and a slow dust field behind so the
|
|
1468
|
+
* board reads as a live wall, not a document. Steer `bars.values` and the
|
|
1469
|
+
* bars glide to the new figures; the labels never move.
|
|
1470
|
+
*/
|
|
1471
|
+
declare const RELAY_BOARD_SPEC: SaverSpec;
|
|
1472
|
+
|
|
1115
1473
|
/**
|
|
1116
1474
|
* Polygons: the classic Mystify-style bouncing-polygon saver, expressed as chain links + heavy
|
|
1117
1475
|
* ghosting. Showcases `links.mode: 'chain'` with `closed` (vertices wired in order
|
|
@@ -1148,4 +1506,4 @@ declare const EXAMPLE_SPECS: SaverSpec[];
|
|
|
1148
1506
|
/** Lookup by saver id. */
|
|
1149
1507
|
declare const EXAMPLE_BY_ID: Readonly<Record<string, SaverSpec>>;
|
|
1150
1508
|
|
|
1151
|
-
export { AQUARIUM_SPEC, AURORA_SPEC, type BackgroundDrift, type BackgroundSpec, type BandSpec, COMETS_SPEC, CONSTELLATION_SPEC, type CycleSpec, DASHBOARD_SPEC, type DominanceEntry, EXAMPLE_BY_ID, EXAMPLE_SPECS, type Entity, type GradientStop, HAIKU_SPEC, type IdleSequence, LANTERNS_SPEC, LIMITS, type LayerMotionStats, type LayerSnapshot, type LayerSpec, type LinkEdge, type LuminanceGrid, type LuminanceGridOptions, MATRIX_RAIN_SPEC, type MotionSpec, NOSTALGHIA_CANDLE_SPEC, ORRERY_SPEC, POLYGONS_SPEC, PROCESSION_SPEC, type PerceiveOptions, type Placed, RAIN_SPEC, type ResolvedSegment, type RevealState, SAKURA_SPEC, SCHEMA_EXAMPLES, SCHEMA_VERSION, SNOWFALL_SPEC, type SaverSpec, type SceneDescription, type SceneDiff, type ScenePerception, type SceneSnapshot, type SchemaExample, type SequenceSegment, type SequenceTransition, type SpecError, type SpecWarning, type SpriteSpec, type SteerDelta, type TextBlockLine, type TextRevealInput, type TextRevealSpec, type TextSpriteInfo, type ValidationResult, WARP_NEAR, WARP_TUNNEL_SPEC, type WanderOsc, adviseSequence, adviseSpec, alphaAt, applyDeltasToSpec, assertValidSequence, assertValidSpec, breakTextBlock, buildEntities, compileSaver, compileSequence, describeScene, diffScenes, dominanceRanking, easeSmooth, graphemeClusters, headingAt, lerpSpec, lifeAlphaAt, linkEdges, linkPairs, luminanceGrid, manifestFor, motionStats, perceiveScene, positionAt, renderBrailleMap, renderDensityMap, resolveSegment, resolveSpecPath, revealState, rotationAt, sizeAt, spriteIndexAt, spriteVariants, steerablePaths, structuralSignature, textSprites, validateSequence, validateSpec };
|
|
1509
|
+
export { AQUARIUM_SPEC, AURORA_SPEC, type BackgroundDrift, type BackgroundSpec, type BandSpec, COHESION_T, COMETS_SPEC, CONSTELLATION_SPEC, type CohesionRead, type CycleSpec, DASHBOARD_SPEC, type DominanceEntry, EXAMPLE_BY_ID, EXAMPLE_SPECS, type Entity, FACETS_SPEC, type GradientStop, HAIKU_SPEC, type IdleSequence, LANTERNS_SPEC, LIMITS, type LayerCohesion, type LayerMotionStats, type LayerSnapshot, type LayerSpec, type LinkEdge, type LuminanceGrid, type LuminanceGridOptions, MATRIX_RAIN_SPEC, type MotionEase, type MotionSpec, NOSTALGHIA_CANDLE_SPEC, ORRERY_SPEC, PINGS_SPEC, POLYGONS_SPEC, PROCESSION_SPEC, type PerceiveOptions, type Placed, RAIN_SPEC, RELAY_BOARD_SPEC, type ResolveOptions, type ResolvedSegment, type RevealState, SAKURA_SPEC, SCHEMA_EXAMPLES, SCHEMA_VERSION, SNOWFALL_SPEC, type SaverSpec, type SceneDescription, type SceneDiff, type ScenePerception, type SceneSnapshot, type SchemaExample, type SequenceSegment, type SequenceTransition, type SpecError, type SpecWarning, type SpriteSpec, type SteerDelta, type TextBlockLine, type TextRevealInput, type TextRevealSpec, type TextSpriteInfo, type ValidationResult, WARP_NEAR, WARP_TUNNEL_SPEC, type WanderOsc, adviseSequence, adviseSpec, alphaAt, applyDeltasToSpec, assertValidSequence, assertValidSpec, breakTextBlock, buildEntities, cohesionOf, compileSaver, compileSequence, describeScene, diffScenes, dominanceRanking, easeSmooth, emitEnvelope, emitWindow, graphemeClusters, headingAt, layerCohesion, lerpSpec, lifeAlphaAt, linkEdges, linkPairs, luminanceGrid, manifestFor, motionStats, perceiveScene, positionAt, renderBrailleMap, renderDensityMap, resolveSegment, resolveSpecPath, revealState, rotationAt, seamCauseOf, seamsWorthWarning, segmentStart, sizeAt, spriteIndexAt, spriteVariants, steerablePaths, structuralSignature, textSprites, textWidthEm, validateSequence, validateSpec };
|