mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-03 04:38:33 +00:00
Merge pull request #1826 from heygen-com/fix/studio-recent-issues
fix(studio): resolve timeline keyframe, click-selection, and nested-video sync regressions
This commit is contained in:
@@ -578,6 +578,56 @@ describe("initSandboxRuntimeModular", () => {
|
||||
expect(video.currentTime).toBe(9);
|
||||
});
|
||||
|
||||
// Regression (#1838): a video authoring its OWN data-start (the normal case
|
||||
// for a timed clip the studio positions on a track) took a fast literal-
|
||||
// value path that skipped adding the host composition's start offset —
|
||||
// unlike the no-own-data-start case above, which already went through
|
||||
// resolveStartForElement and got the offset for free. The video played
|
||||
// from the ROOT timeline's time instead of holding until its parent scene
|
||||
// began, desyncing from the correctly-offset GSAP overlay in the same scene.
|
||||
it("offsets a nested video's own data-start by its host composition's start", () => {
|
||||
const root = document.createElement("div");
|
||||
root.setAttribute("data-composition-id", "main");
|
||||
root.setAttribute("data-root", "true");
|
||||
root.setAttribute("data-width", "1920");
|
||||
root.setAttribute("data-height", "1080");
|
||||
document.body.appendChild(root);
|
||||
|
||||
const child = document.createElement("div");
|
||||
child.setAttribute("data-composition-id", "scene-2");
|
||||
child.setAttribute("data-start", "20");
|
||||
child.setAttribute("data-duration", "16");
|
||||
root.appendChild(child);
|
||||
|
||||
const video = document.createElement("video");
|
||||
// Authored relative to scene-2's own local timeline, not the root's.
|
||||
video.setAttribute("data-start", "0");
|
||||
video.setAttribute("data-duration", "16");
|
||||
child.appendChild(video);
|
||||
Object.defineProperty(video, "duration", { value: 20, writable: true, configurable: true });
|
||||
Object.defineProperty(video, "paused", { value: true, writable: true, configurable: true });
|
||||
Object.defineProperty(video, "readyState", { value: 4, writable: true, configurable: true });
|
||||
Object.defineProperty(video, "currentTime", { value: 0, writable: true, configurable: true });
|
||||
video.load = () => {};
|
||||
video.play = () => Promise.resolve();
|
||||
|
||||
window.__timelines = {
|
||||
main: createMockTimeline(40),
|
||||
"scene-2": createMockTimeline(16),
|
||||
};
|
||||
|
||||
initSandboxRuntimeModular();
|
||||
|
||||
const player = window.__player;
|
||||
expect(player).toBeDefined();
|
||||
|
||||
// Root t=25 is 5s into scene-2 (which starts at root t=20) — the video
|
||||
// must be 5s into its own local playback, not 25s (root time).
|
||||
player?.seek(25);
|
||||
|
||||
expect(video.currentTime).toBe(5);
|
||||
});
|
||||
|
||||
it("updates visibility for timed elements inside nested compositions", () => {
|
||||
const root = document.createElement("div");
|
||||
root.setAttribute("data-composition-id", "main");
|
||||
|
||||
@@ -496,7 +496,16 @@ export function initSandboxRuntimeModular(): void {
|
||||
|
||||
const resolveMediaStartSeconds = (element: Element, fallback = 0): number => {
|
||||
if (!element.hasAttribute("data-hf-auto-start") && element.hasAttribute("data-start")) {
|
||||
return Math.max(0, Number(element.getAttribute("data-start") ?? 0) || 0);
|
||||
// `data-start` is authored relative to the media element's OWN sub-
|
||||
// composition, not the root timeline — `fallback` carries the host
|
||||
// composition's resolved absolute start (see syncMediaForCurrentState's
|
||||
// inheritedStart), so it must be added, not discarded. Skipping it made
|
||||
// a nested video play from root t=0 instead of holding until its
|
||||
// parent scene began (issue #1838) — resolveStartForElement's own
|
||||
// absolute-expression branch already adds this same host offset, this
|
||||
// fast literal-value path just didn't.
|
||||
const own = Math.max(0, Number(element.getAttribute("data-start") ?? 0) || 0);
|
||||
return own + fallback;
|
||||
}
|
||||
return resolveStartForElement(element, fallback);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user