Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9e3dd93939 | ||
|
|
622a85f4fd |
@@ -410,7 +410,7 @@
|
|||||||
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "-";
|
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "-";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
COMBINE_HIDPI_IMAGES = YES;
|
COMBINE_HIDPI_IMAGES = YES;
|
||||||
CURRENT_PROJECT_VERSION = 28;
|
CURRENT_PROJECT_VERSION = 30;
|
||||||
ENABLE_PREVIEWS = YES;
|
ENABLE_PREVIEWS = YES;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
INFOPLIST_FILE = BusyMirror/Info.plist;
|
INFOPLIST_FILE = BusyMirror/Info.plist;
|
||||||
@@ -421,7 +421,7 @@
|
|||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/../Frameworks",
|
"@executable_path/../Frameworks",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 1.9.0;
|
MARKETING_VERSION = 1.9.2;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.cqrenet.BusyMirror;
|
PRODUCT_BUNDLE_IDENTIFIER = com.cqrenet.BusyMirror;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
REGISTER_APP_GROUPS = YES;
|
REGISTER_APP_GROUPS = YES;
|
||||||
@@ -440,7 +440,7 @@
|
|||||||
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "-";
|
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "-";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
COMBINE_HIDPI_IMAGES = YES;
|
COMBINE_HIDPI_IMAGES = YES;
|
||||||
CURRENT_PROJECT_VERSION = 28;
|
CURRENT_PROJECT_VERSION = 30;
|
||||||
ENABLE_PREVIEWS = YES;
|
ENABLE_PREVIEWS = YES;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
INFOPLIST_FILE = BusyMirror/Info.plist;
|
INFOPLIST_FILE = BusyMirror/Info.plist;
|
||||||
@@ -451,7 +451,7 @@
|
|||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/../Frameworks",
|
"@executable_path/../Frameworks",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = 1.9.0;
|
MARKETING_VERSION = 1.9.2;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.cqrenet.BusyMirror;
|
PRODUCT_BUNDLE_IDENTIFIER = com.cqrenet.BusyMirror;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
REGISTER_APP_GROUPS = YES;
|
REGISTER_APP_GROUPS = YES;
|
||||||
|
|||||||
@@ -568,22 +568,25 @@ struct ContentView: View {
|
|||||||
|
|
||||||
@ViewBuilder
|
@ViewBuilder
|
||||||
private var sidebarView: some View {
|
private var sidebarView: some View {
|
||||||
List(SidebarSection.allCases, selection: $selectedSection) { section in
|
List(selection: $selectedSection) {
|
||||||
Label {
|
ForEach(SidebarSection.allCases) { section in
|
||||||
HStack {
|
Label {
|
||||||
Text(section.title)
|
HStack {
|
||||||
if section == .routes && !routes.isEmpty {
|
Text(section.title)
|
||||||
Spacer()
|
if section == .routes && !routes.isEmpty {
|
||||||
Text("\(routes.count)")
|
Spacer()
|
||||||
.font(.caption)
|
Text("\(routes.count)")
|
||||||
.foregroundStyle(.secondary)
|
.font(.caption)
|
||||||
|
.foregroundStyle(.secondary)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
} icon: {
|
||||||
|
Image(systemName: section.icon)
|
||||||
}
|
}
|
||||||
} icon: {
|
.tag(section)
|
||||||
Image(systemName: section.icon)
|
|
||||||
}
|
}
|
||||||
.tag(section as SidebarSection?)
|
|
||||||
}
|
}
|
||||||
|
.listStyle(.sidebar)
|
||||||
.navigationSplitViewColumnWidth(min: 180, ideal: 200)
|
.navigationSplitViewColumnWidth(min: 180, ideal: 200)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -727,6 +730,12 @@ struct ContentView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private var statusSubtitle: String {
|
||||||
|
if let progressText { return progressText }
|
||||||
|
if isRunning { return "Running…" }
|
||||||
|
return hasAccess ? "\(calendars.count) calendars" : "No calendar access"
|
||||||
|
}
|
||||||
|
|
||||||
@ToolbarContentBuilder
|
@ToolbarContentBuilder
|
||||||
private var toolbarContent: some ToolbarContent {
|
private var toolbarContent: some ToolbarContent {
|
||||||
ToolbarItemGroup {
|
ToolbarItemGroup {
|
||||||
@@ -737,15 +746,7 @@ struct ContentView: View {
|
|||||||
.pickerStyle(.segmented)
|
.pickerStyle(.segmented)
|
||||||
.frame(width: 150)
|
.frame(width: 150)
|
||||||
.disabled(isRunning)
|
.disabled(isRunning)
|
||||||
|
.help("Dry Run previews changes without writing. Write actually creates/updates/deletes events.")
|
||||||
HStack(spacing: 6) {
|
|
||||||
Circle()
|
|
||||||
.fill(isRunning ? Color.orange : (appController.lastRunFailed ? Color.red : Color.secondary))
|
|
||||||
.frame(width: 7, height: 7)
|
|
||||||
Text(progressText ?? (isRunning ? "Running…" : (hasAccess ? "\(calendars.count) calendars" : "No access")))
|
|
||||||
.font(.caption)
|
|
||||||
.foregroundStyle(.secondary)
|
|
||||||
}
|
|
||||||
|
|
||||||
if isRunning {
|
if isRunning {
|
||||||
Button("Cancel") { cancelMirror() }
|
Button("Cancel") { cancelMirror() }
|
||||||
@@ -754,6 +755,7 @@ struct ContentView: View {
|
|||||||
startMirrorNow()
|
startMirrorNow()
|
||||||
} label: {
|
} label: {
|
||||||
Label("Sync Now", systemImage: "arrow.triangle.2.circlepath")
|
Label("Sync Now", systemImage: "arrow.triangle.2.circlepath")
|
||||||
|
.labelStyle(.titleAndIcon)
|
||||||
}
|
}
|
||||||
.disabled(!canRunMirrorNow)
|
.disabled(!canRunMirrorNow)
|
||||||
}
|
}
|
||||||
@@ -813,6 +815,7 @@ struct ContentView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
.navigationTitle((selectedSection ?? .routes).title)
|
.navigationTitle((selectedSection ?? .routes).title)
|
||||||
|
.navigationSubtitle(statusSubtitle)
|
||||||
.toolbar { toolbarContent }
|
.toolbar { toolbarContent }
|
||||||
}
|
}
|
||||||
.confirmationDialog(
|
.confirmationDialog(
|
||||||
|
|||||||
@@ -2,6 +2,19 @@
|
|||||||
|
|
||||||
All notable changes to BusyMirror will be documented in this file.
|
All notable changes to BusyMirror will be documented in this file.
|
||||||
|
|
||||||
|
## [1.9.2] - 2026-08-27
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- **"Sync Now" toolbar button showed only its icon**, no text — confirmed via screenshot: the Dry Run/Write toggle and toolbar layout were actually fine (1.9.1's fix worked, sidebar clicks too), but the circular-arrows icon next to it was unlabeled, easy to miss as "Sync Now" entirely. Forced `.labelStyle(.titleAndIcon)` so the text always shows regardless of toolbar width. ([ContentView.swift](BusyMirror/ContentView.swift))
|
||||||
|
|
||||||
|
## [1.9.1] - 2026-08-27
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- **Sidebar not clickable.** `sidebarView` used `List(SidebarSection.allCases, selection:)` (the data-driven initializer), which ties selection to `Identifiable`'s `id` — a `String` for `SidebarSection` — while the binding was typed `SidebarSection?`. Rebuilt with the standard `List(selection:) { ForEach(...) { ... .tag(section) } }` pattern, which binds selection directly to the (`Hashable`) section value with no ambiguity. Also added `.listStyle(.sidebar)`, the standard modifier for a `NavigationSplitView` sidebar (vibrancy, selection color) that was missing.
|
||||||
|
- **"Write to calendars" hard to find.** It was still there as the Dry Run/Write segmented control in the toolbar, but likely lost to toolbar overflow alongside a status readout, Sync Now, and the overflow menu all crammed into one `ToolbarItemGroup`. Moved the calendar-count/status text out of the toolbar entirely into `.navigationSubtitle` (always visible, native, never subject to overflow), leaving the toolbar to just Dry Run/Write, Sync Now/Cancel, and the overflow menu.
|
||||||
|
|
||||||
|
Not independently confirmed by clicking through the UI (no GUI automation available in this environment) — the `List(selection:)` fix follows SwiftUI's standard, unambiguous sidebar-selection pattern.
|
||||||
|
|
||||||
## [1.9.0] - 2026-08-27
|
## [1.9.0] - 2026-08-27
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|||||||
Reference in New Issue
Block a user