@hyperframes/parsers 0.7.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js ADDED
@@ -0,0 +1,2500 @@
1
+ // src/types.ts
2
+ var CANVAS_DIMENSIONS = {
3
+ landscape: { width: 1920, height: 1080 },
4
+ portrait: { width: 1080, height: 1920 },
5
+ "landscape-4k": { width: 3840, height: 2160 },
6
+ "portrait-4k": { width: 2160, height: 3840 },
7
+ square: { width: 1080, height: 1080 },
8
+ "square-4k": { width: 2160, height: 2160 }
9
+ };
10
+ var VALID_CANVAS_RESOLUTIONS = Object.keys(
11
+ CANVAS_DIMENSIONS
12
+ );
13
+ var RESOLUTION_ALIASES = {
14
+ "1080p": "landscape",
15
+ hd: "landscape",
16
+ "1080p-portrait": "portrait",
17
+ "portrait-1080p": "portrait",
18
+ "4k": "landscape-4k",
19
+ uhd: "landscape-4k",
20
+ "4k-portrait": "portrait-4k",
21
+ "1080p-square": "square",
22
+ "square-1080p": "square",
23
+ "4k-square": "square-4k"
24
+ };
25
+ function normalizeResolutionFlag(input) {
26
+ if (!input) return void 0;
27
+ const lowered = input.toLowerCase();
28
+ if (VALID_CANVAS_RESOLUTIONS.includes(lowered)) {
29
+ return lowered;
30
+ }
31
+ return RESOLUTION_ALIASES[lowered];
32
+ }
33
+ var COMPOSITION_VARIABLE_TYPES = [
34
+ "string",
35
+ "number",
36
+ "color",
37
+ "boolean",
38
+ "enum",
39
+ "font",
40
+ "image"
41
+ ];
42
+ function isTextElement(el) {
43
+ return el.type === "text";
44
+ }
45
+ function isMediaElement(el) {
46
+ return el.type === "video" || el.type === "image" || el.type === "audio";
47
+ }
48
+ function isCompositionElement(el) {
49
+ return el.type === "composition";
50
+ }
51
+ var TIMELINE_COLORS = {
52
+ video: "#ec4899",
53
+ image: "#3b82f6",
54
+ text: "#06b6d4",
55
+ audio: "#10b981",
56
+ composition: "#f97316"
57
+ };
58
+ var DEFAULT_DURATIONS = {
59
+ video: 5,
60
+ image: 5,
61
+ text: 2,
62
+ audio: 5,
63
+ composition: 5
64
+ };
65
+ function getDefaultStageZoom(resolution) {
66
+ const { width, height } = CANVAS_DIMENSIONS[resolution];
67
+ return {
68
+ scale: 1,
69
+ focusX: width / 2,
70
+ focusY: height / 2
71
+ };
72
+ }
73
+
74
+ // src/gsapConstants.ts
75
+ var SUPPORTED_PROPS = [
76
+ // 2D Transforms
77
+ "x",
78
+ "y",
79
+ "scale",
80
+ "scaleX",
81
+ "scaleY",
82
+ "rotation",
83
+ "skewX",
84
+ "skewY",
85
+ // 3D Transforms
86
+ "z",
87
+ "rotationX",
88
+ "rotationY",
89
+ "rotationZ",
90
+ "perspective",
91
+ "transformPerspective",
92
+ "transformOrigin",
93
+ // Visibility
94
+ "opacity",
95
+ "visibility",
96
+ "autoAlpha",
97
+ // Dimensions
98
+ "width",
99
+ "height",
100
+ // Colors
101
+ "color",
102
+ "backgroundColor",
103
+ "borderColor",
104
+ // Box model
105
+ "borderRadius",
106
+ // Typography
107
+ "fontSize",
108
+ "letterSpacing",
109
+ // Filter & Clipping
110
+ "filter",
111
+ "clipPath",
112
+ // DOM content (number counters, text roll-ups)
113
+ "innerText"
114
+ ];
115
+ var PROPERTY_GROUPS = {
116
+ position: /* @__PURE__ */ new Set(["x", "y", "xPercent", "yPercent"]),
117
+ scale: /* @__PURE__ */ new Set(["scale", "scaleX", "scaleY"]),
118
+ size: /* @__PURE__ */ new Set(["width", "height"]),
119
+ rotation: /* @__PURE__ */ new Set(["rotation", "skewX", "skewY"]),
120
+ visual: /* @__PURE__ */ new Set(["opacity", "autoAlpha"]),
121
+ other: /* @__PURE__ */ new Set()
122
+ };
123
+ var PROP_TO_GROUP = /* @__PURE__ */ new Map();
124
+ for (const [group, props] of Object.entries(PROPERTY_GROUPS)) {
125
+ for (const p of props) PROP_TO_GROUP.set(p, group);
126
+ }
127
+ function classifyPropertyGroup(prop) {
128
+ return PROP_TO_GROUP.get(prop) ?? "other";
129
+ }
130
+ function classifyTweenPropertyGroup(properties) {
131
+ const groups = /* @__PURE__ */ new Set();
132
+ for (const key of Object.keys(properties)) {
133
+ if (key === "transformOrigin" || key === "_auto" || key === "data") continue;
134
+ const g = classifyPropertyGroup(key);
135
+ groups.add(g);
136
+ }
137
+ if (groups.size === 1) return groups.values().next().value;
138
+ return void 0;
139
+ }
140
+ var SUPPORTED_EASES = [
141
+ "none",
142
+ "power1.in",
143
+ "power1.out",
144
+ "power1.inOut",
145
+ "power2.in",
146
+ "power2.out",
147
+ "power2.inOut",
148
+ "power3.in",
149
+ "power3.out",
150
+ "power3.inOut",
151
+ "power4.in",
152
+ "power4.out",
153
+ "power4.inOut",
154
+ "back.in",
155
+ "back.out",
156
+ "back.inOut",
157
+ "elastic.in",
158
+ "elastic.out",
159
+ "elastic.inOut",
160
+ "bounce.in",
161
+ "bounce.out",
162
+ "bounce.inOut",
163
+ "expo.in",
164
+ "expo.out",
165
+ "expo.inOut",
166
+ "spring-gentle",
167
+ "spring-bouncy",
168
+ "spring-stiff",
169
+ "spring-wobbly",
170
+ "spring-heavy",
171
+ "steps(1)"
172
+ ];
173
+
174
+ // src/gsapSerialize.ts
175
+ function editabilityForProvenance(provenance) {
176
+ if (!provenance || provenance.kind === "literal") return "direct";
177
+ if (provenance.kind === "runtime-dynamic") return "source";
178
+ return "unroll";
179
+ }
180
+ function buildArcPath(coords, curviness, autoRotate, isCubic) {
181
+ const first = coords[0];
182
+ if (coords.length < 2 || !first) return void 0;
183
+ const segments = [];
184
+ let waypoints;
185
+ if (isCubic && coords.length >= 4) {
186
+ waypoints = [first];
187
+ for (let i = 1; i + 2 < coords.length; i += 3) {
188
+ const cp1 = coords[i];
189
+ const cp2 = coords[i + 1];
190
+ const anchor = coords[i + 2];
191
+ if (!cp1 || !cp2 || !anchor) continue;
192
+ waypoints.push(anchor);
193
+ segments.push({ curviness, cp1, cp2 });
194
+ }
195
+ } else {
196
+ waypoints = coords;
197
+ for (let i = 0; i < waypoints.length - 1; i++) segments.push({ curviness });
198
+ }
199
+ return { arcPath: { enabled: true, autoRotate, segments }, waypoints };
200
+ }
201
+ function serializeGsapAnimations(animations, timelineVar = "tl", options) {
202
+ const sorted = [...animations].sort((a, b) => {
203
+ const aNum = a.resolvedStart ?? (typeof a.position === "number" ? a.position : Number.MAX_SAFE_INTEGER);
204
+ const bNum = b.resolvedStart ?? (typeof b.position === "number" ? b.position : Number.MAX_SAFE_INTEGER);
205
+ return aNum - bNum;
206
+ });
207
+ const lines = sorted.map((anim) => {
208
+ const selector = `"${anim.targetSelector}"`;
209
+ const props = { ...anim.properties };
210
+ if (anim.duration !== void 0) props.duration = anim.duration;
211
+ if (anim.ease) props.ease = anim.ease;
212
+ let propsStr = serializeObject(props);
213
+ if (anim.extras && Object.keys(anim.extras).length > 0) {
214
+ const extrasStr = serializeExtras(anim.extras);
215
+ if (Object.keys(props).length === 0) {
216
+ propsStr = `{ ${extrasStr} }`;
217
+ } else {
218
+ propsStr = propsStr.slice(0, -2) + `, ${extrasStr} }`;
219
+ }
220
+ }
221
+ const posStr = typeof anim.position === "string" ? `"${anim.position}"` : anim.position;
222
+ switch (anim.method) {
223
+ case "set":
224
+ return anim.global ? ` gsap.set(${selector}, ${propsStr});` : ` ${timelineVar}.set(${selector}, ${propsStr}, ${posStr});`;
225
+ case "to":
226
+ return ` ${timelineVar}.to(${selector}, ${propsStr}, ${posStr});`;
227
+ case "from":
228
+ return ` ${timelineVar}.from(${selector}, ${propsStr}, ${posStr});`;
229
+ case "fromTo": {
230
+ const fromStr = serializeObject(anim.fromProperties || {});
231
+ return ` ${timelineVar}.fromTo(${selector}, ${fromStr}, ${propsStr}, ${posStr});`;
232
+ }
233
+ }
234
+ });
235
+ let mediaSync = "";
236
+ if (options?.includeMediaSync) {
237
+ mediaSync = `
238
+ ${timelineVar}.eventCallback("onUpdate", function() {
239
+ const time = ${timelineVar}.time();
240
+ document.querySelectorAll("video[data-start], audio[data-start]").forEach(function(media) {
241
+ const start = parseFloat(media.dataset.start);
242
+ const end = parseFloat(media.dataset.end) || Infinity;
243
+ const mediaTime = time - start;
244
+ if (time >= start && time < end) {
245
+ if (Math.abs(media.currentTime - mediaTime) > 0.1) {
246
+ media.currentTime = mediaTime;
247
+ }
248
+ if (media.paused && !${timelineVar}.paused()) {
249
+ media.play().catch(function() {});
250
+ }
251
+ } else if (!media.paused) {
252
+ media.pause();
253
+ }
254
+ });
255
+ });`;
256
+ }
257
+ const preamble = options?.preamble || `const ${timelineVar} = gsap.timeline({ paused: true });`;
258
+ const postamble = options?.postamble ? `
259
+ ${options.postamble}` : "";
260
+ return `
261
+ ${preamble}
262
+ ${lines.join("\n")}${mediaSync}${postamble}
263
+ `;
264
+ }
265
+ function serializeValue(value) {
266
+ if (typeof value === "string" && value.startsWith("__raw:")) {
267
+ return value.slice(6);
268
+ }
269
+ if (typeof value === "string") return JSON.stringify(value);
270
+ return String(value);
271
+ }
272
+ function safeJsKey(key) {
273
+ return /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(key) ? key : JSON.stringify(key);
274
+ }
275
+ function serializeObject(obj) {
276
+ const entries = Object.entries(obj).map(([key, value]) => {
277
+ return `${safeJsKey(key)}: ${serializeValue(value)}`;
278
+ });
279
+ return `{ ${entries.join(", ")} }`;
280
+ }
281
+ function serializeExtras(extras) {
282
+ return Object.entries(extras).map(([key, value]) => {
283
+ return `${safeJsKey(key)}: ${serializeValue(value)}`;
284
+ }).join(", ");
285
+ }
286
+ function getAnimationsForElementId(animations, elementId) {
287
+ const selector = `#${elementId}`;
288
+ return animations.filter((a) => a.targetSelector === selector);
289
+ }
290
+ var FORBIDDEN_GSAP_PATTERNS = [
291
+ { pattern: /\.call\s*\(/, message: "call() method not allowed" },
292
+ { pattern: /\.add\s*\(/, message: "add() method not allowed" },
293
+ { pattern: /\.addPause\s*\(/, message: "addPause() method not allowed" },
294
+ { pattern: /gsap\.registerEffect\s*\(/, message: "registerEffect() not allowed" },
295
+ { pattern: /ScrollTrigger/, message: "ScrollTrigger not allowed" },
296
+ { pattern: /onComplete\s*:/, message: "onComplete callback not allowed" },
297
+ { pattern: /onUpdate\s*:/, message: "onUpdate callback not allowed" },
298
+ { pattern: /onStart\s*:/, message: "onStart callback not allowed" },
299
+ { pattern: /onRepeat\s*:/, message: "onRepeat callback not allowed" },
300
+ { pattern: /onReverseComplete\s*:/, message: "onReverseComplete callback not allowed" },
301
+ { pattern: /repeat\s*:\s*-1/, message: "Infinite repeat (repeat: -1) not allowed" },
302
+ { pattern: /Math\.random\s*\(/, message: "Random values (Math.random) not allowed" },
303
+ { pattern: /Date\.now\s*\(/, message: "Date-dependent values (Date.now) not allowed" },
304
+ { pattern: /new\s+Date\s*\(/, message: "Date constructor not allowed" },
305
+ { pattern: /setTimeout\s*\(/, message: "setTimeout not allowed" },
306
+ { pattern: /setInterval\s*\(/, message: "setInterval not allowed" },
307
+ { pattern: /requestAnimationFrame\s*\(/, message: "requestAnimationFrame not allowed" }
308
+ ];
309
+ function validateCompositionGsap(script) {
310
+ const errors = [];
311
+ const warnings = [];
312
+ for (const { pattern, message } of FORBIDDEN_GSAP_PATTERNS) {
313
+ if (pattern.test(script)) errors.push(message);
314
+ }
315
+ if (/yoyo\s*:\s*true/.test(script)) {
316
+ warnings.push("yoyo animations may behave unexpectedly when scrubbing");
317
+ }
318
+ if (/stagger\s*:/.test(script)) {
319
+ warnings.push("stagger animations may not serialize correctly");
320
+ }
321
+ return { valid: errors.length === 0, errors, warnings };
322
+ }
323
+ function keyframesToGsapAnimations(elementId, keyframes, elementStartTime, base) {
324
+ const sorted = [...keyframes].sort((a, b) => a.time - b.time);
325
+ const animations = [];
326
+ const baseX = base?.x ?? 0;
327
+ const baseY = base?.y ?? 0;
328
+ const baseScale = base?.scale ?? 1;
329
+ sorted.forEach((kf, i) => {
330
+ const absoluteTime = elementStartTime + kf.time;
331
+ const isFirst = i === 0;
332
+ const prevKf = i > 0 ? sorted[i - 1] : null;
333
+ const duration = prevKf ? kf.time - prevKf.time : void 0;
334
+ const position = prevKf ? elementStartTime + prevKf.time : absoluteTime;
335
+ const properties = {};
336
+ for (const [key, value] of Object.entries(kf.properties)) {
337
+ if (typeof value !== "number") continue;
338
+ if (key === "x") properties.x = baseX + value;
339
+ else if (key === "y") properties.y = baseY + value;
340
+ else if (key === "scale") properties.scale = baseScale * value;
341
+ else properties[key] = value;
342
+ }
343
+ animations.push({
344
+ id: `${elementId}-kf-${kf.id}`,
345
+ targetSelector: `#${elementId}`,
346
+ method: isFirst ? "set" : "to",
347
+ position,
348
+ properties,
349
+ duration: isFirst ? void 0 : duration,
350
+ ease: kf.ease
351
+ });
352
+ });
353
+ return animations;
354
+ }
355
+ function gsapAnimationsToKeyframes(animations, elementStartTime, options) {
356
+ const validMethods = ["set", "to", "from", "fromTo"];
357
+ const baseX = options?.baseX ?? 0;
358
+ const baseY = options?.baseY ?? 0;
359
+ const baseScale = options?.baseScale ?? 1;
360
+ const clampTimeToZero = options?.clampTimeToZero ?? true;
361
+ const skipBaseSet = options?.skipBaseSet ?? false;
362
+ const baseTimeEpsilon = 1e-3;
363
+ const baseValueEpsilon = 1e-5;
364
+ return animations.filter(
365
+ (a) => validMethods.includes(a.method) && typeof a.position === "number"
366
+ ).map((a) => {
367
+ const relativeTimeRaw = a.position - elementStartTime;
368
+ const time = clampTimeToZero ? Math.max(0, relativeTimeRaw) : relativeTimeRaw;
369
+ const properties = {};
370
+ for (const [key, value] of Object.entries(a.properties)) {
371
+ if (typeof value !== "number") continue;
372
+ if (key === "x") properties.x = value - baseX;
373
+ else if (key === "y") properties.y = value - baseY;
374
+ else if (key === "scale") {
375
+ properties.scale = baseScale !== 0 ? value / baseScale : value;
376
+ } else {
377
+ properties[key] = value;
378
+ }
379
+ }
380
+ if (skipBaseSet && a.method === "set" && time < baseTimeEpsilon && Object.values(properties).every(
381
+ (v) => typeof v === "number" && Math.abs(v) < baseValueEpsilon
382
+ )) {
383
+ return null;
384
+ }
385
+ return {
386
+ id: a.id.replace(/^.*-kf-/, ""),
387
+ time,
388
+ properties,
389
+ ease: a.ease
390
+ };
391
+ }).filter((kf) => kf !== null);
392
+ }
393
+
394
+ // src/gsapParser.ts
395
+ import * as recast from "recast";
396
+ import { parse as babelParse } from "@babel/parser";
397
+
398
+ // src/springEase.ts
399
+ var SPRING_PRESETS = [
400
+ { name: "spring-gentle", label: "Gentle", mass: 1, stiffness: 100, damping: 15 },
401
+ { name: "spring-bouncy", label: "Bouncy", mass: 1, stiffness: 180, damping: 12 },
402
+ { name: "spring-stiff", label: "Stiff", mass: 1, stiffness: 300, damping: 20 },
403
+ { name: "spring-wobbly", label: "Wobbly", mass: 1, stiffness: 120, damping: 8 },
404
+ { name: "spring-heavy", label: "Heavy", mass: 3, stiffness: 200, damping: 20 }
405
+ ];
406
+ function generateSpringEaseData(mass, stiffness, damping, steps = 120) {
407
+ const w0 = Math.sqrt(stiffness / mass);
408
+ const zeta = damping / (2 * Math.sqrt(stiffness * mass));
409
+ let settleDuration;
410
+ if (zeta < 1) {
411
+ settleDuration = Math.min(5 / (zeta * w0), 10);
412
+ } else {
413
+ const decayRate = zeta * w0 - w0 * Math.sqrt(zeta * zeta - 1);
414
+ settleDuration = Math.min(4 / Math.max(decayRate, 0.01), 10);
415
+ }
416
+ const simDuration = Math.max(settleDuration, 1);
417
+ const segments = ["M0,0"];
418
+ for (let i = 1; i <= steps; i++) {
419
+ const t = i / steps;
420
+ const simT = t * simDuration;
421
+ let value;
422
+ if (zeta < 1) {
423
+ const wd = w0 * Math.sqrt(1 - zeta * zeta);
424
+ value = 1 - Math.exp(-zeta * w0 * simT) * (Math.cos(wd * simT) + zeta * w0 / wd * Math.sin(wd * simT));
425
+ } else if (zeta === 1) {
426
+ value = 1 - (1 + w0 * simT) * Math.exp(-w0 * simT);
427
+ } else {
428
+ const s1 = -w0 * (zeta - Math.sqrt(zeta * zeta - 1));
429
+ const s2 = -w0 * (zeta + Math.sqrt(zeta * zeta - 1));
430
+ value = 1 + (s1 * Math.exp(s2 * simT) - s2 * Math.exp(s1 * simT)) / (s2 - s1);
431
+ }
432
+ segments.push(`${t.toFixed(4)},${value.toFixed(4)}`);
433
+ }
434
+ segments[segments.length - 1] = "1,1";
435
+ return `${segments[0]} L${segments.slice(1).join(" ")}`;
436
+ }
437
+
438
+ // src/gsapParser.ts
439
+ var STUDIO_HOLD_MARKER = "hf-hold";
440
+ function isStudioHoldSet(anim) {
441
+ return anim.method === "set" && anim.properties?.data === STUDIO_HOLD_MARKER;
442
+ }
443
+
444
+ // src/gsapParserAcorn.ts
445
+ import * as acorn from "acorn";
446
+ import * as acornWalk from "acorn-walk";
447
+
448
+ // src/gsapInline.ts
449
+ var SKIP_KEYS = /* @__PURE__ */ new Set(["type", "start", "end", "loc", "range", "__hfProvenance", "__hfOrder"]);
450
+ var FUNCTION_TYPES = /* @__PURE__ */ new Set([
451
+ "ArrowFunctionExpression",
452
+ "FunctionExpression",
453
+ "FunctionDeclaration"
454
+ ]);
455
+ var GSAP_METHODS = /* @__PURE__ */ new Set(["set", "to", "from", "fromTo"]);
456
+ var MAX_DEPTH = 8;
457
+ var MAX_ITERS = 512;
458
+ function isFunctionNode(node) {
459
+ return !!node && FUNCTION_TYPES.has(node.type);
460
+ }
461
+ function isNode(x) {
462
+ return !!x && typeof x === "object" && typeof x.type === "string";
463
+ }
464
+ function transformChildren(node, fn) {
465
+ for (const key of Object.keys(node)) {
466
+ if (SKIP_KEYS.has(key) || isNonValueIdentifierSlot(node, key)) continue;
467
+ const child = node[key];
468
+ if (Array.isArray(child)) {
469
+ for (let i = 0; i < child.length; i++) child[i] = fn(child[i]);
470
+ } else {
471
+ node[key] = fn(child);
472
+ }
473
+ }
474
+ }
475
+ function cloneNode(node) {
476
+ return structuredClone(node);
477
+ }
478
+ function collectPatternNames(pattern, out) {
479
+ if (pattern?.type === "Identifier") out.add(pattern.name);
480
+ else if (pattern?.type === "AssignmentPattern") collectPatternNames(pattern.left, out);
481
+ else if (pattern?.type === "RestElement") collectPatternNames(pattern.argument, out);
482
+ }
483
+ function collectBoundNames(root) {
484
+ const names = /* @__PURE__ */ new Set();
485
+ const visit = (node) => {
486
+ if (!isNode(node)) return node;
487
+ if (isFunctionNode(node)) for (const p of node.params ?? []) collectPatternNames(p, names);
488
+ else if (node.type === "VariableDeclarator") collectPatternNames(node.id, names);
489
+ else if (node.type === "CatchClause") collectPatternNames(node.param, names);
490
+ transformChildren(node, visit);
491
+ return node;
492
+ };
493
+ visit(root);
494
+ return names;
495
+ }
496
+ function isNonValueIdentifierSlot(node, key) {
497
+ if (node.computed) return false;
498
+ return node.type === "MemberExpression" && key === "property" || node.type === "Property" && key === "key";
499
+ }
500
+ function substituteParams(node, bindings) {
501
+ const shadowed = collectBoundNames(node);
502
+ let effective = bindings;
503
+ if (shadowed.size > 0) {
504
+ effective = new Map(bindings);
505
+ for (const name of shadowed) effective.delete(name);
506
+ }
507
+ if (effective.size === 0) return node;
508
+ return replace(node, effective);
509
+ }
510
+ function replace(node, bindings) {
511
+ if (!isNode(node)) return node;
512
+ if (node.type === "Identifier" && bindings.has(node.name)) {
513
+ return cloneNode(bindings.get(node.name));
514
+ }
515
+ transformChildren(node, (child) => replace(child, bindings));
516
+ return node;
517
+ }
518
+ function tagProvenance(node, provenance) {
519
+ if (node && typeof node === "object") node.__hfProvenance = provenance;
520
+ return node;
521
+ }
522
+ function readProvenance(node) {
523
+ return node?.__hfProvenance;
524
+ }
525
+ function numericLiteral(value) {
526
+ return { type: "Literal", value, raw: String(value) };
527
+ }
528
+ function walkNodes(node, fn) {
529
+ if (!isNode(node)) return;
530
+ fn(node);
531
+ for (const key of Object.keys(node)) {
532
+ if (SKIP_KEYS.has(key)) continue;
533
+ const child = node[key];
534
+ if (Array.isArray(child)) for (const c of child) walkNodes(c, fn);
535
+ else walkNodes(child, fn);
536
+ }
537
+ }
538
+ function timelineRootName(call) {
539
+ let obj = call.callee?.object;
540
+ while (obj?.type === "CallExpression") obj = obj.callee?.object;
541
+ return obj?.type === "Identifier" ? obj.name : null;
542
+ }
543
+ function isTimelineRooted(call, timelineVar) {
544
+ if (timelineRootName(call) !== timelineVar) return false;
545
+ return call.callee?.property?.type === "Identifier" && GSAP_METHODS.has(call.callee.property.name);
546
+ }
547
+ function containsTimelineCall(node, timelineVar) {
548
+ let found = false;
549
+ walkNodes(node, (n) => {
550
+ if (n.type === "CallExpression" && isTimelineRooted(n, timelineVar)) found = true;
551
+ });
552
+ return found;
553
+ }
554
+ function rangeOf(node) {
555
+ return typeof node.start === "number" && typeof node.end === "number" ? [node.start, node.end] : void 0;
556
+ }
557
+ function isShapeEligible(fn) {
558
+ return isFunctionNode(fn) && fn.body?.type === "BlockStatement" && !(fn.params ?? []).some((p) => p.type !== "Identifier");
559
+ }
560
+ function callsAny(node, names) {
561
+ let hit = false;
562
+ walkNodes(node, (n) => {
563
+ if (n.type === "CallExpression" && n.callee?.type === "Identifier" && names.has(n.callee.name)) {
564
+ hit = true;
565
+ }
566
+ });
567
+ return hit;
568
+ }
569
+ function varDeclHelper(stmt) {
570
+ if (stmt.declarations?.length !== 1) return null;
571
+ const d = stmt.declarations[0];
572
+ return d.id?.type === "Identifier" && isShapeEligible(d.init) ? [d.id.name, d.init] : null;
573
+ }
574
+ function helperFromStatement(stmt) {
575
+ if (stmt.type === "FunctionDeclaration") {
576
+ return stmt.id && isShapeEligible(stmt) ? [stmt.id.name, stmt] : null;
577
+ }
578
+ if (stmt.type === "VariableDeclaration") return varDeclHelper(stmt);
579
+ return null;
580
+ }
581
+ function gatherHelperCandidates(program) {
582
+ const candidates = /* @__PURE__ */ new Map();
583
+ for (const stmt of program.body ?? []) {
584
+ const helper = helperFromStatement(stmt);
585
+ if (helper) candidates.set(helper[0], helper[1]);
586
+ }
587
+ return candidates;
588
+ }
589
+ function timelineBuildingNames(candidates, timelineVar) {
590
+ const building = /* @__PURE__ */ new Set();
591
+ for (const [name, fn] of candidates) {
592
+ if (containsTimelineCall(fn.body, timelineVar)) building.add(name);
593
+ }
594
+ for (let changed = true; changed; ) {
595
+ changed = false;
596
+ for (const [name, fn] of candidates) {
597
+ if (!building.has(name) && callsAny(fn.body, building)) {
598
+ building.add(name);
599
+ changed = true;
600
+ }
601
+ }
602
+ }
603
+ return building;
604
+ }
605
+ function bump(counts, key) {
606
+ counts.set(key, (counts.get(key) ?? 0) + 1);
607
+ }
608
+ function safelyDroppable(program, candidates) {
609
+ const names = new Set(candidates.keys());
610
+ const totalIds = /* @__PURE__ */ new Map();
611
+ const stmtCalls = /* @__PURE__ */ new Map();
612
+ walkNodes(program, (n) => {
613
+ if (n.type === "Identifier" && names.has(n.name)) bump(totalIds, n.name);
614
+ const e = n.type === "ExpressionStatement" ? n.expression : void 0;
615
+ if (e?.type === "CallExpression" && e.callee?.type === "Identifier" && names.has(e.callee.name)) {
616
+ bump(stmtCalls, e.callee.name);
617
+ }
618
+ });
619
+ const safe = /* @__PURE__ */ new Map();
620
+ for (const [name, fn] of candidates) {
621
+ if ((totalIds.get(name) ?? 0) === 1 + (stmtCalls.get(name) ?? 0)) safe.set(name, fn);
622
+ }
623
+ return safe;
624
+ }
625
+ function collectInlinableHelpers(program, timelineVar) {
626
+ const candidates = gatherHelperCandidates(program);
627
+ if (candidates.size === 0) return candidates;
628
+ const building = timelineBuildingNames(candidates, timelineVar);
629
+ for (const name of [...candidates.keys()]) if (!building.has(name)) candidates.delete(name);
630
+ if (candidates.size === 0) return candidates;
631
+ return safelyDroppable(program, candidates);
632
+ }
633
+ function isHelperDecl(stmt, helpers) {
634
+ if (stmt.type === "FunctionDeclaration") return !!stmt.id && helpers.get(stmt.id.name) === stmt;
635
+ if (stmt.type === "VariableDeclaration" && stmt.declarations?.length === 1) {
636
+ const d = stmt.declarations[0];
637
+ return d.id?.type === "Identifier" && helpers.get(d.id.name) === d.init;
638
+ }
639
+ return false;
640
+ }
641
+ function bodyStatements(node) {
642
+ if (node?.type === "BlockStatement") return node.body ?? [];
643
+ return node ? [{ type: "ExpressionStatement", expression: node }] : [];
644
+ }
645
+ function tagTimelineCalls(stmts, prov, ctx) {
646
+ for (const stmt of stmts) {
647
+ walkNodes(stmt, (n) => {
648
+ if (n.type === "CallExpression" && isTimelineRooted(n, ctx.timelineVar)) {
649
+ tagProvenance(n, { ...prov });
650
+ n.__hfOrder = ctx.order.n++;
651
+ }
652
+ });
653
+ }
654
+ }
655
+ function expandBody(bodyStmts, bindings, prov, ctx) {
656
+ const block = substituteParams(cloneNode({ type: "BlockStatement", body: bodyStmts }), bindings);
657
+ tagTimelineCalls(block.body, prov, ctx);
658
+ return expandStatements(block.body, { ...ctx, depth: ctx.depth + 1 });
659
+ }
660
+ function inlineHelper(call, ctx) {
661
+ const fn = ctx.helpers.get(call.callee.name);
662
+ const bindings = /* @__PURE__ */ new Map();
663
+ (fn.params ?? []).forEach((p, i) => {
664
+ const arg = call.arguments?.[i];
665
+ if (arg) bindings.set(p.name, arg);
666
+ });
667
+ const prov = {
668
+ kind: "helper",
669
+ fn: call.callee.name,
670
+ callSite: ++ctx.site.n,
671
+ sourceRange: rangeOf(call)
672
+ };
673
+ return expandBody(fn.body.body, bindings, prov, ctx);
674
+ }
675
+ function assignStep(update, resolve) {
676
+ if (update.operator === "+=") return asNum(resolve(update.right));
677
+ if (update.operator === "-=") {
678
+ const s = asNum(resolve(update.right));
679
+ return s === void 0 ? void 0 : -s;
680
+ }
681
+ if (update.operator === "=" && update.right?.type === "BinaryExpression") {
682
+ return asNum(resolve(update.right.right));
683
+ }
684
+ return void 0;
685
+ }
686
+ function updatedVarName(update) {
687
+ if (update?.type === "UpdateExpression") return update.argument?.name ?? null;
688
+ if (update?.type === "AssignmentExpression") return update.left?.name ?? null;
689
+ return null;
690
+ }
691
+ function loopStep(update, varName, resolve) {
692
+ if (updatedVarName(update) !== varName) return void 0;
693
+ if (update.type === "UpdateExpression") return update.operator === "++" ? 1 : -1;
694
+ return assignStep(update, resolve);
695
+ }
696
+ function asNum(v) {
697
+ return typeof v === "number" && Number.isFinite(v) ? v : void 0;
698
+ }
699
+ function loopSatisfied(op, x, end) {
700
+ if (op === "<") return x < end;
701
+ if (op === "<=") return x <= end;
702
+ if (op === ">") return x > end;
703
+ if (op === ">=") return x >= end;
704
+ return false;
705
+ }
706
+ function forInitVar(init) {
707
+ if (init?.type !== "VariableDeclaration" || init.declarations?.length !== 1) return null;
708
+ const d = init.declarations[0];
709
+ return d.id?.type === "Identifier" ? { name: d.id.name, initExpr: d.init } : null;
710
+ }
711
+ function parseForHeader(stmt, resolve) {
712
+ const iv = forInitVar(stmt.init);
713
+ const test = stmt.test;
714
+ if (!iv || test?.type !== "BinaryExpression" || test.left?.name !== iv.name) return null;
715
+ const start = asNum(resolve(iv.initExpr));
716
+ const end = asNum(resolve(test.right));
717
+ const step = loopStep(stmt.update, iv.name, resolve);
718
+ if (start === void 0 || end === void 0 || !step) return null;
719
+ return { v: iv.name, start, end, op: test.operator, step };
720
+ }
721
+ function unrollFor(stmt, ctx) {
722
+ const h = parseForHeader(stmt, ctx.resolve);
723
+ if (!h) return null;
724
+ const body = bodyStatements(stmt.body);
725
+ const out = [];
726
+ const site = ++ctx.site.n;
727
+ let iteration = 0;
728
+ for (let x = h.start; loopSatisfied(h.op, x, h.end); x += h.step) {
729
+ if (iteration >= MAX_ITERS) return null;
730
+ const prov = {
731
+ kind: "loop",
732
+ callSite: site,
733
+ iteration,
734
+ sourceRange: rangeOf(stmt)
735
+ };
736
+ out.push(...expandBody(body, /* @__PURE__ */ new Map([[h.v, numericLiteral(x)]]), prov, ctx));
737
+ iteration++;
738
+ }
739
+ return out;
740
+ }
741
+ function forOfVarName(left) {
742
+ if (left?.type === "VariableDeclaration") {
743
+ const id = left.declarations?.[0]?.id;
744
+ return id?.type === "Identifier" ? id.name : null;
745
+ }
746
+ return left?.type === "Identifier" ? left.name : null;
747
+ }
748
+ function unrollOverArray(elements, body, elName, idxName, range, ctx) {
749
+ const out = [];
750
+ const site = ++ctx.site.n;
751
+ elements.forEach((el, i) => {
752
+ if (!el) return;
753
+ const bindings = /* @__PURE__ */ new Map();
754
+ if (elName) bindings.set(elName, el);
755
+ if (idxName) bindings.set(idxName, numericLiteral(i));
756
+ const prov = { kind: "loop", callSite: site, iteration: i, sourceRange: range };
757
+ out.push(...expandBody(body, bindings, prov, ctx));
758
+ });
759
+ return out;
760
+ }
761
+ function unrollForOf(stmt, ctx) {
762
+ if (stmt.right?.type !== "ArrayExpression") return null;
763
+ const elName = forOfVarName(stmt.left);
764
+ if (!elName) return null;
765
+ return unrollOverArray(
766
+ stmt.right.elements ?? [],
767
+ bodyStatements(stmt.body),
768
+ elName,
769
+ null,
770
+ rangeOf(stmt),
771
+ ctx
772
+ );
773
+ }
774
+ function callbackParamNames(cb) {
775
+ const names = [];
776
+ for (const p of [cb.params?.[0], cb.params?.[1]]) {
777
+ if (!p) names.push(null);
778
+ else if (p.type !== "Identifier") return null;
779
+ else names.push(p.name);
780
+ }
781
+ return { el: names[0], idx: names[1] };
782
+ }
783
+ function isForEachCall(callee) {
784
+ return callee?.type === "MemberExpression" && callee.property?.name === "forEach" && callee.object?.type === "ArrayExpression";
785
+ }
786
+ function forEachTarget(call) {
787
+ if (!isForEachCall(call.callee)) return null;
788
+ const cb = call.arguments?.[0];
789
+ return isFunctionNode(cb) ? { elements: call.callee.object.elements ?? [], cb } : null;
790
+ }
791
+ function unrollForEach(call, ctx) {
792
+ const target = forEachTarget(call);
793
+ if (!target) return null;
794
+ const params = callbackParamNames(target.cb);
795
+ if (!params) return null;
796
+ return unrollOverArray(
797
+ target.elements,
798
+ bodyStatements(target.cb.body),
799
+ params.el,
800
+ params.idx,
801
+ rangeOf(call),
802
+ ctx
803
+ );
804
+ }
805
+ function expandCall(call, ctx) {
806
+ if (call.callee?.type === "Identifier" && ctx.helpers.has(call.callee.name)) {
807
+ return inlineHelper(call, ctx);
808
+ }
809
+ return unrollForEach(call, ctx);
810
+ }
811
+ function expandStatement(stmt, ctx) {
812
+ if (ctx.depth >= MAX_DEPTH) return null;
813
+ if (stmt.type === "ForStatement") return unrollFor(stmt, ctx);
814
+ if (stmt.type === "ForOfStatement") return unrollForOf(stmt, ctx);
815
+ if (stmt.type === "ExpressionStatement" && stmt.expression?.type === "CallExpression") {
816
+ return expandCall(stmt.expression, ctx);
817
+ }
818
+ return null;
819
+ }
820
+ function expandStatements(stmts, ctx) {
821
+ const out = [];
822
+ for (const stmt of stmts) {
823
+ const expanded = expandStatement(stmt, ctx);
824
+ if (expanded) out.push(...expanded);
825
+ else out.push(stmt);
826
+ }
827
+ return out;
828
+ }
829
+ function inlineComputedTimelines(ast, timelineVar, resolve) {
830
+ const helpers = collectInlinableHelpers(ast, timelineVar);
831
+ const ctx = {
832
+ helpers,
833
+ timelineVar,
834
+ resolve,
835
+ depth: 0,
836
+ site: { n: 0 },
837
+ order: { n: 0 }
838
+ };
839
+ const body = (ast.body ?? []).filter((stmt) => !isHelperDecl(stmt, helpers));
840
+ ast.body = expandStatements(body, ctx);
841
+ }
842
+
843
+ // src/gsapParserAcorn.ts
844
+ var GSAP_METHODS2 = /* @__PURE__ */ new Set(["set", "to", "from", "fromTo"]);
845
+ var QUERY_METHODS = /* @__PURE__ */ new Set(["querySelector", "querySelectorAll"]);
846
+ var ITERATION_METHODS = /* @__PURE__ */ new Set(["forEach", "map"]);
847
+ var SCOPE_NODE_TYPES = /* @__PURE__ */ new Set([
848
+ "Program",
849
+ "FunctionDeclaration",
850
+ "FunctionExpression",
851
+ "ArrowFunctionExpression"
852
+ ]);
853
+ function resolveNode(node, scope) {
854
+ if (!node) return void 0;
855
+ if (node.type === "NumericLiteral" || node.type === "Literal" && typeof node.value === "number")
856
+ return node.value;
857
+ if (node.type === "StringLiteral" || node.type === "Literal" && typeof node.value === "string")
858
+ return node.value;
859
+ if (node.type === "BooleanLiteral" || node.type === "Literal" && typeof node.value === "boolean")
860
+ return node.value;
861
+ if (node.type === "UnaryExpression" && node.operator === "-" && node.argument) {
862
+ const val = resolveNode(node.argument, scope);
863
+ return typeof val === "number" ? -val : void 0;
864
+ }
865
+ if (node.type === "BinaryExpression") {
866
+ const left = resolveNode(node.left, scope);
867
+ const right = resolveNode(node.right, scope);
868
+ if (typeof left === "number" && typeof right === "number") {
869
+ switch (node.operator) {
870
+ case "+":
871
+ return left + right;
872
+ case "-":
873
+ return left - right;
874
+ case "*":
875
+ return left * right;
876
+ case "/":
877
+ return right !== 0 ? left / right : void 0;
878
+ }
879
+ }
880
+ if (typeof left === "string" && node.operator === "+") return left + String(right ?? "");
881
+ if (typeof right === "string" && node.operator === "+") return String(left ?? "") + right;
882
+ }
883
+ if (node.type === "Identifier" && scope.has(node.name)) {
884
+ return scope.get(node.name);
885
+ }
886
+ if (node.type === "TemplateLiteral" && node.expressions?.length === 0) {
887
+ return node.quasis?.[0]?.value?.cooked ?? void 0;
888
+ }
889
+ return void 0;
890
+ }
891
+ function extractLiteralValue(node, scope) {
892
+ return resolveNode(node, scope);
893
+ }
894
+ function selectorFromQueryCall(node, scope) {
895
+ if (node?.type !== "CallExpression") return null;
896
+ const callee = node.callee;
897
+ if (callee?.type !== "MemberExpression" || callee.property?.type !== "Identifier") return null;
898
+ const method = callee.property.name;
899
+ const argValue = resolveNode(node.arguments?.[0], scope);
900
+ if (typeof argValue !== "string" || argValue.length === 0) return null;
901
+ if (QUERY_METHODS.has(method) || method === "toArray") return argValue;
902
+ if (method === "getElementById") return `#${argValue}`;
903
+ return null;
904
+ }
905
+ function enclosingScopeNodeFromAncestors(ancestors) {
906
+ for (let i = ancestors.length - 2; i >= 0; i--) {
907
+ const node = ancestors[i];
908
+ if (node && SCOPE_NODE_TYPES.has(node.type)) return node;
909
+ }
910
+ return null;
911
+ }
912
+ function scopeChainFromAncestors(ancestors) {
913
+ const chain = [];
914
+ for (let i = ancestors.length - 1; i >= 0; i--) {
915
+ const node = ancestors[i];
916
+ if (node && SCOPE_NODE_TYPES.has(node.type)) chain.push(node);
917
+ }
918
+ return chain;
919
+ }
920
+ function addBinding(bindings, scopeNode, name, selector) {
921
+ let scoped = bindings.get(scopeNode);
922
+ if (!scoped) {
923
+ scoped = /* @__PURE__ */ new Map();
924
+ bindings.set(scopeNode, scoped);
925
+ }
926
+ if (!scoped.has(name)) scoped.set(name, selector);
927
+ }
928
+ function lookupBindingFromAncestors(name, ancestors, bindings) {
929
+ for (const scopeNode of scopeChainFromAncestors(ancestors)) {
930
+ const selector = bindings.get(scopeNode)?.get(name);
931
+ if (selector !== void 0) return selector;
932
+ }
933
+ return bindings.get(null)?.get(name) ?? null;
934
+ }
935
+ function isFunctionNode2(node) {
936
+ return node?.type === "ArrowFunctionExpression" || node?.type === "FunctionExpression" || node?.type === "FunctionDeclaration";
937
+ }
938
+ function resolveCollectionSelector(node, ancestors, scope, bindings) {
939
+ if (node?.type === "Identifier")
940
+ return lookupBindingFromAncestors(node.name, ancestors, bindings);
941
+ if (node?.type === "CallExpression") return selectorFromQueryCall(node, scope);
942
+ return null;
943
+ }
944
+ function collectScopeBindings(ast) {
945
+ const bindings = /* @__PURE__ */ new Map();
946
+ acornWalk.simple(ast, {
947
+ VariableDeclarator(node) {
948
+ const name = node.id?.name;
949
+ const init = node.init;
950
+ if (name && init) {
951
+ const val = resolveNode(init, bindings);
952
+ if (val !== void 0) bindings.set(name, val);
953
+ }
954
+ }
955
+ });
956
+ return bindings;
957
+ }
958
+ function collectTargetBindings(ast, scope) {
959
+ const bindings = /* @__PURE__ */ new Map();
960
+ acornWalk.ancestor(ast, {
961
+ VariableDeclarator(node, _, ancestors) {
962
+ const name = node.id?.name;
963
+ const selector = selectorFromQueryCall(node.init, scope);
964
+ if (name && selector !== null) {
965
+ addBinding(bindings, enclosingScopeNodeFromAncestors(ancestors), name, selector);
966
+ }
967
+ },
968
+ AssignmentExpression(node, _, ancestors) {
969
+ const left = node.left;
970
+ const selector = selectorFromQueryCall(node.right, scope);
971
+ if (left?.type === "Identifier" && selector !== null) {
972
+ addBinding(bindings, enclosingScopeNodeFromAncestors(ancestors), left.name, selector);
973
+ }
974
+ }
975
+ });
976
+ acornWalk.ancestor(ast, {
977
+ // fallow-ignore-next-line complexity
978
+ CallExpression(node, _, ancestors) {
979
+ const callee = node.callee;
980
+ if (callee?.type === "MemberExpression" && callee.property?.type === "Identifier" && ITERATION_METHODS.has(callee.property.name)) {
981
+ const collectionSelector = resolveCollectionSelector(
982
+ callee.object,
983
+ ancestors,
984
+ scope,
985
+ bindings
986
+ );
987
+ const fn = node.arguments?.[0];
988
+ const param = fn?.params?.[0];
989
+ if (collectionSelector && param?.type === "Identifier" && isFunctionNode2(fn)) {
990
+ addBinding(bindings, fn, param.name, collectionSelector);
991
+ }
992
+ }
993
+ }
994
+ });
995
+ return bindings;
996
+ }
997
+ function resolveTargetSelector(node, ancestors, scope, bindings) {
998
+ if (!node) return null;
999
+ if (node.type === "StringLiteral" || node.type === "Literal") {
1000
+ return typeof node.value === "string" ? node.value : null;
1001
+ }
1002
+ if (node.type === "Identifier") {
1003
+ return lookupBindingFromAncestors(node.name, ancestors, bindings);
1004
+ }
1005
+ if (node.type === "CallExpression") {
1006
+ return selectorFromQueryCall(node, scope);
1007
+ }
1008
+ if (node.type === "ArrayExpression") {
1009
+ const parts = node.elements.map((el) => resolveTargetSelector(el, ancestors, scope, bindings)).filter((s) => typeof s === "string" && s.length > 0);
1010
+ return parts.length > 0 ? parts.join(", ") : null;
1011
+ }
1012
+ if (node.type === "MemberExpression" && node.object?.type === "Identifier") {
1013
+ return lookupBindingFromAncestors(node.object.name, ancestors, bindings);
1014
+ }
1015
+ return null;
1016
+ }
1017
+ function isObjectProperty(prop) {
1018
+ return prop?.type === "ObjectProperty" || prop?.type === "Property";
1019
+ }
1020
+ function propKeyName(prop) {
1021
+ return prop?.key?.name ?? prop?.key?.value;
1022
+ }
1023
+ function findPropertyNode(varsArgNode, key) {
1024
+ if (varsArgNode?.type !== "ObjectExpression") return void 0;
1025
+ for (const prop of varsArgNode.properties ?? []) {
1026
+ if (!isObjectProperty(prop)) continue;
1027
+ if (propKeyName(prop) === key) return prop.value;
1028
+ }
1029
+ return void 0;
1030
+ }
1031
+ function extractRawPropertySource(varsArgNode, key, source) {
1032
+ const node = findPropertyNode(varsArgNode, key);
1033
+ return node ? source.slice(node.start, node.end) : void 0;
1034
+ }
1035
+ function objectExpressionToRecord(node, scope, source) {
1036
+ const result = {};
1037
+ if (node?.type !== "ObjectExpression") return result;
1038
+ for (const prop of node.properties ?? []) {
1039
+ if (!isObjectProperty(prop)) continue;
1040
+ const key = prop.key?.name ?? prop.key?.value;
1041
+ if (!key) continue;
1042
+ const resolved = resolveNode(prop.value, scope);
1043
+ if (resolved !== void 0) {
1044
+ result[key] = resolved;
1045
+ } else {
1046
+ result[key] = `__raw:${source.slice(prop.value.start, prop.value.end)}`;
1047
+ }
1048
+ }
1049
+ return result;
1050
+ }
1051
+ function isGsapTimelineCall(node) {
1052
+ return node?.type === "CallExpression" && node.callee?.type === "MemberExpression" && node.callee.object?.name === "gsap" && node.callee.property?.name === "timeline";
1053
+ }
1054
+ function extractTimelineDefaults(callNode, scope) {
1055
+ const arg = callNode.arguments?.[0];
1056
+ if (!arg || arg.type !== "ObjectExpression") return void 0;
1057
+ const defaultsProp = arg.properties?.find(
1058
+ (p) => isObjectProperty(p) && propKeyName(p) === "defaults"
1059
+ );
1060
+ if (!defaultsProp?.value || defaultsProp.value.type !== "ObjectExpression") return void 0;
1061
+ const result = {};
1062
+ for (const prop of defaultsProp.value.properties ?? []) {
1063
+ if (!isObjectProperty(prop)) continue;
1064
+ const key = propKeyName(prop);
1065
+ const val = resolveNode(prop.value, scope);
1066
+ if (key === "ease" && typeof val === "string") result.ease = val;
1067
+ if (key === "duration" && typeof val === "number") result.duration = val;
1068
+ }
1069
+ return Object.keys(result).length > 0 ? result : void 0;
1070
+ }
1071
+ function findTimelineVar(ast, scope) {
1072
+ let timelineVar = null;
1073
+ let timelineCount = 0;
1074
+ let defaults;
1075
+ const emptyScope = scope ?? /* @__PURE__ */ new Map();
1076
+ acornWalk.simple(ast, {
1077
+ VariableDeclarator(node) {
1078
+ if (isGsapTimelineCall(node.init)) {
1079
+ timelineCount += 1;
1080
+ if (!timelineVar) {
1081
+ timelineVar = node.id?.name ?? null;
1082
+ defaults = extractTimelineDefaults(node.init, emptyScope);
1083
+ }
1084
+ }
1085
+ },
1086
+ AssignmentExpression(node) {
1087
+ if (isGsapTimelineCall(node.right)) {
1088
+ timelineCount += 1;
1089
+ if (!timelineVar) {
1090
+ const left = node.left;
1091
+ if (left?.type === "Identifier") timelineVar = left.name;
1092
+ defaults = extractTimelineDefaults(node.right, emptyScope);
1093
+ }
1094
+ }
1095
+ }
1096
+ });
1097
+ return { timelineVar, timelineCount, defaults };
1098
+ }
1099
+ var BUILTIN_VAR_KEYS = /* @__PURE__ */ new Set(["duration", "ease", "delay"]);
1100
+ var DROPPED_VAR_KEYS = /* @__PURE__ */ new Set(["onComplete", "onStart", "onUpdate", "onRepeat"]);
1101
+ var EXTRAS_KEYS = /* @__PURE__ */ new Set([
1102
+ "stagger",
1103
+ "yoyo",
1104
+ "repeat",
1105
+ "repeatDelay",
1106
+ "snap",
1107
+ "overwrite",
1108
+ "immediateRender"
1109
+ ]);
1110
+ function isTimelineRootedCall(callNode, timelineVar) {
1111
+ let obj = callNode.callee?.object;
1112
+ while (obj?.type === "CallExpression") {
1113
+ obj = obj.callee?.object;
1114
+ }
1115
+ return obj?.type === "Identifier" && obj.name === timelineVar;
1116
+ }
1117
+ function findAllTweenCalls(ast, timelineVar, scope, targetBindings) {
1118
+ const results = [];
1119
+ function visit(node, ancestors) {
1120
+ if (!node || typeof node !== "object") return;
1121
+ const nodeAncestors = [...ancestors, node];
1122
+ if (node.type === "CallExpression") {
1123
+ const callee = node.callee;
1124
+ const gsapSetArg = node.arguments?.[0];
1125
+ const isGlobalSet = callee?.type === "MemberExpression" && callee.object?.type === "Identifier" && callee.object.name === "gsap" && callee.property?.type === "Identifier" && callee.property.name === "set" && (gsapSetArg?.type === "StringLiteral" || gsapSetArg?.type === "Literal" && typeof gsapSetArg.value === "string");
1126
+ if (callee?.type === "MemberExpression" && callee.property?.type === "Identifier" && (isTimelineRootedCall(node, timelineVar) || isGlobalSet) && GSAP_METHODS2.has(callee.property.name)) {
1127
+ const method = callee.property.name;
1128
+ const args = node.arguments;
1129
+ const selectorValue = args.length >= 1 ? resolveTargetSelector(args[0], nodeAncestors, scope, targetBindings) ?? "__unresolved__" : "__unresolved__";
1130
+ if (method === "fromTo" && args.length >= 3) {
1131
+ results.push({
1132
+ node,
1133
+ ancestors: nodeAncestors,
1134
+ method: "fromTo",
1135
+ selector: selectorValue,
1136
+ fromArg: args[1],
1137
+ varsArg: args[2],
1138
+ positionArg: args[3]
1139
+ });
1140
+ } else if (method !== "fromTo" && args.length >= 2) {
1141
+ results.push({
1142
+ node,
1143
+ ancestors: nodeAncestors,
1144
+ method,
1145
+ selector: selectorValue,
1146
+ varsArg: args[1],
1147
+ positionArg: args[2],
1148
+ ...isGlobalSet ? { global: true } : {}
1149
+ });
1150
+ }
1151
+ }
1152
+ }
1153
+ for (const key of Object.keys(node)) {
1154
+ if (key === "type" || key === "start" || key === "end" || key === "loc") continue;
1155
+ const child = node[key];
1156
+ if (Array.isArray(child)) {
1157
+ for (const item of child) {
1158
+ if (item && typeof item === "object" && item.type) visit(item, nodeAncestors);
1159
+ }
1160
+ } else if (child && typeof child === "object" && child.type) {
1161
+ visit(child, nodeAncestors);
1162
+ }
1163
+ }
1164
+ }
1165
+ visit(ast, []);
1166
+ return results;
1167
+ }
1168
+ var PERCENTAGE_KEY_RE = /^(\d+(?:\.\d+)?)%$/;
1169
+ function tryResolveStringProp(propValue, scope) {
1170
+ const val = resolveNode(propValue, scope);
1171
+ return typeof val === "string" ? val : void 0;
1172
+ }
1173
+ function parsePercentageKeyframes(node, scope, source) {
1174
+ const keyframes = [];
1175
+ let ease;
1176
+ let easeEach;
1177
+ for (const prop of node.properties ?? []) {
1178
+ if (prop.type !== "ObjectProperty" && prop.type !== "Property") continue;
1179
+ const key = prop.key?.value ?? prop.key?.name;
1180
+ if (typeof key !== "string") continue;
1181
+ const pctMatch = PERCENTAGE_KEY_RE.exec(key);
1182
+ if (pctMatch) {
1183
+ const percentage = Number.parseFloat(pctMatch[1] ?? "0");
1184
+ const record = objectExpressionToRecord(prop.value, scope, source);
1185
+ const properties = {};
1186
+ let kfEase;
1187
+ for (const [k, v] of Object.entries(record)) {
1188
+ if (k === "ease" && typeof v === "string") {
1189
+ kfEase = v;
1190
+ } else if (typeof v === "number" || typeof v === "string") {
1191
+ properties[k] = v;
1192
+ }
1193
+ }
1194
+ keyframes.push({ percentage, properties, ...kfEase ? { ease: kfEase } : {} });
1195
+ } else if (key === "ease") {
1196
+ ease = tryResolveStringProp(prop.value, scope) ?? ease;
1197
+ } else if (key === "easeEach") {
1198
+ easeEach = tryResolveStringProp(prop.value, scope) ?? easeEach;
1199
+ }
1200
+ }
1201
+ keyframes.sort((a, b) => a.percentage - b.percentage);
1202
+ return {
1203
+ format: "percentage",
1204
+ keyframes,
1205
+ ...ease ? { ease } : {},
1206
+ ...easeEach ? { easeEach } : {}
1207
+ };
1208
+ }
1209
+ function computeKeyframesTotalDuration(varsNode, scope, source) {
1210
+ const kfNode = (varsNode.properties ?? []).find(
1211
+ (p) => (p.key?.name ?? p.key?.value) === "keyframes"
1212
+ )?.value;
1213
+ if (!kfNode || kfNode.type !== "ArrayExpression") return void 0;
1214
+ let total = 0;
1215
+ for (const el of kfNode.elements ?? []) {
1216
+ if (!el || el.type !== "ObjectExpression") continue;
1217
+ const r = objectExpressionToRecord(el, scope, source);
1218
+ if (typeof r.duration === "number") total += r.duration;
1219
+ }
1220
+ return total > 0 ? total : void 0;
1221
+ }
1222
+ function parseObjectArrayKeyframes(node, scope, source) {
1223
+ const elements = node.elements ?? [];
1224
+ const raw = [];
1225
+ for (const el of elements) {
1226
+ if (!el || el.type !== "ObjectExpression") continue;
1227
+ const record = objectExpressionToRecord(el, scope, source);
1228
+ const properties = {};
1229
+ let duration;
1230
+ let ease;
1231
+ for (const [k, v] of Object.entries(record)) {
1232
+ if (k === "duration" && typeof v === "number") {
1233
+ duration = v;
1234
+ } else if (k === "ease" && typeof v === "string") {
1235
+ ease = v;
1236
+ } else if (typeof v === "number" || typeof v === "string") {
1237
+ properties[k] = v;
1238
+ }
1239
+ }
1240
+ raw.push({ properties, duration, ease });
1241
+ }
1242
+ const totalDuration = raw.reduce((sum, r) => sum + (r.duration ?? 0), 0);
1243
+ const keyframes = [];
1244
+ if (totalDuration > 0) {
1245
+ let cumulative = 0;
1246
+ for (const entry of raw) {
1247
+ cumulative += entry.duration ?? 0;
1248
+ const percentage = Math.round(cumulative / totalDuration * 100);
1249
+ keyframes.push({
1250
+ percentage,
1251
+ properties: entry.properties,
1252
+ ...entry.ease ? { ease: entry.ease } : {}
1253
+ });
1254
+ }
1255
+ } else {
1256
+ for (let i = 0; i < raw.length; i++) {
1257
+ const entry = raw[i];
1258
+ if (!entry) continue;
1259
+ const percentage = raw.length > 1 ? Math.round(i / (raw.length - 1) * 100) : 0;
1260
+ keyframes.push({
1261
+ percentage,
1262
+ properties: entry.properties,
1263
+ ...entry.ease ? { ease: entry.ease } : {}
1264
+ });
1265
+ }
1266
+ }
1267
+ return { format: "object-array", keyframes };
1268
+ }
1269
+ function parseSimpleArrayKeyframes(node, scope) {
1270
+ const arrayProps = /* @__PURE__ */ new Map();
1271
+ let ease;
1272
+ let easeEach;
1273
+ for (const prop of node.properties ?? []) {
1274
+ if (prop.type !== "ObjectProperty" && prop.type !== "Property") continue;
1275
+ const key = prop.key?.name ?? prop.key?.value;
1276
+ if (typeof key !== "string") continue;
1277
+ if (prop.value?.type === "ArrayExpression") {
1278
+ const values = [];
1279
+ for (const el of prop.value.elements ?? []) {
1280
+ const val = resolveNode(el, scope);
1281
+ if (typeof val === "number" || typeof val === "string") {
1282
+ values.push(val);
1283
+ }
1284
+ }
1285
+ if (values.length > 0) arrayProps.set(key, values);
1286
+ } else if (key === "ease") {
1287
+ ease = tryResolveStringProp(prop.value, scope) ?? ease;
1288
+ } else if (key === "easeEach") {
1289
+ easeEach = tryResolveStringProp(prop.value, scope) ?? easeEach;
1290
+ }
1291
+ }
1292
+ const maxLen = Math.max(...[...arrayProps.values()].map((a) => a.length), 0);
1293
+ const keyframes = [];
1294
+ for (let i = 0; i < maxLen; i++) {
1295
+ const percentage = maxLen > 1 ? Math.round(i / (maxLen - 1) * 100) : 0;
1296
+ const properties = {};
1297
+ for (const [key, values] of arrayProps) {
1298
+ if (i < values.length) properties[key] = values[i];
1299
+ }
1300
+ keyframes.push({ percentage, properties });
1301
+ }
1302
+ return {
1303
+ format: "simple-array",
1304
+ keyframes,
1305
+ ...ease ? { ease } : {},
1306
+ ...easeEach ? { easeEach } : {}
1307
+ };
1308
+ }
1309
+ function parseKeyframesNode(node, scope, source) {
1310
+ if (!node) return void 0;
1311
+ if (node.type === "ArrayExpression") {
1312
+ return parseObjectArrayKeyframes(node, scope, source);
1313
+ }
1314
+ if (node.type !== "ObjectExpression") return void 0;
1315
+ const props = node.properties ?? [];
1316
+ let hasPercentageKey = false;
1317
+ let hasArrayValue = false;
1318
+ for (const prop of props) {
1319
+ if (prop.type !== "ObjectProperty" && prop.type !== "Property") continue;
1320
+ const key = prop.key?.value ?? prop.key?.name;
1321
+ if (typeof key === "string" && PERCENTAGE_KEY_RE.test(key)) {
1322
+ hasPercentageKey = true;
1323
+ break;
1324
+ }
1325
+ if (prop.value?.type === "ArrayExpression") {
1326
+ hasArrayValue = true;
1327
+ }
1328
+ }
1329
+ if (hasPercentageKey) return parsePercentageKeyframes(node, scope, source);
1330
+ if (hasArrayValue) return parseSimpleArrayKeyframes(node, scope);
1331
+ return void 0;
1332
+ }
1333
+ function parseMotionPathNode(node, scope, source) {
1334
+ if (!node) return void 0;
1335
+ let pathNode;
1336
+ let autoRotate = false;
1337
+ let curviness = 1;
1338
+ let isCubic = false;
1339
+ if (node.type === "ObjectExpression") {
1340
+ for (const prop of node.properties ?? []) {
1341
+ if (!isObjectProperty(prop)) continue;
1342
+ const key = propKeyName(prop);
1343
+ if (key === "path") pathNode = prop.value;
1344
+ else if (key === "autoRotate") {
1345
+ const val = resolveNode(prop.value, scope);
1346
+ autoRotate = typeof val === "number" ? val : val === true;
1347
+ } else if (key === "curviness") {
1348
+ const val = resolveNode(prop.value, scope);
1349
+ if (typeof val === "number") curviness = val;
1350
+ } else if (key === "type") {
1351
+ const val = resolveNode(prop.value, scope);
1352
+ if (val === "cubic") isCubic = true;
1353
+ }
1354
+ }
1355
+ } else if (node.type === "ArrayExpression") {
1356
+ pathNode = node;
1357
+ }
1358
+ if (!pathNode || pathNode.type !== "ArrayExpression") return void 0;
1359
+ const elements = pathNode.elements ?? [];
1360
+ const coords = [];
1361
+ for (const elem of elements) {
1362
+ if (!elem || elem.type !== "ObjectExpression") continue;
1363
+ const rec = objectExpressionToRecord(elem, scope, source);
1364
+ const x = typeof rec.x === "number" ? rec.x : void 0;
1365
+ const y = typeof rec.y === "number" ? rec.y : void 0;
1366
+ if (x !== void 0 && y !== void 0) coords.push({ x, y });
1367
+ }
1368
+ return buildArcPath(coords, curviness, autoRotate, isCubic);
1369
+ }
1370
+ function tweenCallToAnimation(call, scope, source) {
1371
+ const vars = objectExpressionToRecord(call.varsArg, scope, source);
1372
+ const properties = {};
1373
+ const extras = {};
1374
+ let keyframesData;
1375
+ let hasUnresolvedKeyframes = false;
1376
+ let motionPathResult;
1377
+ for (const [key, val] of Object.entries(vars)) {
1378
+ if (BUILTIN_VAR_KEYS.has(key)) continue;
1379
+ if (DROPPED_VAR_KEYS.has(key)) continue;
1380
+ if (key === "keyframes") {
1381
+ const kfNode = findPropertyNode(call.varsArg, "keyframes");
1382
+ keyframesData = parseKeyframesNode(kfNode, scope, source);
1383
+ if (!keyframesData && kfNode) hasUnresolvedKeyframes = true;
1384
+ continue;
1385
+ }
1386
+ if (key === "motionPath") {
1387
+ const mpNode = findPropertyNode(call.varsArg, "motionPath");
1388
+ motionPathResult = parseMotionPathNode(mpNode, scope, source);
1389
+ continue;
1390
+ }
1391
+ if (key === "easeEach") continue;
1392
+ if (EXTRAS_KEYS.has(key)) {
1393
+ const rawSource = extractRawPropertySource(call.varsArg, key, source);
1394
+ if (rawSource !== void 0) {
1395
+ extras[key] = `__raw:${rawSource}`;
1396
+ } else if (val !== void 0) {
1397
+ extras[key] = val;
1398
+ }
1399
+ continue;
1400
+ }
1401
+ if (typeof val === "number" || typeof val === "string") {
1402
+ properties[key] = val;
1403
+ }
1404
+ }
1405
+ if (keyframesData && typeof vars.easeEach === "string") {
1406
+ keyframesData.easeEach = vars.easeEach;
1407
+ }
1408
+ if (motionPathResult) {
1409
+ const { waypoints } = motionPathResult;
1410
+ if (!keyframesData) {
1411
+ const kf = waypoints.map((wp, i) => ({
1412
+ percentage: waypoints.length > 1 ? Math.round(i / (waypoints.length - 1) * 100) : 0,
1413
+ properties: { x: wp.x, y: wp.y }
1414
+ }));
1415
+ keyframesData = { format: "percentage", keyframes: kf };
1416
+ } else {
1417
+ const kfs = keyframesData.keyframes;
1418
+ if (kfs.length === waypoints.length) {
1419
+ for (let i = 0; i < kfs.length; i++) {
1420
+ const kf = kfs[i];
1421
+ const wp = waypoints[i];
1422
+ if (kf && wp) {
1423
+ kf.properties.x = wp.x;
1424
+ kf.properties.y = wp.y;
1425
+ }
1426
+ }
1427
+ }
1428
+ }
1429
+ }
1430
+ let fromProperties;
1431
+ if (call.method === "fromTo" && call.fromArg) {
1432
+ fromProperties = {};
1433
+ const fromVars = objectExpressionToRecord(call.fromArg, scope, source);
1434
+ for (const [key, val] of Object.entries(fromVars)) {
1435
+ if (typeof val === "number" || typeof val === "string") {
1436
+ fromProperties[key] = val;
1437
+ }
1438
+ }
1439
+ }
1440
+ const hasPositionArg = !!call.positionArg;
1441
+ const posVal = hasPositionArg ? extractLiteralValue(call.positionArg, scope) : 0;
1442
+ const position = typeof posVal === "number" ? posVal : typeof posVal === "string" ? posVal : 0;
1443
+ let duration = typeof vars.duration === "number" ? vars.duration : void 0;
1444
+ const ease = typeof vars.ease === "string" ? vars.ease : void 0;
1445
+ if (duration === void 0 && keyframesData) {
1446
+ duration = computeKeyframesTotalDuration(call.varsArg, scope, source);
1447
+ }
1448
+ const anim = {
1449
+ targetSelector: call.selector,
1450
+ method: call.method,
1451
+ position,
1452
+ properties,
1453
+ fromProperties,
1454
+ duration,
1455
+ ease
1456
+ };
1457
+ if (!hasPositionArg) anim.implicitPosition = true;
1458
+ let group = classifyTweenPropertyGroup(properties);
1459
+ if (!group && keyframesData) {
1460
+ const kfProps = {};
1461
+ for (const kf of keyframesData.keyframes) {
1462
+ for (const k of Object.keys(kf.properties)) kfProps[k] = true;
1463
+ }
1464
+ group = classifyTweenPropertyGroup(kfProps);
1465
+ }
1466
+ if (group) anim.propertyGroup = group;
1467
+ if (call.global) anim.global = true;
1468
+ if (Object.keys(extras).length > 0) anim.extras = extras;
1469
+ if (keyframesData) anim.keyframes = keyframesData;
1470
+ if (motionPathResult) anim.arcPath = motionPathResult.arcPath;
1471
+ if (hasUnresolvedKeyframes) anim.hasUnresolvedKeyframes = true;
1472
+ if (call.selector === "__unresolved__") anim.hasUnresolvedSelector = true;
1473
+ const provenance = readProvenance(call.node);
1474
+ if (provenance) anim.provenance = provenance;
1475
+ return anim;
1476
+ }
1477
+ var GSAP_DEFAULT_DURATION = 0.5;
1478
+ function resolvePositionString(pos, cursor, prevStart) {
1479
+ const trimmed = pos.trim();
1480
+ if (trimmed === "") return cursor;
1481
+ if (trimmed.startsWith("+=")) {
1482
+ const n2 = Number.parseFloat(trimmed.slice(2));
1483
+ return Number.isFinite(n2) ? cursor + n2 : null;
1484
+ }
1485
+ if (trimmed.startsWith("-=")) {
1486
+ const n2 = Number.parseFloat(trimmed.slice(2));
1487
+ return Number.isFinite(n2) ? cursor - n2 : null;
1488
+ }
1489
+ if (trimmed === "<") return prevStart;
1490
+ if (trimmed === ">") return cursor;
1491
+ if (trimmed.startsWith("<")) {
1492
+ const n2 = Number.parseFloat(trimmed.slice(1));
1493
+ return Number.isFinite(n2) ? prevStart + n2 : null;
1494
+ }
1495
+ if (trimmed.startsWith(">")) {
1496
+ const n2 = Number.parseFloat(trimmed.slice(1));
1497
+ return Number.isFinite(n2) ? cursor + n2 : null;
1498
+ }
1499
+ const n = Number.parseFloat(trimmed);
1500
+ return Number.isFinite(n) ? n : null;
1501
+ }
1502
+ function applyTimelineDefaults(anims, defaults) {
1503
+ if (!defaults) return;
1504
+ for (const anim of anims) {
1505
+ if (anim.method === "set") continue;
1506
+ if (anim.duration === void 0 && defaults.duration !== void 0) {
1507
+ anim.duration = defaults.duration;
1508
+ }
1509
+ if (anim.ease === void 0 && defaults.ease !== void 0) {
1510
+ anim.ease = defaults.ease;
1511
+ }
1512
+ }
1513
+ }
1514
+ function resolveTimelinePositions(anims) {
1515
+ let cursor = 0;
1516
+ let prevStart = 0;
1517
+ for (const anim of anims) {
1518
+ if (anim.method === "set" && anim.global) {
1519
+ anim.resolvedStart = 0;
1520
+ continue;
1521
+ }
1522
+ const duration = anim.method === "set" ? 0 : anim.duration ?? GSAP_DEFAULT_DURATION;
1523
+ let start;
1524
+ if (anim.implicitPosition) {
1525
+ start = cursor;
1526
+ } else if (typeof anim.position === "number") {
1527
+ start = anim.position;
1528
+ } else if (typeof anim.position === "string") {
1529
+ start = resolvePositionString(anim.position, cursor, prevStart);
1530
+ } else {
1531
+ start = cursor;
1532
+ }
1533
+ if (start != null) {
1534
+ anim.resolvedStart = Math.max(0, start);
1535
+ prevStart = anim.resolvedStart;
1536
+ cursor = Math.max(cursor, anim.resolvedStart + duration);
1537
+ }
1538
+ }
1539
+ }
1540
+ function compareByLoc(a, b) {
1541
+ const aLoc = a.node.callee?.property?.loc?.start;
1542
+ const bLoc = b.node.callee?.property?.loc?.start;
1543
+ if (!aLoc || !bLoc) return 0;
1544
+ return aLoc.line - bLoc.line || aLoc.column - bLoc.column;
1545
+ }
1546
+ function compareCallOrder(a, b) {
1547
+ const ao = a.node.__hfOrder;
1548
+ const bo = b.node.__hfOrder;
1549
+ if (ao === void 0 && bo === void 0) return compareByLoc(a, b);
1550
+ if (ao === void 0) return -1;
1551
+ if (bo === void 0) return 1;
1552
+ return ao - bo;
1553
+ }
1554
+ function sortBySourcePosition(calls) {
1555
+ calls.sort(compareCallOrder);
1556
+ }
1557
+ function assignStableIds(anims) {
1558
+ const counts = /* @__PURE__ */ new Map();
1559
+ return anims.map((anim) => {
1560
+ const posKey = typeof anim.position === "number" ? String(Math.round(anim.position * 1e3)) : String(anim.position);
1561
+ const groupSuffix = anim.propertyGroup ? `-${anim.propertyGroup}` : "";
1562
+ const base = `${anim.targetSelector}-${anim.method}-${posKey}${groupSuffix}`;
1563
+ const count = (counts.get(base) ?? 0) + 1;
1564
+ counts.set(base, count);
1565
+ const id = count === 1 ? base : `${base}-${count}`;
1566
+ return { ...anim, id };
1567
+ });
1568
+ }
1569
+ function parseGsapScriptAcornForWrite(script) {
1570
+ try {
1571
+ const ast = acorn.parse(script, {
1572
+ ecmaVersion: "latest",
1573
+ sourceType: "script",
1574
+ locations: true
1575
+ });
1576
+ const scope = collectScopeBindings(ast);
1577
+ const targetBindings = collectTargetBindings(ast, scope);
1578
+ const detection = findTimelineVar(ast, scope);
1579
+ const timelineVar = detection.timelineVar ?? "tl";
1580
+ const calls = findAllTweenCalls(ast, timelineVar, scope, targetBindings);
1581
+ sortBySourcePosition(calls);
1582
+ const rawAnims = calls.map((call) => tweenCallToAnimation(call, scope, script));
1583
+ applyTimelineDefaults(rawAnims, detection.defaults);
1584
+ resolveTimelinePositions(rawAnims);
1585
+ const animations = assignStableIds(rawAnims);
1586
+ const located = calls.map((call, i) => ({
1587
+ id: animations[i].id,
1588
+ call,
1589
+ animation: animations[i]
1590
+ }));
1591
+ return { ast, timelineVar, hasTimeline: detection.timelineVar !== null, located };
1592
+ } catch {
1593
+ return null;
1594
+ }
1595
+ }
1596
+ function parseGsapScriptAcorn(script) {
1597
+ try {
1598
+ const ast = acorn.parse(script, {
1599
+ ecmaVersion: "latest",
1600
+ sourceType: "script",
1601
+ locations: true
1602
+ });
1603
+ const scope = collectScopeBindings(ast);
1604
+ const detection = findTimelineVar(ast, scope);
1605
+ const timelineVar = detection.timelineVar ?? "tl";
1606
+ try {
1607
+ inlineComputedTimelines(ast, timelineVar, (node) => resolveNode(node, scope));
1608
+ } catch {
1609
+ }
1610
+ const targetBindings = collectTargetBindings(ast, scope);
1611
+ const calls = findAllTweenCalls(ast, timelineVar, scope, targetBindings);
1612
+ sortBySourcePosition(calls);
1613
+ const rawAnims = calls.map((call) => tweenCallToAnimation(call, scope, script));
1614
+ applyTimelineDefaults(rawAnims, detection.defaults);
1615
+ resolveTimelinePositions(rawAnims);
1616
+ const animations = assignStableIds(rawAnims);
1617
+ const timelineMatch = script.match(
1618
+ new RegExp(
1619
+ `^[\\s\\S]*?(?:const|let|var)\\s+${timelineVar}\\s*=\\s*gsap\\.timeline\\s*\\([^)]*\\)\\s*;?`
1620
+ )
1621
+ );
1622
+ const preamble = timelineMatch?.[0] ?? `const ${timelineVar} = gsap.timeline({ paused: true });`;
1623
+ const lastCallIdx = script.lastIndexOf(`${timelineVar}.`);
1624
+ let postamble = "";
1625
+ if (lastCallIdx !== -1) {
1626
+ const afterLast = script.slice(lastCallIdx);
1627
+ const endOfCall = afterLast.indexOf(";");
1628
+ if (endOfCall !== -1) {
1629
+ postamble = script.slice(lastCallIdx + endOfCall + 1).trim();
1630
+ }
1631
+ }
1632
+ const result = { animations, timelineVar, preamble, postamble };
1633
+ if (detection.timelineCount > 1) result.multipleTimelines = true;
1634
+ if (detection.timelineCount > 0 && detection.timelineVar === null)
1635
+ result.unsupportedTimelinePattern = true;
1636
+ return result;
1637
+ } catch {
1638
+ return { animations: [], timelineVar: "tl", preamble: "", postamble: "" };
1639
+ }
1640
+ }
1641
+
1642
+ // src/hfIds.ts
1643
+ import { parseHTML } from "linkedom";
1644
+ var EXCLUDED_TAGS = /* @__PURE__ */ new Set([
1645
+ "script",
1646
+ "style",
1647
+ "template",
1648
+ "meta",
1649
+ "link",
1650
+ "noscript",
1651
+ "base"
1652
+ ]);
1653
+ function fnv1a(str) {
1654
+ let h = 2166136261;
1655
+ for (let i = 0; i < str.length; i++) {
1656
+ h ^= str.charCodeAt(i);
1657
+ h = Math.imul(h, 16777619);
1658
+ }
1659
+ return h >>> 0;
1660
+ }
1661
+ function toHfId(hash) {
1662
+ const s = (hash >>> 0).toString(36);
1663
+ const four = s.length >= 4 ? s.slice(-4) : s.padStart(4, "0");
1664
+ return `hf-${four}`;
1665
+ }
1666
+ function ownText(el) {
1667
+ let text = "";
1668
+ el.childNodes.forEach((n) => {
1669
+ if (n.nodeType === 3) text += n.nodeValue ?? "";
1670
+ });
1671
+ return text.trim();
1672
+ }
1673
+ function contentKey(el) {
1674
+ const attrs = Array.from(el.attributes).filter((a) => !a.name.startsWith("data-hf-")).map((a) => `${a.name}\0${a.value}`).sort().join("");
1675
+ return `${el.tagName.toLowerCase()}|${attrs}|${ownText(el)}`;
1676
+ }
1677
+ function mintHfId(el, assigned) {
1678
+ const key = contentKey(el);
1679
+ let id = toHfId(fnv1a(key));
1680
+ let dup = 0;
1681
+ while (assigned.has(id)) {
1682
+ dup += 1;
1683
+ if (dup > 1e4) {
1684
+ id = `hf-${(fnv1a(key) >>> 0).toString(36)}-${dup}`;
1685
+ break;
1686
+ }
1687
+ id = toHfId(fnv1a(`${key}#${dup}`));
1688
+ }
1689
+ assigned.add(id);
1690
+ return id;
1691
+ }
1692
+ function ensureHfIds(html) {
1693
+ const hasDocumentShell = /<!doctype|<html[\s>]/i.test(html);
1694
+ const wrapped = !hasDocumentShell;
1695
+ const { document } = wrapped ? parseHTML(`<!DOCTYPE html><html><head></head><body>${html}</body></html>`) : parseHTML(html);
1696
+ const body = document.body;
1697
+ if (!body) return html;
1698
+ const assigned = /* @__PURE__ */ new Set();
1699
+ for (const el of Array.from(body.querySelectorAll("[data-hf-id]"))) {
1700
+ const existing = el.getAttribute("data-hf-id");
1701
+ if (existing) assigned.add(existing);
1702
+ }
1703
+ for (const el of Array.from(body.querySelectorAll("*"))) {
1704
+ if (EXCLUDED_TAGS.has(el.tagName.toLowerCase())) continue;
1705
+ if (el.getAttribute("data-hf-id")) continue;
1706
+ el.setAttribute("data-hf-id", mintHfId(el, assigned));
1707
+ }
1708
+ return wrapped ? document.body.innerHTML || "" : document.toString();
1709
+ }
1710
+
1711
+ // src/utils/cssSelector.ts
1712
+ function queryByAttr(root, attr, value, tag) {
1713
+ const selector = tag ? `${tag}[${attr}]` : `[${attr}]`;
1714
+ for (const el of root.querySelectorAll(selector)) {
1715
+ if (el.getAttribute(attr) === value) return el;
1716
+ }
1717
+ return null;
1718
+ }
1719
+
1720
+ // src/gsapWriterAcorn.ts
1721
+ import MagicString from "magic-string";
1722
+ import * as acornWalk2 from "acorn-walk";
1723
+ function findEnclosingExpressionStatement(ancestors) {
1724
+ for (let i = ancestors.length - 2; i >= 0; i--) {
1725
+ if (ancestors[i]?.type === "ExpressionStatement") return ancestors[i];
1726
+ }
1727
+ return null;
1728
+ }
1729
+ function removeAnimationFromScript(script, animationId) {
1730
+ const parsed = parseGsapScriptAcornForWrite(script);
1731
+ if (!parsed) return script;
1732
+ const target = parsed.located.find((l) => l.id === animationId);
1733
+ if (!target) return script;
1734
+ const ms = new MagicString(script);
1735
+ const N = target.call.node;
1736
+ const exprStmt = findEnclosingExpressionStatement(target.call.ancestors);
1737
+ if (N.callee?.object?.type !== "CallExpression" && exprStmt?.expression === N) {
1738
+ const end = exprStmt.end < script.length && script[exprStmt.end] === "\n" ? exprStmt.end + 1 : exprStmt.end;
1739
+ ms.remove(exprStmt.start, end);
1740
+ } else {
1741
+ ms.remove(N.callee.object.end, N.end);
1742
+ }
1743
+ return ms.toString();
1744
+ }
1745
+
1746
+ // src/htmlParser.ts
1747
+ var MEDIA_TYPES = /* @__PURE__ */ new Set(["video", "image", "audio"]);
1748
+ function getElementType(el) {
1749
+ const tag = el.tagName.toLowerCase();
1750
+ if (tag === "video") return "video";
1751
+ if (tag === "img") return "image";
1752
+ if (tag === "audio") return "audio";
1753
+ const dataType = el.getAttribute("data-type");
1754
+ if (dataType === "composition") return "composition";
1755
+ if (dataType === "text") return "text";
1756
+ if (tag === "div" || tag === "p" || tag === "h1" || tag === "h2" || tag === "h3" || tag === "span") {
1757
+ return "text";
1758
+ }
1759
+ return null;
1760
+ }
1761
+ function getElementName(el) {
1762
+ const dataName = el.getAttribute("data-name");
1763
+ if (dataName) return dataName;
1764
+ const type = getElementType(el);
1765
+ if (type === "text") {
1766
+ const text = el.textContent?.trim().slice(0, 30) || "Text";
1767
+ return text.length === 30 ? text + "..." : text;
1768
+ }
1769
+ const src = el.getAttribute("src");
1770
+ if (src) {
1771
+ const filename = src.split("/").pop() || src;
1772
+ return filename.split("?")[0] ?? filename;
1773
+ }
1774
+ return el.id || el.className?.toString().split(" ")[0] || "Element";
1775
+ }
1776
+ function getZIndex(el) {
1777
+ const dataLayer = el.getAttribute("data-layer");
1778
+ if (dataLayer) return parseInt(dataLayer, 10) || 0;
1779
+ const style = el.style?.zIndex;
1780
+ if (style) return parseInt(style, 10) || 0;
1781
+ return 0;
1782
+ }
1783
+ function parseResolutionFromCss(doc, cssText) {
1784
+ const stage = doc.getElementById("stage") || doc.querySelector("#stage");
1785
+ if (stage) {
1786
+ const inlineStyle = stage.style;
1787
+ if (inlineStyle?.width && inlineStyle?.height) {
1788
+ const w = parseInt(inlineStyle.width, 10);
1789
+ const h = parseInt(inlineStyle.height, 10);
1790
+ if (w && h) {
1791
+ return resolveResolutionFromDimensions(w, h);
1792
+ }
1793
+ }
1794
+ }
1795
+ if (cssText) {
1796
+ const stageMatch = cssText.match(
1797
+ /#stage\s*\{[^}]*width:\s*(\d+)px[^}]*height:\s*(\d+)px[^}]*\}/
1798
+ );
1799
+ if (stageMatch) {
1800
+ const w = parseInt(stageMatch[1] ?? "", 10);
1801
+ const h = parseInt(stageMatch[2] ?? "", 10);
1802
+ return resolveResolutionFromDimensions(w, h);
1803
+ }
1804
+ const stageMatchReverse = cssText.match(
1805
+ /#stage\s*\{[^}]*height:\s*(\d+)px[^}]*width:\s*(\d+)px[^}]*\}/
1806
+ );
1807
+ if (stageMatchReverse) {
1808
+ const h = parseInt(stageMatchReverse[1] ?? "", 10);
1809
+ const w = parseInt(stageMatchReverse[2] ?? "", 10);
1810
+ return resolveResolutionFromDimensions(w, h);
1811
+ }
1812
+ }
1813
+ return "portrait";
1814
+ }
1815
+ function parseResolutionFromHtml(doc) {
1816
+ const htmlEl = doc.documentElement;
1817
+ const resolutionAttr = htmlEl.getAttribute("data-resolution");
1818
+ if (resolutionAttr === "landscape" || resolutionAttr === "portrait" || resolutionAttr === "landscape-4k" || resolutionAttr === "portrait-4k" || resolutionAttr === "square" || resolutionAttr === "square-4k") {
1819
+ return resolutionAttr;
1820
+ }
1821
+ const widthAttr = htmlEl.getAttribute("data-composition-width");
1822
+ const heightAttr = htmlEl.getAttribute("data-composition-height");
1823
+ if (widthAttr && heightAttr) {
1824
+ const width = parseInt(widthAttr, 10);
1825
+ const height = parseInt(heightAttr, 10);
1826
+ if (width && height) {
1827
+ return resolveResolutionFromDimensions(width, height);
1828
+ }
1829
+ }
1830
+ return null;
1831
+ }
1832
+ var UHD_SQUARE_MIN = 2160;
1833
+ var UHD_RECT_MIN = 3840;
1834
+ function resolveResolutionFromDimensions(width, height) {
1835
+ const longSide = Math.max(width, height);
1836
+ if (width === height) {
1837
+ return longSide >= UHD_SQUARE_MIN ? "square-4k" : "square";
1838
+ }
1839
+ const isLandscape = width > height;
1840
+ const isUhd = longSide >= UHD_RECT_MIN;
1841
+ if (isLandscape) return isUhd ? "landscape-4k" : "landscape";
1842
+ return isUhd ? "portrait-4k" : "portrait";
1843
+ }
1844
+ function parseHtml(html) {
1845
+ const withIds = ensureHfIds(html);
1846
+ const parser = new DOMParser();
1847
+ const doc = parser.parseFromString(withIds, "text/html");
1848
+ const elements = [];
1849
+ const keyframes = {};
1850
+ let idCounter = 0;
1851
+ const htmlEl = doc.documentElement;
1852
+ const customStylesAttr = htmlEl.getAttribute("data-custom-styles");
1853
+ let customStyles = null;
1854
+ if (customStylesAttr) {
1855
+ try {
1856
+ customStyles = JSON.parse(customStylesAttr);
1857
+ } catch {
1858
+ customStyles = customStylesAttr;
1859
+ }
1860
+ }
1861
+ const timedElements = doc.querySelectorAll("[data-start]");
1862
+ timedElements.forEach((el) => {
1863
+ const type = getElementType(el);
1864
+ if (!type) return;
1865
+ const start = parseFloat(el.getAttribute("data-start") || "0");
1866
+ const dataEnd = el.getAttribute("data-end");
1867
+ let duration;
1868
+ if (dataEnd) {
1869
+ duration = Math.max(0, parseFloat(dataEnd) - start);
1870
+ } else {
1871
+ duration = 5;
1872
+ }
1873
+ const id = el.getAttribute("data-hf-id") || el.id || `element-${++idCounter}`;
1874
+ const name = getElementName(el);
1875
+ const zIndex = getZIndex(el);
1876
+ const keyframesAttr = el.getAttribute("data-keyframes");
1877
+ if (keyframesAttr) {
1878
+ try {
1879
+ const parsedKeyframes = JSON.parse(keyframesAttr);
1880
+ if (Array.isArray(parsedKeyframes) && parsedKeyframes.length > 0) {
1881
+ keyframes[id] = parsedKeyframes;
1882
+ }
1883
+ } catch {
1884
+ }
1885
+ }
1886
+ const xAttr = el.getAttribute("data-x");
1887
+ const yAttr = el.getAttribute("data-y");
1888
+ const scaleAttr = el.getAttribute("data-scale");
1889
+ const opacityAttr = el.getAttribute("data-opacity");
1890
+ const x = xAttr ? parseFloat(xAttr) : void 0;
1891
+ const y = yAttr ? parseFloat(yAttr) : void 0;
1892
+ const scale = scaleAttr ? parseFloat(scaleAttr) : void 0;
1893
+ const opacity = opacityAttr ? parseFloat(opacityAttr) : void 0;
1894
+ if (type === "text") {
1895
+ const textEl = el.firstElementChild;
1896
+ const content = textEl?.textContent || name;
1897
+ const color = el.getAttribute("data-color") || void 0;
1898
+ const fontSizeAttr = el.getAttribute("data-font-size");
1899
+ const fontSize = fontSizeAttr ? parseInt(fontSizeAttr, 10) : void 0;
1900
+ const fontWeightAttr = el.getAttribute("data-font-weight");
1901
+ const fontWeight = fontWeightAttr ? parseInt(fontWeightAttr, 10) : void 0;
1902
+ const fontFamily = el.getAttribute("data-font-family") || void 0;
1903
+ const textShadowAttr = el.getAttribute("data-text-shadow");
1904
+ const textShadow = textShadowAttr === "false" ? false : void 0;
1905
+ const textOutlineAttr = el.getAttribute("data-text-outline");
1906
+ const textOutline = textOutlineAttr === "true" ? true : void 0;
1907
+ const textOutlineColor = el.getAttribute("data-text-outline-color") || void 0;
1908
+ const textOutlineWidthAttr = el.getAttribute("data-text-outline-width");
1909
+ const textOutlineWidth = textOutlineWidthAttr ? parseInt(textOutlineWidthAttr, 10) : void 0;
1910
+ const textHighlightAttr = el.getAttribute("data-text-highlight");
1911
+ const textHighlight = textHighlightAttr === "true" ? true : void 0;
1912
+ const textHighlightColor = el.getAttribute("data-text-highlight-color") || void 0;
1913
+ const textHighlightPaddingAttr = el.getAttribute("data-text-highlight-padding");
1914
+ const textHighlightPadding = textHighlightPaddingAttr ? parseInt(textHighlightPaddingAttr, 10) : void 0;
1915
+ const textHighlightRadiusAttr = el.getAttribute("data-text-highlight-radius");
1916
+ const textHighlightRadius = textHighlightRadiusAttr ? parseInt(textHighlightRadiusAttr, 10) : void 0;
1917
+ const textElement = {
1918
+ id,
1919
+ type: "text",
1920
+ name,
1921
+ content,
1922
+ startTime: start,
1923
+ duration,
1924
+ zIndex,
1925
+ x,
1926
+ y,
1927
+ scale,
1928
+ opacity,
1929
+ color,
1930
+ fontSize,
1931
+ fontWeight,
1932
+ fontFamily,
1933
+ textShadow,
1934
+ textOutline,
1935
+ textOutlineColor,
1936
+ textOutlineWidth,
1937
+ textHighlight,
1938
+ textHighlightColor,
1939
+ textHighlightPadding,
1940
+ textHighlightRadius
1941
+ };
1942
+ elements.push(textElement);
1943
+ } else if (type === "composition") {
1944
+ const iframe = el.querySelector("iframe");
1945
+ const src = iframe?.getAttribute("src") || el.getAttribute("src") || "";
1946
+ const compositionId = el.getAttribute("data-composition-id") || "";
1947
+ const sourceDurationAttr = el.getAttribute("data-source-duration");
1948
+ const sourceDuration = sourceDurationAttr ? parseFloat(sourceDurationAttr) : void 0;
1949
+ const sourceWidthAttr = el.getAttribute("data-source-width");
1950
+ const sourceWidth = sourceWidthAttr ? parseInt(sourceWidthAttr, 10) : void 0;
1951
+ const sourceHeightAttr = el.getAttribute("data-source-height");
1952
+ const sourceHeight = sourceHeightAttr ? parseInt(sourceHeightAttr, 10) : void 0;
1953
+ const variableValuesAttr = el.getAttribute("data-variable-values");
1954
+ let variableValues;
1955
+ if (variableValuesAttr) {
1956
+ try {
1957
+ variableValues = JSON.parse(variableValuesAttr);
1958
+ } catch {
1959
+ }
1960
+ }
1961
+ const compositionElement = {
1962
+ id,
1963
+ type: "composition",
1964
+ name,
1965
+ src,
1966
+ compositionId,
1967
+ startTime: start,
1968
+ duration,
1969
+ zIndex,
1970
+ x,
1971
+ y,
1972
+ scale,
1973
+ opacity,
1974
+ sourceDuration,
1975
+ sourceWidth,
1976
+ sourceHeight,
1977
+ variableValues
1978
+ };
1979
+ elements.push(compositionElement);
1980
+ } else {
1981
+ if (!MEDIA_TYPES.has(type)) return;
1982
+ const src = el.getAttribute("src") || "";
1983
+ const mediaStartTimeAttr = el.getAttribute("data-media-start");
1984
+ const mediaStartTime = mediaStartTimeAttr ? parseFloat(mediaStartTimeAttr) : void 0;
1985
+ const sourceDurationAttr = el.getAttribute("data-source-duration");
1986
+ const sourceDuration = sourceDurationAttr ? parseFloat(sourceDurationAttr) : void 0;
1987
+ const isArollAttr = el.getAttribute("data-aroll");
1988
+ const isAroll = isArollAttr === "true" ? true : void 0;
1989
+ const volumeAttr = el.getAttribute("data-volume");
1990
+ const volume = volumeAttr ? parseFloat(volumeAttr) : void 0;
1991
+ const hasAudioAttr = el.getAttribute("data-has-audio");
1992
+ const hasAudio = hasAudioAttr === "true" ? true : void 0;
1993
+ const mediaElement = {
1994
+ id,
1995
+ type,
1996
+ name,
1997
+ src,
1998
+ startTime: start,
1999
+ duration,
2000
+ zIndex,
2001
+ x,
2002
+ y,
2003
+ scale,
2004
+ opacity,
2005
+ mediaStartTime,
2006
+ sourceDuration,
2007
+ isAroll,
2008
+ volume,
2009
+ hasAudio
2010
+ };
2011
+ elements.push(mediaElement);
2012
+ }
2013
+ });
2014
+ const scriptTags = doc.querySelectorAll("script");
2015
+ let gsapScript = null;
2016
+ for (const script of scriptTags) {
2017
+ const src = script.getAttribute("src");
2018
+ if (src && src.includes("gsap")) continue;
2019
+ const content = script.textContent?.trim();
2020
+ if (content && (content.includes("gsap") || content.includes("timeline"))) {
2021
+ gsapScript = content;
2022
+ break;
2023
+ }
2024
+ }
2025
+ for (const element of elements) {
2026
+ const elementKeyframes = keyframes[element.id];
2027
+ if (!elementKeyframes || elementKeyframes.length === 0) continue;
2028
+ const baseX = element.x ?? 0;
2029
+ const baseY = element.y ?? 0;
2030
+ const baseScale = element.type === "video" || element.type === "image" || element.type === "composition" ? element.scale ?? 1 : 1;
2031
+ keyframes[element.id] = normalizeKeyframes(elementKeyframes, baseX, baseY, baseScale);
2032
+ }
2033
+ const styleTags = doc.querySelectorAll("style");
2034
+ const allStyles = Array.from(styleTags).map((s) => s.textContent?.trim()).filter(Boolean).join("\n\n") || null;
2035
+ const customStyleTags = Array.from(styleTags).filter(
2036
+ (s) => s.getAttribute("data-hf-custom") === "true"
2037
+ );
2038
+ const customStylesFromTags = customStyleTags.map((s) => s.textContent?.trim()).filter(Boolean).join("\n\n") || null;
2039
+ const styles = customStyles ?? customStylesFromTags ?? null;
2040
+ const resolution = parseResolutionFromHtml(doc) ?? parseResolutionFromCss(doc, allStyles);
2041
+ const stageZoomKeyframes = parseStageZoomKeyframes(doc);
2042
+ return {
2043
+ elements,
2044
+ gsapScript,
2045
+ styles,
2046
+ resolution,
2047
+ keyframes,
2048
+ stageZoomKeyframes
2049
+ };
2050
+ }
2051
+ function parseStageZoomKeyframes(doc) {
2052
+ const zoomContainer = doc.getElementById("stage-zoom-container");
2053
+ if (!zoomContainer) {
2054
+ return [];
2055
+ }
2056
+ const zoomKeyframesAttr = zoomContainer.getAttribute("data-zoom-keyframes");
2057
+ if (!zoomKeyframesAttr) {
2058
+ return [];
2059
+ }
2060
+ try {
2061
+ const parsed = JSON.parse(zoomKeyframesAttr);
2062
+ if (Array.isArray(parsed)) {
2063
+ return parsed.filter(
2064
+ (kf) => typeof kf === "object" && kf !== null && typeof kf.id === "string" && typeof kf.time === "number" && typeof kf.zoom === "object" && kf.zoom !== null && typeof kf.zoom.scale === "number" && typeof kf.zoom.focusX === "number" && typeof kf.zoom.focusY === "number"
2065
+ );
2066
+ }
2067
+ } catch {
2068
+ }
2069
+ return [];
2070
+ }
2071
+ function normalizeKeyframes(keyframes, baseX, baseY, baseScale) {
2072
+ const timeEpsilon = 1e-3;
2073
+ const valueEpsilon = 1e-5;
2074
+ const hasBaseCheck = (value, base) => value !== void 0 && Math.abs(value - base) <= valueEpsilon && Math.abs(base) > valueEpsilon;
2075
+ const timeZeroKeyframes = keyframes.filter((kf) => Math.abs(kf.time) <= timeEpsilon);
2076
+ const treatAsAbsolute = timeZeroKeyframes.some((kf) => {
2077
+ const props = kf.properties || {};
2078
+ if (hasBaseCheck(props.x, baseX) || hasBaseCheck(props.y, baseY) || baseScale !== 1 && hasBaseCheck(props.scale, baseScale)) {
2079
+ return true;
2080
+ }
2081
+ return false;
2082
+ });
2083
+ return keyframes.map((kf) => {
2084
+ const normalizedProps = {};
2085
+ for (const [key, value] of Object.entries(kf.properties || {})) {
2086
+ if (typeof value !== "number") continue;
2087
+ if (treatAsAbsolute && key === "x") {
2088
+ normalizedProps.x = value - baseX;
2089
+ } else if (treatAsAbsolute && key === "y") {
2090
+ normalizedProps.y = value - baseY;
2091
+ } else if (treatAsAbsolute && key === "scale") {
2092
+ normalizedProps.scale = baseScale !== 0 ? value / baseScale : value;
2093
+ } else {
2094
+ normalizedProps[key] = value;
2095
+ }
2096
+ }
2097
+ return {
2098
+ ...kf,
2099
+ time: Math.max(0, kf.time),
2100
+ properties: normalizedProps
2101
+ };
2102
+ });
2103
+ }
2104
+ function updateElementInHtml(html, elementId, updates) {
2105
+ const parser = new DOMParser();
2106
+ const doc = parser.parseFromString(html, "text/html");
2107
+ const el = doc.getElementById(elementId) || queryByAttr(doc, "data-name", elementId);
2108
+ if (!el) return html;
2109
+ if (updates.startTime !== void 0) {
2110
+ el.setAttribute("data-start", String(updates.startTime));
2111
+ if (el.hasAttribute("data-end") && updates.duration !== void 0) {
2112
+ el.setAttribute("data-end", String(updates.startTime + updates.duration));
2113
+ }
2114
+ }
2115
+ if (updates.duration !== void 0) {
2116
+ const start = parseFloat(el.getAttribute("data-start") || "0");
2117
+ el.setAttribute("data-end", String(start + updates.duration));
2118
+ el.removeAttribute("data-duration");
2119
+ }
2120
+ if (updates.name !== void 0) {
2121
+ el.setAttribute("data-name", updates.name);
2122
+ }
2123
+ if (updates.zIndex !== void 0) {
2124
+ el.setAttribute("data-layer", String(updates.zIndex));
2125
+ }
2126
+ if ("src" in updates && updates.src !== void 0) {
2127
+ el.setAttribute("src", updates.src);
2128
+ }
2129
+ if ("content" in updates && updates.content !== void 0) {
2130
+ const textEl = el.firstElementChild;
2131
+ if (textEl) {
2132
+ textEl.textContent = updates.content;
2133
+ }
2134
+ }
2135
+ if ("color" in updates && updates.color !== void 0) {
2136
+ el.setAttribute("data-color", updates.color);
2137
+ }
2138
+ if ("fontSize" in updates && updates.fontSize !== void 0) {
2139
+ el.setAttribute("data-font-size", String(updates.fontSize));
2140
+ }
2141
+ if ("textShadow" in updates) {
2142
+ if (updates.textShadow === false) {
2143
+ el.setAttribute("data-text-shadow", "false");
2144
+ } else {
2145
+ el.removeAttribute("data-text-shadow");
2146
+ }
2147
+ }
2148
+ if ("volume" in updates) {
2149
+ if (updates.volume !== void 0 && updates.volume !== 1) {
2150
+ el.setAttribute("data-volume", String(updates.volume));
2151
+ } else {
2152
+ el.removeAttribute("data-volume");
2153
+ }
2154
+ }
2155
+ if ("hasAudio" in updates) {
2156
+ if (updates.hasAudio === true) {
2157
+ el.setAttribute("data-has-audio", "true");
2158
+ } else {
2159
+ el.removeAttribute("data-has-audio");
2160
+ }
2161
+ }
2162
+ return "<!DOCTYPE html>\n" + doc.documentElement.outerHTML;
2163
+ }
2164
+ function addElementToHtml(html, element) {
2165
+ const parser = new DOMParser();
2166
+ const doc = parser.parseFromString(html, "text/html");
2167
+ const container = doc.querySelector("#stage-zoom-container") || doc.querySelector(".container") || doc.querySelector("#stage") || doc.body;
2168
+ const id = element.id || `element-${Date.now()}`;
2169
+ let newEl;
2170
+ function applyMediaAttrs(el, mediaEl) {
2171
+ if (mediaEl.src) el.setAttribute("src", mediaEl.src);
2172
+ if (mediaEl.volume !== void 0 && mediaEl.volume !== 1) {
2173
+ el.setAttribute("data-volume", String(mediaEl.volume));
2174
+ }
2175
+ }
2176
+ switch (element.type) {
2177
+ case "video": {
2178
+ const mediaEl = element;
2179
+ newEl = doc.createElement("video");
2180
+ newEl.setAttribute("muted", "");
2181
+ newEl.setAttribute("playsinline", "");
2182
+ applyMediaAttrs(newEl, mediaEl);
2183
+ if (mediaEl.hasAudio) {
2184
+ newEl.setAttribute("data-has-audio", "true");
2185
+ }
2186
+ break;
2187
+ }
2188
+ case "image": {
2189
+ const mediaEl = element;
2190
+ newEl = doc.createElement("img");
2191
+ if (mediaEl.src) newEl.setAttribute("src", mediaEl.src);
2192
+ newEl.setAttribute("alt", element.name);
2193
+ break;
2194
+ }
2195
+ case "audio": {
2196
+ const mediaEl = element;
2197
+ newEl = doc.createElement("audio");
2198
+ applyMediaAttrs(newEl, mediaEl);
2199
+ break;
2200
+ }
2201
+ case "text":
2202
+ default: {
2203
+ const textEl = element;
2204
+ newEl = doc.createElement("div");
2205
+ const textContent = doc.createElement("div");
2206
+ textContent.textContent = textEl.content || element.name;
2207
+ newEl.appendChild(textContent);
2208
+ if (textEl.color) {
2209
+ newEl.setAttribute("data-color", textEl.color);
2210
+ }
2211
+ if (textEl.fontSize) {
2212
+ newEl.setAttribute("data-font-size", String(textEl.fontSize));
2213
+ }
2214
+ break;
2215
+ }
2216
+ }
2217
+ newEl.id = id;
2218
+ newEl.setAttribute("data-start", String(element.startTime));
2219
+ newEl.setAttribute("data-end", String(element.startTime + element.duration));
2220
+ newEl.setAttribute("data-layer", String(element.zIndex));
2221
+ newEl.setAttribute("data-name", element.name);
2222
+ container.appendChild(newEl);
2223
+ return {
2224
+ html: "<!DOCTYPE html>\n" + doc.documentElement.outerHTML,
2225
+ id
2226
+ };
2227
+ }
2228
+ function selectorTargetsId(selector, id) {
2229
+ return selector === `#${id}` || selector === `[data-hf-id="${id}"]` || selector === `[data-hf-id='${id}']`;
2230
+ }
2231
+ function stripGsapForId(script, elementId) {
2232
+ let current = script;
2233
+ for (; ; ) {
2234
+ const parsed = parseGsapScriptAcornForWrite(current);
2235
+ if (!parsed) return current;
2236
+ const match = parsed.located.find(
2237
+ (l) => selectorTargetsId(l.animation.targetSelector, elementId)
2238
+ );
2239
+ if (!match) return current;
2240
+ const updated = removeAnimationFromScript(current, match.id);
2241
+ if (updated === current) return current;
2242
+ current = updated;
2243
+ }
2244
+ }
2245
+ function cascadeRemoveGsapById(doc, elementId) {
2246
+ for (const script of Array.from(doc.querySelectorAll("script"))) {
2247
+ const text = script.textContent ?? "";
2248
+ if (!text.includes("gsap") && !text.includes("ScrollTrigger")) continue;
2249
+ const updated = stripGsapForId(text, elementId);
2250
+ if (updated !== text) script.textContent = updated;
2251
+ }
2252
+ }
2253
+ function removeElementFromHtml(html, elementId) {
2254
+ const parser = new DOMParser();
2255
+ const doc = parser.parseFromString(html, "text/html");
2256
+ doc.getElementById(elementId)?.remove();
2257
+ cascadeRemoveGsapById(doc, elementId);
2258
+ return "<!DOCTYPE html>\n" + doc.documentElement.outerHTML;
2259
+ }
2260
+ function extractCompositionMetadata(html) {
2261
+ const parser = new DOMParser();
2262
+ const doc = parser.parseFromString(html, "text/html");
2263
+ const htmlEl = doc.documentElement;
2264
+ const compositionId = htmlEl.getAttribute("data-composition-id");
2265
+ const durationStr = htmlEl.getAttribute("data-composition-duration");
2266
+ const compositionDuration = durationStr ? parseFloat(durationStr) : null;
2267
+ const variables = parseCompositionVariables(htmlEl);
2268
+ return {
2269
+ compositionId,
2270
+ compositionDuration: compositionDuration && isFinite(compositionDuration) ? compositionDuration : null,
2271
+ variables
2272
+ };
2273
+ }
2274
+ function parseCompositionVariables(htmlEl) {
2275
+ const variablesAttr = htmlEl.getAttribute("data-composition-variables");
2276
+ if (!variablesAttr) {
2277
+ return [];
2278
+ }
2279
+ try {
2280
+ const parsed = JSON.parse(variablesAttr);
2281
+ if (!Array.isArray(parsed)) {
2282
+ return [];
2283
+ }
2284
+ return parsed.filter((v) => {
2285
+ if (typeof v !== "object" || v === null) return false;
2286
+ if (typeof v.id !== "string" || typeof v.label !== "string") return false;
2287
+ if (!["string", "number", "color", "boolean", "enum", "font", "image"].includes(v.type))
2288
+ return false;
2289
+ switch (v.type) {
2290
+ case "string":
2291
+ return typeof v.default === "string";
2292
+ case "number":
2293
+ return typeof v.default === "number";
2294
+ case "color":
2295
+ return typeof v.default === "string";
2296
+ case "boolean":
2297
+ return typeof v.default === "boolean";
2298
+ case "enum":
2299
+ return typeof v.default === "string" && Array.isArray(v.options);
2300
+ case "font":
2301
+ return typeof v.default === "string";
2302
+ case "image":
2303
+ return typeof v.default === "string";
2304
+ default:
2305
+ return false;
2306
+ }
2307
+ });
2308
+ } catch {
2309
+ return [];
2310
+ }
2311
+ }
2312
+ function validateCompositionHtml(html) {
2313
+ const errors = [];
2314
+ const warnings = [];
2315
+ const parser = new DOMParser();
2316
+ const doc = parser.parseFromString(html, "text/html");
2317
+ const htmlEl = doc.documentElement;
2318
+ const compositionId = htmlEl.getAttribute("data-composition-id");
2319
+ if (!compositionId) {
2320
+ errors.push("Missing data-composition-id attribute on <html> element");
2321
+ }
2322
+ const durationStr = htmlEl.getAttribute("data-composition-duration");
2323
+ if (!durationStr) {
2324
+ errors.push("Missing data-composition-duration attribute on <html> element");
2325
+ } else {
2326
+ const duration = parseFloat(durationStr);
2327
+ if (!isFinite(duration) || duration <= 0) {
2328
+ errors.push("data-composition-duration must be a positive finite number");
2329
+ }
2330
+ }
2331
+ const stage = doc.getElementById("stage");
2332
+ if (!stage) {
2333
+ errors.push("Missing #stage element");
2334
+ }
2335
+ if (/\son\w+\s*=/i.test(html)) {
2336
+ errors.push("Inline event handlers (onclick, onload, etc.) not allowed");
2337
+ }
2338
+ if (/javascript\s*:/i.test(html)) {
2339
+ errors.push("javascript: URLs not allowed");
2340
+ }
2341
+ const scripts = doc.querySelectorAll("script");
2342
+ if (scripts.length > 2) {
2343
+ warnings.push("Multiple script tags detected - only GSAP CDN and main script expected");
2344
+ }
2345
+ const gsapScript = extractGsapScript(doc);
2346
+ if (gsapScript) {
2347
+ const gsapValidation = validateCompositionGsap(gsapScript);
2348
+ errors.push(...gsapValidation.errors);
2349
+ warnings.push(...gsapValidation.warnings);
2350
+ }
2351
+ return {
2352
+ valid: errors.length === 0,
2353
+ errors,
2354
+ warnings
2355
+ };
2356
+ }
2357
+ function extractGsapScript(doc) {
2358
+ const scripts = doc.querySelectorAll("script");
2359
+ for (const script of scripts) {
2360
+ const content = script.textContent || "";
2361
+ if (content.includes("gsap.timeline") || content.includes(".set(") || content.includes(".to(")) {
2362
+ return content;
2363
+ }
2364
+ }
2365
+ return null;
2366
+ }
2367
+
2368
+ // src/gsapUnroll.ts
2369
+ import * as acorn2 from "acorn";
2370
+ import MagicString2 from "magic-string";
2371
+ function propEntries(props) {
2372
+ return Object.entries(props).map(([k, v]) => `${safeJsKey(k)}: ${serializeValue(v)}`);
2373
+ }
2374
+ function motionPathEntry(anim) {
2375
+ const waypoints = (anim.keyframes?.keyframes ?? []).filter((k) => typeof k.properties.x === "number" && typeof k.properties.y === "number").map((k) => `{ x: ${serializeValue(k.properties.x)}, y: ${serializeValue(k.properties.y)} }`);
2376
+ const curviness = anim.arcPath?.segments[0]?.curviness ?? 1;
2377
+ const autoRotate = anim.arcPath?.autoRotate;
2378
+ const extra = autoRotate ? `, autoRotate: ${serializeValue(autoRotate)}` : "";
2379
+ return `motionPath: { path: [${waypoints.join(", ")}], curviness: ${curviness}${extra} }`;
2380
+ }
2381
+ function keyframesEntry(anim) {
2382
+ const kfs = (anim.keyframes?.keyframes ?? []).map((k) => {
2383
+ const body = propEntries(k.properties);
2384
+ if (k.ease) body.push(`ease: ${serializeValue(k.ease)}`);
2385
+ return `"${k.percentage}%": { ${body.join(", ")} }`;
2386
+ });
2387
+ if (anim.keyframes?.easeEach) kfs.push(`easeEach: ${serializeValue(anim.keyframes.easeEach)}`);
2388
+ return `keyframes: { ${kfs.join(", ")} }`;
2389
+ }
2390
+ function buildVarsParts(anim) {
2391
+ const parts = [];
2392
+ if (anim.arcPath?.enabled) parts.push(motionPathEntry(anim));
2393
+ else if (anim.keyframes) parts.push(keyframesEntry(anim));
2394
+ parts.push(...propEntries(anim.properties));
2395
+ if (anim.method !== "set" && anim.duration !== void 0) {
2396
+ parts.push(`duration: ${serializeValue(anim.duration)}`);
2397
+ }
2398
+ if (anim.ease) parts.push(`ease: ${serializeValue(anim.ease)}`);
2399
+ for (const [k, v] of Object.entries(anim.extras ?? {})) {
2400
+ parts.push(`${safeJsKey(k)}: ${serializeValue(v)}`);
2401
+ }
2402
+ return parts;
2403
+ }
2404
+ function serializeTweenStatement(timelineVar, anim) {
2405
+ const obj = `{ ${buildVarsParts(anim).join(", ")} }`;
2406
+ const pos = serializeValue(
2407
+ anim.resolvedStart ?? (typeof anim.position === "number" ? anim.position : 0)
2408
+ );
2409
+ const sel = serializeValue(anim.targetSelector);
2410
+ if (anim.method === "fromTo") {
2411
+ const from = `{ ${propEntries(anim.fromProperties ?? {}).join(", ")} }`;
2412
+ return `${timelineVar}.fromTo(${sel}, ${from}, ${obj}, ${pos});`;
2413
+ }
2414
+ return `${timelineVar}.${anim.method}(${sel}, ${obj}, ${pos});`;
2415
+ }
2416
+ function isComputed(anim) {
2417
+ return anim.provenance?.kind === "helper" || anim.provenance?.kind === "loop";
2418
+ }
2419
+ function topLevelStatements(script) {
2420
+ return acorn2.parse(script, { ecmaVersion: "latest", sourceType: "script" }).body ?? [];
2421
+ }
2422
+ function enclosingTopLevel(statements, start, end) {
2423
+ for (const stmt of statements) {
2424
+ if (stmt.start <= start && stmt.end >= end) return stmt;
2425
+ }
2426
+ return null;
2427
+ }
2428
+ function isHelperDeclNamed(stmt, names) {
2429
+ if (stmt.type === "FunctionDeclaration") return names.has(stmt.id?.name);
2430
+ if (stmt.type === "VariableDeclaration") {
2431
+ return (stmt.declarations ?? []).some((d) => names.has(d.id?.name));
2432
+ }
2433
+ return false;
2434
+ }
2435
+ function unrollComputedTimeline(script) {
2436
+ const parsed = parseGsapScriptAcorn(script);
2437
+ const computed = parsed.animations.filter((a) => isComputed(a) && a.provenance?.sourceRange);
2438
+ if (computed.length === 0) return script;
2439
+ const statements = topLevelStatements(script);
2440
+ const byStatement = /* @__PURE__ */ new Map();
2441
+ const helperNames = /* @__PURE__ */ new Set();
2442
+ for (const anim of computed) {
2443
+ if (anim.provenance?.fn) helperNames.add(anim.provenance.fn);
2444
+ const [s, e] = anim.provenance.sourceRange;
2445
+ const stmt = enclosingTopLevel(statements, s, e);
2446
+ if (!stmt) continue;
2447
+ const list = byStatement.get(stmt) ?? [];
2448
+ list.push(anim);
2449
+ byStatement.set(stmt, list);
2450
+ }
2451
+ if (byStatement.size === 0) return script;
2452
+ const ms = new MagicString2(script);
2453
+ for (const [stmt, anims] of byStatement) {
2454
+ const literals = anims.map((a) => serializeTweenStatement(parsed.timelineVar, a)).join("\n");
2455
+ ms.overwrite(stmt.start, stmt.end, literals);
2456
+ }
2457
+ for (const stmt of statements) {
2458
+ if (isHelperDeclNamed(stmt, helperNames)) ms.remove(stmt.start, stmt.end);
2459
+ }
2460
+ return ms.toString();
2461
+ }
2462
+ export {
2463
+ CANVAS_DIMENSIONS,
2464
+ COMPOSITION_VARIABLE_TYPES,
2465
+ DEFAULT_DURATIONS,
2466
+ EXCLUDED_TAGS,
2467
+ PROPERTY_GROUPS,
2468
+ SPRING_PRESETS,
2469
+ SUPPORTED_EASES,
2470
+ SUPPORTED_PROPS,
2471
+ TIMELINE_COLORS,
2472
+ VALID_CANVAS_RESOLUTIONS,
2473
+ addElementToHtml,
2474
+ classifyPropertyGroup,
2475
+ classifyTweenPropertyGroup,
2476
+ editabilityForProvenance,
2477
+ ensureHfIds,
2478
+ extractCompositionMetadata,
2479
+ generateSpringEaseData,
2480
+ getAnimationsForElementId,
2481
+ getDefaultStageZoom,
2482
+ gsapAnimationsToKeyframes,
2483
+ isCompositionElement,
2484
+ isMediaElement,
2485
+ isStudioHoldSet,
2486
+ isTextElement,
2487
+ keyframesToGsapAnimations,
2488
+ mintHfId,
2489
+ normalizeResolutionFlag,
2490
+ parseGsapScriptAcorn as parseGsapScript,
2491
+ parseHtml,
2492
+ queryByAttr,
2493
+ removeElementFromHtml,
2494
+ serializeGsapAnimations,
2495
+ unrollComputedTimeline,
2496
+ updateElementInHtml,
2497
+ validateCompositionGsap,
2498
+ validateCompositionHtml
2499
+ };
2500
+ //# sourceMappingURL=index.js.map