@hyperframes/parsers 0.8.126 → 0.8.128

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.
@@ -7,7 +7,14 @@ import { K as Keyframe, V as ValidationResult } from './types-Ejqndkej.js';
7
7
  * without pulling in gsapParser (which depends on recast / @babel/parser).
8
8
  */
9
9
 
10
+ declare const GSAP_DEFAULT_DURATION = 0.5;
10
11
  declare const SUPPORTED_PROPS: string[];
12
+ /** Keys stored on dedicated GsapAnimation fields (not in properties/extras). */
13
+ declare const BUILTIN_VAR_KEYS: ReadonlySet<string>;
14
+ declare const DROPPED_VAR_KEYS: ReadonlySet<string>;
15
+ /** Keys that go in `extras`: non-editable GSAP config that must survive round-trips. */
16
+ declare const EXTRAS_KEYS: ReadonlySet<string>;
17
+ declare function isTweenConfigKey(key: string): boolean;
11
18
  type PropertyGroupName = "position" | "scale" | "size" | "rotation" | "visual" | "other";
12
19
  declare const PROPERTY_GROUPS: Record<PropertyGroupName, ReadonlySet<string>>;
13
20
  type PositionWrite = Pick<GsapAnimation, "propertyGroup" | "properties" | "fromProperties" | "keyframes">;
@@ -16,7 +23,7 @@ declare function isXYPositionWrite(a: PositionWrite): boolean;
16
23
  declare function classifyPropertyGroup(prop: string): PropertyGroupName;
17
24
  declare function classifyTweenPropertyGroup(properties: Record<string, unknown>): PropertyGroupName | undefined;
18
25
  /**
19
- * What a Studio hold pins from t=0 before a later keyframed tween: its first keyframe's position props,
26
+ * What a Studio hold pins from t=0 before a later keyframed tween: its 0% keyframe's position props,
20
27
  * minus those an earlier timeline tween on the target writes (a global `gsap.set` is a base value).
21
28
  */
22
29
  declare function positionHoldForAnimation(animation: GsapAnimation, animations: readonly GsapAnimation[]): Record<string, number> | null;
@@ -123,6 +130,7 @@ interface GsapKeyframesData<K extends GsapPercentageKeyframe = GsapPercentageKey
123
130
  keyframes: K[];
124
131
  ease?: string;
125
132
  easeEach?: string;
133
+ fromMotionPath?: true;
126
134
  }
127
135
  interface ArcPathSegment {
128
136
  curviness: number;
@@ -208,4 +216,4 @@ declare function gsapAnimationsToKeyframes(animations: GsapAnimation[], elementS
208
216
  skipBaseSet?: boolean;
209
217
  }): Keyframe[];
210
218
 
211
- export { type ArcPathConfig as A, type GsapAnimation as G, type KeyframeEditability as K, type MotionPathShape as M, PROPERTY_GROUPS as P, SUPPORTED_EASES as S, type ArcPathSegment as a, type GsapKeyframesData as b, type GsapMethod as c, type GsapPercentageKeyframe as d, type GsapProvenance as e, type GsapProvenanceKind as f, type ParsedGsap as g, type PropertyGroupName as h, SUPPORTED_PROPS as i, type SourcedGsapPercentageKeyframe as j, type SplitAnimationsOptions as k, type SplitAnimationsResult as l, classifyPropertyGroup as m, classifyTweenPropertyGroup as n, editabilityForProvenance as o, getAnimationsForElementId as p, gsapAnimationsToKeyframes as q, keyframesToGsapAnimations as r, serializeGsapAnimations as s, buildArcPath as t, type GsapKeyframeFormat as u, validateCompositionGsap as v, isXYPositionWrite as w, positionHoldForAnimation as x };
219
+ export { type ArcPathConfig as A, BUILTIN_VAR_KEYS as B, DROPPED_VAR_KEYS as D, EXTRAS_KEYS as E, type GsapMethod as G, type KeyframeEditability as K, type MotionPathShape as M, type ParsedGsap as P, SUPPORTED_EASES as S, type GsapAnimation as a, type ArcPathSegment as b, type GsapProvenance as c, type GsapProvenanceKind as d, buildArcPath as e, editabilityForProvenance as f, type GsapKeyframeFormat as g, type GsapKeyframesData as h, type GsapPercentageKeyframe as i, PROPERTY_GROUPS as j, type PropertyGroupName as k, SUPPORTED_PROPS as l, classifyPropertyGroup as m, classifyTweenPropertyGroup as n, getAnimationsForElementId as o, gsapAnimationsToKeyframes as p, keyframesToGsapAnimations as q, type SplitAnimationsOptions as r, serializeGsapAnimations as s, type SplitAnimationsResult as t, GSAP_DEFAULT_DURATION as u, validateCompositionGsap as v, type SourcedGsapPercentageKeyframe as w, isTweenConfigKey as x, isXYPositionWrite as y, positionHoldForAnimation as z };
@@ -1,9 +1,9 @@
1
- import { G as GsapAnimation, A as ArcPathConfig, k as SplitAnimationsOptions, l as SplitAnimationsResult, a as ArcPathSegment } from './gsapSerialize-CVwsMItk.js';
1
+ import { a as GsapAnimation, A as ArcPathConfig, r as SplitAnimationsOptions, t as SplitAnimationsResult, b as ArcPathSegment } from './gsapSerialize-CfSXM_Ez.js';
2
2
  import './types-Ejqndkej.js';
