@pixodesk/svg-animator-web 1.0.43 → 1.0.45

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -20,18 +20,6 @@ var __spreadValues = (a, b) => {
20
20
  return a;
21
21
  };
22
22
  var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
23
- var __objRest = (source, exclude) => {
24
- var target = {};
25
- for (var prop in source)
26
- if (__hasOwnProp.call(source, prop) && exclude.indexOf(prop) < 0)
27
- target[prop] = source[prop];
28
- if (source != null && __getOwnPropSymbols)
29
- for (var prop of __getOwnPropSymbols(source)) {
30
- if (exclude.indexOf(prop) < 0 && __propIsEnum.call(source, prop))
31
- target[prop] = source[prop];
32
- }
33
- return target;
34
- };
35
23
  var __export = (target, all) => {
36
24
  for (var name in all)
37
25
  __defProp(target, name, { get: all[name], enumerable: true });
@@ -60,7 +48,9 @@ __export(index_exports, {
60
48
  createAdapterAnimator: () => createAdapterAnimator,
61
49
  createAnimator: () => createAnimator,
62
50
  generateNewIds: () => generateNewIds,
51
+ getAllAnimators: () => getAllAnimators,
63
52
  loadTagAnimators: () => loadTagAnimators,
53
+ onAnimatorsChange: () => onAnimatorsChange,
64
54
  px: () => px,
65
55
  renderNode: () => renderNode,
66
56
  setupAnimationTriggers: () => setupAnimationTriggers,
@@ -69,7 +59,7 @@ __export(index_exports, {
69
59
  });
70
60
  module.exports = __toCommonJS(index_exports);
71
61
 
72
- // ../svg-animator-core/dist/chunk-BOTZI36B.js
62
+ // ../svg-animator-core/dist/chunk-YVKMPBHE.js
73
63
  var __defProp2 = Object.defineProperty;
74
64
  var __defProps2 = Object.defineProperties;
75
65
  var __getOwnPropDescs2 = Object.getOwnPropertyDescriptors;
@@ -90,7 +80,7 @@ var __spreadValues2 = (a, b) => {
90
80
  return a;
91
81
  };
92
82
  var __spreadProps2 = (a, b) => __defProps2(a, __getOwnPropDescs2(b));
93
- var __objRest2 = (source, exclude) => {
83
+ var __objRest = (source, exclude) => {
94
84
  var target = {};
95
85
  for (var prop in source)
96
86
  if (__hasOwnProp2.call(source, prop) && exclude.indexOf(prop) < 0)
@@ -603,7 +593,7 @@ var px = {
603
593
  /** Defers schema creation — required for recursive types. Must supply a default value. */
604
594
  lazy: (fn, defaultVal) => new Lazy(fn, defaultVal)
605
595
  };
606
- var PX_WIRE_SCHEMA_VERSION = "1.1";
596
+ var PX_WIRE_SCHEMA_VERSION = "1.2";
607
597
  var VERSION_RE = /^(\d+)\.(\d+)(?:\.(\d+))?$/;
608
598
  function parseWireVersion(raw) {
609
599
  if (typeof raw !== "string") return void 0;
@@ -627,14 +617,18 @@ var PxPlaybackDirection = {
627
617
  };
628
618
  var PX_ANIM_SRC_ATTR_NAME = "data-px-animation-src";
629
619
  var PX_ANIM_ATTR_NAME = "_px_animator";
630
- var PxStartOn = {
620
+ var PxTriggerStart = {
631
621
  load: "load",
632
622
  mouseOver: "mouseOver",
633
623
  click: "click",
634
- scrollIntoView: "scrollIntoView",
635
- programmatic: "programmatic"
624
+ none: "none"
625
+ };
626
+ var PxOffScreenAction = {
627
+ pause: "pause",
628
+ continue: "continue",
629
+ reset: "reset"
636
630
  };
637
- var PxOutAction = {
631
+ var PxMouseOutAction = {
638
632
  continue: "continue",
639
633
  pause: "pause",
640
634
  reset: "reset",
@@ -702,16 +696,20 @@ function mayUseNativeScrollTimeline(engine) {
702
696
  return engine !== PxTimelineEngineSetting.js;
703
697
  }
704
698
  var PX_TRIGGER_DEFAULTS = {
705
- startOn: "load",
706
- outAction: "continue",
707
- scrollIntoViewThreshold: 0
699
+ start: "load",
700
+ offScreen: "pause",
701
+ mouseOut: "continue",
702
+ visibilityThreshold: 0.5,
703
+ visibilityDebounce: 150
708
704
  };
709
705
  function resolveTrigger(trigger) {
710
- var _a2, _b, _c;
706
+ var _a2, _b, _c, _d, _e;
711
707
  return {
712
- startOn: (_a2 = trigger == null ? void 0 : trigger.startOn) != null ? _a2 : PX_TRIGGER_DEFAULTS.startOn,
713
- outAction: (_b = trigger == null ? void 0 : trigger.outAction) != null ? _b : PX_TRIGGER_DEFAULTS.outAction,
714
- scrollIntoViewThreshold: (_c = trigger == null ? void 0 : trigger.scrollIntoViewThreshold) != null ? _c : PX_TRIGGER_DEFAULTS.scrollIntoViewThreshold
708
+ start: (_a2 = trigger == null ? void 0 : trigger.start) != null ? _a2 : PX_TRIGGER_DEFAULTS.start,
709
+ offScreen: (_b = trigger == null ? void 0 : trigger.offScreen) != null ? _b : PX_TRIGGER_DEFAULTS.offScreen,
710
+ mouseOut: (_c = trigger == null ? void 0 : trigger.mouseOut) != null ? _c : PX_TRIGGER_DEFAULTS.mouseOut,
711
+ visibilityThreshold: (_d = trigger == null ? void 0 : trigger.visibilityThreshold) != null ? _d : PX_TRIGGER_DEFAULTS.visibilityThreshold,
712
+ visibilityDebounce: (_e = trigger == null ? void 0 : trigger.visibilityDebounce) != null ? _e : PX_TRIGGER_DEFAULTS.visibilityDebounce
715
713
  };
716
714
  }
717
715
  var PxLoopRepeatAt = {
@@ -821,7 +819,7 @@ function flattenAnimatorTimeline(cfg) {
821
819
  if (timeline === void 0 || timeline === null || typeof timeline !== "object") return cfg;
822
820
  const memoised = flattenMemo.get(cfg);
823
821
  if (memoised) return memoised;
824
- const _a2 = cfg, { timeline: _dropped } = _a2, flat = __objRest2(_a2, ["timeline"]);
822
+ const _a2 = cfg, { timeline: _dropped } = _a2, flat = __objRest(_a2, ["timeline"]);
825
823
  if (timeline.engine !== void 0) flat.engine = timeline.engine;
826
824
  if (timeline.frameRate !== void 0) flat.frameRate = timeline.frameRate;
827
825
  if (timeline.type === "scroll" || timeline.type === "view") {
@@ -847,9 +845,9 @@ function flattenAnimatorTimeline(cfg) {
847
845
  } else {
848
846
  if (timeline.duration !== void 0) flat.duration = timeline.duration;
849
847
  if (timeline.trigger !== void 0) {
850
- const _b = timeline.trigger, { finishAction } = _b, restTrigger = __objRest2(_b, ["finishAction"]);
848
+ const _b = timeline.trigger, { finish } = _b, restTrigger = __objRest(_b, ["finish"]);
851
849
  if (Object.keys(restTrigger).length) flat.trigger = restTrigger;
852
- if (finishAction !== void 0) flat.resetOnFinish = finishAction === "reset";
850
+ if (finish !== void 0) flat.resetOnFinish = finish === "reset";
853
851
  }
854
852
  if (timeline.delay !== void 0) flat.delay = timeline.delay;
855
853
  if (timeline.iterations !== void 0) flat.iterations = timeline.iterations;
@@ -948,13 +946,15 @@ var PxElementAnimationSchema = implementsInterface()(px.union([
948
946
  PxAnimationDefinitionSchema
949
947
  ]));
950
948
  var PxTriggerSchema = implementsInterface()(px.object({
951
- startOn: px.enum([PxStartOn.load, PxStartOn.mouseOver, PxStartOn.click, PxStartOn.scrollIntoView, PxStartOn.programmatic], PX_TRIGGER_DEFAULTS.startOn).optional(),
952
- outAction: px.enum([PxOutAction.continue, PxOutAction.pause, PxOutAction.reset, PxOutAction.reverse], PX_TRIGGER_DEFAULTS.outAction).optional(),
949
+ start: px.enum([PxTriggerStart.load, PxTriggerStart.mouseOver, PxTriggerStart.click, PxTriggerStart.none], PX_TRIGGER_DEFAULTS.start).optional(),
950
+ offScreen: px.enum([PxOffScreenAction.pause, PxOffScreenAction.continue, PxOffScreenAction.reset], PX_TRIGGER_DEFAULTS.offScreen).optional(),
951
+ mouseOut: px.enum([PxMouseOutAction.continue, PxMouseOutAction.pause, PxMouseOutAction.reset, PxMouseOutAction.reverse], PX_TRIGGER_DEFAULTS.mouseOut).optional(),
953
952
  // What happens after a NATURAL finish — `'hold'` (default: keep the end state per
954
- // `fill`) or `'reset'` (snap back to the start state). Pairs with `outAction` ("what
955
- // happens when the trigger condition ends"); both end-of-life knobs now read alike.
956
- finishAction: px.enum([PxFinishAction.hold, PxFinishAction.reset]).optional(),
957
- scrollIntoViewThreshold: px.number().optional()
953
+ // `fill`) or `'reset'` (snap back to the start state). One of four occasion keys
954
+ // (`start`, `offScreen`, `mouseOut`, `finish`), all named the same way.
955
+ finish: px.enum([PxFinishAction.hold, PxFinishAction.reset]).optional(),
956
+ visibilityThreshold: px.number().optional(),
957
+ visibilityDebounce: px.number().optional()
958
958
  }));
959
959
  var PxGlyphSchema = implementsInterface()(px.object({
960
960
  width: px.number(),
@@ -1976,6 +1976,36 @@ function toDomProps(props) {
1976
1976
  }
1977
1977
  return propsCopy;
1978
1978
  }
1979
+ function mergeStaticTransformIntoAnimDef(animDef, staticTransform) {
1980
+ if (!animDef) return animDef;
1981
+ const staticParts = staticTransform && typeof staticTransform === "object" && !Array.isArray(staticTransform) ? staticTransform : parseTransformParts(staticTransform);
1982
+ if (!staticParts || !Object.keys(staticParts).length) return animDef;
1983
+ const mergeKfValue = (v) => v && typeof v === "object" && !Array.isArray(v) ? __spreadValues2(__spreadValues2({}, staticParts), v) : v;
1984
+ const transformAnim = animDef[TRANSFORM_ATTR];
1985
+ if (transformAnim && typeof transformAnim === "object") {
1986
+ const anim = transformAnim;
1987
+ if (Array.isArray(anim.keyframes)) {
1988
+ const out = __spreadProps2(__spreadValues2({}, anim), {
1989
+ keyframes: anim.keyframes.map((kf) => __spreadProps2(__spreadValues2({}, kf), { value: mergeKfValue(kf.value) }))
1990
+ });
1991
+ if (out.value !== void 0) out.value = mergeKfValue(out.value);
1992
+ return __spreadProps2(__spreadValues2({}, animDef), { transform: out });
1993
+ }
1994
+ return animDef;
1995
+ }
1996
+ const channels = Object.keys(animDef).filter((k) => PX_TRANSFORM_FN_NAMES.has(k));
1997
+ if (channels.length !== 1) return animDef;
1998
+ const ch = channels[0];
1999
+ const chAnim = animDef[ch];
2000
+ if (!chAnim || typeof chAnim !== "object" || !Array.isArray(chAnim.keyframes)) return animDef;
2001
+ const lifted = __spreadProps2(__spreadValues2({}, chAnim), {
2002
+ keyframes: chAnim.keyframes.map((kf) => __spreadProps2(__spreadValues2({}, kf), { value: __spreadProps2(__spreadValues2({}, staticParts), { [ch]: kf.value }) }))
2003
+ });
2004
+ if (lifted.value !== void 0) lifted.value = __spreadProps2(__spreadValues2({}, staticParts), { [ch]: lifted.value });
2005
+ const rest = __spreadValues2({}, animDef);
2006
+ delete rest[ch];
2007
+ return __spreadProps2(__spreadValues2({}, rest), { transform: lifted });
2008
+ }
1979
2009
  function getKfTranslate(kf) {
1980
2010
  const v = keyframeValue(kf);
1981
2011
  if (!v) return void 0;
@@ -2344,7 +2374,7 @@ function walkAndMaterialize(node, opts) {
2344
2374
  let newAnimate;
2345
2375
  const animBucket = node.animate;
2346
2376
  if (animBucket && typeof animBucket === "object" && !Array.isArray(animBucket)) {
2347
- const animDef = animBucket;
2377
+ const animDef = mergeStaticTransformIntoAnimDef(animBucket, node.transform);
2348
2378
  const transformAnim = animDef.transform;
2349
2379
  if (transformAnim && typeof transformAnim === "object" && propAnimIsMotionPath(transformAnim)) {
2350
2380
  const materialized = materializeMotionPathInPropAnim(transformAnim, opts);
@@ -2856,36 +2886,6 @@ var _elementIdCounter = 0;
2856
2886
  function generateElementId() {
2857
2887
  return "_px_el_" + ++_elementIdCounter;
2858
2888
  }
2859
- function mergeStaticTransformIntoAnimDef(animDef, staticTransform) {
2860
- if (!animDef) return animDef;
2861
- const staticParts = staticTransform && typeof staticTransform === "object" && !Array.isArray(staticTransform) ? staticTransform : parseTransformParts(staticTransform);
2862
- if (!staticParts || !Object.keys(staticParts).length) return animDef;
2863
- const mergeKfValue = (v) => v && typeof v === "object" && !Array.isArray(v) ? __spreadValues2(__spreadValues2({}, staticParts), v) : v;
2864
- const transformAnim = animDef[TRANSFORM_ATTR];
2865
- if (transformAnim && typeof transformAnim === "object") {
2866
- const anim = transformAnim;
2867
- if (Array.isArray(anim.keyframes)) {
2868
- const out = __spreadProps2(__spreadValues2({}, anim), {
2869
- keyframes: anim.keyframes.map((kf) => __spreadProps2(__spreadValues2({}, kf), { value: mergeKfValue(kf.value) }))
2870
- });
2871
- if (out.value !== void 0) out.value = mergeKfValue(out.value);
2872
- return __spreadProps2(__spreadValues2({}, animDef), { transform: out });
2873
- }
2874
- return animDef;
2875
- }
2876
- const channels = Object.keys(animDef).filter((k) => PX_TRANSFORM_FN_NAMES.has(k));
2877
- if (channels.length !== 1) return animDef;
2878
- const ch = channels[0];
2879
- const chAnim = animDef[ch];
2880
- if (!chAnim || typeof chAnim !== "object" || !Array.isArray(chAnim.keyframes)) return animDef;
2881
- const lifted = __spreadProps2(__spreadValues2({}, chAnim), {
2882
- keyframes: chAnim.keyframes.map((kf) => __spreadProps2(__spreadValues2({}, kf), { value: __spreadProps2(__spreadValues2({}, staticParts), { [ch]: kf.value }) }))
2883
- });
2884
- if (lifted.value !== void 0) lifted.value = __spreadProps2(__spreadValues2({}, staticParts), { [ch]: lifted.value });
2885
- const rest = __spreadValues2({}, animDef);
2886
- delete rest[ch];
2887
- return __spreadProps2(__spreadValues2({}, rest), { transform: lifted });
2888
- }
2889
2889
  function normalizeAnimationDefinition(animDef, duration, defs, engine = PxTimelineEngine.native) {
2890
2890
  const normalized = {};
2891
2891
  for (const [propName, propAnim] of Object.entries(animDef)) {
@@ -2968,7 +2968,7 @@ function getKeyframesPair(keyframes, progress) {
2968
2968
  return { prevKf, nextKf };
2969
2969
  }
2970
2970
  function calcPropertyValue(propName, propAnim, progress) {
2971
- var _a2, _b, _c, _d;
2971
+ var _a2, _b, _c, _d, _e;
2972
2972
  const keyframes = propAnim.keyframes || [];
2973
2973
  if (keyframes.length === 0) return null;
2974
2974
  const { prevKf, nextKf } = getKeyframesPair(keyframes, progress);
@@ -3037,7 +3037,7 @@ function calcPropertyValue(propName, propAnim, progress) {
3037
3037
  !!propAnim.autoOrient
3038
3038
  );
3039
3039
  partsResult.translate = [sample.translate[0], sample.translate[1]];
3040
- if (sample.rotateDeg !== void 0) partsResult.rotate = sample.rotateDeg;
3040
+ if (sample.rotateDeg !== void 0) partsResult.rotate = sample.rotateDeg + ((_e = partsResult.rotate) != null ? _e : 0);
3041
3041
  }
3042
3042
  }
3043
3043
  cssValue = composeTransformParts(partsResult, { withUnits: false });
@@ -3320,12 +3320,12 @@ function deepClonePxNode(value) {
3320
3320
  for (const k of Object.keys(value)) out[k] = deepClonePxNode(value[k]);
3321
3321
  return out;
3322
3322
  }
3323
- function regenerateIdsAndRewriteRefs(root, genId2) {
3323
+ function regenerateIdsAndRewriteRefs(root, genId3) {
3324
3324
  const oldToNew = /* @__PURE__ */ new Map();
3325
3325
  const walkAssign = (n) => {
3326
3326
  var _a2;
3327
3327
  if (typeof n.id === "string") {
3328
- const newId = genId2();
3328
+ const newId = genId3();
3329
3329
  oldToNew.set(n.id, newId);
3330
3330
  n.id = newId;
3331
3331
  }
@@ -3974,68 +3974,6 @@ function applyTextGlyphsAlongPath(node, ctx, pathD, startOffset, textLength, pat
3974
3974
  }
3975
3975
  return materializeGlyphTextAlongPath(node, pathD, startOffset, { glyphs: ctx.glyphs, warnings: ctx.warnings }, textLength, pathOverflow);
3976
3976
  }
3977
- var PxDiagnosticKind = {
3978
- /** The document is wrong — regenerate or repair the file. */
3979
- document: "document",
3980
- /** The page or app cannot provide what the document asks for — fix the mount. */
3981
- host: "host",
3982
- /** The platform cannot do it and the player degraded — usually nothing to fix. */
3983
- platform: "platform",
3984
- /** The call is wrong or self-contradictory — fix the options or props you passed. */
3985
- usage: "usage",
3986
- /** The player failed where it did not expect to — report it to us. */
3987
- internal: "internal"
3988
- };
3989
- var DOCS_URL = "https://github.com/pixodesk/pixodesk-svg-animator/blob/main/docs/diagnostics.md";
3990
- function codeLine(code) {
3991
- return "PX" + code + " " + DOCS_URL + "#px" + code;
3992
- }
3993
- function errorIn(data) {
3994
- return data.find((d) => d instanceof Error);
3995
- }
3996
- function createDiagnostics(config, prefix) {
3997
- const tag = prefix ? prefix + " " : "";
3998
- return {
3999
- warn: (kind, code, ...data) => {
4000
- const message = codeLine(code);
4001
- if (config == null ? void 0 : config.onWarn) {
4002
- config.onWarn({ code, kind, data, message });
4003
- return;
4004
- }
4005
- if (config == null ? void 0 : config.muteWarn) return;
4006
- console.warn(tag + kind + " " + message, ...data);
4007
- },
4008
- error: (kind, code, ...data) => {
4009
- const message = codeLine(code);
4010
- const error = errorIn(data);
4011
- if (config == null ? void 0 : config.onError) {
4012
- config.onError({ code, kind, data, message, error });
4013
- return;
4014
- }
4015
- if (config == null ? void 0 : config.muteError) return;
4016
- console.error(tag + kind + " " + message, ...data);
4017
- }
4018
- };
4019
- }
4020
- var MAX_REPORTED = 6;
4021
- function diagnoseDocument(doc) {
4022
- try {
4023
- return { problems: validateDocument(doc) };
4024
- } catch (e) {
4025
- return { problems: [] };
4026
- }
4027
- }
4028
- function reportDocumentDiagnostics(doc, where) {
4029
- const { problems } = diagnoseDocument(doc);
4030
- if (!problems.length) return;
4031
- const shown = problems.slice(0, MAX_REPORTED);
4032
- const more = problems.length - shown.length;
4033
- console.warn(
4034
- 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."
4035
- );
4036
- }
4037
-
4038
- // ../svg-animator-core/dist/index.js
4039
3977
  function applyTransformByEffect(node, fx, ctx) {
4040
3978
  if (!fx) return node;
4041
3979
  delete node.transform;
@@ -5553,12 +5491,17 @@ function buildOffsetPath(propAnim) {
5553
5491
  const first = keyframeValue(kfs[0]);
5554
5492
  const anchor = (first == null ? void 0 : first.origin) && first.origin.length >= 2 ? [first.origin[0], first.origin[1]] : [0, 0];
5555
5493
  const points = [];
5494
+ let keyframesCarryRotate = false;
5556
5495
  for (const kf of kfs) {
5557
5496
  const v = keyframeValue(kf);
5558
5497
  const tr = v == null ? void 0 : v.translate;
5559
5498
  if (!tr || tr.length < 2) return void 0;
5560
5499
  const parts = Object.keys(v);
5561
- if (parts.some((p) => p !== "translate" && p !== "origin")) return void 0;
5500
+ if (parts.some((p) => p !== TRANSFORM_PART.translate && p !== TRANSFORM_PART.origin && p !== TRANSFORM_PART.rotate)) return void 0;
5501
+ if ((v == null ? void 0 : v.rotate) !== void 0) {
5502
+ if (v.rotate !== 0) return void 0;
5503
+ keyframesCarryRotate = true;
5504
+ }
5562
5505
  const o = (_a2 = v == null ? void 0 : v.origin) != null ? _a2 : [0, 0];
5563
5506
  if (o[0] !== anchor[0] || o[1] !== anchor[1]) return void 0;
5564
5507
  points.push([tr[0] + anchor[0], tr[1] + anchor[1]]);
@@ -5586,7 +5529,7 @@ function buildOffsetPath(propAnim) {
5586
5529
  if (e !== void 0) out.e = e;
5587
5530
  distanceKfs.push(out);
5588
5531
  }
5589
- return { pathStr: d, distanceKfs, autoOrient: !!propAnim.autoOrient, anchor };
5532
+ return { pathStr: d, distanceKfs, autoOrient: !!propAnim.autoOrient, anchor, keyframesCarryRotate };
5590
5533
  }
5591
5534
  function materializeOffsetPathsInTree(root) {
5592
5535
  const walk = (node) => {
@@ -5608,6 +5551,7 @@ function materializeOffsetPathsInTree(root) {
5608
5551
  const t = __spreadValues2({}, staticTr);
5609
5552
  delete t[TRANSFORM_PART.translate];
5610
5553
  delete t[TRANSFORM_PART.origin];
5554
+ if (built.keyframesCarryRotate) delete t[TRANSFORM_PART.rotate];
5611
5555
  newTransform = Object.keys(t).length ? t : void 0;
5612
5556
  }
5613
5557
  out = __spreadProps2(__spreadValues2({}, node), {
@@ -5777,6 +5721,76 @@ function walkAndMaterialize2(node, idMap, animatedIds, genId3, defsCollector, ro
5777
5721
  });
5778
5722
  return changed ? __spreadProps2(__spreadValues2({}, node), { children: newChildren }) : node;
5779
5723
  }
5724
+ var TRANSFORM_CHANNEL = "transform";
5725
+ var REVERSED_DIRECTIONS = /* @__PURE__ */ new Set(["reverse", "alternate-reverse"]);
5726
+ var SCRATCH_ID_PREFIX = "__px_rest_";
5727
+ function materializeRestPosesInTree(root, engine) {
5728
+ var _a2;
5729
+ const out = deepClone(root);
5730
+ const config = getAnimatorConfig(out) || {};
5731
+ const duration = +(config.duration || PX_DEFAULT_DURATION_MS);
5732
+ const firstFrameTime = REVERSED_DIRECTIONS.has(String(config.direction)) ? duration : 0;
5733
+ const nodes = animatedNodes(out);
5734
+ if (!nodes.size) return out;
5735
+ const before = bindingsOf(out, engine);
5736
+ const added = /* @__PURE__ */ new Map();
5737
+ for (const [key, animate] of before) {
5738
+ const node = nodes.get(key);
5739
+ if (!node) continue;
5740
+ for (const channel of Object.keys(animate)) {
5741
+ if (!mayCarryRestPose(node, channel)) continue;
5742
+ const value = firstFrameValue(animate, channel, firstFrameTime);
5743
+ if (value === void 0) continue;
5744
+ node[channel] = value;
5745
+ added.set(key, [...(_a2 = added.get(key)) != null ? _a2 : [], channel]);
5746
+ }
5747
+ }
5748
+ if (added.size) {
5749
+ const after = bindingsOf(out, engine);
5750
+ for (const [key, channels] of added) {
5751
+ if (JSON.stringify(after.get(key)) === JSON.stringify(before.get(key))) continue;
5752
+ const node = nodes.get(key);
5753
+ if (node) for (const channel of channels) delete node[channel];
5754
+ }
5755
+ }
5756
+ return out;
5757
+ }
5758
+ function mayCarryRestPose(node, channel) {
5759
+ if (node[channel] !== void 0) return false;
5760
+ if (PX_TRANSFORM_FN_NAMES.has(channel)) return false;
5761
+ if (channel === TRANSFORM_CHANNEL) {
5762
+ for (const part of PX_TRANSFORM_FN_NAMES) if (node[part] !== void 0) return false;
5763
+ }
5764
+ return true;
5765
+ }
5766
+ function firstFrameValue(animate, channel, timeMs) {
5767
+ const values = Object.values(calcAnimationValues({ [channel]: animate[channel] }, timeMs));
5768
+ return values.length === 1 && values[0] !== "" ? values[0] : void 0;
5769
+ }
5770
+ function bindingsOf(tree, engine) {
5771
+ const scratch = deepClone(tree);
5772
+ const keyById = /* @__PURE__ */ new Map();
5773
+ for (const [key, node] of animatedNodes(scratch)) {
5774
+ if (node.id === void 0) node.id = SCRATCH_ID_PREFIX + key;
5775
+ keyById.set(String(node.id), key);
5776
+ }
5777
+ const out = /* @__PURE__ */ new Map();
5778
+ for (const binding of normalizeBindings(scratch, engine)) {
5779
+ const key = keyById.get(binding.id);
5780
+ if (key !== void 0) out.set(key, binding.animate);
5781
+ }
5782
+ return out;
5783
+ }
5784
+ function animatedNodes(tree) {
5785
+ const out = /* @__PURE__ */ new Map();
5786
+ let counter = 0;
5787
+ const visit = (node) => {
5788
+ if (node.animate) out.set(String(counter++), node);
5789
+ if (node.children) for (const child of node.children) visit(child);
5790
+ };
5791
+ if (tree.children) for (const child of tree.children) visit(child);
5792
+ return out;
5793
+ }
5780
5794
  function materializeAllInTree(doc, engine, options) {
5781
5795
  var _a2, _b;
5782
5796
  let root = materializeNodeEffects(doc).root;
@@ -5788,6 +5802,7 @@ function materializeAllInTree(doc, engine, options) {
5788
5802
  root = materializeAnimatedUseInstances(root);
5789
5803
  root = pruneUnreferencedDefs(root);
5790
5804
  }
5805
+ root = materializeRestPosesInTree(root, engine);
5791
5806
  return root;
5792
5807
  }
5793
5808
  function pruneUnreferencedDefs(root) {
@@ -5819,6 +5834,98 @@ function pruneUnreferencedDefs(root) {
5819
5834
  }
5820
5835
  return root;
5821
5836
  }
5837
+ var PxDiagnosticCode = /* @__PURE__ */ ((PxDiagnosticCode2) => {
5838
+ PxDiagnosticCode2[PxDiagnosticCode2["buildFailed"] = 1001] = "buildFailed";
5839
+ PxDiagnosticCode2[PxDiagnosticCode2["invalidDocumentAtSrc"] = 1002] = "invalidDocumentAtSrc";
5840
+ PxDiagnosticCode2[PxDiagnosticCode2["loadFailed"] = 1003] = "loadFailed";
5841
+ PxDiagnosticCode2[PxDiagnosticCode2["animationBuildFailed"] = 1004] = "animationBuildFailed";
5842
+ PxDiagnosticCode2[PxDiagnosticCode2["effectsShape"] = 1101] = "effectsShape";
5843
+ PxDiagnosticCode2[PxDiagnosticCode2["timelineOverrideIgnored"] = 1102] = "timelineOverrideIgnored";
5844
+ PxDiagnosticCode2[PxDiagnosticCode2["blockedTag"] = 1103] = "blockedTag";
5845
+ PxDiagnosticCode2[PxDiagnosticCode2["unsupportedAnimatedAttrs"] = 1104] = "unsupportedAnimatedAttrs";
5846
+ PxDiagnosticCode2[PxDiagnosticCode2["noBindings"] = 1105] = "noBindings";
5847
+ PxDiagnosticCode2[PxDiagnosticCode2["unresolvedBinding"] = 1106] = "unresolvedBinding";
5848
+ PxDiagnosticCode2[PxDiagnosticCode2["triggersNoRoot"] = 1201] = "triggersNoRoot";
5849
+ PxDiagnosticCode2[PxDiagnosticCode2["noRootForSelector"] = 1202] = "noRootForSelector";
5850
+ PxDiagnosticCode2[PxDiagnosticCode2["noRootElement"] = 1203] = "noRootElement";
5851
+ PxDiagnosticCode2[PxDiagnosticCode2["noElementsForSelector"] = 1206] = "noElementsForSelector";
5852
+ PxDiagnosticCode2[PxDiagnosticCode2["setAttributeNoElement"] = 1207] = "setAttributeNoElement";
5853
+ PxDiagnosticCode2[PxDiagnosticCode2["scrollSmoothingNeedsOwnDriver"] = 1301] = "scrollSmoothingNeedsOwnDriver";
5854
+ PxDiagnosticCode2[PxDiagnosticCode2["scrollNativeUnavailable"] = 1302] = "scrollNativeUnavailable";
5855
+ PxDiagnosticCode2[PxDiagnosticCode2["scrollSubjectInvalid"] = 1303] = "scrollSubjectInvalid";
5856
+ PxDiagnosticCode2[PxDiagnosticCode2["scrollSubjectNoMatch"] = 1304] = "scrollSubjectNoMatch";
5857
+ PxDiagnosticCode2[PxDiagnosticCode2["scrollNoRootToObserve"] = 1305] = "scrollNoRootToObserve";
5858
+ PxDiagnosticCode2[PxDiagnosticCode2["scrollTriggerIgnored"] = 1306] = "scrollTriggerIgnored";
5859
+ PxDiagnosticCode2[PxDiagnosticCode2["rateRejected"] = 1401] = "rateRejected";
5860
+ PxDiagnosticCode2[PxDiagnosticCode2["controlPropsConflict"] = 1501] = "controlPropsConflict";
5861
+ PxDiagnosticCode2[PxDiagnosticCode2["rnCompileFailed"] = 1601] = "rnCompileFailed";
5862
+ PxDiagnosticCode2[PxDiagnosticCode2["rnRenderFailed"] = 1602] = "rnRenderFailed";
5863
+ PxDiagnosticCode2[PxDiagnosticCode2["rnBoundaryCaught"] = 1603] = "rnBoundaryCaught";
5864
+ PxDiagnosticCode2[PxDiagnosticCode2["rnUnsupported"] = 1604] = "rnUnsupported";
5865
+ return PxDiagnosticCode2;
5866
+ })(PxDiagnosticCode || {});
5867
+ var PxDiagnosticKind = {
5868
+ /** The document is wrong — regenerate or repair the file. */
5869
+ document: "document",
5870
+ /** The page or app cannot provide what the document asks for — fix the mount. */
5871
+ host: "host",
5872
+ /** The platform cannot do it and the player degraded — usually nothing to fix. */
5873
+ platform: "platform",
5874
+ /** The call is wrong or self-contradictory — fix the options or props you passed. */
5875
+ usage: "usage",
5876
+ /** The player failed where it did not expect to — report it to us. */
5877
+ internal: "internal"
5878
+ };
5879
+ var DOCS_URL = "https://github.com/pixodesk/pixodesk-svg-animator/blob/main/docs/diagnostics.md";
5880
+ function codeLine(code) {
5881
+ return "PX" + code + " " + DOCS_URL + "#px" + code;
5882
+ }
5883
+ function errorIn(data) {
5884
+ return data.find((d) => d instanceof Error);
5885
+ }
5886
+ function createDiagnostics(config, prefix) {
5887
+ const tag = prefix ? prefix + " " : "";
5888
+ return {
5889
+ warn: (kind, code, ...data) => {
5890
+ const message = codeLine(code);
5891
+ if (config == null ? void 0 : config.onWarn) {
5892
+ config.onWarn({ code, kind, data, message });
5893
+ return;
5894
+ }
5895
+ if (config == null ? void 0 : config.muteWarn) return;
5896
+ console.warn(tag + kind + " " + message, ...data);
5897
+ },
5898
+ error: (kind, code, ...data) => {
5899
+ const message = codeLine(code);
5900
+ const error = errorIn(data);
5901
+ if (config == null ? void 0 : config.onError) {
5902
+ config.onError({ code, kind, data, message, error });
5903
+ return;
5904
+ }
5905
+ if (config == null ? void 0 : config.muteError) return;
5906
+ console.error(tag + kind + " " + message, ...data);
5907
+ }
5908
+ };
5909
+ }
5910
+ var MAX_REPORTED = 6;
5911
+ function diagnoseDocument(doc) {
5912
+ try {
5913
+ return { problems: validateDocument(doc) };
5914
+ } catch (e) {
5915
+ return { problems: [] };
5916
+ }
5917
+ }
5918
+ function reportDocumentDiagnostics(doc, where) {
5919
+ const { problems } = diagnoseDocument(doc);
5920
+ if (!problems.length) return;
5921
+ const shown = problems.slice(0, MAX_REPORTED);
5922
+ const more = problems.length - shown.length;
5923
+ console.warn(
5924
+ 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."
5925
+ );
5926
+ }
5927
+
5928
+ // ../svg-animator-core/dist/index.js
5822
5929
  var PX_RATE_REJECTED = "setPlaybackRate: rate must be finite and non-zero";
5823
5930
  function isValidPlaybackRate(rate) {
5824
5931
  return Number.isFinite(rate) && rate !== 0;
@@ -6130,36 +6237,6 @@ function createAdapterAnimator(doc, adapter, callbacks) {
6130
6237
  };
6131
6238
  return api;
6132
6239
  }
6133
- var PxDiagnosticCode = /* @__PURE__ */ ((PxDiagnosticCode2) => {
6134
- PxDiagnosticCode2[PxDiagnosticCode2["buildFailed"] = 1001] = "buildFailed";
6135
- PxDiagnosticCode2[PxDiagnosticCode2["invalidDocumentAtSrc"] = 1002] = "invalidDocumentAtSrc";
6136
- PxDiagnosticCode2[PxDiagnosticCode2["loadFailed"] = 1003] = "loadFailed";
6137
- PxDiagnosticCode2[PxDiagnosticCode2["animationBuildFailed"] = 1004] = "animationBuildFailed";
6138
- PxDiagnosticCode2[PxDiagnosticCode2["effectsShape"] = 1101] = "effectsShape";
6139
- PxDiagnosticCode2[PxDiagnosticCode2["timelineOverrideIgnored"] = 1102] = "timelineOverrideIgnored";
6140
- PxDiagnosticCode2[PxDiagnosticCode2["blockedTag"] = 1103] = "blockedTag";
6141
- PxDiagnosticCode2[PxDiagnosticCode2["unsupportedAnimatedAttrs"] = 1104] = "unsupportedAnimatedAttrs";
6142
- PxDiagnosticCode2[PxDiagnosticCode2["noBindings"] = 1105] = "noBindings";
6143
- PxDiagnosticCode2[PxDiagnosticCode2["unresolvedBinding"] = 1106] = "unresolvedBinding";
6144
- PxDiagnosticCode2[PxDiagnosticCode2["triggersNoRoot"] = 1201] = "triggersNoRoot";
6145
- PxDiagnosticCode2[PxDiagnosticCode2["noRootForSelector"] = 1202] = "noRootForSelector";
6146
- PxDiagnosticCode2[PxDiagnosticCode2["noRootElement"] = 1203] = "noRootElement";
6147
- PxDiagnosticCode2[PxDiagnosticCode2["noElementsForSelector"] = 1206] = "noElementsForSelector";
6148
- PxDiagnosticCode2[PxDiagnosticCode2["setAttributeNoElement"] = 1207] = "setAttributeNoElement";
6149
- PxDiagnosticCode2[PxDiagnosticCode2["scrollSmoothingNeedsOwnDriver"] = 1301] = "scrollSmoothingNeedsOwnDriver";
6150
- PxDiagnosticCode2[PxDiagnosticCode2["scrollNativeUnavailable"] = 1302] = "scrollNativeUnavailable";
6151
- PxDiagnosticCode2[PxDiagnosticCode2["scrollSubjectInvalid"] = 1303] = "scrollSubjectInvalid";
6152
- PxDiagnosticCode2[PxDiagnosticCode2["scrollSubjectNoMatch"] = 1304] = "scrollSubjectNoMatch";
6153
- PxDiagnosticCode2[PxDiagnosticCode2["scrollNoRootToObserve"] = 1305] = "scrollNoRootToObserve";
6154
- PxDiagnosticCode2[PxDiagnosticCode2["scrollTriggerIgnored"] = 1306] = "scrollTriggerIgnored";
6155
- PxDiagnosticCode2[PxDiagnosticCode2["rateRejected"] = 1401] = "rateRejected";
6156
- PxDiagnosticCode2[PxDiagnosticCode2["controlPropsConflict"] = 1501] = "controlPropsConflict";
6157
- PxDiagnosticCode2[PxDiagnosticCode2["rnCompileFailed"] = 1601] = "rnCompileFailed";
6158
- PxDiagnosticCode2[PxDiagnosticCode2["rnRenderFailed"] = 1602] = "rnRenderFailed";
6159
- PxDiagnosticCode2[PxDiagnosticCode2["rnBoundaryCaught"] = 1603] = "rnBoundaryCaught";
6160
- PxDiagnosticCode2[PxDiagnosticCode2["rnUnsupported"] = 1604] = "rnUnsupported";
6161
- return PxDiagnosticCode2;
6162
- })(PxDiagnosticCode || {});
6163
6240
  var FLAT_ONLY_KEYS = [
6164
6241
  "timelineSource",
6165
6242
  "scroll",
@@ -6234,16 +6311,16 @@ function foldTimelineOverride(timeline, shortcuts) {
6234
6311
  } else {
6235
6312
  base = timeline != null ? timeline : void 0;
6236
6313
  }
6237
- const { duration, delay, iterations, startOn } = shortcuts;
6238
- if (duration === void 0 && delay === void 0 && iterations === void 0 && startOn === void 0) {
6314
+ const { duration, delay, iterations, start } = shortcuts;
6315
+ if (duration === void 0 && delay === void 0 && iterations === void 0 && start === void 0) {
6239
6316
  return base === void 0 ? void 0 : { timeline: base };
6240
6317
  }
6241
6318
  const out = isPlainObject(base) ? __spreadValues2({}, base) : {};
6242
6319
  if (duration !== void 0) out.duration = duration;
6243
6320
  if (delay !== void 0) out.delay = delay;
6244
6321
  if (iterations !== void 0) out.iterations = iterations;
6245
- if (startOn !== void 0) {
6246
- out.trigger = __spreadProps2(__spreadValues2({}, out.trigger), { startOn });
6322
+ if (start !== void 0) {
6323
+ out.trigger = __spreadProps2(__spreadValues2({}, out.trigger), { start });
6247
6324
  }
6248
6325
  return { timeline: out };
6249
6326
  }
@@ -6312,6 +6389,50 @@ function applyAnimatorConfig(doc, patch, options) {
6312
6389
  }
6313
6390
 
6314
6391
  // ../svg-animator-core/dist/internal.js
6392
+ var CLASS_NAME_KEY = "className";
6393
+ var CLASS_ATTR2 = "class";
6394
+ var DOM_TYPE_KEY = "domType";
6395
+ var TYPE_ATTR = "type";
6396
+ var DEFAULT_TAG = "g";
6397
+ function renderPxTree(node, factory, diag) {
6398
+ return node ? renderOne(node, factory, diag, true, 0) : null;
6399
+ }
6400
+ function renderOne(node, factory, diag, isRoot, index) {
6401
+ const _a2 = node, { type, children, style } = _a2, props = __objRest(_a2, ["type", "children", "style"]);
6402
+ const tag = type || DEFAULT_TAG;
6403
+ if (PX_DISALLOWED_SVG_TAGS_LOWER.has(tag.toLowerCase())) {
6404
+ (diag != null ? diag : createDiagnostics(void 0, "[PxAnimator]")).warn(PxDiagnosticKind.document, 1103, tag);
6405
+ return null;
6406
+ }
6407
+ const domType = props[DOM_TYPE_KEY];
6408
+ if (domType !== void 0) delete props[DOM_TYPE_KEY];
6409
+ const attrs = {};
6410
+ let inlineStyle;
6411
+ const domProps = toDomProps(props);
6412
+ for (const propName of Object.keys(domProps)) {
6413
+ const sanitized = sanitizeAttributeValue(propName, domProps[propName]);
6414
+ if (sanitized === void 0) continue;
6415
+ if (PX_CSS_ONLY_STYLE_PROPS.has(propName)) {
6416
+ (inlineStyle != null ? inlineStyle : inlineStyle = {})[propName] = String(sanitized);
6417
+ continue;
6418
+ }
6419
+ attrs[propName === CLASS_NAME_KEY ? CLASS_ATTR2 : camelCaseToKebabWordIfNeeded(propName)] = sanitized;
6420
+ }
6421
+ if (domType !== void 0) attrs[TYPE_ATTR] = String(domType);
6422
+ if (style) {
6423
+ for (const styleProp of Object.keys(style)) (inlineStyle != null ? inlineStyle : inlineStyle = {})[styleProp] = String(style[styleProp]);
6424
+ }
6425
+ const rendered = [];
6426
+ if (children) {
6427
+ children.forEach((child, i) => {
6428
+ const el = renderOne(child, factory, diag, false, i);
6429
+ if (el !== null) rendered.push(el);
6430
+ });
6431
+ }
6432
+ const ownText = props[PX_TEXT_CONTENT_ATTR];
6433
+ const text = !rendered.length && typeof ownText === "string" && ownText ? ownText : void 0;
6434
+ return factory({ tag, attrs, style: inlineStyle, children: rendered, text, node, isRoot, index });
6435
+ }
6315
6436
  function isScrollTimeline(config) {
6316
6437
  return (config == null ? void 0 : config.timelineSource) === "scroll";
6317
6438
  }
@@ -6390,6 +6511,202 @@ function asThrownError(e) {
6390
6511
  return e instanceof Error ? e : new Error(String(e));
6391
6512
  }
6392
6513
 
6514
+ // src/registry/PxAnimatorRegistry.ts
6515
+ var STORE_KEY = /* @__PURE__ */ Symbol.for("@pixodesk/svg-animator-web:animators");
6516
+ var GLOBAL_NAME = "__pixodeskAnimators";
6517
+ function store() {
6518
+ const g = globalThis;
6519
+ let s = g[STORE_KEY];
6520
+ if (!s) {
6521
+ const animators = /* @__PURE__ */ new Set();
6522
+ const listeners = /* @__PURE__ */ new Set();
6523
+ s = {
6524
+ animators,
6525
+ listeners,
6526
+ getAll: () => Array.from(animators),
6527
+ subscribe: (listener) => {
6528
+ listeners.add(listener);
6529
+ return () => {
6530
+ listeners.delete(listener);
6531
+ };
6532
+ }
6533
+ };
6534
+ g[STORE_KEY] = s;
6535
+ if (g[GLOBAL_NAME] === void 0) g[GLOBAL_NAME] = s;
6536
+ }
6537
+ return s;
6538
+ }
6539
+ function getAllAnimators() {
6540
+ return store().getAll();
6541
+ }
6542
+ function onAnimatorsChange(listener) {
6543
+ return store().subscribe(listener);
6544
+ }
6545
+ function notify(event, animator) {
6546
+ for (const listener of store().listeners) {
6547
+ try {
6548
+ listener(event, animator);
6549
+ } catch (e) {
6550
+ setTimeout(() => {
6551
+ throw e;
6552
+ }, 0);
6553
+ }
6554
+ }
6555
+ }
6556
+ function registerAnimator(animator) {
6557
+ const s = store();
6558
+ if (s.animators.has(animator)) return;
6559
+ s.animators.add(animator);
6560
+ notify("add", animator);
6561
+ const destroy = animator.destroy.bind(animator);
6562
+ animator.destroy = () => {
6563
+ destroy();
6564
+ if (s.animators.delete(animator)) notify("remove", animator);
6565
+ };
6566
+ }
6567
+ function withRegistryEvents(callbacks, api) {
6568
+ const fire = (event) => {
6569
+ const a = api();
6570
+ if (a) notify(event, a);
6571
+ };
6572
+ return __spreadProps(__spreadValues({}, callbacks), {
6573
+ onPlay: () => {
6574
+ var _a2;
6575
+ (_a2 = callbacks == null ? void 0 : callbacks.onPlay) == null ? void 0 : _a2.call(callbacks);
6576
+ fire("play");
6577
+ },
6578
+ onPause: () => {
6579
+ var _a2;
6580
+ (_a2 = callbacks == null ? void 0 : callbacks.onPause) == null ? void 0 : _a2.call(callbacks);
6581
+ fire("pause");
6582
+ },
6583
+ onCancel: () => {
6584
+ var _a2;
6585
+ (_a2 = callbacks == null ? void 0 : callbacks.onCancel) == null ? void 0 : _a2.call(callbacks);
6586
+ fire("cancel");
6587
+ },
6588
+ onFinish: () => {
6589
+ var _a2;
6590
+ (_a2 = callbacks == null ? void 0 : callbacks.onFinish) == null ? void 0 : _a2.call(callbacks);
6591
+ fire("finish");
6592
+ }
6593
+ });
6594
+ }
6595
+
6596
+ // src/triggers/PxVisibilityGate.ts
6597
+ var PLAY_WHEN_VISIBLE_DEFAULTS = {
6598
+ offScreen: PX_TRIGGER_DEFAULTS.offScreen,
6599
+ visibilityThreshold: PX_TRIGGER_DEFAULTS.visibilityThreshold,
6600
+ visibilityDebounce: PX_TRIGGER_DEFAULTS.visibilityDebounce
6601
+ };
6602
+ var THRESHOLD_STEPS = Array.from({ length: 21 }, (_, i) => i / 20);
6603
+ function effectiveRatio(entry) {
6604
+ var _a2, _b;
6605
+ const target = entry.boundingClientRect;
6606
+ const visible = entry.intersectionRect;
6607
+ if (!(target == null ? void 0 : target.height) || !visible) return entry.intersectionRatio;
6608
+ const live = typeof window !== "undefined" && window.innerHeight ? window.innerHeight : Infinity;
6609
+ const declared = (_b = (_a2 = entry.rootBounds) == null ? void 0 : _a2.height) != null ? _b : Infinity;
6610
+ const viewport = Math.min(live, declared);
6611
+ const denom = Math.min(target.height, Number.isFinite(viewport) ? viewport : target.height);
6612
+ return denom > 0 ? visible.height / denom : entry.intersectionRatio;
6613
+ }
6614
+ function openGate(host) {
6615
+ return {
6616
+ requestStart: () => host.play(),
6617
+ dispose: () => {
6618
+ }
6619
+ };
6620
+ }
6621
+ function isGated(offScreen) {
6622
+ return offScreen !== PxOffScreenAction.continue;
6623
+ }
6624
+ function createVisibilityGate(root, trigger, host) {
6625
+ if (!isGated(trigger.offScreen)) return openGate(host);
6626
+ if (typeof IntersectionObserver === "undefined") return openGate(host);
6627
+ const threshold = trigger.visibilityThreshold;
6628
+ const debounceMs = trigger.visibilityDebounce;
6629
+ let isOpen = void 0;
6630
+ let startPending = false;
6631
+ let pausedByGate = false;
6632
+ let lastRatio = 0;
6633
+ let openTimer;
6634
+ const cancelPendingOpen = () => {
6635
+ if (openTimer !== void 0) {
6636
+ clearTimeout(openTimer);
6637
+ openTimer = void 0;
6638
+ }
6639
+ };
6640
+ const open = () => {
6641
+ openTimer = void 0;
6642
+ if (isOpen === true) return;
6643
+ isOpen = true;
6644
+ if (startPending || pausedByGate) {
6645
+ startPending = false;
6646
+ pausedByGate = false;
6647
+ host.play();
6648
+ }
6649
+ };
6650
+ const close = () => {
6651
+ cancelPendingOpen();
6652
+ if (isOpen === false) return;
6653
+ isOpen = false;
6654
+ if (!host.isPlaying()) return;
6655
+ if (trigger.offScreen === PxOffScreenAction.reset) {
6656
+ host.cancel();
6657
+ } else {
6658
+ host.pause();
6659
+ }
6660
+ pausedByGate = true;
6661
+ };
6662
+ const apply2 = (ratio) => {
6663
+ lastRatio = ratio;
6664
+ if (typeof document !== "undefined" && document.visibilityState === "hidden") {
6665
+ close();
6666
+ return;
6667
+ }
6668
+ if (ratio >= threshold) {
6669
+ if (isOpen === true || openTimer !== void 0) return;
6670
+ if (debounceMs > 0) openTimer = setTimeout(open, debounceMs);
6671
+ else open();
6672
+ return;
6673
+ }
6674
+ if (ratio <= 0) {
6675
+ close();
6676
+ return;
6677
+ }
6678
+ if (isOpen !== true) cancelPendingOpen();
6679
+ };
6680
+ const observer = new IntersectionObserver((entries) => {
6681
+ const last = entries[entries.length - 1];
6682
+ if (last) apply2(last.isIntersecting ? effectiveRatio(last) : 0);
6683
+ }, { threshold: THRESHOLD_STEPS });
6684
+ observer.observe(root);
6685
+ const onVisibilityChange = () => {
6686
+ apply2(lastRatio);
6687
+ };
6688
+ const hasDocument = typeof document !== "undefined";
6689
+ if (hasDocument) document.addEventListener("visibilitychange", onVisibilityChange);
6690
+ return {
6691
+ requestStart: (immediate) => {
6692
+ if (immediate || isOpen === true) {
6693
+ cancelPendingOpen();
6694
+ isOpen = true;
6695
+ startPending = false;
6696
+ pausedByGate = false;
6697
+ host.play();
6698
+ return;
6699
+ }
6700
+ startPending = true;
6701
+ },
6702
+ dispose: () => {
6703
+ cancelPendingOpen();
6704
+ observer.disconnect();
6705
+ if (hasDocument) document.removeEventListener("visibilitychange", onVisibilityChange);
6706
+ }
6707
+ };
6708
+ }
6709
+
6393
6710
  // src/triggers/PxAnimatorTriggers.ts
6394
6711
  function setupAnimationTriggers(api, trigger, diag) {
6395
6712
  const report = diag != null ? diag : createDiagnostics(void 0, "[PxAnimator]");
@@ -6397,7 +6714,7 @@ function setupAnimationTriggers(api, trigger, diag) {
6397
6714
  const dispose = () => {
6398
6715
  for (const undo of cleanups.splice(0)) undo();
6399
6716
  };
6400
- const { startOn, outAction, scrollIntoViewThreshold } = resolveTrigger(trigger);
6717
+ const resolved = resolveTrigger(trigger);
6401
6718
  const root = api.getRootElement();
6402
6719
  if (!root) {
6403
6720
  report.warn(PxDiagnosticKind.host, PxDiagnosticCode.triggersNoRoot);
@@ -6411,27 +6728,34 @@ function setupAnimationTriggers(api, trigger, diag) {
6411
6728
  }
6412
6729
  api.play();
6413
6730
  };
6414
- const handleEndAction = () => {
6415
- switch (outAction) {
6416
- case "pause":
6731
+ const gate = createVisibilityGate(root, resolved, {
6732
+ isPlaying: () => api.isPlaying(),
6733
+ play: start,
6734
+ pause: () => api.pause(),
6735
+ cancel: () => api.cancel()
6736
+ });
6737
+ cleanups.push(() => gate.dispose());
6738
+ const handleMouseOut = () => {
6739
+ switch (resolved.mouseOut) {
6740
+ case PxMouseOutAction.pause:
6417
6741
  api.pause();
6418
6742
  break;
6419
- case "reset":
6743
+ case PxMouseOutAction.reset:
6420
6744
  api.cancel();
6421
6745
  break;
6422
- case "reverse":
6746
+ case PxMouseOutAction.reverse:
6423
6747
  reversed = true;
6424
6748
  api.setPlaybackRate(-1);
6425
6749
  api.play();
6426
6750
  break;
6427
- case "continue":
6751
+ case PxMouseOutAction.continue:
6428
6752
  default:
6429
6753
  break;
6430
6754
  }
6431
6755
  };
6432
- switch (startOn) {
6433
- case "load": {
6434
- const startHandler = () => start();
6756
+ switch (resolved.start) {
6757
+ case PxTriggerStart.load: {
6758
+ const startHandler = () => gate.requestStart(false);
6435
6759
  if (document.readyState === "complete") {
6436
6760
  startHandler();
6437
6761
  } else {
@@ -6440,14 +6764,14 @@ function setupAnimationTriggers(api, trigger, diag) {
6440
6764
  }
6441
6765
  break;
6442
6766
  }
6443
- case "mouseOver": {
6767
+ case PxTriggerStart.mouseOver: {
6444
6768
  let enteredOnce = false;
6445
6769
  const mouseOverHandler = () => {
6446
6770
  enteredOnce = true;
6447
- start();
6771
+ gate.requestStart(true);
6448
6772
  };
6449
6773
  const mouseOutHandler = () => {
6450
- if (enteredOnce) handleEndAction();
6774
+ if (enteredOnce) handleMouseOut();
6451
6775
  };
6452
6776
  root.addEventListener("mouseenter", mouseOverHandler);
6453
6777
  root.addEventListener("mouseleave", mouseOutHandler);
@@ -6457,51 +6781,16 @@ function setupAnimationTriggers(api, trigger, diag) {
6457
6781
  });
6458
6782
  break;
6459
6783
  }
6460
- case "click": {
6784
+ case PxTriggerStart.click: {
6461
6785
  const clickHandler = () => {
6462
- if (api.isPlaying()) {
6463
- handleEndAction();
6464
- } else {
6465
- start();
6466
- }
6786
+ if (api.isPlaying()) api.pause();
6787
+ else gate.requestStart(true);
6467
6788
  };
6468
6789
  root.addEventListener("click", clickHandler);
6469
6790
  cleanups.push(() => root.removeEventListener("click", clickHandler));
6470
6791
  break;
6471
6792
  }
6472
- case "scrollIntoView": {
6473
- const effectiveRatio = (entry) => {
6474
- var _a2, _b;
6475
- const target = entry.boundingClientRect;
6476
- const visible = entry.intersectionRect;
6477
- if (!(target == null ? void 0 : target.height) || !visible) return entry.intersectionRatio;
6478
- const live = typeof window !== "undefined" && window.innerHeight ? window.innerHeight : Infinity;
6479
- const declared = (_b = (_a2 = entry.rootBounds) == null ? void 0 : _a2.height) != null ? _b : Infinity;
6480
- const viewport = Math.min(live, declared);
6481
- const denom = Math.min(target.height, Number.isFinite(viewport) ? viewport : target.height);
6482
- return denom > 0 ? visible.height / denom : entry.intersectionRatio;
6483
- };
6484
- const thresholdSteps = Array.from({ length: 21 }, (_, i) => i / 20);
6485
- let wasIntersecting = false;
6486
- const observer = new IntersectionObserver(
6487
- (entries) => {
6488
- entries.forEach((entry) => {
6489
- if (entry.isIntersecting && effectiveRatio(entry) >= scrollIntoViewThreshold) {
6490
- wasIntersecting = true;
6491
- start();
6492
- } else if (wasIntersecting) {
6493
- wasIntersecting = false;
6494
- handleEndAction();
6495
- }
6496
- });
6497
- },
6498
- { threshold: thresholdSteps }
6499
- );
6500
- observer.observe(root);
6501
- cleanups.push(() => observer.disconnect());
6502
- break;
6503
- }
6504
- case "programmatic":
6793
+ case PxTriggerStart.none:
6505
6794
  break;
6506
6795
  }
6507
6796
  return dispose;
@@ -6509,7 +6798,12 @@ function setupAnimationTriggers(api, trigger, diag) {
6509
6798
 
6510
6799
  // src/engines/PxAnimatorFrameLoop.ts
6511
6800
  function getSelector(id) {
6512
- return "#" + id;
6801
+ return "#" + escapeCssId(id);
6802
+ }
6803
+ function escapeCssId(id) {
6804
+ const css = globalThis.CSS;
6805
+ if (typeof (css == null ? void 0 : css.escape) === "function") return css.escape(id);
6806
+ return id.replace(/^([0-9])/, (_all, digit) => "\\3" + digit + " ").replace(/([^\w\-\\ ])/g, "\\$1");
6513
6807
  }
6514
6808
  function createFrameLoopAnimator(doc, adapter, callbacks, rootElement) {
6515
6809
  var _a2;
@@ -7204,69 +7498,26 @@ function bindWithEngineChoice(doc, adapter, callbacks, rootElement) {
7204
7498
 
7205
7499
  // src/dom/PxAnimatorDOM.ts
7206
7500
  var SVG_NS = "http://www.w3.org/2000/svg";
7207
- function createElement(tagName, normalizedProps, style, children, textContent, diag) {
7208
- if (PX_DISALLOWED_SVG_TAGS_LOWER.has(tagName.toLowerCase())) {
7209
- (diag != null ? diag : createDiagnostics(void 0, "[PxAnimator]")).warn(PxDiagnosticKind.document, PxDiagnosticCode.blockedTag, tagName);
7210
- return null;
7211
- }
7212
- const element = document.createElementNS(SVG_NS, tagName);
7213
- for (const propName in normalizedProps) {
7214
- const sanitized = sanitizeAttributeValue(propName, normalizedProps[propName]);
7215
- if (sanitized === void 0) continue;
7216
- if (PX_CSS_ONLY_STYLE_PROPS.has(propName)) {
7217
- element.style[propName] = String(sanitized);
7218
- continue;
7219
- }
7220
- element.setAttribute(camelCaseToKebabWordIfNeeded(propName), sanitized);
7221
- }
7501
+ var createDomElement = ({ tag, attrs, style, children, text }) => {
7502
+ const element = document.createElementNS(SVG_NS, tag);
7503
+ for (const name of Object.keys(attrs)) element.setAttribute(name, attrs[name]);
7222
7504
  if (style) {
7223
- for (const styleProp in style) {
7224
- element.style[styleProp] = String(style[styleProp]);
7225
- }
7226
- }
7227
- if (children == null ? void 0 : children.length) {
7228
- for (const child of children) {
7229
- element.appendChild(child);
7230
- }
7231
- } else if (textContent) {
7232
- element.textContent = textContent;
7233
- }
7234
- return element;
7235
- }
7236
- function renderNode(node, defs, diag) {
7237
- if (!node) return null;
7238
- const _a2 = node, { type, children, style } = _a2, props = __objRest(_a2, ["type", "children", "style"]);
7239
- const domType = props.domType;
7240
- if (domType !== void 0) delete props.domType;
7241
- const nodeDefs = getDefinitions(node) || defs;
7242
- const resolvedStyle = style;
7243
- let childElements;
7244
- if (children) {
7245
- for (const ch of children) {
7246
- const child = renderNode(ch, nodeDefs, diag);
7247
- if (child) {
7248
- if (!childElements) childElements = [];
7249
- childElements.push(child);
7250
- }
7251
- }
7505
+ const target = element.style;
7506
+ for (const prop of Object.keys(style)) target[prop] = style[prop];
7252
7507
  }
7253
- const element = createElement(
7254
- type || "g",
7255
- toDomProps(props),
7256
- resolvedStyle,
7257
- childElements,
7258
- props[PX_TEXT_CONTENT_ATTR],
7259
- diag
7260
- );
7261
- if (element && domType !== void 0) element.setAttribute("type", domType);
7508
+ for (const child of children) element.appendChild(child);
7509
+ if (text !== void 0) element.textContent = text;
7262
7510
  return element;
7511
+ };
7512
+ function renderNode(node, _defs, diag) {
7513
+ return renderPxTree(node, createDomElement, diag);
7263
7514
  }
7264
7515
 
7265
7516
  // src/animator/PxAnimator.ts
7266
7517
  function createAnimatorFromConfig(doc, adapter, callbacks, rootElement) {
7267
7518
  return bindWithEngineChoice(doc, adapter, callbacks, rootElement);
7268
7519
  }
7269
- function createAnimatorImpl(doc, adapter, callbacks, containerElement, patch, resetTimeline) {
7520
+ function createAnimatorImpl(doc, adapter, callbacks, containerElement, patch, resetTimeline, providedRoot) {
7270
7521
  const diag = createDiagnostics(callbacks, "[PxAnimator]");
7271
7522
  const effectsWarnings = validateNodeEffects(doc);
7272
7523
  for (const w of effectsWarnings) diag.warn(PxDiagnosticKind.document, PxDiagnosticCode.effectsShape, w);
@@ -7290,6 +7541,7 @@ function createAnimatorImpl(doc, adapter, callbacks, containerElement, patch, re
7290
7541
  }
7291
7542
  }
7292
7543
  }
7544
+ if (!rootElement && providedRoot) rootElement = providedRoot;
7293
7545
  const api = createAnimatorFromConfig(doc, adapter, callbacks, rootElement);
7294
7546
  if (containerElement && rootElement) {
7295
7547
  const rendered = rootElement;
@@ -7302,17 +7554,19 @@ function createAnimatorImpl(doc, adapter, callbacks, containerElement, patch, re
7302
7554
  return api;
7303
7555
  }
7304
7556
  function isInternalOptions(options) {
7305
- return "adapter" in options;
7557
+ return "adapter" in options || "rootElement" in options;
7306
7558
  }
7307
7559
  function resolveTimelineOption(options) {
7308
- const { timeline, duration, delay, iterations, startOn } = options;
7309
- return foldTimelineOverride(timeline, { duration, delay, iterations, startOn });
7560
+ const { timeline, duration, delay, iterations, start } = options;
7561
+ return foldTimelineOverride(timeline, { duration, delay, iterations, start });
7310
7562
  }
7311
7563
  function createAnimator(options) {
7312
7564
  const { src, doc, container, resetTimeline } = options;
7313
7565
  const adapter = isInternalOptions(options) ? options.adapter : void 0;
7566
+ const providedRoot = isInternalOptions(options) ? options.rootElement : void 0;
7314
7567
  const patch = resolveTimelineOption(options);
7315
- const callbacks = toEngineCallbacks(options);
7568
+ let proxy;
7569
+ const callbacks = withRegistryEvents(toEngineCallbacks(options), () => proxy);
7316
7570
  if (doc !== void 0 && src !== void 0) {
7317
7571
  throw new Error("createAnimator: provide either `src` or `doc`, not both");
7318
7572
  }
@@ -7342,25 +7596,13 @@ function createAnimator(options) {
7342
7596
  };
7343
7597
  const build = (document2) => {
7344
7598
  try {
7345
- ready(createAnimatorImpl(document2, adapter, callbacks, container, patch, resetTimeline));
7599
+ ready(createAnimatorImpl(document2, adapter, callbacks, container, patch, resetTimeline, providedRoot));
7346
7600
  } catch (e) {
7347
7601
  const err = asThrownError(e);
7348
7602
  failed(PxDiagnosticKind.internal, PxDiagnosticCode.buildFailed, err);
7349
7603
  }
7350
7604
  };
7351
- if (doc !== void 0) {
7352
- build(doc);
7353
- } else {
7354
- fetch(src).then((res) => res.json()).then((json) => {
7355
- if (destroyed) return;
7356
- if (isPxDocument(json)) build(json);
7357
- else failed(PxDiagnosticKind.document, PxDiagnosticCode.invalidDocumentAtSrc, src);
7358
- }).catch((err) => {
7359
- var _a2;
7360
- failed(PxDiagnosticKind.host, PxDiagnosticCode.loadFailed, src, (_a2 = err == null ? void 0 : err.message) != null ? _a2 : String(err));
7361
- });
7362
- }
7363
- return {
7605
+ proxy = {
7364
7606
  "isReady": () => !!animator,
7365
7607
  "getRootElement": () => animator ? animator.getRootElement() : null,
7366
7608
  "isPlaying": () => (animator == null ? void 0 : animator.isPlaying()) || false,
@@ -7393,6 +7635,20 @@ function createAnimator(options) {
7393
7635
  animator == null ? void 0 : animator.destroy();
7394
7636
  }
7395
7637
  };
7638
+ registerAnimator(proxy);
7639
+ if (doc !== void 0) {
7640
+ build(doc);
7641
+ } else {
7642
+ fetch(src).then((res) => res.json()).then((json) => {
7643
+ if (destroyed) return;
7644
+ if (isPxDocument(json)) build(json);
7645
+ else failed(PxDiagnosticKind.document, PxDiagnosticCode.invalidDocumentAtSrc, src);
7646
+ }).catch((err) => {
7647
+ var _a2;
7648
+ failed(PxDiagnosticKind.host, PxDiagnosticCode.loadFailed, src, (_a2 = err == null ? void 0 : err.message) != null ? _a2 : String(err));
7649
+ });
7650
+ }
7651
+ return proxy;
7396
7652
  }
7397
7653
  function loadTagAnimators(options) {
7398
7654
  const elements = document.querySelectorAll("[" + PX_ANIM_SRC_ATTR_NAME + "]");
@@ -7419,7 +7675,9 @@ function loadTagAnimators(options) {
7419
7675
  createAdapterAnimator,
7420
7676
  createAnimator,
7421
7677
  generateNewIds,
7678
+ getAllAnimators,
7422
7679
  loadTagAnimators,
7680
+ onAnimatorsChange,
7423
7681
  px,
7424
7682
  renderNode,
7425
7683
  setupAnimationTriggers,