@hyperframes/core 0.6.121 → 0.7.0

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.
Files changed (40) hide show
  1. package/dist/generated/runtime-inline.js +1 -1
  2. package/dist/generated/runtime-inline.js.map +1 -1
  3. package/dist/hyperframe.manifest.json +1 -1
  4. package/dist/hyperframe.runtime.iife.js +21 -21
  5. package/dist/hyperframe.runtime.mjs +21 -21
  6. package/dist/lint/rules/gsap.d.ts.map +1 -1
  7. package/dist/lint/rules/gsap.js +39 -34
  8. package/dist/lint/rules/gsap.js.map +1 -1
  9. package/dist/parsers/gsapConstants.d.ts.map +1 -1
  10. package/dist/parsers/gsapConstants.js +4 -1
  11. package/dist/parsers/gsapConstants.js.map +1 -1
  12. package/dist/parsers/gsapParser.d.ts +54 -0
  13. package/dist/parsers/gsapParser.d.ts.map +1 -1
  14. package/dist/parsers/gsapParser.js +348 -4
  15. package/dist/parsers/gsapParser.js.map +1 -1
  16. package/dist/parsers/gsapParserExports.d.ts +1 -0
  17. package/dist/parsers/gsapParserExports.d.ts.map +1 -1
  18. package/dist/parsers/gsapParserExports.js +4 -0
  19. package/dist/parsers/gsapParserExports.js.map +1 -1
  20. package/dist/parsers/gsapWriterAcorn.d.ts.map +1 -1
  21. package/dist/parsers/gsapWriterAcorn.js +107 -3
  22. package/dist/parsers/gsapWriterAcorn.js.map +1 -1
  23. package/dist/runtime/clipTree.d.ts +9 -0
  24. package/dist/runtime/clipTree.d.ts.map +1 -1
  25. package/dist/runtime/clipTree.js +12 -1
  26. package/dist/runtime/clipTree.js.map +1 -1
  27. package/dist/studio-api/helpers/sourceMutation.d.ts +4 -1
  28. package/dist/studio-api/helpers/sourceMutation.d.ts.map +1 -1
  29. package/dist/studio-api/helpers/sourceMutation.js +19 -3
  30. package/dist/studio-api/helpers/sourceMutation.js.map +1 -1
  31. package/dist/studio-api/helpers/subComposition.d.ts.map +1 -1
  32. package/dist/studio-api/helpers/subComposition.js +144 -5
  33. package/dist/studio-api/helpers/subComposition.js.map +1 -1
  34. package/dist/studio-api/routes/files.d.ts.map +1 -1
  35. package/dist/studio-api/routes/files.js +141 -16
  36. package/dist/studio-api/routes/files.js.map +1 -1
  37. package/dist/studio-api/routes/preview.d.ts.map +1 -1
  38. package/dist/studio-api/routes/preview.js +39 -3
  39. package/dist/studio-api/routes/preview.js.map +1 -1
  40. package/package.json +1 -1
@@ -1350,6 +1350,71 @@ function insertInheritedStateSet(script, selector, position, properties) {
1350
1350
  }
1351
1351
  return recast.print(parsed.ast).code;
1352
1352
  }
