Compare commits

..
2 Commits
Author SHA1 Message Date
tomas.kracmarandClaude Sonnet 5 9e3dd93939 Release 1.9.2
Fix Sync Now toolbar button showing icon-only, no text label. Confirmed
via screenshot that 1.9.1's sidebar/toolbar fixes worked; this was the
remaining clarity issue -- forced .labelStyle(.titleAndIcon) so the label
always shows.

All unit tests pass.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-27 17:48:42 +02:00
tomas.kracmarandClaude Sonnet 5 622a85f4fd Release 1.9.1
Fix sidebar not clickable: List(SidebarSection.allCases, selection:) ties
selection to Identifiable's id (String) while the binding was typed
SidebarSection? -- rebuilt with the standard List(selection:) { ForEach {
.tag(section) } } pattern instead, plus .listStyle(.sidebar).

Fix "Write to calendars" hard to find: still present as the Dry Run/Write
toolbar segmented control, but likely lost to toolbar overflow alongside a
status readout, Sync Now, and the overflow menu all in one
ToolbarItemGroup. Moved status out to .navigationSubtitle (native, never
overflows), toolbar now just Dry Run/Write, Sync Now/Cancel, overflow menu.

All unit tests pass.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-27 17:44:44 +02:00
3 changed files with 41 additions and 25 deletions
+4 -4
View File
@@ -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;
+24 -21
View File
@@ -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(
+13
View File
@@ -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