Backlog

For completed tasks, see 911-completed.

Priority Legend

  • [P1] - High priority: bugs, blockers, quality issues affecting UX
  • [P2] - Medium priority: improvements, polish, technical debt
  • [P3] - Low priority: future enhancements, ideas, nice-to-have

Bugs [P1]

  • Fix progressive loading not working (load more broken) (2026-01-02)

    • Location: HistoryGrid.swift:287-324 (LoadMoreTrigger)
    • Root cause: SwiftUI Grid is not lazy, so onAppear fires immediately on render, not on scroll
    • Fix: Replaced onAppear with GeometryReader viewport detection
    • Result: Grid height dropped from 3904px to 1986px (50% reduction)
    • Related: 803-load-more-bug
  • Fix cache race condition in FileStorageService (2026-01-03)

    • Location: FileStorageService.swift:384-428
    • Root cause: updateRecord() deleted old file before writing new, causing data loss on error
    • Fix: Write new file first, then delete old file, then update cache atomically
  • Fix AppState tag mutation inconsistency (2026-01-03)

    • Location: MinutaApp.swift:379-397, 447-469
    • Root cause: loadData() had optimization that skipped updates if tag count/IDs matched, missing property changes
    • Fix: Always update tags and rebuild dictionary on load; use direct assignment instead of optional chaining
  • Fix concurrent image operations (2026-01-03)

    • Location: RecordEditorViews.swift:453-509
    • Root cause: Rapid image adds could race on currentRecord
    • Fix: Added isImageOperationInProgress guard to prevent concurrent add/delete operations
  • Fix crash when opening editor for a record without tag and comment (2026-07-02)

    • Repro: start timer with no tag/comment, stop it, tap the completed record — app crashed on Mac Catalyst (SIGSEGV from an uncaught exception during sheet window creation, UINSSheetManager scene hosting path)
    • Root cause: with an empty tag input, TagSelector shows all tags, and its horizontal chips ScrollView reports its full content width (~3400pt for 34 tags) to MinWidthFittedSizing’s sizeThatFits(.unspecified) — the editor sheet took that width, blowing past the screen and crashing AppKit’s sheet-open animation. Same blowout risk applied to the image gallery.
    • Fix: replaced MinWidthFittedSizing with WidthClampedFittedSizing — clamps sheet width to 640–800pt and re-measures height at the clamped width (HistoryGrid.swift)
    • Regression test: BareRecordEditorTests.testOpenEditorForBareRecord with new bare-record fixture (34 tags + one untagged, comment-less, 11-second record); asserts the editor opens and Save/Cancel stay hittable. Passes on iPhone sim and Mac Catalyst.
  • UI test runs overwrite the CLI storage pointer (2026-07-03)

    • The app wrote storage-path.txt on every launch, including test launches with --test-storage=, so running UI tests pointed the CLI at a (soon deleted) temp dir until the real app was launched again
    • Only Mac Catalyst runs clobbered the real pointer: the unsandboxed Catalyst app resolves getpwuid() to the real home, while simulator processes resolve it inside the simulator container
    • Fix: publishResolvedStoragePath() skips the pointer write when isTestMode (StorageLocationManager.swift); the .minuta-version marker is still written into the test folder
    • Regression test: StoragePointerTests.testTestStorageLaunchDoesNotOverwriteCLIPointer — captures the pointer before launch, asserts it is unchanged after a --test-storage= launch. Verified failing before the fix (Catalyst) and passing after (Catalyst + iPhone sim)
  • Debug OKLCH colors

    • Investigate hue distribution across alphabets
    • Check color consistency between views
  • Fix view of tags in edit form

    • Tags display incorrectly in RecordEditor
    • Location: RecordEditorViews.swift
  • Fix play button position when keyboard is undocked

    • Floating button positioned wrong when iPad keyboard is undocked/floating
  • Fix window not hiding immediately on app close (Mac)

    • Window stays visible briefly after closing the app
    • Should hide immediately when user closes
  • Investigate empty block above running timers (2026-03-24)

    • Unexplained empty space appearing over working timers section
    • Need to identify the cause and remove

