devlog: 2 Tage geschrieben (ζ-0 + ζ-1 + Launch-Flash-Fix)

Tag 1: Hybrid SwiftUI + WKWebView, ζ-0 + ζ-1 + Teile ζ-2.
Tag 2: dreilagiger weißer Flash beim App-Start gefixt.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Till JS 2026-05-15 22:22:49 +02:00
parent cbc9af6bb0
commit 37840e6172
6 changed files with 498 additions and 0 deletions

200
devlog/2026-05-14/data.json Normal file
View file

@ -0,0 +1,200 @@
{
"date": "2026-05-14",
"day_number": 1,
"weekday": "Donnerstag",
"commits": 11,
"authors": [
{
"name": "Till",
"count": 11
}
],
"additions": 3130,
"deletions": 226,
"net_lines": 2904,
"files_changed": 36,
"new_files": 0,
"deleted_files": 0,
"session": {
"first_commit_at": "2026-05-14T10:15:22.000Z",
"last_commit_at": "2026-05-14T19:45:25.000Z",
"total_span_minutes": 570,
"active_minutes": 37,
"pauses": [
{
"from": "12:15",
"to": "12:56",
"minutes": 41
},
{
"from": "13:16",
"to": "20:51",
"minutes": 455
},
{
"from": "21:02",
"to": "21:38",
"minutes": 37
}
],
"longest_focus_minutes": 20
},
"top_dirs": [
{
"path": "Sources/Features/WebShell",
"pct": 15
},
{
"path": "Sources/Resources/Assets.xcassets",
"pct": 9
},
{
"path": "project.yml",
"pct": 9
},
{
"path": "PLAN.md",
"pct": 8
},
{
"path": "Sources/App/RootView.swift",
"pct": 6
}
],
"top_extensions": [
{
"ext": ".swift",
"count": 40
},
{
"ext": ".md",
"count": 8
},
{
"ext": ".yml",
"count": 7
},
{
"ext": ".json",
"count": 4
},
{
"ext": ".xcstrings",
"count": 2
},
{
"ext": ".png",
"count": 2
}
],
"tags": [
"icon",
"dark-mode"
],
"commits_list": [
{
"hash": "0bd59ed",
"short": "ζ-0 Setup: Repo-Skelett, iOS-Build grün, Healthz live",
"type": null,
"scope": null,
"additions": 1468,
"deletions": 0,
"timestamp": "2026-05-14T12:15:22+02:00"
},
{
"hash": "75b5e71",
"short": "ζ-1: WebShellView + Universal-Link-Routing",
"type": null,
"scope": null,
"additions": 466,
"deletions": 77,
"timestamp": "2026-05-14T12:56:05+02:00"
},
{
"hash": "dd10f85",
"short": "ζ-1 abgeschlossen: DeepLinkRouter + Web-Header-Hide",
"type": null,
"scope": null,
"additions": 166,
"deletions": 36,
"timestamp": "2026-05-14T13:06:37+02:00"
},
{
"hash": "c89d48c",
"short": "ζ-2 native: SwiftData-Snapshot-Cache + DailyQuoteWidget",
"type": null,
"scope": null,
"additions": 597,
"deletions": 45,
"timestamp": "2026-05-14T13:16:05+02:00"
},
{
"hash": "03e2f04",
"short": "ζ-1 Polish: robuster Header-Selektor + zitare.mana.how als 2. UL-Domain",
"type": null,
"scope": null,
"additions": 35,
"deletions": 5,
"timestamp": "2026-05-14T20:51:03+02:00"
},
{
"hash": "c0260fa",
"short": "docs: Cloudflare-TODO (DNS-CNAME + CNAME-Cleanup) + PLAN.md externe Blocker",
"type": null,
"scope": null,
"additions": 119,
"deletions": 0,
"timestamp": "2026-05-14T20:52:13+02:00"
},
{
"hash": "7b4cb13",
"short": "fix(icon): TestFlight-Validation grün — Icon + CFBundleIconName",
"type": "fix",
"scope": "icon",
"additions": 188,
"deletions": 18,
"timestamp": "2026-05-14T21:00:24+02:00"
},
{
"hash": "10644ff",
"short": "icon: vereinfachte Anführungszeichen-Glyphe als Platzhalter",
"type": null,
"scope": null,
"additions": 27,
"deletions": 40,
"timestamp": "2026-05-14T21:02:17+02:00"
},
{
"hash": "bc01c0f",
"short": "ui: Mac-Window + Konto-Header nach erstem TestFlight-Build",
"type": null,
"scope": null,
"additions": 20,
"deletions": 3,
"timestamp": "2026-05-14T21:38:48+02:00"
},
{
"hash": "f65b949",
"short": "build: CFBundleVersion 2 für nächsten TestFlight-Upload",
"type": null,
"scope": null,
"additions": 1,
"deletions": 1,
"timestamp": "2026-05-14T21:39:18+02:00"
},
{
"hash": "2616c4f",
"short": "fix(dark-mode): WebView folgt System statt eigener localStorage-Toggle",
"type": "fix",
"scope": "dark-mode",
"additions": 43,
"deletions": 1,
"timestamp": "2026-05-14T21:45:25+02:00"
}
],
"review_state": "auto",
"llm": {
"model": null,
"generated_at": null
}
}

