Record Images
UI for attaching images to a time record and viewing them. A record carries a list of image filenames (TimeRecord.images); the image bytes live on disk alongside the record’s month directory.
Source: Minuta/Sources/Views/Components/RecordImageViews.swift.
Data model
TimeRecord.images is an array of filenames, not bytes. The files are stored in the record’s day directory under the Automerge records/ tree and resolved through the storage service (storage.loadImage(filename:for:) / saveImage / deleteImage). See TimeRecord.
Components
ImageGalleryView
A horizontal scroll of 60×60 thumbnails for record.images. Thumbnails load lazily in loadImages() via appState.storage.loadImage(...); images that fail to load are skipped and logged. Each thumbnail:
- taps to open the record’s images in Quick Look, starting at the tapped one,
- offers a context menu with View (the same), a
ShareLink, and (whenonDeleteis provided) Delete. Delete lives only here: the viewer has no delete button.
The gallery keeps each loaded image’s stored bytes (loadedData) next to the decoded thumbnail, so opening the viewer reads nothing from storage.
onDelete is an optional (Int) -> Void callback keyed by index, so the gallery is reusable in both read-only and editable contexts.
Quick Look
QuickLookPresenter (Minuta/Sources/Views/Components/QuickLookPreview.swift) shows files in the system viewer, QLPreviewController:
- Mac Catalyst: the Quick Look panel (
QLPreviewPanel) over the dimmed app window, with zoom (pinch, Cmd +/-), pan, full screen, Share and Open with Preview. Arrow keys page between items; Esc or Space closes it. - iPhone, iPad: full screen, pinch and double-tap zoom, swipe between items, swipe down or Done to close, Share.
present(files:at:) writes the files into a temporary folder of its own (tmp/QuickLook/<UUID>/), never pointing the viewer at the storage folder, and removes it when the viewer closes (previewControllerDidDismiss). It presents from the topmost view controller of the main window (the record editor or export sheet), found through UIApplication.mainWindowScene on the Mac. Record images are named Image N.<ext> by their place in the record, so the viewer never shows the internal {recordId}_{uuid} names; the title is the name without its extension. The same presenter opens PDF and PNG export previews (Share Export Sheet).
On iOS the controller is presented .overFullScreen: a full-screen presentation from a sheet makes the window’s root the presenter, and Quick Look’s close button then dismissed the sheet under it too. An image shows on black with the bars hidden a moment after it opens; a tap toggles them.
UI tests find it through MinutaUITests.quickLook (on the Mac a window titled “Quick Look”, the image a scroll view Image Preview: Image 1.jpg; on iOS the view QLPreviewControllerView, the title as the navigation bar’s identifier, the close button QLOverlayDoneButtonAccessibilityIdentifier), waitForQuickLook(showing:) and closeQuickLook() (Esc on the Mac; on iOS a tap brings the bars back first).
ImagePickerView
A UIViewControllerRepresentable wrapping PHPickerViewController (single image, images-only) for picking from the photo library. On Mac Catalyst, DocumentImagePickerView wraps UIDocumentPickerViewController instead, handling the security-scoped resource so a file can be picked from the filesystem.
ImageUtilities
Before an image is stored it is normalized:
static let maxDimension: CGFloat = 1920
static let compressionQuality: CGFloat = 0.8 processImage(_:) resizes (preserving aspect ratio) so the longest side is at most 1920px, then encodes JPEG at 0.8 quality. This keeps attachments small without a separate thumbnail pipeline.
Editing flow
The picker and gallery are presented from the record editor, which owns the add/remove actions and persists the updated images list on save.
Report embedding
The same on-disk images can be embedded into exported reports. The SVG report renderer inlines up to a few images as data: URIs (the CLI’s export report --with-images). See SVG report renderer.
Related
- TimeRecord — the
imagesfield and on-disk layout. - Record editor — where images are attached and removed.
- SVG report renderer — embedding images in reports.