The menu bar icon is now a hamburger whose panel gathers every other
app's status item via the Accessibility API, so tray icons no longer
need to be hunted down across the menu bar. Items can be opened from
the panel, and a best-effort toggle pushes them off the real menu bar
so they only live there.
💘 Generated with Crush
Assisted-by: Crush:deepseek-v4.1-flash
71 lines
3.4 KiB
Swift
71 lines
3.4 KiB
Swift
import AppKit
|
|
import ApplicationServices
|
|
|
|
/// Headless verification for `swift run SimpleTopBar --self-test`.
|
|
/// Exercises the background-app overflow helpers and reports the live split
|
|
/// between regular and background applications, then exits 0/1.
|
|
func runSelfTest() {
|
|
var failures = 0
|
|
|
|
func check(_ condition: Bool, _ label: String) {
|
|
print((condition ? "PASS" : "FAIL") + ": " + label)
|
|
if !condition { failures += 1 }
|
|
}
|
|
|
|
// clampedVisibleCount never exceeds the total and never goes negative.
|
|
check(AppListModel.clampedVisibleCount(current: 10, total: 3) == 3, "clamp caps at total")
|
|
check(AppListModel.clampedVisibleCount(current: -2, total: 5) == 0, "clamp floors at zero")
|
|
check(AppListModel.clampedVisibleCount(current: 2, total: 5) == 2, "clamp keeps valid value")
|
|
|
|
// nextVisibleCount pages by backgroundPageSize and stops at the total.
|
|
let page = AppListModel.backgroundPageSize
|
|
check(AppListModel.nextVisibleCount(current: page, total: 100) == page * 2, "show more pages forward")
|
|
check(AppListModel.nextVisibleCount(current: 4, total: 5) == 5, "show more stops at total")
|
|
check(AppListModel.nextVisibleCount(current: 5, total: 5) == 5, "show more is a no-op at the end")
|
|
|
|
// The section reveals exactly `min(visibleCount, total)` entries.
|
|
let apps = (0..<12).map { index in
|
|
RunningApp(
|
|
id: pid_t(index + 1),
|
|
name: "App \(index)",
|
|
bundleID: nil,
|
|
icon: NSImage(),
|
|
isHidden: false,
|
|
isActive: false
|
|
)
|
|
}
|
|
let hidden = max(0, apps.count - 3)
|
|
check(hidden == 9, "overflow count hides the tail")
|
|
|
|
// Live snapshot: the split must cover every running app exactly once.
|
|
let running = NSWorkspace.shared.runningApplications.filter {
|
|
$0.bundleIdentifier != Bundle.main.bundleIdentifier
|
|
}
|
|
let regular = running.filter { $0.activationPolicy == .regular }.count
|
|
let background = running.filter { $0.activationPolicy != .regular }.count
|
|
print("running=\(running.count) regular=\(regular) background=\(background)")
|
|
check(regular + background == running.count, "every running app is classified")
|
|
|
|
// Menu bar item discovery degrades gracefully when it cannot read another
|
|
// app's UI (accessibility not granted, or the element has no extras bar).
|
|
let systemWide = AXUIElementCreateSystemWide()
|
|
check(MenuBarItemManager.extrasMenuBar(of: systemWide) == nil, "no extras bar on a non-app element")
|
|
check(MenuBarItemManager.children(of: systemWide) == nil, "no children on a non-app element")
|
|
|
|
let element = AXUIElementCreateSystemWide()
|
|
let first = MenuBarItem(id: "1-0", pid: 1, appName: "A", title: "T", appIcon: NSImage(), element: element)
|
|
let same = MenuBarItem(id: "1-0", pid: 1, appName: "A", title: "T", appIcon: NSImage(), element: element)
|
|
let different = MenuBarItem(id: "1-1", pid: 1, appName: "A", title: "T", appIcon: NSImage(), element: element)
|
|
check(first == same, "menu bar items compare by id and title")
|
|
check(first != different, "different ids are not equal")
|
|
|
|
let authorized = AXIsProcessTrusted()
|
|
print("accessibility authorized=\(authorized)")
|
|
if authorized {
|
|
print("menu bar items discovered=\(MenuBarItemManager.enumerate().count)")
|
|
}
|
|
|
|
print(failures == 0 ? "ALL PASS" : "\(failures) FAILURE(S)")
|
|
exit(failures == 0 ? 0 : 1)
|
|
}
|