general:made logging consistent
This commit is contained in:
@@ -8,7 +8,6 @@
|
|||||||
import AppKit
|
import AppKit
|
||||||
import Combine
|
import Combine
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
import os.log
|
|
||||||
|
|
||||||
@MainActor
|
@MainActor
|
||||||
class AppDelegate: NSObject, NSApplicationDelegate, ObservableObject {
|
class AppDelegate: NSObject, NSApplicationDelegate, ObservableObject {
|
||||||
@@ -21,9 +20,6 @@ class AppDelegate: NSObject, NSApplicationDelegate, ObservableObject {
|
|||||||
private var isSettingsWindowOpen = false
|
private var isSettingsWindowOpen = false
|
||||||
private var isOnboardingWindowOpen = false
|
private var isOnboardingWindowOpen = false
|
||||||
|
|
||||||
// Logging manager
|
|
||||||
private let logger = LoggingManager.shared
|
|
||||||
|
|
||||||
// Convenience accessor for settings
|
// Convenience accessor for settings
|
||||||
private var settingsManager: any SettingsProviding {
|
private var settingsManager: any SettingsProviding {
|
||||||
serviceContainer.settingsManager
|
serviceContainer.settingsManager
|
||||||
@@ -49,14 +45,10 @@ class AppDelegate: NSObject, NSApplicationDelegate, ObservableObject {
|
|||||||
// Set activation policy to hide dock icon
|
// Set activation policy to hide dock icon
|
||||||
NSApplication.shared.setActivationPolicy(.accessory)
|
NSApplication.shared.setActivationPolicy(.accessory)
|
||||||
|
|
||||||
// Initialize logging
|
logInfo("🚀 Application did finish launching")
|
||||||
logger.configureLogging()
|
|
||||||
logger.appLogger.info("🚀 Application did finish launching")
|
|
||||||
|
|
||||||
// Get timer engine from service container
|
|
||||||
timerEngine = serviceContainer.timerEngine
|
timerEngine = serviceContainer.timerEngine
|
||||||
|
|
||||||
// Setup smart mode services through container
|
|
||||||
serviceContainer.setupSmartModeServices()
|
serviceContainer.setupSmartModeServices()
|
||||||
|
|
||||||
// Check if onboarding needs to be shown automatically
|
// Check if onboarding needs to be shown automatically
|
||||||
@@ -87,7 +79,8 @@ class AppDelegate: NSObject, NSApplicationDelegate, ObservableObject {
|
|||||||
.removeDuplicates()
|
.removeDuplicates()
|
||||||
.sink { [weak self] smartMode in
|
.sink { [weak self] smartMode in
|
||||||
guard let self = self else { return }
|
guard let self = self else { return }
|
||||||
self.serviceContainer.idleService?.updateThreshold(minutes: smartMode.idleThresholdMinutes)
|
self.serviceContainer.idleService?.updateThreshold(
|
||||||
|
minutes: smartMode.idleThresholdMinutes)
|
||||||
self.serviceContainer.usageTrackingService?.updateResetThreshold(
|
self.serviceContainer.usageTrackingService?.updateResetThreshold(
|
||||||
minutes: smartMode.usageResetAfterMinutes)
|
minutes: smartMode.usageResetAfterMinutes)
|
||||||
|
|
||||||
@@ -110,7 +103,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, ObservableObject {
|
|||||||
private func startTimers() {
|
private func startTimers() {
|
||||||
guard !hasStartedTimers else { return }
|
guard !hasStartedTimers else { return }
|
||||||
hasStartedTimers = true
|
hasStartedTimers = true
|
||||||
logger.appLogger.info("Starting timers")
|
logInfo("Starting timers")
|
||||||
timerEngine?.start()
|
timerEngine?.start()
|
||||||
observeReminderEvents()
|
observeReminderEvents()
|
||||||
}
|
}
|
||||||
@@ -134,7 +127,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, ObservableObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func applicationWillTerminate(_ notification: Notification) {
|
func applicationWillTerminate(_ notification: Notification) {
|
||||||
logger.appLogger.info(" applicationWill terminate")
|
logInfo(" applicationWill terminate")
|
||||||
settingsManager.saveImmediately()
|
settingsManager.saveImmediately()
|
||||||
timerEngine?.stop()
|
timerEngine?.stop()
|
||||||
}
|
}
|
||||||
@@ -156,13 +149,13 @@ class AppDelegate: NSObject, NSApplicationDelegate, ObservableObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@objc private func systemWillSleep() {
|
@objc private func systemWillSleep() {
|
||||||
logger.systemLogger.info("System will sleep")
|
logInfo("System will sleep")
|
||||||
timerEngine?.handleSystemSleep()
|
timerEngine?.handleSystemSleep()
|
||||||
settingsManager.saveImmediately()
|
settingsManager.saveImmediately()
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc private func systemDidWake() {
|
@objc private func systemDidWake() {
|
||||||
logger.systemLogger.info("System did wake")
|
logInfo("System did wake")
|
||||||
timerEngine?.handleSystemWake()
|
timerEngine?.handleSystemWake()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -208,7 +201,8 @@ class AppDelegate: NSObject, NSApplicationDelegate, ObservableObject {
|
|||||||
windowManager.showReminderWindow(view, windowType: .overlay)
|
windowManager.showReminderWindow(view, windowType: .overlay)
|
||||||
} else {
|
} else {
|
||||||
let sizePercentage = settingsManager.settings.subtleReminderSize.percentage
|
let sizePercentage = settingsManager.settings.subtleReminderSize.percentage
|
||||||
let view = UserTimerReminderView(timer: timer, sizePercentage: sizePercentage) { [weak self] in
|
let view = UserTimerReminderView(timer: timer, sizePercentage: sizePercentage) {
|
||||||
|
[weak self] in
|
||||||
self?.timerEngine?.dismissReminder()
|
self?.timerEngine?.dismissReminder()
|
||||||
}
|
}
|
||||||
windowManager.showReminderWindow(view, windowType: .subtle)
|
windowManager.showReminderWindow(view, windowType: .subtle)
|
||||||
|
|||||||
@@ -24,7 +24,12 @@ class EyeTrackingService: NSObject, ObservableObject {
|
|||||||
@Published var debugRightPupilRatio: Double?
|
@Published var debugRightPupilRatio: Double?
|
||||||
@Published var debugYaw: Double?
|
@Published var debugYaw: Double?
|
||||||
@Published var debugPitch: Double?
|
@Published var debugPitch: Double?
|
||||||
@Published var enableDebugLogging: Bool = false
|
@Published var enableDebugLogging: Bool = false {
|
||||||
|
didSet {
|
||||||
|
// Sync with PupilDetector's diagnostic logging
|
||||||
|
PupilDetector.enableDiagnosticLogging = enableDebugLogging
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Throttle for debug logging
|
// Throttle for debug logging
|
||||||
private var lastDebugLogTime: Date = .distantPast
|
private var lastDebugLogTime: Date = .distantPast
|
||||||
@@ -228,6 +233,10 @@ class EyeTrackingService: NSObject, ObservableObject {
|
|||||||
result.faceDetected = true
|
result.faceDetected = true
|
||||||
let face = observations.first!
|
let face = observations.first!
|
||||||
|
|
||||||
|
// Always extract yaw/pitch from face, even if landmarks aren't available
|
||||||
|
result.debugYaw = face.yaw?.doubleValue ?? 0.0
|
||||||
|
result.debugPitch = face.pitch?.doubleValue ?? 0.0
|
||||||
|
|
||||||
guard let landmarks = face.landmarks else {
|
guard let landmarks = face.landmarks else {
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
@@ -660,6 +669,9 @@ extension EyeTrackingService: AVCaptureVideoDataOutputSampleBufferDelegate {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Advance frame counter for pupil detector frame skipping
|
||||||
|
PupilDetector.advanceFrame()
|
||||||
|
|
||||||
let request = VNDetectFaceLandmarksRequest { [weak self] request, error in
|
let request = VNDetectFaceLandmarksRequest { [weak self] request, error in
|
||||||
guard let self = self else { return }
|
guard let self = self else { return }
|
||||||
|
|
||||||
|
|||||||
@@ -46,15 +46,12 @@ final class LoggingManager {
|
|||||||
// MARK: - Initialization
|
// MARK: - Initialization
|
||||||
|
|
||||||
private init() {
|
private init() {
|
||||||
// Private initializer to enforce singleton pattern
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Public Methods
|
// MARK: - Public Methods
|
||||||
|
|
||||||
/// Configure the logging system for verbose output when needed
|
|
||||||
func configureLogging() {
|
func configureLogging() {
|
||||||
// For now, we'll use standard OSLog behavior.
|
//nothing needed for now
|
||||||
// This can be extended in the future to support runtime log level changes.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convenience method for debug logging
|
/// Convenience method for debug logging
|
||||||
@@ -86,8 +83,6 @@ final class LoggingManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Global Convenience Functions
|
|
||||||
|
|
||||||
/// Log an info message using the shared LoggingManager
|
/// Log an info message using the shared LoggingManager
|
||||||
public func logInfo(_ message: String, category: String = "General") {
|
public func logInfo(_ message: String, category: String = "General") {
|
||||||
LoggingManager.shared.info(message, category: category)
|
LoggingManager.shared.info(message, category: category)
|
||||||
|
|||||||
@@ -63,7 +63,9 @@ final class PupilCalibration: @unchecked Sendable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private nonisolated func findBestThreshold(eyeData: UnsafePointer<UInt8>, width: Int, height: Int) -> Int {
|
private nonisolated func findBestThreshold(
|
||||||
|
eyeData: UnsafePointer<UInt8>, width: Int, height: Int
|
||||||
|
) -> Int {
|
||||||
let averageIrisSize = 0.48
|
let averageIrisSize = 0.48
|
||||||
var bestThreshold = 50
|
var bestThreshold = 50
|
||||||
var bestDiff = Double.greatestFiniteMagnitude
|
var bestDiff = Double.greatestFiniteMagnitude
|
||||||
@@ -91,7 +93,9 @@ final class PupilCalibration: @unchecked Sendable {
|
|||||||
return bestThreshold
|
return bestThreshold
|
||||||
}
|
}
|
||||||
|
|
||||||
private nonisolated static func irisSize(data: UnsafePointer<UInt8>, width: Int, height: Int) -> Double {
|
private nonisolated static func irisSize(data: UnsafePointer<UInt8>, width: Int, height: Int)
|
||||||
|
-> Double
|
||||||
|
{
|
||||||
let margin = 5
|
let margin = 5
|
||||||
guard width > margin * 2, height > margin * 2 else { return 0 }
|
guard width > margin * 2, height > margin * 2 else { return 0 }
|
||||||
|
|
||||||
@@ -145,13 +149,15 @@ final class PupilDetector: @unchecked Sendable {
|
|||||||
|
|
||||||
nonisolated(unsafe) static var enableDebugImageSaving = false
|
nonisolated(unsafe) static var enableDebugImageSaving = false
|
||||||
nonisolated(unsafe) static var enablePerformanceLogging = false
|
nonisolated(unsafe) static var enablePerformanceLogging = false
|
||||||
|
nonisolated(unsafe) static var enableDiagnosticLogging = false
|
||||||
nonisolated(unsafe) static var frameSkipCount = 10 // Process every Nth frame
|
nonisolated(unsafe) static var frameSkipCount = 10 // Process every Nth frame
|
||||||
|
|
||||||
// MARK: - State (protected by lock)
|
// MARK: - State (protected by lock)
|
||||||
|
|
||||||
private nonisolated(unsafe) static var _debugImageCounter = 0
|
private nonisolated(unsafe) static var _debugImageCounter = 0
|
||||||
private nonisolated(unsafe) static var _frameCounter = 0
|
private nonisolated(unsafe) static var _frameCounter = 0
|
||||||
private nonisolated(unsafe) static var _lastPupilPositions: (left: PupilPosition?, right: PupilPosition?) = (
|
private nonisolated(unsafe) static var _lastPupilPositions:
|
||||||
|
(left: PupilPosition?, right: PupilPosition?) = (
|
||||||
nil, nil
|
nil, nil
|
||||||
)
|
)
|
||||||
private nonisolated(unsafe) static var _metrics = PupilDetectorMetrics()
|
private nonisolated(unsafe) static var _metrics = PupilDetectorMetrics()
|
||||||
@@ -170,7 +176,8 @@ final class PupilDetector: @unchecked Sendable {
|
|||||||
set { _frameCounter = newValue }
|
set { _frameCounter = newValue }
|
||||||
}
|
}
|
||||||
|
|
||||||
private nonisolated static var lastPupilPositions: (left: PupilPosition?, right: PupilPosition?) {
|
private nonisolated static var lastPupilPositions: (left: PupilPosition?, right: PupilPosition?)
|
||||||
|
{
|
||||||
get { _lastPupilPositions }
|
get { _lastPupilPositions }
|
||||||
set { _lastPupilPositions = newValue }
|
set { _lastPupilPositions = newValue }
|
||||||
}
|
}
|
||||||
@@ -218,6 +225,11 @@ final class PupilDetector: @unchecked Sendable {
|
|||||||
|
|
||||||
// MARK: - Public API
|
// MARK: - Public API
|
||||||
|
|
||||||
|
/// Call once per video frame to enable proper frame skipping
|
||||||
|
nonisolated static func advanceFrame() {
|
||||||
|
frameCounter += 1
|
||||||
|
}
|
||||||
|
|
||||||
/// Detects pupil position with frame skipping for performance
|
/// Detects pupil position with frame skipping for performance
|
||||||
/// Returns cached result on skipped frames
|
/// Returns cached result on skipped frames
|
||||||
nonisolated static func detectPupil(
|
nonisolated static func detectPupil(
|
||||||
@@ -252,7 +264,7 @@ final class PupilDetector: @unchecked Sendable {
|
|||||||
let elapsed = (CFAbsoluteTimeGetCurrent() - startTime) * 1000
|
let elapsed = (CFAbsoluteTimeGetCurrent() - startTime) * 1000
|
||||||
metrics.recordProcessingTime(elapsed)
|
metrics.recordProcessingTime(elapsed)
|
||||||
if metrics.processedFrameCount % 30 == 0 {
|
if metrics.processedFrameCount % 30 == 0 {
|
||||||
print(
|
logDebug(
|
||||||
"👁 PupilDetector: \(String(format: "%.2f", elapsed))ms (avg: \(String(format: "%.2f", metrics.averageProcessingTimeMs))ms)"
|
"👁 PupilDetector: \(String(format: "%.2f", elapsed))ms (avg: \(String(format: "%.2f", metrics.averageProcessingTimeMs))ms)"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -266,10 +278,18 @@ final class PupilDetector: @unchecked Sendable {
|
|||||||
imageSize: imageSize
|
imageSize: imageSize
|
||||||
)
|
)
|
||||||
|
|
||||||
guard eyePoints.count >= 6 else { return nil }
|
guard eyePoints.count >= 6 else {
|
||||||
|
if enableDiagnosticLogging {
|
||||||
|
logDebug("👁 PupilDetector: Failed - eyePoints.count=\(eyePoints.count) < 6")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// Step 2: Create eye region bounding box with margin
|
// Step 2: Create eye region bounding box with margin
|
||||||
guard let eyeRegion = createEyeRegion(from: eyePoints, imageSize: imageSize) else {
|
guard let eyeRegion = createEyeRegion(from: eyePoints, imageSize: imageSize) else {
|
||||||
|
if enableDiagnosticLogging {
|
||||||
|
logDebug("👁 PupilDetector: Failed - createEyeRegion returned nil")
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -285,6 +305,9 @@ final class PupilDetector: @unchecked Sendable {
|
|||||||
let eyeBuf = eyeBuffer,
|
let eyeBuf = eyeBuffer,
|
||||||
let tmpBuf = tempBuffer
|
let tmpBuf = tempBuffer
|
||||||
else {
|
else {
|
||||||
|
if enableDiagnosticLogging {
|
||||||
|
logDebug("👁 PupilDetector: Failed - buffers not allocated")
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -293,6 +316,9 @@ final class PupilDetector: @unchecked Sendable {
|
|||||||
extractGrayscaleDataOptimized(
|
extractGrayscaleDataOptimized(
|
||||||
from: pixelBuffer, to: grayBuffer, width: frameWidth, height: frameHeight)
|
from: pixelBuffer, to: grayBuffer, width: frameWidth, height: frameHeight)
|
||||||
else {
|
else {
|
||||||
|
if enableDiagnosticLogging {
|
||||||
|
logDebug("👁 PupilDetector: Failed - grayscale extraction failed")
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -301,7 +327,13 @@ final class PupilDetector: @unchecked Sendable {
|
|||||||
let eyeHeight = Int(eyeRegion.frame.height)
|
let eyeHeight = Int(eyeRegion.frame.height)
|
||||||
|
|
||||||
// Early exit for tiny regions (less than 10x10 pixels)
|
// Early exit for tiny regions (less than 10x10 pixels)
|
||||||
guard eyeWidth >= 10, eyeHeight >= 10 else { return nil }
|
guard eyeWidth >= 10, eyeHeight >= 10 else {
|
||||||
|
if enableDiagnosticLogging {
|
||||||
|
logDebug(
|
||||||
|
"👁 PupilDetector: Failed - eye region too small (\(eyeWidth)x\(eyeHeight))")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
guard
|
guard
|
||||||
isolateEyeWithMaskOptimized(
|
isolateEyeWithMaskOptimized(
|
||||||
@@ -313,6 +345,9 @@ final class PupilDetector: @unchecked Sendable {
|
|||||||
output: eyeBuf
|
output: eyeBuf
|
||||||
)
|
)
|
||||||
else {
|
else {
|
||||||
|
if enableDiagnosticLogging {
|
||||||
|
logDebug("👁 PupilDetector: Failed - isolateEyeWithMask failed")
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -352,9 +387,20 @@ final class PupilDetector: @unchecked Sendable {
|
|||||||
height: eyeHeight
|
height: eyeHeight
|
||||||
)
|
)
|
||||||
else {
|
else {
|
||||||
|
if enableDiagnosticLogging {
|
||||||
|
logDebug(
|
||||||
|
"👁 PupilDetector: Failed - findPupilFromContours returned nil (not enough dark pixels)"
|
||||||
|
)
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if enableDiagnosticLogging {
|
||||||
|
logDebug(
|
||||||
|
"👁 PupilDetector: Success - centroid at (\(String(format: "%.1f", centroidX)), \(String(format: "%.1f", centroidY))) in \(eyeWidth)x\(eyeHeight) region"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
let pupilPosition = PupilPosition(x: CGFloat(centroidX), y: CGFloat(centroidY))
|
let pupilPosition = PupilPosition(x: CGFloat(centroidX), y: CGFloat(centroidY))
|
||||||
|
|
||||||
// Cache result
|
// Cache result
|
||||||
@@ -738,7 +784,9 @@ final class PupilDetector: @unchecked Sendable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private nonisolated static func createEyeRegion(from points: [CGPoint], imageSize: CGSize) -> EyeRegion? {
|
private nonisolated static func createEyeRegion(from points: [CGPoint], imageSize: CGSize)
|
||||||
|
-> EyeRegion?
|
||||||
|
{
|
||||||
guard !points.isEmpty else { return nil }
|
guard !points.isEmpty else { return nil }
|
||||||
|
|
||||||
let margin: CGFloat = 5
|
let margin: CGFloat = 5
|
||||||
@@ -792,10 +840,12 @@ final class PupilDetector: @unchecked Sendable {
|
|||||||
|
|
||||||
CGImageDestinationAddImage(destination, cgImage, nil)
|
CGImageDestinationAddImage(destination, cgImage, nil)
|
||||||
CGImageDestinationFinalize(destination)
|
CGImageDestinationFinalize(destination)
|
||||||
print("💾 Saved debug image: \(url.path)")
|
logDebug("💾 Saved debug image: \(url.path)")
|
||||||
}
|
}
|
||||||
|
|
||||||
private nonisolated static func createCGImage(from data: UnsafePointer<UInt8>, width: Int, height: Int)
|
private nonisolated static func createCGImage(
|
||||||
|
from data: UnsafePointer<UInt8>, width: Int, height: Int
|
||||||
|
)
|
||||||
-> CGImage?
|
-> CGImage?
|
||||||
{
|
{
|
||||||
let mutableData = UnsafeMutablePointer<UInt8>.allocate(capacity: width * height)
|
let mutableData = UnsafeMutablePointer<UInt8>.allocate(capacity: width * height)
|
||||||
|
|||||||
@@ -7,7 +7,6 @@
|
|||||||
|
|
||||||
import Combine
|
import Combine
|
||||||
import Foundation
|
import Foundation
|
||||||
import os.log
|
|
||||||
|
|
||||||
@MainActor
|
@MainActor
|
||||||
class TimerEngine: ObservableObject {
|
class TimerEngine: ObservableObject {
|
||||||
@@ -29,9 +28,6 @@ class TimerEngine: ObservableObject {
|
|||||||
private var idleService: IdleMonitoringService?
|
private var idleService: IdleMonitoringService?
|
||||||
private var cancellables = Set<AnyCancellable>()
|
private var cancellables = Set<AnyCancellable>()
|
||||||
|
|
||||||
// Logging manager
|
|
||||||
private let logger = LoggingManager.shared.timerLogger
|
|
||||||
|
|
||||||
convenience init(
|
convenience init(
|
||||||
settingsManager: any SettingsProviding,
|
settingsManager: any SettingsProviding,
|
||||||
enforceModeService: EnforceModeService? = nil
|
enforceModeService: EnforceModeService? = nil
|
||||||
@@ -88,10 +84,10 @@ class TimerEngine: ObservableObject {
|
|||||||
|
|
||||||
if isFullscreen {
|
if isFullscreen {
|
||||||
pauseAllTimers(reason: .fullscreen)
|
pauseAllTimers(reason: .fullscreen)
|
||||||
logger.info("⏸️ Timers paused: fullscreen detected")
|
logInfo("⏸️ Timers paused: fullscreen detected")
|
||||||
} else {
|
} else {
|
||||||
resumeAllTimers(reason: .fullscreen)
|
resumeAllTimers(reason: .fullscreen)
|
||||||
logger.info("▶️ Timers resumed: fullscreen exited")
|
logInfo("▶️ Timers resumed: fullscreen exited")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -100,10 +96,10 @@ class TimerEngine: ObservableObject {
|
|||||||
|
|
||||||
if isIdle {
|
if isIdle {
|
||||||
pauseAllTimers(reason: .idle)
|
pauseAllTimers(reason: .idle)
|
||||||
logger.info("⏸️ Timers paused: user idle")
|
logInfo("⏸️ Timers paused: user idle")
|
||||||
} else {
|
} else {
|
||||||
resumeAllTimers(reason: .idle)
|
resumeAllTimers(reason: .idle)
|
||||||
logger.info("▶️ Timers resumed: user active")
|
logInfo("▶️ Timers resumed: user active")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -178,7 +174,7 @@ class TimerEngine: ObservableObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private func updateConfigurations() {
|
private func updateConfigurations() {
|
||||||
logger.debug("Updating timer configurations")
|
logDebug("Updating timer configurations")
|
||||||
var newStates: [TimerIdentifier: TimerState] = [:]
|
var newStates: [TimerIdentifier: TimerState] = [:]
|
||||||
|
|
||||||
// Update built-in timers
|
// Update built-in timers
|
||||||
@@ -191,7 +187,7 @@ class TimerEngine: ObservableObject {
|
|||||||
// Timer exists - check if interval changed
|
// Timer exists - check if interval changed
|
||||||
if existingState.originalIntervalSeconds != config.intervalSeconds {
|
if existingState.originalIntervalSeconds != config.intervalSeconds {
|
||||||
// Interval changed - reset with new interval
|
// Interval changed - reset with new interval
|
||||||
logger.debug("Timer interval changed")
|
logDebug("Timer interval changed")
|
||||||
newStates[identifier] = TimerState(
|
newStates[identifier] = TimerState(
|
||||||
identifier: identifier,
|
identifier: identifier,
|
||||||
intervalSeconds: config.intervalSeconds,
|
intervalSeconds: config.intervalSeconds,
|
||||||
@@ -204,7 +200,7 @@ class TimerEngine: ObservableObject {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Timer was just enabled - create new state
|
// Timer was just enabled - create new state
|
||||||
logger.debug("Timer enabled")
|
logDebug("Timer enabled")
|
||||||
newStates[identifier] = TimerState(
|
newStates[identifier] = TimerState(
|
||||||
identifier: identifier,
|
identifier: identifier,
|
||||||
intervalSeconds: config.intervalSeconds,
|
intervalSeconds: config.intervalSeconds,
|
||||||
@@ -226,7 +222,7 @@ class TimerEngine: ObservableObject {
|
|||||||
// Check if interval changed
|
// Check if interval changed
|
||||||
if existingState.originalIntervalSeconds != newIntervalSeconds {
|
if existingState.originalIntervalSeconds != newIntervalSeconds {
|
||||||
// Interval changed - reset with new interval
|
// Interval changed - reset with new interval
|
||||||
logger.debug("User timer interval changed")
|
logDebug("User timer interval changed")
|
||||||
newStates[identifier] = TimerState(
|
newStates[identifier] = TimerState(
|
||||||
identifier: identifier,
|
identifier: identifier,
|
||||||
intervalSeconds: newIntervalSeconds,
|
intervalSeconds: newIntervalSeconds,
|
||||||
@@ -239,7 +235,7 @@ class TimerEngine: ObservableObject {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// New timer - create state
|
// New timer - create state
|
||||||
logger.debug("User timer created")
|
logDebug("User timer created")
|
||||||
newStates[identifier] = TimerState(
|
newStates[identifier] = TimerState(
|
||||||
identifier: identifier,
|
identifier: identifier,
|
||||||
intervalSeconds: newIntervalSeconds,
|
intervalSeconds: newIntervalSeconds,
|
||||||
@@ -300,7 +296,8 @@ class TimerEngine: ObservableObject {
|
|||||||
let config = settingsProvider.timerConfiguration(for: type)
|
let config = settingsProvider.timerConfiguration(for: type)
|
||||||
intervalSeconds = config.intervalSeconds
|
intervalSeconds = config.intervalSeconds
|
||||||
case .user(let id):
|
case .user(let id):
|
||||||
guard let userTimer = settingsProvider.settings.userTimers.first(where: { $0.id == id }) else { return }
|
guard let userTimer = settingsProvider.settings.userTimers.first(where: { $0.id == id })
|
||||||
|
else { return }
|
||||||
intervalSeconds = userTimer.intervalMinutes * 60
|
intervalSeconds = userTimer.intervalMinutes * 60
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -340,7 +337,8 @@ class TimerEngine: ObservableObject {
|
|||||||
if case .builtIn(.lookAway) = identifier {
|
if case .builtIn(.lookAway) = identifier {
|
||||||
if enforceModeService?.shouldEnforceBreak(for: identifier) == true {
|
if enforceModeService?.shouldEnforceBreak(for: identifier) == true {
|
||||||
Task { @MainActor in
|
Task { @MainActor in
|
||||||
await enforceModeService?.startCameraForLookawayTimer(secondsRemaining: updatedState.remainingSeconds)
|
await enforceModeService?.startCameraForLookawayTimer(
|
||||||
|
secondsRemaining: updatedState.remainingSeconds)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -393,7 +391,7 @@ class TimerEngine: ObservableObject {
|
|||||||
/// - Saves current time for elapsed calculation
|
/// - Saves current time for elapsed calculation
|
||||||
/// - Pauses all active timers
|
/// - Pauses all active timers
|
||||||
func handleSystemSleep() {
|
func handleSystemSleep() {
|
||||||
logger.debug("System going to sleep")
|
logDebug("System going to sleep")
|
||||||
sleepStartTime = timeProvider.now()
|
sleepStartTime = timeProvider.now()
|
||||||
for (id, var state) in timerStates {
|
for (id, var state) in timerStates {
|
||||||
state.pauseReasons.insert(.system)
|
state.pauseReasons.insert(.system)
|
||||||
@@ -408,7 +406,7 @@ class TimerEngine: ObservableObject {
|
|||||||
/// - Timers that expired during sleep will trigger immediately (1s delay)
|
/// - Timers that expired during sleep will trigger immediately (1s delay)
|
||||||
/// - Resumes all timers
|
/// - Resumes all timers
|
||||||
func handleSystemWake() {
|
func handleSystemWake() {
|
||||||
logger.debug("System waking up")
|
logDebug("System waking up")
|
||||||
guard let sleepStart = sleepStartTime else {
|
guard let sleepStart = sleepStartTime else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -442,3 +440,4 @@ class TimerEngine: ObservableObject {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -201,7 +201,6 @@ struct MenuBarContentView: View {
|
|||||||
.padding(.vertical, 6)
|
.padding(.vertical, 6)
|
||||||
}
|
}
|
||||||
.buttonStyle(MenuBarHoverButtonStyle())
|
.buttonStyle(MenuBarHoverButtonStyle())
|
||||||
.padding(.horizontal, 8)
|
|
||||||
.padding(.vertical, 8)
|
.padding(.vertical, 8)
|
||||||
Spacer()
|
Spacer()
|
||||||
Text(
|
Text(
|
||||||
|
|||||||
Reference in New Issue
Block a user