From 382062d975340e757019c65fa50893ecfc4acd33 Mon Sep 17 00:00:00 2001 From: teszerrakt Date: Thu, 5 Mar 2026 05:51:49 +0700 Subject: [PATCH] feat(system-monitor): add CPU and memory usage gauges in notch header Add optional system monitor displaying CPU and memory usage as circular gauge rings in the notch header area. Uses macOS Mach kernel APIs to poll host_statistics every 3 seconds with no external dependencies. - SystemMonitorManager: singleton polling CPU ticks and VM stats - SystemMonitorView: color-coded gauge rings (green/yellow/red) with click-to-show popover for detailed stats and Activity Monitor shortcut - SystemMonitorSettingsView: toggle + live preview in Settings - Popover hover persistence prevents notch auto-close while open - Disabled by default, configurable via Settings > System Monitor Closes #1082 --- boringNotch.xcodeproj/project.pbxproj | 20 ++ boringNotch/ContentView.swift | 20 +- boringNotch/Localizable.xcstrings | 34 +++- .../components/Notch/BoringHeader.swift | 3 + .../components/Settings/SettingsView.swift | 5 + .../Views/SystemMonitorSettingsView.swift | 54 +++++ .../SystemMonitor/SystemMonitorView.swift | 191 ++++++++++++++++++ .../managers/SystemMonitorManager.swift | 174 ++++++++++++++++ boringNotch/models/BoringViewModel.swift | 2 + boringNotch/models/Constants.swift | 3 + 10 files changed, 502 insertions(+), 4 deletions(-) create mode 100644 boringNotch/components/Settings/Views/SystemMonitorSettingsView.swift create mode 100644 boringNotch/components/SystemMonitor/SystemMonitorView.swift create mode 100644 boringNotch/managers/SystemMonitorManager.swift diff --git a/boringNotch.xcodeproj/project.pbxproj b/boringNotch.xcodeproj/project.pbxproj index 0e90a4fd3..cc55ce846 100644 --- a/boringNotch.xcodeproj/project.pbxproj +++ b/boringNotch.xcodeproj/project.pbxproj @@ -155,6 +155,9 @@ B1F0A0022E60000100000001 /* BrightnessManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1F0A0012E60000100000001 /* BrightnessManager.swift */; }; B1FEB4992C7686630066EBBC /* PanGesture.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1FEB4982C7686630066EBBC /* PanGesture.swift */; }; F38DE6482D8243E7008B5C6D /* BatteryActivityManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = F38DE6472D8243E2008B5C6D /* BatteryActivityManager.swift */; }; + E5A00004AAAA000000000004 /* SystemMonitorManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5A00001AAAA000000000001 /* SystemMonitorManager.swift */; }; + E5A00005AAAA000000000005 /* SystemMonitorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5A00002AAAA000000000002 /* SystemMonitorView.swift */; }; + E5A00006AAAA000000000006 /* SystemMonitorSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5A00003AAAA000000000003 /* SystemMonitorSettingsView.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -333,6 +336,9 @@ B1F0A0012E60000100000001 /* BrightnessManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BrightnessManager.swift; sourceTree = ""; }; B1FEB4982C7686630066EBBC /* PanGesture.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PanGesture.swift; sourceTree = ""; }; F38DE6472D8243E2008B5C6D /* BatteryActivityManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BatteryActivityManager.swift; sourceTree = ""; }; + E5A00001AAAA000000000001 /* SystemMonitorManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SystemMonitorManager.swift; sourceTree = ""; }; + E5A00002AAAA000000000002 /* SystemMonitorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SystemMonitorView.swift; sourceTree = ""; }; + E5A00003AAAA000000000003 /* SystemMonitorSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SystemMonitorSettingsView.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */ @@ -530,6 +536,7 @@ 11DB266F2EDD0CDF001EA0CF /* SettingsHelpers.swift */, 11DB26702EDD0CDF001EA0CF /* ShelfSettingsView.swift */, 11DB26712EDD0CDF001EA0CF /* ShortcutsSettingsView.swift */, + E5A00003AAAA000000000003 /* SystemMonitorSettingsView.swift */, ); path = Views; sourceTree = ""; @@ -584,6 +591,7 @@ 149E0B982C737D26006418B1 /* Webcam */, B18654312C6F45AE000B926A /* Live activities */, B18654302C6F4590000B926A /* Settings */, + E5A00007AAAA000000000001 /* SystemMonitor */, B186542F2C6F455E000B926A /* Notch */, 9A0887332C7AFF7E00C160EA /* Tabs */, B186542E2C6F453B000B926A /* Music */, @@ -605,10 +613,19 @@ 147163992C5D35FF0068B555 /* MusicManager.swift */, 149E0B962C737D00006418B1 /* WebcamManager.swift */, 14C08BB52C8DE42D000F8AA0 /* CalendarManager.swift */, + E5A00001AAAA000000000001 /* SystemMonitorManager.swift */, ); path = managers; sourceTree = ""; }; + E5A00007AAAA000000000001 /* SystemMonitor */ = { + isa = PBXGroup; + children = ( + E5A00002AAAA000000000002 /* SystemMonitorView.swift */, + ); + path = SystemMonitor; + sourceTree = ""; + }; 149E0B982C737D26006418B1 /* Webcam */ = { isa = PBXGroup; children = ( @@ -1108,6 +1125,9 @@ 1100290C2E847E2800035A57 /* NSItemProvider+LoadHelpers.swift in Sources */, 11CFC6632E09918400748C80 /* MusicControllerSelectionView.swift in Sources */, B1F0A0022E60000100000001 /* BrightnessManager.swift in Sources */, + E5A00004AAAA000000000004 /* SystemMonitorManager.swift in Sources */, + E5A00005AAAA000000000005 /* SystemMonitorView.swift in Sources */, + E5A00006AAAA000000000006 /* SystemMonitorSettingsView.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/boringNotch/ContentView.swift b/boringNotch/ContentView.swift index 8717ddd53..f2d236a39 100644 --- a/boringNotch/ContentView.swift +++ b/boringNotch/ContentView.swift @@ -170,13 +170,13 @@ struct ContentView: View { } } .onReceive(NotificationCenter.default.publisher(for: .sharingDidFinish)) { _ in - if vm.notchState == .open && !isHovering && !vm.isBatteryPopoverActive { + if vm.notchState == .open && !isHovering && !vm.isBatteryPopoverActive && !vm.isSystemMonitorPopoverActive { hoverTask?.cancel() hoverTask = Task { try? await Task.sleep(for: .milliseconds(100)) guard !Task.isCancelled else { return } await MainActor.run { - if self.vm.notchState == .open && !self.isHovering && !self.vm.isBatteryPopoverActive && !SharingStateManager.shared.preventNotchClose { + if self.vm.notchState == .open && !self.isHovering && !self.vm.isBatteryPopoverActive && !self.vm.isSystemMonitorPopoverActive && !SharingStateManager.shared.preventNotchClose { self.vm.close() } } @@ -204,6 +204,20 @@ struct ContentView: View { } } } + .onChange(of: vm.isSystemMonitorPopoverActive) { + if !vm.isSystemMonitorPopoverActive && !isHovering && vm.notchState == .open && !SharingStateManager.shared.preventNotchClose { + hoverTask?.cancel() + hoverTask = Task { + try? await Task.sleep(for: .milliseconds(100)) + guard !Task.isCancelled else { return } + await MainActor.run { + if !self.vm.isSystemMonitorPopoverActive && !self.isHovering && self.vm.notchState == .open && !SharingStateManager.shared.preventNotchClose { + self.vm.close() + } + } + } + } + } .sensoryFeedback(.alignment, trigger: haptics) .contextMenu { Button("Settings") { @@ -583,7 +597,7 @@ struct ContentView: View { self.isHovering = false } - if self.vm.notchState == .open && !self.vm.isBatteryPopoverActive && !SharingStateManager.shared.preventNotchClose { + if self.vm.notchState == .open && !self.vm.isBatteryPopoverActive && !self.vm.isSystemMonitorPopoverActive && !SharingStateManager.shared.preventNotchClose { self.vm.close() } } diff --git a/boringNotch/Localizable.xcstrings b/boringNotch/Localizable.xcstrings index 2a380e21b..e8ccbaabf 100644 --- a/boringNotch/Localizable.xcstrings +++ b/boringNotch/Localizable.xcstrings @@ -1206,6 +1206,10 @@ }, "Accessibility Access Required" : { + }, + "Activity Monitor" : { + "comment" : "A button label that opens the Activity Monitor.", + "isCommentAutoGenerated" : true }, "Add" : { "extractionState" : "stale", @@ -5429,6 +5433,14 @@ } } }, + "CPU Usage" : { + "comment" : "A label displayed next to the CPU usage value in the System Monitor settings view.", + "isCommentAutoGenerated" : true + }, + "Current Stats" : { + "comment" : "A section header for the current CPU and memory usage statistics.", + "isCommentAutoGenerated" : true + }, "Currently selected: %@" : { "localizations" : { "ar" : { @@ -6432,6 +6444,10 @@ } } }, + "Display CPU and memory usage indicators in the notch header area, next to the battery indicator." : { + "comment" : "A description of the feature that allows users to see CPU and memory usage indicators in the notch header area.", + "isCommentAutoGenerated" : true + }, "Download" : { "localizations" : { "ar" : { @@ -11963,6 +11979,10 @@ } } }, + "Memory Used" : { + "comment" : "A label describing the amount of memory currently used by the device.", + "isCommentAutoGenerated" : true + }, "Mic" : { }, @@ -18083,6 +18103,10 @@ } } }, + "Show system monitor" : { + "comment" : "A toggle label that allows the user to enable or disable the system monitor.", + "isCommentAutoGenerated" : true + }, "Slider color" : { "localizations" : { "ar" : { @@ -18739,6 +18763,10 @@ } } }, + "Statistics update every 3 seconds." : { + "comment" : "A footer label for the System Monitor settings view, explaining that the statistics are updated every 3 seconds.", + "isCommentAutoGenerated" : true + }, "Stopped" : { "extractionState" : "stale", "localizations" : { @@ -19040,6 +19068,10 @@ } } }, + "System Monitor" : { + "comment" : "The title of a settings page related to monitoring the system.", + "isCommentAutoGenerated" : true + }, "Time to Full Charge: %lld min" : { "localizations" : { "ar" : { @@ -21158,5 +21190,5 @@ } } }, - "version" : "1.0" + "version" : "1.1" } \ No newline at end of file diff --git a/boringNotch/components/Notch/BoringHeader.swift b/boringNotch/components/Notch/BoringHeader.swift index 9f9da3248..3723807db 100644 --- a/boringNotch/components/Notch/BoringHeader.swift +++ b/boringNotch/components/Notch/BoringHeader.swift @@ -47,6 +47,9 @@ struct BoringHeader: View { ) .transition(.scale(scale: 0.8).combined(with: .opacity)) } else { + if Defaults[.showSystemMonitor] { + SystemMonitorView() + } if Defaults[.showMirror] { Button(action: { vm.toggleCameraPreview() diff --git a/boringNotch/components/Settings/SettingsView.swift b/boringNotch/components/Settings/SettingsView.swift index a1a174886..0ec7031bf 100644 --- a/boringNotch/components/Settings/SettingsView.swift +++ b/boringNotch/components/Settings/SettingsView.swift @@ -40,6 +40,9 @@ struct SettingsView: View { NavigationLink(value: "Battery") { Label("Battery", systemImage: "battery.100.bolt") } + NavigationLink(value: "SystemMonitor") { + Label("System Monitor", systemImage: "gauge.with.dots.needle.33percent") + } NavigationLink(value: "Shelf") { Label("Shelf", systemImage: "books.vertical") } @@ -72,6 +75,8 @@ struct SettingsView: View { OSDSettings() case "Battery": Charge() + case "SystemMonitor": + SystemMonitorSettings() case "Shelf": Shelf() case "Shortcuts": diff --git a/boringNotch/components/Settings/Views/SystemMonitorSettingsView.swift b/boringNotch/components/Settings/Views/SystemMonitorSettingsView.swift new file mode 100644 index 000000000..878f9defa --- /dev/null +++ b/boringNotch/components/Settings/Views/SystemMonitorSettingsView.swift @@ -0,0 +1,54 @@ +// +// SystemMonitorSettingsView.swift +// boringNotch +// +// Created by Zaky Syihab Hatmoko on 05/03/2026. +// + +import Defaults +import SwiftUI + +struct SystemMonitorSettings: View { + @ObservedObject var monitor = SystemMonitorManager.shared + + var body: some View { + Form { + Section { + Defaults.Toggle(key: .showSystemMonitor) { + Text("Show system monitor") + } + } header: { + Text("General") + } footer: { + Text("Display CPU and memory usage indicators in the notch header area, next to the battery indicator.") + .font(.caption) + .foregroundStyle(.secondary) + } + + if Defaults[.showSystemMonitor] { + Section { + HStack { + Text("CPU Usage") + Spacer() + Text(String(format: "%.1f%%", monitor.cpuUsage)) + .foregroundStyle(.secondary) + } + HStack { + Text("Memory Used") + Spacer() + Text(String(format: "%.1f / %.0f GB", monitor.memoryUsed, monitor.memoryTotal)) + .foregroundStyle(.secondary) + } + } header: { + Text("Current Stats") + } footer: { + Text("Statistics update every 3 seconds.") + .font(.caption) + .foregroundStyle(.secondary) + } + } + } + .accentColor(.effectiveAccent) + .navigationTitle("System Monitor") + } +} diff --git a/boringNotch/components/SystemMonitor/SystemMonitorView.swift b/boringNotch/components/SystemMonitor/SystemMonitorView.swift new file mode 100644 index 000000000..1693cf991 --- /dev/null +++ b/boringNotch/components/SystemMonitor/SystemMonitorView.swift @@ -0,0 +1,191 @@ +// +// SystemMonitorView.swift +// boringNotch +// +// Created by Zaky Syihab Hatmoko on 05/03/2026. +// + +import SwiftUI + +// MARK: - Color Coding + +/// Returns a color based on usage level and thresholds. +/// - Parameters: +/// - value: Usage percentage (0-100). +/// - thresholds: A tuple of (warning, critical) thresholds. +private func colorForUsage(_ value: Double, thresholds: (Double, Double)) -> Color { + switch value { + case ..? + + var body: some View { + Button(action: { + withAnimation { + showPopover.toggle() + } + }) { + HStack(spacing: 10) { + GaugeRingView(progress: monitor.cpuUsage, color: monitor.cpuColor, iconName: "cpu") + GaugeRingView(progress: monitor.memoryUsagePercent, color: monitor.memoryColor, iconName: "memorychip") + } + } + .buttonStyle(ScaleButtonStyle()) + .popover(isPresented: $showPopover, arrowEdge: .bottom) { + SystemMonitorMenuView(onDismiss: { showPopover = false }) + .onHover { hovering in + isHoveringPopover = hovering + if hovering { + hideTask?.cancel() + hideTask = nil + } else { + scheduleHideIfNeeded() + } + } + } + .onChange(of: showPopover) { + vm.isSystemMonitorPopoverActive = showPopover + } + .onDisappear { + hideTask?.cancel() + hideTask = nil + } + } + + // MARK: - Hover Persistence + + private func scheduleHideIfNeeded() { + guard !isHoveringPopover else { return } + hideTask?.cancel() + hideTask = Task { + try? await Task.sleep(for: .milliseconds(350)) + guard !Task.isCancelled else { return } + await MainActor.run { withAnimation { showPopover = false } } + } + } +} + +// MARK: - Popover Menu + +/// Detailed system monitor popover shown when clicking the gauge rings. +struct SystemMonitorMenuView: View { + @ObservedObject var monitor = SystemMonitorManager.shared + var onDismiss: () -> Void + + var body: some View { + VStack(alignment: .leading, spacing: 16) { + HStack { + Text("System Monitor") + .font(.headline) + .fontWeight(.semibold) + Spacer() + } + + VStack(alignment: .leading, spacing: 8) { + statRow(label: "CPU Usage", icon: "cpu", value: "\(Int(monitor.cpuUsage))%", color: monitor.cpuColor) + statRow(label: "Memory Usage", icon: "memorychip", value: "\(Int(monitor.memoryUsagePercent))%", color: monitor.memoryColor) + + HStack { + Text("Memory Used") + .font(.subheadline) + .foregroundStyle(.secondary) + Spacer() + Text(String(format: "%.1f / %.0f GB", monitor.memoryUsed, monitor.memoryTotal)) + .font(.subheadline) + .foregroundStyle(.secondary) + } + } + .padding(.vertical, 8) + + Divider().background(Color.white) + + Button(action: openActivityMonitor) { + Label("Activity Monitor", systemImage: "gauge.with.dots.needle.33percent") + } + .frame(maxWidth: .infinity) + .buttonStyle(.plain) + .padding(.vertical, 8) + } + .padding() + .frame(width: 280) + .foregroundColor(.white) + } + + private func statRow(label: String, icon: String, value: String, color: Color) -> some View { + HStack { + Label(label, systemImage: icon) + .font(.subheadline) + Spacer() + Text(value) + .font(.subheadline) + .fontWeight(.medium) + .foregroundColor(color) + } + } + + private func openActivityMonitor() { + NSWorkspace.shared.open( + URL(fileURLWithPath: "/System/Applications/Utilities/Activity Monitor.app") + ) + onDismiss() + } +} + +#Preview { + SystemMonitorView() + .environmentObject(BoringViewModel()) + .padding() + .background(Color.black) +} diff --git a/boringNotch/managers/SystemMonitorManager.swift b/boringNotch/managers/SystemMonitorManager.swift new file mode 100644 index 000000000..ac65e82f7 --- /dev/null +++ b/boringNotch/managers/SystemMonitorManager.swift @@ -0,0 +1,174 @@ +// +// SystemMonitorManager.swift +// boringNotch +// +// Created by Zaky Syihab Hatmoko on 05/03/2026. +// + +import Combine +import Defaults +import Foundation + +/// Manages polling of system CPU and memory usage statistics. +/// Uses Mach kernel APIs for accurate, low-overhead measurements. +class SystemMonitorManager: ObservableObject { + + static let shared = SystemMonitorManager() + + // MARK: - Published Properties + + /// Current CPU usage as a percentage (0-100) + @Published private(set) var cpuUsage: Double = 0.0 + + /// Current memory used in GB + @Published private(set) var memoryUsed: Double = 0.0 + + /// Total physical memory in GB + let memoryTotal: Double + + /// Memory usage as a percentage (0-100) + var memoryUsagePercent: Double { + guard memoryTotal > 0 else { return 0 } + return (memoryUsed / memoryTotal) * 100.0 + } + + // MARK: - Private Properties + + private var timer: Timer? + private var defaultsObservation: Defaults.Observation? + + /// Previous CPU tick counts for delta calculation + private var previousCPUInfo: host_cpu_load_info? + + /// Cached Mach host port to avoid leaking send rights on every poll. + private let hostPort: mach_port_t + + private let pollingInterval: TimeInterval = 3.0 + + // MARK: - Init + + private init() { + self.hostPort = mach_host_self() + self.memoryTotal = Double(ProcessInfo.processInfo.physicalMemory) / (1024 * 1024 * 1024) + + // Take an initial CPU snapshot so the first real reading has a baseline + previousCPUInfo = fetchCPULoadInfo() + + // Observe the setting toggle to start/stop polling + defaultsObservation = Defaults.observe(.showSystemMonitor) { [weak self] change in + DispatchQueue.main.async { + if change.newValue { + self?.startPolling() + } else { + self?.stopPolling() + } + } + } + + // Start polling immediately if the setting is already on + if Defaults[.showSystemMonitor] { + startPolling() + } + } + + // MARK: - Polling + + private func startPolling() { + guard timer == nil else { return } + + // Perform an initial update immediately + updateStats() + + timer = Timer.scheduledTimer(withTimeInterval: pollingInterval, repeats: true) { [weak self] _ in + self?.updateStats() + } + } + + private func stopPolling() { + timer?.invalidate() + timer = nil + } + + private func updateStats() { + let cpu = measureCPUUsage() + let mem = measureMemoryUsed() + + DispatchQueue.main.async { [weak self] in + self?.cpuUsage = cpu + self?.memoryUsed = mem + } + } + + // MARK: - CPU Measurement + + /// Fetches the current aggregate CPU load info from the kernel. + private func fetchCPULoadInfo() -> host_cpu_load_info? { + var size = mach_msg_type_number_t( + MemoryLayout.stride / MemoryLayout.stride + ) + let hostInfo = host_cpu_load_info_t.allocate(capacity: 1) + defer { hostInfo.deallocate() } + + let result = hostInfo.withMemoryRebound(to: integer_t.self, capacity: Int(size)) { ptr in + host_statistics(hostPort, HOST_CPU_LOAD_INFO, ptr, &size) + } + + guard result == KERN_SUCCESS else { return nil } + return hostInfo.pointee + } + + /// Calculates CPU usage percentage based on tick delta since last poll. + private func measureCPUUsage() -> Double { + guard let current = fetchCPULoadInfo() else { return cpuUsage } + + defer { previousCPUInfo = current } + + guard let previous = previousCPUInfo else { return 0 } + + let userDelta = Double(current.cpu_ticks.0 - previous.cpu_ticks.0) // CPU_STATE_USER + let systemDelta = Double(current.cpu_ticks.1 - previous.cpu_ticks.1) // CPU_STATE_SYSTEM + let idleDelta = Double(current.cpu_ticks.2 - previous.cpu_ticks.2) // CPU_STATE_IDLE + let niceDelta = Double(current.cpu_ticks.3 - previous.cpu_ticks.3) // CPU_STATE_NICE + + let totalTicks = userDelta + systemDelta + idleDelta + niceDelta + guard totalTicks > 0 else { return 0 } + + let usedTicks = userDelta + systemDelta + niceDelta + return (usedTicks / totalTicks) * 100.0 + } + + // MARK: - Memory Measurement + + /// Measures current memory usage using Mach VM statistics. + /// Returns memory used in GB (active + wired + compressed). + private func measureMemoryUsed() -> Double { + var stats = vm_statistics64() + var count = mach_msg_type_number_t( + MemoryLayout.stride / MemoryLayout.stride + ) + + let result = withUnsafeMutablePointer(to: &stats) { ptr in + ptr.withMemoryRebound(to: integer_t.self, capacity: Int(count)) { intPtr in + host_statistics64(hostPort, HOST_VM_INFO64, intPtr, &count) + } + } + + guard result == KERN_SUCCESS else { return memoryUsed } + + let pageSize = Double(vm_kernel_page_size) + let active = Double(stats.active_count) * pageSize + let wired = Double(stats.wire_count) * pageSize + let compressed = Double(stats.compressor_page_count) * pageSize + + let usedBytes = active + wired + compressed + return usedBytes / (1024 * 1024 * 1024) + } + + // MARK: - Cleanup + + deinit { + stopPolling() + defaultsObservation?.invalidate() + mach_port_deallocate(mach_task_self_, hostPort) + } +} diff --git a/boringNotch/models/BoringViewModel.swift b/boringNotch/models/BoringViewModel.swift index 6da144393..2909c75b4 100644 --- a/boringNotch/models/BoringViewModel.swift +++ b/boringNotch/models/BoringViewModel.swift @@ -30,6 +30,7 @@ class BoringViewModel: NSObject, ObservableObject { @Published var edgeAutoOpenActive: Bool = false @Published var isHoveringCalendar: Bool = false @Published var isBatteryPopoverActive: Bool = false + @Published var isSystemMonitorPopoverActive: Bool = false @Published var screenUUID: String? @@ -211,6 +212,7 @@ class BoringViewModel: NSObject, ObservableObject { self.closedNotchSize = self.notchSize self.notchState = .closed self.isBatteryPopoverActive = false + self.isSystemMonitorPopoverActive = false if self.coordinator.shouldShowSneakPeek(on: self.screenUUID) { self.coordinator.toggleSneakPeek(status: false, type: .music, targetScreenUUID: self.screenUUID) } diff --git a/boringNotch/models/Constants.swift b/boringNotch/models/Constants.swift index 0747d328d..a3e44d4bc 100644 --- a/boringNotch/models/Constants.swift +++ b/boringNotch/models/Constants.swift @@ -229,6 +229,9 @@ extension Defaults.Keys { static let osdBrightnessSource = Key("osdBrightnessSource", default: .builtin) static let osdVolumeSource = Key("osdVolumeSource", default: .builtin) + // MARK: System Monitor + static let showSystemMonitor = Key("showSystemMonitor", default: false) + // MARK: Shelf static let boringShelf = Key("boringShelf", default: true) static let openShelfByDefault = Key("openShelfByDefault", default: true)