import AppKit import ServiceManagement import SwiftUI final class AppDelegate: NSObject, NSApplicationDelegate { static weak var shared: AppDelegate? private var statusItem: NSStatusItem! private var popover: NSPopover! private var floatingWidget: FloatingWidgetWindow? private var webWindow: NSWindow? private let bugStore = BugStore.shared func applicationDidFinishLaunching(_ notification: Notification) { Self.shared = self NSApp.setActivationPolicy(.accessory) setupStatusItem() setupPopover() if let config = AppStateService.shared.config { if config.showFloatingWidget { showFloatingWidget() } if config.launchAtLogin { try? SMAppService.mainApp.register() } } Task { await PollerService.shared.startIfConfigured() // Real-time change push is non-blocking; no-op if the subscribe // base URL isn't configured (falls back to polling). BitableEventService.shared.connect() CalibrationService.shared.start() } } private func setupStatusItem() { statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength) if let button = statusItem.button { button.image = NSImage(systemSymbolName: "ant.fill", accessibilityDescription: "Bugger") button.action = #selector(togglePopover) button.target = self } updateBadge(count: bugStore.activeBugs.count) } private func setupPopover() { popover = NSPopover() popover.contentSize = NSSize(width: 360, height: 500) popover.behavior = .transient popover.contentViewController = NSHostingController(rootView: BugListPopover()) } func updateBadge(count: Int) { if count > 0 { statusItem.button?.title = " \(count)" } else { statusItem.button?.title = "" } } @objc func togglePopover() { guard let button = statusItem.button else { return } if popover.isShown { closePopover() } else { popover.show(relativeTo: button.bounds, of: button, preferredEdge: .minY) popover.contentViewController?.view.window?.makeKey() } } func closePopover() { popover.performClose(nil) } /// Opens the popover programmatically (e.g. to show the OAuth setup view /// when a write fails due to a missing scope). func openPopover() { guard let button = statusItem?.button, !popover.isShown else { return } popover.show(relativeTo: button.bounds, of: button, preferredEdge: .minY) popover.contentViewController?.view.window?.makeKey() } func showFloatingWidget() { guard floatingWidget == nil else { return } floatingWidget = FloatingWidgetWindow() floatingWidget?.orderFrontRegardless() } func hideFloatingWidget() { floatingWidget?.close() floatingWidget = nil } /// Opens (or reuses) a window rendering the Feishu record page in an /// embedded web view — for actions the API can't do, like advancing the /// 流转状态 workflow. func openWebWindow(url: URL, title: String) { if let webWindow { webWindow.title = title (webWindow.contentViewController as? NSHostingController)? .rootView = FeishuWebWindowContent(url: url) // Recover from a collapsed window frame. if webWindow.frame.width < 400 { webWindow.setContentSize(NSSize(width: 1100, height: 800)) webWindow.center() } } else { let window = NSWindow( contentRect: NSRect(x: 0, y: 0, width: 1100, height: 800), styleMask: [.titled, .closable, .resizable, .miniaturizable], backing: .buffered, defer: false ) window.title = title window.contentViewController = NSHostingController( rootView: FeishuWebWindowContent(url: url) ) window.contentMinSize = NSSize(width: 600, height: 400) window.isReleasedWhenClosed = false // Accessory app: ensure the window lands on the active Space. window.collectionBehavior.insert(.moveToActiveSpace) webWindow = window } webWindow?.orderFrontRegardless() webWindow?.makeKeyAndOrderFront(nil) // Accessory app: pull the window to the front explicitly. NSApp.activate(ignoringOtherApps: true) } }