mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-03 23:03:09 +00:00
fix(studio): activate a composition at any path, not just compositions/
The Comps panel sets activeCompositionPath to the selected file, but useCompositionStack's effect only pushed a stack level when that path started with compositions/. A project laying its comps out anywhere else, for example a generated multi-part build with parts/part-1.html next to the root index.html, matched no branch at all: the row highlighted and the URL hash updated while the stack silently kept the master mounted, so the canvas and timeline stayed on index.html and any edit landed in the root file instead of the part. Replaced the prefix test with a plain truthiness check, so the root stays on the master level and every other path pushes its own level. Label derivation is unchanged, matching CompositionsTab's own convention.
This commit is contained in:
@@ -42,3 +42,72 @@ describe("useCompositionStack — project scoping", () => {
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
describe("useCompositionStack — activating a composition by path", () => {
|
||||
afterEach(() => {
|
||||
document.body.innerHTML = "";
|
||||
});
|
||||
|
||||
function mountStack(activeCompositionPath: string | null) {
|
||||
const host = document.createElement("div");
|
||||
document.body.append(host);
|
||||
const root = createRoot(host);
|
||||
const seen: { stack: ReturnType<typeof useCompositionStack>["compositionStack"] } = {
|
||||
stack: [],
|
||||
};
|
||||
|
||||
function Harness(props: { activeCompositionPath: string | null }) {
|
||||
seen.stack = useCompositionStack({ projectId: "p", ...props }).compositionStack;
|
||||
return null;
|
||||
}
|
||||
|
||||
return { root, seen, Harness, activeCompositionPath };
|
||||
}
|
||||
|
||||
// The root stays on the master level; everything else pushes a second level.
|
||||
for (const path of [
|
||||
"compositions/scene-a.html",
|
||||
"parts/part-1.html",
|
||||
"chapter-2.html",
|
||||
"a/b/c/deep.html",
|
||||
]) {
|
||||
it(`pushes a level for ${path}`, async () => {
|
||||
const { root, seen, Harness } = mountStack(path);
|
||||
|
||||
await act(async () => {
|
||||
root.render(<Harness activeCompositionPath={path} />);
|
||||
});
|
||||
|
||||
expect(seen.stack).toHaveLength(2);
|
||||
expect(seen.stack[1]?.id).toBe(path);
|
||||
expect(seen.stack[1]?.previewUrl).toBe(`/api/projects/p/preview/comp/${path}`);
|
||||
|
||||
act(() => root.unmount());
|
||||
});
|
||||
}
|
||||
|
||||
it("labels a non-compositions/ path without mangling it", async () => {
|
||||
const { root, seen, Harness } = mountStack("parts/part-1.html");
|
||||
|
||||
await act(async () => {
|
||||
root.render(<Harness activeCompositionPath="parts/part-1.html" />);
|
||||
});
|
||||
|
||||
expect(seen.stack[1]?.label).toBe("parts/part-1");
|
||||
|
||||
act(() => root.unmount());
|
||||
});
|
||||
|
||||
it("keeps the master alone for the root composition", async () => {
|
||||
const { root, seen, Harness } = mountStack("index.html");
|
||||
|
||||
await act(async () => {
|
||||
root.render(<Harness activeCompositionPath="index.html" />);
|
||||
});
|
||||
|
||||
expect(seen.stack).toHaveLength(1);
|
||||
expect(seen.stack[0]?.id).toBe("master");
|
||||
|
||||
act(() => root.unmount());
|
||||
});
|
||||
});
|
||||
|
||||
@@ -108,7 +108,13 @@ export function useCompositionStack({
|
||||
if (activeCompositionPath === "index.html") {
|
||||
usePlayerStore.getState().setElements([]);
|
||||
updateCompositionStack([master]);
|
||||
} else if (activeCompositionPath && activeCompositionPath.startsWith("compositions/")) {
|
||||
} else if (activeCompositionPath) {
|
||||
// Any composition file that isn't the root, wherever it lives. Gating
|
||||
// this on a `compositions/` prefix meant a project laying its comps out
|
||||
// anywhere else (`parts/part-1.html`, generated multi-part builds) hit
|
||||
// no branch at all: the stack kept the master mounted while the Comps
|
||||
// panel highlighted the row, so the canvas and timeline stayed on
|
||||
// index.html and edits landed in the root file.
|
||||
const label = activeCompositionPath.replace(/^compositions\//, "").replace(/\.html$/, "");
|
||||
const previewUrl = `/api/projects/${projectId}/preview/comp/${encodePreviewPath(activeCompositionPath)}`;
|
||||
usePlayerStore.getState().setElements([]);
|
||||
@@ -116,7 +122,7 @@ export function useCompositionStack({
|
||||
if (prev[prev.length - 1]?.id === activeCompositionPath) return prev;
|
||||
return [master, { id: activeCompositionPath, label, previewUrl }];
|
||||
});
|
||||
} else if (!activeCompositionPath) {
|
||||
} else {
|
||||
usePlayerStore.getState().setElements([]);
|
||||
updateCompositionStack([master]);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user