general: foregroundColor -> foregroundStyle (will change in future)

This commit is contained in:
Michael Freno
2026-01-16 18:16:04 -05:00
parent c825ce16e2
commit 44445f2fd5
20 changed files with 449 additions and 367 deletions

View File

@@ -34,20 +34,22 @@ struct EyeTrackingCalibrationView: View {
VStack(spacing: 30) { VStack(spacing: 30) {
Image(systemName: "eye.circle.fill") Image(systemName: "eye.circle.fill")
.font(.system(size: 80)) .font(.system(size: 80))
.foregroundColor(.blue) .foregroundStyle(.blue)
Text("Eye Tracking Calibration") Text("Eye Tracking Calibration")
.font(.largeTitle) .font(.largeTitle)
.foregroundStyle(.white)
.fontWeight(.bold) .fontWeight(.bold)
Text("This calibration will help improve eye tracking accuracy.") Text("This calibration will help improve eye tracking accuracy.")
.font(.title3) .font(.title3)
.multilineTextAlignment(.center) .multilineTextAlignment(.center)
.foregroundColor(.secondary) .foregroundStyle(.gray)
VStack(alignment: .leading, spacing: 15) { VStack(alignment: .leading, spacing: 15) {
InstructionRow(icon: "1.circle.fill", text: "Look at each target on the screen") 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: "3.circle.fill", text: "Follow the countdown at each position")
InstructionRow(icon: "4.circle.fill", text: "Takes about 30-45 seconds") InstructionRow(icon: "4.circle.fill", text: "Takes about 30-45 seconds")
} }
@@ -57,11 +59,11 @@ struct EyeTrackingCalibrationView: View {
VStack(spacing: 10) { VStack(spacing: 10) {
Text("Last calibration:") Text("Last calibration:")
.font(.caption) .font(.caption)
.foregroundColor(.secondary) .foregroundStyle(.gray)
Text(calibrationManager.getCalibrationSummary()) Text(calibrationManager.getCalibrationSummary())
.font(.caption) .font(.caption)
.multilineTextAlignment(.center) .multilineTextAlignment(.center)
.foregroundColor(.secondary) .foregroundStyle(.gray)
} }
.padding(.vertical) .padding(.vertical)
} }
@@ -70,6 +72,8 @@ struct EyeTrackingCalibrationView: View {
Button("Cancel") { Button("Cancel") {
dismiss() dismiss()
} }
.foregroundStyle(.white)
.buttonStyle(.plain)
.keyboardShortcut(.escape, modifiers: []) .keyboardShortcut(.escape, modifiers: [])
Button("Start Calibration") { Button("Start Calibration") {
@@ -113,10 +117,10 @@ struct EyeTrackingCalibrationView: View {
VStack(spacing: 10) { VStack(spacing: 10) {
HStack { HStack {
Text("Calibrating...") Text("Calibrating...")
.foregroundColor(.white) .foregroundStyle(.white)
Spacer() Spacer()
Text(calibrationManager.progressText) Text(calibrationManager.progressText)
.foregroundColor(.white.opacity(0.7)) .foregroundStyle(.white.opacity(0.7))
} }
ProgressView(value: calibrationManager.progress) ProgressView(value: calibrationManager.progress)
@@ -141,7 +145,9 @@ struct EyeTrackingCalibrationView: View {
.stroke(Color.blue.opacity(0.3), lineWidth: 3) .stroke(Color.blue.opacity(0.3), lineWidth: 3)
.frame(width: 100, height: 100) .frame(width: 100, height: 100)
.scaleEffect(isCountingDown ? 1.2 : 1.0) .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 // Inner circle
Circle() Circle()
@@ -152,18 +158,18 @@ struct EyeTrackingCalibrationView: View {
if isCountingDown && countdownValue > 0 { if isCountingDown && countdownValue > 0 {
Text("\(countdownValue)") Text("\(countdownValue)")
.font(.system(size: 36, weight: .bold)) .font(.system(size: 36, weight: .bold))
.foregroundColor(.white) .foregroundStyle(.white)
} else if calibrationManager.samplesCollected > 0 { } else if calibrationManager.samplesCollected > 0 {
Image(systemName: "checkmark") Image(systemName: "checkmark")
.font(.system(size: 30, weight: .bold)) .font(.system(size: 30, weight: .bold))
.foregroundColor(.white) .foregroundStyle(.white)
} }
} }
// Instruction text // Instruction text
Text(step.instructionText) Text(step.instructionText)
.font(.title2) .font(.title2)
.foregroundColor(.white) .foregroundStyle(.white)
.padding(.horizontal, 40) .padding(.horizontal, 40)
.padding(.vertical, 15) .padding(.vertical, 15)
.background(Color.black.opacity(0.7)) .background(Color.black.opacity(0.7))
@@ -182,7 +188,7 @@ struct EyeTrackingCalibrationView: View {
calibrationManager.skipStep() calibrationManager.skipStep()
} label: { } label: {
Text("Skip this position") Text("Skip this position")
.foregroundColor(.white) .foregroundStyle(.white)
.padding(.horizontal, 20) .padding(.horizontal, 20)
.padding(.vertical, 10) .padding(.vertical, 10)
.background(Color.white.opacity(0.2)) .background(Color.white.opacity(0.2))
@@ -258,10 +264,11 @@ struct InstructionRow: View {
HStack(spacing: 15) { HStack(spacing: 15) {
Image(systemName: icon) Image(systemName: icon)
.font(.title2) .font(.title2)
.foregroundColor(.blue) .foregroundStyle(.blue)
.frame(width: 30) .frame(width: 30)
Text(text) Text(text)
.foregroundStyle(.white)
.font(.body) .font(.body)
} }
} }

View File

@@ -28,7 +28,7 @@ struct GazeOverlayView: View {
Text(eyeTrackingService.isInFrame ? "In Frame" : "No Face") Text(eyeTrackingService.isInFrame ? "In Frame" : "No Face")
.font(.caption2) .font(.caption2)
.fontWeight(.semibold) .fontWeight(.semibold)
.foregroundColor(.white) .foregroundStyle(.white)
} }
.padding(.horizontal, 10) .padding(.horizontal, 10)
.padding(.vertical, 6) .padding(.vertical, 6)
@@ -46,7 +46,9 @@ struct GazeOverlayView: View {
ForEach(0..<3, id: \.self) { row in ForEach(0..<3, id: \.self) { row in
HStack(spacing: 2) { HStack(spacing: 2) {
ForEach(0..<3, id: \.self) { col in 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) gridCell(row: row, col: col, isActive: isActive)
} }
} }
@@ -68,7 +70,7 @@ struct GazeOverlayView: View {
Text(direction.rawValue) Text(direction.rawValue)
.font(.system(size: 14, weight: .bold)) .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) .frame(width: 28, height: 28)
} }
@@ -95,12 +97,12 @@ struct GazeOverlayView: View {
if let leftH = eyeTrackingService.debugLeftPupilRatio { if let leftH = eyeTrackingService.debugLeftPupilRatio {
Text("L.H: \(String(format: "%.2f", leftH))") Text("L.H: \(String(format: "%.2f", leftH))")
.font(.system(size: 9, weight: .medium, design: .monospaced)) .font(.system(size: 9, weight: .medium, design: .monospaced))
.foregroundColor(.white) .foregroundStyle(.white)
} }
if let rightH = eyeTrackingService.debugRightPupilRatio { if let rightH = eyeTrackingService.debugRightPupilRatio {
Text("R.H: \(String(format: "%.2f", rightH))") Text("R.H: \(String(format: "%.2f", rightH))")
.font(.system(size: 9, weight: .medium, design: .monospaced)) .font(.system(size: 9, weight: .medium, design: .monospaced))
.foregroundColor(.white) .foregroundStyle(.white)
} }
} }
@@ -108,25 +110,26 @@ struct GazeOverlayView: View {
if let leftV = eyeTrackingService.debugLeftVerticalRatio { if let leftV = eyeTrackingService.debugLeftVerticalRatio {
Text("L.V: \(String(format: "%.2f", leftV))") Text("L.V: \(String(format: "%.2f", leftV))")
.font(.system(size: 9, weight: .medium, design: .monospaced)) .font(.system(size: 9, weight: .medium, design: .monospaced))
.foregroundColor(.white) .foregroundStyle(.white)
} }
if let rightV = eyeTrackingService.debugRightVerticalRatio { if let rightV = eyeTrackingService.debugRightVerticalRatio {
Text("R.V: \(String(format: "%.2f", rightV))") Text("R.V: \(String(format: "%.2f", rightV))")
.font(.system(size: 9, weight: .medium, design: .monospaced)) .font(.system(size: 9, weight: .medium, design: .monospaced))
.foregroundColor(.white) .foregroundStyle(.white)
} }
} }
// Show averaged ratios // Show averaged ratios
if let leftH = eyeTrackingService.debugLeftPupilRatio, if let leftH = eyeTrackingService.debugLeftPupilRatio,
let rightH = eyeTrackingService.debugRightPupilRatio, let rightH = eyeTrackingService.debugRightPupilRatio,
let leftV = eyeTrackingService.debugLeftVerticalRatio, let leftV = eyeTrackingService.debugLeftVerticalRatio,
let rightV = eyeTrackingService.debugRightVerticalRatio { let rightV = eyeTrackingService.debugRightVerticalRatio
{
let avgH = (leftH + rightH) / 2.0 let avgH = (leftH + rightH) / 2.0
let avgV = (leftV + rightV) / 2.0 let avgV = (leftV + rightV) / 2.0
Text("Avg H:\(String(format: "%.2f", avgH)) V:\(String(format: "%.2f", avgV))") Text("Avg H:\(String(format: "%.2f", avgH)) V:\(String(format: "%.2f", avgV))")
.font(.system(size: 9, weight: .bold, design: .monospaced)) .font(.system(size: 9, weight: .bold, design: .monospaced))
.foregroundColor(.yellow) .foregroundStyle(.yellow)
} }
} }
.padding(.horizontal, 8) .padding(.horizontal, 8)
@@ -143,7 +146,7 @@ struct GazeOverlayView: View {
VStack(spacing: 4) { VStack(spacing: 4) {
Text("Left") Text("Left")
.font(.system(size: 8, weight: .bold)) .font(.system(size: 8, weight: .bold))
.foregroundColor(.white) .foregroundStyle(.white)
HStack(spacing: 4) { HStack(spacing: 4) {
eyeImageView( eyeImageView(
@@ -165,7 +168,7 @@ struct GazeOverlayView: View {
VStack(spacing: 4) { VStack(spacing: 4) {
Text("Right") Text("Right")
.font(.system(size: 8, weight: .bold)) .font(.system(size: 8, weight: .bold))
.foregroundColor(.white) .foregroundStyle(.white)
HStack(spacing: 4) { HStack(spacing: 4) {
eyeImageView( eyeImageView(
@@ -190,7 +193,9 @@ struct GazeOverlayView: View {
) )
} }
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 let displaySize: CGFloat = 50
return VStack(spacing: 2) { return VStack(spacing: 2) {
@@ -203,7 +208,9 @@ struct GazeOverlayView: View {
.frame(width: displaySize, height: displaySize) .frame(width: displaySize, height: displaySize)
// Draw pupil position marker // 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 scaleX = displaySize / size.width
let scaleY = displaySize / size.height let scaleY = displaySize / size.height
let scale = min(scaleX, scaleY) let scale = min(scaleX, scaleY)
@@ -224,7 +231,7 @@ struct GazeOverlayView: View {
.frame(width: displaySize, height: displaySize) .frame(width: displaySize, height: displaySize)
Text("--") Text("--")
.font(.system(size: 10)) .font(.system(size: 10))
.foregroundColor(.white.opacity(0.5)) .foregroundStyle(.white.opacity(0.5))
} }
} }
.frame(width: displaySize, height: displaySize) .frame(width: displaySize, height: displaySize)
@@ -232,7 +239,7 @@ struct GazeOverlayView: View {
Text(label) Text(label)
.font(.system(size: 7)) .font(.system(size: 7))
.foregroundColor(.white.opacity(0.7)) .foregroundStyle(.white.opacity(0.7))
} }
} }
} }

View File

@@ -16,21 +16,21 @@ struct InfoBox: View {
if let url = url, let urlObj = URL(string: url) { if let url = url, let urlObj = URL(string: url) {
Button(action: { Button(action: {
#if os(iOS) #if os(iOS)
UIApplication.shared.open(urlObj) UIApplication.shared.open(urlObj)
#elseif os(macOS) #elseif os(macOS)
NSWorkspace.shared.open(urlObj) NSWorkspace.shared.open(urlObj)
#endif #endif
}) { }) {
Image(systemName: "info.circle") Image(systemName: "info.circle")
.foregroundColor(.white) .foregroundStyle(.white)
}.buttonStyle(.plain) }.buttonStyle(.plain)
} else { } else {
Image(systemName: "info.circle") Image(systemName: "info.circle")
.foregroundColor(.white) .foregroundStyle(.white)
} }
Text(text) Text(text)
.font(.headline) .font(.headline)
.foregroundColor(.white) .foregroundStyle(.white)
} }
.padding() .padding()
.glassEffectIfAvailable(GlassStyle.regular.tint(.accentColor), in: .rect(cornerRadius: 8)) .glassEffectIfAvailable(GlassStyle.regular.tint(.accentColor), in: .rect(cornerRadius: 8))
@@ -38,6 +38,10 @@ struct InfoBox: View {
} }
#Preview { #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") InfoBox(
.padding() 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()
} }

View File

@@ -19,8 +19,9 @@ struct PupilOverlayView: View {
ZStack { ZStack {
// Left eye // Left eye
if let leftRegion = eyeTrackingService.debugLeftEyeRegion, if let leftRegion = eyeTrackingService.debugLeftEyeRegion,
let leftPupil = eyeTrackingService.debugLeftPupilPosition, let leftPupil = eyeTrackingService.debugLeftPupilPosition,
let imageSize = eyeTrackingService.debugImageSize { let imageSize = eyeTrackingService.debugImageSize
{
EyeOverlayShape( EyeOverlayShape(
eyeRegion: leftRegion, eyeRegion: leftRegion,
pupilPosition: leftPupil, pupilPosition: leftPupil,
@@ -33,8 +34,9 @@ struct PupilOverlayView: View {
// Right eye // Right eye
if let rightRegion = eyeTrackingService.debugRightEyeRegion, if let rightRegion = eyeTrackingService.debugRightEyeRegion,
let rightPupil = eyeTrackingService.debugRightPupilPosition, let rightPupil = eyeTrackingService.debugRightPupilPosition,
let imageSize = eyeTrackingService.debugImageSize { let imageSize = eyeTrackingService.debugImageSize
{
EyeOverlayShape( EyeOverlayShape(
eyeRegion: rightRegion, eyeRegion: rightRegion,
pupilPosition: rightPupil, pupilPosition: rightPupil,
@@ -160,14 +162,14 @@ private struct EyeOverlayShape: View {
// Label // Label
Text(label) Text(label)
.font(.system(size: 10, weight: .bold)) .font(.system(size: 10, weight: .bold))
.foregroundColor(color) .foregroundStyle(color)
.position(x: eyeRect.minX + 8, y: eyeRect.minY - 8) .position(x: eyeRect.minX + 8, y: eyeRect.minY - 8)
// Debug: Show raw coordinates // Debug: Show raw coordinates
Text("\(label): (\(Int(pupilPosition.x)), \(Int(pupilPosition.y)))") Text("\(label): (\(Int(pupilPosition.x)), \(Int(pupilPosition.y)))")
.font(.system(size: 8, design: .monospaced)) .font(.system(size: 8, design: .monospaced))
.foregroundColor(.white) .foregroundStyle(.white)
.background(Color.black.opacity(0.7)) .background(.black.opacity(0.7))
.position(x: eyeRect.midX, y: eyeRect.maxY + 10) .position(x: eyeRect.midX, y: eyeRect.maxY + 10)
} }
} }

View File

@@ -16,7 +16,7 @@ struct SetupHeader: View {
VStack(spacing: 16) { VStack(spacing: 16) {
Image(systemName: icon) Image(systemName: icon)
.font(.system(size: 60)) .font(.system(size: 60))
.foregroundColor(color) .foregroundStyle(color)
Text(title) Text(title)
.font(.system(size: 28, weight: .bold)) .font(.system(size: 28, weight: .bold))
} }

View File

@@ -42,7 +42,7 @@ struct SliderSection: View {
VStack(alignment: .leading, spacing: 12) { VStack(alignment: .leading, spacing: 12) {
Text("Remind me every:") Text("Remind me every:")
.font(.subheadline) .font(.subheadline)
.foregroundColor(.secondary) .foregroundStyle(.secondary)
HStack { HStack {
Slider( Slider(
value: Binding( value: Binding(
@@ -62,7 +62,7 @@ struct SliderSection: View {
if let range = countdownSettings.range { if let range = countdownSettings.range {
Text("Look away for:") Text("Look away for:")
.font(.subheadline) .font(.subheadline)
.foregroundColor(.secondary) .foregroundStyle(.secondary)
HStack { HStack {
Slider( Slider(
value: Binding( value: Binding(
@@ -89,14 +89,14 @@ struct SliderSection: View {
reminderText reminderText
) )
.font(.subheadline) .font(.subheadline)
.foregroundColor(.secondary) .foregroundStyle(.secondary)
.multilineTextAlignment(.center) .multilineTextAlignment(.center)
} else { } else {
Text( Text(
"\(type) reminders are currently disabled." "\(type) reminders are currently disabled."
) )
.font(.caption) .font(.caption)
.foregroundColor(.secondary) .foregroundStyle(.secondary)
} }
Button(action: { Button(action: {
@@ -104,10 +104,10 @@ struct SliderSection: View {
}) { }) {
HStack(spacing: 8) { HStack(spacing: 8) {
Image(systemName: "eye") Image(systemName: "eye")
.foregroundColor(.white) .foregroundStyle(.white)
Text("Preview Reminder") Text("Preview Reminder")
.font(.headline) .font(.headline)
.foregroundColor(.white) .foregroundStyle(.white)
} }
.padding(.horizontal, 16) .padding(.horizontal, 16)
.padding(.vertical, 10) .padding(.vertical, 10)

View File

@@ -90,7 +90,8 @@ final class OnboardingWindowPresenter {
self?.windowController = nil self?.windowController = nil
self?.removeCloseObserver() 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 #if APPSTORE
.frame(minWidth: 1000, minHeight: 700) .frame(minWidth: 1000, minHeight: 700)
#else #else
.frame(minWidth: 1000, minHeight: 900) .frame(minWidth: 1000, minHeight: 900)
#endif #endif
} }
@@ -159,11 +160,12 @@ struct OnboardingContainerView: View {
} }
.font(.headline) .font(.headline)
.frame(minWidth: 100, maxWidth: .infinity, minHeight: 44, maxHeight: 44) .frame(minWidth: 100, maxWidth: .infinity, minHeight: 44, maxHeight: 44)
.foregroundColor(.primary) .foregroundStyle(.primary)
.contentShape(RoundedRectangle(cornerRadius: 10)) .contentShape(RoundedRectangle(cornerRadius: 10))
} }
.buttonStyle(.plain) .buttonStyle(.plain)
.glassEffectIfAvailable(GlassStyle.regular.interactive(), in: .rect(cornerRadius: 10)) .glassEffectIfAvailable(
GlassStyle.regular.interactive(), in: .rect(cornerRadius: 10))
} }
Button(action: { Button(action: {
@@ -173,11 +175,14 @@ struct OnboardingContainerView: View {
currentPage += 1 currentPage += 1
} }
}) { }) {
Text(currentPage == 0 ? "Let's Get Started" : currentPage == 5 ? "Get Started" : "Continue") Text(
.font(.headline) currentPage == 0
.frame(minWidth: 100, maxWidth: .infinity, minHeight: 44, maxHeight: 44) ? "Let's Get Started" : currentPage == 5 ? "Get Started" : "Continue"
.foregroundColor(.white) )
.contentShape(RoundedRectangle(cornerRadius: 10)) .font(.headline)
.frame(minWidth: 100, maxWidth: .infinity, minHeight: 44, maxHeight: 44)
.foregroundStyle(.white)
.contentShape(RoundedRectangle(cornerRadius: 10))
} }
.buttonStyle(.plain) .buttonStyle(.plain)
.glassEffectIfAvailable( .glassEffectIfAvailable(

View File

@@ -50,7 +50,7 @@ struct MenuBarHoverButtonStyle: ButtonStyle {
func makeBody(configuration: Configuration) -> some View { func makeBody(configuration: Configuration) -> some View {
configuration.label configuration.label
.foregroundColor(isHovered ? .white : .primary) .foregroundStyle(isHovered ? .white : .primary)
.glassEffectIfAvailable( .glassEffectIfAvailable(
isHovered isHovered
? GlassStyle.regular.tint(.accentColor).interactive() ? GlassStyle.regular.tint(.accentColor).interactive()
@@ -83,7 +83,7 @@ struct MenuBarContentView: View {
VStack(alignment: .leading, spacing: 12) { VStack(alignment: .leading, spacing: 12) {
Text("Active Timers") Text("Active Timers")
.font(.caption) .font(.caption)
.foregroundColor(.secondary) .foregroundStyle(.secondary)
.padding(.horizontal) .padding(.horizontal)
.padding(.top, 8) .padding(.top, 8)
@@ -176,7 +176,7 @@ struct MenuBarContentView: View {
}) { }) {
HStack { HStack {
Image(systemName: "checkmark.circle.fill") Image(systemName: "checkmark.circle.fill")
.foregroundColor(.accentColor) .foregroundStyle(Color.accentColor)
Text("Complete Onboarding") Text("Complete Onboarding")
Spacer() Spacer()
} }
@@ -193,7 +193,7 @@ struct MenuBarContentView: View {
Button(action: onQuit) { Button(action: onQuit) {
HStack { HStack {
Image(systemName: "power") Image(systemName: "power")
.foregroundColor(.red) .foregroundStyle(.red)
Text("Quit Gaze") Text("Quit Gaze")
Spacer() Spacer()
} }
@@ -207,7 +207,7 @@ struct MenuBarContentView: View {
"v\(Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString") as? String ?? "0.0.0")" "v\(Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString") as? String ?? "0.0.0")"
) )
.font(.caption) .font(.caption)
.foregroundColor(.secondary) .foregroundStyle(.secondary)
} }
.padding(.horizontal, 8) .padding(.horizontal, 8)
.padding(.vertical, 4) .padding(.vertical, 4)
@@ -335,20 +335,20 @@ struct TimerStatusRowWithIndividualControls: View {
} }
Image(systemName: iconName) Image(systemName: iconName)
.foregroundColor(isHoveredBody ? .white : color) .foregroundStyle(isHoveredBody ? .white : color)
.frame(width: 20) .frame(width: 20)
VStack(alignment: .leading, spacing: 2) { VStack(alignment: .leading, spacing: 2) {
Text(displayName) Text(displayName)
.font(.subheadline) .font(.subheadline)
.fontWeight(.medium) .fontWeight(.medium)
.foregroundColor(isHoveredBody ? .white : .primary) .foregroundStyle(isHoveredBody ? .white : .primary)
.lineLimit(1) .lineLimit(1)
if let state = state { if let state = state {
Text(state.remainingSeconds.asTimerDuration) Text(state.remainingSeconds.asTimerDuration)
.font(.caption) .font(.caption)
.foregroundColor(isHoveredBody ? .white.opacity(0.8) : .secondary) .foregroundStyle(isHoveredBody ? .white.opacity(0.8) : .secondary)
.monospacedDigit() .monospacedDigit()
} }
} }
@@ -365,7 +365,7 @@ struct TimerStatusRowWithIndividualControls: View {
Button(action: onDevTrigger) { Button(action: onDevTrigger) {
Image(systemName: "bolt.fill") Image(systemName: "bolt.fill")
.font(.caption) .font(.caption)
.foregroundColor(isHoveredDevTrigger ? .white : .yellow) .foregroundStyle(isHoveredDevTrigger ? .white : .yellow)
.padding(6) .padding(6)
.contentShape(Circle()) .contentShape(Circle())
} }
@@ -394,7 +394,7 @@ struct TimerStatusRowWithIndividualControls: View {
systemName: isPaused ? "play.circle" : "pause.circle" systemName: isPaused ? "play.circle" : "pause.circle"
) )
.font(.caption) .font(.caption)
.foregroundColor(isHoveredPauseButton ? .white : .accentColor) .foregroundStyle(isHoveredPauseButton ? .white : .accentColor)
.padding(6) .padding(6)
.contentShape(Circle()) .contentShape(Circle())
} }
@@ -416,7 +416,7 @@ struct TimerStatusRowWithIndividualControls: View {
Button(action: onSkip) { Button(action: onSkip) {
Image(systemName: "forward.fill") Image(systemName: "forward.fill")
.font(.caption) .font(.caption)
.foregroundColor(isHoveredSkip ? .white : .accentColor) .foregroundStyle(isHoveredSkip ? .white : .accentColor)
.padding(6) .padding(6)
.contentShape(Circle()) .contentShape(Circle())
} }

View File

@@ -33,11 +33,11 @@ struct LookAwayReminderView: View {
VStack(spacing: 40) { VStack(spacing: 40) {
Text("Look Away") Text("Look Away")
.font(.system(size: 64, weight: .bold)) .font(.system(size: 64, weight: .bold))
.foregroundColor(.white) .foregroundStyle(.white)
Text("Look at something 20 feet away") Text("Look at something 20 feet away")
.font(.system(size: 28)) .font(.system(size: 28))
.foregroundColor(.white.opacity(0.9)) .foregroundStyle(.white.opacity(0.9))
GazeLottieView( GazeLottieView(
animationName: AnimationAsset.lookAway.fileName, animationName: AnimationAsset.lookAway.fileName,
@@ -62,14 +62,14 @@ struct LookAwayReminderView: View {
Text("\(remainingSeconds)") Text("\(remainingSeconds)")
.font(.system(size: 48, weight: .bold)) .font(.system(size: 48, weight: .bold))
.foregroundColor(.white) .foregroundStyle(.white)
.monospacedDigit() .monospacedDigit()
.accessibilityIdentifier(AccessibilityIdentifiers.Reminders.countdownLabel) .accessibilityIdentifier(AccessibilityIdentifiers.Reminders.countdownLabel)
} }
Text("Press ESC or Space to skip") Text("Press ESC or Space to skip")
.font(.subheadline) .font(.subheadline)
.foregroundColor(.white.opacity(0.6)) .foregroundStyle(.white.opacity(0.6))
} }
// Skip button in corner // Skip button in corner
@@ -79,7 +79,7 @@ struct LookAwayReminderView: View {
Button(action: dismiss) { Button(action: dismiss) {
Image(systemName: "xmark.circle.fill") Image(systemName: "xmark.circle.fill")
.font(.system(size: 32)) .font(.system(size: 32))
.foregroundColor(.white.opacity(0.7)) .foregroundStyle(.white.opacity(0.7))
} }
.buttonStyle(.plain) .buttonStyle(.plain)
.accessibilityIdentifier(AccessibilityIdentifiers.Reminders.dismissButton) .accessibilityIdentifier(AccessibilityIdentifiers.Reminders.dismissButton)

View File

@@ -22,7 +22,7 @@ struct PostureReminderView: View {
VStack { VStack {
Image(systemName: "arrow.up.circle.fill") Image(systemName: "arrow.up.circle.fill")
.font(.system(size: scale)) .font(.system(size: scale))
.foregroundColor(.accentColor) .foregroundStyle(Color.accentColor)
} }
.opacity(opacity) .opacity(opacity)
.offset(y: yOffset) .offset(y: yOffset)

View File

@@ -32,19 +32,19 @@ struct UserTimerOverlayReminderView: View {
VStack(spacing: 40) { VStack(spacing: 40) {
Text(timer.title) Text(timer.title)
.font(.system(size: 64, weight: .bold)) .font(.system(size: 64, weight: .bold))
.foregroundColor(.white) .foregroundStyle(.white)
if let message = timer.message, !message.isEmpty { if let message = timer.message, !message.isEmpty {
Text(message) Text(message)
.font(.system(size: 28)) .font(.system(size: 28))
.foregroundColor(.white.opacity(0.9)) .foregroundStyle(.white.opacity(0.9))
.multilineTextAlignment(.center) .multilineTextAlignment(.center)
.padding(.horizontal, 40) .padding(.horizontal, 40)
} }
Image(systemName: "clock.fill") Image(systemName: "clock.fill")
.font(.system(size: 120)) .font(.system(size: 120))
.foregroundColor(timer.color) .foregroundStyle(timer.color)
.padding(.vertical, 30) .padding(.vertical, 30)
// Countdown display // Countdown display
@@ -62,13 +62,13 @@ struct UserTimerOverlayReminderView: View {
Text("\(remainingSeconds)") Text("\(remainingSeconds)")
.font(.system(size: 48, weight: .bold)) .font(.system(size: 48, weight: .bold))
.foregroundColor(.white) .foregroundStyle(.white)
.monospacedDigit() .monospacedDigit()
} }
Text("Press ESC or Space to dismiss") Text("Press ESC or Space to dismiss")
.font(.subheadline) .font(.subheadline)
.foregroundColor(.white.opacity(0.6)) .foregroundStyle(.white.opacity(0.6))
} }
// Dismiss button in corner // Dismiss button in corner
@@ -78,7 +78,7 @@ struct UserTimerOverlayReminderView: View {
Button(action: dismiss) { Button(action: dismiss) {
Image(systemName: "xmark.circle.fill") Image(systemName: "xmark.circle.fill")
.font(.system(size: 32)) .font(.system(size: 32))
.foregroundColor(.white.opacity(0.7)) .foregroundStyle(.white.opacity(0.7))
} }
.buttonStyle(.plain) .buttonStyle(.plain)
.padding(30) .padding(30)

View File

@@ -27,12 +27,12 @@ struct UserTimerReminderView: View {
VStack(spacing: 12) { VStack(spacing: 12) {
Image(systemName: "clock.fill") Image(systemName: "clock.fill")
.font(.system(size: baseSize * 0.4)) .font(.system(size: baseSize * 0.4))
.foregroundColor(timer.color) .foregroundStyle(timer.color)
if let message = timer.message, !message.isEmpty { if let message = timer.message, !message.isEmpty {
Text(message) Text(message)
.font(.system(size: baseSize * 0.24)) .font(.system(size: baseSize * 0.24))
.foregroundColor(.primary) .foregroundStyle(.primary)
.multilineTextAlignment(.center) .multilineTextAlignment(.center)
.lineLimit(2) .lineLimit(2)
} }

View File

@@ -21,45 +21,62 @@ struct BlinkSetupView: View {
VStack(spacing: 30) { VStack(spacing: 30) {
HStack(spacing: 12) { HStack(spacing: 12) {
Button(action: { 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) NSWorkspace.shared.open(url)
} }
}) { }) {
Image(systemName: "info.circle") Image(systemName: "info.circle")
.foregroundColor(.white) .foregroundStyle(.white)
} }
.buttonStyle(.plain) .buttonStyle(.plain)
Text("We blink much less when focusing on screens. Regular blink reminders help prevent dry eyes.") Text(
.font(.headline) "We blink much less when focusing on screens. Regular blink reminders help prevent dry eyes."
.foregroundColor(.white) )
.font(.headline)
.foregroundStyle(.white)
} }
.padding() .padding()
.glassEffectIfAvailable(GlassStyle.regular.tint(.accentColor), in: .rect(cornerRadius: 8)) .glassEffectIfAvailable(
GlassStyle.regular.tint(.accentColor), in: .rect(cornerRadius: 8))
VStack(alignment: .leading, spacing: 20) { VStack(alignment: .leading, spacing: 20) {
Toggle("Enable Blink Reminders", isOn: $settingsManager.settings.blinkTimer.enabled) Toggle(
.font(.headline) "Enable Blink Reminders", isOn: $settingsManager.settings.blinkTimer.enabled
)
.font(.headline)
if settingsManager.settings.blinkTimer.enabled { if settingsManager.settings.blinkTimer.enabled {
VStack(alignment: .leading, spacing: 12) { VStack(alignment: .leading, spacing: 12) {
Text("Remind me every:") Text("Remind me every:")
.font(.subheadline) .font(.subheadline)
.foregroundColor(.secondary) .foregroundStyle(.secondary)
HStack { HStack {
Slider( Slider(
value: Binding( value: Binding(
get: { Double(settingsManager.settings.blinkTimer.intervalSeconds / 60) }, get: {
set: { settingsManager.settings.blinkTimer.intervalSeconds = Int($0) * 60 } Double(
settingsManager.settings.blinkTimer.intervalSeconds
/ 60)
},
set: {
settingsManager.settings.blinkTimer.intervalSeconds =
Int($0) * 60
}
), ),
in: 1...20, in: 1...20,
step: 1 step: 1
) )
Text("\(settingsManager.settings.blinkTimer.intervalSeconds / 60) min") Text(
.frame(width: 60, alignment: .trailing) "\(settingsManager.settings.blinkTimer.intervalSeconds / 60) min"
.monospacedDigit() )
.frame(width: 60, alignment: .trailing)
.monospacedDigit()
} }
} }
} }
@@ -68,29 +85,33 @@ struct BlinkSetupView: View {
.glassEffectIfAvailable(GlassStyle.regular, in: .rect(cornerRadius: 12)) .glassEffectIfAvailable(GlassStyle.regular, in: .rect(cornerRadius: 12))
if settingsManager.settings.blinkTimer.enabled { if settingsManager.settings.blinkTimer.enabled {
Text("You will be subtly reminded every \(settingsManager.settings.blinkTimer.intervalSeconds / 60) minutes to blink") Text(
.font(.subheadline) "You will be subtly reminded every \(settingsManager.settings.blinkTimer.intervalSeconds / 60) minutes to blink"
.foregroundColor(.secondary) )
.font(.subheadline)
.foregroundStyle(.secondary)
} else { } else {
Text("Blink reminders are currently disabled.") Text("Blink reminders are currently disabled.")
.font(.caption) .font(.caption)
.foregroundColor(.secondary) .foregroundStyle(.secondary)
} }
Button(action: showPreviewWindow) { Button(action: showPreviewWindow) {
HStack(spacing: 8) { HStack(spacing: 8) {
Image(systemName: "eye") Image(systemName: "eye")
.foregroundColor(.white) .foregroundStyle(.white)
Text("Preview Reminder") Text("Preview Reminder")
.font(.headline) .font(.headline)
.foregroundColor(.white) .foregroundStyle(.white)
} }
.padding(.horizontal, 16) .padding(.horizontal, 16)
.padding(.vertical, 10) .padding(.vertical, 10)
.contentShape(RoundedRectangle(cornerRadius: 10)) .contentShape(RoundedRectangle(cornerRadius: 10))
} }
.buttonStyle(.plain) .buttonStyle(.plain)
.glassEffectIfAvailable(GlassStyle.regular.tint(.accentColor).interactive(), in: .rect(cornerRadius: 10)) .glassEffectIfAvailable(
GlassStyle.regular.tint(.accentColor).interactive(), in: .rect(cornerRadius: 10)
)
} }
Spacer() Spacer()
@@ -104,7 +125,9 @@ struct BlinkSetupView: View {
guard let screen = NSScreen.main else { return } guard let screen = NSScreen.main else { return }
previewWindowController = PreviewWindowHelper.showPreview( previewWindowController = PreviewWindowHelper.showPreview(
on: screen, 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() previewWindowController?.window?.close()
} }
) )

View File

@@ -14,14 +14,14 @@ struct CompletionView: View {
Image(systemName: "checkmark.circle.fill") Image(systemName: "checkmark.circle.fill")
.font(.system(size: 80)) .font(.system(size: 80))
.foregroundColor(.green) .foregroundStyle(.green)
Text("You're All Set!") Text("You're All Set!")
.font(.system(size: 36, weight: .bold)) .font(.system(size: 36, weight: .bold))
Text("Gaze will now help you take care of your eyes and posture") Text("Gaze will now help you take care of your eyes and posture")
.font(.title3) .font(.title3)
.foregroundColor(.secondary) .foregroundStyle(.secondary)
.multilineTextAlignment(.center) .multilineTextAlignment(.center)
.padding(.horizontal, 40) .padding(.horizontal, 40)
@@ -32,7 +32,7 @@ struct CompletionView: View {
HStack(spacing: 16) { HStack(spacing: 16) {
Image(systemName: "menubar.rectangle") Image(systemName: "menubar.rectangle")
.foregroundColor(.accentColor) .foregroundStyle(Color.accentColor)
.frame(width: 30) .frame(width: 30)
Text("Gaze will appear in your menu bar") Text("Gaze will appear in your menu bar")
.font(.subheadline) .font(.subheadline)
@@ -41,7 +41,7 @@ struct CompletionView: View {
HStack(spacing: 16) { HStack(spacing: 16) {
Image(systemName: "clock") Image(systemName: "clock")
.foregroundColor(.accentColor) .foregroundStyle(Color.accentColor)
.frame(width: 30) .frame(width: 30)
Text("Timers will start automatically") Text("Timers will start automatically")
.font(.subheadline) .font(.subheadline)
@@ -50,7 +50,7 @@ struct CompletionView: View {
HStack(spacing: 16) { HStack(spacing: 16) {
Image(systemName: "gearshape") Image(systemName: "gearshape")
.foregroundColor(.accentColor) .foregroundStyle(Color.accentColor)
.frame(width: 30) .frame(width: 30)
Text("Adjust settings anytime from the menu bar") Text("Adjust settings anytime from the menu bar")
.font(.subheadline) .font(.subheadline)
@@ -59,7 +59,7 @@ struct CompletionView: View {
HStack(spacing: 16) { HStack(spacing: 16) {
Image(systemName: "plus.circle") Image(systemName: "plus.circle")
.foregroundColor(.accentColor) .foregroundStyle(Color.accentColor)
.frame(width: 30) .frame(width: 30)
Text("Create custom timers in Settings for additional reminders") Text("Create custom timers in Settings for additional reminders")
.font(.subheadline) .font(.subheadline)

View File

@@ -6,8 +6,8 @@
// //
import AVFoundation import AVFoundation
import SwiftUI
import Foundation import Foundation
import SwiftUI
struct EnforceModeSetupView: View { struct EnforceModeSetupView: View {
@Bindable var settingsManager: SettingsManager @Bindable var settingsManager: SettingsManager
@@ -33,7 +33,7 @@ struct EnforceModeSetupView: View {
VStack(spacing: 30) { VStack(spacing: 30) {
Text("Use your camera to ensure you take breaks") Text("Use your camera to ensure you take breaks")
.font(.title3) .font(.title3)
.foregroundColor(.secondary) .foregroundStyle(.secondary)
.multilineTextAlignment(.center) .multilineTextAlignment(.center)
VStack(spacing: 20) { VStack(spacing: 20) {
@@ -43,7 +43,7 @@ struct EnforceModeSetupView: View {
.font(.headline) .font(.headline)
Text("Camera activates 3 seconds before lookaway reminders") Text("Camera activates 3 seconds before lookaway reminders")
.font(.caption) .font(.caption)
.foregroundColor(.secondary) .foregroundStyle(.secondary)
} }
Spacer() Spacer()
Toggle( Toggle(
@@ -149,7 +149,7 @@ struct EnforceModeSetupView: View {
HStack { HStack {
Image(systemName: "target") Image(systemName: "target")
.font(.title3) .font(.title3)
.foregroundColor(.blue) .foregroundStyle(.blue)
Text("Eye Tracking Calibration") Text("Eye Tracking Calibration")
.font(.headline) .font(.headline)
} }
@@ -158,7 +158,7 @@ struct EnforceModeSetupView: View {
VStack(alignment: .leading, spacing: 8) { VStack(alignment: .leading, spacing: 8) {
Text(calibrationManager.getCalibrationSummary()) Text(calibrationManager.getCalibrationSummary())
.font(.caption) .font(.caption)
.foregroundColor(.secondary) .foregroundStyle(.secondary)
if calibrationManager.needsRecalibration() { if calibrationManager.needsRecalibration() {
Label( Label(
@@ -166,17 +166,17 @@ struct EnforceModeSetupView: View {
systemImage: "exclamationmark.triangle.fill" systemImage: "exclamationmark.triangle.fill"
) )
.font(.caption) .font(.caption)
.foregroundColor(.orange) .foregroundStyle(.orange)
} else { } else {
Label("Calibration active and valid", systemImage: "checkmark.circle.fill") Label("Calibration active and valid", systemImage: "checkmark.circle.fill")
.font(.caption) .font(.caption)
.foregroundColor(.green) .foregroundStyle(.green)
} }
} }
} else { } else {
Text("Not calibrated - using default thresholds") Text("Not calibrated - using default thresholds")
.font(.caption) .font(.caption)
.foregroundColor(.secondary) .foregroundStyle(.secondary)
} }
Button(action: { Button(action: {
@@ -258,7 +258,7 @@ struct EnforceModeSetupView: View {
/*? " Break compliance detected" : " Please look away from screen"*/ /*? " Break compliance detected" : " Please look away from screen"*/
/*)*/ /*)*/
/*.font(.caption)*/ /*.font(.caption)*/
/*.foregroundColor(lookingAway ? .green : .orange)*/ /*.foregroundStyle(lookingAway ? .green : .orange)*/
/*.frame(maxWidth: .infinity, alignment: .center)*/ /*.frame(maxWidth: .infinity, alignment: .center)*/
/*.padding(.top, 4)*/ /*.padding(.top, 4)*/
/*}*/ /*}*/
@@ -277,15 +277,15 @@ struct EnforceModeSetupView: View {
if cameraService.isCameraAuthorized { if cameraService.isCameraAuthorized {
Label("Authorized", systemImage: "checkmark.circle.fill") Label("Authorized", systemImage: "checkmark.circle.fill")
.font(.caption) .font(.caption)
.foregroundColor(.green) .foregroundStyle(.green)
} else if let error = cameraService.cameraError { } else if let error = cameraService.cameraError {
Label(error.localizedDescription, systemImage: "exclamationmark.triangle.fill") Label(error.localizedDescription, systemImage: "exclamationmark.triangle.fill")
.font(.caption) .font(.caption)
.foregroundColor(.orange) .foregroundStyle(.orange)
} else { } else {
Label("Not authorized", systemImage: "xmark.circle.fill") Label("Not authorized", systemImage: "xmark.circle.fill")
.font(.caption) .font(.caption)
.foregroundColor(.secondary) .foregroundStyle(.secondary)
} }
} }
@@ -337,14 +337,14 @@ struct EnforceModeSetupView: View {
HStack { HStack {
Image(systemName: "timer") Image(systemName: "timer")
.font(.title2) .font(.title2)
.foregroundColor(.orange) .foregroundStyle(.orange)
VStack(alignment: .leading, spacing: 4) { VStack(alignment: .leading, spacing: 4) {
Text("Camera Ready") Text("Camera Ready")
.font(.headline) .font(.headline)
Text("Will activate 3 seconds before lookaway reminder") Text("Will activate 3 seconds before lookaway reminder")
.font(.caption) .font(.caption)
.foregroundColor(.secondary) .foregroundStyle(.secondary)
} }
Spacer() Spacer()
@@ -357,11 +357,11 @@ struct EnforceModeSetupView: View {
VStack(spacing: 8) { VStack(spacing: 8) {
Image(systemName: icon) Image(systemName: icon)
.font(.title2) .font(.title2)
.foregroundColor(isActive ? .green : .secondary) .foregroundStyle(isActive ? .green : .secondary)
Text(title) Text(title)
.font(.caption) .font(.caption)
.foregroundColor(.secondary) .foregroundStyle(.secondary)
.multilineTextAlignment(.center) .multilineTextAlignment(.center)
} }
.frame(maxWidth: .infinity) .frame(maxWidth: .infinity)
@@ -372,7 +372,7 @@ struct EnforceModeSetupView: View {
HStack { HStack {
Image(systemName: "lock.shield.fill") Image(systemName: "lock.shield.fill")
.font(.title3) .font(.title3)
.foregroundColor(.blue) .foregroundStyle(.blue)
Text("Privacy Information") Text("Privacy Information")
.font(.headline) .font(.headline)
} }
@@ -381,11 +381,10 @@ struct EnforceModeSetupView: View {
privacyBullet("All processing happens on-device") privacyBullet("All processing happens on-device")
privacyBullet("No images are stored or transmitted") privacyBullet("No images are stored or transmitted")
privacyBullet("Camera only active during lookaway reminders (3 second window)") privacyBullet("Camera only active during lookaway reminders (3 second window)")
privacyBullet("Eyes closed does not affect countdown") privacyBullet("You can always force quit with cmd+q")
privacyBullet("You can disable at any time")
} }
.font(.caption) .font(.caption)
.foregroundColor(.secondary) .foregroundStyle(.secondary)
} }
.padding() .padding()
.glassEffectIfAvailable( .glassEffectIfAvailable(
@@ -396,7 +395,7 @@ struct EnforceModeSetupView: View {
HStack(alignment: .top, spacing: 8) { HStack(alignment: .top, spacing: 8) {
Image(systemName: "checkmark") Image(systemName: "checkmark")
.font(.caption2) .font(.caption2)
.foregroundColor(.blue) .foregroundStyle(.blue)
Text(text) Text(text)
} }
} }
@@ -442,7 +441,7 @@ struct EnforceModeSetupView: View {
systemName: eyeTrackingService.enableDebugLogging systemName: eyeTrackingService.enableDebugLogging
? "ant.circle.fill" : "ant.circle" ? "ant.circle.fill" : "ant.circle"
) )
.foregroundColor(eyeTrackingService.enableDebugLogging ? .orange : .secondary) .foregroundStyle(eyeTrackingService.enableDebugLogging ? .orange : .secondary)
} }
.buttonStyle(.plain) .buttonStyle(.plain)
.help("Toggle console debug logging") .help("Toggle console debug logging")
@@ -461,7 +460,7 @@ struct EnforceModeSetupView: View {
Text("Live Values:") Text("Live Values:")
.font(.caption) .font(.caption)
.fontWeight(.semibold) .fontWeight(.semibold)
.foregroundColor(.secondary) .foregroundStyle(.secondary)
if let leftRatio = eyeTrackingService.debugLeftPupilRatio, if let leftRatio = eyeTrackingService.debugLeftPupilRatio,
let rightRatio = eyeTrackingService.debugRightPupilRatio let rightRatio = eyeTrackingService.debugRightPupilRatio
@@ -470,23 +469,23 @@ struct EnforceModeSetupView: View {
VStack(alignment: .leading, spacing: 2) { VStack(alignment: .leading, spacing: 2) {
Text("Left Pupil: \(String(format: "%.3f", leftRatio))") Text("Left Pupil: \(String(format: "%.3f", leftRatio))")
.font(.caption2) .font(.caption2)
.foregroundColor( .foregroundStyle(
!EyeTrackingConstants.minPupilEnabled !EyeTrackingConstants.minPupilEnabled
&& !EyeTrackingConstants.maxPupilEnabled && !EyeTrackingConstants.maxPupilEnabled
? .secondary ? .secondary
: (leftRatio < EyeTrackingConstants.minPupilRatio : (leftRatio < EyeTrackingConstants.minPupilRatio
|| leftRatio > EyeTrackingConstants.maxPupilRatio) || leftRatio > EyeTrackingConstants.maxPupilRatio)
? .orange : .green ? Color.orange : Color.green
) )
Text("Right Pupil: \(String(format: "%.3f", rightRatio))") Text("Right Pupil: \(String(format: "%.3f", rightRatio))")
.font(.caption2) .font(.caption2)
.foregroundColor( .foregroundStyle(
!EyeTrackingConstants.minPupilEnabled !EyeTrackingConstants.minPupilEnabled
&& !EyeTrackingConstants.maxPupilEnabled && !EyeTrackingConstants.maxPupilEnabled
? .secondary ? .secondary
: (rightRatio < EyeTrackingConstants.minPupilRatio : (rightRatio < EyeTrackingConstants.minPupilRatio
|| rightRatio > EyeTrackingConstants.maxPupilRatio) || 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))" "Range: \(String(format: "%.2f", EyeTrackingConstants.minPupilRatio)) - \(String(format: "%.2f", EyeTrackingConstants.maxPupilRatio))"
) )
.font(.caption2) .font(.caption2)
.foregroundColor(.secondary) .foregroundStyle(.secondary)
let bothEyesOut = let bothEyesOut =
(leftRatio < EyeTrackingConstants.minPupilRatio (leftRatio < EyeTrackingConstants.minPupilRatio
|| leftRatio > EyeTrackingConstants.maxPupilRatio) || leftRatio > EyeTrackingConstants.maxPupilRatio)
@@ -505,13 +504,13 @@ struct EnforceModeSetupView: View {
|| rightRatio > EyeTrackingConstants.maxPupilRatio) || rightRatio > EyeTrackingConstants.maxPupilRatio)
Text(bothEyesOut ? "Both Out ⚠️" : "In Range ✓") Text(bothEyesOut ? "Both Out ⚠️" : "In Range ✓")
.font(.caption2) .font(.caption2)
.foregroundColor(bothEyesOut ? .orange : .green) .foregroundStyle(bothEyesOut ? .orange : .green)
} }
} }
} else { } else {
Text("Pupil data unavailable") Text("Pupil data unavailable")
.font(.caption2) .font(.caption2)
.foregroundColor(.secondary) .foregroundStyle(.secondary)
} }
if let yaw = eyeTrackingService.debugYaw, if let yaw = eyeTrackingService.debugYaw,
@@ -521,21 +520,21 @@ struct EnforceModeSetupView: View {
VStack(alignment: .leading, spacing: 2) { VStack(alignment: .leading, spacing: 2) {
Text("Yaw: \(String(format: "%.3f", yaw))") Text("Yaw: \(String(format: "%.3f", yaw))")
.font(.caption2) .font(.caption2)
.foregroundColor( .foregroundStyle(
!EyeTrackingConstants.yawEnabled !EyeTrackingConstants.yawEnabled
? .secondary ? .secondary
: abs(yaw) > EyeTrackingConstants.yawThreshold : abs(yaw) > EyeTrackingConstants.yawThreshold
? .orange : .green ? Color.orange : Color.green
) )
Text("Pitch: \(String(format: "%.3f", pitch))") Text("Pitch: \(String(format: "%.3f", pitch))")
.font(.caption2) .font(.caption2)
.foregroundColor( .foregroundStyle(
!EyeTrackingConstants.pitchUpEnabled !EyeTrackingConstants.pitchUpEnabled
&& !EyeTrackingConstants.pitchDownEnabled && !EyeTrackingConstants.pitchDownEnabled
? .secondary ? .secondary
: (pitch > EyeTrackingConstants.pitchUpThreshold : (pitch > EyeTrackingConstants.pitchUpThreshold
|| pitch < EyeTrackingConstants.pitchDownThreshold) || pitch < EyeTrackingConstants.pitchDownThreshold)
? .orange : .green ? Color.orange : Color.green
) )
} }
@@ -546,12 +545,12 @@ struct EnforceModeSetupView: View {
"Yaw Max: \(String(format: "%.2f", EyeTrackingConstants.yawThreshold))" "Yaw Max: \(String(format: "%.2f", EyeTrackingConstants.yawThreshold))"
) )
.font(.caption2) .font(.caption2)
.foregroundColor(.secondary) .foregroundStyle(.secondary)
Text( Text(
"Pitch: \(String(format: "%.2f", EyeTrackingConstants.pitchDownThreshold)) to \(String(format: "%.2f", EyeTrackingConstants.pitchUpThreshold))" "Pitch: \(String(format: "%.2f", EyeTrackingConstants.pitchDownThreshold)) to \(String(format: "%.2f", EyeTrackingConstants.pitchUpThreshold))"
) )
.font(.caption2) .font(.caption2)
.foregroundColor(.secondary) .foregroundStyle(.secondary)
} }
} }
} }
@@ -565,48 +564,54 @@ struct EnforceModeSetupView: View {
Text("Current Threshold Values:") Text("Current Threshold Values:")
.font(.caption) .font(.caption)
.fontWeight(.semibold) .fontWeight(.semibold)
.foregroundColor(.secondary) .foregroundStyle(.secondary)
HStack { HStack {
Text("Yaw Threshold:") Text("Yaw Threshold:")
Spacer() Spacer()
Text("\(String(format: "%.2f", EyeTrackingConstants.yawThreshold)) rad") Text("\(String(format: "%.2f", EyeTrackingConstants.yawThreshold)) rad")
.foregroundColor(.secondary) .foregroundStyle(.secondary)
} }
HStack { HStack {
Text("Pitch Up Threshold:") Text("Pitch Up Threshold:")
Spacer() Spacer()
Text("\(String(format: "%.2f", EyeTrackingConstants.pitchUpThreshold)) rad") Text(
.foregroundColor(.secondary) "\(String(format: "%.2f", EyeTrackingConstants.pitchUpThreshold)) rad"
)
.foregroundStyle(.secondary)
} }
HStack { HStack {
Text("Pitch Down Threshold:") Text("Pitch Down Threshold:")
Spacer() Spacer()
Text("\(String(format: "%.2f", EyeTrackingConstants.pitchDownThreshold)) rad") Text(
.foregroundColor(.secondary) "\(String(format: "%.2f", EyeTrackingConstants.pitchDownThreshold)) rad"
)
.foregroundStyle(.secondary)
} }
HStack { HStack {
Text("Min Pupil Ratio:") Text("Min Pupil Ratio:")
Spacer() Spacer()
Text("\(String(format: "%.2f", EyeTrackingConstants.minPupilRatio))") Text("\(String(format: "%.2f", EyeTrackingConstants.minPupilRatio))")
.foregroundColor(.secondary) .foregroundStyle(.secondary)
} }
HStack { HStack {
Text("Max Pupil Ratio:") Text("Max Pupil Ratio:")
Spacer() Spacer()
Text("\(String(format: "%.2f", EyeTrackingConstants.maxPupilRatio))") Text("\(String(format: "%.2f", EyeTrackingConstants.maxPupilRatio))")
.foregroundColor(.secondary) .foregroundStyle(.secondary)
} }
HStack { HStack {
Text("Eye Closed Threshold:") Text("Eye Closed Threshold:")
Spacer() Spacer()
Text("\(String(format: "%.3f", EyeTrackingConstants.eyeClosedThreshold))") Text(
.foregroundColor(.secondary) "\(String(format: "%.3f", EyeTrackingConstants.eyeClosedThreshold))"
)
.foregroundStyle(.secondary)
} }
} }
.padding(.top, 8) .padding(.top, 8)
@@ -622,7 +627,7 @@ struct EnforceModeSetupView: View {
VStack(alignment: .leading, spacing: 12) { VStack(alignment: .leading, spacing: 12) {
Text("Debug Eye Tracking Data") Text("Debug Eye Tracking Data")
.font(.headline) .font(.headline)
.foregroundColor(.blue) .foregroundStyle(.blue)
VStack(alignment: .leading, spacing: 8) { VStack(alignment: .leading, spacing: 8) {
Text("Face Detected: \(eyeTrackingService.faceDetected ? "Yes" : "No")") Text("Face Detected: \(eyeTrackingService.faceDetected ? "Yes" : "No")")
@@ -643,7 +648,7 @@ struct EnforceModeSetupView: View {
} }
} }
.font(.caption) .font(.caption)
.foregroundColor(.secondary) .foregroundStyle(.secondary)
} }
.padding() .padding()
.glassEffectIfAvailable(GlassStyle.regular, in: .rect(cornerRadius: 12)) .glassEffectIfAvailable(GlassStyle.regular, in: .rect(cornerRadius: 12))

View File

@@ -14,26 +14,28 @@ struct GeneralSetupView: View {
var body: some View { var body: some View {
VStack(spacing: 0) { 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() Spacer()
VStack(spacing: 30) { VStack(spacing: 30) {
Text("Configure app preferences and support the project") Text("Configure app preferences and support the project")
.font(.title3) .font(.title3)
.foregroundColor(.secondary) .foregroundStyle(.secondary)
.multilineTextAlignment(.center) .multilineTextAlignment(.center)
VStack(spacing: 20) { VStack(spacing: 20) {
launchAtLoginToggle launchAtLoginToggle
#if !APPSTORE #if !APPSTORE
softwareUpdatesSection softwareUpdatesSection
#endif #endif
subtleReminderSizeSection subtleReminderSizeSection
#if !APPSTORE #if !APPSTORE
supportSection supportSection
#endif #endif
} }
} }
@@ -51,7 +53,7 @@ struct GeneralSetupView: View {
.font(.headline) .font(.headline)
Text("Start Gaze automatically when you log in") Text("Start Gaze automatically when you log in")
.font(.caption) .font(.caption)
.foregroundColor(.secondary) .foregroundStyle(.secondary)
} }
Spacer() Spacer()
Toggle("", isOn: $settingsManager.settings.launchAtLogin) Toggle("", isOn: $settingsManager.settings.launchAtLogin)
@@ -65,42 +67,45 @@ struct GeneralSetupView: View {
} }
#if !APPSTORE #if !APPSTORE
private var softwareUpdatesSection: some View { private var softwareUpdatesSection: some View {
HStack { HStack {
VStack(alignment: .leading, spacing: 4) { VStack(alignment: .leading, spacing: 4) {
Text("Software Updates") Text("Software Updates")
.font(.headline) .font(.headline)
if let lastCheck = updateManager.lastUpdateCheckDate { if let lastCheck = updateManager.lastUpdateCheckDate {
Text("Last checked: \(lastCheck, style: .relative)") Text("Last checked: \(lastCheck, style: .relative)")
.font(.caption) .font(.caption)
.foregroundColor(.secondary) .foregroundStyle(.secondary)
.italic() .italic()
} else { } else {
Text("Never checked for updates") Text("Never checked for updates")
.font(.caption) .font(.caption)
.foregroundColor(.secondary) .foregroundStyle(.secondary)
.italic() .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")
} }
.padding()
Spacer() .glassEffectIfAvailable(GlassStyle.regular, in: .rect(cornerRadius: 12))
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))
}
#endif #endif
private var subtleReminderSizeSection: some View { private var subtleReminderSizeSection: some View {
@@ -110,20 +115,28 @@ struct GeneralSetupView: View {
Text("Adjust the size of blink and posture reminders") Text("Adjust the size of blink and posture reminders")
.font(.caption) .font(.caption)
.foregroundColor(.secondary) .foregroundStyle(.secondary)
HStack(spacing: 12) { HStack(spacing: 12) {
ForEach(ReminderSize.allCases, id: \.self) { size in ForEach(ReminderSize.allCases, id: \.self) { size in
Button(action: { settingsManager.settings.subtleReminderSize = size }) { Button(action: { settingsManager.settings.subtleReminderSize = size }) {
VStack(spacing: 8) { VStack(spacing: 8) {
Circle() 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)) .frame(width: iconSize(for: size), height: iconSize(for: size))
Text(size.displayName) Text(size.displayName)
.font(.caption) .font(.caption)
.fontWeight(settingsManager.settings.subtleReminderSize == size ? .semibold : .regular) .fontWeight(
.foregroundColor(settingsManager.settings.subtleReminderSize == size ? .primary : .secondary) settingsManager.settings.subtleReminderSize == size
? .semibold : .regular
)
.foregroundStyle(
settingsManager.settings.subtleReminderSize == size
? .primary : .secondary)
} }
.frame(maxWidth: .infinity, minHeight: 60) .frame(maxWidth: .infinity, minHeight: 60)
.padding(.vertical, 12) .padding(.vertical, 12)
@@ -142,31 +155,31 @@ struct GeneralSetupView: View {
} }
#if !APPSTORE #if !APPSTORE
private var supportSection: some View { private var supportSection: some View {
VStack(spacing: 12) { VStack(spacing: 12) {
Text("Support & Contribute") Text("Support & Contribute")
.font(.headline) .font(.headline)
.frame(maxWidth: .infinity, alignment: .leading) .frame(maxWidth: .infinity, alignment: .leading)
ExternalLinkButton( ExternalLinkButton(
icon: "chevron.left.forwardslash.chevron.right", icon: "chevron.left.forwardslash.chevron.right",
title: "View on GitHub", title: "View on GitHub",
subtitle: "Star the repo, report issues, contribute", subtitle: "Star the repo, report issues, contribute",
url: "https://github.com/mikefreno/Gaze", url: "https://github.com/mikefreno/Gaze",
tint: nil tint: nil
) )
ExternalLinkButton( ExternalLinkButton(
icon: "cup.and.saucer.fill", icon: "cup.and.saucer.fill",
iconColor: .brown, iconColor: .brown,
title: "Buy Me a Coffee", title: "Buy Me a Coffee",
subtitle: "Support development of Gaze", subtitle: "Support development of Gaze",
url: "https://buymeacoffee.com/mikefreno", url: "https://buymeacoffee.com/mikefreno",
tint: .orange tint: .orange
) )
}
.padding()
} }
.padding()
}
#endif #endif
private func applyLaunchAtLoginSetting(enabled: Bool) { private func applyLaunchAtLoginSetting(enabled: Bool) {
@@ -205,14 +218,14 @@ struct ExternalLinkButton: View {
HStack { HStack {
Image(systemName: icon) Image(systemName: icon)
.font(.title3) .font(.title3)
.foregroundColor(iconColor) .foregroundStyle(iconColor)
VStack(alignment: .leading, spacing: 2) { VStack(alignment: .leading, spacing: 2) {
Text(title) Text(title)
.font(.subheadline) .font(.subheadline)
.fontWeight(.semibold) .fontWeight(.semibold)
Text(subtitle) Text(subtitle)
.font(.caption) .font(.caption)
.foregroundColor(.secondary) .foregroundStyle(.secondary)
} }
Spacer() Spacer()
Image(systemName: "arrow.up.right") Image(systemName: "arrow.up.right")
@@ -224,7 +237,8 @@ struct ExternalLinkButton: View {
} }
.buttonStyle(.plain) .buttonStyle(.plain)
.glassEffectIfAvailable( .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) in: .rect(cornerRadius: 10)
) )
} }

View File

@@ -21,21 +21,27 @@ struct PostureSetupView: View {
VStack(spacing: 30) { VStack(spacing: 30) {
HStack(spacing: 12) { HStack(spacing: 12) {
Button(action: { 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) NSWorkspace.shared.open(url)
} }
}) { }) {
Image(systemName: "info.circle") Image(systemName: "info.circle")
.foregroundColor(.white) .foregroundStyle(.white)
} }
.buttonStyle(.plain) .buttonStyle(.plain)
Text("Regular posture checks help prevent back and neck pain from prolonged sitting") Text(
.font(.headline) "Regular posture checks help prevent back and neck pain from prolonged sitting"
.foregroundColor(.white) )
.font(.headline)
.foregroundStyle(.white)
} }
.padding() .padding()
.glassEffectIfAvailable(GlassStyle.regular.tint(.accentColor), in: .rect(cornerRadius: 8)) .glassEffectIfAvailable(
GlassStyle.regular.tint(.accentColor), in: .rect(cornerRadius: 8))
SliderSection( SliderSection(
intervalSettings: Binding( intervalSettings: Binding(
@@ -46,7 +52,8 @@ struct PostureSetupView: View {
) )
}, },
set: { newValue in set: { newValue in
settingsManager.settings.postureTimer.intervalSeconds = (newValue.val ?? 30) * 60 settingsManager.settings.postureTimer.intervalSeconds =
(newValue.val ?? 30) * 60
} }
), ),
countdownSettings: nil, countdownSettings: nil,
@@ -67,7 +74,9 @@ struct PostureSetupView: View {
guard let screen = NSScreen.main else { return } guard let screen = NSScreen.main else { return }
previewWindowController = PreviewWindowHelper.showPreview( previewWindowController = PreviewWindowHelper.showPreview(
on: screen, 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() previewWindowController?.window?.close()
} }
) )

View File

@@ -17,7 +17,7 @@ struct SmartModeSetupView: View {
Text("Automatically manage timers based on your activity") Text("Automatically manage timers based on your activity")
.font(.subheadline) .font(.subheadline)
.foregroundColor(.secondary) .foregroundStyle(.secondary)
.padding(.bottom, 30) .padding(.bottom, 30)
Spacer() Spacer()
@@ -42,18 +42,21 @@ struct SmartModeSetupView: View {
VStack(alignment: .leading, spacing: 4) { VStack(alignment: .leading, spacing: 4) {
HStack { HStack {
Image(systemName: "arrow.up.left.and.arrow.down.right") Image(systemName: "arrow.up.left.and.arrow.down.right")
.foregroundColor(.blue) .foregroundStyle(.blue)
Text("Auto-pause on Fullscreen") Text("Auto-pause on Fullscreen")
.font(.headline) .font(.headline)
} }
Text("Timers will automatically pause when you enter fullscreen mode (videos, games, presentations)") Text(
.font(.caption) "Timers will automatically pause when you enter fullscreen mode (videos, games, presentations)"
.foregroundColor(.secondary) )
.font(.caption)
.foregroundStyle(.secondary)
} }
Spacer() Spacer()
Toggle("", isOn: $settingsManager.settings.smartMode.autoPauseOnFullscreen) Toggle("", isOn: $settingsManager.settings.smartMode.autoPauseOnFullscreen)
.labelsHidden() .labelsHidden()
.onChange(of: settingsManager.settings.smartMode.autoPauseOnFullscreen) { _, newValue in .onChange(of: settingsManager.settings.smartMode.autoPauseOnFullscreen) {
_, newValue in
if newValue { if newValue {
permissionManager.requestAuthorizationIfNeeded() permissionManager.requestAuthorizationIfNeeded()
} }
@@ -61,7 +64,8 @@ struct SmartModeSetupView: View {
} }
if settingsManager.settings.smartMode.autoPauseOnFullscreen, if settingsManager.settings.smartMode.autoPauseOnFullscreen,
permissionManager.authorizationStatus != .authorized { permissionManager.authorizationStatus != .authorized
{
permissionWarningView permissionWarningView
} }
} }
@@ -81,7 +85,7 @@ struct SmartModeSetupView: View {
Text("macOS requires Screen Recording permission to detect other apps in fullscreen.") Text("macOS requires Screen Recording permission to detect other apps in fullscreen.")
.font(.caption) .font(.caption)
.foregroundColor(.secondary) .foregroundStyle(.secondary)
HStack { HStack {
Button("Grant Access") { Button("Grant Access") {
@@ -107,13 +111,13 @@ struct SmartModeSetupView: View {
VStack(alignment: .leading, spacing: 4) { VStack(alignment: .leading, spacing: 4) {
HStack { HStack {
Image(systemName: "moon.zzz.fill") Image(systemName: "moon.zzz.fill")
.foregroundColor(.indigo) .foregroundStyle(.indigo)
Text("Auto-pause on Idle") Text("Auto-pause on Idle")
.font(.headline) .font(.headline)
} }
Text("Timers will pause when you're inactive for more than the threshold below") Text("Timers will pause when you're inactive for more than the threshold below")
.font(.caption) .font(.caption)
.foregroundColor(.secondary) .foregroundStyle(.secondary)
} }
Spacer() Spacer()
Toggle("", isOn: $settingsManager.settings.smartMode.autoPauseOnIdle) Toggle("", isOn: $settingsManager.settings.smartMode.autoPauseOnIdle)
@@ -139,13 +143,15 @@ struct SmartModeSetupView: View {
VStack(alignment: .leading, spacing: 4) { VStack(alignment: .leading, spacing: 4) {
HStack { HStack {
Image(systemName: "chart.line.uptrend.xyaxis") Image(systemName: "chart.line.uptrend.xyaxis")
.foregroundColor(.green) .foregroundStyle(.green)
Text("Track Usage Statistics") Text("Track Usage Statistics")
.font(.headline) .font(.headline)
} }
Text("Monitor active and idle time, with automatic reset after the specified duration") Text(
.font(.caption) "Monitor active and idle time, with automatic reset after the specified duration"
.foregroundColor(.secondary) )
.font(.caption)
.foregroundStyle(.secondary)
} }
Spacer() Spacer()
Toggle("", isOn: $settingsManager.settings.smartMode.trackUsage) Toggle("", isOn: $settingsManager.settings.smartMode.trackUsage)
@@ -182,7 +188,7 @@ struct ThresholdSlider: View {
Spacer() Spacer()
Text("\(value) \(unit)") Text("\(value) \(unit)")
.font(.subheadline) .font(.subheadline)
.foregroundColor(.secondary) .foregroundStyle(.secondary)
} }
Slider( Slider(

View File

@@ -17,7 +17,7 @@ struct UserTimersView: View {
VStack(spacing: 16) { VStack(spacing: 16) {
Image(systemName: "clock.badge.checkmark") Image(systemName: "clock.badge.checkmark")
.font(.system(size: 60)) .font(.system(size: 60))
.foregroundColor(.purple) .foregroundStyle(.purple)
Text("Custom Timers") Text("Custom Timers")
.font(.system(size: 28, weight: .bold)) .font(.system(size: 28, weight: .bold))
} }
@@ -28,14 +28,14 @@ struct UserTimersView: View {
VStack(spacing: 30) { VStack(spacing: 30) {
Text("Create your own reminder schedules") Text("Create your own reminder schedules")
.font(.title3) .font(.title3)
.foregroundColor(.secondary) .foregroundStyle(.secondary)
HStack(spacing: 12) { HStack(spacing: 12) {
Image(systemName: "info.circle") Image(systemName: "info.circle")
.foregroundColor(.white) .foregroundStyle(.white)
Text("Add up to 3 custom timers with your own intervals and messages") Text("Add up to 3 custom timers with your own intervals and messages")
.font(.headline) .font(.headline)
.foregroundColor(.white) .foregroundStyle(.white)
} }
.padding() .padding()
.glassEffectIfAvailable( .glassEffectIfAvailable(
@@ -66,13 +66,13 @@ struct UserTimersView: View {
VStack(spacing: 12) { VStack(spacing: 12) {
Image(systemName: "clock.badge.questionmark") Image(systemName: "clock.badge.questionmark")
.font(.system(size: 40)) .font(.system(size: 40))
.foregroundColor(.secondary) .foregroundStyle(.secondary)
Text("No custom timers yet") Text("No custom timers yet")
.font(.subheadline) .font(.subheadline)
.foregroundColor(.secondary) .foregroundStyle(.secondary)
Text("Click 'Add Timer' to create your first custom reminder") Text("Click 'Add Timer' to create your first custom reminder")
.font(.caption) .font(.caption)
.foregroundColor(.secondary) .foregroundStyle(.secondary)
} }
.frame(maxWidth: .infinity) .frame(maxWidth: .infinity)
.padding(40) .padding(40)
@@ -153,7 +153,7 @@ struct UserTimerRow: View {
.frame(width: 12, height: 12) .frame(width: 12, height: 12)
Image(systemName: timer.type == .subtle ? "eye.circle" : "rectangle.on.rectangle") Image(systemName: timer.type == .subtle ? "eye.circle" : "rectangle.on.rectangle")
.foregroundColor(timer.color) .foregroundStyle(timer.color)
.frame(width: 24) .frame(width: 24)
VStack(alignment: .leading, spacing: 4) { VStack(alignment: .leading, spacing: 4) {
@@ -165,7 +165,7 @@ struct UserTimerRow: View {
"\(timer.type.displayName)\(timer.timeOnScreenSeconds)s on screen • \(timer.intervalMinutes) min interval" "\(timer.type.displayName)\(timer.timeOnScreenSeconds)s on screen • \(timer.intervalMinutes) min interval"
) )
.font(.caption) .font(.caption)
.foregroundColor(.secondary) .foregroundStyle(.secondary)
} }
Spacer() Spacer()
@@ -179,14 +179,14 @@ struct UserTimerRow: View {
Button(action: onEdit) { Button(action: onEdit) {
Image(systemName: "pencil.circle.fill") Image(systemName: "pencil.circle.fill")
.font(.title3) .font(.title3)
.foregroundColor(.accentColor) .foregroundStyle(Color.accentColor)
} }
.buttonStyle(.plain) .buttonStyle(.plain)
Button(action: { showingDeleteConfirmation = true }) { Button(action: { showingDeleteConfirmation = true }) {
Image(systemName: "trash.circle.fill") Image(systemName: "trash.circle.fill")
.font(.title3) .font(.title3)
.foregroundColor(.red) .foregroundStyle(.red)
} }
.buttonStyle(.plain) .buttonStyle(.plain)
.confirmationDialog("Delete Timer", isPresented: $showingDeleteConfirmation) { .confirmationDialog("Delete Timer", isPresented: $showingDeleteConfirmation) {
@@ -264,7 +264,7 @@ struct UserTimerEditSheet: View {
.textFieldStyle(.roundedBorder) .textFieldStyle(.roundedBorder)
Text("Example: \"Stretch Break\", \"Eye Rest\", \"Water Break\"") Text("Example: \"Stretch Break\", \"Eye Rest\", \"Water Break\"")
.font(.caption) .font(.caption)
.foregroundColor(.secondary) .foregroundStyle(.secondary)
} }
VStack(alignment: .leading, spacing: 8) { VStack(alignment: .leading, spacing: 8) {
@@ -318,7 +318,7 @@ struct UserTimerEditSheet: View {
: "Full screen reminder with animation" : "Full screen reminder with animation"
) )
.font(.caption) .font(.caption)
.foregroundColor(.secondary) .foregroundStyle(.secondary)
} }
if type == .overlay { if type == .overlay {
@@ -359,7 +359,7 @@ struct UserTimerEditSheet: View {
} }
Text("How often this reminder will appear (in minutes)") Text("How often this reminder will appear (in minutes)")
.font(.caption) .font(.caption)
.foregroundColor(.secondary) .foregroundStyle(.secondary)
} }
VStack(alignment: .leading, spacing: 8) { VStack(alignment: .leading, spacing: 8) {
@@ -369,7 +369,7 @@ struct UserTimerEditSheet: View {
.textFieldStyle(.roundedBorder) .textFieldStyle(.roundedBorder)
Text("Leave blank to show a default timer notification") Text("Leave blank to show a default timer notification")
.font(.caption) .font(.caption)
.foregroundColor(.secondary) .foregroundStyle(.secondary)
} }
} }
.padding() .padding()

View File

@@ -14,14 +14,14 @@ struct WelcomeView: View {
Image(systemName: "eye.fill") Image(systemName: "eye.fill")
.font(.system(size: 80)) .font(.system(size: 80))
.foregroundColor(.accentColor) .foregroundStyle(Color.accentColor)
Text("Welcome to Gaze") Text("Welcome to Gaze")
.font(.system(size: 36, weight: .bold)) .font(.system(size: 36, weight: .bold))
Text("Take care of your eyes and posture") Text("Take care of your eyes and posture")
.font(.title3) .font(.title3)
.foregroundColor(.secondary) .foregroundStyle(.secondary)
VStack(alignment: .leading, spacing: 16) { VStack(alignment: .leading, spacing: 16) {
FeatureRow( FeatureRow(
@@ -66,7 +66,7 @@ struct FeatureRow: View {
HStack(alignment: .top, spacing: 16) { HStack(alignment: .top, spacing: 16) {
Image(systemName: icon) Image(systemName: icon)
.font(.title2) .font(.title2)
.foregroundColor(iconColor) .foregroundStyle(iconColor)
.frame(width: 30) .frame(width: 30)
VStack(alignment: .leading, spacing: 4) { VStack(alignment: .leading, spacing: 4) {
@@ -74,7 +74,7 @@ struct FeatureRow: View {
.font(.headline) .font(.headline)
Text(description) Text(description)
.font(.subheadline) .font(.subheadline)
.foregroundColor(.secondary) .foregroundStyle(.secondary)
} }
} }
} }