Commit Graph

129 Commits

Author SHA1 Message Date
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
kutesir
b78b965938 Tasks: square the remaining checkboxes I missed (Today timeline, Next-3, Matrix rows)
Follow-up after the first pass only caught the list-style rows. Converted the
actual tap-to-complete toggles + markers that were still circular:
- Today timeline (tlRows): marker complete/all-day → rounded square (circleD
  18→16); trailing SF-symbol toggle → rounded-square checkbox.
- Today Next-3-Days row: 20pt circle → 16pt rounded square.
- Today "Completed Today" row indicator: green circle → green rounded square.
- Calendar Month agenda (DayTimelineView): trailing SF-symbol toggle → square.
- Matrix quadrant row checkbox: circles → rounded squares.

Left intentionally circular: multi-select sheet (selection, not completion) and
workout-set checkboxes (separate domain).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-25 01:07:50 +03:00
kutesir
1c325c108e Tasks: unify checkboxes to 16pt rounded square across Calendar & Matrix
Match the Today list checkbox (16pt, cornerRadius 5):
- Calendar list-view row: cr3/18 → cr5/16.
- Calendar day task-list row (CalTaskRow): cr6/22 → cr5/16.
- Calendar Month agenda markers (DayTimelineView): task complete + all-day
  markers circle → rounded square, circleD 18 → 16 (workout stays a circle, the
  timed bullet stays a dot, so those remain visually distinct).
