From 4ae536b3258d9460c66795a60790eb2625e3acd2 Mon Sep 17 00:00:00 2001 From: Project AEGIS Date: Tue, 28 Apr 2026 22:50:59 -0700 Subject: [PATCH 1/3] feat: show image pixel dimensions in preview panel MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds pixel dimensions (e.g., "1920×1080") when viewing an image in the preview slideout panel. - HistoryItemDecorator.imageDimensions: computed property that reads pixel dimensions from NSBitmapImageRep, falling back to NSImage.size - Overlay on the bottom-right of the preview image with a semi-transparent black background - Metadata row displayed alongside Application, FirstCopyTime, LastCopyTime, and NumberOfCopies Closes #1139 --- Maccy/Observables/HistoryItemDecorator.swift | 8 ++++++++ Maccy/Views/PreviewItemView.swift | 18 ++++++++++++++++++ Maccy/Views/en.lproj/PreviewItemView.strings | 1 + 3 files changed, 27 insertions(+) diff --git a/Maccy/Observables/HistoryItemDecorator.swift b/Maccy/Observables/HistoryItemDecorator.swift index 573e829d1..6681f9c38 100644 --- a/Maccy/Observables/HistoryItemDecorator.swift +++ b/Maccy/Observables/HistoryItemDecorator.swift @@ -41,6 +41,14 @@ class HistoryItemDecorator: Identifiable, Hashable, HasVisibility { var hasImage: Bool { item.image != nil } + var imageDimensions: String? { + guard let image = item.image else { return nil } + if let rep = image.representations.first as? NSBitmapImageRep { + return "\(rep.pixelsWide)×\(rep.pixelsHigh)" + } + return "\(Int(image.size.width))×\(Int(image.size.height))" + } + var previewImageGenerationTask: Task<(), Error>? var thumbnailImageGenerationTask: Task<(), Error>? var previewImage: NSImage? diff --git a/Maccy/Views/PreviewItemView.swift b/Maccy/Views/PreviewItemView.swift index 9a36e7145..af1f79a48 100644 --- a/Maccy/Views/PreviewItemView.swift +++ b/Maccy/Views/PreviewItemView.swift @@ -21,6 +21,17 @@ struct PreviewItemView: View { previewImage { Image(nsImage: image) .resizable() + .overlay(alignment: .bottomTrailing) { + if let dimensions = item.imageDimensions { + Text(dimensions) + .font(.caption2) + .foregroundStyle(.white) + .padding(.horizontal, 4) + .padding(.vertical, 2) + .background(.black.opacity(0.5), in: .rect(cornerRadius: 3)) + .padding(4) + } + } } } else { previewImage { @@ -88,6 +99,13 @@ struct PreviewItemView: View { Text("NumberOfCopies", tableName: "PreviewItemView") Text(String(item.item.numberOfCopies)) } + + if let dimensions = item.imageDimensions { + HStack(spacing: 3) { + Text("ImageDimensions", tableName: "PreviewItemView") + Text(dimensions) + } + } } .controlSize(.small) } diff --git a/Maccy/Views/en.lproj/PreviewItemView.strings b/Maccy/Views/en.lproj/PreviewItemView.strings index 2c3fb5d2d..e86dc49d9 100644 --- a/Maccy/Views/en.lproj/PreviewItemView.strings +++ b/Maccy/Views/en.lproj/PreviewItemView.strings @@ -2,6 +2,7 @@ "FirstCopyTime" = "First copy time:"; "LastCopyTime" = "Last copy time:"; "NumberOfCopies" = "Number of copies:"; +"ImageDimensions" = "Dimensions:"; "PinKey" = "Press {pinKey} to pin."; "UnpinKey" = "Press {pinKey} to unpin."; "DeleteKey" = "Press {deleteKey} to delete."; From b7112ff5745698a367775bea3b56d3b79a97a453 Mon Sep 17 00:00:00 2001 From: Project AEGIS Date: Wed, 29 Apr 2026 01:22:31 -0700 Subject: [PATCH 2/3] fix: read image dimensions from pasteboard data and file URL fallback The original implementation relied on NSImage.size, which returned nil for items stored as file URLs (e.g., Finder copies). Now reads dimensions directly from the stored image data using NSBitmapImageRep, with a fallback that reads from the file URL when the raw pasteboard data doesn'\''t contain the expected image types (.tiff, .png, .jpeg, .heic). Verified working: - Direct image copies (pasteboard has image types) - File URL copies (Finder, stored as public.file-url) --- Maccy/Models/HistoryItem.swift | 11 +++++++++++ Maccy/Observables/HistoryItemDecorator.swift | 6 +----- Maccy/Views/PreviewItemView.swift | 8 ++++---- 3 files changed, 16 insertions(+), 9 deletions(-) diff --git a/Maccy/Models/HistoryItem.swift b/Maccy/Models/HistoryItem.swift index 08e7b726a..3664e4d02 100644 --- a/Maccy/Models/HistoryItem.swift +++ b/Maccy/Models/HistoryItem.swift @@ -157,6 +157,17 @@ class HistoryItem { return data } + var imageDimensionsValue: String? { + if let data = imageData, let rep = NSBitmapImageRep(data: data) { + return "\(rep.pixelsWide)×\(rep.pixelsHigh)" + } + if let url = fileURLs.first, let data = try? Data(contentsOf: url), + let rep = NSBitmapImageRep(data: data) { + return "\(rep.pixelsWide)×\(rep.pixelsHigh)" + } + return nil + } + var image: NSImage? { guard let data = imageData else { return nil diff --git a/Maccy/Observables/HistoryItemDecorator.swift b/Maccy/Observables/HistoryItemDecorator.swift index 6681f9c38..03ea97c14 100644 --- a/Maccy/Observables/HistoryItemDecorator.swift +++ b/Maccy/Observables/HistoryItemDecorator.swift @@ -42,11 +42,7 @@ class HistoryItemDecorator: Identifiable, Hashable, HasVisibility { var hasImage: Bool { item.image != nil } var imageDimensions: String? { - guard let image = item.image else { return nil } - if let rep = image.representations.first as? NSBitmapImageRep { - return "\(rep.pixelsWide)×\(rep.pixelsHigh)" - } - return "\(Int(image.size.width))×\(Int(image.size.height))" + item.imageDimensionsValue } var previewImageGenerationTask: Task<(), Error>? diff --git a/Maccy/Views/PreviewItemView.swift b/Maccy/Views/PreviewItemView.swift index af1f79a48..121088661 100644 --- a/Maccy/Views/PreviewItemView.swift +++ b/Maccy/Views/PreviewItemView.swift @@ -22,8 +22,8 @@ struct PreviewItemView: View { Image(nsImage: image) .resizable() .overlay(alignment: .bottomTrailing) { - if let dimensions = item.imageDimensions { - Text(dimensions) + if let dim = item.item.imageDimensionsValue { + Text(dim) .font(.caption2) .foregroundStyle(.white) .padding(.horizontal, 4) @@ -100,10 +100,10 @@ struct PreviewItemView: View { Text(String(item.item.numberOfCopies)) } - if let dimensions = item.imageDimensions { + if let dim = item.item.imageDimensionsValue { HStack(spacing: 3) { Text("ImageDimensions", tableName: "PreviewItemView") - Text(dimensions) + Text(dim) } } } From 44fe8a944b4fc608f4ade5376f1614ff8313a865 Mon Sep 17 00:00:00 2001 From: Project AEGIS Date: Wed, 29 Apr 2026 02:04:41 -0700 Subject: [PATCH 3/3] fix: improve OCR accuracy and display OCR text in preview panel - Changed recognition level from .fast to .accurate for better text extraction - Added OCR title display as a metadata row in the preview panel (previously the OCR text was set as the item title but never visible in the UI since image items display a thumbnail instead of text) --- Maccy/Models/HistoryItem.swift | 2 +- Maccy/Views/PreviewItemView.swift | 7 +++++++ Maccy/Views/en.lproj/PreviewItemView.strings | 1 + 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/Maccy/Models/HistoryItem.swift b/Maccy/Models/HistoryItem.swift index 3664e4d02..c7d08bd70 100644 --- a/Maccy/Models/HistoryItem.swift +++ b/Maccy/Models/HistoryItem.swift @@ -231,7 +231,7 @@ class HistoryItem { let requestHandler = VNImageRequestHandler(cgImage: cgImage) let request = VNRecognizeTextRequest(completionHandler: recognizeTextHandler) - request.recognitionLevel = .fast + request.recognitionLevel = .accurate do { try requestHandler.perform([request]) diff --git a/Maccy/Views/PreviewItemView.swift b/Maccy/Views/PreviewItemView.swift index 121088661..aee7ee881 100644 --- a/Maccy/Views/PreviewItemView.swift +++ b/Maccy/Views/PreviewItemView.swift @@ -106,6 +106,13 @@ struct PreviewItemView: View { Text(dim) } } + + if !item.item.title.isEmpty { + HStack(spacing: 3) { + Text("Title", tableName: "PreviewItemView") + Text(item.item.title) + } + } } .controlSize(.small) } diff --git a/Maccy/Views/en.lproj/PreviewItemView.strings b/Maccy/Views/en.lproj/PreviewItemView.strings index e86dc49d9..445b6279a 100644 --- a/Maccy/Views/en.lproj/PreviewItemView.strings +++ b/Maccy/Views/en.lproj/PreviewItemView.strings @@ -3,6 +3,7 @@ "LastCopyTime" = "Last copy time:"; "NumberOfCopies" = "Number of copies:"; "ImageDimensions" = "Dimensions:"; +"Title" = "Title:"; "PinKey" = "Press {pinKey} to pin."; "UnpinKey" = "Press {pinKey} to unpin."; "DeleteKey" = "Press {deleteKey} to delete.";