Clone-fidelity fix waves 1-3 (+ wave 4 in flight): capture settling, media, iframes

Fixes from the cropin.com / ooni.com audit reviews:
- walker: preserve whitespace-only text in inline elements ("ofthe" bug);
  capture ::placeholder styles for inputs
- css: emit visibility (hidden-at-rest wordmark artifact); exempt list
  markers from inherited elision (lost bullets); ::placeholder rules
- seo: sanitize og:type to Next's enum (render crash -> dev badge leak);
  generated next.config disables devIndicators
- capture/stabilize: promote lazy-loader data attrs before snapshots
  (collapsed sections, viewport-inconsistent captures); settle autoplay
  carousels to home slide before every snapshot; force-reveal hidden
  videos for poster shots + log failures
- generate: ship downloaded video files as local <video src>; keep
  picture>source through IR prune with srcset rewrite (mobile-crop-on-
  desktop heroes); pin Tailwind named screens to computeBands boundaries
- capture/graft: capture cross-origin iframe subtrees (Klaviyo signup
  forms) with element-screenshot fallback; asset download retry +
  visual-assets-missing reporting

Checkpoint commit: wave 4 (reveal settling, 206 range-response fix,
hidden-geometry banding, bare-zero utility gating) is mid-implementation.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Samraaj Bath
2026-07-03 17:45:42 -07:00
co-authored by Claude Fable 5
parent da537e68b8
commit 9aed2540aa
31 changed files with 2579 additions and 121 deletions
+15 -1
View File
@@ -273,6 +273,15 @@ function iconUrl(icon: SeoInventory["icons"][number]): string {
return icon.localPath || icon.href;
}
// Next validates metadata.openGraph.type against this fixed enum and THROWS at
// render on anything else (e.g. Shopify's `product.group`). Unsupported values
// are emitted via metadata.other instead so the tag survives without the crash.
const NEXT_OG_TYPES = new Set([
"website", "article", "book", "profile",
"music.song", "music.album", "music.playlist", "music.radio_station",
"video.movie", "video.episode", "video.tv_show", "video.other",
]);
function metadataObject(report: SeoInventory): Record<string, unknown> {
const metadata: Record<string, unknown> = { title: report.title || "Cloned Page" };
if (report.description) metadata.description = report.description;
@@ -297,9 +306,13 @@ function metadataObject(report: SeoInventory): Record<string, unknown> {
const ogSiteName = firstValue(ogEntries, "og:site_name");
const ogUrl = firstValue(ogEntries, "og:url");
const ogImages = ogEntries.filter((entry) => entry.property?.toLowerCase() === "og:image").map((entry) => entry.content);
const other: Record<string, string> = {};
if (ogTitle) og.title = ogTitle;
if (ogDescription) og.description = ogDescription;
if (ogType) og.type = ogType;
if (ogType) {
if (NEXT_OG_TYPES.has(ogType)) og.type = ogType;
else other["og:type"] = ogType;
}
if (ogSiteName) og.siteName = ogSiteName;
if (ogUrl) og.url = ogUrl;
if (ogImages.length) og.images = ogImages;
@@ -335,6 +348,7 @@ function metadataObject(report: SeoInventory): Record<string, unknown> {
}
if (Object.keys(icons).length) metadata.icons = icons;
if (report.manifest) metadata.manifest = report.manifest.localPath || report.manifest.href;
if (Object.keys(other).length) metadata.other = other;
return metadata;
}