hyperframes 0.7.18 → 0.7.20

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/cli.js CHANGED
@@ -50,7 +50,7 @@ var VERSION;
50
50
  var init_version = __esm({
51
51
  "src/version.ts"() {
52
52
  "use strict";
53
- VERSION = true ? "0.7.18" : "0.0.0-dev";
53
+ VERSION = true ? "0.7.20" : "0.0.0-dev";
54
54
  }
55
55
  });
56
56
 
@@ -53193,20 +53193,23 @@ function findEnclosingExpressionStatement(ancestors) {
53193
53193
  }
53194
53194
  return null;
53195
53195
  }
53196
- function removeAnimationFromScript(script, animationId) {
53197
- const parsed = parseGsapScriptAcornForWrite(script);
53198
- if (!parsed) return script;
53199
- const target = parsed.located.find((l) => l.id === animationId);
53200
- if (!target) return script;
53201
- const ms = new MagicString(script);
53202
- const N2 = target.call.node;
53203
- const exprStmt = findEnclosingExpressionStatement(target.call.ancestors);
53196
+ function removeCallFromMagicString(ms, call, script) {
53197
+ const N2 = call.node;
53198
+ const exprStmt = findEnclosingExpressionStatement(call.ancestors);
53204
53199
  if (N2.callee?.object?.type !== "CallExpression" && exprStmt?.expression === N2) {
53205
53200
  const end = exprStmt.end < script.length && script[exprStmt.end] === "\n" ? exprStmt.end + 1 : exprStmt.end;
53206
53201
  ms.remove(exprStmt.start, end);
53207
53202
  } else {
53208
53203
  ms.remove(N2.callee.object.end, N2.end);
53209
53204
  }
53205
+ }
53206
+ function removeAnimationFromScript(script, animationId) {
53207
+ const parsed = parseGsapScriptAcornForWrite(script);
53208
+ if (!parsed) return script;
53209
+ const target = parsed.located.find((l) => l.id === animationId);
53210
+ if (!target) return script;
53211
+ const ms = new MagicString(script);
53212
+ removeCallFromMagicString(ms, target.call, script);
53210
53213
  return ms.toString();
53211
53214
  }
53212
53215
  function getElementType(el) {
@@ -65564,13 +65567,15 @@ function findRootTag(source) {
65564
65567
  function readAttr(tagSource, attr2) {
65565
65568
  if (!tagSource) return null;
65566
65569
  const escaped = attr2.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
65567
- const match = tagSource.match(new RegExp(`\\b${escaped}\\s*=\\s*["']([^"']+)["']`, "i"));
65570
+ const match = tagSource.match(new RegExp(`(?<![\\w-])${escaped}\\s*=\\s*["']([^"']+)["']`, "i"));
65568
65571
  return match?.[1] || null;
65569
65572
  }
65570
65573
  function readJsonAttr(tagSource, attr2) {
65571
65574
  if (!tagSource) return null;
65572
65575
  const escaped = attr2.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
65573
- const match = tagSource.match(new RegExp(`\\b${escaped}\\s*=\\s*(?:"([^"]*)"|'([^']*)')`, "i"));
65576
+ const match = tagSource.match(
65577
+ new RegExp(`(?<![\\w-])${escaped}\\s*=\\s*(?:"([^"]*)"|'([^']*)')`, "i")
65578
+ );
65574
65579
  if (!match) return null;
65575
65580
  return match[1] ?? match[2] ?? null;
65576
65581
  }
@@ -79532,26 +79537,30 @@ function studioSelectionUrl(server) {
79532
79537
  return studioApiUrl(server, "selection");
79533
79538
  }
79534
79539
  function studioApiUrl(server, route) {
79535
- return `http://127.0.0.1:${server.port}/api/projects/${encodeURIComponent(server.projectName)}/${route}`;
79540
+ const host = server.host ?? "127.0.0.1";
79541
+ return `http://${host}:${server.port}/api/projects/${encodeURIComponent(server.projectName)}/${route}`;
79536
79542
  }
79537
79543
  async function findViteStudioServerForProject(normalizedProjectDir, fetchImpl, ports = VITE_STUDIO_DISCOVERY_PORTS) {
79538
79544
  for (const port of ports) {
79539
- try {
79540
- const response = await fetchImpl(`http://127.0.0.1:${port}/api/projects`);
79541
- if (!response.ok) continue;
79542
- const payload = await response.json();
79543
- const project = payload.projects?.find(
79544
- (candidate) => normalizePath(candidate.dir) === normalizedProjectDir
79545
- );
79546
- if (!project) continue;
79547
- return {
79548
- port,
79549
- projectName: project.id,
79550
- projectDir: project.dir,
79551
- version: "studio-dev",
79552
- pid: null
79553
- };
79554
- } catch {
79545
+ for (const host of LOOPBACK_HOSTS) {
79546
+ try {
79547
+ const response = await fetchImpl(`http://${host}:${port}/api/projects`);
79548
+ if (!response.ok) continue;
79549
+ const payload = await response.json();
79550
+ const project = payload.projects?.find(
79551
+ (candidate) => normalizePath(candidate.dir) === normalizedProjectDir
79552
+ );
79553
+ if (!project) continue;
79554
+ return {
79555
+ port,
79556
+ host,
79557
+ projectName: project.id,
79558
+ projectDir: project.dir,
79559
+ version: "studio-dev",
79560
+ pid: null
79561
+ };
79562
+ } catch {
79563
+ }
79555
79564
  }
79556
79565
  }
79557
79566
  return null;
@@ -79572,7 +79581,7 @@ async function fetchStudioLint(server, fetchImpl = fetch) {
79572
79581
  }
79573
79582
  return await response.json();
79574
79583
  }
79575
- var VITE_STUDIO_DISCOVERY_PORTS, AmbiguousPreviewServerError, PreviewServerPortMismatchError;
79584
+ var VITE_STUDIO_DISCOVERY_PORTS, AmbiguousPreviewServerError, PreviewServerPortMismatchError, LOOPBACK_HOSTS;
79576
79585
  var init_studioSelectionClient = __esm({
79577
79586
  "src/utils/studioSelectionClient.ts"() {
79578
79587
  "use strict";
@@ -79602,6 +79611,7 @@ var init_studioSelectionClient = __esm({
79602
79611
  this.ports = ports;
79603
79612
  }
79604
79613
  };
79614
+ LOOPBACK_HOSTS = ["127.0.0.1", "[::1]"];
79605
79615
  }
79606
79616
  });
79607
79617
 
@@ -81315,19 +81325,37 @@ function addAnimationToScript(script, animation) {
81315
81325
  const newId = reParsed?.located[reParsed.located.length - 1]?.id ?? "";
81316
81326
  return { script: result, id: newId };
81317
81327
  }
81328
+ function removeCallFromMagicString2(ms, call, script) {
81329
+ const N2 = call.node;
81330
+ const exprStmt = findEnclosingExpressionStatement2(call.ancestors);
81331
+ if (N2.callee?.object?.type !== "CallExpression" && exprStmt?.expression === N2) {
81332
+ const end = exprStmt.end < script.length && script[exprStmt.end] === "\n" ? exprStmt.end + 1 : exprStmt.end;
81333
+ ms.remove(exprStmt.start, end);
81334
+ } else {
81335
+ ms.remove(N2.callee.object.end, N2.end);
81336
+ }
81337
+ }
81318
81338
  function removeAnimationFromScript2(script, animationId) {
81319
81339
  const parsed = parseGsapScriptAcornForWrite3(script);
81320
81340
  if (!parsed) return script;
81321
81341
  const target = parsed.located.find((l) => l.id === animationId);
81322
81342
  if (!target) return script;
81323
81343
  const ms = new MagicString(script);
81324
- const N2 = target.call.node;
81325
- const exprStmt = findEnclosingExpressionStatement2(target.call.ancestors);
81326
- if (N2.callee?.object?.type !== "CallExpression" && exprStmt?.expression === N2) {
81327
- const end = exprStmt.end < script.length && script[exprStmt.end] === "\n" ? exprStmt.end + 1 : exprStmt.end;
81328
- ms.remove(exprStmt.start, end);
81329
- } else {
81330
- ms.remove(N2.callee.object.end, N2.end);
81344
+ removeCallFromMagicString2(ms, target.call, script);
81345
+ return ms.toString();
81346
+ }
81347
+ function dedupePositionWritesInScript(script, selector, keepId) {
81348
+ const parsed = parseGsapScriptAcornForWrite3(script);
81349
+ if (!parsed) return script;
81350
+ const posWrites = parsed.located.filter(
81351
+ (l) => l.animation.targetSelector === selector && l.animation.propertyGroup === "position"
81352
+ );
81353
+ if (posWrites.length <= 1) return script;
81354
+ const keeper = posWrites.find((l) => l.id === keepId) ?? posWrites[posWrites.length - 1];
81355
+ const ms = new MagicString(script);
81356
+ for (const l of posWrites) {
81357
+ if (l === keeper) continue;
81358
+ removeCallFromMagicString2(ms, l.call, script);
81331
81359
  }
81332
81360
  return ms.toString();
81333
81361
  }
@@ -81698,6 +81726,50 @@ function removeKeyframeFromScript(script, animationId, percentage) {
81698
81726
  removeProp(ms, match.prop, allProps);
81699
81727
  return ms.toString();
81700
81728
  }
81729
+ function moveKeyframeInScript(script, animationId, fromPercentage, toPercentage) {
81730
+ const located = locateWithKeyframes(script, animationId);
81731
+ if (!located) return script;
81732
+ const { kfNode } = located;
81733
+ const match = findKfPropByPct(kfNode, fromPercentage);
81734
+ if (!match) return script;
81735
+ if (Math.abs(fromPercentage - toPercentage) < MOVE_NOOP_EPSILON_PCT) return script;
81736
+ const dest = findKfPropByPct(kfNode, toPercentage);
81737
+ const collision = dest && dest.prop !== match.prop ? dest : null;
81738
+ const entries2 = [];
81739
+ for (const prop2 of percentagePropsOf(kfNode)) {
81740
+ if (prop2 === match.prop) continue;
81741
+ if (collision && prop2 === collision.prop) continue;
81742
+ const pct = percentageFromKey(propKeyName22(prop2) ?? "");
81743
+ if (Number.isNaN(pct)) continue;
81744
+ entries2.push({ pct, record: valueNodeToRecord(prop2.value, script) });
81745
+ }
81746
+ entries2.push({ pct: toPercentage, record: valueNodeToRecord(match.prop.value, script) });
81747
+ entries2.sort((a, b2) => a.pct - b2.pct);
81748
+ const body = entries2.map((e3) => `${JSON.stringify(`${e3.pct}%`)}: ${recordToCode(e3.record)}`).join(", ");
81749
+ const ms = new MagicString(script);
81750
+ ms.overwrite(kfNode.start, kfNode.end, `{ ${body} }`);
81751
+ return ms.toString();
81752
+ }
81753
+ function resizeKeyframedTweenInScript(script, animationId, newPosition, newDuration, pctRemap) {
81754
+ const located = locateWithKeyframes(script, animationId);
81755
+ if (!located) return script;
81756
+ const { target, kfNode } = located;
81757
+ const edits = [];
81758
+ const seen = /* @__PURE__ */ new Set();
81759
+ for (const { from, to } of pctRemap) {
81760
+ const match = findKfPropByPct(kfNode, from);
81761
+ if (!match || seen.has(match.prop.key)) continue;
81762
+ seen.add(match.prop.key);
81763
+ edits.push({ keyNode: match.prop.key, to });
81764
+ }
81765
+ const ms = new MagicString(script);
81766
+ for (const { keyNode, to } of edits) {
81767
+ ms.overwrite(keyNode.start, keyNode.end, JSON.stringify(`${to}%`));
81768
+ }
81769
+ overwritePosition(ms, target.call, newPosition);
81770
+ upsertProp(ms, target.call.varsArg, "duration", newDuration);
81771
+ return ms.toString();
81772
+ }
81701
81773
  function removeAllKeyframesFromScript(script, animationId) {
81702
81774
  const parsed = parseGsapScriptAcornForWrite3(script);
81703
81775
  if (!parsed) return script;
@@ -81714,6 +81786,14 @@ function removeAllKeyframesFromScript(script, animationId) {
81714
81786
  target.call,
81715
81787
  buildVarsObjectCode(buildCollapsedFlatVars(target.animation, collapse))
81716
81788
  );
81789
+ if (target.animation.propertyGroup === "position") {
81790
+ for (const l of parsed.located) {
81791
+ if (l === target) continue;
81792
+ if (l.animation.targetSelector === target.animation.targetSelector && l.animation.propertyGroup === "position") {
81793
+ removeCallFromMagicString2(ms, l.call, script);
81794
+ }
81795
+ }
81796
+ }
81717
81797
  return ms.toString();
81718
81798
  }
81719
81799
  function buildCollapsedFlatVars(animation, collapse) {
@@ -81721,11 +81801,11 @@ function buildCollapsedFlatVars(animation, collapse) {
81721
81801
  for (const [k2, v2] of Object.entries(collapse.properties)) {
81722
81802
  if (k2 !== "ease") flat[k2] = v2;
81723
81803
  }
81724
- if (animation.duration !== void 0) flat.duration = animation.duration;
81725
- if (animation.ease) flat.ease = animation.ease;
81726
81804
  for (const [k2, v2] of Object.entries(animation.extras ?? {})) {
81727
81805
  if (typeof v2 === "number" || typeof v2 === "string") flat[k2] = v2;
81728
81806
  }
81807
+ flat.duration = 0;
81808
+ flat.immediateRender = true;
81729
81809
  return flat;
81730
81810
  }
81731
81811
  function buildKeyframesVarsCode(animation, fromProps, toProps, varsNode, source, setDuration) {
@@ -82392,7 +82472,7 @@ function unrollDynamicAnimations(script, animationId, elements) {
82392
82472
  }
82393
82473
  return ms.toString();
82394
82474
  }
82395
- var PROPERTY_GROUPS4, PROP_TO_GROUP4, CSS_IDENTITY, GSAP_METHODS4, QUERY_METHODS3, ITERATION_METHODS3, SCOPE_NODE_TYPES3, BUILTIN_VAR_KEYS3, DROPPED_VAR_KEYS3, EXTRAS_KEYS3, PERCENTAGE_KEY_RE3, GSAP_DEFAULT_DURATION3, NON_EDITABLE_PROP_KEYS, CSS_IDENTITY2, NON_EDITABLE_VAR_KEYS, PERCENTAGE_KEY_RE22, PCT_TOLERANCE, LITERAL_NODE_TYPES, REFUSE_UNROLL;
82475
+ var PROPERTY_GROUPS4, PROP_TO_GROUP4, CSS_IDENTITY, GSAP_METHODS4, QUERY_METHODS3, ITERATION_METHODS3, SCOPE_NODE_TYPES3, BUILTIN_VAR_KEYS3, DROPPED_VAR_KEYS3, EXTRAS_KEYS3, PERCENTAGE_KEY_RE3, GSAP_DEFAULT_DURATION3, NON_EDITABLE_PROP_KEYS, CSS_IDENTITY2, NON_EDITABLE_VAR_KEYS, PERCENTAGE_KEY_RE22, PCT_TOLERANCE, MOVE_NOOP_EPSILON_PCT, LITERAL_NODE_TYPES, REFUSE_UNROLL;
82396
82476
  var init_gsapWriterAcorn = __esm({
82397
82477
  "../parsers/dist/gsapWriterAcorn.js"() {
82398
82478
  "use strict";
@@ -82481,6 +82561,7 @@ var init_gsapWriterAcorn = __esm({
82481
82561
  ]);
82482
82562
  PERCENTAGE_KEY_RE22 = /^(\d+(?:\.\d+)?)%$/;
82483
82563
  PCT_TOLERANCE = 2;
82564
+ MOVE_NOOP_EPSILON_PCT = 0.05;
82484
82565
  LITERAL_NODE_TYPES = /* @__PURE__ */ new Set(["Literal", "NumericLiteral", "StringLiteral"]);
82485
82566
  REFUSE_UNROLL = /* @__PURE__ */ Symbol("refuse-unroll");
82486
82567
  }
@@ -86657,18 +86738,21 @@ __export(gsapParser_exports, {
86657
86738
  classifyPropertyGroup: () => classifyPropertyGroup5,
86658
86739
  classifyTweenPropertyGroup: () => classifyTweenPropertyGroup4,
86659
86740
  convertToKeyframesInScript: () => convertToKeyframesInScript,
86741
+ dedupePositionWritesInScript: () => dedupePositionWritesInScript2,
86660
86742
  generateSpringEaseData: () => generateSpringEaseData,
86661
86743
  getAnimationsForElementId: () => getAnimationsForElementId2,
86662
86744
  gsapAnimationsToKeyframes: () => gsapAnimationsToKeyframes2,
86663
86745
  isStudioHoldSet: () => isStudioHoldSet,
86664
86746
  keyframesToGsapAnimations: () => keyframesToGsapAnimations2,
86665
86747
  materializeKeyframesInScript: () => materializeKeyframesInScript,
86748
+ moveKeyframeInScript: () => moveKeyframeInScript2,
86666
86749
  parseGsapScript: () => parseGsapScript,
86667
86750
  removeAllKeyframesFromScript: () => removeAllKeyframesFromScript2,
86668
86751
  removeAnimationFromScript: () => removeAnimationFromScript3,
86669
86752
  removeArcPathFromScript: () => removeArcPathFromScript2,
86670
86753
  removeKeyframeFromScript: () => removeKeyframeFromScript2,
86671
86754
  removeMotionPathPointInScript: () => removeMotionPathPointInScript,
86755
+ resizeKeyframedTweenInScript: () => resizeKeyframedTweenInScript2,
86672
86756
  scalePositionsInScript: () => scalePositionsInScript2,
86673
86757
  serializeGsapAnimations: () => serializeGsapAnimations2,
86674
86758
  setArcPathInScript: () => setArcPathInScript2,
@@ -88004,9 +88088,13 @@ function removeAnimationFromScript3(script, animationId) {
88004
88088
  target = parsed.located.find((l) => l.id === convertedId);
88005
88089
  }
88006
88090
  if (!target) return script;
88007
- const node = target.call.node;
88008
- const stmtPath = findStatementPath(target.call.path);
88009
- if (!stmtPath) return script;
88091
+ removeCallFromAst(target.call);
88092
+ return recast2.print(parsed.ast).code;
88093
+ }
88094
+ function removeCallFromAst(call) {
88095
+ const node = call.node;
88096
+ const stmtPath = findStatementPath(call.path);
88097
+ if (!stmtPath) return;
88010
88098
  const parentCall = findChainParentCall(stmtPath.node, node);
88011
88099
  if (parentCall) {
88012
88100
  parentCall.callee.object = node.callee.object;
@@ -88015,6 +88103,23 @@ function removeAnimationFromScript3(script, animationId) {
88015
88103
  } else {
88016
88104
  stmtPath.prune();
88017
88105
  }
88106
+ }
88107
+ function dedupePositionWritesInScript2(script, selector, keepId) {
88108
+ let parsed;
88109
+ try {
88110
+ parsed = parseGsapAst(script);
88111
+ } catch {
88112
+ return script;
88113
+ }
88114
+ const posWrites = parsed.located.filter(
88115
+ (l) => l.animation.targetSelector === selector && l.animation.propertyGroup === "position"
88116
+ );
88117
+ if (posWrites.length <= 1) return script;
88118
+ const keeper = posWrites.find((l) => l.id === keepId) ?? posWrites[posWrites.length - 1];
88119
+ for (const l of posWrites) {
88120
+ if (l === keeper) continue;
88121
+ removeCallFromAst(l.call);
88122
+ }
88018
88123
  return recast2.print(parsed.ast).code;
88019
88124
  }
88020
88125
  function insertInheritedStateSet(script, selector, position, properties) {
@@ -88428,6 +88533,49 @@ function removeKeyframeFromScript2(script, animationId, percentage) {
88428
88533
  }
88429
88534
  return recast2.print(loc.parsed.ast).code;
88430
88535
  }
88536
+ function moveKeyframeInScript2(script, animationId, fromPercentage, toPercentage) {
88537
+ const loc = locateAnimationWithFallback(script, animationId);
88538
+ if (!loc) return script;
88539
+ const kfNode = findKeyframesObjectNode(loc.target.call.varsArg);
88540
+ if (!kfNode) return script;
88541
+ const match = findKeyframePropByPct(kfNode, fromPercentage);
88542
+ if (!match) return script;
88543
+ if (Math.abs(fromPercentage - toPercentage) < MOVE_NOOP_EPSILON_PCT2) return script;
88544
+ const dest = findKeyframePropByPct(kfNode, toPercentage);
88545
+ const collision = dest && dest.prop !== match.prop ? dest : null;
88546
+ const movedValue = match.prop.value;
88547
+ const entries2 = [];
88548
+ for (const prop2 of filterPercentageProps(kfNode)) {
88549
+ if (prop2 === match.prop) continue;
88550
+ if (collision && prop2 === collision.prop) continue;
88551
+ const pct = percentageFromKey2(propKeyName4(prop2) ?? "");
88552
+ if (Number.isNaN(pct)) continue;
88553
+ entries2.push({ pct, value: prop2.value });
88554
+ }
88555
+ entries2.push({ pct: toPercentage, value: movedValue });
88556
+ entries2.sort((a, b2) => a.pct - b2.pct);
88557
+ kfNode.properties = entries2.map((e3) => {
88558
+ const p2 = parseExpr(`{ ${JSON.stringify(`${e3.pct}%`)}: {} }`).properties[0];
88559
+ p2.value = e3.value;
88560
+ return p2;
88561
+ });
88562
+ return recast2.print(loc.parsed.ast).code;
88563
+ }
88564
+ function resizeKeyframedTweenInScript2(script, animationId, newPosition, newDuration, pctRemap) {
88565
+ const loc = locateAnimationWithFallback(script, animationId);
88566
+ if (!loc) return script;
88567
+ const kfNode = findKeyframesObjectNode(loc.target.call.varsArg);
88568
+ if (!kfNode) return script;
88569
+ const seen = /* @__PURE__ */ new Set();
88570
+ for (const { from, to } of pctRemap) {
88571
+ const match = findKeyframePropByPct(kfNode, from);
88572
+ if (!match || seen.has(match.prop)) continue;
88573
+ seen.add(match.prop);
88574
+ match.prop.key = parseExpr(`{ ${JSON.stringify(`${to}%`)}: 0 }`).properties[0].key;
88575
+ }
88576
+ applyUpdatesToCall(loc.target.call, { position: newPosition, duration: newDuration });
88577
+ return recast2.print(loc.parsed.ast).code;
88578
+ }
88431
88579
  function updateKeyframeInScript2(script, animationId, percentage, properties, ease) {
88432
88580
  const arrLoc = locateAnimationWithFallback(script, animationId);
88433
88581
  const arrVal = arrLoc && findPropertyNode4(arrLoc.target.call.varsArg, "keyframes");
@@ -88550,6 +88698,17 @@ function removeAllKeyframesFromScript2(script, animationId) {
88550
88698
  const collapseEntry = method === "from" ? kfEntries[0] : kfEntries[kfEntries.length - 1];
88551
88699
  const record = objectExpressionToRecord4(collapseEntry.prop.value, loc.parsed.scope);
88552
88700
  collapseKeyframesToFlat2(loc.target.call.varsArg, record);
88701
+ removeVarsKey(loc.target.call.varsArg, "ease");
88702
+ setVarsKey(loc.target.call.varsArg, "duration", 0);
88703
+ setVarsKey(loc.target.call.varsArg, "immediateRender", true);
88704
+ if (loc.target.animation.propertyGroup === "position") {
88705
+ for (const l of loc.parsed.located) {
88706
+ if (l === loc.target) continue;
88707
+ if (l.animation.targetSelector === loc.target.animation.targetSelector && l.animation.propertyGroup === "position") {
88708
+ removeCallFromAst(l.call);
88709
+ }
88710
+ }
88711
+ }
88553
88712
  return recast2.print(loc.parsed.ast).code;
88554
88713
  }
88555
88714
  function materializeKeyframesInScript(script, animationId, keyframes, easeEach, resolvedSelector) {
@@ -88971,7 +89130,7 @@ function unrollDynamicAnimations2(script, animationId, elements) {
88971
89130
  }
88972
89131
  return script;
88973
89132
  }
88974
- var recast2, import_parser3, SUPPORTED_PROPS, PROPERTY_GROUPS5, PROP_TO_GROUP5, SUPPORTED_EASES, FORBIDDEN_GSAP_PATTERNS2, CSS_IDENTITY3, SPRING_PRESETS, GSAP_METHODS5, QUERY_METHODS4, ITERATION_METHODS4, SCOPE_NODE_TYPES4, BUILTIN_VAR_KEYS4, DROPPED_VAR_KEYS4, EXTRAS_KEYS4, PERCENTAGE_KEY_RE4, GSAP_DEFAULT_DURATION4, STUDIO_HOLD_MARKER, PCT_TOLERANCE2, ANIM_ID_RE;
89133
+ var recast2, import_parser3, SUPPORTED_PROPS, PROPERTY_GROUPS5, PROP_TO_GROUP5, SUPPORTED_EASES, FORBIDDEN_GSAP_PATTERNS2, CSS_IDENTITY3, SPRING_PRESETS, GSAP_METHODS5, QUERY_METHODS4, ITERATION_METHODS4, SCOPE_NODE_TYPES4, BUILTIN_VAR_KEYS4, DROPPED_VAR_KEYS4, EXTRAS_KEYS4, PERCENTAGE_KEY_RE4, GSAP_DEFAULT_DURATION4, STUDIO_HOLD_MARKER, PCT_TOLERANCE2, MOVE_NOOP_EPSILON_PCT2, ANIM_ID_RE;
88975
89134
  var init_gsapParser = __esm({
88976
89135
  "../parsers/dist/gsapParser.js"() {
88977
89136
  "use strict";
@@ -89119,6 +89278,7 @@ var init_gsapParser = __esm({
89119
89278
  GSAP_DEFAULT_DURATION4 = 0.5;
89120
89279
  STUDIO_HOLD_MARKER = "hf-hold";
89121
89280
  PCT_TOLERANCE2 = 2;
89281
+ MOVE_NOOP_EPSILON_PCT2 = 0.05;
89122
89282
  ANIM_ID_RE = /^(.*)-(fromTo|from|to|set)-(\d+)-([a-z]+)$/;
89123
89283
  }
89124
89284
  });
@@ -90276,6 +90436,14 @@ function executeGsapMutationAcorn(body, block, respond2) {
90276
90436
  }
90277
90437
  return script;
90278
90438
  }
90439
+ case "consolidate-position-writes": {
90440
+ if (!body.targetSelector) return block.scriptText;
90441
+ return dedupePositionWritesInScript(
90442
+ block.scriptText,
90443
+ body.targetSelector,
90444
+ body.keepAnimationId
90445
+ );
90446
+ }
90279
90447
  case "remove-property": {
90280
90448
  const r2 = requireAnimation(block.scriptText, body.animationId);
90281
90449
  if ("err" in r2) return r2.err;
@@ -90307,6 +90475,23 @@ function executeGsapMutationAcorn(body, block, respond2) {
90307
90475
  case "remove-keyframe": {
90308
90476
  return removeKeyframeFromScript(block.scriptText, body.animationId, body.percentage);
90309
90477
  }
90478
+ case "move-keyframe": {
90479
+ return moveKeyframeInScript(
90480
+ block.scriptText,
90481
+ body.animationId,
90482
+ body.fromPercentage,
90483
+ body.toPercentage
90484
+ );
90485
+ }
90486
+ case "resize-keyframed-tween": {
90487
+ return resizeKeyframedTweenInScript(
90488
+ block.scriptText,
90489
+ body.animationId,
90490
+ body.position,
90491
+ body.duration,
90492
+ body.pctRemap
90493
+ );
90494
+ }
90310
90495
  case "update-keyframe": {
90311
90496
  return updateKeyframeInScript(
90312
90497
  block.scriptText,
@@ -90439,6 +90624,8 @@ async function executeGsapMutationRecast(body, block, respond2) {
90439
90624
  removeAnimationFromScript: removeAnimationFromScript22,
90440
90625
  addKeyframeToScript: addKeyframeToScript22,
90441
90626
  removeKeyframeFromScript: removeKeyframeFromScript22,
90627
+ moveKeyframeInScript: moveKeyframeInScript22,
90628
+ resizeKeyframedTweenInScript: resizeKeyframedTweenInScript22,
90442
90629
  updateKeyframeInScript: updateKeyframeInScript22,
90443
90630
  convertToKeyframesInScript: convertToKeyframesInScript2,
90444
90631
  removeAllKeyframesFromScript: removeAllKeyframesFromScript22,
@@ -90453,7 +90640,8 @@ async function executeGsapMutationRecast(body, block, respond2) {
90453
90640
  removeArcPathFromScript: removeArcPathFromScript22,
90454
90641
  addAnimationWithKeyframesToScript: addAnimationWithKeyframesToScript22,
90455
90642
  splitAnimationsInScript: splitAnimationsInScript22,
90456
- splitIntoPropertyGroups: splitIntoPropertyGroups2
90643
+ splitIntoPropertyGroups: splitIntoPropertyGroups2,
90644
+ dedupePositionWritesInScript: dedupePositionWritesInScript22
90457
90645
  } = parser;
90458
90646
  function requireAnimation(scriptText, animationId) {
90459
90647
  const parsed = parseGsapScriptAcorn2(scriptText);
@@ -90538,6 +90726,14 @@ async function executeGsapMutationRecast(body, block, respond2) {
90538
90726
  }
90539
90727
  return script;
90540
90728
  }
90729
+ case "consolidate-position-writes": {
90730
+ if (!body.targetSelector) return block.scriptText;
90731
+ return dedupePositionWritesInScript22(
90732
+ block.scriptText,
90733
+ body.targetSelector,
90734
+ body.keepAnimationId
90735
+ );
90736
+ }
90541
90737
  case "remove-property": {
90542
90738
  const r2 = requireAnimation(block.scriptText, body.animationId);
90543
90739
  if ("err" in r2) return r2.err;
@@ -90569,6 +90765,23 @@ async function executeGsapMutationRecast(body, block, respond2) {
90569
90765
  case "remove-keyframe": {
90570
90766
  return removeKeyframeFromScript22(block.scriptText, body.animationId, body.percentage);
90571
90767
  }
90768
+ case "move-keyframe": {
90769
+ return moveKeyframeInScript22(
90770
+ block.scriptText,
90771
+ body.animationId,
90772
+ body.fromPercentage,
90773
+ body.toPercentage
90774
+ );
90775
+ }
90776
+ case "resize-keyframed-tween": {
90777
+ return resizeKeyframedTweenInScript22(
90778
+ block.scriptText,
90779
+ body.animationId,
90780
+ body.position,
90781
+ body.duration,
90782
+ body.pctRemap
90783
+ );
90784
+ }
90572
90785
  case "update-keyframe": {
90573
90786
  return updateKeyframeInScript22(
90574
90787
  block.scriptText,
@@ -92751,11 +92964,13 @@ function registerThumbnailRoutes(api, adapter2) {
92751
92964
  let compW = vpWidth || 1920;
92752
92965
  let compH = vpHeight || 1080;
92753
92966
  let sourceMtime = 0;
92754
- if (!vpWidth) {
92755
- const htmlFile = join112(project.dir, compPath);
92756
- if (existsSync72(htmlFile)) {
92757
- sourceMtime = Math.round(statSync42(htmlFile).mtimeMs);
92758
- const html = readFileSync102(htmlFile, "utf-8");
92967
+ let sourceKey = "";
92968
+ const htmlFile = join112(project.dir, compPath);
92969
+ if (existsSync72(htmlFile)) {
92970
+ const html = readFileSync102(htmlFile, "utf-8");
92971
+ sourceKey = `_${createHash22("sha1").update(html).digest("hex").slice(0, 16)}`;
92972
+ sourceMtime = Math.round(statSync42(htmlFile).mtimeMs);
92973
+ if (!vpWidth) {
92759
92974
  const wMatch = html.match(/data-width=["'](\d+)["']/);
92760
92975
  const hMatch = html.match(/data-height=["'](\d+)["']/);
92761
92976
  if (wMatch?.[1]) compW = parseInt(wMatch[1]);
@@ -92780,11 +92995,11 @@ function registerThumbnailRoutes(api, adapter2) {
92780
92995
  const cacheDir = join112(project.dir, ".thumbnails");
92781
92996
  const selectorKey = selector ? `_${selector.replace(/[^a-zA-Z0-9_-]+/g, "_").slice(0, 80)}_${selectorIndex ?? 0}` : "";
92782
92997
  const urlVersionKey = urlVersion ? `_${urlVersion.replace(/[^a-zA-Z0-9_-]+/g, "_").slice(0, 32)}` : "";
92783
- const cacheKey = `${THUMBNAIL_CACHE_VERSION}${urlVersionKey}${manualEditsKey}${motionKey}_${format}_${compPath.replace(/\//g, "_")}_${compW}x${compH}_${sourceMtime}_${seekTime.toFixed(2)}${selectorKey}.${format === "png" ? "png" : "jpg"}`;
92998
+ const cacheKey = `${THUMBNAIL_CACHE_VERSION}${urlVersionKey}${manualEditsKey}${motionKey}${sourceKey}_${format}_${compPath.replace(/\//g, "_")}_${compW}x${compH}_${sourceMtime}_${seekTime.toFixed(2)}${selectorKey}.${format === "png" ? "png" : "jpg"}`;
92784
92999
  const cachePath2 = join112(cacheDir, cacheKey);
92785
93000
  if (existsSync72(cachePath2)) {
92786
93001
  return new Response(new Uint8Array(readFileSync102(cachePath2)), {
92787
- headers: { "Content-Type": contentType, "Cache-Control": "public, max-age=60" }
93002
+ headers: { "Content-Type": contentType, "Cache-Control": "no-cache" }
92788
93003
  });
92789
93004
  }
92790
93005
  try {
@@ -92808,7 +93023,7 @@ function registerThumbnailRoutes(api, adapter2) {
92808
93023
  if (!existsSync72(cacheDir)) mkdirSync52(cacheDir, { recursive: true });
92809
93024
  writeFileSync62(cachePath2, buffer);
92810
93025
  return new Response(new Uint8Array(buffer), {
92811
- headers: { "Content-Type": contentType, "Cache-Control": "public, max-age=60" }
93026
+ headers: { "Content-Type": contentType, "Cache-Control": "no-cache" }
92812
93027
  });
92813
93028
  } catch (err) {
92814
93029
  const msg = err instanceof Error ? err.message : String(err);
@@ -93172,6 +93387,8 @@ var init_dist9 = __esm({
93172
93387
  "add-keyframe",
93173
93388
  "update-keyframe",
93174
93389
  "remove-keyframe",
93390
+ "move-keyframe",
93391
+ "resize-keyframed-tween",
93175
93392
  "remove-all-keyframes",
93176
93393
  "add-with-keyframes",
93177
93394
  "replace-with-keyframes",
@@ -98906,6 +99123,17 @@ var init_captureStageError = __esm({
98906
99123
  }
98907
99124
  });
98908
99125
 
99126
+ // ../producer/src/services/render/captureBeyondViewport.ts
99127
+ function resolveVideoCaptureBeyondViewport(videoCount, browserGpuMode) {
99128
+ if (videoCount <= 0) return void 0;
99129
+ return browserGpuMode === "hardware";
99130
+ }
99131
+ var init_captureBeyondViewport = __esm({
99132
+ "../producer/src/services/render/captureBeyondViewport.ts"() {
99133
+ "use strict";
99134
+ }
99135
+ });
99136
+
98909
99137
  // ../producer/src/services/render/captureCost.ts
98910
99138
  import { join as join34 } from "path";
98911
99139
  function estimateCaptureCostMultiplier(compiled) {
@@ -100547,6 +100775,18 @@ function rewriteUnresolvableGsapToCdn(html, projectDir) {
100547
100775
  }
100548
100776
  );
100549
100777
  }
100778
+ function assignMissingMediaIds(html) {
100779
+ const { document: document2 } = parseHTML(html);
100780
+ const media = document2.querySelectorAll("video[data-start], audio[data-start]");
100781
+ let seq = 0;
100782
+ let changed = false;
100783
+ for (const el of Array.from(media)) {
100784
+ if (el.getAttribute("id")) continue;
100785
+ el.setAttribute("id", `hf-media-${seq++}`);
100786
+ changed = true;
100787
+ }
100788
+ return changed ? document2.toString() : html;
100789
+ }
100550
100790
  async function compileForRender(projectDir, htmlPath, downloadDir, options = {}) {
100551
100791
  const rawHtml = rewriteUnresolvableGsapToCdn(readFileSync23(htmlPath, "utf-8"), projectDir);
100552
100792
  const { html: compiledHtml, unresolvedCompositions } = await compileHtmlFile(
@@ -100612,7 +100852,11 @@ async function compileForRender(projectDir, htmlPath, downloadDir, options = {})
100612
100852
  defaultLogger.info(`[Compiler] Prepared ${preparedGifs.length} animated GIF input(s) as WebM`);
100613
100853
  }
100614
100854
  const embeddedHtml = await embedLocalFontFaces(htmlWithPreparedGifs, projectDir);
100615
- const { html, externalAssets } = collectExternalAssets(embeddedHtml, projectDir);
100855
+ const { html: htmlBeforeMediaIds, externalAssets } = collectExternalAssets(
100856
+ embeddedHtml,
100857
+ projectDir
100858
+ );
100859
+ const html = assignMissingMediaIds(htmlBeforeMediaIds);
100616
100860
  for (const [relPath, absPath] of remoteMediaAssets) {
100617
100861
  externalAssets.set(relPath, absPath);
100618
100862
  }
@@ -104681,6 +104925,15 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
104681
104925
  }
104682
104926
  const framesDir = join49(workDir, "captured-frames");
104683
104927
  if (!existsSync43(framesDir)) mkdirSync23(framesDir, { recursive: true });
104928
+ const resolvedBrowserGpuMode = await resolveBrowserGpuMode(cfg.browserGpuMode, {
104929
+ chromePath: resolveHeadlessShellPath(cfg),
104930
+ browserTimeout: cfg.browserTimeout
104931
+ });
104932
+ updateCaptureObservability({ browserGpuMode: resolvedBrowserGpuMode });
104933
+ const videoCaptureBeyondViewport = resolveVideoCaptureBeyondViewport(
104934
+ composition.videos.length,
104935
+ resolvedBrowserGpuMode
104936
+ );
104684
104937
  const captureOptions = {
104685
104938
  width,
104686
104939
  height,
@@ -104689,7 +104942,7 @@ async function executeRenderJob(job, projectDir, outputPath, onProgress, abortSi
104689
104942
  quality: needsAlpha ? void 0 : job.config.quality === "draft" ? 80 : 95,
104690
104943
  variables: job.config.variables,
104691
104944
  deviceScaleFactor,
104692
- ...composition.videos.length > 0 ? { captureBeyondViewport: true } : {}
104945
+ ...videoCaptureBeyondViewport !== void 0 ? { captureBeyondViewport: videoCaptureBeyondViewport } : {}
104693
104946
  };
104694
104947
  resolvedCaptureBeyondViewport = captureOptions.captureBeyondViewport ?? resolvedCaptureBeyondViewport;
104695
104948
  if (resolvedCaptureBeyondViewport !== void 0) {
@@ -105210,6 +105463,7 @@ var init_renderOrchestrator = __esm({
105210
105463
  init_hdrMode();
105211
105464
  init_perfSummary();
105212
105465
  init_captureStageError();
105466
+ init_captureBeyondViewport();
105213
105467
  init_captureCost();
105214
105468
  init_observability();
105215
105469
  init_compileStage();
@@ -107045,6 +107299,10 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath) {
107045
107299
  rebuildExtractedFramesFromPlanDir(planDir, planVideos.extracted)
107046
107300
  )
107047
107301
  ) : null;
107302
+ const videoCaptureBeyondViewport = resolveVideoCaptureBeyondViewport(
107303
+ planVideos?.videos.length ?? 0,
107304
+ "software"
107305
+ );
107048
107306
  const workDir = `${outputChunkPath}.work.${process.pid}.${randomBytes2(4).toString("hex")}`;
107049
107307
  mkdirSync27(workDir, { recursive: true });
107050
107308
  const framesDir = join57(workDir, "captured-frames");
@@ -107071,7 +107329,7 @@ async function renderChunk(planDir, chunkIndex, outputChunkPath) {
107071
107329
  // declare `data-composition-variables` leave this undefined and the
107072
107330
  // engine skips the `evaluateOnNewDocument` injection.
107073
107331
  variables: encoder.variables,
107074
- ...(planVideos?.videos.length ?? 0) > 0 ? { captureBeyondViewport: true } : {},
107332
+ ...videoCaptureBeyondViewport !== void 0 ? { captureBeyondViewport: videoCaptureBeyondViewport } : {},
107075
107333
  // lock the BeginFrame warmup loop to a fixed iteration count so
107076
107334
  // `beginFrameTimeTicks` is host-independent. Only chunks ever set this.
107077
107335
  lockWarmupTicks: true
@@ -107239,6 +107497,7 @@ var init_renderChunk = __esm({
107239
107497
  init_logger();
107240
107498
  init_encodeStage();
107241
107499
  init_captureStage();
107500
+ init_captureBeyondViewport();
107242
107501
  init_freezePlan();
107243
107502
  init_planHash();
107244
107503
  init_runtimeEnvSnapshot();
@@ -108475,12 +108734,12 @@ import { existsSync as existsSync54, lstatSync as lstatSync3, symlinkSync as sym
108475
108734
  import { resolve as resolve31, dirname as dirname24, basename as basename9, join as join61 } from "path";
108476
108735
  import { fileURLToPath as fileURLToPath8 } from "url";
108477
108736
  import { createRequire as createRequire2 } from "module";
108478
- function previewBaseUrl(port) {
108479
- return `http://127.0.0.1:${port}`;
108737
+ function previewBaseUrl(port, host = "127.0.0.1") {
108738
+ return `http://${host}:${port}`;
108480
108739
  }
108481
- function absolutePreviewUrl(port, path2) {
108740
+ function absolutePreviewUrl(port, path2, host = "127.0.0.1") {
108482
108741
  if (/^https?:\/\//.test(path2)) return path2;
108483
- return `${previewBaseUrl(port)}${path2.startsWith("/") ? path2 : `/${path2}`}`;
108742
+ return `${previewBaseUrl(port, host)}${path2.startsWith("/") ? path2 : `/${path2}`}`;
108484
108743
  }
108485
108744
  function hasExplicitPreviewPort(argv2) {
108486
108745
  return argv2.some((arg) => arg === "--port" || arg.startsWith("--port="));
@@ -108501,7 +108760,7 @@ function previewServerPayload(server) {
108501
108760
  port: server.port,
108502
108761
  projectName: server.projectName,
108503
108762
  projectDir: server.projectDir,
108504
- url: previewBaseUrl(server.port)
108763
+ url: previewBaseUrl(server.port, server.host)
108505
108764
  };
108506
108765
  }
108507
108766
  function parseContextFields(value) {
@@ -108578,7 +108837,7 @@ async function printCurrentSelection(projectDir, startPort, json, preferredPort)
108578
108837
  }
108579
108838
  const selection = {
108580
108839
  ...response.selection,
108581
- thumbnailUrl: absolutePreviewUrl(server.port, response.selection.thumbnailUrl)
108840
+ thumbnailUrl: absolutePreviewUrl(server.port, response.selection.thumbnailUrl, server.host)
108582
108841
  };
108583
108842
  if (json) {
108584
108843
  console.log(
@@ -108728,7 +108987,7 @@ async function printCurrentContext(projectDir, startPort, options) {
108728
108987
  console.log(`${c.success("\u25C7")} Studio context`);
108729
108988
  if (contextIncludes(fields, "server")) {
108730
108989
  console.log(` ${c.dim("Project")} ${server.projectName}`);
108731
- console.log(` ${c.dim("Studio")} ${previewBaseUrl(server.port)}`);
108990
+ console.log(` ${c.dim("Studio")} ${previewBaseUrl(server.port, server.host)}`);
108732
108991
  }
108733
108992
  if (contextIncludes(fields, "selection")) {
108734
108993
  if (selection.ok) {