diff --git a/VoiceInk/ClipboardManager.swift b/VoiceInk/ClipboardManager.swift index 1f1cbfb..bc0aaa5 100644 --- a/VoiceInk/ClipboardManager.swift +++ b/VoiceInk/ClipboardManager.swift @@ -1,14 +1,20 @@ import SwiftUI +import AppKit struct ClipboardManager { - static func copyToClipboard(_ text: String) { - #if os(macOS) + enum ClipboardError: Error { + case copyFailed + case accessDenied + } + + static func copyToClipboard(_ text: String) -> Bool { let pasteboard = NSPasteboard.general pasteboard.clearContents() - pasteboard.setString(text, forType: .string) - #else - UIPasteboard.general.string = text - #endif + return pasteboard.setString(text, forType: .string) + } + + static func getClipboardContent() -> String? { + return NSPasteboard.general.string(forType: .string) } } diff --git a/VoiceInk/CursorPaster.swift b/VoiceInk/CursorPaster.swift index cd8f9ad..0ced9b0 100644 --- a/VoiceInk/CursorPaster.swift +++ b/VoiceInk/CursorPaster.swift @@ -1,7 +1,9 @@ import Foundation -import Cocoa +import AppKit class CursorPaster { + private static let pasteCompletionDelay: TimeInterval = 0.3 + static func pasteAtCursor(_ text: String) { guard AXIsProcessTrusted() else { print("Accessibility permissions not granted. Cannot paste at cursor.") @@ -33,8 +35,9 @@ class CursorPaster { vUp?.post(tap: .cghidEventTap) cmdUp?.post(tap: .cghidEventTap) - // Restore the original pasteboard contents - DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) { + // Restore the original pasteboard contents after a delay + // Use a background queue to not block the main thread + DispatchQueue.global(qos: .userInitiated).asyncAfter(deadline: .now() + pasteCompletionDelay) { if let oldContents = oldContents { pasteboard.clearContents() pasteboard.setString(oldContents, forType: .string) diff --git a/VoiceInk/Views/TranscriptionHistoryView.swift b/VoiceInk/Views/TranscriptionHistoryView.swift index 95293f6..5625348 100644 --- a/VoiceInk/Views/TranscriptionHistoryView.swift +++ b/VoiceInk/Views/TranscriptionHistoryView.swift @@ -536,9 +536,10 @@ struct TranscriptionCard: View { } private func copyToClipboard(_ text: String) { - let pasteboard = NSPasteboard.general - pasteboard.clearContents() - pasteboard.setString(text, forType: .string) + let success = ClipboardManager.copyToClipboard(text) + if !success { + print("Failed to copy text to clipboard") + } } private func formatDuration(_ duration: TimeInterval) -> String { diff --git a/VoiceInk/WhisperState.swift b/VoiceInk/WhisperState.swift index 5c36da0..cf16c4e 100644 --- a/VoiceInk/WhisperState.swift +++ b/VoiceInk/WhisperState.swift @@ -563,8 +563,13 @@ class WhisperState: NSObject, ObservableObject, AVAudioRecorderDelegate { SoundManager.shared.playStopSound() if isAutoCopyEnabled { - ClipboardManager.copyToClipboard(text) - clipboardMessage = "Transcription copied to clipboard" + let success = ClipboardManager.copyToClipboard(text) + if success { + clipboardMessage = "Transcription copied to clipboard" + } else { + clipboardMessage = "Failed to copy to clipboard" + messageLog += "Failed to copy transcription to clipboard\n" + } } if AXIsProcessTrusted() { @@ -576,11 +581,6 @@ class WhisperState: NSObject, ObservableObject, AVAudioRecorderDelegate { messageLog += "Accessibility permissions not granted. Transcription not pasted automatically.\n" } - Task { - try await Task.sleep(nanoseconds: 3_000_000_000) - clipboardMessage = "" - } - await cleanupResources() // Don't set processing states to false here @@ -628,16 +628,6 @@ class WhisperState: NSObject, ObservableObject, AVAudioRecorderDelegate { } } - private func copyToClipboard(_ text: String) { - #if os(macOS) - let pasteboard = NSPasteboard.general - pasteboard.clearContents() - pasteboard.setString(text, forType: .string) - #else - UIPasteboard.general.string = text - #endif - } - @Published var isVisualizerActive = false @Published var isMiniRecorderVisible = false {