diff --git a/Gaze/AppDelegate.swift b/Gaze/AppDelegate.swift index 06dc1c4..1293bf9 100644 --- a/Gaze/AppDelegate.swift +++ b/Gaze/AppDelegate.swift @@ -12,8 +12,6 @@ import Combine @MainActor class AppDelegate: NSObject, NSApplicationDelegate { var timerEngine: TimerEngine? - private var statusItem: NSStatusItem? - private var popover: NSPopover? private var settingsManager: SettingsManager? private var reminderWindowController: NSWindowController? private var settingsWindowController: NSWindowController? @@ -28,7 +26,6 @@ class AppDelegate: NSObject, NSApplicationDelegate { settingsManager = SettingsManager.shared timerEngine = TimerEngine(settingsManager: settingsManager!) - setupMenuBar() setupLifecycleObservers() observeSettingsChanges() @@ -42,48 +39,6 @@ class AppDelegate: NSObject, NSApplicationDelegate { startTimers() } - private func setupMenuBar() { - statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength) - - if let button = statusItem?.button { - button.image = NSImage(systemSymbolName: "eye.fill", accessibilityDescription: "Gaze") - button.action = #selector(togglePopover) - button.target = self - } - } - - @objc private func togglePopover() { - if let popover = popover, popover.isShown { - popover.close() - } else { - showPopover() - } - } - - private func showPopover() { - // Reuse existing popover or create new one - if popover == nil { - let newPopover = NSPopover() - newPopover.contentSize = NSSize(width: 300, height: 400) - newPopover.behavior = .transient - popover = newPopover - } - - // Always set fresh content - popover?.contentViewController = NSHostingController( - rootView: MenuBarContentView( - timerEngine: timerEngine!, - settingsManager: settingsManager!, - onQuit: { NSApplication.shared.terminate(nil) }, - onOpenSettings: { [weak self] in self?.openSettings() } - ) - ) - - if let button = statusItem?.button { - popover?.show(relativeTo: button.bounds, of: button, preferredEdge: .minY) - } - } - private func startTimers() { guard !hasStartedTimers else { return } hasStartedTimers = true @@ -226,11 +181,6 @@ class AppDelegate: NSObject, NSApplicationDelegate { reminderWindowController = nil } - // Public method to get menubar icon position for animations - func getMenuBarIconPosition() -> NSRect? { - return statusItem?.button?.window?.frame - } - // Public method to open settings window func openSettings() { // If window already exists, just bring it to front diff --git a/Gaze/GazeApp.swift b/Gaze/GazeApp.swift index 9e2be63..71f9d5e 100644 --- a/Gaze/GazeApp.swift +++ b/Gaze/GazeApp.swift @@ -13,6 +13,7 @@ struct GazeApp: App { @StateObject private var settingsManager = SettingsManager.shared var body: some Scene { + // Onboarding window (only shown when not completed) WindowGroup { if settingsManager.settings.hasCompletedOnboarding { EmptyView() @@ -33,6 +34,19 @@ struct GazeApp: App { .commands { CommandGroup(replacing: .newItem) { } } + + // Menu bar extra (always present once onboarding is complete) + MenuBarExtra("Gaze", systemImage: "eye.fill") { + if let timerEngine = appDelegate.timerEngine { + MenuBarContentView( + timerEngine: timerEngine, + settingsManager: settingsManager, + onQuit: { NSApplication.shared.terminate(nil) }, + onOpenSettings: { appDelegate.openSettings() } + ) + } + } + .menuBarExtraStyle(.window) } private func closeAllWindows() { diff --git a/Gaze/Views/Onboarding/OnboardingContainerView.swift b/Gaze/Views/Onboarding/OnboardingContainerView.swift index e291eb8..f1abf6b 100644 --- a/Gaze/Views/Onboarding/OnboardingContainerView.swift +++ b/Gaze/Views/Onboarding/OnboardingContainerView.swift @@ -188,30 +188,14 @@ struct OnboardingContainerView: View { return } - // Get menubar icon position from AppDelegate - let appDelegate = NSApplication.shared.delegate as? AppDelegate - let targetFrame = appDelegate?.getMenuBarIconPosition() - - // Calculate target position (menubar icon or top-center as fallback) - let targetRect: NSRect - if let menuBarFrame = targetFrame { - // Use menubar icon position - targetRect = NSRect( - x: menuBarFrame.midX, - y: menuBarFrame.midY, - width: 0, - height: 0 - ) - } else { - // Fallback to top-center of screen - let screen = NSScreen.main?.frame ?? .zero - targetRect = NSRect( - x: screen.midX, - y: screen.maxY, - width: 0, - height: 0 - ) - } + // Calculate target position (top-center of screen where menu bar is) + let screen = NSScreen.main?.frame ?? .zero + let targetRect = NSRect( + x: screen.midX, + y: screen.maxY, + width: 0, + height: 0 + ) // Start SwiftUI animation for visual effects withAnimation(.easeInOut(duration: 0.7)) {