@idle-screens/schema 3.2.0 → 3.4.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 CHANGED
@@ -31,7 +31,8 @@ Additions to version 1 so far, all optional and backward compatible:
31
31
  sprites, `colorWeights`, `pulse.wave`, `layout` (grid), `life`,
32
32
  `links.mode/falloff/closed`, `blend: screen|multiply`, orbit layer-parents
33
33
  (2026-07-21 — "the v1 ceiling");
34
- `textBlock` sprite — deterministic multi-line text with viewport-unit sizing.
34
+ `textBlock` sprite — deterministic multi-line text with viewport-unit sizing;
35
+ `textBlock.reveal` — animated typing/deleting via one steerable paint param.
35
36
 
36
37
  ## Safety invariants
37
38
 
@@ -169,6 +170,25 @@ with distance, removing pop-in at the cutoff.
169
170
  layout target, not a hard clip.
170
171
  Use with `count: 1`, `motion: { type: "static" }`, and `position`.
171
172
 
173
+ textBlock also accepts `reveal` — animated typing/deleting:
174
+ `{ "reveal": { "progress": 1, "mode": "typewriter", "speed": 0, "caret": true } }`.
175
+ Layout always runs on the **full** text; reveal only masks which glyphs are
176
+ painted, so lines never reflow and the block never resizes while typing.
177
+ `progress` (0–1, default 1) is the frontier — it is a numeric **paint**
178
+ param, so steering it with `dur`/`ease` animates typing in a single
179
+ `setParam` call, and gliding it back toward 0 deletes. `mode` quantizes the
180
+ frontier per grapheme (`typewriter`, default), per `word`, or per `line`.
181
+ `speed` (graphemes/sec, max 120) makes the block type itself from scene
182
+ start: effective progress is `min(progress, speed·t/total)`, so a steered
183
+ `progress` can still hold or delete a self-typing block. `caret` draws a
184
+ blinking caret at the frontier (`true`, or `{ "blink": 1.2, "color": "#fff" }`;
185
+ blink is in full cycles/sec, capped at 3 for flash safety, and is a square
186
+ wave of `t` like every other animation). With `align: "center"`/`"right"`
187
+ the revealing line stays anchored to its alignment as it types — only
188
+ left-aligned text reads as a classic typewriter. To "edit" text live:
189
+ glide `reveal.progress` to 0, swap `text` while nothing is visible, glide
190
+ back to 1.
191
+
172
192
  `circle`, `ring`, `streak`, and `rect` all accept `colors: [...]` (seeded
173
193
  per-entity palette pick) and `colorWeights: [...]` (relative weights, same
174
194
  length — "mostly cool tones, occasional ember").
@@ -313,8 +333,14 @@ the final segment may omit duration (holds indefinitely).
313
333
  **Advance mode:** `auto` (default), `input`, or `either`. Validated but not
314
334
  wired to runtime behavior — timer/input drivers are planned for a follow-up.
315
335
 
316
- **Transitions:** `{ type: 'cut' }` is the only supported transition. `fade` is
317
- defined as a type but rejected at validation.
336
+ **Transitions:** `{ type: 'cut' }` (default) performs a hard switch.
337
+ `{ type: 'morph', dur: number }` smoothly interpolates paint properties
338
+ (colors, opacity values) over `dur` ms when crossing into the next segment.
339
+ Morph requires structurally identical adjacent segments (same
340
+ `structuralSignature`); if they differ, the engine falls back to cut and the
341
+ validator emits a `morph-structural-mismatch` warning. During a morph, entity
342
+ placement inherits the outgoing segment's seed — the incoming segment's own
343
+ seed is unused. `dur` must be between 200 and 5000 ms.
318
344
 
319
345
  **Time mapping:** global clock `T` maps to `(segmentIndex, localT)` via prefix
320
346
  sums of durations. Half-open segments: `[start, start+duration)`. With
package/dist/index.d.ts CHANGED
@@ -274,7 +274,42 @@ type SpriteSpec = {
274
274
  lineHeight?: number;
275
275
  align?: 'left' | 'center' | 'right';
276
276
  color?: string;
277
+ reveal?: TextRevealSpec;
277
278
  };
279
+ /**
280
+ * Animated typing/deleting for `textBlock`. Layout always runs on the FULL
281
+ * text — reveal only masks which glyphs are painted, so lines never reflow
282
+ * and the block's box never changes size. `progress` is a numeric paint
283
+ * param (excluded from the structural signature), so steering it with
284
+ * `dur`/`ease` animates typing client-side in one call; gliding it back to 0
285
+ * deletes. With center/right `align`, the revealing line stays anchored to
286
+ * its alignment as it types (only left-aligned text reads as a classic
287
+ * typewriter).
288
+ */
289
+ interface TextRevealSpec {
290
+ /**
291
+ * 0..1 fraction of the text revealed (default 1 = fully shown). Quantized
292
+ * by `mode`. Steerable paint — glide it to animate typing or deleting.
293
+ */
294
+ progress?: number;
295
+ /** Reveal granularity: per-grapheme (default), per-word, or per-line. */
296
+ mode?: 'typewriter' | 'word' | 'line';
297
+ /**
298
+ * Self-typing rate in graphemes/second from scene start (media time). The
299
+ * effective progress is `min(progress, speed·t/total)` — so a steered
300
+ * `progress` can hold or delete even while `speed` is set. 0 = off.
301
+ */
302
+ speed?: number;
303
+ /**
304
+ * Caret at the reveal frontier. `true` for defaults, or configure blink
305
+ * rate (Hz, full cycles/sec, capped at 3 for flash safety) and color.
306
+ * Blink is a square wave of `t` — deterministic like everything else.
307
+ */
308
+ caret?: boolean | {
309
+ blink?: number;
310
+ color?: string;
311
+ };
312
+ }
278
313
  /** Rotate through sprite variants over time. Each entity offsets by its seeded phase. */