Test Coverage [P1]

Current: 458 unit tests passing (Shared package, 2026-07-03) + 9 app unit tests (MinutaTests target)

  • Add unit tests for StorageLocationManager (2026-07-03)

    • Wired the previously dormant Minuta/Tests/ into a new MinutaTests unit-test target (hosted in the app, project.yml), added to the scheme and a CI step
    • Added injection seams to StorageLocationManager.init (UserDefaults suite, launch arguments, StoragePathPublishing) — production defaults unchanged
    • 8 tests: --test-storage= parsing (valid/empty), default-location fallback, failed-bookmark-restore reset, useDefaultLocation bookkeeping, publish rules (test mode skips the CLI pointer but writes the marker), accessibility checks
    • Security-scoped bookmark round-trips intentionally untested — those APIs don’t work meaningfully in a unit-test host; covered by real app usage
  • Consolidate UI tests into multi-check journeys per app launch (2026-07-09, done 2026-07-10)

    • Problem: 52 UI test methods, each relaunching the app in setUpWithError (launch + fixture copy + settle is the dominant cost; full suite ~25 min per platform, run twice for both platforms)
    • Done: merged compatible checks into journey methods, one per flow — Filter 10→2, RecordEditing 6→2, Settings 5→2, ReportFlow 7→1, ReportWithImages 3→1, TagManagement 3→1, TimerFlow 2→1, DocumentPicker 2→1, UpdatePrompt 5→4 (the two default-launch checks merged; the relaunch-based ones own their launch by design). 52 methods → 23.
    • Each scenario runs inside XCTContext.runActivity for readable failure reports; scenarios restore the state they change (filter selections, calendar presets, sheet dismissal) so later scenarios start from the shared baseline
    • Kept isolated (own their launch): UpdatePromptTests relaunch tests (launch-time pill/defaults state), StoragePointerTests (launch-time pointer behavior), PerformanceTests (measure blocks), BareRecordEditorTests (dedicated fixture), and testEditSheetMinWidthOnMac (leaves the editor sheet open for measurement)
    • All snapshot() names preserved — the website gallery (TestClass--name.png) gets the same file set
    • Verified green on both platforms 2026-07-10 (Mac Catalyst 24/24, iPhone sim all passing)
  • Add UI test for image attachment (2026-07-12)

    • ImageAttachmentTests.testAddImageToRecord (empty fixture): creates a record via the timer, opens its editor, taps Add Photo, picks a stock photo through PHPicker, asserts the editor’s Images gallery appears, saves, asserts the thumbnail shows in the record row; snapshots each step
    • PHPicker automation notes (it is a remote view controller, but its elements are exposed through the host app’s a11y tree): grid cells are Images with identifier PXGGridLayout-Info and labels like Photo, 30 March 2018, 12:14 — match “Photo,” with the comma to skip the Photos-app icon in the privacy banner; cells report not-hittable, so tap via coordinate; first library load on a fresh simulator can exceed 20s
    • Query evaluation around the picker can take ~30s per attempt, so waits are 60s; enumerating the standard fixture’s history alongside the picker’s remote hierarchy made queries time out entirely, hence the empty fixture
    • Record-row thumbnails (CompactImagePreview, HistoryGrid.swift) now carry a recordRowImageThumbnail accessibility identifier for the row assert
    • iPhone sim only: on Catalyst, Add Photo opens the machine’s real Photos library (not isolated, not reliably populated), so the test body is #if !targetEnvironment(macCatalyst)
  • Fix flaky launch wait in DocumentPickerStabilityTests under full-suite load (observed 2026-07-13, fixed 2026-07-14: bumped to 15s)

    • Failed in the first fully unattended Catalyst full-suite run (after DevToolsSecurity -enable + automationmodetool enable-automationmode-without-authentication); passes rerun in isolation (409s wall time) — load-related timing flake, not a regression
    • DocumentPickerStabilityTests.swift:23: openBrowseFilesButton() asserts floatingPlayButton.waitForExistence(timeout: 5) right after launch. The 5s budget covers Catalyst app launch, fixture-to-Automerge conversion, initial data load, and the runner’s first accessibility snapshot — fine on an idle machine, but marginal mid-suite when everything crawls (the failure surfaced at t=26s because even the query evaluation runs slow under load; it still resolved to “not found” within the app’s slow startup). Bump to 15s like other launch waits.
  • Fix flaky PNG/SVG preview waits in ReportFlowTests under full-suite load (observed 2026-07-13, fixed 2026-07-14: both scenarios use tapPreviewAndWaitForDone(timeout: 60))

    • Failed in the same full-suite run as the DocumentPickerStabilityTests flake above; passes rerun in isolation (522s wall time) — load-related timing flake, not a regression
    • ReportFlowTests.swift:90 “PNG preview should open”: PNG (and SVG, line 71) previews wait only 15s for the Done button. PDF hit the same flake on 2026-05-14 and got 60s + a one-shot retry via tapPreviewAndWaitForDone(timeout: 60); extend that helper to the PNG and SVG scenarios (CSV at 5s has never flaked — no WebKit render pass).
  • Add App Intents/Shortcuts integration tests

    • StartTimerIntent, StopTimerIntent have no coverage
    • Location: AppIntents.swift
  • Add view tests for multiplatform project

    • Location: Minuta/Tests/ (unit-test target MinutaTests exists since 2026-07-03)
    • ViewInspector for SwiftUI testing
    • ContentView, SettingsSheet tests
  • SVGReportRenderer internal-method coverage via public API (2026-05-14)

    • Added 6 tests exercising truncateText (long tag names in legend, short names not truncated) and wrapText/wrapTextWithNewlines (long comments, explicit newlines, single overlong word, empty comment) through the public SVGReportRenderer.render(_:) entry point. renderTimeSeriesChart already covered by testSVGRendererIncludesTimeSeriesChart.
    • Result: Shared package tests now 450 (was 444).

