- watchOS: add WatchSessionManager (WCSession bridge), WatchNotificationHandler (snooze/dismiss actions), recommendations() for AppIntentTimelineProvider - watchOS: WatchTimerStore tick loop with cascade haptics, App Group sync - macOS: CreateTimerSheet (countdown/alarm/pomodoro), launch-at-login toggle - macOS: MenuBarState showCreateSheet, MacSettingsView data tab - Xcode project updated with new file references
122 lines
3.7 KiB
Swift
122 lines
3.7 KiB
Swift
// ── macOS Settings View ───────────────────────────────────────
|
|
|
|
import SwiftUI
|
|
import ServiceManagement
|
|
import os
|
|
|
|
struct MacSettingsView: View {
|
|
@EnvironmentObject var store: MacTimerStore
|
|
@AppStorage("cm_defaultUrgency") private var defaultUrgency = "standard"
|
|
@AppStorage("cm_defaultCascade") private var defaultCascade = "standard"
|
|
@AppStorage("cm_launchAtLogin") private var launchAtLogin = false
|
|
|
|
private let logger = Logger(subsystem: "com.chronomind.mac", category: "Settings")
|
|
|
|
var body: some View {
|
|
TabView {
|
|
generalTab
|
|
.tabItem { Label("General", systemImage: "gearshape") }
|
|
|
|
dataTab
|
|
.tabItem { Label("Data", systemImage: "externaldrive") }
|
|
|
|
aboutTab
|
|
.tabItem { Label("About", systemImage: "info.circle") }
|
|
}
|
|
.frame(width: 400, height: 300)
|
|
}
|
|
|
|
// MARK: - General
|
|
|
|
private var generalTab: some View {
|
|
Form {
|
|
Toggle("Launch at Login", isOn: Binding(
|
|
get: { launchAtLogin },
|
|
set: { newValue in
|
|
launchAtLogin = newValue
|
|
do {
|
|
if newValue {
|
|
try SMAppService.mainApp.register()
|
|
logger.info("Registered for launch at login")
|
|
} else {
|
|
try SMAppService.mainApp.unregister()
|
|
logger.info("Unregistered from launch at login")
|
|
}
|
|
} catch {
|
|
logger.error("Launch at login toggle failed: \(error.localizedDescription)")
|
|
launchAtLogin = !newValue
|
|
}
|
|
}
|
|
))
|
|
|
|
Picker("Default Urgency", selection: $defaultUrgency) {
|
|
ForEach(UrgencyLevel.allCases) { level in
|
|
Text(getUrgencyConfig(level).label).tag(level.rawValue)
|
|
}
|
|
}
|
|
|
|
Picker("Default Cascade", selection: $defaultCascade) {
|
|
ForEach(CascadePreset.allCases.filter { $0 != .custom }) { preset in
|
|
Text(preset.label).tag(preset.rawValue)
|
|
}
|
|
}
|
|
}
|
|
.padding()
|
|
}
|
|
|
|
// MARK: - Data
|
|
|
|
private var dataTab: some View {
|
|
Form {
|
|
HStack {
|
|
Text("Total Timers")
|
|
Spacer()
|
|
Text("\(store.timers.count)")
|
|
.foregroundStyle(.secondary)
|
|
}
|
|
|
|
HStack {
|
|
Text("Active")
|
|
Spacer()
|
|
Text("\(store.activeTimers.count)")
|
|
.foregroundStyle(.secondary)
|
|
}
|
|
|
|
Divider()
|
|
|
|
Button("Clear Completed") {
|
|
store.timers.removeAll { [.completed, .dismissed].contains($0.state) }
|
|
}
|
|
|
|
Button("Delete All Timers", role: .destructive) {
|
|
store.timers.removeAll()
|
|
}
|
|
}
|
|
.padding()
|
|
}
|
|
|
|
// MARK: - About
|
|
|
|
private var aboutTab: some View {
|
|
VStack(spacing: 12) {
|
|
Image(systemName: "clock.fill")
|
|
.font(.system(size: 48))
|
|
.foregroundStyle(CMColors.accent)
|
|
|
|
Text("ChronoMind")
|
|
.font(.title2.bold())
|
|
|
|
Text("Version 1.0.0")
|
|
.foregroundStyle(.secondary)
|
|
|
|
Text("Time management with cascade warnings")
|
|
.font(.caption)
|
|
.foregroundStyle(.tertiary)
|
|
|
|
Spacer()
|
|
}
|
|
.padding()
|
|
.frame(maxWidth: .infinity)
|
|
}
|
|
}
|