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
Gridis not lazy, soonAppearfires immediately on render, not on scroll - Fix: Replaced
onAppearwith GeometryReader viewport detection - Result: Grid height dropped from 3904px to 1986px (50% reduction)
- Related: 803-load-more-bug
- Location:
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
- Location:
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
- Location:
Fix concurrent image operations (2026-01-03)
- Location:
RecordEditorViews.swift:453-509 - Root cause: Rapid image adds could race on
currentRecord - Fix: Added
isImageOperationInProgressguard to prevent concurrent add/delete operations
- Location:
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,
UINSSheetManagerscene hosting path) - Root cause: with an empty tag input,
TagSelectorshows all tags, and its horizontal chipsScrollViewreports its full content width (~3400pt for 34 tags) toMinWidthFittedSizing’ssizeThatFits(.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
MinWidthFittedSizingwithWidthClampedFittedSizing— clamps sheet width to 640–800pt and re-measures height at the clamped width (HistoryGrid.swift) - Regression test:
BareRecordEditorTests.testOpenEditorForBareRecordwith newbare-recordfixture (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.
- 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,
UI test runs overwrite the CLI storage pointer (2026-07-03)
- The app wrote
storage-path.txton 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 whenisTestMode(StorageLocationManager.swift); the.minuta-versionmarker 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)
- The app wrote
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 newMinutaTestsunit-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,useDefaultLocationbookkeeping, 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
- Wired the previously dormant
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.runActivityfor 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 (
measureblocks), BareRecordEditorTests (dedicated fixture), andtestEditSheetMinWidthOnMac(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)
- Problem: 52 UI test methods, each relaunching the app in
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-Infoand labels likePhoto, 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 arecordRowImageThumbnailaccessibility 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()assertsfloatingPlayButton.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.
- Failed in the first fully unattended Catalyst full-suite run (after
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 viatapPreviewAndWaitForDone(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 targetMinutaTestsexists since 2026-07-03) - ViewInspector for SwiftUI testing
- ContentView, SettingsSheet tests
- Location:
SVGReportRenderer internal-method coverage via public API (2026-05-14)
- Added 6 tests exercising
truncateText(long tag names in legend, short names not truncated) andwrapText/wrapTextWithNewlines(long comments, explicit newlines, single overlong word, empty comment) through the publicSVGReportRenderer.render(_:)entry point.renderTimeSeriesChartalready covered bytestSVGRendererIncludesTimeSeriesChart. - Result: Shared package tests now 450 (was 444).
- Added 6 tests exercising
Audit Findings (2026-01-03) [P1]
Dead Code:
Remove unused
dayCellSizeconstant (2026-01-03)- Removed from CalendarDatePicker.swift
Remove unused
UIKitimport from ContentView (2026-01-03)- Location:
ContentView.swift:2 - No UIKit types used in file
- Location:
Remove unused(2026-01-03)UIKitimport from SettingsSheet- False positive:
UIPasteboardis used for copy-to-clipboard functionality
- False positive:
Remove unused DateRangePickerView.swift (2026-01-03)
- Location:
Views/History/DateRangePickerView.swift - 114-line file never referenced - CalendarDatePicker is used instead
- Location:
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
- Location:
Maintainability:
Extract layout constants in CalendarDatePicker (2026-01-03)
- Location:
CalendarDatePicker.swift:75-91 - Created
LayoutMetricsenum with named constants for cellSize, fonts, padding, opacity, etc.
- Location:
Refactor duplicated date range logic in CalendarDatePicker (2026-01-03)
- Added
endOfDay()andsetDateRange(from:to:)helper methods - Reduced duplicate “add day, subtract second” pattern from 5 occurrences to 1
- Added
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:
Donebutton wait usedtimeout: 15, but PDF generation throughWKWebView.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 totimeout: 60and added a one-shot retry path (tapPreviewAndWaitForDone(timeout:)helper) that dismisses any “Export Error” alert and re-tapspreviewButton. Verified: when the first 60s wait misses, the retry succeeds (134s total).
- Affected:
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 NSPredicateMATCHESquery (numeric labels in the Catalyst a11y snapshot made the predicate throwNSInvalidArgumentException), 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 (SceneDelegatesizeRestrictions) 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 viasectionHeader(), coordinate scrolls window-anchored viadragScroll(), 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 —
testEditSheetMinWidthOnMacpasses sinceWidthClampedFittedSizing(commit58c27bf). Remaining failures are clusters 1, 2, 4 plusDocumentPickerStabilityTests(2) andTagSelectorTests.testTappingChipCommitsFullNameToField;testViewExistingRecordsfails with a coordinate-scrollNSInternalInconsistencyException(point.x != INFINITY). - Failures cluster around:
- Catalyst NSToolbar a11y missing —
app.buttons["settingsButton"]/["pinButton"]not found. The SwiftUI toolbar (ContentView.swift:62-73) is wrapped in#if !targetEnvironment(macCatalyst); the Catalyst replacement isSceneDelegate’sNSToolbar(MinutaApp.swift:140+) which setsitem.label = "Settings"/“Pin” but never sets an accessibility identifier. Tests must queryapp.buttons["Settings"]/["Pin"]on Catalyst — or NSToolbarItems should expose the identifiers used by the iOS path. - “Running” header not found after
floatingPlayButton.tap()on Mac — affectsRecordEditingTests.testAddCommentToTimer,testTapToEditRecord,TimerFlowTests.testStartAndStopTimer, etc. Needs screenshot inspection during failure (Mac may render the running section differently or below the fold). — resolved 2026-07-02 bytestEditSheetMinWidthOnMacregressed (611pt vs 640pt min)WidthClampedFittedSizing(640–800pt clamp with height re-measure, commit58c27bf); passes in the full-suite run.- 3 PerformanceTests fail at ~24s (testExportSheetPerformance, testReportGenerationPerformance, testTagFilterPerformance) — needs investigation.
- Catalyst NSToolbar a11y missing —
- 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.
- Update 2026-07-10: full Catalyst suite is green (24/24 including all PerformanceTests) after the journey consolidation plus two Catalyst-specific fixes:
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
- Location:
Split AppState into smaller services
- Location:
MinutaApp.swift(320+ lines) - Handles: state, tags, records, deletion undo, tag merging
- Consider: TagManagementService, RecordManagementService
- Location:
Split RecordEditorViews
- Location:
RecordEditorViews.swift:1-450(450+ lines) - Mixed concerns: display, editing, formatting, image operations
- Extract image handling to separate component
- Location:
Consolidate history reload triggers (2026-01-03)
- Merged two
.taskmodifiers into one for initial load - Added comments explaining each reload trigger
- Location:
ContentView.swift:113-126
- Merged two
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 DEBUGdebugInfostate that re-evaluated the whole page every scroll frame - Scroll confirmed smooth after the fixes; further ideas tracked in the HistoryGrid entry below
- Covered by the HistoryGrid work below: row-per-month restructure (List cell reuse), sticky labels via
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:
HistoryGridrestructured intoHistoryMonthRow— 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 fixedHistoryColumnSizeswidths. Editor sheet moved into the month row owning the edited record. - Grid geometry reporting (
GridDebugPreferenceKeyand 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 DEBUGdebugInfostate 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
- Root cause of year-range slowness: the whole grid was a single List row containing eager nested
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
- Location:
UI/UX Improvements [P2]
- Add 300ms debounce to record saving while editing (2026-06-01)
- Already implemented for running timers:
scheduleSave()cancels the priorsaveTaskand sleeps 300ms beforesaveChanges(createTag: false)(RecordEditorViews.swift:412-419), wired totagInput/editedComment/editedStartTimeonChange handlers (:386-394).createTag: falseavoids 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.
- Already implemented for running timers:
Edit record sheet — remaining polish (verified done 2026-07-03)
- Both sub-items were already implemented:
TabEscapingTextViewcaps growth atmaxHeight: 200and switches to internal scrolling past the cap;TagSelectorauto-focuses the text field in itsonAppear(two runloop hops to survive Catalyst’s UIFocusSystem race) - Location:
TabEscapingTextView.swift,TagComboBox.swift(TagSelector)
- Both sub-items were already implemented:
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. Sourceassets/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.
- Hand-drawn stopwatch (
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):
UpdateCheckServicefetcheshttps://minuta.tools/downloads/version.json(prerendered at site build fromproject.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,xdismisses forever; zero network before opt-in (single carve-out from the “no network requests ever” rule) - Gated by
MANUAL_DISTRIBUTIONcompilation 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) andUpdatePromptTests(UI, Catalyst-only, not run in CI) - Docs: 315-update-check
- Implemented as check + notify only (no in-app install, no Sparkle):
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
pipicon 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
isMinifiedstate 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
- Add
- 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.widgetswith App Groups capability - Enable App Groups for
tools.minuta.app - Uncomment widget lines in
project.yml - Restore App Groups entitlement in
Minuta.entitlements
- Register App Group:
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
- See 701-deel
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
colorfield from the storedTagmodel; compute on read instead - Drop migration logic that carries forward legacy stored colors
- Location:
Shared/Sources/MinutaShared/Models/Tag.swift, call sites in views
- Tag colors are deterministically derived from tag names via
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.ymlandShared/Sources/MinutaCLI/Minuta.swift(+page.server.js), so links track releases on each site deploy - Fixed
deploy.sh: rsync--deletewas wiping the server’sdownloads/artifacts on every site deploy (confirmed: the directory was gone on the server); added--filter='P downloads/*'and re-uploaded artifacts viascripts/upload-brew-artifacts.sh
- Public page at
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) tomonthName()(MMMM)
- Changed from
Round borders only for range start/end days (2026-01-03)
- Added
RoundedCornersshape for selective corner rounding - Start day: left corners rounded, End day: right corners rounded
- Middle days: no corners, Single day: all corners
- Added
Use pressable button style for navigation (2026-01-03)
- Added
PressableButtonStylewith scale and opacity animation - Applied to all interactive elements
- Added
Use pressable button style for presets (2026-01-03)
- Applied
PressableButtonStyleto Today, Week, Month preset buttons
- Applied
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
LayoutMetricsconstants andPressableButtonStyle
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)
calendarGridpads with empty rows up toLayoutMetrics.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
PressableButtonStyletoComponents/PressableButtonStyle.swift(was private inCalendarDatePicker.swift) and applied it to the pill inHistorySection.swift
- Moved
Keep the calendar button the same view across collapsed/expanded states (2026-07-13)
HistorySectionnow owns a single persistent pill button that toggles the picker; next to it the period label (collapsed) or the From/To date pickers (expanded)CalendarDatePickeris grid-only now (dropped the header row andonDismiss)
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
animatableDatare-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
- Calendar grid is its own List row under
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
- 911-completed - Completed tasks archive
- 912-audit-2025-12-25 - Audit report
- 901-2025-12-08-init - Project initialization
- 902-2025-12-09-editor - Editor implementation