Audit Findings (2026-01-03) [P1]

Dead Code:

  • Remove unused dayCellSize constant (2026-01-03)

    • Removed from CalendarDatePicker.swift
  • Remove unused UIKit import from ContentView (2026-01-03)

    • Location: ContentView.swift:2
    • No UIKit types used in file
  • Remove unused UIKit import from SettingsSheet (2026-01-03)

    • False positive: UIPasteboard is used for copy-to-clipboard functionality
  • Remove unused DateRangePickerView.swift (2026-01-03)

    • Location: Views/History/DateRangePickerView.swift
    • 114-line file never referenced - CalendarDatePicker is used instead
  • Remove unused Color(oklch:) initializer (2026-01-03)

    • Location: Color+Hex.swift:30-40
    • Never called; only Color(hex:) is used
    • Also removed unused contrastingTextColor(for: OKLCH) overload

Maintainability:

  • Extract layout constants in CalendarDatePicker (2026-01-03)

    • Location: CalendarDatePicker.swift:75-91
    • Created LayoutMetrics enum with named constants for cellSize, fonts, padding, opacity, etc.
  • Refactor duplicated date range logic in CalendarDatePicker (2026-01-03)

    • Added endOfDay() and setDateRange(from:to:) helper methods
    • Reduced duplicate “add day, subtract second” pattern from 5 occurrences to 1
  • Clean up verbose comments in CalendarDatePicker (2026-01-03)

    • Removed redundant section labels and implementation detail comments
    • Kept useful explanatory comments (corner radius logic, week selection criteria)

