"""Rasterize frontend/public/logo.svg into PWA icons.
Outputs (committed):
frontend/public/icons/icon-192.png / icon-512.png — any purpose
frontend/public/icons/icon-192-maskable.png / icon-512-maskable.png
Maskable variants keep the full-bleed background with the logo scaled to the
80% safe zone, so Android's circular adaptive-icon cut never clips the shield.
Usage: python tools/rasterize_icons.py (needs the project .venv's playwright)
"""
from pathlib import Path
from tempfile import TemporaryDirectory
from playwright.sync_api import sync_playwright
PROJECT_ROOT = Path(__file__).resolve().parent.parent
SVG_PATH = PROJECT_ROOT / "frontend" / "public" / "logo.svg"
OUT_DIR = PROJECT_ROOT / "frontend" / "public" / "icons"
SIZES = [(192, False), (512, False), (192, True), (512, True)]
SAFE_SCALE = 0.8
HTML = """<!DOCTYPE html><html><head><meta charset="utf-8"><style>
html, body {{ margin: 0; padding: 0; background: #16161E; }}
.frame {{ width: {size}px; height: {size}px; background: #16161E;
display: flex; align-items: center; justify-content: center;
overflow: hidden; }}
.frame svg {{ width: {inner}px; height: {inner}px; }}
</style></head><body><div class="frame">{svg}</div></body></html>"""
def write_icons() -> None:
svg = SVG_PATH.read_text(encoding="utf-8")
OUT_DIR.mkdir(parents=True, exist_ok=True)
with sync_playwright() as p, TemporaryDirectory() as tmp:
browser = p.chromium.launch(
executable_path="/usr/bin/chromium",
args=["--no-sandbox", "--force-color-profile=srgb"],
)
page = browser.new_page()
for size, maskable in SIZES:
inner = int(size * SAFE_SCALE) if maskable else size
html = HTML.format(size=size, inner=inner, svg=svg)
html_path = Path(tmp) / f"icon-{size}-{int(maskable)}.html"
html_path.write_text(html, encoding="utf-8")
page.set_viewport_size({"width": size, "height": size})
page.goto(f"file://{html_path}")
name = f"icon-{size}{'-maskable' if maskable else ''}.png"
page.screenshot(path=str(OUT_DIR / name), omit_background=False)
print(f"wrote {name}")
browser.close()
if __name__ == "__main__":
write_icons()