1353
+ /** Marker on Studio-emitted pre-keyframe hold `set`s. `data` is a GSAP-reserved
1354
+ * config key (attached to the tween, never applied to the target), so it carries
1355
+ * the tag without triggering GSAP's "Invalid property" warning. */
1356
+ const STUDIO_HOLD_MARKER = "hf-hold";
1357
+ /** True for a `tl.set(...)` this module emitted to hold a keyframe before its tween.
1358
+ * The Studio filters these out so they never appear as user keyframes/diamonds. */
1359
+ export function isStudioHoldSet(anim) {
1360
+ return anim.method === "set" && anim.properties?.data === STUDIO_HOLD_MARKER;
1361
+ }
1362
+ /**
1363
+ * Keep a `tl.set(selector, {x,y}, 0)` "hold" in front of every position-keyframed
1364
+ * tween that starts after t=0, so the element holds its first keyframe's position
1365
+ * BEFORE the tween plays instead of snapping to its CSS base (the universal NLE
1366
+ * "hold before first keyframe" behavior). The set is tagged with `data: "hf-hold"`
1367
+ * so this pass owns it: every call wipes the prior holds and recomputes from the
1368
+ * current keyframes, keeping them in sync as keyframes are added/moved/deleted.
1369
+ *
1370
+ * Idempotent. Only position props (x/y/xPercent/yPercent) are held — opacity/scale
1371
+ * keep their authored pre-tween behavior. A tween already starting at 0 needs no
1372
+ * hold (no gap before it).
1373
+ */
1374
+ export function syncPositionHoldsBeforeKeyframes(script) {
1375
+ let parsed;
1376
+ try {
1377
+ parsed = parseGsapScript(script);
1378
+ }
1379
+ catch {
1380
+ return script;
1381
+ }
1382
+ // 1. Drop every hold this pass previously emitted, so we recompute fresh.
1383
+ let result = script;
1384
+ const staleHoldIds = parsed.animations.filter(isStudioHoldSet).map((a) => a.id);
1385
+ for (const id of staleHoldIds)
1386
+ result = removeAnimationFromScript(result, id);
1387
+ // 2. Re-add a hold for each position-keyframed tween that starts after t=0.
1388
+ let reparsed;
1389
+ try {
1390
+ reparsed = parseGsapScript(result);
1391
+ }
1392
+ catch {
1393
+ return result;
1394
+ }
1395
+ for (const anim of reparsed.animations) {
1396
+ if (!anim.keyframes)
1397
+ continue;
1398
+ const start = anim.resolvedStart ?? (typeof anim.position === "number" ? anim.position : 0);
1399
+ if (!(start > 0.001))
1400
+ continue;
1401
+ const firstKf = [...anim.keyframes.keyframes].sort((a, b) => a.percentage - b.percentage)[0];
1402
+ if (!firstKf)
1403
+ continue;
1404
+ const posProps = {};
1405
+ for (const [k, v] of Object.entries(firstKf.properties)) {
1406
+ if (classifyPropertyGroup(k) === "position" && typeof v === "number")
1407
+ posProps[k] = v;
1408
+ }
1409
+ if (Object.keys(posProps).length === 0)
1410
+ continue;
1411
+ result = insertInheritedStateSet(result, anim.targetSelector, 0, {
1412
+ ...posProps,
1413
+ data: STUDIO_HOLD_MARKER,
1414
+ });
1415
+ }
1416
+ return result;
1417
+ }
1353
1418
  // fallow-ignore-next-line complexity
1354
1419
  export function splitAnimationsInScript(script, opts) {
1355
1420
  const parsed = parseGsapScript(script);
@@ -1404,9 +1469,27 @@ export function splitAnimationsInScript(script, opts) {
1404
1469
  }
1405
1470
  continue;
1406
1471
  }
