Bühne: Live-Vorschau mit echter Bäckerei-Website, Chat als Live-Demo mit Zoom, weiches Zuklappen

- Live-Vorschau: Im MacBook steht die Beispiel-Website der Bäckerei Sonnenschein selbst
  (Startbereich, Sortiment, Backstube, Torten, Öffnungszeiten, Fuß; Instrument Serif,
  CC0-Fotos von Wikimedia Commons, nur Backwaren). Beim Scrollen durch die Station läuft
  sie einmal durch (--kp-v, nur transform). Portal-Aufnahmen (Projekte, Als App) neu mit
  derselben Website im Vorschaukasten.
- Chat: Portal-Chat als Live-Demo. Die Kamera zoomt aufs Gespräch, Foto anhängen, tippen,
  Sprachnachricht, Antworten von Kenso ploppen auf (spielerische Texte, Sie-Form). Das iPhone
  gleitet in dieser Station weg. Hinweis auf die E-Mail bei Team-Antworten (K17).
- „Funktionen“: Unterpunkte falten sich beim Verlassen ein, danach gleitet „Als App“ per FLIP
  nach oben (auch beim Aufklappen nach unten), Pfeil zeigt den Zustand.
- Schrittlängen je Station (normal, Demo, Vorschau, Chat), bei reduzierter Bewegung kurz.
- Tests: Bühne angepasst, neue Tests für Chat-Ablauf und Bäckerei-Website (287 grün).

Spezifikation: docs/superpowers/specs/2026-10-04-vorschau-chat.md

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
rootandClaude Opus 5.5 committed 2026-10-04 18:16:15 +00:00
1 parent 58ac7745e6
commit c1d775e090
47 files changed
+2537 -87

No files matched your search

