@glissade/core 0.8.1 → 0.9.0-pre.1

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/dist/index.d.ts CHANGED
@@ -1,4 +1,7 @@
1
+ import { $ as validateTrack, A as GainEnvelope, At as pathType, B as emitDevWarning, C as resolveTweenTarget, Ct as ValueTypeInferenceError, D as ChildEntry, Dt as getValueType, E as AudioClip, Et as colorType, F as TimelineValidationError, Ft as vec2Type, G as TrackValidationError, H as Key, I as audioOffsetSamples, J as resolveEaseDerivative, K as key, L as compileTimeline, M as Marker, Mt as stringType, N as Timeline, Nt as vec2ArcType, O as CompiledTimeline, Ot as inferValueType, P as TimelineInit, Pt as vec2Equals, Q as track, R as isDurationEditable, S as isEditableNodeId, St as ValueTypeId, T as AssetRef, Tt as booleanType, U as KeyOpts, V as setDevWarning, W as Track, X as springTo, Y as sampleTrack, Z as stagger, _ as setSidecarTrack, _t as HandoffKind, a as SidecarOrphan, at as springEasing, b as TweenTarget, bt as UnknownValueTypeError, c as SidecarVersionError, ct as DEFAULT_EASE, d as emptySidecar, dt as UnknownEasingError, et as velocityAt, f as hashKeys, ft as cubicBezier, g as normalizeEditedKeys, gt as namedEasing, h as migrateSidecar, ht as easings, i as SidecarDocV1, it as spring, j as Json, jt as registerValueType, k as FontFaceRef, kt as numberType, l as assignKeyIds, lt as EaseSpec, m as mergeSidecarDetailed, mt as easingDerivatives, n as OrphanReason, nt as SpringConfig, o as SidecarTimelineEntry, ot as springEasingDerivative, p as mergeSidecar, pt as cubicBezierDerivative, q as resolveEase, r as SidecarDoc, rt as SpringEase, s as SidecarTrackEntry, st as springPresets, t as MergeResult, tt as RetargetSpring, u as deleteSidecarTrack, ut as EasingFn, v as TARGET_PATH, vt as PathContour, w as targetNodeId, wt as Vec2, x as UnresolvableTargetError, xt as ValueType, y as TargetCarrier, yt as PathValue, z as DevWarning } from "./sidecar.js";
2
+
1
3
  //#region src/signal.d.ts
4
+
2
5
  /**
3
6
  * Pull-based reactive signals: lazy, cached, dependency-tracked (DESIGN.md §2.1).
4
7
  *
@@ -42,69 +45,6 @@ declare function computed<T>(fn: () => T, options?: SignalOptions<T>): ReadonlyS
42
45
  /** Run `fn` without registering dependencies on the active consumer. */
43
46
  declare function untracked<T>(fn: () => T): T;
44
47
  //#endregion
