mirror of
https://github.com/tiennm99/ccs.git
synced 2026-09-02 14:19:56 +00:00
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:
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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([]);
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user