diff --git a/TextGrabber2/Sources/App/App+Detection.swift b/TextGrabber2/Sources/App/App+Detection.swift index 0ea7ff5..8f3ef04 100644 --- a/TextGrabber2/Sources/App/App+Detection.swift +++ b/TextGrabber2/Sources/App/App+Detection.swift @@ -10,7 +10,7 @@ import AppKit // MARK: - Text Detecting extension App { - func startDetection(userInitiated: Bool = false) async { + func startDetection(userInitiated: Bool = false, allowingFileAccess: Bool = true) async { let newCount = NSPasteboard.general.changeCount if silentDetectCount == newCount { if userInitiated { @@ -21,6 +21,7 @@ extension App { return } + silentDetectCount = nil currentResult = nil clearMenuItems() @@ -32,11 +33,17 @@ extension App { copyAllQuickItem.isHidden = true copyAllMenuItem.isEnabled = false - let image = NSPasteboard.general.image?.cgImage + let containsFiles = NSPasteboard.general.containsFileReferences + let image = NSPasteboard.general.image(allowingFileAccess: allowingFileAccess)?.cgImage let text = NSPasteboard.general.string if let result = await Recognizer.detect(image: image, level: .accurate) { updateResult(result, textCopied: text, in: mainMenu) + + // Skipped or unreadable files must remain eligible for an explicit retry + if !containsFiles || image != nil { + silentDetectCount = newCount + } } else { Logger.log(.error, "Failed to detect text from image") } @@ -45,8 +52,7 @@ extension App { Logger.log(.debug, "Presenting newly detected results") presentMainMenu() } else { - Logger.log(.debug, "Silently detected and cached") - silentDetectCount = newCount + Logger.log(.debug, "Silent detection finished") } } } @@ -71,6 +77,7 @@ private extension App { return } + clearMenuItems() currentResult = resultData translateItem.isEnabled = resultData.candidates.hasValue quickLookItem.isEnabled = resultData.candidates.hasValue diff --git a/TextGrabber2/Sources/App/App+Observer.swift b/TextGrabber2/Sources/App/App+Observer.swift index fa05ba8..69df8ce 100644 --- a/TextGrabber2/Sources/App/App+Observer.swift +++ b/TextGrabber2/Sources/App/App+Observer.swift @@ -26,7 +26,7 @@ extension App { ContentFilters.processRules(for: pasteboard) Task { - await self?.startDetection() + await self?.startDetection(allowingFileAccess: false) } } diff --git a/TextGrabber2/Sources/App/App.swift b/TextGrabber2/Sources/App/App.swift index cd5506a..dd65b35 100644 --- a/TextGrabber2/Sources/App/App.swift +++ b/TextGrabber2/Sources/App/App.swift @@ -14,7 +14,7 @@ final class App: NSObject, NSApplicationDelegate { var copyObserver: Task? var currentResult: Recognizer.ResultData? var previewingFileURL: URL? - var silentDetectCount = 0 + var silentDetectCount: Int? lazy var statusItem: NSStatusItem = { let item = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength) diff --git a/TextGrabber2/Sources/Extensions/NSPasteboard+Extension.swift b/TextGrabber2/Sources/Extensions/NSPasteboard+Extension.swift index 7704ee7..c7f8854 100644 --- a/TextGrabber2/Sources/Extensions/NSPasteboard+Extension.swift +++ b/TextGrabber2/Sources/Extensions/NSPasteboard+Extension.swift @@ -27,6 +27,21 @@ extension NSPasteboard { } var image: NSImage? { + image(allowingFileAccess: true) + } + + var containsFileReferences: Bool { + types?.contains { + $0 == .fileURL || $0.rawValue == "NSFilenamesPboardType" + } ?? false + } + + func image(allowingFileAccess: Bool) -> NSImage? { + // Check types before requesting data or letting AppKit resolve file references + if !allowingFileAccess && containsFileReferences { + return nil + } + // Copied file if let data = data(forType: .fileURL), let string = String(data: data, encoding: .utf8),