fix(studio): harden composition timeline reliability (#2615)

* fix(studio): preserve composition playback continuity

* feat(studio): drag compositions into the timeline

* fix(studio): collapse expanded composition move aliases

* fix(studio): make timeline cuts atomic

* fix(studio): group inspector gesture history

* test(studio): cover masked text selection

* fix(studio): harden composition timeline reliability

* fix(studio): satisfy CI source gates

* fix(studio): harden composition mutation requests
This commit is contained in:
Miguel Ángel
2026-07-17 14:15:30 -04:00
committed by GitHub
parent 2be8a62c00
commit 2b65b4efce
93 changed files with 3925 additions and 758 deletions
@@ -48,9 +48,51 @@ describe("buildExpandedElements", () => {
const out = buildExpandedElements(elements, manifest, parentMap, "s3", "s3");
const child = out.find((e) => e.domId === "stat-1")!;
expect(child.expandedParentStart).toBe(16);
expect(child.expandedHostKey).toBe("s3");
expect(child.sourceFile).toBe("stats.html");
});
it("keeps repeated same-source composition hosts as distinct move identities", () => {
const elements = [
el({
id: "host-a",
key: "index.html#host-a",
start: 0,
duration: 5,
compositionSrc: "scene.html",
}),
el({
id: "host-b",
key: "index.html#host-b",
start: 8,
duration: 5,
compositionSrc: "scene.html",
}),
];
const manifest = [
clip({ id: "host-a", start: 0, duration: 5, compositionSrc: "scene.html" }),
clip({ id: "child-a", start: 1, duration: 2 }),
clip({ id: "host-b", start: 8, duration: 5, compositionSrc: "scene.html" }),
clip({ id: "child-b", start: 9, duration: 2 }),
];
const parentMap = new Map([
["child-a", "host-a"],
["child-b", "host-b"],
]);
const childA = buildExpandedElements(elements, manifest, parentMap, "host-a", "host-a").find(
(element) => element.domId === "child-a",
);
const childB = buildExpandedElements(elements, manifest, parentMap, "host-b", "host-b").find(
(element) => element.domId === "child-b",
);
expect(childA?.sourceFile).toBe("scene.html");
expect(childB?.sourceFile).toBe("scene.html");
expect(childA?.expandedHostKey).toBe("index.html#host-a");
expect(childB?.expandedHostKey).toBe("index.html#host-b");
});
// fallow-ignore-next-line code-duplication
it("rebases a 2-level child onto its NESTED host, not the top-level scene", () => {
// top host A@10 (a.html) embeds host B@12 (b.html); child C lives in b.html.
@@ -133,6 +133,7 @@ function buildChildElements(
siblings: ClipManifestClip[],
display: DisplayBounds,
editBasis: { start: number; sourceFile: string | undefined },
expandedHostKey: string,
): TimelineElement[] {
const result: TimelineElement[] = [];
for (const child of siblings) {
@@ -182,6 +183,7 @@ function buildChildElements(
authoredTrack: base.authoredTrack,
stackingContextId: base.stackingContextId,
expandedParentStart: editBasis.start,
expandedHostKey,
domId,
selector,
sourceFile: editBasis.sourceFile,
@@ -260,6 +262,7 @@ export function buildExpandedElements(
track: topLevelElement.track,
},
editBasis,
parentKey,
);
if (expanded.length === 0) return filterToTopLevel(elements, parentMap);