TimeRecord Model

Definition

struct TimeRecord: Identifiable, Codable {
    let id: UUID
    var startTime: Date
    var endTime: Date?      // nil = running timer
    var tagId: UUID?        // nil = untagged
    var comment: String?
    var images: [String]    // filenames of attached images
}

Storage Format

Each record is persisted as a binary Automerge document (CRDT), one file per record. The fields above map directly to Automerge map keys, so concurrent edits from multiple devices merge automatically (last-writer-wins per field). Legacy JSON records (.json) are read-only and auto-migrated to Automerge.

Conceptually, a record holds:

id        : uuid
startTime : 2024-01-15T09:30:00Z
endTime   : 2024-01-15T11:45:00Z   // absent = running timer
tagId     : uuid                   // absent = untagged
comment   : "Working on feature X"
images    : ["uuid-string_0.jpg", "uuid-string_1.jpg"]

Note: The images field is optional for backward compatibility. Old records without the field decode with an empty array.

File Naming

YYYY-MM-DD_HHmmss.automerge based on startTime

Storage Path

records/YYYY/MM/YYYY-MM-DD_HHmmss.automerge

Business Rules

  • Multiple concurrent running timers allowed
  • Overlapping records are valid
  • tagId is optional (untagged records valid)
  • endTime is optional (nil = running)
  • images array is optional (old records without images field decode with empty array)

Image Storage

Images are stored in the same directory as the record document:

  • Path: records/YYYY/MM/ (same as the record’s .automerge file)
  • Naming: {record-id}_{index}.{ext}
    • Example: 550e8400-e29b-41d4-a716-446655440000_0.jpg
  • Supported formats: JPEG, PNG, HEIF
  • Processing: Resized to max 1920px width/height, compressed to 80% JPEG quality
  • When a record is deleted, all its images are automatically deleted

Related

Data

  • 201-tag - Tag reference for categorization

Services

UI

Reports