import Foundation @MainActor public final class DictationController { public private(set) var state: DictationState = .idle { didSet { onStateChange?(state) } } public var onStateChange: ((DictationState) -> Void)? public var onError: ((String) -> Void)? private let recorder: AudioRecording private let transcriber: Transcriber private let cleaner: Cleaner private let inserter: TextInserting private let minimumSamples: Int /// - Parameter minimumSamples: 16 kHz samples; default is 0.3 s. public init( recorder: AudioRecording, transcriber: Transcriber, cleaner: Cleaner, inserter: TextInserting, minimumSamples: Int = 4_800 ) { self.recorder = recorder self.transcriber = transcriber self.cleaner = cleaner self.inserter = inserter self.minimumSamples = minimumSamples } public func handle(_ event: HotkeyEvent) async { switch (event, state) { case (.pressed, .idle): do { try recorder.start() state = .recording } catch { onError?(error.localizedDescription) } case (.released, .recording): let samples = recorder.stop() guard samples.count >= minimumSamples else { state = .idle return } await process(samples) default: break } } private func process(_ samples: [Float]) async { defer { state = .idle } do { state = .transcribing let raw = try await transcriber.transcribe(samples) let text = raw.trimmingCharacters(in: .whitespacesAndNewlines) guard !text.isEmpty else { return } let cleaned = try await cleaner.clean(text, context: CleaningContext()) state = .inserting try await inserter.insert(cleaned) } catch { onError?(error.localizedDescription) } } }