Compare commits
13 Commits
Author | SHA1 | Date | |
---|---|---|---|
3ecf29f499 | |||
eb643ac74d | |||
df06564434 | |||
74b9949610 | |||
6676e62889 | |||
d1fbd4c81f | |||
6ef0feecc1 | |||
aac4de3fb3 | |||
8f80a5f672 | |||
ae40b42e6f | |||
691575c554 | |||
b931f3ba2c | |||
53f21492da |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -19,3 +19,5 @@ ExportOptions.plist
|
||||
# Misc
|
||||
*.swp
|
||||
*.zip
|
||||
*.sha256
|
||||
dist/
|
||||
|
@@ -410,7 +410,7 @@
|
||||
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "-";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
CURRENT_PROJECT_VERSION = 9;
|
||||
ENABLE_PREVIEWS = YES;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_FILE = BusyMirror/Info.plist;
|
||||
@@ -421,7 +421,7 @@
|
||||
"$(inherited)",
|
||||
"@executable_path/../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.0.0;
|
||||
MARKETING_VERSION = 1.3.1;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.cqrenet.BusyMirror;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
REGISTER_APP_GROUPS = YES;
|
||||
@@ -440,7 +440,7 @@
|
||||
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "-";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
CURRENT_PROJECT_VERSION = 9;
|
||||
ENABLE_PREVIEWS = YES;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_FILE = BusyMirror/Info.plist;
|
||||
@@ -451,7 +451,7 @@
|
||||
"$(inherited)",
|
||||
"@executable_path/../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.0.0;
|
||||
MARKETING_VERSION = 1.3.1;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.cqrenet.BusyMirror;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
REGISTER_APP_GROUPS = YES;
|
||||
@@ -465,7 +465,7 @@
|
||||
buildSettings = {
|
||||
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
CURRENT_PROJECT_VERSION = 2;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
MACOSX_DEPLOYMENT_TARGET = 15.5;
|
||||
MARKETING_VERSION = 1.0;
|
||||
@@ -482,7 +482,7 @@
|
||||
buildSettings = {
|
||||
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
CURRENT_PROJECT_VERSION = 2;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
MACOSX_DEPLOYMENT_TARGET = 15.5;
|
||||
MARKETING_VERSION = 1.0;
|
||||
@@ -498,7 +498,7 @@
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
CURRENT_PROJECT_VERSION = 2;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
MARKETING_VERSION = 1.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.cqrenet.BusyMirrorUITests;
|
||||
@@ -513,7 +513,7 @@
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
CURRENT_PROJECT_VERSION = 2;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
MARKETING_VERSION = 1.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.cqrenet.BusyMirrorUITests;
|
||||
|
File diff suppressed because it is too large
Load Diff
28
CHANGELOG.md
Normal file
28
CHANGELOG.md
Normal file
@@ -0,0 +1,28 @@
|
||||
# Changelog
|
||||
# Changelog
|
||||
|
||||
All notable changes to BusyMirror will be documented in this file.
|
||||
|
||||
## [1.3.1] - 2025-10-13
|
||||
- Fix: auto-delete of mirrored placeholders when the source is removed now works even if no source instances remain in the window. Also cleans legacy mirrors without URLs by matching exact times.
|
||||
|
||||
## [1.2.4] - 2025-10-10
|
||||
- Fix: enable “Mirror Now” when Routes are defined even if no Source/Targets are checked in the main window. Button now enables if either routes exist or a manual selection is present.
|
||||
|
||||
## [1.3.0] - 2025-10-10
|
||||
- New: Mark Private option to mirror with prefix + real title and set event privacy on supported servers; available globally and per-route; persisted.
|
||||
- Misc: calendar access fixes, concurrency annotations, accepted‑only filter, settings autosave/restore, Mirror Now enablement.
|
||||
|
||||
## [1.2.3] - 2025-10-10
|
||||
- Fix: reliably save and restore settings between runs via autosave of key options and restoration of source/target selections by persistent IDs.
|
||||
- UX: persist Source and Target selections; rebuild indices on launch so UI matches saved IDs.
|
||||
- Build: bump version to 1.2.3 (build 5).
|
||||
|
||||
## [1.2.1] - 2025-10-10
|
||||
- Fix: reinitialize EKEventStore after permission grant to avoid “Loaded 0 calendars” right after approval.
|
||||
- Fix: attendee status filter uses current user’s attendee `participantStatus == .accepted` instead of unavailable APIs.
|
||||
- Concurrency: mark `requestAccess()` and `reloadCalendars()` as `@MainActor` to satisfy strict concurrency checks.
|
||||
- Dev: add Makefile with `build-debug`, `build-release`, and `package` targets; produce versioned ZIP + SHA-256.
|
||||
|
||||
## [1.2.0] - 2024-09-29
|
||||
- Feature: multi-route mirroring, overlap modes, merge gaps, work hours filter, CLI support, export/import settings.
|
46
Makefile
Normal file
46
Makefile
Normal file
@@ -0,0 +1,46 @@
|
||||
# Simple build and package helpers for BusyMirror
|
||||
|
||||
SCHEME ?= BusyMirror
|
||||
PROJECT ?= BusyMirror.xcodeproj
|
||||
DERIVED ?= build/DerivedData
|
||||
DEST := platform=macOS
|
||||
|
||||
# Extract marketing version from project settings
|
||||
VERSION := $(shell sed -n 's/.*MARKETING_VERSION = \([0-9.]*\);.*/\1/p' $(PROJECT)/project.pbxproj | head -n1)
|
||||
|
||||
.PHONY: all clean build-debug build-release open app package
|
||||
|
||||
all: build-release
|
||||
|
||||
clean:
|
||||
@echo "Cleaning derived data…"
|
||||
xcodebuild -scheme $(SCHEME) -project $(PROJECT) -derivedDataPath $(DERIVED) -destination '$(DEST)' CODE_SIGNING_ALLOWED=NO clean >/dev/null
|
||||
@echo "Done."
|
||||
|
||||
build-debug:
|
||||
@echo "Building Debug…"
|
||||
xcodebuild -scheme $(SCHEME) -project $(PROJECT) -configuration Debug -destination '$(DEST)' -derivedDataPath $(DERIVED) CODE_SIGNING_ALLOWED=NO build
|
||||
|
||||
build-release:
|
||||
@echo "Building Release…"
|
||||
xcodebuild -scheme $(SCHEME) -project $(PROJECT) -configuration Release -destination '$(DEST)' -derivedDataPath $(DERIVED) CODE_SIGNING_ALLOWED=NO build
|
||||
|
||||
# Convenience to open the built app in Finder
|
||||
open: app
|
||||
@open "$<"
|
||||
|
||||
# Path to built app (Release)
|
||||
APP_PATH := $(DERIVED)/Build/Products/Release/BusyMirror.app
|
||||
|
||||
app: build-release
|
||||
@# Ensure the app exists
|
||||
@test -d "$(APP_PATH)" && echo "Built: $(APP_PATH)" || (echo "App not found at $(APP_PATH)" && exit 1)
|
||||
@echo "Version: $(VERSION)"
|
||||
@echo "OK"
|
||||
|
||||
package: app
|
||||
@echo "Packaging BusyMirror $(VERSION)…"
|
||||
@zip -qry "BusyMirror-$(VERSION)-macOS.zip" "$(APP_PATH)"
|
||||
@shasum -a 256 "BusyMirror-$(VERSION)-macOS.zip" | awk '{print $$1}' > "BusyMirror-$(VERSION)-macOS.zip.sha256"
|
||||
@echo "Created BusyMirror-$(VERSION)-macOS.zip and .sha256"
|
||||
|
34
README.md
34
README.md
@@ -2,23 +2,43 @@
|
||||
|
||||
BusyMirror mirrors meetings between your calendars so your availability stays consistent across accounts/devices.
|
||||
|
||||
## What it does (current checkpoint)
|
||||
- Manual “Run” to mirror events across selected routes (Source → Targets).
|
||||
- DRY-RUN mode shows what would happen.
|
||||
- Prefix-based tagging of mirrored events.
|
||||
- Cleanup of placeholders (with confirmation).
|
||||
- Loop/duplicate guards so mirrors don’t replicate themselves.
|
||||
- Time window and merge-gap settings.
|
||||
## 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.
|
||||
- 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.
|
||||
- Prefix-based tagging and loop guards to prevent re-mirroring mirrors.
|
||||
- Settings: autosave/restore, Import/Export JSON.
|
||||
|
||||
## Why
|
||||
Use one calendar’s 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`
|
||||
- Flags exist for privacy, all-day, merge gap, days window, overlap mode, and cleanup.
|
||||
|
||||
## Roadmap
|
||||
See [ROADMAP.md](ROADMAP.md)
|
||||
|
||||
|
21
ROADMAP.md
21
ROADMAP.md
@@ -1,17 +1,26 @@
|
||||
# BusyMirror Roadmap
|
||||
|
||||
## Shipped (highlights)
|
||||
- Route-driven mirroring (multi-source)
|
||||
- Accepted-only filter (mirror your accepted meetings)
|
||||
- Persistent settings with autosave/restore; Import/Export JSON
|
||||
- Overlap modes (allow, skipCovered, fillGaps) and merge-gap
|
||||
- Work Hours filter and title-based skip filters
|
||||
- Privacy: placeholders with prefix + customizable title
|
||||
- 1.3.0: Mark Private option (global + per-route)
|
||||
|
||||
## Next
|
||||
- Source filters (name patterns like `[HOLD]`, `#nomirror`)
|
||||
- Mirror only **Accepted** meetings (exclude tentative/declined)
|
||||
- Persistent settings (routes, window, prefix)
|
||||
- Import/Export settings (.busymirror.json)
|
||||
- Auto-refresh calendars on `EKEventStoreChanged` (live refresh button-less)
|
||||
- Hint near "Mirror Now" indicating run mode (Routes vs Manual)
|
||||
- Better server-side privacy mapping (per-provider heuristics)
|
||||
|
||||
## Then
|
||||
- iOS/iPadOS app (Run Now, Shortcuts, iCloud sync)
|
||||
- UI: route editor & clearer toggles
|
||||
- Signed/notarized binaries and release pipeline
|
||||
- CLI quality: friendlier `--routes` parsing and help flag
|
||||
- “Dry-run by default” preference
|
||||
|
||||
## Later
|
||||
- Background monitoring (macOS)
|
||||
- Smarter cleanup & conflict resolution
|
||||
- iOS/iPadOS helper (Shortcuts integration)
|
||||
- Profiles & MDM/Managed Config support
|
||||
|
24
ReleaseNotes-1.2.3.md
Normal file
24
ReleaseNotes-1.2.3.md
Normal file
@@ -0,0 +1,24 @@
|
||||
# BusyMirror 1.2.3 — 2025-10-10
|
||||
|
||||
This release focuses on reliable settings persistence and quality-of-life fixes from the 1.2.1 hotfix.
|
||||
|
||||
Highlights
|
||||
- Settings persist between runs: autosave key options on change; restore on launch.
|
||||
- Source/Target selection is remembered using calendar IDs and rehydrated into UI indices.
|
||||
|
||||
Fixes and improvements
|
||||
- Save on change for: days back/forward, default merge gap, privacy/copy notes, all-day, accepted-only, overlap mode, title/placeholder prefixes, auto-delete.
|
||||
- Restore saved `selectedSourceID` and `selectedTargetIDs` and rebuild index selections.
|
||||
- Keep backward compatibility with older saved payloads.
|
||||
- Version bump to 1.2.3 (build 5).
|
||||
|
||||
Included from 1.2.1
|
||||
- Reinitialize `EKEventStore` after permission grant to avoid “Loaded 0 calendars”.
|
||||
- Use attendee `participantStatus == .accepted` for accepted-only filter.
|
||||
- Mark `requestAccess()` and `reloadCalendars()` as `@MainActor`.
|
||||
- Makefile for reproducible builds and packaging.
|
||||
|
||||
Build
|
||||
- `make build-release`
|
||||
- `make package` → BusyMirror-1.2.3-macOS.zip and .sha256
|
||||
|
11
ReleaseNotes-1.2.4.md
Normal file
11
ReleaseNotes-1.2.4.md
Normal file
@@ -0,0 +1,11 @@
|
||||
# BusyMirror 1.2.4 — 2025-10-10
|
||||
|
||||
Bugfix release improving route-driven mirroring.
|
||||
|
||||
Fixes
|
||||
- Mirror Now is enabled when routes are defined, even if nothing is checked in the main window. This allows fully route-driven runs without requiring a temporary manual selection.
|
||||
|
||||
Build
|
||||
- `make build-release`
|
||||
- `make package` → BusyMirror-1.2.4-macOS.zip and .sha256
|
||||
|
17
ReleaseNotes-1.3.0.md
Normal file
17
ReleaseNotes-1.3.0.md
Normal file
@@ -0,0 +1,17 @@
|
||||
# BusyMirror 1.3.0 — 2025-10-10
|
||||
|
||||
New
|
||||
- Mark Private option: mirror events with your prefix + real title while marking them Private on supported servers (e.g., Exchange). Co‑workers see the time block but not the details.
|
||||
- Per-route and global toggles for Mark Private; persists in settings and export/import.
|
||||
|
||||
Fixes & improvements
|
||||
- More reliable calendar loading after permission grant (reinit EKEventStore).
|
||||
- Concurrency: `@MainActor` on permission/refresh methods.
|
||||
- Accepted‑only filter via current user attendee `participantStatus`.
|
||||
- Settings autosave and restore (including source/target selections by IDs).
|
||||
- Mirror Now enabled when calendars available; routes or manual selection used as appropriate.
|
||||
|
||||
Build
|
||||
- `make build-release`
|
||||
- `make package` → BusyMirror-1.3.0-macOS.zip and .sha256
|
||||
|
6
ReleaseNotes-1.3.1.md
Normal file
6
ReleaseNotes-1.3.1.md
Normal file
@@ -0,0 +1,6 @@
|
||||
BusyMirror 1.3.1 — Bugfix Release
|
||||
|
||||
- Fix: Auto-delete mirrored placeholders when the source event is removed.
|
||||
- Triggers even if no source instances remain in the selected window.
|
||||
- Also cleans legacy mirrors without mirror URLs by matching exact times.
|
||||
|
Reference in New Issue
Block a user