fix(studio): give the motion path and the fallbacks a one-element target

The narrowing this branch adds missed the motion-path overlay, and every
caller that could not narrow fell back to the exact bare class the narrowing
exists to replace.

- motionPathSelection.selectorFor now goes through writeTargetSelector. It
  feeds both the geometry read and the "set destination" write, so a class
  sibling measured its home off the FIRST sibling and then authored
  add-motion-path onto all of them. The toolbar toggle hides when no
  one-element form exists rather than arming a press that is dropped.
- The five new-tween writers that fell back to the selection's own selector
  now drop the commit instead. A gesture that does not persist reverts on the
  next reload; a tween silently aimed at five elements does not.
- tweenTargetsElement only follows the DOM to a target that matches exactly
  one element. A target the element merely shares with its siblings is a group
  tween, and these callers mutate what they find, so an individual nudge was
  rewriting the group's own tween and moving all five.
This commit is contained in:
Miguel Angel Simon Sierra
2026-07-28 21:21:37 +02:00
parent 3d92436066
commit 1f3fd2800c
9 changed files with 190 additions and 41 deletions
@@ -178,7 +178,14 @@ export function useGestureCommit({
showToast("Cannot save — element has no selector", "error");
return;
}
const writeSelector = writeTargetSelector(sel) ?? selector;
// A recorded gesture becomes a NEW tween, so its target must address one
// element; the selection's own selector would record the motion onto
// every sibling sharing its class (see writeTargetSelector).
const writeSelector = writeTargetSelector(sel);
if (!writeSelector) {
showToast("Cannot save: element has no unique selector", "error");
return;
}
if (liveSession.commitMutation) {
const recStart = recordingStartTimeRef.current;
const rawKeyframes = sortedPcts.map((pct) => ({