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 full-screen viewer,
  • offers a context menu with View, a ShareLink, and (when onDelete is provided) Delete.

onDelete is an optional (Int) -> Void callback keyed by index, so the gallery is reusable in both read-only and editable contexts.

FullScreenImageView

A modal (NavigationStack) that shows one image fit-to-screen with pinch-to-zoom (MagnificationGesture) and drag-to-pan (DragGesture). Zooming below 1.0 snaps back. The toolbar has a Done button, a ShareLink, and an optional Delete action.

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