import SwiftUI @main struct BlitztextMacApp: App { @NSApplicationDelegateAdaptor(AppDelegate.self) var appDelegate var body: some Scene { Settings { EmptyView() } } } @MainActor final class AppDelegate: NSObject, NSApplicationDelegate, NSPopoverDelegate { private var statusItem: NSStatusItem! private var popover: NSPopover! private let menuBarStatusController = MenuBarStatusController() let appState = AppState() func applicationDidFinishLaunching(_ notification: Notification) { statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.squareLength) if let button = statusItem.button { menuBarStatusController.attach(to: button) button.action = #selector(togglePopover) button.target = self } popover = NSPopover() popover.contentSize = NSSize(width: 340, height: 480) popover.behavior = .transient popover.delegate = self popover.contentViewController = NSHostingController(rootView: MenuBarView(appState: appState)) NSApp.setActivationPolicy(.accessory) // Hotkey events appState.hotkeyService.onHotkeyEvent = { [weak self] event in self?.handleHotkeyEvent(event) } appState.onMenuBarStatusChange = { [weak self] status in self?.menuBarStatusController.update(to: status) } appState.hotkeyService.start() // Listen for popover dismiss requests (from auto-paste) NotificationCenter.default.addObserver( self, selector: #selector(handleDismissPopover), name: .dismissPopover, object: nil ) DispatchQueue.main.async { [weak self] in self?.showOnboardingIfNeeded() } } @objc private func handleDismissPopover() { appState.isPopoverShown = false popover.performClose(nil) } private func handleHotkeyEvent(_ event: HotkeyEvent) { switch event { case .down(let type): handleHotkeyDown(type) case .up(let type): handleHotkeyUp(type) case .cancel: handleHotkeyCancel() } } private func handleHotkeyDown(_ type: WorkflowType) { guard appState.isConfigured else { return } let mode = appState.appSettings.hotkeyMode switch mode { case .hold: // Hold mode: start recording on key down appState.startWorkflow(type, source: .hotkeyBackground) case .toggle: // Toggle mode: if already recording same workflow, stop it if let active = appState.activeWorkflow, active.type == type, active.phase.isActive { active.stop() } else { appState.prepareForPopoverPresentation() appState.startWorkflow(type, source: .manual) showPopover() } } } private func handleHotkeyUp(_ type: WorkflowType) { let mode = appState.appSettings.hotkeyMode guard mode == .hold else { return } // Hold mode: stop recording on key release if let active = appState.activeWorkflow, active.type == type { // Only stop if currently recording (running phase) if case .running = active.phase { active.stop() } } } private func handleHotkeyCancel() { appState.activeWorkflow?.stop() } @objc private func togglePopover() { if popover.isShown { popover.performClose(nil) appState.isPopoverShown = false } else { appState.prepareForPopoverPresentation() showPopover() } } private func showOnboardingIfNeeded() { guard appState.shouldShowOnboarding else { return } appState.prepareForPopoverPresentation() showPopover() } private func showPopover() { guard let button = statusItem.button else { return } popover.show(relativeTo: button.bounds, of: button, preferredEdge: .minY) appState.isPopoverShown = true NSApp.activate(ignoringOtherApps: true) } nonisolated func popoverDidClose(_ notification: Notification) { Task { @MainActor in appState.isPopoverShown = false switch appState.currentPhase { case .done, .error: appState.resetCurrentWorkflow() default: appState.page = .main } } } }