Commit Graph

33 Commits

Author SHA1 Message Date
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
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
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
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
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
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
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
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
6817d3e24a Recurring tasks: occurrence engine on calendar + lists (KC-12)
Recurring tasks only ever showed on their single dueDate. Add a
recurrence engine that expands occurrences per-date with independent
per-occurrence completion.

- TaskItem: recurrenceEnd + completedOccurrences (optional, safe decode).
- TaskViewModel: isOccurrence/occurrenceComplete/toggleOccurrence/
  occurrenceCopy/nextOccurrence/occurrences(on:); Daily/Weekly/Monthly/
  Yearly/Every-Weekday; respects recurrenceEnd; works across years.
- toggle() routes recurring rows to per-occurrence completion.
- Date filters re-inject per-date occurrence copies; recurring excluded
  from overdue. Calendar dots + day detail use occurrences(on:).

Also log KC-11 (year display) + KC-12.

Pending: "repeat until" date UI (engine already honors recurrenceEnd).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-09 01:41:15 +03:00
kutesir
f9081cfcf7 Workout: auto-switch to today's program + Rest Day state
The Workout tab kept showing the last-used program (with its old
completion) regardless of the weekly schedule, so a rest day still
displayed a stale, fully-checked workout.

- On a new day, the active program now follows the weekly schedule
  (applyScheduledProgramForToday in resetSetsForNewDayIfNeeded).
- Add isRestDayToday + a Rest Day card shown when today has no scheduled
  program, with a "Work out anyway" override. Header reads "Rest Day".
- Daily set reset (KC-1) unchanged — no day starts pre-checked.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-07 15:47:49 +03:00
kutesir
345415ee7b Bump build number to 2 for re-upload
The previous App Store upload failed on the iPad launch-screen check
because it was a stale archive built before the LaunchScreen fix
(72f4566). Bump CURRENT_PROJECT_VERSION so a fresh archive uploads
cleanly.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-07 14:07:55 +03:00
kutesir
7b98be1d8d Event widget: upcoming-queue auto-select + cleaner config & preview
- Add "Upcoming queue" toggle to the Event Countdown widget. When on, it
  auto-tracks the Nth nearest upcoming event (Nearest / 2nd … 5th, new
  AutoSelectRank enum). When off, track a picked or fully custom event.
- parameterSummary now shows only the relevant fields per mode (toggle +
  auto-select, or event/name/date), so the date input only appears for
  custom events.
- Replace the dense 380-day placeholder with a ~90-day window so the
  gallery preview shows a legible dot grid.

(The interactive task-checkbox widget already exists as "My Tasks".)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-07 12:43:02 +03:00
kutesir
72f4566d91 Fix iPad launch-screen validation failure
App Store upload rejected the bundle: iPad-multitasking apps must declare
a launch screen, but project.yml set an empty UILaunchStoryboardName ("")
and provided no UILaunchScreen.

Add a minimal LaunchScreen.storyboard (blank, systemBackgroundColor) and
point INFOPLIST_KEY_UILaunchStoryboardName at it. Regenerated the project.
Bundle now ships UILaunchStoryboardName=LaunchScreen + compiled
LaunchScreen.storyboardc; builds clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-07 12:20:41 +03:00
kutesir
0a4545f042 Share a single CalendarStore for unified permission state
Introduce CalendarStore.shared so Today, Calendar, and Onboarding read
the same auth status and event cache instead of each spinning up its own
EKEventStore. Trust the system's authoritative authorization status over
the granted bool, debounce repeat permission requests, route denied users
to Settings, and prompt when enabling local calendars without access.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-07 03:27:24 +03:00
kutesir
d0d982f4f5 Add unified task menu, Live Activity, and calendar event actions
Task context menu (new shared TaskMenuItems) consistent across Today,
Matrix, and Calendar: Pin · Date · Move · Priority · Tags · Add to Live
Activity · Delete. Adds TaskViewModel.setPriority.

Live Activity (ActivityKit):
- TaskActivityAttributes shared between app and widget targets
- LiveActivityManager (start/update/end/toggle, iOS 16.1+)
- TaskLiveActivity widget: lock-screen banner + Dynamic Island
- NSSupportsLiveActivities via project.yml; project regenerated

Calendar events (non-subscription, writable only):
- Edit via system EKEventEditViewController (EventEditView wrapper)
- Delete via new CalendarStore.deleteEvent

Also sweeps in prior in-progress edits already present in the working
tree (AuthManager, NotificationManager, TaskItem, task views) and the
updated ISSUES.md.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-06 19:29:25 +03:00
Robin Kutesa
646cdf6a9b feat: My Tasks widget, event countdown redesign, completed section
- My Tasks widget: today's tasks with interactive check-off (ToggleTaskIntent)
  and a "+" that opens the app's add-task sheet; writes preserve all task
  fields and sync via the App Group.
- Event Countdown: AZURE/AWS-style header (accent name + countdown label +
  percent); tap the label to cycle days → weeks → time remaining.
- Today: collapsible "Completed" archive section below Upcoming.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-03 13:31:04 +03:00
Robin Kutesa
8b094691f5 feat: home/lock widgets, Health-aware streaks, workout editor, and entitlement fixes
Widgets (new KisaniCalWidgets extension)
- Event Countdown widget: configurable via AppIntent, pick from your events
  or set a custom name/date; dot-grid progress toward the event.
- Day Progress and Tasks-Done-Today widgets; lock screen widgets.
- Shared dot grid sizes circles to fill the widget evenly at any count.
- Tasks/calendar prefs now persist to the App Group so widgets read live data.

Health & streaks
- Persist HealthKit authorization and re-establish on launch (fixes the
  Today dashboard and streak sync disappearing after relaunch).
