@vosjs/cli 0.53.0 → 0.53.2

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.
@@ -0,0 +1,591 @@
1
+ <!-- Generated by scripts/build-element-reference.mjs from @vosjs/core 0.25.1, @vosjs/elements 0.8.2 and @vosjs/timeline 0.4.1. Do not edit by hand: re-run the script. -->
2
+
3
+ # Elements, the context and eases: the declarations
4
+
5
+ What a program's config and functions accept, copied from the published
6
+ type declarations so a port never has to read `node_modules`. The notes
7
+ between the blocks are the facts the declarations do not say; the units
8
+ are in SKILL.md's "coordinates, measured" and the draw order in its
9
+ "Layering".
10
+
11
+ ## The config
12
+
13
+ A `config.json` is this shape, functions as strings. `elements` is typed
14
+ loosely here; its members are the element types below.
15
+
16
+ ```ts
17
+ interface FontFaceDecl {
18
+ /** Family name as used in `font.family` / canvas font strings. */
19
+ family: string;
20
+ /** Font file URL (woff2/woff/ttf). Must be reachable from render pages. */
21
+ url: string;
22
+ /** CSS weight the file carries (default 'normal'). One decl per weight. */
23
+ weight?: number | string;
24
+ style?: 'normal' | 'italic';
25
+ }
26
+
27
+ /**
28
+ * JSON-serializable version of VosConfig.
29
+ * Functions are stored as strings that can be embedded in the compiled template.
30
+ *
31
+ * Note: Elements are typed loosely as Record<string, unknown>[] for JSON
32
+ * transport compatibility. The actual ElementConfig type validation happens
33
+ * at compile time in compileVosConfig.
34
+ */
35
+ interface VosConfigJson {
36
+ /**
37
+ * Which schema era this config was written against.
38
+ *
39
+ * Required, because this is the CANONICAL shape: what is stored, served
40
+ * and read back later, when nobody can tell the era from the file itself.
41
+ * `migrateConfig` stamps it, so the type you hand a storer always has one.
42
+ * The authoring shape that may omit it is `AuthoredVosConfigJson`.
43
+ */
44
+ version: number;
45
+ /**
46
+ * Total duration of one animation cycle in seconds.
47
+ */
48
+ duration: number;
49
+ /** Scene configuration (background, fog) */
50
+ scene?: SceneConfig;
51
+ /** Camera configuration */
52
+ camera: CameraConfig;
53
+ /** Post-processing effects */
54
+ postprocessing?: PostprocessingEffect[];
55
+ /** Declare per-layer effect types for addon imports */
56
+ perLayerEffects?: PostprocessingEffect[];
57
+ /** Enable per-frame render group rebuild when zIndex changes at runtime */
58
+ dynamicLayers?: boolean;
59
+ /** 2D Elements rendered as textured planes (loosely typed for JSON transport) */
60
+ elements?: Record<string, unknown>[];
61
+ /** Declarative world-space 3D objects (primitives / GLB). */
62
+ objects?: Record<string, unknown>[];
63
+ /**
64
+ * Webfont faces to register and load BEFORE anything rasterizes text.
65
+ * Canvas text silently falls back to a default font when a family isn't
66
+ * loaded — headless render environments have near-zero system fonts, so any
67
+ * non-generic family used by text elements (or setup-drawn canvases) should
68
+ * be declared here with a self-hosted URL. Loading is awaited capped and
69
+ * fail-open: a dead URL degrades to fallback stacks, never a hung page.
70
+ */
71
+ fonts?: FontFaceDecl[];
72
+ /**
73
+ * Arbitrary input data made available to functions as `ctx.data`.
74
+ * The shape is the author's/app's, not vos's — vos passes it through verbatim.
75
+ * Overridable at runtime via `initVos(container, deps)` `deps.data` (so a live
76
+ * editor can update data without recompiling); `config.data` is the baked default.
77
+ * @example { cursor: [{ t: 0, x: 10, y: 20, type: 'down' }] }
78
+ */
79
+ data?: Record<string, unknown>;
80
+ /**
81
+ * Async setup hook as a string.
82
+ * @example "(ctx) => { const loader = new ctx.loaders.FontLoader(); ... }"
83
+ */
84
+ setup?: string;
85
+ /**
86
+ * Create scene content function as a string.
87
+ * @example "(ctx, setupData) => { const { THREE, scene } = ctx; ... }"
88
+ */
89
+ createContent: string;
90
+ /**
91
+ * Create GSAP timeline function as a string.
92
+ * @example "(ctx, content, duration) => { const tl = ctx.gsap.timeline(); ... }"
93
+ */
94
+ createTimeline: string;
95
+ /**
96
+ * Optional per-frame update function as a string.
97
+ * @example "(ctx, content, deltaTime) => { content.refs.uniforms.iTime.value += deltaTime; }"
98
+ */
99
+ onFrame?: string;
100
+ /**
101
+ * Evaluate the program at `f(t)`, as a string: `(t, data) => number`, a
102
+ * pure function of the OUTPUT time and `ctx.data`. Slow motion, ramps,
103
+ * reverse, freeze frames, ping-pong loops. See `VosConfig.retime`.
104
+ */
105
+ retime?: string;
106
+ /**
107
+ * The program stack: more programs on this context, run after the main one
108
+ * in array order, each with its own `ctx.data` and error boundary. A HUD, a
109
+ * subtitle pass, a watermark, an overlay a remixer adds without touching the
110
+ * main program's code. No timeline of their own — one master clock.
111
+ */
112
+ stack?: ProgramEntryJson[];
113
+ }
114
+ ```
115
+
116
+ ## Elements (`config.elements[]`)
117
+
118
+ `content`, `font.family` and `font.color` of a text element are the three
119
+ fields that take a `{ "$data": "<key>" }` binding, and the only ones that
120
+ re-resolve on a data edit; every other field is read once at build.
121
+ `font.size`, `letterSpacing`, `transform.translateX/Y`, `stroke.width`
122
+ and `shadow.blur` are design pixels (a 1080-high frame). There is no
123
+ `mask`, `clip`, `blend` or group field on any element: SKILL.md's
124
+ "honest gaps" says what to do instead.
125
+
126
+ ```ts
127
+ /**
128
+ * `{$data: key}` binding — the value resolves from the host's data object at
129
+ * render time and re-resolves on setData (a pure data edit, no recompile).
130
+ */
131
+ interface DataRef {
132
+ $data: string;
133
+ }
134
+
135
+ /**
136
+ * Base interface for all element types
137
+ */
138
+ interface BaseElement {
139
+ /** Unique identifier for referencing in createTimeline */
140
+ id?: string;
141
+ type: 'text' | 'image' | 'svg' | 'video' | 'audio';
142
+ /** Position on screen */
143
+ position: ElementPosition;
144
+ /** Transform origin */
145
+ anchor?: Anchor;
146
+ /** Layer order (higher = on top, default: 100) */
147
+ zIndex?: number;
148
+ /** Opacity 0-1 */
149
+ opacity?: number;
150
+ /** 3D transform */
151
+ transform?: Transform;
152
+ }
153
+
154
+ /**
155
+ * Position can be pixels, percentages, or preset strings
156
+ */
157
+ type ElementPosition = {
158
+ x: number;
159
+ y: number;
160
+ } | {
161
+ x: string;
162
+ y: string;
163
+ } | PositionPreset;
164
+
165
+ type PositionPreset = 'center' | 'top-left' | 'top-center' | 'top-right' | 'center-left' | 'center-right' | 'bottom-left' | 'bottom-center' | 'bottom-right';
166
+
167
+ type Anchor = 'center' | 'top-left' | 'top' | 'top-right' | 'left' | 'right' | 'bottom-left' | 'bottom' | 'bottom-right';
168
+
169
+ /**
170
+ * 3D transform properties
171
+ */
172
+ interface Transform {
173
+ translateX?: number;
174
+ translateY?: number;
175
+ translateZ?: number;
176
+ rotateX?: number;
177
+ rotateY?: number;
178
+ rotateZ?: number;
179
+ rotation?: number;
180
+ scale?: number;
181
+ scaleX?: number;
182
+ scaleY?: number;
183
+ perspective?: number;
184
+ origin?: {
185
+ x: string;
186
+ y: string;
187
+ };
188
+ }
189
+
190
+ interface TextElement extends BaseElement {
191
+ type: 'text';
192
+ /** Text content (supports \n for multiline), or a `{$data}` binding */
193
+ content: string | DataRef;
194
+ font?: {
195
+ family?: string | DataRef;
196
+ size?: number;
197
+ weight?: number | string;
198
+ style?: 'normal' | 'italic';
199
+ color?: string | DataRef;
200
+ letterSpacing?: number;
201
+ lineHeight?: number;
202
+ align?: 'left' | 'center' | 'right';
203
+ };
204
+ stroke?: {
205
+ color: string;
206
+ width: number;
207
+ };
208
+ shadow?: {
209
+ color: string;
210
+ blur: number;
211
+ offsetX?: number;
212
+ offsetY?: number;
213
+ };
214
+ /**
215
+ * Split text into segments for per-character/word/line animation.
216
+ * When split is defined, the element exposes a `segments` array
217
+ * of ElementProps for animating individual parts.
218
+ */
219
+ split?: {
220
+ /** Type of split: 'chars', 'words', or 'lines' */
221
+ type: 'chars' | 'words' | 'lines';
222
+ };
223
+ }
224
+
225
+ interface ImageElement extends BaseElement {
226
+ type: 'image';
227
+ /** URL, base64, or imported asset path */
228
+ src: string;
229
+ size?: {
230
+ width?: number | 'auto';
231
+ height?: number | 'auto';
232
+ fit?: 'contain' | 'cover' | 'fill';
233
+ };
234
+ filters?: {
235
+ brightness?: number;
236
+ contrast?: number;
237
+ saturate?: number;
238
+ blur?: number;
239
+ hueRotate?: number;
240
+ grayscale?: number;
241
+ };
242
+ borderRadius?: number;
243
+ }
244
+
245
+ interface SVGElement extends BaseElement {
246
+ type: 'svg';
247
+ /** SVG string, URL, or imported asset */
248
+ src: string;
249
+ size?: {
250
+ width?: number | 'auto';
251
+ height?: number | 'auto';
252
+ };
253
+ /** Override colors in SVG */
254
+ colors?: Record<string, string>;
255
+ }
256
+
257
+ interface VideoElement extends BaseElement {
258
+ type: 'video';
259
+ src: string;
260
+ size?: {
261
+ width?: number | 'auto';
262
+ height?: number | 'auto';
263
+ fit?: 'contain' | 'cover' | 'fill';
264
+ };
265
+ loop?: boolean;
266
+ muted?: boolean;
267
+ playbackRate?: number;
268
+ startTime?: number;
269
+ /**
270
+ * Decode strategy:
271
+ * - 'html5' (default): HTMLVideoElement + VideoTexture (legacy; not frame-accurate)
272
+ * - 'webcodecs': frame-accurate WebCodecs decode (deterministic export/scrub)
273
+ * - 'auto': webcodecs when available, else html5
274
+ */
275
+ frameSource?: 'auto' | 'webcodecs' | 'html5';
276
+ }
277
+
278
+ /**
279
+ * Non-visual element that plays an audio file synced to the master clock.
280
+ * Drive it like an html5 video: set `playing` and/or animate `currentTime`
281
+ * in createTimeline; playback honors the global pause/seek state, and
282
+ * `props.gain` (0-1) is animatable for fades. Renders no pixels — position,
283
+ * anchor and transform are accepted for BaseElement compatibility but ignored.
284
+ */
285
+ interface AudioElement extends Omit<BaseElement, 'position'> {
286
+ type: 'audio';
287
+ /** Audio file URL (anything the browser's media stack decodes) */
288
+ src: string;
289
+ /** Ignored (audio renders no pixels) */
290
+ position?: ElementPosition;
291
+ /** Initial volume 0-1 (default: 1); animatable via props.gain */
292
+ gain?: number;
293
+ loop?: boolean;
294
+ /** Offset into the source when playback begins, seconds (default: 0) */
295
+ startTime?: number;
296
+ /**
297
+ * A gain envelope over OUTPUT time: `[t, gain]` points, linear between
298
+ * them, held flat outside, multiplied with `props.gain`. Fades, ducking, a
299
+ * bed that swells under a title, as data the offline renderer replays
300
+ * (`@vosjs/core/audio`) and live playback follows frame by frame.
301
+ */
302
+ gainEnvelope?: Array<[t: number, gain: number]>;
303
+ }
304
+ ```
305
+
306
+ ## An element at runtime (`ctx.elements.get(id)`)
307
+
308
+ What `createTimeline` and `onFrame` animate: `el.props` (and
309
+ `el.segments[i]` under `split`). `props.x` / `props.y` are RENDER pixels
310
+ from the frame centre with `y` down, and `rotation` is degrees
311
+ counter-clockwise, unlike the config's design-pixel `transform`.
312
+
313
+ Writing any of `content`, `fontSize`, `fontFamily`, `fontWeight`, `fontStyle`, `letterSpacing`, `color`, `strokeColor`, `strokeWidth` on a text element's `props`
314
+ re-rasters it (the runtime's own list), so a scramble or a counter writes
315
+ `el.props.content` in `onFrame` and a colour change sets `el.props.color`.
316
+ The tween dialect animates numbers only: tween `fontSize` and
317
+ `letterSpacing`, SET the strings. `props.zIndex` is settable too.
318
+
319
+ Every such write queues a re-raster, even of an unchanged value, so guard
320
+ a per-frame write: `if (el.props.content !== next) el.props.content = next`.
321
+ A `split` text element does not re-raster at all: these writes are ignored
322
+ on it, so text that changes and text that animates per letter are two
323
+ elements.
324
+
325
+ ```ts
326
+ /**
327
+ * GSAP-animatable properties exposed on each element instance
328
+ */
329
+ interface ElementProps {
330
+ x: number;
331
+ y: number;
332
+ z: number;
333
+ opacity: number;
334
+ scale: number;
335
+ scaleX: number;
336
+ scaleY: number;
337
+ rotation: number;
338
+ rotationX: number;
339
+ rotationY: number;
340
+ /** Layer order inside the element's overlay scene (writes `renderOrder`) */
341
+ zIndex?: number;
342
+ content?: string;
343
+ fontSize?: number;
344
+ fontFamily?: string;
345
+ fontWeight?: number | string;
346
+ fontStyle?: 'normal' | 'italic';
347
+ letterSpacing?: number;
348
+ color?: string;
349
+ strokeColor?: string;
350
+ strokeWidth?: number;
351
+ /** Current playback position in seconds (animatable with GSAP) */
352
+ currentTime?: number;
353
+ /** Video duration in seconds (read-only) */
354
+ readonly duration?: number;
355
+ /** Whether the video is playing (controls native playback) */
356
+ playing?: boolean;
357
+ /** Start offset for video playback */
358
+ startOffset?: number;
359
+ /** Volume 0-1 (audio elements; animatable for fades) */
360
+ gain?: number;
361
+ }
362
+
363
+ /**
364
+ * Runtime element instance with animatable props and methods
365
+ */
366
+ interface ElementInstance {
367
+ /** Original configuration */
368
+ config: ElementConfig;
369
+ /** Three.js mesh (textured plane) */
370
+ mesh: THREE.Mesh;
371
+ /** DOM node for SplitText (text elements only) - typed as unknown for portability */
372
+ node: unknown;
373
+ /** GSAP-animatable properties */
374
+ props: ElementProps;
375
+ /**
376
+ * Split text segments (only available when split config is defined).
377
+ * Each segment has its own ElementProps for individual animation.
378
+ */
379
+ segments?: ElementProps[];
380
+ /** Update element content (text or image src) */
381
+ setContent: (content: string) => void;
382
+ /**
383
+ * Re-resolve `{$data}`-bound props against fresh data (called by the
384
+ * compiled module's setData). Returns true when a change was picked up.
385
+ */
386
+ updateData?: (data: Record<string, unknown> | null | undefined) => boolean;
387
+ /**
388
+ * Re-raster with unchanged values — the late-webfont hook (a data-carried
389
+ * face landing after first paint re-draws over the fallback stack).
390
+ */
391
+ refreshRaster?: () => boolean;
392
+ /** Re-rasterize canvas-backed textures for a new output resolution */
393
+ updateResolution?: (resolution: unknown) => boolean;
394
+ /** Remove element from scene */
395
+ destroy: () => void;
396
+ }
397
+ ```
398
+
399
+ ## The context (`ctx`)
400
+
401
+ `setup(ctx)` gets a `SetupContext` and may return assets;
402
+ `createContent(ctx, setupData)` returns a `ContentResult`;
403
+ `createTimeline(ctx, content, duration)` returns the timeline;
404
+ `onFrame(ctx, content, deltaTime)` runs every frame, `ctx.time` being the
405
+ program's time in seconds. `ctx.data` is read-only: a knob changes it from outside,
406
+ and `onFrame` reads the new value on the next frame. Read `ctx.data` on
407
+ every frame, never a copy taken in `createContent`.
408
+
409
+ ```ts
410
+ /**
411
+ * Resolution configuration passed to animations
412
+ */
413
+ interface Resolution {
414
+ width: number;
415
+ height: number;
416
+ pixelRatio: number;
417
+ /** Physical drawing buffer width (width × pixelRatio) - for shader uniforms */
418
+ drawingBufferWidth: number;
419
+ /** Physical drawing buffer height (height × pixelRatio) - for shader uniforms */
420
+ drawingBufferHeight: number;
421
+ }
422
+
423
+ /**
424
+ * Loaders registry - common Three.js loaders
425
+ */
426
+ interface LoadersRegistry {
427
+ FontLoader: any;
428
+ TextureLoader: any;
429
+ GLTFLoader: any;
430
+ HDRLoader: any;
431
+ CubeTextureLoader: any;
432
+ }
433
+
434
+ /**
435
+ * Utilities registry - common Three.js utilities
436
+ */
437
+ interface UtilsRegistry {
438
+ MeshSurfaceSampler: any;
439
+ BufferGeometryUtils: any;
440
+ TextGeometry: any;
441
+ }
442
+
443
+ /**
444
+ * Context available during async setup phase
445
+ */
446
+ interface SetupContext {
447
+ THREE: typeof THREE;
448
+ resolution: Resolution;
449
+ loaders: LoadersRegistry;
450
+ utils: UtilsRegistry;
451
+ /**
452
+ * Read-only input data exposed to all functions as `ctx.data`.
453
+ * Sourced from `config.data`, overridable at runtime by `initVos` `deps.data`.
454
+ * Always defined (defaults to `{}`). Shape is the author's/app's, not vos's.
455
+ */
456
+ data: Readonly<Record<string, unknown>>;
457
+ }
458
+
459
+ /**
460
+ * Context available during animation creation
461
+ */
462
+ interface VosContext extends SetupContext {
463
+ gsap: typeof gsap;
464
+ scene: THREE.Scene;
465
+ camera: THREE.Camera;
466
+ renderer: THREE.WebGLRenderer;
467
+ /** Dedicated scene for 2D overlay elements (rendered on top of main scene) */
468
+ overlayScene: THREE.Scene;
469
+ /** Orthographic camera for 2D overlay (pixel-space: 1 unit = 1 pixel) */
470
+ overlayCamera: THREE.OrthographicCamera;
471
+ composer?: unknown;
472
+ /** Element instances for timeline animations */
473
+ elements: Map<string, ElementInstance>;
474
+ /** Current playback time in seconds (available in onFrame) */
475
+ time: number;
476
+ /** Playback progress 0-1 (available in onFrame) */
477
+ progress: number;
478
+ /**
479
+ * The OUTPUT time in seconds: what the transport shows and the capture
480
+ * counts. Equal to `time` unless the config carries a `retime`, in which
481
+ * case `time` is `retime(outputTime, data)` — the program's own time —
482
+ * while `outputTime` keeps counting the output. Stack entries read
483
+ * `ctx.time` as output time (they are output-anchored by contract).
484
+ */
485
+ outputTime: number;
486
+ }
487
+
488
+ /**
489
+ * Result from createContent function
490
+ */
491
+ interface ContentResult {
492
+ /** Objects added to scene */
493
+ objects: THREE.Object3D[];
494
+ /** Named references for timeline animations */
495
+ refs?: Record<string, unknown>;
496
+ /** Cleanup function for content-specific resources */
497
+ dispose?: () => void;
498
+ }
499
+ ```
500
+
501
+ ## The timeline (what `createTimeline` returns)
502
+
503
+ Author it as GSAP: `const tl = ctx.gsap.timeline({ paused: true })`, tweens
504
+ on `el.props`, `tl.addLabel(name, t)` per scene, return `tl`. This is the
505
+ whole surface the engine calls on it.
506
+
507
+ ```ts
508
+ /**
509
+ * Structural master-clock interface the engine seeks each frame.
510
+ *
511
+ * This is the ONLY surface the runtime uses from the timeline object returned by
512
+ * `createTimeline` (pause/seek/play + transport queries + carrier retiming). It is
513
+ * satisfied structurally by `gsap.core.Timeline` today, so authoring against real
514
+ * GSAP is unchanged — but the public API no longer hard-depends on the `gsap` type,
515
+ * which lets an alternate deterministic backend provide a conformant timeline later
516
+ * without a breaking change. Method shorthand is intentional (bivariant params) so
517
+ * GSAP's overloaded signatures remain structurally assignable.
518
+ */
519
+ interface VosTimeline {
520
+ /** Pause playback (frame-stepped export pauses before the first frame). */
521
+ pause(): unknown;
522
+ /** Resume playback. */
523
+ play(): unknown;
524
+ /** Seek to `time` seconds. `suppressEvents=false` fires onUpdate callbacks. */
525
+ seek(time: number, suppressEvents?: boolean): unknown;
526
+ /** Rebuild/clear children (used by the vosCarrier duration-retime path). */
527
+ clear(): unknown;
528
+ /** Set playback rate. */
529
+ timeScale(value: number): unknown;
530
+ /** Current playhead in seconds. */
531
+ time(): number;
532
+ /** Normalized progress 0..1. */
533
+ progress(): number;
534
+ /**
535
+ * Optional (the vos tween backend): re-apply a tween-timing overlay over the
536
+ * recorded tweens, from the recording every time. The bridge's
537
+ * SET_TWEEN_EDITS rides it; absent on a gsap timeline.
538
+ */
539
+ applyEdits?(edits: readonly Record<string, unknown>[]): unknown;
540
+ /** Configured duration in seconds. */
541
+ duration(): number;
542
+ /** Total duration including repeats (seconds). */
543
+ totalDuration(): number;
544
+ /** Attach/read a lifecycle callback (engine uses onUpdate). */
545
+ eventCallback(type: string, callback?: (...args: any[]) => void): unknown;
546
+ /** Opaque author-attached marker (e.g. `{ vosCarrier: true }`). */
547
+ data?: unknown;
548
+ }
549
+ ```
550
+
551
+ ## Eases
552
+
553
+ An `ease` string anywhere GSAP takes one. Beyond the names below the
554
+ engine accepts the parameterized forms `back.out(1.7)`,
555
+ `elastic.out(1, 0.3)`, `steps(5)`, and `css-bezier(x1, y1, x2, y2)`,
556
+ which is CSS's `cubic-bezier` and Remotion's `Easing.bezier` with the same
557
+ four numbers (`cubic-bezier` itself is not a name). A bare family
558
+ (`'power2'`) means `.out`. **An unknown name is not an error: it plays
559
+ linear.** `vos check` warns `unknown-ease` for a literal `ease: '…'` it
560
+ cannot resolve; through `@vosjs/core` 0.25.0 that warning also fired on
561
+ `css-bezier`, wrongly, so ignore it there. An ease held in a variable is
562
+ not checked: the side-by-side is where it shows.
563
+
564
+ ```ts
565
+ /**
566
+ * Value types for deterministic timeline math.
567
+ *
568
+ * These are meant to be EMBEDDED inside an app's own document schema — @vosjs/timeline
569
+ * is an evaluation library, not a document format. Everything is JSON-serializable
570
+ * (eases are registry names, never functions) so the same values travel through
571
+ * `ctx.data` into a running vos program and evaluate identically on both sides.
572
+ */
573
+ type EaseFamily = 'power1' | 'power2' | 'power3' | 'power4' | 'sine' | 'expo' | 'circ' | 'back' | 'elastic' | 'bounce';
574
+
575
+ type EaseDirection = 'in' | 'out' | 'inOut';
576
+
577
+ /**
578
+ * Serializable easing name. The vocabulary (and the curves) match GSAP's so one
579
+ * ease language spans freeform function-strings and declarative keyframes.
580
+ */
581
+ type EaseName = 'none' | 'linear' | `${EaseFamily}.${EaseDirection}`;
582
+
583
+ /**
584
+ * Resolve an ease by name, including parameterized forms (`back.out(1.7)`,
585
+ * `elastic.out(1, 0.3)`, `steps(5)`) and GSAP's bare-family default
586
+ * (`'power2'` → `power2.out`). Unknown names fall back to linear — evaluation
587
+ * must never throw per-frame inside a running program; authoring layers are
588
+ * expected to validate names at edit time instead.
589
+ */
590
+ function resolveEase(name: string | undefined): EaseFn;
591
+ ```
@@ -21,6 +21,7 @@ units (render pixels, y down, degrees counter-clockwise).
21
21
  | a sub-composition or scene `div` with a time window | `tl.addLabel(name, t)` and its elements tweened in and out inside the window |
22
22
  | an `onUpdate` proxy clock that draws (`renderAll(t)`) | `onFrame(ctx)`, reading `ctx.time`, for exactly those procedural parts |
23
23
  | GSAP ease names (`power3.out`, `back.out(1.7)`, `expo.inOut`) | the same names |
24
+ | a CSS `cubic-bezier(a, b, c, d)` timing | `ease: 'css-bezier(a, b, c, d)'` (exact; spelled `cubic-bezier` it plays linear) |
24
25
  | CSS `--chrome` set by the timeline | a `data` value read in `onFrame`, or a tween on the element that shows it |
25
26
  | faces resolved by the runtime from CSS families | `fonts: [{ family, weight, url }]` from the catalog |
26
27
  | `mix-blend-mode`, `clip-path`, `overflow: hidden` reveals | GAPS: the painter, or an opacity approximation, said |