Canvas raster fallback, CDP rest-state screenshots, lottie overlay containment
- Visible canvases (>=48px) are captured as PNG stills at capture time (toDataURL, element-screenshot fallback for tainted/webgl) and emitted as cid-carrying <img> elements filling the captured box, so canvas-driven bands no longer render blank - Source AND clone full-page screenshots now capture via CDP Page.captureScreenshot with captureBeyondViewport: no scroll-stitching, no scroll events, so scroll-linked animations can't smear mid-scrub states into perceptual evidence (pixel-identical to the old path on static pages; Playwright fallback retained) - DittoLottie's runtime overlay stays position:absolute/inset:0 for its whole life and the host carries data-ditto-lottie with a scoped svg/canvas fit rule, so the runtime animation always fills the pinned per-viewport box instead of escaping to aspect height 259 tests pass (12 new), typecheck clean. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
e33354cfb5
commit
15d1e7a9e3
@@ -2,7 +2,7 @@ import { describe, it } from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
import type { IR, IRNode, IRChild, StyleMap, BBox } from "../src/normalize/ir.js";
|
||||
import type { RawSizing } from "../src/capture/walker.js";
|
||||
import { generateCss, collectNodeRules } from "../src/generate/css.js";
|
||||
import { generateCss, collectNodeRules, RESET_CSS } from "../src/generate/css.js";
|
||||
|
||||
const VPS = [375, 1280];
|
||||
const CANONICAL = 1280;
|
||||
@@ -618,6 +618,32 @@ describe("collectNodeRules lottie mount height pin", () => {
|
||||
});
|
||||
});
|
||||
|
||||
// The reset ships a rule that forces the lottie runtime svg/canvas to fit its overlay box. The
|
||||
// player re-mounts its media into an absolute overlay that fills the host's pinned (per-viewport)
|
||||
// height; without this rule an aspect-mismatched viewBox (a portrait animation in a shorter,
|
||||
// letterboxed source box) inflates past that height. Scoped to the runtime-marked host only, so it
|
||||
// is inert when a page has no lottie.
|
||||
describe("RESET_CSS lottie runtime-fit rule", () => {
|
||||
it("constrains the runtime svg/canvas to the overlay box, scoped to the marked host", () => {
|
||||
assert.match(RESET_CSS, /\[data-ditto-lottie\]\s*>\s*div\s*>\s*svg/);
|
||||
assert.match(RESET_CSS, /\[data-ditto-lottie\]\s*>\s*div\s*>\s*canvas/);
|
||||
// the rule must pin both dimensions so height:100% resolves against the definite overlay
|
||||
const m = RESET_CSS.match(/\[data-ditto-lottie\][^{]*\{([^}]*)\}/);
|
||||
assert.ok(m, "the data-ditto-lottie rule must be present");
|
||||
assert.match(m![1], /width:\s*100%/);
|
||||
assert.match(m![1], /height:\s*100%/);
|
||||
});
|
||||
|
||||
it("does not affect the pre-swap placeholder (a direct-child svg, not nested under a div)", () => {
|
||||
// The selector targets `> div > svg` (the runtime overlay), never a `> svg` placeholder, so the
|
||||
// captured placeholder keeps its own pinned classes and pre/post-swap geometry stays identical.
|
||||
assert.ok(
|
||||
!/\[data-ditto-lottie\]\s*>\s*svg\b/.test(RESET_CSS),
|
||||
"the rule must not target a direct-child placeholder svg",
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
// Defect E — a content-sized flex ROW's items are dropped to `width:auto` only when NO shrink fired
|
||||
// (positive slack ⇒ items at content size). But a WRAPPING text leaf paints at its balanced-wrap
|
||||
// width, which is BELOW its max-content: `width:auto` on the block child fills the line to
|
||||
|
||||
Reference in New Issue
Block a user