refactor(studio): split oversized files to pass 600-line check (#1313)

This commit is contained in:
Miguel Ángel
2026-06-09 19:53:14 -04:00
committed by GitHub
parent d13ae13670
commit 81416ab3c9
17 changed files with 1380 additions and 1055 deletions
@@ -6,6 +6,11 @@ import { applySoftReload } from "../utils/gsapSoftReload";
import { executeOptimistic } from "../utils/optimisticUpdate";
import { usePlayerStore, type KeyframeCacheEntry } from "../player/store/playerStore";
import { commitKeyframeAtTimeImpl } from "./gsapKeyframeCommit";
import {
updateKeyframeCacheFromParsed,
readKeyframeSnapshot,
writeKeyframeCache,
} from "./gsapKeyframeCacheHelpers";
const PROPERTY_DEFAULTS: Record<string, number> = {
opacity: 1,
@@ -72,84 +77,6 @@ async function mutateGsapScript(
return null;
}
}
function updateKeyframeCacheFromParsed(
animations: GsapAnimation[],
targetPath: string,
selectionId: string | undefined,
mutation: Record<string, unknown>,
): void {
const { setKeyframeCache, elements } = usePlayerStore.getState();
const idsWithKeyframes = new Set<string>();
const merged = new Map<string, KeyframeCacheEntry>();
for (const anim of animations) {
const id = anim.targetSelector.match(/^#([\w-]+)/)?.[1];
if (!id || !anim.keyframes) continue;
idsWithKeyframes.add(id);
// Convert tween-relative percentages to clip-relative so diamonds
// render at the correct position within the timeline clip.
const tweenPos = typeof anim.position === "number" ? anim.position : 0;
const tweenDur = anim.duration ?? 1;
const timelineEl = elements.find(
(el) => el.domId === id || (el.key ?? el.id) === `${targetPath}#${id}`,
);
const elStart = timelineEl?.start ?? 0;
const elDuration = timelineEl?.duration ?? 4;
const clipKeyframes = anim.keyframes.keyframes.map((kf) => {
const absTime = tweenPos + (kf.percentage / 100) * tweenDur;
const clipPct =
elDuration > 0 ? Math.round(((absTime - elStart) / elDuration) * 1000) / 10 : kf.percentage;
return { ...kf, percentage: clipPct };
});
const existing = merged.get(id);
if (existing) {
const byPct = new Map<number, (typeof existing.keyframes)[0]>();
for (const kf of [...existing.keyframes, ...clipKeyframes]) {
const prev = byPct.get(kf.percentage);
if (prev) {
prev.properties = { ...prev.properties, ...kf.properties };
if (kf.ease) prev.ease = kf.ease;
} else {
byPct.set(kf.percentage, { ...kf, properties: { ...kf.properties } });
}
}
existing.keyframes = Array.from(byPct.values()).sort((a, b) => a.percentage - b.percentage);
} else {
merged.set(id, { ...anim.keyframes, keyframes: clipKeyframes });
}
}
for (const [id, entry] of merged) {
setKeyframeCache(`${targetPath}#${id}`, entry);
setKeyframeCache(id, entry);
if (targetPath !== "index.html") setKeyframeCache(`index.html#${id}`, entry);
}
const targetId =
(mutation as { targetSelector?: string }).targetSelector?.match(/^#([\w-]+)/)?.[1] ??
selectionId;
if (targetId && !idsWithKeyframes.has(targetId)) {
setKeyframeCache(`${targetPath}#${targetId}`, undefined);
if (targetPath !== "index.html") setKeyframeCache(`index.html#${targetId}`, undefined);
}
}
function buildCacheKey(sourceFile: string, elementId: string): string {
return `${sourceFile}#${elementId}`;
}
function readKeyframeSnapshot(
sourceFile: string,
elementId: string | null | undefined,
): KeyframeCacheEntry | undefined {
if (!elementId) return undefined;
return usePlayerStore.getState().keyframeCache.get(buildCacheKey(sourceFile, elementId));
}
function writeKeyframeCache(
sourceFile: string,
elementId: string | null | undefined,
data: KeyframeCacheEntry | undefined,
): void {
if (!elementId) return;
usePlayerStore.getState().setKeyframeCache(buildCacheKey(sourceFile, elementId), data);
}
interface GsapScriptCommitsParams {
projectIdRef: React.MutableRefObject<string | null>;
activeCompPath: string | null;