Completed Tasks
Archive of completed backlog items.
Rename (2025-12-28)
- Rename TimeTracker to Minuta (2025-12-28)
- Renamed folders, filenames, texts throughout codebase
- Verified build works after rename
UI/UX Improvements (2025-12-17/18)
Toast notification for record deletion with undo (2025-12-17)
- Created ToastView.swift with DeletionToastView and DeletionToastContainer
- Shows at bottom of screen for 60 seconds with countdown
- Undo button restores deleted record
- Animated transitions
Fix row tap area in History table (2025-12-17)
- Changed Grid cells to HStack with fixed widths and
.contentShape(Rectangle()) - Entire row is now tappable including empty space
- Changed Grid cells to HStack with fixed widths and
Unify timer editing form (2025-12-18)
- Created unified
RecordEditorcomponent in RecordEditorViews.swift - Replaced
RunningTimerEditorandEditableRecordRowwith single component - Running timer: ticking duration (TimelineView), blue circle stop button, auto-save on edit
- Completed record: Start/End date pickers with duration display, Cancel/Save buttons
- Added time range validation (end must be after start)
- Close button (X) on top of edit form (except running timers)
- Tags created only when timer is stopped (not during auto-save)
- Running timers hidden from History section
- History refreshes automatically when records change
- Created unified
Improve tag button styling (2025-12-18)
- Tags now have colored borders matching tag color
- Fill with tag color when selected, contrasting text (black/white based on luminance)
- Removed colored dot indicators (redundant with border colors)
- Updated TagComboBox.swift, TagFilterView.swift, Color+Hex.swift
Add background to record editor in History view (2025-12-18)
- Added gray background with rounded corners to inline editor
- Visually distinguishes the expanded editor from other rows
Make “Add photo” and “Browse files” look like links (2025-12-18)
- Changed from bordered buttons to plain link-style buttons
- Uses accent color, dims when disabled
Stacked bar chart by tag (2025-12-18)
- Each day shows stacked segments colored by tag
- Duration text inside segments (with contrasting black/white)
- Total duration on top of combined bar
- Removed Y axis scale (labels inside bars instead)
- Added ReportDayTagSegment model, updated ReportService and SVGReportRenderer
Tag context menu in History filter (2025-12-18)
- Long press on tag filter buttons shows context menu
- Archive/Unarchive option (toggles based on current state)
- Delete option to remove tag
Harmony color palette for tags (2025-12-18)
- Replaced custom colors with Evil Martians’ Harmony palette (500 shade)
- 17 accessible, consistent colors: red, orange, amber, yellow, lime, green, emerald, teal, cyan, sky, blue, indigo, violet, purple, fuchsia, pink, rose
- Native Swift enum with CaseIterable for type-safe access
- Source: https://github.com/evilmartians/harmony
Foundation (Phase 0)
- Improve debugability and testability (2025-12-17)
- Added TimeTrackingServiceProtocol for dependency injection
- Added StorageLocationManagerProtocol (@MainActor)
- Created AppLogger with OSLog (storage, tracking, app, intents categories)
- Created MockTimeTrackingService actor for testing
- Added DebugStateView for runtime state inspection (DEBUG builds)
- Replaced all print() statements with structured logging
- Files: Logger.swift, MockTimeTrackingService.swift, DebugStateView.swift (new)
Phase 1: Critical Fixes (2025-12-17)
Export popup closes itself on first open
- Fixed with snapshot pattern to prevent re-renders from dismissing sheet
- HistorySection captures ExportSnapshot when button pressed
- Sheet uses snapshot data instead of live bindings
Fix CSV export escaping
- Added carriage return (\r) handling per RFC 4180
- CSVParser.escapeField now handles \r, \n, quotes, and commas
Add tagsByID dictionary to AppState
- Added
tagsByID: [UUID: Tag]dictionary for O(1) lookups - Added
tag(for:)helper method - Updated all views to use new lookup pattern
- Added
Document StorageLocationManager singleton pattern
- Pattern: singleton for production, protocol for testing
- Added StorageLocationManagerProtocol (@MainActor)
Terminal build commands documentation
- Added comprehensive commands to CLAUDE.md
- iOS Simulator, Mac Catalyst, iPad targets documented
Use ExportService instead of inline CSV
- HistorySection now uses DefaultExportService
- Removed duplicate CSV generation code
Phase 2: Records Layout Enhancement (2025-12-17)
Tap to edit records
- Removed context menu from EditableRecordRow
- Added tap gesture to open edit mode
- Tapping anywhere on record row opens editor
- Swipe-to-delete still works (handled by ForEach .onDelete)
History table layout (compact mode)
- Added
isCompactparameter to EditableRecordRow - Table layout: tag | date | duration | start — end | comment | chevron
- Horizontal layout with proper spacing (8pt between elements)
- Used in HistorySection and TodaySectionView (isCompact: true)
- Visual affordance: chevron.right icon for tap-to-edit
- Better use of horizontal space, more scannable format
- Added
Phase 3: UI Quick Fixes (2025-12-17)
Remove app title from window
- Removed
.navigationTitle("TimeTracker")from ContentView
- Removed
Fix element positions
- Reviewed layout - well-structured with proper spacing
- No issues found requiring fixes
A4 horizontal for reports
- Changed to 842x595 points (A4 landscape)
- Adjusted layout for wider format
- Updated chart and record sections for horizontal orientation
Phase 3: Testing (2025-12-17)
Add report-generator JS tests
- 53 tests for buildReport(), div(), truncate(), chart, records
- Location: report-generator/tests/
- Note: Superseded - JS replaced with pure Swift SVGReportRenderer
Add AppState integration tests
- 15 tests covering workflows, data flow, error propagation
- Location: Shared/Tests/TimeTrackerSharedTests/Integration/
Add Swift-JS integration tests
- Documented manual testing procedures in 601-testing-guide.md
- Note: Superseded - JS replaced with pure Swift SVGReportRenderer
Phase 4: Performance (2025-12-17)
Cache filtered records
- Views already use computed properties (acceptable pattern)
- No additional caching needed
Implement record caching
- Added in-memory cache to LocalFileStorageService
- Cache invalidated on save/update/delete
- Reduces disk I/O for repeated queries
Phase 5: Features (2025-12-17)
Archive tags
- Added
isArchivedproperty to Tag model - Backward-compatible decoding (defaults to false)
- Archived tags hidden from selection UI
- Archived tags greyed out in History filter
- Swipe to archive/unarchive in Settings
- Added
Delete records from Today and History
- Swipe-to-delete already existed
- Added context menu for Mac Catalyst (right-click delete)
Keyboard shortcuts (Mac)
- Cmd+N: Start new timer
- Cmd+.: Stop all running timers
- Implemented via FocusedValue and SwiftUI Commands
Avoid font bundling in reports
- Superseded by pure Swift SVG renderer (no external fonts needed)
Horizontal export is not horizontal (2025-12-17)
- JS had correct dimensions (842x595), Swift had portrait (595x842)
- Fixed WebKitReportService.swift: webview frame, PDF rect, PNG snapshot
Replace JS report rendering with pure Swift (2025-12-17)
- Location:
Shared/Sources/TimeTrackerShared/Services/SVGReportRenderer.swift - Stack: Core Text (text measurement) + manual layout + SVG string generation
- Benefits: eliminated JS bundle entirely, simpler architecture
- Deleted: report.js, report-generator/, pre-build script
- See 501-yoga-satori-investigation for analysis
- Location:
Test Coverage (2025-12-19)
- Add UI tests for report generation (2025-12-19)
- Created ReportFlowTests.swift with 7 comprehensive tests
- Tests all export formats: PDF, PNG, SVG, CSV preview
- Captures 15 baseline screenshots for visual regression
- Added accessibility identifiers: format-pdf/svg/png/csv, previewButton, shareExportButton, copyAsTextButton
- Ran xcodegen to include new test file in project
Critical Bugs (Fixed)
Fix share popup closing unexpectedly (2025-12-19)
- Share/export sheet would close immediately after opening
- Root cause:
@Statevariables in HistorySection were being reset when parent view updated - Fix: Moved
showingShareSheetandexportSnapshotstates to ContentView, added guards to skip data reloads while sheet is open - Also guards 60-second auto-refresh timer when share sheet is open
- Files: ContentView.swift, HistorySection.swift
- Added accessibility identifier
shareButtonand UI testtestShareSheetStaysOpen
Fix endlessly reopening form in history (2025-12-19)
- Form kept reopening after dismissal due to race condition
- Root cause:
onChange(of: appState.todayRecords)triggeredloadHistoryRecords()which rebuilt the ForEach whileeditingRecordIdwas still set - Fix: Moved
editingRecordIdto ContentView as binding, skip reload wheneditingHistoryRecordId != nil - Files: ContentView.swift, HistorySection.swift
- Added UI test:
testHistoryRecordEditAndDismissin RecordEditingTests
Fix Svelte SSR + Satori browser incompatibility (2025-12-17)
- Note: Superseded - entire JS stack replaced with pure Swift SVGReportRenderer
- Original fix replaced Svelte SSR with pure Satori object structure
Fix Save as file not working (2025-12-16)
- Location:
HistorySection.swift(fileExporter) - Issue: Save button does not export file
- Fix: Changed csvDocument from optional to non-optional, added onChange handler for state timing
- Location:
Fix filtered list delete index mismatch (2025-12-16)
- Location:
TodayView.swift(both platforms) - Issue:
.onDeleteuses index from filtered array but array is re-filtered - Fix: Added
completedRecordscomputed property to ensure consistent filtering
- Location:
Fix force unwrap on date calculation (2025-12-16)
- Location:
HistoryView.swift(both platforms) - Issue:
calendar.date(byAdding:)!could crash on edge cases - Fix: Changed to
guard letwith early return
- Location:
Code Review 2025-12-16
Add LocalFileStorageService tests (2025-12-16)
- Location:
Shared/Tests/TimeTrackerSharedTests/Services/LocalFileStorageServiceTests.swift - Coverage: 49 tests covering all persistence operations
- Tasks:
- Directory creation tests (2 tests)
- Tags persistence tests (8 tests - save/load/missing file/corrupt JSON)
- Record persistence tests (6 tests - save/load/update/delete)
- Error handling tests (7 tests - all FileStorageError cases)
- Edge cases (6 tests - special chars, unicode, long comments)
- Date range queries (3 tests)
- Running records (2 tests)
- Update/delete operations (7 tests)
- Concurrent access tests (2 tests)
- Full workflow integration test (1 test)
- Location:
Refactor ContentView God Object (2025-12-16)
- Result: 722 lines -> 105 lines, 10 new files created
- New structure:
Views/Components/FlowLayout.swift(43 lines)Views/Components/CSVDocument.swift(25 lines)Views/Sections/RunningTimersSection.swift(16 lines)Views/Sections/TodaySectionView.swift(48 lines)Views/History/DateRangePickerView.swift(68 lines)Views/History/TagFilterView.swift(45 lines)Views/History/HistorySection.swift(173 lines)Views/Settings/SettingsSheet.swift(220 lines)Shared/Services/ImportService.swift(118 lines)
Fix AppState concurrency issues (2025-12-16)
- Analysis: @MainActor already provides sufficient thread safety - no race conditions
- Tasks:
- Analyzed array mutations - @MainActor serializes all access, no additional sync needed
- Fix AppIntents creating separate service instances (bypasses AppState)
- Added
Notification.Name.timeTrackerDataChangedextension - AppIntents post notification after modifying data
- AppState observes notification and reloads data
- RootView reloads data on scenePhase becoming active
- Added
- Dictionary conversion not needed - arrays work fine with @MainActor isolation
Add deinit to AppState (2025-12-16)
- Location:
TimeTracker/Sources/TimeTrackerApp.swift - Issue: NotificationCenter observer not cleaned up (cleanupTimer was already handled)
- Fix: Added
dataChangedObserverproperty and cleanup in deinit
- Location:
Fix race condition in stopTimer (2025-12-16)
- Status: CLOSED - NOT AN ISSUE
- Analysis: @MainActor ensures sequential execution, actor-isolated storage serializes file I/O
- Safe patterns used (firstIndex with ID matching, optional binding)
- Design is “eventually consistent” - appropriate for local-first app
Extract duplicated code (2025-12-16)
- New utilities in Shared/Sources/TimeTrackerShared/Utilities/:
DurationFormatter.swift- 4 format styles (timer, compact, csv, paddedMinutes)ColorPalette.swift- presetColors array + randomColor()CSVParser.swift- parseLine() + escapeField()
- Added
resolveTagId(from:)method to AppState - Removed: 8 formatDuration, 5 randomColor, 3 resolveTagId, 3 presetColors, 2 CSV parsing instances
- New utilities in Shared/Sources/TimeTrackerShared/Utilities/:
Rename misleading view files (2025-12-16)
- Renamed:
TodayView.swift->NewTimerSheet.swiftSettingsView.swift->ExportView.swiftTagsView.swift->AddTagSheet.swift
- Renamed:
Remove unused code (2025-12-16)
- Tasks:
- Removed
import Combinefrom ContentView.swift - StorageLocationManager methods documented as public API (for future use)
- Removed
- Tasks:
Update docs (2025-12-16)
- Tasks:
- Fixed 904-2024-12-12-catalyst.md - removed TabView reference
- Updated 402-ios.md - new file structure, components
- Updated 000-index.md - added utilities, services, views
- Updated 101-overview.md - added StorageLocationManager, view hierarchy
- Created 305-storage-location.md - full StorageLocationManager docs
- Updated 302-export.md - added ImportService documentation
- Tasks:
Validation & Error Handling
Add time validation (2025-12-16)
- Location:
TimeRecord.swift,TimeTrackingService.swift,FileStorageService.swift - Added
TimeRecordErrorenum withendTimeBeforeStartTimecase - Added
validateTimeRange()andhasValidTimeRangeto TimeRecord - Added validation in
updateRecord()andcreateManualRecord() - Tests: 4 TimeRecord tests + 5 TimeTrackingService tests
- Location:
Add tag name validation (2025-12-16)
- Location:
Tag.swift,TimeTrackingService.swift,FileStorageService.swift - Added
TagErrorenum withemptyNamecase - Added
validateName()andhasValidNameto Tag model - Added validation in
createTag()andupdateTag() - Tests: 4 tests (empty name, whitespace-only name, update validation, valid name)
- Location:
Code Duplication
Move AppState to Shared package (2025-12-16)
- Resolved: Created multiplatform Xcode project with single AppState
- See 903-2024-12-09-multiplatform
Move Color+Hex to Shared package (2025-12-16)
- Resolved: Single implementation in multiplatform project
Test Coverage
Add LocalFileStorageService tests (2025-12-16)
- Completed: 49 tests covering entire persistence layer
Add TimeTrackingService tests (2025-12-16)
- Tag lifecycle (create, update, delete)
- Record filtering
- Timer state management
Add ExportService tests (2025-12-16)
- CSV header validation
- Escaping edge cases
- Column selection
Set up GitHub Actions for CI (2025-12-16)
- Created
.github/workflows/build.yml - Builds Shared package and TimeTracker app
- Created
Add ImportService tests (2025-12-16)
- Location:
Shared/Tests/TimeTrackerSharedTests/Services/ImportServiceTests.swift - Coverage: 21 tests covering all HEY CSV import scenarios
- Categories:
- Valid CSV Parsing (5 tests - basic, multiple records, date format, empty notes, duration ignored)
- Empty/Missing Content (4 tests - empty string, header only, empty lines, empty category)
- Malformed CSV (4 tests - fewer fields, invalid dates, mixed valid/invalid, extra fields)
- Tag Creation/Reuse (5 tests - new tags, existing tags, case insensitive, deduplication, color palette)
- Edge Cases (3 tests - quoted fields with commas, unicode, whitespace-only lines)
- Location:
Add ReportService tests (2025-12-16)
- Location:
Shared/Tests/TimeTrackerSharedTests/Services/ReportServiceTests.swift - Coverage: 37 tests covering ReportData.build() and model Codable conformance
- Categories:
- ReportData Build Tests (6 tests - empty/single/multiple/untagged/mixed records)
- Date Range Filtering Tests (4 tests - within/outside range, boundary dates)
- Tag Filtering Tests (6 tests - specific tags, multiple tags, include/exclude untagged)
- Chart Data Tests (5 tests - aggregation, sorting, top 5 limit, colors)
- Record Sorting Tests (1 test - newest first)
- Total Hours Tests (2 tests - calculation with/without filtering)
- Metadata Tests (2 tests - title, date range string)
- Record Item Tests (3 tests - data correctness, nil comment, untagged)
- Codable Tests (5 tests - all model encode/decode)
- Edge Cases (3 tests - unknown tagId, empty tags, zero/long duration)
- Location:
Phase 6: Image Attachments (2025-12-17)
- Add images to log entries (2025-12-17)
- Model: Added
images: [String]array to TimeRecord (filenames) - Storage: LocalFileStorageService has saveImage, loadImage, deleteImage, imageURL methods
- Service: TimeTrackingService has addImage, removeImage methods
- UI: RecordImageViews.swift with gallery, full-screen viewer, image pickers
- Integration: EditableRecordRow shows gallery and add photo buttons in edit mode
- Features:
- Image picker (PHPickerViewController on iOS/iPad)
- Document picker for Mac Catalyst file selection
- Full-screen image viewer with pinch-to-zoom and drag gestures
- Context menu with View, Share, Delete options
- Gallery showing thumbnails with lazy loading
- Images stored in same directory as records:
records/YYYY/MM/{record-id}_{index}.jpg - Automatic image deletion when record is deleted
- Processing: Images resized to 1920px max, compressed to 80% JPEG quality
- Backward Compatibility: Old records without images field decode with empty array
- Tests: All 196 existing tests pass, backward compatibility verified
- Files Modified:
Shared/Sources/TimeTrackerShared/Models/TimeRecord.swift- added images array + imageFilename helperShared/Sources/TimeTrackerShared/Services/FileStorageService.swift- added image protocol methodsShared/Sources/TimeTrackerShared/Services/TimeTrackingService.swift- added addImage, removeImageTimeTracker/Sources/Views/Components/RecordImageViews.swift(new) - UI componentsdocs/200-models/202-time-record.md- updated documentation
- Model: Added
Phase 7: Report Redesign (2025-12-17)
- Redesign report layout (2025-12-17)
- Chart: Time series with days on X axis, hours on Y axis (replaced tag-based bar chart)
- Columns: 3 text columns below chart with comments and images
- Models: Added ReportDayItem (date, totalHours) and ReportCommentItem (recordId, date, tag info, comment, image filenames)
- ReportData: Extended with dayItems and commentItems arrays, backward-compatible decoding
- SVGReportRenderer: Complete redesign
- Vertical bar chart for daily hours with grid lines
- Round-robin comment distribution across 3 columns
- ReportImageProvider protocol for lazy image loading
- Max 8 images embedded as base64 thumbnails (80x80px)
- Text wrapping utility for multi-line comments (4 line limit)
- Tests: 62 ReportService tests (13 new for time series, comments, backwards compatibility)
- Files Modified:
Shared/Sources/TimeTrackerShared/Models/ReportData.swift- new models, custom CodableShared/Sources/TimeTrackerShared/Services/ReportService.swift- buildDayItems, buildCommentItemsShared/Sources/TimeTrackerShared/Services/SVGReportRenderer.swift- complete rewriteShared/Tests/TimeTrackerSharedTests/Services/ReportServiceTests.swift- updated tests
Phase 8: UI Refinements (2025-12-17)
History Grid table layout (2025-12-17)
- Replaced List ForEach with SwiftUI Grid for auto-sized columns
- Removed day grouping headers (flat list sorted by date)
- System date format (adapts to locale)
- Inline editing: tap row to expand edit view within Grid
- Context menu for delete (replaces swipe-to-delete in Grid)
onRecordUpdatedcallback to refresh UI after save
Multiline comment field (2025-12-17)
- Changed TextField to TextEditor in EditableRecordRow edit mode
- Height: 60-120pt (expands with content)
- Styled with rounded border and gray background
- Scrollable when content exceeds max height
EditableRecordRow callbacks (2025-12-17)
- Added
startInEditModefor external edit state control - Added
onDismissEditcallback for edit completion - Added
onRecordUpdatedcallback with updated record
- Added
Enhancements
Satori Migration for Reports (2025-12-16)
- Note: Superseded - replaced with pure Swift SVGReportRenderer (2025-12-17)
- Original implementation used Satori for HTML-to-SVG conversion
Unified Share Button (2025-12-16)
- Replaced three export buttons with single Share button in History section
- New ShareExportSheet with format selection: PDF, SVG, PNG, CSV
- Action buttons: Preview, Save File (Mac only), Share, Copy as Text (SVG/CSV only)
- Preview uses PDFKit for PDF, UIImage for PNG, monospaced text for SVG/CSV
- Added generateSVG() and generatePNG() to WebKitReportService
- Added ExportFormat.contentType for proper UTType handling
- Location:
TimeTracker/Sources/Views/Components/ShareExportSheet.swift
Report Generator (2025-12-16)
- PDF report generation with chart visualization
- Models: ReportData, ReportChartItem, ReportRecordItem, ReportOptions
- Features: date range filtering, tag filtering, top 5 chart aggregation
- Location:
Shared/Sources/TimeTrackerShared/Services/ReportService.swift,Shared/Sources/TimeTrackerShared/Models/ReportData.swift - Documentation:
docs/300-services/306-report.md
Import from HEY (2025-12-16)
- CSV import from HEY email time tracking
- Format:
Start,End,Duration,Category,Notes - Auto-creates tags from Category field
- Located in SettingsPopup
Shortcuts Integration (2025-12-16)
- StartTimerIntent and StopTimerIntent
- Works with Apple Shortcuts app
Unified Single-View Layout (2025-12-16)
- Removed tabs, combined Today/History/Settings
- Floating play button
- Settings popup via gear icon
Tag Filters for History (2025-12-16)
- FlowLayout for wrapping buttons
- Toggle selection for multiple tags
- Total time display
Export Improvements (2025-12-16)
- Save panel for Mac (fileExporter)
- Share sheet for iOS
- CSV with date range in filename
Mac Catalyst Polish (2025-12-16)
- Hidden title bar
- Native save panel
Enhance tag filters in History (2025-12-16)
- Added text field to filter tags by name (case-insensitive search)
- Tags now sorted alphabetically by name
- Existing toggle selection behavior preserved
Low-Hanging Fruit Batch 2 (2025-12-28)
Pull to refresh (2025-12-28)
- Added
.refreshablemodifier to List in ContentView - Reloads app data and history records on pull
- Added
Cancel timer button (2025-12-28)
- Added gray X button next to stop button for running timers
- Deletes timer without saving (uses existing soft delete)
- Location: RecordEditorViews.swift
Dependency injection for ReportService (2025-12-28)
- ShareExportSheet now accepts
reportService: ReportServiceProtocolvia init - Updated ReportServiceProtocol to include imageProvider method overloads
- Improves testability
- ShareExportSheet now accepts
Optimize history filtering (2025-12-28)
- Cached filtered records in
@StatewithonChangehandlers - Added static
DurationFormatterto avoid repeated instantiation - Location: HistorySection.swift
- Cached filtered records in
Fix deletion of records (2025-12-28)
- Record deletion now works correctly
History Grid Redesign (2025-12-30)
- Smart history layout with nested grid structure (2025-12-30)
- Nested Grid structure: Year > Month > Day > Records
- Vertical text for year and full month name (rotated -90 degrees, aligned to top)
- 4x larger font (44pt) for date columns
- 2-digit day with leading zero
- Comment always on second line (removed inline option)
- Tag column flexible width (fills remaining space)
- Debug borders on cells for layout visualization
- Smart visibility: hide year/month/day columns based on date range
- Hide tag column when single tag filter active
- Files:
HistoryGrid.swift(new) - nested grid structure with YearGroup, MonthGroup, DayGroup, RecordGridRowHistoryRecordRow.swift(new) - HistoryColumnSizes constantsHistoryVisibility.swift(new) - visibility computationHistorySection.swift- simplified, uses HistoryGridTodaySectionView.swift- uses TodayGrid with RecordGridRow
Low-Hanging Fruit (2025-12-26)
Add DurationFormatter tests (2025-12-26)
- Location:
Shared/Tests/TimeTrackerSharedTests/Utilities/DurationFormatterTests.swift - 46 tests covering all 4 format methods: timerFormat, compactFormat, csvFormat, paddedMinutesFormat
- Edge cases: zero, negative, fractional seconds, boundaries, large values
- Location:
Add CSVParser tests (2025-12-26)
- Location:
Shared/Tests/TimeTrackerSharedTests/Utilities/CSVParserTests.swift - 31 tests covering parseLine() and escapeField() per RFC 4180
- Categories: basic parsing, quoted fields, empty fields, escaping, round-trip
- Location:
Replace print() with AppLogger (2025-12-26)
- Location:
RecordImageViews.swift:101 - Changed
print("Failed to load image...")toAppLogger.storage.error("loadImage: ...") - Proper structured logging for image load failures
- Location:
Extract tag lookup to shared utility (2025-12-26)
- Added
Array<Tag>.findByName(_:)andfindByName(_:excludingId:)extensions to Tag.swift - Updated 4 call sites: AppIntents.swift (1), TimeTrackerApp.swift (3)
- Eliminates duplicated case-insensitive tag lookup pattern
- Added
Fix timer cleanup in AppState (2025-12-26)
- Location:
TimeTrackerApp.swift:241 - Added
cleanupTimer?.invalidate()at start ofstartCleanupTimer() - Prevents multiple timers if method called more than once
- Location:
Standardize protocol naming convention (2025-12-26)
- Added “Protocol” suffix to all service protocols
- Renamed: FileStorageServiceProtocol, ExportServiceProtocol, ImportServiceProtocol, ReportServiceProtocol, ReportImageProviderProtocol
- Updated 8 source files and 1 mock file
Cache DateFormatter instances (2025-12-26)
- Created
DateFormatters.swiftwith 10 static cached formatters - Updated: RecordEditorViews.swift, HistorySection.swift, TimeRecord.swift, AppIntents.swift
- Performance improvement: DateFormatter creation is expensive
- Created
Swipe to dismiss deletion toast (2025-12-26)
- Added DragGesture to DeletionToastView for swipe-down dismissal
- Added
confirmDelete(_:)method to AppState - Swipe down >50pt confirms deletion and dismisses toast
- Spring animation snaps back if threshold not met
Related
- 910-backlog - Current backlog
- 901-2024-12-08-init - Project initialization
- 902-2024-12-09-editor - Editor implementation