feat(bar): show running app version in the panel header

A right-aligned v{CFBundleShortVersionString} label fills the unused
header space next to the CCS name, styled like the subtitle and hidden
when no bundle version is available (e.g. swift run). The display
logic lives in CCSBarCore as a pure helper with ccs-bar-check
coverage, making the on-screen build identifiable after reinstalls.
This commit is contained in:
Tam Nhu Tran
2026-06-10 15:25:45 -04:00
parent 39ede638d6
commit 3438c3940e
3 changed files with 42 additions and 0 deletions
@@ -192,6 +192,11 @@ struct BarMenuView: View {
Text("usage & accounts").font(.caption2).foregroundStyle(.secondary)
}
Spacer()
if let v = BarVersionDisplay.string() {
Text(v)
.font(.caption2)
.foregroundStyle(.secondary)
}
if viewModel.isRefreshing {
ProgressView().controlSize(.small)
}
+9
View File
@@ -1436,6 +1436,15 @@ do {
check(selectedIdx == 1, "screenPicker: exact hit selects right screen (index 1) for #1502 anchor")
}
// MARK: BarVersionDisplay pure display-string helper
check(BarVersionDisplay.displayString(for: nil) == nil, "version nil raw -> nil (no dangling 'v')")
check(BarVersionDisplay.displayString(for: "") == nil, "version empty raw -> nil (no dangling 'v')")
check(BarVersionDisplay.displayString(for: "1.5.0") == "v1.5.0", "version '1.5.0' -> 'v1.5.0'")
check(BarVersionDisplay.displayString(for: "2.0.0") == "v2.0.0", "version '2.0.0' -> 'v2.0.0'")
// Outside a bundle, string() must not crash and returns nil (no assertion on value bundle absent).
let _ = BarVersionDisplay.string()
// cleanup
try? FileManager.default.removeItem(atPath: tmp)
@@ -0,0 +1,28 @@
import Foundation
/// Pure helper for the version string shown in the bar panel header.
///
/// Kept tiny so it can be tested in CCSBarCheck without a bundle present:
/// the display-string logic is pure-Foundation and has no AppKit dependency.
public enum BarVersionDisplay {
/// Converts a raw version string to a "v{version}" display string.
///
/// - Returns: `"v\(raw)"` when `raw` is non-nil and non-empty; `nil` otherwise.
///
/// This is the logic under test. The actual `Bundle.main` lookup is in `string()`.
public static func displayString(for raw: String?) -> String? {
guard let v = raw, !v.isEmpty else { return nil }
return "v\(v)"
}
/// Returns the display string for the running app's bundle version, or `nil`
/// when the key is absent (e.g. `swift run` outside a bundle).
///
/// Never produces a dangling "v" prefix: if `CFBundleShortVersionString` is
/// missing or empty, returns `nil` so the caller can omit the label entirely.
public static func string() -> String? {
let raw = Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString") as? String
return displayString(for: raw)
}
}