Grotti 3.2.0: Jetzt-Liste, Filament-Pause, Strom-Torte, UI-Polish.
Jetzt/Topbar und Material-Routing, Pause bei Filament-Ende, Druckbericht mit Stromanteilen, Design- und Kamera-Stream-Polish; Bedarf-Alert-Spam und Platte-belegt-Bugs behoben.
This commit is contained in:
1 parent
6e59df22e5
commit
4ddffbbeb1
68 files changed
+4348
-1215
No files matched your search
@@ -0,0 +1,207 @@
|
||||
"""Lucide-Icons als lokales SVG-Sprite bauen.
|
||||
|
||||
python scripts/build_icons.py
|
||||
|
||||
Schreibt web/static/icons.svg. IDs bleiben i-<grotti-name> (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/[email protected]/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 + <svg ...>paths</svg>
|
||||
text = re.sub(r"<!--.*?-->", "", svg, flags=re.DOTALL).strip()
|
||||
match = re.search(r"<svg\b[^>]*>(.*)</svg>\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' <symbol id="i-{grotti}" viewBox="0 0 24 24" fill="none" '
|
||||
f'stroke="currentColor" stroke-width="2" stroke-linecap="round" '
|
||||
f'stroke-linejoin="round">\n {inner}\n </symbol>'
|
||||
)
|
||||
|
||||
|
||||
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' <symbol id="i-{grotti}" viewBox="0 0 24 24" fill="none" '
|
||||
f'stroke="currentColor" stroke-width="2" stroke-linecap="round" '
|
||||
f'stroke-linejoin="round">\n'
|
||||
f' <path d="M18 6 6 18"/><path d="m6 6 12 12"/>\n </symbol>'
|
||||
)
|
||||
print("placeholder", grotti)
|
||||
|
||||
body = "\n".join(symbols)
|
||||
OUT.parent.mkdir(parents=True, exist_ok=True)
|
||||
OUT.write_text(
|
||||
'<?xml version="1.0" encoding="UTF-8"?>\n'
|
||||
f'<!-- Lucide icons, built by scripts/build_icons.py — {len(symbols)} symbols -->\n'
|
||||
'<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">\n'
|
||||
f"{body}\n"
|
||||
"</svg>\n",
|
||||
encoding="utf-8",
|
||||
)
|
||||
print("wrote", OUT, "symbols", len(symbols))
|
||||
if failed:
|
||||
print("warnings:", len(failed))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in new issue
Block a user