Commit Graph

172 Commits

Author SHA1 Message Date
kutesir
172bb011ff tasks: typography polish on task rows, no size changes (KC-62 follow-up 2)
Title weight bumped to medium for hierarchy, tracking added to the
mono date/countdown lines for legibility at small size, slightly
looser line spacing. Impeccable's detect engine only parses HTML, not
Swift, so this was done by hand rather than via the skill.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 22:57:13 +00:00
kutesir
92a533a930 tasks: rebalance row layout, countdown+icons move to right column (KC-62 follow-up)
The category icon column sat empty for most rows (default tasks have
no glyph/alarm/series), leaving a lone repeat icon floating in dead
space on the right per user's device screenshot. Move the countdown
label out from under the title into a trailing column paired with the
indicator icons, so both sides of the row carry equal weight.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 22:57:13 +00:00
kutesir
80fbbd7895 tasks: replace category text pill with TickTick-style icon glyphs (KC-62)
Every task row was tagging itself "Reminder" via a text badge for the
default category, adding no information. Swapped for icon-only glyphs
(nil for the generic .reminder case) folded into the existing alarm/
repeat indicator row; countdown/date info was already present or
redundant with the leading time-track, so no separate countdown text
was added.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 22:57:13 +00:00
kutesir
cddbefd314 onboarding+liveactivity+habits: 3 fixes from device feedback (KC-61)
1. Onboarding Habits caption now explicitly mentions the toggles are
   changeable in Settings, not just times/custom reminder.

2. TaskLiveActivity: removed the checkbox-look circle glyph (hourglass
   instead). iOS has no API for an app to block the system's swipe/dismiss
   gesture on a Live Activity -- not attempted. Added a real Stop control via
   a new StopCountdownIntent (LiveActivityIntent, iOS 17+) embedded directly
   in the card (lock screen '×' + Dynamic Island 'Stop Countdown' button) --
   the actual iOS-supported equivalent of 'long press for options', since
   Live Activities don't support notification-style long-press menus.

3. Habit notifications gain 'Not Today' (dismiss only, intentional no-op --
   the lifetime-tally philosophy has no concept of a logged miss) and 'Stop
   Tracking' (destructive-styled; flips the habit's master toggle off and
   cancels every pending notification for that type across all its slots).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 22:57:13 +00:00
kutesir
fc6ecc4979 tutorial: introduce habit reminders in the Settings tutorial too (KC-60)
Habit reminders (KC-55) were in Onboarding but not the app's separate
spotlight/coachmark tutorial system -- skipping onboarding meant no second
chance to discover Prayer/Bed/Coffee reminders exist.

Adds a fourth TutorialManager track (settingsTips/settingsStep/settingsDone),
matching the existing Today/Workout tip-track pattern exactly, and wires it
into SettingsView via the same shared InViewTutorialCard component -- shown
once, first time Settings opens.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 22:57:13 +00:00
kutesir
d3e781c8e7 tasks: add 'Complete & Stop Series' for recurring tasks (KC-59)
Recurring tasks only had Delete (erases the task + its entire completion
history) as a way to stop a series -- no in-between option to wind one down
while keeping its record.

TaskViewModel.completeAndStopSeries(_🔛) marks the occurrence complete and
sets recurrenceEnd to that day -- a field that already existed in the model
and was already respected by isOccurrence, just had no UI. Task, title, and
full completedOccurrences history stay intact; only future occurrences stop.

New menu item in the shared TaskMenuItems (Today/Matrix/Calendar), shown only
for recurring tasks. Wired through all 8 call sites, several via a new
optional closure threaded through intermediate wrapper views (QuadrantCard,
DayTimelineView, OverdueCard, UpcomingSection).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 22:57:13 +00:00
kutesir
e0bc5538c1 Bump build to 2.0 (10)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 22:57:13 +00:00
kutesir
ec58032246 habits: clarify the custom coffee reminder row (KC-58)
The custom-slot row just said 'Coffee' -- identical-looking to the card's own
'Coffee reminders' title, with no affordance signaling it's an editable,
user-nameable 5th slot (distinct from the 4 fixed Morning/Midday/Afternoon/
Evening ones).

