Skip to content

Commit 26450c1

Browse files
committed
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
1 parent e2f7f6a commit 26450c1

3 files changed

Lines changed: 29 additions & 17 deletions

File tree

packages/core/src/compiler/compositionScoping.test.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -513,6 +513,17 @@ window.__afterTimeline = window.__timelines.scene;
513513
expect(scoped).not.toMatch(/#intro\b/);
514514
});
515515

516+
it('does not rewrite [id="intro"] attribute selectors', () => {
517+
// The function only targets #intro hash selectors, not [id="intro"] attribute selectors
518+
const result = scopeCssToComposition(
519+
'[id="intro"] .title { color: red; }',
520+
"intro",
521+
undefined,
522+
"intro",
523+
);
524+
expect(result).toContain('[id="intro"]');
525+
});
526+
516527
it("wraps scripts with authored root id normalization for #id GSAP selectors", () => {
517528
const { document } = parseHTML(`
518529
<div data-composition-id="intro">

packages/core/src/compiler/inlineSubCompositions.test.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,8 @@ describe("inlineSubCompositions – #ID selector scoping divergence", () => {
147147
* Proper fix (follow-up): make the producer path add data-hf-authored-id
148148
* to the host element when the inner root has an id attribute.
149149
*/
150+
// FIXME(#969): flip these assertions once the producer path adds
151+
// data-hf-authored-id to the host element. See PR #965 "Proper fix (follow-up)".
150152
it("documents the divergence: producer path lacks data-hf-authored-id element", () => {
151153
const document = makeHostDocument("intro");
152154
const host = document.querySelector('[data-composition-src="intro.html"]')!;

packages/core/src/runtime/init.ts

Lines changed: 16 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1724,40 +1724,39 @@ export function initSandboxRuntimeModular(): void {
17241724
}
17251725
};
17261726

1727-
// Unpause all non-root timelines. Per GSAP semantics, paused(false) on a
1728-
// child timeline that hasn't been reached by the parent's playhead is a
1729-
// no-op — the child won't fire onStart/onUpdate until the parent seeks
1730-
// past its insertion point. Per-frame visibility is gated by the engine.
1731-
const activateNestedChildTimelines = (masterTimeline: RuntimeTimelineLike) => {
1727+
// Unpause all non-root timelines registered in window.__timelines (siblings
1728+
// in the registry, not GSAP child tweens). Matches the naming convention in
1729+
// player.ts:32 (forEachSiblingTimeline) and player.ts:89 (activateSiblingTimelines).
1730+
//
1731+
// Unlike the player's seek path which re-pauses siblings after seeking,
1732+
// render-seek is one-frame-at-a-time with no transport tick between frames,
1733+
// so the residual unpaused state is harmless — the next call re-activates
1734+
// idempotently.
1735+
const activateSiblingTimelines = (masterTimeline: RuntimeTimelineLike) => {
17321736
const timelines = (window.__timelines ?? {}) as Record<string, RuntimeTimelineLike | undefined>;
17331737
for (const tl of Object.values(timelines)) {
17341738
if (!tl || tl === masterTimeline) continue;
17351739
try {
1736-
const tlWithPaused = tl as RuntimeTimelineLike & {
1737-
paused?: (value?: boolean) => unknown;
1738-
};
1739-
if (typeof tlWithPaused.paused === "function") {
1740-
tlWithPaused.paused(false);
1741-
}
1740+
tl.play();
17421741
} catch (err) {
1743-
swallow("runtime.init.activateNested", err);
1742+
swallow("runtime.init.activateSiblings", err);
17441743
}
17451744
}
17461745
};
17471746

1748-
const seekTimelineAndAdapters = (t: number, activateChildren = false) => {
1747+
const seekTimelineAndAdapters = (t: number, opts?: { activateChildren?: boolean }) => {
17491748
const tl = state.capturedTimeline;
17501749
if (tl) {
17511750
// When rendering frame-by-frame (activateChildren=true), ensure all
1752-
// nested child timelines are unpaused before seeking the root. GSAP
1751+
// sibling timelines are unpaused before seeking the root. GSAP
17531752
// does not propagate totalTime() to children that are internally
17541753
// paused, which leaves sub-compositions at their initial CSS state
17551754
// (typically opacity:0). This mirrors the activateSiblingTimelines
17561755
// call in player.ts renderSeek and is critical for sub-compositions
17571756
// whose data-start is at or near 0 — they are added to the root
17581757
// while it is paused and may never receive an explicit play().
1759-
if (activateChildren) {
1760-
activateNestedChildTimelines(tl);
1758+
if (opts?.activateChildren) {
1759+
activateSiblingTimelines(tl);
17611760
}
17621761
try {
17631762
if (typeof tl.totalTime === "function") {
@@ -2033,7 +2032,7 @@ export function initSandboxRuntimeModular(): void {
20332032
state.currentTime = clock.now();
20342033
state.isPlaying = false;
20352034
state.mediaForceSyncNextTick = true;
2036-
seekTimelineAndAdapters(state.currentTime, true);
2035+
seekTimelineAndAdapters(state.currentTime, { activateChildren: true });
20372036
syncMediaForCurrentState();
20382037
postState(true);
20392038
};

0 commit comments

Comments
 (0)