8 Commits
Author SHA1 Message Date
tomas.kracmarandClaude Sonnet 5 d40bc986c5 Release 1.8.2
Standard app instead of menu-bar-only: removed LSUIElement from Info.plist.
Dock icon, Cmd+Tab, and the standard app menu (Cmd+Q) are back. The
accessory-app design was making the app hard to quit reliably -- no Dock
icon and no accessible app menu meant the only way to quit was finding the
menu bar dropdown's "Quit BusyMirror" item, which was blocking updates
(old process holds the bundle open, "app in use" on replace). Menu bar
extra stays as a secondary quick-access point.

Verified via `lsappinfo` (type="Foreground", was "UIElement") since this is
an app-type change a screenshot wouldn't show either way.

All unit tests pass.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-27 07:20:19 +02:00
tomas.kracmarandClaude Sonnet 5 edddf65288 Release 1.8.0
- Real Settings (Cmd+,) window via PreferencesView, hosting the pure
  @AppStorage-backed defaults that used to live in the main window's
  "General Settings" panel. Both windows share the same live values since
  they're backed by the same UserDefaults keys.
- Menu bar icon reflects idle/syncing/error state; dropdown shows last-sync
  time/result and whether auto-sync is armed, sourced from the same
  lastRunAtISO/lastRunOK/lastRunSummary keys --status reads.
- ContentView.swift split from ~2000 lines into CalendarsSectionView,
  RoutesSectionView, ScheduleSectionView, LogSectionView + a small shared
  CalendarDisplay.swift. View-layer extraction only — state ownership and
  settings persistence deliberately left alone.
- Fixed a latent revert-on-relaunch bug caught while building the Settings
  window: moving preference controls out of ContentView meant they no longer
  re-triggered saveSettingsToDefaults(), so settings.v2 could go stale and
  the next launch would silently revert a just-changed preference via the
  shared applySnapshot path. Split launch-time restore (routes + selection
  only) from Import's full restore.

All 47 unit tests pass.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-26 21:26:07 +02:00
tomas.kracmarandClaude Sonnet 5 08e9fe5325 Release 1.7.0
- Event-driven background sync, replacing the hourly launchd StartInterval
  poll (only fired if the Mac happened to be awake at that instant, and got
  throttled/coalesced by macOS). Once saved routes exist: registers as a
  login item (SMAppService.mainApp), removes any old launchd schedule, and
  reacts to EKEventStoreChanged (debounced ~3s), NSWorkspace.didWakeNotification,
  plus a 30-min fallback timer as a safety net. Auto-sync always writes,
  matching what the old scheduled runs did.
- This lives in BusyMirrorAppController (app-lifetime), not ContentView —
  ContentView's own EKEventStoreChanged observer is torn down when the main
  window closes, which would make auto-sync a no-op exactly when it needs to
  keep working (window closed, menu-bar-only). Confirmed via MenuBarSupport's
  existing "Sync Now" handler, which already has to reopen the window before
  it can run anything.

All 47 unit tests pass.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-26 18:37:23 +02:00
tomas.kracmarandClaude Sonnet 5 61cea918a6 Release 1.6.1
- Sync reminders: new "Sync reminders when mirroring" option (global and
  per-route) copies source event alarms into mirrored placeholders. CLI flag
  --sync-reminders.
- CLI polish: --help, --list-calendars, --status (all support --json).
  Scheduled/headless runs record last-run time/result/summary. Failure paths
  now exit with distinct nonzero codes (2 = no calendar access, 3 = no saved
  routes) instead of always exiting 0.
- Fix settings silently wiped on upgrade: SettingsPayload's auto-synthesized
  Codable threw on any settings blob missing a field added since (e.g. the
  new syncReminders key), failing the whole decode and letting the next
  autosave persist an empty state over real routes/filters. Custom decode
  now falls back per-field like Route already did, and loadSettingsFromDefaults
  recovers routes from the legacy routes.v1 key if settings.v2 comes back
  empty, repairing installs already hit by this.
- Roadmap: event-driven background sync (SMAppService + EKEventStoreChanged +
  wake notification) and an external MCP wrapper around the CLI queued next;
  decided against direct Google/CalDAV API integration since EventKit already
  covers it via System Settings accounts.

All 47 unit tests pass (45 existing + 2 new SettingsPayloadTests).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-26 13:10:23 +02:00
tomas.kracmar f625ecc263 Release 1.4.0
Fixes:
- Sandbox: add LaunchAgent temporary-exception entitlement
- Mirror URL: fix broken buildMirrorURL (URLComponents with ; separator)
- Cleanup: add bounds check to prevent crash on missing source
- State safety: pass MirrorConfig instead of mutating global @State
- KVC: remove misleading do-catch around setValue:forKey:
- Log cap: limit in-memory log to 2000 lines
- CLI: fix race with calendar loading
- launchCtl: separate stdout/stderr pipes

Features:
- Cancel button for long-running mirrors
- Progress indicator for multi-route runs (Route X of Y)
- Target event cache across routes

Code quality:
- Extract BlockMath, MirrorUtils, EventFilters, MirrorConfig
- Add 45 unit tests across 3 test files
- Refactor mergeGapMin to computed property
- Make log editor read-only

Build:
- Bump version to 1.4.0 (build 18)
- Add LSMinimumSystemVersion 15.5
2026-05-27 11:00:18 +02:00
tomas.kracmar a838e021a1 Docs: refresh README and roadmap 2026-03-13 09:12:19 +01:00
tomas.kracmar eb643ac74d Version update 2025-10-10 10:00:57 +02:00
tomas.kracmar 3ac1faa3dc Initial commit 2025-08-23 15:39:57 +02:00