Added a small pencil + 'YOUR OWN REMINDER' label above the field; changed the
default from pre-filled 'Coffee' to empty so the placeholder ('Name it, e.g.
"Second coffee"') actually does its job. NotificationManager falls back to
'Coffee' for the notification text only if the user never typed a name.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 22:57:13 +00:00
kutesir
a6e21e3b52 fix(theme): add AppColors.coffeeBrown -- Color(r:g:b:) is file-private (KC-57)
Build error: Color(r:g:b:) is a private extension scoped to DesignTokens.swift
(only UIColor's sibling init is also private, both file-scoped by design).
KC-55/56 called it directly from SettingsView.swift and OnboardingView.swift
for the coffee icon color -- invisible there, so the compiler fell back to
unrelated overloads ('Cannot convert Int to Color', 'Extra argument b').

Added AppColors.coffeeBrown (defined where the private init is visible),
replacing both inline Color(r:g:b:) call sites -- matches how every other
color in the app is centralized and accessible everywhere.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 22:57:13 +00:00
kutesir
6fe896083f habits: add a 'Done' notification action + private day-count badge (KC-56)
Follow-up to KC-55's design question: habit reminders (Prayer/Bed/Coffee) stay
out of Today/Matrix/Activity History (would add 3-9 daily checkboxes forever,
against 'nothing shouts'), but now get a 'Done' action on the notification
itself -- like the workout confirm flow's 'Yes, log it' -- that logs a private
timestamp.

New HABIT_LOG category/HABIT_DONE action; every habit notification carries
userInfo['habitType'] (bed/prayer/coffee -- slot detail collapses to the type).
logHabitDone/habitDoneCount store a deduped date array in UserDefaults.kisani,
mirroring the lifetime-tally streak philosophy from KC-40 rather than a
breakable streak. Settings shows a quiet 'Xd' badge per habit -- the only
place the count appears; nothing touches the task list.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 22:57:13 +00:00
kutesir
39c95784db habits: add Prayer, Bed-making, and Coffee/Caffeine reminders (KC-55)
Three new opt-in daily local-notification habits, toggleable in both Settings
and Onboarding, backed by the same UserDefaults.kisani (App Group) keys:

- Prayer: up to 4 independently-toggleable times/day (Morning/Afternoon/
  Evening/Night), 'Have you had a chance to pray and give thanks?'
- Bed-making: one daily nudge, default 8am (research-backed first win of the
  day per the user's note).
- Coffee/Caffeine: up to 4 named times + one custom-labeled reminder.

NotificationManager.scheduleHabitReminders() schedules/cancels all slots
idempotently via fixed identifiers, wired into the existing reschedule()
orchestration. Onboarding gets 3 restrained master toggles with sensible
defaults (no per-slot editors, to avoid a long form); Settings gets the full
Habit Reminders sheet with per-slot times and the custom coffee reminder.

Found and worked around a pre-existing store mismatch: workoutCheckInEnabled/
workoutConfirmEnabled etc. write to UserDefaults.standard (no explicit store:)
but NotificationManager reads them from UserDefaults.kisani -- not fixed here
(out of scope), but all new keys explicitly use store: UserDefaults.kisani so
this feature actually fires.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 22:57:13 +00:00
kutesir
dc5c403a36 tasks: fix timed recurring tasks vanishing from Today after their time passes (KC-54)
Real bug: todayTasks excludes timed tasks whose time has passed ('belongs in
overdue'), but overdueTasks deliberately excludes ALL recurring tasks (it feeds
postpone/postponeAllOverdue, which mutate dueDate by id -- for a recurring task
that's the recurrence anchor, not just today's occurrence; including them there
would let Postpone All corrupt the whole series). Recurring tasks fell into the
gap between the two rules and vanished from the Today tab entirely once their
time passed, while Calendar's day view (no time-of-day gating) kept showing
them fine.

Fix: todayTasks only excludes past-time NON-recurring tasks. Recurring
occurrences always stay in Today, matching Calendar, without ever touching the
overdue/postpone path.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 22:57:13 +00:00
kutesir
7bb37d1995 widgets: add 'This Year' mode to Progress Dots (KC-53)
The Progress Dots lock-screen widget already supports Day/Week/Month via its
configuration picker — no code needed for those. Year was the only mode
missing. Added ProgressDotMode.year, a matching provider branch
(Calendar.dateInterval(of: .year)), and yearTitle(for:), mirroring the
existing day/week/month pattern exactly.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 22:57:13 +00:00
kutesir
709eee2579 workout: stop auto-completing the day when sets are ticked (KC-52 follow-up 3)
Real bug, not cosmetic: setAllSetsDone/toggleSet/setExerciseDone still
auto-called logWorkoutCompleted() whenever doneSets == totalSets — leftover
pre-redesign behavior that fought the new completion card. Ticking the last
box (or tapping 'Check all sets') silently counted the day and snapped the UI
to the green 'done' takeover without the user ever hitting Finish.

Removed the auto-log from all three set-mutation methods. Only the card's
explicit Finish Workout/Finish Anyway (-> markWorkoutDone) now counts a day as
done. Check all sets just checks boxes.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 22:57:13 +00:00
kutesir
65c93769c5 workout: drop seal icon, confirm every destructive quick action (KC-52 follow-up 2)
checkmark.seal.fill (unused elsewhere, reads as a generic 'AI verified badge')
replaced with checkmark.circle.fill, the app's actual convention. All four
state-changing card actions (Finish Anyway, Check All Sets, Clear All Sets,
Didn't Train) now confirm via one PendingAction enum + confirmationDialog
before executing, each with a tailored message; destructive ones use the
.destructive role. The header menu's Clear All Sets (any completion level)
also now confirms instead of executing instantly.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 22:57:13 +00:00
kutesir
76366a0065 workout: restrained button, finish confirmation, clear toggle (KC-52 follow-up)
Addresses feedback on the completion card: the primary button was a
permanently-filled accent pill (violates 'nothing shouts'/'color earns its
place') and Finish Anyway skipped confirming before overriding real progress.

- QuietAccentButtonStyle: outlined at rest, fills solid accent only while
  pressed.
- confirmationDialog on Finish Anyway showing the logged % (skipped at 100%).
- Check all sets flips to Clear all sets once fully checked — undo is the
  button itself.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 22:57:13 +00:00
kutesir
4b2463b017 workout: bring completion controls onto the main page (KC-52)
DotProgressCard becomes a 3-state completion card (pending/done/missed) with a
primary Finish Workout/Finish Anyway action that captures today's workout
honestly — regardless of how many sets got ticked, since Health/Watch already
confirms the workout independently. Secondary actions: check all sets, mark
not completed. Done/missed states show an Undo.

Adds WorkoutViewModel.unmarkWorkoutDone/unmarkWorkoutMissed. Removes 'Mark All
Complete' from the header '...' menu (superseded by the card); keeps 'Clear
All Sets' as a distinct reset utility.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 22:57:13 +00:00
kutesir
c75a067993 fix(healthkit): use let for interval captured across async let tasks
Build error (Xcode, Swift 6): 'interval' was a var built via two statements
then captured by 3 concurrent async let tasks in dailyReference — Swift 6
requires captured values to be immutable. It was never mutated after setup;
switched to a single-expression let (DateComponents(day: 1)).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 22:57:13 +00:00
kutesir
764e1c2f3d fix(analytics): mark WorkoutSnapshot.init(vm:) @MainActor
Build error (Xcode, Swift 6 strict concurrency): WorkoutSnapshot.init(vm:) read
history/workoutDates/schedule/missedDates from @MainActor WorkoutViewModel in a
nonisolated init. The init is only ever called from AnalyticsService.reconcile
(@MainActor) — mark it @MainActor to match its actual call site.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 22:57:13 +00:00
kutesir
b4813272f6 analytics(healthkit): wire historical HealthKit reference into reports (KC-51 ph4e)
Completes 'available HealthKit trends' for weekly/monthly reports. Adds
HealthKitManager.dailyReference (3 parallel bucketed HKStatisticsCollectionQuery
for steps/active-calories/resting-HR over ~13 months), AnalyticsService.
refreshHealthReference to cache and merge it into DaySamples, and avgSteps/
avgActiveCalories/avgRestingHR on PeriodSummary (nil when absent — honest).
Surfaced in AnalyticsView's weekly/monthly cards.

Verified standalone (swiftc, 8/8): aggregation correct when present, nil when
absent, workout metrics unaffected, reports stay deterministic. Tests added to
AnalyticsEngineTests. This completes all analytics logic — remaining work is the
first Xcode build (device, user-run) to catch any SwiftUI compile issues.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 22:57:13 +00:00
kutesir
2930557142 analytics(service): capture snapshot value in coordinator closures (concurrency hardening)
Moves sample-building to nonisolated static funcs over a captured WorkoutSnapshot
so the coordinator's escaping closures don't touch @MainActor state — avoids a
main-actor isolation error. Matches the app's existing @MainActor + static shared
pattern (HealthKitManager).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 22:57:13 +00:00
kutesir
bcbcda5ba5 analytics(deeplink): route report notification tap to its report (KC-51 ph4c)
Completes deep-link routing (goal #7): AnalyticsDeepLink is Identifiable; a
tapped weekly/monthly report notification is consumed in ContentView on
launch/active and presents AnalyticsView opened to that area. Core round-trip
re-verified standalone; SwiftUI wiring needs an Xcode build.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 22:57:13 +00:00
kutesir
f8a1f90f8c analytics(ui): AnalyticsView (6 areas + states + charts) + Workout entry (KC-51 ph4b)
Self-contained Analytics UI: Overview/Daily/Weekly/Monthly/12-Month/Exercises
with color+glyph status, empty/partial states, Swift Charts, VoiceOver labels,
Wenza tokens, and the non-medical disclaimer. Entry via an IButton in the
Workout header (sheet). Added Identifiable chart points.

Self-reviewed for compile issues (fixed tuple key-paths, a11y trait literal,
format string) but NOT build-verified — no iOS runtime; SwiftUI/app deps. Logged
in ISSUES.md as device-required.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 22:57:13 +00:00
kutesir
3f46369078 analytics(app): AnalyticsService + lifecycle reconcile + deep-linked notifications (KC-51 ph4a)
Wires the verified analytics core into the app: AnalyticsService feeds live
WorkoutViewModel data into the reconcile coordinator and exposes UI reads;
ContentView reconciles on launch + foreground; NotificationManager fires one
permission-aware, deduped weekly/monthly report notification carrying an
AnalyticsDeepLink, captured on tap for routing.

NOT build-verified (no iOS runtime; app-module/SwiftUI deps). Logged in
ISSUES.md; historical HealthKit reference + user-scheduled notification time are
follow-ups.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 22:57:13 +00:00
kutesir
f3213c080e analytics(integration): adapter + reconcile coordinator + deep links (KC-51 ph3)
Phase 3 core (verifiable): WorkoutAnalyticsAdapter (app data → DaySample),
AnalyticsCoordinator (idempotent backfill of missing weekly/monthly reports +
retention prune, injectable providers), AnalyticsDeepLink (wenza://analytics
report routing).

Fixed a reconcile bug caught by tests: a retention-boundary week was generated
then immediately pruned, regenerating every run — aligned completedWeekStartKeys
to the prune cutoff → idempotent.

Tests: AnalyticsAdapterTests + AnalyticsCoordinatorTests. Verified standalone via
swiftc (~90 analytics assertions total across engine/store/adapter/coordinator).
Remaining (app wiring, notifications, UI) requires an Xcode build — logged in
ISSUES.md as device-only.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 22:57:13 +00:00
kutesir
8e73615e8b analytics(persistence): file-based store + reconciliation selection (KC-51 ph2)
Phase 2: AnalyticsStore (JSON files under Application Support) with immutable
frozen daily snapshots + immutable, idempotent, deduped weekly/monthly reports,
and 12-month retention pruning. Engine gains completedWeekStartKeys/
completedMonthKeys/missingKeys for idempotent reconciliation selection.

AnalyticsStoreTests: 6 XCTest cases. Verified standalone via swiftc — 23-
assertion temp-dir harness all pass. Registered files via xcodegen.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 22:57:13 +00:00
kutesir
31cf38e911 analytics(engine): pure deterministic AnalyticsEngine + models + tests (KC-51 ph1)
Phase 1 of the analytics system (/goal): the framework-independent analysis
engine. Value-type models (MetricDelta, TrendResult, DaySample, WeeklyReport,
MonthlyReport, ...) and AnalyticsEngine with documented formulas (volume,
%change, consistency, trend) and honest degradation (nil/zero/tiny baseline,
new exercise, rest day, outliers via winsorize, unit-dimension compatibility,
divide-by-zero). Calendar math takes an injected Calendar (locale/firstWeekday/
timezone/DST-safe).

AnalyticsEngineTests: 35 XCTest cases. Independently verified by compiling the
engine with swiftc and running a 42-assertion harness — all pass. (Xcode test
target not runnable in this env; assertions mirror the XCTest file.)

Registered the new files via xcodegen. Logs KC-51 phase-1 progress in ISSUES.md.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 22:57:13 +00:00
kutesir
a4abbb0d4d docs(issues): KC-51 — analytics system plan, persistence eval, blockers
Records the /goal analytics-system implementation plan: reusable-functionality
survey, SwiftData/CoreData/current persistence evaluation, proposed architecture
(stores, immutable reports, pure analysis engine, reconciliation, notifications,
UI, tests), and the two blockers (build-verification unavailable; persistence +
min-iOS decision).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 22:57:13 +00:00
kutesir
508d321619 Calendar: force timeline fill (dead space) + long-press menu on blocks
KC-49: week/3-day dead space — the outer .frame only allowed fill; force the
timeline ScrollView greedy (.frame maxHeight .infinity) and pin each timeline
root VStack top, so the header pins to the top instead of floating mid-screen.

KC-50: timeline blocks now have a long-press context menu — Complete, Snooze
(15m/1h/tomorrow), and the shared TaskMenuItems (reschedule/move/priority/
category/edit/delete). Events get no menu. CVTimeBlock takes @ViewBuilder menu.

Logs KC-49 (supersedes KC-48) and KC-50 in ISSUES.md.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 22:57:13 +00:00
kutesir
e8065820b4 docs(issues): log KC-40–48 (streak tally, stats, history, banner, calendar rework)
Records this session's work in the issue tracker: workout streak → lifetime
tally (KC-40), recurring-aware stats (KC-41), Activity History (KC-42), neutral
Today checkbox (KC-43), swipeable streak banner (KC-44), continuous year view
(KC-45), month TickTick bars+collapse reverted (KC-46), week timeline + drag +
grid toggle (KC-47), week/3-day dead-space fix (KC-48).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 22:57:13 +00:00
kutesir
51c716ae97 Calendar: fix week/3-day dead space (pin content to top)
The timeline views' content is a thin header + a greedy ScrollView (≈0 ideal
height), so the top-level VStack shrank and the bottomTrailing ZStack floated
it to the bottom — leaving a big empty gap above the day header. Month view
has a tall fixed grid so it never surfaced. Pin the VStack to fill height and
top-align.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 22:57:13 +00:00
kutesir
a34afabb2f Revert "Calendar month view: TickTick-style bars + collapse-to-week"
This reverts commit 2ec3278632.
2026-07-12 22:57:13 +00:00
kutesir
d32e55fd44 Revert "Calendar month: drag-to-collapse + full-column selection highlight"
This reverts commit bd43eecfbe.
2026-07-12 22:57:13 +00:00
kutesir
6e20d665fd Calendar week view: timeline ⇄ grid layout toggle
Completes the TickTick weekly reference — a toggle flips the week between the
timeline and a grid of day-cards (2 columns), each card listing that day's
schedule as colored bars. Tapping a card opens that day. Both layouts swipe
by week.

- weekGrid state + toggle button (timeline/grid icons)
- weekGridLayout + weekDayCard

(Not build-verified — sim runtime removed to reclaim disk.)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 22:57:13 +00:00
kutesir
39f4c0fcfa Calendar timeline: drag a task block to reschedule its time
The headline of the TickTick weekly timeline — task blocks are now draggable
vertically to change their time, snapped to 15-minute steps. Applies on the
week, 3-day, and day timelines.

- CVTimeItem carries taskID + recurring; task items are tagged, events are not
- CVTimeBlock: a draggable block (GestureState offset while dragging, commits
  on release); events and recurring tasks render fixed (non-draggable)
- rescheduleTask: clamps to the visible hour range and writes the new time via
  taskVM.setDate on the same day

(Not build-verified — sim runtime removed to reclaim disk.)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 22:57:13 +00:00
kutesir
7fdf50c898 Calendar week view: 7-day timeline (TickTick-style)
Replaces the mini-month + agenda-list split with a proper weekly timeline —
hours down the left axis, one column per weekday, events as time-positioned
blocks — reusing the same cvTimeGrid/cvDayColumnHeader the day and 3-day
views already use. Horizontal swipe navigates by week (navigateWeek).

Removes the now-unused cvAgendaItems helper.

(Not build-verified — sim runtime removed to reclaim disk; reuses existing
timeline components.)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 22:57:13 +00:00
kutesir
2441efe304 Calendar month: drag-to-collapse + full-column selection highlight
Two refinements to get closer to the TickTick reference:
- Drag up on the calendar collapses to the selected week; drag down expands
  to the full month (horizontal swipe still changes month). One direction-
  aware DragGesture handles both axes.
- The selected day now gets a rounded full-column highlight behind its
  number + bars when collapsed (as in the tapped-day screenshot), instead
  of only the day circle.

(Not build-verified — sim runtime removed to reclaim disk.)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 22:57:13 +00:00
kutesir
8f89d3c73b Calendar month view: TickTick-style bars + collapse-to-week
Matches the referenced TickTick monthly view:
- Day cells now show stacked colored schedule BARS (workout/events/tasks,
  up to 4) instead of dots — a glanceable overview of the month.
- Selecting a day COLLAPSES the grid to just that week, giving the day's
  agenda room below (as in the tapped-day screenshot). A chevron grabber
  expands back to the full month.

- DayCell: bars param + stacked RoundedRectangles, top-aligned, height 54
- monthView: renders week rows (all when expanded, only the selected week
  when collapsed) via weekRows()/selectedWeekRow(); grabber toggles
- eventDots cap raised to 4 for the bar stack

(Not build-verified — sim runtime removed to reclaim disk.)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 22:57:13 +00:00
kutesir
23fac88b1d Calendar: header follows the scrolled-to year in continuous year view
The top title now tracks whichever year is at the top of the year scroll
(e.g. scroll into 2027 and the header reads '2027'), instead of staying on
the displayed month. In month/week/day modes it still shows month + year.

- visibleYear state driven by a YearTopKey preference reporting each year
  block's top offset in the 'yearScroll' coordinate space
- headerTitle switches on viewMode
- iOS 16-safe (GeometryReader + PreferenceKey, no scrollPosition API)

(Not build-verified — sim runtime removed to reclaim disk.)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 22:57:13 +00:00
kutesir
83d1f1d542 Calendar year view: continuous TickTick-style multi-year scroll
Replaces the single paged year (< 2026 >, with dead space below December)
with an infinite vertical scroll where years flow one after another. Opens
anchored on the current year (accent-colored); scroll up for past years,
down for future. Bottom padding clears the floating tab bar / + button.

- yearView: ScrollViewReader + LazyVStack over a year range, scrollTo(current)
- yearBlock(_:): bold year label + 12 mini-months (reuses cvMiniMonth)
- Removed navigateYear + the ◀ year ▶ header (superseded by inline labels)

(Not build-verified — sim runtime removed to reclaim disk; standard SwiftUI
+ existing cvMiniMonth helper.)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 22:57:13 +00:00
kutesir
a69b54ff19 Workout streak banner: swipeable week / year / vs-last-week pages
Replaces the single lifetime-tally banner (which used a meaningless streak%7
tick bar) with a 3-page paged TabView:
  1. This week — count + 7 per-day ticks for the current week
  2. This year — count + 12 month ticks, with all-time total underneath
  3. vs last week — this week's count and the delta (▲ more / ▼ fewer / same)

Adds WorkoutViewModel breakdowns: workoutsThisWeek/LastWeek/ThisYear,
currentWeekDays (7 flags), currentYearMonths (12 flags).

(Not build-verified — sim runtime removed to reclaim disk; uses only APIs
already present in the original banner.)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 22:57:13 +00:00
kutesir
97cc5589cc Today list: neutral checkbox stroke (color already on accent bar + chip)
The Tomorrow/Later rows carry their quadrant color on the left accent bar
and the category chip, so tinting the checkbox too was redundant. Match the
neutral text3 stroke the timeline rows already use.

(Not build-verified — local sim runtime removed to reclaim disk; trivial
one-line swap to an API already used elsewhere in the same file.)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 22:57:13 +00:00
kutesir
e79cced64a docs: add CICD.md with pipeline flow diagram and gate rules
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 22:57:13 +00:00
kutesir
b9d2ca104e docs: add STRUCTURE.md describing targets, layout, and architecture
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 22:57:13 +00:00
cd2dc1cee6 ci: lowercase runner label (runner registered as self-hosted,macos) 2026-07-12 22:57:13 +00:00
f7bc0b9b3c ci: lowercase runner label (runner registered as self-hosted,macos) 2026-07-12 22:57:13 +00:00
kutesir
cf5dc8943a ci: add Gitea Actions CI/CD, branch workflow, and contributor docs
- .gitea/workflows/ci.yml: build + test KisaniCal scheme on a self-hosted
  macOS runner for every PR into main/develop (and pushes to develop).
- .gitea/workflows/release.yml: archive + export IPA on push to main, with
  a commented TestFlight upload placeholder (App Store Connect API key).
- ExportOptions.plist: export template (Team ID K8BLMMR883, app-store method).
- scripts/gitea-setup.sh: idempotent Gitea API setup for develop default
  branch + main branch protection.
- CONTRIBUTING.md: feature/* -> develop -> main workflow and PR gate rules.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 22:57:13 +00:00
kutesir
c0dd298e5b Workout streak: lifetime tally that only grows (never resets)
Per user model: the workout count should reflect every workout achieved and
never break. Replaces the consecutive/weekly-goal streak with a simple count
of distinct workout days (totalAchievedWorkoutDays). A below-goal week (4/5)
and a full week (5/5) both count fully (=9); a missed day or blank week never
reduces it; duplicate HealthKit days count once.

- ExerciseModels: streakDays = Set(workoutDates).count
- StreakLogicTests: retargeted to tally semantics (4/5+5/5=9, blank-week,
  duplicates, skipped-day, empty)
- SettingsView: goal caption no longer claims the streak can be lost

NOTE: not built/tested locally — CLI xcodebuild wedges on actool via the
broken CoreSimulator daemon (missing sim runtimes, machine-wide). Verify in
Xcode GUI (prefs now fixed) or on device.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-05 19:51:46 +03:00
kutesir
6b5f6bd3ea Stats: schedule-aware weekly streaks, recurring-aware counts, activity history
- Workout streak now honors the weekly goal ('reach your goal every week'):
  counts workout days across consecutive kept weeks, so rest days and a
  skipped day within goal no longer reset it to 1 (KC: profile showed
  streak 1 with 5 workouts that week).
- Task 7-day bars / 'this week' now count recurring occurrences
  (completedOccurrences), fixing permanently-empty bars alongside a
  nonzero done rate.
- New task day-streak stat on the profile Tasks card.
- New ActivityHistoryView: 90-day day-by-day archive of completed tasks
  (times, repeat markers), workout logs (sets/volume), missed/made-up days.
- StreakLogicTests: 8 unit tests; algorithm additionally verified via
  standalone harness (7/7 scenarios incl. the skipped-Thursday case).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-05 15:05:14 +03:00
kutesir
dd22e94efe Set build to 2.0 (9)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-25 01:10:36 +03:00