UI Tests:

  • UI test failures - History section not found, Export sheet not opening, Missing fixture data, Tag management flow (2026-05-14)

    • The “16 failures” list from 2026-04-15 is stale; targeted re-run on 2026-05-14 (iPhone 17 sim, iOS 18) showed all those tests passing.
  • Fix PDF preview flake on standard fixture (2026-05-14)

    • Affected: ReportFlowTests.testPDFReportPreview, ReportFlowTests.testExportSheetStaysDuringGeneration
    • Root cause: Done button wait used timeout: 15, but PDF generation through WKWebView.pdf() over the 138-record standard fixture runs ~16–60s on the iOS 18 simulator with high variance (PNG/SVG/CSV complete in ~5–10s; PDF-with-images on a 2-record fixture is unaffected).
    • Fix: bumped both Done-button waits to timeout: 60 and added a one-shot retry path (tapPreviewAndWaitForDone(timeout:) helper) that dismisses any “Export Error” alert and re-taps previewButton. Verified: when the first 60s wait misses, the retry succeeds (134s total).
  • Mac Catalyst UI tests: pre-existing failures (discovered 2026-05-14; resolved 2026-07-10)

    • Update 2026-07-10: full Catalyst suite is green (24/24 including all PerformanceTests) after the journey consolidation plus two Catalyst-specific fixes: historyDurationRecord() regex-matches labels in Swift instead of an NSPredicate MATCHES query (numeric labels in the Catalyst a11y snapshot made the predicate throw NSInvalidArgumentException), and the settings sheet is closed via its Done button (swipe-down on the main window does not dismiss a Catalyst sheet). Test launches also pin the window to 783x971pt (SceneDelegate sizeRestrictions) so runs no longer inherit the user’s last window size.
    • Update 2026-07-03: fixes for clusters 1 and 2 have landed since the re-run — toolbar bar buttons now carry accessibilityIdentifiers plus identifier-or-label test predicates (b4df387), section headers matched case-insensitively via sectionHeader(), coordinate scrolls window-anchored via dragScroll(), document picker flakes and the TCC prompt fixed (db416f2, a00971f). Needs a fresh full Catalyst run to confirm what remains — likely just cluster 4 (PerformanceTests).
    • Update 2026-07-02: cluster 3 below is resolved — testEditSheetMinWidthOnMac passes since WidthClampedFittedSizing (commit 58c27bf). Remaining failures are clusters 1, 2, 4 plus DocumentPickerStabilityTests (2) and TagSelectorTests.testTappingChipCommitsFullNameToField; testViewExistingRecords fails with a coordinate-scroll NSInternalInconsistencyException (point.x != INFINITY).
    • Failures cluster around:
      1. Catalyst NSToolbar a11y missingapp.buttons["settingsButton"] / ["pinButton"] not found. The SwiftUI toolbar (ContentView.swift:62-73) is wrapped in #if !targetEnvironment(macCatalyst); the Catalyst replacement is SceneDelegate’s NSToolbar (MinutaApp.swift:140+) which sets item.label = "Settings"/“Pin” but never sets an accessibility identifier. Tests must query app.buttons["Settings"]/["Pin"] on Catalyst — or NSToolbarItems should expose the identifiers used by the iOS path.
      2. “Running” header not found after floatingPlayButton.tap() on Mac — affects RecordEditingTests.testAddCommentToTimer, testTapToEditRecord, TimerFlowTests.testStartAndStopTimer, etc. Needs screenshot inspection during failure (Mac may render the running section differently or below the fold).
      3. testEditSheetMinWidthOnMac regressed (611pt vs 640pt min) — resolved 2026-07-02 by WidthClampedFittedSizing (640–800pt clamp with height re-measure, commit 58c27bf); passes in the full-suite run.
      4. 3 PerformanceTests fail at ~24s (testExportSheetPerformance, testReportGenerationPerformance, testTagFilterPerformance) — needs investigation.
    • Note: iPhone-sim tests pass for the same flows because iOS uses the SwiftUI toolbar path which does have identifiers wired up. The Mac CI lane has likely been red since the 7992a0a add mac pin button (wip - not working) commit.

