Merge pull request #1508 from kaitranntt/kai/feat/bar-header-version

feat(bar): show app version in the panel header
This commit is contained in:
Kai (Tam Nhu) Tran
2026-06-10 15:42:54 -04:00
committed by GitHub
5 changed files with 49 additions and 6 deletions
@@ -195,6 +195,11 @@ struct BarMenuView: View {
if viewModel.isRefreshing {
ProgressView().controlSize(.small)
}
if let v = BarVersionDisplay.string() {
Text(v)
.font(.caption2)
.foregroundStyle(.secondary)
}
}
.padding(.horizontal, 14)
.padding(.vertical, 10)
+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)
}
}
+1 -1
View File
@@ -681,7 +681,7 @@ export async function handleBarInstall(
if (barIsRunning && !forceLaunch) {
console.log(
'[!] CCS Bar is currently running the previous version. Quit and reopen it (or run `ccs bar`) to use the update.'
'[!] CCS Bar is currently running an older build. Quit it from the menu bar, then run `ccs bar` to relaunch the updated app.'
);
} else if (forceLaunch) {
await launchBar([]);
+6 -5
View File
@@ -2622,8 +2622,9 @@ describe('bar install: already-running detection (Finding 3)', () => {
const allOutput = consoleOutput.join('\n');
// Must print the restart hint
expect(allOutput).toMatch(/currently running the previous version/i);
expect(allOutput).toMatch(/Quit and reopen/i);
expect(allOutput).toMatch(/currently running an older build/i);
expect(allOutput).toMatch(/Quit it from the menu bar/i);
expect(allOutput).toMatch(/run `ccs bar` to relaunch/i);
});
it('when not running after install: prompt path unchanged', async () => {
@@ -2646,7 +2647,7 @@ describe('bar install: already-running detection (Finding 3)', () => {
expect(promptCalled).toBe(true);
const allOutput = consoleOutput.join('\n');
// The restart hint must NOT appear when bar is not running
expect(allOutput).not.toMatch(/currently running the previous version/i);
expect(allOutput).not.toMatch(/currently running an older build/i);
});
it('pgrep error treated as not running: prompt path unchanged', async () => {
@@ -2670,7 +2671,7 @@ describe('bar install: already-running detection (Finding 3)', () => {
// pgrep error treated as not running → normal prompt flow
expect(promptCalled).toBe(true);
const allOutput = consoleOutput.join('\n');
expect(allOutput).not.toMatch(/currently running the previous version/i);
expect(allOutput).not.toMatch(/currently running an older build/i);
});
it('--launch with bar already running: launchBar still invoked', async () => {
@@ -2693,7 +2694,7 @@ describe('bar install: already-running detection (Finding 3)', () => {
expect(launchCalled).toBe(true);
// Restart hint must NOT appear when --launch is passed
const allOutput = consoleOutput.join('\n');
expect(allOutput).not.toMatch(/currently running the previous version/i);
expect(allOutput).not.toMatch(/currently running an older build/i);
});
});