From 2904c0c652157f1c84a40e8b528f6b0053789568 Mon Sep 17 00:00:00 2001 From: Michael Cerisano Date: Sat, 17 Oct 2026 10:27:42 -0400 Subject: [PATCH] Draw a compact BitMatch menu bar mark --- BitMatch/App/MacMenuBarCompanion.swift | 40 ++++++++++++++++++++++++-- CHANGELOG.md | 2 +- 2 files changed, 38 insertions(+), 4 deletions(-) diff --git a/BitMatch/App/MacMenuBarCompanion.swift b/BitMatch/App/MacMenuBarCompanion.swift index 698a19b2..4f2d5a13 100644 --- a/BitMatch/App/MacMenuBarCompanion.swift +++ b/BitMatch/App/MacMenuBarCompanion.swift @@ -144,9 +144,8 @@ struct MacMenuBarStatusLabel: View { } .accessibilityLabel("BitMatch, \(presentation.title), \(presentation.percentText ?? "progress not yet measured")") } else { - Image(systemName: "square.on.square") - .resizable() - .scaledToFit() + MenuBarBrandMark() + .stroke(Color.primary, style: StrokeStyle(lineWidth: 1.7, lineCap: .round, lineJoin: .round)) .accessibilityLabel("BitMatch") } } @@ -154,6 +153,41 @@ struct MacMenuBarStatusLabel: View { } } +/// Two copies and a check, drawn as a single-color menu bar template. The +/// normal app icon loses its fine segmented ring at this size. +private struct MenuBarBrandMark: Shape { + func path(in rect: CGRect) -> Path { + let side = min(rect.width, rect.height) + let origin = CGPoint(x: rect.midX - side / 2, y: rect.midY - side / 2) + let unit = side / 18 + func point(_ x: CGFloat, _ y: CGFloat) -> CGPoint { + CGPoint(x: origin.x + x * unit, y: origin.y + y * unit) + } + + var mark = Path() + // Only the exposed edges of the rear copy are drawn; lines do not show + // through the foreground copy when macOS changes the menu bar color. + mark.move(to: point(4.9, 13.1)) + mark.addLine(to: point(3.7, 13.1)) + mark.addQuadCurve(to: point(2.5, 11.9), control: point(2.5, 13.1)) + mark.addLine(to: point(2.5, 3.8)) + mark.addQuadCurve(to: point(3.8, 2.5), control: point(2.5, 2.5)) + mark.addLine(to: point(11.8, 2.5)) + mark.addQuadCurve(to: point(13.1, 3.8), control: point(13.1, 2.5)) + mark.addLine(to: point(13.1, 4.8)) + + mark.addRoundedRect( + in: CGRect(x: origin.x + 5 * unit, y: origin.y + 5 * unit, + width: 10.5 * unit, height: 10.5 * unit), + cornerSize: CGSize(width: 1.35 * unit, height: 1.35 * unit) + ) + mark.move(to: point(7.4, 10.2)) + mark.addLine(to: point(9.4, 12.2)) + mark.addLine(to: point(13.1, 8.45)) + return mark + } +} + enum MenuBarDestinationLabel { static func text(for state: DestinationProgressRow.State) -> String { switch state { diff --git a/CHANGELOG.md b/CHANGELOG.md index d857d8c3..63846e09 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,7 @@ All notable changes to this project will be documented in this file. ## [Unreleased] -- Mac: The menu bar shows the current transfer and its destinations, or the most recent result when idle. Closing the main window during a transfer hides it while the work continues; Open BitMatch brings the same window back. Quit still asks before stopping active work. +- Mac: The menu bar shows the current transfer and its destinations, or the most recent result when idle. The idle icon is a small copy-and-check mark. Closing the main window during a transfer hides it while the work continues; Open BitMatch brings the same window back. Quit still asks before stopping active work. - Expanded transfer details show bytes written and read back for each destination, with separate live rates. These are measurements of active file I/O, not a drive-health or verification verdict. - Optional local Apple crash and hang summaries can be added to a diagnostics export. This is off by default, never uploaded by BitMatch, and Apple may not deliver a report. - Keep copy and verification failures below 100% in live progress; the engine emits “Complete” only when all selected files pass the chosen mode. The final safety verdict still includes later ASC MHL and report steps.