diff --git a/VoiceInk.xcodeproj/project.pbxproj b/VoiceInk.xcodeproj/project.pbxproj index 2a405a4..eebeac1 100644 --- a/VoiceInk.xcodeproj/project.pbxproj +++ b/VoiceInk.xcodeproj/project.pbxproj @@ -459,7 +459,7 @@ "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 149; + CURRENT_PROJECT_VERSION = 150; DEVELOPMENT_ASSET_PATHS = "\"VoiceInk/Preview Content\""; DEVELOPMENT_TEAM = V6J6A3VWY2; ENABLE_HARDENED_RUNTIME = YES; @@ -493,7 +493,7 @@ "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 149; + CURRENT_PROJECT_VERSION = 150; DEVELOPMENT_ASSET_PATHS = "\"VoiceInk/Preview Content\""; DEVELOPMENT_TEAM = V6J6A3VWY2; ENABLE_HARDENED_RUNTIME = YES; diff --git a/VoiceInk/PowerMode/PowerModeViewComponents.swift b/VoiceInk/PowerMode/PowerModeViewComponents.swift index 4c659e8..b0a70c1 100644 --- a/VoiceInk/PowerMode/PowerModeViewComponents.swift +++ b/VoiceInk/PowerMode/PowerModeViewComponents.swift @@ -289,29 +289,7 @@ struct ConfigurationRow: View { } .background(CardBackground(isSelected: isEditing)) .opacity(config.isEnabled ? 1.0 : 0.5) - .overlay( - Group { - if isHovering { - VStack { - HStack(spacing: 4) { - Image(systemName: "hand.tap") - .font(.system(size: 10)) - Text("Double-click to edit • Right-click for more options") - .font(.caption2) - } - .foregroundColor(.secondary) - .padding(.horizontal, 8) - .padding(.vertical, 4) - .background( - Capsule() - .fill(Color(NSColor.controlBackgroundColor).opacity(0.9)) - ) - .padding(.top, 8) - Spacer() - } - } - } - ) + .onHover { hovering in withAnimation(.easeInOut(duration: 0.15)) { isHovering = hovering diff --git a/VoiceInk/Recorder.swift b/VoiceInk/Recorder.swift index 582774f..686e010 100644 --- a/VoiceInk/Recorder.swift +++ b/VoiceInk/Recorder.swift @@ -4,7 +4,7 @@ import CoreAudio import os @MainActor -class Recorder: ObservableObject { +class Recorder: NSObject, ObservableObject, AVAudioRecorderDelegate { private var recorder: AVAudioRecorder? private let logger = Logger(subsystem: "com.prakashjoshipax.voiceink", category: "Recorder") private let deviceManager = AudioDeviceManager.shared @@ -101,6 +101,7 @@ class Recorder: ObservableObject { do { recorder = try AVAudioRecorder(url: url, settings: recordSettings) + recorder?.delegate = self recorder?.isMeteringEnabled = true if recorder?.record() == false { @@ -196,6 +197,32 @@ class Recorder: ObservableObject { audioMeter = newAudioMeter } + // MARK: - AVAudioRecorderDelegate + + func audioRecorderDidFinishRecording(_ recorder: AVAudioRecorder, successfully flag: Bool) { + if !flag { + logger.error("❌ Recording finished unsuccessfully - file may be corrupted or empty") + Task { @MainActor in + NotificationManager.shared.showNotification( + title: "Recording failed - audio file corrupted", + type: .error + ) + } + } + } + + func audioRecorderEncodeErrorDidOccur(_ recorder: AVAudioRecorder, error: Error?) { + if let error = error { + logger.error("❌ Recording encode error during session: \(error.localizedDescription)") + Task { @MainActor in + NotificationManager.shared.showNotification( + title: "Recording error: \(error.localizedDescription)", + type: .error + ) + } + } + } + deinit { if let observer = deviceObserver { NotificationCenter.default.removeObserver(observer) diff --git a/VoiceInk/Whisper/WhisperState.swift b/VoiceInk/Whisper/WhisperState.swift index f6364b6..c38e151 100644 --- a/VoiceInk/Whisper/WhisperState.swift +++ b/VoiceInk/Whisper/WhisperState.swift @@ -170,7 +170,7 @@ class WhisperState: NSObject, ObservableObject { self.recordedFile = permanentURL try await self.recorder.startRecording(toOutputFile: permanentURL) - + await MainActor.run { self.recordingState = .recording }