"""Favicons from the logo mark (public/logo.svg), rendered with the renderer's headless Chrome. Writes to public/: favicon.ico (16, 32, 48), favicon-32.png, apple-touch-icon.png (180, full bleed: iOS rounds the corners itself), icon-192.png, icon-512.png and icon-maskable-512.png (planet inside Android's 80% safe zone). Run from the repo root: python frontend/scripts/make_favicons.py """ import struct import subprocess import tempfile from pathlib import Path ROOT = Path(__file__).resolve().parents[2] PUBLIC = ROOT / "frontend" / "public" CHROME = next((ROOT / "renderer" / "node_modules" / ".remotion").rglob("chrome-headless-shell.exe")) MARK = (PUBLIC / "logo.svg").read_text(encoding="utf-8") # The planet and sparkle without the rounded tile, to place on a full bleed square. ART = """ """ def full_bleed(scale: float) -> str: """White square, artwork scaled about the centre (iOS and Android apply their own masks).""" off = 32 * (1 - scale) return ( '' '' f'{ART}' ) def render(svg: str, size: int, out: Path) -> bytes: with tempfile.TemporaryDirectory() as tmp: page = Path(tmp) / "icon.html" page.write_text( "" f"
{svg.replace('" "", encoding="utf-8", ) shot = Path(tmp) / "shot.png" subprocess.run( [str(CHROME), "--headless", "--disable-gpu", "--hide-scrollbars", "--force-device-scale-factor=1", "--default-background-color=00000000", f"--window-size={size},{size}", f"--screenshot={shot}", page.as_uri()], check=True, capture_output=True, ) data = shot.read_bytes() out.write_bytes(data) return data def ico(images: dict[int, bytes]) -> bytes: """ICO with PNG payloads (supported by every browser since IE Vista era).""" header = struct.pack("