Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ describe("verifyStaticFramesSafe catches drift the old fixed-point density would
expect(result.badFrame).toBe(changeAt);
});

it("uses silent verification seeks and restores the playhead to frame zero", async () => {
it("uses event-enabled verification seeks and restores the playhead to frame zero", async () => {
const seekCalls: Array<{ t: number; options?: { suppressEvents?: boolean } }> = [];
const page = {
evaluate: vi.fn(async (fn: (tt: number) => void, t: number) => {
Expand Down Expand Up @@ -103,7 +103,7 @@ describe("verifyStaticFramesSafe catches drift the old fixed-point density would

expect(result.outcome).toBe("verified");
expect(seekCalls.map((call) => Math.round(call.t * fps))).toEqual([0, 3, 0]);
expect(seekCalls.every((call) => call.options?.suppressEvents === true)).toBe(true);
expect(seekCalls.every((call) => call.options?.suppressEvents === false)).toBe(true);
});

it("disarms within a wall-clock budget instead of spending minutes verifying a long static run", async () => {
Expand Down
14 changes: 11 additions & 3 deletions packages/engine/src/services/frameCapture.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2736,6 +2736,13 @@ export async function computeStaticFrameSet(
let hasTimelineCall = false;
function walk(tl: AnyTween, offset: number): void {
if (typeof tl.getChildren !== "function") return;
// A timeline with an onUpdate callback can drive motion without any
// visible tween (e.g. setting x/y from Math.random inside onUpdate).
// Mark its entire span as animated so those frames are never deduped.
if (typeof tl.vars?.onUpdate === "function") {
const total = typeof tl.totalDuration === "function" ? tl.totalDuration() : 0;
if (total > 0) intervals.push({ start: offset, end: offset + total });
}
for (const child of tl.getChildren(false, true, true)) {
const start = offset + (typeof child.startTime === "function" ? child.startTime() : 0);
const single = typeof child.duration === "function" ? child.duration() : 0;
Expand Down Expand Up @@ -3054,8 +3061,9 @@ interface StaticVerificationDependencies {

/**
* Prepare an isolated page for static-frame verification. Verification seeks
* intentionally visit frames out of capture order, so they must never mutate
* the page that will later be captured sequentially.
* intentionally visit frames out of capture order on a SEPARATE page that is
* never used for the real capture — out-of-order event-callback side effects
* are contained to this disposable page and cannot corrupt sequential capture.
*/
export async function createStaticVerificationPage(session: CaptureSession): Promise<Page> {
const page = await session.browser.newPage();
Expand Down Expand Up @@ -3167,7 +3175,7 @@ export async function verifyStaticFramesSafe(
__hf?: { seek?: (t: number, options?: { suppressEvents?: boolean }) => void };
}
).__hf;
if (hf && typeof hf.seek === "function") hf.seek(tt, { suppressEvents: true });
if (hf && typeof hf.seek === "function") hf.seek(tt, { suppressEvents: false });
}, t);
};
const hardCap = Math.max(
Expand Down
Loading