fix(studio): order the timing write after the z-index commit on a diagonal drag

A drag that both moved a clip in time and restacked it fired two writers on
the same file via separate queues (a targeted z-index patch and a full-file
timing overwrite), so the overwrite could clobber the just-persisted z-index
and the restack silently vanished after reload. The move now awaits the
z-index commit before persisting timing, giving the file one ordered writer
per gesture. Adds a regression test that gates the commit and asserts the
timing write waits.
This commit is contained in:
Miguel Angel Simon Sierra
2026-07-09 00:04:35 -04:00
parent 6f0bf75b69
commit 51dd8a0753
5 changed files with 133 additions and 55 deletions
@@ -170,13 +170,14 @@ export function useElementLifecycleOps({
sourceFile: string;
}>,
) => {
if (entries.length === 0) return;
if (entries.length === 0) return Promise.resolve();
// Resolver shadow (telemetry-only, decoupled from cutover): record whether
// the SDK resolves each reordered element — the reorderElements op's targets.
onReorderShadow?.(
entries.map((e) => readHfId(e.element)).filter((id): id is string => id != null),
);
const coalesceKey = `z-reorder:${entries.map((e) => e.id ?? e.selector ?? e.element.getAttribute("data-hf-id") ?? "el").join(":")}`;
const saves: Array<Promise<void>> = [];
for (let i = 0; i < entries.length; i++) {
const entry = entries[i];
entry.element.style.zIndex = String(entry.zIndex);
@@ -192,23 +193,28 @@ export function useElementLifecycleOps({
} catch {
/* cross-origin or detached — skip */
}
void commitPositionPatchToHtml(
{
element: entry.element,
id: entry.id ?? null,
hfId: readHfId(entry.element),
selector: entry.selector,
selectorIndex: entry.selectorIndex,
sourceFile: entry.sourceFile,
} as unknown as DomEditSelection,
patches,
{
label: "Reorder layers",
coalesceKey,
skipRefresh: i < entries.length - 1,
},
).catch(() => undefined);
saves.push(
commitPositionPatchToHtml(
{
element: entry.element,
id: entry.id ?? null,
hfId: readHfId(entry.element),
selector: entry.selector,
selectorIndex: entry.selectorIndex,
sourceFile: entry.sourceFile,
} as unknown as DomEditSelection,
patches,
{
label: "Reorder layers",
coalesceKey,
skipRefresh: i < entries.length - 1,
},
).catch(() => undefined),
);
}
// Resolves once every z-index patch is persisted so a same-file timing write
// can be ordered after it (see applyTimelineStackingReorder callers).
return Promise.all(saves).then(() => undefined);
},
[commitPositionPatchToHtml, onReorderShadow],
);