From 44445f2fd559bf449c191d9629ac00b89e257697 Mon Sep 17 00:00:00 2001 From: Michael Freno Date: Fri, 16 Jan 2026 18:16:04 -0500 Subject: [PATCH] general: foregroundColor -> foregroundStyle (will change in future) --- .../EyeTrackingCalibrationView.swift | 103 ++++++------ Gaze/Views/Components/GazeOverlayView.swift | 81 +++++----- Gaze/Views/Components/InfoBox.swift | 22 +-- Gaze/Views/Components/PupilOverlayView.swift | 54 ++++--- Gaze/Views/Components/SetupHeader.swift | 2 +- Gaze/Views/Components/SliderSection.swift | 12 +- .../Containers/OnboardingContainerView.swift | 25 +-- Gaze/Views/MenuBar/MenuBarContentView.swift | 22 +-- .../Reminders/LookAwayReminderView.swift | 10 +- .../Views/Reminders/PostureReminderView.swift | 2 +- .../UserTimerOverlayReminderView.swift | 12 +- .../Reminders/UserTimerReminderView.swift | 4 +- Gaze/Views/Setup/BlinkSetupView.swift | 69 +++++--- Gaze/Views/Setup/CompletionView.swift | 30 ++-- Gaze/Views/Setup/EnforceModeSetupView.swift | 117 +++++++------- Gaze/Views/Setup/GeneralSetupView.swift | 148 ++++++++++-------- Gaze/Views/Setup/PostureSetupView.swift | 27 ++-- Gaze/Views/Setup/SmartModeSetupView.swift | 38 +++-- Gaze/Views/Setup/UserTimersView.swift | 30 ++-- Gaze/Views/Setup/WelcomeView.swift | 8 +- 20 files changed, 449 insertions(+), 367 deletions(-) diff --git a/Gaze/Views/Components/EyeTrackingCalibrationView.swift b/Gaze/Views/Components/EyeTrackingCalibrationView.swift index 9059ce9..9df9fc6 100644 --- a/Gaze/Views/Components/EyeTrackingCalibrationView.swift +++ b/Gaze/Views/Components/EyeTrackingCalibrationView.swift @@ -10,15 +10,15 @@ import SwiftUI struct EyeTrackingCalibrationView: View { @StateObject private var calibrationManager = CalibrationManager.shared @Environment(\.dismiss) private var dismiss - + @State private var countdownValue = 3 @State private var isCountingDown = false - + var body: some View { ZStack { // Full-screen black background Color.black.ignoresSafeArea() - + if calibrationManager.isCalibrating { calibrationContentView } else { @@ -27,51 +27,55 @@ struct EyeTrackingCalibrationView: View { } .frame(minWidth: 800, minHeight: 600) } - + // MARK: - Introduction Screen - + private var introductionScreenView: some View { VStack(spacing: 30) { Image(systemName: "eye.circle.fill") .font(.system(size: 80)) - .foregroundColor(.blue) - + .foregroundStyle(.blue) + Text("Eye Tracking Calibration") .font(.largeTitle) + .foregroundStyle(.white) .fontWeight(.bold) - + Text("This calibration will help improve eye tracking accuracy.") .font(.title3) .multilineTextAlignment(.center) - .foregroundColor(.secondary) - + .foregroundStyle(.gray) + VStack(alignment: .leading, spacing: 15) { InstructionRow(icon: "1.circle.fill", text: "Look at each target on the screen") - InstructionRow(icon: "2.circle.fill", text: "Keep your head still, only move your eyes") + InstructionRow( + icon: "2.circle.fill", text: "Keep your head still, only move your eyes") InstructionRow(icon: "3.circle.fill", text: "Follow the countdown at each position") InstructionRow(icon: "4.circle.fill", text: "Takes about 30-45 seconds") } .padding(.vertical, 20) - + if calibrationManager.calibrationData.isComplete { VStack(spacing: 10) { Text("Last calibration:") .font(.caption) - .foregroundColor(.secondary) + .foregroundStyle(.gray) Text(calibrationManager.getCalibrationSummary()) .font(.caption) .multilineTextAlignment(.center) - .foregroundColor(.secondary) + .foregroundStyle(.gray) } .padding(.vertical) } - + HStack(spacing: 20) { Button("Cancel") { dismiss() } + .foregroundStyle(.white) + .buttonStyle(.plain) .keyboardShortcut(.escape, modifiers: []) - + Button("Start Calibration") { startCalibration() } @@ -83,9 +87,9 @@ struct EyeTrackingCalibrationView: View { .padding(60) .frame(maxWidth: 600) } - + // MARK: - Calibration Content - + private var calibrationContentView: some View { ZStack { // Progress indicator at top @@ -93,12 +97,12 @@ struct EyeTrackingCalibrationView: View { progressBar Spacer() } - + // Calibration target if let step = calibrationManager.currentStep { calibrationTarget(for: step) } - + // Skip button at bottom VStack { Spacer() @@ -106,19 +110,19 @@ struct EyeTrackingCalibrationView: View { } } } - + // MARK: - Progress Bar - + private var progressBar: some View { VStack(spacing: 10) { HStack { Text("Calibrating...") - .foregroundColor(.white) + .foregroundStyle(.white) Spacer() Text(calibrationManager.progressText) - .foregroundColor(.white.opacity(0.7)) + .foregroundStyle(.white.opacity(0.7)) } - + ProgressView(value: calibrationManager.progress) .progressViewStyle(.linear) .tint(.blue) @@ -126,13 +130,13 @@ struct EyeTrackingCalibrationView: View { .padding() .background(Color.black.opacity(0.5)) } - + // MARK: - Calibration Target - + @ViewBuilder private func calibrationTarget(for step: CalibrationStep) -> some View { let position = targetPosition(for: step) - + VStack(spacing: 20) { // Target circle with countdown ZStack { @@ -141,29 +145,31 @@ struct EyeTrackingCalibrationView: View { .stroke(Color.blue.opacity(0.3), lineWidth: 3) .frame(width: 100, height: 100) .scaleEffect(isCountingDown ? 1.2 : 1.0) - .animation(.easeInOut(duration: 0.6).repeatForever(autoreverses: true), value: isCountingDown) - + .animation( + .easeInOut(duration: 0.6).repeatForever(autoreverses: true), + value: isCountingDown) + // Inner circle Circle() .fill(Color.blue) .frame(width: 60, height: 60) - + // Countdown number or checkmark if isCountingDown && countdownValue > 0 { Text("\(countdownValue)") .font(.system(size: 36, weight: .bold)) - .foregroundColor(.white) + .foregroundStyle(.white) } else if calibrationManager.samplesCollected > 0 { Image(systemName: "checkmark") .font(.system(size: 30, weight: .bold)) - .foregroundColor(.white) + .foregroundStyle(.white) } } - + // Instruction text Text(step.instructionText) .font(.title2) - .foregroundColor(.white) + .foregroundStyle(.white) .padding(.horizontal, 40) .padding(.vertical, 15) .background(Color.black.opacity(0.7)) @@ -174,15 +180,15 @@ struct EyeTrackingCalibrationView: View { startStepCountdown() } } - + // MARK: - Skip Button - + private var skipButton: some View { Button { calibrationManager.skipStep() } label: { Text("Skip this position") - .foregroundColor(.white) + .foregroundStyle(.white) .padding(.horizontal, 20) .padding(.vertical, 10) .background(Color.white.opacity(0.2)) @@ -190,17 +196,17 @@ struct EyeTrackingCalibrationView: View { } .padding(.bottom, 40) } - + // MARK: - Helper Methods - + private func startCalibration() { calibrationManager.startCalibration() } - + private func startStepCountdown() { countdownValue = 3 isCountingDown = true - + // Countdown 3, 2, 1 Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { timer in if countdownValue > 0 { @@ -211,16 +217,16 @@ struct EyeTrackingCalibrationView: View { } } } - + private func targetPosition(for step: CalibrationStep) -> CGPoint { let screenBounds = NSScreen.main?.frame ?? CGRect(x: 0, y: 0, width: 1920, height: 1080) let width = screenBounds.width let height = screenBounds.height - + let centerX = width / 2 let centerY = height / 2 let margin: CGFloat = 150 - + switch step { case .center: return CGPoint(x: centerX, y: centerY) @@ -253,15 +259,16 @@ struct EyeTrackingCalibrationView: View { struct InstructionRow: View { let icon: String let text: String - + var body: some View { HStack(spacing: 15) { Image(systemName: icon) .font(.title2) - .foregroundColor(.blue) + .foregroundStyle(.blue) .frame(width: 30) - + Text(text) + .foregroundStyle(.white) .font(.body) } } diff --git a/Gaze/Views/Components/GazeOverlayView.swift b/Gaze/Views/Components/GazeOverlayView.swift index e91cb95..da038c7 100644 --- a/Gaze/Views/Components/GazeOverlayView.swift +++ b/Gaze/Views/Components/GazeOverlayView.swift @@ -9,7 +9,7 @@ import SwiftUI struct GazeOverlayView: View { @ObservedObject var eyeTrackingService: EyeTrackingService - + var body: some View { VStack(spacing: 8) { inFrameIndicator @@ -19,7 +19,7 @@ struct GazeOverlayView: View { } .padding(12) } - + private var inFrameIndicator: some View { HStack(spacing: 6) { Circle() @@ -28,7 +28,7 @@ struct GazeOverlayView: View { Text(eyeTrackingService.isInFrame ? "In Frame" : "No Face") .font(.caption2) .fontWeight(.semibold) - .foregroundColor(.white) + .foregroundStyle(.white) } .padding(.horizontal, 10) .padding(.vertical, 6) @@ -37,16 +37,18 @@ struct GazeOverlayView: View { .fill(Color.black.opacity(0.6)) ) } - + private var gazeDirectionGrid: some View { let currentDirection = eyeTrackingService.gazeDirection let currentPos = currentDirection.gridPosition - + return VStack(spacing: 2) { ForEach(0..<3, id: \.self) { row in HStack(spacing: 2) { ForEach(0..<3, id: \.self) { col in - let isActive = currentPos.x == col && currentPos.y == row && eyeTrackingService.isInFrame + let isActive = + currentPos.x == col && currentPos.y == row + && eyeTrackingService.isInFrame gridCell(row: row, col: col, isActive: isActive) } } @@ -58,21 +60,21 @@ struct GazeOverlayView: View { .fill(Color.black.opacity(0.5)) ) } - + private func gridCell(row: Int, col: Int, isActive: Bool) -> some View { let direction = directionFor(row: row, col: col) - + return ZStack { RoundedRectangle(cornerRadius: 4) .fill(isActive ? Color.green : Color.white.opacity(0.2)) - + Text(direction.rawValue) .font(.system(size: 14, weight: .bold)) - .foregroundColor(isActive ? .white : .white.opacity(0.6)) + .foregroundStyle(isActive ? .white : .white.opacity(0.6)) } .frame(width: 28, height: 28) } - + private func directionFor(row: Int, col: Int) -> GazeDirection { switch (col, row) { case (0, 0): return .upLeft @@ -87,7 +89,7 @@ struct GazeOverlayView: View { default: return .center } } - + private var ratioDebugView: some View { VStack(alignment: .leading, spacing: 2) { // Show individual L/R ratios @@ -95,38 +97,39 @@ struct GazeOverlayView: View { if let leftH = eyeTrackingService.debugLeftPupilRatio { Text("L.H: \(String(format: "%.2f", leftH))") .font(.system(size: 9, weight: .medium, design: .monospaced)) - .foregroundColor(.white) + .foregroundStyle(.white) } if let rightH = eyeTrackingService.debugRightPupilRatio { Text("R.H: \(String(format: "%.2f", rightH))") .font(.system(size: 9, weight: .medium, design: .monospaced)) - .foregroundColor(.white) + .foregroundStyle(.white) } } - + HStack(spacing: 8) { if let leftV = eyeTrackingService.debugLeftVerticalRatio { Text("L.V: \(String(format: "%.2f", leftV))") .font(.system(size: 9, weight: .medium, design: .monospaced)) - .foregroundColor(.white) + .foregroundStyle(.white) } if let rightV = eyeTrackingService.debugRightVerticalRatio { Text("R.V: \(String(format: "%.2f", rightV))") .font(.system(size: 9, weight: .medium, design: .monospaced)) - .foregroundColor(.white) + .foregroundStyle(.white) } } - + // Show averaged ratios if let leftH = eyeTrackingService.debugLeftPupilRatio, - let rightH = eyeTrackingService.debugRightPupilRatio, - let leftV = eyeTrackingService.debugLeftVerticalRatio, - let rightV = eyeTrackingService.debugRightVerticalRatio { + let rightH = eyeTrackingService.debugRightPupilRatio, + let leftV = eyeTrackingService.debugLeftVerticalRatio, + let rightV = eyeTrackingService.debugRightVerticalRatio + { let avgH = (leftH + rightH) / 2.0 let avgV = (leftV + rightV) / 2.0 Text("Avg H:\(String(format: "%.2f", avgH)) V:\(String(format: "%.2f", avgV))") .font(.system(size: 9, weight: .bold, design: .monospaced)) - .foregroundColor(.yellow) + .foregroundStyle(.yellow) } } .padding(.horizontal, 8) @@ -136,15 +139,15 @@ struct GazeOverlayView: View { .fill(Color.black.opacity(0.5)) ) } - + private var eyeImagesDebugView: some View { HStack(spacing: 12) { // Left eye VStack(spacing: 4) { Text("Left") .font(.system(size: 8, weight: .bold)) - .foregroundColor(.white) - + .foregroundStyle(.white) + HStack(spacing: 4) { eyeImageView( image: eyeTrackingService.debugLeftEyeInput, @@ -160,13 +163,13 @@ struct GazeOverlayView: View { ) } } - + // Right eye VStack(spacing: 4) { Text("Right") .font(.system(size: 8, weight: .bold)) - .foregroundColor(.white) - + .foregroundStyle(.white) + HStack(spacing: 4) { eyeImageView( image: eyeTrackingService.debugRightEyeInput, @@ -189,10 +192,12 @@ struct GazeOverlayView: View { .fill(Color.black.opacity(0.5)) ) } - - private func eyeImageView(image: NSImage?, pupilPosition: PupilPosition?, eyeSize: CGSize?, label: String) -> some View { + + private func eyeImageView( + image: NSImage?, pupilPosition: PupilPosition?, eyeSize: CGSize?, label: String + ) -> some View { let displaySize: CGFloat = 50 - + return VStack(spacing: 2) { ZStack { if let nsImage = image { @@ -201,15 +206,17 @@ struct GazeOverlayView: View { .interpolation(.none) .aspectRatio(contentMode: .fit) .frame(width: displaySize, height: displaySize) - + // Draw pupil position marker - if let pupil = pupilPosition, let size = eyeSize, size.width > 0, size.height > 0 { + if let pupil = pupilPosition, let size = eyeSize, size.width > 0, + size.height > 0 + { let scaleX = displaySize / size.width let scaleY = displaySize / size.height let scale = min(scaleX, scaleY) let scaledWidth = size.width * scale let scaledHeight = size.height * scale - + Circle() .fill(Color.red) .frame(width: 4, height: 4) @@ -224,15 +231,15 @@ struct GazeOverlayView: View { .frame(width: displaySize, height: displaySize) Text("--") .font(.system(size: 10)) - .foregroundColor(.white.opacity(0.5)) + .foregroundStyle(.white.opacity(0.5)) } } .frame(width: displaySize, height: displaySize) .clipShape(RoundedRectangle(cornerRadius: 4)) - + Text(label) .font(.system(size: 7)) - .foregroundColor(.white.opacity(0.7)) + .foregroundStyle(.white.opacity(0.7)) } } } diff --git a/Gaze/Views/Components/InfoBox.swift b/Gaze/Views/Components/InfoBox.swift index 78929f2..b4ac3bf 100644 --- a/Gaze/Views/Components/InfoBox.swift +++ b/Gaze/Views/Components/InfoBox.swift @@ -10,27 +10,27 @@ import SwiftUI struct InfoBox: View { let text: String let url: String? - + var body: some View { HStack(spacing: 12) { if let url = url, let urlObj = URL(string: url) { Button(action: { #if os(iOS) - UIApplication.shared.open(urlObj) + UIApplication.shared.open(urlObj) #elseif os(macOS) - NSWorkspace.shared.open(urlObj) + NSWorkspace.shared.open(urlObj) #endif }) { Image(systemName: "info.circle") - .foregroundColor(.white) + .foregroundStyle(.white) }.buttonStyle(.plain) } else { Image(systemName: "info.circle") - .foregroundColor(.white) + .foregroundStyle(.white) } Text(text) .font(.headline) - .foregroundColor(.white) + .foregroundStyle(.white) } .padding() .glassEffectIfAvailable(GlassStyle.regular.tint(.accentColor), in: .rect(cornerRadius: 8)) @@ -38,6 +38,10 @@ struct InfoBox: View { } #Preview { - InfoBox(text: "This is an informational message that provides helpful context to the user.", url: "https://www.healthline.com/health/eye-health/20-20-20-rule") - .padding() -} \ No newline at end of file + InfoBox( + text: "This is an informational message that provides helpful context to the user.", + url: "https://www.healthline.com/health/eye-health/20-20-20-rule" + ) + .padding() +} + diff --git a/Gaze/Views/Components/PupilOverlayView.swift b/Gaze/Views/Components/PupilOverlayView.swift index b196938..8945eb1 100644 --- a/Gaze/Views/Components/PupilOverlayView.swift +++ b/Gaze/Views/Components/PupilOverlayView.swift @@ -10,17 +10,18 @@ import SwiftUI /// Draws pupil detection markers directly on top of the camera preview struct PupilOverlayView: View { @ObservedObject var eyeTrackingService: EyeTrackingService - + var body: some View { GeometryReader { geometry in let viewSize = geometry.size - + // Draw eye regions and pupil markers ZStack { // Left eye if let leftRegion = eyeTrackingService.debugLeftEyeRegion, - let leftPupil = eyeTrackingService.debugLeftPupilPosition, - let imageSize = eyeTrackingService.debugImageSize { + let leftPupil = eyeTrackingService.debugLeftPupilPosition, + let imageSize = eyeTrackingService.debugImageSize + { EyeOverlayShape( eyeRegion: leftRegion, pupilPosition: leftPupil, @@ -30,11 +31,12 @@ struct PupilOverlayView: View { label: "L" ) } - + // Right eye if let rightRegion = eyeTrackingService.debugRightEyeRegion, - let rightPupil = eyeTrackingService.debugRightPupilPosition, - let imageSize = eyeTrackingService.debugImageSize { + let rightPupil = eyeTrackingService.debugRightPupilPosition, + let imageSize = eyeTrackingService.debugImageSize + { EyeOverlayShape( eyeRegion: rightRegion, pupilPosition: rightPupil, @@ -57,7 +59,7 @@ private struct EyeOverlayShape: View { let viewSize: CGSize let color: Color let label: String - + private var transformedCoordinates: (eyeRect: CGRect, pupilPoint: CGPoint) { // Standard macOS Camera Coordinate System (Landscape): // Raw Buffer: @@ -71,20 +73,20 @@ private struct EyeOverlayShape: View { // - Screen Y increases Down // - BUT the image content is flipped horizontally // (Raw Left is Screen Right, Raw Right is Screen Left) - + // Use dimensions directly (no rotation swap) let rawImageWidth = imageSize.width let rawImageHeight = imageSize.height - + // Calculate aspect-fill scaling // We compare the raw aspect ratio to the view aspect ratio let imageAspect = rawImageWidth / rawImageHeight let viewAspect = viewSize.width / viewSize.height - + let scale: CGFloat let offsetX: CGFloat let offsetY: CGFloat - + if imageAspect > viewAspect { // Image is wider than view - crop sides (pillarbox behavior in aspect fill) // Wait, aspect fill means we fill the view, so we crop the excess. @@ -98,7 +100,7 @@ private struct EyeOverlayShape: View { offsetX = 0 offsetY = (viewSize.height - rawImageHeight * scale) / 2 } - + // Transform Eye Region // Mirroring X: The 'left' of the raw image becomes the 'right' of the screen // Raw Rect: x, y, w, h @@ -107,47 +109,47 @@ private struct EyeOverlayShape: View { let eyeRawY = eyeRegion.frame.origin.y let eyeRawW = eyeRegion.frame.width let eyeRawH = eyeRegion.frame.height - + // Calculate Screen Coordinates let eyeScreenX = (rawImageWidth - (eyeRawX + eyeRawW)) * scale + offsetX let eyeScreenY = eyeRawY * scale + offsetY let eyeScreenW = eyeRawW * scale let eyeScreenH = eyeRawH * scale - + // Transform Pupil Position // Global Raw Pupil X = eyeRawX + pupilPosition.x // Global Raw Pupil Y = eyeRawY + pupilPosition.y let pupilGlobalRawX = eyeRawX + pupilPosition.x let pupilGlobalRawY = eyeRawY + pupilPosition.y - + // Mirror X for Pupil let pupilScreenX = (rawImageWidth - pupilGlobalRawX) * scale + offsetX let pupilScreenY = pupilGlobalRawY * scale + offsetY - + return ( eyeRect: CGRect(x: eyeScreenX, y: eyeScreenY, width: eyeScreenW, height: eyeScreenH), pupilPoint: CGPoint(x: pupilScreenX, y: pupilScreenY) ) } - + var body: some View { let coords = transformedCoordinates let eyeRect = coords.eyeRect let pupilPoint = coords.pupilPoint - + ZStack { // Eye region rectangle Rectangle() .stroke(color, lineWidth: 2) .frame(width: eyeRect.width, height: eyeRect.height) .position(x: eyeRect.midX, y: eyeRect.midY) - + // Pupil marker (red dot) Circle() .fill(Color.red) .frame(width: 8, height: 8) .position(x: pupilPoint.x, y: pupilPoint.y) - + // Crosshair at pupil position Path { path in path.move(to: CGPoint(x: pupilPoint.x - 6, y: pupilPoint.y)) @@ -156,18 +158,18 @@ private struct EyeOverlayShape: View { path.addLine(to: CGPoint(x: pupilPoint.x, y: pupilPoint.y + 6)) } .stroke(Color.red, lineWidth: 1) - + // Label Text(label) .font(.system(size: 10, weight: .bold)) - .foregroundColor(color) + .foregroundStyle(color) .position(x: eyeRect.minX + 8, y: eyeRect.minY - 8) - + // Debug: Show raw coordinates Text("\(label): (\(Int(pupilPosition.x)), \(Int(pupilPosition.y)))") .font(.system(size: 8, design: .monospaced)) - .foregroundColor(.white) - .background(Color.black.opacity(0.7)) + .foregroundStyle(.white) + .background(.black.opacity(0.7)) .position(x: eyeRect.midX, y: eyeRect.maxY + 10) } } diff --git a/Gaze/Views/Components/SetupHeader.swift b/Gaze/Views/Components/SetupHeader.swift index 0bc23b1..d458b6f 100644 --- a/Gaze/Views/Components/SetupHeader.swift +++ b/Gaze/Views/Components/SetupHeader.swift @@ -16,7 +16,7 @@ struct SetupHeader: View { VStack(spacing: 16) { Image(systemName: icon) .font(.system(size: 60)) - .foregroundColor(color) + .foregroundStyle(color) Text(title) .font(.system(size: 28, weight: .bold)) } diff --git a/Gaze/Views/Components/SliderSection.swift b/Gaze/Views/Components/SliderSection.swift index 11a0261..feed548 100644 --- a/Gaze/Views/Components/SliderSection.swift +++ b/Gaze/Views/Components/SliderSection.swift @@ -42,7 +42,7 @@ struct SliderSection: View { VStack(alignment: .leading, spacing: 12) { Text("Remind me every:") .font(.subheadline) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) HStack { Slider( value: Binding( @@ -62,7 +62,7 @@ struct SliderSection: View { if let range = countdownSettings.range { Text("Look away for:") .font(.subheadline) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) HStack { Slider( value: Binding( @@ -89,14 +89,14 @@ struct SliderSection: View { reminderText ) .font(.subheadline) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) .multilineTextAlignment(.center) } else { Text( "\(type) reminders are currently disabled." ) .font(.caption) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) } Button(action: { @@ -104,10 +104,10 @@ struct SliderSection: View { }) { HStack(spacing: 8) { Image(systemName: "eye") - .foregroundColor(.white) + .foregroundStyle(.white) Text("Preview Reminder") .font(.headline) - .foregroundColor(.white) + .foregroundStyle(.white) } .padding(.horizontal, 16) .padding(.vertical, 10) diff --git a/Gaze/Views/Containers/OnboardingContainerView.swift b/Gaze/Views/Containers/OnboardingContainerView.swift index 2376990..31d7ddb 100644 --- a/Gaze/Views/Containers/OnboardingContainerView.swift +++ b/Gaze/Views/Containers/OnboardingContainerView.swift @@ -90,7 +90,8 @@ final class OnboardingWindowPresenter { self?.windowController = nil self?.removeCloseObserver() } - NotificationCenter.default.post(name: Notification.Name("OnboardingWindowDidClose"), object: nil) + NotificationCenter.default.post( + name: Notification.Name("OnboardingWindowDidClose"), object: nil) } } @@ -142,9 +143,9 @@ struct OnboardingContainerView: View { } } #if APPSTORE - .frame(minWidth: 1000, minHeight: 700) + .frame(minWidth: 1000, minHeight: 700) #else - .frame(minWidth: 1000, minHeight: 900) + .frame(minWidth: 1000, minHeight: 900) #endif } @@ -159,11 +160,12 @@ struct OnboardingContainerView: View { } .font(.headline) .frame(minWidth: 100, maxWidth: .infinity, minHeight: 44, maxHeight: 44) - .foregroundColor(.primary) + .foregroundStyle(.primary) .contentShape(RoundedRectangle(cornerRadius: 10)) } .buttonStyle(.plain) - .glassEffectIfAvailable(GlassStyle.regular.interactive(), in: .rect(cornerRadius: 10)) + .glassEffectIfAvailable( + GlassStyle.regular.interactive(), in: .rect(cornerRadius: 10)) } Button(action: { @@ -173,11 +175,14 @@ struct OnboardingContainerView: View { currentPage += 1 } }) { - Text(currentPage == 0 ? "Let's Get Started" : currentPage == 5 ? "Get Started" : "Continue") - .font(.headline) - .frame(minWidth: 100, maxWidth: .infinity, minHeight: 44, maxHeight: 44) - .foregroundColor(.white) - .contentShape(RoundedRectangle(cornerRadius: 10)) + Text( + currentPage == 0 + ? "Let's Get Started" : currentPage == 5 ? "Get Started" : "Continue" + ) + .font(.headline) + .frame(minWidth: 100, maxWidth: .infinity, minHeight: 44, maxHeight: 44) + .foregroundStyle(.white) + .contentShape(RoundedRectangle(cornerRadius: 10)) } .buttonStyle(.plain) .glassEffectIfAvailable( diff --git a/Gaze/Views/MenuBar/MenuBarContentView.swift b/Gaze/Views/MenuBar/MenuBarContentView.swift index d7f2bc0..ef803aa 100644 --- a/Gaze/Views/MenuBar/MenuBarContentView.swift +++ b/Gaze/Views/MenuBar/MenuBarContentView.swift @@ -50,7 +50,7 @@ struct MenuBarHoverButtonStyle: ButtonStyle { func makeBody(configuration: Configuration) -> some View { configuration.label - .foregroundColor(isHovered ? .white : .primary) + .foregroundStyle(isHovered ? .white : .primary) .glassEffectIfAvailable( isHovered ? GlassStyle.regular.tint(.accentColor).interactive() @@ -83,7 +83,7 @@ struct MenuBarContentView: View { VStack(alignment: .leading, spacing: 12) { Text("Active Timers") .font(.caption) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) .padding(.horizontal) .padding(.top, 8) @@ -176,7 +176,7 @@ struct MenuBarContentView: View { }) { HStack { Image(systemName: "checkmark.circle.fill") - .foregroundColor(.accentColor) + .foregroundStyle(Color.accentColor) Text("Complete Onboarding") Spacer() } @@ -193,7 +193,7 @@ struct MenuBarContentView: View { Button(action: onQuit) { HStack { Image(systemName: "power") - .foregroundColor(.red) + .foregroundStyle(.red) Text("Quit Gaze") Spacer() } @@ -207,7 +207,7 @@ struct MenuBarContentView: View { "v\(Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString") as? String ?? "0.0.0")" ) .font(.caption) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) } .padding(.horizontal, 8) .padding(.vertical, 4) @@ -335,20 +335,20 @@ struct TimerStatusRowWithIndividualControls: View { } Image(systemName: iconName) - .foregroundColor(isHoveredBody ? .white : color) + .foregroundStyle(isHoveredBody ? .white : color) .frame(width: 20) VStack(alignment: .leading, spacing: 2) { Text(displayName) .font(.subheadline) .fontWeight(.medium) - .foregroundColor(isHoveredBody ? .white : .primary) + .foregroundStyle(isHoveredBody ? .white : .primary) .lineLimit(1) if let state = state { Text(state.remainingSeconds.asTimerDuration) .font(.caption) - .foregroundColor(isHoveredBody ? .white.opacity(0.8) : .secondary) + .foregroundStyle(isHoveredBody ? .white.opacity(0.8) : .secondary) .monospacedDigit() } } @@ -365,7 +365,7 @@ struct TimerStatusRowWithIndividualControls: View { Button(action: onDevTrigger) { Image(systemName: "bolt.fill") .font(.caption) - .foregroundColor(isHoveredDevTrigger ? .white : .yellow) + .foregroundStyle(isHoveredDevTrigger ? .white : .yellow) .padding(6) .contentShape(Circle()) } @@ -394,7 +394,7 @@ struct TimerStatusRowWithIndividualControls: View { systemName: isPaused ? "play.circle" : "pause.circle" ) .font(.caption) - .foregroundColor(isHoveredPauseButton ? .white : .accentColor) + .foregroundStyle(isHoveredPauseButton ? .white : .accentColor) .padding(6) .contentShape(Circle()) } @@ -416,7 +416,7 @@ struct TimerStatusRowWithIndividualControls: View { Button(action: onSkip) { Image(systemName: "forward.fill") .font(.caption) - .foregroundColor(isHoveredSkip ? .white : .accentColor) + .foregroundStyle(isHoveredSkip ? .white : .accentColor) .padding(6) .contentShape(Circle()) } diff --git a/Gaze/Views/Reminders/LookAwayReminderView.swift b/Gaze/Views/Reminders/LookAwayReminderView.swift index 2f3ec44..a4acd89 100644 --- a/Gaze/Views/Reminders/LookAwayReminderView.swift +++ b/Gaze/Views/Reminders/LookAwayReminderView.swift @@ -33,11 +33,11 @@ struct LookAwayReminderView: View { VStack(spacing: 40) { Text("Look Away") .font(.system(size: 64, weight: .bold)) - .foregroundColor(.white) + .foregroundStyle(.white) Text("Look at something 20 feet away") .font(.system(size: 28)) - .foregroundColor(.white.opacity(0.9)) + .foregroundStyle(.white.opacity(0.9)) GazeLottieView( animationName: AnimationAsset.lookAway.fileName, @@ -62,14 +62,14 @@ struct LookAwayReminderView: View { Text("\(remainingSeconds)") .font(.system(size: 48, weight: .bold)) - .foregroundColor(.white) + .foregroundStyle(.white) .monospacedDigit() .accessibilityIdentifier(AccessibilityIdentifiers.Reminders.countdownLabel) } Text("Press ESC or Space to skip") .font(.subheadline) - .foregroundColor(.white.opacity(0.6)) + .foregroundStyle(.white.opacity(0.6)) } // Skip button in corner @@ -79,7 +79,7 @@ struct LookAwayReminderView: View { Button(action: dismiss) { Image(systemName: "xmark.circle.fill") .font(.system(size: 32)) - .foregroundColor(.white.opacity(0.7)) + .foregroundStyle(.white.opacity(0.7)) } .buttonStyle(.plain) .accessibilityIdentifier(AccessibilityIdentifiers.Reminders.dismissButton) diff --git a/Gaze/Views/Reminders/PostureReminderView.swift b/Gaze/Views/Reminders/PostureReminderView.swift index 3b7938f..361adca 100644 --- a/Gaze/Views/Reminders/PostureReminderView.swift +++ b/Gaze/Views/Reminders/PostureReminderView.swift @@ -22,7 +22,7 @@ struct PostureReminderView: View { VStack { Image(systemName: "arrow.up.circle.fill") .font(.system(size: scale)) - .foregroundColor(.accentColor) + .foregroundStyle(Color.accentColor) } .opacity(opacity) .offset(y: yOffset) diff --git a/Gaze/Views/Reminders/UserTimerOverlayReminderView.swift b/Gaze/Views/Reminders/UserTimerOverlayReminderView.swift index f868b63..690774a 100644 --- a/Gaze/Views/Reminders/UserTimerOverlayReminderView.swift +++ b/Gaze/Views/Reminders/UserTimerOverlayReminderView.swift @@ -32,19 +32,19 @@ struct UserTimerOverlayReminderView: View { VStack(spacing: 40) { Text(timer.title) .font(.system(size: 64, weight: .bold)) - .foregroundColor(.white) + .foregroundStyle(.white) if let message = timer.message, !message.isEmpty { Text(message) .font(.system(size: 28)) - .foregroundColor(.white.opacity(0.9)) + .foregroundStyle(.white.opacity(0.9)) .multilineTextAlignment(.center) .padding(.horizontal, 40) } Image(systemName: "clock.fill") .font(.system(size: 120)) - .foregroundColor(timer.color) + .foregroundStyle(timer.color) .padding(.vertical, 30) // Countdown display @@ -62,13 +62,13 @@ struct UserTimerOverlayReminderView: View { Text("\(remainingSeconds)") .font(.system(size: 48, weight: .bold)) - .foregroundColor(.white) + .foregroundStyle(.white) .monospacedDigit() } Text("Press ESC or Space to dismiss") .font(.subheadline) - .foregroundColor(.white.opacity(0.6)) + .foregroundStyle(.white.opacity(0.6)) } // Dismiss button in corner @@ -78,7 +78,7 @@ struct UserTimerOverlayReminderView: View { Button(action: dismiss) { Image(systemName: "xmark.circle.fill") .font(.system(size: 32)) - .foregroundColor(.white.opacity(0.7)) + .foregroundStyle(.white.opacity(0.7)) } .buttonStyle(.plain) .padding(30) diff --git a/Gaze/Views/Reminders/UserTimerReminderView.swift b/Gaze/Views/Reminders/UserTimerReminderView.swift index 21ebcb2..960cd4b 100644 --- a/Gaze/Views/Reminders/UserTimerReminderView.swift +++ b/Gaze/Views/Reminders/UserTimerReminderView.swift @@ -27,12 +27,12 @@ struct UserTimerReminderView: View { VStack(spacing: 12) { Image(systemName: "clock.fill") .font(.system(size: baseSize * 0.4)) - .foregroundColor(timer.color) + .foregroundStyle(timer.color) if let message = timer.message, !message.isEmpty { Text(message) .font(.system(size: baseSize * 0.24)) - .foregroundColor(.primary) + .foregroundStyle(.primary) .multilineTextAlignment(.center) .lineLimit(2) } diff --git a/Gaze/Views/Setup/BlinkSetupView.swift b/Gaze/Views/Setup/BlinkSetupView.swift index 291dd11..75109cc 100644 --- a/Gaze/Views/Setup/BlinkSetupView.swift +++ b/Gaze/Views/Setup/BlinkSetupView.swift @@ -21,45 +21,62 @@ struct BlinkSetupView: View { VStack(spacing: 30) { HStack(spacing: 12) { Button(action: { - if let url = URL(string: "https://www.aao.org/eye-health/tips-prevention/computer-usage#:~:text=Humans normally blink about 15 times in one minute. However, studies show that we only blink about 5 to 7 times in a minute while using computers and other digital screen devices.") { + if let url = URL( + string: + "https://www.aao.org/eye-health/tips-prevention/computer-usage#:~:text=Humans normally blink about 15 times in one minute. However, studies show that we only blink about 5 to 7 times in a minute while using computers and other digital screen devices." + ) { NSWorkspace.shared.open(url) } }) { Image(systemName: "info.circle") - .foregroundColor(.white) + .foregroundStyle(.white) } .buttonStyle(.plain) - - Text("We blink much less when focusing on screens. Regular blink reminders help prevent dry eyes.") - .font(.headline) - .foregroundColor(.white) + + Text( + "We blink much less when focusing on screens. Regular blink reminders help prevent dry eyes." + ) + .font(.headline) + .foregroundStyle(.white) } .padding() - .glassEffectIfAvailable(GlassStyle.regular.tint(.accentColor), in: .rect(cornerRadius: 8)) + .glassEffectIfAvailable( + GlassStyle.regular.tint(.accentColor), in: .rect(cornerRadius: 8)) VStack(alignment: .leading, spacing: 20) { - Toggle("Enable Blink Reminders", isOn: $settingsManager.settings.blinkTimer.enabled) - .font(.headline) + Toggle( + "Enable Blink Reminders", isOn: $settingsManager.settings.blinkTimer.enabled + ) + .font(.headline) if settingsManager.settings.blinkTimer.enabled { VStack(alignment: .leading, spacing: 12) { Text("Remind me every:") .font(.subheadline) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) HStack { Slider( value: Binding( - get: { Double(settingsManager.settings.blinkTimer.intervalSeconds / 60) }, - set: { settingsManager.settings.blinkTimer.intervalSeconds = Int($0) * 60 } + get: { + Double( + settingsManager.settings.blinkTimer.intervalSeconds + / 60) + }, + set: { + settingsManager.settings.blinkTimer.intervalSeconds = + Int($0) * 60 + } ), in: 1...20, step: 1 ) - Text("\(settingsManager.settings.blinkTimer.intervalSeconds / 60) min") - .frame(width: 60, alignment: .trailing) - .monospacedDigit() + Text( + "\(settingsManager.settings.blinkTimer.intervalSeconds / 60) min" + ) + .frame(width: 60, alignment: .trailing) + .monospacedDigit() } } } @@ -68,29 +85,33 @@ struct BlinkSetupView: View { .glassEffectIfAvailable(GlassStyle.regular, in: .rect(cornerRadius: 12)) if settingsManager.settings.blinkTimer.enabled { - Text("You will be subtly reminded every \(settingsManager.settings.blinkTimer.intervalSeconds / 60) minutes to blink") - .font(.subheadline) - .foregroundColor(.secondary) + Text( + "You will be subtly reminded every \(settingsManager.settings.blinkTimer.intervalSeconds / 60) minutes to blink" + ) + .font(.subheadline) + .foregroundStyle(.secondary) } else { Text("Blink reminders are currently disabled.") .font(.caption) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) } Button(action: showPreviewWindow) { HStack(spacing: 8) { Image(systemName: "eye") - .foregroundColor(.white) + .foregroundStyle(.white) Text("Preview Reminder") .font(.headline) - .foregroundColor(.white) + .foregroundStyle(.white) } .padding(.horizontal, 16) .padding(.vertical, 10) .contentShape(RoundedRectangle(cornerRadius: 10)) } .buttonStyle(.plain) - .glassEffectIfAvailable(GlassStyle.regular.tint(.accentColor).interactive(), in: .rect(cornerRadius: 10)) + .glassEffectIfAvailable( + GlassStyle.regular.tint(.accentColor).interactive(), in: .rect(cornerRadius: 10) + ) } Spacer() @@ -104,7 +125,9 @@ struct BlinkSetupView: View { guard let screen = NSScreen.main else { return } previewWindowController = PreviewWindowHelper.showPreview( on: screen, - content: BlinkReminderView(sizePercentage: settingsManager.settings.subtleReminderSize.percentage) { [weak previewWindowController] in + content: BlinkReminderView( + sizePercentage: settingsManager.settings.subtleReminderSize.percentage + ) { [weak previewWindowController] in previewWindowController?.window?.close() } ) diff --git a/Gaze/Views/Setup/CompletionView.swift b/Gaze/Views/Setup/CompletionView.swift index 2b309a3..1514422 100644 --- a/Gaze/Views/Setup/CompletionView.swift +++ b/Gaze/Views/Setup/CompletionView.swift @@ -11,55 +11,55 @@ struct CompletionView: View { var body: some View { VStack(spacing: 30) { Spacer() - + Image(systemName: "checkmark.circle.fill") .font(.system(size: 80)) - .foregroundColor(.green) - + .foregroundStyle(.green) + Text("You're All Set!") .font(.system(size: 36, weight: .bold)) - + Text("Gaze will now help you take care of your eyes and posture") .font(.title3) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) .multilineTextAlignment(.center) .padding(.horizontal, 40) - + VStack(alignment: .leading, spacing: 16) { Text("What happens next:") .font(.headline) .padding(.horizontal) - + HStack(spacing: 16) { Image(systemName: "menubar.rectangle") - .foregroundColor(.accentColor) + .foregroundStyle(Color.accentColor) .frame(width: 30) Text("Gaze will appear in your menu bar") .font(.subheadline) } .padding(.horizontal) - + HStack(spacing: 16) { Image(systemName: "clock") - .foregroundColor(.accentColor) + .foregroundStyle(Color.accentColor) .frame(width: 30) Text("Timers will start automatically") .font(.subheadline) } .padding(.horizontal) - + HStack(spacing: 16) { Image(systemName: "gearshape") - .foregroundColor(.accentColor) + .foregroundStyle(Color.accentColor) .frame(width: 30) Text("Adjust settings anytime from the menu bar") .font(.subheadline) } .padding(.horizontal) - + HStack(spacing: 16) { Image(systemName: "plus.circle") - .foregroundColor(.accentColor) + .foregroundStyle(Color.accentColor) .frame(width: 30) Text("Create custom timers in Settings for additional reminders") .font(.subheadline) @@ -68,7 +68,7 @@ struct CompletionView: View { } .padding() .glassEffectIfAvailable(GlassStyle.regular, in: .rect(cornerRadius: 12)) - + Spacer() } .frame(width: 600, height: 450) diff --git a/Gaze/Views/Setup/EnforceModeSetupView.swift b/Gaze/Views/Setup/EnforceModeSetupView.swift index 3e71fe8..cc3cf23 100644 --- a/Gaze/Views/Setup/EnforceModeSetupView.swift +++ b/Gaze/Views/Setup/EnforceModeSetupView.swift @@ -6,8 +6,8 @@ // import AVFoundation -import SwiftUI import Foundation +import SwiftUI struct EnforceModeSetupView: View { @Bindable var settingsManager: SettingsManager @@ -33,7 +33,7 @@ struct EnforceModeSetupView: View { VStack(spacing: 30) { Text("Use your camera to ensure you take breaks") .font(.title3) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) .multilineTextAlignment(.center) VStack(spacing: 20) { @@ -43,7 +43,7 @@ struct EnforceModeSetupView: View { .font(.headline) Text("Camera activates 3 seconds before lookaway reminders") .font(.caption) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) } Spacer() Toggle( @@ -149,7 +149,7 @@ struct EnforceModeSetupView: View { HStack { Image(systemName: "target") .font(.title3) - .foregroundColor(.blue) + .foregroundStyle(.blue) Text("Eye Tracking Calibration") .font(.headline) } @@ -158,7 +158,7 @@ struct EnforceModeSetupView: View { VStack(alignment: .leading, spacing: 8) { Text(calibrationManager.getCalibrationSummary()) .font(.caption) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) if calibrationManager.needsRecalibration() { Label( @@ -166,17 +166,17 @@ struct EnforceModeSetupView: View { systemImage: "exclamationmark.triangle.fill" ) .font(.caption) - .foregroundColor(.orange) + .foregroundStyle(.orange) } else { Label("Calibration active and valid", systemImage: "checkmark.circle.fill") .font(.caption) - .foregroundColor(.green) + .foregroundStyle(.green) } } } else { Text("Not calibrated - using default thresholds") .font(.caption) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) } Button(action: { @@ -214,10 +214,10 @@ struct EnforceModeSetupView: View { if let layer = previewLayer { ZStack { CameraPreviewView(previewLayer: layer, borderColor: borderColor) - + // Pupil detection overlay (drawn on video) PupilOverlayView(eyeTrackingService: eyeTrackingService) - + // Debug info overlay (top-right corner) VStack { HStack { @@ -258,7 +258,7 @@ struct EnforceModeSetupView: View { /*? "✓ Break compliance detected" : "⚠️ Please look away from screen"*/ /*)*/ /*.font(.caption)*/ - /*.foregroundColor(lookingAway ? .green : .orange)*/ + /*.foregroundStyle(lookingAway ? .green : .orange)*/ /*.frame(maxWidth: .infinity, alignment: .center)*/ /*.padding(.top, 4)*/ /*}*/ @@ -277,15 +277,15 @@ struct EnforceModeSetupView: View { if cameraService.isCameraAuthorized { Label("Authorized", systemImage: "checkmark.circle.fill") .font(.caption) - .foregroundColor(.green) + .foregroundStyle(.green) } else if let error = cameraService.cameraError { Label(error.localizedDescription, systemImage: "exclamationmark.triangle.fill") .font(.caption) - .foregroundColor(.orange) + .foregroundStyle(.orange) } else { Label("Not authorized", systemImage: "xmark.circle.fill") .font(.caption) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) } } @@ -337,14 +337,14 @@ struct EnforceModeSetupView: View { HStack { Image(systemName: "timer") .font(.title2) - .foregroundColor(.orange) + .foregroundStyle(.orange) VStack(alignment: .leading, spacing: 4) { Text("Camera Ready") .font(.headline) Text("Will activate 3 seconds before lookaway reminder") .font(.caption) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) } Spacer() @@ -357,11 +357,11 @@ struct EnforceModeSetupView: View { VStack(spacing: 8) { Image(systemName: icon) .font(.title2) - .foregroundColor(isActive ? .green : .secondary) + .foregroundStyle(isActive ? .green : .secondary) Text(title) .font(.caption) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) .multilineTextAlignment(.center) } .frame(maxWidth: .infinity) @@ -372,7 +372,7 @@ struct EnforceModeSetupView: View { HStack { Image(systemName: "lock.shield.fill") .font(.title3) - .foregroundColor(.blue) + .foregroundStyle(.blue) Text("Privacy Information") .font(.headline) } @@ -381,11 +381,10 @@ struct EnforceModeSetupView: View { privacyBullet("All processing happens on-device") privacyBullet("No images are stored or transmitted") privacyBullet("Camera only active during lookaway reminders (3 second window)") - privacyBullet("Eyes closed does not affect countdown") - privacyBullet("You can disable at any time") + privacyBullet("You can always force quit with cmd+q") } .font(.caption) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) } .padding() .glassEffectIfAvailable( @@ -396,7 +395,7 @@ struct EnforceModeSetupView: View { HStack(alignment: .top, spacing: 8) { Image(systemName: "checkmark") .font(.caption2) - .foregroundColor(.blue) + .foregroundStyle(.blue) Text(text) } } @@ -442,7 +441,7 @@ struct EnforceModeSetupView: View { systemName: eyeTrackingService.enableDebugLogging ? "ant.circle.fill" : "ant.circle" ) - .foregroundColor(eyeTrackingService.enableDebugLogging ? .orange : .secondary) + .foregroundStyle(eyeTrackingService.enableDebugLogging ? .orange : .secondary) } .buttonStyle(.plain) .help("Toggle console debug logging") @@ -461,7 +460,7 @@ struct EnforceModeSetupView: View { Text("Live Values:") .font(.caption) .fontWeight(.semibold) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) if let leftRatio = eyeTrackingService.debugLeftPupilRatio, let rightRatio = eyeTrackingService.debugRightPupilRatio @@ -470,23 +469,23 @@ struct EnforceModeSetupView: View { VStack(alignment: .leading, spacing: 2) { Text("Left Pupil: \(String(format: "%.3f", leftRatio))") .font(.caption2) - .foregroundColor( + .foregroundStyle( !EyeTrackingConstants.minPupilEnabled && !EyeTrackingConstants.maxPupilEnabled ? .secondary : (leftRatio < EyeTrackingConstants.minPupilRatio || leftRatio > EyeTrackingConstants.maxPupilRatio) - ? .orange : .green + ? Color.orange : Color.green ) Text("Right Pupil: \(String(format: "%.3f", rightRatio))") .font(.caption2) - .foregroundColor( + .foregroundStyle( !EyeTrackingConstants.minPupilEnabled && !EyeTrackingConstants.maxPupilEnabled ? .secondary : (rightRatio < EyeTrackingConstants.minPupilRatio || rightRatio > EyeTrackingConstants.maxPupilRatio) - ? .orange : .green + ? Color.orange : Color.green ) } @@ -497,7 +496,7 @@ struct EnforceModeSetupView: View { "Range: \(String(format: "%.2f", EyeTrackingConstants.minPupilRatio)) - \(String(format: "%.2f", EyeTrackingConstants.maxPupilRatio))" ) .font(.caption2) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) let bothEyesOut = (leftRatio < EyeTrackingConstants.minPupilRatio || leftRatio > EyeTrackingConstants.maxPupilRatio) @@ -505,13 +504,13 @@ struct EnforceModeSetupView: View { || rightRatio > EyeTrackingConstants.maxPupilRatio) Text(bothEyesOut ? "Both Out ⚠️" : "In Range ✓") .font(.caption2) - .foregroundColor(bothEyesOut ? .orange : .green) + .foregroundStyle(bothEyesOut ? .orange : .green) } } } else { Text("Pupil data unavailable") .font(.caption2) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) } if let yaw = eyeTrackingService.debugYaw, @@ -521,21 +520,21 @@ struct EnforceModeSetupView: View { VStack(alignment: .leading, spacing: 2) { Text("Yaw: \(String(format: "%.3f", yaw))") .font(.caption2) - .foregroundColor( + .foregroundStyle( !EyeTrackingConstants.yawEnabled ? .secondary : abs(yaw) > EyeTrackingConstants.yawThreshold - ? .orange : .green + ? Color.orange : Color.green ) Text("Pitch: \(String(format: "%.3f", pitch))") .font(.caption2) - .foregroundColor( + .foregroundStyle( !EyeTrackingConstants.pitchUpEnabled && !EyeTrackingConstants.pitchDownEnabled ? .secondary : (pitch > EyeTrackingConstants.pitchUpThreshold || pitch < EyeTrackingConstants.pitchDownThreshold) - ? .orange : .green + ? Color.orange : Color.green ) } @@ -546,12 +545,12 @@ struct EnforceModeSetupView: View { "Yaw Max: \(String(format: "%.2f", EyeTrackingConstants.yawThreshold))" ) .font(.caption2) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) Text( "Pitch: \(String(format: "%.2f", EyeTrackingConstants.pitchDownThreshold)) to \(String(format: "%.2f", EyeTrackingConstants.pitchUpThreshold))" ) .font(.caption2) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) } } } @@ -565,48 +564,54 @@ struct EnforceModeSetupView: View { Text("Current Threshold Values:") .font(.caption) .fontWeight(.semibold) - .foregroundColor(.secondary) - + .foregroundStyle(.secondary) + HStack { Text("Yaw Threshold:") Spacer() Text("\(String(format: "%.2f", EyeTrackingConstants.yawThreshold)) rad") - .foregroundColor(.secondary) + .foregroundStyle(.secondary) } - + HStack { Text("Pitch Up Threshold:") Spacer() - Text("\(String(format: "%.2f", EyeTrackingConstants.pitchUpThreshold)) rad") - .foregroundColor(.secondary) + Text( + "\(String(format: "%.2f", EyeTrackingConstants.pitchUpThreshold)) rad" + ) + .foregroundStyle(.secondary) } - + HStack { Text("Pitch Down Threshold:") Spacer() - Text("\(String(format: "%.2f", EyeTrackingConstants.pitchDownThreshold)) rad") - .foregroundColor(.secondary) + Text( + "\(String(format: "%.2f", EyeTrackingConstants.pitchDownThreshold)) rad" + ) + .foregroundStyle(.secondary) } - + HStack { Text("Min Pupil Ratio:") Spacer() Text("\(String(format: "%.2f", EyeTrackingConstants.minPupilRatio))") - .foregroundColor(.secondary) + .foregroundStyle(.secondary) } - + HStack { Text("Max Pupil Ratio:") Spacer() Text("\(String(format: "%.2f", EyeTrackingConstants.maxPupilRatio))") - .foregroundColor(.secondary) + .foregroundStyle(.secondary) } - + HStack { Text("Eye Closed Threshold:") Spacer() - Text("\(String(format: "%.3f", EyeTrackingConstants.eyeClosedThreshold))") - .foregroundColor(.secondary) + Text( + "\(String(format: "%.3f", EyeTrackingConstants.eyeClosedThreshold))" + ) + .foregroundStyle(.secondary) } } .padding(.top, 8) @@ -622,7 +627,7 @@ struct EnforceModeSetupView: View { VStack(alignment: .leading, spacing: 12) { Text("Debug Eye Tracking Data") .font(.headline) - .foregroundColor(.blue) + .foregroundStyle(.blue) VStack(alignment: .leading, spacing: 8) { Text("Face Detected: \(eyeTrackingService.faceDetected ? "Yes" : "No")") @@ -643,7 +648,7 @@ struct EnforceModeSetupView: View { } } .font(.caption) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) } .padding() .glassEffectIfAvailable(GlassStyle.regular, in: .rect(cornerRadius: 12)) diff --git a/Gaze/Views/Setup/GeneralSetupView.swift b/Gaze/Views/Setup/GeneralSetupView.swift index eea1401..e60e845 100644 --- a/Gaze/Views/Setup/GeneralSetupView.swift +++ b/Gaze/Views/Setup/GeneralSetupView.swift @@ -14,26 +14,28 @@ struct GeneralSetupView: View { var body: some View { VStack(spacing: 0) { - SetupHeader(icon: "gearshape.fill", title: isOnboarding ? "Final Settings" : "General Settings", color: .accentColor) + SetupHeader( + icon: "gearshape.fill", title: isOnboarding ? "Final Settings" : "General Settings", + color: .accentColor) Spacer() VStack(spacing: 30) { Text("Configure app preferences and support the project") .font(.title3) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) .multilineTextAlignment(.center) VStack(spacing: 20) { launchAtLoginToggle - + #if !APPSTORE - softwareUpdatesSection + softwareUpdatesSection #endif subtleReminderSizeSection #if !APPSTORE - supportSection + supportSection #endif } } @@ -51,7 +53,7 @@ struct GeneralSetupView: View { .font(.headline) Text("Start Gaze automatically when you log in") .font(.caption) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) } Spacer() Toggle("", isOn: $settingsManager.settings.launchAtLogin) @@ -65,42 +67,45 @@ struct GeneralSetupView: View { } #if !APPSTORE - private var softwareUpdatesSection: some View { - HStack { - VStack(alignment: .leading, spacing: 4) { - Text("Software Updates") - .font(.headline) + private var softwareUpdatesSection: some View { + HStack { + VStack(alignment: .leading, spacing: 4) { + Text("Software Updates") + .font(.headline) - if let lastCheck = updateManager.lastUpdateCheckDate { - Text("Last checked: \(lastCheck, style: .relative)") - .font(.caption) - .foregroundColor(.secondary) - .italic() - } else { - Text("Never checked for updates") - .font(.caption) - .foregroundColor(.secondary) - .italic() + if let lastCheck = updateManager.lastUpdateCheckDate { + Text("Last checked: \(lastCheck, style: .relative)") + .font(.caption) + .foregroundStyle(.secondary) + .italic() + } else { + Text("Never checked for updates") + .font(.caption) + .foregroundStyle(.secondary) + .italic() + } } + + Spacer() + + Button("Check for Updates Now") { + updateManager.checkForUpdates() + } + .buttonStyle(.bordered) + + Toggle( + "Automatically check for updates", + isOn: Binding( + get: { updateManager.automaticallyChecksForUpdates }, + set: { updateManager.automaticallyChecksForUpdates = $0 } + ) + ) + .labelsHidden() + .help("Check for new versions of Gaze in the background") } - - Spacer() - - Button("Check for Updates Now") { - updateManager.checkForUpdates() - } - .buttonStyle(.bordered) - - Toggle("Automatically check for updates", isOn: Binding( - get: { updateManager.automaticallyChecksForUpdates }, - set: { updateManager.automaticallyChecksForUpdates = $0 } - )) - .labelsHidden() - .help("Check for new versions of Gaze in the background") + .padding() + .glassEffectIfAvailable(GlassStyle.regular, in: .rect(cornerRadius: 12)) } - .padding() - .glassEffectIfAvailable(GlassStyle.regular, in: .rect(cornerRadius: 12)) - } #endif private var subtleReminderSizeSection: some View { @@ -110,20 +115,28 @@ struct GeneralSetupView: View { Text("Adjust the size of blink and posture reminders") .font(.caption) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) HStack(spacing: 12) { ForEach(ReminderSize.allCases, id: \.self) { size in Button(action: { settingsManager.settings.subtleReminderSize = size }) { VStack(spacing: 8) { Circle() - .fill(settingsManager.settings.subtleReminderSize == size ? Color.accentColor : Color.secondary.opacity(0.3)) + .fill( + settingsManager.settings.subtleReminderSize == size + ? Color.accentColor : Color.secondary.opacity(0.3) + ) .frame(width: iconSize(for: size), height: iconSize(for: size)) Text(size.displayName) .font(.caption) - .fontWeight(settingsManager.settings.subtleReminderSize == size ? .semibold : .regular) - .foregroundColor(settingsManager.settings.subtleReminderSize == size ? .primary : .secondary) + .fontWeight( + settingsManager.settings.subtleReminderSize == size + ? .semibold : .regular + ) + .foregroundStyle( + settingsManager.settings.subtleReminderSize == size + ? .primary : .secondary) } .frame(maxWidth: .infinity, minHeight: 60) .padding(.vertical, 12) @@ -142,31 +155,31 @@ struct GeneralSetupView: View { } #if !APPSTORE - private var supportSection: some View { - VStack(spacing: 12) { - Text("Support & Contribute") - .font(.headline) - .frame(maxWidth: .infinity, alignment: .leading) + private var supportSection: some View { + VStack(spacing: 12) { + Text("Support & Contribute") + .font(.headline) + .frame(maxWidth: .infinity, alignment: .leading) - ExternalLinkButton( - icon: "chevron.left.forwardslash.chevron.right", - title: "View on GitHub", - subtitle: "Star the repo, report issues, contribute", - url: "https://github.com/mikefreno/Gaze", - tint: nil - ) + ExternalLinkButton( + icon: "chevron.left.forwardslash.chevron.right", + title: "View on GitHub", + subtitle: "Star the repo, report issues, contribute", + url: "https://github.com/mikefreno/Gaze", + tint: nil + ) - ExternalLinkButton( - icon: "cup.and.saucer.fill", - iconColor: .brown, - title: "Buy Me a Coffee", - subtitle: "Support development of Gaze", - url: "https://buymeacoffee.com/mikefreno", - tint: .orange - ) + ExternalLinkButton( + icon: "cup.and.saucer.fill", + iconColor: .brown, + title: "Buy Me a Coffee", + subtitle: "Support development of Gaze", + url: "https://buymeacoffee.com/mikefreno", + tint: .orange + ) + } + .padding() } - .padding() - } #endif private func applyLaunchAtLoginSetting(enabled: Bool) { @@ -205,14 +218,14 @@ struct ExternalLinkButton: View { HStack { Image(systemName: icon) .font(.title3) - .foregroundColor(iconColor) + .foregroundStyle(iconColor) VStack(alignment: .leading, spacing: 2) { Text(title) .font(.subheadline) .fontWeight(.semibold) Text(subtitle) .font(.caption) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) } Spacer() Image(systemName: "arrow.up.right") @@ -224,7 +237,8 @@ struct ExternalLinkButton: View { } .buttonStyle(.plain) .glassEffectIfAvailable( - tint != nil ? GlassStyle.regular.tint(tint!).interactive() : GlassStyle.regular.interactive(), + tint != nil + ? GlassStyle.regular.tint(tint!).interactive() : GlassStyle.regular.interactive(), in: .rect(cornerRadius: 10) ) } diff --git a/Gaze/Views/Setup/PostureSetupView.swift b/Gaze/Views/Setup/PostureSetupView.swift index 47f5e4b..659be71 100644 --- a/Gaze/Views/Setup/PostureSetupView.swift +++ b/Gaze/Views/Setup/PostureSetupView.swift @@ -21,21 +21,27 @@ struct PostureSetupView: View { VStack(spacing: 30) { HStack(spacing: 12) { Button(action: { - if let url = URL(string: "https://pubmed.ncbi.nlm.nih.gov/40111906/#:~:text=For%20studies%20exploring%20sitting%20posture%2C%20seven%20found%20a%20relationship%20with%20LBP.%20Regarding%20studies%20on%20sitting%20behavior%2C%20only%20one%20showed%20no%20relationship%20between%20LBP%20prevalence") { + if let url = URL( + string: + "https://pubmed.ncbi.nlm.nih.gov/40111906/#:~:text=For%20studies%20exploring%20sitting%20posture%2C%20seven%20found%20a%20relationship%20with%20LBP.%20Regarding%20studies%20on%20sitting%20behavior%2C%20only%20one%20showed%20no%20relationship%20between%20LBP%20prevalence" + ) { NSWorkspace.shared.open(url) } }) { Image(systemName: "info.circle") - .foregroundColor(.white) + .foregroundStyle(.white) } .buttonStyle(.plain) - - Text("Regular posture checks help prevent back and neck pain from prolonged sitting") - .font(.headline) - .foregroundColor(.white) + + Text( + "Regular posture checks help prevent back and neck pain from prolonged sitting" + ) + .font(.headline) + .foregroundStyle(.white) } .padding() - .glassEffectIfAvailable(GlassStyle.regular.tint(.accentColor), in: .rect(cornerRadius: 8)) + .glassEffectIfAvailable( + GlassStyle.regular.tint(.accentColor), in: .rect(cornerRadius: 8)) SliderSection( intervalSettings: Binding( @@ -46,7 +52,8 @@ struct PostureSetupView: View { ) }, set: { newValue in - settingsManager.settings.postureTimer.intervalSeconds = (newValue.val ?? 30) * 60 + settingsManager.settings.postureTimer.intervalSeconds = + (newValue.val ?? 30) * 60 } ), countdownSettings: nil, @@ -67,7 +74,9 @@ struct PostureSetupView: View { guard let screen = NSScreen.main else { return } previewWindowController = PreviewWindowHelper.showPreview( on: screen, - content: PostureReminderView(sizePercentage: settingsManager.settings.subtleReminderSize.percentage) { [weak previewWindowController] in + content: PostureReminderView( + sizePercentage: settingsManager.settings.subtleReminderSize.percentage + ) { [weak previewWindowController] in previewWindowController?.window?.close() } ) diff --git a/Gaze/Views/Setup/SmartModeSetupView.swift b/Gaze/Views/Setup/SmartModeSetupView.swift index d4bb471..f87b8aa 100644 --- a/Gaze/Views/Setup/SmartModeSetupView.swift +++ b/Gaze/Views/Setup/SmartModeSetupView.swift @@ -14,10 +14,10 @@ struct SmartModeSetupView: View { var body: some View { VStack(spacing: 0) { SetupHeader(icon: "brain.fill", title: "Smart Mode", color: .purple) - + Text("Automatically manage timers based on your activity") .font(.subheadline) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) .padding(.bottom, 30) Spacer() @@ -42,18 +42,21 @@ struct SmartModeSetupView: View { VStack(alignment: .leading, spacing: 4) { HStack { Image(systemName: "arrow.up.left.and.arrow.down.right") - .foregroundColor(.blue) + .foregroundStyle(.blue) Text("Auto-pause on Fullscreen") .font(.headline) } - Text("Timers will automatically pause when you enter fullscreen mode (videos, games, presentations)") - .font(.caption) - .foregroundColor(.secondary) + Text( + "Timers will automatically pause when you enter fullscreen mode (videos, games, presentations)" + ) + .font(.caption) + .foregroundStyle(.secondary) } Spacer() Toggle("", isOn: $settingsManager.settings.smartMode.autoPauseOnFullscreen) .labelsHidden() - .onChange(of: settingsManager.settings.smartMode.autoPauseOnFullscreen) { _, newValue in + .onChange(of: settingsManager.settings.smartMode.autoPauseOnFullscreen) { + _, newValue in if newValue { permissionManager.requestAuthorizationIfNeeded() } @@ -61,7 +64,8 @@ struct SmartModeSetupView: View { } if settingsManager.settings.smartMode.autoPauseOnFullscreen, - permissionManager.authorizationStatus != .authorized { + permissionManager.authorizationStatus != .authorized + { permissionWarningView } } @@ -81,7 +85,7 @@ struct SmartModeSetupView: View { Text("macOS requires Screen Recording permission to detect other apps in fullscreen.") .font(.caption) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) HStack { Button("Grant Access") { @@ -107,13 +111,13 @@ struct SmartModeSetupView: View { VStack(alignment: .leading, spacing: 4) { HStack { Image(systemName: "moon.zzz.fill") - .foregroundColor(.indigo) + .foregroundStyle(.indigo) Text("Auto-pause on Idle") .font(.headline) } Text("Timers will pause when you're inactive for more than the threshold below") .font(.caption) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) } Spacer() Toggle("", isOn: $settingsManager.settings.smartMode.autoPauseOnIdle) @@ -139,13 +143,15 @@ struct SmartModeSetupView: View { VStack(alignment: .leading, spacing: 4) { HStack { Image(systemName: "chart.line.uptrend.xyaxis") - .foregroundColor(.green) + .foregroundStyle(.green) Text("Track Usage Statistics") .font(.headline) } - Text("Monitor active and idle time, with automatic reset after the specified duration") - .font(.caption) - .foregroundColor(.secondary) + Text( + "Monitor active and idle time, with automatic reset after the specified duration" + ) + .font(.caption) + .foregroundStyle(.secondary) } Spacer() Toggle("", isOn: $settingsManager.settings.smartMode.trackUsage) @@ -182,7 +188,7 @@ struct ThresholdSlider: View { Spacer() Text("\(value) \(unit)") .font(.subheadline) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) } Slider( diff --git a/Gaze/Views/Setup/UserTimersView.swift b/Gaze/Views/Setup/UserTimersView.swift index 5a6a58a..d747110 100644 --- a/Gaze/Views/Setup/UserTimersView.swift +++ b/Gaze/Views/Setup/UserTimersView.swift @@ -17,7 +17,7 @@ struct UserTimersView: View { VStack(spacing: 16) { Image(systemName: "clock.badge.checkmark") .font(.system(size: 60)) - .foregroundColor(.purple) + .foregroundStyle(.purple) Text("Custom Timers") .font(.system(size: 28, weight: .bold)) } @@ -28,14 +28,14 @@ struct UserTimersView: View { VStack(spacing: 30) { Text("Create your own reminder schedules") .font(.title3) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) HStack(spacing: 12) { Image(systemName: "info.circle") - .foregroundColor(.white) + .foregroundStyle(.white) Text("Add up to 3 custom timers with your own intervals and messages") .font(.headline) - .foregroundColor(.white) + .foregroundStyle(.white) } .padding() .glassEffectIfAvailable( @@ -66,13 +66,13 @@ struct UserTimersView: View { VStack(spacing: 12) { Image(systemName: "clock.badge.questionmark") .font(.system(size: 40)) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) Text("No custom timers yet") .font(.subheadline) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) Text("Click 'Add Timer' to create your first custom reminder") .font(.caption) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) } .frame(maxWidth: .infinity) .padding(40) @@ -153,7 +153,7 @@ struct UserTimerRow: View { .frame(width: 12, height: 12) Image(systemName: timer.type == .subtle ? "eye.circle" : "rectangle.on.rectangle") - .foregroundColor(timer.color) + .foregroundStyle(timer.color) .frame(width: 24) VStack(alignment: .leading, spacing: 4) { @@ -165,7 +165,7 @@ struct UserTimerRow: View { "\(timer.type.displayName) • \(timer.timeOnScreenSeconds)s on screen • \(timer.intervalMinutes) min interval" ) .font(.caption) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) } Spacer() @@ -179,14 +179,14 @@ struct UserTimerRow: View { Button(action: onEdit) { Image(systemName: "pencil.circle.fill") .font(.title3) - .foregroundColor(.accentColor) + .foregroundStyle(Color.accentColor) } .buttonStyle(.plain) Button(action: { showingDeleteConfirmation = true }) { Image(systemName: "trash.circle.fill") .font(.title3) - .foregroundColor(.red) + .foregroundStyle(.red) } .buttonStyle(.plain) .confirmationDialog("Delete Timer", isPresented: $showingDeleteConfirmation) { @@ -264,7 +264,7 @@ struct UserTimerEditSheet: View { .textFieldStyle(.roundedBorder) Text("Example: \"Stretch Break\", \"Eye Rest\", \"Water Break\"") .font(.caption) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) } VStack(alignment: .leading, spacing: 8) { @@ -318,7 +318,7 @@ struct UserTimerEditSheet: View { : "Full screen reminder with animation" ) .font(.caption) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) } if type == .overlay { @@ -359,7 +359,7 @@ struct UserTimerEditSheet: View { } Text("How often this reminder will appear (in minutes)") .font(.caption) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) } VStack(alignment: .leading, spacing: 8) { @@ -369,7 +369,7 @@ struct UserTimerEditSheet: View { .textFieldStyle(.roundedBorder) Text("Leave blank to show a default timer notification") .font(.caption) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) } } .padding() diff --git a/Gaze/Views/Setup/WelcomeView.swift b/Gaze/Views/Setup/WelcomeView.swift index f658a9e..ba6c7c0 100644 --- a/Gaze/Views/Setup/WelcomeView.swift +++ b/Gaze/Views/Setup/WelcomeView.swift @@ -14,14 +14,14 @@ struct WelcomeView: View { Image(systemName: "eye.fill") .font(.system(size: 80)) - .foregroundColor(.accentColor) + .foregroundStyle(Color.accentColor) Text("Welcome to Gaze") .font(.system(size: 36, weight: .bold)) Text("Take care of your eyes and posture") .font(.title3) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) VStack(alignment: .leading, spacing: 16) { FeatureRow( @@ -66,7 +66,7 @@ struct FeatureRow: View { HStack(alignment: .top, spacing: 16) { Image(systemName: icon) .font(.title2) - .foregroundColor(iconColor) + .foregroundStyle(iconColor) .frame(width: 30) VStack(alignment: .leading, spacing: 4) { @@ -74,7 +74,7 @@ struct FeatureRow: View { .font(.headline) Text(description) .font(.subheadline) - .foregroundColor(.secondary) + .foregroundStyle(.secondary) } } }