BigMarc a7bf45b466 Add management window; sales-focused README; FounderGem LLC
- Management app: sidebar window with Dashboard (usage insights), Devices
  (slot usage, deactivate this Mac), Writing Modes (custom + app-aware map),
  Dictionary, Snippets, Shortcuts, AI Model, Privacy, General, License, About.
- New local stores: Preferences (launch-at-login, appearance), UsageStats
  (privacy-friendly local insights), CustomModesStore, SnippetStore.
- License model now tracks device name + activation usage/limit for the
  Devices screen.
- README rewritten as a conversion-focused product page (no internal cost/setup).
- Legal entity set to FounderGem LLC across README, LICENSE, Info.plist, About.

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

56 lines
2.4 KiB
Swift

import Foundation
import AppKit
import ServiceManagement
/// User-facing app preferences, persisted to UserDefaults. Kept separate from
/// licensing/entitlements so the General settings stay simple.
@MainActor
final class Preferences: ObservableObject {
enum Appearance: String, CaseIterable, Identifiable {
case system, light, dark
var id: String { rawValue }
var label: String { rawValue.capitalized }
}
enum MenuBarStyle: String, CaseIterable, Identifiable {
case icon, monogram
var id: String { rawValue }
var label: String { self == .icon ? "Icon" : "Monogram" }
}
private let d = UserDefaults.standard
@Published var launchAtLogin: Bool { didSet { applyLaunchAtLogin() } }
@Published var playSounds: Bool { didSet { d.set(playSounds, forKey: "pref.playSounds") } }
@Published var liveUnderlines: Bool { didSet { d.set(liveUnderlines, forKey: "pref.liveUnderlines") } }
@Published var restoreClipboard: Bool { didSet { d.set(restoreClipboard, forKey: "pref.restoreClipboard") } }
@Published var appearance: Appearance { didSet { d.set(appearance.rawValue, forKey: "pref.appearance"); applyAppearance() } }
@Published var menuBarStyle: MenuBarStyle { didSet { d.set(menuBarStyle.rawValue, forKey: "pref.menuBarStyle") } }
init() {
launchAtLogin = SMAppService.mainApp.status == .enabled
playSounds = d.object(forKey: "pref.playSounds") as? Bool ?? true
liveUnderlines = d.object(forKey: "pref.liveUnderlines") as? Bool ?? true
restoreClipboard = d.object(forKey: "pref.restoreClipboard") as? Bool ?? true
appearance = Appearance(rawValue: d.string(forKey: "pref.appearance") ?? "") ?? .system
menuBarStyle = MenuBarStyle(rawValue: d.string(forKey: "pref.menuBarStyle") ?? "") ?? .icon
}
private func applyLaunchAtLogin() {
do {
if launchAtLogin { try SMAppService.mainApp.register() }
else { try SMAppService.mainApp.unregister() }
} catch {
Log.app.error("Launch-at-login change failed: \(error.localizedDescription, privacy: .public)")
}
}
private func applyAppearance() {
switch appearance {
case .system: NSApp.appearance = nil
case .light: NSApp.appearance = NSAppearance(named: .aqua)
case .dark: NSApp.appearance = NSAppearance(named: .darkAqua)
}
}
}