- Matrix row (QDTaskRow): 22 → 16.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-25 00:56:48 +03:00
kutesir
24219f8289 Bump build to 2.0 (10)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-25 00:40:38 +03:00
kutesir
5a83f232a6 Tasks: square (rounded) checkbox, slightly smaller
TaskRowView checkbox: 18pt circle -> 16pt rounded square (cornerRadius 5),
matching the requested look. Keeps the quadrant-color stroke and 36x44 tap target.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-25 00:28:47 +03:00
kutesir
4a05844ee3 Voice: fix audio-session leak causing insufficientPriority (561017449)
Decoded the device error: NSOSStatusErrorDomain 561017449 = '!pri' =
AVAudioSession insufficientPriority — the mic was denied because a session was
left active (which also blocks the keyboard's own dictation).

Root cause: the early-return guards (recognizer unavailable / invalid format) ran
AFTER setActive(true) but never deactivated, leaking an active session. Also the
.measurement + .duckOthers config is more prone to priority denials.

Fixes:
- Deactivate any stale active session before starting.
- Use .record / .default (least-exclusive recording config).
- stopEngine() now always releases the session (no early-out), and the bailout
  guards call it so the session is never left active.
- Map 561017449 to an actionable message (close other audio apps / restart).

Device-specific; not reproducible on the sim (blocks at the permission dialog).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 21:56:45 +03:00
kutesir
09acf7afa5 Workout: restore history/missed/compensations from iCloud (KC-39)
save() pushed all 7 workout keys to iCloud KVS, but init only restored 4 —
workout history, missed dates, and rest-day compensations were uploaded yet
never restored, so they were lost on reinstall/update. Added the three missing
restoreIfMissing calls so restore mirrors save.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 21:52:16 +03:00
kutesir
b6c9f5b0ca Fix: restore token highlighting in Quick Add field (regression)
A fast-path early-return added in the voice-binding fix
(`if tv.textColor == .label && tv.text == text && editing { return }`) skipped
rebuilding the attributed text while editing — so token highlights never
reapplied as the parser updated highlightRanges on each keystroke. Removed it so
highlights always render. Also switched the highlight tint from systemBlue to the
app accent (orange) to match the established design.

Verified on simulator: "Remind me to pray everyday" → "Remind me to" + "everyday"
highlighted in accent orange; chips Today / Daily.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 21:36:21 +03:00
kutesir
c9f0fd0777 Voice: harden startEngine against "Could not start recording"
Addresses the most common causes of the engine failing to start:
- Defensive teardown before starting (removeTap + stop + reset) — a leftover tap
  from a prior attempt makes installTap throw "already has a tap".
- Validate the input node's hardware format (sampleRate/channels > 0) and fail
  with a clear "No microphone input" message instead of throwing.
- Use inputNode.inputFormat(forBus:0) for the tap.
- Category .playAndRecord + [.duckOthers, .defaultToSpeaker] (more permissive
  than .record with .duckOthers).
- Surface the underlying NSError domain+code in the banner + log to pinpoint any
  remaining device-specific failure.

DEBUG-only KISANI_VOICE_START hook to drive the real engine on a sim.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 21:33:16 +03:00
kutesir
ad304913e4 Voice: show dictated transcript in the Quick Add field
The mic captured speech and the transcript reached rawText, but it never appeared
in the field. NLTaskField.updateUIView (UITextView representable) returned early
when the field wasn't focused (`guard coordinator.isEditing`), so externally-set
text (voice dictation, the normal not-focused case) was dropped and the
placeholder stayed.

Fix: render non-empty `text` even when not editing, placing the caret at the end;
keep the cursor-preserving fast path while actively editing. No new voice system —
this only fixes the existing mic button's transcript→field binding.

Verified on simulator via the real speech.transcript path: "Buy milk tomorrow" →
field shows it (chip: Tomorrow); "Call Romeo at 11 AM" → field shows it (chip:
Today 11:00). Adds DEBUG-only test hooks KISANI_AUTOADD / KISANI_VOICE_SIM
(compiled out of Release). Logged under KC-38.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 21:22:45 +03:00
kutesir
f8d0a80f17 docs: log KC-38 (voice input — speech-to-task via mic button)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 21:13:26 +03:00
kutesir
e8c2c5c8ea docs: log KC-32–37 (calendar views, grid alignment, future-task confirm, visual cleanup, dynamic version, everyday recurrence)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 21:05:33 +03:00
kutesir
8370125ec0 Quick-add: detect "everyday" and "every weekday" recurrence
parseRecurrence required a space ("every day"), so "everyday" (one word) was
missed. Now \bevery\s*day\b matches both, plus "each day". Added Every Weekday
detection ("every weekday", "weekdays") ahead of the broader weekly/daily
patterns. Sub-day cadence ("every hour"/"hourly") is intentionally NOT matched —
the occurrence engine is day-granular, so it stays a one-off rather than being
mislabeled. 6 parser tests added.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 21:02:57 +03:00
kutesir
af2996876c Tests: recurring-task occurrence expansion (date mapping)
8 tests covering daily / weekly / every-weekday / monthly / yearly expansion,
repeat-until cutoff, time-of-day preservation on the mapped local day, and
non-recurring single-day placement. Uses Calendar.current so it holds in any
timezone. Closes the recurrence-coverage gap from the calendar fix.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 20:59:43 +03:00
kutesir
eb7d563bbd feat(voice): wire existing mic button to live speech recognition
The mic icon in AddTaskSheet was a static Image with no action and no
speech service behind it. Full implementation:

SpeechRecognizer.swift (new):
- @MainActor ObservableObject wrapping SFSpeechRecognizer + AVAudioEngine
- Requests speech recognition then microphone permission in sequence
- Streams partial results into @Published transcript; final result stops engine
- Suppresses transient error 301 (no speech detected)
- reset() cleans up on sheet dismiss
- [VOICE] log trail at every step: tapped → granted → started → transcript → saved

TodayView.swift — AddTaskSheet:
- @StateObject private var speech = SpeechRecognizer()
- onChange(speech.transcript) writes transcript to rawText (task field)
- Mic Image → Button { speech.toggle() } with mic.fill + pulse animation while recording
- .safeAreaInset shows a red dismissible error banner on permission denial
- submit() stops recording before saving; logs task title
- .onDisappear resets speech state

project.yml:
- NSMicrophoneUsageDescription
- NSSpeechRecognitionUsageDescription

Co-Authored-By: Kutesir <tqwyy79vzn@privaterelay.appleid.com>
2026-06-24 19:48:00 +03:00
kutesir
716e6e8f31 Notifications: drop decorative ✓ glyphs from action title and done body
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 19:24:54 +03:00
kutesir
38db257c4a Calendar: fix weekday-header/week-number alignment to firstWeekday + tests
Root cause: a data-integrity bug, not visual. The month grid is built from
.weekOfMonth (respects Calendar.current.firstWeekday), but the weekday header
was hardcoded Sunday-first ("S M T W T F S") and the week-number label was gated
on isSunday (weekday == 1). On any non-Sunday-first locale (or when the user sets
"Start Week On" = Monday) every column was mislabeled by one and the W-number
landed on the wrong column — e.g. June 1 2026 (a Monday) appearing under "S".

Fix:
- Extract pure, testable date math into CalendarGrid (monthGrid + weekdaySymbols).
- Header now derived from firstWeekday, so it always matches the grid.
- Week-number label gated on isWeekStart (weekday == firstWeekday), not Sunday.
- Document that week numbers are locale weekOfYear (consistent with the layout),
  not ISO.
- gridDays() and the year-view mini-month dedupe to CalendarGrid.

Verification:
- New KisaniCalTests target with 9 tests, all passing on simulator: exact
  Sunday-first June 2026 grid, header↔grid alignment for firstWeekday 1...7,
  cell date identity, month navigation (May/Jul/Feb 2026 + Feb 2028 leap +
  Dec 2026→Jan 2027), and event-day bucketing (all-day, late-night, midnight-
  crossing) across 4 timezones.
- Verified in the running app: header S M T W T F S, May 31 in the Sunday
  column, June 1 under Monday, June 24 (Wednesday) selected.

Adds a DEBUG-only KISANI_INITIAL_TAB launch env (compiled out of Release) used
to screenshot the calendar past the sign-in gate.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 19:24:49 +03:00
kutesir
eeb60dc51b fix(tasks): timed tasks become overdue immediately when time passes (TickTick behavior)
Previous logic compared dueDate against startOfDay for all tasks, so a task
due at 2:00 PM stayed in "Today" until midnight even after the time passed.

TickTick rule:
- hasTime=true  → overdue the moment dueDate < now (wall clock)
- hasTime=false → overdue at start of next day (dueDate < startOfDay)

overdueTasks: now uses `dueDate < now` for timed tasks, `dueDate < today`
for date-only tasks.

todayTasks: now excludes timed tasks whose time has already passed — those
belong in overdue, not today — while keeping date-only tasks in Today for
the full calendar day.

Co-Authored-By: Kutesir <tqwyy79vzn@privaterelay.appleid.com>
2026-06-23 19:00:57 +03:00
kutesir
23d3d201f7 Settings/widget: drop decorative emoji; show real bundle version
- Remove 🎉 from the My Tasks widget empty state.
- Remove 𝕏 👾 📸 from Settings → Follow Us row.
- About row and About sheet now read the version from the bundle
  (new Bundle.marketingVersion / buildNumber / versionDisplay helpers)
  instead of a hardcoded "v1.0.0", so it always tracks the real build.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 20:35:41 +03:00
kutesir
5cf32a4370 Calendar: fix Day/3-Day/Week/List task rendering; lighter checkbox; confirm future completes
Calendar timeline views only populated in Month. Fixes:
- Day/3-Day time grid dropped untimed (midnight) tasks and ignored
  recurrence. Now uses occurrences(on:) + hasTime, and adds an all-day
  band for untimed tasks, all-day events, and the day's workout.
- Week showed abstract color bars; now lists real task/event titles
  (up to 4/day + "N more") via a shared cvAgendaItems helper.
- List view and the task-list toggle now use occurrences(on:) so
  recurring tasks appear.

TodayView:
- Slimmer task checkbox (18pt open ring vs 22pt filled donut).
- Confirm before completing a future-dated task (Next 7 Days / Later)
  to prevent accidental completion; today/overdue still toggle instantly.

Bump build to 2.0 (9). Add App Store support-site one-pager.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 03:03:28 +03:00
kutesir
6a5275b60b Apply Xcode recommended project settings + lock to portrait
- Bump LastUpgradeCheck/scheme to Xcode 2620
- Enable DEAD_CODE_STRIPPING, ENABLE_USER_SCRIPT_SANDBOXING, and
  STRING_CATALOG_GENERATE_SYMBOLS across targets
- Set CURRENT_PROJECT_VERSION to 8
- Lock iPhone orientation to portrait; normalize iPad orientations
- Use explicitFileType for app/appex product references

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-20 19:07:48 +03:00
kutesir
9e01a7e7b5 Settings: add "Time Milestones" toggle for period notifications (KC-31 #2)
Adds a Customize → "Time Milestones" toggle bound to the App-Group
kisani.periodMilestones flag (so NotificationManager reads the same value).
Flipping it reschedules immediately — off clears the day/week/month/year
notifications, on re-schedules them. Defaults on. Closes the pending #2 from the
KC-31 audit; #6 (configurable thresholds/snooze) remains pending.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-17 00:10:47 +03:00
kutesir
694db506b6 Audit fixes: lock-screen sync, live notification urgency, batch postpone (KC-31)
- Lock-screen Mark Complete / Snooze now push to iCloud (CloudSyncManager) and
  reload widget timelines, so background completions reach iCloud/Watch/widgets
  immediately instead of waiting for the next app run.
- Notification copy ("Urgent — tap to mark complete" + evening check-in) now uses
  the live computed Matrix quadrant: reschedule snapshots urgent task IDs from
  displayQuadrant and threads them into scheduleTasks/notifBody, instead of the
  stale stored task.quadrant.
- postponeAllOverdue now mirrors single postpone (clears urgencyOverride and
  re-syncs the stored quadrant per task).
- ISSUES.md: added KC-31 (audit), corrected KC-30 (midnight day/week/month/year);
  left #2 (period-notif Settings toggle) and #6 (configurable thresholds/snooze)
  as pending; noted #1 (Move lists 3 quadrants) as intentional KC-25 behavior.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-17 00:03:22 +03:00
kutesir
cb0dc3e271 Add Apple Watch app (TickTick-style) + midnight period notifications
WenzaWatch (new watchOS target, embed disabled until the watchOS platform is
installed — see project.yml note):
- Today list of actionable tasks (dated today/overdue + recurring occurrences),
  tap-to-complete with haptics, and add-via-dictation.
- Reads/writes the same tasks through the shared iCloud KV store that
  CloudSyncManager mirrors on the phone; watch entitlement uses the iOS app's
  kvstore identifier so they share one store. New tasks include all required
  TaskItem keys so the phone decodes them.
- NOTE: not compiled here (watchOS SDK absent on this machine); iOS build
  verified green with the embed commented out.

Period notifications: day/week/month/year now all fire at midnight (00:00),
adding the daily "One more day passed." (Pretty Progress style).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-16 23:50:37 +03:00
kutesir
bcb66ef691 Workout "mark all complete" + recurring-task done confirmation notification
- Workout window: the header ⋯ menu gains "Mark All Complete" and "Clear All
  Sets" (Manage Workouts preserved). New WorkoutViewModel.setAllSetsDone(_:)
  ticks every set across all sections/exercises and runs the normal completion/
  logging path.
- Recurring tasks: completing an occurrence now posts a quiet "✓ Done. Repeats
  <tomorrow / in 1 week / …>" confirmation via NotificationManager
  .notifyRecurringCompleted, phrased from the next active occurrence. Hooked
  into TaskViewModel.toggleOccurrence; un-completing does nothing.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-16 23:20:13 +03:00
kutesir
e7e8d07100 ISSUES.md: log KC-27–30 and KC-26 tracked-event follow-up
- KC-26: documented the tracked-event auto-advance follow-up (trackedTask).
- KC-27: Today ⋯ menu — Background, Group & Sort, Select.
- KC-28: clearer Snooze labels on notifications and the in-app Postpone menu.
- KC-29: lock-screen Progress Dots legibility.
- KC-30: period milestone notifications (week / month / year).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-16 02:43:31 +03:00
kutesir
48e2f4fc10 Widgets/notifications: tracked-event auto-advance, lock-screen dots, Snooze labels, period milestones
- Bars widget: tracked ("Track Countdown") events now also advance — a completed
  or past (non-recurring) tracked event falls through to the next-nearest, and the
  refresh boundary covers the tracked event's expiry. Recurring tracked events
  still roll forward. Completes the KC-26 auto-advance for all paths.
- Progress Dots on the lock screen (accessoryRectangular): show 28 larger dots
  instead of 100 tiny ones, render with .primary for the vibrant tint, and use
  AccessoryWidgetBackground so it's legible (Home Screen keeps the 100-dot grid).
- Notifications: snooze action labels now read "Snooze 15 min/30 min/1 hour/
  2 hours" (lock screen) and "Snooze 15 Minutes…" (in-app Postpone menu).
- New period milestone notifications: recurring "One more week/month/year passed"
  at end of week (Sun 20:00), month (1st 09:00), and year (Jan 1 09:00); gated by
  a kisani.periodMilestones flag, scheduled via the existing reschedule path.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-16 02:12:52 +03:00
kutesir
87c999c676 Today menu features, Matrix Move trim, widget auto-advance, Wenza logo (KC-23–26)
- Today ⋯ menu: implemented the placeholder actions.
  - Background: 5 selectable themes (Default/Warm/Cool/Mono/Paper), persisted.
  - Group & Sort: group by Date (default)/Priority/Category/Quadrant and sort by
    Smart/Due/Priority/Title; non-date groupings render via UpcomingSection.
  - Select: multi-select sheet with Select All/Deselect All and bulk
    Complete/Priority/Move/Delete.
  New isolated file TodayMenuFeatures.swift; added TaskViewModel.allActiveRows.
- Matrix "Move" submenu now lists only the other three quadrants (excludes the
  current one) using clear matrix labels.
- Event Countdown (Bars) widget auto-advances: each timeline entry re-resolves
  "nearest to finish" as of its own time and refreshes at the moment the current
  event expires, so it rolls to the next event on expiry/completion.
- Splash wordmark rebranded to "wenza." (regular-weight typewriter + orange dot).
- ISSUES.md: logged KC-23 (Pick Date + time-based reminders), KC-24 (Wenza
  rebrand), KC-25 (Move trim), KC-26 (widget auto-advance).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 19:53:14 +03:00
kutesir
81813530c8 Rebrand app to "Wenza" (display name + user-facing copy)
Renames the user-facing app to Wenza without changing identity:
- CFBundleDisplayName → "Wenza" (app) and "Wenza Widgets" (widget extension).
- Calendar/Health permission prompts now reference Wenza.
- In-app copy updated: sidebar title, Auth + Onboarding screens, Settings
  about/help rows, feedback email subject, tutorial text, widget description.

Bundle identifier, App Group, UserDefaults storage keys, team ID, and the
internal Xcode target/project names are intentionally unchanged, so this stays
the same App Store app with no user data loss. App Store listing name must be
changed separately in App Store Connect.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 18:31:51 +03:00
kutesir
ac147c55b0 Matrix/menus: Pick Date everywhere, time-based reminders, notification snooze (KC-21)
- Date menu now offers "Pick Date" (and Edit) in the Matrix grid cards and the
  Calendar reschedule/move menu, matching the tasks view — both open the shared
  TaskEditSheet via a new editingTask sheet.
- Reworked the reminder picker (TaskDatePickerSheet, shared by all entry points)
  from day/week offsets to sensible time-based ones relative to the event time:
  None, On time, 5 min, 30 min, 1 hour, 1 day, plus a Minutes/Hours/Days custom
  wheel. Existing absolute reminderDates migrate to the nearest minute offset.
- Notifications gain Snooze actions (15/30/60/120 min) that reschedule the task
  reminder; postpone now clears the Matrix urgency override.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 18:14:10 +03:00
kutesir
4b16e8587d Matrix: TickTick-faithful placement — axis-split urgency, category windows, Manual override (KC-21)
Refines the Eisenhower Matrix toward TickTick's "view over attributes" model:

- Split the single quadrant override into two axes: importance is always
  Priority (High = top row); urgency is date-derived unless manually pinned
  via the new `urgencyOverride` flag. Horizontal drags pin urgency instead of
  fabricating/destroying a real due date; vertical drags set Priority directly.
- A drag only flags "Manual" when the target column disagrees with the
  date-derived urgency, so dropping into a task's natural column stays on Auto.
- Tiles show a "Manual" badge; the task menu gains "Reset Matrix Urgency".
- Urgency window is now 3 days by default, widened to 14 for prep-heavy event
  types (birthday/domain/annual + subscription/renewal/exam/deadline titles).
- New tasks no longer default to today — no date stays no date (not urgent).
- Stored `quadrant` is kept synced to the computed placement so task color and
  widget accents follow the Matrix; Settings stats count by displayQuadrant.
- Completed tasks are hidden from the Matrix grid by default (toggle to show).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 13:22:25 +03:00
kutesir
fae56cb2a8 Matrix: backfill legacy task priorities so birthdays place correctly (KC-21)
The Priority defaults only fired at creation, so tasks saved before Priority
existed (e.g. birthdays) all had `.none` and fell to the Matrix bottom row
instead of Q2. Added a one-time, flag-guarded migration on load that applies
the KisaniCal category defaults (birthday/domain/annual/exam/subscription →
High, workout → Medium) to existing `.none`-priority tasks.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 12:59:48 +03:00
kutesir
3592d2a9d8 Matrix: TickTick-style Priority×deadline view + Progress Dots widget (KC-21)
Matrix (KC-21): the Eisenhower Matrix is now a computed view of tasks.
Importance = Priority (High = top row), urgency = deadline proximity, so
tasks auto-place into Q1–Q4. Added a persisted `matrixOverride` so a manual
drag pins the task and syncs its Priority to that row (top → High, moving
down demotes High → Medium) and never gets forced back; editing Priority,
due date, or the editor clears the override to re-place live. New tasks get
KisaniCal priority defaults (birthday/domain/annual/exam/subscription → High,
workout → Medium). AddTaskSheet drops the manual quadrant picker for a
Priority menu (pick Priority + Date only).

Also includes in-progress Progress Dots widget work (day/week/month/custom
event modes, App-Group anchor, recurrence-aware spans).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 12:40:39 +03:00
kutesir
2b48509a35 Bars widget: recurrence-aware progress in ALL paths (KC-20 follow-up)
The real bug behind "Leona's Birthday, 5 days left" showing 0% filled:
Mode B (recurring span: previous -> next occurrence) only ran for
app-tracked events, while the upcoming-queue / picked-event paths still
anchored from first-seen (so a yearly birthday started at 0%).

- EventEntity now carries isRecurring/recurrenceLabel (populated from the
  shared task data) so the auto-select and picker paths know the rule.
- resolveSpan() centralizes the window: recurring -> prev/next occurrence
  everywhere; one-off -> explicit start / tracking date / first-seen.
- A yearly birthday 5 days away now reads ~98% (≈360/365 days elapsed).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-11 23:20:46 +03:00
kutesir
b6eaba78ba Bars widget: app-driven "Track Countdown" with zero config (KC-20)
- Task long-press menu gains Track/Stop Tracking Countdown, storing the
  tracked event id + tracking date in the App Group (CountdownTracker)
  and reloading widget timelines.
- The widget reads the tracked event directly and bypasses its config:
  Mode A (one-off) = tracking date -> event date; Mode B (recurring) =
  previous occurrence -> next occurrence via the task's recurrence rule.
- WidgetTask decodes isRecurring/recurrenceLabel from the shared JSON.
- With nothing tracked, the configured modes remain the fallback.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-11 23:05:33 +03:00
kutesir
a7639f6454 Widgets: align every widget to the exact logo palette (KC-19)
WidgetTheme now uses the true brand colors — accent RGB(232,98,42) on
the logo's dark RGB(26,29,34) — replacing a near-miss orange on slate.
Bars countdown, My Tasks, and Day Progress inherit it; Task Live
Activity drops its hardcoded accent/black tint for the theme; lock
screen rings tint brand orange.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-11 19:33:01 +03:00
kutesir
0606d9c3aa Bars widget: fix empty progress bar + dead-feeling date pickers (KC-18)
- Persist a per-event "first tracked" anchor in the App Group instead of
  resetting the progress start to now on every refresh (auto mode), so
  the bar actually fills toward the event. Explicit "Counting from" wins
  when it's before the event date.
- Make Event date / Counting from optional ("Choose") — the same-second
  defaults produced a zero-length span (always 0%) and looked broken.
- Pre-render timeline entries every 30 min (12h horizon) so the bar and
  time-left label move between refreshes instead of only at midnight.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-11 18:16:43 +03:00
kutesir
49b22b5f2f Widgets: remove Event Countdown (dots) + Tasks Done Today (KC-17)
Drop the dot-grid EventCountdownWidget and TasksCompleteWidget from the
gallery: widget structs, their views, the todayTaskCompletion() helper
(only consumer), and bundle registrations. Shared event plumbing
(EventConfigIntent/Provider/Entry, CycleCountdownUnitIntent) stays for
the surviving Event Countdown (Bars); DotGrid stays for Day Progress.
Remaining widgets untouched.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-11 11:49:35 +03:00
kutesir
443fe8504f Workout check-in: missed state + rest-day compensation (KC-16)
- Notification actions on workout reminders/check-ins: "Yes, I completed
  it", "No, I didn't" (marks the day missed, then asks to compensate on a
  rest day), "Remind me in 1 hour" (snooze).
- VM: missedDates + compensations (persisted/synced), per-date
  markWorkoutDone/Missed, workoutStatus, upcomingRestDays,
  scheduleCompensation, promptCompensation, checkPendingMissed.
  program(for:) returns a compensation program on its rest day.
- CompensationSheet: pick a rest day or keep as missed; compensation days
  get a one-off "Missed workout recovery" reminder.
- Long-press menus: workout row (Done / Not Done / Compensate) in Today +
  Calendar; exercise cards (Mark as Done / Not Done). All actions are
  per-date — one occurrence never affects another.
- Workout row shows Pending / Done / Missed + "Compensation" label.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-10 23:06:58 +03:00
kutesir
f252fc7707 fix: remove unused 'cal' in statBuckets (warning)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-10 17:36:35 +03:00
kutesir
0362d586e5 Widgets: bars countdown widget + unified slate/orange theme (KC-15)
- New EventBarsWidget ("Event Countdown (Bars)") using BarGrid + EventBarsView,
  with a fine "X hr, Y min left" label; registered in the bundle.
- Re-themed every home-screen widget to the shared WidgetTheme (dark slate
  background + brand orange): Day Progress, Tasks Done Today, Event Countdown
  (dots), My Tasks. Lock-screen widgets keep .thinMaterial (system-tinted).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-10 17:31:55 +03:00
kutesir
063df4ba03 Widgets: add shared WidgetTheme + BarGrid (foundation)
Groundwork for the slate-background / brand-orange widget re-theme and the
new bar-style event countdown:
- WidgetTheme (dark slate background, brand orange accent, text tones).
- BarGrid: vertical-bar (comb) progress view.

Re-theming the existing widgets and adding the bars widget follows.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-10 17:18:52 +03:00
kutesir
a191cebeed chore: set version 2.0 (5) to match current uploads
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-10 10:29:17 +03:00
kutesir
9768af0017 Add Workout Stats screen (daily/weekly/monthly) — KC-14
Aggregate the per-day workout logs into performance stats:
- WorkoutDayLog.volume + StatPeriod/WorkoutStat/StatBucket.
- WorkoutViewModel.stat/statInterval/statBuckets (current vs previous).
- WorkoutStatsView: Day/Week/Month selector, Workouts/Sets/Volume cards
  with %-delta vs previous period, metric chooser, and a Swift Charts bar
  trend. Reached via a chart button in the Workout header.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-10 10:21:12 +03:00