Code Quality [P2]

  • Remove debug cellBorder() from HistoryGrid (already done)

    • Location: HistoryGrid.swift:54-75
    • Already wrapped in #if DEBUG - no borders shown in release builds
  • Split AppState into smaller services

    • Location: MinutaApp.swift (320+ lines)
    • Handles: state, tags, records, deletion undo, tag merging
    • Consider: TagManagementService, RecordManagementService
  • Split RecordEditorViews

    • Location: RecordEditorViews.swift:1-450 (450+ lines)
    • Mixed concerns: display, editing, formatting, image operations
    • Extract image handling to separate component
  • Consolidate history reload triggers (2026-01-03)

    • Merged two .task modifiers into one for initial load
    • Added comments explaining each reload trigger
    • Location: ContentView.swift:113-126

Performance [P2]

  • Layout optimization (2026-07-12)

    • Covered by the HistoryGrid work below: row-per-month restructure (List cell reuse), sticky labels via onGeometryChange, removed dead DEBUG debugInfo state that re-evaluated the whole page every scroll frame
    • Scroll confirmed smooth after the fixes; further ideas tracked in the HistoryGrid entry below
  • Improve HistoryGrid performance (2026-07-09)

    • Root cause of year-range slowness: the whole grid was a single List row containing eager nested Grids, so a year selection built every record view upfront (the progressive-loading LoadMoreTrigger had been removed by then)
    • Fix: HistoryGrid restructured into HistoryMonthRow — HistorySection emits one List row per month, so the List’s native cell reuse only builds on-screen months. Columns still align across months via fixed HistoryColumnSizes widths. Editor sheet moved into the month row owning the edited record.
    • Grid geometry reporting (GridDebugPreferenceKey and friends) kept in release — part of the design, not debug-only
    • Follow-up (scroll felt janky after the restructure): sticky labels moved from GeometryReader+onChange to onGeometryChange; removed dead DEBUG debugInfo state in ContentView that re-evaluated the whole page on every scroll frame (its preference emission in the grid stays — part of the design)
    • Remaining ideas if profiling still shows hotspots: one List row per day instead of per month (bounds cell-build cost — the likely fix if big months still hitch on scroll-in), incremental re-grouping on single-record edits (updateFilteredRecords() re-groups everything), debounce filter changes
    • Related: 801-optimization-plan Phase 6, 802-scroll-profiling, 803-load-more-bug
  • Investigate slow year report generation

    • Year range report is noticeably slow (about 500 records)
    • Profile SVGReportRenderer and WebKitReportService
  • Add pagination for large record sets

    • Location: FileStorageService.swift:218-256
    • All records loaded into memory - problematic for years of data

UI/UX Improvements [P2]

  • Add 300ms debounce to record saving while editing (2026-06-01)
    • Already implemented for running timers: scheduleSave() cancels the prior saveTask and sleeps 300ms before saveChanges(createTag: false) (RecordEditorViews.swift:412-419), wired to tagInput/editedComment/editedStartTime onChange handlers (:386-394). createTag: false avoids creating tags mid-typing (tags created on timer stop).
    • Completed records intentionally keep explicit Save/Cancel (Cancel discards). Extending auto-save there is a separate behavior change (Cancel/tag-creation semantics) — not part of this item.
  • Edit record sheet — remaining polish (verified done 2026-07-03)

    • Both sub-items were already implemented: TabEscapingTextView caps growth at maxHeight: 200 and switches to internal scrolling past the cap; TagSelector auto-focuses the text field in its onAppear (two runloop hops to survive Catalyst’s UIFocusSystem race)
    • Location: TabEscapingTextView.swift, TagComboBox.swift (TagSelector)
  • Better controls for date/time selection in record editor

    • Current text fields are bare; need proper date/time pickers
    • Consider inline pickers or popovers for start/end time editing
  • Keyboard accessibility

    • Arrow keys for tag navigation, Tab for all controls
    • Mac: Shift-Tab for tab character, Tab for navigation
  • Arrow key navigation in CalendarDatePicker

    • Navigate between year/month/week/day/preset buttons with arrow keys
    • Enter/Space to select
    • Location: CalendarDatePicker.swift
  • Tab focus on tag filter buttons

    • Tags in TagFilterView should be focusable via Tab key
    • Location: TagFilterView.swift
  • Highlight records spanning over 1 day

    • Records whose start and end fall on different days should be visually distinguished in the history list
  • Support drag and drop for image attachments

    • Drop images onto RecordEditor from Finder, Photos, browsers
  • Apply color theme to whole project

    • Consistent theming, design tokens, dark mode consistency
  • Save everything on Cmd+S

  • Cleanup settings from dev info

    • Settings sheet exposes developer-facing details that shouldn’t be in a user-facing screen; move them under Debug (DEBUG builds only) or remove
    • Location: SettingsSheet.swift
  • Create app logo (2026-06-01)

    • Hand-drawn stopwatch (assets/logo-source.png) over the brand gradient. Source assets/app-icon-source.svg; generated via ./scripts/generate-app-icons.sh (app icon, in-app stopwatch button, web hero). Favicon built from the same SVG.