3
3
 
4
4
  type TweenTarget = Pick<GsapAnimation, "targetSelector" | "hasPartialSelector">;
5
5
  type TweenTime = Pick<GsapAnimation, "position" | "implicitPosition">;
6
- declare function clipTweenMatcher(clipSelectors: string | readonly string[], root?: ParentNode): (tween: TweenTarget) => boolean;
6
+ declare function clipTweenMatcher(clipSelectors: string | readonly string[], root?: ParentNode, queries?: Map<string, Element[]>): (tween: TweenTarget) => boolean;
7
7
  /** Where a GSAP script's clips live: linkedom keeps a `<template>`'s children under it, out of document queries. */
8
8
  declare function clipQueryRoot(script: Element): ParentNode;
9
9
  /** A written position; an implicit one follows the tween before it and must stay unwritten. */
@@ -21,6 +21,26 @@ declare function updateAnimationInScript(script: string, animationId: string, up
21
21
  * (used by timeline clip-move to keep GSAP positions in sync with the clip start).
22
22
  */
23
23
  declare function shiftPositionsInScript(script: string, targetSelector: string, delta: number, root?: ParentNode): string;
24
+ type ClipTweenRetime = {
25
+ kind: "shift";
26
+ targetSelector: string;
27
+ delta: number;
28
+ } | {
29
+ kind: "scale";
30
+ targetSelector: string;
31
+ oldStart: number;
32
+ oldDuration: number;
33
+ newStart: number;
34
+ newDuration: number;
35
+ };
36
+ /**
37
+ * Applies `retimes` in order with one parse: the bytes equal running shift/scalePositionsInScript once
38
+ * per retime. `changed[i]` says whether retime i moved a value.
39
+ */
40
+ declare function retimeClipTweensInScript(script: string, retimes: readonly ClipTweenRetime[], root?: ParentNode): {
41
+ script: string;
42
+ changed: boolean[];
43
+ };
24
44
  /** Copies each tween on `fromSelector` for `toSelector`, `delta` seconds later, in its own argument text. Exact or
25
45
  * absent: only a tween at a number, written straight in the timeline's own block, is copied. */
26
46
  declare function copyAnimationsInScript(script: string, fromSelector: string, toSelector: string, delta: number): string;
@@ -177,4 +197,4 @@ type UnrollElement = {
177
197
  */
178
198
  declare function unrollDynamicAnimations(script: string, animationId: string, elements: UnrollElement[]): string;
179
199
 
180
- export { type UnrollElement, addAnimationToScript, addAnimationWithKeyframesToScript, addKeyframeToScript, addLabelToScript, addMotionPathPointInScript, addMotionPathToScript, clipQueryRoot, clipTweenMatcher, convertToKeyframesFromScript, copyAnimationsInScript, dedupePositionWritesInScript, hasExplicitTime, materializeKeyframesFromScript, moveKeyframeInScript, removeAllKeyframesFromScript, removeAnimationFromScript, removeArcPathFromScript, removeKeyframeFromScript, removeLabelFromScript, removeMotionPathPointInScript, removePropertyFromAnimation, replaceTweenWithKeyframesInScript, resizeKeyframedTweenInScript, scalePositionsInScript, setArcPathInScript, shiftPositionsInScript, splitAnimationsInScript, splitIntoPropertyGroupsFromScript, syncPositionHoldsBeforeKeyframes, unrollDynamicAnimations, updateAnimationInScript, updateArcSegmentInScript, updateKeyframeInScript, updateMotionPathPointInScript };
200
+ export { type ClipTweenRetime, type UnrollElement, addAnimationToScript, addAnimationWithKeyframesToScript, addKeyframeToScript, addLabelToScript, addMotionPathPointInScript, addMotionPathToScript, clipQueryRoot, clipTweenMatcher, convertToKeyframesFromScript, copyAnimationsInScript, dedupePositionWritesInScript, hasExplicitTime, materializeKeyframesFromScript, moveKeyframeInScript, removeAllKeyframesFromScript, removeAnimationFromScript, removeArcPathFromScript, removeKeyframeFromScript, removeLabelFromScript, removeMotionPathPointInScript, removePropertyFromAnimation, replaceTweenWithKeyframesInScript, resizeKeyframedTweenInScript, retimeClipTweensInScript, scalePositionsInScript, setArcPathInScript, shiftPositionsInScript, splitAnimationsInScript, splitIntoPropertyGroupsFromScript, syncPositionHoldsBeforeKeyframes, unrollDynamicAnimations, updateAnimationInScript, updateArcSegmentInScript, updateKeyframeInScript, updateMotionPathPointInScript };
@@ -2,6 +2,26 @@
2
2
  import MagicString from "magic-string";
3
3
 
4
4
  // src/gsapConstants.ts
5
+ var GSAP_DEFAULT_DURATION = 0.5;
6
+ var BUILTIN_VAR_KEYS = /* @__PURE__ */ new Set(["duration", "ease", "delay"]);
7
+ var DROPPED_VAR_KEYS = /* @__PURE__ */ new Set([
8
+ "onComplete",
9
+ "onStart",
10
+ "onUpdate",
11
+ "onRepeat"
12
+ ]);
13
+ var EXTRAS_KEYS = /* @__PURE__ */ new Set([
14
+ "stagger",
15
+ "yoyo",
16
+ "repeat",
17
+ "repeatDelay",
18
+ "snap",
19
+ "overwrite",
20
+ "immediateRender"
21
+ ]);
22
+ function isTweenConfigKey(key) {
23
+ return BUILTIN_VAR_KEYS.has(key) || DROPPED_VAR_KEYS.has(key) || EXTRAS_KEYS.has(key);
24
+ }
5
25
  var PROPERTY_GROUPS = {
6
26
  position: /* @__PURE__ */ new Set(["x", "y", "xPercent", "yPercent"]),
7
27
  scale: /* @__PURE__ */ new Set(["scale", "scaleX", "scaleY"]),
@@ -41,16 +61,14 @@ function positionHoldForAnimation(animation, animations) {
41
61
  if (!animation.keyframes) return null;
42
62
  const start = knownStart(animation) ?? 0;
43
63
  if (!(start > 1e-3)) return null;
44
- const first = [...animation.keyframes.keyframes].sort(
45
- (left, right) => left.percentage - right.percentage
46
- )[0];
47
- if (!first) return null;
64
+ const atStart = animation.keyframes.keyframes.find((keyframe) => keyframe.percentage === 0);
65
+ if (!atStart) return null;
48
66
  const earlier = animations.filter((other) => {
49
67
  const otherStart = knownStart(other);
50
68
  return other !== animation && !other.global && other.targetSelector === animation.targetSelector && otherStart !== void 0 && otherStart < start - 1e-3;
51
69
  });
52
70
  const position = {};
53
- for (const [property, value] of Object.entries(first.properties)) {
71
+ for (const [property, value] of Object.entries(atStart.properties)) {
54
72
  if (classifyPropertyGroup(property) !== "position" || typeof value !== "number") continue;
55
73
  if (earlier.some((other) => writesProperty(other, property))) continue;
56
74
  position[property] = value;
@@ -59,6 +77,9 @@ function positionHoldForAnimation(animation, animations) {
59
77
  }
60
78
 
61
79
  // src/gsapSerialize.ts
80
+ function authorsKeyframes(anim) {
81
+ return anim.keyframes !== void 0 || anim.hasUnresolvedKeyframes === true;
82
+ }
62
83
  function mergePercentageKeyframes(keyframes) {
63
84
  const byPercentage = /* @__PURE__ */ new Map();
64
85
  for (const keyframe of keyframes) {
@@ -210,34 +231,27 @@ function readProvenance(node) {
210
231
  var roundPercentage = (percentage) => Math.round(percentage * 10) / 10;
211
232
  var OBJECT_ARRAY_PERCENTAGE_TOLERANCE = 2;
212
233
  function getObjectArrayKeyframeTiming(durations) {
213
- const hasAuthoredDuration = durations.some((duration) => duration !== void 0);
214
- if (hasAuthoredDuration) {
215
- if (!durations.every(
216
- (duration) => typeof duration === "number" && Number.isFinite(duration) && duration > 0
217
- )) {
218
- return null;
219
- }
220
- const totalDuration = durations.reduce((sum, duration) => sum + duration, 0);
221
- let cumulative = 0;
222
- return {
223
- percentages: durations.map((duration) => {
224
- cumulative += duration;
225
- return roundPercentage(cumulative / totalDuration * 100);
226
- }),
227
- totalDuration
228
- };
234
+ const steps = durations.map((duration) => duration ?? GSAP_DEFAULT_DURATION);
235
+ if (!steps.every(
236
+ (duration) => typeof duration === "number" && Number.isFinite(duration) && duration > 0
237
+ )) {
238
+ return null;
229
239
  }
230
- const lastIndex = durations.length - 1;
240
+ const totalDuration = steps.reduce((sum, duration) => sum + duration, 0);
241
+ let cumulative = 0;
231
242
  return {
232
- percentages: durations.map(
233
- (_, index) => lastIndex > 0 ? roundPercentage(index / lastIndex * 100) : 0
234
- )
243
+ percentages: steps.map((duration) => {
244
+ cumulative += duration;
245
+ return roundPercentage(cumulative / totalDuration * 100);
246
+ }),
247
+ totalDuration
235
248
  };
236
249
  }
237
250
  function getCompatibleObjectArrayKeyframeTiming(durations, outerDuration) {
238
251
  const timing = getObjectArrayKeyframeTiming(durations);
239
252
  if (!timing) return null;
240
- if (timing.totalDuration === void 0 || outerDuration === void 0) return timing;
253
+ const stepsAuthorDuration = durations.some((duration) => duration !== void 0);
254
+ if (!stepsAuthorDuration || outerDuration === void 0) return timing;
241
255
  if (typeof outerDuration === "number" && Math.abs(outerDuration - timing.totalDuration) <= Number.EPSILON) {
242
256
  return timing;
243
257
  }
@@ -755,17 +769,6 @@ function findTimelineVar(ast, scope) {
755
769
  });
756
770
  return { timelineVar, ref, timelineCount, defaults };
757
771
  }
758
- var BUILTIN_VAR_KEYS = /* @__PURE__ */ new Set(["duration", "ease", "delay"]);
759
- var DROPPED_VAR_KEYS = /* @__PURE__ */ new Set(["onComplete", "onStart", "onUpdate", "onRepeat"]);
760
- var EXTRAS_KEYS = /* @__PURE__ */ new Set([
761
- "stagger",
762
- "yoyo",
763
- "repeat",
764
- "repeatDelay",
765
- "snap",
766
- "overwrite",
767
- "immediateRender"
768
- ]);
769
772
  function isTimelineRootedCall(callNode, ref) {
770
773
  let obj = callNode.callee?.object;
771
774
  while (obj?.type === "CallExpression") {
@@ -844,6 +847,7 @@ function parsePercentageKeyframes(node, scope, source) {
844
847
  if (typeof key !== "string") continue;
845
848
  const pctMatch = PERCENTAGE_KEY_RE.exec(key);
846
849
  if (pctMatch) {
850
+ if (!isPlainStep(prop.value)) return void 0;
847
851
  const percentage = Number.parseFloat(pctMatch[1] ?? "0");
848
852
  const record = objectExpressionToRecord(prop.value, scope, source);
849
853
  const properties = {};
@@ -862,6 +866,8 @@ function parsePercentageKeyframes(node, scope, source) {
862
866
  ease = tryResolveStringProp(prop.value, scope, source) ?? ease;
863
867
  } else if (key === "easeEach") {
864
868
  easeEach = tryResolveStringProp(prop.value, scope, source) ?? easeEach;
869
+ } else {
870
+ return void 0;
865
871
  }
866
872
  }
867
873
  keyframes.sort((a, b) => a.percentage - b.percentage);
@@ -885,11 +891,19 @@ function computeKeyframesTotalDuration(varsNode, scope, source) {
885
891
  }
886
892
  return getObjectArrayKeyframeTiming(durations)?.totalDuration;
887
893
  }
894
+ function staticKeyName(prop) {
895
+ if (prop.type !== "ObjectProperty" && prop.type !== "Property") return void 0;
896
+ if (prop.method || prop.kind && prop.kind !== "init") return void 0;
897
+ const key = prop.computed ? prop.key?.type === "Literal" && prop.key.value : prop.key?.name ?? prop.key?.value;
898
+ return typeof key === "string" ? key : void 0;
899
+ }
900
+ var isPlainObject = (node) => node?.type === "ObjectExpression" && (node.properties ?? []).every((p) => staticKeyName(p) !== void 0);
901
+ var isPlainStep = (node) => node?.type === "Literal" || isPlainObject(node);
888
902
  function parseObjectArrayKeyframes(node, scope, source) {
889
903
  const elements = node.elements ?? [];
890
904
  const raw = [];
891
905
  for (const el of elements) {
892
- if (!el || el.type !== "ObjectExpression") continue;
906
+ if (!isPlainObject(el)) return void 0;
893
907
  const record = objectExpressionToRecord(el, scope, source);
894
908
  const properties = {};
895
909
  let duration;
@@ -901,6 +915,8 @@ function parseObjectArrayKeyframes(node, scope, source) {
901
915
  ease = v;
902
916
  } else if (typeof v === "number" || typeof v === "string") {
903
917
  properties[k] = v;
918
+ } else {
919
+ return void 0;
904
920
  }
905
921
  }
906
922
  raw.push({ properties, duration, ease });
@@ -926,9 +942,8 @@ function parseSimpleArrayKeyframes(node, scope) {
926
942
  const values = [];
927
943
  for (const el of prop.value.elements ?? []) {
928
944
  const val = resolveNode(el, scope);
929
- if (typeof val === "number" || typeof val === "string") {
930
- values.push(val);
931
- }
945
+ if (typeof val !== "number" && typeof val !== "string") return void 0;
946
+ values.push(val);
932
947
  }
933
948
  if (values.length > 0) arrayProps.set(key, values);
934
949
  } else if (key === "ease") {
@@ -961,6 +976,7 @@ function parseKeyframesNode(node, scope, source) {
961
976
  }
962
977
  if (node.type !== "ObjectExpression") return void 0;
963
978
  const props = node.properties ?? [];
979
+ if (!props.every((p) => staticKeyName(p) !== void 0)) return void 0;
964
980
  let hasPercentageKey = false;
965
981
  let hasArrayValue = false;
966
982
  for (const prop of props) {
@@ -1061,7 +1077,7 @@ function tweenCallToAnimation(call, scope, source, identifierBindings) {
1061
1077
  percentage: waypoints.length > 1 ? Math.round(i / (waypoints.length - 1) * 100) : 0,
1062
1078
  properties: { x: wp.x, y: wp.y }
1063
1079
  }));
1064
- keyframesData = { format: "percentage", keyframes: kf };
1080
+ keyframesData = { format: "percentage", keyframes: kf, fromMotionPath: true };
1065
1081
  } else {
1066
1082
  const kfs = keyframesData.keyframes;
1067
1083
  if (kfs.length === waypoints.length) {
@@ -1091,10 +1107,10 @@ function tweenCallToAnimation(call, scope, source, identifierBindings) {
1091
1107
  const position = typeof posVal === "number" ? posVal : typeof posVal === "string" ? posVal : hasPositionArg ? `__raw:${source.slice(call.positionArg.start, call.positionArg.end)}` : 0;
1092
1108
  let duration = typeof vars.duration === "number" ? vars.duration : void 0;
1093
1109
  const ease = typeof vars.ease === "string" ? vars.ease : void 0;
1094
- if (duration === void 0 && keyframesData) {
1110
+ const durationUnresolved = call.method !== "set" && duration === void 0 && hasUnknownDuration(call.varsArg, scope);
1111
+ if (duration === void 0 && keyframesData && !durationUnresolved) {
1095
1112
  duration = computeKeyframesTotalDuration(call.varsArg, scope, source);
1096
1113
  }
1097
- const durationUnresolved = call.method !== "set" && duration === void 0 && hasUnknownDuration(call.varsArg, scope);
1098
1114
  let selector = call.selector;
1099
1115
  let targetIdentity;
1100
1116
  if (selector === "__unresolved__") {
@@ -1148,7 +1164,6 @@ function tweenCallToAnimation(call, scope, source, identifierBindings) {
1148
1164
  if (provenance) anim.provenance = provenance;
1149
1165
  return anim;
1150
1166
  }
1151
- var GSAP_DEFAULT_DURATION = 0.5;
1152
1167
  function resolvePositionString(pos, cursor, prevStart) {
1153
1168
  const trimmed = pos.trim();
1154
1169
  if (trimmed === "") return cursor;
@@ -1181,7 +1196,7 @@ function applyTimelineDefaults(anims, defaults) {
1181
1196
  if (defaults.duration !== void 0) anim.duration = defaults.duration;
1182
1197
  else if (defaults.durationUnresolved) anim.durationUnresolved = true;
1183
1198
  }
1184
- if (anim.ease === void 0 && defaults.ease !== void 0 && !anim.keyframes) {
1199
+ if (anim.ease === void 0 && defaults.ease !== void 0 && (!anim.keyframes || anim.keyframes.fromMotionPath)) {
1185
1200
  anim.ease = defaults.ease;
1186
1201
  }
1187
1202
  }
@@ -1350,14 +1365,25 @@ function parseGsapScriptAcornForWrite(script) {
1350
1365
  import * as acornWalk2 from "acorn-walk";
1351
1366
 
1352
1367
  // src/clipTweens.ts
1353
- function clipTweenMatcher(clipSelectors, root) {
1368
+ function clipTweenMatcher(clipSelectors, root, queries = /* @__PURE__ */ new Map()) {
1354
1369
  const own = typeof clipSelectors === "string" ? [clipSelectors] : clipSelectors;
1355
- const clips = root ? own.flatMap((selector) => queryAll(root, selector)) : [];
1370
+ if (!root)
1371
+ return ({ targetSelector, hasPartialSelector }) => !hasPartialSelector && own.includes(targetSelector);
1372
+ const queryAll = (selector) => {
1373
+ if (ID_SELECTOR.test(selector)) {
1374
+ if (!queries.has(ALL_IDS)) indexIds(root, queries);
1375
+ return queries.get(selector) ?? [];
1376
+ }
1377
+ let found = queries.get(selector);
1378
+ if (!found) queries.set(selector, found = queryRoot(root, selector));
1379
+ return found;
1380
+ };
1381
+ const clips = own.flatMap((selector) => queryAll(selector));
1356
1382
  return ({ targetSelector, hasPartialSelector }) => {
1357
1383
  if (hasPartialSelector) return false;
1358
1384
  if (own.includes(targetSelector)) return true;
1359
- if (!root || clips.length === 0) return false;
1360
- const targets = queryAll(root, targetSelector);
1385
+ if (clips.length === 0) return false;
1386
+ const targets = queryAll(targetSelector);
1361
1387
  return targets.length > 0 && targets.every((target) => {
1362
1388
  const owner = target.closest("[data-start]");
1363
1389
  return clips.includes(target) || owner !== null && clips.includes(owner);
@@ -1370,7 +1396,20 @@ function clipQueryRoot(script) {
1370
1396
  function hasExplicitTime(animation) {
1371
1397
  return typeof animation.position === "number" && !animation.implicitPosition;
1372
1398
  }
1373
- function queryAll(root, selector) {
1399
+ var ID_SELECTOR = /^#-?[A-Za-z_][\w-]*$/;
1400
+ var ALL_IDS = "[id]";
1401
+ function indexIds(root, queries) {
1402
+ const all = queryRoot(root, ALL_IDS);
1403
+ queries.set(ALL_IDS, all);
1404
+ for (const el of all) {
1405
+ const key = `#${el.id}`;
1406
+ if (!ID_SELECTOR.test(key)) continue;
1407
+ const list = queries.get(key);
1408
+ if (list) list.push(el);
1409
+ else queries.set(key, [el]);
1410
+ }
1411
+ }
1412
+ function queryRoot(root, selector) {
1374
1413
  try {
1375
1414
  return Array.from(root.querySelectorAll(selector));
1376
1415
  } catch {
@@ -1486,24 +1525,8 @@ function upsertProp(ms, objNode, key, value) {
1486
1525
  ms.appendLeft(objNode.end - 1, `${sep}${safeKey(key)}: ${valueToCode(value)}`);
1487
1526
  }
1488
1527
  }
1489
- var NON_EDITABLE_PROP_KEYS = /* @__PURE__ */ new Set([
1490
- "duration",
1491
- "ease",
1492
- "delay",
1493
- "onComplete",
1494
- "onStart",
1495
- "onUpdate",
1496
- "onRepeat",
1497
- "stagger",
1498
- "yoyo",
1499
- "repeat",
1500
- "repeatDelay",
1501
- "snap",
1502
- "overwrite",
1503
- "immediateRender"
1504
- ]);
1505
1528
  function isEditableVarKey(key) {
1506
- return !NON_EDITABLE_PROP_KEYS.has(key);
1529
+ return !isTweenConfigKey(key);
1507
1530
  }
1508
1531
  function preservedEntries(objNode, source, drop, overrides) {
1509
1532
  const entries = [];
@@ -1562,7 +1585,7 @@ function updateAnimationInScript(script, animationId, updates) {
1562
1585
  const parsed = parseGsapScriptAcornForWrite(script);
1563
1586
  if (!parsed) return script;
1564
1587
  const target = parsed.located.find((l) => l.id === animationId);
1565
- if (!target) return script;
1588
+ if (!target || updates.properties && target.animation.hasUnresolvedKeyframes) return script;
1566
1589
  const ms = new MagicString(script);
1567
1590
  const { call } = target;
1568
1591
  if (updates.properties) {
@@ -1626,17 +1649,54 @@ function overwritePosition(ms, call, position) {
1626
1649
  }
1627
1650
  }
1628
1651
  function shiftPositionsInScript(script, targetSelector, delta, root) {
1629
- const parsed = parseGsapScriptAcornForWrite(script);
1630
- if (!parsed) return script;
1631
- const carries = clipTweenMatcher(targetSelector, root);
1652
+ return retimeClipTweensInScript(script, [{ kind: "shift", targetSelector, delta }], root).script;
1653
+ }
1654
+ var isLiveRetime = (r) => r.kind === "shift" || r.oldDuration > 0 && r.newDuration > 0;
1655
+ function applyRetime(animation, retime) {
1656
+ if (retime.kind === "shift") {
1657
+ if (!hasExplicitTime(animation)) return { position: false, duration: false };
1658
+ animation.position = shiftedPosition(animation.position, retime.delta);
1659
+ return { position: true, duration: false };
1660
+ }
1661
+ const touched = { position: false, duration: false };
1662
+ if (!isLiveRetime(retime) || typeof animation.position !== "number") return touched;
1663
+ const ratio = retime.newDuration / retime.oldDuration;
1664
+ if (hasExplicitTime(animation)) {
1665
+ const scaled = retime.newStart + (animation.position - retime.oldStart) * ratio;
1666
+ animation.position = Math.max(0, Math.round(scaled * 1e3) / 1e3);
1667
+ touched.position = true;
1668
+ }
1669
+ if (typeof animation.duration === "number" && animation.duration > 0) {
1670
+ animation.duration = Math.max(1e-3, Math.round(animation.duration * ratio * 1e3) / 1e3);
1671
+ touched.duration = true;
1672
+ }
1673
+ return touched;
1674
+ }
1675
+ function retimeClipTweensInScript(script, retimes, root) {
1676
+ const changed = retimes.map(() => false);
1677
+ const parsed = retimes.some(isLiveRetime) ? parseGsapScriptAcornForWrite(script) : null;
1678
+ if (!parsed) return { script, changed };
1679
+ const queries = /* @__PURE__ */ new Map();
1680
+ const matchers = retimes.map((r) => clipTweenMatcher(r.targetSelector, root, queries));
1632
1681
  const ms = new MagicString(script);
1633
- let changed = false;
1682
+ let wrote = false;
1634
1683
  for (const entry of parsed.located) {
1635
- if (!carries(entry.animation) || !hasExplicitTime(entry.animation)) continue;
1636
- overwritePosition(ms, entry.call, shiftedPosition(entry.animation.position, delta));
1637
- changed = true;
1684
+ const animation = { ...entry.animation };
1685
+ let position = false;
1686
+ let duration = false;
1687
+ retimes.forEach((retime, i) => {
1688
+ if (!matchers[i](animation)) return;
1689
+ const before = [animation.position, animation.duration];
1690
+ const touched = applyRetime(animation, retime);
1691
+ position ||= touched.position;
1692
+ duration ||= touched.duration;
1693
+ if (before[0] !== animation.position || before[1] !== animation.duration) changed[i] = true;
1694
+ });
1695
+ if (position) overwritePosition(ms, entry.call, animation.position);
1696
+ if (duration) upsertProp(ms, entry.call.varsArg, "duration", animation.duration);
1697
+ wrote ||= position || duration;
1638
1698
  }
1639
- return changed ? ms.toString() : script;
1699
+ return { script: wrote ? ms.toString() : script, changed };
1640
1700
  }
1641
1701
  function copyAnimationsInScript(script, fromSelector, toSelector, delta) {
1642
1702
  const parsed = parseGsapScriptAcornForWrite(script);
@@ -1708,29 +1768,11 @@ function isLoopOrForEach(node) {
1708
1768
  return isLoopNode(node) || isForEachStatement(node) || node?.type === "DoWhileStatement";
1709
1769
  }
1710
1770
  function scalePositionsInScript(script, targetSelector, oldStart, oldDuration, newStart, newDuration, root) {
1711
- if (oldDuration <= 0 || newDuration <= 0) return script;
1712
- const ratio = newDuration / oldDuration;
1713
- const parsed = parseGsapScriptAcornForWrite(script);
1714
- if (!parsed) return script;
1715
- const carries = clipTweenMatcher(targetSelector, root);
1716
- const ms = new MagicString(script);
1717
- let changed = false;
1718
- for (const entry of parsed.located) {
1719
- if (!carries(entry.animation) || typeof entry.animation.position !== "number") continue;
1720
- if (hasExplicitTime(entry.animation)) {
1721
- const newPos = Math.max(
1722
- 0,
1723
- Math.round((newStart + (entry.animation.position - oldStart) * ratio) * 1e3) / 1e3
1724
- );
1725
- overwritePosition(ms, entry.call, newPos);
1726
- }
1727
- if (typeof entry.animation.duration === "number" && entry.animation.duration > 0) {
1728
- const newDur = Math.max(1e-3, Math.round(entry.animation.duration * ratio * 1e3) / 1e3);
1729
- upsertProp(ms, entry.call.varsArg, "duration", newDur);
1730
- }
1731
- changed = true;
1732
- }
1733
- return changed ? ms.toString() : script;
1771
+ return retimeClipTweensInScript(
1772
+ script,
1773
+ [{ kind: "scale", targetSelector, oldStart, oldDuration, newStart, newDuration }],
1774
+ root
1775
+ ).script;
1734
1776
  }
1735
1777
  function addAnimationToScript(script, animation) {
1736
1778
  const parsed = parseGsapScriptAcornForWrite(script);
@@ -2004,7 +2046,7 @@ function updateKeyframeInScript(script, animationId, percentage, properties, eas
2004
2046
  const parsed = parseGsapScriptAcornForWrite(script);
2005
2047
  if (!parsed) return script;
2006
2048
  const target = parsed.located.find((l) => l.id === animationId);
2007
- if (!target) return script;
2049
+ if (!target || target.animation.hasUnresolvedKeyframes) return script;
2008
2050
  const kfPropNode = findPropertyNode2(target.call.varsArg, "keyframes");
2009
2051
  if (!kfPropNode) {
2010
2052
  return updateMotionPathKeyframe(script, target, percentage, properties, ease) ?? script;
@@ -2072,7 +2114,7 @@ function locateWithKeyframes(script, animationId) {
2072
2114
  if (!parsed) return null;
2073
2115
  const convertedId = animationId.replace(/-from-|-fromTo-/, "-to-");
2074
2116
  const target = parsed.located.find((l) => l.id === animationId) ?? parsed.located.find((l) => l.id === convertedId);
2075
- if (!target) return null;
2117
+ if (!target || target.animation.hasUnresolvedKeyframes) return null;
2076
2118
  const kfPropNode = findPropertyNode2(target.call.varsArg, "keyframes");
2077
2119
  if (!kfPropNode || kfPropNode.value?.type !== "ObjectExpression") return null;
2078
2120
  return { script, parsed, target, kfNode: kfPropNode.value };
@@ -2096,8 +2138,12 @@ function convertArrayKeyframesToObject(script, target) {
2096
2138
  return `${JSON.stringify(`${timing.percentages[i]}%`)}: ${recordToCode(record)}`;
2097
2139
  });
2098
2140
  const ms = new MagicString(script);
2099
- ms.overwrite(kfPropNode.value.start, kfPropNode.value.end, `{ ${entries.join(", ")} }`);
2100
- if (timing.totalDuration !== void 0 && findPropertyNode2(target.call.varsArg, "duration") === void 0) {
2141
+ ms.overwrite(
2142
+ kfPropNode.value.start,
2143
+ kfPropNode.value.end,
2144
+ `{ ${entries.join(", ")}, easeEach: "none" }`
2145
+ );
2146
+ if (findPropertyNode2(target.call.varsArg, "duration") === void 0) {
2101
2147
  upsertProp(ms, target.call.varsArg, "duration", timing.totalDuration);
2102
2148
  }
2103
2149
  return ms.toString();
@@ -2107,7 +2153,7 @@ function ensureKeyframesNode(script, animationId) {
2107
2153
  if (direct) return direct;
2108
2154
  const parsed = parseGsapScriptAcornForWrite(script);
2109
2155
  const target = parsed?.located.find((l) => l.id === animationId);
2110
- if (!target) return null;
2156
+ if (!target || target.animation.hasUnresolvedKeyframes) return null;
2111
2157
  const kfProp = findPropertyNode2(target.call.varsArg, "keyframes");
2112
2158
  if (kfProp?.value?.type === "ArrayExpression") {
2113
2159
  const normalized = convertArrayKeyframesToObject(script, target);
@@ -2209,7 +2255,7 @@ function removeKeyframeFromScript(script, animationId, percentage) {
2209
2255
  const parsed = parseGsapScriptAcornForWrite(script);
2210
2256
  if (!parsed) return script;
2211
2257
  const target = parsed.located.find((l) => l.id === animationId);
2212
- if (!target) return script;
2258
+ if (!target || target.animation.hasUnresolvedKeyframes) return script;
2213
2259
  const kfPropNode = findPropertyNode2(target.call.varsArg, "keyframes");
2214
2260
  if (!kfPropNode) return script;
2215
2261
  if (kfPropNode.value?.type === "ArrayExpression") {
@@ -2348,7 +2394,7 @@ function convertToKeyframesFromScript(script, animationId, resolvedFromValues, s
2348
2394
  const target = parsed.located.find((l) => l.id === animationId);
2349
2395
  if (!target) return script;
2350
2396
  const { animation, call } = target;
2351
- if (animation.keyframes) return script;
2397
+ if (authorsKeyframes(animation)) return script;
2352
2398
  const isSet = call.method === "set";
2353
2399
  const { fromProps, toProps } = resolveConversionProps(animation, resolvedFromValues);
2354
2400
  const ms = new MagicString(script);
@@ -2564,6 +2610,7 @@ function splitIntoPropertyGroupsFromScript(script, animationId) {
2564
2610
  const target = parsed.located.find((l) => l.id === animationId);
2565
2611
  if (!target) return { script, ids: [animationId] };
2566
2612
  const { animation } = target;
2613
+ if (animation.hasUnresolvedKeyframes) return { script, ids: [animationId] };
2567
2614
  const allPropKeys = collectPropertyKeys(animation);
2568
2615
  const groupProps = partitionPropertyGroups(allPropKeys);
2569
2616
  if (groupProps.size <= 1) return { script, ids: [animationId] };
@@ -2884,8 +2931,8 @@ function computeForwardBaselines(matching, splitTime) {
2884
2931
  const pos = typeof anim.position === "number" ? anim.position : 0;
2885
2932
  const dur = anim.duration ?? 0;
2886
2933
  const animEnd = pos + dur;
2887
- if (anim.keyframes) {
2888
- const kfs = anim.keyframes.keyframes;
2934
+ if (authorsKeyframes(anim)) {
2935
+ const kfs = anim.keyframes?.keyframes ?? [];
2889
2936
  if (pos >= splitTime) {
2890
2937
  } else if (animEnd > splitTime) {
2891
2938
  for (const kf of kfs) {
@@ -2946,17 +2993,22 @@ function buildSpanningSplit(result, anim, pos, dur, fromSource, ctx) {
2946
2993
  extras: anim.extras
2947
2994
  }).script;
2948
2995
  }
2996
+ function applyKeyframedTweenSplit(result, anim, pos, animEnd, ctx, skippedSelectors) {
2997
+ if (pos >= ctx.splitTime)
2998
+ return updateAnimationSelectorInScript(result, anim.id, ctx.newSelector);
2999
+ if (animEnd > ctx.splitTime) {
3000
+ skippedSelectors.push(`${ctx.originalSelector} (keyframes spanning split)`);
3001
+ } else if (anim.hasUnresolvedKeyframes) {
3002
+ skippedSelectors.push(`${ctx.originalSelector} (unreadable keyframes before split)`);
3003
+ }
3004
+ return result;
3005
+ }
2949
3006
  function applyTweenSplit(result, anim, baselineBefore, ctx, skippedSelectors) {
2950
3007
  const pos = typeof anim.position === "number" ? anim.position : 0;
2951
3008
  const dur = anim.duration ?? 0;
2952
3009
  const animEnd = pos + dur;
2953
- if (anim.keyframes) {
2954
- if (pos >= ctx.splitTime)
2955
- return updateAnimationSelectorInScript(result, anim.id, ctx.newSelector);
2956
- if (animEnd > ctx.splitTime) {
2957
- skippedSelectors.push(`${ctx.originalSelector} (keyframes spanning split)`);
2958
- }
2959
- return result;
3010
+ if (authorsKeyframes(anim)) {
3011
+ return applyKeyframedTweenSplit(result, anim, pos, animEnd, ctx, skippedSelectors);
2960
3012
  }
2961
3013
  if (animEnd <= ctx.splitTime) return result;
2962
3014
  if (pos >= ctx.splitTime)
@@ -3185,6 +3237,7 @@ export {
3185
3237
  removePropertyFromAnimation,
3186
3238
  replaceTweenWithKeyframesInScript,
3187
3239
  resizeKeyframedTweenInScript,
3240
+ retimeClipTweensInScript,
3188
3241
  scalePositionsInScript,
3189
3242
  setArcPathInScript,
3190
3243
  shiftPositionsInScript,