fix(studio): keep timeline selection authoritative in the preview sync

The store-to-preview sync no longer applies a partial selection: if a resolvable member's
DOM node is not ready yet it bails and retries on the next effect run, so the write-back can
never shrink the store's selection by dropping an unresolved member.

Marquee row hit-testing reuses shouldShowTimelineLayerGroupHeader instead of re-deriving
the group-header placement rule, keeping one owner for that predicate.
This commit is contained in:
Miguel Angel Simon Sierra
2026-07-09 16:54:34 -04:00
parent 0fe38e8cc8
commit 6673c32868
2 changed files with 9 additions and 1 deletions
@@ -88,13 +88,20 @@ export function useTimelineSelectionPreviewSync({
let cancelled = false;
const syncSelection = async () => {
const selections: DomEditSelection[] = [];
let resolvableCount = 0;
for (const id of selectedIds) {
const element = timelineElements.find((item) => (item.key ?? item.id) === id);
if (!element) continue;
resolvableCount += 1;
const selection = await buildDomSelectionForTimelineElement(element);
if (selection) selections.push(selection);
}
if (cancelled) return;
// The store is the source of truth: applying a partial set would write that
// shrunk set back and silently drop the members whose DOM node was not ready.
// Bail instead; a later effect run (on timelineElements/DOM change) applies the
// full set once every resolvable member has a live node.
if (selections.length < resolvableCount) return;
if (selections.length === 0) {
applyDomSelection(null, { revealPanel: false });
} else if (selections.length === 1) {
@@ -2,6 +2,7 @@ import { formatTime } from "../lib/time";
import { roundToCenti } from "../../utils/rounding";
import type { StackingTimelineLayer, TimelineLayerId } from "./timelineTrackOrder";
import { resolveTimelineLayerStackingMove } from "./timelineLayerDrag";
import { shouldShowTimelineLayerGroupHeader } from "./TimelineLayerGroupHeader";
import type { TimelineStackingElement, TimelineStackingReorderIntent } from "./timelineStacking";
import {
@@ -348,7 +349,7 @@ export function selectTimelineElementsInMarquee({
for (const layerId of layerOrder) {
const layer = layerById.get(layerId);
if (!layer) continue;
if (layer.contextKey !== "" && layer.contextKey !== previousContextKey) {
if (shouldShowTimelineLayerGroupHeader(layer.contextKey, previousContextKey)) {
rowTop += groupHeaderHeight;
}
const rowBottom = rowTop + trackHeight;