mana-swift-ui/CHANGELOG.md
Till JS e956a05e3c ManaAccountAvatarButton: Ökosystem-Profilbild (Stufe 1)
Der geteilte Konto-Avatar lädt jetzt selbst das im Avatar-Baukasten
gebaute Profilbild (mana-me primary kind=avatar → öffentliche
mana-media-Thumb) und zeigt es rund beschnitten, mit Fallback auf
Initialen/Icon. Neuer ManaAvatarImageStore (prozessweiter Cache,
dedupliziert über alle Tab-Buttons). Null App-Code nötig — Defaults
zeigen auf Produktion; jede App mit manaAccountToolbar erbt es nach
Rebuild. Deckt die gesamte native Swift-Flotte.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-05 22:13:14 +02:00

29 KiB
Raw Permalink Blame History

Changelog

Alle Änderungen werden hier dokumentiert. Format orientiert an Keep a Changelog, Versionierung nach Semver.

[Unreleased]

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 Rows (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. Titel oben, 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, große Icons in fester Spalte.
  • 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 Bindings 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 „ öffnen"-Button bleibt nach dem Freischalten sichtbar (mit geändertem Infotext „Signatur-Theme von " 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 aus, um „" freizuschalten" plus ein „ ö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:

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.