This commit is contained in:
Michael Freno
2026-01-08 10:37:03 -05:00
parent 3d8f4674dc
commit 650f5b2b15
7 changed files with 94 additions and 228 deletions

View File

@@ -10,8 +10,6 @@ import SwiftUI
struct BlinkSetupView: View { struct BlinkSetupView: View {
@Binding var enabled: Bool @Binding var enabled: Bool
@Binding var intervalMinutes: Int @Binding var intervalMinutes: Int
var onContinue: () -> Void
var onBack: (() -> Void)?
var body: some View { var body: some View {
VStack(spacing: 30) { 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") InfoBox(text: "We blink much less when focusing on screens. Regular blink reminders help prevent dry eyes")
Spacer() Spacer()
HStack(spacing: 12) {
if let onBack = onBack {
Button(action: onBack) {
HStack {
Image(systemName: "chevron.left")
Text("Back")
} }
.font(.headline) .frame(width: 600, height: 450)
.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)
.padding() .padding()
.background(.clear) .background(.clear)
} }
@@ -91,8 +63,6 @@ struct BlinkSetupView: View {
#Preview { #Preview {
BlinkSetupView( BlinkSetupView(
enabled: .constant(true), enabled: .constant(true),
intervalMinutes: .constant(5), intervalMinutes: .constant(5)
onContinue: {},
onBack: {}
) )
} }

View File

@@ -9,7 +9,6 @@ import SwiftUI
struct CompletionView: View { struct CompletionView: View {
var onComplete: () -> Void var onComplete: () -> Void
var onBack: (() -> Void)?
var body: some View { var body: some View {
VStack(spacing: 30) { VStack(spacing: 30) {
@@ -64,39 +63,13 @@ struct CompletionView: View {
.glassEffect(in: .rect(cornerRadius: 12)) .glassEffect(in: .rect(cornerRadius: 12))
Spacer() Spacer()
HStack(spacing: 12) {
if let onBack = onBack {
Button(action: onBack) {
HStack {
Image(systemName: "chevron.left")
Text("Back")
} }
.font(.headline) .frame(width: 600, height: 450)
.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)
.padding() .padding()
.background(.clear) .background(.clear)
} }
} }
#Preview { #Preview {
CompletionView(onComplete: {}, onBack: {}) CompletionView(onComplete: {})
} }

View File

@@ -11,8 +11,6 @@ struct LookAwaySetupView: View {
@Binding var enabled: Bool @Binding var enabled: Bool
@Binding var intervalMinutes: Int @Binding var intervalMinutes: Int
@Binding var countdownSeconds: Int @Binding var countdownSeconds: Int
var onContinue: () -> Void
var onBack: (() -> Void)?
var body: some View { var body: some View {
VStack(spacing: 30) { 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") InfoBox(text: "Every \(intervalMinutes) minutes, look in the distance for \(countdownSeconds) seconds to reduce eye strain")
Spacer() Spacer()
HStack(spacing: 12) {
if let onBack = onBack {
Button(action: onBack) {
HStack {
Image(systemName: "chevron.left")
Text("Back")
} }
.font(.headline) .frame(width: 600, height: 450)
.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)
.padding() .padding()
.background(.clear) .background(.clear)
} }
@@ -124,8 +96,6 @@ struct InfoBox: View {
LookAwaySetupView( LookAwaySetupView(
enabled: .constant(true), enabled: .constant(true),
intervalMinutes: .constant(20), intervalMinutes: .constant(20),
countdownSeconds: .constant(20), countdownSeconds: .constant(20)
onContinue: {},
onBack: {}
) )
} }

View File

@@ -1,14 +1,6 @@
//
// OnboardingContainerView.swift
// Gaze
//
// Created by Mike Freno on 1/7/26.
//
import SwiftUI
import AppKit import AppKit
import SwiftUI
// NSVisualEffectView wrapper for SwiftUI
struct VisualEffectView: NSViewRepresentable { struct VisualEffectView: NSViewRepresentable {
let material: NSVisualEffectView.Material let material: NSVisualEffectView.Material
let blendingMode: NSVisualEffectView.BlendingMode let blendingMode: NSVisualEffectView.BlendingMode
@@ -43,10 +35,8 @@ struct OnboardingContainerView: View {
var body: some View { var body: some View {
ZStack { ZStack {
// Semi-transparent background with blur
VisualEffectView(material: .hudWindow, blendingMode: .behindWindow) VisualEffectView(material: .hudWindow, blendingMode: .behindWindow)
.ignoresSafeArea() .ignoresSafeArea()
VStack(spacing: 0) { VStack(spacing: 0) {
TabView(selection: $currentPage) { TabView(selection: $currentPage) {
WelcomeView( WelcomeView(
@@ -60,9 +50,7 @@ struct OnboardingContainerView: View {
LookAwaySetupView( LookAwaySetupView(
enabled: $lookAwayEnabled, enabled: $lookAwayEnabled,
intervalMinutes: $lookAwayIntervalMinutes, intervalMinutes: $lookAwayIntervalMinutes,
countdownSeconds: $lookAwayCountdownSeconds, countdownSeconds: $lookAwayCountdownSeconds
onContinue: { currentPage = 2 },
onBack: { currentPage = 0 }
) )
.tag(1) .tag(1)
.tabItem { .tabItem {
@@ -71,9 +59,7 @@ struct OnboardingContainerView: View {
BlinkSetupView( BlinkSetupView(
enabled: $blinkEnabled, enabled: $blinkEnabled,
intervalMinutes: $blinkIntervalMinutes, intervalMinutes: $blinkIntervalMinutes
onContinue: { currentPage = 3 },
onBack: { currentPage = 1 }
) )
.tag(2) .tag(2)
.tabItem { .tabItem {
@@ -82,9 +68,7 @@ struct OnboardingContainerView: View {
PostureSetupView( PostureSetupView(
enabled: $postureEnabled, enabled: $postureEnabled,
intervalMinutes: $postureIntervalMinutes, intervalMinutes: $postureIntervalMinutes
onContinue: { currentPage = 4 },
onBack: { currentPage = 2 }
) )
.tag(3) .tag(3)
.tabItem { .tabItem {
@@ -92,9 +76,7 @@ struct OnboardingContainerView: View {
} }
SettingsOnboardingView( SettingsOnboardingView(
launchAtLogin: $launchAtLogin, launchAtLogin: $launchAtLogin
onContinue: { currentPage = 5 },
onBack: { currentPage = 3 }
) )
.tag(4) .tag(4)
.tabItem { .tabItem {
@@ -104,8 +86,7 @@ struct OnboardingContainerView: View {
CompletionView( CompletionView(
onComplete: { onComplete: {
completeOnboarding() completeOnboarding()
}, }
onBack: { currentPage = 4 }
) )
.tag(5) .tag(5)
.tabItem { .tabItem {
@@ -113,6 +94,39 @@ struct OnboardingContainerView: View {
} }
} }
.tabViewStyle(.automatic) .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) .opacity(isAnimatingOut ? 0 : 1)
@@ -157,7 +171,11 @@ struct OnboardingContainerView: View {
private func performVacuumAnimation() { private func performVacuumAnimation() {
// Get the NSWindow reference // 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 // Fallback: just dismiss without animation
dismiss() dismiss()
return return
@@ -194,19 +212,17 @@ struct OnboardingContainerView: View {
} }
// Animate window frame using AppKit // Animate window frame using AppKit
NSAnimationContext.runAnimationGroup({ context in NSAnimationContext.runAnimationGroup(
{ context in
context.duration = 0.7 context.duration = 0.7
context.timingFunction = CAMediaTimingFunction(name: .easeIn) context.timingFunction = CAMediaTimingFunction(name: .easeIn)
window.animator().setFrame(targetRect, display: true) window.animator().setFrame(targetRect, display: true)
window.animator().alphaValue = 0 window.animator().alphaValue = 0
}, completionHandler: { },
completionHandler: {
// Close window after animation completes // Close window after animation completes
self.dismiss() self.dismiss()
window.close() window.close()
}) })
} }
} }
#Preview {
OnboardingContainerView(settingsManager: SettingsManager.shared)
}

View File

@@ -10,8 +10,6 @@ import SwiftUI
struct PostureSetupView: View { struct PostureSetupView: View {
@Binding var enabled: Bool @Binding var enabled: Bool
@Binding var intervalMinutes: Int @Binding var intervalMinutes: Int
var onContinue: () -> Void
var onBack: (() -> Void)?
var body: some View { var body: some View {
VStack(spacing: 30) { VStack(spacing: 30) {
@@ -55,34 +53,8 @@ struct PostureSetupView: View {
InfoBox(text: "Regular posture checks help prevent back and neck pain from prolonged sitting") InfoBox(text: "Regular posture checks help prevent back and neck pain from prolonged sitting")
Spacer() Spacer()
HStack(spacing: 12) {
if let onBack = onBack {
Button(action: onBack) {
HStack {
Image(systemName: "chevron.left")
Text("Back")
} }
.font(.headline) .frame(width: 600, height: 450)
.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)
.padding() .padding()
.background(.clear) .background(.clear)
} }
@@ -91,8 +63,6 @@ struct PostureSetupView: View {
#Preview { #Preview {
PostureSetupView( PostureSetupView(
enabled: .constant(true), enabled: .constant(true),
intervalMinutes: .constant(30), intervalMinutes: .constant(30)
onContinue: {},
onBack: {}
) )
} }

View File

@@ -9,8 +9,6 @@ import SwiftUI
struct SettingsOnboardingView: View { struct SettingsOnboardingView: View {
@Binding var launchAtLogin: Bool @Binding var launchAtLogin: Bool
var onContinue: () -> Void
var onBack: (() -> Void)?
var body: some View { var body: some View {
VStack(spacing: 30) { VStack(spacing: 30) {
@@ -116,34 +114,8 @@ struct SettingsOnboardingView: View {
} }
Spacer() Spacer()
HStack(spacing: 12) {
if let onBack = onBack {
Button(action: onBack) {
HStack {
Image(systemName: "chevron.left")
Text("Back")
} }
.font(.headline) .frame(width: 600, height: 450)
.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)
.padding() .padding()
.background(.clear) .background(.clear)
} }
@@ -163,8 +135,6 @@ struct SettingsOnboardingView: View {
#Preview { #Preview {
SettingsOnboardingView( SettingsOnboardingView(
launchAtLogin: .constant(false), launchAtLogin: .constant(false)
onContinue: {},
onBack: {}
) )
} }

15
run
View File

@@ -4,7 +4,7 @@
# Usage: ./run [build|test|run] # Usage: ./run [build|test|run]
# Default action is build and run # Default action is build and run
ACTION=${1:-build} ACTION=${1:-run}
VERBOSE=false VERBOSE=false
OUTPUT_FILE="" OUTPUT_FILE=""
@@ -71,20 +71,17 @@ elif [ "$ACTION" = "test" ]; then
fi fi
elif [ "$ACTION" = "run" ]; then elif [ "$ACTION" = "run" ]; then
echo "Running Gaze application..." echo "Building and running Gaze application..."
# First ensure we have a built app # Always build first, then run
if [ -d "build/Debug/Gaze.app" ]; then
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" run_with_output "xcodebuild -project Gaze.xcodeproj -scheme Gaze -configuration Debug build"
if [ $? -eq 0 ]; then if [ $? -eq 0 ]; then
echo "✅ Build succeeded!"
run_with_output "open -a \"Gaze\"" run_with_output "open -a \"Gaze\""
else else
echo "❌ Build failed during run attempt!" echo "❌ Build failed!"
exit 1 exit 1
fi fi
fi
else else
echo "Usage: $0 [build|test|run] [-v|--verbose] [-o|--output <file_name>]" echo "Usage: $0 [build|test|run] [-v|--verbose] [-o|--output <file_name>]"