45
- //#region src/valueTypes.d.ts
46
- /**
47
- * Value-type registry with pluggable per-type interpolation (DESIGN.md §2.2).
48
- * `extrapolates` declares whether a type's lerp accepts easedT outside [0,1]
49
- * (spring overshoot); non-extrapolating types clamp.
50
- */
51
- type Vec2 = readonly [number, number];
52
- /** One bezier contour in Lottie's vertex form: anchor points + RELATIVE in/out tangents. */
53
- interface PathContour {
54
- closed: boolean;
55
- v: Vec2[];
56
- in: Vec2[];
57
- out: Vec2[];
58
- }
59
- /** The 'path' document value (§2.2): plain JSON, serializes with no new hooks. */
60
- type PathValue = PathContour[];
61
- /** Transition handoff policies (v2 addendum §A.4/§B.1); 'crossfade' reserved. */
62
- type HandoffKind = 'cut' | 'decay' | 'spring' | 'blend-from-frozen';
63
- interface ValueType<T> {
64
- id: string;
65
- lerp(a: T, b: T, t: number): T;
66
- /** Accepts easedT outside [0,1] (spring overshoot)? Otherwise clamped. */
67
- extrapolates: boolean;
68
- equals(a: T, b: T): boolean;
69
- /** Optional linear-space operators (offset decay + reserved additive blending, §B.6). */
70
- add?(a: T, b: T): T;
71
- sub?(a: T, b: T): T;
72
- scale?(a: T, k: number): T;
73
- /** Type-class handoff default (§B.1): spring for kinetic, cut for hold-only. */
74
- defaultHandoff?: HandoffKind;
75
- /** Document (de)serialization; default identity for JSON-native types (§2.2). */
76
- serialize?(value: T): unknown;
77
- deserialize?(raw: unknown): T;
78
- }
79
- type ValueTypeId = 'number' | 'vec2' | 'color' | 'string' | 'boolean' | (string & {});
80
- declare function registerValueType<T>(vt: ValueType<T>): void;
81
- declare class UnknownValueTypeError extends Error {
82
- constructor(id: string);
83
- }
84
- declare function getValueType<T = unknown>(id: ValueTypeId): ValueType<T>;
85
- declare const numberType: ValueType<number>;
86
- declare const vec2Equals: (a: Vec2, b: Vec2) => boolean;
87
- declare const vec2Type: ValueType<Vec2>;
88
- /** vec2 swept along a circular arc: polar lerp of radius + shortest-path angle (§2.2). */
89
- declare const vec2ArcType: ValueType<Vec2>;
90
- declare const colorType: ValueType<string>;
91
- declare const stringType: ValueType<string>;
92
- declare const booleanType: ValueType<boolean>;
93
- /**
94
- * Path morphing (§2.2): pairwise lerp of anchors and tangents — exactly how
95
- * lottie-web morphs, so imported animations are pixel-faithful. Mismatched
96
- * topology snaps (hold a, then b at t ≥ 1) with a one-time dev warning; the
97
- * de Casteljau normalization fallback for arbitrary native morphs is tracked
98
- * future work. Lerp-only: offsets are not well-defined under mismatched
99
- * topology, so no add/sub/scale — handoffs blend from the frozen value.
100
- */
101
- declare const pathType: ValueType<PathValue>;
102
- declare class ValueTypeInferenceError extends Error {
103
- constructor(value: unknown);
104
- }
105
- /** Infer a registered type id from a sample value (builder + bake authoring surfaces). */
106
- declare function inferValueType(value: unknown): ValueTypeId;
107
- //#endregion
108
48
  //#region src/vec2Signal.d.ts