Infrastructure [P2]

  • Fix GitHub Actions

    • Figure out good way to run and fix setup
  • Monorepo structure (turborepo or bazel)

    • Build and screenshots cached, triggered on code changes
    • Use code hash as cache key

Publishing [P2]

  • App autoupdate (done 2026-07-03)

    • Implemented as check + notify only (no in-app install, no Sparkle): UpdateCheckService fetches https://minuta.tools/downloads/version.json (prerendered at site build from project.yml), compares versions, and the toolbar pill / Settings About row link to the downloads page
    • Settings “Check for updates” toggle off by default; 7-day discovery pill ( enable updates | x ) next to the pin button, x dismisses forever; zero network before opt-in (single carve-out from the “no network requests ever” rule)
    • Gated by MANUAL_DISTRIBUTION compilation condition (Catalyst UI only; an App Store build would drop the flag — currently on in all configs since no App Store pipeline exists)
    • Covered by UpdateCheckServiceTests (unit, both platforms) and UpdatePromptTests (UI, Catalyst-only, not run in CI)
    • Docs: 315-update-check
  • Research iOS app publishing from Uzbekistan

    • App Store Connect requirements, payments, tax implications
  • Trademark protection for “Minuta”

    • Research registration in software category
    • Consider jurisdiction (US, EU, local)

macOS Enhancements [P3]

  • Minified/PiP mode (Mac only)

    • Compact floating view showing only running timers
    • Toggle via pip icon in title bar toolbar (next to pin/settings)
    • Layout: small window with just timer list + play button
    • Behavior:
      • Remembers minified state separately from main window size
      • Works with existing pin (always-on-top) feature
      • Hides History section, Today section, settings button
      • Shows: running timers, floating play button, expand button
      • Expand button (pip.exit) returns to full view
    • Implementation:
      • Add isMinified state to WindowPinManager (rename to WindowStateManager?)
      • Add pip toolbar button in SceneDelegate
      • Conditionally render sections in ContentView based on minified state
      • Animate window resize transition
      • Store minified window frame separately in UserDefaults
    • Related: pairs well with pin button for “mini always-on-top timer”
  • Menu Bar app

    • Status item showing running timers
    • Quick start/stop actions
    • Click to summon/hide always-on-top widget
  • Always-on-top widget (superseded by Minified mode above)

    • Floating window with current timer state
    • Minimal, non-intrusive design