1472
+ // `<=` (not `<`) is deliberate: a tween whose end coincides exactly with
1473
+ // the split boundary has fully played by splitTime, so it belongs to the
1474
+ // first half and contributes its resting state to the clone. The spanning
1475
+ // branch below handles only strictly-mid-flight tweens (pos < split < end).
1407
1476
  if (animEnd <= opts.splitTime) {
1408
- for (const [k, v] of Object.entries(anim.properties)) {
1409
- inheritedProps[k] = v;
1477
+ // Only a completed .from() reverts the element to its natural state, so
1478
+ // its recorded properties are the HIDDEN start (e.g. opacity:0), not the
1479
+ // resting state — clearing them keeps the clone at its natural value
1480
+ // instead of pinning it to the from-values (which made it invisible).
1481
+ // .fromTo() and .to() both END at their to-values (no revert), so they
1482
+ // fall through to `else` and inherit `anim.properties` (the to-values) —
1483
+ // .fromTo() must NOT join the .from() clear-branch or the clone would
1484
+ // drop the very state the fromTo just established.
1485
+ if (anim.method === "from") {
1486
+ for (const k of Object.keys(anim.properties))
1487
+ delete inheritedProps[k];
1488
+ }
1489
+ else {
1490
+ for (const [k, v] of Object.entries(anim.properties)) {
1491
+ inheritedProps[k] = v;
1492
+ }
1410
1493
  }
1411
1494
  continue;
1412
1495
  }
@@ -1523,20 +1606,81 @@ function locateAnimation(script, animationId) {
1523
1606
  const target = parsed.located.find((l) => l.id === animationId);
1524
1607
  return target ? { parsed, target } : null;
1525
1608
  }
1609
+ // Animation ids encode the tween's timeline position in ms
1610
+ // (`#puck-a-to-1200-position`). A gesture/convert can re-emit a tween at a
1611
+ // different position, changing its id — so a client that cached the old id (its
1612
+ // selectedGsapAnimations hasn't refreshed) edits a now-nonexistent id and the op
1613
+ // no-ops. Parse `{selector}-{method}-{posMs}-{group}` so we can fall back to the
1614
+ // same selector+method+group tween nearest the requested position.
1615
+ const ANIM_ID_RE = /^(.*)-(fromTo|from|to|set)-(\d+)-([a-z]+)$/;
1526
1616
  function locateAnimationWithFallback(script, animationId) {
1527
1617
  const loc = locateAnimation(script, animationId);
1528
1618
  if (loc)
1529
1619
  return loc;
1530
1620
  const convertedId = animationId.replace(/-from-|-fromTo-/, "-to-");
1531
- if (convertedId === animationId)
1621
+ if (convertedId !== animationId) {
1622
+ const converted = locateAnimation(script, convertedId);
1623
+ if (converted)
1624
+ return converted;
1625
+ }
1626
+ // Position-drift fallback: match by stable identity (selector+method+group),
1627
+ // disambiguating by the position closest to the one the caller asked for.
1628
+ const want = ANIM_ID_RE.exec(animationId);
1629
+ if (!want)
1532
1630
  return null;
1533
- return locateAnimation(script, convertedId);
1631
+ const [, sel, method, wantPosStr, group] = want;
1632
+ const wantPos = Number(wantPosStr);
1633
+ let parsed;
1634
+ try {
1635
+ parsed = parseGsapAst(script);
1636
+ }
1637
+ catch {
1638
+ return null;
1639
+ }
1640
+ let best = null;
1641
+ let bestDist = Number.POSITIVE_INFINITY;
1642
+ for (const l of parsed.located) {
1643
+ const m = ANIM_ID_RE.exec(l.id);
1644
+ if (!m || m[1] !== sel || m[2] !== method || m[4] !== group)
1645
+ continue;
1646
+ const dist = Math.abs(Number(m[3]) - wantPos);
1647
+ if (dist < bestDist) {
1648
+ best = l;
1649
+ bestDist = dist;
1650
+ }
1651
+ }
1652
+ return best ? { parsed, target: best } : null;
1534
1653
  }
1535
1654
  /** Find the keyframes ObjectExpression node on a tween's varsArg, or null. */
1536
1655
  function findKeyframesObjectNode(varsArg) {
1537
1656
  const node = findPropertyNode(varsArg, "keyframes");
1538
1657
  return node?.type === "ObjectExpression" ? node : null;
1539
1658
  }
1659
+ /**
1660
+ * Convert array-form keyframes (`keyframes: [{x,y}, …]`) to even-percentage object
1661
+ * form (`{ "0%": {…}, "33.3%": {…}, … }`) IN PLACE, returning the new object node
1662
+ * (or null if not array-form). GSAP distributes an array evenly, so this is
1663
+ * runtime-identical — but it gives the percentage-keyed write ops something to
1664
+ * target. Needed before INSERTING a keyframe at an arbitrary percentage, which an
1665
+ * even array can't host.
1666
+ */
1667
+ function convertArrayKeyframesToObjectNode(varsArg) {
1668
+ if (varsArg?.type !== "ObjectExpression")
1669
+ return null;
1670
+ const prop = (varsArg.properties ?? []).find((p) => isObjectProperty(p) && propKeyName(p) === "keyframes");
1671
+ if (!prop || prop.value?.type !== "ArrayExpression")
1672
+ return null;
1673
+ const els = (prop.value.elements ?? []).filter((e) => !!e && e.type === "ObjectExpression");
1674
+ const n = els.length;
1675
+ if (n === 0)
1676
+ return null;
1677
+ const entries = els.map((el, i) => {
1678
+ const pct = n > 1 ? Math.round((i / (n - 1)) * 1000) / 10 : 0;
1679
+ return `${JSON.stringify(`${pct}%`)}: ${recast.print(el).code}`;
1680
+ });
1681
+ prop.value = parseExpr(`{ ${entries.join(", ")} }`);
1682
+ return prop.value;
1683
+ }
1540
1684
  /** Filter percentage-keyed properties from a keyframes ObjectExpression. */
1541
1685
  function filterPercentageProps(kfNode) {
1542
1686
  return kfNode.properties.filter((p) => {
@@ -1584,6 +1728,11 @@ export function addKeyframeToScript(script, animationId, percentage, properties,
1584
1728
  if (!loc)
1585
1729
  return script;
1586
1730
  let kfNode = findKeyframesObjectNode(loc.target.call.varsArg);
1731
+ // Array-form keyframes can't host an arbitrary new percentage — normalize to
1732
+ // object form in place first. (convertToKeyframesInScript below only converts
1733
+ // FLAT tweens; it early-returns when keyframes already exist.)
1734
+ if (!kfNode)
1735
+ kfNode = convertArrayKeyframesToObjectNode(loc.target.call.varsArg);
1587
1736
  if (!kfNode) {
1588
1737
  script = convertToKeyframesInScript(script, animationId);
1589
1738
  loc = locateAnimationWithFallback(script, animationId);
@@ -1687,6 +1836,43 @@ export function addKeyframeToScript(script, animationId, percentage, properties,
1687
1836
  * remaining keyframe's properties.
1688
1837
  */
1689
1838
  export function removeKeyframeFromScript(script, animationId, percentage) {
1839
+ // Array-form keyframes (`keyframes: [{x,y}, …]`) have no explicit percentages —
1840
+ // GSAP distributes them evenly. The object-form path below can't see them
1841
+ // (findKeyframesObjectNode only matches ObjectExpression), so removing from an
1842
+ // array-form tween silently no-op'd. Resolve the element by its implicit
1843
+ // percentage and splice it; collapse to a flat tween when fewer than two remain.
1844
+ const arrLoc = locateAnimationWithFallback(script, animationId);
1845
+ // findPropertyNode here returns the property's VALUE node directly.
1846
+ const arrVal = arrLoc && findPropertyNode(arrLoc.target.call.varsArg, "keyframes");
1847
+ if (arrLoc && arrVal?.type === "ArrayExpression") {
1848
+ const elements = (arrVal.elements ?? []).filter((e) => !!e && e.type === "ObjectExpression");
1849
+ const n = elements.length;
1850
+ if (n === 0)
1851
+ return script;
1852
+ let matchIdx = -1;
1853
+ let bestDist = Number.POSITIVE_INFINITY;
1854
+ for (let i = 0; i < n; i++) {
1855
+ const pct = n > 1 ? (i / (n - 1)) * 100 : 0;
1856
+ const dist = Math.abs(pct - percentage);
1857
+ if (dist <= PCT_TOLERANCE && dist < bestDist) {
1858
+ matchIdx = i;
1859
+ bestDist = dist;
1860
+ }
1861
+ }
1862
+ if (matchIdx === -1)
1863
+ return script;
1864
+ const remaining = elements.filter((_, i) => i !== matchIdx);
1865
+ if (remaining.length < 2) {
1866
+ const sole = remaining[0];
1867
+ const record = sole ? objectExpressionToRecord(sole, arrLoc.parsed.scope) : {};
1868
+ collapseKeyframesToFlat(arrLoc.target.call.varsArg, record);
1869
+ }
1870
+ else {
1871
+ const realIdx = arrVal.elements.indexOf(elements[matchIdx]);
1872
+ arrVal.elements.splice(realIdx, 1);
1873
+ }
1874
+ return recast.print(arrLoc.parsed.ast).code;
1875
+ }
1690
1876
  const ctx = locateKeyframeCtx(script, animationId, percentage);
1691
1877
  if (!ctx)
1692
1878
  return script;
@@ -1709,6 +1895,35 @@ export function removeKeyframeFromScript(script, animationId, percentage) {
1709
1895
  * Replace the properties (and optionally ease) at an existing keyframe percentage.
1710
1896
  */
1711
1897
  export function updateKeyframeInScript(script, animationId, percentage, properties, ease) {
1898
+ // Array-form keyframes (`keyframes: [{x,y}, …]`) have no explicit percentages —
1899
+ // GSAP distributes them evenly. The percentage-keyed object path below can't
1900
+ // match them (findKeyframesObjectNode only matches ObjectExpression), so dragging
1901
+ // a motion-path node on an array-authored tween silently no-op'd. Resolve the
1902
+ // element by its implicit percentage and replace it in place. Mirrors the array
1903
+ // branch in removeKeyframeFromScript.
1904
+ const arrLoc = locateAnimationWithFallback(script, animationId);
1905
+ const arrVal = arrLoc && findPropertyNode(arrLoc.target.call.varsArg, "keyframes");
1906
+ if (arrLoc && arrVal?.type === "ArrayExpression") {
1907
+ const elements = (arrVal.elements ?? []).filter((e) => !!e && e.type === "ObjectExpression");
1908
+ const n = elements.length;
1909
+ if (n === 0)
1910
+ return script;
1911
+ let matchIdx = -1;
1912
+ let bestDist = Number.POSITIVE_INFINITY;
1913
+ for (let i = 0; i < n; i++) {
1914
+ const pct = n > 1 ? (i / (n - 1)) * 100 : 0;
1915
+ const dist = Math.abs(pct - percentage);
1916
+ if (dist <= PCT_TOLERANCE && dist < bestDist) {
1917
+ matchIdx = i;
1918
+ bestDist = dist;
1919
+ }
1920
+ }
1921
+ if (matchIdx === -1)
1922
+ return script;
1923
+ const realIdx = arrVal.elements.indexOf(elements[matchIdx]);
1924
+ arrVal.elements[realIdx] = buildKeyframeValueNode(properties, ease);
1925
+ return recast.print(arrLoc.parsed.ast).code;
1926
+ }
1712
1927
  const ctx = locateKeyframeCtx(script, animationId, percentage);
1713
1928
  if (!ctx)
1714
1929
  return script;
@@ -1991,6 +2206,135 @@ export function updateArcSegmentInScript(script, animationId, segmentIndex, upda
1991
2206
  }
1992
2207
  return recast.print(loc.parsed.ast).code;
1993
2208
  }
2209
+ /**
2210
+ * Move a single motionPath waypoint (anchor) to a new position. The waypoint
2211
+ * list is normalized to anchors for both straight and cubic paths, so
2212
+ * `pointIndex` matches the node order the studio overlay renders; cubic control
2213
+ * points are preserved. No-op when the animation/arc is missing or the index is
2214
+ * out of range.
2215
+ */
2216
+ export function updateMotionPathPointInScript(script, animationId, pointIndex, point) {
2217
+ const loc = locateAnimation(script, animationId);
2218
+ if (!loc)
2219
+ return script;
2220
+ const anim = loc.target.animation;
2221
+ if (!anim.arcPath?.enabled)
2222
+ return script;
2223
+ const waypoints = extractArcWaypoints(anim);
2224
+ if (pointIndex < 0 || pointIndex >= waypoints.length || waypoints.length < 2)
2225
+ return script;
2226
+ const nextWaypoints = waypoints.map((wp, i) => i === pointIndex ? { x: point.x, y: point.y } : wp);
2227
+ const motionPathCode = buildMotionPathObjectCode({
2228
+ waypoints: nextWaypoints,
2229
+ segments: anim.arcPath.segments,
2230
+ autoRotate: anim.arcPath.autoRotate,
2231
+ });
2232
+ const varsArg = loc.target.call.varsArg;
2233
+ const existingProp = varsArg.properties.find((p) => isObjectProperty(p) && propKeyName(p) === "motionPath");
2234
+ if (existingProp) {
2235
+ existingProp.value = parseExpr(motionPathCode);
2236
+ }
2237
+ return recast.print(loc.parsed.ast).code;
2238
+ }
2239
+ /** True when any segment carries explicit cubic control points. Add/remove are
2240
+ * restricted to curviness (non-cubic) paths — synthesizing control points for
2241
+ * an inserted cubic anchor is out of scope. */
2242
+ function hasCubicSegments(segments) {
2243
+ return segments.some((s) => s.cp1 != null || s.cp2 != null);
2244
+ }
2245
+ function writeMotionPathValue(loc, waypoints, segments, autoRotate) {
2246
+ const motionPathCode = buildMotionPathObjectCode({ waypoints, segments, autoRotate });
2247
+ const varsArg = loc.target.call.varsArg;
2248
+ const existingProp = varsArg.properties.find((p) => isObjectProperty(p) && propKeyName(p) === "motionPath");
2249
+ if (existingProp)
2250
+ existingProp.value = parseExpr(motionPathCode);
2251
+ return recast.print(loc.parsed.ast).code;
2252
+ }
2253
+ /**
2254
+ * Insert a waypoint at `index` (between existing anchors), splitting the segment
2255
+ * it lands on so the new neighbor inherits its curviness. Non-cubic paths only.
2256
+ * No-op for missing animation/arc, out-of-range index, or cubic paths.
2257
+ */
2258
+ export function addMotionPathPointInScript(script, animationId, index, point) {
2259
+ const loc = locateAnimation(script, animationId);
2260
+ if (!loc)
2261
+ return script;
2262
+ const anim = loc.target.animation;
2263
+ if (!anim.arcPath?.enabled || hasCubicSegments(anim.arcPath.segments))
2264
+ return script;
2265
+ const waypoints = extractArcWaypoints(anim);
2266
+ // Insert strictly between two anchors: index 1..length-1.
2267
+ if (index < 1 || index > waypoints.length - 1)
2268
+ return script;
2269
+ const segments = [...anim.arcPath.segments];
2270
+ waypoints.splice(index, 0, { x: point.x, y: point.y });
2271
+ const splitCurviness = segments[index - 1]?.curviness ?? 1;
2272
+ segments.splice(index - 1, 0, { curviness: splitCurviness });
2273
+ return writeMotionPathValue(loc, waypoints, segments, anim.arcPath.autoRotate);
2274
+ }
2275
+ /**
2276
+ * Remove the waypoint at `index`. Refuses to drop below two anchors (a path
2277
+ * can't have fewer). Non-cubic paths only. No-op for missing animation/arc,
2278
+ * out-of-range index, cubic paths, or a 2-point path.
2279
+ */
2280
+ export function removeMotionPathPointInScript(script, animationId, index) {
2281
+ const loc = locateAnimation(script, animationId);
2282
+ if (!loc)
2283
+ return script;
2284
+ const anim = loc.target.animation;
2285
+ if (!anim.arcPath?.enabled || hasCubicSegments(anim.arcPath.segments))
2286
+ return script;
2287
+ const waypoints = extractArcWaypoints(anim);
2288
+ if (waypoints.length <= 2 || index < 0 || index >= waypoints.length)
2289
+ return script;
2290
+ const segments = [...anim.arcPath.segments];
2291
+ waypoints.splice(index, 1);
2292
+ // Drop the segment on the side that still exists (last anchor → preceding segment).
2293
+ segments.splice(Math.min(index, segments.length - 1), 1);
2294
+ return writeMotionPathValue(loc, waypoints, segments, anim.arcPath.autoRotate);
2295
+ }
2296
+ /**
2297
+ * Author a fresh 2-anchor motionPath tween on a target element: a straight line
2298
+ * from the element's home (0,0) to `point`, gentle ease, ready for waypoint
2299
+ * editing. Mirrors `addAnimationWithKeyframesToScript`.
2300
+ */
2301
+ export function addMotionPathToScript(script, targetSelector, position, duration, point, ease = "power1.inOut") {
2302
+ // `id: null` on the failure paths is a deliberate sentinel: callers must
2303
+ // null-check before chaining (e.g. locating the new tween). An empty string
2304
+ // would silently flow into selector/locate calls and match nothing.
2305
+ let parsed;
2306
+ try {
2307
+ parsed = parseGsapAst(script);
2308
+ }
2309
+ catch (e) {
2310
+ console.warn("[gsap-parser] addMotionPathToScript parse failed:", e);
2311
+ return { script, id: null };
2312
+ }
2313
+ if (parsed.located.length === 0 && parsed.detection.timelineVar === null) {
2314
+ return { script, id: null };
2315
+ }
2316
+ const motionPathCode = buildMotionPathObjectCode({
2317
+ waypoints: [
2318
+ { x: 0, y: 0 },
2319
+ { x: point.x, y: point.y },
2320
+ ],
2321
+ segments: [{ curviness: 1 }],
2322
+ autoRotate: false,
2323
+ });
2324
+ const selector = JSON.stringify(targetSelector);
2325
+ const varEntries = [
2326
+ `motionPath: ${motionPathCode}`,
2327
+ `duration: ${valueToCode(duration)}`,
2328
+ `ease: ${JSON.stringify(ease)}`,
2329
+ ];
2330
+ const stmtCode = `${parsed.timelineVar}.to(${selector}, { ${varEntries.join(", ")} }, ${valueToCode(position)});`;
2331
+ const newStatement = parseScript(stmtCode).program.body[0];
2332
+ insertAfterAnchor(parsed, newStatement);
2333
+ const result = recast.print(parsed.ast).code;
2334
+ const reParsed = parseGsapAst(result);
2335
+ const newId = reParsed.located[reParsed.located.length - 1]?.id ?? null;
2336
+ return { script: result, id: newId };
2337
+ }
1994
2338
  export function removeArcPathFromScript(script, animationId) {
1995
2339
  return setArcPathInScript(script, animationId, {
1996
2340
  enabled: false,