Files
JannisandClaude Opus 5.5 6c845615e3 feat: add Blazor WASM shell with BFF proxy, login and PWA scaffold
Web host serves the WebAssembly app without prerendering and proxies /api, /hubs and /ics to the Api via YARP. MudBlazor shell with German UI, dark mode, drawer and bottom navigation, login and forced password change, manifest, icons and service worker.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 18:25:10 +02:00

80 lines
2.7 KiB
Python

"""Erzeugt die PWA-Icons (PNG + SVG) für FamilyPlanner. Aufruf: python make_icons.py <zielordner>"""
import math
import os
import sys
from PIL import Image, ImageDraw
BG = "#1565C0"
FG = "#FFFFFF"
HEART = "#FF7043"
def heart_points(cx, cy, scale):
pts = []
for i in range(120):
t = 2 * math.pi * i / 120
x = 16 * math.sin(t) ** 3
y = 13 * math.cos(t) - 5 * math.cos(2 * t) - 2 * math.cos(3 * t) - math.cos(4 * t)
pts.append((cx + x * scale, cy - y * scale))
return pts
def glyph(s, inset):
"""Koordinaten im 512er-Raster, skaliert auf das Inset (für maskable Icons kleiner)."""
f = (512 - 2 * inset) / 512
def p(x, y):
return (inset + x * f) * s / 512, (inset + y * f) * s / 512
roof = [p(256, 92), p(80, 244), p(432, 244)]
body = [p(132, 222), p(380, 424)]
heart = [p(x, y) for x, y in heart_points(256, 318, 5.2)]
return roof, body, heart, f
def draw_icon(size, maskable):
s = size * 4 # Supersampling
img = Image.new("RGBA", (s, s), (0, 0, 0, 0))
d = ImageDraw.Draw(img)
if maskable:
d.rectangle([0, 0, s, s], fill=BG)
inset = 80
else:
d.rounded_rectangle([0, 0, s - 1, s - 1], radius=int(s * 0.22), fill=BG)
inset = 0
roof, body, heart, f = glyph(s, inset)
d.polygon(roof, fill=FG)
d.rounded_rectangle([body[0], body[1]], radius=int(s * 0.03 * f), fill=FG)
d.polygon(heart, fill=HEART)
return img.resize((size, size), Image.LANCZOS)
def write_svg(path):
heart = " ".join(f"{x:.1f},{y:.1f}" for x, y in heart_points(256, 318, 5.2))
svg = f"""<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" role="img" aria-label="Familienplaner">
<rect width="512" height="512" rx="112" fill="{BG}"/>
<polygon points="256,92 80,244 432,244" fill="{FG}"/>
<rect x="132" y="222" width="248" height="202" rx="15" fill="{FG}"/>
<polygon points="{heart}" fill="{HEART}"/>
</svg>
"""
with open(path, "w", encoding="utf-8", newline="\n") as fh:
fh.write(svg)
def main(target):
os.makedirs(target, exist_ok=True)
for size in (48, 72, 96, 128, 144, 152, 192, 256, 384, 512):
draw_icon(size, False).save(os.path.join(target, f"icon-{size}.png"), optimize=True)
for size in (192, 512):
draw_icon(size, True).save(os.path.join(target, f"icon-maskable-{size}.png"), optimize=True)
draw_icon(32, False).save(os.path.join(target, "favicon-32.png"), optimize=True)
# Apple ignoriert Transparenz -> vollflächig
draw_icon(180, True).save(os.path.join(target, "apple-touch-icon.png"), optimize=True)
write_svg(os.path.join(target, "icon.svg"))
if __name__ == "__main__":
main(sys.argv[1])