mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-03 23:03:09 +00:00
refactor: address review — rename activateSiblingTimelines, opts arg, FIXME tracking
- Rename activateNestedChildTimelines → activateSiblingTimelines (matches player.ts)
- Use tl.play() instead of tl.paused(false) for consistency
- Convert positional activateChildren boolean to { activateChildren } opts
- Add FIXME(#969) to divergence test with tracking issue link
- Add [id="intro"] no-rewrite boundary test
- Add comment about deliberate no-restore behavior in render-seek path
This commit is contained in:
@@ -513,6 +513,17 @@ window.__afterTimeline = window.__timelines.scene;
|
||||
expect(scoped).not.toMatch(/#intro\b/);
|
||||
});
|
||||
|
||||
it('does not rewrite [id="intro"] attribute selectors', () => {
|
||||
// The function only targets #intro hash selectors, not [id="intro"] attribute selectors
|
||||
const result = scopeCssToComposition(
|
||||
'[id="intro"] .title { color: red; }',
|
||||
"intro",
|
||||
undefined,
|
||||
"intro",
|
||||
);
|
||||
expect(result).toContain('[id="intro"]');
|
||||
});
|
||||
|
||||
it("wraps scripts with authored root id normalization for #id GSAP selectors", () => {
|
||||
const { document } = parseHTML(`
|
||||
<div data-composition-id="intro">
|
||||
|
||||
@@ -147,6 +147,8 @@ describe("inlineSubCompositions – #ID selector scoping divergence", () => {
|
||||
* Proper fix (follow-up): make the producer path add data-hf-authored-id
|
||||
* to the host element when the inner root has an id attribute.
|
||||
*/
|
||||
// FIXME(#969): flip these assertions once the producer path adds
|
||||
// data-hf-authored-id to the host element. See PR #965 "Proper fix (follow-up)".
|
||||
it("documents the divergence: producer path lacks data-hf-authored-id element", () => {
|
||||
const document = makeHostDocument("intro");
|
||||
const host = document.querySelector('[data-composition-src="intro.html"]')!;
|
||||
|
||||
@@ -1724,40 +1724,39 @@ export function initSandboxRuntimeModular(): void {
|
||||
}
|
||||
};
|
||||
|
||||
// Unpause all non-root timelines. Per GSAP semantics, paused(false) on a
|
||||
// child timeline that hasn't been reached by the parent's playhead is a
|
||||
// no-op — the child won't fire onStart/onUpdate until the parent seeks
|
||||
// past its insertion point. Per-frame visibility is gated by the engine.
|
||||
const activateNestedChildTimelines = (masterTimeline: RuntimeTimelineLike) => {
|
||||
// Unpause all non-root timelines registered in window.__timelines (siblings
|
||||
// in the registry, not GSAP child tweens). Matches the naming convention in
|
||||
// player.ts:32 (forEachSiblingTimeline) and player.ts:89 (activateSiblingTimelines).
|
||||
//
|
||||
// Unlike the player's seek path which re-pauses siblings after seeking,
|
||||
// render-seek is one-frame-at-a-time with no transport tick between frames,
|
||||
// so the residual unpaused state is harmless — the next call re-activates
|
||||
// idempotently.
|
||||
const activateSiblingTimelines = (masterTimeline: RuntimeTimelineLike) => {
|
||||
const timelines = (window.__timelines ?? {}) as Record<string, RuntimeTimelineLike | undefined>;
|
||||
for (const tl of Object.values(timelines)) {
|
||||
if (!tl || tl === masterTimeline) continue;
|
||||
try {
|
||||
const tlWithPaused = tl as RuntimeTimelineLike & {
|
||||
paused?: (value?: boolean) => unknown;
|
||||
};
|
||||
if (typeof tlWithPaused.paused === "function") {
|
||||
tlWithPaused.paused(false);
|
||||
}
|
||||
tl.play();
|
||||
} catch (err) {
|
||||
swallow("runtime.init.activateNested", err);
|
||||
swallow("runtime.init.activateSiblings", err);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const seekTimelineAndAdapters = (t: number, activateChildren = false) => {
|
||||
const seekTimelineAndAdapters = (t: number, opts?: { activateChildren?: boolean }) => {
|
||||
const tl = state.capturedTimeline;
|
||||
if (tl) {
|
||||
// When rendering frame-by-frame (activateChildren=true), ensure all
|
||||
// nested child timelines are unpaused before seeking the root. GSAP
|
||||
// sibling timelines are unpaused before seeking the root. GSAP
|
||||
// does not propagate totalTime() to children that are internally
|
||||
// paused, which leaves sub-compositions at their initial CSS state
|
||||
// (typically opacity:0). This mirrors the activateSiblingTimelines
|
||||
// call in player.ts renderSeek and is critical for sub-compositions
|
||||
// whose data-start is at or near 0 — they are added to the root
|
||||
// while it is paused and may never receive an explicit play().
|
||||
if (activateChildren) {
|
||||
activateNestedChildTimelines(tl);
|
||||
if (opts?.activateChildren) {
|
||||
activateSiblingTimelines(tl);
|
||||
}
|
||||
try {
|
||||
if (typeof tl.totalTime === "function") {
|
||||
@@ -2033,7 +2032,7 @@ export function initSandboxRuntimeModular(): void {
|
||||
state.currentTime = clock.now();
|
||||
state.isPlaying = false;
|
||||
state.mediaForceSyncNextTick = true;
|
||||
seekTimelineAndAdapters(state.currentTime, true);
|
||||
seekTimelineAndAdapters(state.currentTime, { activateChildren: true });
|
||||
syncMediaForCurrentState();
|
||||
postState(true);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user