101
devlog/2026-05-14/macher.md Normal file
View file

@ -0,0 +1,101 @@
---
date: 2026-05-14
day: 1
view: macher
weekday: Donnerstag
commits: 11
review: written
---
# Donnerstag, 2026-05-14 — Tag 1 (Macher-Sicht)
Sonderfall im Native-Stack: Hybrid SwiftUI + WKWebView. Zitare hat
keinen Hardware-USP gegenüber dem Browser — Native bringt nur
Widget, ShareExtension, Spotlight, Submit. Lesen bleibt im Web.
Phasen ζ-0 + ζ-1 + Teile von ζ-2 in einer Schicht.
## Stats
11 Commits, +3 130 / 226 LoC, 36 Files. 40× `.swift`, 7× `.yml`,
8× `.md`. Top-Dirs `Sources/Features/WebShell` (15 %),
Assets (9 %). Tags: `icon`, `dark-mode`. Session 10:15 → 21:45,
37 aktive Minuten in 3 Blöcken (Vormittag + Abend).
## Phasen
- **ζ-0 — Setup.** Repo-Skelett, `project.yml` (XcodeGen), ManaCore
+ ManaTokens als Package-Dependency, leerer Build im Simulator
grün, `/healthz`-Probe gegen `zitare-api.mana.how`.
- **ζ-1 — WebShellView.** `WKWebView` als `UIViewRepresentable`/
`NSViewRepresentable`-Bridge, Universal-Link-Routing, Cookie-SSO-
Bridge (`mana.access` auf `.mana.how`).
- **ζ-1 abgeschlossen**`DeepLinkRouter` + Web-Header-Hide via
injizierter CSS. Header + Footer der Website werden in der App
ausgeblendet, weil App-Chrome reicht.
- **ζ-2 native**`SwiftData`-Snapshot-Cache + `DailyQuoteWidget`
vorbereitet. Snapshot kommt von `https://zitare.com/index-min.json`,
App-Group `group.ev.mana.zitare` reicht es an Widget durch.
- **ζ-1 Polish** — robuster Header-Selektor (DOM-Drift-Tolerant),
`zitare.mana.how` als zweite UL-Domain.
- **Cloudflare-TODO + PLAN.md externe Blocker.**
`zitare.mana.how`-DNS-Record + CNAME-Cleanup nötig, sonst
AASA-Validation flapsig.
- **TestFlight-Validation grün** — Icon-Asset + `CFBundleIconName`
korrekt gesetzt. Erste Upload-Versuche scheiterten an iconAsset.
- **Icon-Platzhalter** — vereinfachte Anführungszeichen-Glyphe,
Pixel-Render. Echtes Icon kommt vor Submit.
- **Mac-Window + Konto-Header** nach erstem TestFlight-Build.
- **CFBundleVersion 2** für nächsten Upload-Roundtrip.
- **Dark-Mode-Fix** — WebView folgt System-`prefers-color-scheme`
statt zitare.com-localStorage-Toggle. Vorher hat man im App-Modus
unbeabsichtigt den Web-Schalter umgestellt.
## Architektur-Entscheidungen
- **Hybrid ausnahmsweise.** Lese-Surfaces via `WKWebView`, Native-
Surfaces (Widget, ShareExt, Submit, Spotlight) pure SwiftUI.
Trennung ist fest — keine schleichende Native-Re-Implementation
von Read-Routes (CLAUDE.md Invariante 1, „Don't do").
- **AASA-Domain `zitare.com`**, nicht `zitare.mana.how`. Public-URL
trägt die App, mana.how ist API/Backstage.
- **Cookie-SSO-Bridge** statt JWT-Header in WebView. Domain
`.mana.how` setzt Cookie, WebView lädt `zitare.mana.how` mit
Cookie automatisch.
- **Snapshot lokal für Widget + Spotlight**, nicht für WebView.
WebView lädt live, Widget/Spotlight können nicht live laden
(Background-Refresh-Budget).
- **Header-Hide via injizierter CSS**, statt eigene App-Routes.
Selektor robust gegen DOM-Drift; bricht im worst case lautlos,
zeigt aber dann Header — kein Crash.
- **Theme: `paper`-Variant** aus `@mana/themes/paper`, lokal in
`ZitareTheme.swift` nachgebaut. Match zur Website.
## Trade-offs
- **WebView-Hybrid** spart Native-Read-Surface-Aufwand komplett,
aber: WebView-DOM ist nicht App-DOM. Selektor-Drift, Cookie-
Sync-Edge-Cases, Performance-Regressions im JS sind Web-Sorgen,
nicht App-Sorgen — und wir merken sie hier später.
- **`zitare.com` als AASA-Domain + `zitare.mana.how` als zweite**:
doppelte Universal-Link-Logik. Wenn Web-Routing sich ändert,
muss App + AASA + Web nachziehen.
- **Snapshot-Pull bei Launch** macht App schneller-startend gegen
Widget-Daten, kostet aber bei jedem Cold-Start 11+ Quotes
Bandbreite. Bei 50 k Quotes-Korpus später Schmerz; heute
irrelevant.
- **TestFlight-Roundtrip** kostet Iterationen — `CFBundleIconName`
fehlte, `Icon`-Asset hatte falsche Größe, dadurch zwei Failed-
Validations. Standard für native App-Erstbuilds.
## Offene Punkte
- **DNS-CNAME für `zitare.mana.how`** in Cloudflare setzen, alter
CNAME entfernen.
- **Echtes AppIcon** statt vereinfachter Anführungszeichen-Glyphe.
- **ζ-3 Submit-View** in SwiftUI mit ManaAuthGate (Action-Level-
Login-Eskalation). Pending.
- **ζ-4 Spotlight + ShareExtension + App Intents.** Pending.
- **ζ-5 Polish** — iPad-Split-Layout, Accessibility.
- **ζ-6 App-Store-Submission** — Bundle `ev.mana.zitare`, Team
registriert.
- **DailyQuoteWidget auf echtem Gerät** testen — Simulator zeigt's,
echtes iPhone-Widget-Behavior ist gelegentlich anders.

View file

@ -0,0 +1,43 @@
---
date: 2026-05-14
day: 1
view: spieler
weekday: Donnerstag
commits: 11
review: written
---
# Donnerstag, 2026-05-14 — Tag 1
Zitare bekommt eine eigene App. Anders als bei Memoro, Cards oder
Manaspur **bringt sie nicht alles neu**: Lesen, Suchen und Stöbern
machst du weiterhin auf der Website (zitare.com), die App schält sich
nur drumherum — und legt obendrauf Sachen, die nur am iPhone oder
Mac Sinn ergeben.
## Was die App heute schon kann
- **zitare.com lädt als App auf, ohne Browser-Drumherum.** Header und
Footer werden ausgeblendet, weil dein Telefon sie nicht braucht.
- **Universal-Links** funktionieren: Wer einen Zitat-Link in
Messenger/Mail tippt, landet direkt in der App, nicht im Browser.
- **Zitat-des-Tages-Widget** auf dem Home-Screen oder Sperrbildschirm
(in Vorbereitung — Datenbasis lokal gespiegelt, Widget-Surface
steht).
- **System-Dunkelmodus** — die App folgt deinem iPhone, kein eigener
Schalter.
## Was offen ist
- **Eigene Zitate einsenden** — die Submit-Maske kommt als nächstes,
inkl. Anmeldung über deinen Verein-Account.
- **Aus anderer App teilen → Zitare** (Share-Extension) — vorbereitet,
nicht aktiv.
- **System-weite Suche (Spotlight)** — vorbereitet, nicht aktiv.
- **TestFlight** läuft, aber wartet noch auf den endgültigen Domain-
Switch (`zitare.mana.how` wird ergänzt).
## Hintergrund
Zitare ist Lese-App, keine Tipp-App. Die Website kann das gut, die
App soll sich nicht in den Weg stellen. Deshalb: dünne Hülle um das
Web plus Widget, Suche und „in Zitare schicken" — sonst nichts.

View file

@ -0,0 +1,73 @@
{
"date": "2026-05-15",
"day_number": 2,
"weekday": "Freitag",
"commits": 1,
"authors": [
{
"name": "Till JS",
"count": 1
}
],
"additions": 54,
"deletions": 1,
"net_lines": 53,
"files_changed": 3,
"new_files": 0,
"deleted_files": 0,
"session": {
"first_commit_at": "2026-05-15T11:59:49.000Z",
"last_commit_at": "2026-05-15T11:59:49.000Z",
"total_span_minutes": 0,
"active_minutes": 0,
"pauses": [],
"longest_focus_minutes": 0
},
"top_dirs": [
{
"path": "Sources/Features/WebShell",
"pct": 33
},
{
"path": "Sources/Resources/Assets.xcassets",
"pct": 33
},
{
"path": "project.yml",
"pct": 33
}
],
"top_extensions": [
{
"ext": ".swift",
"count": 1
},
{
"ext": ".json",
"count": 1
},
{
"ext": ".yml",
"count": 1
}
],
"tags": [
"launch"
],
"commits_list": [
{
"hash": "cbc9af6",
"short": "fix(launch): drei Layer weißen Flash beim Start gefixt",
"type": "fix",
"scope": "launch",
"additions": 54,
"deletions": 1,
"timestamp": "2026-05-15T13:59:49+02:00"
}
],
"review_state": "auto",
"llm": {
"model": null,
"generated_at": null
}
}

View file

@ -0,0 +1,62 @@
---
date: 2026-05-15
day: 2
view: macher
weekday: Freitag
commits: 1
review: written
---
# Freitag, 2026-05-15 — Tag 2 (Macher-Sicht)
Ein-Commit-Tag: drei aufeinanderfolgende weiße Flashes beim App-
Start gefixt. Klassisch unauffällig, aber spürbar polish.
## Stats
1 Commit, +54 / 1 LoC, 3 Files (Launch-Screen, Asset-Catalog,
`project.yml`). Tag `launch`. Active-Time 0 Min (Single-Commit).
## Was passierte
Drei Layer waren weiß, bis der WebView-Content da war:
1. **Launch-Screen** war Default (System-Weiß).
2. **App-Window-Background** war `.systemBackground` ohne explizite
Override → folgte zwar Dark-Mode, aber zeigte Weiß im Light-
Mode.
3. **WebView-Background** war default-weiß, bis die geladene
Seite ihre Background-Farbe ausspielen konnte (typisch 200400 ms
auf 4G).
Fix:
- **Launch-Screen** mit dunklem Paper-Theme-Background.
- **App-Window-Background** auf `ZitareTheme.background` festgesetzt,
Hintergrund matched System-Theme.
- **WebView.isOpaque = false** + `backgroundColor` = Theme-
Background. Browser-Engine rendert auf transparenten Hintergrund,
sodass App-Background durchscheint bis der Web-Background da ist.
## Architektur-Bemerkung
WebShell hat damit erstmals einen sauberen „App-Frame", der nicht
vom Web-Content abhängt. Künftige Features (Pull-to-Refresh-Indicator,
Bottom-Toolbar) können sich auf den Frame verlassen, ohne sich am
Web-Rendering-Zeitpunkt zu orientieren.
## Trade-offs
- **WebView-Transparenz** ist in seltenen WebKit-Versionen eine
Render-Quelle für Tearing. Acceptable; falls Reports kommen,
zurück auf opaque mit synchron gesetztem Background.
- **Hardcoded Background-Farbe** in 3 Stellen (LaunchScreen, App-
Window, WebView). Wenn Theme-Switch kommt, müssen alle 3
nachziehen. ZitareTheme-Bridge sollte das später kapseln.
## Offene Punkte
- **Hardcoded-Theme-Background-Trio** in eine Bridge ziehen vor
Theme-Variants-Release.
- **ζ-3 Submit-View** bleibt offen (Tag 1 nicht angefangen).
- **TestFlight-Build** mit dem Fix raus — externe Validation steht
noch aus.

View file

@ -0,0 +1,19 @@
---
date: 2026-05-15
day: 2
view: spieler
weekday: Freitag
commits: 1
review: written
---
# Freitag, 2026-05-15 — Tag 2
Kleine, aber spürbare Änderung: **kein weißes Aufblitzen mehr beim
App-Start**. Wenn du Zitare öffnest, geht der Bildschirm sauber von
dunkel in den Inhalt, ohne kurz weiß zu sein.
## Hintergrund
Ein App-Start war eine Kette aus drei kurzen weißen Bildschirmen
(Launch-Screen, App-Background, WebView, bevor die Seite gerendert
ist). Das ist heute behoben. Beim nächsten Update merkst du es.