+94
View File
@@ -0,0 +1,94 @@
#!/usr/bin/env python3
"""
Relaunch: Fotos der erfundenen Beispiel-Website „Bäckerei Sonnenschein“ (Station Live-Vorschau und Chat-Demo).
Alle Fotos stammen von Wikimedia Commons und stehen unter CC0 (gemeinfrei, keine Namensnennung nötig).
Es sind nur Backwaren zu sehen, keine Menschen. Quellen: docs/relaunch/assets/baeckerei/QUELLEN.md.
Wikimedia Commons (einmalig, Zwischenspeicher docs/relaunch/assets/baeckerei/roh/, nicht im Repository)
-> public/relaunch/demos/baeckerei/<name>-<breite>.webp
Aufruf (aus dem Projektverzeichnis): python3 scripts/relaunch-baeckerei-fotos.py
Braucht Python 3 und Pillow mit WebP-Unterstützung.
"""
from pathlib import Path
import sys
import urllib.parse
import urllib.request
from PIL import Image
WURZEL = Path(__file__).resolve().parent.parent
ROH = WURZEL / "docs" / "relaunch" / "assets" / "baeckerei" / "roh"
ZIEL = WURZEL / "public" / "relaunch" / "demos" / "baeckerei"
KENNUNG = {"User-Agent": "WEBklar-relaunch/1.0 (support@webklar.com)"}
KB = 1024
QUALITAET = 78
MIN_QUALITAET = 50
# Name -> (Datei auf Commons, Ausschnitt als Anteile (links, oben, rechts, unten), Zielbreiten, Seitenverhältnis B/H, Budget je Datei)
FOTOS = {
# Startseite und Chat-Foto: dasselbe Roggenbrot (Marlene schickt es im Chat, danach steht es auf der Website)
"roggenbrot": ("Rye bread Roggenbengel 2025-02-14 13.jpg", (0.0, 0.0, 1.0, 1.0), (600,), 1.08, 58 * KB),
"roggenbrot-anschnitt": ("Cut rye bread Kartoffelkruste 2025-02-20 02.jpg", (0.12, 0.12, 0.92, 0.95), (400,), 0.8, 30 * KB),
"broetchen": ("Bread-rolls.jpg", (0.0, 0.0, 1.0, 1.0), (400,), 0.8, 30 * KB),
"croissants": ("Freshippo croissant bread.jpg", (0.05, 0.0, 0.95, 1.0), (400,), 0.8, 34 * KB),
"zimtschnecken": ("IKEA cinnamon bun.jpg", (0.05, 0.0, 0.95, 1.0), (400,), 0.8, 30 * KB),
"baguettes": ("Baguette-246424.jpg", (0.0, 0.0, 1.0, 1.0), (700,), 1.2, 40 * KB),
"zwetschgenkuchen": ("Plum cake, Ehrenbach.jpg", (0.18, 0.08, 1.0, 1.0), (520,), 1.0, 50 * KB),
}
def lade(datei: str) -> Path:
"""Lädt das Foto einmal in 2000 px Breite von Commons (Special:FilePath) und hält es im Zwischenspeicher."""
ROH.mkdir(parents=True, exist_ok=True)
ziel = ROH / datei.replace(" ", "_")
if not ziel.exists():
url = "https://commons.wikimedia.org/w/index.php?title=Special:FilePath/" + urllib.parse.quote(datei) + "&width=2000"
with urllib.request.urlopen(urllib.request.Request(url, headers=KENNUNG), timeout=60) as antwort:
ziel.write_bytes(antwort.read())
return ziel
def zuschneiden(bild: Image.Image, anteile: tuple, verhaeltnis: float) -> Image.Image:
"""Schneidet erst den Ausschnitt aus, dann mittig auf das Seitenverhältnis."""
b, h = bild.size
l, o, r, u = anteile
bild = bild.crop((round(l * b), round(o * h), round(r * b), round(u * h)))
b, h = bild.size
if b / h > verhaeltnis:
neu = round(h * verhaeltnis)
return bild.crop(((b - neu) // 2, 0, (b - neu) // 2 + neu, h))
neu = round(b / verhaeltnis)
return bild.crop((0, (h - neu) // 2, b, (h - neu) // 2 + neu))
def speichern(bild: Image.Image, ziel: Path, budget: int) -> tuple[int, int]:
qualitaet = QUALITAET
while True:
bild.save(ziel, "WEBP", quality=qualitaet, method=6)
groesse = ziel.stat().st_size
if groesse <= budget or qualitaet - 4 < MIN_QUALITAET:
return groesse, qualitaet
qualitaet -= 4
def main() -> int:
ZIEL.mkdir(parents=True, exist_ok=True)
for alt in ZIEL.glob("*.webp"):
alt.unlink()
summe = 0
for name, (datei, anteile, breiten, verhaeltnis, budget) in FOTOS.items():
roh = zuschneiden(Image.open(lade(datei)).convert("RGB"), anteile, verhaeltnis)
for breite in breiten:
klein = roh.resize((breite, round(breite / verhaeltnis)), Image.LANCZOS)
groesse, q = speichern(klein, ZIEL / f"{name}-{breite}.webp", budget)
summe += groesse
print(f"{name}-{breite}.webp {klein.size[0]}x{klein.size[1]} {groesse / KB:.1f} KB q{q}")
print(f"Summe: {summe / KB:.0f} KB")
return 0
if __name__ == "__main__":
sys.exit(main())
+5 -4
View File
@@ -43,15 +43,16 @@ LANG_HOEHE = 5131
# Dateiname (ohne .webp) -> (Art, Zielbreiten)
BILDER = {
# Desktop-Fenster (Roh 3024 x 1786 = MacBook-Safari-Fläche 1512 x 893 @2x)
"desktop-1-projekte": ("desktop", (800, 1200, 1600)),
# Desktop-Fenster (Roh 3024 x 1786 = MacBook-Safari-Fläche 1512 x 893 @2x); die Live-Vorschau zeigt im MacBook die
# Beispiel-Website selbst (BaeckereiSeite), kein Bild
"desktop-2-fortschritt": ("desktop", (800, 1200, 1600)),
"desktop-3b-chat-foto": ("desktop", (800, 1200, 1600)),
# Chat-Demo (Station Chat): leerer Chat als Kulisse, die Nachrichten sind live; 2400 px, weil die Kamera hineinzoomt
"desktop-3c-chat-leer": ("desktop", (1600, 2400)),
"desktop-4-rechnungen": ("desktop", (800, 1200, 1600)),
# Handy (Roh 804 x 1748 = iPhone 16 Pro @2x, Safe Area 59/34; das lange Bild 804 x 5340, beschnitten auf 5131)
"phone-1-projekte": ("handy", (390, 780)),
"phone-3-projektdetails": ("handy", (390, 780)),
"phone-4b-chat-foto": ("handy", (390, 780)),
"phone-4c-chat-leer": ("handy", (390, 780)),
"phone-5-rechnungen": ("handy", (390, 780)),
# Funktions-Stationen (Spezifikation 2026-10-04-funktionen-demos.md): Handy-Ansicht der Funktion im Kundenportal
"phone-9-termine": ("handy", (390, 780)),