- Add a Health permission toggle to onboarding; unify Settings/Profile on the
  manager's state.
- Day streak counts from a logged Health workout OR marking all exercises
  complete; nudge to mark exercises complete even when Health logged the workout.

Workout editor
- Drag to reorder exercises and move them across sections (drag-and-drop);
  swipe to delete.
- Add-exercise sheet: global search and keep-adding-multiple with a running count.

Fixes
- Restore app entitlements link (Sign in with Apple, HealthKit, App Group, iCloud).
- Add HealthKit usage strings; widget Info.plist NSExtension + nested bundle id.
- Calendar "Connect" routes to Settings when access was denied.
- Bake DEVELOPMENT_TEAM and shared versions into project.yml.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-03 12:27:45 +03:00
Robin Kutesa
0647583036 feat: iCloud KV Store sync for tasks, workout data, and settings
- Add NSUbiquitousKeyValueStore entitlement to KisaniCal.entitlements
- New CloudSyncManager: mirrors UserDefaults to iCloud KV Store on every save; restores to UserDefaults on fresh install when local data is missing; observes external changes to pull updates in real-time; guards against 1 MB limit
- TaskViewModel: restore from iCloud on init, push to iCloud on every save
- WorkoutViewModel: restore programs/schedule/activePid/completedDates from iCloud on init, push all four keys on every save
- ContentView: restoreSettings() + backupSettings() on appear and foreground — syncs ~18 small settings keys (appearance, tabs, tutorial flags, calendar prefs, workout notifications)

Co-Authored-By: Kutesir <tqwyy79vzn@privaterelay.appleid.com>
2026-06-01 20:45:26 +03:00
Robin Kutesa
8f8825ac04 Add tooltip coach mark tutorial for new users
- TutorialManager: tracks 4 steps (NLP, Calendar, Matrix, Workout)
  persisted via AppStorage; shown once then never again
- TutorialView: fullscreen overlay with even-odd spotlight cutout,
  animated accent border, ultraThinMaterial callout card, pill progress
  dots, Next/Get started button, and Skip
- TutorialFrameKey: PreferenceKey collects CGRect for each anchor index;
  .tutorialAnchor() modifier wired to FAB (step 0) and all 4 tab buttons
- Callout auto-positions above or below the spotlight based on screen half
- ContentView collects frames via onPreferenceChange and layers the overlay

Co-Authored-By: Kutesir <tqwyy79vzn@privaterelay.appleid.com>
2026-06-01 02:23:12 +03:00
Robin Kutesa
d424a2a814 Clear demo data, namespace storage by user, add profile setup
- Remove all personal sampleTasks from TaskViewModel (tasks start empty)
- Namespace UserDefaults keys by Apple ID for task and workout data isolation
- Add ProfileSetupView for name capture after first Sign in with Apple
- Add updateDisplayName() to AuthManager
- Route to ProfileSetupView when user has no display name yet

Co-Authored-By: Kutesir <tqwyy79vzn@privaterelay.appleid.com>
2026-06-01 01:31:55 +03:00
Robin Kutesa
ac7f2d0614 fix: add AuthManager and AuthView to Xcode project target 2026-06-01 01:16:19 +03:00
kutesir
cab5c4ef05 feat: home screen quick actions (long press app icon)
Adds four UIApplicationShortcutItems registered dynamically at launch:
  • Add Task — opens the add-task sheet immediately, any tab
  • Today — switches to the Today tab
  • Calendar — switches to the Calendar tab
  • Workout — switches to the Workout tab (only registered if the
    workout tab is enabled in settings; re-registers when the setting changes)

Cold-launch and foreground-activation paths both handled via AppDelegate.
ShortcutHandler singleton bridges UIKit callbacks to SwiftUI via @Published.
2026-05-30 02:38:15 +03:00
kutesir
28f8b4a0f1 Remove widget extension
Delete KisaniCalWidget target, Shared/WidgetDataStore, WidgetBridge,
App Group entitlements, and all syncWidget() calls.
2026-05-29 10:36:39 +03:00
kutesir
dc99a3f1f0 fix: widget iOS 17 deployment target + register FloatingTabState
- Widget extension: 16.0 → 17.0 (containerBackground/widget API require 17)
- xcodegen re-run picks up FloatingTabState.swift (was on disk but
  not registered in project.pbxproj after previous xcodegen run)
2026-05-28 20:35:44 +03:00
kutesir
5e232f2d81 feat: add widget extension — small, medium, large
- KisaniCalWidget: 3 sizes sharing the app's design language
  Small: date badge + kisaniCAL. wordmark + task count
  Medium: badge + task list (3 items) + workout row
  Large: badge + full task list (6 items) + workout row

- WidgetDataStore (Shared/): lightweight Codable types for App Group
  UserDefaults cross-process data exchange

- WidgetBridge: TaskViewModel/WorkoutViewModel extensions that sync
  data into App Group and call WidgetCenter.reloadAllTimelines()

- ContentView: calls syncWidget() on appear and scene becoming active

- project.yml + entitlements updated; project regenerated via xcodegen
2026-05-28 19:48:01 +03:00
kutesir
21bbde8980 fix: register SplashView.swift in Xcode project file
Add PBXFileReference, PBXBuildFile, Views group entry, and
PBXSourcesBuildPhase entry so Xcode compiles the new file.
2026-05-28 00:35:13 +03:00
kutesir
f67429de0b Initial commit: KisaniCal iOS app
Task management, Eisenhower matrix, workout logging with persistence,
and calendar with 6 view modes (Month, List, Year, Week, 3 Day, Day).
Custom floating tab bar, dynamic calendar icon, and workout data seeded.
2026-05-27 02:09:44 +03:00