SPB Git

spb/zyquo-router Public MIT

One local endpoint, every AI provider — a private OpenAI-compatible LLM gateway for your Mac (170 models, 12 providers).

Swift 95.7% Python 2.3% Shell 1.2% Makefile 0.9%
3.8 KB · 135 lines swift
Raw Blame History
1//2//  AppearanceStore.swift3//  Zyquo Router4//5//  Author: Simon-Pierre Boucher6//  Mail: contact@spboucher.ai7//8//  User appearance preferences: theme mode, accent choice, font size.9//  Persisted to appearance.json via PersistenceService. Family design with10//  the Router's accent set: cyan (default) + graphite, sky, emerald, violet,11//  copper.12//1314import SwiftUI1516/// Accent color choices in Settings → Appearance.17enum AccentChoice: String, Codable, CaseIterable, Identifiable {18    case cyan19    case graphite20    case sky21    case emerald22    case violet23    case copper2425    var id: String { rawValue }2627    var displayName: String {28        switch self {29        case .cyan: return "Signal Cyan"30        case .graphite: return "Graphite"31        case .sky: return "Sky"32        case .emerald: return "Emerald"33        case .violet: return "Violet"34        case .copper: return "Copper"35        }36    }3738    /// (light, dark) accent hex pair.39    var accentHex: (UInt32, UInt32) {40        switch self {41        case .cyan: return (0x0891B2, 0x22B8D4)42        case .graphite: return (0x374151, 0x9AA4B2)43        case .sky: return (0x2E7CD6, 0x5A9BE3)44        case .emerald: return (0x1E9E6A, 0x3CBA87)45        case .violet: return (0x7451D8, 0x9273E4)46        case .copper: return (0xB2622B, 0xC97F4B)47        }48    }4950    /// (light, dark) subtle-tint hex pair (selected rows, active tints).51    var subtleHex: (UInt32, UInt32) {52        switch self {53        case .cyan: return (0xE5F5F9, 0x143A44)54        case .graphite: return (0xEEF1F4, 0x2A2F36)55        case .sky: return (0xE8F1FC, 0x1B3350)56        case .emerald: return (0xE6F6EF, 0x173B2C)57        case .violet: return (0xF0EBFB, 0x2C2347)58        case .copper: return (0xF9EFE6, 0x3D2C1D)59        }60    }61}6263enum ThemeMode: String, Codable, CaseIterable, Identifiable {64    case system, light, dark65    var id: String { rawValue }6667    var displayName: String {68        switch self {69        case .system: return "System"70        case .light: return "Light"71        case .dark: return "Dark"72        }73    }7475    var colorScheme: ColorScheme? {76        switch self {77        case .system: return nil78        case .light: return .light79        case .dark: return .dark80        }81    }82}8384/// Observable appearance preferences, persisted with the app settings.85@MainActor86final class AppearanceStore: ObservableObject {87    struct Stored: Codable {88        var themeMode: ThemeMode = .system89        var accent: AccentChoice = .cyan90        var fontSize: Double = 1391    }9293    static let fileName = "appearance.json"9495    @Published var themeMode: ThemeMode { didSet { save() } }96    @Published var accent: AccentChoice { didSet { save() } }97    /// Base body font size, clamped to 12–16pt.98    @Published var fontSize: Double {99        didSet {100            let clamped = min(16, max(12, fontSize))101            if clamped != fontSize { fontSize = clamped }102            save()103        }104    }105106    private let persistence: PersistenceService107108    init(persistence: PersistenceService = .shared) {109        self.persistence = persistence110        let stored = persistence.load(Stored.self, from: Self.fileName) ?? Stored()111        themeMode = stored.themeMode112        accent = stored.accent113        fontSize = stored.fontSize114    }115116    /// Current accent color resolved for the active appearance.117    var accentColor: Color {118        let (light, dark) = accent.accentHex119        return ZyquoColor.dynamic(light: light, dark: dark)120    }121122    /// Current subtle accent tint resolved for the active appearance.123    var accentSubtleColor: Color {124        let (light, dark) = accent.subtleHex125        return ZyquoColor.dynamic(light: light, dark: dark)126    }127128    private func save() {129        persistence.save(130            Stored(themeMode: themeMode, accent: accent, fontSize: fontSize),131            to: Self.fileName132        )133    }134}135