소스 검색

Fix scanner, premium access, and import edge cases.

Prevent stale premium cache bypass, grayscale band corruption, partial scan files, duplicate bookmarks, stacked overlays, and gate scan file import separately.

Co-authored-by: Cursor <cursoragent@cursor.com>
AhtashamShahzad1 3 주 전
부모
커밋
0c4c45dfb6

+ 16 - 4
smart_printer/ImportedFilesStore.swift

@@ -31,9 +31,18 @@ enum ImportedFilesStore {
         guard AppSettings.saveRecentFiles else { return urls.count }
 
         var bookmarks = defaults.array(forKey: bookmarksKey) as? [Data] ?? []
+        var existingPaths = Set(
+            bookmarks.compactMap { data -> String? in
+                guard case .valid(let url) = resolveBookmarkData(data) else { return nil }
+                return normalizedPath(for: url)
+            }
+        )
         var added = 0
 
         for url in urls {
+            let path = normalizedPath(for: url)
+            guard !existingPaths.contains(path) else { continue }
+
             let accessed = url.startAccessingSecurityScopedResource()
             defer {
                 if accessed { url.stopAccessingSecurityScopedResource() }
@@ -45,16 +54,19 @@ enum ImportedFilesStore {
                 relativeTo: nil
             ) else { continue }
 
-            if !bookmarks.contains(bookmark) {
-                bookmarks.append(bookmark)
-                added += 1
-            }
+            bookmarks.append(bookmark)
+            existingPaths.insert(path)
+            added += 1
         }
 
         defaults.set(bookmarks, forKey: bookmarksKey)
         return added
     }
 
+    private static func normalizedPath(for url: URL) -> String {
+        url.standardizedFileURL.path
+    }
+
     private enum BookmarkResolution {
         case valid(URL)
         case stale

+ 1 - 1
smart_printer/PaywallView.swift

@@ -456,7 +456,7 @@ enum PremiumAccess {
     static func canAccess(_ feature: AppFeature) -> Bool {
         let store = StoreManager.shared
         if store.isResolvingEntitlements {
-            return store.isPremium || FreeTierManager.canAccess(feature, isPremium: false)
+            return FreeTierManager.canAccess(feature, isPremium: false)
         }
         return FreeTierManager.canAccess(feature, isPremium: store.isPremium)
     }

+ 1 - 0
smart_printer/ScanFileView.swift

@@ -46,6 +46,7 @@ enum ScanFileService {
         case .scanner:
             startScannerScan(from: window)
         case .importFile:
+            guard PremiumAccess.require(feature: .scanImport, from: window) else { return }
             importFile(from: window)
         }
     }

+ 24 - 11
smart_printer/ScannerService.swift

@@ -586,9 +586,6 @@ extension ScannerService: ICDeviceDelegate, ICScannerDeviceDelegate {
     nonisolated func scannerDevice(_ scanner: ICScannerDevice, didScanTo url: URL) {
         Task { @MainActor in
             scannedImageURL = url
-            if let image = imageFromScanURL(url) {
-                finishScan(with: .success(image))
-            }
         }
     }
 
@@ -649,14 +646,31 @@ private extension ScanResolution {
 }
 
 private extension ScannerService {
+    struct BandImageFormat {
+        let colorSpace: CGColorSpace
+        let bitmapInfo: CGBitmapInfo
+    }
+
+    func bandImageFormat(for bandData: ICScannerBandData) -> BandImageFormat {
+        if Int(bandData.bitsPerPixel) <= 8 {
+            return BandImageFormat(
+                colorSpace: CGColorSpaceCreateDeviceGray(),
+                bitmapInfo: CGBitmapInfo(rawValue: CGImageAlphaInfo.none.rawValue)
+            )
+        }
+        return BandImageFormat(
+            colorSpace: CGColorSpaceCreateDeviceRGB(),
+            bitmapInfo: CGBitmapInfo(rawValue: CGImageAlphaInfo.noneSkipLast.rawValue)
+        )
+    }
+
     func imageFromBandData(_ bandData: ICScannerBandData) -> NSImage? {
         guard let data = bandData.dataBuffer,
               !data.isEmpty,
               bandData.fullImageWidth > 0,
               bandData.dataNumRows > 0 else { return nil }
 
-        let colorSpace = CGColorSpaceCreateDeviceRGB()
-        let bitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.noneSkipLast.rawValue)
+        let format = bandImageFormat(for: bandData)
         guard let provider = CGDataProvider(data: data as CFData),
               let cgImage = CGImage(
                   width: Int(bandData.fullImageWidth),
@@ -664,8 +678,8 @@ private extension ScannerService {
                   bitsPerComponent: Int(bandData.bitsPerComponent),
                   bitsPerPixel: Int(bandData.bitsPerPixel),
                   bytesPerRow: Int(bandData.bytesPerRow),
-                  space: colorSpace,
-                  bitmapInfo: bitmapInfo,
+                  space: format.colorSpace,
+                  bitmapInfo: format.bitmapInfo,
                   provider: provider,
                   decode: nil,
                   shouldInterpolate: true,
@@ -700,8 +714,7 @@ private extension ScannerService {
             fullData.replaceSubrange(startOffset..<(startOffset + copyLength), with: bandData.prefix(copyLength))
         }
 
-        let colorSpace = CGColorSpaceCreateDeviceRGB()
-        let bitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.noneSkipLast.rawValue)
+        let format = bandImageFormat(for: first)
         var mutableData = fullData
         return mutableData.withUnsafeMutableBytes { buffer in
             guard let baseAddress = buffer.baseAddress,
@@ -711,8 +724,8 @@ private extension ScannerService {
                       height: height,
                       bitsPerComponent: Int(first.bitsPerComponent),
                       bytesPerRow: bytesPerRow,
-                      space: colorSpace,
-                      bitmapInfo: bitmapInfo.rawValue
+                      space: format.colorSpace,
+                      bitmapInfo: format.bitmapInfo.rawValue
                   ),
                   let cgImage = context.makeImage() else {
                 return nil

+ 6 - 0
smart_printer/ViewController.swift

@@ -298,6 +298,9 @@ class ViewController: NSViewController {
         guard guardPremiumFeature(.photoPreview) else { return }
         photoPreviewOverlay?.dismiss(animated: false)
         filePreviewOverlay?.dismiss(animated: false)
+        printTextOverlay?.dismiss(animated: false)
+        printContactsOverlay?.dismiss(animated: false)
+        drawPrintOverlay?.dismiss(animated: false)
         ocrFileOverlay?.dismiss(animated: false)
         scanFileOverlay?.dismiss(animated: false)
         dismissScannerOverlay()
@@ -317,6 +320,9 @@ class ViewController: NSViewController {
         guard guardPremiumFeature(.filePreview) else { return }
         photoPreviewOverlay?.dismiss(animated: false)
         filePreviewOverlay?.dismiss(animated: false)
+        printTextOverlay?.dismiss(animated: false)
+        printContactsOverlay?.dismiss(animated: false)
+        drawPrintOverlay?.dismiss(animated: false)
         ocrFileOverlay?.dismiss(animated: false)
         scanFileOverlay?.dismiss(animated: false)
         dismissScannerOverlay()