"""Lucide-Icons als lokales SVG-Sprite bauen. python scripts/build_icons.py Schreibt web/static/icons.svg. IDs bleiben i- (bestehende Templates). """ from __future__ import annotations import re import urllib.error import urllib.request from pathlib import Path ROOT = Path(__file__).resolve().parents[1] OUT = ROOT / "web" / "static" / "icons.svg" CDN = "https://unpkg.com/lucide-static@0.468.0/icons/{name}.svg" # grotti-id → lucide dateiname (ohne .svg) ICONS: dict[str, str] = { # bestehend "printer": "printer", "camera": "camera", "file": "file", "spool": "disc-3", "folder": "folder", "folder-open": "folder-open", "list": "list", "activity": "activity", "clock": "clock", "check": "check", "alert": "triangle-alert", "flame": "flame", "pause": "pause", "play": "play", "stop": "square", "layers": "layers", "x": "x", "up": "chevron-up", "down": "chevron-down", "upload": "upload", "refresh": "refresh-cw", "queue": "list-ordered", "send": "send", "book": "book-open", "cube": "box", "bell": "bell", "sweep": "brush", "thermo": "thermometer", "settings": "settings", "bulb": "lightbulb", "search": "search", "filter": "filter", "info": "info", "scan": "scan-line", "trash": "trash-2", "inbox": "inbox", "image": "image", "grid": "layout-grid", "menu": "menu", "panel": "panel-left", "nozzle": "pipette", "bed": "rectangle-horizontal", "chamber": "box", "fan": "fan", "fan-chamber": "air-vent", "fan-aux": "wind", # neu für Polish "plus": "plus", "minus": "minus", "edit": "pencil", "save": "save", "copy": "copy", "external": "external-link", "user": "user", "users": "users", "key": "key", "lock": "lock", "unlock": "lock-open", "wifi": "wifi", "wifi-off": "wifi-off", "power": "power", "zap": "zap", "gauge": "gauge", "chart": "chart-column", "calendar": "calendar", "timer": "timer", "weight": "weight", "package": "package", "tag": "tag", "hash": "hash", "link": "link", "download": "download", "more": "ellipsis", "chevron-right": "chevron-right", "chevron-left": "chevron-left", "home": "house", "wrench": "wrench", "hard-drive": "hard-drive", "server": "server", "message": "message-circle", "mail": "mail", "shield": "shield", "sliders": "sliders-horizontal", "palette": "palette", "moon": "moon", "sun": "sun", "monitor": "monitor", "smartphone": "smartphone", "keyboard": "keyboard", "undo": "undo-2", "redo": "redo-2", "circle-check": "circle-check", "circle-x": "circle-x", "circle-alert": "circle-alert", "loader": "loader-circle", "sparkles": "sparkles", "clipboard": "clipboard-list", "receipt": "receipt", "truck": "truck", "droplet": "droplet", "percent": "percent", "euro": "euro", "arrow-up": "arrow-up", "arrow-down": "arrow-down", "history": "history", "eye": "eye", "eye-off": "eye-off", "star": "star", "heart": "heart", "ban": "ban", "rotate-ccw": "rotate-ccw", "brush": "brush", } def fetch(name: str) -> str: url = CDN.format(name=name) req = urllib.request.Request(url, headers={"User-Agent": "Grotti-icon-build/1"}) with urllib.request.urlopen(req, timeout=30) as resp: return resp.read().decode("utf-8") def to_symbol(grotti: str, svg: str) -> str: # Lucide: optional comment + paths text = re.sub(r"", "", svg, flags=re.DOTALL).strip() match = re.search(r"]*>(.*)\s*$", text, flags=re.DOTALL | re.IGNORECASE) if not match: raise ValueError("kein SVG-Root") inner = match.group(1).strip() # verschachtelte svg-Attribute/Klassen aus Kind-Elementen nicht nötig return ( f' \n {inner}\n ' ) def main() -> None: symbols = [] failed = [] for grotti, lucide in sorted(ICONS.items()): try: raw = fetch(lucide) symbols.append(to_symbol(grotti, raw)) print("ok", grotti, "<-", lucide) except urllib.error.HTTPError as exc: failed.append((grotti, lucide, str(exc))) print("FAIL", grotti, lucide, exc.code) except Exception as exc: failed.append((grotti, lucide, str(exc))) print("FAIL", grotti, lucide, type(exc).__name__) if failed and len(failed) == len(ICONS): raise SystemExit("Keine Icons geladen.") # Fallbacks für fehlgeschlagene: leeres symbol vermeiden — check als Ersatz ok_ids = {s.split('id="i-')[1].split('"')[0] for s in symbols} for grotti, lucide, _ in failed: if grotti in ok_ids: continue # einfaches X als Platzhalter symbols.append( f' \n' f' \n ' ) print("placeholder", grotti) body = "\n".join(symbols) OUT.parent.mkdir(parents=True, exist_ok=True) OUT.write_text( '\n' f'\n' '\n' f"{body}\n" "\n", encoding="utf-8", ) print("wrote", OUT, "symbols", len(symbols)) if failed: print("warnings:", len(failed)) if __name__ == "__main__": main()