@hyperframes/studio 0.8.128 → 0.8.129
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/assets/{hyperframes-player-CKCx0CFA.js → hyperframes-player-DfEcBl9r.js} +1 -1
- package/dist/assets/{index-CF31Pq1E.js → index-9ZaZFCMZ.js} +1 -1
- package/dist/assets/{index-CwS08gWW.js → index-Bmy-KVib.js} +1 -1
- package/dist/assets/{index-ClLzyuwb.js → index-C9YkEyN4.js} +154 -154
- package/dist/index.html +1 -1
- package/dist/index.js +206 -51
- package/dist/index.js.map +1 -1
- package/package.json +8 -8
- package/src/hooks/timelineTimingSync.ts +20 -21
- package/src/utils/gsapLiveRetime.realGsap.test.ts +544 -0
- package/src/utils/gsapLiveRetime.ts +286 -0
- package/src/utils/gsapSoftReload.ts +16 -7
- package/src/utils/gsapUndoRestore.test.ts +43 -0
- package/src/utils/gsapUndoRestore.ts +6 -3
package/dist/index.html
CHANGED
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
document.documentElement.dataset.theme = "paper";
|
|
15
15
|
}
|
|
16
16
|
</script>
|
|
17
|
-
<script type="module" crossorigin src="/assets/index-
|
|
17
|
+
<script type="module" crossorigin src="/assets/index-C9YkEyN4.js"></script>
|
|
18
18
|
<link rel="stylesheet" crossorigin href="/assets/index-D11atkBt.css">
|
|
19
19
|
</head>
|
|
20
20
|
<body>
|
package/dist/index.js
CHANGED
|
@@ -17395,6 +17395,14 @@ function applySoftReloadFinalization(iframe, currentTime) {
|
|
|
17395
17395
|
function timelineKeys(scriptText) {
|
|
17396
17396
|
return [...scriptText.matchAll(/__timelines\s*\[\s*["'`]([^"'`]+)["'`]\s*\]/g)].map((m) => m[1]).filter((key2) => key2 !== "__proxied");
|
|
17397
17397
|
}
|
|
17398
|
+
function scriptsRegistering(scripts, keys) {
|
|
17399
|
+
return scripts.filter((script) => {
|
|
17400
|
+
const text = script.textContent || "";
|
|
17401
|
+
return keys.some(
|
|
17402
|
+
(key2) => text.includes(`__timelines["${key2}"]`) || text.includes(`__timelines['${key2}']`)
|
|
17403
|
+
);
|
|
17404
|
+
});
|
|
17405
|
+
}
|
|
17398
17406
|
function applySoftReload(iframe, scriptText, options = {}) {
|
|
17399
17407
|
const { onAsyncFailure, currentTimeOverride, authoredHtml } = options;
|
|
17400
17408
|
if (!iframe || !scriptText) return "cannot-soft-reload";
|
|
@@ -17407,12 +17415,7 @@ function applySoftReload(iframe, scriptText, options = {}) {
|
|
|
17407
17415
|
if (targetKeys.length === 0) return "cannot-soft-reload";
|
|
17408
17416
|
const gsapScripts = findGsapScriptElements(doc);
|
|
17409
17417
|
if (gsapScripts.length === 0) return "cannot-soft-reload";
|
|
17410
|
-
const staleScripts = gsapScripts
|
|
17411
|
-
(script) => targetKeys.some((key2) => {
|
|
17412
|
-
const text = script.textContent || "";
|
|
17413
|
-
return text.includes(`__timelines["${key2}"]`) || text.includes(`__timelines['${key2}']`);
|
|
17414
|
-
})
|
|
17415
|
-
);
|
|
17418
|
+
const staleScripts = scriptsRegistering(gsapScripts, targetKeys);
|
|
17416
17419
|
if (gsapScripts.length > 1 && staleScripts.length === 0) return "cannot-soft-reload";
|
|
17417
17420
|
const currentTime = currentTimeOverride ?? win.__player?.getTime?.() ?? 0;
|
|
17418
17421
|
let deferredToAsync = false;
|
|
@@ -17682,6 +17685,159 @@ function postGsapMutations(projectId, filePath, mutations, fallback) {
|
|
|
17682
17685
|
// src/hooks/timelineTimingSync.ts
|
|
17683
17686
|
import { findTimelineScript } from "@hyperframes/core/gsap-parser-acorn";
|
|
17684
17687
|
import { walkCompositionDescendants } from "@hyperframes/parsers/hf-ids";
|
|
17688
|
+
|
|
17689
|
+
// src/utils/gsapLiveRetime.ts
|
|
17690
|
+
import { parseGsapScriptAcorn } from "@hyperframes/core/gsap-parser-acorn";
|
|
17691
|
+
import { hasExplicitTime } from "@hyperframes/parsers/gsap-writer-acorn";
|
|
17692
|
+
import { RUNTIME_FILLER } from "@hyperframes/core/runtime/protocol";
|
|
17693
|
+
var RERUN = { kind: "rerun" };
|
|
17694
|
+
var withoutLayout = (code) => code.replace(/\s+/g, " ").replace(/ ?([^\w$ ]) ?/g, "$1").trim();
|
|
17695
|
+
function untimedShape(animation) {
|
|
17696
|
+
const { id: _id, position: _p, resolvedStart: _s, duration: _d, ...rest } = animation;
|
|
17697
|
+
const provenance = rest.provenance && { ...rest.provenance, sourceRange: void 0 };
|
|
17698
|
+
return JSON.stringify(
|
|
17699
|
+
{ ...rest, provenance },
|
|
17700
|
+
(_key, value) => typeof value === "string" && value.startsWith("__raw:") ? withoutLayout(value) : value
|
|
17701
|
+
);
|
|
17702
|
+
}
|
|
17703
|
+
var onTimeline = (animation) => !animation.global;
|
|
17704
|
+
var placedAlone = (animation) => hasExplicitTime(animation) && typeof animation.resolvedStart === "number";
|
|
17705
|
+
var lengthOf = (animation) => animation.method === "set" ? 0 : animation.durationUnresolved ? void 0 : animation.duration;
|
|
17706
|
+
var withoutNumbers = (script) => withoutLayout(script.replace(/(?<![\w$#.])\d*\.?\d+/g, "0"));
|
|
17707
|
+
function planLiveRetime(before, after) {
|
|
17708
|
+
if (withoutNumbers(before) !== withoutNumbers(after)) return RERUN;
|
|
17709
|
+
const keys = timelineKeys(after);
|
|
17710
|
+
if (keys.length !== 1 || timelineKeys(before).join() !== keys.join()) return RERUN;
|
|
17711
|
+
const was = parseGsapScriptAcorn(before).animations.filter(onTimeline);
|
|
17712
|
+
const now2 = parseGsapScriptAcorn(after).animations.filter(onTimeline);
|
|
17713
|
+
if (was.length !== now2.length) return RERUN;
|
|
17714
|
+
const tweens = [];
|
|
17715
|
+
for (const [index, next] of now2.entries()) {
|
|
17716
|
+
const prev = was[index];
|
|
17717
|
+
if (untimedShape(prev) !== untimedShape(next)) return RERUN;
|
|
17718
|
+
if (!placedAlone(next) || !placedAlone(prev)) return RERUN;
|
|
17719
|
+
const duration = lengthOf(next);
|
|
17720
|
+
const wasDuration = lengthOf(prev);
|
|
17721
|
+
if (duration === void 0 || wasDuration === void 0) return RERUN;
|
|
17722
|
+
tweens.push({
|
|
17723
|
+
selector: next.targetSelector,
|
|
17724
|
+
start: next.resolvedStart,
|
|
17725
|
+
duration,
|
|
17726
|
+
wasStart: prev.resolvedStart,
|
|
17727
|
+
wasDuration,
|
|
17728
|
+
moved: next.resolvedStart !== prev.resolvedStart || duration !== wasDuration,
|
|
17729
|
+
source: index
|
|
17730
|
+
});
|
|
17731
|
+
}
|
|
17732
|
+
tweens.sort((x, y) => x.wasStart - y.wasStart || x.source - y.source);
|
|
17733
|
+
return { kind: "retime", key: keys[0], after, tweens };
|
|
17734
|
+
}
|
|
17735
|
+
function planLiveRetimeFromPreview(iframe, after) {
|
|
17736
|
+
const doc = iframe?.contentDocument;
|
|
17737
|
+
const [key2] = timelineKeys(after);
|
|
17738
|
+
if (!doc || !key2) return RERUN;
|
|
17739
|
+
const [script, ...extra] = scriptsRegistering(findGsapScriptElements(doc), [key2]);
|
|
17740
|
+
if (!script || extra.length > 0) return RERUN;
|
|
17741
|
+
const plan = planLiveRetime(script.textContent ?? "", after);
|
|
17742
|
+
return plan.kind === "retime" ? { ...plan, script } : RERUN;
|
|
17743
|
+
}
|
|
17744
|
+
var same = (a, b) => Math.abs(a - b) < 1e-6;
|
|
17745
|
+
var targetsMatch = (tween, selector) => {
|
|
17746
|
+
const targets = tween.targets?.() ?? [];
|
|
17747
|
+
try {
|
|
17748
|
+
return targets.length > 0 && targets.every((t2) => t2.matches?.(selector) === true);
|
|
17749
|
+
} catch {
|
|
17750
|
+
return false;
|
|
17751
|
+
}
|
|
17752
|
+
};
|
|
17753
|
+
function pairedLiveTweens(timeline, plan) {
|
|
17754
|
+
const children = timeline.getChildren(false, true, false).filter((tween) => tween.data !== RUNTIME_FILLER);
|
|
17755
|
+
if (children.length !== plan.tweens.length) return null;
|
|
17756
|
+
const paired = plan.tweens.every((t2, i) => {
|
|
17757
|
+
const live = children[i];
|
|
17758
|
+
return targetsMatch(live, t2.selector) && same(live.startTime() - live.delay(), t2.wasStart) && same(live.duration(), t2.wasDuration);
|
|
17759
|
+
});
|
|
17760
|
+
return paired ? children : null;
|
|
17761
|
+
}
|
|
17762
|
+
var near2 = (a, b) => Math.abs(a - b) <= 2e-3;
|
|
17763
|
+
function sharesOneStretch(tweens) {
|
|
17764
|
+
const first = tweens[0];
|
|
17765
|
+
const last = tweens.at(-1);
|
|
17766
|
+
const longest = tweens.reduce((x, y) => y.wasDuration > x.wasDuration ? y : x);
|
|
17767
|
+
const spread = last.wasStart - first.wasStart;
|
|
17768
|
+
const stretch = spread > longest.wasDuration ? (last.start - first.start) / spread : longest.wasDuration > 0 ? longest.duration / longest.wasDuration : 1;
|
|
17769
|
+
const fits = tweens.every(
|
|
17770
|
+
(t2) => near2(t2.start, first.start + stretch * (t2.wasStart - first.wasStart)) && near2(t2.duration, stretch * t2.wasDuration)
|
|
17771
|
+
);
|
|
17772
|
+
return stretch > 0 && fits;
|
|
17773
|
+
}
|
|
17774
|
+
function inOrHostsSubComposition(target, ownId) {
|
|
17775
|
+
const element = target;
|
|
17776
|
+
const owner = element.closest?.("[data-composition-id]");
|
|
17777
|
+
return owner != null && owner.getAttribute("data-composition-id") !== ownId || element.querySelector?.("[data-composition-id]") != null;
|
|
17778
|
+
}
|
|
17779
|
+
var keepsOrder = (tweens) => tweens.every(
|
|
17780
|
+
(t2, k) => k === 0 || (tweens[k - 1].start - t2.start || tweens[k - 1].source - t2.source) < 0
|
|
17781
|
+
);
|
|
17782
|
+
function movesKeepEachElementsHistory(timeline, plan, children) {
|
|
17783
|
+
const byElement = /* @__PURE__ */ new Map();
|
|
17784
|
+
children.forEach((child, i) => {
|
|
17785
|
+
for (const target of child.targets?.() ?? []) {
|
|
17786
|
+
const indexes = byElement.get(target);
|
|
17787
|
+
if (indexes) indexes.push(i);
|
|
17788
|
+
else byElement.set(target, [i]);
|
|
17789
|
+
}
|
|
17790
|
+
});
|
|
17791
|
+
const moved = /* @__PURE__ */ new Set();
|
|
17792
|
+
for (const [target, indexes] of byElement) {
|
|
17793
|
+
if (!indexes.some((i) => plan.tweens[i].moved)) continue;
|
|
17794
|
+
if (inOrHostsSubComposition(target, plan.key)) return false;
|
|
17795
|
+
const tweens = indexes.map((i) => plan.tweens[i]);
|
|
17796
|
+
const shifted = tweens.every((t2) => t2.duration === t2.wasDuration);
|
|
17797
|
+
const undelayed = indexes.every((i) => !children[i].delay() && !children[i].repeatDelay?.());
|
|
17798
|
+
if (!keepsOrder(tweens) || !sharesOneStretch(tweens) || !(shifted || undelayed)) return false;
|
|
17799
|
+
moved.add(target);
|
|
17800
|
+
}
|
|
17801
|
+
return !timeline.getChildren(false, false, true).some(
|
|
17802
|
+
(sub) => sub.getChildren(true, true, false).some((tween) => tween.targets?.().some((t2) => moved.has(t2)))
|
|
17803
|
+
);
|
|
17804
|
+
}
|
|
17805
|
+
function applyLiveRetime(iframe, plan) {
|
|
17806
|
+
const win = iframe?.contentWindow;
|
|
17807
|
+
const timeline = win?.__timelines?.[plan.key];
|
|
17808
|
+
if (typeof timeline?.getChildren !== "function") return false;
|
|
17809
|
+
const children = pairedLiveTweens(timeline, plan);
|
|
17810
|
+
if (!children) return false;
|
|
17811
|
+
if (plan.tweens.some((t2) => t2.moved)) {
|
|
17812
|
+
if (!movesKeepEachElementsHistory(timeline, plan, children)) return false;
|
|
17813
|
+
const bySource = plan.tweens.map((t2, i) => ({ ...t2, tween: children[i] })).sort((a, b) => a.source - b.source);
|
|
17814
|
+
for (const { tween } of bySource) timeline.remove(tween);
|
|
17815
|
+
for (const { tween, start, duration } of bySource) {
|
|
17816
|
+
if (tween.duration() !== duration) tween.duration(duration);
|
|
17817
|
+
timeline.add(tween, start);
|
|
17818
|
+
}
|
|
17819
|
+
const at = timeline.time();
|
|
17820
|
+
timeline.progress(1e-4, true);
|
|
17821
|
+
timeline.totalTime(at, false);
|
|
17822
|
+
}
|
|
17823
|
+
plan.script.textContent = plan.after;
|
|
17824
|
+
return true;
|
|
17825
|
+
}
|
|
17826
|
+
function moveLiveTweens(iframe, scriptText, currentTime, reloadPreview) {
|
|
17827
|
+
const plan = planLiveRetimeFromPreview(iframe, scriptText);
|
|
17828
|
+
if (plan.kind !== "retime") return false;
|
|
17829
|
+
try {
|
|
17830
|
+
if (!applyLiveRetime(iframe, plan)) return false;
|
|
17831
|
+
} catch (error) {
|
|
17832
|
+
console.error("[Studio] Moving the live tweens threw; reloading the preview", error);
|
|
17833
|
+
reloadPreview();
|
|
17834
|
+
return true;
|
|
17835
|
+
}
|
|
17836
|
+
if (!applySoftReloadFinalization(iframe, currentTime)) reloadPreview();
|
|
17837
|
+
return true;
|
|
17838
|
+
}
|
|
17839
|
+
|
|
17840
|
+
// src/hooks/timelineTimingSync.ts
|
|
17685
17841
|
async function readFileContent(projectId, targetPath) {
|
|
17686
17842
|
if (targetPath.includes("\0") || targetPath.includes("..")) {
|
|
17687
17843
|
throw new Error(`Unsafe path: ${targetPath}`);
|
|
@@ -17722,18 +17878,24 @@ function captureDurationRollback(iframe) {
|
|
|
17722
17878
|
function rebindPreviewTiming(iframe, currentTime) {
|
|
17723
17879
|
return applySoftReloadFinalization(iframe, currentTime);
|
|
17724
17880
|
}
|
|
17725
|
-
function syncTimingEditPreview(iframe, outcome,
|
|
17881
|
+
async function syncTimingEditPreview(iframe, outcome, projectId, reloadPreview, rebindWhenUnmutated) {
|
|
17882
|
+
const currentTime = () => usePlayerStore.getState().currentTime;
|
|
17726
17883
|
if (!outcome.mutated && rebindWhenUnmutated) {
|
|
17727
|
-
if (!rebindPreviewTiming(iframe, currentTime)) reloadPreview();
|
|
17884
|
+
if (!rebindPreviewTiming(iframe, currentTime())) reloadPreview();
|
|
17728
17885
|
return;
|
|
17729
17886
|
}
|
|
17730
|
-
|
|
17887
|
+
const { scriptText } = outcome;
|
|
17888
|
+
if (!iframe || !scriptText) {
|
|
17731
17889
|
reloadPreview();
|
|
17732
17890
|
return;
|
|
17733
17891
|
}
|
|
17734
|
-
|
|
17892
|
+
if (moveLiveTweens(iframe, scriptText, currentTime(), reloadPreview)) return;
|
|
17893
|
+
const nestedFiles = await settleNestedReads(
|
|
17894
|
+
projectId ? readNestedFiles(iframe, scriptText, (path) => readFileContent(projectId, path)) : null
|
|
17895
|
+
);
|
|
17896
|
+
const result = applySoftReload(iframe, scriptText, {
|
|
17735
17897
|
onAsyncFailure: reloadPreview,
|
|
17736
|
-
currentTimeOverride: currentTime,
|
|
17898
|
+
currentTimeOverride: currentTime(),
|
|
17737
17899
|
authoredHtml: outcome.after,
|
|
17738
17900
|
nestedFiles
|
|
17739
17901
|
});
|
|
@@ -17751,21 +17913,12 @@ async function finishTimelineTimingFallback(input) {
|
|
|
17751
17913
|
return;
|
|
17752
17914
|
}
|
|
17753
17915
|
}
|
|
17754
|
-
|
|
17755
|
-
const nestedFiles = await settleNestedReads(
|
|
17756
|
-
projectId && outcome.scriptText ? readNestedFiles(
|
|
17757
|
-
input.iframe,
|
|
17758
|
-
outcome.scriptText,
|
|
17759
|
-
(path) => readFileContent(projectId, path)
|
|
17760
|
-
) : null
|
|
17761
|
-
);
|
|
17762
|
-
syncTimingEditPreview(
|
|
17916
|
+
await syncTimingEditPreview(
|
|
17763
17917
|
input.iframe,
|
|
17764
17918
|
outcome,
|
|
17765
|
-
|
|
17919
|
+
input.projectId,
|
|
17766
17920
|
input.reloadPreview,
|
|
17767
|
-
input.rebindWhenUnmutated
|
|
17768
|
-
nestedFiles
|
|
17921
|
+
input.rebindWhenUnmutated
|
|
17769
17922
|
);
|
|
17770
17923
|
}
|
|
17771
17924
|
var UNTIL_NEXT_EDIT = Number.POSITIVE_INFINITY;
|
|
@@ -26306,9 +26459,9 @@ function mergeAudioInSource(source, input) {
|
|
|
26306
26459
|
}
|
|
26307
26460
|
var tagOf2 = (el) => el.tag.trim().toLowerCase();
|
|
26308
26461
|
var keyOf11 = (el) => el.key ?? el.id;
|
|
26309
|
-
var
|
|
26462
|
+
var near3 = (a, b) => Math.abs(a - b) <= SYNC_TOLERANCE_S;
|
|
26310
26463
|
function hasIdenticalTiming(a, b) {
|
|
26311
|
-
return
|
|
26464
|
+
return near3(a.start, b.start) && near3(a.duration, b.duration) && near3(a.playbackStart ?? 0, b.playbackStart ?? 0) && near3(a.playbackRate ?? 1, b.playbackRate ?? 1);
|
|
26312
26465
|
}
|
|
26313
26466
|
var sameAssetInScope = (a, b) => {
|
|
26314
26467
|
const asset = mediaAssetIdentity(a);
|
|
@@ -26349,7 +26502,7 @@ function isPairInSync(a, b) {
|
|
|
26349
26502
|
playbackRate: el.playbackRate ?? 1
|
|
26350
26503
|
});
|
|
26351
26504
|
const [x, y] = [timing(a), timing(b)];
|
|
26352
|
-
return
|
|
26505
|
+
return near3(x.playbackRate, y.playbackRate) && near3(sourceZeroTime(x), sourceZeroTime(y));
|
|
26353
26506
|
}
|
|
26354
26507
|
function pickDetachedAudioTrack(elements, window2) {
|
|
26355
26508
|
const trackOf = (el) => el.authoredTrack ?? el.track;
|
|
@@ -27425,8 +27578,8 @@ async function decodeVideoThumbnail(request, signal, budgets = TIMELINE_VIEWPORT
|
|
|
27425
27578
|
for (const time of timestamps) {
|
|
27426
27579
|
const key2 = await keys.getKeyPacket(time, { metadataOnly: true });
|
|
27427
27580
|
if (signal.aborted) return;
|
|
27428
|
-
const
|
|
27429
|
-
yield
|
|
27581
|
+
const near4 = key2 && key2.timestamp >= sourceStart && time - key2.timestamp <= maxKeyframeLead;
|
|
27582
|
+
yield near4 ? key2.timestamp : time;
|
|
27430
27583
|
}
|
|
27431
27584
|
}
|
|
27432
27585
|
const aspect = displayWidth / displayHeight;
|
|
@@ -45961,12 +46114,12 @@ function statesValueAt(anim, time) {
|
|
|
45961
46114
|
const duration = resolveTweenDuration(anim);
|
|
45962
46115
|
if (!(duration > 0)) return false;
|
|
45963
46116
|
const pct = (time - (resolveTweenStart(anim) ?? 0)) / duration * 100;
|
|
45964
|
-
const
|
|
46117
|
+
const near4 = (keyframe) => Math.abs(keyframe - pct) <= KEYFRAME_PCT_MATCH;
|
|
45965
46118
|
const runEase = runEaseOf(anim);
|
|
45966
46119
|
if (anim.keyframes)
|
|
45967
|
-
return anim.keyframes.keyframes.some((kf) =>
|
|
45968
|
-
if (anim.method === "from") return
|
|
45969
|
-
return
|
|
46120
|
+
return anim.keyframes.keyframes.some((kf) => near4(timeAtProgress(runEase, kf.percentage)));
|
|
46121
|
+
if (anim.method === "from") return near4(0);
|
|
46122
|
+
return near4(100) || anim.method === "fromTo" && near4(0);
|
|
45970
46123
|
}
|
|
45971
46124
|
function pickClosestToPlayhead(anims) {
|
|
45972
46125
|
if (anims.length <= 1) return anims[0] ?? null;
|
|
@@ -74513,7 +74666,7 @@ function useFileManager({
|
|
|
74513
74666
|
import { useCallback as useCallback106, useRef as useRef130, useState as useState114 } from "react";
|
|
74514
74667
|
|
|
74515
74668
|
// src/utils/gsapUndoRestore.ts
|
|
74516
|
-
import { isCompositionTemplate as isCompositionTemplate2 } from "@hyperframes/parsers/hf-ids";
|
|
74669
|
+
import { ensureHfIds as ensureHfIds2, isCompositionTemplate as isCompositionTemplate2 } from "@hyperframes/parsers/hf-ids";
|
|
74517
74670
|
function elementIdentityKey(el) {
|
|
74518
74671
|
const hfId = el.getAttribute("data-hf-id");
|
|
74519
74672
|
if (hfId) return `hf:${hfId}`;
|
|
@@ -74649,8 +74802,10 @@ function scopeTargets(scope, keys, restoredByKey, previousByKey) {
|
|
|
74649
74802
|
return targets;
|
|
74650
74803
|
}
|
|
74651
74804
|
function fileTargets(doc, path, isActive, file) {
|
|
74652
|
-
const
|
|
74653
|
-
const
|
|
74805
|
+
const previewStamped = doc.querySelector("[data-hf-id]") !== null;
|
|
74806
|
+
const stampedLikeThePreview = (html2) => previewStamped && !html2.includes("data-hf-id") ? ensureHfIds2(html2) : html2;
|
|
74807
|
+
const prevDoc = parseRestoreSource(stampedLikeThePreview(file.previous));
|
|
74808
|
+
const nextDoc = parseRestoreSource(stampedLikeThePreview(file.restored));
|
|
74654
74809
|
const scripted = restoreScripted(prevDoc, nextDoc, isActive);
|
|
74655
74810
|
if (scripted === null) return null;
|
|
74656
74811
|
const keys = diffRestoreDocs(prevDoc, nextDoc);
|
|
@@ -74802,11 +74957,11 @@ function syncStoredElementAttribute(target, attr, value) {
|
|
|
74802
74957
|
const fields = STORED_FIELD[attr]?.(value);
|
|
74803
74958
|
if (!fields) return;
|
|
74804
74959
|
const key2 = getTimelineElementIdentity(target);
|
|
74805
|
-
const
|
|
74960
|
+
const same2 = (element) => Object.keys(fields).every((k) => element[k] === fields[k]);
|
|
74806
74961
|
usePlayerStore.setState((state) => {
|
|
74807
74962
|
let changed = false;
|
|
74808
74963
|
const elements = state.elements.map((element) => {
|
|
74809
|
-
if (getTimelineElementIdentity(element) !== key2 ||
|
|
74964
|
+
if (getTimelineElementIdentity(element) !== key2 || same2(element)) return element;
|
|
74810
74965
|
changed = true;
|
|
74811
74966
|
return { ...element, ...fields };
|
|
74812
74967
|
});
|
|
@@ -81675,13 +81830,13 @@ function useGsapKeyframeOps({
|
|
|
81675
81830
|
|
|
81676
81831
|
// src/hooks/useGsapPropertyDebounce.ts
|
|
81677
81832
|
import { useCallback as useCallback138, useEffect as useEffect118, useRef as useRef146 } from "react";
|
|
81678
|
-
import { parseGsapScriptAcorn } from "@hyperframes/core/gsap-parser-acorn";
|
|
81833
|
+
import { parseGsapScriptAcorn as parseGsapScriptAcorn2 } from "@hyperframes/core/gsap-parser-acorn";
|
|
81679
81834
|
var DEBOUNCE_MS = 150;
|
|
81680
81835
|
function mergeTweenProperties(sdkSession, animationId, edited, kind) {
|
|
81681
81836
|
try {
|
|
81682
81837
|
const script = extractGsapScriptText(sdkSession.serialize());
|
|
81683
81838
|
if (!script) return { ...edited };
|
|
81684
|
-
const anim =
|
|
81839
|
+
const anim = parseGsapScriptAcorn2(script).animations.find((a) => a.id === animationId);
|
|
81685
81840
|
if (!anim) return { ...edited };
|
|
81686
81841
|
const existing = kind === "from" ? anim.fromProperties ?? {} : anim.properties;
|
|
81687
81842
|
return { ...existing, ...edited };
|
|
@@ -84234,8 +84389,8 @@ var resolvedSelections = /* @__PURE__ */ new WeakMap();
|
|
|
84234
84389
|
var checkedSelections = /* @__PURE__ */ new WeakMap();
|
|
84235
84390
|
function checkedPreflight(resolved2, inputs) {
|
|
84236
84391
|
const known = checkedSelections.get(resolved2);
|
|
84237
|
-
const
|
|
84238
|
-
if (
|
|
84392
|
+
const same2 = known?.animations === inputs.animations && known.iframe === inputs.iframe && known.group === inputs.group && known.version === inputs.version;
|
|
84393
|
+
if (same2) return known;
|
|
84239
84394
|
const { animations, iframe, group } = inputs;
|
|
84240
84395
|
const checked = {
|
|
84241
84396
|
...inputs,
|
|
@@ -89052,16 +89207,16 @@ function averageSpeed(samples, from, to) {
|
|
|
89052
89207
|
return total / (seg.length - 1);
|
|
89053
89208
|
}
|
|
89054
89209
|
function speedAtEdge(samples, t2, window2, side) {
|
|
89055
|
-
const
|
|
89210
|
+
const near4 = samples.filter(
|
|
89056
89211
|
(s) => side === "start" ? s.time >= t2 && s.time <= t2 + window2 : s.time >= t2 - window2 && s.time <= t2
|
|
89057
89212
|
);
|
|
89058
|
-
if (
|
|
89213
|
+
if (near4.length < 2) return 0;
|
|
89059
89214
|
let total = 0;
|
|
89060
|
-
for (let i = 1; i <
|
|
89061
|
-
const dt =
|
|
89062
|
-
if (dt > 0) total += Math.abs(
|
|
89215
|
+
for (let i = 1; i < near4.length; i++) {
|
|
89216
|
+
const dt = near4[i].time - near4[i - 1].time;
|
|
89217
|
+
if (dt > 0) total += Math.abs(near4[i].value - near4[i - 1].value) / dt;
|
|
89063
89218
|
}
|
|
89064
|
-
return total / (
|
|
89219
|
+
return total / (near4.length - 1);
|
|
89065
89220
|
}
|
|
89066
89221
|
function fitEasesFromVelocity(keyframes, rawSamples, totalDuration) {
|
|
89067
89222
|
if (keyframes.length < 2 || rawSamples.length < 3) return keyframes;
|
|
@@ -91692,9 +91847,9 @@ function useLastFrame(request, snapshot3) {
|
|
|
91692
91847
|
}
|
|
91693
91848
|
function useNearViewport() {
|
|
91694
91849
|
const [element, setElement] = useState141(null);
|
|
91695
|
-
const [
|
|
91850
|
+
const [near4, setNear] = useState141(false);
|
|
91696
91851
|
useEffect144(() => {
|
|
91697
|
-
if (!element ||
|
|
91852
|
+
if (!element || near4) return;
|
|
91698
91853
|
if (typeof IntersectionObserver === "undefined") return setNear(true);
|
|
91699
91854
|
const observer = new IntersectionObserver(
|
|
91700
91855
|
(entries) => entries.some((entry2) => entry2.isIntersecting) && setNear(true),
|
|
@@ -91702,8 +91857,8 @@ function useNearViewport() {
|
|
|
91702
91857
|
);
|
|
91703
91858
|
observer.observe(element);
|
|
91704
91859
|
return () => observer.disconnect();
|
|
91705
|
-
}, [element,
|
|
91706
|
-
return [setElement,
|
|
91860
|
+
}, [element, near4]);
|
|
91861
|
+
return [setElement, near4];
|
|
91707
91862
|
}
|
|
91708
91863
|
function CompCard({
|
|
91709
91864
|
projectId,
|