import SwiftUI import AppKit import ServiceManagement @main struct SimpleTopBarApp: App { @NSApplicationDelegateAdaptor(AppDelegate.self) private var appDelegate var body: some Scene { // The app is a menu bar extra, so it has no real windows. Settings { EmptyView() } } } /// Owns the menu bar status item directly (instead of `MenuBarExtra`) so the /// item can be given an `autosaveName` and a persisted preferred position. On a /// full menu bar, a brand new item lands under the notch and is invisible; /// writing `NSStatusItem Preferred Position ` into defaults puts it in a /// visible slot, and the autosave name lets the user's own drag persist. final class AppDelegate: NSObject, NSApplicationDelegate { private var statusItem: NSStatusItem? private var popover: NSPopover? func applicationDidFinishLaunching(_ notification: Notification) { if CommandLine.arguments.contains("--self-test") { runSelfTest() return } if CommandLine.arguments.contains("--dump-items") { dumpMenuBarItems() return } // Single instance: stale copies keep a menu bar icon around after an // update and swallow clicks meant for the new instance. let myPid = ProcessInfo.processInfo.processIdentifier let others = NSRunningApplication.runningApplications(withBundleIdentifier: "dev.tzgyn.simpletopbar") .filter { $0.processIdentifier != myPid } for other in others { other.forceTerminate() } AppListModel.shared.start() MenuBarItemManager.shared.start() // Create the hamburger before the cover so its own icon is never covered. setupStatusItem() MenuBarItemManager.shared.refresh() if CommandLine.arguments.contains("--diag-press") { DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) { if let item = MenuBarItemManager.enumerate().first(where: { $0.title.lowercased().contains("wireguard") }) { NSLog("diag-press: pressing \(item.appName)") _ = AXUIElementPerformAction(item.element, kAXPressAction as CFString) } } DispatchQueue.main.asyncAfter(deadline: .now() + 3.5) { dumpWindows() } return } if CommandLine.arguments.contains("--diag-cover") { DispatchQueue.main.asyncAfter(deadline: .now() + 3.0) { dumpWindows() } return } if CommandLine.arguments.contains("--diag") { DispatchQueue.main.asyncAfter(deadline: .now() + 2.5) { dumpMenuBarItems() } return } if CommandLine.arguments.contains("--diag-hide") { MenuBarHider.shared.enabled = true DispatchQueue.main.asyncAfter(deadline: .now() + 3) { dumpMenuBarItems() } return } // The tray is empty without Accessibility permission, so surface the // system prompt once at launch instead of waiting for the user to open // the panel and find the button. if !AccessibilityPermission.isGranted, !UserDefaults.standard.bool(forKey: "didRequestAccessibility") { UserDefaults.standard.set(true, forKey: "didRequestAccessibility") AccessibilityPermission.request() } } func applicationWillTerminate(_ notification: Notification) { // Never leave the menu bar resized behind us. MenuBarHider.shared.restore() } private func setupStatusItem() { let name = "SimpleTopBar" let positionKey = "NSStatusItem Preferred Position \(name)" // Only seed the position on first run so the user's own drag wins and // persists through the autosave name. if UserDefaults.standard.object(forKey: positionKey) == nil { UserDefaults.standard.set(100.0, forKey: positionKey) } let item = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength) item.autosaveName = name if let button = item.button { let image = NSImage(systemSymbolName: "line.3.horizontal", accessibilityDescription: name) image?.isTemplate = true button.image = image button.target = self button.action = #selector(togglePopover) button.setAccessibilityLabel("SimpleTopBar") } statusItem = item let popover = NSPopover() popover.behavior = .transient popover.animates = false popover.contentViewController = NSHostingController( rootView: TopBarView( model: AppListModel.shared, menuBar: MenuBarItemManager.shared, hider: MenuBarHider.shared ) ) self.popover = popover } @objc private func togglePopover() { guard let popover, let button = statusItem?.button else { return } if popover.isShown { popover.performClose(nil) } else { NSApp.activate(ignoringOtherApps: true) popover.show(relativeTo: button.bounds, of: button, preferredEdge: .minY) popover.contentViewController?.view.window?.makeKey() } } } enum LoginItem { static var isEnabled: Bool { SMAppService.mainApp.status == .enabled } static func toggle() -> Bool { let service = SMAppService.mainApp do { if service.status == .enabled { try service.unregister() } else { try service.register() } return true } catch { return false } } }