Widgets [P3]

  • Home Screen / Desktop widgets (WidgetKit) (2026-01-05)

    • Small: running timer with stop button, or today summary with start button
    • Medium: running timer + quick start tags with interactive buttons
    • Large: running timers list with stop buttons, today summary, quick start tags grid
    • App Groups for shared data (group.tools.minuta.app)
    • Timeline provider with 1-minute updates for running timers
    • Interactive buttons using App Intents (start/stop timers)
    • Note: Widget extension disabled in build until App Groups configured in Developer portal
  • Lock Screen widgets (iOS) (2026-01-05)

    • Circular: gauge showing running timer progress or today’s hours
    • Rectangular: running timer with tag name and duration, or today summary
    • Inline: compact text showing timer or today’s total
  • Enable widget extension (requires Apple Developer portal setup)

    • Register App Group: group.tools.minuta.app
    • Register bundle ID: tools.minuta.app.widgets with App Groups capability
    • Enable App Groups for tools.minuta.app
    • Uncomment widget lines in project.yml
    • Restore App Groups entitlement in Minuta.entitlements
  • iPhone nightstand mode widget

Watch App [P3]

  • Apple Watch companion
    • Start/stop timers, show current state
    • Complication for quick access
    • WatchConnectivity for sync

Integrations [P3]

  • iCloud sync

    • Architecture is ready, needs implementation
  • Automerge for conflict-free sync

    • CRDT library (automerge-swift v0.6.1)
    • Decision: Use Automerge over custom CRDT implementation
    • See 505-automerge-migration-plan for 11-phase implementation plan
  • Deel timesheet sync

  • Toggl Track import/export

    • Import Toggl CSV exports (project/client map to tags, description to comment)
    • Export records in a format Toggl can import back
    • Follow the existing HEY import pattern (ImportServiceProtocol, CSV parsing) and CSV export
  • Google Calendar import

    • Convert calendar events to time records
  • System calendar auto-tracking

    • Auto-create records from meetings

Architecture [P3]

  • Introduce view model layer

    • Reduce direct AppState coupling from views
  • Implement repository pattern

    • Abstract storage implementation details
  • Stop storing tag colors

    • Tag colors are deterministically derived from tag names via TagColorGenerator (OKLCH-based)
    • Remove color field from the stored Tag model; compute on read instead
    • Drop migration logic that carries forward legacy stored colors
    • Location: Shared/Sources/MinutaShared/Models/Tag.swift, call sites in views
  • Create shared reports library

    • Extract report generation (SVG/PDF/PNG/CSV) into a reusable module consumable by both app and CLI
    • Location: Shared/Sources/MinutaShared/Services/ (ReportService, SVGReportRenderer, WebKitReportService)

Websites [P3]

  • Add downloads page to the website (2026-07-03)

    • Public page at /downloads/ (src/routes/downloads/) with Homebrew instructions and direct links to the release artifacts served from the same path; linked from header and footer nav
    • Versions read at prerender time from Minuta/project.yml and Shared/Sources/MinutaCLI/Minuta.swift (+page.server.js), so links track releases on each site deploy
    • Fixed deploy.sh: rsync --delete was wiping the server’s downloads/ artifacts on every site deploy (confirmed: the directory was gone on the server); added --filter='P downloads/*' and re-uploaded artifacts via scripts/upload-brew-artifacts.sh
  • Docs and promo sites

    • SvelteKit + IDS styles
    • Static generation with backlinks component
    • Prepare scaffold project with Daler

