@pixodesk/svg-animator-core 1.0.40 → 1.0.41

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.
@@ -1 +1,378 @@
1
- export { C as COLOR_ATTR_NAMES, f as CSS_ONLY_STYLE_PROPS, D as DEFAULT_DURATION_MS, g as DISALLOWED_SVG_TAGS_LOWER, G as GlyphCharBox, L as LOOP_JUMP_SHIFT_MS, M as MaterializeAllOptions, h as PCT_BASED_ATTR_NAMES, k as PX_ANIM_ATTR_NAME, l as PX_ANIM_SRC_ATTR_NAME, o as PX_UNKNOWN_KEY_ERROR, r as PxAnimatable, x as PxAnyKeyframe, O as PxCreateElement, aj as PxNormalizedKeyframe, ak as PxNormalizedPropertyAnimation, a_ as STYLE_ATTR_NAMES, a$ as TEXT_CONTENT_ATTR, b0 as TRANSFORM_FN_NAMES, b9 as bezierToSvgPath, bb as camelCaseToKebabWordIfNeeded, bc as clamp, be as composeTransformParts, bh as createDiagnostics, bi as createPathSampler, bj as cubicBezier, bk as deepClone, bn as diffFieldUniverse, bo as diffInEffect, bq as extendedPathForBrowser, bu as generateUniqueId, bB as interpolateValue, bF as isScrollTimeline, bG as kebabToCamelCaseWord, bH as kfEasing, bI as kfValue, bJ as layoutGlyphTextChars, bL as materializeGlyphText, bM as materializeGlyphTextAlongPath, bN as materializeMotionPathInPropAnim, bP as mergeStaticTransformIntoAnimDef, bS as planSchemaRelease, bV as releaseLogProblems, bW as reportDocumentDiagnostics, b_ as reverseEasing, b$ as sanitizeAttributeValue, c0 as schemaFieldUniverse, c2 as scrollOffsetProgress, c3 as scrollPhaseInterval, c4 as scrollResolveAxis, c5 as scrollTotalDurationMs, c6 as scrollViewProgress, c7 as splitEasing, c8 as subdivideCubicBezier, c9 as toRGBA } from './internal-BZSJtt_h.js';
1
+ import { e as PxNode, O as PxGlyphFont, bm as PxAnimatable, c as PxAnimatorConfig, ag as PxScroll, aj as PxScrollPhase, s as PxBezierPath, aC as PxTransformParts, ae as PxSchema, aQ as PxWireVersionStep, aN as PxWireStepKind } from './PxDocumentDiagnostic-COGEjBbg.js';
2
+ export { bn as PX_ANIM_ATTR_NAME, bo as PX_ANIM_SRC_ATTR_NAME, bp as PX_CSS_ONLY_STYLE_PROPS, bq as PX_DISALLOWED_SVG_TAGS_LOWER, br as PX_LOOP_JUMP_SHIFT_MS, bs as PX_TEXT_CONTENT_ATTR, bt as PX_UNKNOWN_KEY_ERROR, bu as PxAnyKeyframe, bv as PxMaterializeAllOptions, bw as PxNormalizedKeyframe, bx as PxNormalizedPropertyAnimation, by as createDiagnostics, bz as deepClone, bA as generateUniqueId, bB as interpolateValue, bC as keyframeEasing, bD as keyframeValue, bE as materializeMotionPathInPropAnim, bF as mergeStaticTransformIntoAnimDef, bG as reportDocumentDiagnostics, bH as sanitizeAttributeValue } from './PxDocumentDiagnostic-COGEjBbg.js';
3
+
4
+ interface VmNode {
5
+ type?: string;
6
+ children?: Array<VmNode>;
7
+ [attr: string]: any;
8
+ }
9
+ interface EffectDiff {
10
+ time: number;
11
+ onlyInA: Array<string>;
12
+ onlyInB: Array<string>;
13
+ }
14
+ /**
15
+ * Compares two trees "in effect" across all keyframe instants found in either.
16
+ * Returns one entry per time where the painted-primitive multisets differ.
17
+ * @internal
18
+ */
19
+ declare function diffInEffect(a: VmNode, b: VmNode): Array<EffectDiff>;
20
+
21
+ /**
22
+ * Element-creation factory — abstracts WHAT an "element" is so the same
23
+ * geometry/layout code (e.g. the glyph text materializer) can emit plain wire
24
+ * nodes here, or the editor's React / px elements when called from the editor.
25
+ *
26
+ * The signature intentionally mirrors the editor's `createPxElement(type,
27
+ * props, children, fixReactKeysIfNeeded?)` so the editor's own factory drops in
28
+ * unchanged.
29
+ * @internal
30
+ */
31
+ type PxCreateElement<E = any> = (type: string, props: {
32
+ [k: string]: any;
33
+ }, children?: Array<E> | E | null, fixReactKeysIfNeeded?: boolean) => E;
34
+
35
+ /** @internal */
36
+ interface PathPoint {
37
+ x: number;
38
+ y: number;
39
+ angle: number;
40
+ }
41
+ /** @internal */
42
+ interface PathSampler {
43
+ totalLength: number;
44
+ /** True when the path loops back on itself (explicit `Z` or coincident ends) —
45
+ * no open tip to run off, so overflow clamps/wraps rather than clipping. */
46
+ closed: boolean;
47
+ sampleAtDistance(dist: number): PathPoint;
48
+ }
49
+ /** @internal */
50
+ declare function createPathSampler(d: string): PathSampler | null;
51
+
52
+ /**
53
+ * Glyph text materializer — turns a `<text>`/`<tspan>` subtree into `<path>`
54
+ * outlines from `definitions.fonts`, so the text renders with no external font.
55
+ *
56
+ * - HORIZONTAL ({@link materializeGlyphTextHorizontal}) — left-to-right by
57
+ * advance width; honors font-size, text-anchor, letter/word-spacing,
58
+ * per-tspan x/y/dx/dy, fill/stroke, nested tspans.
59
+ * - ALONG-PATH ({@link materializeGlyphTextAlongPath}) — each glyph placed and
60
+ * rotated to the referenced path's tangent. Static `startOffset` → glyphs
61
+ * bake+merge; animated `startOffset` → per-glyph `<path>` with sampled
62
+ * `animate.transform`. Text-level `x`/`dx` add distance ALONG the path (≈
63
+ * startOffset) and `dy` shifts PERPENDICULAR — matching native `<textPath>`
64
+ * (see {@link alongPathNodeOffsets}); `y` and per-tspan positioning are ignored
65
+ * (a single run).
66
+ *
67
+ * Element creation goes through an injected {@link PxCreateElement} factory, so
68
+ * the SAME layout produces plain wire nodes here (the effects pipeline) or the
69
+ * editor's React/px elements when the editor calls it — see
70
+ * {@link materializeGlyphText}.
71
+ *
72
+ * v1 scope (see svga.text.design.md): keyframe-interval easing is linear;
73
+ * kerning/ligatures, per-tspan opacity, text-level animated fill are out of scope.
74
+ */
75
+
76
+ /** Inputs for a glyph materialization, decoupled from the effects `ApplyContext`
77
+ * so the editor can call the materializer directly. * @internal
78
+ */
79
+ interface GlyphMaterializeOptions<E = any> {
80
+ /** Embedded glyph fonts, keyed by `font-family`. */
81
+ glyphs: Record<string, PxGlyphFont>;
82
+ /** Element factory — defaults to plain wire nodes ({@link jsonElementFactory}). */
83
+ create?: PxCreateElement<E>;
84
+ /** Optional diagnostics sink. */
85
+ warnings?: Array<string>;
86
+ }
87
+ /** Per-CHARACTER advance box (local, pre-transform coords). `x,y` = the char's baseline start,
88
+ * `width` = its advance, `ascent`/`fontSize` size its bbox. * @internal
89
+ */
90
+ interface PxGlyphCharBox {
91
+ x: number;
92
+ y: number;
93
+ width: number;
94
+ ascent: number;
95
+ fontSize: number;
96
+ /** Along-path only: baseline END point (leading edge of the next char). Absent for
97
+ * horizontal, where the end is `x + width` on the same baseline. */
98
+ endX?: number;
99
+ endY?: number;
100
+ /** Along-path only: char rotation in DEGREES (path tangent; 0 = horizontal). */
101
+ rotation?: number;
102
+ }
103
+ /** Optional along-path geometry for {@link layoutGlyphTextChars}: when given, chars are
104
+ * placed + rotated along `pathD` (mirrors {@link materializeGlyphTextAlongPath}) at the
105
+ * STATIC / frame-0 startOffset, so the editor caret follows the path. * @internal
106
+ */
107
+ interface GlyphCharBoxAlongPath {
108
+ pathD?: string;
109
+ startOffset?: PxAnimatable<number>;
110
+ textLength?: PxAnimatable<number>;
111
+ pathOverflow?: string;
112
+ }
113
+ /** Per-character layout boxes for a glyph text, in reading/DOM order INCLUDING spaces
114
+ * (a space has no glyph but advances the pen) AND one zero-width filler box per EMPTY
115
+ * line — the editor's edit canvas renders a zero-width filler char for an empty line
116
+ * (so the caret has something to measure), and DOM char indices must stay aligned.
117
+ * HORIZONTAL by default — mirrors `materializeGlyphTextHorizontal`'s pen-walk exactly
118
+ * (same x/y/dx/dy, spacing and text-anchor). When `opts.alongPath` is given, mirrors
119
+ * `materializeGlyphTextAlongPath` (each char placed + rotated to the path tangent). So
120
+ * an editor caret built from these lands on the rendered glyphs. Empty for a text with
121
+ * no glyph font / unparsable path. * @internal
122
+ */
123
+ declare function layoutGlyphTextChars(node: PxNode, opts: Pick<GlyphMaterializeOptions, 'glyphs' | 'warnings'> & {
124
+ alongPath?: GlyphCharBoxAlongPath;
125
+ }): Array<PxGlyphCharBox>;
126
+ /** @internal */
127
+ declare function materializeGlyphTextAlongPath<E = any>(node: PxNode, pathD: string | undefined, startOffset: PxAnimatable<number> | undefined, opts: GlyphMaterializeOptions<E>, textLength?: PxAnimatable<number>, pathOverflow?: string): E | null;
128
+ /** Single entry the EDITOR calls: materializes a glyph `<text>` node into the
129
+ * factory's element type, choosing along-path when `alongPath` is given. * @internal
130
+ */
131
+ declare function materializeGlyphText<E = any>(node: PxNode, opts: GlyphMaterializeOptions<E> & {
132
+ alongPath?: {
133
+ pathD?: string;
134
+ startOffset?: PxAnimatable<number>;
135
+ textLength?: PxAnimatable<number>;
136
+ pathOverflow?: string;
137
+ };
138
+ }): E | null;
139
+
140
+ /** Inputs for {@link extendedPathForBrowser}. `advance` = the text run-width used to
141
+ * size the end extension (caller-measured; the player estimates it from the node,
142
+ * the editor from its text model — browser fonts have no glyph metrics available). * @internal
143
+ */
144
+ interface ExtendPathOptions {
145
+ pathOverflow?: string;
146
+ startOffset?: PxAnimatable<number>;
147
+ textLength?: PxAnimatable<number>;
148
+ advance?: number;
149
+ }
150
+ /** Result of {@link extendedPathForBrowser}: the (possibly) extended `d`, plus
151
+ * `startShift` — the length of the prepended START lead-in. Because that lead-in
152
+ * moves the `<textPath>` origin back by `startShift`, EVERY `startOffset` (all
153
+ * keyframes) MUST be shifted by `+startShift` so the text lands where it would on
154
+ * the un-extended path (`extend` only adds a tail, it must never move the text). * @internal
155
+ */
156
+ interface ExtendedPath {
157
+ d: string;
158
+ startShift: number;
159
+ }
160
+ /** For `pathOverflow:'extend'` (browser-font): extend an OPEN path along its endpoint
161
+ * tangents so the browser lays overflow glyphs onto the straight extension (matching
162
+ * glyph-mode's tangent behavior) instead of dropping them. `'clip'`/closed paths are
163
+ * returned unchanged (browser clips natively). Shared by the player's browser-font
164
+ * applier and the editor's live/heavy `<textPath>` def generate (single source of truth).
165
+ * Returns the extended `d` AND `startShift` — see {@link ExtendedPath}. * @internal
166
+ */
167
+ declare function extendedPathForBrowser(pathD: string, opts: ExtendPathOptions): ExtendedPath;
168
+
169
+ /** Is this document scroll-driven? (`animator.timelineSource === 'scroll'`) @internal */
170
+ declare function isScrollTimeline(config: PxAnimatorConfig | undefined): boolean;
171
+ /**
172
+ * The seek-space length (ms) a scroll progress of 1 maps to: duration × finite
173
+ * iterations. `'infinite'` is meaningless on a finite progress timeline (see design doc
174
+ * D4) — treated as 1 with the read-side warning left to the consumer.
175
+ * @internal
176
+ */
177
+ declare function scrollTotalDurationMs(config: PxAnimatorConfig | undefined): number;
178
+ /**
179
+ * A named phase's interval in `u`-space.
180
+ *
181
+ * `u` is the subject's "journey distance": with `sTop` = subject's leading edge in
182
+ * scrollport coordinates, `u = vpSize − sTop` — 0 exactly when the subject is about to
183
+ * enter (leading edge at the scrollport's trailing edge), growing as the user scrolls.
184
+ * The `min`/`max` pairs make every formula valid BOTH for a subject smaller than the
185
+ * scrollport and one larger than it (where "fully visible" flips to "covers the
186
+ * scrollport") — the same case split CSS specifies for its named timeline ranges.
187
+ * @internal
188
+ */
189
+ declare function scrollPhaseInterval(phase: PxScrollPhase, subjectSize: number, scrollportSize: number): [number, number];
190
+ /**
191
+ * `kind: 'view'` progress ∈ [0, 1]: where the subject's journey sits within the
192
+ * configured range.
193
+ *
194
+ * @param subjectStart subject's leading edge in scrollport coordinates
195
+ * (`subjectRect.top − scrollportRect.top` on the resolved axis)
196
+ * @param subjectSize subject size on the axis
197
+ * @param scrollportSize scrollport size on the axis
198
+ *
199
+ * A degenerate/inverted range (uStart ≥ uEnd — e.g. zero-size subject with an `entry`
200
+ * range) reports 1 once the point is passed, 0 before — never NaN.
201
+ * @internal
202
+ */
203
+ declare function scrollViewProgress(subjectStart: number, subjectSize: number, scrollportSize: number, range: PxScroll['range'] | undefined): number;
204
+ /**
205
+ * `kind: 'scroll'` progress ∈ [0, 1]: the scroller's offset ratio mapped through the
206
+ * range (phases don't exist here — `fraction` is of the total scroll range).
207
+ *
208
+ * `maxOffset === 0` (nothing to scroll) reports 1, matching the CSS spec's rule that a
209
+ * zero-length timeline is at 100%.
210
+ * @internal
211
+ */
212
+ declare function scrollOffsetProgress(offset: number, maxOffset: number, range: PxScroll['range'] | undefined): number;
213
+ /**
214
+ * Resolve a logical axis to a physical one. `block`/`inline` are writing-mode relative:
215
+ * in horizontal writing (`horizontal-tb`, the default) block flows vertically; in
216
+ * vertical writing modes it flows horizontally.
217
+ * @internal
218
+ */
219
+ declare function scrollResolveAxis(axis: PxScroll['axis'] | undefined, writingMode: string | undefined): 'x' | 'y';
220
+
221
+ /**
222
+ * Converts a PxBezierPath to an SVG path string.
223
+ * Control points (i, o) are treated as ABSOLUTE coordinates.
224
+ * @param {PxBezierPath} path
225
+ * @returns {string}
226
+ */
227
+ /**
228
+ * @param forceCurves Emit EVERY segment (incl. the closing one) as a cubic `C`, even when
229
+ * its control points are degenerate (a straight line). Needed for keyframe values the
230
+ * BROWSER interpolates (WAAPI / CSS `path()`): CSS only interpolates paths with
231
+ * IDENTICAL command sequences, so an opportunistic `L` in one keyframe vs a `C` in the
232
+ * next (e.g. a round-corner radius animating from 0) turns the whole animation
233
+ * DISCRETE — it flips at 50% instead of morphing.
234
+ * @internal
235
+ */
236
+ declare function bezierToSvgPath(path: PxBezierPath, forceCurves?: boolean): string;
237
+ /**
238
+ * Creates a cubic-bezier easing function.
239
+ * @param easing An array of four numbers [x1, y1, x2, y2] defining the bezier curve.
240
+ * @returns A function that takes a progress value (0-1) and returns an eased value.
241
+ * @internal
242
+ */
243
+ declare function cubicBezier(easing: [number, number, number, number]): (x: number) => number;
244
+ type Point2 = [number, number];
245
+ /**
246
+ * Splits a cubic bezier curve at parameter t using De Casteljau's algorithm.
247
+ * Returns the left and right sub-curves as 4-point tuples.
248
+ * @internal
249
+ */
250
+ declare function subdivideCubicBezier(p0: Point2, p1: Point2, p2: Point2, p3: Point2, t: number): {
251
+ left: [Point2, Point2, Point2, Point2];
252
+ right: [Point2, Point2, Point2, Point2];
253
+ };
254
+ type Easing = [number, number, number, number];
255
+ /**
256
+ * Splits a CSS cubic-bezier easing [x1,y1,x2,y2] at a given x-axis fraction.
257
+ * Each half is re-normalized to map [0,0]→[1,1].
258
+ * Returns undefined for either half if the input is undefined (linear) or the split is degenerate.
259
+ * @internal
260
+ */
261
+ declare function splitEasing(easing: Easing | undefined, xFraction: number): {
262
+ left: Easing | undefined;
263
+ right: Easing | undefined;
264
+ };
265
+ /**
266
+ * Reverses a cubic-bezier easing for backward playback.
267
+ * [x1,y1,x2,y2] → [1-x2, 1-y2, 1-x1, 1-y1].
268
+ * @internal
269
+ */
270
+ declare function reverseEasing(easing: Easing | undefined): Easing | undefined;
271
+ /**
272
+ * Converts a color from a [r, g, b, a] array (where values are 0-1) to an rgba() or rgb() CSS string.
273
+ * @param color The color array.
274
+ * @internal
275
+ */
276
+ declare function toRGBA(color: Array<number>): string;
277
+ /** @internal */
278
+ declare const PX_COLOR_ATTR_NAMES: Set<string>;
279
+ /** @internal */
280
+ declare const PX_TRANSFORM_FN_NAMES: Set<string>;
281
+ /** @internal */
282
+ declare const PX_PCT_BASED_ATTR_NAMES: Set<string>;
283
+ /**
284
+ * Compose a `PxTransformParts` record into a single SVG/CSS transform string in
285
+ * the canonical order:
286
+ *
287
+ * translate, translate(+origin), rotate, scale, translate(-origin)
288
+ *
289
+ * Each part is omitted when not present. `origin` becomes a `translate(+o)` /
290
+ * `translate(-o)` pair surrounding the rotate/scale segment — the SVG-native
291
+ * way to render a transform-origin pivot.
292
+ *
293
+ * @param parts the parts record (translate / rotate / scale / origin)
294
+ * @param opts.withUnits when true (default), translates use `px` and rotate
295
+ * uses `deg` — required for CSS / WebAnimations keyframes. When false, no
296
+ * units are emitted — required for the SVG `transform` attribute.
297
+ * @internal
298
+ */
299
+ declare function composeTransformParts(parts: PxTransformParts | null | undefined, opts?: {
300
+ withUnits?: boolean;
301
+ }): string;
302
+ /** @internal */
303
+ declare const PX_STYLE_ATTR_NAMES: Set<string>;
304
+ /** @internal */
305
+ declare const PX_DEFAULT_DURATION_MS = 1000;
306
+ /**
307
+ * Converts a kebab-case string to camelCase.
308
+ * @param kebab The kebab-case string.
309
+ * @internal
310
+ */
311
+ declare function kebabToCamelCaseWord(kebab: string): string;
312
+ /**
313
+ * Converts a camelCase string to kebab-case.
314
+ * @param camel The camelCase string.
315
+ * @internal
316
+ */
317
+ declare function camelCaseToKebabWordIfNeeded(camel: string): string;
318
+ /**
319
+ * Clamps a number between a minimum and maximum value.
320
+ * @param value The number to clamp.
321
+ * @param min The minimum value.
322
+ * @param max The maximum value.
323
+ * @internal
324
+ */
325
+ declare function clamp(value: number, min: number, max: number): number;
326
+
327
+ /** Every field identity `root` can carry, as canonical paths, sorted. @internal */
328
+ declare function schemaFieldUniverse(root: PxSchema<any, any>): Array<string>;
329
+
330
+ /** One entry of the release log — what shipped, when, and what it changed. @internal */
331
+ interface SchemaReleaseRecord {
332
+ readonly version: string;
333
+ /** ISO date, `YYYY-MM-DD`. */
334
+ readonly date: string;
335
+ /** The first release: nothing to compare it against. */
336
+ readonly baseline?: boolean;
337
+ readonly added: ReadonlyArray<string>;
338
+ readonly removed: ReadonlyArray<string>;
339
+ readonly note?: string;
340
+ }
341
+ /** Keys that appeared and keys that left between two inventories, each sorted. @internal */
342
+ declare function diffFieldUniverse(previous: ReadonlyArray<string>, current: ReadonlyArray<string>): {
343
+ added: Array<string>;
344
+ removed: Array<string>;
345
+ };
346
+ /** What a release must do about the version. `refuse` set means: do not release as-is. @internal */
347
+ interface SchemaReleasePlan {
348
+ /** Did any key appear or leave since the last release? */
349
+ readonly changed: boolean;
350
+ /** Which kind of step the change requires — a removal is never additive. */
351
+ readonly requiredKind?: PxWireStepKind;
352
+ /** The version this release must carry. */
353
+ readonly requiredVersion?: string;
354
+ readonly refuse?: string;
355
+ }
356
+ /**
357
+ * THE BUMP RULE. A key change requires `b + 1` and a step that explains it; no key change
358
+ * requires nothing. The rule never picks the number by taste — the inventory diff does.
359
+ * @internal
360
+ */
361
+ declare function planSchemaRelease(p: {
362
+ readonly added: ReadonlyArray<string>;
363
+ readonly removed: ReadonlyArray<string>;
364
+ /** `PX_WIRE_SCHEMA_VERSION` — what the source says now. */
365
+ readonly declared: string;
366
+ /** The version of the last release record. */
367
+ readonly lastReleased: string;
368
+ readonly steps: ReadonlyArray<PxWireVersionStep>;
369
+ }): SchemaReleasePlan;
370
+ /**
371
+ * Everything wrong with the release log, as sentences — empty when it is consistent. The log
372
+ * must start at the baseline, move strictly forward, END at the version the source declares,
373
+ * and every release after the baseline must have the step that explains it.
374
+ * @internal
375
+ */
376
+ declare function releaseLogProblems(releases: ReadonlyArray<SchemaReleaseRecord>, steps: ReadonlyArray<PxWireVersionStep>, declared: string, baseline: string): Array<string>;
377
+
378
+ export { PX_COLOR_ATTR_NAMES, PX_DEFAULT_DURATION_MS, PX_PCT_BASED_ATTR_NAMES, PX_STYLE_ATTR_NAMES, PX_TRANSFORM_FN_NAMES, PxAnimatable, type PxCreateElement, type PxGlyphCharBox, bezierToSvgPath, camelCaseToKebabWordIfNeeded, clamp, composeTransformParts, createPathSampler, cubicBezier, diffFieldUniverse, diffInEffect, extendedPathForBrowser, isScrollTimeline, kebabToCamelCaseWord, layoutGlyphTextChars, materializeGlyphText, materializeGlyphTextAlongPath, planSchemaRelease, releaseLogProblems, reverseEasing, schemaFieldUniverse, scrollOffsetProgress, scrollPhaseInterval, scrollResolveAxis, scrollTotalDurationMs, scrollViewProgress, splitEasing, subdivideCubicBezier, toRGBA };