Native Submit-Flow gegen zitare-api. SwiftUI-Form mit:
- TextEditor mit 10-1000-Zeichen-Validation + Counter
- Sprache (de/en/fr/es/it Picker)
- Author-Name (mandatory)
- Optional Source-Section (Toggle): Titel + Art (book/article/talk/film/other) + Jahr
- CC-BY-SA-4.0-Zustimmung als Pflicht-Toggle
- Submit-Button erst aktiv wenn alle 3 Bedingungen erfüllt
- authGate.require(reason: "submit") öffnet Login-Sheet wenn nötig;
Submit feuert auto nach signedIn
- Error-Banner mit lokalisiertem API-Code (api.error.<code> wird
in xcstrings nachgeschlagen)
- Success-Banner mit Slug + "wartet auf Moderation"-Hinweis
Neu in Submit-Tab als 4. Tab (Lesen / Erkunden / Einreichen / Konto).
- ZitareAPI: submitQuote(_:), QuoteDraft, SubmittedQuote, ZitareAPIError
- SubmitQuoteView ersetzt Placeholder-Stub
- RootView: AppTab.submit ergänzt
Offen: Offline-Queue (PendingSubmission via SwiftData) — bei Network-
Failure bleibt der Draft im Form-State und User retried manuell.
Nicht in ζ-3 abgeschlossen, gehört in ζ-3.5.
Offen: api.error.*-Keys in zitare-native Localizable.xcstrings —
aktuell nur DE-Source. EN/FR/ES/IT folgen separat.
iOS + macOS BUILD SUCCEEDED.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
AccountView hatte zwar einen Status-Text aber keinen Login-Button,
und der ManaAuthGate war überhaupt nicht im App-Tree eingebaut —
Guest-Mode-User konnten sich nirgends anmelden.
- ZitareNativeApp: ManaAuthGate(auth:) instantiiert + via environment
durchgereicht
- RootView: .manaBrand(ZitareBrand.manaBrand) +
.manaAuthGate(authGate) { ManaLoginView(…) } für globales
Sign-In-Sheet
- AccountView: authActionCard mit "Mit mana-Konto anmelden" /
"Abmelden" (keepGuestMode: true)
- ZitareBrand neu (paper-Theme-Brücke zu ManaBrandConfig)
- project.yml: platformFilter: iOS für Widget+Share-Extensions
(macOS-Build war pre-existing kaputt mit "embedded iOS content")
iOS + macOS BUILD SUCCEEDED.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Migriert die App auf die kanonische shared Keychain-Group
`ManaSharedKeychainGroup` aus mana-swift-core. Alle nativen
mana-e.V.-Apps (memoro, wordeck, nutriphi, herbatrium, zitare,
seepuls, viadocu, manameme, werdrobe, pageta, comicello, moodlit)
teilen damit ihren Auth-Token auf demselben Device — ein Login in
einer App, alle anderen starten direkt im .signedIn-Status.
Wichtig: für echtes Cross-App-Sharing müssen sowohl `keychainService`
als auch `keychainAccessGroup` identisch sein (Keychain-Lookup-Tupel
`(service, account, accessGroup)`) — beide jetzt auf
`ManaSharedKeychainGroup`. Bestehender App-eigener Bucket
(`ev.mana.<app>`) wird beim ersten Login mit dem neuen Token
überschrieben; User in TestFlight-Apps brauchen einen Re-Login.
Voraussetzung Apple-Dev-Portal (Tills manueller Schritt):
- Capability "Keychain Sharing" für die App ID aktivieren
- Group `ev.mana.session` hinzufügen
- Provisioning-Profile neu downloaden (Xcode auto)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Symptom: User wurden nach App-Update / längerer App-Pause aus-
geloggt, obwohl Refresh-Token theoretisch noch gültig war. Ursache:
mit `keychainAccessGroup: nil` landet das Token im impliziten
default-bucket; bei TestFlight-Cert-Drift oder Provisioning-
Profile-Wechsel wurde es nach Update für die neue App-Instanz
unzugänglich.
Bestehende Tokens werden via ManaCore v1.5.1 KeychainStore-
Migration-Fallback automatisch in den expliziten Bucket gespiegelt
— kein erzwungener Logout.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
System-Launch-Screen war default-weiß (UILaunchScreen leer), WKWebView
malt vor first paint opaque weiß, und das HTML selbst hat keinen
Background bevor das CSS-Bundle lädt. Alle drei Layer jetzt auf den
Paper-Theme-Background gesetzt — sRGB-konvertiert aus `--color-background`
in mana/packages/themes/paper.css.
- LaunchBackground.colorset mit Light + Dark Variante
- project.yml: UILaunchScreen.UIColorName: LaunchBackground
- WKWebView: isOpaque=false + clear backgrounds (iOS), drawsBackground=false (macOS)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Symptom: in TestFlight-Build wirkte Account-Tab dunkel, aber
Lesen + Erkunden hell (oder umgekehrt, je nachdem was im Web-
localStorage stand). Inkonsistent, weil:
- AccountView (SwiftUI) nutzt ZitareTheme.dynamic() — folgt System
- WebView las localStorage['zitare-mode'], das nur über den
Theme-Toggle-Button im Web-Header gesetzt wurde — den wir aber
nativ ausgeblendet haben → kein User-Steuerpfad
Fix: neuer User-Script `syncDarkMode` injiziert at document.start:
- liest prefers-color-scheme via matchMedia
- schreibt localStorage['zitare-mode'] = 'dark' / removes
- togglet die `.dark`-Class auf <html>
- bleibt aktiv via matchMedia-change-Listener für Live-Switches
Reihenfolge in WebView-Config: syncDarkMode VOR hideWebHeader,
damit das Theme richtig ist bevor Header-CSS rendert.
Build 3 für nächsten TestFlight-Upload.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- WebShellView füllt Window-Höhe explizit
(.frame(maxWidth: .infinity, maxHeight: .infinity)) — sonst hatte
WKWebView auf Mac einen Rest-Spalt, der den Web-Footer abschnitt.
- RootView TabBar-Hintergrund: Paper-Background auf Window-Toolbar
(macOS-only via #if os(macOS), windowToolbar-Placement existiert
unter iOS nicht). Title-Bar passt jetzt zum Content statt System-Grau.
- AccountView Header: SF Symbol "quote.opening" durch eine einzelne,
zentrierte Öffnungs-Anführung in Georgia-Bold ersetzt. Die SF-
Doppelglyphe war asymmetrisch positioniert, sieht jetzt sauber aus.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Z + zwei Akzent-Quotes lieferten ein unsauber gerendertes PNG
(Z fehlte aufgrund Font-Bounds-Mathematik bei großem Pointsize).
Reduziert auf eine zentrierte Öffnungs-Anführung („) — ikonisch
auch bei 40×40, klar erkennbar als Quotes-App.
Platzhalter — vor Launch durch Designer-Icon ersetzen.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Apple Validator hatte drei Fehler geworfen:
- Missing 120x120 (iPhone) und 152x152 (iPad)
- Missing Info.plist key CFBundleIconName
Root-Cause: AppIcon.appiconset hatte keinen filename gesetzt → keine
PNG-Variants im Bundle. Plus: bei GENERATE_INFOPLIST_FILE=NO injiziert
Xcode CFBundleIconName nicht automatisch, das muss explizit in die
plist.
Fixes:
- scripts/make-appicon.swift erzeugt 1024×1024-PNG-Platzhalter in
paper-Theme-Farben (Sienna-Background, dunkles Z, zwei
Anführungszeichen-Akzente) analog cards-native
- Sources/Resources/Assets.xcassets/AppIcon.appiconset/Contents.json
verlinkt AppIcon-1024.png für light / dark / tinted (3 Appearances)
- project.yml setzt CFBundleIconName: AppIcon im Info.plist-Root
Archive-Verifikation:
$ /usr/libexec/PlistBuddy -c "Print :CFBundleIconName" Info.plist
→ AppIcon
$ ls ZitareNative.app | grep AppIcon
→ AppIcon, AppIcon60x60@2x.png (=120×120), AppIcon76x76@2x~ipad.png
(=152×152)
Platzhalter — vor produktivem App-Store-Launch durch designtes Icon
ersetzen.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- WebShellScripts.hideWebHeader: drei Selektor-Strategien gestapelt
(data-app-nav-Marker / strukturell via :has(a.brand) / positionell
via :first-of-type), damit Klassen-Renames in zitare-web das Hide
nicht still brechen
- project.yml entitlements: applinks:zitare.mana.how als zweite
Universal-Link-Domain, solange zitare.com-DNS-Zone fehlt. Beide
Hosts liefern dasselbe AASA-File. Nach Cloudflare-Cut kann der
Eintrag bleiben — schadet nicht
- Live-E2E verifiziert: simctl openurl https://zitare.mana.how/q/...
→ App empfängt Deep-Link, WebShell lädt die Quote-Page, native
TabBar bleibt, Web-Header bleibt versteckt
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- WebShellView (UIViewRepresentable + NSViewRepresentable) wrapt
WKWebView, KVO-Observation für Loading/Progress/canGoBack/URL,
Pull-to-Refresh via UIRefreshControl
- WebShellCoordinator (MainActor) hält WKNavigationDelegate +
WKUIDelegate, externe Links via openURL aus dem Environment in
System-Browser, Host-Whitelist auf zitare.com + .mana.how
- RootView refactored: Lesen-Tab lädt webBaseURL/, Erkunden-Tab
/explore. Universal-Links zitare.com/q|a|c/<slug>, /search,
/region/*, /thema/* etc. routen in den passenden Tab,
reloadToken zwingt Re-Navigation auch bei selber URL
- AppConfig.webBaseURL = appBaseURL (zitare.mana.how) bis
Cloudflare-Zone für zitare.com live ist; publicWebURL als
Konstante schon eingetragen
- CookieBridge-Skeleton für mana.access auf .mana.how —
scharfgeschaltet erst in ζ-3 nach Live-Auth-Smoke
- iPhone 16e Simulator: zitare.mana.how lädt, Carl-Spitteler-Quote
rendert, Healthz weiter 200
- 16 Files swiftlint-grün, alle Tests grün
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- project.yml mit Bundle ev.mana.zitare + Widget + ShareExt-Targets
- ManaSwiftCore (ManaCore + ManaTokens) + ManaSwiftUI (ManaAuthUI)
als Package-Dependencies via path:
- Pure SwiftUI für Native-Surfaces, WKWebView nur für Lese-Tabs
(Hybrid-Sonderfall vs cards/memoro/manaspur, dokumentiert im
Playbook ZITARE_NATIVE_GREENFIELD.md)
- Theme: paper-Variant aus @mana/themes
- ZitareAPI.healthCheck via direct URLSession (öffentlicher
Endpoint, kein AuthenticatedTransport-Gate)
- 6/6 AppConfigTests + 1/1 UI-Smoke grün auf iPhone 16e Simulator
- Live: zitare-api.mana.how/healthz → HTTP/2 200
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>