fix: nested GSAP sub-composition lint and render handling (#405)

## Summary

- allow nested sub-composition files to inherit GSAP from their host without tripping `missing_gsap_script`
- keep nested render seeks stable for sub-compositions without regressing producer baselines
- stop producer render-hint detection from treating the compiler's own nested mount retry wrapper as user-authored `requestAnimationFrame()` usage

## Root Cause

- the core linter treated template-based nested compositions like standalone root compositions, so it incorrectly required a local GSAP loader even when the host composition already provided GSAP
- producer `detectRenderModeHints()` runs before CDN scripts are inlined, so nested GSAP exports were never failing because of the GSAP payload itself
- the nested-only false positive came from the compiler-generated mount bootstrap that waits for the inlined sub-composition root with `requestAnimationFrame()` before running the hoisted inline script
- preview and export seek paths also needed to stay split so the nested timeline re-arm behavior that stabilizes scrubbing does not collapse render baselines

## What Changed

- lint: keep the nested GSAP false-positive fix and regression coverage for template sub-compositions
- runtime: keep the render-seek behavior that preserves nested child offsets during export without changing preview scrubbing behavior
- producer: mark compiler-owned mount bootstrap blocks and strip only those blocks before scanning inline scripts for raw `requestAnimationFrame()`
- producer tests now cover both cases: compiler-generated wrappers are ignored, but real user-authored nested `requestAnimationFrame()` still opts into screenshot mode

## Validation

- `bun test packages/core/src/lint/rules/gsap.test.ts`
- `bun test packages/producer/src/services/htmlCompiler.test.ts`
- `bunx oxfmt packages/producer/src/services/htmlCompiler.ts packages/producer/src/services/htmlCompiler.test.ts`
- `bunx oxlint packages/producer/src/services/htmlCompiler.ts packages/producer/src/services/htmlCompiler.test.ts`
- `bun run --filter @hyperframes/producer test --sequential chat style-11-prod`
  - `style-11-prod` passed locally
  - `chat` still shows local-only visual drift on this macOS/ARM workstation, but the render metadata now reports `renderModeHints.recommendScreenshot=false`, which is the concrete acceptance condition for `#402`
- Docker CI-image repro is blocked locally by OrbStack x86/arm64 loader mismatch, so final regression confirmation is deferred to GitHub Actions

Closes #392
Closes #402
This commit is contained in:
Miguel Ángel
2026-04-22 16:10:38 +02:00
committed by GitHub
parent 29b6274ebc
commit d740f5ce42
6 changed files with 188 additions and 10 deletions
+5 -1
View File
@@ -381,7 +381,7 @@ describe("createRuntimePlayer", () => {
expect(deps.onRenderFrameSeek).toHaveBeenCalled();
});
it("renderSeek rearms paused siblings before seeking the master timeline", () => {
it("renderSeek rearms paused siblings and keeps them active for export frames", () => {
const { master, scene1, scene2, scene5 } = createNestedTimelineHarness();
const deps = createMockDeps(master);
const player = createRuntimePlayer({
@@ -390,12 +390,16 @@ describe("createRuntimePlayer", () => {
});
player.pause();
player.renderSeek(5);
expect(master.totalTime).toHaveBeenCalledWith(5, false);
expect(scene1.time()).toBe(1.5);
expect(scene2.time()).toBe(3.5);
expect(scene5.time()).toBe(0);
expect(scene1.play).toHaveBeenCalledTimes(1);
expect(scene2.play).toHaveBeenCalledTimes(1);
expect(scene5.play).toHaveBeenCalledTimes(1);
expect(scene1.pause).toHaveBeenCalledTimes(1);
expect(scene2.pause).toHaveBeenCalledTimes(1);
expect(scene5.pause).toHaveBeenCalledTimes(1);
});
});