From 650f5b2b15332bccb25bcfda7cc7ff0c37a2f20b Mon Sep 17 00:00:00 2001 From: Michael Freno Date: Thu, 8 Jan 2026 10:37:03 -0500 Subject: [PATCH] guh --- Gaze/Views/Onboarding/BlinkSetupView.swift | 34 +---- Gaze/Views/Onboarding/CompletionView.swift | 31 +--- Gaze/Views/Onboarding/LookAwaySetupView.swift | 34 +---- .../Onboarding/OnboardingContainerView.swift | 134 ++++++++++-------- Gaze/Views/Onboarding/PostureSetupView.swift | 34 +---- .../Onboarding/SettingsOnboardingView.swift | 34 +---- run | 21 ++- 7 files changed, 94 insertions(+), 228 deletions(-) diff --git a/Gaze/Views/Onboarding/BlinkSetupView.swift b/Gaze/Views/Onboarding/BlinkSetupView.swift index b44ea82..5d3aa1c 100644 --- a/Gaze/Views/Onboarding/BlinkSetupView.swift +++ b/Gaze/Views/Onboarding/BlinkSetupView.swift @@ -10,8 +10,6 @@ import SwiftUI struct BlinkSetupView: View { @Binding var enabled: Bool @Binding var intervalMinutes: Int - var onContinue: () -> Void - var onBack: (() -> Void)? var body: some View { VStack(spacing: 30) { @@ -55,34 +53,8 @@ struct BlinkSetupView: View { InfoBox(text: "We blink much less when focusing on screens. Regular blink reminders help prevent dry eyes") Spacer() - - HStack(spacing: 12) { - if let onBack = onBack { - Button(action: onBack) { - HStack { - Image(systemName: "chevron.left") - Text("Back") - } - .font(.headline) - .frame(maxWidth: .infinity) - .padding() - } - .buttonStyle(.plain) - .glassEffect(.regular.interactive()) - } - - Button(action: onContinue) { - Text("Continue") - .font(.headline) - .frame(maxWidth: .infinity) - .padding() - } - .buttonStyle(.plain) - .glassEffect(.regular.tint(.blue).interactive()) - } - .padding(.horizontal, 40) } - .frame(width: 600, height: 500) + .frame(width: 600, height: 450) .padding() .background(.clear) } @@ -91,8 +63,6 @@ struct BlinkSetupView: View { #Preview { BlinkSetupView( enabled: .constant(true), - intervalMinutes: .constant(5), - onContinue: {}, - onBack: {} + intervalMinutes: .constant(5) ) } diff --git a/Gaze/Views/Onboarding/CompletionView.swift b/Gaze/Views/Onboarding/CompletionView.swift index 0908d46..40102a0 100644 --- a/Gaze/Views/Onboarding/CompletionView.swift +++ b/Gaze/Views/Onboarding/CompletionView.swift @@ -9,7 +9,6 @@ import SwiftUI struct CompletionView: View { var onComplete: () -> Void - var onBack: (() -> Void)? var body: some View { VStack(spacing: 30) { @@ -64,39 +63,13 @@ struct CompletionView: View { .glassEffect(in: .rect(cornerRadius: 12)) Spacer() - - HStack(spacing: 12) { - if let onBack = onBack { - Button(action: onBack) { - HStack { - Image(systemName: "chevron.left") - Text("Back") - } - .font(.headline) - .frame(maxWidth: .infinity) - .padding() - } - .buttonStyle(.plain) - .glassEffect(.regular.interactive()) - } - - Button(action: onComplete) { - Text("Get Started") - .font(.headline) - .frame(maxWidth: .infinity) - .padding() - } - .buttonStyle(.plain) - .glassEffect(.regular.tint(.green).interactive()) - } - .padding(.horizontal, 40) } - .frame(width: 600, height: 500) + .frame(width: 600, height: 450) .padding() .background(.clear) } } #Preview { - CompletionView(onComplete: {}, onBack: {}) + CompletionView(onComplete: {}) } diff --git a/Gaze/Views/Onboarding/LookAwaySetupView.swift b/Gaze/Views/Onboarding/LookAwaySetupView.swift index 983f3c2..442814f 100644 --- a/Gaze/Views/Onboarding/LookAwaySetupView.swift +++ b/Gaze/Views/Onboarding/LookAwaySetupView.swift @@ -11,8 +11,6 @@ struct LookAwaySetupView: View { @Binding var enabled: Bool @Binding var intervalMinutes: Int @Binding var countdownSeconds: Int - var onContinue: () -> Void - var onBack: (() -> Void)? var body: some View { VStack(spacing: 30) { @@ -71,34 +69,8 @@ struct LookAwaySetupView: View { InfoBox(text: "Every \(intervalMinutes) minutes, look in the distance for \(countdownSeconds) seconds to reduce eye strain") Spacer() - - HStack(spacing: 12) { - if let onBack = onBack { - Button(action: onBack) { - HStack { - Image(systemName: "chevron.left") - Text("Back") - } - .font(.headline) - .frame(maxWidth: .infinity) - .padding() - } - .buttonStyle(.plain) - .glassEffect(.regular.interactive()) - } - - Button(action: onContinue) { - Text("Continue") - .font(.headline) - .frame(maxWidth: .infinity) - .padding() - } - .buttonStyle(.plain) - .glassEffect(.regular.tint(.blue).interactive()) - } - .padding(.horizontal, 40) } - .frame(width: 600, height: 500) + .frame(width: 600, height: 450) .padding() .background(.clear) } @@ -124,8 +96,6 @@ struct InfoBox: View { LookAwaySetupView( enabled: .constant(true), intervalMinutes: .constant(20), - countdownSeconds: .constant(20), - onContinue: {}, - onBack: {} + countdownSeconds: .constant(20) ) } diff --git a/Gaze/Views/Onboarding/OnboardingContainerView.swift b/Gaze/Views/Onboarding/OnboardingContainerView.swift index 375ff08..7e57a85 100644 --- a/Gaze/Views/Onboarding/OnboardingContainerView.swift +++ b/Gaze/Views/Onboarding/OnboardingContainerView.swift @@ -1,18 +1,10 @@ -// -// OnboardingContainerView.swift -// Gaze -// -// Created by Mike Freno on 1/7/26. -// - -import SwiftUI import AppKit +import SwiftUI -// NSVisualEffectView wrapper for SwiftUI struct VisualEffectView: NSViewRepresentable { let material: NSVisualEffectView.Material let blendingMode: NSVisualEffectView.BlendingMode - + func makeNSView(context: Context) -> NSVisualEffectView { let view = NSVisualEffectView() view.material = material @@ -20,7 +12,7 @@ struct VisualEffectView: NSViewRepresentable { view.state = .active return view } - + func updateNSView(_ nsView: NSVisualEffectView, context: Context) { nsView.material = material nsView.blendingMode = blendingMode @@ -40,13 +32,11 @@ struct OnboardingContainerView: View { @State private var launchAtLogin = false @State private var isAnimatingOut = false @Environment(\.dismiss) private var dismiss - + var body: some View { ZStack { - // Semi-transparent background with blur VisualEffectView(material: .hudWindow, blendingMode: .behindWindow) .ignoresSafeArea() - VStack(spacing: 0) { TabView(selection: $currentPage) { WelcomeView( @@ -56,56 +46,47 @@ struct OnboardingContainerView: View { .tabItem { Image(systemName: "hand.wave.fill") } - + LookAwaySetupView( enabled: $lookAwayEnabled, intervalMinutes: $lookAwayIntervalMinutes, - countdownSeconds: $lookAwayCountdownSeconds, - onContinue: { currentPage = 2 }, - onBack: { currentPage = 0 } + countdownSeconds: $lookAwayCountdownSeconds ) .tag(1) .tabItem { Image(systemName: "eye.fill") } - + BlinkSetupView( enabled: $blinkEnabled, - intervalMinutes: $blinkIntervalMinutes, - onContinue: { currentPage = 3 }, - onBack: { currentPage = 1 } + intervalMinutes: $blinkIntervalMinutes ) .tag(2) .tabItem { Image(systemName: "eye.circle.fill") } - + PostureSetupView( enabled: $postureEnabled, - intervalMinutes: $postureIntervalMinutes, - onContinue: { currentPage = 4 }, - onBack: { currentPage = 2 } + intervalMinutes: $postureIntervalMinutes ) .tag(3) .tabItem { Image(systemName: "figure.stand") } - + SettingsOnboardingView( - launchAtLogin: $launchAtLogin, - onContinue: { currentPage = 5 }, - onBack: { currentPage = 3 } + launchAtLogin: $launchAtLogin ) .tag(4) .tabItem { Image(systemName: "gearshape.fill") } - + CompletionView( onComplete: { completeOnboarding() - }, - onBack: { currentPage = 4 } + } ) .tag(5) .tabItem { @@ -113,12 +94,45 @@ struct OnboardingContainerView: View { } } .tabViewStyle(.automatic) + + if currentPage >= 1 { + HStack(spacing: 12) { + Button(action: { currentPage -= 1 }) { + HStack { + Image(systemName: "chevron.left") + Text("Back") + } + .font(.headline) + .frame(maxWidth: .infinity) + .padding() + } + .buttonStyle(.plain) + .glassEffect(.regular.interactive()) + + Button(action: { + if currentPage == 5 { + completeOnboarding() + } else { + currentPage += 1 + } + }) { + Text(currentPage == 5 ? "Get Started" : "Continue") + .font(.headline) + .frame(maxWidth: .infinity) + .padding() + } + .buttonStyle(.plain) + .glassEffect(.regular.tint(currentPage == 5 ? .green : .blue).interactive()) + } + .padding(.horizontal, 40) + .padding(.bottom, 20) + } } } .opacity(isAnimatingOut ? 0 : 1) .scaleEffect(isAnimatingOut ? 0.3 : 1.0) } - + private func completeOnboarding() { // Save settings settingsManager.settings.lookAwayTimer = TimerConfiguration( @@ -126,20 +140,20 @@ struct OnboardingContainerView: View { intervalSeconds: lookAwayIntervalMinutes * 60 ) settingsManager.settings.lookAwayCountdownSeconds = lookAwayCountdownSeconds - + settingsManager.settings.blinkTimer = TimerConfiguration( enabled: blinkEnabled, intervalSeconds: blinkIntervalMinutes * 60 ) - + settingsManager.settings.postureTimer = TimerConfiguration( enabled: postureEnabled, intervalSeconds: postureIntervalMinutes * 60 ) - + settingsManager.settings.launchAtLogin = launchAtLogin settingsManager.settings.hasCompletedOnboarding = true - + // Apply launch at login setting do { if launchAtLogin { @@ -150,23 +164,27 @@ struct OnboardingContainerView: View { } catch { print("Failed to set launch at login: \(error)") } - + // Perform vacuum animation performVacuumAnimation() } - + private func performVacuumAnimation() { // Get the NSWindow reference - guard let window = NSApplication.shared.windows.first(where: { $0.isVisible && $0.contentView != nil }) else { + guard + let window = NSApplication.shared.windows.first(where: { + $0.isVisible && $0.contentView != nil + }) + else { // Fallback: just dismiss without animation dismiss() 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 { @@ -187,26 +205,24 @@ struct OnboardingContainerView: View { 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() - }) + 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 { - OnboardingContainerView(settingsManager: SettingsManager.shared) -} diff --git a/Gaze/Views/Onboarding/PostureSetupView.swift b/Gaze/Views/Onboarding/PostureSetupView.swift index 3e36176..c87b8d9 100644 --- a/Gaze/Views/Onboarding/PostureSetupView.swift +++ b/Gaze/Views/Onboarding/PostureSetupView.swift @@ -10,8 +10,6 @@ import SwiftUI struct PostureSetupView: View { @Binding var enabled: Bool @Binding var intervalMinutes: Int - var onContinue: () -> Void - var onBack: (() -> Void)? var body: some View { VStack(spacing: 30) { @@ -55,34 +53,8 @@ struct PostureSetupView: View { InfoBox(text: "Regular posture checks help prevent back and neck pain from prolonged sitting") Spacer() - - HStack(spacing: 12) { - if let onBack = onBack { - Button(action: onBack) { - HStack { - Image(systemName: "chevron.left") - Text("Back") - } - .font(.headline) - .frame(maxWidth: .infinity) - .padding() - } - .buttonStyle(.plain) - .glassEffect(.regular.interactive()) - } - - Button(action: onContinue) { - Text("Continue") - .font(.headline) - .frame(maxWidth: .infinity) - .padding() - } - .buttonStyle(.plain) - .glassEffect(.regular.tint(.blue).interactive()) - } - .padding(.horizontal, 40) } - .frame(width: 600, height: 500) + .frame(width: 600, height: 450) .padding() .background(.clear) } @@ -91,8 +63,6 @@ struct PostureSetupView: View { #Preview { PostureSetupView( enabled: .constant(true), - intervalMinutes: .constant(30), - onContinue: {}, - onBack: {} + intervalMinutes: .constant(30) ) } diff --git a/Gaze/Views/Onboarding/SettingsOnboardingView.swift b/Gaze/Views/Onboarding/SettingsOnboardingView.swift index 62ffaea..8fc0a9a 100644 --- a/Gaze/Views/Onboarding/SettingsOnboardingView.swift +++ b/Gaze/Views/Onboarding/SettingsOnboardingView.swift @@ -9,8 +9,6 @@ import SwiftUI struct SettingsOnboardingView: View { @Binding var launchAtLogin: Bool - var onContinue: () -> Void - var onBack: (() -> Void)? var body: some View { VStack(spacing: 30) { @@ -116,34 +114,8 @@ struct SettingsOnboardingView: View { } Spacer() - - HStack(spacing: 12) { - if let onBack = onBack { - Button(action: onBack) { - HStack { - Image(systemName: "chevron.left") - Text("Back") - } - .font(.headline) - .frame(maxWidth: .infinity) - .padding() - } - .buttonStyle(.plain) - .glassEffect(.regular.interactive()) - } - - Button(action: onContinue) { - Text("Continue") - .font(.headline) - .frame(maxWidth: .infinity) - .padding() - } - .buttonStyle(.plain) - .glassEffect(.regular.tint(.blue).interactive()) - } - .padding(.horizontal, 40) } - .frame(width: 600, height: 500) + .frame(width: 600, height: 450) .padding() .background(.clear) } @@ -163,8 +135,6 @@ struct SettingsOnboardingView: View { #Preview { SettingsOnboardingView( - launchAtLogin: .constant(false), - onContinue: {}, - onBack: {} + launchAtLogin: .constant(false) ) } diff --git a/run b/run index d05d8eb..a49edd4 100755 --- a/run +++ b/run @@ -4,7 +4,7 @@ # Usage: ./run [build|test|run] # Default action is build and run -ACTION=${1:-build} +ACTION=${1:-run} VERBOSE=false OUTPUT_FILE="" @@ -71,19 +71,16 @@ elif [ "$ACTION" = "test" ]; then fi elif [ "$ACTION" = "run" ]; then - echo "Running Gaze application..." - # First ensure we have a built app - if [ -d "build/Debug/Gaze.app" ]; then + echo "Building and running Gaze application..." + # Always build first, then run + run_with_output "xcodebuild -project Gaze.xcodeproj -scheme Gaze -configuration Debug build" + + if [ $? -eq 0 ]; then + echo "✅ Build succeeded!" run_with_output "open -a \"Gaze\"" else - echo "⚠️ No built app found. Building first..." - run_with_output "xcodebuild -project Gaze.xcodeproj -scheme Gaze -configuration Debug build" - if [ $? -eq 0 ]; then - run_with_output "open -a \"Gaze\"" - else - echo "❌ Build failed during run attempt!" - exit 1 - fi + echo "❌ Build failed!" + exit 1 fi else