mana-swift-ui/CHANGELOG.md
Till JS 7a0216dc44 feat(feedback): Screenshot-Anhaenge via PhotosPicker im ManaFeedbackSheet (v0.13.0)
PhotosPicker (bis 3 Bilder) + Thumbnails + Entfernen; FeedbackImagePrep
(ImageIO/CoreGraphics, iOS+macOS, EXIF-frei). FeedbackFormModel haelt
pickedItems/images. Braucht ManaCore >= 2.2.0.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-12 19:56:10 +02:00

854 lines
43 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Changelog
Alle Änderungen werden hier dokumentiert. Format orientiert an
[Keep a Changelog](https://keepachangelog.com), Versionierung nach
[Semver](https://semver.org).
## [Unreleased]
### Neu — Screenshot-Anhänge im `ManaFeedbackSheet`
`PhotosPicker` (bis 3 Bilder) mit Thumbnails + Entfernen; herunterskaliert
und EXIF-frei via neuem `FeedbackImagePrep` (ImageIO/CoreGraphics, baut auf
iOS **und** macOS). `FeedbackFormModel` hält `pickedItems`/`images`, lädt +
komprimiert in `loadPickedImages()` und hängt sie als `FeedbackImage` an die
Einreichung. Schließt die letzte Web↔Nativ-Lücke beim Feedback. Braucht
`ManaCore` ≥ 2.2.0.
### Neu — `ManaAccountView`: „Profilbild ändern"-Zeile (Vereinheitlichung Weg B)
Der geteilte Konto-Screen rendert im eingeloggten Zustand jetzt eine
**„Profilbild ändern"**-Zeile im Konto-Block (nach „Name ändern"), die das
Profilbild-Studio `hub.mana.how/profil` im Browser öffnet. Damit ist das
Profilbild-Setzen (Foto→3D + Pixel-Baukasten) in **allen** nativen Apps
erreichbar, ohne den Editor pro App zu duplizieren — das Studio bleibt
single-source im Vereins-Hub. Pendant zur Compose-`ManaAccountScreen`
(mana-kotlin-ui 0.18.0) + Web-`UserMenuPanel` (@mana/shared-ui 1.21.0).
### Neu — `ManaComponentsUI`: `ManaScreenHeader` / `ManaSectionHeader` / `ManaPinnedGlyph`
Geteilte Bausteine für die Listen-Optik, drift-sicher als Pendant-Paare zur
Compose-Lib (mana-kotlin-ui):
- **`ManaScreenHeader`** — links-bündiger, unterstrichener Seitentitel (jetzt
**fett**) + optionaler Untertitel/Trailing-Slot. `titleColor`-Override für
monochrome Titel auf dunkler Fläche.
- **`ManaSectionHeader`** — großer (`.title2`/SemiBold) Abschnitts-Header für
scrollende Listen, optional einklappbar (Chevron). Ersetzt das winzige
Caption-Muster.
- **`ManaPinnedGlyph`** — gezeichneter Fünfzack-Stern (geteilte Geometrie mit
Android, `ManaStarShape`) als „Angepinnt"-Marker; kein SF-Symbol, damit er
pixelgleich zum Material-Pendant bleibt.
### Neu — `ManaComponentsUI`: `ManaSettingsSection` / `ManaSettingsRow`
Geteilte Settings-Bausteine, SwiftUI-Pendant zur Compose-`ManaSettingsSection`
(mana-kotlin-ui 0.11.0). `ManaSettingsSection(icon:title:footer:)` rendert eine
native, gruppierte `Section` mit Icon-`Label`-Header + Footer; `ManaSettingsRow`
ist die auswählbare Zeile (Symbol + Titel/Untertitel + Häkchen). Damit lassen sich
Settings-Screens iOS↔Android struktur- und wortgleich bauen (gleiche API, je native
Optik). Erstkonsument: memoro-natives KI-Menü (`AISettingsView`).
### Geändert — `ManaOnboardingUI`: eine scrollbare Seite, ein Titel
Der Flow ist jetzt **eine einzige scrollbare Seite mit genau einem
Titel** statt mehrerer Wisch-Seiten:
- Kein `TabView`-Pager, keine Seiten-Indikator-Punkte mehr.
- Ganz oben **ein** Titel (der der ersten `ManaOnboardingPage`) neben dem
App-Icon (jetzt 40×40 statt 48×48). Darunter ausschließlich die
**Info-Karten** — alle Zeilen aller Seiten flach untereinander, **ohne
Zwischen-/Abschnitts-Titel**.
- Unten genau **ein** Button („Los geht's“), kein „Weiter“ mehr.
- Vollständig datengetrieben aus dem bestehenden `[ManaOnboardingPage]` —
**keine App muss etwas anpassen**, greift via Pfad-Dependency überall.
(Titel von Seiten ab der zweiten werden nicht mehr angezeigt.)
### Geändert — `ManaOnboardingUI`: ruhigere Seiten (Icon überall, kein Fließtext)
Flottenweite Design-Angleichung aller Onboarding-Seiten:
- App-Icon steht jetzt **auf jeder Seite** neben dem Titel (vorher nur auf
der ersten/Willkommens-Seite).
- Titel **eine Stufe kleiner** (`.title` → `.title2`), Icon entsprechend
48×48 statt 54×54.
- **Vorspann (`intro`) und Fußnote (`footnote`) werden nicht mehr
gerendert** — eine Seite trägt nur noch Überschrift + Zeilen-Karten.
Die Model-Felder bleiben quellkompatibel (Default `nil`), werden aber
ignoriert; Apps müssen nichts anpassen. Bei Gelegenheit können die
jetzt toten `intro:`/`footnote:`-Argumente in den App-Onboardings
entfernt werden.
Greift automatisch in allen Apps (lokale Pfad-Dependency), kein Bump nötig.
### Hinzugefügt — `ManaTransparencyUI` (neues Product): „Danke & Transparenz"
Neues Library-Product `ManaTransparencyUI` — der geteilte native Renderer der
ökosystemweiten „Danke & Transparenz"-Fläche (Open-Source-Dank + Offenlegung
des Stacks). Pure SwiftUI, app-agnostisch, keine Dependencies.
- `ManaCredit` (Codable/Sendable) + `ManaTransparencyData.load(_:in:)` —
dekodiert die vom Generator (`mana/scripts/transparenz/generate.mjs`, Ziel
`native`) erzeugte JSON aus dem App-Bundle.
- `ManaTransparencyView(intro:entries:)` bzw. `ManaTransparencyView(_:)` —
gruppiert nach Sektion (Aufbauend auf / Frameworks / Bibliotheken / Schriften
/ Dienste / Bewusst externe Dienste), Einträge mit Lizenz + tippbarem Link.
- Pendant zur Web-`<Transparenz>` (@mana/shared-ui). SOT:
`../mana/docs/TRANSPARENZ.md`. Adoption: 1 Dep + JSON ins Bundle + ein
`ManaTransparencyView(...)` in den Einstellungen.
### Hinzugefügt — `ManaCreditsUI` (neues Product): Gesamt-Mana-Anzeige
Neues Library-Product `ManaCreditsUI` — die native „Mana"-Seite, Pendant zur
Web-`<ManaPage>`:
- **`ManaView(client:)`** — Gesamt-Guthaben (groß) + EUR-Gegenwert + Topf-Split
(80/5/5/5/5 aus MISSION.md) + letzte Bewegungen. Read-only (Aufladen im Web).
- **`ManaBalanceRow(balance:)`** — kompakte Guthaben-Zeile für Account-/Settings-
Screens.
- **`ManaBalanceLink(auth:)`** — fertiger Eintrag (Row + Laden + Navigation zur
`ManaView`) als Einzeiler für `Form`/`List`-Sections; macht den App-Rollout
trivial.
- **`ManaAccountView.Config.showManaBalance`** (ManaAuthUI, Default `false`) —
schaltet die Gesamt-Mana-Anzeige direkt im geteilten Konto-Gerüst ein. Apps,
die `ManaAccountView` nutzen, brauchen so nur das Flag (kein eigener Code).
ManaAuthUI hängt dafür neu (transitiv, leichtgewichtig) an `ManaCreditsUI`.
- **`ManaFormat`** — Mana/EUR/Datum-Formatierung; **`ManaCreditsModel`** — Loader.
- Konsumiert `ManaCreditsClient` aus ManaCore; System-Styling (app-agnostisch),
keine ManaTokens-Abhängigkeit.
### Hinzugefügt — `ManaAiActionMetaView` (ManaLLMUI)
Geteilte, app-agnostische Anzeige-Zeile für KI-Verbrauch: Modell · Dienst
· Dauer · Tokens · Mana · ≈ Strom (System-Styling, `.secondary`). Pendant
zur Web-Komponente `<AiActionMeta>` in `@mana/shared-ui`. Konsumiert
`ManaAiMeta` aus `ManaLLMShared`. Energie ist bewusst als Schätzung
markiert (≈). SOT: `mana/docs/AI_TRANSPARENCY.md`.
- **`ManaAiActionMetaView(meta:)`** in `ManaLLMUI`.
- **Apps:** opt-in — unter ein KI-Ergebnis setzen, sobald die App ein
`ManaAiMeta` von ihrer Produkt-API erhält.
### Geändert — Login-AutoFill zuverlässiger (`.manaUsernameField()`)
- Neuer View-Modifier `manaUsernameField()` (`ManaFields`): wie
`manaEmailField()`, aber mit `textContentType(.username)` statt
`.emailAddress`. `ManaLoginView` nutzt ihn jetzt fürs Account-Feld.
**Warum:** iOS-AutoFill erkennt das Feld dadurch als Credential-Account
und überschreibt es auch, wenn der User bereits ein paar Zeichen getippt
hat (vorher füllte `.emailAddress` oft nur leere Felder). Sign-Up/Reset
bleiben bewusst bei `manaEmailField()` (reine Email-Eingabe).
- **Apps:** kein Anpassungsbedarf — `ManaLoginView` wirkt automatisch. Eigene
Login-Formulare können `manaEmailField()` → `manaUsernameField()` umstellen.
### Hinzugefügt — `ManaJobStatusView` (Wartezeit-Anzeige für GPU-Jobs)
- Ehrliche Inline-Anzeige für lang laufende KI-/GPU-Jobs (Stem-Separation,
Musik-Generierung, Transkription) in `ManaComponentsUI`. Zustände
`queued`/`running`/`done`/`failed` mit „läuft seit …", „dauert meist … /
bei Andrang bis zu X min" und optionaler Warteschlangen-Position; optionaler
Abbrechen-Button via `onCancel`. Sichtbares Gegenstück zum **GPU-Broker**
(`../mana/docs/GPU_BROKER.md`), der die geteilte RTX 3090 serialisiert — die
dadurch mögliche Wartezeit wird benannt statt versteckt.
- Natives Pendant zu `<JobStatus>` aus `@mana/shared-ui` 1.14.0 (Web) —
identische Formulierungen, damit web + nativ gleich klingen.
- Spinner über `ProgressView` (respektiert Reduce-Motion automatisch), Farben
ausschließlich aus dem injizierten `manaTheme` (12 Tokens), `Spacing`/`Radius`
aus `ManaTokens`. Reine Texthelfer `ManaJobStatusText` (formatElapsed/
formatEstimate/defaultTitle) separat — getestet im neuen Test-Target
`ManaComponentsUITests` (9 Tests, `swift test` grün).
### Hinzugefügt — `ManaSearchResultsView` (iOS-26-Such-Tab-Baustein)
- Geteilte Hülle für die **native Such-Ansicht** hinter `Tab(role: .search)`
(iOS 26: unten schwebendes Liquid-Glass-Suchfeld; ab iOS 18 als regulärer
Such-Tab — **kein Availability-Gating** nötig). Vereinheitlicht die drei
Such-Zustände flottenweit: leere Query (Einstiegs-Prompt), keine Treffer
(`ContentUnavailableView.search`), Treffer (app-eigene Liste). Die App reicht
nur Query + Treffer-Status + Ergebnis-Liste; `NavigationStack`/`.searchable`/
`navigationDestination` bleiben app-seitig (Ziel-Typen sind app-spezifisch).
- **Pilot: memoro** (Such-Tab über alle Memos: Titel/Headline/Intro/Transkript),
im iOS-26-Sim verifiziert (Suchfeld unten, eigener Such-Tab). Ersetzt
perspektivisch den Custom-``ManaSearchButton`` durch das native Muster.
- **Rollout abgeschlossen (2026-06-06):** memoro, zitare, seepuls (Events +
Orte zusammengeführt), pageta, audioguide, comicello, wordeck (eigene Decks
lokal + Marktplatz server) haben alle den nativen Such-Tab; alle Inline-
`.searchable`/`ManaSearchButton`-Suchen sind entfernt.
### Veraltet — `ManaSearchButton`
- Mit `@available(*, deprecated)` markiert. Die Suche läuft flottenweit über den
nativen Such-Tab (`Tab(role: .search)`) + ``ManaSearchResultsView`` statt über
ein Custom-Such-Icon in der Filter-Leiste. Keine Konsumenten mehr; bleibt für
Übergangs-Fälle, soll aber nicht neu adoptiert werden. Playbook:
`../mana/docs/playbooks/SEARCH_TAB_ROLLOUT.md`.
### Behoben — `ManaAccountView` folgt dem Theme-Variant in **allen** Sektionen
- Bisher folgten nur die selbstgetönten Karten (Anmelden/Feedback) dem
Theme; die Standard-Sektionen (Konto/Sicherheit, „Darstellung", „Status",
„Über" und app-eigene `extra()`-Sektionen) nutzten den System-Grouped-
**Cell**-Hintergrund und blieben beim Variant-Wechsel neutral-grau. **Fix:**
`ManaAccountView` liest `@Environment(\.manaTheme)` und legt
`.listRowBackground(theme.surface)` auf alle Nicht-Karten-Sektionen
(inkl. `extra()`). `listRowBackground` ist ein Default — app-eigene Zeilen
mit eigenem Row-Background überschreiben ihn weiterhin.
- **Page-Hintergrund themed + recessed für genug Card-Kontrast.** Die
Theme-Tokens setzen `background` in Light fast deckungsgleich mit `surface`
(L≈98 vs 100) — die Card-Abgrenzung ist im Theme über den Border gedacht,
der full-bleed in der grouped `Form` an den Ecken clippt. Lösung: Page-
Hintergrund = in **Light** der klar recessed `theme.muted` (L≈92), in **Dark**
das dunklere `theme.background` (deutlich unter `surface`) — gewählt per
`@Environment(\.colorScheme)`. Beides ist themed (folgt dem Variant) **und**
klar von den `surface`-Cards abgesetzt; mehr Kontrast als der zuvor genutzte
near-white System-Grouped-Background, in beiden Modi.
### Hinzugefügt — `ManaAccountView`: Profil-Avatar im Sheet + Icons
- Eingeloggt zeigt der Konto-Block jetzt oben einen **Profil-Header**: das
große, rund beschnittene Ökosystem-Avatarbild (gleiche Quelle wie der
Toolbar-Avatar, ``ManaAvatarImageStore``), darunter **Anzeigename** (aus
`AuthClient.getProfile()`) und **E-Mail**. Fällt auf ein
`person.crop.circle.fill`-Glyph (in `theme.primary`) zurück, solange kein
Bild gesetzt/geladen ist. Kein App-Code nötig — `ManaAccountView` lädt selbst.
- **Icons auf den Konto-Einträgen:** E-Mail (`envelope`), E-Mail ändern
(`pencil`), Passwort ändern (`key`), Abmelden
(`rectangle.portrait.and.arrow.right`), Konto löschen (`trash`). Stil
(gedämpftes Icon + Vordergrund-Titel + Chevron) konsistent mit
``ManaTwoFactorAccountRow``; destruktive Zeilen in `error`-Farbe ohne Chevron.
- **„Name ändern"-Zeile** in der Konto-Sektion (`person`-Icon) — leichter
Alert mit Textfeld, schreibt via `AuthClient.updateName(_:)` (mana-swift-core
≥ Unreleased). Der Avatar-Header-Name aktualisiert sich danach. Damit ist der
Anzeigename erstmals **nativ** änderbar (vorher nur Registrierung/Web).
Voraussetzung: `mana-swift-core` mit `updateName`.
### Behoben — Karten-Border in `ManaAccountView` an den Ecken abgeschnitten
- Der Anmelde-CTA (`ManaSignInCTA`) und die Feedback-Karte
(`ManaFeedbackCard`) saßen full-bleed in der inset-grouped `Form` und
zogen einen `.manaCard(border:)`. Die gerundete Section-Maske der `Form`
clippt den Zelleninhalt auf ihre Ecken — **unabhängig vom Karten-Radius**
(empirisch verifiziert: 4/10/12 pt liefern identische Ecken). Ein Overlay-
`strokeBorder` wird dadurch an den vier Ecken abgeschnitten; sichtbar vor
allem in dunklen Themes (seepuls). **Fix:** Die beiden Karten verzichten
full-bleed auf den Manual-Border und definieren sich über **Surface-
Kontrast**, genau wie eine native grouped Section — **volle Section-Breite**
bleibt erhalten, keine abgeschnittenen Ecken mehr. Neuer geteilter Marker
**`View.manaCardListRow()`** (ManaThemeUI) kapselt die full-bleed-Row-
Konfiguration (Null-Insets + transparenter Row-Background) und dokumentiert
die Regel.
- Der Anmelde-CTA-Block trägt jetzt den Section-Header **„Konto"** (parallel
zur eingeloggten Konto-Sektion), die Feedback-Karte den Header
**„Feedback"** — damit auch diese beiden Blöcke, wie alle übrigen
Konto-Tab-Sektionen, eine Überschrift haben.
### Hinzugefügt — `ManaAccountAvatarButton`: Ökosystem-Profilbild (Stufe 1)
- Der geteilte Konto-Avatar oben rechts zeigt jetzt das im Avatar-Baukasten
(`avatar.mana.how`) gebaute **Profilbild** des angemeldeten Nutzers, rund
beschnitten — statt nur Initialen/Icon. Fällt sauber auf Initialen bzw.
System-Icon zurück (lädt / kein Bild gesetzt / ausgeloggt).
- Neuer **`ManaAvatarImageStore`** (`@MainActor`, prozessweiter Cache):
fragt mana-me `GET /api/v1/me/images/primary/avatar` mit dem User-JWT ab
und baut die öffentliche mana-media-Thumb-URL. Dedupliziert über alle
Buttons (einer pro Tab) und verwirft den Cache bei Abmeldung.
`invalidate()` für „Profilbild gerade geändert".
- **Null App-Code nötig:** der Button lädt selbst, Defaults zeigen auf die
Produktiv-Services (`me.mana.how` / `media.mana.how`). Jede App, die
`ManaAccountAvatarButton` / `manaAccountToolbar` schon nutzt, erbt das
Bild nach einem Rebuild. Overrides additiv: `loadsProfileImage` (aus für
rein anonyme Apps), `meBaseURL` / `mediaBaseURL` (Dev/Localhost).
### Hinzugefügt — `ManaLLMUI`: KI-Einstellungen als geteilte Unterseite
- **`ManaLLMSettingsLink`** — eine `NavigationLink`-Zeile (SF-Symbol +
Titel + Use-Case-Untertitel), die auf eine eigene KI-Unterseite pusht,
statt die vier KI-Sections inline in den Konto-/Settings-Tab zu kippen.
Flottenweites Muster, damit der KI-Bereich in allen nativen Apps gleich
aussieht.
- **`ManaLLMSettingsScreen`** — die Zielseite: hostet die bestehende
`ManaLLMSettingsView`-Komposition in einer `Form` plus optionalen
app-eigenen KI-Inhalt über einen generischen `extra`-Slot
(`@ViewBuilder`, Default `EmptyView`). So zieht z.B. memoros
Server-LLM-Picker hinter denselben einen KI-Link.
- Beide Views additiv: `ManaLLMSettingsView` und die granularen
`ManaLLM*Section`-Views bleiben unverändert und weiter public. Apps,
die bewusst inline bleiben wollen, sind nicht betroffen.
- Defaults: Titel „Künstliche Intelligenz", Icon `sparkles` — beides
überschreibbar.
### Hinzugefügt — `ManaOnboardingUI` (geteilter Einstiegs-Flow)
- Neues Library-Product **`ManaOnboardingUI`** — Onboarding-Flow im Stil
von Apples „Neue Funktionen“-Sheet für alle nativen Verein-Apps. Damit
der Einstieg flottenweit gleich aussieht/sich anfühlt, aber im
Signatur-Theme der App (Farben aus `@Environment(\.manaTheme)`, kein
hardcodetes Branding).
- **`ManaOnboardingPage`** — app-geliefertes Seiten-Modell: großer Titel,
optionaler Vorspann, eine Liste von `Row`s (SF-Symbol + Titel + Text),
optionale Fußnote. **Voll `Sendable`** (keine Action-Closures).
- **Doktrin: kein Berechtigungs-Abfragen im Onboarding.** Das Paket
fragt *nie* System-Rechte ab — Berechtigungen hängen an konkreten
Features und werden just-in-time erfragt. Onboarding **informiert** nur
(Werte-Entscheidung 2026-06-05: kein Extrahieren auf Vorrat).
- **`ManaOnboardingView`** — Container. Links ausgerichteter Titel (auf
Seite 1 mit dem **App-Icon links daneben**, automatisch aus dem
Bundle), Zeilen als **eingefasste Karten** (`.manaCard`), **wischbar**
zwischen Seiten (iOS Page-Style; macOS Cross-Fade), unten der
brand-getönte Seiten-Indikator + genau **ein** Primär-Button.
Reduced-Motion-aware.
- **`View.manaOnboarding(storageKey:pages:onFinish:)`** — `@AppStorage`-
Gate, präsentiert als `.sheet` mit `.presentationDetents([.large])` +
Griff (Apple-Optik, nicht eingezwängt). Per Wischen verlassbar = als
„gesehen“ gemerkt. Versionierter Key (`…vN`) zeigt bei großen Updates
erneut. Erster Konsument: `stetig-native`.
- Hängt nur an `ManaTokens` + `ManaThemeUI`. Keine externen Deps,
Public API `Sendable`/`@MainActor`. Tests: `ManaOnboardingUITests`
(7, Seiten-Modell).
### Hinzugefügt — BYOK in der LLM-Settings-UI
- **`ManaLLMByokSection`** (ManaLLMUI) — natives Pendant zum Web-
`ByokKeyManager`. Verwaltet pro Anbieter (OpenAI / Anthropic / Gemini /
Mistral) genau einen API-Schlüssel: hinzufügen (SecureField + Modell-
Picker), ändern, löschen; bei ≥2 hinterlegten Schlüsseln ein
„Standard-Anbieter“-Picker. Schlüssel liegen in der `ByokKeyVault`
(Keychain, app-privat, keine iCloud-Sync) aus `ManaLLM` ≥0.3.0. Der
Schlüssel wird nie zurück in die UI geschrieben (nur „hinterlegt“ +
Modell sind sichtbar).
- **`ManaLLMSettingsView(context:showByok:)`** — neuer `showByok`-Parameter
(Default `true`) blendet die BYOK-Section ein/aus. Apps ohne Dritt-
Anbieter-Wunsch setzen `false`.
- **`ManaLLMSettingsState`** verwaltet jetzt BYOK: `byokVault`,
`byokStoredProviders`, `byokDefaultProvider` (UserDefaults-persistiert),
`saveByokKey/deleteByokKey/byokModel/setByokDefaultProvider`. `makeRouter()`
reicht den Vault-Resolver durch, sodass `.byok` in der Availability-Map
als `.available` erscheint, sobald ein Schlüssel hinterlegt ist.
- **`ManaLLMBackendPickerSection`** kennt jetzt die Quelle
`LLMBackendID.byok` (Icon `key.fill`, eigener Footer-Text, „Schlüssel
hinterlegen“-Hinweis statt generischem „Fehlt: …“).
> **Voraussetzung:** `ManaLLM` (mana-swift-llm) ≥ **0.3.0** — diese
> Version fügt `LLMBackendID.byok` hinzu. Konsumenten-Apps mit eigenen
> erschöpfenden `switch`-Statements über `LLMBackendID` müssen den Fall
> `.byok` ergänzen (Xcode-Rebuild nötig).
### Hinzugefügt
- **`ManaAccountAvatarButton`** + **`.manaAccountToolbar(auth:profile:onTap:)`**
(ManaAuthUI). Runder Konto-Avatar für die Navigationsleiste oben rechts;
Tippen öffnet das Konto als Sheet statt eines eigenen Bottom-Tabs
(Apple-Muster aus Podcasts / App Store). Darstellung nach Auth-Zustand:
Initialen (signed-in + Name) → `person.circle` (signed-in ohne Name) →
`person.crop.circle` (guest/signed-out); immer sichtbar, damit Settings
+ Sign-In im Sheet in jedem Zustand erreichbar bleiben. Initialer
Konsument: memoro. Flotten-Rollout siehe
`../mana/docs/playbooks/ACCOUNT_AVATAR_MIGRATION.md`.
### Behoben
- **2FA-Status flackerte endlos** im Konto-Tab aller nativen Apps (Text
sprang zwischen „2FA-Status lädt…" und „2FA aktivieren", teils
„Netzwerkverbindung abgebrochen"). Ursache: `.task`/`.sheet` lagen auf
einem `Group`, der einen zustandsabhängigen `switch` umschloss — Group
verteilt Modifier auf jeden Zweig, also re-attachte sich `.task` bei
jedem State-Wechsel und feuerte `reload()` erneut (Endlosschleife; der
in-flight `getProfile()` wurde dabei gecancelt → `URLError.cancelled`).
`stateContent` ist jetzt eine `@ViewBuilder`-Property, der Modifier
wrappt von außen → `.task` feuert einmal pro Erscheinen.
(`ManaTwoFactorAccountRow`)
- **`ManaLLMSettingsView`**: dieselbe `Group`+`.task`-Falle —
`refreshAvailability()` lief pro Section (3×) und erneut beim
Ein-/Ausblenden der Prepare-Section. `.task` jetzt an der stabilen
Backend-Picker-Section.
### Hinzugefügt
- **ManaFeedbackUI Soft-Anerkennung** — die Erfolgs-Ansicht zeigt
eingeloggten Nutzern jetzt einen ehrlichen Aura-Hinweis („Wird daraus
ein bestätigter Bug oder ein vielgewünschtes Feature, schreiben wir
dir Aura gut."). Keine Sofort-Aura fürs reine Absenden — Anerkennung
erst bei Bestätigung (siehe `mana/docs/INVITES_AND_AURA.md`).
- **`ManaSafariLink`** (ManaAuthUI) — öffnet eine URL **in-App** (iOS:
`SFSafariViewController`, macOS: System-Browser-Fallback) statt eine separate
Browser-App aufzureißen. Für rechtliche/redaktionelle Seiten (Impressum,
Datenschutz, Lizenz). Rendert als `Form`-Zeile wie ein `Link`.
- **`ManaAboutSection.Link.inApp`** (ManaAuthUI) — neuer Init-Parameter
(`inApp: Bool = false`). `true` → die „Über"-Sektion öffnet den Link via
``ManaSafariLink`` in-App statt extern. Damit deckt **eine** Sektion App-Info
*und* Rechtliches ab, statt einer separaten Legal-Links-Sektion pro App.
Additiv, Default unverändert (externer Link).
- **`ManaAppearanceSection`** (ManaThemeUI) — vereinheitlichte „Darstellung"-
Sektion für den Konto-/Einstellungen-Tab: bündelt `ManaAppearancePicker`
(Hell/Dunkel/Automatik) + `ManaThemeGallery` in *einer* `Section` mit Header
+ ökosystemweit gleichem Footer (`defaultFooter`). Die Einzel-Picker waren
schon geteilt, der Section-Rahmen + Footer-Wortlaut nicht — jede App baute
ihn (leicht abweichend) von Hand. Persistenz bleibt App-Sache (zwei `Binding`s
rein, kein `UserDefaults`-Schreiben); Theme-Pass via `unlocked`/`onLockedTap`
durchgereicht. Additiv.
- **`ManaAppearanceCard`** (ManaThemeUI) — Karten-Variante von
`ManaAppearanceSection` für Apps mit **Card-Layout** (`ScrollView`/`VStack`
statt `Form`/`List`, z.B. viadocu). Gleicher Inhalt im `.manaCard`-Container
(continuous Squircle + Border) statt einer `Section`; Farben aus
`@Environment(\.manaTheme)`. Schließt die letzte Appearance-Lücke der Flotte.
- **`ManaComponentsUI`** (neues Library-Target) mit **`ManaPill`** — eine
einheitliche Kapsel-Pille (Filter-Chip / Bulk-Aktion) für alle nativen Apps.
Größe und Form sind zentral festgelegt (Referenz: seepuls-Filterleiste —
18/11 Padding, `.callout.semibold`, `Capsule`), die Farben kommen aus
`@Environment(\.manaTheme)` (keine Farb-Parameter → jede App behält ihre
Theme-Variant). Varianten `.selected` / `.neutral` / `.destructive` plus
`isOn`-Komfort-Init für Toggle-Filter. Behebt die zu kleinen Bulk-Aktions-
Pills im Auswahl-Modus und vereinheitlicht sie mit den seepuls-Filtern.
Dependency: nur `ManaTokens`.
- **`.manaCard(surface:border:cornerRadius:borderWidth:padding:)`**
(ManaThemeUI) — geteiltes Karten-Chrome für alle Verein-Apps: gefüllte
`surface`-Fläche, durchgehende **continuous** (Apple-Squircle-)Ecken und
optionaler Hairline-Border aus *einer* gemeinsamen Form (sauberer
Eckenverlauf statt zwei getrennter circular Shapes). Theming-agnostisch
(Farben explizit), nutzt `Radius`-Token als Defaults. Eine Quelle für die
Kartenoptik, statt pro Komponente hardcoded `cornerRadius` mal mit, mal ohne
Border. `ManaThemeUI` ist jetzt Dependency von `ManaAuthUI` + `ManaFeedbackUI`.
- **`RoundedRectangle.mana(_:)`** (ManaThemeUI) — Vereins-Standard-Form:
continuous (Squircle-)Ecke an einem `Radius`-Token. Für Buttons, Eingabe-
felder und kleine Inline-Boxen, die *keine* volle `.manaCard` brauchen, aber
dieselbe Ecken-Sprache sprechen sollen. Verhindert stilles Zurückfallen auf
den eckigeren `.circular`-Default.
- **`ManaAboutSection`** (ManaAuthUI) — vereinheitlichte „Über"-Sektion für den
Konto-Tab: App-Name + Version/Build (automatisch aus dem `Bundle`) +
konfigurierbare Link-Liste. `ManaAccountView` rendert sie an **fester
Position** (ganz unten, nach den App-Sektionen), wenn der neue `about:`-
Parameter gesetzt ist — additiv, kein Breaking-Change (Default `nil`). Beendet
das per-App-Handbauen der App-Info-Sektion (vorher uneinheitlich: comicello
App/Version/Build+Links, wordeck nur Hosts, memoro gar nichts).
### Geändert
- **Rest-Chrome auf `Radius`-Token + continuous gezogen** — `ManaTextField` /
`ManaSecureField` (Feld-Box + Border, jetzt `strokeBorder` statt `stroke`),
`ManaPrimaryButton`, die Feedback-Buttons (`ManaFeedbackCard`/`ManaFeedbackSheet`)
und die 2FA-Views (QR-Box, Backup-Code-Chips, Kopier-Buttons) nutzen statt
hardcoded `cornerRadius`-Literalen (6/8/10/12) jetzt `RoundedRectangle.mana(Radius.*)`.
Radien konvergieren auf die Token-Skala (Felder/Buttons → `Radius.md` 8,
Code-Chips → `Radius.sm` 4, QR-Box → `Radius.lg` 12); alle Ecken jetzt
continuous. Rein optisch, keine API-Änderung.
- **`ManaSignInCTA` (Login-CTA) + `ManaFeedbackCard` ziehen auf `.manaCard`** —
beide Karten teilen jetzt identisches Chrome (continuous Squircle-Ecken,
`Radius.lg`, 1pt-Border in `border`-Farbe). Vorher: Login rahmenlos mit
circular `cornerRadius 12`, Feedback mit getrenntem Background+Overlay-Border
bei `cornerRadius 16`, dessen Strich den Ecken nicht sauber folgte. Optische
Vereinheitlichung über alle Apps mit `ManaAccountView`. *Kein API-Bruch.*
- **`ManaSignInCTA`** (ManaAuthUI) — prominente Anmelde-Card für den
abgemeldeten Zustand: optionale Headline + Erklärtext + vollbreiter
Brand-Button (`ManaPrimaryButton`), komplett über `\.manaBrand`
getönt. Gehoben aus viadocus handgebautem `guestBanner`, der als
einzige App optisch „richtig" wirkte. Ersetzt die blasse Form-
Listenzeile, die ein nackter `Button`/`Label` in einer `Section`
rendert.
- **`ManaAccountView.Config.signInHeadline`** (ManaAuthUI) — Headline
über dem Anmelde-CTA (Default „Noch nicht angemeldet", `nil` blendet
sie aus).
### Geändert
- **`ManaThemeGallery` Reihenfolge + App-Brücke** — (1) **freigeschaltete
Variants stehen jetzt zuerst**, gesperrte danach (stabile Partition über
`unlocked`); vorher lagen freie Themes quer zwischen Schlössern verstreut.
(2) Der **„<App> öffnen"-Button bleibt nach dem Freischalten sichtbar** (mit
geändertem Infotext „Signatur-Theme von <App>" statt der Freischalt-
Aufforderung) — als dauerhafte Brücke zur Herkunfts-App, damit die anderen
mana-Apps immer erreichbar bleiben und klar bleibt, woher das Theme stammt.
Baseline-Variants (ohne Signatur-App) bekommen weiterhin keinen Button.
`onLockedTap` feuert jetzt auch beim freigeschalteten Button-Druck. Reine
View-Änderung, keine API-Signatur-Änderung. Siehe THEME_PASS.md.
- **`ManaThemeGallery` Theme-Pass-Lock** — gesperrte Variant öffnet die
Signatur-App **nicht mehr automatisch**. Tippen/Einrasten zentriert sie
nur; darunter erscheint „Probier <App> aus, um „<Variant>" freizuschalten"
**plus ein „<App> öffnen"-Button**, der erst auf Druck die eigene
Landingpage öffnet (`ManaTheme.signatureAppHomepage`, mana-swift-core
≥ 1.15.0; via `@Environment(\.openURL)`). `onLockedTap` ist jetzt ein
optionaler Analytics-Hook (feuert beim Button-Druck), keine Navigation
mehr — Konsumenten können den Closure droppen. Datei zugleich auf die
Repo-Konvention (4 Spaces) normalisiert. Siehe THEME_PASS.md.
- **`ManaAccountView` signed-out** rendert jetzt ``ManaSignInCTA`` statt
einer Label-Listenzeile — alle Apps, die ``ManaAccountView`` nutzen
(seepuls, pageta, wordeck, audioguide), bekommen den prominenten CTA
automatisch, ohne Code-Änderung.
- **`ManaFeedbackCard`** (ManaFeedbackUI) — prominenter Feedback-Block
für den Konto-/Einstellungen-Tab: Icon + aktivierender Titel
(„Hilf mit, die App zu formen") + ein Satz Einladung + gefüllter
CTA in Vereinsfarbe. Löst die dezente `ManaFeedbackButton`-Listenzeile
ab und präsentiert dasselbe `ManaFeedbackSheet`. Theme-aware
(`\.manaTheme`, `surface`+`border` als Karten-Rahmen). Copy
überschreibbar (`title`/`message`/`cta`).
- **`ManaAccountView.feedbackClient`** (ManaAuthUI) — neuer optionaler
Init-Parameter. Ist er gesetzt, rendert der Standard-Block die
`ManaFeedbackCard` an **fester Position** (direkt nach dem
Konto-/Anmelden-Block, vor `extra`) — vereinheitlicht Platzierung +
Optik des Feedbacks über alle Apps, statt es pro App handgebaut tief
im `extra`-Slot zu verstecken. `ManaAuthUI` hängt dafür neu von
`ManaFeedbackUI` ab. Additiv, kein Breaking-Change (Default `nil`).
- **`ManaPasswordStrength`** + **`ManaPasswordStrengthView`** (ManaAuthUI)
— segmentierte Passwortstärke-Anzeige unter den `newPassword`-Feldern
in `ManaSignUpView` und `ManaResetPasswordView`. Reine Heuristik
(Länge + Zeichenklassen, keine externe Bibliothek), testbar
(`PasswordStrengthTests`). Blendet sich bei leerem Feld aus, markiert
`.weak` per `brand.error`. Stufen: schwach/mittel/gut/stark.
## [0.9.0] — 2026-06-01
Minor — **zwei neue Library-Produkte** (`ManaFeedbackUI`,
`ManaThemeUI`) plus das vereinheitlichte Konto-Gerüst `ManaAccountView`
und der Passwort-Reveal in `ManaSecureField`.
### Hinzugefügt
- **`ManaThemeUI`** — neues Library-Produkt. Geteilte Theme-Galerie
(`ManaThemeGallery` — Orb-Karussell mit Snap-to-Center und
Lock-State für den Theme-Pass) plus `ManaAppearancePicker`
(Hell/Dunkel/System). Konsumiert `ManaTokens`. Beendet das
per-App-Handbauen der Theme-Auswahl. Siehe `mana/docs/THEME_PASS.md`.
- **`ManaAccountView`** (ManaAuthUI) — vereinheitlichtes Konto-Gerüst:
Form-basierter Standard-Block (E-Mail-Anzeige, E-Mail/Passwort ändern,
2FA, Konto löschen [Apple 5.1.1(v)], Abmelden) aus einem `AuthClient`,
plus Sign-In-CTA im abgemeldeten Zustand. Config-Flags
(`showChangeEmail`/`showChangePassword`/`showTwoFactor`),
`onSignIn`/`onSignOut`-Callbacks, `@ViewBuilder extra`-Slot für
App-spezifische Sektionen. Pendant zur Login-Konsolidierung — beendet
das per-App-Handbauen des Konto-Tabs.
- **`ManaFeedbackUI`** — neues Library-Produkt. `ManaFeedbackSheet`
(Kind-Picker Wunsch/Problem/Feedback, Titel, Beschreibung; bei anonymer
Nutzung optionales Kontakt-Mail-Feld + Moderations-Hinweis),
`ManaFeedbackButton` (Default-Label „Feedback" oder Custom-Label) und
der `.manaFeedbackSheet(isPresented:client:isLoggedIn:)`-Modifier für
eigene Trigger (z.B. Toolbar/Settings-Row). Postet via
`ManaCore.FeedbackClient` an die zentrale Wunsch-App — Pendant zum
Web-`@mana/shared-feedback`-Widget. Theming über `\.manaTheme`
(ManaTokens). Siehe `mana/docs/FEEDBACK_NATIVE_PLAN.md`.
### Geändert
- **`ManaSecureField`** (ManaAuthUI) — Augen-Button zum Sichtbarmachen
des Passworts (`SecureField` ↔ `TextField`, Default verborgen).
Barrierearm gelabelt („Passwort anzeigen"/„Passwort verbergen"),
Fokus bleibt über den Feld-Tausch erhalten. Der Button erscheint
erst, wenn etwas im Feld steht; ein geleertes Feld kehrt zur
Maskierung zurück. Auf macOS warnt ein Feststelltasten-Symbol
(`capslock.fill`) bei aktivem Caps-Lock — wie in Apples eigenen
Passwortfeldern. Wirkt ökosystemweit: alle nativen Apps teilen
diese Komponente (Login, Sign-Up, Reset, Passwort ändern, 2FA,
Konto löschen).
- `ManaWebShell`: Die Fehler-Leiste in `WebShellView` hat jetzt einen
„Erneut laden"-Button. Bisher war ein Lade-Fehler eine Sackgasse —
der User sah die Meldung, konnte die Seite aber nicht neu laden.
Intern via `reloadNudge` → `effectiveTarget.reloadToken`.
## [0.8.0] — 2026-05-22
Minor — **neues Library-Product `ManaLLMUI`**. Drop-in-Settings-UI
für die lokalen LLM-Backends aus `mana-swift-llm`. Pendant zu
`ManaAuthUI` — vorher hatte nur Memoro die UI handgeschrieben, die
drei anderen Konsumenten (pageta, comicello, herbatrium) hatten
gar nichts.
### Hintergrund
Vier Apps konsumieren `ManaLLM` (Memoro, Pageta, Comicello, Herbatrium).
Memoros 250-Zeilen-LLM-UI (Picker + Prepare + Cellular-Toggle) war
strukturell shared-fähig — wurde rausgehoben, generalisiert und steht
jetzt allen Apps zur Verfügung.
### Neu
- `ManaLLMUI`-Product (deps: `ManaLLM`, `ManaLLMShared`)
- `ManaLLMSettingsView` — Convenience-Wrapper, drei Sections in
einem Schwung
- `ManaLLMBackendPickerSection` — Picker mit Availability-Status +
Empfohlen-Badge
- `ManaLLMPrepareSection` — Download/Init-Card mit Progress, nur
sichtbar für Gemma-Backends (`shouldShow(for:)`-Gate)
- `ManaLLMDownloadPolicySection` — WiFi-only-Toggle
- `ManaLLMSettingsState` — `@Observable`-State-Klasse, hält
Backend-Wahl + Availability + Prepare-Progress + delegiert an die
Stores aus `mana-swift-llm`
- `ManaLLMContext` — App-spezifischer Kontext (`useCaseShort` +
`useCaseLong`) für Section-Texte. `.generic` als Fallback
### Migration
Apps die heute eigene LLM-Settings-UI haben (Memoro):
- `import ManaLLM` für die Settings-View durch `import ManaLLMUI` ersetzen
- Lokale `llmSection`/`llmPrepareSection`/`llmDownloadPolicySection`-
Bodies entfernen
- `ManaLLMSettingsView(context: ...)` einhängen
- `LLMBackendPreferenceStore` + `LLMDownloadOverCellularStore` bleiben
funktionsgleich — wandern aber nach `ManaLLM` (siehe
`mana-swift-llm` CHANGELOG)
Apps die heute keine LLM-Settings-UI haben (Pageta, Comicello,
Herbatrium):
- `ManaLLMUI`-Product in `project.yml` adden
- `ManaLLMSettingsView(context: ...)` an passender Stelle einhängen
(eigene Settings-View oder NavigationLink im Profile-Tab)
## [0.7.0] — 2026-05-22
Minor — **`logoAssetName`** in `ManaBrandConfig`. Apps können jetzt
ein eigenes Logo-Asset (Asset-Catalog-Name) statt eines SF-Symbols
für den Login-/Sign-Up-/Forgot-Password-Header liefern.
### Hintergrund
Pageta hat ein eigenes Apple-Icon-Composer-SVG; das SF-Symbol
`book.pages` (vorher) sah neben dem polierten App-Icon unecht aus.
Andere Apps mit echten Logo-Assets (kommt) werden den gleichen
Migrationspfad gehen können.
### Neu
- `ManaBrandConfig.logoAssetName: String?` — Name eines Image-Assets
im Bundle der konsumierenden App. Hat Vorrang vor `logoSymbol`.
- `ManaAuthScaffold` rendert `logoAssetName` 64×64pt, `aspectRatio(.fit)`,
ohne Tint (Asset behält Originalfarben — typisch Apple-Icon-
Composer-Output mit Gradient). Fallback bleibt SF-Symbol mit Tint.
### Geändert
- `ManaBrandConfig.init` hat einen zusätzlichen optionalen Parameter
`logoAssetName: String? = nil`. Quellkompatibel — bestehende Apps
brauchen nichts ändern.
- `systemDefault`-Config setzt `logoAssetName: nil` explizit (kein
Verhaltenswechsel).
### Tests
- 50/50 grün (keine neuen Tests — die `ManaBrandConfig`-Änderung ist
rein additiv, gerendertes Asset hängt am Bundle der App).
### Adoption
Apps mit eigenem Logo:
```swift
ManaBrandConfig(
appName: "Pageta",
logoSymbol: "book.pages", // SF-Fallback bleibt
logoAssetName: "PagetaLogo", // Asset-Catalog-Name, hat Vorrang
...
)
```
## [0.6.0] — 2026-05-17
Minor — **neues Library-Product `ManaWebShell`**. WKWebView-Hülle für
Hybrid-Apps (Web-Lese-Surfaces + native Submit/Widget/ShareExt).
Extrahiert aus den fast-byte-identischen `WebShell/`-Ordnern in
`seepuls-native` und `zitare-native` (~900 LOC, davon ~700 LOC
Duplikat). Audit 2026-05-17 Vorschlag V2.
### Neu
- `WebShellView` (public SwiftUI View) — `WKWebView`-Wrapper mit
Progress-Bar, Pull-to-Refresh (iOS), Fehler-Snackbar, External-Link-
Delegation in den System-Browser. Universal (iOS + macOS).
- `WebShellConfig` (public, Sendable) — Host-Whitelist mit Wildcard-
Support (`"*.mana.how"`), User-Agent, Theme-Hints (background,
progressTint, errorBackground/Foreground/Icon), User-Scripts.
- `WebTarget` (public, Equatable+Sendable) — URL + monoton wachsender
`reloadToken`. Forciert Reload bei Universal-Link auf aktuelle URL.
- `WebNavState` (public, @Observable, @MainActor) — reaktiver
Navigation-State (isLoading, estimatedProgress, lastError,
currentURL, canGoBack).
- `WebShellCoordinator` (public, @MainActor) — `WKNavigationDelegate`
+ `WKUIDelegate`-Implementierung. KVO-Observations, Pull-to-Refresh-
Action.
- `WebShellScripts` (public Enum, @MainActor) — vor-gefertigte
`WKUserScript`-Helfer: `preferDarkScheme`, `syncDarkMode(localStorageKey:)`,
`hideElements(selectors:tagName:)`. Apps stapeln nach Bedarf.
### Logging
- ManaWebShell loggt unter Subsystem `ev.mana.webshell`, Kategorie
`web`. App-OSLog bleibt unverändert.
### Tests
- `ManaWebShellTests` mit 6 Tests gegen `WebShellConfig.isAllowed`.
Coverage für exakte Hosts, `*.root`-Wildcard, Root-selbst,
Negativ-Cases, leere Whitelist. 6/6 grün.
### Migrations-Hinweis
`seepuls-native` und `zitare-native` können ihre lokalen
`Sources/Features/WebShell/`-Dateien gegen `ManaWebShell` ersetzen.
Pattern in `mana/docs/playbooks/HYBRID_NATIVE_APP.md` (entsteht
parallel). App-spezifisches (CookieBridge, App-Theme als
`config.backgroundColor`) bleibt in der App.
## [0.5.0] — 2026-05-14
Minor — `ManaTwoFactorAccountRow` + `ManaBackupCodeRegenerateView`.
Macht den 2FA-Vollausbau in der AccountView nutzbar. Setzt
mana-swift-core ≥ 1.5.0 voraus (`getProfile()`).
### Neu
- `ManaTwoFactorAccountRow` — Drop-in für AccountView. Holt den
2FA-Status via `AuthClient.getProfile()` und zeigt:
- **Off:** "Zwei-Faktor aktivieren" → öffnet `ManaTwoFactorEnrollView`
- **An:** "Zwei-Faktor aktiv" + "Backup-Codes erneuern" +
"Zwei-Faktor deaktivieren"
- `ManaBackupCodeRegenerateView` — Re-Auth via Passwort, zeigt neue
Backup-Codes + Copy-to-Clipboard.
- `TwoFactorAccountRowModel` — internes `@Observable`-VM, reloaded
Status nach Enroll/Disable/Regenerate.
Damit ist 2FA in den Apps end-to-end nutzbar — User kann aktivieren,
Backup-Codes verwalten, deaktivieren. Der Login-Flow ist seit v0.3.0
durchgängig.
## [0.4.0] — 2026-05-14
Minor — 2FA-Enrollment-UI (Mini-Sprint B). Setzt mana-swift-core
≥ 1.4.0 voraus.
### Neu
- `ManaTwoFactorEnrollView` + `TwoFactorEnrollmentViewModel` —
3-Phasen-Wizard:
1. Passwort eingeben (Re-Auth)
2. QR-Code (via `CoreImage.CIFilter.qrCodeGenerator`, plattform-
unabhängig auf iOS+macOS) scannen + 6-stelligen Test-Code
eingeben
3. Backup-Codes anzeigen + Copy-to-Clipboard
- `ManaTwoFactorDisableView` — Single-Step-Sheet, Re-Auth via
Passwort + destruktiver Bestätigungs-Button.
### Tests
- 5 neue Tests für Enroll-VM (Success, falsches PW, canSubmitVerify
6-Ziffern-Guard, confirmVerify Phase-Wechsel, backupCodes-Accessor).
- 44/44 grün.
## [0.3.0] — 2026-05-14
Minor — `ManaTwoFactorChallengeView` für 2FA-Login. Setzt
mana-swift-core ≥ 1.3.0 voraus (Status `.twoFactorRequired`).
### Neu
- `ManaTwoFactorChallengeView` + `TwoFactorChallengeViewModel` —
6-stelliger TOTP-Code-Input (Number-Pad auf iOS), Fallback auf
Backup-Codes via Toggle, "Abbrechen" routet via
`auth.signOut(keepGuestMode:)` zurück zum Login.
- `LoginViewModel.Status.twoFactorRequired(email:)` als neuer Case.
- `ManaLoginView` schaltet bei `.twoFactorRequired` automatisch auf
`ManaTwoFactorChallengeView` um (analog zu `.emailNotVerified`).
### Tests
- 6 neue Tests für `TwoFactorChallengeViewModel`: canSubmit-Guards
(TOTP 6 Ziffern, Backup beliebig), toggleMode-State-Reset, submit
bei Erfolg/Fehler, Backup-Code-Routing.
- 39/39 grün.
## [0.2.0] — 2026-05-13
Minor — Action-Level-Gate für Apps mit Guest-/Login-optional-Modus.
Komplett additiv; braucht `mana-swift-core` ≥ 1.2.0.
### ManaAuthUI — neu: ManaAuthGate
- `ManaAuthGate` — `@Observable`-State-Maschine, die eine Aktion erst
laufen lässt, wenn der User eingeloggt ist. Wenn nicht, wird das
Sign-In-Sheet aufgeklappt und die Aktion gemerkt; nach erfolgreichem
Sign-In läuft sie automatisch.
- Zwei `require`-Overloads: synchron (`() -> Void`) und async
(`() async -> Void`). Konsumenten schreiben `gate.require { ... }`
ohne sich um das Gate-Lifecycle zu kümmern.
- `ManaAuthGateModifier` / `View.manaAuthGate(_:signIn:)` — hängt das
Sign-In-Sheet an einen Root-View und beobachtet `auth.status`.
Wechsel auf `.signedIn` schließt das Sheet und löst die Pending-
Aktion aus; manuelles Dismiss verwirft die Pending-Aktion.
- `lastReason` als optionaler Telemetrie-Hint pro `require`-Call.
### Konvention
Native-Apps sollen `mana-swift-core` v1.2.0 + Guest-Mode + diesen Gate
als Standardweg nutzen. Pattern für Cards/Manaspur/Memoro:
1. Beim App-Start: `bootstrap()`, dann bei `.signedOut` → `enterGuestMode()`.
2. Root-View zeigt immer App-Inhalte; **nie** eine Vollbild-Login-Wall.
3. Aktionen, die einen Account brauchen, werden in
`gate.require { ... }` gewrappt — Login wird zur Inline-Eskalation
statt zum App-Block.
Memoro hat dieses Muster informell schon umgesetzt (ContentView ohne
Hard-Gate). Cards + Manaspur ziehen mit ihren nächsten Releases nach.
### Tests
- 7 neue Tests für `ManaAuthGate`: sofortiger Run bei `.signedIn`,
Defer bei `.signedOut`/`.guest`, `resolvePending` nach Sign-In,
`cancelPending`, `lastReason`-Tracking.
## [0.1.0] — 2026-05-13
Phase 2 aus dem Native-Auth-Vollausbau-Plan (Option A, siehe
`../mana/docs/MANA_SWIFT.md`). Entstanden weil drei Apps fast-byte-
identische `LoginView.swift`-Dateien hatten und Sign-Up/Forgot-PW
komplett fehlten.
### ManaAuthUI (neu)
- `ManaBrandConfig` — App-injiziertes Bündel aus appName, tagline,
primary/surface/background/error-Colors. Apps liefern hier ihr
Theme (z.B. forest für Cards/Manaspur, default-mana für Memoro).
- Base-Components: `ManaAuthScaffold`, `ManaPrimaryButton`,
`ManaTextField`, `ManaSecureField` — geteilte Bausteine, alle
brand-aware.
- `ManaLoginView` + `LoginViewModel` — Email/PW-Login mit
Sign-Up- und Forgot-PW-Buttons. Bei `.emailNotVerified` automatisch
ins `ManaEmailVerifyGateView` umgeleitet (Resend-Mail-Button).
- `ManaSignUpView` + `SignUpViewModel` — Registrierung mit
Email/Name/Passwort. Nach Submit: Bestätigungs-Mail-Hinweis-Screen.
- `ManaEmailVerifyGateView` — wenn Login `.emailNotVerified` warf,
bietet "Bestätigungs-Mail erneut senden".
- `ManaForgotPasswordView` + `ForgotPasswordViewModel` — Reset-Mail
anfordern. Server antwortet immer 200 (keine User-Enumeration),
UI meldet generisch.
- `ManaResetPasswordView` + `ResetPasswordViewModel` — neues
Passwort setzen mit Token aus Reset-Mail. Wird aus dem
Universal-Link-Handler der App aufgerufen.
- `ManaChangeEmailView`, `ManaChangePasswordView`,
`ManaDeleteAccountView` — Account-Bausteine für die AccountView
der App. **`ManaDeleteAccountView` ist App-Store-Pflicht
(Guideline 5.1.1(v))** für jede App mit Account-Erstellung.
### Tests
- ViewModel-Tests via URLProtocol-Mock für jeden Auth-Flow.
- Brand-Config-Defaults.
### Bekannte Einschränkungen
- `ManaChangeEmailView`/`ManaChangePasswordView`/`ManaDeleteAccountView`
funktionieren erst nach Phase-3-Server-PR (Bearer-Plugin in
`mana-auth`). UI ist fertig, Wire ist fertig, Server muss nachziehen.
- 2FA, Magic-Link, Passkey-Flows nicht enthalten. Folgen in v0.2.0
zusammen mit ManaCore v1.2.0 und dem Server-PR.