68 lines
2.3 KiB
Swift
68 lines
2.3 KiB
Swift
import Foundation
|
|
import Observation
|
|
|
|
/// Which speech engine transcribes an utterance.
|
|
public enum SpeechEngineChoice: String, CaseIterable, Sendable {
|
|
case apple
|
|
case parakeet
|
|
|
|
public var displayName: String {
|
|
switch self {
|
|
case .apple: "Apple (streaming)"
|
|
case .parakeet: "Parakeet (batch)"
|
|
}
|
|
}
|
|
|
|
/// Apple shows text while you talk; Parakeet only resolves on release.
|
|
public var showsLiveText: Bool { self == .apple }
|
|
}
|
|
|
|
@MainActor
|
|
@Observable
|
|
public final class Settings {
|
|
public static let shared = Settings()
|
|
|
|
public var pushToTalkKey: PushToTalkKey {
|
|
didSet { defaults.set(pushToTalkKey.rawValue, forKey: Keys.pushToTalkKey) }
|
|
}
|
|
|
|
public var engine: SpeechEngineChoice {
|
|
didSet { defaults.set(engine.rawValue, forKey: Keys.engine) }
|
|
}
|
|
|
|
/// Run the cleanup pass before injecting. Off = raw engine output.
|
|
public var cleanupEnabled: Bool {
|
|
didSet { defaults.set(cleanupEnabled, forKey: Keys.cleanupEnabled) }
|
|
}
|
|
|
|
/// Use the on-device LLM for cleanup instead of the deterministic rule pass.
|
|
public var smartCleanup: Bool {
|
|
didSet { defaults.set(smartCleanup, forKey: Keys.smartCleanup) }
|
|
}
|
|
|
|
/// Play a short tick when capture starts and stops.
|
|
public var soundEnabled: Bool {
|
|
didSet { defaults.set(soundEnabled, forKey: Keys.soundEnabled) }
|
|
}
|
|
|
|
private let defaults = UserDefaults.standard
|
|
|
|
private enum Keys {
|
|
static let pushToTalkKey = "pushToTalkKey"
|
|
static let cleanupEnabled = "cleanupEnabled"
|
|
static let soundEnabled = "soundEnabled"
|
|
static let engine = "engine"
|
|
static let smartCleanup = "smartCleanup"
|
|
}
|
|
|
|
private init() {
|
|
let raw = defaults.string(forKey: Keys.pushToTalkKey) ?? PushToTalkKey.rightOption.rawValue
|
|
pushToTalkKey = PushToTalkKey(rawValue: raw) ?? .rightOption
|
|
// Apple by default: no download, no dependency, live text while speaking.
|
|
engine = SpeechEngineChoice(rawValue: defaults.string(forKey: Keys.engine) ?? "") ?? .apple
|
|
cleanupEnabled = defaults.object(forKey: Keys.cleanupEnabled) as? Bool ?? true
|
|
smartCleanup = defaults.object(forKey: Keys.smartCleanup) as? Bool ?? false
|
|
soundEnabled = defaults.object(forKey: Keys.soundEnabled) as? Bool ?? true
|
|
}
|
|
}
|