diff --git a/Gaze/AppDelegate.swift b/Gaze/AppDelegate.swift index 687627f..5060844 100644 --- a/Gaze/AppDelegate.swift +++ b/Gaze/AppDelegate.swift @@ -216,22 +216,43 @@ class AppDelegate: NSObject, NSApplicationDelegate, ObservableObject { DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { [weak self] in guard let self = self, let settingsManager = self.settingsManager else { return } - let window = NSWindow( - contentRect: NSRect(x: 0, y: 0, width: 700, height: 700), - styleMask: [.titled, .closable, .miniaturizable], - backing: .buffered, - defer: false - ) + // Check if onboarding window already exists from the WindowGroup + let existingWindow = NSApplication.shared.windows.first { window in + // Check if window contains OnboardingContainerView by examining its content view + if let hostingView = window.contentView as? NSHostingView { + return true + } + // Also check for windows with our expected size (onboarding window dimensions) + return window.frame.size.width == 700 && window.frame.size.height == 700 + && window.styleMask.contains(.titled) + && window.title.isEmpty // WindowGroup windows have empty title by default + } - window.title = "Gaze Onboarding" - window.center() - window.isReleasedWhenClosed = true - window.contentView = NSHostingView( - rootView: OnboardingContainerView(settingsManager: settingsManager) - ) - - window.makeKeyAndOrderFront(nil) - NSApp.activate(ignoringOtherApps: true) + if let window = existingWindow { + // Reuse existing window - just bring it to front + window.makeKeyAndOrderFront(nil) + NSApp.activate(ignoringOtherApps: true) + } else { + // Create new window matching WindowGroup style + let window = NSWindow( + contentRect: NSRect(x: 0, y: 0, width: 700, height: 700), + styleMask: [.titled, .closable, .miniaturizable, .fullSizeContentView], + backing: .buffered, + defer: false + ) + + // Match the WindowGroup style: hiddenTitleBar + window.titleVisibility = .hidden + window.titlebarAppearsTransparent = true + window.center() + window.isReleasedWhenClosed = true + window.contentView = NSHostingView( + rootView: OnboardingContainerView(settingsManager: settingsManager) + ) + + window.makeKeyAndOrderFront(nil) + NSApp.activate(ignoringOtherApps: true) + } } } diff --git a/Gaze/Views/Containers/OnboardingContainerView.swift b/Gaze/Views/Containers/OnboardingContainerView.swift index 9905319..8afda37 100644 --- a/Gaze/Views/Containers/OnboardingContainerView.swift +++ b/Gaze/Views/Containers/OnboardingContainerView.swift @@ -31,7 +31,6 @@ struct OnboardingContainerView: View { @State private var postureIntervalMinutes = 30 @State private var launchAtLogin = false @State private var subtleReminderSize: ReminderSize = .medium - @State private var isAnimatingOut = false @Environment(\.dismiss) private var dismiss var body: some View { @@ -151,8 +150,6 @@ struct OnboardingContainerView: View { } } .frame(minWidth: 1000, minHeight: 800) - .opacity(isAnimatingOut ? 0 : 1) - .scaleEffect(isAnimatingOut ? 0.3 : 1.0) } private func completeOnboarding() { @@ -188,49 +185,19 @@ struct OnboardingContainerView: View { print("Failed to set launch at login: \(error)") } - // Perform vacuum animation - performVacuumAnimation() - } + // Close window with standard macOS animation + dismiss() - private func performVacuumAnimation() { - // Get the NSWindow reference - guard - let window = NSApplication.shared.windows.first(where: { - $0.isVisible && $0.contentView != nil - }) - else { - // Fallback: just dismiss without animation - dismiss() - return + // After a brief delay, trigger the menu bar extra to open + DispatchQueue.main.asyncAfter(deadline: .now()) { + if let menuBarWindow = NSApp.windows.first(where: { + $0.className.contains("MenuBarExtra") || $0.className.contains("StatusBar") + }), + let statusItem = menuBarWindow.value(forKey: "statusItem") as? NSStatusItem + { + statusItem.button?.performClick(nil) + } } - - // 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)) { - isAnimatingOut = true - } - - // Animate window frame using AppKit - NSAnimationContext.runAnimationGroup( - { context in - context.duration = 0.7 - context.timingFunction = CAMediaTimingFunction(name: .easeIn) - window.animator().setFrame(targetRect, display: true) - window.animator().alphaValue = 0 - }, - completionHandler: { - // Close window after animation completes - self.dismiss() - window.close() - }) } } #Preview("Onboarding Container") {