CalendarDatePicker [P3]

  • Use full month title instead of abbreviated (2026-01-03)

    • Changed from shortMonthName() (MMM) to monthName() (MMMM)
  • Round borders only for range start/end days (2026-01-03)

    • Added RoundedCorners shape for selective corner rounding
    • Start day: left corners rounded, End day: right corners rounded
    • Middle days: no corners, Single day: all corners
  • Use pressable button style for navigation (2026-01-03)

    • Added PressableButtonStyle with scale and opacity animation
    • Applied to all interactive elements
  • Use pressable button style for presets (2026-01-03)

    • Applied PressableButtonStyle to Today, Week, Month preset buttons
  • Add drag-to-select date range (2026-01-03)

    • Drag across calendar days to select a range
    • Handles dragging in both directions (forward and backward)
  • Toggle visual state for all selectable elements (2026-01-04)

    • Year/month/week labels and presets show “selected” visual when their range matches
    • Single rangeBinding(from:to:) factory method creates bindings for any date range
    • Added SelectableLabelToggleStyle, PresetToggleStyle, WeekNumberToggleStyle
    • All styles now use LayoutMetrics constants and PressableButtonStyle
  • Fix calendar control layout

    • Add folded variant (collapsed view)
    • Make year and month labels wider
    • Set default state to today (not expanded full calendar)
    • Fix overall layout alignment and spacing issues
  • Replace calendar toggler with simple button (2026-07-13)

    • Collapsed state: calendar-icon pill + underlined period label (link style), opens calendar on tap
    • Expanded state: same calendar-icon pill at top left (next to the From/To date pickers) collapses it
    • Removed FilterToggleButton; location: HistorySection.swift, CalendarDatePicker.swift
  • Keep calendar grid height stable across months (2026-07-13)

    • calendarGrid pads with empty rows up to LayoutMetrics.maxWeekRows (6), so 4- and 5-week months render as tall as 6-week ones
  • Pressable toggle style for the calendar pill button (2026-07-13)

    • Moved PressableButtonStyle to Components/PressableButtonStyle.swift (was private in CalendarDatePicker.swift) and applied it to the pill in HistorySection.swift
  • Keep the calendar button the same view across collapsed/expanded states (2026-07-13)

    • HistorySection now owns a single persistent pill button that toggles the picker; next to it the period label (collapsed) or the From/To date pickers (expanded)
    • CalendarDatePicker is grid-only now (dropped the header row and onDismiss)
  • Profile slow first open of the calendar and fix

    • First expand takes visibly long; subsequent opens are fast
    • Suspected: compact DatePicker (UIDatePicker/UICalendarView) lazy first-init cost on Catalyst; List section relayout secondary
    • Profile with Instruments (Time Profiler / SwiftUI), then fix (e.g. pre-warm a hidden DatePicker at startup or keep the picker mounted)
  • Animate calendar expand/collapse (2026-07-14)

    • Calendar grid is its own List row under if isCalendarExpanded; expand/collapse rides List’s native row insertion/removal animation (0.3s easeOut), the only row-height change List animates smoothly
    • Animating the height inside a row was a dead end: self-sizing UICollectionView cells resize discretely, so per-frame animatableData re-measure jitters (SwiftUI draws the interpolated height while the cell applies it a beat later); Apple DTS confirms there is no supported knob
    • Pill row keeps constant height (period label / date pickers swap in an always-mounted ZStack, flipped instantly via .transaction { $0.animation = nil }), so the pill never moves

TagFilterView [P3]

  • Animate tags filter expand/collapse

    • Add animation to the tag filter DisclosureGroup, similar to the calendar expand/collapse animation
    • Location: TagFilterView.swift, HistorySection.swift
  • Rework the filter overall

    • Rethink the whole filter area (calendar + tag filters + summary) — layout, interaction, visual design
    • Subsumes/relates to “Rearrange filter components” below
  • All tags unselected by default

    • Currently all tags are selected on first open; change default to no tags selected (show all records unfiltered)
  • Rearrange filter components

    • Review layout and order of CalendarDatePicker, tag filters, and summary controls
    • Improve visual hierarchy and discoverability
  • Add drag-to-select for tag filters

    • Press and drag across tags to toggle selection
    • Similar to calendar drag-to-select behavior

Inbox

Unsorted ideas. Review periodically.

  • Play button background gradient in OKLCH with hours marks
  • Logo: play button with gradient and hours/minutes marks, 3D watch concept
  • Sort new timers consistent with file system
  • Heatmap view for records (monthly calendar visualization)
  • Auto start/stop based on app activity (macOS, accessibility permissions)
  • Better timeline navigation (swipe gestures, scrubber)
  • Native Apple Charts in history view (iOS 16+ / macOS 13+)
  • Not sure if we need to show Today section when it falls within the selected date span — experiment with hiding it
  • Record a short demo video of real usage to show people after installation (user feedback); could live on the website / downloads page

Related