d2ab6ea377
New files: AppTheme (palette + env key), ThemeManager (Observable singleton, UserDefaults persistence). Themes: Default, Emacs, Dracula, One Dark, Monokai, Material. Each controls background, accent, text, code block colors and a highlight.js theme for syntax highlighting. RootView applies preferredColorScheme, tint and appTheme env to the root group. SettingsView applies the same modifiers to its NavigationStack so the modal sheet is themed immediately (sheets run in a separate UIWindow and don't inherit preferredColorScheme from the parent hierarchy). Theme selection in Settings uses onTapGesture instead of buttonStyle(.plain) inside Form, which was silently intercepting the button action. CodeBlockView, TimelineView, NotificationsView all read from appTheme env.
22 lines
553 B
Swift
22 lines
553 B
Swift
import Foundation
|
|
import Observation
|
|
|
|
@Observable @MainActor
|
|
final class ThemeManager {
|
|
static let shared = ThemeManager()
|
|
|
|
private(set) var current: AppTheme = .default
|
|
|
|
private static let defaultsKey = "appThemeID"
|
|
|
|
private init() {
|
|
let saved = UserDefaults.standard.string(forKey: Self.defaultsKey) ?? "default"
|
|
current = AppTheme.all.first { $0.id == saved } ?? .default
|
|
}
|
|
|
|
func select(_ theme: AppTheme) {
|
|
current = theme
|
|
UserDefaults.standard.set(theme.id, forKey: Self.defaultsKey)
|
|
}
|
|
}
|