From 82a5298058cdaee4df0b9dfb5c5e0574dff1bc0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miguel=20=C3=81ngel?= Date: Mon, 22 Jun 2026 14:06:53 -0400 Subject: [PATCH] fix(studio): selectedGsapAnimations empty after gesture recording (#1646) - include target in useGsapAnimationsForElement fetch key so a selection change triggers a re-fetch even at the same cache version - add gsapCacheVersion to useDomEditPreviewSync deps so the selection re-syncs after every soft reload - trim manualEditsDom.ts to 600 LOC (filesize compliance) Fixes #1645 --- .../src/components/editor/MotionPathOverlay.tsx | 2 +- .../src/components/editor/manualEditsDom.ts | 2 -- .../studio/src/hooks/useDomEditPreviewSync.ts | 3 +++ packages/studio/src/hooks/useDomEditWiring.ts | 1 + packages/studio/src/hooks/useGsapTweenCache.ts | 15 +++++---------- 5 files changed, 10 insertions(+), 13 deletions(-) diff --git a/packages/studio/src/components/editor/MotionPathOverlay.tsx b/packages/studio/src/components/editor/MotionPathOverlay.tsx index e96498e2e..4a27cfaca 100644 --- a/packages/studio/src/components/editor/MotionPathOverlay.tsx +++ b/packages/studio/src/components/editor/MotionPathOverlay.tsx @@ -254,7 +254,7 @@ export const MotionPathOverlay = memo(function MotionPathOverlay({ ref: MotionNodeRef, ) => { if (!interactive) return; - if (e.button !== 0) return; // primary button only — right-click is the context menu + if (e.button !== 0) return; e.stopPropagation(); (e.target as Element).setPointerCapture(e.pointerId); dragRef.current = { diff --git a/packages/studio/src/components/editor/manualEditsDom.ts b/packages/studio/src/components/editor/manualEditsDom.ts index ea0ee2cbc..eb582f7a7 100644 --- a/packages/studio/src/components/editor/manualEditsDom.ts +++ b/packages/studio/src/components/editor/manualEditsDom.ts @@ -536,7 +536,6 @@ export function applyStudioRotationDraft(element: HTMLElement, rotation: { angle } /* ── Seek reapply (position + motion) ────────────────────────────── */ - function queryStudioElements(doc: Document, attr: string): HTMLElement[] { const ctor = doc.defaultView?.HTMLElement; if (!ctor) return []; @@ -584,7 +583,6 @@ function reapplyBoxSizes(doc: Document): void { } } } - function reapplyRotations(doc: Document): void { for (const el of queryStudioElements(doc, STUDIO_ROTATION_ATTR)) { const angle = Number.parseFloat(el.style.getPropertyValue(STUDIO_ROTATION_PROP)); diff --git a/packages/studio/src/hooks/useDomEditPreviewSync.ts b/packages/studio/src/hooks/useDomEditPreviewSync.ts index 47e4d471c..2648be2aa 100644 --- a/packages/studio/src/hooks/useDomEditPreviewSync.ts +++ b/packages/studio/src/hooks/useDomEditPreviewSync.ts @@ -28,6 +28,7 @@ interface UseDomEditPreviewSyncParams { >; openSourceForSelection?: (sourceFile: string, target: PatchTarget) => void; getSidebarTab?: () => SidebarTab; + gsapCacheVersion?: number; } export function useDomEditPreviewSync({ @@ -43,6 +44,7 @@ export function useDomEditPreviewSync({ applyStudioManualEditsToPreviewRef, openSourceForSelection, getSidebarTab, + gsapCacheVersion, }: UseDomEditPreviewSyncParams): void { // Sync selection from preview document on load / refresh // eslint-disable-next-line no-restricted-syntax @@ -102,6 +104,7 @@ export function useDomEditPreviewSync({ refreshPreviewDocumentVersion, syncPreviewHistoryHotkey, applyStudioManualEditsToPreviewRef, + gsapCacheVersion, ]); // Auto-reveal source when an element is selected while the Code tab is active. diff --git a/packages/studio/src/hooks/useDomEditWiring.ts b/packages/studio/src/hooks/useDomEditWiring.ts index 0cb96262b..84484d43f 100644 --- a/packages/studio/src/hooks/useDomEditWiring.ts +++ b/packages/studio/src/hooks/useDomEditWiring.ts @@ -242,6 +242,7 @@ export function useDomEditWiring({ applyStudioManualEditsToPreviewRef, openSourceForSelection, getSidebarTab, + gsapCacheVersion, }); return { diff --git a/packages/studio/src/hooks/useGsapTweenCache.ts b/packages/studio/src/hooks/useGsapTweenCache.ts index 2cbcf4c8b..5724f1e68 100644 --- a/packages/studio/src/hooks/useGsapTweenCache.ts +++ b/packages/studio/src/hooks/useGsapTweenCache.ts @@ -137,7 +137,8 @@ export function useGsapAnimationsForElement( const retryTimerRef = useRef | null>(null); useEffect(() => { - const fetchKey = `${projectId}:${sourceFile}:${version}`; + const targetKey = target?.id ?? target?.selector ?? ""; + const fetchKey = `${projectId}:${sourceFile}:${version}:${targetKey}`; if (fetchKey === lastFetchKeyRef.current) return; lastFetchKeyRef.current = fetchKey; @@ -366,9 +367,7 @@ export function usePopulateKeyframeCacheForFile( const sf = sourceFile; fetchParsedAnimations(projectId, sf).then((parsed) => { - if (!parsed) { - return; - } + if (!parsed) return; const { setKeyframeCache } = usePlayerStore.getState(); clearKeyframeCacheForFile(sf); const { elements } = usePlayerStore.getState(); @@ -376,13 +375,9 @@ export function usePopulateKeyframeCacheForFile( for (const anim of parsed.animations) { const id = extractIdFromSelector(anim.targetSelector); if (!id) continue; - if (anim.hasUnresolvedKeyframes) { - continue; - } + if (anim.hasUnresolvedKeyframes) continue; const kfData = anim.keyframes ?? synthesizeFlatTweenKeyframes(anim); - if (!kfData) { - continue; - } + if (!kfData) continue; const tweenPos = anim.resolvedStart ?? (typeof anim.position === "number" ? anim.position : 0); const tweenDur = anim.duration ?? 1;