mirror of
https://github.com/FBukovina/Atoll.git
synced 2026-10-28 15:44:32 +00:00
Somewhat working file tray
This commit is contained in:
parent
4c7e79b954
commit
30422c3d95
29 changed files with 4032 additions and 65 deletions
|
|
@ -0,0 +1,98 @@
|
|||
//
|
||||
// ThumbnailService.swift
|
||||
// boringNotch
|
||||
//
|
||||
// Created by Alexander on 2025-10-07.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import AppKit
|
||||
import QuickLookThumbnailing
|
||||
import UniformTypeIdentifiers
|
||||
|
||||
actor ThumbnailService {
|
||||
static let shared = ThumbnailService()
|
||||
|
||||
private var cache: [String: NSImage] = [:]
|
||||
private var pendingRequests: [String: Task<NSImage?, Never>] = [:]
|
||||
private let thumbnailGenerator = QLThumbnailGenerator.shared
|
||||
|
||||
private init() {}
|
||||
|
||||
func thumbnail(for url: URL, size: CGSize) async -> NSImage? {
|
||||
let cacheKey = "\(url.path)_\(size.width)x\(size.height)"
|
||||
|
||||
if let cached = cache[cacheKey] {
|
||||
return cached
|
||||
}
|
||||
|
||||
if let pending = pendingRequests[cacheKey] {
|
||||
return await pending.value
|
||||
}
|
||||
|
||||
let task = Task<NSImage?, Never> {
|
||||
let thumbnail = await generateQuickLookThumbnail(for: url, size: size)
|
||||
if let thumbnail = thumbnail {
|
||||
cache[cacheKey] = thumbnail
|
||||
}
|
||||
pendingRequests[cacheKey] = nil
|
||||
return thumbnail
|
||||
}
|
||||
|
||||
pendingRequests[cacheKey] = task
|
||||
return await task.value
|
||||
}
|
||||
|
||||
func clearCache() {
|
||||
cache.removeAll()
|
||||
}
|
||||
|
||||
func clearCache(for url: URL) {
|
||||
cache = cache.filter { !$0.key.starts(with: url.path) }
|
||||
}
|
||||
|
||||
// MARK: - Private Methods
|
||||
|
||||
private func generateQuickLookThumbnail(for url: URL, size: CGSize) async -> NSImage? {
|
||||
let scale = await MainActor.run { NSScreen.main?.backingScaleFactor ?? 2.0 }
|
||||
|
||||
return await url.accessSecurityScopedResource { scopedURL in
|
||||
NSLog("🔐 ThumbnailService: obtaining security scope for \(scopedURL.path)")
|
||||
let request = QLThumbnailGenerator.Request(
|
||||
fileAt: scopedURL,
|
||||
size: size,
|
||||
scale: scale,
|
||||
representationTypes: .all
|
||||
)
|
||||
request.iconMode = true
|
||||
|
||||
return await withCheckedContinuation { (continuation: CheckedContinuation<NSImage?, Never>) in
|
||||
thumbnailGenerator.generateBestRepresentation(for: request) { representation, error in
|
||||
if let rep = representation {
|
||||
NSLog("🔍 ThumbnailService: generated thumbnail for \(scopedURL.path)")
|
||||
continuation.resume(returning: rep.nsImage)
|
||||
} else {
|
||||
if let err = error {
|
||||
NSLog("⚠️ ThumbnailService: thumbnail error for \(scopedURL.path): \(err.localizedDescription)")
|
||||
}
|
||||
continuation.resume(returning: nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Extensions
|
||||
|
||||
extension QLThumbnailRepresentation {
|
||||
var nsImage: NSImage {
|
||||
return NSImage(cgImage: self.cgImage, size: self.cgImage.size)
|
||||
}
|
||||
}
|
||||
|
||||
extension CGImage {
|
||||
var size: NSSize {
|
||||
return NSSize(width: self.width, height: self.height)
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue