Three things squashed into one commit since they're all release-prep:
1. Bump CFBundleVersion 2 → 3, CFBundleShortVersionString 0.2.0 →
0.3.0, WorkerPlugin.version & ExpandedView footer string to match.
Aligns with the 5-feature v0.3.0 ship.
2. fix(water): lastWaterReminderHr was an Int hour (0-23) so a
yesterday-fired 17:00 blocked today's 17:00 reminder. Now stores
"yyyy-MM-dd HH" composite key so dedupe is scoped per-day.
3. Holidays.swift: 2026 entry table re-calibrated per typical
State Council 调休 pattern. Notable changes:
- 春节 starts on 除夕 02-16 (not 初一 02-17) per 2024 policy
- 中秋 + 国庆 merged into 10-day 09-25 ~ 10-04 window (since
2026 中秋 09-25 is 6 days before 国庆 10-01)
- 劳动节 renamed from "五一" for clarity
Disclaimer still in header: verify against 国务院 announcement
when published, edit + re-release if 调休 differs.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Core change: both timers stop lying when the Mac sleeps or the user
walks away. Old code measured wallclock from a stored "start"
timestamp, so 4 hours of lid-closed showed up as 4 hours of "sitting"
and a 25-minute focus could quietly stretch across multiple hours
because Timer.scheduledTimer pauses during sleep.
What changed:
- Sit timer now reads CGEventSource HID idle to count only seconds
with real input activity. New Sources/engine/SystemIdle.swift
iterates the user-input event types and takes the min. Idle ≥ 5
min resets the accumulator (counts as a real break). Tick-gap >
30 s flags a sleep/wake and clears too — covers the case where
the user unlocks and idle resets to ~0 instantly.
- Pomodoro switched from "decrement counter every second" to a
wallclock end-time. Persisted to UserDefaults so quit/relaunch
recovers correctly: still-running phases keep counting; phases
whose end-time has already passed get caught up (one focus
credited if missed) and the timer returns to idle.
- Auto-loop with classic 4-pack rhythm: focus → short break →
focus → … on the 4th focus a long break (default 15 min) replaces
the short break, then the cycle resets. Default ON; toggle pill
in the settings row turns it off when the user wants manual
restarts.
- New cycle-progress dot row (●●○○) next to the phase badge so
the user can see where they are in the 4-pack at a glance.
- SitView tip text rewritten to reflect the new semantics: "只统计
你真正在键盘前的时间。离开 5 分钟以上自动归零,午休/会议不会被
算进去。"
User-facing wins:
- Closing the lid for lunch no longer fakes 90 minutes of sitting.
- A 25-min focus that survives a sleep cycle ends accurately.
- Start a focus once, the rhythm runs all morning.
Office-survival toolkit. Five tabs in the notch:
- 🍅 番茄钟: 25/5 focus/break with circular ring timer + tally.
- 🪑 久坐: count-up since last break, dock-bounce alert at 45min.
- 💧 喝水: animated cup, daily goal tracking with progress dots.
- 🏃 下班: H:M:S countdown to clock-out hour with gradient bar.
- 🎉 周末: 天/时/分 to next Saturday + rotating 打工语录.
100% local. UNUserNotificationCenter for alerts, dock-bounce
fallback when authorization denied. UserDefaults persistence.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>