mirror of
https://github.com/BigMarc/grammargem.git
synced 2026-10-13 17:01:55 +02:00
- Added LiveMonitor class to scan text areas in the frontmost window for grammar and spelling issues. - Introduced DetectedField struct to represent text areas with issues, including suggestions for corrections. - Implemented functionality to apply corrections and dismiss suggestions. - Added Permissions class to manage Accessibility permissions required for monitoring. - Created SnippetStore for managing text-expansion snippets, including persistence to UserDefaults. - Developed TextCapture class to read and write text from the frontmost application using Accessibility API and clipboard fallback. - Implemented TextFieldScanner to traverse the Accessibility tree and collect editable text elements. - Added TextReplacementCoordinator to orchestrate the text correction process. - Integrated UpdaterManager for handling application updates using Sparkle. - Created comprehensive unit tests for various components, including grammar corrections and entitlement checks.
70 lines
3.1 KiB
Swift
70 lines
3.1 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") } }
|
|
|
|
@Published var fixHotkey: HotkeyCombo { didSet { persist(fixHotkey, "pref.fixHotkey") } }
|
|
@Published var askHotkey: HotkeyCombo { didSet { persist(askHotkey, "pref.askHotkey") } }
|
|
|
|
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
|
|
fixHotkey = Preferences.load("pref.fixHotkey") ?? .defaultFix
|
|
askHotkey = Preferences.load("pref.askHotkey") ?? .defaultAsk
|
|
}
|
|
|
|
private func persist(_ combo: HotkeyCombo, _ key: String) {
|
|
if let raw = try? JSONEncoder().encode(combo) { d.set(raw, forKey: key) }
|
|
}
|
|
|
|
private static func load(_ key: String) -> HotkeyCombo? {
|
|
guard let raw = UserDefaults.standard.data(forKey: key) else { return nil }
|
|
return try? JSONDecoder().decode(HotkeyCombo.self, from: raw)
|
|
}
|
|
|
|
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)
|
|
}
|
|
}
|
|
}
|