"""Headless renderer for visual answers (SVG / HTML / canvas / three.js) with objective checks.
render(kind, source, png_path) opens the page in headless Chrome (the installed browser, via playwright) at 1024x768,
waits for it to settle and returns a report:
errors JavaScript exceptions and console errors (messages)
failed network requests that failed (e.g. a wrong library URL)
blank the screenshot is (almost) a single colour
overflow the page is wider than the viewport (horizontal scroll)
animated two screenshots 0.7 s apart differ (for animation tasks)
coverage share of the viewport that is not background colour
parse_error for SVG: the XML does not parse
The screenshot is saved to png_path. Used by the visual benchmark and by the render-check-repair module.
"""
import io
import os
import re
import tempfile
import xml.dom.minidom
import numpy as np
from PIL import Image
from playwright.sync_api import sync_playwright
W, H = 1024, 768
TMP = os.path.join(tempfile.gettempdir(), "lfm3_render")
os.makedirs(TMP, exist_ok=True)
SVG_PAGE = """
__SVG__
"""
def _stats(png):
a = np.asarray(Image.open(io.BytesIO(png)).convert("RGB")).astype(np.int16)
flat = a.reshape(-1, 3)
vals, counts = np.unique(flat[::7], axis=0, return_counts=True)
bg = vals[counts.argmax()]
diff = np.abs(a - bg).sum(axis=2) > 24
return a, float(diff.mean())
def extract(text, kind):
if text is None:
return None
blocks = re.findall(r"```(\w*)\s*\n(.*?)```", text, re.S)
for lang, body in reversed(blocks):
if kind == "svg" and ("