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
2026-05-27 11:00:18 +02:00
2026-05-27 11:00:18 +02:00
2026-05-27 11:00:18 +02:00
2026-03-13 06:56:46 +01:00
2026-05-27 11:00:18 +02:00
2026-05-27 11:00:18 +02:00
2025-08-23 13:32:37 +00:00
2026-04-08 11:56:01 +02:00
2026-04-09 15:55:09 +02:00
2026-03-13 06:56:46 +01:00
2026-03-13 06:56:46 +01:00
2026-03-13 06:56:46 +01:00
2026-03-13 09:08:31 +01:00
2026-03-24 10:36:44 +01:00
2026-04-08 11:56:01 +02:00
2026-04-09 15:55:09 +02:00
2026-05-27 11:00:18 +02:00

BusyMirror

BusyMirror mirrors meetings between your calendars so your availability stays consistent across accounts/devices.

On macOS, BusyMirror now runs as a menu bar app. Use the menu bar icon to sync manually or open the main window; it no longer appears in the Dock.

What it does (current)

  • Route-driven mirroring (multi-source): define Source → Targets routes and run them in one go.
  • Manual selection mirroring: pick a source and targets in the UI and run.
  • Two privacy modes:
    • Private (hide details): mirrors placeholders with prefix + placeholder title (e.g., "🪞 Busy").
    • Mark Private: mirrors prefix + real title, but marks events Private on supported servers (best-effort).
  • DRY-RUN mode: see what would be created/updated/deleted without writing.
  • Activity Log in the app plus persistent file logging on disk.
  • In-app scheduling: install or remove a launchd LaunchAgent from the Scheduled runs section.
  • Menu bar controls: trigger Sync Now, open the main window, or quit without keeping a Dock icon around.
  • Overlap modes: allow, skipCovered, fillGaps.
  • Merge adjacent events with a configurable gap.
  • Time window controls (days back/forward) and Work Hours filter.
  • Accepted-only filter (mirror your accepted meetings only).
  • Cleanup of placeholders, including auto-delete of mirrors whose source disappeared.
  • Refresh Calendars prunes stale saved calendars and routes when calendars are removed from the system.
  • Prefix-based tagging and loop guards to prevent re-mirroring mirrors.
  • Settings: autosave/restore, Import/Export JSON, saved routes for scheduled/headless runs.

Why

Use one calendars confirmed meetings to block time in other calendars (e.g., corporate iPad vs. personal devices).

Build (macOS)

Option A — Xcode

  1. Open BusyMirror.xcodeproj in Xcode.
  2. Select the BusyMirror scheme → My Mac.
  3. Product → Build.
  4. Product → Archive → Distribute App → Copy App (no notarization) to export a .app (or ZIP it for sharing).

Option B — Makefile (reproducible)

  • Build Release: make build-release
  • Package ZIP: make package (creates BusyMirror-<version>-macOS.zip + .sha256)
  • Built app: build/DerivedData/Build/Products/Release/BusyMirror.app

See CHANGELOG.md for notable changes.

CLI (optional)

  • Run from Terminal with --routes to mirror without the UI. Example:
    • BusyMirror.app/Contents/MacOS/BusyMirror --routes "1->2,3; 4->5" --write 1 --days-forward 7 --mode allow --exit
  • Run the routes already saved in the app settings:
    • BusyMirror.app/Contents/MacOS/BusyMirror --run-saved-routes --write 1 --exit
  • Flags exist for privacy, all-day, merge gap, days window, overlap mode, cleanup, and filters.
  • Filters:
    • --exclude-titles "token1, token2"
    • --exclude-organizers "alice@example.com, Example Org"
    • Tokens are comma or newline separated; matching is case-insensitive.

Logs

  • BusyMirror now writes a persistent log file to ~/Library/Logs/BusyMirror/BusyMirror.log.
  • When the file grows large, the previous file is rotated to ~/Library/Logs/BusyMirror/BusyMirror.previous.log.
  • launchd stdout/stderr for scheduled runs are also written in the same folder.
  • In the UI, use Reveal Log File to open the current log directly in Finder.

Scheduling

  • BusyMirror can create its own schedule from the app UI in Scheduled runs.
  • Choose Hourly, Daily, or Weekdays, then click Install Schedule.
  • The installed LaunchAgent runs:
    • /Applications/BusyMirror.app/Contents/MacOS/BusyMirror --run-saved-routes --write 1 --exit
  • This is more stable than index-based --routes, because it uses the routes and per-route options you already configured in the UI.
  • Hourly schedules use launchd StartInterval; daily and weekday schedules use StartCalendarInterval.
  • You can remove the job from the same UI with Remove Schedule, and inspect the generated plist with Reveal LaunchAgent.
  • Note: scheduled headless runs depend on Calendar permission being granted to the installed app. Because these local builds are unsigned, macOS may require re-granting permission after replacing the app bundle with a new build.

Roadmap

See ROADMAP.md

License

MIT — see LICENSE.

S
Description
No description provided
Readme MIT 2.7 MiB
2026-05-27 13:50:24 +00:00
Languages
Swift 98.5%
Makefile 1.5%