pageta-native/Sources/Features/SettingsView.swift
Till JS dde1b491c0 Theme-Galerie auf geteiltes ManaThemeUI umgestellt
Pageta konsumiert jetzt ManaAppearancePicker + ManaThemeGallery aus
mana-swift-ui statt eigener Kopien. Dünne Pageta-Wrapper halten die
@AppStorage-Keys (pageta.theme.variant / pageta.appearance) und reichen
Bindings durch. AppearanceMode → ManaAppearanceMode. Eigene
ThemeGalleryView.swift + AppearanceMode.swift entfernt (jetzt geteilt).
Verhalten unverändert, Build grün.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-31 23:43:26 +02:00

152 lines
3.5 KiB
Swift

#if os(macOS)
import ManaAuthUI
import ManaCore
import ManaLLMUI
import SwiftUI
/// macOS-Settings-Scene (`Settings { ... }` in `PagetaNativeApp`),
/// erreichbar über **⌘,** — der erwartete native Pattern für
/// App-Konfiguration auf dem Mac. Vier Sub-Tabs:
///
/// - **Konto**: Email-Status, Abmelden
/// - **Darstellung**: Light/Dark/Automatisch-Picker
/// - **Benachrichtigungen**: App-Icon-Badge
/// - **KI**: lokales LLM für Artikel-Zusammenfassung (geteilt mit
/// anderen mana-Apps via App-Group)
///
/// Auf iOS lebt die identische Konfig im normalen Konto-Tab
/// — `AccountTabView` ist dort der Settings-Hub.
struct SettingsView: View {
var body: some View {
TabView {
AccountSettingsPane()
.tabItem { Label("Konto", systemImage: "person.crop.circle") }
AppearanceSettingsPane()
.tabItem { Label("Darstellung", systemImage: "paintbrush") }
NotificationSettingsPane()
.tabItem { Label("Benachrichtigungen", systemImage: "bell") }
AISettingsPane()
.tabItem { Label("KI", systemImage: "sparkles") }
}
.frame(width: 520, height: 360)
}
}
private struct AISettingsPane: View {
var body: some View {
Form {
ManaLLMSettingsView(context: AccountTabView.llmContext)
}
.formStyle(.grouped)
.padding(20)
}
}
private struct AccountSettingsPane: View {
@Environment(AuthClient.self) private var auth
@Environment(ManaAuthGate.self) private var authGate
private var isSignedIn: Bool {
if case .signedIn = auth.status { return true }
return false
}
var body: some View {
Form {
Section {
if let email = auth.currentEmail {
LabeledContent("E-Mail", value: email)
} else {
Text("Nicht eingeloggt")
.foregroundStyle(.secondary)
}
}
Section {
if isSignedIn {
HStack {
Spacer()
Button("Abmelden", role: .destructive) {
Task {
await SpotlightIndex().clearAll()
await auth.signOut()
}
}
}
} else {
Button {
authGate.require(reason: "konto") {}
} label: {
Label(
"Mit mana-Konto anmelden",
systemImage: "person.crop.circle.badge.checkmark"
)
}
}
}
}
.formStyle(.grouped)
.padding(20)
}
}
private struct AppearanceSettingsPane: View {
var body: some View {
Form {
Section("Hell / Dunkel") {
PagetaAppearancePicker()
Text("„Automatisch\" folgt der System-Einstellung.")
.font(.footnote)
.foregroundStyle(.secondary)
}
Section("Thema") {
PagetaThemeGallery()
Text("Bestimmt die Farbwelt der App — frei wählbar, gilt sofort überall.")
.font(.footnote)
.foregroundStyle(.secondary)
}
}
.formStyle(.grouped)
.padding(20)
}
}
private struct NotificationSettingsPane: View {
@AppStorage(BadgeManager.userDefaultsKey) private var badgeEnabled = false
var body: some View {
Form {
Section {
Toggle("App-Icon-Badge zeigen", isOn: $badgeEnabled)
.onChange(of: badgeEnabled) { _, enabled in
Task { await handleBadgeToggle(enabled: enabled) }
}
} footer: {
Text(
"Zeigt die Anzahl ungelesener Artikel am Dock-Icon. Beim Aktivieren wird die System-Berechtigung abgefragt."
)
.font(.footnote)
.foregroundStyle(.secondary)
}
}
.formStyle(.grouped)
.padding(20)
}
private func handleBadgeToggle(enabled: Bool) async {
if enabled {
let granted = await BadgeManager.requestPermission()
if !granted {
badgeEnabled = false
}
} else {
await BadgeManager.setBadge(count: 0)
}
}
}
#endif