109
49
  interface Vec2Signal extends ReadonlySignal<Vec2> {
110
50
  readonly x: BindableSignal<number>;
@@ -119,133 +59,6 @@ declare function vec2Signal(initial: Vec2 | {
119
59
  y: number;
120
60
  }): Vec2Signal;
121
61
  //#endregion
122
- //#region src/easing.d.ts
123
- /**
124
- * Easing registry + cubic bézier (DESIGN.md §2.2). All functions map [0,1]→ℝ
125
- * with f(0)=0 and f(1)=1; back/elastic intentionally leave [0,1].
126
- */
127
- type EasingFn = (t: number) => number;
128
- type EaseSpec = string | {
129
- kind: 'cubicBezier';
130
- pts: [number, number, number, number];
131
- } | {
132
- kind: 'spring';
133
- stiffness: number;
134
- damping: number;
135
- mass: number;
136
- };
137
- declare const easings: Record<string, EasingFn>;
138
- /**
139
- * Analytic derivatives d(u) of every named ease (§B.6) — closed-form, used
140
- * for reading velocity off in-flight curves at interruption time. Property-
141
- * tested against central differences at interior points.
142
- */
143
- declare const easingDerivatives: Record<string, EasingFn>;
144
- /** Default property-tween ease (Motion Canvas precedent). */
145
- declare const DEFAULT_EASE = "easeInOutCubic";
146
- /**
147
- * CSS-style cubic bézier where x is time and y is progress. Newton's method
148
- * with a bisection fallback for the flat-derivative regions.
149
- */
150
- declare function cubicBezier(p1x: number, p1y: number, p2x: number, p2y: number): EasingFn;
151
- /** Analytic dy/dx of a cubic bézier ease: y'(s)/x'(s) at the solved parameter (§B.6). */
152
- declare function cubicBezierDerivative(p1x: number, p1y: number, p2x: number, p2y: number): EasingFn;
153
- declare class UnknownEasingError extends Error {
154
- constructor(name: string);
155
- }
156
- declare function namedEasing(name: string): EasingFn;
157
- //#endregion
158
- //#region src/spring.d.ts
159
- interface SpringConfig {
160
- stiffness: number;
161
- damping: number;
162
- mass?: number;
163
- }
164
- interface SpringEase {
165
- kind: 'spring';
166
- stiffness: number;
167
- damping: number;
168
- mass: number;
169
- }
170
- /**
171
- * Settle duration: the earliest time after which |x - 1| stays within
172
- * settleTolerance. Closed-form via the decay envelope for the underdamped
173
- * case; bisection on the monotone tail otherwise. Deterministic.
174
- */
175
- declare function duration(cfg: SpringConfig, opts?: {
176
- settleTolerance?: number;
177
- }): number;
178
- /**
179
- * Spring progress at local time t, affinely rescaled so value(duration) = 1
180
- * exactly — the raw form only approaches 1, and an unscaled curve would snap
181
- * at the key (§2.7 "endpoint continuity"). May exceed 1 (overshoot).
182
- */
183
- declare function value(cfg: SpringConfig, t: number, opts?: {
184
- settleTolerance?: number;
185
- }): number;
186
- /**
187
- * Velocity-matched retarget oscillator (v2 addendum §B.3): the same damped
188
- * harmonic oscillator on an OFFSET in value units with nonzero initial
189
- * velocity — y(0)=x0, y'(0)=v0, decaying to 0. No affine rescale (the target
190
- * is exactly 0). Pure closed forms; seek-safe at any τ.
191
- */
192
- interface RetargetSpring {
193
- value(tau: number): number;
194
- velocity(tau: number): number;
195
- /** Earliest τ after which |value| stays within tol (default 0.005·|x0|+1e-6 floor). */
196
- settleTime(tol?: number): number;
197
- }
198
- declare function retarget(cfg: SpringConfig, x0: number, v0: number): RetargetSpring;
199
- interface SpringFactory {
200
- (cfg: SpringConfig): SpringEase;
201
- duration: typeof duration;
202
- value: typeof value;
203
- retarget: typeof retarget;
204
- }
205
- declare const spring: SpringFactory;
206
- /**
207
- * Named spring feels (react-spring conventions, mass 1) — a vocabulary instead
208
- * of hand-tuned stiffness/damping. Use `spring(springPresets.wobbly)` or
209
- * `springTo(t, a, b, springPresets.gentle)`.
210
- */
211
- declare const springPresets: {
212
- readonly default: {
213
- readonly stiffness: 170;
214
- readonly damping: 26;
215
- };
216
- readonly gentle: {
217
- readonly stiffness: 120;
218
- readonly damping: 14;
219
- };
220
- readonly wobbly: {
221
- readonly stiffness: 180;
222
- readonly damping: 12;
223
- };
224
- readonly stiff: {
225
- readonly stiffness: 210;
226
- readonly damping: 20;
227
- };
228
- readonly slow: {
229
- readonly stiffness: 280;
230
- readonly damping: 60;
231
- };
232
- readonly molasses: {
233
- readonly stiffness: 280;
234
- readonly damping: 120;
235
- };
236
- };
237
- /**
238
- * The spring as a normalized easing over a segment whose length must equal
239
- * spring.duration(cfg) (validated at the document layer, §2.7).
240
- */
241
- declare function springEasing(cfg: SpringConfig): EasingFn;
242
- /**
243
- * Analytic d/dp of springEasing (§B.6): oscillator derivative × the affine
244
- * rescale factor × duration (chain rule p → t = p·D). Flat past p=1,
245
- * matching value()'s clamp (right-derivative convention).
246
- */
247
- declare function springEasingDerivative(cfg: SpringConfig): EasingFn;
248
- //#endregion
249
62
  //#region src/color.d.ts
250
63
  /**
251
64
  * Minimal color support for tracks: hex/rgb(a) parsing, OKLab interpolation
@@ -274,194 +87,87 @@ declare function oklabToRgba(c: OkLab): Rgba;
274
87
  /** Interpolate two CSS color strings in OKLab; t may extrapolate. */
275
88
  declare function lerpColor(from: string, to: string, t: number): string;
276
89
  //#endregion
277
- //#region src/track.d.ts
278
- interface Key<T = unknown> {
279
- t: number;
280
- value: T;
281
- /** Shape of the segment ARRIVING at this key (from the previous key). */
282
- ease?: EaseSpec;
283
- interp?: 'default' | 'hold';
284
- /** Stable key id (studio-assigned); optional in code-authored documents. */
285
- id?: string;
286
- /** Builder-resolved implicit from-value; re-resolved on merge (§2.6, §6.2). */
287
- derived?: boolean;
288
- /** Reserved (§4.7): a v2 synthesized-transition leading key reads the live value. v1 accepts but ignores it. */
289
- from?: 'live';
290
- }
291
- interface Track<T = unknown> {
292
- /** Canonical path: '<nodeId>/<prop.path>', e.g. 'circle/position.x'. */
293
- target: string;
294
- type: ValueTypeId;
295
- /** Sorted by t; enforced by validateTrack(). */
296
- keys: Key<T>[];
297
- /** Studio may own this track's keys via sidecar (§6.2). */
298
- editable?: boolean;
299
- /** Reserved (§2.2/§B.6): v2 additive blending. v1 accepts but ignores it (coalesce stays last-wins). */
300
- additive?: boolean;
301
- }
302
- declare class TrackValidationError extends Error {
303
- constructor(target: string, message: string);
304
- }
305
- declare function validateTrack(track: Track): void;
306
- type KeyOpts<T> = Partial<Omit<Key<T>, 't' | 'value'>>;
307
- declare function key<T>(t: number, value: T, easeOrOpts?: EaseSpec | KeyOpts<T>): Key<T>;
308
- /**
309
- * The settle-ON-the-beat helper: a spring key must sit at prev.t +
310
- * spring.duration(cfg) (§2.7), so beat-anchored authoring otherwise means
311
- * hand-computing the launch time. springTo returns the [launch, settle] key
312
- * pair with the arithmetic done — spread it into a raw track():
313
- * track('x/width', 'number', [...springTo(beats.start('drop'), 0, 320, cfg)])
314
- */
315
- declare function springTo<T>(endT: number, from: T, to: T, cfg: SpringConfig): [Key<T>, Key<T>];
316
- /**
317
- * Cascade a set of tracks by shifting each one's key times — the classic
318
- * stagger for animating a list of nodes with a delay between them. `delay` is
319
- * the per-index gap in seconds, or a function of the index for non-linear
320
- * cascades. Pure: returns new tracks, leaving the inputs untouched.
321
- *
322
- * stagger(items.map((it, i) => track(`${it.id}/opacity`, 'number',
323
- * [key(0, 0), key(0.3, 1, 'easeOutCubic')])), 0.08)
324
- */
325
- declare function stagger<T>(tracks: readonly Track<T>[], delay: number | ((index: number) => number)): Track<T>[];
326
- declare function track<T>(target: string, type: ValueTypeId, keys: Key<T>[], opts?: {
327
- editable?: boolean;
328
- }): Track<T>;
329
- declare function resolveEase(spec: EaseSpec | undefined): EasingFn;
330
- /**
331
- * Analytic d(u) for an ease spec (§B.6). Custom-registered eases without a
332
- * derivative fall back to a symmetric difference with a one-time dev warning.
333
- */
334
- declare function resolveEaseDerivative(spec: EaseSpec | undefined): EasingFn;
335
- /**
336
- * Analytic track derivative at time t, in value-units per second of local
337
- * track time (v2 addendum §B.3/§B.6 conventions, pinned):
338
- * (a) at a key boundary, velocity is the RIGHT derivative;
339
- * (b) hold segments and the clamped regions outside the keys have v = 0;
340
- * (c) types without sub/scale operators return null (no kinetic velocity).
341
- */
342
- declare function velocityAt<T>(tr: Track<T>, t: number): T | null;
343
- /** Pure sample of a track at time t (§2.4). */
344
- declare function sampleTrack<T>(tr: Track<T>, t: number): T;
345
- //#endregion
346
- //#region src/devWarning.d.ts
347
- /** The configurable dev-warning channel (no DOM lib in core; console may not exist). */
348
- type DevWarning = (message: string) => void;
349
- declare function setDevWarning(fn: DevWarning): void;
350
- /** Internal: emit through the configurable channel. */
351
- declare function emitDevWarning(message: string): void;
352
- //#endregion
353
- //#region src/timeline.d.ts
354
- type Json = null | boolean | number | string | Json[] | {
355
- [k: string]: Json;
356
- };
357
- interface Marker {
358
- t: number;
359
- name: string;
360
- data?: Json;
361
- }
362
- interface AssetRef {
363
- kind: 'font' | 'image' | 'audio' | 'video' | 'timeline';
90
+ //#region src/fontRegistry.d.ts
91
+ /** A concrete, fully-resolved face: family + the URL to load it from. */
92
+ interface ResolvedFace {
93
+ family: string;
364
94
  url: string;
365
- }
366
- /**
367
- * A gain envelope: keys of linear gain multipliers on the clip's local time
368
- * axis. A full Track satisfies it structurally, but its target/type carry no
369
- * meaning here — `{ keys: [...] }` is all a clip needs.
370
- */
371
- interface GainEnvelope {
372
- keys: Key[];
373
- }
374
- /** Audio is timeline metadata, never a render product (§5.3). */
375
- interface AudioClip {
376
- asset: AssetRef;
377
- /** timeline seconds (frame-quantized at export via sample-position arithmetic) */
378
- at: number;
379
- /** seconds within the source asset */
380
- trim?: {
381
- start: number;
382
- end: number;
383
- };
384
- gain?: GainEnvelope;
385
- playbackRate?: number;
386
- }
387
- interface ChildEntry {
388
- timeline: Timeline;
389
- /** Offset on the parent time axis. */
390
- at: number;
95
+ weight: number;
96
+ style: 'normal' | 'italic';
97
+ }
98
+ interface FontRegistry {
99
+ /** true iff `family` was declared as a font asset (case-sensitive, §3.6). */
100
+ has(family: string): boolean;
101
+ /** Every declared face across every family — what the loaders register. */
102
+ faces(): ResolvedFace[];
391
103
  /**
392
- * 'add': flattened at compile time into the parent's track space.
393
- * 'sync': opaque sub-timeline with its own clock (parent t maps through
394
- * at/timeScale); never coalesced against parent tracks.
104
+ * CSS nearest-weight matching within `family` (and the requested style when
105
+ * an exact style match exists), or undefined when the family is unregistered.
395
106
  */
396
- mode: 'add' | 'sync';
397
- /** sync-mode only: child plays at this rate. */
398
- timeScale?: number;
399
- }
400
- interface Timeline {
401
- version: 1;
402
- duration?: number;
403
- fps?: number;
404
- posterTime?: number;
405
- tracks: Track[];
406
- labels?: Record<string, number>;
407
- markers?: Marker[];
408
- children?: ChildEntry[];
409
- audio?: AudioClip[];
410
- assets?: Record<string, AssetRef>;
411
- }
412
- interface TimelineInit {
413
- tracks?: Track[];
414
- duration?: number;
415
- fps?: number;
416
- posterTime?: number;
417
- labels?: Record<string, number>;
418
- markers?: Marker[];
419
- children?: ChildEntry[];
420
- audio?: AudioClip[];
421
- assets?: Record<string, AssetRef>;
422
- }
423
- declare class TimelineValidationError extends Error {
424
- constructor(message: string);
425
- }
426
- interface CompiledTimeline {
427
- duration: number;
428
- labels: Record<string, number>;
429
- markers: Marker[];
430
- /** One track per target (§2.2), keys rebased to the root time axis. */
431
- tracks: Map<string, Track>;
432
- /** Audio clips rebased to the root time axis (§5.3); sync timeScale scales playbackRate. */
433
- audio: AudioClip[];
107
+ resolveFace(family: string, weight?: number, style?: 'normal' | 'italic'): ResolvedFace | undefined;
108
+ /** `[family, ...declaredFallback]` the order glyph coverage walks. */
109
+ fallbackChain(family: string): string[];
434
110
  }
111
+ declare function buildFontRegistry(assets?: Record<string, AssetRef> | undefined): FontRegistry;
112
+ //#endregion
113
+ //#region src/cmap.d.ts
435
114
  /**
436
- * Sample-indexed clip offset the single A/V-sync source of truth (§5.3),
437
- * `round(at * sampleRate)`. Both the CLI (FFmpeg) and the browser
438
- * (OfflineAudioContext) mixers derive their delay from this, so A/V offsets
439
- * agree across paths by construction rather than one rounding to milliseconds
440
- * and the other to raw float seconds. Default rate is the canonical mix grid.
115
+ * Minimal sfnt `cmap` reader (DESIGN.md §3.6 glyph-coverage check) pure,
116
+ * byte-only, ZERO DOM / ZERO Node. Given the raw bytes of a TrueType/OpenType
117
+ * (or the first table of a TTC) font, returns the set of Unicode code points
118
+ * the font claims to cover. I/O (reading the file / fetching the URL) happens
119
+ * at the call site; this function only parses bytes.
120
+ *
121
+ * Hand-rolled (no font-parsing dependency) to keep core zero-dep and in budget.
122
+ * It covers the two cmap subtable formats that matter in practice:
123
+ * - format 4 (segment mapping to delta values — the BMP workhorse)
124
+ * - format 12 (segmented coverage — astral planes, e.g. emoji)
125
+ * Malformed or unsupported input yields an empty set; it never throws or hangs.
441
126
  */
442
- declare function audioOffsetSamples(at: number, sampleRate?: number): number;
443
- declare function compileTimeline(doc: Timeline): CompiledTimeline;
127
+ declare function parseCmap(bytes: ArrayBuffer): Set<number>;
444
128
  //#endregion
445
- //#region src/targetRef.d.ts
129
+ //#region src/fontValidation.d.ts
130
+ type FontMode = 'strict' | 'dev';
131
+ /** One text usage to validate: a family and the (full, un-revealed) text. */
132
+ interface FontUsage {
133
+ family: string;
134
+ text: string;
135
+ }
136
+ interface MissingGlyphs {
137
+ family: string;
138
+ /** the uncovered code points, ascending, de-duplicated */
139
+ codePoints: number[];
140
+ }
141
+ interface CoverageReport {
142
+ /** non-generic, unregistered families referenced by Text content */
143
+ unregistered: string[];
144
+ /** per-family code points no face in the family's chain covers */
145
+ missingGlyphs: MissingGlyphs[];
146
+ }
147
+ declare class FontValidationError extends Error {
148
+ readonly report: CoverageReport;
149
+ constructor(report: CoverageReport);
150
+ }
446
151
  /**
447
- * Target references (DESIGN.md §2.6): the builder accepts either a canonical
448
- * target string ('circle/opacity') or a property signal that carries its own
449
- * path attached by the scene package at node construction via TARGET_PATH.
152
+ * Is `family` exempt from the unregistered-family check? Generics are exempt by
153
+ * the spec; an `osFamilies` allowlist lets a caller mark OS-installed families
154
+ * (which the rasterizer can resolve without registration) as exempt too.
450
155
  */
451
- declare const TARGET_PATH: unique symbol;
452
- interface TargetCarrier {
453
- [TARGET_PATH]?: string;
156
+ declare function isExemptFamily(family: string, osFamilies?: ReadonlySet<string>): boolean;
157
+ interface ValidateFontsOptions {
158
+ /** OS-installed families to treat as registered (lowercased). */
159
+ osFamilies?: ReadonlySet<string> | undefined;
454
160
  }
455
161
  /**
456
- * A target string, or any object carrying TARGET_PATH (property signals of
457
- * id-bearing nodes). Typed as `object` because signals are callables and the
458
- * symbol prop is attached dynamically; resolution is checked at build time.
162
+ * Validate `usages` against `registry`. `cmaps` maps family covered code
163
+ * points (already parsed via parseCmap by the caller's I/O). A family with no
164
+ * cmap entry contributes no coverage (its glyphs all count as missing) unless
165
+ * it is exempt or its chain reaches a covered family.
166
+ *
167
+ * Returns the report. In `'strict'` mode it throws FontValidationError when the
168
+ * report is non-empty; in `'dev'` mode it emits a dev warning instead.
459
169
  */
460
- type TweenTarget = string | object;
461
- declare class UnresolvableTargetError extends Error {
462
- constructor();
463
- }
464
- declare function resolveTweenTarget(target: TweenTarget): string;
170
+ declare function validateFonts(usages: readonly FontUsage[], registry: FontRegistry, cmaps: ReadonlyMap<string, ReadonlySet<number>>, mode: FontMode, options?: ValidateFontsOptions): CoverageReport;
465
171
  //#endregion
466
172
  //#region src/builder.d.ts
467
173
  type Position = number | string;
@@ -501,6 +207,12 @@ interface TimelineBuilder {
501
207
  }): TimelineBuilder;
502
208
  /** Mark the preceding track editable for the studio (§6.2). */
503
209
  editable(): TimelineBuilder;
210
+ /**
211
+ * Opt the timeline duration into studio editing (§6.2 rule 4). Duration is
212
+ * code-owned and read-only in the editor by default; this mirrors
213
+ * `.editable()` for the duration itself. Order-independent within the chain.
214
+ */
215
+ editableDuration(): TimelineBuilder;
504
216
  }
505
217
  declare class PositionError extends Error {
506
218
  constructor(pos: string, detail: string);
@@ -592,79 +304,4 @@ interface CheckpointedSim {
592
304
  }
593
305
  declare function bakeCheckpointed<W, S = W>(cfg: CheckpointedBakeConfig<W, S>): CheckpointedSim;
594
306
  //#endregion
595
- //#region src/sidecar.d.ts
596
- type OrphanReason = 'node-missing' | 'prop-missing' | 'type-changed';
597
- interface SidecarTrackEntry {
598
- /** value type, so an editor-created track binds without a code baseline. */
599
- type: ValueTypeId;
600
- /** hash of the code baseline this track branched from; null = editor-created. */
601
- baseHash: string | null;
602
- keys: Key[];
603
- }
604
- interface SidecarOrphan {
605
- type: ValueTypeId;
606
- keys: Key[];
607
- reason: OrphanReason;
608
- }
609
- interface SidecarTimelineEntry {
610
- /** editor-owned tracks, keyed by canonical target. */
611
- tracks: Record<string, SidecarTrackEntry>;
612
- /** editor-created labels. */
613
- labels?: Record<string, number>;
614
- /** tracks parked off the merge because their target drifted (§6.2 rule 3). */
615
- orphans?: Record<string, SidecarOrphan>;
616
- }
617
- interface SidecarDoc {
618
- sidecarVersion: 2;
619
- /** edits namespaced by timeline id; the linear timeline is 'main'. */
620
- timelines: Record<string, SidecarTimelineEntry>;
621
- }
622
- /** The flat v1 shape, accepted on load and migrated forward. */
623
- interface SidecarDocV1 {
624
- sidecarVersion: 1;
625
- tracks: Track[];
626
- labels?: Record<string, number>;
627
- }
628
- declare class SidecarVersionError extends Error {
629
- constructor(version: unknown);
630
- }
631
- declare function emptySidecar(): SidecarDoc;
632
- /** Lift a v1 (or already-v2) document to the v2 shape. Throws on unknown versions. */
633
- declare function migrateSidecar(doc: SidecarDoc | SidecarDocV1 | null | undefined): SidecarDoc | null;
634
- /** Stable hash of a code track's keys — the baseline an editor branch records. */
635
- declare function hashKeys(keys: readonly Key[]): string;
636
- /** Assign stable monotonic `k<N>` ids to keys lacking one; existing ids preserved. */
637
- declare function assignKeyIds(keys: readonly Key[]): Key[];
638
- /**
639
- * Set one editable track's keys in the sidecar (§6.2) — the studio write path.
640
- * `codeBaselineKeys` is the code track this branches from (null = editor-created
641
- * with no baseline). Keys get stable `k<N>` ids. Returns a new document.
642
- */
643
- declare function setSidecarTrack(doc: SidecarDoc, timelineId: string, target: string, type: ValueTypeId, keys: Key[], codeBaselineKeys: readonly Key[] | null): SidecarDoc;
644
- interface MergeResult {
645
- timeline: Timeline;
646
- /** targets whose code baseline changed beneath the editor's keys (§6.2 rule 2). */
647
- drift: string[];
648
- /** sidecar tracks parked off the merge (§6.2 rule 3). */
649
- orphans: Record<string, SidecarOrphan>;
650
- }
651
- /**
652
- * Merge with the full §6.2 report: the bindable Timeline, the list of targets
653
- * whose code baseline drifted, and the orphaned tracks (type-changed, or whose
654
- * code track vanished). Orphans are NEVER merged, so a drifted edit can't make
655
- * the whole overlay fail to bind — the good edits survive. Inputs unmutated.
656
- */
657
- declare function mergeSidecarDetailed(code: Timeline, sidecar: SidecarDoc | SidecarDocV1 | null | undefined): MergeResult;
658
- /** Merge the sidecar overlay onto the code baseline → a bindable Timeline (§6.2). */
659
- declare function mergeSidecar(code: Timeline, sidecar: SidecarDoc | SidecarDocV1 | null | undefined): Timeline;
660
- /**
661
- * Editor-edit normalization (§2.7 invariant): a spring-eased key's t is
662
- * intrinsic — prev.t + spring.duration(cfg) — so after any retime, sort and
663
- * re-pin spring keys to their predecessors. Dragging a spring key itself
664
- * therefore snaps back; retiming its predecessor carries it along. Returns a
665
- * new array. Colliding keys are NUDGED apart (+1ms), never deleted — an editor
666
- * must not silently destroy keyframe data on an exact-t collision.
667
- */
668
- declare function normalizeEditedKeys(keys: Key[]): Key[];
669
- //#endregion
670
- export { type AssetRef, type AudioClip, type BakeConfig, BakeError, type BindTarget, type BindableSignal, type BoundTimeline, type CheckpointedBakeConfig, type CheckpointedSim, type ChildEntry, CircularDependencyError, ColorParseError, type CompiledTimeline, type CurveSampler, DEFAULT_EASE, type DevWarning, type EaseSpec, type EasingFn, type Equals, type GainEnvelope, type HandoffKind, type Json, type Key, type KeyOpts, type Marker, type MergeResult, type OkLab, type OrphanReason, type PathContour, type PathValue, type Playhead, type Position, PositionError, type ReadonlySignal, type RetargetSpring, type Rgba, type Rng, type SidecarDoc, type SidecarDocV1, type SidecarOrphan, type SidecarTimelineEntry, type SidecarTrackEntry, SidecarVersionError, type Signal, type SignalOptions, type SpringConfig, type SpringEase, TARGET_PATH, type TargetCarrier, type Timeline, type TimelineBuilder, type TimelineInit, TimelineValidationError, type Track, TrackValidationError, type TweenOpts, type TweenTarget, UnboundTargetError, UnknownEasingError, UnknownValueTypeError, UnresolvableTargetError, type ValueType, type ValueTypeId, ValueTypeInferenceError, type Vec2, type Vec2Signal, WriteDuringEvaluationError, assignKeyIds, audioOffsetSamples, bake, bakeCheckpointed, beginReadPhase, bindTimeline, booleanType, buildTimeline, colorType, compileTimeline, computed, createPlayhead, cubicBezier, cubicBezierDerivative, easingDerivatives, easings, emitDevWarning, emptySidecar, endReadPhase, evaluateAt, formatColor, getTimelineCallbacks, getValueType, hashKeys, inReadPhase, inferValueType, key, lerpColor, mergeSidecar, mergeSidecarDetailed, migrateSidecar, namedEasing, normalizeEditedKeys, numberType, oklabToRgba, parseColor, pathType, random, registerValueType, resolveEase, resolveEaseDerivative, resolveTweenTarget, rgbaToOklab, sampleTrack, setDevWarning, setSidecarTrack, signal, spring, springEasing, springEasingDerivative, springPresets, springTo, stagger, stringType, timeline, track, untracked, validateTrack, vec2ArcType, vec2Equals, vec2Signal, vec2Type, velocityAt };
307
+ export { type AssetRef, type AudioClip, type BakeConfig, BakeError, type BindTarget, type BindableSignal, type BoundTimeline, type CheckpointedBakeConfig, type CheckpointedSim, type ChildEntry, CircularDependencyError, ColorParseError, type CompiledTimeline, type CoverageReport, type CurveSampler, DEFAULT_EASE, type DevWarning, type EaseSpec, type EasingFn, type Equals, type FontFaceRef, type FontMode, type FontRegistry, type FontUsage, FontValidationError, type GainEnvelope, type HandoffKind, type Json, type Key, type KeyOpts, type Marker, type MergeResult, type MissingGlyphs, type OkLab, type OrphanReason, type PathContour, type PathValue, type Playhead, type Position, PositionError, type ReadonlySignal, type ResolvedFace, type RetargetSpring, type Rgba, type Rng, type SidecarDoc, type SidecarDocV1, type SidecarOrphan, type SidecarTimelineEntry, type SidecarTrackEntry, SidecarVersionError, type Signal, type SignalOptions, type SpringConfig, type SpringEase, TARGET_PATH, type TargetCarrier, type Timeline, type TimelineBuilder, type TimelineInit, TimelineValidationError, type Track, TrackValidationError, type TweenOpts, type TweenTarget, UnboundTargetError, UnknownEasingError, UnknownValueTypeError, UnresolvableTargetError, type ValidateFontsOptions, type ValueType, type ValueTypeId, ValueTypeInferenceError, type Vec2, type Vec2Signal, WriteDuringEvaluationError, assignKeyIds, audioOffsetSamples, bake, bakeCheckpointed, beginReadPhase, bindTimeline, booleanType, buildFontRegistry, buildTimeline, colorType, compileTimeline, computed, createPlayhead, cubicBezier, cubicBezierDerivative, deleteSidecarTrack, easingDerivatives, easings, emitDevWarning, emptySidecar, endReadPhase, evaluateAt, formatColor, getTimelineCallbacks, getValueType, hashKeys, inReadPhase, inferValueType, isDurationEditable, isEditableNodeId, isExemptFamily, key, lerpColor, mergeSidecar, mergeSidecarDetailed, migrateSidecar, namedEasing, normalizeEditedKeys, numberType, oklabToRgba, parseCmap, parseColor, pathType, random, registerValueType, resolveEase, resolveEaseDerivative, resolveTweenTarget, rgbaToOklab, sampleTrack, setDevWarning, setSidecarTrack, signal, spring, springEasing, springEasingDerivative, springPresets, springTo, stagger, stringType, targetNodeId, timeline, track, untracked, validateFonts, validateTrack, vec2ArcType, vec2Equals, vec2Signal, vec2Type, velocityAt };