Working a bit

This commit is contained in:
Hariharan Mudaliar 2025-12-01 13:01:44 +05:30
commit 1f0eea3cc6
8 changed files with 881 additions and 91 deletions

View file

@ -135,7 +135,7 @@ class DynamicIslandViewCoordinator: ObservableObject {
sneakPeekDuration = resolvedDuration sneakPeekDuration = resolvedDuration
if type != .music && type != .timer && type != .reminder { if type != .music && type != .timer && type != .reminder {
// close() // close()
if !hudReplacement { if !Defaults[.enableSystemHUD] {
return return
} }
} }

View file

@ -1119,6 +1119,10 @@
} }
} }
}, },
"• Frosted Glass: Translucent blur effect" : {
"comment" : "A bullet point in the \"Appearance\" section of the Custom OSD settings, describing a glass material option.",
"isCommentAutoGenerated" : true
},
"• Improved performance" : { "• Improved performance" : {
"localizations" : { "localizations" : {
"ar" : { "ar" : {
@ -1219,6 +1223,10 @@
} }
} }
}, },
"• Liquid Glass: Modern glass effect (macOS 26+)" : {
"comment" : "A description of the \"Liquid Glass\" option in the Custom OSD settings.",
"isCommentAutoGenerated" : true
},
"• New feature 1" : { "• New feature 1" : {
"localizations" : { "localizations" : {
"ar" : { "ar" : {
@ -1319,6 +1327,10 @@
} }
} }
}, },
"• Solid Dark/Light/Auto: Opaque backgrounds" : {
"comment" : "A bullet point in the \"Appearance\" section of the Custom OSD settings, describing the appearance of the OSD when using solid colors.",
"isCommentAutoGenerated" : true
},
"• Thinking" : { "• Thinking" : {
"localizations" : { "localizations" : {
"fr" : { "fr" : {
@ -2324,6 +2336,10 @@
} }
} }
}, },
"Adjust settings above to see changes in real-time. The actual OSD appears at the bottom center of your screen." : {
"comment" : "A footer in the \"Preview\" section of the Custom OSD settings, explaining how to use the live preview feature.",
"isCommentAutoGenerated" : true
},
"AI Assistant" : { "AI Assistant" : {
"localizations" : { "localizations" : {
"fr" : { "fr" : {
@ -3270,6 +3286,16 @@
} }
} }
}, },
"Atoll" : {
"localizations" : {
"fr" : {
"stringUnit" : {
"state" : "translated",
"value" : "Atoll"
}
}
}
},
"Attached Files" : { "Attached Files" : {
"localizations" : { "localizations" : {
"fr" : { "fr" : {
@ -3492,6 +3518,10 @@
} }
} }
}, },
"Backlight" : {
"comment" : "A control for adjusting the keyboard backlight intensity.",
"isCommentAutoGenerated" : true
},
"Battery" : { "Battery" : {
"localizations" : { "localizations" : {
"ar" : { "ar" : {
@ -4074,6 +4104,10 @@
} }
} }
}, },
"Brightness" : {
"comment" : "A control that adjusts the brightness of the device.",
"isCommentAutoGenerated" : true
},
"Brightness HUD" : { "Brightness HUD" : {
"localizations" : { "localizations" : {
"fr" : { "fr" : {
@ -4084,6 +4118,10 @@
} }
} }
}, },
"Brightness OSD" : {
"comment" : "A label for a toggle that controls whether the brightness OSD is enabled",
"isCommentAutoGenerated" : true
},
"Built-in" : { "Built-in" : {
"comment" : "A label indicating that an animation is a built-in one.", "comment" : "A label indicating that an animation is a built-in one.",
"isCommentAutoGenerated" : true, "isCommentAutoGenerated" : true,
@ -4097,6 +4135,7 @@
} }
}, },
"Built-in System Monitoring" : { "Built-in System Monitoring" : {
"extractionState" : "stale",
"localizations" : { "localizations" : {
"fr" : { "fr" : {
"stringUnit" : { "stringUnit" : {
@ -4107,6 +4146,7 @@
} }
}, },
"Built-in system monitoring detects volume, brightness, and keyboard backlight changes directly without requiring external apps." : { "Built-in system monitoring detects volume, brightness, and keyboard backlight changes directly without requiring external apps." : {
"extractionState" : "stale",
"localizations" : { "localizations" : {
"de" : { "de" : {
"stringUnit" : { "stringUnit" : {
@ -4997,6 +5037,14 @@
} }
} }
}, },
"Choose which system controls should display custom OSD windows." : {
"comment" : "A footer description for the \"Controls\" section of the Custom OSD settings view.",
"isCommentAutoGenerated" : true
},
"Choose which system controls should display HUD notifications." : {
"comment" : "A footer for the \"Controls\" section in the HUD settings, explaining that users can choose which system controls should display HUD notifications.",
"isCommentAutoGenerated" : true
},
"Choose your preferred AI model and configuration" : { "Choose your preferred AI model and configuration" : {
"localizations" : { "localizations" : {
"fr" : { "fr" : {
@ -5448,6 +5496,10 @@
} }
} }
}, },
"Color options control the icon and progress bar appearance. Auto adapts to system theme." : {
"comment" : "A description under the \"Appearance\" section of the Custom OSD settings, explaining that the color options control the icon and progress bar appearance, and that \"Auto\" automatically adapts to the system theme.",
"isCommentAutoGenerated" : true
},
"Color Picked!" : { "Color Picked!" : {
"localizations" : { "localizations" : {
"fr" : { "fr" : {
@ -5754,9 +5806,8 @@
"comment" : "A footer for the \"Timer Widget\" section in the Lock Screen Settings view.", "comment" : "A footer for the \"Timer Widget\" section in the Lock Screen Settings view.",
"isCommentAutoGenerated" : true "isCommentAutoGenerated" : true
}, },
"Controls whether Atoll mirrors lock/unlock events with its own live activity and audible chimes." : { "Controls whether Dynamic Island mirrors lock/unlock events with its own live activity and audible chimes." : {
"comment" : "A footer for the \"Live Activity & Feedback\" section in the Lock Screen Settings.",
"isCommentAutoGenerated" : true
}, },
"Copy event link" : { "Copy event link" : {
"comment" : "A tooltip for a button that copies the link of a reminder event to the clipboard.", "comment" : "A tooltip for a button that copies the link of a reminder event to the clipboard.",
@ -6192,6 +6243,14 @@
} }
} }
}, },
"Custom OSD" : {
"comment" : "The title of the settings screen for customizing the OSD.",
"isCommentAutoGenerated" : true
},
"Custom OSD is disabled because HUDs are enabled. Disable HUDs in the HUDs tab to use Custom OSD." : {
"comment" : "A message displayed below the toggle to inform the user that they cannot enable Custom OSD if HUDs are enabled.",
"isCommentAutoGenerated" : true
},
"Custom Timer" : { "Custom Timer" : {
"localizations" : { "localizations" : {
"fr" : { "fr" : {
@ -6919,6 +6978,10 @@
} }
} }
}, },
"Display custom macOS-style OSD windows at the bottom center of the screen when adjusting volume, brightness, or keyboard backlight." : {
"comment" : "A description of the functionality of the Custom OSD feature.",
"isCommentAutoGenerated" : true
},
"Display Mode" : { "Display Mode" : {
"localizations" : { "localizations" : {
"fr" : { "fr" : {
@ -7302,16 +7365,6 @@
} }
} }
}, },
"Atoll" : {
"localizations" : {
"fr" : {
"stringUnit" : {
"state" : "translated",
"value" : "Atoll"
}
}
}
},
"dynamic.island" : { "dynamic.island" : {
"localizations" : { "localizations" : {
"fr" : { "fr" : {
@ -7561,6 +7614,7 @@
"isCommentAutoGenerated" : true "isCommentAutoGenerated" : true
}, },
"Enable Built-in System HUD" : { "Enable Built-in System HUD" : {
"extractionState" : "stale",
"localizations" : { "localizations" : {
"fr" : { "fr" : {
"stringUnit" : { "stringUnit" : {
@ -7571,6 +7625,7 @@
} }
}, },
"Enable built-in system monitoring to replace macOS HUD notifications with Atoll displays." : { "Enable built-in system monitoring to replace macOS HUD notifications with Atoll displays." : {
"extractionState" : "stale",
"localizations" : { "localizations" : {
"fr" : { "fr" : {
"stringUnit" : { "stringUnit" : {
@ -7691,6 +7746,10 @@
} }
} }
}, },
"Enable Custom OSD" : {
"comment" : "A toggle that enables or disables the custom OSD feature.",
"isCommentAutoGenerated" : true
},
"Enable global keyboard shortcuts above to customize your shortcuts." : { "Enable global keyboard shortcuts above to customize your shortcuts." : {
}, },
@ -7800,6 +7859,7 @@
} }
}, },
"Enable HUD replacement" : { "Enable HUD replacement" : {
"extractionState" : "stale",
"localizations" : { "localizations" : {
"ar" : { "ar" : {
"stringUnit" : { "stringUnit" : {
@ -7899,6 +7959,10 @@
} }
} }
}, },
"Enable HUDs" : {
"comment" : "A toggle that enables or disables the use of HUDs.",
"isCommentAutoGenerated" : true
},
"Enable music live activity" : { "Enable music live activity" : {
"localizations" : { "localizations" : {
"ar" : { "ar" : {
@ -10115,6 +10179,14 @@
} }
} }
}, },
"HUDs are disabled because Custom OSD is enabled. Disable Custom OSD in the Custom OSD tab to use HUDs." : {
"comment" : "A message displayed below the toggle to disable HUDs, explaining that it's disabled because Custom OSD is enabled.",
"isCommentAutoGenerated" : true
},
"Icon & Progress Color" : {
"comment" : "A picker that lets users choose the color scheme for the icon and progress bar in the OSD.",
"isCommentAutoGenerated" : true
},
"Idle" : { "Idle" : {
"comment" : "A label displayed alongside the status of a GPU device.", "comment" : "A label displayed alongside the status of a GPU device.",
"isCommentAutoGenerated" : true, "isCommentAutoGenerated" : true,
@ -10537,6 +10609,10 @@
} }
} }
}, },
"Keyboard Backlight OSD" : {
"comment" : "A label displayed above the toggle for the keyboard backlight OSD.",
"isCommentAutoGenerated" : true
},
"Keyboard shortcuts are disabled" : { "Keyboard shortcuts are disabled" : {
"localizations" : { "localizations" : {
"fr" : { "fr" : {
@ -10723,6 +10799,10 @@
} }
} }
}, },
"Live Preview" : {
"comment" : "A section header that indicates the live preview of custom OSD settings.",
"isCommentAutoGenerated" : true
},
"Live stream indicator" : { "Live stream indicator" : {
"comment" : "A visual indicator that appears when a music stream is live.", "comment" : "A visual indicator that appears when a music stream is live.",
"isCommentAutoGenerated" : true "isCommentAutoGenerated" : true
@ -11289,6 +11369,10 @@
"comment" : "A picker that lets the user select the material of the lock screen glass.", "comment" : "A picker that lets the user select the material of the lock screen glass.",
"isCommentAutoGenerated" : true "isCommentAutoGenerated" : true
}, },
"Material Options:" : {
"comment" : "A label describing the material options for the custom OSD.",
"isCommentAutoGenerated" : true
},
"Max Capacity: %lld%%" : { "Max Capacity: %lld%%" : {
"localizations" : { "localizations" : {
"ar" : { "ar" : {
@ -15495,8 +15579,9 @@
} }
} }
}, },
"Replaces system HUD notifications with Atoll displays." : { "Replaces macOS system HUD with Dynamic Island displays for volume, brightness, and keyboard backlight." : {
"comment" : "A footer description of the HUDs feature.",
"isCommentAutoGenerated" : true
}, },
"Request Access" : { "Request Access" : {
"localizations" : { "localizations" : {
@ -16288,6 +16373,7 @@
} }
}, },
"Sensitivity" : { "Sensitivity" : {
"extractionState" : "stale",
"localizations" : { "localizations" : {
"fr" : { "fr" : {
"stringUnit" : { "stringUnit" : {
@ -18794,8 +18880,9 @@
} }
} }
}, },
"Toggle the Atoll open or closed from anywhere." : { "Toggle the Dynamic Island open or closed from anywhere." : {
"comment" : "A description of the \"Toggle Navigation\" keyboard shortcut.",
"isCommentAutoGenerated" : true
}, },
"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.", "comment" : "A label displaying the total storage capacity of a disk device. The argument is the total storage capacity of the disk device.",
@ -19860,6 +19947,10 @@
} }
} }
}, },
"Volume" : {
"comment" : "A button that sets the preview to show a volume OSD.",
"isCommentAutoGenerated" : true
},
"Volume HUD" : { "Volume HUD" : {
"localizations" : { "localizations" : {
"fr" : { "fr" : {
@ -19870,6 +19961,10 @@
} }
} }
}, },
"Volume OSD" : {
"comment" : "A toggle that enables or disables the volume OSD",
"isCommentAutoGenerated" : true
},
"Weather" : { "Weather" : {
"comment" : "A label and its accompanying text displayed in a lock screen panel.", "comment" : "A label and its accompanying text displayed in a lock screen panel.",
"isCommentAutoGenerated" : true "isCommentAutoGenerated" : true
@ -20125,7 +20220,7 @@
} }
} }
}, },
"With %lld graphs enabled, the Atoll will expand horizontally to accommodate all graphs in a single row." : { "With %lld graphs enabled, the Dynamic Island will expand horizontally to accommodate all graphs in a single row." : {
}, },
"Write" : { "Write" : {

View file

@ -0,0 +1,182 @@
#if os(macOS)
import SwiftUI
import Defaults
/// Custom OSD panel that mimics macOS native OSD appearance
/// Supports volume, brightness, and keyboard backlight displays
struct CustomOSDView: View {
@Binding var type: SneakContentType
@Binding var value: CGFloat
@Binding var icon: String
@Default(.osdMaterial) var osdMaterial
@Default(.osdIconColorStyle) var osdIconColorStyle
@Environment(\.colorScheme) var colorScheme
private let iconSize: CGFloat = 56
private let progressBarHeight: CGFloat = 6
private let spacing: CGFloat = 20
private let segmentCount: Int = 16
var body: some View {
VStack(spacing: spacing) {
// Icon
Image(systemName: symbolName)
.font(.system(size: iconSize, weight: .regular))
.foregroundStyle(iconColor)
.symbolRenderingMode(.hierarchical)
// Progress/Value Indicator
progressView
.frame(width: 140)
}
.frame(width: 200, height: 200)
.background(
backgroundView
.clipShape(RoundedRectangle(cornerRadius: 18, style: .continuous))
)
.clipShape(RoundedRectangle(cornerRadius: 18, style: .continuous))
.shadow(color: Color.black.opacity(0.25), radius: 24, x: 0, y: 8)
}
// MARK: - Symbol Logic
private var symbolName: String {
if !icon.isEmpty {
return icon
}
switch type {
case .volume:
if value < 0.01 {
return "speaker.slash.fill"
} else if value < 0.33 {
return "speaker.fill"
} else if value < 0.66 {
return "speaker.wave.1.fill"
} else {
return "speaker.wave.3.fill"
}
case .brightness:
return "sun.max.fill"
case .backlight:
return value >= 0.5 ? "light.max" : "light.min"
default:
return "questionmark"
}
}
private var iconColor: Color {
switch osdIconColorStyle {
case .white:
return .white
case .whiteTransparent:
return .white.opacity(0.85)
case .lightGray:
return Color(white: 0.85)
case .darkGray:
return Color(white: 0.3)
case .black:
return .black
case .blackTransparent:
return .black.opacity(0.85)
case .auto:
return colorScheme == .dark ? .white : .black
case .autoTransparent:
return colorScheme == .dark ? .white.opacity(0.85) : .black.opacity(0.85)
}
}
private var textColor: Color {
switch osdIconColorStyle {
case .white, .whiteTransparent, .lightGray:
return .white
case .darkGray, .black, .blackTransparent:
return .black
case .auto, .autoTransparent:
return colorScheme == .dark ? .white : .black
}
}
// MARK: - Progress View
private var progressView: some View {
HStack(spacing: 3) {
ForEach(0..<segmentCount, id: \.self) { index in
Capsule()
.fill(index < Int(value * CGFloat(segmentCount)) ? iconColor : inactiveSegmentColor)
.frame(width: 6, height: progressBarHeight)
}
}
}
private var inactiveSegmentColor: Color {
switch osdIconColorStyle {
case .white, .whiteTransparent, .lightGray:
return Color.white.opacity(0.25)
case .darkGray, .black, .blackTransparent:
return Color.black.opacity(0.25)
case .auto, .autoTransparent:
return colorScheme == .dark ? Color.white.opacity(0.25) : Color.black.opacity(0.25)
}
}
// MARK: - Background
@ViewBuilder
private var backgroundView: some View {
switch osdMaterial {
case .frosted:
RoundedRectangle(cornerRadius: 18, style: .continuous)
.fill(.ultraThinMaterial)
case .liquid:
if #available(macOS 26.0, *) {
RoundedRectangle(cornerRadius: 18, style: .continuous)
.glassEffect(
.clear.interactive(),
in: .rect(cornerRadius: 18)
)
} else {
RoundedRectangle(cornerRadius: 18, style: .continuous)
.fill(.ultraThinMaterial)
}
case .solidDark:
RoundedRectangle(cornerRadius: 18, style: .continuous)
.fill(Color.black.opacity(0.85))
case .solidLight:
RoundedRectangle(cornerRadius: 18, style: .continuous)
.fill(Color.white.opacity(0.85))
case .solidAuto:
RoundedRectangle(cornerRadius: 18, style: .continuous)
.fill((colorScheme == .dark ? Color.black : Color.white).opacity(0.85))
}
}
}
// MARK: - OSD Material Enum
enum OSDMaterial: String, CaseIterable, Defaults.Serializable {
case frosted = "Frosted Glass"
case liquid = "Liquid Glass"
case solidDark = "Solid Dark"
case solidLight = "Solid Light"
case solidAuto = "Solid Auto"
}
// MARK: - OSD Icon Color Style Enum
enum OSDIconColorStyle: String, CaseIterable, Defaults.Serializable {
case white = "White"
case whiteTransparent = "White (Transparent)"
case lightGray = "Light Gray"
case darkGray = "Dark Gray"
case black = "Black"
case blackTransparent = "Black (Transparent)"
case auto = "Auto"
case autoTransparent = "Auto (Transparent)"
}
#endif

View file

@ -24,6 +24,7 @@ private enum SettingsTab: String, CaseIterable, Identifiable {
case timer case timer
case calendar case calendar
case hud case hud
case osd
case battery case battery
case stats case stats
case clipboard case clipboard
@ -46,6 +47,7 @@ private enum SettingsTab: String, CaseIterable, Identifiable {
case .timer: return "Timer" case .timer: return "Timer"
case .calendar: return "Calendar" case .calendar: return "Calendar"
case .hud: return "HUDs" case .hud: return "HUDs"
case .osd: return "Custom OSD"
case .battery: return "Battery" case .battery: return "Battery"
case .stats: return "Stats" case .stats: return "Stats"
case .clipboard: return "Clipboard" case .clipboard: return "Clipboard"
@ -68,6 +70,7 @@ private enum SettingsTab: String, CaseIterable, Identifiable {
case .timer: return "timer" case .timer: return "timer"
case .calendar: return "calendar" case .calendar: return "calendar"
case .hud: return "dial.medium.fill" case .hud: return "dial.medium.fill"
case .osd: return "square.fill.on.square.fill"
case .battery: return "battery.100.bolt" case .battery: return "battery.100.bolt"
case .stats: return "chart.xyaxis.line" case .stats: return "chart.xyaxis.line"
case .clipboard: return "clipboard" case .clipboard: return "clipboard"
@ -90,6 +93,7 @@ private enum SettingsTab: String, CaseIterable, Identifiable {
case .timer: return .red case .timer: return .red
case .calendar: return .cyan case .calendar: return .cyan
case .hud: return .indigo case .hud: return .indigo
case .osd: return .teal
case .battery: return .yellow case .battery: return .yellow
case .stats: return .teal case .stats: return .teal
case .clipboard: return .mint case .clipboard: return .mint
@ -352,6 +356,7 @@ struct SettingsView: View {
.timer, .timer,
.calendar, .calendar,
.hud, .hud,
.osd,
.battery, .battery,
.stats, .stats,
.clipboard, .clipboard,
@ -536,7 +541,7 @@ struct SettingsView: View {
SettingsSearchEntry(tab: .general, title: "Show on all displays", keywords: ["multi-display", "external monitor"], highlightID: SettingsTab.general.highlightID(for: "Show on all displays")), SettingsSearchEntry(tab: .general, title: "Show on all displays", keywords: ["multi-display", "external monitor"], highlightID: SettingsTab.general.highlightID(for: "Show on all displays")),
SettingsSearchEntry(tab: .general, title: "Show on a specific display", keywords: ["preferred screen", "display picker"], highlightID: SettingsTab.general.highlightID(for: "Show on a specific display")), SettingsSearchEntry(tab: .general, title: "Show on a specific display", keywords: ["preferred screen", "display picker"], highlightID: SettingsTab.general.highlightID(for: "Show on a specific display")),
SettingsSearchEntry(tab: .general, title: "Automatically switch displays", keywords: ["auto switch", "displays"], highlightID: SettingsTab.general.highlightID(for: "Automatically switch displays")), SettingsSearchEntry(tab: .general, title: "Automatically switch displays", keywords: ["auto switch", "displays"], highlightID: SettingsTab.general.highlightID(for: "Automatically switch displays")),
SettingsSearchEntry(tab: .general, title: "Hide Atoll during screenshots & recordings", keywords: ["privacy", "screenshot", "recording"], highlightID: SettingsTab.general.highlightID(for: "Hide Atoll during screenshots & recordings")), SettingsSearchEntry(tab: .general, title: "Hide Dynamic Island during screenshots & recordings", keywords: ["privacy", "screenshot", "recording"], highlightID: SettingsTab.general.highlightID(for: "Hide Dynamic Island during screenshots & recordings")),
SettingsSearchEntry(tab: .general, title: "Enable Screen Recording Detection", keywords: ["screen recording", "indicator"], highlightID: SettingsTab.general.highlightID(for: "Enable Screen Recording Detection")), SettingsSearchEntry(tab: .general, title: "Enable Screen Recording Detection", keywords: ["screen recording", "indicator"], highlightID: SettingsTab.general.highlightID(for: "Enable Screen Recording Detection")),
SettingsSearchEntry(tab: .general, title: "Show Recording Indicator", keywords: ["recording indicator", "red dot"], highlightID: SettingsTab.general.highlightID(for: "Show Recording Indicator")), SettingsSearchEntry(tab: .general, title: "Show Recording Indicator", keywords: ["recording indicator", "red dot"], highlightID: SettingsTab.general.highlightID(for: "Show Recording Indicator")),
SettingsSearchEntry(tab: .general, title: "Enable Focus Detection", keywords: ["focus", "do not disturb", "dnd"], highlightID: SettingsTab.general.highlightID(for: "Enable Focus Detection")), SettingsSearchEntry(tab: .general, title: "Enable Focus Detection", keywords: ["focus", "do not disturb", "dnd"], highlightID: SettingsTab.general.highlightID(for: "Enable Focus Detection")),
@ -577,6 +582,14 @@ struct SettingsView: View {
SettingsSearchEntry(tab: .hud, title: "Enable glowing effect", keywords: ["glow", "indicator"], highlightID: SettingsTab.hud.highlightID(for: "Enable glowing effect")), SettingsSearchEntry(tab: .hud, title: "Enable glowing effect", keywords: ["glow", "indicator"], highlightID: SettingsTab.hud.highlightID(for: "Enable glowing effect")),
SettingsSearchEntry(tab: .hud, title: "Use accent color", keywords: ["accent", "color"], highlightID: SettingsTab.hud.highlightID(for: "Use accent color")), SettingsSearchEntry(tab: .hud, title: "Use accent color", keywords: ["accent", "color"], highlightID: SettingsTab.hud.highlightID(for: "Use accent color")),
// Custom OSD
SettingsSearchEntry(tab: .osd, title: "Enable Custom OSD", keywords: ["osd", "on-screen display", "custom osd"], highlightID: SettingsTab.osd.highlightID(for: "Enable Custom OSD")),
SettingsSearchEntry(tab: .osd, title: "Volume OSD", keywords: ["volume", "osd"], highlightID: SettingsTab.osd.highlightID(for: "Volume OSD")),
SettingsSearchEntry(tab: .osd, title: "Brightness OSD", keywords: ["brightness", "osd"], highlightID: SettingsTab.osd.highlightID(for: "Brightness OSD")),
SettingsSearchEntry(tab: .osd, title: "Keyboard Backlight OSD", keywords: ["keyboard", "backlight", "osd"], highlightID: SettingsTab.osd.highlightID(for: "Keyboard Backlight OSD")),
SettingsSearchEntry(tab: .osd, title: "Material", keywords: ["material", "frosted", "liquid", "glass", "solid", "osd"], highlightID: SettingsTab.osd.highlightID(for: "Material")),
SettingsSearchEntry(tab: .osd, title: "Icon & Progress Color", keywords: ["color", "icon", "white", "black", "gray", "osd"], highlightID: SettingsTab.osd.highlightID(for: "Icon & Progress Color")),
// Media // Media
SettingsSearchEntry(tab: .media, title: "Music Source", keywords: ["media source", "controller"], highlightID: SettingsTab.media.highlightID(for: "Music Source")), SettingsSearchEntry(tab: .media, title: "Music Source", keywords: ["media source", "controller"], highlightID: SettingsTab.media.highlightID(for: "Music Source")),
SettingsSearchEntry(tab: .media, title: "Skip buttons", keywords: ["skip", "controls", "±10"], highlightID: SettingsTab.media.highlightID(for: "Skip buttons")), SettingsSearchEntry(tab: .media, title: "Skip buttons", keywords: ["skip", "controls", "±10"], highlightID: SettingsTab.media.highlightID(for: "Skip buttons")),
@ -713,6 +726,10 @@ struct SettingsView: View {
SettingsForm(tab: .hud) { SettingsForm(tab: .hud) {
HUD() HUD()
} }
case .osd:
SettingsForm(tab: .osd) {
CustomOSDSettings()
}
case .battery: case .battery:
SettingsForm(tab: .battery) { SettingsForm(tab: .battery) {
Charge() Charge()
@ -839,8 +856,8 @@ struct GeneralSettings: View {
} }
.disabled(showOnAllDisplays) .disabled(showOnAllDisplays)
.settingsHighlight(id: highlightID("Automatically switch displays")) .settingsHighlight(id: highlightID("Automatically switch displays"))
Defaults.Toggle("Hide Atoll during screenshots & recordings", key: .hideDynamicIslandFromScreenCapture) Defaults.Toggle("Hide Dynamic Island during screenshots & recordings", key: .hideDynamicIslandFromScreenCapture)
.settingsHighlight(id: highlightID("Hide Atoll during screenshots & recordings")) .settingsHighlight(id: highlightID("Hide Dynamic Island during screenshots & recordings"))
} header: { } header: {
Text("System features") Text("System features")
} }
@ -1239,48 +1256,31 @@ struct HUD: View {
var body: some View { var body: some View {
Form { Form {
Section { Section {
Toggle("Enable HUD replacement", isOn: $coordinator.hudReplacement) Toggle("Enable HUDs", isOn: $enableSystemHUD)
.disabled(Defaults[.enableCustomOSD])
} header: { } header: {
Text("General") Text("General")
} footer: { } footer: {
Text("Replaces system HUD notifications with Atoll displays.") if Defaults[.enableCustomOSD] {
.foregroundStyle(.secondary) Text("HUDs are disabled because Custom OSD is enabled. Disable Custom OSD in the Custom OSD tab to use HUDs.")
.font(.caption)
}
Section {
Toggle("Enable Built-in System HUD", isOn: $enableSystemHUD)
if enableSystemHUD {
Toggle("Volume HUD", isOn: $enableVolumeHUD)
Toggle("Brightness HUD", isOn: $enableBrightnessHUD)
Toggle("Keyboard Backlight HUD", isOn: $enableKeyboardBacklightHUD)
HStack {
Text("Sensitivity")
Spacer()
Slider(value: Binding(
get: { Double(systemHUDSensitivity) },
set: { systemHUDSensitivity = Int($0) }
), in: 1...10, step: 1) {
Text("Sensitivity")
}
.frame(width: 120)
Text("\(systemHUDSensitivity)")
.foregroundStyle(.secondary)
.font(.caption)
.frame(width: 20)
}
}
} header: {
Text("Built-in System Monitoring")
} footer: {
if enableSystemHUD {
Text("Built-in system monitoring detects volume, brightness, and keyboard backlight changes directly without requiring external apps.")
.foregroundStyle(.secondary) .foregroundStyle(.secondary)
.font(.caption) .font(.caption)
} else { } else {
Text("Enable built-in system monitoring to replace macOS HUD notifications with Atoll displays.") Text("Replaces macOS system HUD with Dynamic Island displays for volume, brightness, and keyboard backlight.")
.foregroundStyle(.secondary)
.font(.caption)
}
}
if enableSystemHUD && !Defaults[.enableCustomOSD] {
Section {
Toggle("Volume HUD", isOn: $enableVolumeHUD)
Toggle("Brightness HUD", isOn: $enableBrightnessHUD)
Toggle("Keyboard Backlight HUD", isOn: $enableKeyboardBacklightHUD)
} header: {
Text("Controls")
} footer: {
Text("Choose which system controls should display HUD notifications.")
.foregroundStyle(.secondary) .foregroundStyle(.secondary)
.font(.caption) .font(.caption)
} }
@ -2414,7 +2414,7 @@ struct LockScreenSettings: View {
} header: { } header: {
Text("Live Activity & Feedback") Text("Live Activity & Feedback")
} footer: { } footer: {
Text("Controls whether Atoll mirrors lock/unlock events with its own live activity and audible chimes.") Text("Controls whether Dynamic Island mirrors lock/unlock events with its own live activity and audible chimes.")
} }
Section { Section {
@ -3003,7 +3003,7 @@ struct Shortcuts: View {
} header: { } header: {
Text("Navigation") Text("Navigation")
} footer: { } footer: {
Text("Toggle the Atoll open or closed from anywhere.") Text("Toggle the Dynamic Island open or closed from anywhere.")
.multilineTextAlignment(.trailing) .multilineTextAlignment(.trailing)
.foregroundStyle(.secondary) .foregroundStyle(.secondary)
.font(.caption) .font(.caption)
@ -3777,7 +3777,7 @@ struct StatsSettings: View {
Text("Graph Visibility") Text("Graph Visibility")
} footer: { } footer: {
if enabledGraphsCount >= 4 { if enabledGraphsCount >= 4 {
Text("With \(enabledGraphsCount) graphs enabled, the Atoll will expand horizontally to accommodate all graphs in a single row.") Text("With \(enabledGraphsCount) graphs enabled, the Dynamic Island will expand horizontally to accommodate all graphs in a single row.")
.multilineTextAlignment(.trailing) .multilineTextAlignment(.trailing)
.foregroundStyle(.secondary) .foregroundStyle(.secondary)
.font(.caption) .font(.caption)
@ -4360,6 +4360,151 @@ struct ColorPickerSettings: View {
} }
} }
struct CustomOSDSettings: View {
@Default(.enableCustomOSD) var enableCustomOSD
@Default(.enableOSDVolume) var enableOSDVolume
@Default(.enableOSDBrightness) var enableOSDBrightness
@Default(.enableOSDKeyboardBacklight) var enableOSDKeyboardBacklight
@Default(.osdMaterial) var osdMaterial
@Default(.osdIconColorStyle) var osdIconColorStyle
@Default(.enableSystemHUD) var enableSystemHUD
@State private var previewValue: CGFloat = 0.65
@State private var previewType: SneakContentType = .volume
private func highlightID(_ title: String) -> String {
SettingsTab.osd.highlightID(for: title)
}
var body: some View {
Form {
Section {
Toggle("Enable Custom OSD", isOn: $enableCustomOSD)
.settingsHighlight(id: highlightID("Enable Custom OSD"))
.disabled(enableSystemHUD)
.onChange(of: enableCustomOSD) { _, enabled in
if enabled {
// Disable System HUD when OSD is enabled
enableSystemHUD = false
}
}
} header: {
Text("General")
} footer: {
if enableSystemHUD {
Text("Custom OSD is disabled because HUDs are enabled. Disable HUDs in the HUDs tab to use Custom OSD.")
.foregroundStyle(.secondary)
.font(.caption)
} else {
Text("Display custom macOS-style OSD windows at the bottom center of the screen when adjusting volume, brightness, or keyboard backlight.")
.foregroundStyle(.secondary)
.font(.caption)
}
}
if enableCustomOSD {
Section {
Toggle("Volume OSD", isOn: $enableOSDVolume)
.settingsHighlight(id: highlightID("Volume OSD"))
Toggle("Brightness OSD", isOn: $enableOSDBrightness)
.settingsHighlight(id: highlightID("Brightness OSD"))
Toggle("Keyboard Backlight OSD", isOn: $enableOSDKeyboardBacklight)
.settingsHighlight(id: highlightID("Keyboard Backlight OSD"))
} header: {
Text("Controls")
} footer: {
Text("Choose which system controls should display custom OSD windows.")
.foregroundStyle(.secondary)
.font(.caption)
}
Section {
Picker("Material", selection: $osdMaterial) {
ForEach(OSDMaterial.allCases, id: \.self) { material in
Text(material.rawValue).tag(material)
}
}
.settingsHighlight(id: highlightID("Material"))
.onChange(of: osdMaterial) { _, _ in
previewValue = previewValue == 0.65 ? 0.651 : 0.65
}
Picker("Icon & Progress Color", selection: $osdIconColorStyle) {
ForEach(OSDIconColorStyle.allCases, id: \.self) { style in
Text(style.rawValue).tag(style)
}
}
.settingsHighlight(id: highlightID("Icon & Progress Color"))
.onChange(of: osdIconColorStyle) { _, _ in
previewValue = previewValue == 0.65 ? 0.651 : 0.65
}
} header: {
Text("Appearance")
} footer: {
VStack(alignment: .leading, spacing: 4) {
Text("Material Options:")
Text("• Frosted Glass: Translucent blur effect")
Text("• Liquid Glass: Modern glass effect (macOS 26+)")
Text("• Solid Dark/Light/Auto: Opaque backgrounds")
Text("")
Text("Color options control the icon and progress bar appearance. Auto adapts to system theme.")
}
.foregroundStyle(.secondary)
.font(.caption)
}
Section {
HStack {
Spacer()
VStack(spacing: 16) {
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
}
.buttonStyle(.bordered)
Button("Backlight") {
previewType = .backlight
}
.buttonStyle(.bordered)
}
.controlSize(.small)
Slider(value: $previewValue, in: 0...1)
.frame(width: 160)
}
.padding(.vertical, 12)
Spacer()
}
} header: {
Text("Preview")
} footer: {
Text("Adjust settings above to see changes in real-time. The actual OSD appears at the bottom center of your screen.")
.foregroundStyle(.secondary)
.font(.caption)
}
}
}
.navigationTitle("Custom OSD")
}
}
#Preview { #Preview {
HUD() HUD()
} }

View file

@ -0,0 +1,252 @@
#if os(macOS)
import AppKit
import SwiftUI
import SkyLightWindow
import QuartzCore
import Defaults
/// Manages custom OSD windows for volume, brightness, and keyboard backlight controls
/// Mimics macOS native OSD behavior with custom styling
@MainActor
final class CustomOSDWindowManager {
static let shared = CustomOSDWindowManager()
private var volumeWindow: OSDWindow?
private var brightnessWindow: OSDWindow?
private var backlightWindow: OSDWindow?
private var hideWorkItem: DispatchWorkItem?
private let displayDuration: TimeInterval = 2.0
private let animationDuration: TimeInterval = 0.3
private var isInitialized = false
// Standard macOS OSD dimensions (approximate)
private let osdWidth: CGFloat = 200
private let osdHeight: CGFloat = 200
private init() {}
// MARK: - Public API
func showVolume(value: CGFloat, isMuted: Bool = false, icon: String = "") {
guard Defaults[.enableCustomOSD], isInitialized else { return }
show(type: .volume, value: value, icon: icon)
}
func showBrightness(value: CGFloat) {
guard Defaults[.enableCustomOSD], isInitialized else { return }
show(type: .brightness, value: value, icon: "")
}
func showBacklight(value: CGFloat) {
guard Defaults[.enableCustomOSD], isInitialized else { return }
show(type: .backlight, value: value, icon: "")
}
func initialize() {
isInitialized = true
}
// MARK: - Private Implementation
private func show(type: SneakContentType, value: CGFloat, icon: String) {
guard let screen = NSScreen.main else { return }
// Close other windows first
hideAllWindowsExcept(type: type)
let window = ensureWindow(for: type)
updateContent(window: window, type: type, value: value, icon: icon)
let targetFrame = calculateFrame(for: screen)
if window.nsWindow.alphaValue <= 0.01 {
// Initial presentation
presentWindow(window, targetFrame: targetFrame)
} else {
// Update existing window
window.nsWindow.setFrame(targetFrame, display: true)
window.nsWindow.orderFrontRegardless()
}
scheduleHide(for: type)
}
private func ensureWindow(for type: SneakContentType) -> OSDWindow {
let window: OSDWindow
switch type {
case .volume:
if let existing = volumeWindow {
return existing
}
window = createWindow(for: type)
volumeWindow = window
case .brightness:
if let existing = brightnessWindow {
return existing
}
window = createWindow(for: type)
brightnessWindow = window
case .backlight:
if let existing = backlightWindow {
return existing
}
window = createWindow(for: type)
backlightWindow = window
default:
fatalError("Unsupported OSD type: \(type)")
}
return window
}
private func createWindow(for type: SneakContentType) -> OSDWindow {
guard let screen = NSScreen.main else {
fatalError("No main screen available")
}
let osdView = CustomOSDView(type: .constant(type), value: .constant(0), icon: .constant(""))
let hostingView = NSHostingView(rootView: osdView)
let frame = calculateFrame(for: screen)
let window = NSWindow(
contentRect: frame,
styleMask: [.borderless, .nonactivatingPanel],
backing: .buffered,
defer: false
)
window.isOpaque = false
window.backgroundColor = .clear
window.level = .statusBar + 1
window.collectionBehavior = [.canJoinAllSpaces, .stationary, .ignoresCycle, .fullScreenAuxiliary]
window.hasShadow = false
window.contentView = hostingView
window.alphaValue = 0
// Delegate to SkyLight for proper rendering
SkyLightOperator.shared.delegateWindow(window)
return OSDWindow(nsWindow: window, hostingView: hostingView, type: type)
}
private func updateContent(window: OSDWindow, type: SneakContentType, value: CGFloat, icon: String) {
let osdView = CustomOSDView(type: .constant(type), value: .constant(value), icon: .constant(icon))
window.hostingView.rootView = osdView
}
private func calculateFrame(for screen: NSScreen) -> NSRect {
let screenFrame = screen.frame
let centerX = screenFrame.midX - (osdWidth / 2)
// Position at bottom center, similar to native macOS OSD
let bottomOffset: CGFloat = 120 // Distance from bottom of screen
let centerY = screenFrame.minY + bottomOffset
return NSRect(x: centerX, y: centerY, width: osdWidth, height: osdHeight)
}
private func presentWindow(_ window: OSDWindow, targetFrame: NSRect) {
let startFrame = targetFrame.offsetBy(dx: 0, dy: -20)
window.nsWindow.setFrame(startFrame, display: true)
window.nsWindow.alphaValue = 0
window.nsWindow.orderFrontRegardless()
NSAnimationContext.runAnimationGroup { context in
context.duration = animationDuration
context.timingFunction = CAMediaTimingFunction(name: .easeOut)
window.nsWindow.animator().setFrame(targetFrame, display: true)
window.nsWindow.animator().alphaValue = 1
}
}
private func hideAllWindowsExcept(type: SneakContentType) {
if type != .volume, let volumeWindow = volumeWindow, volumeWindow.nsWindow.alphaValue > 0.01 {
hideWindowImmediately(volumeWindow)
}
if type != .brightness, let brightnessWindow = brightnessWindow, brightnessWindow.nsWindow.alphaValue > 0.01 {
hideWindowImmediately(brightnessWindow)
}
if type != .backlight, let backlightWindow = backlightWindow, backlightWindow.nsWindow.alphaValue > 0.01 {
hideWindowImmediately(backlightWindow)
}
}
private func hideWindowImmediately(_ window: OSDWindow) {
NSAnimationContext.runAnimationGroup { context in
context.duration = 0.15
context.timingFunction = CAMediaTimingFunction(name: .easeIn)
window.nsWindow.animator().alphaValue = 0
} completionHandler: {
window.nsWindow.orderOut(nil)
}
}
private func scheduleHide(for type: SneakContentType) {
hideWorkItem?.cancel()
let workItem = DispatchWorkItem { [weak self] in
guard let self else { return }
Task { @MainActor in
self.hideWindow(for: type)
}
}
hideWorkItem = workItem
DispatchQueue.main.asyncAfter(deadline: .now() + displayDuration, execute: workItem)
}
private func hideWindow(for type: SneakContentType) {
let window: OSDWindow?
switch type {
case .volume:
window = volumeWindow
case .brightness:
window = brightnessWindow
case .backlight:
window = backlightWindow
default:
return
}
guard let window else { return }
let currentFrame = window.nsWindow.frame
let hideFrame = currentFrame.offsetBy(dx: 0, dy: -20)
NSAnimationContext.runAnimationGroup { context in
context.duration = animationDuration
context.timingFunction = CAMediaTimingFunction(name: .easeIn)
window.nsWindow.animator().setFrame(hideFrame, display: true)
window.nsWindow.animator().alphaValue = 0
} completionHandler: {
window.nsWindow.orderOut(nil)
}
}
// MARK: - Cleanup
func tearDown() {
hideWorkItem?.cancel()
hideWorkItem = nil
volumeWindow?.nsWindow.orderOut(nil)
brightnessWindow?.nsWindow.orderOut(nil)
backlightWindow?.nsWindow.orderOut(nil)
volumeWindow = nil
brightnessWindow = nil
backlightWindow = nil
}
}
// MARK: - OSD Window Container
private struct OSDWindow {
let nsWindow: NSWindow
let hostingView: NSHostingView<CustomOSDView>
let type: SneakContentType
}
#endif

View file

@ -145,30 +145,61 @@ final class SystemChangesObserver: MediaKeyInterceptorDelegate {
if HUDSuppressionCoordinator.shared.shouldSuppressVolumeHUD { if HUDSuppressionCoordinator.shared.shouldSuppressVolumeHUD {
return return
} }
coordinator?.toggleSneakPeek(
status: true, // Send to custom OSD if enabled
type: .volume, if Defaults[.enableCustomOSD] && Defaults[.enableOSDVolume] {
value: CGFloat(value), Task { @MainActor in
icon: "" CustomOSDWindowManager.shared.showVolume(value: CGFloat(value))
) }
}
// Send to notch HUD if enabled and OSD is not enabled
if Defaults[.enableSystemHUD] && !Defaults[.enableCustomOSD] && Defaults[.enableVolumeHUD] {
coordinator?.toggleSneakPeek(
status: true,
type: .volume,
value: CGFloat(value),
icon: ""
)
}
} }
private func sendBrightnessNotification(value: Float) { private func sendBrightnessNotification(value: Float) {
coordinator?.toggleSneakPeek( // Send to custom OSD if enabled
status: true, if Defaults[.enableCustomOSD] && Defaults[.enableOSDBrightness] {
type: .brightness, Task { @MainActor in
value: CGFloat(value), CustomOSDWindowManager.shared.showBrightness(value: CGFloat(value))
icon: "" }
) }
// Send to notch HUD if enabled and OSD is not enabled
if Defaults[.enableSystemHUD] && !Defaults[.enableCustomOSD] && Defaults[.enableBrightnessHUD] {
coordinator?.toggleSneakPeek(
status: true,
type: .brightness,
value: CGFloat(value),
icon: ""
)
}
} }
private func sendKeyboardBacklightNotification(value: Float) { private func sendKeyboardBacklightNotification(value: Float) {
coordinator?.toggleSneakPeek( // Send to custom OSD if enabled
status: true, if Defaults[.enableCustomOSD] && Defaults[.enableOSDKeyboardBacklight] {
type: .backlight, Task { @MainActor in
value: CGFloat(value), CustomOSDWindowManager.shared.showBacklight(value: CGFloat(value))
icon: "" }
) }
// Send to notch HUD if enabled and OSD is not enabled
if Defaults[.enableSystemHUD] && !Defaults[.enableCustomOSD] && Defaults[.enableKeyboardBacklightHUD] {
coordinator?.toggleSneakPeek(
status: true,
type: .backlight,
value: CGFloat(value),
icon: ""
)
}
} }
private func configureKeyboardBacklightCallback() { private func configureKeyboardBacklightCallback() {

View file

@ -23,16 +23,40 @@ class SystemHUDManager {
} }
private func setupSettingsObserver() { private func setupSettingsObserver() {
// Observe master toggle // Observe master HUD toggle
Defaults.publisher(.enableSystemHUD, options: []).sink { [weak self] change in Defaults.publisher(.enableSystemHUD, options: []).sink { [weak self] change in
guard let self = self, self.isSetupComplete else {
return
}
Task { @MainActor in
if change.newValue && !Defaults[.enableCustomOSD] {
await self.startSystemObserver()
} else if !Defaults[.enableCustomOSD] {
// Only stop if OSD is also disabled
await self.stopSystemObserver()
}
}
}.store(in: &cancellables)
// Observe OSD toggle
Defaults.publisher(.enableCustomOSD, options: []).sink { [weak self] change in
guard let self = self, self.isSetupComplete else { guard let self = self, self.isSetupComplete else {
return return
} }
Task { @MainActor in Task { @MainActor in
if change.newValue { if change.newValue {
await self.startSystemHUD() // Start observer for OSD if not already running
if self.changesObserver == nil {
await self.startSystemObserver()
}
} else if Defaults[.enableSystemHUD] {
// Keep observer running for HUD
if self.changesObserver == nil {
await self.startSystemObserver()
}
} else { } else {
await self.stopSystemHUD() // Stop observer if both HUD and OSD are disabled
await self.stopSystemObserver()
} }
} }
}.store(in: &cancellables) }.store(in: &cancellables)
@ -70,6 +94,40 @@ class SystemHUDManager {
keyboardBacklightEnabled: change.newValue keyboardBacklightEnabled: change.newValue
) )
}.store(in: &cancellables) }.store(in: &cancellables)
// Observe individual OSD toggles
Defaults.publisher(.enableOSDVolume, options: []).sink { [weak self] change in
guard let self = self, self.isSetupComplete, Defaults[.enableCustomOSD] else {
return
}
self.changesObserver?.update(
volumeEnabled: change.newValue,
brightnessEnabled: Defaults[.enableOSDBrightness],
keyboardBacklightEnabled: Defaults[.enableOSDKeyboardBacklight]
)
}.store(in: &cancellables)
Defaults.publisher(.enableOSDBrightness, options: []).sink { [weak self] change in
guard let self = self, self.isSetupComplete, Defaults[.enableCustomOSD] else {
return
}
self.changesObserver?.update(
volumeEnabled: Defaults[.enableOSDVolume],
brightnessEnabled: change.newValue,
keyboardBacklightEnabled: Defaults[.enableOSDKeyboardBacklight]
)
}.store(in: &cancellables)
Defaults.publisher(.enableOSDKeyboardBacklight, options: []).sink { [weak self] change in
guard let self = self, self.isSetupComplete, Defaults[.enableCustomOSD] else {
return
}
self.changesObserver?.update(
volumeEnabled: Defaults[.enableOSDVolume],
brightnessEnabled: Defaults[.enableOSDBrightness],
keyboardBacklightEnabled: change.newValue
)
}.store(in: &cancellables)
} }
private var cancellables = Set<AnyCancellable>() private var cancellables = Set<AnyCancellable>()
@ -82,9 +140,15 @@ class SystemHUDManager {
func setup(coordinator: DynamicIslandViewCoordinator) { func setup(coordinator: DynamicIslandViewCoordinator) {
self.coordinator = coordinator self.coordinator = coordinator
if Defaults[.enableSystemHUD] { // Initialize OSD manager
Task { @MainActor in
CustomOSDWindowManager.shared.initialize()
}
// Start observer if either HUD or OSD is enabled
if Defaults[.enableSystemHUD] || Defaults[.enableCustomOSD] {
Task { @MainActor in Task { @MainActor in
await startSystemHUD() await startSystemObserver()
self.isSetupComplete = true self.isSetupComplete = true
} }
} else { } else {
@ -93,42 +157,55 @@ class SystemHUDManager {
} }
@MainActor @MainActor
private func startSystemHUD() async { private func startSystemObserver() async {
guard let coordinator = coordinator, !isSystemOperationInProgress else { return } guard let coordinator = coordinator, !isSystemOperationInProgress else { return }
isSystemOperationInProgress = true isSystemOperationInProgress = true
await stopSystemHUD() // Stop any existing observer await stopSystemObserver() // Stop any existing observer
changesObserver = SystemChangesObserver(coordinator: coordinator) changesObserver = SystemChangesObserver(coordinator: coordinator)
let volumeEnabled = Defaults[.enableVolumeHUD]
let brightnessEnabled = Defaults[.enableBrightnessHUD] // Determine which controls to enable based on HUD or OSD mode
let keyboardBacklightEnabled = Defaults[.enableKeyboardBacklightHUD] let volumeEnabled: Bool
let brightnessEnabled: Bool
let keyboardBacklightEnabled: Bool
if Defaults[.enableCustomOSD] {
volumeEnabled = Defaults[.enableOSDVolume]
brightnessEnabled = Defaults[.enableOSDBrightness]
keyboardBacklightEnabled = Defaults[.enableOSDKeyboardBacklight]
} else {
volumeEnabled = Defaults[.enableVolumeHUD]
brightnessEnabled = Defaults[.enableBrightnessHUD]
keyboardBacklightEnabled = Defaults[.enableKeyboardBacklightHUD]
}
changesObserver?.startObserving( changesObserver?.startObserving(
volumeEnabled: volumeEnabled, volumeEnabled: volumeEnabled,
brightnessEnabled: brightnessEnabled, brightnessEnabled: brightnessEnabled,
keyboardBacklightEnabled: keyboardBacklightEnabled keyboardBacklightEnabled: keyboardBacklightEnabled
) )
print("System HUD replacement started") print("System observer started (HUD: \(Defaults[.enableSystemHUD]), OSD: \(Defaults[.enableCustomOSD]))")
isSystemOperationInProgress = false isSystemOperationInProgress = false
} }
@MainActor @MainActor
private func stopSystemHUD() async { private func stopSystemObserver() async {
guard !isSystemOperationInProgress else { return } guard !isSystemOperationInProgress else { return }
isSystemOperationInProgress = true isSystemOperationInProgress = true
changesObserver?.stopObserving() changesObserver?.stopObserving()
changesObserver = nil changesObserver = nil
print("System HUD replacement stopped") print("System observer stopped")
isSystemOperationInProgress = false isSystemOperationInProgress = false
} }
deinit { deinit {
cancellables.removeAll() cancellables.removeAll()
Task { @MainActor in Task { @MainActor in
await stopSystemHUD() await stopSystemObserver()
} }
} }
} }

View file

@ -587,6 +587,14 @@ extension Defaults.Keys {
static let enableKeyboardBacklightHUD = Key<Bool>("enableKeyboardBacklightHUD", default: true) static let enableKeyboardBacklightHUD = Key<Bool>("enableKeyboardBacklightHUD", default: true)
static let systemHUDSensitivity = Key<Int>("systemHUDSensitivity", default: 5) static let systemHUDSensitivity = Key<Int>("systemHUDSensitivity", default: 5)
// MARK: Custom OSD Window Feature
static let enableCustomOSD = Key<Bool>("enableCustomOSD", default: false)
static let enableOSDVolume = Key<Bool>("enableOSDVolume", default: true)
static let enableOSDBrightness = Key<Bool>("enableOSDBrightness", default: true)
static let enableOSDKeyboardBacklight = Key<Bool>("enableOSDKeyboardBacklight", default: true)
static let osdMaterial = Key<OSDMaterial>("osdMaterial", default: .frosted)
static let osdIconColorStyle = Key<OSDIconColorStyle>("osdIconColorStyle", default: .white)
// MARK: Screen Recording Detection Feature // MARK: Screen Recording Detection Feature
static let enableScreenRecordingDetection = Key<Bool>("enableScreenRecordingDetection", default: true) static let enableScreenRecordingDetection = Key<Bool>("enableScreenRecordingDetection", default: true)
static let showRecordingIndicator = Key<Bool>("showRecordingIndicator", default: true) static let showRecordingIndicator = Key<Bool>("showRecordingIndicator", default: true)