import SwiftUI struct PreferencesView: View { private enum Tab: CaseIterable { case monitor, notifications, alerts var title: String { switch self { case .monitor: return "Monitor" case .notifications: return "Notifications" case .alerts: return "Alerts" } } var icon: String { switch self { case .monitor: return "waveform.path.ecg" case .notifications: return "bell.badge" case .alerts: return "exclamationmark.triangle" } } } @AppStorage("pingInterval") private var storedPingInterval: Int = 10 @AppStorage("refreshInterval") private var storedRefreshInterval: Int = 60 @AppStorage("showIntervalIndicator") private var showIntervalIndicator: Bool = true @State private var pingIntervalSlider: Double = 10 @State private var refreshIntervalSlider: Double = 60 @State private var selection: Tab = .monitor @State private var hoveredTab: Tab? private let minimumInterval: Double = 10 private let maximumPingInterval: Double = 60 private let maximumRefreshInterval: Double = 600 var body: some View { HStack(spacing: 0) { sidebar Divider() ScrollView { detailContent(for: selection) .frame(maxWidth: .infinity, alignment: .leading) .padding() } .frame(minWidth: 360, maxWidth: .infinity, maxHeight: .infinity) } .frame(minWidth: 560, minHeight: 360) .onAppear { pingIntervalSlider = Double(storedPingInterval) refreshIntervalSlider = Double(storedRefreshInterval) } .onChange(of: pingIntervalSlider) { _, newValue in storedPingInterval = Int(newValue) } .onChange(of: refreshIntervalSlider) { _, newValue in storedRefreshInterval = Int(newValue) } } private var sidebar: some View { VStack(alignment: .leading, spacing: 12) { Text("Preferences") .font(.headline) .padding(.bottom, 8) ForEach(Tab.allCases, id: \.self) { tab in Button { selection = tab } label: { HStack(spacing: 10) { Image(systemName: tab.icon) .frame(width: 20) Text(tab.title) Spacer() } .padding(.vertical, 8) .padding(.horizontal, 10) .frame(maxWidth: .infinity, alignment: .leading) } .buttonStyle(.plain) .focusable(false) .contentShape(Capsule()) .background( Capsule(style: .continuous) .fill(backgroundColor(for: tab)) ) .foregroundColor(selection == tab ? .white : .primary) .onHover { isHovering in hoveredTab = isHovering ? tab : (hoveredTab == tab ? nil : hoveredTab) } } Spacer() } .padding() .frame(width: 180, alignment: .top) } @ViewBuilder private func detailContent(for tab: Tab) -> some View { VStack(alignment: .leading, spacing: 12) { Text(tab.title) .font(.title2) .padding(.bottom, 12) switch tab { case .monitor: MonitorPreferencesView( pingIntervalSlider: $pingIntervalSlider, refreshIntervalSlider: $refreshIntervalSlider, showIntervalIndicator: $showIntervalIndicator, minimumInterval: minimumInterval, maximumPingInterval: maximumPingInterval, maximumRefreshInterval: maximumRefreshInterval, pingChanged: handlePingSliderEditing(_:), refreshChanged: handleRefreshSliderEditing(_:) ) case .notifications: NotificationsPreferencesView() case .alerts: AlertsPreferencesView() } } } private func handlePingSliderEditing(_ editing: Bool) { if !editing { storedPingInterval = Int(pingIntervalSlider) } } private func handleRefreshSliderEditing(_ editing: Bool) { if !editing { storedRefreshInterval = Int(refreshIntervalSlider) } } private func backgroundColor(for tab: Tab) -> Color { if selection == tab { return Color.accentColor } if hoveredTab == tab { return Color.accentColor.opacity(0.2) } return Color.accentColor.opacity(0.08) } } private struct MonitorPreferencesView: View { @Binding var pingIntervalSlider: Double @Binding var refreshIntervalSlider: Double @Binding var showIntervalIndicator: Bool let minimumInterval: Double let maximumPingInterval: Double let maximumRefreshInterval: Double let pingChanged: (Bool) -> Void let refreshChanged: (Bool) -> Void var body: some View { VStack(alignment: .leading, spacing: 24) { intervalSection( title: "Ping interval", value: $pingIntervalSlider, range: minimumInterval...maximumPingInterval, onEditingChanged: pingChanged ) intervalSection( title: "Refresh interval", value: $refreshIntervalSlider, range: minimumInterval...maximumRefreshInterval, onEditingChanged: refreshChanged ) Divider() Toggle("Show interval indicator", isOn: $showIntervalIndicator) .toggleStyle(.switch) Spacer() } .frame(maxWidth: .infinity, alignment: .leading) } private func intervalSection( title: String, value: Binding, range: ClosedRange, onEditingChanged: @escaping (Bool) -> Void ) -> some View { VStack(alignment: .leading, spacing: 8) { HStack { Text(title) .font(.headline) Spacer() Text("\(Int(value.wrappedValue)) seconds") .font(.subheadline) .foregroundColor(.secondary) } Slider( value: value, in: range, step: 5 ) { Text(title) } minimumValueLabel: { Text("\(Int(range.lowerBound))s") .font(.caption) .foregroundColor(.secondary) } maximumValueLabel: { Text("\(Int(range.upperBound))s") .font(.caption) .foregroundColor(.secondary) } onEditingChanged: { editing in onEditingChanged(editing) } } } } private struct NotificationsPreferencesView: View { var body: some View { VStack(alignment: .leading, spacing: 12) { Text("Notifications") .font(.headline) .padding(.bottom) Text("Configure notification behavior here.") .foregroundColor(.secondary) Spacer() } .frame(maxWidth: .infinity, alignment: .leading) .padding() } } private struct AlertsPreferencesView: View { var body: some View { VStack(alignment: .leading, spacing: 12) { Text("Alerts") .font(.headline) .padding(.bottom) Text("Configure alert thresholds and behavior.") .foregroundColor(.secondary) Spacer() } .frame(maxWidth: .infinity, alignment: .leading) .padding() } } #Preview { PreferencesView() }