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
@@ -391,6 +391,10 @@ export function propsList(node: IRNode, assetMap: Map<string, string>, sourceUrl
|
||||
|
||||
if (isVideo && !videoLocal && !props.some(([k]) => k === "preload")) props.push(["preload", JSON.stringify("none")]);
|
||||
|
||||
// A canvas emitted as its raster still (<img> — see resolveTag) is decorative
|
||||
// painted surface with no captured alt text; emit alt="" so the img is valid.
|
||||
if (node.tag === "canvas" && node.attrs.src && !props.some(([k]) => k === "alt")) props.push(["alt", JSON.stringify("")]);
|
||||
|
||||
if (node.rawHTML && node.tag === "svg") {
|
||||
// Strip the Stage-4 capture-id (`data-cid-cap`) the interaction pass stamps on
|
||||
// elements: it's internal instrumentation, render-inert, and would otherwise
|
||||
@@ -557,6 +561,12 @@ export function resolveTag(node: IRNode, insideInteractive: boolean, insideTable
|
||||
// children inside <iframe> are unrendered fallback content, so emit a <div> container
|
||||
// carrying the iframe's box/styles (CSS is keyed by cid, so the geometry is identical).
|
||||
if (tag === "iframe" && node.children.some((c) => !isTextChild(c))) tag = "div";
|
||||
// A canvas is runtime-drawn surface the clone cannot reproduce; when capture
|
||||
// rasterized it (canvas-still synthetic URL stamped as `src` — see
|
||||
// captureCanvasStillsInPage) emit the still as an <img> filling the canvas's box
|
||||
// (CSS is keyed by cid, so the geometry is identical). A canvas with no still
|
||||
// keeps rendering as an empty <canvas> box, same as before.
|
||||
if (tag === "canvas" && node.attrs.src) tag = "img";
|
||||
if (TABLE_SCOPED.has(tag) && !insideTable) tag = "div"; // orphan table element → neutral box
|
||||
if (violatesContentModel(node, tag)) tag = "div";
|
||||
return tag;
|
||||
|
||||
Reference in New Issue
Block a user