@pixodesk/svg-animator-core 1.0.44 → 1.0.46

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/internal.cjs CHANGED
@@ -55,6 +55,7 @@ __export(internal_exports, {
55
55
  PX_COLOR_ATTR_NAMES: () => PX_COLOR_ATTR_NAMES,
56
56
  PX_CSS_ONLY_STYLE_PROPS: () => PX_CSS_ONLY_STYLE_PROPS,
57
57
  PX_DEFAULT_DURATION_MS: () => PX_DEFAULT_DURATION_MS,
58
+ PX_DEFAULT_ITERATIONS: () => PX_DEFAULT_ITERATIONS,
58
59
  PX_DISALLOWED_SVG_TAGS_LOWER: () => PX_DISALLOWED_SVG_TAGS_LOWER,
59
60
  PX_LOOP_JUMP_SHIFT_MS: () => PX_LOOP_JUMP_SHIFT_MS,
60
61
  PX_PCT_BASED_ATTR_NAMES: () => PX_PCT_BASED_ATTR_NAMES,
@@ -85,7 +86,9 @@ __export(internal_exports, {
85
86
  materializeMotionPathInPropAnim: () => materializeMotionPathInPropAnim,
86
87
  mergeStaticTransformIntoAnimDef: () => mergeStaticTransformIntoAnimDef,
87
88
  planSchemaRelease: () => planSchemaRelease,
89
+ prepareDocumentForRender: () => prepareDocumentForRender,
88
90
  releaseLogProblems: () => releaseLogProblems,
91
+ renderPxTree: () => renderPxTree,
89
92
  reportDocumentDiagnostics: () => reportDocumentDiagnostics,
90
93
  reverseEasing: () => reverseEasing,
91
94
  sanitizeAttributeValue: () => sanitizeAttributeValue,
@@ -113,19 +116,31 @@ function pathStr(path) {
113
116
  return result;
114
117
  }
115
118
  var Base = class {
119
+ /** Structural schemas (objects, arrays, unions of objects…) pass `false`: they state no scalar default. */
120
+ constructor(_statesDefault) {
121
+ this._statesDefault = _statesDefault;
122
+ }
116
123
  _canSanitize(raw) {
117
124
  return this.isValid(raw);
118
125
  }
126
+ /** A REQUIRED field is never absent; its default is the repair value. */
127
+ absentDefault() {
128
+ return this._default;
129
+ }
119
130
  optional() {
120
131
  return new Optional(this);
121
132
  }
122
133
  };
123
134
  var Optional = class extends Base {
124
135
  constructor(inner) {
125
- super();
136
+ super(inner._statesDefault);
126
137
  this.inner = inner;
127
138
  this._default = void 0;
128
139
  }
140
+ /** Absent means the inner schema's STATED default — or nothing at all when it states none. */
141
+ absentDefault() {
142
+ return this.inner._statesDefault ? this.inner._default : void 0;
143
+ }
129
144
  sanitize(raw) {
130
145
  if (raw === void 0 || raw === null) return void 0;
131
146
  return this.inner._canSanitize(raw) ? this.inner.sanitize(raw) : void 0;
@@ -139,8 +154,8 @@ var Optional = class extends Base {
139
154
  }
140
155
  };
141
156
  var Str = class extends Base {
142
- constructor(_default = "") {
143
- super();
157
+ constructor(_default, statesDefault) {
158
+ super(statesDefault);
144
159
  this._default = _default;
145
160
  }
146
161
  sanitize(raw) {
@@ -153,8 +168,8 @@ var Str = class extends Base {
153
168
  }
154
169
  };
155
170
  var Num = class extends Base {
156
- constructor(_default = 0) {
157
- super();
171
+ constructor(_default, statesDefault) {
172
+ super(statesDefault);
158
173
  this._default = _default;
159
174
  }
160
175
  sanitize(raw) {
@@ -167,8 +182,8 @@ var Num = class extends Base {
167
182
  }
168
183
  };
169
184
  var Bool = class extends Base {
170
- constructor(_default = false) {
171
- super();
185
+ constructor(_default, statesDefault) {
186
+ super(statesDefault);
172
187
  this._default = _default;
173
188
  }
174
189
  sanitize(raw) {
@@ -181,8 +196,9 @@ var Bool = class extends Base {
181
196
  }
182
197
  };
183
198
  var Literal = class extends Base {
199
+ /** A literal IS its own value: absent means it. */
184
200
  constructor(value) {
185
- super();
201
+ super(true);
186
202
  this.value = value;
187
203
  this._default = value;
188
204
  }
@@ -196,8 +212,8 @@ var Literal = class extends Base {
196
212
  }
197
213
  };
198
214
  var Enum = class extends Base {
199
- constructor(values, defaultVal) {
200
- super();
215
+ constructor(values, defaultVal, statesDefault) {
216
+ super(statesDefault);
201
217
  this.values = values;
202
218
  this._default = defaultVal != null ? defaultVal : values[0];
203
219
  }
@@ -212,8 +228,8 @@ var Enum = class extends Base {
212
228
  };
213
229
  var UNION_MEMBER_ERROR_LIMIT = 4;
214
230
  var Union = class extends Base {
215
- constructor(schemas, defaultVal) {
216
- super();
231
+ constructor(schemas, defaultVal, statesDefault) {
232
+ super(statesDefault);
217
233
  this.schemas = schemas;
218
234
  /** Structural tag read by {@link describeSchema} — `schemas` alone cannot tell Union from Tuple. */
219
235
  this._kind = "union";
@@ -267,7 +283,7 @@ var Union = class extends Base {
267
283
  var DiscriminatedUnion = class extends Base {
268
284
  constructor(_key, _schemas, defaultVal) {
269
285
  var _a2;
270
- super();
286
+ super(false);
271
287
  this._key = _key;
272
288
  this._schemas = _schemas;
273
289
  /** Structural tag read by {@link describeSchema}. */
@@ -307,13 +323,19 @@ var DiscriminatedUnion = class extends Base {
307
323
  return schema ? schema._canSanitize(raw) : this._schemas[0]._canSanitize(raw);
308
324
  }
309
325
  };
326
+ function statedDefaults(shape) {
327
+ const out = {};
328
+ for (const key of Object.keys(shape)) if (shape[key]._statesDefault) out[key] = shape[key].absentDefault();
329
+ return Object.freeze(out);
330
+ }
310
331
  var Obj = class extends Base {
311
332
  constructor(_shape) {
312
- super();
333
+ super(false);
313
334
  this._shape = _shape;
314
335
  const d = {};
315
336
  for (const key of Object.keys(_shape)) d[key] = _shape[key]._default;
316
337
  this._default = d;
338
+ this.defaults = statedDefaults(_shape);
317
339
  }
318
340
  sanitize(raw) {
319
341
  const src = raw && typeof raw === "object" && !Array.isArray(raw) ? raw : {};
@@ -355,12 +377,13 @@ var Obj = class extends Base {
355
377
  };
356
378
  var OpenObj = class extends Base {
357
379
  constructor(_shape, _openSchema) {
358
- super();
380
+ super(false);
359
381
  this._shape = _shape;
360
382
  this._openSchema = _openSchema;
361
383
  const d = {};
362
384
  for (const key of Object.keys(_shape)) d[key] = _shape[key]._default;
363
385
  this._default = d;
386
+ this.defaults = statedDefaults(_shape);
364
387
  }
365
388
  sanitize(raw) {
366
389
  const src = raw && typeof raw === "object" && !Array.isArray(raw) ? raw : {};
@@ -405,7 +428,7 @@ var OpenObj = class extends Base {
405
428
  };
406
429
  var Arr = class extends Base {
407
430
  constructor(item) {
408
- super();
431
+ super(false);
409
432
  this.item = item;
410
433
  this._default = [];
411
434
  }
@@ -437,7 +460,7 @@ var Arr = class extends Base {
437
460
  };
438
461
  var Rec = class extends Base {
439
462
  constructor(value) {
440
- super();
463
+ super(false);
441
464
  this.value = value;
442
465
  /** Structural tag read by {@link describeSchema}. */
443
466
  this._kind = "record";
@@ -471,7 +494,7 @@ var Rec = class extends Base {
471
494
  };
472
495
  var Any = class extends Base {
473
496
  constructor() {
474
- super(...arguments);
497
+ super(false);
475
498
  this._default = void 0;
476
499
  }
477
500
  sanitize(raw) {
@@ -486,7 +509,7 @@ var Any = class extends Base {
486
509
  };
487
510
  var Defined = class extends Base {
488
511
  constructor() {
489
- super(...arguments);
512
+ super(false);
490
513
  this._default = void 0;
491
514
  }
492
515
  sanitize(raw) {
@@ -503,7 +526,7 @@ var Defined = class extends Base {
503
526
  };
504
527
  var Lazy = class extends Base {
505
528
  constructor(fn, _default) {
506
- super();
529
+ super(false);
507
530
  this.fn = fn;
508
531
  this._default = _default;
509
532
  this.resolved = null;
@@ -524,7 +547,7 @@ var Lazy = class extends Base {
524
547
  };
525
548
  var Tuple = class extends Base {
526
549
  constructor(schemas) {
527
- super();
550
+ super(false);
528
551
  this.schemas = schemas;
529
552
  /** Structural tag read by {@link describeSchema} — `schemas` alone cannot tell Tuple from Union. */
530
553
  this._kind = "tuple";
@@ -575,22 +598,29 @@ function describeSchema(schema) {
575
598
  if ("fn" in s) return { kind: "lazy", resolved: (_a2 = s.resolved) != null ? _a2 : s.resolved = s.fn() };
576
599
  return { kind: "leaf" };
577
600
  }
601
+ function string(defaultVal) {
602
+ return defaultVal === void 0 ? new Str("", false) : new Str(defaultVal, true);
603
+ }
604
+ function number(defaultVal) {
605
+ return defaultVal === void 0 ? new Num(0, false) : new Num(defaultVal, true);
606
+ }
607
+ function boolean(defaultVal) {
608
+ return defaultVal === void 0 ? new Bool(false, false) : new Bool(defaultVal, true);
609
+ }
610
+ function oneOf(values, defaultVal) {
611
+ return defaultVal === void 0 ? new Enum(values, void 0, false) : new Enum(values, defaultVal, true);
612
+ }
613
+ function unionOf(schemas, defaultVal) {
614
+ return defaultVal === void 0 ? new Union(schemas, void 0, false) : new Union(schemas, defaultVal, true);
615
+ }
578
616
  var px = {
579
- /** Matches a string. Default: '' or provided value. */
580
- string: (defaultVal = "") => new Str(defaultVal),
581
- /** Matches a finite number. Default: 0 or provided value. */
582
- number: (defaultVal = 0) => new Num(defaultVal),
583
- /** Matches a boolean. Default: false or provided value. */
584
- boolean: (defaultVal = false) => new Bool(defaultVal),
585
- /** Matches one exact primitive value; its default is the value itself. */
617
+ string,
618
+ number,
619
+ boolean,
620
+ /** Matches one exact primitive value; absent means the value itself. */
586
621
  literal: (value) => new Literal(value),
587
- /** Matches one of a fixed set of string/number values. Default: first value. */
588
- enum: (values, defaultVal) => new Enum(values, defaultVal),
589
- /**
590
- * Returns the first schema whose isValid passes.
591
- * TypeScript infers the union of all member types automatically.
592
- */
593
- union: (schemas, defaultVal) => new Union(schemas, defaultVal),
622
+ enum: oneOf,
623
+ union: unionOf,
594
624
  /**
595
625
  * Discriminated union — reads `raw[key]`, finds the member schema whose
596
626
  * literal at `key` matches, then delegates sanitize/isValid to that member.
@@ -598,7 +628,8 @@ var px = {
598
628
  * TypeScript infers the union of all member types automatically.
599
629
  */
600
630
  discriminatedUnion: (key, schemas) => new DiscriminatedUnion(key, schemas),
601
- /** Typed object — unknown keys are stripped. Required fields fall back to their default. */
631
+ /** Typed object — unknown keys are stripped. Required fields fall back to their default.
632
+ * Its `defaults` say what each stated field means when a document leaves it out. */
602
633
  object: (shape) => new Obj(shape),
603
634
  /**
604
635
  * Open object — validates known keys; passes unknown keys through as-is,
@@ -711,13 +742,19 @@ var PxTimelineEngine = {
711
742
  var PxTimelineEngineSetting = __spreadProps(__spreadValues({}, PxTimelineEngine), {
712
743
  auto: "auto"
713
744
  });
745
+ function resolveTimelineEngine(engine) {
746
+ return engine === PxTimelineEngineSetting.js ? PxTimelineEngine.js : PxTimelineEngine.native;
747
+ }
714
748
  var PX_TRIGGER_DEFAULTS = {
715
749
  start: "load",
716
750
  offScreen: "pause",
717
751
  mouseOut: "continue",
718
752
  visibilityThreshold: 0.5,
719
- visibilityDebounce: 150
753
+ visibilityDebounce: 150,
754
+ finish: "hold"
720
755
  };
756
+ var PX_DEFAULT_DURATION_MS = 1e3;
757
+ var PX_DEFAULT_ITERATIONS = 1;
721
758
  var PxLoopRepeatAt = {
722
759
  /** Segment from the START; the repetition runs BEFORE the first keyframe
723
760
  * (intro loops that play until the main timeline begins). */
@@ -767,6 +804,16 @@ var PxStrokeTrimSubPaths = {
767
804
  var PX_TEXT_CONTENT_ATTR = "textContent";
768
805
  var CLASS_ATTR = "class";
769
806
  var TRANSFORM_ATTR = "transform";
807
+ var OFFSET_DISTANCE_ATTR = "offsetDistance";
808
+ var INTERNAL_ATTRS = /* @__PURE__ */ new Set([
809
+ "type",
810
+ "children",
811
+ "animator",
812
+ "meta",
813
+ "animate",
814
+ "effects",
815
+ PX_TEXT_CONTENT_ATTR
816
+ ]);
770
817
  var TRANSFORM_PART = {
771
818
  translate: "translate",
772
819
  rotate: "rotate",
@@ -847,6 +894,16 @@ function flattenAnimatorTimeline(cfg) {
847
894
  flattenMemo.set(cfg, flat);
848
895
  return flat;
849
896
  }
897
+ function getDefinitions(doc) {
898
+ var _a2;
899
+ if (!doc) return void 0;
900
+ return (_a2 = getAnimatorConfig(doc)) == null ? void 0 : _a2.definitions;
901
+ }
902
+ function getBindings(doc) {
903
+ var _a2;
904
+ if (!doc) return void 0;
905
+ return (_a2 = getAnimatorConfig(doc)) == null ? void 0 : _a2.bindings;
906
+ }
850
907
 
851
908
  // src/version/PxSchemaVersion.ts
852
909
  var PX_WIRE_SCHEMA_VERSION = "1.2";
@@ -910,15 +967,15 @@ var keyframeTangentIn = (kf) => anyKf(kf).tangentIn;
910
967
  var keyframeTangentOut = (kf) => anyKf(kf).tangentOut;
911
968
  var PxLoopSchema = implementsInterface()(px.object({
912
969
  segmentCount: px.number().optional(),
913
- repeatAt: px.enum([PxLoopRepeatAt.start, PxLoopRepeatAt.end]).optional(),
914
- direction: px.enum([PxLoopDirection.normal, PxLoopDirection.alternate]).optional()
970
+ repeatAt: px.enum([PxLoopRepeatAt.start, PxLoopRepeatAt.end], PxLoopRepeatAt.end).optional(),
971
+ direction: px.enum([PxLoopDirection.normal, PxLoopDirection.alternate], PxLoopDirection.normal).optional()
915
972
  }));
916
973
  var PxPropertyAnimationSchema = implementsInterface()(px.object({
917
974
  value: PxKeyframeValueSchema.optional(),
918
975
  keyframes: px.array(PxKeyframeSchema).optional(),
919
976
  loop: px.union([PxLoopSchema, px.boolean()]).optional(),
920
977
  autoOrient: px.boolean().optional(),
921
- alongPathMode: px.enum([PxAlongPathMode.sampled, PxAlongPathMode.offsetPath]).optional()
978
+ alongPathMode: px.enum([PxAlongPathMode.sampled, PxAlongPathMode.offsetPath], PxAlongPathMode.sampled).optional()
922
979
  }));
923
980
  var PxTransformPartsSchema = implementsInterface()(px.object({
924
981
  translate: px.tuple([px.number(), px.number()]).optional(),
@@ -948,9 +1005,9 @@ var PxTriggerSchema = implementsInterface()(px.object({
948
1005
  // What happens after a NATURAL finish — `'hold'` (default: keep the end state per
949
1006
  // `fill`) or `'reset'` (snap back to the start state). One of four occasion keys
950
1007
  // (`start`, `offScreen`, `mouseOut`, `finish`), all named the same way.
951
- finish: px.enum([PxFinishAction.hold, PxFinishAction.reset]).optional(),
952
- visibilityThreshold: px.number().optional(),
953
- visibilityDebounce: px.number().optional()
1008
+ finish: px.enum([PxFinishAction.hold, PxFinishAction.reset], PX_TRIGGER_DEFAULTS.finish).optional(),
1009
+ visibilityThreshold: px.number(PX_TRIGGER_DEFAULTS.visibilityThreshold).optional(),
1010
+ visibilityDebounce: px.number(PX_TRIGGER_DEFAULTS.visibilityDebounce).optional()
954
1011
  }));
955
1012
  var PxGlyphSchema = implementsInterface()(px.object({
956
1013
  width: px.number(),
@@ -976,7 +1033,7 @@ var PxScrollRangePointSchema = implementsInterface()(px.object({
976
1033
  PxScrollPhase.exit,
977
1034
  PxScrollPhase.entryCrossing,
978
1035
  PxScrollPhase.exitCrossing
979
- ]).optional(),
1036
+ ], PxScrollPhase.cover).optional(),
980
1037
  fraction: px.number().optional()
981
1038
  }));
982
1039
  var PxScrollRangeSchema = px.object({
@@ -984,37 +1041,37 @@ var PxScrollRangeSchema = px.object({
984
1041
  end: PxScrollRangePointSchema.optional()
985
1042
  });
986
1043
  var PxScrollSchema = implementsInterface()(px.object({
987
- kind: px.enum([PxScrollKind.view, PxScrollKind.scroll]).optional(),
988
- axis: px.enum([PxScrollAxis.block, PxScrollAxis.inline, PxScrollAxis.x, PxScrollAxis.y]).optional(),
989
- source: px.enum([PxScrollSource.nearest, PxScrollSource.root]).optional(),
1044
+ kind: px.enum([PxScrollKind.view, PxScrollKind.scroll], PxScrollKind.view).optional(),
1045
+ axis: px.enum([PxScrollAxis.block, PxScrollAxis.inline, PxScrollAxis.x, PxScrollAxis.y], PxScrollAxis.block).optional(),
1046
+ source: px.enum([PxScrollSource.nearest, PxScrollSource.root], PxScrollSource.nearest).optional(),
990
1047
  // Free-form: the two keywords `parent`/`scroller` plus any CSS selector.
991
1048
  subject: px.string().optional(),
992
1049
  smoothing: px.number().optional(),
993
- pin: px.boolean().optional(),
994
- pinAlign: px.enum([PxPinAlign.top, PxPinAlign.center, PxPinAlign.bottom]).optional(),
1050
+ pin: px.boolean(false).optional(),
1051
+ pinAlign: px.enum([PxPinAlign.top, PxPinAlign.center, PxPinAlign.bottom], PxPinAlign.top).optional(),
995
1052
  pinOffset: px.number().optional(),
996
1053
  pinDistance: px.number().optional(),
997
1054
  range: PxScrollRangeSchema.optional()
998
1055
  }));
999
1056
  var PxTimelinePinSchema = implementsInterface()(px.object({
1000
- align: px.enum([PxPinAlign.top, PxPinAlign.center, PxPinAlign.bottom]).optional(),
1057
+ align: px.enum([PxPinAlign.top, PxPinAlign.center, PxPinAlign.bottom], PxPinAlign.top).optional(),
1001
1058
  offset: px.number().optional(),
1002
1059
  distance: px.number().optional()
1003
1060
  }));
1004
- var PxTimelineEngineSchema = px.enum([PxTimelineEngineSetting.auto, PxTimelineEngineSetting.native, PxTimelineEngineSetting.js]).optional();
1061
+ var PxTimelineEngineSchema = px.enum([PxTimelineEngineSetting.auto, PxTimelineEngineSetting.native, PxTimelineEngineSetting.js], PxTimelineEngineSetting.auto).optional();
1005
1062
  var PxTimeTimelineSchema = implementsInterface()(px.object({
1006
1063
  type: px.literal("time").optional(),
1007
1064
  engine: PxTimelineEngineSchema,
1008
1065
  frameRate: px.number().optional(),
1009
1066
  // §2.8: duration is a property of the TIMELINE — how long one pass takes.
1010
- duration: px.number().optional(),
1067
+ duration: px.number(PX_DEFAULT_DURATION_MS).optional(),
1011
1068
  trigger: PxTriggerSchema.optional(),
1012
- delay: px.number().optional(),
1013
- iterations: px.union([px.number(), px.literal("infinite")]).optional(),
1069
+ delay: px.number(0).optional(),
1070
+ iterations: px.union([px.number(PX_DEFAULT_ITERATIONS), px.literal("infinite")], PX_DEFAULT_ITERATIONS).optional(),
1014
1071
  // `fillMode` on the wire (CSS `animation-fill-mode`; the runtime view calls it `fill`)
1015
1072
  // — never `fill`, which is paint everywhere else in the format.
1016
- fillMode: px.enum([PxFillMode.forwards, PxFillMode.backwards, PxFillMode.both, PxFillMode.none]).optional(),
1017
- direction: px.enum([PxPlaybackDirection.normal, PxPlaybackDirection.reverse, PxPlaybackDirection.alternate, PxPlaybackDirection.alternateReverse]).optional()
1073
+ fillMode: px.enum([PxFillMode.forwards, PxFillMode.backwards, PxFillMode.both, PxFillMode.none], PxFillMode.forwards).optional(),
1074
+ direction: px.enum([PxPlaybackDirection.normal, PxPlaybackDirection.reverse, PxPlaybackDirection.alternate, PxPlaybackDirection.alternateReverse], PxPlaybackDirection.normal).optional()
1018
1075
  }));
1019
1076
  var scrollishTimelineShape = {
1020
1077
  // §2.8: duration is a property of the TIMELINE — under scrubbing it is the keyframe
@@ -1025,8 +1082,8 @@ var scrollishTimelineShape = {
1025
1082
  iterations: px.number().optional(),
1026
1083
  engine: PxTimelineEngineSchema,
1027
1084
  frameRate: px.number().optional(),
1028
- axis: px.enum([PxScrollAxis.block, PxScrollAxis.inline, PxScrollAxis.x, PxScrollAxis.y]).optional(),
1029
- source: px.enum([PxScrollSource.nearest, PxScrollSource.root]).optional(),
1085
+ axis: px.enum([PxScrollAxis.block, PxScrollAxis.inline, PxScrollAxis.x, PxScrollAxis.y], PxScrollAxis.block).optional(),
1086
+ source: px.enum([PxScrollSource.nearest, PxScrollSource.root], PxScrollSource.nearest).optional(),
1030
1087
  subject: px.string().optional(),
1031
1088
  // 'parent' | 'scroller' | any CSS selector
1032
1089
  smoothing: px.number().optional(),
@@ -1106,9 +1163,10 @@ var PxRepeaterEffectSchema = implementsInterface()(px.object({
1106
1163
  }));
1107
1164
  var PxMaskedByEffectSchema = implementsInterface()(px.object({
1108
1165
  source: px.string().optional(),
1109
- maskType: px.enum([PxMaskType.luminance, PxMaskType.alpha]).optional(),
1110
- maskUnits: px.enum([PxUnits.userSpaceOnUse, PxUnits.objectBoundingBox]).optional(),
1111
- maskContentUnits: px.enum([PxUnits.userSpaceOnUse, PxUnits.objectBoundingBox]).optional(),
1166
+ // SVG's own initial values — what a <mask> does when the attribute is not there.
1167
+ maskType: px.enum([PxMaskType.luminance, PxMaskType.alpha], PxMaskType.luminance).optional(),
1168
+ maskUnits: px.enum([PxUnits.userSpaceOnUse, PxUnits.objectBoundingBox], PxUnits.objectBoundingBox).optional(),
1169
+ maskContentUnits: px.enum([PxUnits.userSpaceOnUse, PxUnits.objectBoundingBox], PxUnits.userSpaceOnUse).optional(),
1112
1170
  x: px.number().optional(),
1113
1171
  y: px.number().optional(),
1114
1172
  width: px.number().optional(),
@@ -1120,7 +1178,7 @@ var PxClipPathEffectSchema = implementsInterface()(px.object({
1120
1178
  var PxStrokeTrimEffectSchema = implementsInterface()(px.object({
1121
1179
  offset: PxAnimatableNumberSchema.optional(),
1122
1180
  range: PxAnimatableVec2Schema.optional(),
1123
- subPaths: px.enum([PxStrokeTrimSubPaths.separate, PxStrokeTrimSubPaths.combined]).optional()
1181
+ subPaths: px.enum([PxStrokeTrimSubPaths.separate, PxStrokeTrimSubPaths.combined], PxStrokeTrimSubPaths.separate).optional()
1124
1182
  }));
1125
1183
  var PxRetimeEffectSchema = implementsInterface()(px.object({
1126
1184
  start: px.number().optional(),
@@ -1152,17 +1210,20 @@ var PxFillGradientEffectSchema = implementsInterface()(px.object({
1152
1210
  radius: PxAnimatableNumberSchema.optional(),
1153
1211
  focal: PxAnimatableVec2Schema.optional(),
1154
1212
  stops: PxAnimatableGradientStopsSchema.optional(),
1155
- gradientUnits: px.enum([PxUnits.userSpaceOnUse, PxUnits.objectBoundingBox]).optional(),
1156
- spreadMethod: px.enum([PxGradientSpreadMethod.pad, PxGradientSpreadMethod.reflect, PxGradientSpreadMethod.repeat]).optional(),
1213
+ // SVG's own initial values — what a gradient does when the attribute is not there.
1214
+ gradientUnits: px.enum([PxUnits.userSpaceOnUse, PxUnits.objectBoundingBox], PxUnits.objectBoundingBox).optional(),
1215
+ spreadMethod: px.enum([PxGradientSpreadMethod.pad, PxGradientSpreadMethod.reflect, PxGradientSpreadMethod.repeat], PxGradientSpreadMethod.pad).optional(),
1157
1216
  gradientTransform: px.string().optional()
1158
1217
  }));
1159
1218
  var PxStrokeGradientEffectSchema = PxFillGradientEffectSchema;
1160
1219
  var PxTextPathEffectSchema = implementsInterface()(px.object({
1161
1220
  pathData: px.string(),
1162
- pathOverflow: px.enum([PxPathOverflow.clip, PxPathOverflow.extend]).optional(),
1163
- lengthAdjust: px.enum([PxLengthAdjust.spacing, PxLengthAdjust.spacingAndGlyphs]).optional(),
1164
- method: px.enum([PxTextPathMethod.align, PxTextPathMethod.stretch]).optional(),
1165
- spacing: px.enum([PxTextPathSpacing.auto, PxTextPathSpacing.exact]).optional(),
1221
+ // `extend` is what an omitted pathOverflow means (glyphs continue along the tangent); the
1222
+ // other three are SVG's own initial values for the native <textPath> attributes.
1223
+ pathOverflow: px.enum([PxPathOverflow.clip, PxPathOverflow.extend], PxPathOverflow.extend).optional(),
1224
+ lengthAdjust: px.enum([PxLengthAdjust.spacing, PxLengthAdjust.spacingAndGlyphs], PxLengthAdjust.spacing).optional(),
1225
+ method: px.enum([PxTextPathMethod.align, PxTextPathMethod.stretch], PxTextPathMethod.align).optional(),
1226
+ spacing: px.enum([PxTextPathSpacing.auto, PxTextPathSpacing.exact], PxTextPathSpacing.exact).optional(),
1166
1227
  startOffset: PxAnimatableNumberSchema.optional(),
1167
1228
  textLength: PxAnimatableNumberSchema.optional()
1168
1229
  }));
@@ -1407,6 +1468,11 @@ function interpolateBezier(path1, path2, progress) {
1407
1468
  }
1408
1469
  return { v, i: i.length ? i : void 0, o: o.length ? o : void 0, c: (_i = path1.c) != null ? _i : path2.c };
1409
1470
  }
1471
+ function remap(value, inMin, inMax, outMin, outMax) {
1472
+ if (inMax === inMin) return outMin;
1473
+ const t = (value - inMin) / (inMax - inMin);
1474
+ return outMin + t * (outMax - outMin);
1475
+ }
1410
1476
  function solveCubicBezierX(p1x, p2x, x) {
1411
1477
  if (x <= 0) return 0;
1412
1478
  if (x >= 1) return 1;
@@ -1511,6 +1577,43 @@ function toRGBA(color) {
1511
1577
  const b = Math.round(color[2] * 255);
1512
1578
  return color.length === 4 ? "rgba(" + r + "," + g + "," + b + "," + color[3] + ")" : "rgb(" + r + "," + g + "," + b + ")";
1513
1579
  }
1580
+ function parseRgba(s) {
1581
+ var _a2;
1582
+ const inner = (_a2 = s.match(/rgba?\((.*)\)/)) == null ? void 0 : _a2[1];
1583
+ if (!inner) throw new Error("Invalid rgb/rgba format");
1584
+ const parts = inner.split(",").map((v) => +v.trim());
1585
+ return [parts[0] / 255, parts[1] / 255, parts[2] / 255, ...parts[3] !== void 0 ? [parts[3]] : []];
1586
+ }
1587
+ function parseHex(s) {
1588
+ const hex = s.slice(1);
1589
+ const isShort = hex.length <= 4;
1590
+ const r = isShort ? hex[0] + hex[0] : hex.slice(0, 2);
1591
+ const g = isShort ? hex[1] + hex[1] : hex.slice(2, 4);
1592
+ const b = isShort ? hex[2] + hex[2] : hex.slice(4, 6);
1593
+ const a = hex.length === 4 ? hex[3] + hex[3] : hex.length === 8 ? hex.slice(6, 8) : null;
1594
+ const result = [
1595
+ parseInt(r, 16) / 255,
1596
+ parseInt(g, 16) / 255,
1597
+ parseInt(b, 16) / 255
1598
+ ];
1599
+ if (a !== null) {
1600
+ result.push(parseInt(a, 16) / 255);
1601
+ }
1602
+ return result;
1603
+ }
1604
+ function parseColor(s) {
1605
+ if (!s) return void 0;
1606
+ if (Array.isArray(s)) return s;
1607
+ if (typeof s !== "string") return void 0;
1608
+ if (s.startsWith("#")) {
1609
+ return parseHex(s);
1610
+ } else if (s.startsWith("rgb")) {
1611
+ return parseRgba(s);
1612
+ } else {
1613
+ console.warn("Unsupported color format: " + s);
1614
+ }
1615
+ return void 0;
1616
+ }
1514
1617
  var PX_COLOR_ATTR_NAMES = /* @__PURE__ */ new Set(["color", "fill", "flood-color", "lighting-color", "stop-color", "stroke"]);
1515
1618
  var PX_TRANSFORM_FN_NAMES = /* @__PURE__ */ new Set(["translate", "rotate", "scale", "skew"]);
1516
1619
  var PX_PCT_BASED_ATTR_NAMES = /* @__PURE__ */ new Set(["offset-distance", "offsetDistance"]);
@@ -1567,10 +1670,12 @@ function parseTransformParts(str2) {
1567
1670
  return Object.keys(out).length ? out : void 0;
1568
1671
  }
1569
1672
  var PX_STYLE_ATTR_NAMES = /* @__PURE__ */ new Set(["offset-distance", "offsetDistance"]);
1570
- var PX_DEFAULT_DURATION_MS = 1e3;
1571
1673
  function kebabToCamelCaseWord(kebab) {
1572
1674
  return kebab.includes("-") ? kebab.replace(/-([a-z])/g, (_, letter) => letter.toUpperCase()) : kebab;
1573
1675
  }
1676
+ function isCamelCaseWord(word) {
1677
+ return !word.includes("-") && /[a-z][A-Z]/.test(word);
1678
+ }
1574
1679
  var SVG_CAMEL_CASE_ATTRS = /* @__PURE__ */ new Set([
1575
1680
  // Transform/positioning
1576
1681
  "viewBox",
@@ -1726,6 +1831,38 @@ function invertEasing(easing) {
1726
1831
  return cubicBezier(flipped);
1727
1832
  }
1728
1833
 
1834
+ // src/animation/PxStaticTransformMerge.ts
1835
+ function mergeStaticTransformIntoAnimDef(animDef, staticTransform) {
1836
+ if (!animDef) return animDef;
1837
+ const staticParts = staticTransform && typeof staticTransform === "object" && !Array.isArray(staticTransform) ? staticTransform : parseTransformParts(staticTransform);
1838
+ if (!staticParts || !Object.keys(staticParts).length) return animDef;
1839
+ const mergeKfValue = (v) => v && typeof v === "object" && !Array.isArray(v) ? __spreadValues(__spreadValues({}, staticParts), v) : v;
1840
+ const transformAnim = animDef[TRANSFORM_ATTR];
1841
+ if (transformAnim && typeof transformAnim === "object") {
1842
+ const anim = transformAnim;
1843
+ if (Array.isArray(anim.keyframes)) {
1844
+ const out = __spreadProps(__spreadValues({}, anim), {
1845
+ keyframes: anim.keyframes.map((kf) => __spreadProps(__spreadValues({}, kf), { value: mergeKfValue(kf.value) }))
1846
+ });
1847
+ if (out.value !== void 0) out.value = mergeKfValue(out.value);
1848
+ return __spreadProps(__spreadValues({}, animDef), { transform: out });
1849
+ }
1850
+ return animDef;
1851
+ }
1852
+ const channels = Object.keys(animDef).filter((k) => PX_TRANSFORM_FN_NAMES.has(k));
1853
+ if (channels.length !== 1) return animDef;
1854
+ const ch = channels[0];
1855
+ const chAnim = animDef[ch];
1856
+ if (!chAnim || typeof chAnim !== "object" || !Array.isArray(chAnim.keyframes)) return animDef;
1857
+ const lifted = __spreadProps(__spreadValues({}, chAnim), {
1858
+ keyframes: chAnim.keyframes.map((kf) => __spreadProps(__spreadValues({}, kf), { value: __spreadProps(__spreadValues({}, staticParts), { [ch]: kf.value }) }))
1859
+ });
1860
+ if (lifted.value !== void 0) lifted.value = __spreadProps(__spreadValues({}, staticParts), { [ch]: lifted.value });
1861
+ const rest = __spreadValues({}, animDef);
1862
+ delete rest[ch];
1863
+ return __spreadProps(__spreadValues({}, rest), { transform: lifted });
1864
+ }
1865
+
1729
1866
  // src/materialize/PxMotionPath.ts
1730
1867
  function getKfTranslate(kf) {
1731
1868
  const v = keyframeValue(kf);
@@ -1777,6 +1914,33 @@ function getSegmentCache(prevKf, nextKf, prevPos, nextPos) {
1777
1914
  byNext.set(nextKf, entry);
1778
1915
  return entry;
1779
1916
  }
1917
+ function evaluateMotionPathSegment(prevKf, nextKf, prevPos, nextPos, localProgress, autoOrient) {
1918
+ const seg = getSegmentCache(prevKf, nextKf, prevPos, nextPos);
1919
+ const t = seg.totalArc === 0 ? localProgress : tFromArcFraction(seg.lut, localProgress);
1920
+ const point = bezier2D_pointAt(seg.P0, seg.P1, seg.P2, seg.P3, t);
1921
+ if (!autoOrient) return { translate: point };
1922
+ const tan = bezier2D_derivativeAt(seg.P0, seg.P1, seg.P2, seg.P3, t);
1923
+ const rotateDeg = Math.atan2(tan[1], tan[0]) * 180 / Math.PI;
1924
+ return { translate: point, rotateDeg };
1925
+ }
1926
+ function tFromArcFraction(lut, arcFrac) {
1927
+ const total = lut.ds[lut.ds.length - 1];
1928
+ const target = arcFrac * total;
1929
+ const { ts, ds } = lut;
1930
+ const last = ds.length - 1;
1931
+ if (target <= 0) return ts[0];
1932
+ if (target >= ds[last]) return ts[last];
1933
+ let lo = 1, hi = last;
1934
+ while (lo < hi) {
1935
+ const mid = lo + hi >>> 1;
1936
+ if (ds[mid] < target) lo = mid + 1;
1937
+ else hi = mid;
1938
+ }
1939
+ const dPrev = ds[hi - 1];
1940
+ const span = ds[hi] - dPrev;
1941
+ const frac = span > 0 ? (target - dPrev) / span : 0;
1942
+ return ts[hi - 1] + frac * (ts[hi] - ts[hi - 1]);
1943
+ }
1780
1944
  var DEFAULT_FLATNESS_TOL = 0.5;
1781
1945
  var DEFAULT_ROTATION_TOL = 5;
1782
1946
  var DEFAULT_MAX_SAMPLES = 32;
@@ -2049,9 +2213,211 @@ function perpDist(q, pA, pB) {
2049
2213
  const cross = (q[0] - pA[0]) * dy - (q[1] - pA[1]) * dx;
2050
2214
  return Math.abs(cross) / Math.sqrt(len2);
2051
2215
  }
2216
+ function materializeMotionPathsInTree(root, opts) {
2217
+ const out = walkAndMaterialize(root, opts);
2218
+ return out != null ? out : root;
2219
+ }
2220
+ function walkAndMaterialize(node, opts) {
2221
+ let newChildren;
2222
+ if (node.children) {
2223
+ for (let i = 0; i < node.children.length; i++) {
2224
+ const ch = node.children[i];
2225
+ const ret = walkAndMaterialize(ch, opts);
2226
+ if (ret !== null) {
2227
+ if (!newChildren) newChildren = node.children.slice();
2228
+ newChildren[i] = ret;
2229
+ }
2230
+ }
2231
+ }
2232
+ let newAnimate;
2233
+ const animBucket = node.animate;
2234
+ if (animBucket && typeof animBucket === "object" && !Array.isArray(animBucket)) {
2235
+ const animDef = mergeStaticTransformIntoAnimDef(animBucket, node.transform);
2236
+ const transformAnim = animDef.transform;
2237
+ if (transformAnim && typeof transformAnim === "object" && propAnimIsMotionPath(transformAnim)) {
2238
+ const materialized = materializeMotionPathInPropAnim(transformAnim, opts);
2239
+ if (materialized !== transformAnim) {
2240
+ newAnimate = __spreadProps(__spreadValues({}, animDef), { transform: materialized });
2241
+ }
2242
+ }
2243
+ }
2244
+ if (!newChildren && !newAnimate) return null;
2245
+ const cloned = __spreadValues({}, node);
2246
+ if (newChildren) cloned.children = newChildren;
2247
+ if (newAnimate) cloned.animate = newAnimate;
2248
+ return cloned;
2249
+ }
2052
2250
 
2053
2251
  // src/animation/PxDefinitions.ts
2054
2252
  var PX_LOOP_JUMP_SHIFT_MS = 1;
2253
+ function deepEqualValue(a, b) {
2254
+ if (a === b) return true;
2255
+ if (typeof a !== typeof b || a === null || b === null || typeof a !== "object") return false;
2256
+ if (Array.isArray(a) !== Array.isArray(b)) return false;
2257
+ const ka = Object.keys(a);
2258
+ const kb = Object.keys(b);
2259
+ if (ka.length !== kb.length) return false;
2260
+ return ka.every((k) => deepEqualValue(a[k], b[k]));
2261
+ }
2262
+ function parsePathCommands(d) {
2263
+ const tokens = d.split(/([MLCZmlcz]|[\s,]+)/).map((t) => t.trim()).filter((t) => t && t !== ",");
2264
+ const commands = [];
2265
+ let currentCommand = null;
2266
+ for (const token of tokens) {
2267
+ if (/[MLCZmlcz]/.test(token)) {
2268
+ currentCommand = { type: token, values: [] };
2269
+ commands.push(currentCommand);
2270
+ } else if (currentCommand) {
2271
+ const value = +token;
2272
+ currentCommand.values.push(Number.isNaN(value) ? 0 : value);
2273
+ }
2274
+ }
2275
+ return commands;
2276
+ }
2277
+ function parseSvgPathToBezier(d) {
2278
+ const res = [];
2279
+ let currentPath;
2280
+ const commands = parsePathCommands(d);
2281
+ for (const command of commands) {
2282
+ const type = command.type;
2283
+ const values = command.values;
2284
+ if (type === "M" || type === "m") {
2285
+ const x = values[0] || 0;
2286
+ const y = values[1] || 0;
2287
+ currentPath = {
2288
+ v: [[x, y]],
2289
+ i: [[x, y]],
2290
+ o: [[x, y]],
2291
+ c: false
2292
+ };
2293
+ res.push(currentPath);
2294
+ continue;
2295
+ }
2296
+ if (!currentPath) {
2297
+ currentPath = {
2298
+ v: [[0, 0]],
2299
+ i: [[0, 0]],
2300
+ o: [[0, 0]],
2301
+ c: false
2302
+ };
2303
+ res.push(currentPath);
2304
+ }
2305
+ if (type === "L") {
2306
+ const x = values[0] || 0;
2307
+ const y = values[1] || 0;
2308
+ currentPath.v.push([x, y]);
2309
+ currentPath.i.push([x, y]);
2310
+ currentPath.o.push([x, y]);
2311
+ } else if (type === "C") {
2312
+ const outX = values[0] || 0;
2313
+ const outY = values[1] || 0;
2314
+ const inX2 = values[2] || 0;
2315
+ const inY2 = values[3] || 0;
2316
+ const x2 = values[4] || 0;
2317
+ const y2 = values[5] || 0;
2318
+ currentPath.o[currentPath.o.length - 1] = [outX, outY];
2319
+ currentPath.v.push([x2, y2]);
2320
+ currentPath.i.push([inX2, inY2]);
2321
+ currentPath.o.push([x2, y2]);
2322
+ } else if (type === "Z" || type === "z") {
2323
+ currentPath.c = true;
2324
+ } else {
2325
+ console.warn('Unsupported path command "' + type + '"');
2326
+ }
2327
+ }
2328
+ return res;
2329
+ }
2330
+ function extractPathData(str2) {
2331
+ if (str2.startsWith("path(") && str2.endsWith(")")) {
2332
+ return str2.slice(5, -1);
2333
+ }
2334
+ if (/^[MmZzLlHhVvCcSsQqTtAa]/.test(str2)) {
2335
+ return str2;
2336
+ }
2337
+ return void 0;
2338
+ }
2339
+ function isPathString(value) {
2340
+ return typeof value === "string" && extractPathData(value) !== void 0;
2341
+ }
2342
+ function normalizePathValue(value) {
2343
+ if (value && typeof value === "object" && typeof value.pathData === "string") {
2344
+ const d = extractPathData(value.pathData);
2345
+ return d ? { paths: parseSvgPathToBezier(d) } : value;
2346
+ }
2347
+ if (value && typeof value === "object" && "paths" in value) {
2348
+ const pathsArray = value.paths;
2349
+ if (Array.isArray(pathsArray) && pathsArray.length > 0) {
2350
+ if (isPathString(pathsArray[0])) {
2351
+ const paths = [];
2352
+ for (const pathStr2 of pathsArray) {
2353
+ const d = extractPathData(pathStr2);
2354
+ if (d) {
2355
+ paths.push(...parseSvgPathToBezier(d));
2356
+ }
2357
+ }
2358
+ return { paths };
2359
+ }
2360
+ }
2361
+ return value;
2362
+ }
2363
+ if (Array.isArray(value)) {
2364
+ if (value.length > 0 && isPathString(value[0])) {
2365
+ const paths = [];
2366
+ for (const pathStr2 of value) {
2367
+ const d = extractPathData(pathStr2);
2368
+ if (d) {
2369
+ paths.push(...parseSvgPathToBezier(d));
2370
+ }
2371
+ }
2372
+ return { paths };
2373
+ }
2374
+ return { paths: value };
2375
+ }
2376
+ if (isPathString(value)) {
2377
+ const d = extractPathData(value);
2378
+ return { paths: parseSvgPathToBezier(d) };
2379
+ }
2380
+ return value;
2381
+ }
2382
+ function resolveEasing(easing, defs) {
2383
+ var _a2;
2384
+ if (!easing) return void 0;
2385
+ if (Array.isArray(easing)) {
2386
+ return easing;
2387
+ }
2388
+ if ((_a2 = defs == null ? void 0 : defs.easings) == null ? void 0 : _a2[easing]) {
2389
+ return defs.easings[easing];
2390
+ }
2391
+ console.warn("Unknown easing name: " + easing);
2392
+ return void 0;
2393
+ }
2394
+ function resolveAnimation(animRef, defs) {
2395
+ var _a2;
2396
+ if (typeof animRef === "string") {
2397
+ const resolved = (_a2 = defs == null ? void 0 : defs.animations) == null ? void 0 : _a2[animRef];
2398
+ if (!resolved) {
2399
+ console.warn("Unknown animation name: " + animRef);
2400
+ }
2401
+ return resolved;
2402
+ }
2403
+ return animRef;
2404
+ }
2405
+ function resolveElementAnimation(animate, defs) {
2406
+ if (!animate) return [];
2407
+ const results = [];
2408
+ if (typeof animate === "string") {
2409
+ const resolved = resolveAnimation(animate, defs);
2410
+ if (resolved) results.push(resolved);
2411
+ } else if (Array.isArray(animate)) {
2412
+ for (const item of animate) {
2413
+ const resolved = resolveAnimation(item, defs);
2414
+ if (resolved) results.push(resolved);
2415
+ }
2416
+ } else {
2417
+ results.push(animate);
2418
+ }
2419
+ return results;
2420
+ }
2055
2421
  function interpolateValue(propName, a, b, t) {
2056
2422
  var _a2, _b;
2057
2423
  if (propName === "d") {
@@ -2090,35 +2456,498 @@ function interpolateTransformParts(a, b, t) {
2090
2456
  }
2091
2457
  return out;
2092
2458
  }
2093
- function mergeStaticTransformIntoAnimDef(animDef, staticTransform) {
2094
- if (!animDef) return animDef;
2095
- const staticParts = staticTransform && typeof staticTransform === "object" && !Array.isArray(staticTransform) ? staticTransform : parseTransformParts(staticTransform);
2096
- if (!staticParts || !Object.keys(staticParts).length) return animDef;
2097
- const mergeKfValue = (v) => v && typeof v === "object" && !Array.isArray(v) ? __spreadValues(__spreadValues({}, staticParts), v) : v;
2098
- const transformAnim = animDef[TRANSFORM_ATTR];
2099
- if (transformAnim && typeof transformAnim === "object") {
2100
- const anim = transformAnim;
2101
- if (Array.isArray(anim.keyframes)) {
2102
- const out = __spreadProps(__spreadValues({}, anim), {
2103
- keyframes: anim.keyframes.map((kf) => __spreadProps(__spreadValues({}, kf), { value: mergeKfValue(kf.value) }))
2104
- });
2105
- if (out.value !== void 0) out.value = mergeKfValue(out.value);
2106
- return __spreadProps(__spreadValues({}, animDef), { transform: out });
2459
+ function expandLoopKeyframes(propName, keyframes, loop, duration) {
2460
+ var _a2, _b, _c, _d, _e;
2461
+ const totalIntervals = keyframes.length - 1;
2462
+ const segCount = clamp((_a2 = loop.segmentCount) != null ? _a2 : totalIntervals, 1, totalIntervals);
2463
+ let segKfs;
2464
+ if (loop.repeatAt === PxLoopRepeatAt.start) {
2465
+ segKfs = keyframes.slice(0, segCount + 1);
2466
+ } else {
2467
+ segKfs = keyframes.slice(totalIntervals - segCount);
2468
+ }
2469
+ const firstT = (_b = keyframes[0].t) != null ? _b : 0;
2470
+ const lastT = (_c = keyframes[keyframes.length - 1].t) != null ? _c : 0;
2471
+ let fillStart, fillEnd;
2472
+ if (loop.repeatAt === PxLoopRepeatAt.start) {
2473
+ fillStart = 0;
2474
+ fillEnd = firstT;
2475
+ } else {
2476
+ fillStart = lastT;
2477
+ fillEnd = duration;
2478
+ }
2479
+ const fillDuration = fillEnd - fillStart;
2480
+ if (fillDuration <= 0) return keyframes;
2481
+ const segStartT = (_d = segKfs[0].t) != null ? _d : 0;
2482
+ const segEndT = (_e = segKfs[segKfs.length - 1].t) != null ? _e : 0;
2483
+ const segDuration = segEndT - segStartT;
2484
+ if (segDuration <= 0) return keyframes;
2485
+ const template = segKfs.map((kf) => ({
2486
+ relT: (kf.t - segStartT) / segDuration,
2487
+ v: kf.v,
2488
+ e: kf.e,
2489
+ tangentIn: keyframeTangentIn(kf),
2490
+ tangentOut: keyframeTangentOut(kf)
2491
+ }));
2492
+ const fullReps = Math.floor(fillDuration / segDuration);
2493
+ const remainder = fillDuration - fullReps * segDuration;
2494
+ const partialFraction = remainder / segDuration;
2495
+ const looped = [];
2496
+ const separateBoundary = loop.repeatAt !== PxLoopRepeatAt.start;
2497
+ const originalTerminalKf = keyframes[keyframes.length - 1];
2498
+ let terminalEasingOverride;
2499
+ let hasTerminalEasingOverride = false;
2500
+ function appendRep(repStart, isReversed, partial) {
2501
+ var _a3;
2502
+ let entries;
2503
+ if (isReversed) {
2504
+ entries = [];
2505
+ for (let i = template.length - 1; i >= 0; i--) {
2506
+ entries.push({
2507
+ relT: 1 - template[i].relT,
2508
+ v: template[i].v,
2509
+ // Easing for reversed transition: use reversed easing from the forward "from" keyframe
2510
+ e: i > 0 ? reverseEasing(template[i - 1].e) : void 0,
2511
+ // Reversed traversal swaps each vertex's in/out spatial tangents
2512
+ // (geometry is identical, walked backwards), so curvature and
2513
+ // auto-orientation survive the reversed rep.
2514
+ tangentIn: template[i].tangentOut,
2515
+ tangentOut: template[i].tangentIn
2516
+ });
2517
+ }
2518
+ } else {
2519
+ entries = template;
2520
+ }
2521
+ const cutRelT = partial !== void 0 ? partial : 1;
2522
+ for (let i = 0; i < entries.length; i++) {
2523
+ const entry = entries[i];
2524
+ if (entry.relT > cutRelT + 1e-9) {
2525
+ const prev = entries[i - 1];
2526
+ const intervalSpan = entry.relT - prev.relT;
2527
+ const localFrac = (cutRelT - prev.relT) / intervalSpan;
2528
+ const easedFrac = prev.e ? cubicBezier(prev.e)(localFrac) : localFrac;
2529
+ const cutValue = interpolateValue(propName, prev.v, entry.v, easedFrac);
2530
+ const { left: leftEasing } = splitEasing(prev.e, localFrac);
2531
+ if (looped.length > 0 && prev.relT <= cutRelT) {
2532
+ looped[looped.length - 1].e = leftEasing;
2533
+ }
2534
+ looped.push({ t: repStart + cutRelT * segDuration, v: cutValue, e: void 0 });
2535
+ return;
2536
+ }
2537
+ const prevKf = looped.length > 0 ? looped[looped.length - 1] : originalTerminalKf;
2538
+ const isBoundary = separateBoundary && i === 0 && prevKf !== void 0 && Math.abs(((_a3 = prevKf.t) != null ? _a3 : 0) - (repStart + entry.relT * segDuration)) < 1e-9;
2539
+ if (isBoundary) {
2540
+ if (deepEqualValue(prevKf.v, entry.v)) {
2541
+ if (looped.length > 0) {
2542
+ prevKf.e = entry.e;
2543
+ prevKf.tangentOut = entry.tangentOut;
2544
+ } else {
2545
+ terminalEasingOverride = entry.e;
2546
+ hasTerminalEasingOverride = true;
2547
+ }
2548
+ continue;
2549
+ }
2550
+ if (looped.length > 0) {
2551
+ delete prevKf.tangentIn;
2552
+ delete prevKf.tangentOut;
2553
+ }
2554
+ }
2555
+ const pushed = {
2556
+ t: repStart + entry.relT * segDuration + (isBoundary ? PX_LOOP_JUMP_SHIFT_MS : 0),
2557
+ v: entry.v,
2558
+ e: i < entries.length - 1 ? entry.e : void 0
2559
+ };
2560
+ if (entry.tangentIn) pushed.tangentIn = entry.tangentIn;
2561
+ if (entry.tangentOut) pushed.tangentOut = entry.tangentOut;
2562
+ looped.push(pushed);
2107
2563
  }
2108
- return animDef;
2109
2564
  }
2110
- const channels = Object.keys(animDef).filter((k) => PX_TRANSFORM_FN_NAMES.has(k));
2111
- if (channels.length !== 1) return animDef;
2112
- const ch = channels[0];
2113
- const chAnim = animDef[ch];
2114
- if (!chAnim || typeof chAnim !== "object" || !Array.isArray(chAnim.keyframes)) return animDef;
2115
- const lifted = __spreadProps(__spreadValues({}, chAnim), {
2116
- keyframes: chAnim.keyframes.map((kf) => __spreadProps(__spreadValues({}, kf), { value: __spreadProps(__spreadValues({}, staticParts), { [ch]: kf.value }) }))
2565
+ function appendRepTail(repStart, isReversed, tailFraction) {
2566
+ let entries;
2567
+ if (isReversed) {
2568
+ entries = [];
2569
+ for (let i = template.length - 1; i >= 0; i--) {
2570
+ entries.push({
2571
+ relT: 1 - template[i].relT,
2572
+ v: template[i].v,
2573
+ e: i > 0 ? reverseEasing(template[i - 1].e) : void 0,
2574
+ tangentIn: template[i].tangentOut,
2575
+ tangentOut: template[i].tangentIn
2576
+ });
2577
+ }
2578
+ } else {
2579
+ entries = template;
2580
+ }
2581
+ const startRelT = 1 - tailFraction;
2582
+ for (let i = 0; i < entries.length; i++) {
2583
+ const entry = entries[i];
2584
+ if (entry.relT < startRelT - 1e-9) continue;
2585
+ const prev = entries[i - 1];
2586
+ if (prev && prev.relT < startRelT - 1e-9 && entry.relT > startRelT + 1e-9) {
2587
+ const intervalSpan = entry.relT - prev.relT;
2588
+ const localFrac = (startRelT - prev.relT) / intervalSpan;
2589
+ const easedFrac = prev.e ? cubicBezier(prev.e)(localFrac) : localFrac;
2590
+ const startValue = interpolateValue(propName, prev.v, entry.v, easedFrac);
2591
+ const { right: rightEasing } = splitEasing(prev.e, localFrac);
2592
+ looped.push({ t: repStart, v: startValue, e: rightEasing });
2593
+ }
2594
+ const pushed = {
2595
+ t: repStart + (entry.relT - startRelT) * segDuration,
2596
+ v: entry.v,
2597
+ e: i < entries.length - 1 ? entry.e : void 0
2598
+ };
2599
+ if (entry.tangentIn) pushed.tangentIn = entry.tangentIn;
2600
+ if (entry.tangentOut) pushed.tangentOut = entry.tangentOut;
2601
+ looped.push(pushed);
2602
+ }
2603
+ }
2604
+ if (loop.repeatAt === PxLoopRepeatAt.start) {
2605
+ if (partialFraction > 1e-9) {
2606
+ const isReversed = loop.direction === PxLoopDirection.alternate && fullReps % 2 === 0;
2607
+ appendRepTail(fillStart, isReversed, partialFraction);
2608
+ }
2609
+ for (let rep = 0; rep < fullReps; rep++) {
2610
+ const distFromBoundary = fullReps - 1 - rep;
2611
+ const isReversed = loop.direction === PxLoopDirection.alternate && distFromBoundary % 2 === 0;
2612
+ const repStart = fillStart + remainder + rep * segDuration;
2613
+ appendRep(repStart, isReversed);
2614
+ }
2615
+ } else {
2616
+ for (let rep = 0; rep < fullReps; rep++) {
2617
+ const isReversed = loop.direction === PxLoopDirection.alternate && rep % 2 === 0;
2618
+ const repStart = fillStart + rep * segDuration;
2619
+ appendRep(repStart, isReversed);
2620
+ }
2621
+ if (partialFraction > 1e-9) {
2622
+ const isReversed = loop.direction === PxLoopDirection.alternate && fullReps % 2 === 0;
2623
+ const repStart = fillStart + fullReps * segDuration;
2624
+ appendRep(repStart, isReversed, partialFraction);
2625
+ }
2626
+ }
2627
+ if (loop.repeatAt === PxLoopRepeatAt.start) {
2628
+ return [...looped, ...keyframes];
2629
+ } else {
2630
+ if (hasTerminalEasingOverride && keyframes.length > 0) {
2631
+ const head = keyframes.slice(0, -1);
2632
+ const tail = __spreadProps(__spreadValues({}, keyframes[keyframes.length - 1]), { e: terminalEasingOverride });
2633
+ return [...head, tail, ...looped];
2634
+ }
2635
+ return [...keyframes, ...looped];
2636
+ }
2637
+ }
2638
+ function normalizeKeyframes(propName, propAnim, duration, defs) {
2639
+ var _a2;
2640
+ const keyframes = propAnim.keyframes || [];
2641
+ const normalized = [];
2642
+ for (const kf of keyframes) {
2643
+ const timePct = keyframeTime(kf);
2644
+ let value = keyframeValue(kf);
2645
+ const easing = keyframeEasing(kf);
2646
+ if (propName === "d") {
2647
+ value = normalizePathValue(value);
2648
+ }
2649
+ const propNameKebab = isCamelCaseWord(propName) ? camelCaseToKebabWordIfNeeded(propName) : propName;
2650
+ if (PX_COLOR_ATTR_NAMES.has(propNameKebab)) {
2651
+ value = (_a2 = parseColor(value)) != null ? _a2 : value;
2652
+ }
2653
+ const normKf = {
2654
+ t: timePct,
2655
+ v: value,
2656
+ e: resolveEasing(easing, defs)
2657
+ };
2658
+ const tIn = keyframeTangentIn(kf);
2659
+ const tOut = keyframeTangentOut(kf);
2660
+ if (tIn) normKf.tangentIn = tIn;
2661
+ if (tOut) normKf.tangentOut = tOut;
2662
+ normalized.push(normKf);
2663
+ }
2664
+ normalized.sort((a, b) => {
2665
+ var _a3, _b;
2666
+ return ((_a3 = a.t) != null ? _a3 : 0) - ((_b = b.t) != null ? _b : 0);
2117
2667
  });
2118
- if (lifted.value !== void 0) lifted.value = __spreadProps(__spreadValues({}, staticParts), { [ch]: lifted.value });
2119
- const rest = __spreadValues({}, animDef);
2120
- delete rest[ch];
2121
- return __spreadProps(__spreadValues({}, rest), { transform: lifted });
2668
+ const loopRaw = propAnim.loop;
2669
+ const loop = loopRaw === true ? {} : loopRaw || void 0;
2670
+ if (loop && normalized.length >= 2) {
2671
+ return expandLoopKeyframes(propName, normalized, loop, duration);
2672
+ }
2673
+ return normalized;
2674
+ }
2675
+ function mergeAnimationDefinitions(animations) {
2676
+ const merged = {};
2677
+ for (const anim of animations) {
2678
+ for (const [prop, propAnim] of Object.entries(anim)) {
2679
+ merged[prop] = propAnim;
2680
+ }
2681
+ }
2682
+ return merged;
2683
+ }
2684
+ function materializeInternalLoopsInPropAnim(propName, propAnim, duration) {
2685
+ const loopRaw = propAnim.loop;
2686
+ if (loopRaw === void 0 || loopRaw === null || loopRaw === false) return propAnim;
2687
+ const loop = loopRaw === true ? {} : loopRaw;
2688
+ const rawKfs = propAnim.keyframes;
2689
+ if (!Array.isArray(rawKfs) || rawKfs.length < 2) return propAnim;
2690
+ const propNameKebab = isCamelCaseWord(propName) ? camelCaseToKebabWordIfNeeded(propName) : propName;
2691
+ const isColor = PX_COLOR_ATTR_NAMES.has(propNameKebab);
2692
+ const kfs = rawKfs.map((kf) => {
2693
+ var _a2;
2694
+ const t = keyframeTime(kf);
2695
+ let v = keyframeValue(kf);
2696
+ if (propName === "d") v = normalizePathValue(v);
2697
+ if (isColor) v = (_a2 = parseColor(v)) != null ? _a2 : v;
2698
+ const e = keyframeEasing(kf);
2699
+ const out2 = { t, v, e };
2700
+ const tIn = keyframeTangentIn(kf);
2701
+ const tOut = keyframeTangentOut(kf);
2702
+ if (tIn) out2.tangentIn = tIn;
2703
+ if (tOut) out2.tangentOut = tOut;
2704
+ return out2;
2705
+ });
2706
+ const expanded = expandLoopKeyframes(propName, kfs, loop, duration);
2707
+ const out = { keyframes: expanded };
2708
+ if (propAnim.autoOrient !== void 0) out.autoOrient = propAnim.autoOrient;
2709
+ return out;
2710
+ }
2711
+ function materializeInternalLoopsInTree(root, duration) {
2712
+ const ret = walkAndMaterializeLoops(root, duration);
2713
+ return ret != null ? ret : root;
2714
+ }
2715
+ function walkAndMaterializeLoops(node, duration) {
2716
+ let newChildren;
2717
+ if (node.children) {
2718
+ for (let i = 0; i < node.children.length; i++) {
2719
+ const ret = walkAndMaterializeLoops(node.children[i], duration);
2720
+ if (ret !== null) {
2721
+ if (!newChildren) newChildren = node.children.slice();
2722
+ newChildren[i] = ret;
2723
+ }
2724
+ }
2725
+ }
2726
+ let newAnimate;
2727
+ const animBucket = node.animate;
2728
+ if (animBucket && typeof animBucket === "object" && !Array.isArray(animBucket)) {
2729
+ const animDef = animBucket;
2730
+ for (const propName of Object.keys(animDef)) {
2731
+ const propAnim = animDef[propName];
2732
+ const materialized = materializeInternalLoopsInPropAnim(propName, propAnim, duration);
2733
+ if (materialized !== propAnim) {
2734
+ if (!newAnimate) newAnimate = __spreadValues({}, animDef);
2735
+ newAnimate[propName] = materialized;
2736
+ }
2737
+ }
2738
+ }
2739
+ if (!newChildren && !newAnimate) return null;
2740
+ const cloned = __spreadValues({}, node);
2741
+ if (newChildren) cloned.children = newChildren;
2742
+ if (newAnimate) cloned.animate = newAnimate;
2743
+ return cloned;
2744
+ }
2745
+ var _elementIdCounter = 0;
2746
+ function generateElementId() {
2747
+ return "_px_el_" + ++_elementIdCounter;
2748
+ }
2749
+ function normalizeAnimationDefinition(animDef, duration, defs, engine = PxTimelineEngine.native) {
2750
+ const normalized = {};
2751
+ for (const [propName, propAnim] of Object.entries(animDef)) {
2752
+ if (propName === "transform" && propAnim.alongPathMode === "offsetPath" && animDef["offsetDistance"] !== void 0) {
2753
+ continue;
2754
+ }
2755
+ const normalizedKfs = normalizeKeyframes(propName, propAnim, duration, defs);
2756
+ if (normalizedKfs.length > 0) {
2757
+ const out = { keyframes: normalizedKfs };
2758
+ if (propAnim.autoOrient !== void 0) out.autoOrient = propAnim.autoOrient;
2759
+ if (propAnim.loop !== void 0) out.loop = propAnim.loop;
2760
+ normalized[propName] = engine === PxTimelineEngine.native && propName === "transform" ? materializeMotionPathInPropAnim(out) : out;
2761
+ }
2762
+ }
2763
+ return normalized;
2764
+ }
2765
+ function normalizeBindings(doc, engine = PxTimelineEngine.native) {
2766
+ const animatorConfig = getAnimatorConfig(doc) || {};
2767
+ const defs = getDefinitions(doc);
2768
+ const duration = animatorConfig.duration || 1e3;
2769
+ const bindings = [];
2770
+ const processAnimation = (id, animDefs, staticTransform) => {
2771
+ if (animDefs.length === 0) return null;
2772
+ const merged = mergeStaticTransformIntoAnimDef(mergeAnimationDefinitions(animDefs), staticTransform);
2773
+ const normalizedAnim = normalizeAnimationDefinition(merged, duration, defs, engine);
2774
+ if (Object.keys(normalizedAnim).length === 0) return null;
2775
+ return {
2776
+ id,
2777
+ animate: normalizedAnim
2778
+ };
2779
+ };
2780
+ const docBindings = getBindings(doc);
2781
+ if (docBindings) {
2782
+ for (const binding of docBindings) {
2783
+ const id = binding.target.startsWith("#") ? binding.target.slice(1) : binding.target;
2784
+ const animDefs = binding.animateWith.map((name) => resolveAnimation(name, defs)).filter((d) => !!d);
2785
+ const normalized = processAnimation(id, animDefs);
2786
+ if (normalized) bindings.push(normalized);
2787
+ }
2788
+ }
2789
+ const processNode = (node) => {
2790
+ const inlineAnim = node.animate;
2791
+ if (inlineAnim && Object.keys(inlineAnim).length > 0) {
2792
+ const nodeId = node.id || generateElementId();
2793
+ node.id = nodeId;
2794
+ const normalized = processAnimation(nodeId, resolveElementAnimation(inlineAnim, defs), node.transform);
2795
+ if (normalized) bindings.push(normalized);
2796
+ }
2797
+ if (node.children) {
2798
+ for (let i = 0; i < node.children.length; i++) {
2799
+ processNode(node.children[i]);
2800
+ }
2801
+ }
2802
+ };
2803
+ if (doc.children) {
2804
+ for (let i = 0; i < doc.children.length; i++) {
2805
+ processNode(doc.children[i]);
2806
+ }
2807
+ }
2808
+ return bindings;
2809
+ }
2810
+ function getKeyframesPair(keyframes, progress) {
2811
+ var _a2, _b;
2812
+ const last = keyframes.length - 1;
2813
+ let prevKf = keyframes[0];
2814
+ let nextKf = keyframes[last > 0 ? 1 : 0];
2815
+ for (let j = 0; j < last; j++) {
2816
+ const aOff = (_a2 = keyframes[j].t) != null ? _a2 : 0;
2817
+ const bOff = (_b = keyframes[j + 1].t) != null ? _b : 0;
2818
+ if (aOff <= progress && progress <= bOff) {
2819
+ prevKf = keyframes[j];
2820
+ nextKf = keyframes[j + 1];
2821
+ break;
2822
+ }
2823
+ if (progress > bOff && j === last - 1) {
2824
+ prevKf = keyframes[last > 0 ? last - 1 : 0];
2825
+ nextKf = keyframes[last];
2826
+ }
2827
+ }
2828
+ return { prevKf, nextKf };
2829
+ }
2830
+ function calcPropertyValue(propName, propAnim, progress) {
2831
+ var _a2, _b, _c, _d, _e;
2832
+ const keyframes = propAnim.keyframes || [];
2833
+ if (keyframes.length === 0) return null;
2834
+ const { prevKf, nextKf } = getKeyframesPair(keyframes, progress);
2835
+ let localProgress = prevKf === nextKf ? 0 : remap(progress, (_a2 = prevKf.t) != null ? _a2 : 0, (_b = nextKf.t) != null ? _b : 0, 0, 1);
2836
+ localProgress = clamp(localProgress, 0, 1);
2837
+ const easing = keyframeEasing(prevKf);
2838
+ if (easing && Array.isArray(easing)) {
2839
+ try {
2840
+ localProgress = cubicBezier(easing)(localProgress);
2841
+ } catch (e) {
2842
+ }
2843
+ }
2844
+ let cssAttrName = isCamelCaseWord(propName) ? camelCaseToKebabWordIfNeeded(propName) : propName;
2845
+ let cssValue = null;
2846
+ const prevV = prevKf == null ? void 0 : prevKf.v;
2847
+ const nextV = nextKf == null ? void 0 : nextKf.v;
2848
+ if (cssAttrName === "d") {
2849
+ const prevPaths = (_c = prevV == null ? void 0 : prevV.paths) != null ? _c : Array.isArray(prevV) ? prevV : [];
2850
+ const nextPaths = (_d = nextV == null ? void 0 : nextV.paths) != null ? _d : Array.isArray(nextV) ? nextV : [];
2851
+ cssValue = interpolateBeziers(
2852
+ prevPaths,
2853
+ nextPaths,
2854
+ localProgress
2855
+ ).map((bz) => bezierToSvgPath(bz)).join("");
2856
+ } else if (PX_COLOR_ATTR_NAMES.has(cssAttrName)) {
2857
+ cssValue = toRGBA(interpolateColor(
2858
+ prevV || [0, 0, 0, 1],
2859
+ nextV || [0, 0, 0, 1],
2860
+ localProgress
2861
+ ));
2862
+ cssAttrName = propName;
2863
+ } else if (cssAttrName === "stroke-dasharray") {
2864
+ cssValue = interpolateVec(
2865
+ prevV || [],
2866
+ nextV || [],
2867
+ localProgress
2868
+ ).join(" ");
2869
+ cssAttrName = propName;
2870
+ } else if (cssAttrName === "transform" && prevV !== null && typeof prevV === "object" && !Array.isArray(prevV)) {
2871
+ const partKeys = /* @__PURE__ */ new Set([
2872
+ ...prevV ? Object.keys(prevV) : [],
2873
+ ...nextV ? Object.keys(nextV) : []
2874
+ ]);
2875
+ const partsResult = {};
2876
+ for (const partKey of partKeys) {
2877
+ const prevPart = prevV == null ? void 0 : prevV[partKey];
2878
+ const nextPart = nextV == null ? void 0 : nextV[partKey];
2879
+ if (partKey === "rotate" || partKey === "skew") {
2880
+ partsResult[partKey] = interpolateNum(+(prevPart != null ? prevPart : 0), +(nextPart != null ? nextPart : 0), localProgress);
2881
+ } else if (partKey === "translate" || partKey === "scale" || partKey === "origin") {
2882
+ const fallback = partKey === "scale" ? [1, 1] : [0, 0];
2883
+ const interp = interpolateVec(prevPart || fallback, nextPart || fallback, localProgress);
2884
+ partsResult[partKey] = interp;
2885
+ }
2886
+ }
2887
+ if (propAnimIsMotionPath(propAnim)) {
2888
+ const prevTr = prevV.translate;
2889
+ const nextTr = nextV.translate;
2890
+ if (Array.isArray(prevTr) && Array.isArray(nextTr)) {
2891
+ const sample = evaluateMotionPathSegment(
2892
+ prevKf,
2893
+ nextKf,
2894
+ [+prevTr[0], +prevTr[1]],
2895
+ [+nextTr[0], +nextTr[1]],
2896
+ localProgress,
2897
+ !!propAnim.autoOrient
2898
+ );
2899
+ partsResult.translate = [sample.translate[0], sample.translate[1]];
2900
+ if (sample.rotateDeg !== void 0) partsResult.rotate = sample.rotateDeg + ((_e = partsResult.rotate) != null ? _e : 0);
2901
+ }
2902
+ }
2903
+ cssValue = composeTransformParts(partsResult, { withUnits: false });
2904
+ cssAttrName = "transform";
2905
+ } else if (cssAttrName === "translate") {
2906
+ const v = interpolateVec(
2907
+ prevV || [0, 0],
2908
+ nextV || [0, 0],
2909
+ localProgress
2910
+ );
2911
+ cssValue = "translate(" + v.join(",") + ")";
2912
+ cssAttrName = "transform";
2913
+ } else if (cssAttrName === "rotate") {
2914
+ const v = interpolateNum(
2915
+ +(prevV || 0),
2916
+ +(nextV || 0),
2917
+ localProgress
2918
+ );
2919
+ cssValue = "rotate(" + v + ")";
2920
+ cssAttrName = "transform";
2921
+ } else if (cssAttrName === "scale") {
2922
+ const v = interpolateVec(
2923
+ prevV || [1, 1],
2924
+ nextV || [1, 1],
2925
+ localProgress
2926
+ );
2927
+ cssValue = "scale(" + v.join(",") + ")";
2928
+ cssAttrName = "transform";
2929
+ } else {
2930
+ const num2 = interpolateNum(
2931
+ +(prevV || 0),
2932
+ +(nextV || 0),
2933
+ localProgress
2934
+ );
2935
+ cssValue = num2;
2936
+ }
2937
+ if (PX_PCT_BASED_ATTR_NAMES.has(cssAttrName) && typeof cssValue === "number") {
2938
+ cssValue = cssValue * 100 + "%";
2939
+ }
2940
+ return { k: cssAttrName, v: cssValue === null ? "" : "" + cssValue };
2941
+ }
2942
+ function calcAnimationValues(animDef, progress) {
2943
+ const result = {};
2944
+ for (const [propName, propAnim] of Object.entries(animDef)) {
2945
+ const computed = calcPropertyValue(propName, propAnim, progress);
2946
+ if (computed) {
2947
+ result[computed.k] = computed.v;
2948
+ }
2949
+ }
2950
+ return result;
2122
2951
  }
2123
2952
 
2124
2953
  // src/effects/PlayerEffectsUtil.visualModel.ts
@@ -2534,6 +3363,15 @@ function transformPathData(d, m, decimals = 2) {
2534
3363
  }
2535
3364
 
2536
3365
  // src/effects/shared/transformParts.ts
3366
+ function partsRecord(part, value, origin) {
3367
+ const rec = {};
3368
+ if (part === "translate" /* Translate */) rec.translate = value;
3369
+ else if (part === "rotate" /* Rotate */) rec.rotate = value;
3370
+ else if (part === "skew" /* Skew */) rec.skew = value;
3371
+ else rec.scale = value;
3372
+ if (origin && part !== "translate" /* Translate */) rec.origin = origin;
3373
+ return rec;
3374
+ }
2537
3375
  function readAnimatable(raw) {
2538
3376
  var _a2, _b;
2539
3377
  if (raw === void 0) return { kind: "absent" /* Absent */ };
@@ -2552,6 +3390,24 @@ function readAnimatable(raw) {
2552
3390
  }
2553
3391
  return { kind: "static" /* Static */, value: raw };
2554
3392
  }
3393
+ function writeAnimatableChannel(node, attrName, read, opts) {
3394
+ var _a2, _b, _c, _d;
3395
+ const toOut = (v) => (opts == null ? void 0 : opts.asString) && v !== void 0 && v !== null ? String(v) : v;
3396
+ if (read.kind === "absent" /* Absent */) return;
3397
+ if (read.kind === "static" /* Static */) {
3398
+ node[attrName] = toOut(read.value);
3399
+ return;
3400
+ }
3401
+ const prevAnimate = node.animate && typeof node.animate === "object" && !Array.isArray(node.animate) ? node.animate : void 0;
3402
+ const animate = __spreadValues({}, prevAnimate || {});
3403
+ const block = { keyframes: read.keyframes };
3404
+ if (read.loop !== void 0) block.loop = read.loop;
3405
+ if (read.autoOrient !== void 0) block.autoOrient = read.autoOrient;
3406
+ animate[attrName] = block;
3407
+ node.animate = animate;
3408
+ const baseline = (_d = (_b = read.base) != null ? _b : (_a2 = read.keyframes[0]) == null ? void 0 : _a2.value) != null ? _d : (_c = read.keyframes[0]) == null ? void 0 : _c.v;
3409
+ if (baseline !== void 0) node[attrName] = toOut(baseline);
3410
+ }
2555
3411
  function normalizeKeyframe(kf) {
2556
3412
  if (!kf || typeof kf !== "object") return kf;
2557
3413
  const k = kf;
@@ -2566,6 +3422,22 @@ function normalizeKeyframe(kf) {
2566
3422
  if (out.tangentIn === void 0 && k.ti !== void 0) out.tangentIn = k.ti;
2567
3423
  return out;
2568
3424
  }
3425
+ function readStaticOrigin(raw, ctx) {
3426
+ var _a2;
3427
+ const o = readAnimatable(raw);
3428
+ if (o.kind === "absent" /* Absent */) return void 0;
3429
+ if (o.kind === "static" /* Static */) return o.value;
3430
+ ctx.warnings.push("transformBy.origin: animated origin approximated by its first keyframe");
3431
+ return (_a2 = o.keyframes[0]) == null ? void 0 : _a2.value;
3432
+ }
3433
+ function keyframeWith(kf, value) {
3434
+ const out = { value };
3435
+ if (kf.time !== void 0) out.time = kf.time;
3436
+ if (kf.easing !== void 0) out.easing = kf.easing;
3437
+ if (kf.tangentOut !== void 0) out.tangentOut = kf.tangentOut;
3438
+ if (kf.tangentIn !== void 0) out.tangentIn = kf.tangentIn;
3439
+ return out;
3440
+ }
2569
3441
 
2570
3442
  // src/effects/text/textGlyphsEffect.ts
2571
3443
  var DEFAULT_FONT_SIZE = 16;
@@ -3114,8 +3986,109 @@ function materializeGlyphText(node, opts) {
3114
3986
  if (opts.alongPath) return materializeGlyphTextAlongPath(node, opts.alongPath.pathD, opts.alongPath.startOffset, opts, opts.alongPath.textLength, opts.alongPath.pathOverflow);
3115
3987
  return materializeGlyphTextHorizontal(node, opts);
3116
3988
  }
3117
-
3118
- // src/effects/text/textPathEffect.ts
3989
+ function applyTextGlyphsEffect(node, fx, ctx) {
3990
+ if (!(fx == null ? void 0 : fx.useGlyphs)) return node;
3991
+ if (!ctx.glyphs) {
3992
+ ctx.warnings.push("textGlyphs: no definitions.fonts \u2014 left as native <text>");
3993
+ return node;
3994
+ }
3995
+ return materializeGlyphTextHorizontal(node, { glyphs: ctx.glyphs, warnings: ctx.warnings });
3996
+ }
3997
+ function applyTextGlyphsAlongPath(node, ctx, pathD, startOffset, textLength, pathOverflow) {
3998
+ if (!ctx.glyphs) {
3999
+ ctx.warnings.push("textGlyphs: no definitions.fonts");
4000
+ return null;
4001
+ }
4002
+ return materializeGlyphTextAlongPath(node, pathD, startOffset, { glyphs: ctx.glyphs, warnings: ctx.warnings }, textLength, pathOverflow);
4003
+ }
4004
+
4005
+ // src/util/PxNodeCloneUtil.ts
4006
+ function deepClonePxNode(value) {
4007
+ if (value === null || typeof value !== "object") return value;
4008
+ if (Array.isArray(value)) return value.map(deepClonePxNode);
4009
+ const out = {};
4010
+ for (const k of Object.keys(value)) out[k] = deepClonePxNode(value[k]);
4011
+ return out;
4012
+ }
4013
+ function regenerateIdsAndRewriteRefs(root, genId3) {
4014
+ const oldToNew = /* @__PURE__ */ new Map();
4015
+ const walkAssign = (n) => {
4016
+ var _a2;
4017
+ if (typeof n.id === "string") {
4018
+ const newId = genId3();
4019
+ oldToNew.set(n.id, newId);
4020
+ n.id = newId;
4021
+ }
4022
+ (_a2 = n.children) == null ? void 0 : _a2.forEach(walkAssign);
4023
+ };
4024
+ walkAssign(root);
4025
+ const rewriteUrl = (s) => s.replace(/url\(#([^)]+)\)/g, (m, oldId) => {
4026
+ const newId = oldToNew.get(oldId);
4027
+ return newId ? "url(#" + newId + ")" : m;
4028
+ });
4029
+ const walkRewrite = (n) => {
4030
+ var _a2;
4031
+ if (typeof n.href === "string" && n.href.startsWith("#")) {
4032
+ const newId = oldToNew.get(n.href.slice(1));
4033
+ if (newId) n.href = "#" + newId;
4034
+ }
4035
+ for (const k of Object.keys(n)) {
4036
+ if (k === "children" || k === "effects" || k === "meta" || k === "animate" || k === "href" || k === "id") continue;
4037
+ const v = n[k];
4038
+ if (typeof v === "string" && v.indexOf("url(#") !== -1) {
4039
+ n[k] = rewriteUrl(v);
4040
+ }
4041
+ }
4042
+ (_a2 = n.children) == null ? void 0 : _a2.forEach(walkRewrite);
4043
+ };
4044
+ walkRewrite(root);
4045
+ return oldToNew;
4046
+ }
4047
+ function toFiniteNum(v) {
4048
+ const n = typeof v === "number" ? v : typeof v === "string" ? parseFloat(v) : NaN;
4049
+ return Number.isFinite(n) ? n : 0;
4050
+ }
4051
+ function applyUseOffsetToG(gNode) {
4052
+ var _a2;
4053
+ const x = toFiniteNum(gNode.x);
4054
+ const y = toFiniteNum(gNode.y);
4055
+ delete gNode.x;
4056
+ delete gNode.y;
4057
+ if (!x && !y) return gNode;
4058
+ const offset = "translate(" + x + "," + y + ")";
4059
+ const carriesTransform = gNode.transform !== void 0 || gNode.animate !== void 0;
4060
+ if (carriesTransform) {
4061
+ const inner = { type: "g", transform: offset, children: (_a2 = gNode.children) != null ? _a2 : [] };
4062
+ gNode.children = [inner];
4063
+ } else {
4064
+ gNode.transform = offset;
4065
+ }
4066
+ return gNode;
4067
+ }
4068
+
4069
+ // src/effects/shared/util.ts
4070
+ function genId(ctx, prefix) {
4071
+ return "_lw_" + prefix + "_" + ctx.nextId++;
4072
+ }
4073
+ function stripHash(href) {
4074
+ return typeof href === "string" ? href.replace(/^#/, "") : void 0;
4075
+ }
4076
+ function indexById(node, map) {
4077
+ var _a2;
4078
+ if (typeof node.id === "string") map.set(node.id, node);
4079
+ (_a2 = node.children) == null ? void 0 : _a2.forEach((child) => indexById(child, map));
4080
+ }
4081
+ function spliceDefs(root, defs) {
4082
+ if (!defs.length) return;
4083
+ const existing = root.children || (root.children = []);
4084
+ existing.unshift({ type: "defs", children: defs });
4085
+ }
4086
+ var clone = deepClonePxNode;
4087
+ function regenerateIdsInClone(root, ctx) {
4088
+ return regenerateIdsAndRewriteRefs(root, () => genId(ctx, "retimed"));
4089
+ }
4090
+
4091
+ // src/effects/text/textPathEffect.ts
3119
4092
  var EXTEND_MARGIN_FRAC = 0.15;
3120
4093
  function numRange(v) {
3121
4094
  const read = readAnimatable(v);
@@ -3124,58 +4097,2159 @@ function numRange(v) {
3124
4097
  const vals = read.keyframes.map((k) => Number(k.value) || 0);
3125
4098
  return { min: Math.min(...vals), max: Math.max(...vals) };
3126
4099
  }
3127
- return { min: 0, max: 0 };
4100
+ return { min: 0, max: 0 };
4101
+ }
4102
+ function estimateTextAdvance(node) {
4103
+ let chars = 0, maxFont = 16;
4104
+ const walk = (el) => {
4105
+ var _a2;
4106
+ const fs = parseFloat(String((_a2 = el.fontSize) != null ? _a2 : "")) || 0;
4107
+ if (fs) maxFont = Math.max(maxFont, fs);
4108
+ const t = el.textContent;
4109
+ if (typeof t === "string") chars += t.length;
4110
+ if (el.children) for (const c of el.children) walk(c);
4111
+ };
4112
+ walk(node);
4113
+ return chars * maxFont;
4114
+ }
4115
+ var r3 = (n) => Math.round(n * 1e3) / 1e3;
4116
+ function shiftAnimatable(v, by) {
4117
+ if (!by || v === void 0 || v === null) return v;
4118
+ const read = readAnimatable(v);
4119
+ if (read.kind === "absent" /* Absent */) return v;
4120
+ if (read.kind === "static" /* Static */) return typeof v === "number" ? read.value + by : { value: read.value + by };
4121
+ const out = {
4122
+ keyframes: read.keyframes.map((k) => __spreadProps(__spreadValues({}, k), { value: (Number(k.value) || 0) + by }))
4123
+ };
4124
+ if (read.loop !== void 0) out.loop = read.loop;
4125
+ if (read.autoOrient !== void 0) out.autoOrient = read.autoOrient;
4126
+ if (read.base !== void 0) out.value = read.base + by;
4127
+ return out;
4128
+ }
4129
+ function extendedPathForBrowser(pathD, opts) {
4130
+ var _a2;
4131
+ if (opts.pathOverflow === "clip") return { d: pathD, startShift: 0 };
4132
+ const sampler = createPathSampler(pathD);
4133
+ if (!sampler || sampler.closed || sampler.totalLength <= 0) return { d: pathD, startShift: 0 };
4134
+ const L = sampler.totalLength;
4135
+ const margin = EXTEND_MARGIN_FRAC * L;
4136
+ const so = numRange(opts.startOffset);
4137
+ const runWidth = numRange(opts.textLength).max || ((_a2 = opts.advance) != null ? _a2 : 0);
4138
+ const startOverflow = Math.max(0, -so.min);
4139
+ const endOverflow = Math.max(0, so.max + runWidth - L);
4140
+ const startExt = startOverflow > 0 ? startOverflow + margin : 0;
4141
+ const endExt = endOverflow > 0 ? endOverflow + margin : 0;
4142
+ if (endExt <= 0 && startExt <= 0) return { d: pathD, startShift: 0 };
4143
+ const s = sampler.sampleAtDistance(0);
4144
+ const e = sampler.sampleAtDistance(L);
4145
+ let d = pathD;
4146
+ if (startExt > 0) {
4147
+ const sx = s.x - Math.cos(s.angle) * startExt, sy = s.y - Math.sin(s.angle) * startExt;
4148
+ const rest = pathD.replace(/^\s*[Mm]\s*-?[\d.]+[\s,]+-?[\d.]+/, "");
4149
+ d = "M" + r3(sx) + "," + r3(sy) + "L" + r3(s.x) + "," + r3(s.y) + rest;
4150
+ }
4151
+ if (endExt > 0) {
4152
+ const ex = e.x + Math.cos(e.angle) * endExt, ey = e.y + Math.sin(e.angle) * endExt;
4153
+ d += "L" + r3(ex) + "," + r3(ey);
4154
+ }
4155
+ return { d, startShift: startExt };
4156
+ }
4157
+ function applyTextPathEffect(node, fx, ctx) {
4158
+ var _a2;
4159
+ if (!fx || typeof fx.pathData !== "string" || !fx.pathData) return node;
4160
+ const pathId = genId(ctx, "tpath");
4161
+ const { d, startShift } = extendedPathForBrowser(fx.pathData, {
4162
+ pathOverflow: fx.pathOverflow,
4163
+ startOffset: fx.startOffset,
4164
+ textLength: fx.textLength,
4165
+ advance: estimateTextAdvance(node)
4166
+ });
4167
+ ctx.defs.push({ type: "path", id: pathId, d });
4168
+ const textPath = {
4169
+ type: "textPath",
4170
+ href: "#" + pathId,
4171
+ children: (_a2 = node.children) != null ? _a2 : []
4172
+ };
4173
+ if (fx.lengthAdjust !== void 0) textPath.lengthAdjust = fx.lengthAdjust;
4174
+ if (fx.method !== void 0) textPath.method = fx.method;
4175
+ if (fx.spacing !== void 0) textPath.spacing = fx.spacing;
4176
+ applyAnimatableNumber(textPath, "startOffset", shiftAnimatable(fx.startOffset, startShift));
4177
+ applyAnimatableNumber(textPath, "textLength", fx.textLength);
4178
+ node.children = [textPath];
4179
+ return node;
4180
+ }
4181
+ function applyAnimatableNumber(node, attrName, raw) {
4182
+ if (raw === void 0 || raw === null) return;
4183
+ writeAnimatableChannel(node, attrName, readAnimatable(raw), { asString: true });
4184
+ }
4185
+
4186
+ // src/format/PxDocumentDiagnostic.ts
4187
+ var MAX_REPORTED = 6;
4188
+ function diagnoseDocument(doc) {
4189
+ try {
4190
+ return { problems: validateDocument(doc) };
4191
+ } catch (e) {
4192
+ return { problems: [] };
4193
+ }
4194
+ }
4195
+ function reportDocumentDiagnostics(doc, where) {
4196
+ const { problems } = diagnoseDocument(doc);
4197
+ if (!problems.length) return;
4198
+ const shown = problems.slice(0, MAX_REPORTED);
4199
+ const more = problems.length - shown.length;
4200
+ console.warn(
4201
+ where + ": this document does not match the animation schema in " + problems.length + " place" + (problems.length === 1 ? "" : "s") + ".\n" + shown.map((p) => " - " + p).join("\n") + (more > 0 ? "\n \u2026 and " + more + " more" : "") + "\n\nIf you did not author these keys, the usual cause is a build that MANGLES PROPERTY NAMES. An animation document is data loaded at runtime, so renaming the property reads inside the player stops them matching the keys in the JSON, and the animation silently does nothing. Feed the published reserved-name list to your minifier \u2014 @pixodesk/svg-animator-web/mangle-reserved.json \u2014 see docs/library/minification.md."
4202
+ );
4203
+ }
4204
+
4205
+ // src/effects/transform/transformationEffect.ts
4206
+ function applyTransformByEffect(node, fx, ctx) {
4207
+ if (!fx) return node;
4208
+ delete node.transform;
4209
+ let n = node;
4210
+ n = wrapOrigin(
4211
+ n,
4212
+ fx.origin,
4213
+ /*invert=*/
4214
+ true
4215
+ );
4216
+ n = wrapTransformPart(n, "scale" /* Scale */, normalizeScale(fx.scale), ctx);
4217
+ n = wrapTransformPart(n, "skew" /* Skew */, fx.skew, ctx);
4218
+ n = wrapTransformPart(n, "rotate" /* Rotate */, fx.rotate, ctx);
4219
+ n = wrapOrigin(
4220
+ n,
4221
+ fx.origin,
4222
+ /*invert=*/
4223
+ false
4224
+ );
4225
+ if (translateHasAutoOrient(fx.translate)) {
4226
+ n = wrapOrigin(
4227
+ n,
4228
+ fx.origin,
4229
+ /*invert=*/
4230
+ true
4231
+ );
4232
+ n = wrapTransformPart(n, "translate" /* Translate */, fx.translate, ctx);
4233
+ n = wrapOrigin(
4234
+ n,
4235
+ fx.origin,
4236
+ /*invert=*/
4237
+ false
4238
+ );
4239
+ } else {
4240
+ n = wrapTransformPart(n, "translate" /* Translate */, fx.translate, ctx);
4241
+ }
4242
+ if (n !== node && node.id) {
4243
+ n.id = node.id;
4244
+ delete node.id;
4245
+ }
4246
+ return n;
4247
+ }
4248
+ function translateHasAutoOrient(translate) {
4249
+ if (!translate || typeof translate !== "object") return false;
4250
+ const obj = translate;
4251
+ if (obj.autoOrient) return true;
4252
+ return Array.isArray(obj.keyframes) && obj.keyframes.some((kf) => kf.tangentOut || kf.tangentIn);
4253
+ }
4254
+ function normalizeScale(raw) {
4255
+ return raw;
4256
+ }
4257
+ function wrapTransformPart(inner, part, raw, ctx) {
4258
+ if (raw === void 0) return inner;
4259
+ const v = readAnimatable(raw);
4260
+ if (v.kind === "static" /* Static */) {
4261
+ return { type: "g", transform: { value: partsRecord(part, v.value, void 0) }, children: [inner] };
4262
+ }
4263
+ if (v.kind === "animated" /* Animated */) {
4264
+ const animTr = { keyframes: v.keyframes.map((kf) => keyframeWith(kf, partsRecord(part, kf.value, void 0))) };
4265
+ if (v.autoOrient) animTr.autoOrient = true;
4266
+ if (v.loop !== void 0) animTr.loop = v.loop;
4267
+ return {
4268
+ type: "g",
4269
+ animate: { transform: animTr },
4270
+ children: [inner]
4271
+ };
4272
+ }
4273
+ return inner;
4274
+ }
4275
+ function wrapOrigin(inner, raw, invert) {
4276
+ if (raw === void 0) return inner;
4277
+ const v = readAnimatable(raw);
4278
+ const sign = (value) => invert ? [-value[0], -value[1]] : value;
4279
+ if (v.kind === "absent" /* Absent */) return inner;
4280
+ if (v.kind === "static" /* Static */) {
4281
+ if (v.value[0] === 0 && v.value[1] === 0) return inner;
4282
+ return { type: "g", transform: { value: { translate: sign(v.value) } }, children: [inner] };
4283
+ }
4284
+ if (v.kind === "animated" /* Animated */) {
4285
+ const animTr = { keyframes: v.keyframes.map((kf) => keyframeWith(kf, { translate: sign(kf.value) })) };
4286
+ if (v.loop !== void 0) animTr.loop = v.loop;
4287
+ return {
4288
+ type: "g",
4289
+ animate: { transform: animTr },
4290
+ children: [inner]
4291
+ };
4292
+ }
4293
+ return inner;
4294
+ }
4295
+
4296
+ // src/effects/reference/contentRefSplit.ts
4297
+ function identifyContentRefTargets(node, ctx, allocator) {
4298
+ var _a2, _b, _c;
4299
+ if (node.type === "use" && ((_b = (_a2 = node.effects) == null ? void 0 : _a2.clone) == null ? void 0 : _b.without) === "translate") {
4300
+ const sourceId = stripHash(node.effects.clone.source);
4301
+ if (typeof sourceId === "string" && sourceId && !ctx.contentRefInnerIds.has(sourceId)) {
4302
+ ctx.contentRefInnerIds.set(sourceId, allocator(sourceId));
4303
+ }
4304
+ }
4305
+ (_c = node.children) == null ? void 0 : _c.forEach((c) => identifyContentRefTargets(c, ctx, allocator));
4306
+ }
4307
+ function splitForContentRef(node, transformBy, originalId, innerId, ctx) {
4308
+ const outerBody = liftBodyTranslate(node, transformBy);
4309
+ if (typeof node.id === "string") delete node.id;
4310
+ const { outer: outerTr, inner: innerTr } = splitTransformByEffect(transformBy);
4311
+ let innerNode = node;
4312
+ innerNode = applyTransformByEffect(innerNode, innerTr, ctx);
4313
+ const innerWrapper = { type: "g", id: innerId, children: [innerNode] };
4314
+ let outerWrapper = { type: "g", id: originalId, children: [innerWrapper] };
4315
+ if (outerBody.transform !== void 0) outerWrapper.transform = outerBody.transform;
4316
+ if (outerBody.animate !== void 0) outerWrapper.animate = outerBody.animate;
4317
+ if (outerTr) {
4318
+ delete outerWrapper.id;
4319
+ outerWrapper = applyTransformByEffect(outerWrapper, outerTr, ctx);
4320
+ outerWrapper.id = originalId;
4321
+ }
4322
+ return outerWrapper;
4323
+ }
4324
+ function liftBodyTranslate(node, transformBy) {
4325
+ var _a2, _b, _c;
4326
+ const out = {};
4327
+ let didLiftAnimate = false;
4328
+ const animTr = (_a2 = node.animate) == null ? void 0 : _a2.transform;
4329
+ if (animTr && typeof animTr === "object" && Array.isArray(animTr.keyframes)) {
4330
+ const kfs = animTr.keyframes;
4331
+ const hasTranslate = kfs.some((kf) => kf.value && kf.value.translate);
4332
+ if (hasTranslate) {
4333
+ const outerHasOrigin = needsOriginOnOuter(animTr);
4334
+ const outerKfs = kfs.map((kf) => {
4335
+ const v = kf.value || {};
4336
+ const newValue = {};
4337
+ if (v.translate !== void 0) newValue.translate = v.translate;
4338
+ if (outerHasOrigin && v.origin !== void 0) newValue.origin = v.origin;
4339
+ const outerKf = { value: newValue };
4340
+ if (kf.time !== void 0) outerKf.time = kf.time;
4341
+ if (kf.easing !== void 0) outerKf.easing = kf.easing;
4342
+ if (kf.tangentOut !== void 0) outerKf.tangentOut = kf.tangentOut;
4343
+ if (kf.tangentIn !== void 0) outerKf.tangentIn = kf.tangentIn;
4344
+ return outerKf;
4345
+ });
4346
+ const outerAnimTr = { keyframes: outerKfs };
4347
+ if (animTr.autoOrient) outerAnimTr.autoOrient = true;
4348
+ const srcLoop = animTr.loop;
4349
+ if (srcLoop !== void 0) outerAnimTr.loop = srcLoop;
4350
+ out.animate = { transform: outerAnimTr };
4351
+ const innerHasPivotedPart = kfs.some((kf) => {
4352
+ const v = kf.value || {};
4353
+ return v.rotate !== void 0 || v.scale !== void 0;
4354
+ });
4355
+ const innerKfs = kfs.map((kf) => {
4356
+ const v = kf.value || {};
4357
+ const newValue = {};
4358
+ if (v.rotate !== void 0) newValue.rotate = v.rotate;
4359
+ if (v.scale !== void 0) newValue.scale = v.scale;
4360
+ if (v.origin !== void 0 && (!outerHasOrigin || innerHasPivotedPart)) newValue.origin = v.origin;
4361
+ const innerKf = { value: newValue };
4362
+ if (kf.time !== void 0) innerKf.time = kf.time;
4363
+ if (kf.easing !== void 0) innerKf.easing = kf.easing;
4364
+ return innerKf;
4365
+ });
4366
+ const allInnerEmpty = innerKfs.every((kf) => Object.keys(kf.value).length === 0);
4367
+ if (allInnerEmpty) {
4368
+ delete node.animate.transform;
4369
+ if (node.animate && Object.keys(node.animate).length === 0) delete node.animate;
4370
+ } else {
4371
+ const innerAnimTr = { keyframes: innerKfs };
4372
+ if (srcLoop !== void 0) innerAnimTr.loop = srcLoop;
4373
+ node.animate.transform = innerAnimTr;
4374
+ }
4375
+ didLiftAnimate = true;
4376
+ }
4377
+ }
4378
+ const transformationHasTranslate = (transformBy == null ? void 0 : transformBy.translate) !== void 0;
4379
+ const stripBodyTranslateOnly = didLiftAnimate || transformationHasTranslate;
4380
+ const liftedAnimateIsAutoOriented = didLiftAnimate && needsOriginOnOuter(((_b = node.animate) == null ? void 0 : _b.transform) || void 0) || didLiftAnimate && needsOriginOnOuter(((_c = out.animate) == null ? void 0 : _c.transform) || void 0);
4381
+ if (typeof node.transform === "string") {
4382
+ const split = splitTransformString(node.transform);
4383
+ if (stripBodyTranslateOnly) {
4384
+ if (split.translate !== void 0) {
4385
+ if (split.rest) node.transform = split.rest;
4386
+ else delete node.transform;
4387
+ } else if (isPureTranslateBody(node.transform)) {
4388
+ delete node.transform;
4389
+ } else if (liftedAnimateIsAutoOriented && isSingleMatrixBody(node.transform)) {
4390
+ delete node.transform;
4391
+ }
4392
+ } else if (split.translate) {
4393
+ out.transform = split.translate;
4394
+ if (split.rest) node.transform = split.rest;
4395
+ else delete node.transform;
4396
+ }
4397
+ } else if (node.transform && typeof node.transform === "object" && !Array.isArray(node.transform) && !node.transform.keyframes) {
4398
+ const wrapped = node.transform.value;
4399
+ const isWrapped = !!(wrapped && typeof wrapped === "object");
4400
+ const value = isWrapped ? wrapped : node.transform;
4401
+ const rewrap = (rec) => isWrapped ? { value: rec } : rec;
4402
+ if (Array.isArray(value.translate)) {
4403
+ const rest = __spreadValues({}, value);
4404
+ delete rest.translate;
4405
+ const hasRest = Object.keys(rest).length > 0;
4406
+ if (stripBodyTranslateOnly) {
4407
+ if (hasRest) node.transform = rewrap(rest);
4408
+ else delete node.transform;
4409
+ } else {
4410
+ out.transform = rewrap({ translate: value.translate });
4411
+ if (hasRest) node.transform = rewrap(rest);
4412
+ else delete node.transform;
4413
+ }
4414
+ }
4415
+ }
4416
+ return out;
4417
+ }
4418
+ function isSingleMatrixBody(s) {
4419
+ const re = /(translate|rotate|scale|matrix|skewX|skewY)\(([^)]*)\)/g;
4420
+ let count = 0;
4421
+ let isMatrix = false;
4422
+ let m;
4423
+ while (m = re.exec(s)) {
4424
+ count++;
4425
+ if (m[1] === "matrix") isMatrix = true;
4426
+ }
4427
+ return count === 1 && isMatrix;
4428
+ }
4429
+ function isPureTranslateBody(s) {
4430
+ const re = /(translate|rotate|scale|matrix|skewX|skewY)\(([^)]*)\)/g;
4431
+ const ops = [];
4432
+ let m;
4433
+ while (m = re.exec(s)) ops.push({ name: m[1], full: m[0] });
4434
+ if (ops.length !== 1) return false;
4435
+ if (ops[0].name === "translate") return true;
4436
+ if (ops[0].name !== "matrix") return false;
4437
+ const args = /matrix\(([^)]*)\)/.exec(ops[0].full);
4438
+ if (!args) return false;
4439
+ const nums = args[1].split(/[\s,]+/).filter(Boolean).map(Number);
4440
+ return nums.length >= 4 && nums[0] === 1 && nums[1] === 0 && nums[2] === 0 && nums[3] === 1;
4441
+ }
4442
+ function splitTransformString(s) {
4443
+ const ops = [];
4444
+ const re = /(translate|rotate|scale|matrix|skewX|skewY)\(([^)]*)\)/g;
4445
+ let m;
4446
+ while (m = re.exec(s)) ops.push({ name: m[1], full: m[0] });
4447
+ if (!ops.length) return { rest: s || void 0 };
4448
+ if (ops.every((o) => o.name === "translate")) {
4449
+ return { translate: ops.map((o) => o.full).join("") };
4450
+ }
4451
+ const leading = ops[0];
4452
+ const trailing = ops[ops.length - 1];
4453
+ if (trailing.name === "translate" && leading.name === "translate") {
4454
+ const trailingVec = parseTranslateArgs(trailing.full);
4455
+ const leadingVec = parseTranslateArgs(leading.full);
4456
+ const ox = -trailingVec[0];
4457
+ const oy = -trailingVec[1];
4458
+ const userTx = leadingVec[0] - ox;
4459
+ const userTy = leadingVec[1] - oy;
4460
+ if (userTx === 0 && userTy === 0) return { rest: s };
4461
+ const middleAndTrailing = "translate(" + ox + "," + oy + ")" + ops.slice(1).map((o) => o.full).join("");
4462
+ return { translate: "translate(" + userTx + "," + userTy + ")", rest: middleAndTrailing };
4463
+ }
4464
+ if (trailing.name === "translate") return { rest: s };
4465
+ const lifted = [];
4466
+ let i = 0;
4467
+ while (i < ops.length && ops[i].name === "translate") {
4468
+ lifted.push(ops[i].full);
4469
+ i++;
4470
+ }
4471
+ if (!lifted.length) return { rest: s };
4472
+ const rest = ops.slice(i).map((o) => o.full).join("");
4473
+ return {
4474
+ translate: lifted.join(""),
4475
+ rest: rest || void 0
4476
+ };
4477
+ }
4478
+ function parseTranslateArgs(translateOp) {
4479
+ const m = /translate\(([^)]*)\)/.exec(translateOp);
4480
+ if (!m) return [0, 0];
4481
+ const nums = m[1].split(/[\s,]+/).filter(Boolean).map(Number);
4482
+ return [nums[0] || 0, nums[1] || 0];
4483
+ }
4484
+ function splitTransformByEffect(fx) {
4485
+ if (!fx) return {};
4486
+ const originOnOuter = needsOriginOnOuter(fx.translate);
4487
+ const innerHasPivotedPart = fx.rotate !== void 0 || fx.scale !== void 0;
4488
+ const outer = {};
4489
+ const inner = {};
4490
+ if (fx.translate !== void 0) outer.translate = fx.translate;
4491
+ if (originOnOuter && fx.origin !== void 0) outer.origin = fx.origin;
4492
+ if (fx.rotate !== void 0) inner.rotate = fx.rotate;
4493
+ if (fx.scale !== void 0) inner.scale = fx.scale;
4494
+ if (fx.skew !== void 0) inner.skew = fx.skew;
4495
+ if (fx.origin !== void 0 && (!originOnOuter || innerHasPivotedPart)) inner.origin = fx.origin;
4496
+ return {
4497
+ outer: Object.keys(outer).length ? outer : void 0,
4498
+ inner: Object.keys(inner).length ? inner : void 0
4499
+ };
4500
+ }
4501
+ function needsOriginOnOuter(translateAnim) {
4502
+ if (!translateAnim || typeof translateAnim !== "object") return false;
4503
+ const obj = translateAnim;
4504
+ if (obj.autoOrient) return true;
4505
+ if (Array.isArray(obj.keyframes)) {
4506
+ return obj.keyframes.some((kf) => kf.tangentOut || kf.tangentIn);
4507
+ }
4508
+ return false;
4509
+ }
4510
+
4511
+ // src/effects/paint/gradientEffect.ts
4512
+ function applyFillGradientEffect(node, fx, ctx) {
4513
+ return applyGradient(node, fx, ctx, "fill");
4514
+ }
4515
+ function applyStrokeGradientEffect(node, fx, ctx) {
4516
+ return applyGradient(node, fx, ctx, "stroke");
4517
+ }
4518
+ function applyGradient(node, fx, ctx, attr) {
4519
+ if (!fx) return node;
4520
+ const id = genId(ctx, "grad");
4521
+ const def = synthesiseGradientDef(fx, id, ctx);
4522
+ ctx.defs.push(def);
4523
+ node[attr] = "url(#" + id + ")";
4524
+ return node;
4525
+ }
4526
+ function synthesiseGradientDef(fx, id, ctx) {
4527
+ const out = {
4528
+ type: fx.type === PxGradientType.radial ? "radialGradient" : "linearGradient",
4529
+ id
4530
+ };
4531
+ if (fx.type === PxGradientType.linear) {
4532
+ applyGeomVec(out, "x1", "y1", fx.start);
4533
+ applyGeomVec(out, "x2", "y2", fx.end);
4534
+ } else {
4535
+ applyGeomVec(out, "cx", "cy", fx.center);
4536
+ applyGeomNumber(out, "r", fx.radius);
4537
+ applyGeomVec(out, "fx", "fy", fx.focal);
4538
+ }
4539
+ if (fx.gradientUnits) out.gradientUnits = fx.gradientUnits;
4540
+ if (fx.spreadMethod) out.spreadMethod = fx.spreadMethod;
4541
+ if (fx.gradientTransform) out.gradientTransform = fx.gradientTransform;
4542
+ out.children = buildStopChildren(fx.stops, ctx);
4543
+ return out;
4544
+ }
4545
+ function applyGeomVec(out, xAttr, yAttr, raw) {
4546
+ var _a2, _b, _c;
4547
+ const read = readAnimatable(raw);
4548
+ if (read.kind === "absent" /* Absent */) return;
4549
+ if (read.kind === "static" /* Static */) {
4550
+ out[xAttr] = String(read.value[0]);
4551
+ out[yAttr] = String(read.value[1]);
4552
+ return;
4553
+ }
4554
+ const axisChannel = (idx) => {
4555
+ const block = {
4556
+ keyframes: read.keyframes.map((kf) => {
4557
+ const axisKf = { time: kf.time, value: Array.isArray(kf.value) ? kf.value[idx] : void 0 };
4558
+ if (kf.easing !== void 0) axisKf.easing = kf.easing;
4559
+ return axisKf;
4560
+ })
4561
+ };
4562
+ if (read.loop !== void 0) block.loop = read.loop;
4563
+ return block;
4564
+ };
4565
+ const animate = (_a2 = out.animate) != null ? _a2 : {};
4566
+ animate[xAttr] = axisChannel(0);
4567
+ animate[yAttr] = axisChannel(1);
4568
+ out.animate = animate;
4569
+ const baseline = (_c = read.base) != null ? _c : (_b = read.keyframes[0]) == null ? void 0 : _b.value;
4570
+ if (Array.isArray(baseline)) {
4571
+ out[xAttr] = String(baseline[0]);
4572
+ out[yAttr] = String(baseline[1]);
4573
+ }
4574
+ }
4575
+ function applyGeomNumber(out, attrName, raw) {
4576
+ const read = readAnimatable(raw);
4577
+ if (read.kind === "absent" /* Absent */) return;
4578
+ writeAnimatableChannel(out, attrName, read, { asString: true });
4579
+ }
4580
+ function buildStopChildren(stops, ctx) {
4581
+ var _a2, _b;
4582
+ if (!stops) return [];
4583
+ const read = readAnimatable(stops);
4584
+ if (read.kind === "absent" /* Absent */) return [];
4585
+ if (read.kind === "static" /* Static */) return Array.isArray(read.value) ? read.value.map(staticStopNode) : [];
4586
+ const kfs = read.keyframes;
4587
+ if (!kfs.length) return [];
4588
+ const loopFromSource = read.loop;
4589
+ let stopCount = 0;
4590
+ for (const kf of kfs) {
4591
+ const v = keyframeValue(kf);
4592
+ if (Array.isArray(v) && v.length > stopCount) stopCount = v.length;
4593
+ }
4594
+ if (!stopCount) return [];
4595
+ const firstKfValue = keyframeValue(kfs[0]);
4596
+ const baselineStops = [];
4597
+ for (let i = 0; i < stopCount; i++) {
4598
+ const s = (_b = (_a2 = firstKfValue == null ? void 0 : firstKfValue[i]) != null ? _a2 : prevDefinedStop(kfs, 0, i)) != null ? _b : { offset: i / Math.max(1, stopCount - 1), color: "#000000" };
4599
+ baselineStops.push({ offset: s.offset, color: s.color });
4600
+ }
4601
+ return baselineStops.map((bs, i) => animatedStopNode(bs, kfs, i, ctx, loopFromSource));
4602
+ }
4603
+ function staticStopNode(s) {
4604
+ return {
4605
+ type: "stop",
4606
+ offset: formatOffset(s.offset),
4607
+ stopColor: s.color
4608
+ };
4609
+ }
4610
+ function animatedStopNode(baseline, kfs, stopIdx, _ctx, loop) {
4611
+ var _a2;
4612
+ const colorKfs = [];
4613
+ const offsetKfs = [];
4614
+ let offsetVaries = false;
4615
+ for (const kf of kfs) {
4616
+ const t = keyframeTime(kf);
4617
+ const arr = keyframeValue(kf);
4618
+ const sliced = (_a2 = arr == null ? void 0 : arr[stopIdx]) != null ? _a2 : prevDefinedStop(kfs, kfs.indexOf(kf), stopIdx);
4619
+ if (!sliced) continue;
4620
+ const easing = keyframeEasing(kf);
4621
+ const colorOut = { time: t, value: sliced.color };
4622
+ if (easing !== void 0) colorOut.easing = easing;
4623
+ colorKfs.push(colorOut);
4624
+ const offsetOut = { time: t, value: sliced.offset };
4625
+ if (easing !== void 0) offsetOut.easing = easing;
4626
+ offsetKfs.push(offsetOut);
4627
+ if (sliced.offset !== baseline.offset) offsetVaries = true;
4628
+ }
4629
+ const stop = {
4630
+ type: "stop",
4631
+ offset: formatOffset(baseline.offset),
4632
+ stopColor: baseline.color
4633
+ };
4634
+ const animate = {};
4635
+ if (colorKfs.length) {
4636
+ animate.stopColor = { keyframes: colorKfs };
4637
+ if (loop !== void 0) animate.stopColor.loop = loop;
4638
+ }
4639
+ if (offsetVaries && offsetKfs.length) {
4640
+ animate.offset = { keyframes: offsetKfs };
4641
+ if (loop !== void 0) animate.offset.loop = loop;
4642
+ }
4643
+ if (Object.keys(animate).length) stop.animate = animate;
4644
+ return stop;
4645
+ }
4646
+ function prevDefinedStop(kfs, fromIdx, stopIdx) {
4647
+ for (let i = fromIdx; i >= 0; i--) {
4648
+ const arr = keyframeValue(kfs[i]);
4649
+ if (arr == null ? void 0 : arr[stopIdx]) return arr[stopIdx];
4650
+ }
4651
+ for (let i = fromIdx + 1; i < kfs.length; i++) {
4652
+ const arr = keyframeValue(kfs[i]);
4653
+ if (arr == null ? void 0 : arr[stopIdx]) return arr[stopIdx];
4654
+ }
4655
+ return void 0;
4656
+ }
4657
+ function formatOffset(o) {
4658
+ const pct = Math.round(o * 1e3) / 10;
4659
+ return pct + "%";
4660
+ }
4661
+
4662
+ // src/effects/clipping/clipPathEffect.ts
4663
+ function applyClipPathEffect(node, fx, ctx) {
4664
+ if (!(fx == null ? void 0 : fx.pathData)) return node;
4665
+ const clipId = genId(ctx, "clip");
4666
+ const pathChild = { type: "path" };
4667
+ const read = readAnimatable(fx.pathData);
4668
+ if (read.kind !== "absent" /* Absent */) {
4669
+ if (read.kind === "static" /* Static */) {
4670
+ pathChild.d = pathString(read.value);
4671
+ } else {
4672
+ writeAnimatableChannel(pathChild, "d", read);
4673
+ if (pathChild.d !== void 0) pathChild.d = pathString(pathChild.d);
4674
+ }
4675
+ }
4676
+ ctx.defs.push({ type: "clipPath", id: clipId, children: [pathChild] });
4677
+ node.clipPath = "url(#" + clipId + ")";
4678
+ return node;
4679
+ }
4680
+ function pathString(v) {
4681
+ if (typeof v === "string") return v;
4682
+ if (v && typeof v === "object" && typeof v.pathData === "string") return v.pathData;
4683
+ return void 0;
4684
+ }
4685
+
4686
+ // src/effects/clipping/maskedByEffect.ts
4687
+ function applyMaskedByEffect(node, fx, transformBy, ctx) {
4688
+ if (!fx) return node;
4689
+ const sourceId = stripHash(fx.source);
4690
+ if (!sourceId) {
4691
+ ctx.errors.push("maskedBy.source missing \u2014 cannot build mask");
4692
+ return node;
4693
+ }
4694
+ const maskId = genId(ctx, "mask");
4695
+ let content = { type: "use", href: "#" + sourceId };
4696
+ if (transformBy) {
4697
+ content = wrapInverseTransform(content, transformBy, ctx);
4698
+ } else if (hasAnimateTransform(node)) {
4699
+ content = wrapInverseAnimatedBodyTransform(content, node, ctx);
4700
+ } else {
4701
+ const bodyStatic = readTransformationFromBody(node);
4702
+ if (bodyStatic) content = wrapInverseTransform(content, bodyStatic, ctx);
4703
+ }
4704
+ const includeTargetOwn = transformBy === void 0 && !nodeHasBodyTransform(node);
4705
+ content = wrapAncestorChainCompensation(content, node, sourceId, ctx, includeTargetOwn);
4706
+ const mask = { type: "mask", id: maskId, children: [content] };
4707
+ if (fx.maskType) mask.maskType = fx.maskType;
4708
+ if (fx.maskUnits) mask.maskUnits = fx.maskUnits;
4709
+ if (fx.maskContentUnits) mask.maskContentUnits = fx.maskContentUnits;
4710
+ if (fx.x !== void 0) mask.x = String(fx.x);
4711
+ if (fx.y !== void 0) mask.y = String(fx.y);
4712
+ if (fx.width !== void 0) mask.width = String(fx.width);
4713
+ if (fx.height !== void 0) mask.height = String(fx.height);
4714
+ ctx.defs.push(mask);
4715
+ node.mask = "url(#" + maskId + ")";
4716
+ return node;
4717
+ }
4718
+ function wrapInverseTransform(inner, fx, ctx) {
4719
+ if (!fx) return inner;
4720
+ const origin = readStaticOrigin(fx.origin, ctx);
4721
+ let n = inner;
4722
+ n = wrapInversePart(n, "translate" /* Translate */, fx.translate, void 0, ctx);
4723
+ n = wrapInversePart(n, "rotate" /* Rotate */, fx.rotate, origin, ctx);
4724
+ n = wrapInversePart(n, "scale" /* Scale */, fx.scale, origin, ctx);
4725
+ return n;
4726
+ }
4727
+ function wrapInversePart(inner, part, raw, origin, ctx) {
4728
+ if (raw === void 0) return inner;
4729
+ const normalizedRaw = part === "scale" /* Scale */ && Array.isArray(raw) ? [raw[0] / 100, raw[1] / 100] : raw;
4730
+ const v = readAnimatable(normalizedRaw);
4731
+ if (v.kind === "static" /* Static */) {
4732
+ return { type: "g", transform: { value: partsRecord(part, invertPartValue(part, v.value), origin) }, children: [inner] };
4733
+ }
4734
+ if (v.kind === "animated" /* Animated */) {
4735
+ const animTr = { keyframes: v.keyframes.map((kf) => {
4736
+ const out = keyframeWith(kf, partsRecord(part, invertPartValue(part, kf.value), origin));
4737
+ return part === "translate" /* Translate */ ? __spreadValues(__spreadValues({}, out), negatedSpatialTangents(kf)) : out;
4738
+ }) };
4739
+ if (v.loop !== void 0) animTr.loop = v.loop;
4740
+ return {
4741
+ type: "g",
4742
+ animate: { transform: animTr },
4743
+ children: [inner]
4744
+ };
4745
+ }
4746
+ return inner;
4747
+ }
4748
+ function invertPartValue(part, value) {
4749
+ if (part === "translate" /* Translate */) return [-value[0], -value[1]];
4750
+ if (part === "rotate" /* Rotate */) return -value;
4751
+ return [1 / value[0], 1 / value[1]];
4752
+ }
4753
+ function negatedSpatialTangents(kf) {
4754
+ var _a2, _b;
4755
+ const out = {};
4756
+ const to = (_a2 = kf.tangentOut) != null ? _a2 : kf.to;
4757
+ const ti = (_b = kf.tangentIn) != null ? _b : kf.ti;
4758
+ if (Array.isArray(to)) out.tangentOut = [-to[0], -to[1]];
4759
+ if (Array.isArray(ti)) out.tangentIn = [-ti[0], -ti[1]];
4760
+ return out;
4761
+ }
4762
+ function wrapInverseAnimatedBodyTransform(inner, node, _ctx) {
4763
+ var _a2;
4764
+ const animate = node.animate && typeof node.animate === "object" && !Array.isArray(node.animate) ? node.animate : void 0;
4765
+ const animTr = animate == null ? void 0 : animate.transform;
4766
+ const kfs = animTr && typeof animTr === "object" && Array.isArray(animTr.keyframes) ? animTr.keyframes : void 0;
4767
+ if (!kfs || !kfs.length) return inner;
4768
+ const translateKfs = [];
4769
+ const rotateKfs = [];
4770
+ const scaleKfs = [];
4771
+ for (const kf of kfs) {
4772
+ const v = ((_a2 = kf.value) != null ? _a2 : kf.v) || {};
4773
+ const baseKf = keyframeWith(kf, void 0);
4774
+ if (Array.isArray(v.translate)) {
4775
+ translateKfs.push(__spreadProps(__spreadValues(__spreadValues({}, baseKf), negatedSpatialTangents(kf)), { value: { translate: [-v.translate[0], -v.translate[1]] } }));
4776
+ }
4777
+ if (typeof v.rotate === "number") {
4778
+ const rec = { rotate: -v.rotate };
4779
+ if (Array.isArray(v.origin)) rec.origin = [v.origin[0], v.origin[1]];
4780
+ rotateKfs.push(__spreadProps(__spreadValues({}, baseKf), { value: rec }));
4781
+ }
4782
+ if (Array.isArray(v.scale)) {
4783
+ const rec = { scale: [1 / v.scale[0], 1 / v.scale[1]] };
4784
+ if (Array.isArray(v.origin)) rec.origin = [v.origin[0], v.origin[1]];
4785
+ scaleKfs.push(__spreadProps(__spreadValues({}, baseKf), { value: rec }));
4786
+ }
4787
+ }
4788
+ const srcLoop = animTr == null ? void 0 : animTr.loop;
4789
+ const withLoop = (kfs2) => {
4790
+ const block = { keyframes: kfs2 };
4791
+ if (srcLoop !== void 0) block.loop = srcLoop;
4792
+ return block;
4793
+ };
4794
+ let n = inner;
4795
+ if (translateKfs.length) n = { type: "g", animate: { transform: withLoop(translateKfs) }, children: [n] };
4796
+ if (rotateKfs.length) n = { type: "g", animate: { transform: withLoop(rotateKfs) }, children: [n] };
4797
+ if (scaleKfs.length) n = { type: "g", animate: { transform: withLoop(scaleKfs) }, children: [n] };
4798
+ return n;
4799
+ }
4800
+ function nodeHasBodyTransform(node) {
4801
+ if (typeof node.transform === "string") return true;
4802
+ if (node.transform && typeof node.transform === "object") return true;
4803
+ return hasAnimateTransform(node);
4804
+ }
4805
+ function hasAnimateTransform(node) {
4806
+ const animate = node.animate && typeof node.animate === "object" && !Array.isArray(node.animate) ? node.animate : void 0;
4807
+ return !!(animate && animate.transform);
4808
+ }
4809
+ function readTransformationFromBody(node) {
4810
+ if (typeof node.transform === "string") {
4811
+ const parts = parseTransformStringToParts(node.transform);
4812
+ if (!parts) return void 0;
4813
+ const out = {};
4814
+ if (parts.translate) out.translate = parts.translate;
4815
+ if (parts.rotate !== void 0) out.rotate = parts.rotate;
4816
+ if (parts.scale) out.scale = { value: parts.scale };
4817
+ if (parts.origin) out.origin = parts.origin;
4818
+ return Object.keys(out).length ? out : void 0;
4819
+ }
4820
+ if (node.transform && typeof node.transform === "object" && !node.transform.keyframes) {
4821
+ const wrapped = node.transform.value;
4822
+ const value = wrapped && typeof wrapped === "object" ? wrapped : node.transform;
4823
+ if (value && typeof value === "object") {
4824
+ const out = {};
4825
+ if (Array.isArray(value.translate)) out.translate = value.translate;
4826
+ if (typeof value.rotate === "number") out.rotate = value.rotate;
4827
+ if (typeof value.skew === "number") out.skew = value.skew;
4828
+ if (Array.isArray(value.scale)) out.scale = { value: value.scale };
4829
+ if (Array.isArray(value.origin)) out.origin = value.origin;
4830
+ return Object.keys(out).length ? out : void 0;
4831
+ }
4832
+ }
4833
+ return void 0;
4834
+ }
4835
+ function parseTransformStringToParts(s) {
4836
+ var _a2, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m;
4837
+ const re = /([a-zA-Z]+)\s*\(([^)]*)\)/g;
4838
+ let m;
4839
+ const ops = [];
4840
+ while ((m = re.exec(s)) !== null) {
4841
+ const args = m[2].split(/[\s,]+/).filter((a) => a.length > 0).map(Number);
4842
+ ops.push({ name: m[1], args });
4843
+ }
4844
+ if (!ops.length) return void 0;
4845
+ const last = ops[ops.length - 1];
4846
+ if (last.name === "translate") {
4847
+ for (let j = ops.length - 2; j >= 0; j--) {
4848
+ const cand = ops[j];
4849
+ if (cand.name !== "translate") continue;
4850
+ const ox = (_a2 = cand.args[0]) != null ? _a2 : 0;
4851
+ const oy = (_b = cand.args[1]) != null ? _b : 0;
4852
+ const lx = (_c = last.args[0]) != null ? _c : 0;
4853
+ const ly = (_d = last.args[1]) != null ? _d : 0;
4854
+ if (lx !== -ox || ly !== -oy) continue;
4855
+ const out2 = {};
4856
+ out2.origin = [ox, oy];
4857
+ for (let k = 0; k < j; k++) {
4858
+ if (ops[k].name === "translate") {
4859
+ const tx = (_e = ops[k].args[0]) != null ? _e : 0;
4860
+ const ty = (_f = ops[k].args[1]) != null ? _f : 0;
4861
+ out2.translate = out2.translate ? [out2.translate[0] + tx, out2.translate[1] + ty] : [tx, ty];
4862
+ }
4863
+ }
4864
+ for (let k = j + 1; k < ops.length - 1; k++) {
4865
+ const op = ops[k];
4866
+ if (op.name === "rotate") out2.rotate = ((_g = out2.rotate) != null ? _g : 0) + ((_h = op.args[0]) != null ? _h : 0);
4867
+ else if (op.name === "scale") {
4868
+ const sx = (_i = op.args[0]) != null ? _i : 1;
4869
+ const sy = op.args.length > 1 ? op.args[1] : sx;
4870
+ out2.scale = out2.scale ? [out2.scale[0] * sx, out2.scale[1] * sy] : [sx, sy];
4871
+ }
4872
+ }
4873
+ return out2;
4874
+ }
4875
+ }
4876
+ let translate;
4877
+ let rotate;
4878
+ let scale;
4879
+ for (const op of ops) {
4880
+ if (op.name === "translate") {
4881
+ const dx = (_j = op.args[0]) != null ? _j : 0;
4882
+ const dy = (_k = op.args[1]) != null ? _k : 0;
4883
+ translate = translate ? [translate[0] + dx, translate[1] + dy] : [dx, dy];
4884
+ } else if (op.name === "rotate") {
4885
+ rotate = (rotate != null ? rotate : 0) + ((_l = op.args[0]) != null ? _l : 0);
4886
+ } else if (op.name === "scale") {
4887
+ const sx = (_m = op.args[0]) != null ? _m : 1;
4888
+ const sy = op.args.length > 1 ? op.args[1] : sx;
4889
+ scale = scale ? [scale[0] * sx, scale[1] * sy] : [sx, sy];
4890
+ }
4891
+ }
4892
+ const out = {};
4893
+ if (translate) out.translate = translate;
4894
+ if (rotate !== void 0) out.rotate = rotate;
4895
+ if (scale) out.scale = scale;
4896
+ return Object.keys(out).length ? out : void 0;
4897
+ }
4898
+ function wrapAncestorChainCompensation(inner, maskedNode, sourceId, ctx, includeTargetOwn) {
4899
+ const sourceNode = ctx.idMap.get(sourceId);
4900
+ const targetAncestors = ctx.maskAncestorChains.get(maskedNode) || [];
4901
+ const targetOwn = includeTargetOwn ? extractTranslateOnly(maskedNode, ctx) : void 0;
4902
+ const targetChain = targetOwn ? [...targetAncestors, targetOwn] : targetAncestors;
4903
+ const sourceChain = sourceNode && ctx.maskAncestorChains.get(sourceNode) || [];
4904
+ if (!targetChain.length && !sourceChain.length) return inner;
4905
+ const times = /* @__PURE__ */ new Set();
4906
+ for (const a of targetChain) if (a.translateKeyframes) for (const kf of a.translateKeyframes) times.add(kf.time);
4907
+ for (const a of sourceChain) if (a.translateKeyframes) for (const kf of a.translateKeyframes) times.add(kf.time);
4908
+ const animated = times.size > 0;
4909
+ if (!animated) {
4910
+ const tgt = sumStaticTranslate(targetChain);
4911
+ const src = sumStaticTranslate(sourceChain);
4912
+ const dx = src[0] - tgt[0];
4913
+ const dy = src[1] - tgt[1];
4914
+ if (dx === 0 && dy === 0) return inner;
4915
+ return { type: "g", transform: "translate(" + dx + "," + dy + ")", children: [inner] };
4916
+ }
4917
+ const sortedTimes = Array.from(times).sort((a, b) => a - b);
4918
+ const keyframes = sortedTimes.map((t) => {
4919
+ const tgt = sumTranslateAt(targetChain, t);
4920
+ const src = sumTranslateAt(sourceChain, t);
4921
+ return { time: t, value: { translate: [src[0] - tgt[0], src[1] - tgt[1]] } };
4922
+ });
4923
+ return { type: "g", animate: { transform: { keyframes } }, children: [inner] };
4924
+ }
4925
+ function sumStaticTranslate(chain) {
4926
+ let x = 0, y = 0;
4927
+ for (const a of chain) {
4928
+ if (a.translate) {
4929
+ x += a.translate[0];
4930
+ y += a.translate[1];
4931
+ }
4932
+ }
4933
+ return [x, y];
4934
+ }
4935
+ function sumTranslateAt(chain, t) {
4936
+ let x = 0, y = 0;
4937
+ for (const a of chain) {
4938
+ if (a.translateKeyframes && a.translateKeyframes.length) {
4939
+ const v = interpKfs(a.translateKeyframes, t);
4940
+ x += v[0];
4941
+ y += v[1];
4942
+ } else if (a.translate) {
4943
+ x += a.translate[0];
4944
+ y += a.translate[1];
4945
+ }
4946
+ }
4947
+ return [x, y];
4948
+ }
4949
+ function interpKfs(kfs, t) {
4950
+ if (t <= kfs[0].time) return kfs[0].value;
4951
+ if (t >= kfs[kfs.length - 1].time) return kfs[kfs.length - 1].value;
4952
+ for (let i = 1; i < kfs.length; i++) {
4953
+ if (t <= kfs[i].time) {
4954
+ const prev = kfs[i - 1];
4955
+ const cur = kfs[i];
4956
+ const a = (t - prev.time) / (cur.time - prev.time);
4957
+ return [prev.value[0] + (cur.value[0] - prev.value[0]) * a, prev.value[1] + (cur.value[1] - prev.value[1]) * a];
4958
+ }
4959
+ }
4960
+ return kfs[kfs.length - 1].value;
4961
+ }
4962
+ function collectMaskAncestorChains(root, ctx) {
4963
+ const interestingNodes = /* @__PURE__ */ new Set();
4964
+ const collectInterestingNodes = (n) => {
4965
+ var _a2, _b;
4966
+ const maskSourceId = stripHash((_b = (_a2 = n.effects) == null ? void 0 : _a2.maskedBy) == null ? void 0 : _b.source);
4967
+ if (typeof maskSourceId === "string") {
4968
+ interestingNodes.add(n);
4969
+ const sourceNode = ctx.idMap.get(maskSourceId);
4970
+ if (sourceNode) interestingNodes.add(sourceNode);
4971
+ }
4972
+ if (Array.isArray(n.children)) for (const ch of n.children) collectInterestingNodes(ch);
4973
+ };
4974
+ collectInterestingNodes(root);
4975
+ if (interestingNodes.size === 0) return;
4976
+ const walk = (node, chain) => {
4977
+ if (interestingNodes.has(node)) ctx.maskAncestorChains.set(node, chain);
4978
+ if (Array.isArray(node.children)) {
4979
+ const own = extractTranslateOnly(node, ctx);
4980
+ const next = own ? [...chain, own] : chain;
4981
+ for (const ch of node.children) walk(ch, next);
4982
+ }
4983
+ };
4984
+ walk(root, []);
4985
+ }
4986
+ function extractTranslateOnly(node, ctx) {
4987
+ var _a2, _b, _c;
4988
+ const tr = node.transform;
4989
+ const animateBlock = node.animate && typeof node.animate === "object" && !Array.isArray(node.animate) ? node.animate.transform : void 0;
4990
+ if (tr === void 0 && !animateBlock) return void 0;
4991
+ const out = {};
4992
+ if (typeof tr === "string") {
4993
+ const parts = parseTranslateOnlyFromString(tr, ctx);
4994
+ if (parts) out.translate = parts;
4995
+ } else if (tr && typeof tr === "object" && !tr.keyframes) {
4996
+ const wrapped = tr.value;
4997
+ const value = wrapped && typeof wrapped === "object" ? wrapped : tr;
4998
+ if (value && typeof value === "object" && Array.isArray(value.translate)) {
4999
+ out.translate = [value.translate[0] || 0, value.translate[1] || 0];
5000
+ }
5001
+ if (value && (value.rotate !== void 0 || value.scale !== void 0 || value.skew !== void 0)) {
5002
+ ctx.warnings.push("maskedBy ancestor: non-translate transform parts ignored (rotate/scale not yet supported)");
5003
+ }
5004
+ }
5005
+ if (animateBlock && Array.isArray(animateBlock.keyframes)) {
5006
+ const kfs = animateBlock.keyframes;
5007
+ const translateKfs = [];
5008
+ for (const kf of kfs) {
5009
+ const v = (_a2 = kf.value) != null ? _a2 : kf.v;
5010
+ const t = (_c = (_b = kf.time) != null ? _b : kf.t) != null ? _c : 0;
5011
+ if (v && typeof v === "object" && Array.isArray(v.translate)) {
5012
+ translateKfs.push({ time: t, value: [v.translate[0] || 0, v.translate[1] || 0] });
5013
+ if (v.rotate !== void 0 || v.scale !== void 0 || v.skew !== void 0) {
5014
+ ctx.warnings.push("maskedBy ancestor: animated non-translate parts ignored");
5015
+ }
5016
+ }
5017
+ }
5018
+ if (translateKfs.length) out.translateKeyframes = translateKfs;
5019
+ }
5020
+ return out.translate || out.translateKeyframes ? out : void 0;
5021
+ }
5022
+ function parseTranslateOnlyFromString(s, ctx) {
5023
+ const re = /([a-zA-Z]+)\s*\(([^)]*)\)/g;
5024
+ let m;
5025
+ let x = 0, y = 0;
5026
+ let seen = false;
5027
+ let droppedNonTranslate = false;
5028
+ while ((m = re.exec(s)) !== null) {
5029
+ const name = m[1];
5030
+ const args = m[2].split(/[\s,]+/).filter((a) => a.length > 0).map(Number);
5031
+ if (name === "translate") {
5032
+ x += args[0] || 0;
5033
+ y += args[1] || 0;
5034
+ seen = true;
5035
+ } else {
5036
+ droppedNonTranslate = true;
5037
+ }
5038
+ }
5039
+ if (droppedNonTranslate) ctx.warnings.push("maskedBy ancestor: non-translate transform in string ignored: " + s);
5040
+ return seen ? [x, y] : void 0;
5041
+ }
5042
+
5043
+ // src/effects/reference/refEffect.ts
5044
+ function applyRefHref(node, clone2, ctx) {
5045
+ if (!clone2) return;
5046
+ const sourceId = stripHash(clone2.source);
5047
+ if (!sourceId) {
5048
+ if (clone2.without === PxCloneWithout.translate) ctx.errors.push("clone: content ref missing `source`");
5049
+ return;
5050
+ }
5051
+ const targetId = clone2.without === PxCloneWithout.translate ? ctx.contentRefInnerIds.get(sourceId) || sourceId : sourceId;
5052
+ node.href = "#" + targetId;
5053
+ }
5054
+ function applyRefAndTransformationEffect(node, clone2, transformBy, ctx) {
5055
+ applyRefHref(node, clone2, ctx);
5056
+ return applyTransformByEffect(node, transformBy, ctx);
5057
+ }
5058
+
5059
+ // src/effects/transform/repeaterEffect.ts
5060
+ function applyRepeaterEffect(node, fx, ctx) {
5061
+ var _a2, _b;
5062
+ if (!fx) return node;
5063
+ const copies = (_a2 = fx.copies) != null ? _a2 : 1;
5064
+ if (copies < 1) {
5065
+ ctx.errors.push("repeater.copies invalid: " + fx.copies);
5066
+ return node;
5067
+ }
5068
+ const sharedTransform = node.transform;
5069
+ const sharedAnimTransform = (_b = node.animate) == null ? void 0 : _b.transform;
5070
+ const base = clone(node);
5071
+ delete base.transform;
5072
+ if (base.animate) {
5073
+ delete base.animate.transform;
5074
+ if (Object.keys(base.animate).length === 0) delete base.animate;
5075
+ }
5076
+ delete base.id;
5077
+ const children = [base];
5078
+ for (let i = 1; i < copies; i++) {
5079
+ const baseClone = clone(base);
5080
+ const synthFx = synthesisePerCopyFx(fx, i);
5081
+ const wrapped = synthFx ? applyTransformByEffect(baseClone, synthFx, ctx) : baseClone;
5082
+ children.push(wrapped);
5083
+ }
5084
+ const wrapper = { type: "g", children };
5085
+ if (node.id) wrapper.id = node.id;
5086
+ if (sharedTransform !== void 0) wrapper.transform = sharedTransform;
5087
+ if (sharedAnimTransform !== void 0) wrapper.animate = { transform: sharedAnimTransform };
5088
+ return wrapper;
5089
+ }
5090
+ function synthesisePerCopyFx(fx, i) {
5091
+ const out = {};
5092
+ if (fx.translate !== void 0) {
5093
+ out.translate = mapAnimatable(fx.translate, (v) => [v[0] * i, v[1] * i]);
5094
+ }
5095
+ if (fx.rotate !== void 0) {
5096
+ out.rotate = mapAnimatable(fx.rotate, (v) => v * i);
5097
+ }
5098
+ if (fx.skew !== void 0) {
5099
+ out.skew = mapAnimatable(fx.skew, (v) => v * i);
5100
+ }
5101
+ if (fx.scale !== void 0) {
5102
+ out.scale = synthesiseScale(fx.scale, i);
5103
+ }
5104
+ if (fx.origin !== void 0) {
5105
+ out.origin = fx.origin;
5106
+ }
5107
+ return Object.keys(out).length ? out : void 0;
5108
+ }
5109
+ function mapAnimatable(raw, fn, wrapStatic = false) {
5110
+ const read = readAnimatable(raw);
5111
+ if (read.kind === "absent" /* Absent */) return raw;
5112
+ if (read.kind === "static" /* Static */) {
5113
+ const mapped = fn(read.value);
5114
+ const wasRawStatic = typeof raw === "number" || Array.isArray(raw);
5115
+ return wasRawStatic && !wrapStatic ? mapped : { value: mapped };
5116
+ }
5117
+ const out = {
5118
+ keyframes: read.keyframes.map((kf) => kf && kf.value !== void 0 ? __spreadProps(__spreadValues({}, kf), { value: fn(kf.value) }) : kf)
5119
+ };
5120
+ if (read.loop !== void 0) out.loop = read.loop;
5121
+ if (read.autoOrient !== void 0) out.autoOrient = read.autoOrient;
5122
+ if (read.base !== void 0) out.value = fn(read.base);
5123
+ return out;
5124
+ }
5125
+ function synthesiseScale(raw, i) {
5126
+ const scalePower = (v) => [Math.pow(v[0], i), Math.pow(v[1], i)];
5127
+ return mapAnimatable(raw, scalePower, true);
5128
+ }
5129
+
5130
+ // src/effects/reference/retimeEffect.ts
5131
+ var RETIME_MATERIALIZATION_MODE_INLINE_G = false;
5132
+ function asRetime(r) {
5133
+ var _a2, _b;
5134
+ return { start: (_a2 = r.start) != null ? _a2 : 0, stretch: (_b = r.stretch) != null ? _b : 1 };
5135
+ }
5136
+ var CROP_EDGE_MS = 1;
5137
+ function applyTimeCrop(useNode, crop, ctx) {
5138
+ const [start, end] = crop;
5139
+ if (!Number.isFinite(start) || !Number.isFinite(end)) {
5140
+ ctx.warnings.push("retime: timeCrop is not a pair of finite numbers \u2014 ignored");
5141
+ return;
5142
+ }
5143
+ const keyframes = end <= start ? [{ time: 0, value: 0 }] : [
5144
+ ...start > 0 ? [{ time: Math.max(0, start - CROP_EDGE_MS), value: 0 }] : [],
5145
+ { time: Math.max(0, start), value: 1 },
5146
+ { time: end, value: 1 },
5147
+ { time: end + CROP_EDGE_MS, value: 0 }
5148
+ ];
5149
+ const inner = __spreadValues({}, useNode);
5150
+ for (const k of Object.keys(useNode)) delete useNode[k];
5151
+ useNode.type = "g";
5152
+ useNode.children = [inner];
5153
+ useNode.animate = { opacity: { keyframes } };
5154
+ }
5155
+ function concatRetime(child, parent) {
5156
+ return {
5157
+ start: parent.start + parent.stretch * child.start,
5158
+ stretch: parent.stretch * child.stretch
5159
+ };
5160
+ }
5161
+ function readCloneRetime(n) {
5162
+ var _a2, _b;
5163
+ return (_b = (_a2 = n.effects) == null ? void 0 : _a2.clone) == null ? void 0 : _b.retime;
5164
+ }
5165
+ function clearCloneRetime(n) {
5166
+ var _a2;
5167
+ const clone2 = (_a2 = n.effects) == null ? void 0 : _a2.clone;
5168
+ if (!clone2) return;
5169
+ delete clone2.retime;
5170
+ if (Object.keys(clone2).length === 0) delete n.effects.clone;
5171
+ if (n.effects && Object.keys(n.effects).length === 0) delete n.effects;
5172
+ }
5173
+ function applyAllRetimeEffects(root, ctx) {
5174
+ ctx.idMap.clear();
5175
+ indexById(root, ctx.idMap);
5176
+ const sites = [];
5177
+ const collect = (n) => {
5178
+ var _a2;
5179
+ if (readCloneRetime(n)) sites.push(n);
5180
+ (_a2 = n.children) == null ? void 0 : _a2.forEach(collect);
5181
+ };
5182
+ collect(root);
5183
+ const reachCount = /* @__PURE__ */ new Map();
5184
+ for (const site of sites) {
5185
+ let count = 0;
5186
+ const visited = /* @__PURE__ */ new Set();
5187
+ const walk = (n) => {
5188
+ var _a2;
5189
+ if (!n) return;
5190
+ if (n !== site && readCloneRetime(n)) count++;
5191
+ if (n.type === "use" && n.href) {
5192
+ const id = stripHash(n.href);
5193
+ if (id && !visited.has(id)) {
5194
+ visited.add(id);
5195
+ walk(ctx.idMap.get(id));
5196
+ }
5197
+ }
5198
+ (_a2 = n.children) == null ? void 0 : _a2.forEach(walk);
5199
+ };
5200
+ const rootId = stripHash(site.href);
5201
+ if (rootId) {
5202
+ visited.add(rootId);
5203
+ walk(ctx.idMap.get(rootId));
5204
+ }
5205
+ reachCount.set(site, count);
5206
+ }
5207
+ sites.sort((a, b) => {
5208
+ var _a2, _b;
5209
+ return ((_a2 = reachCount.get(b)) != null ? _a2 : 0) - ((_b = reachCount.get(a)) != null ? _b : 0);
5210
+ });
5211
+ for (const useNode of sites) {
5212
+ const retime = readCloneRetime(useNode);
5213
+ if (!retime) continue;
5214
+ const crop = retime.timeCrop;
5215
+ clearCloneRetime(useNode);
5216
+ materializeRetime(useNode, asRetime(retime), ctx);
5217
+ if (crop) applyTimeCrop(useNode, crop, ctx);
5218
+ }
5219
+ }
5220
+ function materializeRetime(useNode, retime, ctx) {
5221
+ const targetId = stripHash(useNode.href);
5222
+ if (!targetId) {
5223
+ ctx.errors.push("retime: <use> has no href to follow");
5224
+ return;
5225
+ }
5226
+ const chainRootId = buildChainClone(targetId, retime, ctx, /* @__PURE__ */ new Set());
5227
+ if (!chainRootId) return;
5228
+ if (RETIME_MATERIALIZATION_MODE_INLINE_G) {
5229
+ const cloneNode = ctx.idMap.get(chainRootId);
5230
+ useNode.type = "g";
5231
+ delete useNode.href;
5232
+ useNode.children = [cloneNode];
5233
+ applyUseOffsetToG(useNode);
5234
+ ctx.defs = ctx.defs.filter((d) => d !== cloneNode);
5235
+ } else {
5236
+ useNode.href = "#" + chainRootId;
5237
+ }
5238
+ }
5239
+ function buildChainClone(targetId, accum, ctx, chain) {
5240
+ if (chain.has(targetId)) {
5241
+ ctx.errors.push('retime: loop via "' + targetId + '"');
5242
+ return void 0;
5243
+ }
5244
+ const target = ctx.idMap.get(targetId);
5245
+ if (!target) {
5246
+ ctx.warnings.push('retime: target "' + targetId + '" not found');
5247
+ return void 0;
5248
+ }
5249
+ const cloneNode = clone(target);
5250
+ regenerateIdsInClone(cloneNode, ctx);
5251
+ if (target.type === "use") {
5252
+ remapKeyframeTimesOnly(cloneNode, accum.start, accum.stretch);
5253
+ } else {
5254
+ remapKeyframeTimes(cloneNode, accum.start, accum.stretch);
5255
+ }
5256
+ clearCloneRetime(cloneNode);
5257
+ if (target.type === "use" && target.href) {
5258
+ const subId = stripHash(target.href);
5259
+ if (subId) {
5260
+ const innerRetime = readCloneRetime(target);
5261
+ const subAccum = innerRetime ? concatRetime(asRetime(innerRetime), accum) : accum;
5262
+ const subChain = new Set(chain);
5263
+ subChain.add(targetId);
5264
+ const subId2 = buildChainClone(subId, subAccum, ctx, subChain);
5265
+ if (subId2) cloneNode.href = "#" + subId2;
5266
+ }
5267
+ } else {
5268
+ materializeNestedRetimeUses(cloneNode, accum, ctx, chain, targetId);
5269
+ }
5270
+ ctx.defs.push(cloneNode);
5271
+ if (typeof cloneNode.id === "string") ctx.idMap.set(cloneNode.id, cloneNode);
5272
+ return typeof cloneNode.id === "string" ? cloneNode.id : void 0;
5273
+ }
5274
+ function materializeNestedRetimeUses(node, accum, ctx, chain, parentTargetId) {
5275
+ const visit = (n) => {
5276
+ var _a2;
5277
+ const retime = readCloneRetime(n);
5278
+ if (n.type === "use" && retime && n.href) {
5279
+ const subId = stripHash(n.href);
5280
+ if (subId) {
5281
+ const subAccum = concatRetime(asRetime(retime), accum);
5282
+ const subChain = new Set(chain);
5283
+ subChain.add(parentTargetId);
5284
+ const subId2 = buildChainClone(subId, subAccum, ctx, subChain);
5285
+ if (subId2) n.href = "#" + subId2;
5286
+ }
5287
+ clearCloneRetime(n);
5288
+ }
5289
+ (_a2 = n.children) == null ? void 0 : _a2.forEach(visit);
5290
+ };
5291
+ visit(node);
5292
+ }
5293
+ function remapKeyframeTimes(node, start, stretch) {
5294
+ var _a2;
5295
+ remapKeyframeTimesOnly(node, start, stretch);
5296
+ (_a2 = node.children) == null ? void 0 : _a2.forEach((c) => remapKeyframeTimes(c, start, stretch));
5297
+ }
5298
+ function remapKeyframeTimesOnly(node, start, stretch) {
5299
+ const remap2 = (kfs) => {
5300
+ for (const kf of kfs) if (typeof kf.time === "number") kf.time = start + kf.time * stretch;
5301
+ };
5302
+ if (node.transform && typeof node.transform === "object" && Array.isArray(node.transform.keyframes)) {
5303
+ remap2(node.transform.keyframes);
5304
+ }
5305
+ if (node.animate && typeof node.animate === "object") {
5306
+ for (const prop of Object.keys(node.animate)) {
5307
+ const anim = node.animate[prop];
5308
+ if (anim && Array.isArray(anim.keyframes)) remap2(anim.keyframes);
5309
+ }
5310
+ }
5311
+ }
5312
+
5313
+ // src/effects/stroke/strokeTrimEffect.ts
5314
+ function applyStrokeTrimEffect(node, strokeTrim, ctx) {
5315
+ if (!strokeTrim) return node;
5316
+ const combined = strokeTrim.subPaths === PxStrokeTrimSubPaths.combined;
5317
+ const leafEntries = [];
5318
+ const measure = (n) => {
5319
+ if (Array.isArray(n.children) && n.children.length > 0) {
5320
+ for (const ch of n.children) measure(ch);
5321
+ return;
5322
+ }
5323
+ const d = typeof n.d === "string" ? n.d : shapeToPathD(n);
5324
+ if (d === void 0) return;
5325
+ const subpaths = parseSvgPathToBezier(d);
5326
+ if (!subpaths.length) return;
5327
+ const entry = { leaf: n, subpaths: [] };
5328
+ for (const sp of subpaths) {
5329
+ const lengthPx = pxBezierPathLength(sp);
5330
+ entry.subpaths.push({ subpath: sp, lengthPx, startOffsetPx: 0 });
5331
+ }
5332
+ leafEntries.push(entry);
5333
+ };
5334
+ measure(node);
5335
+ if (!leafEntries.length) return node;
5336
+ let acc = 0;
5337
+ const iterOrder = combined ? [...leafEntries].reverse() : leafEntries;
5338
+ for (const entry of iterOrder) {
5339
+ for (const sp of entry.subpaths) {
5340
+ if (!combined) acc = 0;
5341
+ sp.startOffsetPx = acc;
5342
+ acc += sp.lengthPx;
5343
+ }
5344
+ }
5345
+ const chainLengthPx = acc;
5346
+ if (combined && chainLengthPx < 1e-3) return node;
5347
+ const offsetReadRaw = readAnimatable(strokeTrim.offset);
5348
+ const offsetRead = offsetReadRaw.kind === "absent" /* Absent */ ? { kind: "static" /* Static */, value: 0 } : offsetReadRaw;
5349
+ const rangeReadRaw = readRangeWithCrossings(strokeTrim.range);
5350
+ const rangeRead = rangeReadRaw.kind === "absent" /* Absent */ ? { kind: "static" /* Static */, value: [0, 1] } : rangeReadRaw;
5351
+ const offsetValues = readScalarValues(offsetRead);
5352
+ const minOffset = offsetValues.length ? Math.min(...offsetValues) : 0;
5353
+ const maxOffset = offsetValues.length ? Math.max(...offsetValues) : 0;
5354
+ const minMaxOffset = [minOffset, maxOffset];
5355
+ if (leafEntries.length === 1 && leafEntries[0].leaf === node && leafEntries[0].subpaths.length === 1) {
5356
+ const entry = leafEntries[0];
5357
+ const sp = entry.subpaths[0];
5358
+ const pathLengthPx = combined ? chainLengthPx : sp.lengthPx;
5359
+ if (pathLengthPx < 1e-3) return node;
5360
+ const startOffsetPct = combined ? sp.startOffsetPx / pathLengthPx : 0;
5361
+ return collapseLeafWithTrim(entry.leaf, pathLengthPx, startOffsetPct, minMaxOffset, offsetRead, rangeRead);
5362
+ }
5363
+ const replacements = /* @__PURE__ */ new Map();
5364
+ for (const entry of leafEntries) {
5365
+ const newChildren = [];
5366
+ for (const sp of entry.subpaths) {
5367
+ const pathLengthPx = combined ? chainLengthPx : sp.lengthPx;
5368
+ if (pathLengthPx < 1e-3) continue;
5369
+ const startOffsetPct = combined ? sp.startOffsetPx / pathLengthPx : 0;
5370
+ newChildren.push(...buildSubpathNodes(entry.leaf, sp.subpath, pathLengthPx, startOffsetPct, minMaxOffset, offsetRead, rangeRead, ctx));
5371
+ }
5372
+ replacements.set(entry.leaf, wrapLeafAsGroup(entry.leaf, newChildren));
5373
+ }
5374
+ const swap = (n) => {
5375
+ const r = replacements.get(n);
5376
+ if (r) return r;
5377
+ if (Array.isArray(n.children) && n.children.length > 0) {
5378
+ return __spreadProps(__spreadValues({}, n), { children: n.children.map(swap) });
5379
+ }
5380
+ return n;
5381
+ };
5382
+ return swap(node);
5383
+ }
5384
+ function wrapLeafAsGroup(leaf, children) {
5385
+ const wrapper = __spreadProps(__spreadValues({}, leaf), { type: "g", children });
5386
+ delete wrapper.d;
5387
+ delete wrapper.strokeDasharray;
5388
+ delete wrapper.strokeDashoffset;
5389
+ delete wrapper.effects;
5390
+ return wrapper;
5391
+ }
5392
+ function buildSubpathNodes(leaf, subpath, pathLengthPx, startOffsetPct, minMaxOffset, offsetRead, rangeRead, ctx) {
5393
+ const offsetToDashOffset = makeOffsetToDashOffset(startOffsetPct, pathLengthPx, minMaxOffset);
5394
+ const rangeToDasharray = makeRangeToDasharray(pathLengthPx, minMaxOffset);
5395
+ const dashOffsetAttr = computeAnimAttr(offsetRead, offsetToDashOffset);
5396
+ const dashArrayAttr = computeAnimAttr(rangeRead, rangeToDasharray);
5397
+ const strokeOpacityAttr = computeOpacityFromRange(rangeRead);
5398
+ const dStr = bezierToSvgPath(subpath);
5399
+ const base = makeBareSubpath(dStr);
5400
+ applyAttr(base, "strokeDasharray", dashArrayAttr);
5401
+ applyAttr(base, "strokeDashoffset", dashOffsetAttr);
5402
+ applyAttr(base, "strokeOpacity", strokeOpacityAttr);
5403
+ return [base];
5404
+ }
5405
+ function collapseLeafWithTrim(leaf, pathLengthPx, startOffsetPct, minMaxOffset, offsetRead, rangeRead) {
5406
+ const offsetToDashOffset = makeOffsetToDashOffset(startOffsetPct, pathLengthPx, minMaxOffset);
5407
+ const rangeToDasharray = makeRangeToDasharray(pathLengthPx, minMaxOffset);
5408
+ const node = __spreadValues({}, leaf);
5409
+ delete node.effects;
5410
+ applyAttr(node, "strokeDasharray", computeAnimAttr(rangeRead, rangeToDasharray));
5411
+ applyAttr(node, "strokeDashoffset", computeAnimAttr(offsetRead, offsetToDashOffset));
5412
+ applyAttr(node, "strokeOpacity", computeOpacityFromRange(rangeRead));
5413
+ return node;
5414
+ }
5415
+ function makeBareSubpath(dStr) {
5416
+ return { type: "path", d: dStr };
5417
+ }
5418
+ var SMALL_PADDING_PX = 1;
5419
+ function makeOffsetToDashOffset(startOffsetPct, pathLengthPx, minMaxOffset) {
5420
+ const [minIdx] = getOffsetIndexRange(minMaxOffset);
5421
+ return (offsetVal) => pathLengthPx * (-offsetVal - minIdx + startOffsetPct) + SMALL_PADDING_PX;
5422
+ }
5423
+ function makeRangeToDasharray(pathLengthPx, minMaxOffset) {
5424
+ const [minIdx, maxIdx] = getOffsetIndexRange(minMaxOffset);
5425
+ const repeats = maxIdx - minIdx + 1;
5426
+ return (rangeVal) => {
5427
+ const a = clamp(rangeVal[0], 0, 1);
5428
+ const b = clamp(rangeVal[1], 0, 1);
5429
+ const minR = Math.min(a, b);
5430
+ const maxR = Math.max(a, b);
5431
+ const out = [0];
5432
+ let gap = SMALL_PADDING_PX;
5433
+ for (let i = 0; i < repeats; i++) {
5434
+ out.push(gap + minR * pathLengthPx);
5435
+ out.push((maxR - minR) * pathLengthPx);
5436
+ gap = (1 - maxR) * pathLengthPx;
5437
+ }
5438
+ out.push(gap + SMALL_PADDING_PX);
5439
+ return out;
5440
+ };
5441
+ }
5442
+ function getOffsetIndexRange(minMaxOffset) {
5443
+ return [
5444
+ Math.floor(Math.min(-minMaxOffset[0], -minMaxOffset[1])),
5445
+ Math.ceil(Math.max(-minMaxOffset[0], -minMaxOffset[1]))
5446
+ ];
5447
+ }
5448
+ function readScalarValues(r) {
5449
+ if (r.kind === "absent" /* Absent */) return [];
5450
+ if (r.kind === "static" /* Static */) return [r.value];
5451
+ const out = [];
5452
+ for (const kf of r.keyframes) {
5453
+ const v = keyframeValue(kf);
5454
+ if (typeof v === "number") out.push(v);
5455
+ }
5456
+ return out;
5457
+ }
5458
+ function computeAnimAttr(read, map) {
5459
+ if (read.kind === "absent" /* Absent */) return void 0;
5460
+ if (read.kind === "static" /* Static */) return { kind: "static" /* Static */, value: map(read.value) };
5461
+ return {
5462
+ kind: "animated" /* Animated */,
5463
+ keyframes: read.keyframes.map((kf) => ({
5464
+ time: keyframeTime(kf),
5465
+ value: map(keyframeValue(kf)),
5466
+ easing: keyframeEasing(kf)
5467
+ })),
5468
+ loop: read.loop
5469
+ };
5470
+ }
5471
+ function applyAttr(node, attrName, attr) {
5472
+ if (!attr) return;
5473
+ writeAnimatableChannel(node, attrName, attr);
5474
+ }
5475
+ var OPACITY_STEP_MS = 10;
5476
+ function computeOpacityFromRange(rangeRead) {
5477
+ const hide = (v) => v[0] === v[1];
5478
+ if (rangeRead.kind === "absent" /* Absent */) return void 0;
5479
+ if (rangeRead.kind === "static" /* Static */) return hide(rangeRead.value) ? { kind: "static" /* Static */, value: 0 } : void 0;
5480
+ const kfs = rangeRead.keyframes;
5481
+ let anyHide = false;
5482
+ let allHide = true;
5483
+ for (const kf of kfs) {
5484
+ if (hide(keyframeValue(kf))) anyHide = true;
5485
+ else allHide = false;
5486
+ }
5487
+ if (!anyHide) return void 0;
5488
+ if (allHide) return { kind: "static" /* Static */, value: 0 };
5489
+ const out = [];
5490
+ for (let i = 0; i < kfs.length; i++) {
5491
+ const kf = kfs[i];
5492
+ const prevKf = i > 0 ? kfs[i - 1] : void 0;
5493
+ const nextKf = i < kfs.length - 1 ? kfs[i + 1] : void 0;
5494
+ const t = keyframeTime(kf);
5495
+ const thisHide = hide(keyframeValue(kf));
5496
+ const prevHide = prevKf ? thisHide && hide(keyframeValue(prevKf)) : thisHide;
5497
+ const nextHide = nextKf ? thisHide && hide(keyframeValue(nextKf)) : thisHide;
5498
+ if (prevHide && !nextHide) {
5499
+ out.push({ time: t, value: 0 });
5500
+ out.push({ time: t + OPACITY_STEP_MS, value: 1 });
5501
+ } else if (!prevHide && nextHide) {
5502
+ out.push({ time: t - OPACITY_STEP_MS, value: 1 });
5503
+ out.push({ time: t, value: 0 });
5504
+ }
5505
+ }
5506
+ if (out.length <= 1) return void 0;
5507
+ return { kind: "animated" /* Animated */, keyframes: out };
5508
+ }
5509
+ function readRangeWithCrossings(raw) {
5510
+ const r = readAnimatable(raw);
5511
+ if (r.kind !== "animated" /* Animated */) return r;
5512
+ const kfs = r.keyframes.map((kf) => ({
5513
+ time: keyframeTime(kf),
5514
+ value: keyframeValue(kf),
5515
+ easing: keyframeEasing(kf)
5516
+ }));
5517
+ const hasReverse = kfs.some((kf) => kf.value[0] > kf.value[1]);
5518
+ if (!hasReverse) {
5519
+ return {
5520
+ kind: "animated" /* Animated */,
5521
+ keyframes: kfs.map((kf) => ({ time: kf.time, value: kf.value, easing: kf.easing }))
5522
+ };
5523
+ }
5524
+ const crossingTimes = [];
5525
+ for (let i = 1; i < kfs.length; i++) {
5526
+ const prev = kfs[i - 1];
5527
+ const cur = kfs[i];
5528
+ const dPrev = prev.value[1] - prev.value[0];
5529
+ const dCur = cur.value[1] - cur.value[0];
5530
+ if (dPrev * dCur < 0) {
5531
+ const t = bisectionForRangeCrossing(prev, cur);
5532
+ if (t !== null && t > prev.time && t < cur.time) {
5533
+ crossingTimes.push(Math.round(t));
5534
+ }
5535
+ }
5536
+ }
5537
+ const uniqueTs = Array.from(new Set(crossingTimes)).sort((a, b) => a - b);
5538
+ const out = [];
5539
+ let j = 0;
5540
+ for (const kf of kfs) {
5541
+ while (j < uniqueTs.length && uniqueTs[j] < kf.time) {
5542
+ const t = uniqueTs[j++];
5543
+ const v2 = interpolateRangeAt(kfs, t);
5544
+ const m = (v2[0] + v2[1]) / 2;
5545
+ out.push({ time: t, value: [m, m] });
5546
+ }
5547
+ const v = kf.value[0] > kf.value[1] ? [kf.value[1], kf.value[0]] : kf.value;
5548
+ out.push({ time: kf.time, value: v, easing: kf.easing });
5549
+ }
5550
+ while (j < uniqueTs.length) {
5551
+ const t = uniqueTs[j++];
5552
+ const v = interpolateRangeAt(kfs, t);
5553
+ const m = (v[0] + v[1]) / 2;
5554
+ out.push({ time: t, value: [m, m] });
5555
+ }
5556
+ return { kind: "animated" /* Animated */, keyframes: out };
5557
+ }
5558
+ function bisectionForRangeCrossing(prev, cur) {
5559
+ const f = (t) => {
5560
+ const a = (t - prev.time) / (cur.time - prev.time);
5561
+ const v0 = prev.value[0] + (cur.value[0] - prev.value[0]) * a;
5562
+ const v1 = prev.value[1] + (cur.value[1] - prev.value[1]) * a;
5563
+ return v1 - v0;
5564
+ };
5565
+ let lo = prev.time, hi = cur.time;
5566
+ let fLo = f(lo);
5567
+ if (fLo === 0) return lo;
5568
+ const fHi = f(hi);
5569
+ if (fHi === 0) return hi;
5570
+ if (fLo * fHi > 0) return null;
5571
+ for (let i = 0; i < 100; i++) {
5572
+ const mid = (lo + hi) / 2;
5573
+ const fMid = f(mid);
5574
+ if (fMid === 0 || Math.abs(hi - lo) < 1e-4) return mid;
5575
+ if (fLo * fMid < 0) {
5576
+ hi = mid;
5577
+ } else {
5578
+ lo = mid;
5579
+ fLo = fMid;
5580
+ }
5581
+ }
5582
+ return (lo + hi) / 2;
5583
+ }
5584
+ function interpolateRangeAt(kfs, t) {
5585
+ if (t <= kfs[0].time) return kfs[0].value;
5586
+ if (t >= kfs[kfs.length - 1].time) return kfs[kfs.length - 1].value;
5587
+ for (let i = 1; i < kfs.length; i++) {
5588
+ if (t <= kfs[i].time) {
5589
+ const prev = kfs[i - 1];
5590
+ const cur = kfs[i];
5591
+ const a = (t - prev.time) / (cur.time - prev.time);
5592
+ return [
5593
+ prev.value[0] + (cur.value[0] - prev.value[0]) * a,
5594
+ prev.value[1] + (cur.value[1] - prev.value[1]) * a
5595
+ ];
5596
+ }
5597
+ }
5598
+ return kfs[kfs.length - 1].value;
5599
+ }
5600
+ function pxBezierPathLength(path) {
5601
+ const v = path.v;
5602
+ if (!v || v.length < 2) return 0;
5603
+ let total = 0;
5604
+ for (let i = 0; i < v.length - 1; i++) {
5605
+ total += segmentLength(path, i, i + 1);
5606
+ }
5607
+ if (path.c && v.length > 1) {
5608
+ total += segmentLength(path, v.length - 1, 0);
5609
+ }
5610
+ return total;
5611
+ }
5612
+ function segmentLength(path, from, to) {
5613
+ var _a2, _b, _c, _d;
5614
+ const v = path.v;
5615
+ const p0 = v[from];
5616
+ const p3 = v[to];
5617
+ const p1 = (_b = (_a2 = path.o) == null ? void 0 : _a2[from]) != null ? _b : p0;
5618
+ const p2 = (_d = (_c = path.i) == null ? void 0 : _c[to]) != null ? _d : p3;
5619
+ const lut = bezier2D_arcLengthLUT(p0, p1, p2, p3);
5620
+ return lut.ds[lut.ds.length - 1];
5621
+ }
5622
+ var ARC_KAPPA = 0.5522847498307936;
5623
+ function shapeToPathD(node) {
5624
+ var _a2, _b, _c, _d, _e, _f, _g, _h, _i, _j;
5625
+ if (node.type === "rect") {
5626
+ const x = Number((_a2 = node.x) != null ? _a2 : 0), y = Number((_b = node.y) != null ? _b : 0);
5627
+ const w = Number((_c = node.width) != null ? _c : 0), h = Number((_d = node.height) != null ? _d : 0);
5628
+ return "M" + (x + w) + "," + y + "L" + (x + w) + "," + (y + h) + "L" + x + "," + (y + h) + "L" + x + "," + y + "L" + (x + w) + "," + y + "z";
5629
+ }
5630
+ if (node.type === "ellipse" || node.type === "circle") {
5631
+ const cx = Number((_e = node.cx) != null ? _e : 0), cy = Number((_f = node.cy) != null ? _f : 0);
5632
+ const rx = node.type === "circle" ? Number((_g = node.r) != null ? _g : 0) : Number((_h = node.rx) != null ? _h : 0);
5633
+ const ry = node.type === "circle" ? Number((_i = node.r) != null ? _i : 0) : Number((_j = node.ry) != null ? _j : 0);
5634
+ if (!(rx > 0) || !(ry > 0)) return void 0;
5635
+ const kx = rx * ARC_KAPPA, ky = ry * ARC_KAPPA;
5636
+ const c = (x1, y1, x2, y2, x, y) => "C" + x1 + "," + y1 + " " + x2 + "," + y2 + " " + x + "," + y;
5637
+ return "M" + (cx + rx) + "," + cy + c(cx + rx, cy + ky, cx + kx, cy + ry, cx, cy + ry) + c(cx - kx, cy + ry, cx - rx, cy + ky, cx - rx, cy) + c(cx - rx, cy - ky, cx - kx, cy - ry, cx, cy - ry) + c(cx + kx, cy - ry, cx + rx, cy - ky, cx + rx, cy) + "z";
5638
+ }
5639
+ return void 0;
5640
+ }
5641
+
5642
+ // src/effects/PlayerEffectsUtil.ts
5643
+ function materializeNodeEffects(root) {
5644
+ var _a2, _b;
5645
+ const ctx = {
5646
+ defs: [],
5647
+ warnings: [],
5648
+ errors: [],
5649
+ idMap: /* @__PURE__ */ new Map(),
5650
+ nextId: 0,
5651
+ contentRefInnerIds: /* @__PURE__ */ new Map(),
5652
+ maskAncestorChains: /* @__PURE__ */ new Map(),
5653
+ // Resolved engine: `frames` ONLY when explicitly set; auto/waapi/unset →
5654
+ // waapi (we're not 100% sure it's frames, and CSS/WAAPI need the inline form).
5655
+ engine: resolveTimelineEngine((_a2 = getAnimatorConfig(root)) == null ? void 0 : _a2.engine),
5656
+ glyphs: (_b = getDefinitions(root)) == null ? void 0 : _b.fonts
5657
+ };
5658
+ const working = clone(root);
5659
+ indexById(working, ctx.idMap);
5660
+ identifyContentRefTargets(working, ctx, () => genId(ctx, "inner"));
5661
+ collectMaskAncestorChains(working, ctx);
5662
+ const afterPass1 = applyPlayerEffects_exceptRetime(working, ctx);
5663
+ const out = applyPlayerEffects_retime(afterPass1, ctx);
5664
+ spliceDefs(out, ctx.defs);
5665
+ return { root: out, defs: ctx.defs, warnings: ctx.warnings, errors: ctx.errors };
5666
+ }
5667
+ function applyPlayerEffects_exceptRetime(node, ctx) {
5668
+ if (node.children) node.children = node.children.map((child) => applyPlayerEffects_exceptRetime(child, ctx));
5669
+ const fx = node.effects;
5670
+ const originalId = typeof node.id === "string" ? node.id : void 0;
5671
+ const innerIdForContentRef = originalId ? ctx.contentRefInnerIds.get(originalId) : void 0;
5672
+ if (!fx && !innerIdForContentRef) return node;
5673
+ const { transformBy, repeater, maskedBy, clipPath, strokeTrim, clone: cloneFx, fillGradient, strokeGradient, textPath, text } = fx != null ? fx : {};
5674
+ if (fx) delete node.effects;
5675
+ let n = node;
5676
+ let consumedByGlyphs = false;
5677
+ if (text == null ? void 0 : text.useGlyphs) {
5678
+ if (textPath) {
5679
+ const pathD = typeof textPath.pathData === "string" ? textPath.pathData : void 0;
5680
+ const glyphed = applyTextGlyphsAlongPath(n, ctx, pathD, textPath.startOffset, textPath.textLength, textPath.pathOverflow);
5681
+ if (glyphed) {
5682
+ n = glyphed;
5683
+ consumedByGlyphs = true;
5684
+ }
5685
+ } else {
5686
+ n = applyTextGlyphsEffect(n, text, ctx);
5687
+ consumedByGlyphs = true;
5688
+ }
5689
+ }
5690
+ if (!consumedByGlyphs) n = applyTextPathEffect(n, textPath, ctx);
5691
+ n = applyFillGradientEffect(n, fillGradient, ctx);
5692
+ n = applyStrokeGradientEffect(n, strokeGradient, ctx);
5693
+ n = applyStrokeTrimEffect(n, strokeTrim, ctx);
5694
+ n = applyRepeaterEffect(n, repeater, ctx);
5695
+ n = applyMaskedByEffect(n, maskedBy, transformBy, ctx);
5696
+ n = applyClipPathEffect(n, clipPath, ctx);
5697
+ if (innerIdForContentRef) {
5698
+ applyRefHref(n, cloneFx, ctx);
5699
+ n = splitForContentRef(n, transformBy, originalId, innerIdForContentRef, ctx);
5700
+ } else {
5701
+ n = applyRefAndTransformationEffect(n, cloneFx, transformBy, ctx);
5702
+ }
5703
+ if (cloneFx == null ? void 0 : cloneFx.retime) node.effects = { clone: { retime: cloneFx.retime } };
5704
+ if (originalId) ctx.idMap.set(originalId, n);
5705
+ return n;
5706
+ }
5707
+ function applyPlayerEffects_retime(node, ctx) {
5708
+ applyAllRetimeEffects(node, ctx);
5709
+ return node;
5710
+ }
5711
+
5712
+ // src/materialize/PxOffsetPathMaterializer.ts
5713
+ function cubicAt(p0, c1, c2, p1, t) {
5714
+ const u = 1 - t;
5715
+ const a = u * u * u, b = 3 * u * u * t, c = 3 * u * t * t, d = t * t * t;
5716
+ return [
5717
+ a * p0[0] + b * c1[0] + c * c2[0] + d * p1[0],
5718
+ a * p0[1] + b * c1[1] + c * c2[1] + d * p1[1]
5719
+ ];
5720
+ }
5721
+ function cubicLength(p0, c1, c2, p1, steps = 64) {
5722
+ let len = 0;
5723
+ let prev = p0;
5724
+ for (let i = 1; i <= steps; i++) {
5725
+ const pt = cubicAt(p0, c1, c2, p1, i / steps);
5726
+ len += Math.hypot(pt[0] - prev[0], pt[1] - prev[1]);
5727
+ prev = pt;
5728
+ }
5729
+ return len;
5730
+ }
5731
+ var fmt2 = (n) => {
5732
+ const r = Math.round(n * 1e4) / 1e4;
5733
+ return Object.is(r, -0) ? "0" : String(r);
5734
+ };
5735
+ function buildOffsetPath(propAnim) {
5736
+ var _a2, _b, _c;
5737
+ if (propAnim.alongPathMode !== "offsetPath") return void 0;
5738
+ const kfs = propAnim.keyframes;
5739
+ if (!kfs || kfs.length < 2) return void 0;
5740
+ const first = keyframeValue(kfs[0]);
5741
+ const anchor = (first == null ? void 0 : first.origin) && first.origin.length >= 2 ? [first.origin[0], first.origin[1]] : [0, 0];
5742
+ const points = [];
5743
+ let keyframesCarryRotate = false;
5744
+ for (const kf of kfs) {
5745
+ const v = keyframeValue(kf);
5746
+ const tr = v == null ? void 0 : v.translate;
5747
+ if (!tr || tr.length < 2) return void 0;
5748
+ const parts = Object.keys(v);
5749
+ if (parts.some((p) => p !== TRANSFORM_PART.translate && p !== TRANSFORM_PART.origin && p !== TRANSFORM_PART.rotate)) return void 0;
5750
+ if ((v == null ? void 0 : v.rotate) !== void 0) {
5751
+ if (v.rotate !== 0) return void 0;
5752
+ keyframesCarryRotate = true;
5753
+ }
5754
+ const o = (_a2 = v == null ? void 0 : v.origin) != null ? _a2 : [0, 0];
5755
+ if (o[0] !== anchor[0] || o[1] !== anchor[1]) return void 0;
5756
+ points.push([tr[0] + anchor[0], tr[1] + anchor[1]]);
5757
+ }
5758
+ if (!kfs.some((kf) => keyframeTangentIn(kf) || keyframeTangentOut(kf))) return void 0;
5759
+ let d = "M" + fmt2(points[0][0]) + "," + fmt2(points[0][1]);
5760
+ const segLens = [];
5761
+ for (let i = 0; i < points.length - 1; i++) {
5762
+ const p0 = points[i], p1 = points[i + 1];
5763
+ const to = (_b = keyframeTangentOut(kfs[i])) != null ? _b : [0, 0];
5764
+ const ti = (_c = keyframeTangentIn(kfs[i + 1])) != null ? _c : [0, 0];
5765
+ const c1 = [p0[0] + to[0], p0[1] + to[1]];
5766
+ const c2 = [p1[0] + ti[0], p1[1] + ti[1]];
5767
+ d += "C" + fmt2(c1[0]) + "," + fmt2(c1[1]) + "," + fmt2(c2[0]) + "," + fmt2(c2[1]) + "," + fmt2(p1[0]) + "," + fmt2(p1[1]);
5768
+ segLens.push(cubicLength(p0, c1, c2, p1));
5769
+ }
5770
+ const total = segLens.reduce((a, b) => a + b, 0);
5771
+ if (!(total > 0)) return void 0;
5772
+ const distanceKfs = [];
5773
+ let cum = 0;
5774
+ for (let i = 0; i < kfs.length; i++) {
5775
+ if (i > 0) cum += segLens[i - 1];
5776
+ const out = { t: keyframeTime(kfs[i]), v: cum / total };
5777
+ const e = keyframeEasing(kfs[i]);
5778
+ if (e !== void 0) out.e = e;
5779
+ distanceKfs.push(out);
5780
+ }
5781
+ return { pathStr: d, distanceKfs, autoOrient: !!propAnim.autoOrient, anchor, keyframesCarryRotate };
5782
+ }
5783
+ function materializeOffsetPathsInTree(root) {
5784
+ const walk = (node) => {
5785
+ var _a2;
5786
+ let out = node;
5787
+ const anim = node.animate;
5788
+ const transform = anim == null ? void 0 : anim["transform"];
5789
+ if (transform) {
5790
+ const built = buildOffsetPath(transform);
5791
+ if (built) {
5792
+ const newAnimate = __spreadValues({}, anim);
5793
+ delete newAnimate[TRANSFORM_ATTR];
5794
+ const distance = { keyframes: built.distanceKfs };
5795
+ if (transform.loop !== void 0) distance.loop = transform.loop;
5796
+ newAnimate[OFFSET_DISTANCE_ATTR] = distance;
5797
+ const staticTr = node.transform;
5798
+ let newTransform = staticTr;
5799
+ if (staticTr && typeof staticTr === "object") {
5800
+ const t = __spreadValues({}, staticTr);
5801
+ delete t[TRANSFORM_PART.translate];
5802
+ delete t[TRANSFORM_PART.origin];
5803
+ if (built.keyframesCarryRotate) delete t[TRANSFORM_PART.rotate];
5804
+ newTransform = Object.keys(t).length ? t : void 0;
5805
+ }
5806
+ out = __spreadProps(__spreadValues({}, node), {
5807
+ animate: newAnimate,
5808
+ style: __spreadProps(__spreadValues({}, node.style), {
5809
+ offsetPath: "path('" + built.pathStr + "')",
5810
+ offsetAnchor: fmt2(built.anchor[0]) + "px " + fmt2(built.anchor[1]) + "px",
5811
+ offsetRotate: built.autoOrient ? "auto" : "0deg",
5812
+ offsetDistance: "0%"
5813
+ })
5814
+ });
5815
+ if (newTransform !== void 0) out.transform = newTransform;
5816
+ else delete out.transform;
5817
+ }
5818
+ }
5819
+ if ((_a2 = out.children) == null ? void 0 : _a2.length) {
5820
+ const children = out.children.map(walk);
5821
+ if (children.some((c, i) => c !== out.children[i])) out = __spreadProps(__spreadValues({}, out), { children });
5822
+ }
5823
+ return out;
5824
+ };
5825
+ return walk(root);
5826
+ }
5827
+
5828
+ // src/materialize/PxAnimatorUseMaterializer.ts
5829
+ function materializeAnimatedUseInstances(root) {
5830
+ var _a2;
5831
+ const idMap = buildIdMap2(root);
5832
+ const animatedIds = computeAnimatedSubtreeIds(root, idMap);
5833
+ if (animatedIds.size === 0) return root;
5834
+ let idCounter = 0;
5835
+ const genId3 = () => "_lw_use_mat_" + ++idCounter;
5836
+ const rootViewport = readRootViewport(root);
5837
+ const defsCollector = [];
5838
+ const walked = walkAndMaterialize2(root, idMap, animatedIds, genId3, defsCollector, rootViewport);
5839
+ if (defsCollector.length === 0) return walked;
5840
+ const defsNode = { type: "defs", children: defsCollector };
5841
+ const newChildren = [...(_a2 = walked.children) != null ? _a2 : [], defsNode];
5842
+ return __spreadProps(__spreadValues({}, walked), { children: newChildren });
5843
+ }
5844
+ function readRootViewport(root) {
5845
+ const vb = parseViewBox(root.viewBox);
5846
+ if (vb) return [vb[2], vb[3]];
5847
+ const w = numericAttr(root.width);
5848
+ const h = numericAttr(root.height);
5849
+ if (w !== void 0 && h !== void 0) return [w, h];
5850
+ return [1, 1];
5851
+ }
5852
+ function numericAttr(v) {
5853
+ if (typeof v === "number") return v;
5854
+ if (typeof v === "string") {
5855
+ const n = parseFloat(v);
5856
+ return Number.isFinite(n) ? n : void 0;
5857
+ }
5858
+ return void 0;
5859
+ }
5860
+ function buildIdMap2(root) {
5861
+ const map = /* @__PURE__ */ new Map();
5862
+ const visit = (n) => {
5863
+ var _a2;
5864
+ if (typeof n.id === "string") map.set(n.id, n);
5865
+ (_a2 = n.children) == null ? void 0 : _a2.forEach(visit);
5866
+ };
5867
+ visit(root);
5868
+ return map;
5869
+ }
5870
+ function computeAnimatedSubtreeIds(root, idMap) {
5871
+ const cache = /* @__PURE__ */ new WeakMap();
5872
+ const result = /* @__PURE__ */ new Set();
5873
+ const hasAnim = (n, visiting) => {
5874
+ const cached = cache.get(n);
5875
+ if (cached !== void 0) return cached;
5876
+ if (visiting.has(n)) return false;
5877
+ visiting.add(n);
5878
+ let r = false;
5879
+ if (n.animate && typeof n.animate === "object" && !Array.isArray(n.animate)) {
5880
+ for (const _ in n.animate) {
5881
+ r = true;
5882
+ break;
5883
+ }
5884
+ }
5885
+ if (!r && n.children) {
5886
+ for (const ch of n.children) {
5887
+ if (hasAnim(ch, visiting)) {
5888
+ r = true;
5889
+ break;
5890
+ }
5891
+ }
5892
+ }
5893
+ if (!r && n.type === "use" && typeof n.href === "string") {
5894
+ const targetId = stripHash2(n.href);
5895
+ const target = targetId ? idMap.get(targetId) : void 0;
5896
+ if (target) r = hasAnim(target, visiting);
5897
+ }
5898
+ visiting.delete(n);
5899
+ cache.set(n, r);
5900
+ return r;
5901
+ };
5902
+ for (const [id, node] of idMap) {
5903
+ if (hasAnim(node, /* @__PURE__ */ new Set())) result.add(id);
5904
+ }
5905
+ return result;
5906
+ }
5907
+ function stripHash2(href) {
5908
+ if (typeof href !== "string") return void 0;
5909
+ return href.startsWith("#") ? href.slice(1) : href;
5910
+ }
5911
+ function materializeOneUse(useNode, target, idMap, animatedIds, genId3, defsCollector, rootViewport) {
5912
+ var _a2, _b, _c, _d;
5913
+ const clone2 = deepClonePxNode(target);
5914
+ regenerateIdsAndRewriteRefs(clone2, genId3);
5915
+ const symbolViewBox = clone2.type === "symbol" ? parseViewBox(clone2.viewBox) : void 0;
5916
+ const useW = (_b = (_a2 = numericAttr(useNode.width)) != null ? _a2 : symbolViewBox == null ? void 0 : symbolViewBox[2]) != null ? _b : rootViewport[0];
5917
+ const useH = (_d = (_c = numericAttr(useNode.height)) != null ? _c : symbolViewBox == null ? void 0 : symbolViewBox[3]) != null ? _d : rootViewport[1];
5918
+ const rewrittenClone = clone2.type === "symbol" ? rewriteSymbolRootToGroup(clone2, genId3, defsCollector, useW, useH) : clone2;
5919
+ const materializedClone = walkAndMaterialize2(rewrittenClone, idMap, animatedIds, genId3, defsCollector, rootViewport);
5920
+ const newNode = __spreadProps(__spreadValues({}, useNode), { type: "g", children: [materializedClone] });
5921
+ delete newNode.href;
5922
+ delete newNode.width;
5923
+ delete newNode.height;
5924
+ return applyUseOffsetToG(newNode);
5925
+ }
5926
+ function rewriteSymbolRootToGroup(symbolNode, genId3, defsCollector, useW, useH) {
5927
+ const viewBox = parseViewBox(symbolNode.viewBox);
5928
+ const g = __spreadProps(__spreadValues({}, symbolNode), { type: "g" });
5929
+ delete g.viewBox;
5930
+ delete g.preserveAspectRatio;
5931
+ delete g.width;
5932
+ delete g.height;
5933
+ if (!viewBox) return g;
5934
+ const [vbX, vbY, vbW, vbH] = viewBox;
5935
+ const scale = vbW > 0 && vbH > 0 ? Math.min(useW / vbW, useH / vbH) : 1;
5936
+ const xOff = (useW - vbW * scale) / 2;
5937
+ const yOff = (useH - vbH * scale) / 2;
5938
+ const parts = [];
5939
+ if (xOff !== 0 || yOff !== 0) parts.push("translate(" + xOff + "," + yOff + ")");
5940
+ if (scale !== 1) parts.push("scale(" + scale + ")");
5941
+ if (vbX !== 0 || vbY !== 0) parts.push("translate(" + -vbX + "," + -vbY + ")");
5942
+ if (parts.length) g.transform = parts.join("");
5943
+ const clipId = genId3();
5944
+ defsCollector.push({
5945
+ type: "clipPath",
5946
+ id: clipId,
5947
+ children: [{ type: "rect", x: vbX, y: vbY, width: vbW, height: vbH }]
5948
+ });
5949
+ g.clipPath = "url(#" + clipId + ")";
5950
+ return g;
5951
+ }
5952
+ function parseViewBox(v) {
5953
+ if (typeof v !== "string") return void 0;
5954
+ const parts = v.trim().split(/[\s,]+/).map(Number);
5955
+ if (parts.length < 4 || parts.some((n) => !Number.isFinite(n))) return void 0;
5956
+ return [parts[0], parts[1], parts[2], parts[3]];
5957
+ }
5958
+ function walkAndMaterialize2(node, idMap, animatedIds, genId3, defsCollector, rootViewport) {
5959
+ if (node.type === "use" && typeof node.href === "string") {
5960
+ const targetId = stripHash2(node.href);
5961
+ if (targetId && animatedIds.has(targetId)) {
5962
+ const target = idMap.get(targetId);
5963
+ if (target) return materializeOneUse(node, target, idMap, animatedIds, genId3, defsCollector, rootViewport);
5964
+ }
5965
+ }
5966
+ if (!node.children) return node;
5967
+ let changed = false;
5968
+ const newChildren = node.children.map((ch) => {
5969
+ const m = walkAndMaterialize2(ch, idMap, animatedIds, genId3, defsCollector, rootViewport);
5970
+ if (m !== ch) changed = true;
5971
+ return m;
5972
+ });
5973
+ return changed ? __spreadProps(__spreadValues({}, node), { children: newChildren }) : node;
3128
5974
  }
3129
- var r3 = (n) => Math.round(n * 1e3) / 1e3;
3130
- function extendedPathForBrowser(pathD, opts) {
5975
+
5976
+ // src/util/PxIdUtil.ts
5977
+ var _idCounter = 0;
5978
+ function generateUniqueId() {
5979
+ const timestamp = Date.now().toString(36);
5980
+ const counter = (++_idCounter).toString(36);
5981
+ const random = Math.random().toString(36).substring(2, 6);
5982
+ return "_px_" + timestamp + counter + random;
5983
+ }
5984
+ function deepClone(value) {
5985
+ if (value === null || typeof value !== "object") return value;
5986
+ if (Array.isArray(value)) return value.map((item) => deepClone(item));
5987
+ const obj = value;
5988
+ const cloned = {};
5989
+ for (const key of Object.keys(obj)) {
5990
+ cloned[key] = deepClone(obj[key]);
5991
+ }
5992
+ return cloned;
5993
+ }
5994
+ function generateNewIds(doc) {
3131
5995
  var _a2;
3132
- if (opts.pathOverflow === "clip") return { d: pathD, startShift: 0 };
3133
- const sampler = createPathSampler(pathD);
3134
- if (!sampler || sampler.closed || sampler.totalLength <= 0) return { d: pathD, startShift: 0 };
3135
- const L = sampler.totalLength;
3136
- const margin = EXTEND_MARGIN_FRAC * L;
3137
- const so = numRange(opts.startOffset);
3138
- const runWidth = numRange(opts.textLength).max || ((_a2 = opts.advance) != null ? _a2 : 0);
3139
- const startOverflow = Math.max(0, -so.min);
3140
- const endOverflow = Math.max(0, so.max + runWidth - L);
3141
- const startExt = startOverflow > 0 ? startOverflow + margin : 0;
3142
- const endExt = endOverflow > 0 ? endOverflow + margin : 0;
3143
- if (endExt <= 0 && startExt <= 0) return { d: pathD, startShift: 0 };
3144
- const s = sampler.sampleAtDistance(0);
3145
- const e = sampler.sampleAtDistance(L);
3146
- let d = pathD;
3147
- if (startExt > 0) {
3148
- const sx = s.x - Math.cos(s.angle) * startExt, sy = s.y - Math.sin(s.angle) * startExt;
3149
- const rest = pathD.replace(/^\s*[Mm]\s*-?[\d.]+[\s,]+-?[\d.]+/, "");
3150
- d = "M" + r3(sx) + "," + r3(sy) + "L" + r3(s.x) + "," + r3(s.y) + rest;
5996
+ const cloned = deepClone(doc);
5997
+ const idMap = /* @__PURE__ */ new Map();
5998
+ const hashRefAttrs = /* @__PURE__ */ new Set(["href", "xlink:href"]);
5999
+ const urlRefAttrs = /* @__PURE__ */ new Set([
6000
+ "fill",
6001
+ "stroke",
6002
+ "clip-path",
6003
+ "clipPath",
6004
+ "mask",
6005
+ "marker",
6006
+ "marker-start",
6007
+ "marker-mid",
6008
+ "marker-end",
6009
+ "filter",
6010
+ "flood-color",
6011
+ "lighting-color"
6012
+ ]);
6013
+ const directIdRefAttrs = /* @__PURE__ */ new Set(["targetId", "boundElementId"]);
6014
+ const isEffectSourceRef = (key, parentKey) => key === "source" && (parentKey === "maskedBy" || parentKey === "clone");
6015
+ function collectIds(node) {
6016
+ if (!node || typeof node !== "object") return;
6017
+ if (node.id && typeof node.id === "string") {
6018
+ const oldId = node.id;
6019
+ const newId = generateUniqueId();
6020
+ idMap.set(oldId, newId);
6021
+ node.id = newId;
6022
+ } else if (node.animate) {
6023
+ node.id = generateUniqueId();
6024
+ }
6025
+ if (Array.isArray(node.children)) {
6026
+ for (const child of node.children) {
6027
+ collectIds(child);
6028
+ }
6029
+ }
3151
6030
  }
3152
- if (endExt > 0) {
3153
- const ex = e.x + Math.cos(e.angle) * endExt, ey = e.y + Math.sin(e.angle) * endExt;
3154
- d += "L" + r3(ex) + "," + r3(ey);
6031
+ function updateRefs(node, parentKey) {
6032
+ if (!node || typeof node !== "object") return;
6033
+ for (const [key, value] of Object.entries(node)) {
6034
+ if (key === "children") {
6035
+ if (Array.isArray(value)) {
6036
+ for (const child of value) {
6037
+ updateRefs(child);
6038
+ }
6039
+ }
6040
+ continue;
6041
+ }
6042
+ if (typeof value === "string") {
6043
+ if (hashRefAttrs.has(key) && value.startsWith("#")) {
6044
+ const oldId = value.slice(1);
6045
+ const newId = idMap.get(oldId);
6046
+ if (newId) {
6047
+ node[key] = "#" + newId;
6048
+ }
6049
+ } else if (urlRefAttrs.has(key)) {
6050
+ node[key] = replaceUrlRefs(value, idMap);
6051
+ } else if (directIdRefAttrs.has(key) || isEffectSourceRef(key, parentKey)) {
6052
+ const hasHash = value.startsWith("#");
6053
+ const newId = idMap.get(hasHash ? value.slice(1) : value);
6054
+ if (newId) {
6055
+ node[key] = hasHash ? "#" + newId : newId;
6056
+ }
6057
+ } else if (value.includes("url(#")) {
6058
+ node[key] = replaceUrlRefs(value, idMap);
6059
+ }
6060
+ } else if (key === "style" && typeof value === "object" && value !== null) {
6061
+ for (const [styleProp, styleValue] of Object.entries(value)) {
6062
+ if (typeof styleValue === "string") {
6063
+ value[styleProp] = replaceUrlRefs(styleValue, idMap);
6064
+ }
6065
+ }
6066
+ } else if (typeof value === "object" && value !== null) {
6067
+ updateRefs(value, key);
6068
+ }
6069
+ }
3155
6070
  }
3156
- return { d, startShift: startExt };
6071
+ collectIds(cloned);
6072
+ updateRefs(cloned);
6073
+ const docBindings = (_a2 = cloned.animator) == null ? void 0 : _a2.bindings;
6074
+ if (Array.isArray(docBindings)) {
6075
+ const updatedBindings = docBindings.map((binding) => {
6076
+ var _a3;
6077
+ const hashed = binding.target.startsWith("#");
6078
+ const id = hashed ? binding.target.slice(1) : binding.target;
6079
+ const newId = (_a3 = idMap.get(id)) != null ? _a3 : id;
6080
+ return __spreadProps(__spreadValues({}, binding), { target: hashed ? "#" + newId : newId });
6081
+ });
6082
+ cloned.animator = __spreadProps(__spreadValues({}, cloned.animator), { bindings: updatedBindings });
6083
+ }
6084
+ return cloned;
6085
+ }
6086
+ function replaceUrlRefs(value, idMap) {
6087
+ return value.replace(/url\(#([^)]+)\)/g, (match, oldId) => {
6088
+ const newId = idMap.get(oldId);
6089
+ return newId ? "url(#" + newId + ")" : match;
6090
+ });
3157
6091
  }
3158
6092
 
3159
- // src/format/PxDocumentDiagnostic.ts
3160
- var MAX_REPORTED = 6;
3161
- function diagnoseDocument(doc) {
3162
- try {
3163
- return { problems: validateDocument(doc) };
3164
- } catch (e) {
3165
- return { problems: [] };
6093
+ // src/materialize/PxRestPose.ts
6094
+ var TRANSFORM_CHANNEL = "transform";
6095
+ var REVERSED_DIRECTIONS = /* @__PURE__ */ new Set(["reverse", "alternate-reverse"]);
6096
+ var VERIFY_SAMPLE_FRACTIONS = [0, 0.25, 0.5, 0.75, 1];
6097
+ var SCRATCH_ID_PREFIX = "__px_rest_";
6098
+ function materializeRestPosesInTree(root, engine) {
6099
+ var _a2, _b;
6100
+ const out = deepClone(root);
6101
+ const config = getAnimatorConfig(out) || {};
6102
+ const duration = +(config.duration || PX_DEFAULT_DURATION_MS);
6103
+ const firstFrameTime = REVERSED_DIRECTIONS.has(String(config.direction)) ? duration : 0;
6104
+ const nodes = animatedNodes(out);
6105
+ if (!nodes.size) return out;
6106
+ const before = bindingsOf(out, engine);
6107
+ const added = /* @__PURE__ */ new Map();
6108
+ for (const [key, animate] of before) {
6109
+ const node = nodes.get(key);
6110
+ if (!node) continue;
6111
+ const channels = Object.keys(animate);
6112
+ for (const channel of channels) {
6113
+ if (PX_TRANSFORM_FN_NAMES.has(channel)) continue;
6114
+ if (!mayCarryRestPose(node, channel)) continue;
6115
+ const value = firstFrameValue(animate, channel, firstFrameTime);
6116
+ if (value === void 0) continue;
6117
+ node[channel] = value;
6118
+ added.set(key, [...(_a2 = added.get(key)) != null ? _a2 : [], channel]);
6119
+ }
6120
+ if (channels.some((channel) => PX_TRANSFORM_FN_NAMES.has(channel)) && mayCarryTransformRestPose(node)) {
6121
+ const parts = firstFrameTransformParts(animate, firstFrameTime);
6122
+ if (parts) {
6123
+ node[TRANSFORM_CHANNEL] = parts;
6124
+ added.set(key, [...(_b = added.get(key)) != null ? _b : [], TRANSFORM_CHANNEL]);
6125
+ }
6126
+ }
6127
+ }
6128
+ if (added.size) {
6129
+ const after = bindingsOf(out, engine);
6130
+ for (const [key, channels] of added) {
6131
+ if (writesTheSameFrames(before.get(key), after.get(key), duration)) continue;
6132
+ const node = nodes.get(key);
6133
+ if (node) for (const channel of channels) delete node[channel];
6134
+ }
3166
6135
  }
6136
+ return out;
3167
6137
  }
3168
- function reportDocumentDiagnostics(doc, where) {
3169
- const { problems } = diagnoseDocument(doc);
3170
- if (!problems.length) return;
3171
- const shown = problems.slice(0, MAX_REPORTED);
3172
- const more = problems.length - shown.length;
3173
- console.warn(
3174
- where + ": this document does not match the animation schema in " + problems.length + " place" + (problems.length === 1 ? "" : "s") + ".\n" + shown.map((p) => " - " + p).join("\n") + (more > 0 ? "\n \u2026 and " + more + " more" : "") + "\n\nIf you did not author these keys, the usual cause is a build that MANGLES PROPERTY NAMES. An animation document is data loaded at runtime, so renaming the property reads inside the player stops them matching the keys in the JSON, and the animation silently does nothing. Feed the published reserved-name list to your minifier \u2014 @pixodesk/svg-animator-web/mangle-reserved.json \u2014 see docs/library/minification.md."
3175
- );
6138
+ function mayCarryRestPose(node, channel) {
6139
+ if (node[channel] !== void 0) return false;
6140
+ if (channel === TRANSFORM_CHANNEL) return mayCarryTransformRestPose(node);
6141
+ return true;
6142
+ }
6143
+ function mayCarryTransformRestPose(node) {
6144
+ if (node[TRANSFORM_CHANNEL] !== void 0) return false;
6145
+ for (const part of PX_TRANSFORM_FN_NAMES) if (node[part] !== void 0) return false;
6146
+ return true;
6147
+ }
6148
+ function firstFrameValue(animate, channel, timeMs) {
6149
+ const values = Object.values(calcAnimationValues({ [channel]: animate[channel] }, timeMs));
6150
+ return values.length === 1 && values[0] !== "" ? values[0] : void 0;
6151
+ }
6152
+ function firstFrameTransformParts(animate, timeMs) {
6153
+ const family = {};
6154
+ for (const channel of Object.keys(animate)) if (PX_TRANSFORM_FN_NAMES.has(channel)) family[channel] = animate[channel];
6155
+ const written = calcAnimationValues(family, timeMs)[TRANSFORM_CHANNEL];
6156
+ if (!written) return void 0;
6157
+ const parts = parseTransformParts(written.replace(/(px|deg)\b/g, ""));
6158
+ return parts && Object.keys(parts).length ? parts : void 0;
6159
+ }
6160
+ function writesTheSameFrames(a, b, durationMs) {
6161
+ if (!a || !b) return a === b;
6162
+ const frame = (animate, t) => JSON.stringify(calcAnimationValues(animate, t)).replace(/\s+/g, "");
6163
+ return VERIFY_SAMPLE_FRACTIONS.every((fraction) => frame(a, fraction * durationMs) === frame(b, fraction * durationMs));
6164
+ }
6165
+ function bindingsOf(tree, engine) {
6166
+ const scratch = deepClone(tree);
6167
+ const keyById = /* @__PURE__ */ new Map();
6168
+ for (const [key, node] of animatedNodes(scratch)) {
6169
+ if (node.id === void 0) node.id = SCRATCH_ID_PREFIX + key;
6170
+ keyById.set(String(node.id), key);
6171
+ }
6172
+ const out = /* @__PURE__ */ new Map();
6173
+ for (const binding of normalizeBindings(scratch, engine)) {
6174
+ const key = keyById.get(binding.id);
6175
+ if (key !== void 0) out.set(key, binding.animate);
6176
+ }
6177
+ return out;
6178
+ }
6179
+ function animatedNodes(tree) {
6180
+ const out = /* @__PURE__ */ new Map();
6181
+ let counter = 0;
6182
+ const visit = (node) => {
6183
+ if (node.animate) out.set(String(counter++), node);
6184
+ if (node.children) for (const child of node.children) visit(child);
6185
+ };
6186
+ if (tree.children) for (const child of tree.children) visit(child);
6187
+ return out;
6188
+ }
6189
+
6190
+ // src/materialize/PxAnimatorMaterializeAll.ts
6191
+ function materializeAllInTree(doc, engine, options) {
6192
+ var _a2, _b;
6193
+ let root = materializeNodeEffects(doc).root;
6194
+ root = materializeOffsetPathsInTree(root);
6195
+ const duration = (_b = (_a2 = getAnimatorConfig(root)) == null ? void 0 : _a2.duration) != null ? _b : PX_DEFAULT_DURATION_MS;
6196
+ root = materializeInternalLoopsInTree(root, duration);
6197
+ if (engine === PxTimelineEngine.native) {
6198
+ root = materializeMotionPathsInTree(root, options == null ? void 0 : options.motionPath);
6199
+ root = materializeAnimatedUseInstances(root);
6200
+ root = pruneUnreferencedDefs(root);
6201
+ }
6202
+ root = materializeRestPosesInTree(root, engine);
6203
+ return root;
6204
+ }
6205
+ function prepareDocumentForRender(doc) {
6206
+ var _a2;
6207
+ const engine = resolveTimelineEngine((_a2 = getAnimatorConfig(doc)) == null ? void 0 : _a2.engine);
6208
+ return generateNewIds(materializeAllInTree(doc, engine));
6209
+ }
6210
+ function pruneUnreferencedDefs(root) {
6211
+ const stripHash3 = (h) => h.startsWith("#") ? h.slice(1) : h;
6212
+ const walk = (n, fn) => {
6213
+ var _a2;
6214
+ fn(n);
6215
+ (_a2 = n.children) == null ? void 0 : _a2.forEach((c) => walk(c, fn));
6216
+ };
6217
+ let changed = true;
6218
+ while (changed) {
6219
+ changed = false;
6220
+ const referenced = /* @__PURE__ */ new Set();
6221
+ walk(root, (n) => {
6222
+ if (n.type === "use" && typeof n.href === "string") referenced.add(stripHash3(n.href));
6223
+ });
6224
+ walk(root, (n) => {
6225
+ if (!n.children) return;
6226
+ let kept = n.children;
6227
+ if (n.type === "defs") {
6228
+ kept = kept.filter((c) => !((c.type === "g" || c.type === "symbol") && typeof c.id === "string" && !referenced.has(c.id)));
6229
+ }
6230
+ kept = kept.filter((c) => !(c.type === "defs" && (!c.children || c.children.length === 0)));
6231
+ if (kept.length !== n.children.length) {
6232
+ n.children = kept;
6233
+ changed = true;
6234
+ }
6235
+ });
6236
+ }
6237
+ return root;
3176
6238
  }
3177
6239
 
3178
6240
  // src/playback/PxDiagnostics.ts
6241
+ var PxDiagnosticKind = {
6242
+ /** The document is wrong — regenerate or repair the file. */
6243
+ document: "document",
6244
+ /** The page or app cannot provide what the document asks for — fix the mount. */
6245
+ host: "host",
6246
+ /** The platform cannot do it and the player degraded — usually nothing to fix. */
6247
+ platform: "platform",
6248
+ /** The call is wrong or self-contradictory — fix the options or props you passed. */
6249
+ usage: "usage",
6250
+ /** The player failed where it did not expect to — report it to us. */
6251
+ internal: "internal"
6252
+ };
3179
6253
  var DOCS_URL = "https://github.com/pixodesk/pixodesk-svg-animator/blob/main/docs/diagnostics.md";
3180
6254
  function codeLine(code) {
3181
6255
  return "PX" + code + " " + DOCS_URL + "#px" + code;
@@ -3208,81 +6282,6 @@ function createDiagnostics(config, prefix) {
3208
6282
  };
3209
6283
  }
3210
6284
 
3211
- // src/playback/PxScrollMath.ts
3212
- function isScrollTimeline(config) {
3213
- return (config == null ? void 0 : config.timelineSource) === "scroll";
3214
- }
3215
- function scrollTotalDurationMs(config) {
3216
- const duration = typeof (config == null ? void 0 : config.duration) === "number" && config.duration > 0 ? config.duration : PX_DEFAULT_DURATION_MS;
3217
- const iterations = typeof (config == null ? void 0 : config.iterations) === "number" && config.iterations > 0 ? config.iterations : 1;
3218
- return duration * iterations;
3219
- }
3220
- function scrollPhaseInterval(phase, subjectSize, scrollportSize) {
3221
- const s = subjectSize, vp = scrollportSize;
3222
- switch (phase) {
3223
- case "cover":
3224
- return [0, s + vp];
3225
- case "entry":
3226
- return [0, Math.min(s, vp)];
3227
- case "contain":
3228
- return [Math.min(s, vp), Math.max(s, vp)];
3229
- case "exit":
3230
- return [Math.max(s, vp), s + vp];
3231
- case "entry-crossing":
3232
- return [0, s];
3233
- case "exit-crossing":
3234
- return [vp, s + vp];
3235
- }
3236
- }
3237
- var DEFAULT_PHASE = "cover";
3238
- function resolveRangePointU(point, defaultFraction, subjectSize, scrollportSize) {
3239
- var _a2;
3240
- const [u0, u1] = scrollPhaseInterval((_a2 = point == null ? void 0 : point.phase) != null ? _a2 : DEFAULT_PHASE, subjectSize, scrollportSize);
3241
- const fraction = typeof (point == null ? void 0 : point.fraction) === "number" ? point.fraction : defaultFraction;
3242
- return u0 + fraction * (u1 - u0);
3243
- }
3244
- function scrollViewProgress(subjectStart, subjectSize, scrollportSize, range) {
3245
- const u = scrollportSize - subjectStart;
3246
- const uStart = resolveRangePointU(range == null ? void 0 : range.start, 0, subjectSize, scrollportSize);
3247
- const uEnd = resolveRangePointU(range == null ? void 0 : range.end, 1, subjectSize, scrollportSize);
3248
- if (uEnd <= uStart) return u >= uEnd ? 1 : 0;
3249
- return clamp((u - uStart) / (uEnd - uStart), 0, 1);
3250
- }
3251
- function scrollOffsetProgress(offset, maxOffset, range) {
3252
- var _a2, _b;
3253
- const raw = maxOffset > 0 ? clamp(offset / maxOffset, 0, 1) : 1;
3254
- const start = typeof ((_a2 = range == null ? void 0 : range.start) == null ? void 0 : _a2.fraction) === "number" ? range.start.fraction : 0;
3255
- const end = typeof ((_b = range == null ? void 0 : range.end) == null ? void 0 : _b.fraction) === "number" ? range.end.fraction : 1;
3256
- if (end <= start) return raw >= end ? 1 : 0;
3257
- return clamp((raw - start) / (end - start), 0, 1);
3258
- }
3259
- function scrollResolveAxis(axis, writingMode) {
3260
- const a = axis != null ? axis : "block";
3261
- if (a === "x" || a === "y") return a;
3262
- const vertical = !!writingMode && writingMode.startsWith("vertical");
3263
- if (a === "inline") return vertical ? "y" : "x";
3264
- return vertical ? "x" : "y";
3265
- }
3266
-
3267
- // src/util/PxIdUtil.ts
3268
- var _idCounter = 0;
3269
- function generateUniqueId() {
3270
- const timestamp = Date.now().toString(36);
3271
- const counter = (++_idCounter).toString(36);
3272
- const random = Math.random().toString(36).substring(2, 6);
3273
- return "_px_" + timestamp + counter + random;
3274
- }
3275
- function deepClone(value) {
3276
- if (value === null || typeof value !== "object") return value;
3277
- if (Array.isArray(value)) return value.map((item) => deepClone(item));
3278
- const obj = value;
3279
- const cloned = {};
3280
- for (const key of Object.keys(obj)) {
3281
- cloned[key] = deepClone(obj[key]);
3282
- }
3283
- return cloned;
3284
- }
3285
-
3286
6285
  // src/util/PxNodeProps.ts
3287
6286
  var PX_DISALLOWED_SVG_TAGS_LOWER = /* @__PURE__ */ new Set([
3288
6287
  "script",
@@ -3346,6 +6345,135 @@ function sanitizeAttributeValue(name, value) {
3346
6345
  }
3347
6346
  return value;
3348
6347
  }
6348
+ function toDomProps(props) {
6349
+ const propsCopy = {};
6350
+ for (const rawKey of Object.keys(props)) {
6351
+ const key = kebabToCamelCaseWord(rawKey);
6352
+ if (INTERNAL_ATTRS.has(key)) continue;
6353
+ if (key === "style") continue;
6354
+ let value = props[rawKey];
6355
+ if (PX_COLOR_ATTR_NAMES.has(key) && Array.isArray(value)) {
6356
+ propsCopy[key] = toRGBA(value);
6357
+ } else if (key === "transform" && value !== null && typeof value === "object" && !Array.isArray(value) && !value.keyframes) {
6358
+ const parts = value.value && typeof value.value === "object" ? value.value : value;
6359
+ propsCopy[TRANSFORM_ATTR] = composeTransformParts(parts, { withUnits: false });
6360
+ } else if (PX_TRANSFORM_FN_NAMES.has(key)) {
6361
+ if (Array.isArray(value)) {
6362
+ if (key === "translate") value = value.map((v) => v + "px");
6363
+ value = value.join(",");
6364
+ }
6365
+ if (key === "rotate") value = value + "deg";
6366
+ propsCopy[TRANSFORM_ATTR] = key + "(" + value + ")";
6367
+ } else if (Array.isArray(value)) {
6368
+ propsCopy[key] = value.join(",");
6369
+ } else if (value !== void 0 && value !== null) {
6370
+ propsCopy[key] = String(value);
6371
+ }
6372
+ }
6373
+ return propsCopy;
6374
+ }
6375
+
6376
+ // src/render/PxRenderTree.ts
6377
+ var CLASS_NAME_KEY = "className";
6378
+ var CLASS_ATTR2 = "class";
6379
+ var DOM_TYPE_KEY = "domType";
6380
+ var TYPE_ATTR = "type";
6381
+ var DEFAULT_TAG = "g";
6382
+ function renderPxTree(node, factory, diag) {
6383
+ return node ? renderOne(node, factory, diag, true, 0) : null;
6384
+ }
6385
+ function renderOne(node, factory, diag, isRoot, index) {
6386
+ const _a2 = node, { type, children, style } = _a2, props = __objRest(_a2, ["type", "children", "style"]);
6387
+ const tag = type || DEFAULT_TAG;
6388
+ if (PX_DISALLOWED_SVG_TAGS_LOWER.has(tag.toLowerCase())) {
6389
+ (diag != null ? diag : createDiagnostics(void 0, "[PxAnimator]")).warn(PxDiagnosticKind.document, 1103 /* blockedTag */, tag);
6390
+ return null;
6391
+ }
6392
+ const domType = props[DOM_TYPE_KEY];
6393
+ if (domType !== void 0) delete props[DOM_TYPE_KEY];
6394
+ const attrs = {};
6395
+ let inlineStyle;
6396
+ const domProps = toDomProps(props);
6397
+ for (const propName of Object.keys(domProps)) {
6398
+ const sanitized = sanitizeAttributeValue(propName, domProps[propName]);
6399
+ if (sanitized === void 0) continue;
6400
+ if (PX_CSS_ONLY_STYLE_PROPS.has(propName)) {
6401
+ (inlineStyle != null ? inlineStyle : inlineStyle = {})[propName] = String(sanitized);
6402
+ continue;
6403
+ }
6404
+ attrs[propName === CLASS_NAME_KEY ? CLASS_ATTR2 : camelCaseToKebabWordIfNeeded(propName)] = sanitized;
6405
+ }
6406
+ if (domType !== void 0) attrs[TYPE_ATTR] = String(domType);
6407
+ if (style) {
6408
+ for (const styleProp of Object.keys(style)) (inlineStyle != null ? inlineStyle : inlineStyle = {})[styleProp] = String(style[styleProp]);
6409
+ }
6410
+ const rendered = [];
6411
+ if (children) {
6412
+ children.forEach((child, i) => {
6413
+ const el = renderOne(child, factory, diag, false, i);
6414
+ if (el !== null) rendered.push(el);
6415
+ });
6416
+ }
6417
+ const ownText = props[PX_TEXT_CONTENT_ATTR];
6418
+ const text = !rendered.length && typeof ownText === "string" && ownText ? ownText : void 0;
6419
+ return factory({ tag, attrs, style: inlineStyle, children: rendered, text, node, isRoot, index });
6420
+ }
6421
+
6422
+ // src/playback/PxScrollMath.ts
6423
+ function isScrollTimeline(config) {
6424
+ return (config == null ? void 0 : config.timelineSource) === "scroll";
6425
+ }
6426
+ function scrollTotalDurationMs(config) {
6427
+ const duration = typeof (config == null ? void 0 : config.duration) === "number" && config.duration > 0 ? config.duration : PX_DEFAULT_DURATION_MS;
6428
+ const iterations = typeof (config == null ? void 0 : config.iterations) === "number" && config.iterations > 0 ? config.iterations : 1;
6429
+ return duration * iterations;
6430
+ }
6431
+ function scrollPhaseInterval(phase, subjectSize, scrollportSize) {
6432
+ const s = subjectSize, vp = scrollportSize;
6433
+ switch (phase) {
6434
+ case "cover":
6435
+ return [0, s + vp];
6436
+ case "entry":
6437
+ return [0, Math.min(s, vp)];
6438
+ case "contain":
6439
+ return [Math.min(s, vp), Math.max(s, vp)];
6440
+ case "exit":
6441
+ return [Math.max(s, vp), s + vp];
6442
+ case "entry-crossing":
6443
+ return [0, s];
6444
+ case "exit-crossing":
6445
+ return [vp, s + vp];
6446
+ }
6447
+ }
6448
+ var DEFAULT_PHASE = "cover";
6449
+ function resolveRangePointU(point, defaultFraction, subjectSize, scrollportSize) {
6450
+ var _a2;
6451
+ const [u0, u1] = scrollPhaseInterval((_a2 = point == null ? void 0 : point.phase) != null ? _a2 : DEFAULT_PHASE, subjectSize, scrollportSize);
6452
+ const fraction = typeof (point == null ? void 0 : point.fraction) === "number" ? point.fraction : defaultFraction;
6453
+ return u0 + fraction * (u1 - u0);
6454
+ }
6455
+ function scrollViewProgress(subjectStart, subjectSize, scrollportSize, range) {
6456
+ const u = scrollportSize - subjectStart;
6457
+ const uStart = resolveRangePointU(range == null ? void 0 : range.start, 0, subjectSize, scrollportSize);
6458
+ const uEnd = resolveRangePointU(range == null ? void 0 : range.end, 1, subjectSize, scrollportSize);
6459
+ if (uEnd <= uStart) return u >= uEnd ? 1 : 0;
6460
+ return clamp((u - uStart) / (uEnd - uStart), 0, 1);
6461
+ }
6462
+ function scrollOffsetProgress(offset, maxOffset, range) {
6463
+ var _a2, _b;
6464
+ const raw = maxOffset > 0 ? clamp(offset / maxOffset, 0, 1) : 1;
6465
+ const start = typeof ((_a2 = range == null ? void 0 : range.start) == null ? void 0 : _a2.fraction) === "number" ? range.start.fraction : 0;
6466
+ const end = typeof ((_b = range == null ? void 0 : range.end) == null ? void 0 : _b.fraction) === "number" ? range.end.fraction : 1;
6467
+ if (end <= start) return raw >= end ? 1 : 0;
6468
+ return clamp((raw - start) / (end - start), 0, 1);
6469
+ }
6470
+ function scrollResolveAxis(axis, writingMode) {
6471
+ const a = axis != null ? axis : "block";
6472
+ if (a === "x" || a === "y") return a;
6473
+ const vertical = !!writingMode && writingMode.startsWith("vertical");
6474
+ if (a === "inline") return vertical ? "y" : "x";
6475
+ return vertical ? "x" : "y";
6476
+ }
3349
6477
 
3350
6478
  // src/version/PxSchemaFieldUniverse.ts
3351
6479
  function schemaFieldUniverse(root) {
@@ -3476,6 +6604,7 @@ function releaseLogProblems(releases, steps, declared, baseline) {
3476
6604
  PX_COLOR_ATTR_NAMES,
3477
6605
  PX_CSS_ONLY_STYLE_PROPS,
3478
6606
  PX_DEFAULT_DURATION_MS,
6607
+ PX_DEFAULT_ITERATIONS,
3479
6608
  PX_DISALLOWED_SVG_TAGS_LOWER,
3480
6609
  PX_LOOP_JUMP_SHIFT_MS,
3481
6610
  PX_PCT_BASED_ATTR_NAMES,
@@ -3506,7 +6635,9 @@ function releaseLogProblems(releases, steps, declared, baseline) {
3506
6635
  materializeMotionPathInPropAnim,
3507
6636
  mergeStaticTransformIntoAnimDef,
3508
6637
  planSchemaRelease,
6638
+ prepareDocumentForRender,
3509
6639
  releaseLogProblems,
6640
+ renderPxTree,
3510
6641
  reportDocumentDiagnostics,
3511
6642
  reverseEasing,
3512
6643
  sanitizeAttributeValue,