Geteilter Konto-Screen verlinkt eingeloggt aufs Profilbild-Studio (hub.mana.how/profil) statt den Editor pro App zu duplizieren. Damit ist das Profilbild-Setzen in allen nativen Apps erreichbar. swift build grün. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
845 lines
42 KiB
Markdown
845 lines
42 KiB
Markdown
# Changelog
|
||
|
||
Alle Änderungen werden hier dokumentiert. Format orientiert an
|
||
[Keep a Changelog](https://keepachangelog.com), Versionierung nach
|
||
[Semver](https://semver.org).
|
||
|
||
## [Unreleased]
|
||
|
||
### 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.
|