279
314
  interface CycleSpec {
280
315
  period: number;
@@ -426,14 +461,19 @@ declare const LIMITS: {
426
461
  readonly minTextBlockFontSize: 0.01;
427
462
  readonly maxTextBlockFontSize: 0.2;
428
463
  readonly maxTextBlockMaxWidth: 1;
464
+ readonly maxRevealSpeed: 120;
465
+ readonly maxCaretBlinkHz: 3;
429
466
  readonly maxSegments: 24;
430
467
  readonly minSegmentDuration: 1000;
431
468
  readonly maxTransitionDur: 5000;
432
469
  readonly minTransitionDur: 200;
433
470
  };
434
- interface SequenceTransition {
471
+ type SequenceTransition = {
435
472
  type: 'cut';
436
- }
473
+ } | {
474
+ type: 'morph';
475
+ dur: number;
476
+ };
437
477
  interface SequenceSegment {
438
478
  key: string;
439
479
  scene: SaverSpec;
@@ -614,6 +654,36 @@ interface TextBlockLine {
614
654
  * Explicit `\n` always forces a break. Pure, deterministic, no canvas needed.
615
655
  */
616
656
  declare function breakTextBlock(text: string, maxWidthEm: number): TextBlockLine[];
657
+ /**
658
+ * Split a string into paint-safe grapheme clusters: surrogate pairs stay
659
+ * whole, combining marks / variation selectors / skin tones attach to their
660
+ * base, ZWJ joins sequences (emoji families), regional indicators pair up
661
+ * (flags). Deterministic across engines by construction.
662
+ */
663
+ declare function graphemeClusters(text: string): string[];
664
+ interface TextRevealInput {
665
+ progress?: number;
666
+ mode?: 'typewriter' | 'word' | 'line';
667
+ speed?: number;
668
+ }
669
+ interface RevealState {
670
+ /** Effective 0..1 progress after combining authored progress and speed at t. */
671
+ progress: number;
672
+ /** Number of fully visible lines. */
673
+ fullLines: number;
674
+ /** Grapheme-safe visible prefix of the next line ('' when none). */
675
+ partialText: string;
676
+ /** Caret anchor: line index and the visible prefix on that line. */
677
+ caretLine: number;
678
+ caretPrefix: string;
679
+ }
680
+ /**
681
+ * Resolve which glyphs of a broken textBlock are visible at time `tMs`.
682
+ * Pure function of (lines, reveal, tMs) — the renderer paints exactly this,
683
+ * and perceive models exactly this, so agents and pixels agree. `speed` is
684
+ * graphemes/sec regardless of mode; mode only quantizes the frontier.
685
+ */
686
+ declare function revealState(lines: TextBlockLine[], reveal: TextRevealInput, tMs: number): RevealState;
617
687
 
618
688
  /** Derive a manifest so a compiled spec composes with @idle-screens/capabilities. */
619
689
  declare function manifestFor(spec: SaverSpec): SaverManifest;
@@ -681,6 +751,14 @@ declare function adviseSpec(spec: SaverSpec, viewport?: {
681
751
  width: number;
682
752
  height: number;
683
753
  }): SpecWarning[];
754
+ /**
755
+ * Non-blocking advisory warnings for a validated sequence envelope.
756
+ * Checks cross-segment luminance jumps at boundaries and per-segment advisories.
757
+ */
758
+ declare function adviseSequence(seq: IdleSequence, viewport?: {
759
+ width: number;
760
+ height: number;
761
+ }): SpecWarning[];
684
762
 
685
763
  interface LayerSnapshot {
686
764
  key: string | undefined;
@@ -795,6 +873,8 @@ interface TextSpriteInfo {
795
873
  count: number;
796
874
  /** Representative rendered glyph height in px at the sample viewport. */
797
875
  sizePx: number;
876
+ /** For a revealing textBlock: effective 0..1 fraction shown at sample t. */
877
+ revealed?: number;
798
878
  }
799
879
  /**
800
880
  * List every text layer's actual strings and rendered size. No grid resolution
@@ -1075,4 +1155,4 @@ declare const EXAMPLE_SPECS: SaverSpec[];
1075
1155
  /** Lookup by saver id. */
1076
1156
  declare const EXAMPLE_BY_ID: Readonly<Record<string, SaverSpec>>;
1077
1157
 
1078
- 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, 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 TextSpriteInfo, type ValidationResult, WARP_NEAR, WARP_TUNNEL_SPEC, type WanderOsc, adviseSpec, alphaAt, applyDeltasToSpec, assertValidSequence, assertValidSpec, breakTextBlock, buildEntities, compileSaver, compileSequence, describeScene, diffScenes, dominanceRanking, easeSmooth, headingAt, lerpSpec, lifeAlphaAt, linkEdges, linkPairs, luminanceGrid, manifestFor, motionStats, perceiveScene, positionAt, renderBrailleMap, renderDensityMap, resolveSegment, resolveSpecPath, rotationAt, sizeAt, spriteIndexAt, spriteVariants, steerablePaths, structuralSignature, textSprites, validateSequence, validateSpec };
1158
+ 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 };