import AppKit import Foundation import QuickLookThumbnailing /// サムネイル生成。QuickLook 任せなので HEIC や RAW もそのまま扱える。 @MainActor final class ThumbnailLoader { static let shared = ThumbnailLoader() private let cache = NSCache() private init() { cache.countLimit = 600 } /// すでに読み込み済みなら即座に返す。スクロールで戻ってきたときに /// 一瞬プレースホルダが出るのを防ぐために使う。 func cached(for url: URL, size: CGFloat) -> NSImage? { cache.object(forKey: Self.key(url, size)) } func thumbnail(for url: URL, size: CGFloat) async -> NSImage? { let key = Self.key(url, size) if let cached = cache.object(forKey: key) { return cached } let scale = NSScreen.main?.backingScaleFactor ?? 2 let request = QLThumbnailGenerator.Request( fileAt: url, size: CGSize(width: size, height: size), scale: scale, representationTypes: .thumbnail ) guard let representation = try? await QLThumbnailGenerator.shared .generateBestRepresentation(for: request) else { return nil } let image = NSImage(cgImage: representation.cgImage, size: representation.contentRect.size) cache.setObject(image, forKey: key) return image } private static func key(_ url: URL, _ size: CGFloat) -> NSString { "\(url.path)|\(Int(size))" as NSString } }