diff --git a/DynamicIsland/Localizable.xcstrings b/DynamicIsland/Localizable.xcstrings index 5b63bb2..b7b0c06 100644 --- a/DynamicIsland/Localizable.xcstrings +++ b/DynamicIsland/Localizable.xcstrings @@ -1891,6 +1891,9 @@ } } } + }, + "1m" : { + }, "1x" : { "extractionState" : "stale", @@ -2006,6 +2009,9 @@ } } } + }, + "5m" : { + }, "7 items" : { "localizations" : { @@ -2126,6 +2132,9 @@ } } } + }, + "15m" : { + }, "20" : { "extractionState" : "stale", @@ -4340,6 +4349,9 @@ } } } + }, + "App" : { + }, "App icon" : { "localizations" : { @@ -4858,6 +4870,9 @@ } } } + }, + "Auto-hide inactive notch media player" : { + }, "Auto-scroll to next event" : { "comment" : "A toggle that enables or disables the automatic scrolling to the next event in the calendar view.", @@ -5076,6 +5091,12 @@ } } } + }, + "Automatically switch displays" : { + + }, + "Average" : { + }, "Averages" : { "comment" : "A label displayed above the average usage and last update information for a GPU.", @@ -6120,6 +6141,9 @@ } } } + }, + "Cached" : { + }, "Calendar" : { "localizations" : { @@ -8072,6 +8096,9 @@ } } } + }, + "Close gesture" : { + }, "Code and debug with color picker, stats monitoring, and screen assistant." : { "comment" : "Description of a user profile when the name is \"Developer\".", @@ -8280,6 +8307,9 @@ } } } + }, + "Color-coded battery display" : { + }, "Color-coded fills and smooth gradients are unavailable in Segmented mode. Switch to Hierarchical or Gradient to adjust these options." : { "comment" : "An explanatory text that appears when the user tries to enable color-coded color gradients in Segmented progress bars. It instructs the user to switch to Hierarchical or Gradient progress bars to enable these features.", @@ -8316,6 +8346,12 @@ } } } + }, + "Color-coded Volume" : { + + }, + "Color-coded volume display" : { + }, "ColorPicker Disabled" : { "localizations" : { @@ -8448,6 +8484,12 @@ } } } + }, + "Compressed" : { + + }, + "Compute" : { + }, "Configuration" : { "localizations" : { @@ -8946,6 +8988,15 @@ } }, "shouldTranslate" : false + }, + "Core Clock" : { + + }, + "Cores" : { + + }, + "Corner radius scaling" : { + }, "Countdown style" : { "comment" : "A label for the style of countdown to use when reminders are live.", @@ -8976,6 +9027,9 @@ } } } + }, + "CPU Overview" : { + }, "CPU Usage" : { "localizations" : { @@ -9038,6 +9092,9 @@ } } } + }, + "Critical" : { + }, "Current custom timer:" : { "extractionState" : "stale", @@ -10748,6 +10805,12 @@ } } } + }, + "Disk I/O" : { + + }, + "Disk Overview" : { + }, "Disk Read" : { "localizations" : { @@ -10977,6 +11040,9 @@ } } } + }, + "Down" : { + }, "Download" : { "localizations" : { @@ -11182,6 +11248,9 @@ } } } + }, + "Downloaded" : { + }, "Downloads" : { "localizations" : { @@ -11812,6 +11881,12 @@ } } } + }, + "Enable album art parallax" : { + + }, + "Enable album art parallax effect" : { + }, "Enable and style the media controls that appear above the system clock when the screen is locked." : { "comment" : "A description of the media panel settings in the lock screen settings view.", @@ -11830,6 +11905,9 @@ } } } + }, + "Enable blur effect behind album art" : { + }, "Enable Built-in System HUD" : { "extractionState" : "stale", @@ -11896,9 +11974,20 @@ } } } + }, + "Enable Camera Detection" : { + + }, + "Enable Clipboard Manager" : { + + }, + "Enable Color Filtering" : { + + }, + "Enable Color Picker" : { + }, "Enable colored spectrograms" : { - "extractionState" : "stale", "localizations" : { "ar" : { "stringUnit" : { @@ -12019,6 +12108,9 @@ } } } + }, + "Enable Create from Clipboard" : { + }, "Enable customizable controls" : { "comment" : "A toggle that allows users to enable or disable customizable controls for media playback.", @@ -12037,6 +12129,9 @@ } } } + }, + "Enable download detection" : { + }, "Enable Dynamic Island media controls to manage the lock screen panel." : { "localizations" : { @@ -12047,6 +12142,9 @@ } } } + }, + "Enable Dynamic mirror" : { + }, "Enable extension diagnostics logging" : { "comment" : "A toggle that enables or disables logging of diagnostic information related to extensions.", @@ -12095,6 +12193,15 @@ } } } + }, + "Enable Focus Detection" : { + + }, + "Enable gestures" : { + + }, + "Enable global keyboard shortcuts" : { + }, "Enable global keyboard shortcuts above to customize your shortcuts." : { "localizations" : { @@ -12111,6 +12218,9 @@ } } } + }, + "Enable glowing effect" : { + }, "Enable graph visibility in Settings → Stats to view performance data." : { "comment" : "A message displayed when no graphs are enabled in the Stats tab.", @@ -12131,7 +12241,6 @@ } }, "Enable haptics" : { - "extractionState" : "stale", "localizations" : { "ar" : { "stringUnit" : { @@ -12341,6 +12450,12 @@ } } } + }, + "Enable lock screen live activity" : { + + }, + "Enable lyrics" : { + }, "Enable media panel blur" : { "comment" : "A toggle switch that enables or disables the blur effect on the media panel.", @@ -12365,6 +12480,12 @@ } } } + }, + "Enable Microphone Detection" : { + + }, + "Enable Minimalistic UI" : { + }, "Enable music live activity" : { "localizations" : { @@ -12465,6 +12586,21 @@ } } } + }, + "Enable Note Pinning" : { + + }, + "Enable Note Search" : { + + }, + "Enable Notes" : { + + }, + "Enable reminder live activity" : { + + }, + "Enable Screen Assistant" : { + }, "Enable screen color picking functionality. Use Cmd+Shift+P to quickly access the color picker." : { "localizations" : { @@ -12481,6 +12617,12 @@ } } } + }, + "Enable Screen Recording Detection" : { + + }, + "Enable shelf" : { + }, "Enable sneak peek" : { "localizations" : { @@ -12519,6 +12661,12 @@ } } } + }, + "Enable system stats monitoring" : { + + }, + "Enable terminal" : { + }, "Enable the battery capsule and configure its layout." : { "localizations" : { @@ -12611,6 +12759,9 @@ } } } + }, + "Enable timer feature" : { + }, "Enable timer live activity" : { "localizations" : { @@ -12667,6 +12818,9 @@ } } } + }, + "Enable window shadow" : { + }, "Engine Utilization" : { "comment" : "A label describing the utilization of the GPU's engine components.", @@ -13230,6 +13384,9 @@ } } } + }, + "Extend hover area" : { + }, "Extension tab unavailable" : { "comment" : "A message displayed when the extension tab is not available.", @@ -13384,6 +13541,9 @@ } } } + }, + "Fan Speed" : { + }, "Favorites" : { "comment" : "Localized name of the \"Favorites\" tab in the clipboard.", @@ -13700,6 +13860,9 @@ } } } + }, + "Free" : { + }, "Free · %@" : { "comment" : "A label displaying the amount of free storage on a disk device. The argument is the number of free bytes on the disk.", @@ -13730,6 +13893,9 @@ } } } + }, + "Frequency" : { + }, "Frosted Glass" : { "comment" : "The name of the frosted glass style.", @@ -14521,6 +14687,9 @@ } } } + }, + "GPU Overview" : { + }, "GPU Usage" : { "localizations" : { @@ -14720,6 +14889,9 @@ } } } + }, + "HH" : { + }, "Hide" : { "localizations" : { @@ -14778,6 +14950,9 @@ } } } + }, + "Hide Dynamic Island during screenshots & recordings" : { + }, "Hide DynamicIsland Options" : { "localizations" : { @@ -14910,6 +15085,9 @@ } } } + }, + "Hide until hovered on non-notch displays" : { + }, "Hierarchical" : { "localizations" : { @@ -15571,6 +15749,9 @@ } } } + }, + "Idle Animation" : { + }, "Idle Animation Style" : { "comment" : "The header label for the section that lets users choose the style of idle animation.", @@ -16077,6 +16258,9 @@ } } } + }, + "Interfaces" : { + }, "IPv4 · %@" : { "comment" : "A label displaying an IP address for a network interface. The text inside the parentheses is the IP address of the interface.", @@ -16379,6 +16563,9 @@ } } } + }, + "Launch at login" : { + }, "Layout" : { "comment" : "A label for the segment control that lets the user choose the layout of the weather widget.", @@ -16670,6 +16857,15 @@ } } } + }, + "Load 1m" : { + + }, + "Load 5m" : { + + }, + "Load 15m" : { + }, "Load Average" : { "comment" : "A label displayed above the load average values.", @@ -16888,6 +17084,9 @@ } } } + }, + "Logical cores" : { + }, "Loop Mode" : { "localizations" : { @@ -17802,7 +18001,6 @@ }, "Media change with horizontal gestures" : { "comment" : "A toggle that lets the user enable or disable the feature of changing the currently playing media using horizontal gestures.", - "extractionState" : "stale", "isCommentAutoGenerated" : true, "localizations" : { "ar" : { @@ -18485,6 +18683,18 @@ } } } + }, + "Memory Clock" : { + + }, + "Memory Overview" : { + + }, + "Memory pressure is critical; the system may purge aggressively." : { + + }, + "Memory pressure is elevated. Closing apps may help." : { + }, "Memory Usage" : { "localizations" : { @@ -18515,7 +18725,6 @@ } }, "Menubar icon" : { - "extractionState" : "stale", "localizations" : { "ar" : { "stringUnit" : { @@ -19251,6 +19460,9 @@ } } } + }, + "MM" : { + }, "Monitor clipboard changes and keep a history of recent copies. Use Cmd+Shift+V to quickly access clipboard history." : { "localizations" : { @@ -19950,6 +20162,9 @@ } } } + }, + "Network Activity" : { + }, "Network Download" : { "localizations" : { @@ -19978,6 +20193,9 @@ } } } + }, + "Network Overview" : { + }, "Network Upload" : { "localizations" : { @@ -20956,6 +21174,9 @@ } } } + }, + "Normal" : { + }, "Not active" : { "comment" : "A description of whether the camera is currently active.", @@ -21856,6 +22077,12 @@ } } } + }, + "Open notch on hover" : { + + }, + "Open shelf tab by default if items added" : { + }, "Open System Settings" : { "localizations" : { @@ -21966,6 +22193,9 @@ } } } + }, + "Other" : { + }, "Output devices" : { "comment" : "Section title for the list of available audio output devices in the media output selector.", @@ -22089,6 +22319,9 @@ }, "Pause" : { "shouldTranslate" : false + }, + "Peak" : { + }, "Pending" : { "comment" : "A label indicating that a user's permission to access a feature is being checked.", @@ -22107,6 +22340,9 @@ } } } + }, + "Per-Core Usage" : { + }, "Percentage" : { "localizations" : { @@ -22325,6 +22561,15 @@ } } } + }, + "Play feedback when volume is changed" : { + + }, + "Play lock/unlock sounds" : { + + }, + "Play low battery alert sound" : { + }, "Plays the supplied feedback clip whenever you press the hardware volume keys." : { "comment" : "A help text describing the audio feedback feature.", @@ -22627,6 +22872,9 @@ } } } + }, + "Pressure & Swap" : { + }, "Prevents the terminal from closing when the cursor accidentally leaves the notch area." : { "localizations" : { @@ -23730,6 +23978,12 @@ } } } + }, + "Render" : { + + }, + "Renderer" : { + }, "Request Access" : { "localizations" : { @@ -24219,6 +24473,12 @@ } } } + }, + "Reverse open/close scroll gestures" : { + + }, + "Reverse swipe gestures" : { + }, "Revoke Access" : { "comment" : "A button that revokes access to an app.", @@ -24690,6 +24950,9 @@ } } } + }, + "Scroll device name in HUD" : { + }, "Scrollback" : { "localizations" : { @@ -25383,6 +25646,12 @@ } } } + }, + "Session Avg" : { + + }, + "Session Totals" : { + }, "Set" : { "localizations" : { @@ -25505,6 +25774,9 @@ } } } + }, + "Settings icon in notch" : { + }, "Settings…" : { "comment" : "A command that shows the settings window.", @@ -25745,9 +26017,14 @@ } } } + }, + "Show All Color Formats" : { + + }, + "Show AQI widget" : { + }, "Show battery indicator" : { - "extractionState" : "stale", "localizations" : { "ar" : { "stringUnit" : { @@ -25846,6 +26123,30 @@ } } } + }, + "Show battery percentage" : { + + }, + "Show battery percentage text in HUD" : { + + }, + "Show Bluetooth battery" : { + + }, + "Show Bluetooth device connections" : { + + }, + "Show calendar" : { + + }, + "Show Caps Lock Indicator" : { + + }, + "Show Caps Lock label" : { + + }, + "Show Character Count" : { + }, "Show charging indicator" : { "extractionState" : "stale", @@ -25875,6 +26176,15 @@ } } } + }, + "Show charging percentage" : { + + }, + "Show charging status" : { + + }, + "Show Clipboard Icon" : { + }, "Show Color History" : { "localizations" : { @@ -25897,6 +26207,9 @@ } } } + }, + "Show Color Picker Icon" : { + }, "Show Color Picker Panel" : { "localizations" : { @@ -26075,6 +26388,9 @@ } } } + }, + "Show event for entire duration" : { + }, "Show events from all calendars" : { "localizations" : { @@ -26129,6 +26445,18 @@ } } } + }, + "Show Focus as brief toast" : { + + }, + "Show Focus Indicator" : { + + }, + "Show Focus Label" : { + + }, + "Show focus widget" : { + }, "Show full event titles" : { "comment" : "A toggle that shows or hides the full event titles in the calendar view", @@ -26147,6 +26475,33 @@ } } } + }, + "Show location label" : { + + }, + "Show lock screen media panel" : { + + }, + "Show lock screen reminder" : { + + }, + "Show lock screen timer" : { + + }, + "Show lock screen timer widget" : { + + }, + "Show lock screen weather" : { + + }, + "Show media app icon" : { + + }, + "Show media controls in Dynamic Island" : { + + }, + "Show next calendar event" : { + }, "Show on a specific display" : { "localizations" : { @@ -26347,6 +26702,9 @@ } } } + }, + "Show panel border" : { + }, "Show Percentage" : { "localizations" : { @@ -26363,6 +26721,15 @@ } } } + }, + "Show percentages beside progress bars" : { + + }, + "Show power status icons" : { + + }, + "Show power status notifications" : { + }, "Show preset list in timer tab" : { "comment" : "A toggle that lets users decide whether to show a list of presets in the timer tab.", @@ -26397,6 +26764,9 @@ } } } + }, + "Show Recording Indicator" : { + }, "Show shuffle and repeat buttons" : { "extractionState" : "stale", @@ -26516,6 +26886,12 @@ } } } + }, + "Show start time after event begins" : { + + }, + "Show sunrise time" : { + }, "Show the active Focus name inside the indicator." : { "comment" : "A description of why the \"Show Focus Label\" toggle is disabled when the \"Show Focus as brief toast\" toggle is enabled.", @@ -26534,6 +26910,9 @@ } } } + }, + "Show time remaining" : { + }, "Show timer name" : { "localizations" : { @@ -26789,6 +27168,9 @@ } } } + }, + "Smooth color transitions" : { + }, "Smooth transitions blend Green (0–60%), Yellow (60–85%), and Red (85–100%) through the entire fill." : { "comment" : "A description of how the color-coding feature works.", @@ -27397,6 +27779,9 @@ } } } + }, + "SS" : { + }, "Standard" : { "localizations" : { @@ -27790,6 +28175,17 @@ } } }, + "stats_resource_active" : { + "extractionState" : "extracted_with_value", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "new", + "value" : "Active" + } + } + } + }, "Status" : { "comment" : "A label displayed next to the status indicator in the \"Third-party DDC provider\" section of the External Display Integrations settings.", "isCommentAutoGenerated" : true, @@ -27888,6 +28284,9 @@ } } } + }, + "Stop monitoring after closing the notch" : { + }, "Stop recording" : { "localizations" : { @@ -28010,6 +28409,9 @@ } } } + }, + "Storage Devices" : { + }, "Student" : { "comment" : "Name of a user profile for a student.", @@ -28346,6 +28748,9 @@ } } } + }, + "System memory pressure is nominal." : { + }, "System Monospaced" : { "localizations" : { @@ -28413,6 +28818,9 @@ } } } + }, + "Temperature" : { + }, "Temperature unit" : { "localizations" : { @@ -28617,6 +29025,9 @@ } } } + }, + "Tiler" : { + }, "Time Range" : { "extractionState" : "stale", @@ -29232,6 +29643,12 @@ } } } + }, + "Top Processes" : { + + }, + "Total" : { + }, "Total · %@" : { "comment" : "A label displaying the total storage capacity of a disk device. The argument is the total storage capacity of the disk device.", @@ -29840,6 +30257,9 @@ } } } + }, + "Up" : { + }, "Update interval" : { "localizations" : { @@ -30004,6 +30424,12 @@ } }, "shouldTranslate" : false + }, + "Uploaded" : { + + }, + "Uptime" : { + }, "URL" : { "comment" : "Name of a clipboard item type that represents a URL.", @@ -30070,6 +30496,9 @@ } } } + }, + "Use accent color" : { + }, "Use Accent Color" : { "localizations" : { @@ -30086,6 +30515,12 @@ } } } + }, + "Use circular battery indicator" : { + + }, + "Use colored gauges" : { + }, "Use DevTools" : { "comment" : "One option in the confirmation dialog in the `OnboardingView` that lets the user choose to use the developer tools for focus monitoring.", @@ -30098,6 +30533,9 @@ } } } + }, + "Use MacBook icon when on battery" : { + }, "Use music visualizer spectrogram" : { "localizations" : { @@ -30198,6 +30636,9 @@ } } } + }, + "Use simpler close animation" : { + }, "Use the Media tab to configure sneak peek, lyrics, and floating media controls." : { "comment" : "A footer for the \"Media Live Activity\" section, explaining how to configure the media controls.", @@ -30298,6 +30739,9 @@ } } } + }, + "User" : { + }, "Uses event-driven private API for real-time screen recording detection" : { "localizations" : { @@ -30601,6 +31045,9 @@ } }, "shouldTranslate" : false + }, + "Video" : { + }, "View on GitHub: th-ch/youtube-music" : { "localizations" : { @@ -30771,6 +31218,9 @@ } } } + }, + "Warning" : { + }, "Weather" : { "comment" : "A label and its accompanying text displayed in a lock screen panel.", @@ -31276,6 +31726,9 @@ } } } + }, + "Wired" : { + }, "With %lld graphs enabled, the Dynamic Island will expand horizontally to accommodate all graphs in a single row." : { "localizations" : { @@ -31334,6 +31787,9 @@ } } } + }, + "Written" : { + }, "You" : { "localizations" : { diff --git a/DynamicIsland/components/Notch/NotchTimerView.swift b/DynamicIsland/components/Notch/NotchTimerView.swift index c64606e..15dfe29 100644 --- a/DynamicIsland/components/Notch/NotchTimerView.swift +++ b/DynamicIsland/components/Notch/NotchTimerView.swift @@ -615,11 +615,11 @@ private struct DurationInputRow: View { var body: some View { HStack(alignment: .center, spacing: 8) { - DurationField(label: "HH", value: $hours, range: 0...23, width: fieldWidth) + DurationField(label: String(localized: "HH"), value: $hours, range: 0...23, width: fieldWidth) colon - DurationField(label: "MM", value: $minutes, range: 0...59, width: fieldWidth) + DurationField(label: String(localized: "MM"), value: $minutes, range: 0...59, width: fieldWidth) colon - DurationField(label: "SS", value: $seconds, range: 0...59, width: fieldWidth) + DurationField(label: String(localized: "SS"), value: $seconds, range: 0...59, width: fieldWidth) } } diff --git a/DynamicIsland/components/Settings/SettingsView.swift b/DynamicIsland/components/Settings/SettingsView.swift index a31e0c5..498fc63 100644 --- a/DynamicIsland/components/Settings/SettingsView.swift +++ b/DynamicIsland/components/Settings/SettingsView.swift @@ -34,12 +34,12 @@ private enum SettingsTabGroup: String, CaseIterable, Identifiable { var title: String? { switch self { case .core: return nil - case .mediaAndDisplay: return "Media & Display" - case .system: return "System" - case .productivity: return "Productivity" - case .utilities: return "Utilities" - case .developer: return "Developer" - case .integrations: return "Integrations" + case .mediaAndDisplay: return String(localized: "Media & Display") + case .system: return String(localized: "System") + case .productivity: return String(localized: "Productivity") + case .utilities: return String(localized: "Utilities") + case .developer: return String(localized: "Developer") + case .integrations: return String(localized: "Integrations") case .info: return nil } } @@ -87,27 +87,27 @@ private enum SettingsTab: String, CaseIterable, Identifiable { var title: String { switch self { - case .general: return "General" - case .liveActivities: return "Live Activities" - case .appearance: return "Appearance" - case .lockScreen: return "Lock Screen" - case .media: return "Media" - case .devices: return "Devices" - case .extensions: return "Extensions" - case .timer: return "Timer" - case .calendar: return "Calendar" - case .hudAndOSD: return "Controls" - case .battery: return "Battery" - case .stats: return "Stats" - case .clipboard: return "Clipboard" - case .screenAssistant: return "Screen Assistant" - case .colorPicker: return "Color Picker" - case .downloads: return "Downloads" - case .shelf: return "Shelf" - case .shortcuts: return "Shortcuts" - case .notes: return "Notes" - case .terminal: return "Terminal" - case .about: return "About" + case .general: return String(localized: "General") + case .liveActivities: return String(localized: "Live Activities") + case .appearance: return String(localized: "Appearance") + case .lockScreen: return String(localized: "Lock Screen") + case .media: return String(localized: "Media") + case .devices: return String(localized: "Devices") + case .extensions: return String(localized: "Extensions") + case .timer: return String(localized: "Timer") + case .calendar: return String(localized: "Calendar") + case .hudAndOSD: return String(localized: "Controls") + case .battery: return String(localized: "Battery") + case .stats: return String(localized: "Stats") + case .clipboard: return String(localized: "Clipboard") + case .screenAssistant: return String(localized: "Screen Assistant") + case .colorPicker: return String(localized: "Color Picker") + case .downloads: return String(localized: "Downloads") + case .shelf: return String(localized: "Shelf") + case .shortcuts: return String(localized: "Shortcuts") + case .notes: return String(localized: "Notes") + case .terminal: return String(localized: "Terminal") + case .about: return String(localized: "About") } } @@ -1046,25 +1046,31 @@ struct GeneralSettings: View { var body: some View { Form { Section { - Defaults.Toggle("Enable Minimalistic UI", key: .enableMinimalisticUI) - .onChange(of: enableMinimalisticUI) { _, newValue in - if newValue { - // Auto-enable simpler animation mode - Defaults[.useModernCloseAnimation] = true - } + Defaults.Toggle(key: .enableMinimalisticUI) { + Text("Enable Minimalistic UI") + } + .onChange(of: enableMinimalisticUI) { _, newValue in + if newValue { + // Auto-enable simpler animation mode + Defaults[.useModernCloseAnimation] = true } - .settingsHighlight(id: highlightID("Enable Minimalistic UI")) + } + .settingsHighlight(id: highlightID("Enable Minimalistic UI")) } header: { Text("UI Mode") } footer: { Text("Minimalistic mode focuses on media controls and system HUDs, hiding all extra features for a clean, focused experience. Automatically enables simpler animations.") } - + Section { - Defaults.Toggle("Menubar icon", key: .menubarIcon) - .settingsHighlight(id: highlightID("Menubar icon")) - LaunchAtLogin.Toggle("Launch at login") - .settingsHighlight(id: highlightID("Launch at login")) + Defaults.Toggle(key: .menubarIcon) { + Text("Menubar icon") + } + .settingsHighlight(id: highlightID("Menubar icon")) + LaunchAtLogin.Toggle { + Text("Launch at login") + } + .settingsHighlight(id: highlightID("Launch at login")) Defaults.Toggle(key: .showOnAllDisplays) { Text("Show on all displays") } @@ -1082,40 +1088,44 @@ struct GeneralSettings: View { } .disabled(showOnAllDisplays) .settingsHighlight(id: highlightID("Show on a specific display")) - Defaults.Toggle("Automatically switch displays", key: .automaticallySwitchDisplay) + Defaults.Toggle(key: .automaticallySwitchDisplay) { + Text("Automatically switch displays") + } .onChange(of: automaticallySwitchDisplay) { NotificationCenter.default.post(name: Notification.Name.automaticallySwitchDisplayChanged, object: nil) } .disabled(showOnAllDisplays) .settingsHighlight(id: highlightID("Automatically switch displays")) - Defaults.Toggle("Hide Dynamic Island during screenshots & recordings", key: .hideDynamicIslandFromScreenCapture) - .settingsHighlight(id: highlightID("Hide Dynamic Island during screenshots & recordings")) + Defaults.Toggle(key: .hideDynamicIslandFromScreenCapture) { + Text("Hide Dynamic Island during screenshots & recordings") + } + .settingsHighlight(id: highlightID("Hide Dynamic Island during screenshots & recordings")) } header: { Text("System features") } - + Section { Picker(selection: $notchHeightMode, label: - Text("Notch display height")) { - Text("Match real notch size") - .tag(WindowHeightMode.matchRealNotchSize) - Text("Match menubar height") - .tag(WindowHeightMode.matchMenuBar) - Text("Custom height") - .tag(WindowHeightMode.custom) - } - .onChange(of: notchHeightMode) { - switch notchHeightMode { - case .matchRealNotchSize: - notchHeight = 38 - case .matchMenuBar: - notchHeight = 44 - case .custom: - notchHeight = 38 + Text("Notch display height")) { + Text("Match real notch size") + .tag(WindowHeightMode.matchRealNotchSize) + Text("Match menubar height") + .tag(WindowHeightMode.matchMenuBar) + Text("Custom height") + .tag(WindowHeightMode.custom) + } + .onChange(of: notchHeightMode) { + switch notchHeightMode { + case .matchRealNotchSize: + notchHeight = 38 + case .matchMenuBar: + notchHeight = 44 + case .custom: + notchHeight = 38 + } + NotificationCenter.default.post(name: Notification.Name.notchHeightChanged, object: nil) } - NotificationCenter.default.post(name: Notification.Name.notchHeightChanged, object: nil) - } - .settingsHighlight(id: highlightID("Notch display height")) + .settingsHighlight(id: highlightID("Notch display height")) if notchHeightMode == .custom { Slider(value: $notchHeight, in: 15...45, step: 1) { Text("Custom notch size - \(notchHeight, specifier: "%.0f")") @@ -1172,16 +1182,20 @@ struct GeneralSettings: View { } } } - + @ViewBuilder func gestureControls() -> some View { Section { - Defaults.Toggle("Enable gestures", key: .enableGestures) - .disabled(!openNotchOnHover) - .settingsHighlight(id: highlightID("Enable gestures")) + Defaults.Toggle(key: .enableGestures) { + Text("Enable gestures") + } + .disabled(!openNotchOnHover) + .settingsHighlight(id: highlightID("Enable gestures")) if enableGestures { - Defaults.Toggle("Media change with horizontal gestures", key: .enableHorizontalMusicGestures) - .settingsHighlight(id: highlightID("Horizontal media gestures")) + Defaults.Toggle(key: .enableHorizontalMusicGestures) { + Text("Media change with horizontal gestures") + } + .settingsHighlight(id: highlightID("Horizontal media gestures")) if enableHorizontalMusicGestures { Picker("Gesture skip behavior", selection: $musicGestureBehavior) { @@ -1197,12 +1211,16 @@ struct GeneralSettings: View { .font(.caption) .foregroundStyle(.secondary) - Defaults.Toggle("Reverse swipe gestures", key: .reverseSwipeGestures) - .settingsHighlight(id: highlightID("Reverse swipe gestures")) + Defaults.Toggle(key: .reverseSwipeGestures) { + Text("Reverse swipe gestures") + } + .settingsHighlight(id: highlightID("Reverse swipe gestures")) } - Defaults.Toggle("Close gesture", key: .closeGestureEnabled) - .settingsHighlight(id: highlightID("Close gesture")) + Defaults.Toggle(key: .closeGestureEnabled) { + Text("Close gesture") + } + .settingsHighlight(id: highlightID("Close gesture")) Slider(value: $gestureSensitivity, in: 100...300, step: 100) { HStack { Text("Gesture sensitivity") @@ -1212,8 +1230,10 @@ struct GeneralSettings: View { } } - Defaults.Toggle("Reverse open/close scroll gestures", key: .reverseScrollGestures) - .settingsHighlight(id: highlightID("Reverse scroll gestures")) + Defaults.Toggle(key: .reverseScrollGestures) { + Text("Reverse open/close scroll gestures") + } + .settingsHighlight(id: highlightID("Reverse scroll gestures")) } } header: { HStack { @@ -1231,12 +1251,18 @@ struct GeneralSettings: View { @ViewBuilder func NotchBehaviour() -> some View { Section { - Defaults.Toggle("Extend hover area", key: .extendHoverArea) - .settingsHighlight(id: highlightID("Extend hover area")) - Defaults.Toggle("Enable haptics", key: .enableHaptics) - .settingsHighlight(id: highlightID("Enable haptics")) - Defaults.Toggle("Open notch on hover", key: .openNotchOnHover) - .settingsHighlight(id: highlightID("Open notch on hover")) + Defaults.Toggle(key: .extendHoverArea) { + Text("Extend hover area") + } + .settingsHighlight(id: highlightID("Extend hover area")) + Defaults.Toggle(key: .enableHaptics) { + Text("Enable haptics") + } + .settingsHighlight(id: highlightID("Enable haptics")) + Defaults.Toggle(key: .openNotchOnHover) { + Text("Open notch on hover") + } + .settingsHighlight(id: highlightID("Open notch on hover")) Toggle("Remember last tab", isOn: $coordinator.openLastTabByDefault) if openNotchOnHover { Slider(value: $minimumHoverDuration, in: 0...1, step: 0.1) { @@ -1264,8 +1290,10 @@ struct GeneralSettings: View { Text(externalDisplayStyle.description) .font(.caption) .foregroundStyle(.secondary) - Defaults.Toggle("Hide until hovered on non-notch displays", key: .hideNonNotchUntilHover) - .settingsHighlight(id: highlightID("Hide until hovered")) + Defaults.Toggle(key: .hideNonNotchUntilHover) { + Text("Hide until hovered on non-notch displays") + } + .settingsHighlight(id: highlightID("Hide until hovered")) Text("When enabled, the notch slides up and hides on external (non-notch) displays until you hover over it.") .font(.caption) .foregroundStyle(.secondary) @@ -1284,20 +1312,30 @@ struct Charge: View { Form { if BatteryActivityManager.shared.hasBattery() { Section { - Defaults.Toggle("Show battery indicator", key: .showBatteryIndicator) - .settingsHighlight(id: highlightID("Show battery indicator")) - Defaults.Toggle("Show power status notifications", key: .showPowerStatusNotifications) - .settingsHighlight(id: highlightID("Show power status notifications")) - Defaults.Toggle("Play low battery alert sound", key: .playLowBatteryAlertSound) - .settingsHighlight(id: highlightID("Play low battery alert sound")) + Defaults.Toggle(key: .showBatteryIndicator) { + Text("Show battery indicator") + } + .settingsHighlight(id: highlightID("Show battery indicator")) + Defaults.Toggle(key: .showPowerStatusNotifications) { + Text("Show power status notifications") + } + .settingsHighlight(id: highlightID("Show power status notifications")) + Defaults.Toggle(key: .playLowBatteryAlertSound) { + Text("Play low battery alert sound") + } + .settingsHighlight(id: highlightID("Play low battery alert sound")) } header: { Text("General") } Section { - Defaults.Toggle("Show battery percentage", key: .showBatteryPercentage) - .settingsHighlight(id: highlightID("Show battery percentage")) - Defaults.Toggle("Show power status icons", key: .showPowerStatusIcons) - .settingsHighlight(id: highlightID("Show power status icons")) + Defaults.Toggle(key: .showBatteryPercentage) { + Text("Show battery percentage") + } + .settingsHighlight(id: highlightID("Show battery percentage")) + Defaults.Toggle(key: .showPowerStatusIcons) { + Text("Show power status icons") + } + .settingsHighlight(id: highlightID("Show power status icons")) } header: { Text("Battery Information") } @@ -1327,13 +1365,15 @@ struct Downloads: View { var body: some View { SwiftUI.Form { Section { - Defaults.Toggle("Enable download detection", key: .enableDownloadListener) - .settingsHighlight(id: highlightID("Enable download detection")) + Defaults.Toggle(key: .enableDownloadListener) { + Text("Enable download detection") + } + .settingsHighlight(id: highlightID("Enable download detection")) VStack(alignment: .leading, spacing: 12) { Text("Download indicator style") .font(.system(size: 13, weight: .semibold)) .foregroundStyle(.white) - + HStack(spacing: 16) { DownloadStyleButton( style: .progress, @@ -1342,7 +1382,7 @@ struct Downloads: View { ) { selectedDownloadIndicatorStyle = .progress } - + DownloadStyleButton( style: .circle, isSelected: selectedDownloadIndicatorStyle == .circle, @@ -1361,15 +1401,15 @@ struct Downloads: View { } .navigationTitle("Downloads") } - + struct DownloadStyleButton: View { let style: DownloadIndicatorStyle let isSelected: Bool let disabled: Bool let action: () -> Void - + @State private var isHovering = false - + var body: some View { VStack(spacing: 8) { ZStack { @@ -1379,7 +1419,7 @@ struct Downloads: View { RoundedRectangle(cornerRadius: 12) .strokeBorder(borderColor, lineWidth: isSelected ? 2 : 1) ) - + if style == .progress { ProgressView() .progressViewStyle(.linear) @@ -1397,7 +1437,7 @@ struct Downloads: View { } } } - + Text(style.rawValue) .font(.caption) .fontWeight(.medium) @@ -1414,14 +1454,14 @@ struct Downloads: View { } .opacity(disabled ? 0.5 : 1.0) } - + private var backgroundColor: Color { if disabled { return Color(nsColor: .controlBackgroundColor) } if isSelected { return Color.accentColor.opacity(0.1) } if isHovering { return Color.primary.opacity(0.05) } return Color(nsColor: .controlBackgroundColor) } - + private var borderColor: Color { if isSelected { return Color.accentColor } if isHovering { return Color.primary.opacity(0.1) } @@ -1433,19 +1473,19 @@ struct Downloads: View { final class HUDPreviewViewModel: ObservableObject { @Published var level: Float = 0 @Published var iconName: String = "speaker.wave.3.fill" - + private var cancellables = Set() - + init() { setup() } - + private func setup() { // Ensure controllers are active SystemVolumeController.shared.start() SystemBrightnessController.shared.start() SystemKeyboardBacklightController.shared.start() - + // Initial state from volume let vol = SystemVolumeController.shared.currentVolume self.level = vol @@ -1453,7 +1493,7 @@ final class HUDPreviewViewModel: ObservableObject { else if vol < 0.33 { self.iconName = "speaker.wave.1.fill" } else if vol < 0.66 { self.iconName = "speaker.wave.2.fill" } else { self.iconName = "speaker.wave.3.fill" } - + // Listeners NotificationCenter.default.publisher(for: .systemVolumeDidChange) .compactMap { $0.userInfo } @@ -1469,7 +1509,7 @@ final class HUDPreviewViewModel: ObservableObject { } } .store(in: &cancellables) - + NotificationCenter.default.publisher(for: .systemBrightnessDidChange) .compactMap { $0.userInfo } .receive(on: DispatchQueue.main) @@ -1481,7 +1521,7 @@ final class HUDPreviewViewModel: ObservableObject { } } .store(in: &cancellables) - + NotificationCenter.default.publisher(for: .keyboardBacklightDidChange) .compactMap { $0.userInfo } .receive(on: DispatchQueue.main) @@ -1522,7 +1562,7 @@ private struct HUDAndOSDSettingsView: View { @Default(.verticalHUDMaterial) var verticalHUDMaterial @Default(.verticalHUDLiquidGlassCustomizationMode) var verticalHUDLiquidGlassCustomizationMode @Default(.verticalHUDLiquidGlassVariant) var verticalHUDLiquidGlassVariant - + // Circular HUD Props @Default(.circularHUDShowValue) var circularHUDShowValue @Default(.circularHUDSize) var circularHUDSize @@ -1536,7 +1576,7 @@ private struct HUDAndOSDSettingsView: View { case osd = "Custom OSD" case vertical = "Vertical Bar" case circular = "Circular" - + var id: String { rawValue } } @@ -1590,7 +1630,7 @@ private struct HUDAndOSDSettingsView: View { .font(.system(size: 9, weight: .semibold)) .foregroundStyle(.white) .frame(width: 12) - + GeometryReader { geo in Capsule() .fill(Color.white.opacity(0.2)) @@ -1607,7 +1647,7 @@ private struct HUDAndOSDSettingsView: View { } } } - + HUDSelectionCard( title: "Custom OSD", isSelected: selectedTab == .osd, @@ -1619,7 +1659,7 @@ private struct HUDAndOSDSettingsView: View { enableCircularHUD = false } ) { - RoundedRectangle(cornerRadius: 12) + RoundedRectangle(cornerRadius: 12) .fill(.ultraThinMaterial) .overlay { RoundedRectangle(cornerRadius: 12) @@ -1633,7 +1673,7 @@ private struct HUDAndOSDSettingsView: View { .foregroundStyle(.secondary) .symbolRenderingMode(.hierarchical) .contentTransition(.symbolEffect(.replace)) - + GeometryReader { geo in Capsule() .fill(Color.secondary.opacity(0.2)) @@ -1649,7 +1689,7 @@ private struct HUDAndOSDSettingsView: View { } .frame(width: 44, height: 44) } - + HUDSelectionCard( title: "Vertical Bar", isSelected: selectedTab == .vertical, @@ -1662,35 +1702,35 @@ private struct HUDAndOSDSettingsView: View { } ) { RoundedRectangle(cornerRadius: 8) - .fill(.ultraThinMaterial) - .overlay { - RoundedRectangle(cornerRadius: 8) - .stroke(.white.opacity(0.1), lineWidth: 1) - } - .shadow(color: .black.opacity(0.1), radius: 3, y: 1) - .overlay { - VStack { - GeometryReader { geo in - VStack { - Spacer() - RoundedRectangle(cornerRadius: 6, style: .continuous) - .fill(Color.white) - .frame(height: max(0, geo.size.height * CGFloat(previewModel.level))) - .animation(.spring(response: 0.3), value: previewModel.level) - } - } - .mask(RoundedRectangle(cornerRadius: 6, style: .continuous)) - .padding(.bottom, 2) - - Image(systemName: previewModel.iconName) - .font(.system(size: 9)) - .foregroundStyle(previewModel.level > 0.15 ? .black : .secondary) - .symbolRenderingMode(.hierarchical) - .contentTransition(.symbolEffect(.replace)) - } - .padding(4) - } - .frame(width: 22, height: 54) + .fill(.ultraThinMaterial) + .overlay { + RoundedRectangle(cornerRadius: 8) + .stroke(.white.opacity(0.1), lineWidth: 1) + } + .shadow(color: .black.opacity(0.1), radius: 3, y: 1) + .overlay { + VStack { + GeometryReader { geo in + VStack { + Spacer() + RoundedRectangle(cornerRadius: 6, style: .continuous) + .fill(Color.white) + .frame(height: max(0, geo.size.height * CGFloat(previewModel.level))) + .animation(.spring(response: 0.3), value: previewModel.level) + } + } + .mask(RoundedRectangle(cornerRadius: 6, style: .continuous)) + .padding(.bottom, 2) + + Image(systemName: previewModel.iconName) + .font(.system(size: 9)) + .foregroundStyle(previewModel.level > 0.15 ? .black : .secondary) + .symbolRenderingMode(.hierarchical) + .contentTransition(.symbolEffect(.replace)) + } + .padding(4) + } + .frame(width: 22, height: 54) } HUDSelectionCard( @@ -1727,17 +1767,17 @@ private struct HUDAndOSDSettingsView: View { case .hud: HUD() case .osd: - if #available(macOS 15.0, *) { + if #available(macOS 15.0, *) { CustomOSDSettings() } else { - VStack(spacing: 16) { + VStack(spacing: 16) { Image(systemName: "exclamationmark.triangle.fill") .font(.system(size: 48)) .foregroundStyle(.orange) - + Text("macOS 15 or later required") .font(.headline) - + Text("Custom OSD feature requires macOS 15 or later.") .font(.subheadline) .foregroundStyle(.secondary) @@ -1779,7 +1819,7 @@ private struct HUDAndOSDSettingsView: View { .font(.caption) } } - + Section { Toggle("Show Percentage", isOn: $verticalHUDShowValue) Toggle("Use Accent Color", isOn: $verticalHUDUseAccentColor) @@ -1817,21 +1857,25 @@ private struct HUDAndOSDSettingsView: View { .foregroundStyle(.secondary) } } - Defaults.Toggle("Color-coded Volume", key: .useColorCodedVolumeDisplay) + Defaults.Toggle(key: .useColorCodedVolumeDisplay) { + Text("Color-coded Volume") + } if Defaults[.useColorCodedVolumeDisplay] { - Defaults.Toggle("Smooth color transitions", key: .useSmoothColorGradient) + Defaults.Toggle(key: .useSmoothColorGradient) { + Text("Smooth color transitions") + } } } header: { Text("Behavior & Style") } - + Section { Picker("HUD Position", selection: $verticalHUDPosition) { Text("Left").tag("left") Text("Right").tag("right") } .pickerStyle(.menu) - + VStack(alignment: .leading) { Text("Screen Padding: \(Int(verticalHUDPadding))px") Slider(value: $verticalHUDPadding, in: 0...100, step: 4) @@ -1843,7 +1887,7 @@ private struct HUDAndOSDSettingsView: View { .foregroundStyle(.secondary) .font(.caption) } - + Section { VStack(alignment: .leading) { Text("Width: \(Int(verticalHUDWidth))px") @@ -1896,18 +1940,22 @@ private struct HUDAndOSDSettingsView: View { .font(.caption) } } - + Section { Toggle("Show Percentage", isOn: $circularHUDShowValue) Toggle("Use Accent Color", isOn: $circularHUDUseAccentColor) - Defaults.Toggle("Color-coded Volume", key: .useColorCodedVolumeDisplay) + Defaults.Toggle(key: .useColorCodedVolumeDisplay) { + Text("Color-coded Volume") + } if Defaults[.useColorCodedVolumeDisplay] { - Defaults.Toggle("Smooth color transitions", key: .useSmoothColorGradient) + Defaults.Toggle(key: .useSmoothColorGradient) { + Text("Smooth color transitions") + } } } header: { Text("Style") } - + Section { VStack(alignment: .leading) { Text("Size: \(Int(circularHUDSize))px") @@ -2081,9 +2129,9 @@ private struct HUDSelectionCard: View { let isSelected: Bool let action: () -> Void @ViewBuilder let preview: Preview - + @State private var isHovering = false - + var body: some View { Button(action: action) { VStack(spacing: 12) { @@ -2098,16 +2146,16 @@ private struct HUDSelectionCard: View { ) ) .shadow(color: .black.opacity(0.05), radius: 2, y: 1) - + preview } .frame(width: 110, height: 80) - + VStack(spacing: 4) { Text(title) .font(.system(size: 13, weight: .medium)) .foregroundStyle(isSelected ? .primary : .secondary) - + if isSelected { Circle() .fill(Color.accentColor) @@ -2144,14 +2192,22 @@ private struct DevicesSettingsView: View { var body: some View { Form { Section { - Defaults.Toggle("Show Bluetooth device connections", key: .showBluetoothDeviceConnections) - .settingsHighlight(id: highlightID("Show Bluetooth device connections")) - Defaults.Toggle("Use circular battery indicator", key: .useCircularBluetoothBatteryIndicator) - .settingsHighlight(id: highlightID("Use circular battery indicator")) - Defaults.Toggle("Show battery percentage text in HUD", key: .showBluetoothBatteryPercentageText) - .settingsHighlight(id: highlightID("Show battery percentage text in HUD")) - Defaults.Toggle("Scroll device name in HUD", key: .showBluetoothDeviceNameMarquee) - .settingsHighlight(id: highlightID("Scroll device name in HUD")) + Defaults.Toggle(key: .showBluetoothDeviceConnections) { + Text("Show Bluetooth device connections") + } + .settingsHighlight(id: highlightID("Show Bluetooth device connections")) + Defaults.Toggle(key: .useCircularBluetoothBatteryIndicator) { + Text("Use circular battery indicator") + } + .settingsHighlight(id: highlightID("Use circular battery indicator")) + Defaults.Toggle(key: .showBluetoothBatteryPercentageText) { + Text("Show battery percentage text in HUD") + } + .settingsHighlight(id: highlightID("Show battery percentage text in HUD")) + Defaults.Toggle(key: .showBluetoothDeviceNameMarquee) { + Text("Scroll device name in HUD") + } + .settingsHighlight(id: highlightID("Scroll device name in HUD")) VStack(alignment: .leading, spacing: 12) { Text("HUD icon style") .font(.system(size: 13, weight: .semibold)) @@ -2184,9 +2240,11 @@ private struct DevicesSettingsView: View { } Section { - Defaults.Toggle("Color-coded battery display", key: .useColorCodedBatteryDisplay) - .disabled(colorCodingDisabled) - .settingsHighlight(id: highlightID("Color-coded battery display")) + Defaults.Toggle(key: .useColorCodedBatteryDisplay) { + Text("Color-coded battery display") + } + .disabled(colorCodingDisabled) + .settingsHighlight(id: highlightID("Color-coded battery display")) } header: { Text("Battery Indicator Styling") } footer: { @@ -2233,7 +2291,7 @@ struct HUD: View { private var colorCodingDisabled: Bool { progressBarStyle == .segmented } - + var body: some View { Form { if !hasAccessibilityPermission && !enableThirdPartyDDCIntegration { @@ -2249,7 +2307,7 @@ struct HUD: View { } - + if enableSystemHUD && !Defaults[.enableCustomOSD] && (hasAccessibilityPermission || enableThirdPartyDDCIntegration) { Section { Toggle("Volume HUD", isOn: $enableVolumeHUD) @@ -2267,9 +2325,11 @@ struct HUD: View { } Section { - Defaults.Toggle("Play feedback when volume is changed", key: .playVolumeChangeFeedback) - .settingsHighlight(id: highlightID("Play feedback when volume is changed")) - .help("Plays the supplied feedback clip whenever you press the hardware volume keys.") + Defaults.Toggle(key: .playVolumeChangeFeedback) { + Text("Play feedback when volume is changed") + } + .settingsHighlight(id: highlightID("Play feedback when volume is changed")) + .help("Plays the supplied feedback clip whenever you press the hardware volume keys.") } header: { Text("Audio feedback") } footer: { @@ -2279,17 +2339,23 @@ struct HUD: View { } Section { - Defaults.Toggle("Color-coded volume display", key: .useColorCodedVolumeDisplay) - .disabled(colorCodingDisabled) - .settingsHighlight(id: highlightID("Color-coded volume display")) + Defaults.Toggle(key: .useColorCodedVolumeDisplay) { + Text("Color-coded volume display") + } + .disabled(colorCodingDisabled) + .settingsHighlight(id: highlightID("Color-coded volume display")) if !colorCodingDisabled && (Defaults[.useColorCodedBatteryDisplay] || Defaults[.useColorCodedVolumeDisplay]) { - Defaults.Toggle("Smooth color transitions", key: .useSmoothColorGradient) - .settingsHighlight(id: highlightID("Smooth color transitions")) + Defaults.Toggle(key: .useSmoothColorGradient) { + Text("Smooth color transitions") + } + .settingsHighlight(id: highlightID("Smooth color transitions")) } - Defaults.Toggle("Show percentages beside progress bars", key: .showProgressPercentages) - .settingsHighlight(id: highlightID("Show percentages beside progress bars")) + Defaults.Toggle(key: .showProgressPercentages) { + Text("Show percentages beside progress bars") + } + .settingsHighlight(id: highlightID("Show percentages beside progress bars")) } header: { Text("Dynamic Island Progress Bars") } footer: { @@ -2307,7 +2373,7 @@ struct HUD: View { .font(.caption) } } - + Section { Picker("HUD style", selection: $inlineHUD) { Text("Default") @@ -2333,10 +2399,14 @@ struct HUD: View { .tag(ProgressBarStyle.segmented) } .settingsHighlight(id: highlightID("Progressbar style")) - Defaults.Toggle("Enable glowing effect", key: .systemEventIndicatorShadow) - .settingsHighlight(id: highlightID("Enable glowing effect")) - Defaults.Toggle("Use accent color", key: .systemEventIndicatorUseAccent) - .settingsHighlight(id: highlightID("Use accent color")) + Defaults.Toggle(key: .systemEventIndicatorShadow) { + Text("Enable glowing effect") + } + .settingsHighlight(id: highlightID("Enable glowing effect")) + Defaults.Toggle(key: .systemEventIndicatorUseAccent) { + Text("Use accent color") + } + .settingsHighlight(id: highlightID("Use accent color")) } header: { HStack { Text("Appearance") @@ -2416,13 +2486,17 @@ struct Media: View { } } Section { - Defaults.Toggle("Show media controls in Dynamic Island", key: .showStandardMediaControls) - .disabled(enableMinimalisticUI) - .settingsHighlight(id: highlightID("Show media controls in Dynamic Island")) + Defaults.Toggle(key: .showStandardMediaControls) { + Text("Show media controls in Dynamic Island") + } + .disabled(enableMinimalisticUI) + .settingsHighlight(id: highlightID("Show media controls in Dynamic Island")) - Defaults.Toggle("Auto-hide inactive notch media player", key: .autoHideInactiveNotchMediaPlayer) - .disabled(enableMinimalisticUI || !showStandardMediaControls) - .settingsHighlight(id: highlightID("Auto-hide inactive notch media player")) + Defaults.Toggle(key: .autoHideInactiveNotchMediaPlayer) { + Text("Auto-hide inactive notch media player") + } + .disabled(enableMinimalisticUI || !showStandardMediaControls) + .settingsHighlight(id: highlightID("Auto-hide inactive notch media player")) if enableMinimalisticUI { Text("Disable Minimalistic UI to configure the standard notch media controls.") @@ -2463,8 +2537,10 @@ struct Media: View { } Section(header: Text("Lock Screen Media")) { - Defaults.Toggle("Enable album art parallax", key: .lockScreenMusicAlbumParallaxEnabled) - .settingsHighlight(id: highlightID("Enable album art parallax")) + Defaults.Toggle(key: .lockScreenMusicAlbumParallaxEnabled) { + Text("Enable album art parallax") + } + .settingsHighlight(id: highlightID("Enable album art parallax")) Text("Applies the notch-style parallax effect to the lock screen media widget album art.") .font(.caption) .foregroundStyle(.secondary) @@ -2502,10 +2578,14 @@ struct Media: View { Toggle("Enable sneak peek", isOn: $enableSneakPeek) Toggle("Show sneak peek on playback changes", isOn: $showSneakPeekOnTrackChange) .disabled(!enableSneakPeek) - Defaults.Toggle("Enable lyrics", key: .enableLyrics) - .settingsHighlight(id: highlightID("Enable lyrics")) - Defaults.Toggle("Enable album art parallax effect", key: .enableParallaxEffect) - .settingsHighlight(id: highlightID("Enable album art parallax effect")) + Defaults.Toggle(key: .enableLyrics) { + Text("Enable lyrics") + } + .settingsHighlight(id: highlightID("Enable lyrics")) + Defaults.Toggle(key: .enableParallaxEffect) { + Text("Enable album art parallax effect") + } + .settingsHighlight(id: highlightID("Enable album art parallax effect")) Picker("Sneak Peek Style", selection: $sneakPeekStyles){ ForEach(SneakPeekStyle.allCases) { style in Text(style.rawValue).tag(style) @@ -2513,7 +2593,7 @@ struct Media: View { } .disabled(!enableSneakPeek) .settingsHighlight(id: highlightID("Sneak Peek Style")) - + HStack { Stepper(value: $waitInterval, in: 0...10, step: 1) { HStack { @@ -2529,19 +2609,27 @@ struct Media: View { } Section { - Defaults.Toggle("Show lock screen media panel", key: .enableLockScreenMediaWidget) - Defaults.Toggle("Show media app icon", key: .lockScreenShowAppIcon) - .disabled(!enableLockScreenMediaWidget) - Defaults.Toggle("Show panel border", key: .lockScreenPanelShowsBorder) - .disabled(!enableLockScreenMediaWidget) + Defaults.Toggle(key: .enableLockScreenMediaWidget) { + Text("Show lock screen media panel") + } + Defaults.Toggle(key: .lockScreenShowAppIcon) { + Text("Show media app icon") + } + .disabled(!enableLockScreenMediaWidget) + Defaults.Toggle(key: .lockScreenPanelShowsBorder) { + Text("Show panel border") + } + .disabled(!enableLockScreenMediaWidget) if lockScreenGlassCustomizationMode == .customLiquid { customLiquidBlurRow .opacity(enableLockScreenMediaWidget ? 1 : 0.5) .settingsHighlight(id: highlightID("Enable media panel blur")) } else if lockScreenGlassStyle == .frosted { - Defaults.Toggle("Enable media panel blur", key: .lockScreenPanelUsesBlur) - .disabled(!enableLockScreenMediaWidget) - .settingsHighlight(id: highlightID("Enable media panel blur")) + Defaults.Toggle(key: .lockScreenPanelUsesBlur) { + Text("Enable media panel blur") + } + .disabled(!enableLockScreenMediaWidget) + .settingsHighlight(id: highlightID("Enable media panel blur")) } else { unavailableBlurRow .opacity(enableLockScreenMediaWidget ? 1 : 0.5) @@ -2556,17 +2644,17 @@ struct Media: View { .opacity(showStandardMediaControls ? 1 : 0.5) Picker(selection: $hideNotchOption, label: - HStack { - Text("Hide DynamicIsland Options") - customBadge(text: "Beta") - }) { - Text("Always hide in fullscreen").tag(HideNotchOption.always) - Text("Hide only when NowPlaying app is in fullscreen").tag(HideNotchOption.nowPlayingOnly) - Text("Never hide").tag(HideNotchOption.never) - } - .onChange(of: hideNotchOption) { - Defaults[.enableFullscreenMediaDetection] = hideNotchOption != .never - } + HStack { + Text("Hide DynamicIsland Options") + customBadge(text: "Beta") + }) { + Text("Always hide in fullscreen").tag(HideNotchOption.always) + Text("Hide only when NowPlaying app is in fullscreen").tag(HideNotchOption.nowPlayingOnly) + Text("Never hide").tag(HideNotchOption.never) + } + .onChange(of: hideNotchOption) { + Defaults[.enableFullscreenMediaDetection] = hideNotchOption != .never + } } .navigationTitle("Media") } @@ -2663,7 +2751,7 @@ struct CalendarSettings: View { .foregroundColor(.red) .multilineTextAlignment(.center) .padding() - + HStack { Button("Request Access") { Task { @@ -2672,7 +2760,7 @@ struct CalendarSettings: View { } } .buttonStyle(.borderedProminent) - + Button("Open System Settings") { if let settingsURL = URL(string: "x-apple.systempreferences:com.apple.preference.security?Privacy_Calendars") { NSWorkspace.shared.open(settingsURL) @@ -2695,9 +2783,11 @@ struct CalendarSettings: View { .foregroundColor(color(for: calendarManager.reminderAuthorizationStatus)) } } - - Defaults.Toggle("Show calendar", key: .showCalendar) - .settingsHighlight(id: highlightID("Show calendar")) + + Defaults.Toggle(key: .showCalendar) { + Text("Show calendar") + } + .settingsHighlight(id: highlightID("Show calendar")) Section(header: Text("Event List")) { Toggle("Hide completed reminders", isOn: $hideCompletedReminders) @@ -2718,10 +2808,12 @@ struct CalendarSettings: View { .foregroundStyle(.secondary) .fixedSize(horizontal: false, vertical: true) } - + Section(header: Text("Reminder Live Activity")) { - Defaults.Toggle("Enable reminder live activity", key: .enableReminderLiveActivity) - .settingsHighlight(id: highlightID("Enable reminder live activity")) + Defaults.Toggle(key: .enableReminderLiveActivity) { + Text("Enable reminder live activity") + } + .settingsHighlight(id: highlightID("Enable reminder live activity")) Picker("Countdown style", selection: $reminderPresentationStyle) { ForEach(ReminderPresentationStyle.allCases) { style in @@ -2763,8 +2855,10 @@ struct CalendarSettings: View { } Section(header: Text("Lock Screen Reminder Widget")) { - Defaults.Toggle("Show lock screen reminder", key: .enableLockScreenReminderWidget) - .settingsHighlight(id: highlightID("Show lock screen reminder")) + Defaults.Toggle(key: .enableLockScreenReminderWidget) { + Text("Show lock screen reminder") + } + .settingsHighlight(id: highlightID("Show lock screen reminder")) Picker("Chip color", selection: $lockScreenReminderChipStyle) { ForEach(LockScreenReminderChipStyle.allCases) { style in @@ -2782,8 +2876,10 @@ struct CalendarSettings: View { .foregroundStyle(.secondary) .font(.caption) ) { - Defaults.Toggle("Show next calendar event", key: .lockScreenShowCalendarEvent) - .settingsHighlight(id: highlightID("Show next calendar event")) + Defaults.Toggle(key: .lockScreenShowCalendarEvent) { + Text("Show next calendar event") + } + .settingsHighlight(id: highlightID("Show next calendar event")) LabeledContent("Show events within the next") { HStack { @@ -2851,18 +2947,22 @@ struct CalendarSettings: View { .disabled(!lockScreenShowCalendarEvent) } - Defaults.Toggle("Show countdown", key: .lockScreenShowCalendarCountdown) - .disabled(!lockScreenShowCalendarEvent) - .settingsHighlight(id: highlightID("Show countdown")) + Defaults.Toggle(key: .lockScreenShowCalendarCountdown) { + Text("Show countdown") + } + .disabled(!lockScreenShowCalendarEvent) + .settingsHighlight(id: highlightID("Show countdown")) - Defaults.Toggle("Show event for entire duration", key: .lockScreenShowCalendarEventEntireDuration) - .disabled(!lockScreenShowCalendarEvent) - .settingsHighlight(id: highlightID("Show event for entire duration")) - .onChange(of: Defaults[.lockScreenShowCalendarEventEntireDuration]) { _, newValue in - if newValue { - Defaults[.lockScreenShowCalendarEventAfterStartEnabled] = false - } + Defaults.Toggle(key: .lockScreenShowCalendarEventEntireDuration) { + Text("Show event for entire duration") + } + .disabled(!lockScreenShowCalendarEvent) + .settingsHighlight(id: highlightID("Show event for entire duration")) + .onChange(of: Defaults[.lockScreenShowCalendarEventEntireDuration]) { _, newValue in + if newValue { + Defaults[.lockScreenShowCalendarEventAfterStartEnabled] = false } + } Defaults.Toggle( "Hide active event and show next upcoming event", @@ -2895,13 +2995,17 @@ struct CalendarSettings: View { .font(.caption) .foregroundStyle(.secondary) - Defaults.Toggle("Show time remaining", key: .lockScreenShowCalendarTimeRemaining) - .disabled(!lockScreenShowCalendarEvent) - .settingsHighlight(id: highlightID("Show time remaining")) + Defaults.Toggle(key: .lockScreenShowCalendarTimeRemaining) { + Text("Show time remaining") + } + .disabled(!lockScreenShowCalendarEvent) + .settingsHighlight(id: highlightID("Show time remaining")) - Defaults.Toggle("Show start time after event begins", key: .lockScreenShowCalendarStartTimeAfterBegins) - .disabled(!lockScreenShowCalendarEvent) - .settingsHighlight(id: highlightID("Show start time after event begins")) + Defaults.Toggle(key: .lockScreenShowCalendarStartTimeAfterBegins) { + Text("Show start time after event begins") + } + .disabled(!lockScreenShowCalendarEvent) + .settingsHighlight(id: highlightID("Show start time after event begins")) } Section(header: Text("Select Calendars")) { @@ -2959,7 +3063,7 @@ struct CalendarSettings: View { } .navigationTitle("Calendar") } - + private func statusText(for status: EKAuthorizationStatus) -> String { switch status { case .fullAccess, .authorized: return "Full Access" @@ -2970,7 +3074,7 @@ struct CalendarSettings: View { @unknown default: return "Unknown" } } - + private func color(for status: EKAuthorizationStatus) -> Color { switch status { case .fullAccess, .authorized: return .green @@ -3066,10 +3170,10 @@ struct About: View { .background(.regularMaterial) } .toolbar { -// Button("Welcome window") { -// openWindow(id: "onboarding") -// } -// .controlSize(.extraLarge) + // Button("Welcome window") { + // openWindow(id: "onboarding") + // } + // .controlSize(.extraLarge) CheckForUpdatesView(updater: updaterController.updater) } .navigationTitle("About") @@ -3285,12 +3389,16 @@ struct Shelf: View { } Section { - Defaults.Toggle("Enable shelf", key: .dynamicShelf) - .disabled(!canEnableShelf) - .settingsHighlight(id: highlightID("Enable shelf")) + Defaults.Toggle(key: .dynamicShelf) { + Text("Enable shelf") + } + .disabled(!canEnableShelf) + .settingsHighlight(id: highlightID("Enable shelf")) - Defaults.Toggle("Open shelf tab by default if items added", key: .openShelfByDefault) - .settingsHighlight(id: highlightID("Open shelf tab by default if items added")) + Defaults.Toggle(key: .openShelfByDefault) { + Text("Open shelf tab by default if items added") + } + .settingsHighlight(id: highlightID("Open shelf tab by default if items added")) Defaults.Toggle(key: .expandedDragDetection) { Text("Expanded drag detection area") @@ -3397,12 +3505,16 @@ struct LiveActivitiesSettings: View { var body: some View { Form { Section { - Defaults.Toggle("Enable Screen Recording Detection", key: .enableScreenRecordingDetection) - .settingsHighlight(id: highlightID("Enable Screen Recording Detection")) + Defaults.Toggle(key: .enableScreenRecordingDetection) { + Text("Enable Screen Recording Detection") + } + .settingsHighlight(id: highlightID("Enable Screen Recording Detection")) - Defaults.Toggle("Show Recording Indicator", key: .showRecordingIndicator) - .disabled(!enableScreenRecordingDetection) - .settingsHighlight(id: highlightID("Show Recording Indicator")) + Defaults.Toggle(key: .showRecordingIndicator) { + Text("Show Recording Indicator") + } + .disabled(!enableScreenRecordingDetection) + .settingsHighlight(id: highlightID("Show Recording Indicator")) if recordingManager.isMonitoring { HStack { @@ -3442,22 +3554,30 @@ struct LiveActivitiesSettings: View { ) } - Defaults.Toggle("Enable Focus Detection", key: .enableDoNotDisturbDetection) - .settingsHighlight(id: highlightID("Enable Focus Detection")) + Defaults.Toggle(key: .enableDoNotDisturbDetection) { + Text("Enable Focus Detection") + } + .settingsHighlight(id: highlightID("Enable Focus Detection")) - Defaults.Toggle("Show Focus Indicator", key: .showDoNotDisturbIndicator) - .disabled(!enableDoNotDisturbDetection) - .settingsHighlight(id: highlightID("Show Focus Indicator")) + Defaults.Toggle(key: .showDoNotDisturbIndicator) { + Text("Show Focus Indicator") + } + .disabled(!enableDoNotDisturbDetection) + .settingsHighlight(id: highlightID("Show Focus Indicator")) - Defaults.Toggle("Show Focus Label", key: .showDoNotDisturbLabel) - .disabled(!enableDoNotDisturbDetection || focusIndicatorNonPersistent) - .help(focusIndicatorNonPersistent ? "Labels are forced to compact on/off text while brief toast mode is enabled." : "Show the active Focus name inside the indicator.") - .settingsHighlight(id: highlightID("Show Focus Label")) + Defaults.Toggle(key: .showDoNotDisturbLabel) { + Text("Show Focus Label") + } + .disabled(!enableDoNotDisturbDetection || focusIndicatorNonPersistent) + .help(focusIndicatorNonPersistent ? "Labels are forced to compact on/off text while brief toast mode is enabled." : "Show the active Focus name inside the indicator.") + .settingsHighlight(id: highlightID("Show Focus Label")) - Defaults.Toggle("Show Focus as brief toast", key: .focusIndicatorNonPersistent) - .disabled(!enableDoNotDisturbDetection) - .settingsHighlight(id: highlightID("Show Focus as brief toast")) - .help("When enabled, Focus appears briefly (on/off) and then collapses instead of staying visible.") + Defaults.Toggle(key: .focusIndicatorNonPersistent) { + Text("Show Focus as brief toast") + } + .disabled(!enableDoNotDisturbDetection) + .settingsHighlight(id: highlightID("Show Focus as brief toast")) + .help("When enabled, Focus appears briefly (on/off) and then collapses instead of staying visible.") if doNotDisturbManager.isMonitoring { HStack { @@ -3491,12 +3611,16 @@ struct LiveActivitiesSettings: View { } Section { - Defaults.Toggle("Show Caps Lock Indicator", key: .enableCapsLockIndicator) - .settingsHighlight(id: highlightID("Show Caps Lock Indicator")) + Defaults.Toggle(key: .enableCapsLockIndicator) { + Text("Show Caps Lock Indicator") + } + .settingsHighlight(id: highlightID("Show Caps Lock Indicator")) - Defaults.Toggle("Show Caps Lock label", key: .showCapsLockLabel) - .disabled(!Defaults[.enableCapsLockIndicator]) - .settingsHighlight(id: highlightID("Show Caps Lock label")) + Defaults.Toggle(key: .showCapsLockLabel) { + Text("Show Caps Lock label") + } + .disabled(!Defaults[.enableCapsLockIndicator]) + .settingsHighlight(id: highlightID("Show Caps Lock label")) Picker("Caps Lock color", selection: $capsLockTintMode) { ForEach(CapsLockIndicatorTintMode.allCases) { mode in @@ -3513,10 +3637,14 @@ struct LiveActivitiesSettings: View { } Section { - Defaults.Toggle("Enable Camera Detection", key: .enableCameraDetection) - .settingsHighlight(id: highlightID("Enable Camera Detection")) - Defaults.Toggle("Enable Microphone Detection", key: .enableMicrophoneDetection) - .settingsHighlight(id: highlightID("Enable Microphone Detection")) + Defaults.Toggle(key: .enableCameraDetection) { + Text("Enable Camera Detection") + } + .settingsHighlight(id: highlightID("Enable Camera Detection")) + Defaults.Toggle(key: .enableMicrophoneDetection) { + Text("Enable Microphone Detection") + } + .settingsHighlight(id: highlightID("Enable Microphone Detection")) if privacyManager.isMonitoring { HStack { @@ -3572,8 +3700,10 @@ struct LiveActivitiesSettings: View { } Section { - Defaults.Toggle("Enable reminder live activity", key: .enableReminderLiveActivity) - .settingsHighlight(id: highlightID("Enable reminder live activity")) + Defaults.Toggle(key: .enableReminderLiveActivity) { + Text("Enable reminder live activity") + } + .settingsHighlight(id: highlightID("Enable reminder live activity")) } header: { Text("Reminder Live Activity") } footer: { @@ -3673,14 +3803,22 @@ struct Appearance: View { Form { Section { Toggle("Always show tabs", isOn: $coordinator.alwaysShowTabs) - Defaults.Toggle("Settings icon in notch", key: .settingsIconInNotch) - .settingsHighlight(id: highlightID("Settings icon in notch")) - Defaults.Toggle("Enable window shadow", key: .enableShadow) - .settingsHighlight(id: highlightID("Enable window shadow")) - Defaults.Toggle("Corner radius scaling", key: .cornerRadiusScaling) - .settingsHighlight(id: highlightID("Corner radius scaling")) - Defaults.Toggle("Use simpler close animation", key: .useModernCloseAnimation) - .settingsHighlight(id: highlightID("Use simpler close animation")) + Defaults.Toggle(key: .settingsIconInNotch) { + Text("Settings icon in notch") + } + .settingsHighlight(id: highlightID("Settings icon in notch")) + Defaults.Toggle(key: .enableShadow) { + Text("Enable window shadow") + } + .settingsHighlight(id: highlightID("Enable window shadow")) + Defaults.Toggle(key: .cornerRadiusScaling) { + Text("Corner radius scaling") + } + .settingsHighlight(id: highlightID("Corner radius scaling")) + Defaults.Toggle(key: .useModernCloseAnimation) { + Text("Use simpler close animation") + } + .settingsHighlight(id: highlightID("Use simpler close animation")) } header: { Text("General") } @@ -3786,12 +3924,16 @@ struct Appearance: View { } Section { - Defaults.Toggle("Enable colored spectrograms", key: .coloredSpectrogram) - .settingsHighlight(id: highlightID("Enable colored spectrograms")) + Defaults.Toggle(key: .coloredSpectrogram) { + Text("Enable colored spectrograms") + } + .settingsHighlight(id: highlightID("Enable colored spectrograms")) Defaults .Toggle("Player tinting", key: .playerColorTinting) - Defaults.Toggle("Enable blur effect behind album art", key: .lightingEffect) - .settingsHighlight(id: highlightID("Enable blur effect behind album art")) + Defaults.Toggle(key: .lightingEffect) { + Text("Enable blur effect behind album art") + } + .settingsHighlight(id: highlightID("Enable blur effect behind album art")) Picker("Slider color", selection: $sliderColor) { ForEach(SliderColorEnum.allCases, id: \.self) { option in Text(option.rawValue) @@ -3971,9 +4113,11 @@ struct Appearance: View { } Section { - Defaults.Toggle("Enable Dynamic mirror", key: .showMirror) - .disabled(!checkVideoInput()) - .settingsHighlight(id: highlightID("Enable Dynamic mirror")) + Defaults.Toggle(key: .showMirror) { + Text("Enable Dynamic mirror") + } + .disabled(!checkVideoInput()) + .settingsHighlight(id: highlightID("Enable Dynamic mirror")) Picker("Mirror shape", selection: $mirrorShape) { Text("Circle") .tag(MirrorShapeEnum.circle) @@ -3981,14 +4125,16 @@ struct Appearance: View { .tag(MirrorShapeEnum.rectangle) } .settingsHighlight(id: highlightID("Mirror shape")) - Defaults.Toggle("Idle Animation", key: .showNotHumanFace) - .settingsHighlight(id: highlightID("Idle Animation")) + Defaults.Toggle(key: .showNotHumanFace) { + Text("Idle Animation") + } + .settingsHighlight(id: highlightID("Idle Animation")) } header: { HStack { Text("Additional features") } } - + // MARK: - Custom Idle Animations Section IdleAnimationsSettingsSection() @@ -4247,8 +4393,8 @@ struct Appearance: View { } let description = enableMinimalisticUI - ? "Width adjustments apply only to the standard notch layout. Disable Minimalistic UI to edit this value." - : "Recommended minimum width adjusts automatically based on the number of enabled tabs." + ? "Width adjustments apply only to the standard notch layout. Disable Minimalistic UI to edit this value." + : "Recommended minimum width adjusts automatically based on the number of enabled tabs." Text(description) .font(.caption) @@ -4393,10 +4539,14 @@ struct LockScreenSettings: View { var body: some View { Form { Section { - Defaults.Toggle("Enable lock screen live activity", key: .enableLockScreenLiveActivity) - .settingsHighlight(id: highlightID("Enable lock screen live activity")) - Defaults.Toggle("Play lock/unlock sounds", key: .enableLockSounds) - .settingsHighlight(id: highlightID("Play lock/unlock sounds")) + Defaults.Toggle(key: .enableLockScreenLiveActivity) { + Text("Enable lock screen live activity") + } + .settingsHighlight(id: highlightID("Enable lock screen live activity")) + Defaults.Toggle(key: .enableLockSounds) { + Text("Play lock/unlock sounds") + } + .settingsHighlight(id: highlightID("Play lock/unlock sounds")) } header: { Text("Live Activity & Feedback") } footer: { @@ -4462,14 +4612,20 @@ struct LockScreenSettings: View { } Section { - Defaults.Toggle("Show lock screen media panel", key: .enableLockScreenMediaWidget) - .settingsHighlight(id: highlightID("Show lock screen media panel")) - Defaults.Toggle("Show media app icon", key: .lockScreenShowAppIcon) - .disabled(!enableLockScreenMediaWidget) - .settingsHighlight(id: highlightID("Show media app icon")) - Defaults.Toggle("Show panel border", key: .lockScreenPanelShowsBorder) - .disabled(!enableLockScreenMediaWidget) - .settingsHighlight(id: highlightID("Show panel border")) + Defaults.Toggle(key: .enableLockScreenMediaWidget) { + Text("Show lock screen media panel") + } + .settingsHighlight(id: highlightID("Show lock screen media panel")) + Defaults.Toggle(key: .lockScreenShowAppIcon) { + Text("Show media app icon") + } + .disabled(!enableLockScreenMediaWidget) + .settingsHighlight(id: highlightID("Show media app icon")) + Defaults.Toggle(key: .lockScreenPanelShowsBorder) { + Text("Show panel border") + } + .disabled(!enableLockScreenMediaWidget) + .settingsHighlight(id: highlightID("Show panel border")) if lockScreenGlassCustomizationMode == .customLiquid { variantSlider( title: "Music panel variant", @@ -4479,9 +4635,11 @@ struct LockScreenSettings: View { highlight: highlightID("Music panel variant") ) } else if lockScreenGlassStyle == .frosted { - Defaults.Toggle("Enable media panel blur", key: .lockScreenPanelUsesBlur) - .disabled(!enableLockScreenMediaWidget) - .settingsHighlight(id: highlightID("Enable media panel blur")) + Defaults.Toggle(key: .lockScreenPanelUsesBlur) { + Text("Enable media panel blur") + } + .disabled(!enableLockScreenMediaWidget) + .settingsHighlight(id: highlightID("Enable media panel blur")) } else { blurSettingUnavailableRow .opacity(enableLockScreenMediaWidget ? 1 : 0.5) @@ -4503,8 +4661,10 @@ struct LockScreenSettings: View { .opacity(showStandardMediaControls ? 1 : 0.5) Section { - Defaults.Toggle("Show lock screen timer", key: .enableLockScreenTimerWidget) - .settingsHighlight(id: highlightID("Show lock screen timer")) + Defaults.Toggle(key: .enableLockScreenTimerWidget) { + Text("Show lock screen timer") + } + .settingsHighlight(id: highlightID("Show lock screen timer")) Picker("Timer surface", selection: timerSurfaceBinding) { ForEach(LockScreenTimerSurfaceMode.allCases) { mode in Text(mode.rawValue).tag(mode) @@ -4565,8 +4725,10 @@ struct LockScreenSettings: View { } Section { - Defaults.Toggle("Show lock screen weather", key: .enableLockScreenWeatherWidget) - .settingsHighlight(id: highlightID("Show lock screen weather")) + Defaults.Toggle(key: .enableLockScreenWeatherWidget) { + Text("Show lock screen weather") + } + .settingsHighlight(id: highlightID("Show lock screen weather")) if enableLockScreenWeatherWidget { Picker("Layout", selection: $lockScreenWeatherWidgetStyle) { @@ -4593,17 +4755,23 @@ struct LockScreenSettings: View { .pickerStyle(.segmented) .settingsHighlight(id: highlightID("Temperature unit")) - Defaults.Toggle("Show location label", key: .lockScreenWeatherShowsLocation) - .disabled(lockScreenWeatherWidgetStyle == .circular) - .settingsHighlight(id: highlightID("Show location label")) + Defaults.Toggle(key: .lockScreenWeatherShowsLocation) { + Text("Show location label") + } + .disabled(lockScreenWeatherWidgetStyle == .circular) + .settingsHighlight(id: highlightID("Show location label")) - Defaults.Toggle("Show sunrise time", key: .lockScreenWeatherShowsSunrise) - .disabled(lockScreenWeatherWidgetStyle != .inline) - .settingsHighlight(id: highlightID("Show sunrise time")) + Defaults.Toggle(key: .lockScreenWeatherShowsSunrise) { + Text("Show sunrise time") + } + .disabled(lockScreenWeatherWidgetStyle != .inline) + .settingsHighlight(id: highlightID("Show sunrise time")) - Defaults.Toggle("Show AQI widget", key: .lockScreenWeatherShowsAQI) - .disabled(!lockScreenWeatherProviderSource.supportsAirQuality) - .settingsHighlight(id: highlightID("Show AQI widget")) + Defaults.Toggle(key: .lockScreenWeatherShowsAQI) { + Text("Show AQI widget") + } + .disabled(!lockScreenWeatherProviderSource.supportsAirQuality) + .settingsHighlight(id: highlightID("Show AQI widget")) if lockScreenWeatherShowsAQI && lockScreenWeatherProviderSource.supportsAirQuality { Picker("Air quality scale", selection: $lockScreenWeatherAQIScale) { @@ -4621,8 +4789,10 @@ struct LockScreenSettings: View { .foregroundStyle(.secondary) } - Defaults.Toggle("Use colored gauges", key: .lockScreenWeatherUsesGaugeTint) - .settingsHighlight(id: highlightID("Use colored gauges")) + Defaults.Toggle(key: .lockScreenWeatherUsesGaugeTint) { + Text("Use colored gauges") + } + .settingsHighlight(id: highlightID("Use colored gauges")) } } header: { Text("Weather Widget") @@ -4631,8 +4801,10 @@ struct LockScreenSettings: View { } Section { - Defaults.Toggle("Show lock screen reminder", key: .enableLockScreenReminderWidget) - .settingsHighlight(id: highlightID("Show lock screen reminder")) + Defaults.Toggle(key: .enableLockScreenReminderWidget) { + Text("Show lock screen reminder") + } + .settingsHighlight(id: highlightID("Show lock screen reminder")) Picker("Chip color", selection: $lockScreenReminderChipStyle) { ForEach(LockScreenReminderChipStyle.allCases) { style in @@ -4670,26 +4842,36 @@ struct LockScreenSettings: View { } footer: { Text("Controls the lock screen reminder chip and its positioning.") } - + if BatteryActivityManager.shared.hasBattery() { Section { - Defaults.Toggle("Show battery indicator", key: .lockScreenBatteryShowsBatteryGauge) - .settingsHighlight(id: highlightID("Show battery indicator")) - + Defaults.Toggle(key: .lockScreenBatteryShowsBatteryGauge) { + Text("Show battery indicator") + } + .settingsHighlight(id: highlightID("Show battery indicator")) + if lockScreenWeatherShowsBatteryGauge { - Defaults.Toggle("Use MacBook icon when on battery", key: .lockScreenBatteryUsesLaptopSymbol) - .settingsHighlight(id: highlightID("Use MacBook icon when on battery")) - - Defaults.Toggle("Show charging status", key: .lockScreenBatteryShowsCharging) - .settingsHighlight(id: highlightID("Show charging status")) - - if lockScreenWeatherShowsCharging { - Defaults.Toggle("Show charging percentage", key: .lockScreenBatteryShowsChargingPercentage) - .settingsHighlight(id: highlightID("Show charging percentage")) + Defaults.Toggle(key: .lockScreenBatteryUsesLaptopSymbol) { + Text("Use MacBook icon when on battery") } - - Defaults.Toggle("Show Bluetooth battery", key: .lockScreenBatteryShowsBluetooth) - .settingsHighlight(id: highlightID("Show Bluetooth battery")) + .settingsHighlight(id: highlightID("Use MacBook icon when on battery")) + + Defaults.Toggle(key: .lockScreenBatteryShowsCharging) { + Text("Show charging status") + } + .settingsHighlight(id: highlightID("Show charging status")) + + if lockScreenWeatherShowsCharging { + Defaults.Toggle(key: .lockScreenBatteryShowsChargingPercentage) { + Text("Show charging percentage") + } + .settingsHighlight(id: highlightID("Show charging percentage")) + } + + Defaults.Toggle(key: .lockScreenBatteryShowsBluetooth) { + Text("Show Bluetooth battery") + } + .settingsHighlight(id: highlightID("Show Bluetooth battery")) } } header: { Text("Battery Widget") @@ -4699,8 +4881,10 @@ struct LockScreenSettings: View { } Section { - Defaults.Toggle("Show focus widget", key: .enableLockScreenFocusWidget) - .settingsHighlight(id: highlightID("Show focus widget")) + Defaults.Toggle(key: .enableLockScreenFocusWidget) { + Text("Show focus widget") + } + .settingsHighlight(id: highlightID("Show focus widget")) } header: { Text("Focus Widget") } footer: { @@ -4708,8 +4892,10 @@ struct LockScreenSettings: View { } Section { - Defaults.Toggle("Show next calendar event", key: .lockScreenShowCalendarEvent) - .settingsHighlight(id: highlightID("Show next calendar event")) + Defaults.Toggle(key: .lockScreenShowCalendarEvent) { + Text("Show next calendar event") + } + .settingsHighlight(id: highlightID("Show next calendar event")) LabeledContent("Show events within the next") { HStack { @@ -4777,18 +4963,22 @@ struct LockScreenSettings: View { .disabled(!lockScreenShowCalendarEvent) } - Defaults.Toggle("Show countdown", key: .lockScreenShowCalendarCountdown) - .disabled(!lockScreenShowCalendarEvent) - .settingsHighlight(id: highlightID("Show countdown")) + Defaults.Toggle(key: .lockScreenShowCalendarCountdown) { + Text("Show countdown") + } + .disabled(!lockScreenShowCalendarEvent) + .settingsHighlight(id: highlightID("Show countdown")) - Defaults.Toggle("Show event for entire duration", key: .lockScreenShowCalendarEventEntireDuration) - .disabled(!lockScreenShowCalendarEvent) - .settingsHighlight(id: highlightID("Show event for entire duration")) - .onChange(of: Defaults[.lockScreenShowCalendarEventEntireDuration]) { _, newValue in - if newValue { - Defaults[.lockScreenShowCalendarEventAfterStartEnabled] = false - } + Defaults.Toggle(key: .lockScreenShowCalendarEventEntireDuration) { + Text("Show event for entire duration") + } + .disabled(!lockScreenShowCalendarEvent) + .settingsHighlight(id: highlightID("Show event for entire duration")) + .onChange(of: Defaults[.lockScreenShowCalendarEventEntireDuration]) { _, newValue in + if newValue { + Defaults[.lockScreenShowCalendarEventAfterStartEnabled] = false } + } Defaults.Toggle( "Hide active event and show next upcoming event", @@ -4821,13 +5011,17 @@ struct LockScreenSettings: View { .font(.caption) .foregroundStyle(.secondary) - Defaults.Toggle("Show time remaining", key: .lockScreenShowCalendarTimeRemaining) - .disabled(!lockScreenShowCalendarEvent) - .settingsHighlight(id: highlightID("Show time remaining")) + Defaults.Toggle(key: .lockScreenShowCalendarTimeRemaining) { + Text("Show time remaining") + } + .disabled(!lockScreenShowCalendarEvent) + .settingsHighlight(id: highlightID("Show time remaining")) - Defaults.Toggle("Show start time after event begins", key: .lockScreenShowCalendarStartTimeAfterBegins) - .disabled(!lockScreenShowCalendarEvent) - .settingsHighlight(id: highlightID("Show start time after event begins")) + Defaults.Toggle(key: .lockScreenShowCalendarStartTimeAfterBegins) { + Text("Show start time after event begins") + } + .disabled(!lockScreenShowCalendarEvent) + .settingsHighlight(id: highlightID("Show start time after event begins")) } header: { Text("Calendar Widget") } footer: { @@ -5028,8 +5222,8 @@ private struct LockScreenPositioningControls: View { musicWidth: musicWidthBinding, timerWidth: timerWidthBinding ) - .frame(height: 260) - .padding(.vertical, 8) + .frame(height: 260) + .padding(.vertical, 8) HStack(alignment: .top, spacing: 24) { offsetColumn( @@ -5484,16 +5678,18 @@ struct Shortcuts: View { @Default(.enableShortcuts) var enableShortcuts @Default(.enableStatsFeature) var enableStatsFeature @Default(.enableColorPickerFeature) var enableColorPickerFeature - + private func highlightID(_ title: String) -> String { SettingsTab.shortcuts.highlightID(for: title) } - + var body: some View { Form { Section { - Defaults.Toggle("Enable global keyboard shortcuts", key: .enableShortcuts) - .settingsHighlight(id: highlightID("Enable global keyboard shortcuts")) + Defaults.Toggle(key: .enableShortcuts) { + Text("Enable global keyboard shortcuts") + } + .settingsHighlight(id: highlightID("Enable global keyboard shortcuts")) } header: { Text("General") } footer: { @@ -5502,7 +5698,7 @@ struct Shortcuts: View { .foregroundStyle(.secondary) .font(.caption) } - + if enableShortcuts { Section { KeyboardShortcuts.Recorder("Toggle Sneak Peek:", name: .toggleSneakPeek) @@ -5515,7 +5711,7 @@ struct Shortcuts: View { .foregroundStyle(.secondary) .font(.caption) } - + Section { KeyboardShortcuts.Recorder("Toggle Notch Open:", name: .toggleNotchOpen) .disabled(!enableShortcuts) @@ -5527,7 +5723,7 @@ struct Shortcuts: View { .foregroundStyle(.secondary) .font(.caption) } - + Section { HStack { VStack(alignment: .leading) { @@ -5550,7 +5746,7 @@ struct Shortcuts: View { .foregroundStyle(.secondary) .font(.caption) } - + Section { HStack { VStack(alignment: .leading) { @@ -5573,7 +5769,7 @@ struct Shortcuts: View { .foregroundStyle(.secondary) .font(.caption) } - + Section { HStack { VStack(alignment: .leading) { @@ -5596,7 +5792,7 @@ struct Shortcuts: View { .foregroundStyle(.secondary) .font(.caption) } - + Section { HStack { VStack(alignment: .leading) { @@ -5619,7 +5815,7 @@ struct Shortcuts: View { .foregroundStyle(.secondary) .font(.caption) } - + Section { HStack { VStack(alignment: .leading) { @@ -5648,7 +5844,7 @@ struct Shortcuts: View { Text("Keyboard shortcuts are disabled") .font(.headline) .foregroundStyle(.secondary) - + Text("Enable global keyboard shortcuts above to customize your shortcuts.") .font(.caption) .foregroundStyle(.secondary) @@ -5743,7 +5939,7 @@ struct TimerSettings: View { @State private var customMinutes: Int = 10 @State private var customSeconds: Int = 0 @State private var showingResetConfirmation = false - + private func highlightID(_ title: String) -> String { SettingsTab.timer.highlightID(for: title) } @@ -5768,7 +5964,7 @@ struct TimerSettings: View { set: { mode in timerGlassModeIsGlass = (mode == .glass) } ) } - + var body: some View { Form { timerFeatureSection @@ -5785,8 +5981,10 @@ struct TimerSettings: View { @ViewBuilder private var timerFeatureSection: some View { Section { - Defaults.Toggle("Enable timer feature", key: .enableTimerFeature) - .settingsHighlight(id: highlightID("Enable timer feature")) + Defaults.Toggle(key: .enableTimerFeature) { + Text("Enable timer feature") + } + .settingsHighlight(id: highlightID("Enable timer feature")) if enableTimerFeature { Toggle("Enable timer live activity", isOn: $coordinator.timerLiveActivityEnabled) @@ -5797,8 +5995,8 @@ struct TimerSettings: View { alphaBadge() } } - .help("Shows the system Clock timer in the notch when available. Requires Accessibility permission to read the status item.") - .settingsHighlight(id: highlightID("Mirror macOS Clock timers")) + .help("Shows the system Clock timer in the notch when available. Requires Accessibility permission to read the status item.") + .settingsHighlight(id: highlightID("Mirror macOS Clock timers")) Picker("Timer controls appear as", selection: $timerDisplayMode) { ForEach(TimerDisplayMode.allCases) { mode in @@ -5840,8 +6038,10 @@ struct TimerSettings: View { @ViewBuilder private var lockScreenIntegrationSection: some View { Section { - Defaults.Toggle("Show lock screen timer widget", key: .enableLockScreenTimerWidget) - .settingsHighlight(id: highlightID("Show lock screen timer widget")) + Defaults.Toggle(key: .enableLockScreenTimerWidget) { + Text("Show lock screen timer widget") + } + .settingsHighlight(id: highlightID("Show lock screen timer widget")) Picker("Timer surface", selection: timerSurfaceBinding) { ForEach(LockScreenTimerSurfaceMode.allCases) { mode in Text(mode.rawValue).tag(mode) @@ -6055,14 +6255,14 @@ struct TimerSettings: View { Text("Select a custom sound to play when a timer ends. Supported formats include MP3, M4A, WAV, and AIFF.") } } - + private var customDurationDisplay: String { let formatter = DateComponentsFormatter() formatter.allowedUnits = customTimerDuration >= 3600 ? [.hour, .minute, .second] : [.minute, .second] formatter.zeroFormattingBehavior = [.pad] return formatter.string(from: customTimerDuration) ?? "0:00" } - + private func syncCustomDuration(_ value: Double? = nil) { let baseValue = value ?? customTimerDuration let components = TimerPreset.components(for: baseValue) @@ -6070,12 +6270,12 @@ struct TimerSettings: View { customMinutes = components.minutes customSeconds = components.seconds } - + private func updateCustomDuration() { let duration = TimeInterval(customHours * 3600 + customMinutes * 60 + customSeconds) customTimerDuration = duration } - + private func addPreset() { let nextIndex = timerPresets.count + 1 let defaultColor = Defaults[.accentColor] @@ -6084,34 +6284,34 @@ struct TimerSettings: View { timerPresets.append(newPreset) } } - + private func movePresetUp(_ index: Int) { guard index > timerPresets.startIndex else { return } _ = withAnimation(.smooth) { timerPresets.swapAt(index, index - 1) } } - + private func movePresetDown(_ index: Int) { guard index < timerPresets.index(before: timerPresets.endIndex) else { return } _ = withAnimation(.smooth) { timerPresets.swapAt(index, index + 1) } } - + private func removePreset(_ index: Int) { guard timerPresets.indices.contains(index) else { return } _ = withAnimation(.smooth) { timerPresets.remove(at: index) } } - + private func resetPresets() { _ = withAnimation(.smooth) { timerPresets = TimerPreset.defaultPresets } } - + private func selectCustomTimerSound() { let panel = NSOpenPanel() panel.title = "Select Timer Sound" @@ -6119,7 +6319,7 @@ struct TimerSettings: View { panel.allowsMultipleSelection = false panel.canChooseDirectories = false panel.canChooseFiles = true - + if panel.runModal() == .OK { if let url = panel.url { UserDefaults.standard.set(url.path, forKey: "customTimerSoundPath") @@ -6132,7 +6332,7 @@ private struct TimerDurationStepperRow: View { let title: String @Binding var value: Int let range: ClosedRange - + var body: some View { Stepper(value: $value, in: range) { HStack { @@ -6202,39 +6402,39 @@ private struct TimerPresetEditorRow: View { self.moveDown = moveDown self.remove = remove } - + private var components: TimerPreset.DurationComponents { TimerPreset.components(for: preset.duration) } - + private var hoursBinding: Binding { Binding( get: { components.hours }, set: { updateDuration(hours: $0) } ) } - + private var minutesBinding: Binding { Binding( get: { components.minutes }, set: { updateDuration(minutes: $0) } ) } - + private var secondsBinding: Binding { Binding( get: { components.seconds }, set: { updateDuration(seconds: $0) } ) } - + private var colorBinding: Binding { Binding( get: { preset.color }, set: { preset.updateColor($0) } ) } - + var body: some View { VStack(alignment: .leading, spacing: 12) { HStack(spacing: 10) { @@ -6245,41 +6445,41 @@ private struct TimerPresetEditorRow: View { Circle() .stroke(Color.white.opacity(0.3), lineWidth: 1) ) - + TextField("Preset name", text: $preset.name) .textFieldStyle(.roundedBorder) - + Spacer() - + Text(preset.formattedDuration) .font(.system(size: 12, weight: .medium, design: .monospaced)) .foregroundStyle(.secondary) } - + HStack(spacing: 16) { TimerPresetComponentControl(title: "Hours", value: hoursBinding, range: 0...23) TimerPresetComponentControl(title: "Minutes", value: minutesBinding, range: 0...59) TimerPresetComponentControl(title: "Seconds", value: secondsBinding, range: 0...59) } - + ColorPicker("Accent colour", selection: colorBinding, supportsOpacity: false) .frame(maxWidth: 240, alignment: .leading) - + HStack(spacing: 12) { Button(action: moveUp) { Label("Move Up", systemImage: "chevron.up") } .buttonStyle(.bordered) .disabled(isFirst) - + Button(action: moveDown) { Label("Move Down", systemImage: "chevron.down") } .buttonStyle(.bordered) .disabled(isLast) - + Spacer() - + Button(role: .destructive, action: remove) { Label("Delete", systemImage: "trash") } @@ -6290,7 +6490,7 @@ private struct TimerPresetEditorRow: View { .padding(.vertical, 6) .settingsHighlightIfPresent(highlightID) } - + private func updateDuration(hours: Int? = nil, minutes: Int? = nil, seconds: Int? = nil) { var values = components if let hours { values.hours = hours } @@ -6304,7 +6504,7 @@ private struct TimerPresetComponentControl: View { let title: String @Binding var value: Int let range: ClosedRange - + var body: some View { Stepper(value: $value, in: range) { VStack(alignment: .leading, spacing: 4) { @@ -6330,11 +6530,11 @@ struct StatsSettings: View { @Default(.showNetworkGraph) var showNetworkGraph @Default(.showDiskGraph) var showDiskGraph @Default(.cpuTemperatureUnit) var cpuTemperatureUnit - + private func highlightID(_ title: String) -> String { SettingsTab.stats.highlightID(for: title) } - + var enabledGraphsCount: Int { [showCpuGraph, showMemoryGraph, showGpuGraph, showNetworkGraph, showDiskGraph].filter { $0 }.count } @@ -6353,19 +6553,21 @@ struct StatsSettings: View { private var shouldShowStatsBatteryWarning: Bool { !statsStopWhenNotchCloses && statsUpdateInterval <= 5 } - + var body: some View { Form { Section { - Defaults.Toggle("Enable system stats monitoring", key: .enableStatsFeature) - .settingsHighlight(id: highlightID("Enable system stats monitoring")) - .onChange(of: enableStatsFeature) { _, newValue in - if !newValue { - statsManager.stopMonitoring() - } - // Note: Smart monitoring will handle starting when switching to stats tab + Defaults.Toggle(key: .enableStatsFeature) { + Text("Enable system stats monitoring") + } + .settingsHighlight(id: highlightID("Enable system stats monitoring")) + .onChange(of: enableStatsFeature) { _, newValue in + if !newValue { + statsManager.stopMonitoring() } - + // Note: Smart monitoring will handle starting when switching to stats tab + } + } header: { Text("General") } footer: { @@ -6374,12 +6576,14 @@ struct StatsSettings: View { .foregroundStyle(.secondary) .font(.caption) } - + if enableStatsFeature { Section { - Defaults.Toggle("Stop monitoring after closing the notch", key: .statsStopWhenNotchCloses) - .settingsHighlight(id: highlightID("Stop monitoring after closing the notch")) - .help("When enabled, stats monitoring stops a few seconds after the notch closes.") + Defaults.Toggle(key: .statsStopWhenNotchCloses) { + Text("Stop monitoring after closing the notch") + } + .settingsHighlight(id: highlightID("Stop monitoring after closing the notch")) + .help("When enabled, stats monitoring stops a few seconds after the notch closes.") VStack(alignment: .leading, spacing: 8) { HStack { @@ -6417,8 +6621,10 @@ struct StatsSettings: View { } Section { - Defaults.Toggle("CPU Usage", key: .showCpuGraph) - .settingsHighlight(id: highlightID("CPU Usage")) + Defaults.Toggle(key: .showCpuGraph) { + Text("CPU Usage") + } + .settingsHighlight(id: highlightID("CPU Usage")) if showCpuGraph { Picker("Temperature unit", selection: $cpuTemperatureUnit) { @@ -6429,14 +6635,22 @@ struct StatsSettings: View { .pickerStyle(.segmented) .settingsHighlight(id: highlightID("Temperature unit")) } - Defaults.Toggle("Memory Usage", key: .showMemoryGraph) - .settingsHighlight(id: highlightID("Memory Usage")) - Defaults.Toggle("GPU Usage", key: .showGpuGraph) - .settingsHighlight(id: highlightID("GPU Usage")) - Defaults.Toggle("Network Activity", key: .showNetworkGraph) - .settingsHighlight(id: highlightID("Network Activity")) - Defaults.Toggle("Disk I/O", key: .showDiskGraph) - .settingsHighlight(id: highlightID("Disk I/O")) + Defaults.Toggle(key: .showMemoryGraph) { + Text("Memory Usage") + } + .settingsHighlight(id: highlightID("Memory Usage")) + Defaults.Toggle(key: .showGpuGraph) { + Text("GPU Usage") + } + .settingsHighlight(id: highlightID("GPU Usage")) + Defaults.Toggle(key: .showNetworkGraph) { + Text("Network Activity") + } + .settingsHighlight(id: highlightID("Network Activity")) + Defaults.Toggle(key: .showDiskGraph) { + Text("Disk I/O") + } + .settingsHighlight(id: highlightID("Disk I/O")) } header: { Text("Graph Visibility") } footer: { @@ -6452,7 +6666,7 @@ struct StatsSettings: View { .font(.caption) } } - + Section { HStack { Text("Monitoring Status") @@ -6465,7 +6679,7 @@ struct StatsSettings: View { .foregroundStyle(.secondary) } } - + if statsManager.isMonitoring { if showCpuGraph { HStack { @@ -6475,7 +6689,7 @@ struct StatsSettings: View { .foregroundStyle(.secondary) } } - + if showMemoryGraph { HStack { Text("Memory Usage") @@ -6484,7 +6698,7 @@ struct StatsSettings: View { .foregroundStyle(.secondary) } } - + if showGpuGraph { HStack { Text("GPU Usage") @@ -6493,7 +6707,7 @@ struct StatsSettings: View { .foregroundStyle(.secondary) } } - + if showNetworkGraph { HStack { Text("Network Download") @@ -6501,7 +6715,7 @@ struct StatsSettings: View { Text(String(format: "%.1f MB/s", statsManager.networkDownload)) .foregroundStyle(.secondary) } - + HStack { Text("Network Upload") Spacer() @@ -6509,7 +6723,7 @@ struct StatsSettings: View { .foregroundStyle(.secondary) } } - + if showDiskGraph { HStack { Text("Disk Read") @@ -6517,7 +6731,7 @@ struct StatsSettings: View { Text(String(format: "%.1f MB/s", statsManager.diskRead)) .foregroundStyle(.secondary) } - + HStack { Text("Disk Write") Spacer() @@ -6525,7 +6739,7 @@ struct StatsSettings: View { .foregroundStyle(.secondary) } } - + HStack { Text("Last Updated") Spacer() @@ -6536,7 +6750,7 @@ struct StatsSettings: View { } header: { Text("Live Performance Data") } - + Section { HStack { Button(statsManager.isMonitoring ? "Stop Monitoring" : "Start Monitoring") { @@ -6548,9 +6762,9 @@ struct StatsSettings: View { } .buttonStyle(.borderedProminent) .foregroundColor(statsManager.isMonitoring ? .red : .blue) - + Spacer() - + Button("Clear Data") { statsManager.clearHistory() } @@ -6572,34 +6786,38 @@ struct ClipboardSettings: View { @Default(.clipboardHistorySize) var clipboardHistorySize @Default(.showClipboardIcon) var showClipboardIcon @Default(.clipboardDisplayMode) var clipboardDisplayMode - + private func highlightID(_ title: String) -> String { SettingsTab.clipboard.highlightID(for: title) } - + var body: some View { Form { Section { - Defaults.Toggle("Enable Clipboard Manager", key: .enableClipboardManager) - .settingsHighlight(id: highlightID("Enable Clipboard Manager")) - .onChange(of: enableClipboardManager) { _, enabled in - if enabled { - clipboardManager.startMonitoring() - } else { - clipboardManager.stopMonitoring() - } + Defaults.Toggle(key: .enableClipboardManager) { + Text("Enable Clipboard Manager") + } + .settingsHighlight(id: highlightID("Enable Clipboard Manager")) + .onChange(of: enableClipboardManager) { _, enabled in + if enabled { + clipboardManager.startMonitoring() + } else { + clipboardManager.stopMonitoring() } + } } header: { Text("Clipboard Manager") } footer: { Text("Monitor clipboard changes and keep a history of recent copies. Use Cmd+Shift+V to quickly access clipboard history.") } - + if enableClipboardManager { Section { - Defaults.Toggle("Show Clipboard Icon", key: .showClipboardIcon) - .settingsHighlight(id: highlightID("Show Clipboard Icon")) - + Defaults.Toggle(key: .showClipboardIcon) { + Text("Show Clipboard Icon") + } + .settingsHighlight(id: highlightID("Show Clipboard Icon")) + HStack { Text("Display Mode") Spacer() @@ -6612,7 +6830,7 @@ struct ClipboardSettings: View { .frame(minWidth: 100) } .settingsHighlight(id: highlightID("Display Mode")) - + HStack { Text("History Size") Spacer() @@ -6626,21 +6844,21 @@ struct ClipboardSettings: View { .frame(minWidth: 100) } .settingsHighlight(id: highlightID("History Size")) - + HStack { Text("Current Items") Spacer() Text("\(clipboardManager.clipboardHistory.count)") .foregroundColor(.secondary) } - + HStack { Text("Pinned Items") Spacer() Text("\(clipboardManager.pinnedItems.count)") .foregroundColor(.secondary) } - + HStack { Text("Monitoring Status") Spacer() @@ -6659,14 +6877,14 @@ struct ClipboardSettings: View { Text("Separate Tab mode integrates Copied Items and Notes into a single view. If both are enabled, Notes appear on the right and Clipboard on the left.") } } - + Section { Button("Clear Clipboard History") { clipboardManager.clearHistory() } .foregroundColor(.red) .disabled(clipboardManager.clipboardHistory.isEmpty) - + Button("Clear Pinned Items") { clipboardManager.pinnedItems.removeAll() clipboardManager.savePinnedItemsToDefaults() @@ -6678,7 +6896,7 @@ struct ClipboardSettings: View { } footer: { Text("Clear clipboard history removes recent copies. Clear pinned items removes your favorites. Both actions are permanent.") } - + if !clipboardManager.clipboardHistory.isEmpty { Section { ForEach(clipboardManager.clipboardHistory) { item in @@ -6715,10 +6933,10 @@ struct ClipboardSettings: View { } } } - + private func timeAgoString(from date: Date) -> String { let interval = Date().timeIntervalSince(date) - + if interval < 60 { return "Just now" } else if interval < 3600 { @@ -6741,22 +6959,24 @@ struct ScreenAssistantSettings: View { @Default(.geminiApiKey) var geminiApiKey @State private var apiKeyText = "" @State private var showingApiKey = false - + private func highlightID(_ title: String) -> String { SettingsTab.screenAssistant.highlightID(for: title) } - + var body: some View { Form { Section { - Defaults.Toggle("Enable Screen Assistant", key: .enableScreenAssistant) - .settingsHighlight(id: highlightID("Enable Screen Assistant")) + Defaults.Toggle(key: .enableScreenAssistant) { + Text("Enable Screen Assistant") + } + .settingsHighlight(id: highlightID("Enable Screen Assistant")) } header: { Text("AI Assistant") } footer: { Text("AI-powered assistant that can analyze files, images, and provide conversational help. Use Cmd+Shift+A to quickly access the assistant.") } - + if enableScreenAssistant { Section { HStack { @@ -6769,7 +6989,7 @@ struct ScreenAssistantSettings: View { Text("••••••••") .foregroundColor(.green) } - + Button(showingApiKey ? "Hide" : (geminiApiKey.isEmpty ? "Set" : "Change")) { if showingApiKey { showingApiKey = false @@ -6783,24 +7003,24 @@ struct ScreenAssistantSettings: View { } } } - + if showingApiKey { VStack(alignment: .leading, spacing: 8) { SecureField("Enter your Gemini API Key", text: $apiKeyText) .textFieldStyle(.roundedBorder) - + Text("Get your free API key from Google AI Studio") .font(.caption) .foregroundColor(.secondary) - + HStack { Button("Open Google AI Studio") { NSWorkspace.shared.open(URL(string: "https://aistudio.google.com/app/apikey")!) } .buttonStyle(.link) - + Spacer() - + Button("Save") { Defaults[.geminiApiKey] = apiKeyText showingApiKey = false @@ -6810,7 +7030,7 @@ struct ScreenAssistantSettings: View { } } } - + HStack { Text("Display Mode") Spacer() @@ -6830,7 +7050,7 @@ struct ScreenAssistantSettings: View { Text("\(screenAssistantManager.attachedFiles.count)") .foregroundColor(.secondary) } - + HStack { Text("Recording Status") Spacer() @@ -6847,7 +7067,7 @@ struct ScreenAssistantSettings: View { Text("Panel mode shows the assistant in a floating window near the notch. Popover mode shows the assistant as a dropdown attached to the AI button.") } } - + Section { Button("Clear All Files") { screenAssistantManager.clearAllFiles() @@ -6859,7 +7079,7 @@ struct ScreenAssistantSettings: View { } footer: { Text("Clear all files removes all attached files and audio recordings. This action is permanent.") } - + if !screenAssistantManager.attachedFiles.isEmpty { Section { ForEach(screenAssistantManager.attachedFiles) { file in @@ -6891,10 +7111,10 @@ struct ScreenAssistantSettings: View { .formStyle(.grouped) .navigationTitle("Screen Assistant") } - + private func timeAgoString(from date: Date) -> String { let interval = Date().timeIntervalSince(date) - + if interval < 60 { return "Just now" } else if interval < 3600 { @@ -6917,27 +7137,31 @@ struct ColorPickerSettings: View { @Default(.colorPickerDisplayMode) var colorPickerDisplayMode @Default(.colorHistorySize) var colorHistorySize @Default(.showColorPickerIcon) var showColorPickerIcon - + private func highlightID(_ title: String) -> String { SettingsTab.colorPicker.highlightID(for: title) } - + var body: some View { Form { Section { - Defaults.Toggle("Enable Color Picker", key: .enableColorPickerFeature) - .settingsHighlight(id: highlightID("Enable Color Picker")) + Defaults.Toggle(key: .enableColorPickerFeature) { + Text("Enable Color Picker") + } + .settingsHighlight(id: highlightID("Enable Color Picker")) } header: { Text("Color Picker") } footer: { Text("Enable screen color picking functionality. Use Cmd+Shift+P to quickly access the color picker.") } - + if enableColorPickerFeature { Section { - Defaults.Toggle("Show Color Picker Icon", key: .showColorPickerIcon) - .settingsHighlight(id: highlightID("Show Color Picker Icon")) - + Defaults.Toggle(key: .showColorPickerIcon) { + Text("Show Color Picker Icon") + } + .settingsHighlight(id: highlightID("Show Color Picker Icon")) + HStack { Text("Display Mode") Spacer() @@ -6964,10 +7188,12 @@ struct ColorPickerSettings: View { .frame(minWidth: 100) } .settingsHighlight(id: highlightID("History Size")) - - Defaults.Toggle("Show All Color Formats", key: .showColorFormats) - .settingsHighlight(id: highlightID("Show All Color Formats")) - + + Defaults.Toggle(key: .showColorFormats) { + Text("Show All Color Formats") + } + .settingsHighlight(id: highlightID("Show All Color Formats")) + } header: { Text("Settings") } footer: { @@ -6978,7 +7204,7 @@ struct ColorPickerSettings: View { Text("Panel mode shows color picker in a floating window. Popover mode shows color picker as a dropdown attached to the color picker button.") } } - + Section { HStack { Text("Color History") @@ -6986,35 +7212,35 @@ struct ColorPickerSettings: View { Text("\(colorPickerManager.colorHistory.count)") .foregroundColor(.secondary) } - + HStack { Text("Picking Status") Spacer() Text(colorPickerManager.isPickingColor ? "Active" : "Ready") .foregroundColor(colorPickerManager.isPickingColor ? .green : .secondary) } - + Button("Show Color Picker Panel") { ColorPickerPanelManager.shared.showColorPickerPanel() } .disabled(!enableColorPickerFeature) - + } header: { Text("Status & Actions") } - + Section { Button("Clear Color History") { colorPickerManager.clearHistory() } .foregroundColor(.red) .disabled(colorPickerManager.colorHistory.isEmpty) - + Button("Start Color Picking") { colorPickerManager.startColorPicking() } .disabled(!enableColorPickerFeature || colorPickerManager.isPickingColor) - + } header: { Text("Quick Actions") } footer: { @@ -7039,11 +7265,11 @@ struct CustomOSDSettings: View { @Default(.osdIconColorStyle) var osdIconColorStyle @Default(.enableSystemHUD) var enableSystemHUD @ObservedObject private var accessibilityPermission = AccessibilityPermissionStore.shared - + @State private var showAlphaWarning = false @State private var previewValue: CGFloat = 0.65 @State private var previewType: SneakContentType = .volume - + private func highlightID(_ title: String) -> String { SettingsTab.hudAndOSD.highlightID(for: title) } @@ -7072,7 +7298,7 @@ struct CustomOSDSettings: View { } ) } - + var body: some View { Form { if !hasAccessibilityPermission && !enableThirdPartyDDCIntegration { @@ -7104,7 +7330,7 @@ struct CustomOSDSettings: View { .foregroundStyle(.secondary) .font(.caption) } - + Section { Picker("Material", selection: $osdMaterial) { ForEach(availableOSDMaterials, id: \.self) { material in @@ -7143,7 +7369,7 @@ struct CustomOSDSettings: View { .foregroundStyle(.secondary) } } - + Picker("Icon & Progress Color", selection: $osdIconColorStyle) { ForEach(OSDIconColorStyle.allCases, id: \.self) { style in Text(style.rawValue).tag(style) @@ -7167,7 +7393,7 @@ struct CustomOSDSettings: View { .foregroundStyle(.secondary) .font(.caption) } - + Section { HStack { Spacer() @@ -7175,20 +7401,20 @@ struct CustomOSDSettings: View { Text("Live Preview") .font(.caption) .foregroundStyle(.secondary) - + CustomOSDView( type: .constant(previewType), value: .constant(previewValue), icon: .constant("") ) .frame(width: 200, height: 200) - + HStack(spacing: 8) { Button("Volume") { previewType = .volume } .buttonStyle(.bordered) - + Button("Brightness") { previewType = .brightness } @@ -7307,13 +7533,25 @@ struct NotesSettingsView: View { var body: some View { Form { Section { - Defaults.Toggle("Enable Notes", key: .enableNotes) + Defaults.Toggle(key: .enableNotes) { + Text("Enable Notes") + } if Defaults[.enableNotes] { - Defaults.Toggle("Enable Note Pinning", key: .enableNotePinning) - Defaults.Toggle("Enable Note Search", key: .enableNoteSearch) - Defaults.Toggle("Enable Color Filtering", key: .enableNoteColorFiltering) - Defaults.Toggle("Enable Create from Clipboard", key: .enableCreateFromClipboard) - Defaults.Toggle("Show Character Count", key: .enableNoteCharCount) + Defaults.Toggle(key: .enableNotePinning) { + Text("Enable Note Pinning") + } + Defaults.Toggle(key: .enableNoteSearch) { + Text("Enable Note Search") + } + Defaults.Toggle(key: .enableNoteColorFiltering) { + Text("Enable Color Filtering") + } + Defaults.Toggle(key: .enableCreateFromClipboard) { + Text("Enable Create from Clipboard") + } + Defaults.Toggle(key: .enableNoteCharCount) { + Text("Show Character Count") + } } } header: { Text("General") @@ -7379,8 +7617,10 @@ struct TerminalSettings: View { var body: some View { Form { Section { - Defaults.Toggle("Enable terminal", key: .enableTerminalFeature) - .settingsHighlight(id: highlightID("Enable terminal")) + Defaults.Toggle(key: .enableTerminalFeature) { + Text("Enable terminal") + } + .settingsHighlight(id: highlightID("Enable terminal")) if enableTerminalFeature { Defaults.Toggle(key: .terminalStickyMode) { diff --git a/DynamicIsland/components/Stats/CPUStatsDetailView.swift b/DynamicIsland/components/Stats/CPUStatsDetailView.swift index 8826b79..08b1178 100644 --- a/DynamicIsland/components/Stats/CPUStatsDetailView.swift +++ b/DynamicIsland/components/Stats/CPUStatsDetailView.swift @@ -29,7 +29,7 @@ struct CPUStatsDetailView: View { var body: some View { ScrollView { VStack(spacing: 16) { - StatsCard(title: "CPU Overview") { + StatsCard(title: String(localized: "CPU Overview")) { CPUUsageDashboard( breakdown: statsManager.cpuBreakdown, loadAverage: statsManager.cpuLoadAverage, @@ -43,12 +43,12 @@ struct CPUStatsDetailView: View { ) } - StatsCard(title: "Top Processes") { + StatsCard(title: String(localized: "Top Processes")) { CPUProcessList(processes: topProcesses, accentColor: userColor) } if !statsManager.cpuCoreUsage.isEmpty { - StatsCard(title: "Per-Core Usage") { + StatsCard(title: String(localized: "Per-Core Usage")) { CPUCoreUsageGrid(cores: statsManager.cpuCoreUsage, accentColor: userColor) } } diff --git a/DynamicIsland/components/Stats/CPUStatsSharedViews.swift b/DynamicIsland/components/Stats/CPUStatsSharedViews.swift index 68150c2..29b96be 100644 --- a/DynamicIsland/components/Stats/CPUStatsSharedViews.swift +++ b/DynamicIsland/components/Stats/CPUStatsSharedViews.swift @@ -88,12 +88,12 @@ struct CPUUsageDashboard: View { Text("System") .font(.caption) .foregroundColor(.secondary) - DetailRow(color: nil, label: "Logical cores", value: "\(max(coreCount, 1))") - DetailRow(color: nil, label: "Uptime", value: StatsFormatting.abbreviatedDuration(uptime)) + DetailRow(color: nil, label: String(localized: "Logical cores"), value: "\(max(coreCount, 1))") + DetailRow(color: nil, label: String(localized: "Uptime"), value: StatsFormatting.abbreviatedDuration(uptime)) Divider().padding(.vertical, 4) - DetailRow(color: nil, label: "Load 1m", value: String(format: "%.2f", loadAverage.oneMinute)) - DetailRow(color: nil, label: "Load 5m", value: String(format: "%.2f", loadAverage.fiveMinutes)) - DetailRow(color: nil, label: "Load 15m", value: String(format: "%.2f", loadAverage.fifteenMinutes)) + DetailRow(color: nil, label: String(localized: "Load 1m"), value: String(format: "%.2f", loadAverage.oneMinute)) + DetailRow(color: nil, label: String(localized: "Load 5m"), value: String(format: "%.2f", loadAverage.fiveMinutes)) + DetailRow(color: nil, label: String(localized: "Load 15m"), value: String(format: "%.2f", loadAverage.fifteenMinutes)) } .frame(maxWidth: .infinity, alignment: .leading) @@ -205,7 +205,7 @@ private struct CPUFrequencyGauge: View { var body: some View { CircularGaugeView( - title: "Frequency", + title: String(localized: "Frequency"), value: normalizedValue, tint: tint, centerPrimaryText: centerPrimary, @@ -264,7 +264,7 @@ private struct CPUSegmentDonut: View { VStack(spacing: 4) { Text(StatsFormatting.percentage(breakdown.activeUsage)) .font(.system(size: 30, weight: .bold, design: .rounded)) - Text("Active") + Text(String(localized: "stats_resource_active", defaultValue: "Active")) .font(.footnote) .foregroundColor(.secondary) } @@ -335,9 +335,9 @@ struct LoadAverageRow: View { .font(.caption) .foregroundColor(.secondary) HStack(spacing: 12) { - LoadAverageValue(label: "1m", value: loadAverage.oneMinute) - LoadAverageValue(label: "5m", value: loadAverage.fiveMinutes) - LoadAverageValue(label: "15m", value: loadAverage.fifteenMinutes) + LoadAverageValue(label: String(localized: "1m"), value: loadAverage.oneMinute) + LoadAverageValue(label: String(localized: "5m"), value: loadAverage.fiveMinutes) + LoadAverageValue(label: String(localized: "15m"), value: loadAverage.fifteenMinutes) } } } @@ -455,10 +455,10 @@ struct CPUDetailsGrid: View { Text("Usage Breakdown") .font(.caption) .foregroundColor(.secondary) - DetailRow(color: nil, label: "Total", value: StatsFormatting.percentage(breakdown.activeUsage)) - DetailRow(color: systemColor, label: "System", value: StatsFormatting.percentage(breakdown.system)) - DetailRow(color: userColor, label: "User", value: StatsFormatting.percentage(breakdown.user)) - DetailRow(color: Color.gray.opacity(0.4), label: "Idle", value: StatsFormatting.percentage(breakdown.idle)) + DetailRow(color: nil, label: String(localized: "Total"), value: StatsFormatting.percentage(breakdown.activeUsage)) + DetailRow(color: systemColor, label: String(localized: "System"), value: StatsFormatting.percentage(breakdown.system)) + DetailRow(color: userColor, label: String(localized: "User"), value: StatsFormatting.percentage(breakdown.user)) + DetailRow(color: Color.gray.opacity(0.4), label: String(localized: "Idle"), value: StatsFormatting.percentage(breakdown.idle)) } Divider().padding(.vertical, 4) @@ -467,8 +467,8 @@ struct CPUDetailsGrid: View { Text("System") .font(.caption) .foregroundColor(.secondary) - DetailRow(color: nil, label: "Logical cores", value: "\(max(coreCount, 1))") - DetailRow(color: nil, label: "Uptime", value: StatsFormatting.abbreviatedDuration(uptime)) + DetailRow(color: nil, label: String(localized: "Logical cores"), value: "\(max(coreCount, 1))") + DetailRow(color: nil, label: String(localized: "Uptime"), value: StatsFormatting.abbreviatedDuration(uptime)) } if showLoadAverage { diff --git a/DynamicIsland/components/Stats/DiskStatsDetailView.swift b/DynamicIsland/components/Stats/DiskStatsDetailView.swift index c455269..4edc812 100644 --- a/DynamicIsland/components/Stats/DiskStatsDetailView.swift +++ b/DynamicIsland/components/Stats/DiskStatsDetailView.swift @@ -28,7 +28,7 @@ struct DiskStatsDetailView: View { var body: some View { ScrollView { VStack(spacing: 16) { - StatsCard(title: "Disk Overview", padding: 16, background: cardBackground, cornerRadius: 12) { + StatsCard(title: String(localized: "Disk Overview"), padding: 16, background: cardBackground, cornerRadius: 12) { DiskOverview( readSpeed: statsManager.diskRead, writeSpeed: statsManager.diskWrite, @@ -39,11 +39,11 @@ struct DiskStatsDetailView: View { ) } - StatsCard(title: "Storage Devices", padding: 16, background: cardBackground, cornerRadius: 12) { + StatsCard(title: String(localized: "Storage Devices"), padding: 16, background: cardBackground, cornerRadius: 12) { DiskDevicesCard(devices: statsManager.diskDevices, accentColor: readColor) } - StatsCard(title: "Session Totals", padding: 16, background: cardBackground, cornerRadius: 12) { + StatsCard(title: String(localized: "Session Totals"), padding: 16, background: cardBackground, cornerRadius: 12) { DiskTotalsView(totals: statsManager.diskTotals, readColor: readColor, writeColor: writeColor) } } @@ -99,8 +99,8 @@ private struct DiskTotalsView: View { var body: some View { VStack(spacing: 10) { - DetailRow(color: readColor.opacity(0.75), label: "Read", value: formattedBytes(totals.readMB)) - DetailRow(color: writeColor.opacity(0.75), label: "Written", value: formattedBytes(totals.writtenMB)) + DetailRow(color: readColor.opacity(0.75), label: String(localized: "Read"), value: formattedBytes(totals.readMB)) + DetailRow(color: writeColor.opacity(0.75), label: String(localized: "Written"), value: formattedBytes(totals.writtenMB)) } } diff --git a/DynamicIsland/components/Stats/GPUStatsDetailView.swift b/DynamicIsland/components/Stats/GPUStatsDetailView.swift index 7bd4077..064d2b5 100644 --- a/DynamicIsland/components/Stats/GPUStatsDetailView.swift +++ b/DynamicIsland/components/Stats/GPUStatsDetailView.swift @@ -29,7 +29,7 @@ struct GPUStatsDetailView: View { var body: some View { ScrollView { VStack(spacing: 16) { - StatsCard(title: "GPU Overview", padding: 16, background: cardBackground, cornerRadius: 12) { + StatsCard(title: String(localized: "GPU Overview"), padding: 16, background: cardBackground, cornerRadius: 12) { GPUUsageDashboard( usage: statsManager.gpuUsage, breakdown: statsManager.gpuBreakdown, @@ -40,12 +40,12 @@ struct GPUStatsDetailView: View { ) } - StatsCard(title: "Top Processes", padding: 16, background: cardBackground, cornerRadius: 12) { + StatsCard(title: String(localized: "Top Processes"), padding: 16, background: cardBackground, cornerRadius: 12) { CPUProcessList(processes: topProcesses, accentColor: accentColor, displayLimit: processDisplayLimit) } if !statsManager.gpuDevices.isEmpty { - StatsCard(title: "Devices", padding: 16, background: cardBackground, cornerRadius: 12) { + StatsCard(title: String(localized: "Devices"), padding: 16, background: cardBackground, cornerRadius: 12) { GPUDeviceList(devices: statsManager.gpuDevices, accentColor: accentColor) } } @@ -119,10 +119,10 @@ private struct GPUUsageDashboard: View { private var breakdownSection: some View { VStack(alignment: .leading, spacing: 12) { - DetailRow(color: accentColor.opacity(0.9), label: "Render", value: StatsFormatting.percentage(breakdown.render)) - DetailRow(color: accentColor.opacity(0.7), label: "Compute", value: StatsFormatting.percentage(breakdown.compute)) - DetailRow(color: accentColor.opacity(0.55), label: "Video", value: StatsFormatting.percentage(breakdown.video)) - DetailRow(color: accentColor.opacity(0.45), label: "Other", value: StatsFormatting.percentage(breakdown.other)) + DetailRow(color: accentColor.opacity(0.9), label: String(localized: "Render"), value: StatsFormatting.percentage(breakdown.render)) + DetailRow(color: accentColor.opacity(0.7), label: String(localized: "Compute"), value: StatsFormatting.percentage(breakdown.compute)) + DetailRow(color: accentColor.opacity(0.55), label: String(localized: "Video"), value: StatsFormatting.percentage(breakdown.video)) + DetailRow(color: accentColor.opacity(0.45), label: String(localized: "Other"), value: StatsFormatting.percentage(breakdown.other)) } .frame(maxWidth: .infinity, alignment: .leading) } @@ -132,15 +132,15 @@ private struct GPUUsageDashboard: View { Text("Averages") .font(.caption) .foregroundColor(.secondary) - DetailRow(color: nil, label: "Session Avg", value: StatsFormatting.percentage(averageUsage)) - DetailRow(color: nil, label: "Last Update", value: formattedTimestamp(lastUpdated)) + DetailRow(color: nil, label: String(localized: "Session Avg"), value: StatsFormatting.percentage(averageUsage)) + DetailRow(color: nil, label: String("localized: Last Update"), value: formattedTimestamp(lastUpdated)) if let device = primaryDevice { Divider().padding(.vertical, 4) - DetailRow(color: nil, label: "Active GPU", value: device.formattedVendorModel) - DetailRow(color: nil, label: "Status", value: device.isActive ? "Active" : "Idle") - DetailRow(color: nil, label: "Temperature", value: device.temperatureText) + DetailRow(color: nil, label: String(localized: "Active GPU"), value: device.formattedVendorModel) + DetailRow(color: nil, label: String(localized: "Status"), value: device.isActive ? String(localized: "Active") : String(localized: "Idle")) + DetailRow(color: nil, label: String(localized: "Temperature"), value: device.temperatureText) if let cores = device.cores { - DetailRow(color: nil, label: "Cores", value: "\(cores)") + DetailRow(color: nil, label: String(localized: "Cores"), value: "\(cores)") } } } @@ -170,9 +170,9 @@ private struct GPUEngineGauges: View { .font(.caption) .foregroundColor(.secondary) HStack(alignment: .center, spacing: 18) { - EngineGaugeView(title: "Render", value: render, tint: accentColor, size: 72) + EngineGaugeView(title: String(localized: "Render"), value: render, tint: accentColor, size: 72) GPUUsageRing(usage: usage, accentColor: accentColor, diameter: ringDiameter, lineWidth: ringLineWidth) - EngineGaugeView(title: "Tiler", value: tiler, tint: accentColor.opacity(0.75), size: 72) + EngineGaugeView(title: String(localized: "Tiler"), value: tiler, tint: accentColor.opacity(0.75), size: 72) } .frame(maxWidth: .infinity, alignment: .center) } @@ -193,7 +193,7 @@ private struct GPUUsageRing: View { VStack(spacing: 4) { Text(StatsFormatting.percentage(usage)) .font(.system(size: 26, weight: .bold, design: .rounded)) - Text("Active") + Text(String(localized: "stats_resource_active", defaultValue: "Active")) .font(.footnote) .foregroundColor(.secondary) } @@ -272,7 +272,7 @@ private struct GPUDeviceRow: View { VStack(alignment: .leading, spacing: 2) { Text(device.formattedVendorModel) .font(.system(size: 13, weight: .semibold)) - Text(device.isActive ? "Active" : "Idle") + Text(device.isActive ? String(localized: "stats_resource_active", defaultValue: "Active") : String(localized: "Idle")) .font(.caption2) .foregroundColor(device.isActive ? .green : .secondary) } @@ -288,25 +288,25 @@ private struct GPUDeviceRow: View { } VStack(spacing: 8) { if let render = device.renderUtilization { - DetailRow(color: accentColor.opacity(0.9), label: "Renderer", value: StatsFormatting.percentage(render)) + DetailRow(color: accentColor.opacity(0.9), label: String(localized: "Renderer"), value: StatsFormatting.percentage(render)) } if let tiler = device.tilerUtilization { - DetailRow(color: accentColor.opacity(0.6), label: "Tiler", value: StatsFormatting.percentage(tiler)) + DetailRow(color: accentColor.opacity(0.6), label: String(localized: "Tiler"), value: StatsFormatting.percentage(tiler)) } if let temp = device.temperature { - DetailRow(color: nil, label: "Temperature", value: String(format: "%.0f°C", temp)) + DetailRow(color: nil, label: String(localized: "Temperature"), value: String(format: "%.0f°C", temp)) } if let fan = device.fanSpeed { - DetailRow(color: nil, label: "Fan Speed", value: "\(fan)%") + DetailRow(color: nil, label: String(localized: "Fan Speed"), value: "\(fan)%") } if let coreClock = device.coreClock { - DetailRow(color: nil, label: "Core Clock", value: "\(coreClock) MHz") + DetailRow(color: nil, label: String(localized: "Core Clock"), value: "\(coreClock) MHz") } if let memoryClock = device.memoryClock { - DetailRow(color: nil, label: "Memory Clock", value: "\(memoryClock) MHz") + DetailRow(color: nil, label: String(localized: "Memory Clock"), value: "\(memoryClock) MHz") } if let cores = device.cores { - DetailRow(color: nil, label: "Cores", value: "\(cores)") + DetailRow(color: nil, label: String(localized: "Cores"), value: "\(cores)") } } } diff --git a/DynamicIsland/components/Stats/MemoryStatsDetailView.swift b/DynamicIsland/components/Stats/MemoryStatsDetailView.swift index e6ed7cf..bebfb91 100644 --- a/DynamicIsland/components/Stats/MemoryStatsDetailView.swift +++ b/DynamicIsland/components/Stats/MemoryStatsDetailView.swift @@ -29,15 +29,15 @@ struct MemoryStatsDetailView: View { var body: some View { ScrollView { VStack(spacing: 16) { - StatsCard(title: "Memory Overview", padding: 16, background: cardBackground, cornerRadius: 12) { + StatsCard(title: String(localized: "Memory Overview"), padding: 16, background: cardBackground, cornerRadius: 12) { MemoryUsageDashboard(breakdown: statsManager.memoryBreakdown, accentColor: accentColor) } - StatsCard(title: "Top Processes", padding: 16, background: cardBackground, cornerRadius: 12) { + StatsCard(title: String(localized: "Top Processes"), padding: 16, background: cardBackground, cornerRadius: 12) { CPUProcessList(processes: topProcesses, accentColor: accentColor, displayLimit: processDisplayLimit) } - StatsCard(title: "Pressure & Swap", padding: 16, background: cardBackground, cornerRadius: 12) { + StatsCard(title: String(localized: "Pressure & Swap"), padding: 16, background: cardBackground, cornerRadius: 12) { MemoryHealthView(breakdown: statsManager.memoryBreakdown, accentColor: accentColor) } } @@ -64,9 +64,9 @@ private struct MemoryUsageDashboard: View { var body: some View { let totalsSection = VStack(alignment: .leading, spacing: 12) { - DetailRow(color: accentColor.opacity(0.8), label: "Used", value: formattedBytes(breakdown.usedBytes)) - DetailRow(color: freeColor.opacity(0.9), label: "Free", value: formattedBytes(breakdown.freeBytes)) - DetailRow(color: nil, label: "Total", value: formattedBytes(breakdown.totalBytes)) + DetailRow(color: accentColor.opacity(0.8), label: String(localized: "Used"), value: formattedBytes(breakdown.usedBytes)) + DetailRow(color: freeColor.opacity(0.9), label: String(localized: "Free"), value: formattedBytes(breakdown.freeBytes)) + DetailRow(color: nil, label: String(localized: "Total"), value: formattedBytes(breakdown.totalBytes)) } .frame(maxWidth: .infinity, alignment: .leading) @@ -74,10 +74,10 @@ private struct MemoryUsageDashboard: View { Text("Breakdown") .font(.caption) .foregroundColor(.secondary) - MemoryBreakdownRow(label: "App", value: breakdown.appBytes, total: breakdown.totalBytes, color: accentColor.opacity(0.85)) - MemoryBreakdownRow(label: "Cached", value: breakdown.cacheBytes, total: breakdown.totalBytes, color: accentColor.opacity(0.65)) - MemoryBreakdownRow(label: "Wired", value: breakdown.wiredBytes, total: breakdown.totalBytes, color: accentColor.opacity(0.45)) - MemoryBreakdownRow(label: "Compressed", value: breakdown.compressedBytes, total: breakdown.totalBytes, color: accentColor.opacity(0.35)) + MemoryBreakdownRow(label: String(localized: "App"), value: breakdown.appBytes, total: breakdown.totalBytes, color: accentColor.opacity(0.85)) + MemoryBreakdownRow(label: String(localized: "Cached"), value: breakdown.cacheBytes, total: breakdown.totalBytes, color: accentColor.opacity(0.65)) + MemoryBreakdownRow(label: String(localized: "Wired"), value: breakdown.wiredBytes, total: breakdown.totalBytes, color: accentColor.opacity(0.45)) + MemoryBreakdownRow(label: String(localized: "Compressed"), value: breakdown.compressedBytes, total: breakdown.totalBytes, color: accentColor.opacity(0.35)) } .frame(maxWidth: .infinity, alignment: .leading) @@ -170,11 +170,11 @@ private struct MemoryPressureIndicator: View { private var status: (title: String, color: Color, description: String) { switch pressure.level { case .normal: - return ("Normal", Color.green, "System memory pressure is nominal.") + return (String(localized: "Normal"), Color.green, String(localized: "System memory pressure is nominal.")) case .warning: - return ("Warning", Color.orange, "Memory pressure is elevated. Closing apps may help.") + return (String(localized: "Warning"), Color.orange, String(localized: "Memory pressure is elevated. Closing apps may help.")) case .critical: - return ("Critical", Color.red, "Memory pressure is critical; the system may purge aggressively.") + return (String(localized: "Critical"), Color.red, String(localized: "Memory pressure is critical; the system may purge aggressively.")) } } @@ -220,9 +220,9 @@ private struct SwapUsageView: View { .foregroundColor(.secondary) if swap.totalBytes > 0 { - DetailRow(color: accentColor.opacity(0.85), label: "Used", value: StatsFormatting.bytes(swap.usedBytes)) - DetailRow(color: accentColor.opacity(0.55), label: "Free", value: StatsFormatting.bytes(swap.freeBytes)) - DetailRow(color: nil, label: "Total", value: StatsFormatting.bytes(swap.totalBytes)) + DetailRow(color: accentColor.opacity(0.85), label: String(localized: "Used"), value: StatsFormatting.bytes(swap.usedBytes)) + DetailRow(color: accentColor.opacity(0.55), label: String(localized: "Free"), value: StatsFormatting.bytes(swap.freeBytes)) + DetailRow(color: nil, label: String(localized: "Total"), value: StatsFormatting.bytes(swap.totalBytes)) SwapUsageBar(usedBytes: swap.usedBytes, totalBytes: swap.totalBytes, tint: accentColor) .frame(height: 8) } else { diff --git a/DynamicIsland/components/Stats/NetworkStatsDetailView.swift b/DynamicIsland/components/Stats/NetworkStatsDetailView.swift index 41edc1b..1d4eee2 100644 --- a/DynamicIsland/components/Stats/NetworkStatsDetailView.swift +++ b/DynamicIsland/components/Stats/NetworkStatsDetailView.swift @@ -28,7 +28,7 @@ struct NetworkStatsDetailView: View { var body: some View { ScrollView { VStack(spacing: 16) { - StatsCard(title: "Network Overview", padding: 16, background: cardBackground, cornerRadius: 12) { + StatsCard(title: String(localized: "Network Overview"), padding: 16, background: cardBackground, cornerRadius: 12) { NetworkOverview( download: statsManager.networkDownload, upload: statsManager.networkUpload, @@ -41,7 +41,7 @@ struct NetworkStatsDetailView: View { ) } - StatsCard(title: "Interfaces", padding: 16, background: cardBackground, cornerRadius: 12) { + StatsCard(title: String(localized: "Interfaces"), padding: 16, background: cardBackground, cornerRadius: 12) { NetworkInterfacesCard( interfaces: statsManager.networkInterfaces, downloadColor: downloadColor, @@ -49,7 +49,7 @@ struct NetworkStatsDetailView: View { ) } - StatsCard(title: "Session Totals", padding: 16, background: cardBackground, cornerRadius: 12) { + StatsCard(title: String(localized: "Session Totals"), padding: 16, background: cardBackground, cornerRadius: 12) { NetworkTotalsView(totals: statsManager.networkTotals, downloadColor: downloadColor, uploadColor: uploadColor) } } @@ -101,8 +101,8 @@ private struct NetworkOverview: View { Text(StatsFormatting.throughput(upload)) .font(.system(size: 26, weight: .bold, design: .rounded)) .foregroundColor(.primary) - DetailRow(color: uploadColor.opacity(0.6), label: "Average", value: StatsFormatting.throughput(avgUpload)) - DetailRow(color: uploadColor.opacity(0.45), label: "Peak", value: StatsFormatting.throughput(peakUpload)) + DetailRow(color: uploadColor.opacity(0.6), label: String(localized: "Average"), value: StatsFormatting.throughput(avgUpload)) + DetailRow(color: uploadColor.opacity(0.45), label: String(localized: "Peak"), value: StatsFormatting.throughput(peakUpload)) } .frame(maxWidth: .infinity, alignment: .leading) } @@ -116,8 +116,8 @@ private struct NetworkTotalsView: View { var body: some View { VStack(spacing: 10) { - DetailRow(color: downloadColor.opacity(0.75), label: "Downloaded", value: formattedBytes(totals.downloadedMB)) - DetailRow(color: uploadColor.opacity(0.75), label: "Uploaded", value: formattedBytes(totals.uploadedMB)) + DetailRow(color: downloadColor.opacity(0.75), label: String(localized: "Downloaded"), value: formattedBytes(totals.downloadedMB)) + DetailRow(color: uploadColor.opacity(0.75), label: String(localized: "Uploaded"), value: formattedBytes(totals.uploadedMB)) } } @@ -193,7 +193,7 @@ private struct NetworkInterfaceRow: View { .font(.system(size: 13, weight: .semibold)) .foregroundColor(.primary) if interface.isActive { - Text("Active") + Text(String(localized: "stats_resource_active", defaultValue: "Active")) .font(.caption2) .foregroundColor(downloadColor) .padding(.horizontal, 6) @@ -222,8 +222,8 @@ private struct NetworkInterfaceRow: View { Spacer() VStack(alignment: .trailing, spacing: 4) { - metricRow(label: "Down", value: StatsFormatting.throughput(interface.currentDownload), color: downloadColor) - metricRow(label: "Up", value: StatsFormatting.throughput(interface.currentUpload), color: uploadColor) + metricRow(label: String(localized: "Down"), value: StatsFormatting.throughput(interface.currentDownload), color: downloadColor) + metricRow(label: String(localized: "Up"), value: StatsFormatting.throughput(interface.currentUpload), color: uploadColor) } } }