mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-10 22:20:14 +00:00
fix(studio): author every new tween against one element
U3 fixed "add keyframe at playhead" widening a write to every sibling sharing a class, but wired writeTargetSelector into only two paths. The same bug was still reachable from the add-animation button, drag, resize, rotate, gesture recording, and the property panel: each derived its target from selectorFromSelection, which hands back a bare class for an id-less element, so one edit authored a tween over all five siblings and the timeline collapsed their rows into one. Route every path that authors a NEW tween through the existing ladder: - ensureElementAddressable now accepts selection.selector only when it addresses exactly one element, so the id-minting fallback right below it (previously unreachable whenever any selector was present) does the work. - gsapDragCommit's five new-tween branches go through one newTweenTarget helper; instant patches reuse the written target so the runtime moves the element the source write names. - useGestureCommit and useAnimatedPropertyCommit keep the existing selector for matching/retargeting and author new tweens with a separate write selector. Retargets of an EXISTING tween are deliberately untouched: they keep anim.targetSelector, so a tween aimed at a whole group stays aimed at it. Narrowing the write alone regressed idempotency, verified by test: the "is there already a write for this element" lookups matched targetSelector by string, so the next nudge missed the write it had just made and appended a second, conflicting one. The read half now falls back to the live DOM (tweenTargetsElement, same contract as getAnimationsForElement), which also still matches a deliberate group tween. Tests reproduce each site through a real writer, re-parse with the real parser, and resolve through resolveSelectorElementIds (what feeds the keyframe cache and the lanes), plus pins for the new-tween vs retarget-existing distinction so a future change cannot collapse the two.
This commit is contained in:
@@ -13,7 +13,7 @@ import type { GsapAnimation } from "@hyperframes/core/gsap-parser";
|
||||
import type { CommitMutationOptions } from "./gsapScriptCommitTypes";
|
||||
import { roundTo3 } from "../utils/rounding";
|
||||
import { classifyPropertyGroup } from "@hyperframes/core/gsap-parser";
|
||||
import { isInstantHold, idSelector } from "./gsapShared";
|
||||
import { isInstantHold, idSelector, writeTargetSelector, tweenTargetsElement } from "./gsapShared";
|
||||
|
||||
type RecordedKeyframe = {
|
||||
percentage: number;
|
||||
@@ -168,11 +168,17 @@ export function useGestureCommit({
|
||||
if (!sortedPcts.includes(0)) sortedPcts.unshift(0);
|
||||
}
|
||||
|
||||
// Two different jobs, two different selectors. `selector` is the string an
|
||||
// ALREADY-AUTHORED tween is matched against (and retargeted with, so a
|
||||
// tween aimed at a whole group stays aimed at it). `writeSelector` is what
|
||||
// a NEW tween is authored with: the bare class the id-less case yields here
|
||||
// would record the gesture onto every sibling sharing it.
|
||||
const selector = sel.id ? idSelector(sel.id) : sel.selector;
|
||||
if (!selector) {
|
||||
showToast("Cannot save — element has no selector", "error");
|
||||
return;
|
||||
}
|
||||
const writeSelector = writeTargetSelector(sel) ?? selector;
|
||||
if (liveSession.commitMutation) {
|
||||
const recStart = recordingStartTimeRef.current;
|
||||
const rawKeyframes = sortedPcts.map((pct) => ({
|
||||
@@ -186,7 +192,11 @@ export function useGestureCommit({
|
||||
);
|
||||
const allAnims = liveSession.selectedGsapAnimations ?? [];
|
||||
const existingPositionTween = hasPositionProps
|
||||
? allAnims.find((a) => a.propertyGroup === "position" && a.targetSelector === selector)
|
||||
? allAnims.find(
|
||||
(a) =>
|
||||
a.propertyGroup === "position" &&
|
||||
tweenTargetsElement(a.targetSelector, selector, sel.element),
|
||||
)
|
||||
: undefined;
|
||||
if (existingPositionTween) {
|
||||
if (isInstantHold(existingPositionTween)) {
|
||||
@@ -261,7 +271,7 @@ export function useGestureCommit({
|
||||
await liveSession.commitMutation(
|
||||
{
|
||||
type: "add-with-keyframes",
|
||||
targetSelector: selector,
|
||||
targetSelector: writeSelector,
|
||||
position: roundTo3(recStart),
|
||||
duration: roundTo3(duration),
|
||||
keyframes: groupKfs,
|
||||
@@ -287,7 +297,7 @@ export function useGestureCommit({
|
||||
await liveSession.commitMutation(
|
||||
{
|
||||
type: "add-with-keyframes",
|
||||
targetSelector: selector,
|
||||
targetSelector: writeSelector,
|
||||
position: roundTo3(recStart),
|
||||
duration: roundTo3(duration),
|
||||
keyframes: groupKfs,
|
||||
|
||||
Reference in New Issue
Block a user