Atoll/DynamicIsland/models/Constants.swift
Jis G Jacob 5bd84270aa Atoll: fullscreen album artwork as lockscreen wallpaper
Right-click on the album art in the lock screen music panel to set it
as the desktop wallpaper. Right-click again or click anywhere on the
background to restore the original wallpaper. Wallpaper also restores
automatically on track change or screen unlock.

Uses direct plist manipulation of the wallpaper config to avoid leaving
traces in the wallpaper picker. Album art PNG is pre-cached in the
background on track change for instant application.

Adds video artwork URL fetching from iTunes catalog for Apple Music
tracks (musicVideo entity search).

Signed-off-by: Jis G Jacob <studiokeys@blissroms.org>
2026-04-29 18:39:17 +02:00

1300 lines
61 KiB
Swift

/*
* Atoll (DynamicIsland)
* Copyright (C) 2024-2026 Atoll Contributors
*
* Originally from boring.notch project
* Modified and adapted for Atoll (DynamicIsland)
* See NOTICE for details.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import SwiftUI
import Defaults
import Lottie
import Foundation
private let availableDirectories = FileManager
.default
.urls(for: .documentDirectory, in: .userDomainMask)
let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
let bundleIdentifier = Bundle.main.bundleIdentifier!
let appVersion = "\(Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? "") (\(Bundle.main.infoDictionary?["CFBundleVersion"] as? String ?? ""))"
let temporaryDirectory = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).first!
let spacing: CGFloat = 16
struct CustomVisualizer: Codable, Hashable, Equatable, Defaults.Serializable {
let UUID: UUID
var name: String
var url: URL
var speed: CGFloat = 1.0
}
struct CustomAppIcon: Codable, Hashable, Equatable, Defaults.Serializable, Identifiable {
let id: UUID
var name: String
var fileName: String
var addedAt: Date
init(id: UUID = UUID(), name: String, fileName: String, addedAt: Date = .now) {
self.id = id
self.name = name
self.fileName = fileName
self.addedAt = addedAt
}
static let iconDirectory: URL = {
let base = FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask).first!
let dir = base.appendingPathComponent("DynamicIsland", isDirectory: true)
.appendingPathComponent("AppIcons", isDirectory: true)
try? FileManager.default.createDirectory(at: dir, withIntermediateDirectories: true)
return dir
}()
var fileURL: URL {
Self.iconDirectory.appendingPathComponent(fileName)
}
}
// MARK: - Custom Idle Animation Models
struct CustomIdleAnimation: Codable, Hashable, Equatable, Defaults.Serializable, Identifiable {
let id: UUID
var name: String
var source: AnimationSource
var speed: CGFloat = 1.0
var isBuiltIn: Bool = false // Track if it's bundled vs user-added
init(id: UUID = UUID(), name: String, source: AnimationSource, speed: CGFloat = 1.0, isBuiltIn: Bool = false) {
self.id = id
self.name = name
self.source = source
self.speed = speed
self.isBuiltIn = isBuiltIn
}
/// Get the effective transform config (override or default)
func getTransformConfig() -> AnimationTransformConfig {
let override = Defaults[.animationTransformOverrides][id.uuidString]
if let override = override {
print("📋 [CustomIdleAnimation] Found override for '\(name)': \(override)")
} else {
print("📋 [CustomIdleAnimation] No override for '\(name)', using default")
}
return override ?? .default
}
}
struct AnimationTransformConfig: Codable, Hashable, Equatable, Defaults.Serializable {
var scale: CGFloat = 1.0
var offsetX: CGFloat = 0
var offsetY: CGFloat = 0
var cropWidth: CGFloat = 30
var cropHeight: CGFloat = 20
var rotation: CGFloat = 0
var opacity: CGFloat = 1.0
var paddingBottom: CGFloat = 0 // Allow adjustment to fill notch from bottom
var expandWithAnimation: Bool = false // Whether notch should expand horizontally with animation
var loopMode: AnimationLoopMode = .loop // Loop mode for animation
static let `default` = AnimationTransformConfig()
}
enum AnimationLoopMode: String, Codable, CaseIterable {
case loop = "Loop"
case playOnce = "Play Once"
case autoReverse = "Auto Reverse"
var lottieLoopMode: LottieLoopMode {
switch self {
case .loop: return .loop
case .playOnce: return .playOnce
case .autoReverse: return .autoReverse
}
}
}
enum AnimationSource: Codable, Hashable, Equatable {
case lottieFile(URL) // Local file (in app support or bundle)
case lottieURL(URL) // Remote URL
var displayType: String {
switch self {
case .lottieFile: return "Local"
case .lottieURL: return "Remote"
}
}
}
// MARK: - Extension Authorization Models
enum ExtensionPermissionScope: String, CaseIterable, Codable, Defaults.Serializable {
case liveActivities
case lockScreenWidgets
case notchExperiences
case fileSharing
var displayName: String {
switch self {
case .liveActivities: return "Live Activities"
case .lockScreenWidgets: return "Lock Screen Widgets"
case .notchExperiences: return "Notch Experiences"
case .fileSharing: return "File Sharing"
}
}
}
enum ExtensionAuthorizationStatus: String, CaseIterable, Codable, Defaults.Serializable {
case pending
case authorized
case denied
case revoked
var isActive: Bool {
switch self {
case .authorized: return true
case .pending, .denied, .revoked: return false
}
}
}
struct ExtensionAuthorizationEntry: Codable, Defaults.Serializable, Identifiable, Hashable {
let bundleIdentifier: String
var appName: String
var status: ExtensionAuthorizationStatus
var allowedScopes: Set<ExtensionPermissionScope>
var requestedAt: Date
var grantedAt: Date?
var lastActivityAt: Date?
var lastDeniedReason: String?
var notes: String?
var id: String { bundleIdentifier }
init(
bundleIdentifier: String,
appName: String,
status: ExtensionAuthorizationStatus,
allowedScopes: Set<ExtensionPermissionScope> = Set(ExtensionPermissionScope.allCases),
requestedAt: Date = .now,
grantedAt: Date? = nil,
lastActivityAt: Date? = nil,
lastDeniedReason: String? = nil,
notes: String? = nil
) {
self.bundleIdentifier = bundleIdentifier
self.appName = appName
self.status = status
self.allowedScopes = allowedScopes
self.requestedAt = requestedAt
self.grantedAt = grantedAt
self.lastActivityAt = lastActivityAt
self.lastDeniedReason = lastDeniedReason
self.notes = notes
}
var isAuthorized: Bool { status.isActive }
}
struct ExtensionRateLimitRecord: Codable, Defaults.Serializable, Hashable, Identifiable {
let bundleIdentifier: String
var activityTimestamps: [Date]
var widgetTimestamps: [Date]
var notchExperienceTimestamps: [Date]
var id: String { bundleIdentifier }
init(
bundleIdentifier: String,
activityTimestamps: [Date] = [],
widgetTimestamps: [Date] = [],
notchExperienceTimestamps: [Date] = []
) {
self.bundleIdentifier = bundleIdentifier
self.activityTimestamps = activityTimestamps
self.widgetTimestamps = widgetTimestamps
self.notchExperienceTimestamps = notchExperienceTimestamps
}
private enum CodingKeys: String, CodingKey {
case bundleIdentifier
case activityTimestamps
case widgetTimestamps
case notchExperienceTimestamps
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
bundleIdentifier = try container.decode(String.self, forKey: .bundleIdentifier)
activityTimestamps = try container.decodeIfPresent([Date].self, forKey: .activityTimestamps) ?? []
widgetTimestamps = try container.decodeIfPresent([Date].self, forKey: .widgetTimestamps) ?? []
notchExperienceTimestamps = try container.decodeIfPresent([Date].self, forKey: .notchExperienceTimestamps) ?? []
}
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(bundleIdentifier, forKey: .bundleIdentifier)
try container.encode(activityTimestamps, forKey: .activityTimestamps)
try container.encode(widgetTimestamps, forKey: .widgetTimestamps)
try container.encode(notchExperienceTimestamps, forKey: .notchExperienceTimestamps)
}
}
enum CalendarSelectionState: Codable, Defaults.Serializable {
case all
case selected(Set<String>)
}
enum FantasticalViewStyle: String, CaseIterable, Codable, Defaults.Serializable {
case mini = "mini"
case calendar = "calendar"
var displayName: String {
switch self {
case .mini: return "Mini View"
case .calendar: return "Full Calendar"
}
}
}
enum ThirdPartyCalendarApp: String, CaseIterable, Codable, Defaults.Serializable, Identifiable {
case fantastical = "fantastical"
case notionCalendar = "notionCalendar"
var id: String { rawValue }
var displayName: String {
switch self {
case .fantastical: return "Fantastical"
case .notionCalendar: return "Notion Calendar"
}
}
/// Bundle identifiers to try when looking up the app icon (first match wins).
var bundleIdentifiers: [String] {
switch self {
case .fantastical: return ["com.flexibits.fantastical2.mac", "com.flexibits.fantastical"]
case .notionCalendar: return ["com.cron.electron"]
}
}
var fallbackIconName: String {
switch self {
case .fantastical: return "calendar.badge.clock"
case .notionCalendar: return "calendar.badge.plus"
}
}
var fallbackIconColor: Color {
switch self {
case .fantastical: return .red
case .notionCalendar: return .blue
}
}
}
enum ClipboardDisplayMode: String, CaseIterable, Codable, Defaults.Serializable {
case popover = "popover" // Traditional popover attached to button
case panel = "panel" // Floating panel near notch
case separateTab = "separateTab" // Separate tab in Dynamic Island
var displayName: String {
switch self {
case .popover: return String(localized: "Popover")
case .panel: return String(localized: "Panel")
case .separateTab: return String(localized: "Separate Tab")
}
}
var description: String {
switch self {
case .popover: return "Shows clipboard as a dropdown attached to the clipboard button"
case .panel: return "Shows clipboard in a floating panel near the notch"
case .separateTab: return "Shows copied items in a separate tab within the Dynamic Island (merges with Notes if enabled)"
}
}
}
enum ScreenAssistantDisplayMode: String, CaseIterable, Codable, Defaults.Serializable {
case popover = "popover" // Traditional popover attached to button
case panel = "panel" // Floating panel near notch
var displayName: String {
switch self {
case .popover: return String(localized: "Popover")
case .panel: return String(localized: "Panel")
}
}
var description: String {
switch self {
case .popover: return String(localized: "Shows screen assistant as a dropdown attached to the AI button")
case .panel: return String(localized: "Shows screen assistant in a floating panel near the notch")
}
}
}
enum ColorPickerDisplayMode: String, CaseIterable, Codable, Defaults.Serializable {
case popover = "popover" // Traditional popover attached to button
case panel = "panel" // Floating panel near notch
var displayName: String {
switch self {
case .popover: return "Popover"
case .panel: return "Panel"
}
}
var description: String {
switch self {
case .popover: return "Shows color picker as a dropdown attached to the color picker button"
case .panel: return "Shows color picker in a floating panel near the notch"
}
}
}
enum ThirdPartyDDCProvider: String, CaseIterable, Codable, Defaults.Serializable, Identifiable {
case betterDisplay
case lunar
var id: String { rawValue }
var displayName: String {
switch self {
case .betterDisplay:
return "BetterDisplay"
case .lunar:
return "Lunar"
}
}
/// Bundle identifiers to try when looking up the app icon.
var bundleIdentifiers: [String] {
switch self {
case .betterDisplay: return ["pro.betterdisplay.BetterDisplay"]
case .lunar: return ["fyi.lunar.Lunar"]
}
}
}
enum HideNotchOption: String, Defaults.Serializable {
case always
case nowPlayingOnly
case never
}
// Define notification names at file scope
extension Notification.Name {
static let mediaControllerChanged = Notification.Name("mediaControllerChanged")
}
// Media controller types for selection in settings
enum MediaControllerType: String, CaseIterable, Identifiable, Defaults.Serializable {
case nowPlaying = "Now Playing"
case appleMusic = "Apple Music"
case spotify = "Spotify"
case youtubeMusic = "Youtube Music"
case amazonMusic = "Amazon Music"
var id: String { self.rawValue }
var localizedName: String {
switch self {
case .nowPlaying: return String(localized: "Now Playing")
case .appleMusic: return String(localized: "Apple Music")
case .spotify: return String(localized: "Spotify")
case .youtubeMusic: return String(localized: "Youtube Music")
case .amazonMusic: return String(localized: "Amazon Music")
}
}
}
// Sneak peek styles for selection in settings
enum SneakPeekStyle: String, CaseIterable, Identifiable, Defaults.Serializable {
case standard = "Default"
case inline = "Inline"
var id: String { self.rawValue }
var LocalizedName : String {
switch self {
case .standard: return String(localized: "Default")
case .inline: return String(localized: "Inline")
}
}
}
enum CapsLockIndicatorTintMode: String, CaseIterable, Identifiable, Defaults.Serializable {
case green
case accent
case white
var id: String { rawValue }
var displayName: String {
switch self {
case .green:
return String(localized: "Green")
case .accent:
return String(localized: "Accent")
case .white:
return String(localized: "White")
}
}
var color: Color {
switch self {
case .green:
return .green
case .accent:
return .accentColor
case .white:
return .white
}
}
}
enum ProgressBarStyle: String, CaseIterable, Identifiable, Defaults.Serializable {
case hierarchical = "Hierarchical"
case gradient = "Gradient"
case segmented = "Segmented"
var id: String { self.rawValue }
}
enum MusicAuxiliaryControl: String, CaseIterable, Identifiable, Defaults.Serializable {
case shuffle
case repeatMode
case mediaOutput
case lyrics
var id: String { rawValue }
var displayName: String {
switch self {
case .shuffle:
return "Shuffle"
case .repeatMode:
return "Repeat"
case .mediaOutput:
return "Media Output"
case .lyrics:
return "Lyrics"
}
}
var symbolName: String {
switch self {
case .shuffle:
return "shuffle"
case .repeatMode:
return "repeat"
case .mediaOutput:
return "laptopcomputer"
case .lyrics:
return "quote.bubble"
}
}
static func alternative(
excluding control: MusicAuxiliaryControl,
preferring candidate: MusicAuxiliaryControl? = nil
) -> MusicAuxiliaryControl {
if let candidate, candidate != control {
return candidate
}
return allCases.first { $0 != control } ?? .shuffle
}
}
enum MusicSkipBehavior: String, CaseIterable, Identifiable, Defaults.Serializable {
case track
case tenSecond
var id: String { rawValue }
var displayName: String {
switch self {
case .track:
return String(localized: "Track Skip")
case .tenSecond:
return String(localized: "±10 Seconds")
}
}
var description: String {
switch self {
case .track:
return String(localized: "Standard previous/next track controls")
case .tenSecond:
return String(localized: "Skip forward or backward by ten seconds")
}
}
}
enum TimerIconColorMode: String, CaseIterable, Identifiable, Defaults.Serializable {
case adaptive = "Adaptive"
case solid = "Solid"
var id: String { rawValue }
var displayName: String {
switch self {
case .adaptive: return String(localized:"Adaptive gradient")
case .solid: return String(localized:"Solid colour")
}
}
}
enum TimerProgressStyle: String, CaseIterable, Identifiable, Defaults.Serializable {
case bar = "Bar"
case ring = "Ring"
var id: String { rawValue }
var localizedName: String {
switch self {
case .bar: return String(localized:"Bar")
case .ring: return String(localized:"Ring")
}
}
}
enum FocusMonitoringMode: String, CaseIterable, Identifiable, Defaults.Serializable {
case withoutDevTools = "withoutDevTools"
case useDevTools = "useDevTools"
var id: String { rawValue }
var displayName: String {
switch self {
case .withoutDevTools:
return "Use without DevTools"
case .useDevTools:
return "Use DevTools"
}
}
}
enum ReminderPresentationStyle: String, CaseIterable, Identifiable, Defaults.Serializable {
case ringCountdown = "Ring"
case digital = "Digital"
case minutes = "Minutes"
var id: String { rawValue }
var displayName: String {
switch self {
case .ringCountdown:
return String(localized: "Ring")
case .digital:
return String(localized: "Digital")
case .minutes:
return String(localized: "Minutes")
}
}
}
// AI Model types for screen assistant
enum AIModelProvider: String, CaseIterable, Identifiable, Defaults.Serializable {
case gemini = "Gemini"
case openai = "OpenAI GPT"
case claude = "Claude"
case local = "Local Model"
case groq = "Groq"
var id: String { self.rawValue }
var displayName: String {
return self.rawValue
}
var description: String {
switch self {
case .gemini: return "Google's Gemini AI with multimodal capabilities"
case .openai: return "OpenAI's GPT models with advanced reasoning"
case .claude: return "Anthropic's Claude with strong analytical skills"
case .local: return "Local AI model (Ollama or similar)"
case .groq: return "Groq's fast inference for OpenAI-compatible models"
}
}
var supportedModels: [AIModel] {
switch self {
case .gemini:
return [
// Gemini 2.5 Models (Latest)
AIModel(id: "gemini-2.5-pro", name: "Gemini 2.5 Pro", supportsThinking: true),
AIModel(id: "gemini-2.5-flash", name: "Gemini 2.5 Flash", supportsThinking: true),
AIModel(id: "gemini-2.5-flash-lite", name: "Gemini 2.5 Flash-Lite", supportsThinking: false),
AIModel(id: "gemini-2.5-flash-live", name: "Gemini 2.5 Flash Live", supportsThinking: false),
AIModel(id: "gemini-2.5-flash-native-audio", name: "Gemini 2.5 Flash Native Audio", supportsThinking: true),
// Gemini 2.0 Models
AIModel(id: "gemini-2.0-flash", name: "Gemini 2.0 Flash", supportsThinking: false),
AIModel(id: "gemini-2.0-flash-lite", name: "Gemini 2.0 Flash-Lite", supportsThinking: false),
AIModel(id: "gemini-2.0-flash-live", name: "Gemini 2.0 Flash Live", supportsThinking: false),
// Legacy 1.5 Models (for compatibility)
AIModel(id: "gemini-1.5-pro", name: "Gemini 1.5 Pro", supportsThinking: false),
AIModel(id: "gemini-1.5-flash", name: "Gemini 1.5 Flash", supportsThinking: false)
]
case .openai:
return [
AIModel(id: "gpt-4o", name: "GPT-4o", supportsThinking: false),
AIModel(id: "gpt-4o-mini", name: "GPT-4o Mini", supportsThinking: false),
AIModel(id: "o1-preview", name: "o1 Preview", supportsThinking: true),
AIModel(id: "o1-mini", name: "o1 Mini", supportsThinking: true)
]
case .claude:
return [
AIModel(id: "claude-3-5-sonnet", name: "Claude 3.5 Sonnet", supportsThinking: false),
AIModel(id: "claude-3-haiku", name: "Claude 3 Haiku", supportsThinking: false)
]
case .local:
return [
AIModel(id: "llama3.2", name: "Llama 3.2", supportsThinking: false),
AIModel(id: "qwen2.5", name: "Qwen 2.5", supportsThinking: false)
]
case .groq:
return [
AIModel(id: "llama-3.3-70b-versatile", name: "Llama 3.3 70B Versatile", supportsThinking: false),
AIModel(id: "llama-3.1-8b-instant", name: "Llama 3.1 8B Instant", supportsThinking: false),
AIModel(id: "qwen-qwq-32b", name: "Qwen QWQ 32B", supportsThinking: false),
AIModel(id: "mixtral-8x7b-32768", name: "Mixtral 8x7B", supportsThinking: false)
]
}
}
}
struct AIModel: Codable, Identifiable, Defaults.Serializable {
let id: String
let name: String
let supportsThinking: Bool
var displayName: String {
return name + (supportsThinking ? " (Thinking)" : "")
}
}
struct NoteItem: Codable, Identifiable, Defaults.Serializable, Hashable {
var id: UUID = UUID()
var title: String
var content: String
var creationDate: Date
var colorIndex: Int // 0: Yellow, 1: Blue, 2: Red, 3: Green
var isPinned: Bool = false
var imageFileName: String? = nil // Store filename instead of raw data
// Internal property for migration
private enum CodingKeys: String, CodingKey {
case id, title, content, creationDate, colorIndex, isPinned, imageFileName, imageData
}
init(id: UUID = UUID(), title: String, content: String, creationDate: Date, colorIndex: Int, isPinned: Bool = false, imageFileName: String? = nil) {
self.id = id
self.title = title
self.content = content
self.creationDate = creationDate
self.colorIndex = colorIndex
self.isPinned = isPinned
self.imageFileName = imageFileName
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
id = try container.decode(UUID.self, forKey: .id)
title = try container.decode(String.self, forKey: .title)
content = try container.decode(String.self, forKey: .content)
creationDate = try container.decode(Date.self, forKey: .creationDate)
colorIndex = try container.decode(Int.self, forKey: .colorIndex)
isPinned = try container.decode(Bool.self, forKey: .isPinned)
// Migration logic: if imageData exists but imageFileName doesn't, save it to disk
if let data = try container.decodeIfPresent(Data.self, forKey: .imageData) {
let fileName = "note_image_\(id.uuidString).png"
let fileURL = NoteItem.noteImageDataDirectory.appendingPathComponent(fileName)
try? data.write(to: fileURL)
imageFileName = fileName
} else {
imageFileName = try container.decodeIfPresent(String.self, forKey: .imageFileName)
}
}
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(id, forKey: .id)
try container.encode(title, forKey: .title)
try container.encode(content, forKey: .content)
try container.encode(creationDate, forKey: .creationDate)
try container.encode(colorIndex, forKey: .colorIndex)
try container.encode(isPinned, forKey: .isPinned)
try container.encode(imageFileName, forKey: .imageFileName)
}
static let colors: [Color] = [.yellow, .blue, .red, .green, .purple, .orange]
// Directory for storing note image files
static let noteImageDataDirectory: URL = {
let documentsPath = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
let notesDir = documentsPath.appendingPathComponent("NoteImages")
try? FileManager.default.createDirectory(at: notesDir, withIntermediateDirectories: true)
return notesDir
}()
var color: Color {
if colorIndex >= 0 && colorIndex < NoteItem.colors.count {
return NoteItem.colors[colorIndex]
}
return .yellow
}
// Helper to get image data from file
func getImageData() -> Data? {
guard let fileName = imageFileName else { return nil }
let fileURL = NoteItem.noteImageDataDirectory.appendingPathComponent(fileName)
return try? Data(contentsOf: fileURL)
}
}
extension Defaults.Keys {
// MARK: General
static let menubarIcon = Key<Bool>("menubarIcon", default: true)
static let showOnAllDisplays = Key<Bool>("showOnAllDisplays", default: false)
static let automaticallySwitchDisplay = Key<Bool>("automaticallySwitchDisplay", default: true)
static let releaseName = Key<String>("releaseName", default: "Seychelles")
static let hideDynamicIslandFromScreenCapture = Key<Bool>("hideDynamicIslandFromScreenCapture", default: false)
// MARK: Behavior
static let minimumHoverDuration = Key<TimeInterval>("minimumHoverDuration", default: 0.3)
static let enableHaptics = Key<Bool>("enableHaptics", default: true)
static let openNotchOnHover = Key<Bool>("openNotchOnHover", default: true)
static let extendHoverArea = Key<Bool>("extendHoverArea", default: false)
static let externalDisplayStyle = Key<ExternalDisplayStyle>(
"externalDisplayStyle",
default: .notch
)
static let hideNonNotchUntilHover = Key<Bool>("hideNonNotchUntilHover", default: false)
static let notchHeightMode = Key<WindowHeightMode>(
"notchHeightMode",
default: WindowHeightMode.matchRealNotchSize
)
static let nonNotchHeightMode = Key<WindowHeightMode>(
"nonNotchHeightMode",
default: WindowHeightMode.matchMenuBar
)
static let nonNotchHeight = Key<CGFloat>("nonNotchHeight", default: 32)
static let notchHeight = Key<CGFloat>("notchHeight", default: 32)
static let openNotchWidth = Key<CGFloat>("openNotchWidth", default: 640)
//static let openLastTabByDefault = Key<Bool>("openLastTabByDefault", default: false)
// MARK: Appearance
static let showEmojis = Key<Bool>("showEmojis", default: false)
//static let alwaysShowTabs = Key<Bool>("alwaysShowTabs", default: true)
static let showMirror = Key<Bool>("showMirror", default: false)
static let mirrorShape = Key<MirrorShapeEnum>("mirrorShape", default: MirrorShapeEnum.rectangle)
static let selectedCameraID = Key<String>("selectedCameraID", default: "")
static let settingsIconInNotch = Key<Bool>("settingsIconInNotch", default: true)
static let lightingEffect = Key<Bool>("lightingEffect", default: true)
static let accentColor = Key<Color>("accentColor", default: Color.blue)
static let enableShadow = Key<Bool>("enableShadow", default: true)
static let cornerRadiusScaling = Key<Bool>("cornerRadiusScaling", default: true)
static let useModernCloseAnimation = Key<Bool>("useModernCloseAnimation", default: true)
static let showNotHumanFace = Key<Bool>("showNotHumanFace", default: false)
static let customIdleAnimations = Key<[CustomIdleAnimation]>("customIdleAnimations", default: [])
static let selectedIdleAnimation = Key<CustomIdleAnimation?>("selectedIdleAnimation", default: nil)
static let animationTransformOverrides = Key<[String: AnimationTransformConfig]>("animationTransformOverrides", default: [:])
static let tileShowLabels = Key<Bool>("tileShowLabels", default: false)
static let showCalendar = Key<Bool>("showCalendar", default: true)
static let hideCompletedReminders = Key<Bool>("hideCompletedReminders", default: true)
static let hideAllDayEvents = Key<Bool>("hideAllDayEvents", default: false)
static let sliderColor = Key<SliderColorEnum>(
"sliderUseAlbumArtColor",
default: SliderColorEnum.white
)
static let playerColorTinting = Key<Bool>("playerColorTinting", default: true)
static let useMusicVisualizer = Key<Bool>("useMusicVisualizer", default: true)
static let customVisualizers = Key<[CustomVisualizer]>("customVisualizers", default: [])
static let selectedVisualizer = Key<CustomVisualizer?>("selectedVisualizer", default: nil)
static let customAppIcons = Key<[CustomAppIcon]>("customAppIcons", default: [])
static let selectedAppIconID = Key<String?>("selectedAppIconID", default: nil)
// MARK: Gestures
static let enableGestures = Key<Bool>("enableGestures", default: true)
static let closeGestureEnabled = Key<Bool>("closeGestureEnabled", default: true)
static let gestureSensitivity = Key<CGFloat>("gestureSensitivity", default: 200.0)
static let enableHorizontalMusicGestures = Key<Bool>("enableHorizontalMusicGestures", default: true)
static let musicGestureBehavior = Key<MusicSkipBehavior>("musicGestureBehavior", default: .track)
static let reverseSwipeGestures = Key<Bool>("reverseSwipeGestures", default: false)
static let reverseScrollGestures = Key<Bool>("reverseScrollGestures", default: false)
// MARK: Media playback
static let coloredSpectrogram = Key<Bool>("coloredSpectrogram", default: true)
static let enableRealTimeWaveform = Key<Bool>("enableRealTimeWaveform", default: false)
static let enableSneakPeek = Key<Bool>("enableSneakPeek", default: false)
static let sneakPeekStyles = Key<SneakPeekStyle>("sneakPeekStyles", default: .standard)
static let showSneakPeekOnTrackChange = Key<Bool>("showSneakPeekOnTrackChange", default: true)
static let enableFullscreenMediaDetection = Key<Bool>("enableFullscreenMediaDetection", default: true)
static let parallaxEffectIntensity = Key<Double>("parallaxEffectIntensity", default: 6.0)
static let waitInterval = Key<Double>("waitInterval", default: 3)
static let showShuffleAndRepeat = Key<Bool>("showShuffleAndRepeat", default: true)
static let showMediaOutputControl = Key<Bool>("showMediaOutputControl", default: true)
static let musicAuxLeftControl = Key<MusicAuxiliaryControl>("musicAuxLeftControl", default: .shuffle)
static let musicAuxRightControl = Key<MusicAuxiliaryControl>("musicAuxRightControl", default: .repeatMode)
static let didMigrateMusicAuxControls = Key<Bool>("didMigrateMusicAuxControls", default: false)
static let musicControlSlots = Key<[MusicControlButton]>("musicControlSlots", default: MusicControlButton.defaultLayout)
static let didMigrateMusicControlSlots = Key<Bool>("didMigrateMusicControlSlots", default: false)
static let musicSkipBehavior = Key<MusicSkipBehavior>("musicSkipBehavior", default: .track)
static let musicControlWindowEnabled = Key<Bool>("musicControlWindowEnabled", default: false)
static let showStandardMediaControls = Key<Bool>("showStandardMediaControls", default: true)
static let autoHideInactiveNotchMediaPlayer = Key<Bool>("autoHideInactiveNotchMediaPlayer", default: true)
static let cachedMusicLiveActivityPreference = Key<Bool?>("cachedMusicLiveActivityPreference", default: nil)
static let cachedLockScreenMediaWidgetPreference = Key<Bool?>("cachedLockScreenMediaWidgetPreference", default: nil)
static let cachedMusicControlWindowPreference = Key<Bool?>("cachedMusicControlWindowPreference", default: nil)
// Enable lock screen media widget (shows the standalone panel when screen is locked)
static let enableLockScreenMediaWidget = Key<Bool>("enableLockScreenMediaWidget", default: true)
static let enableLockScreenWeatherWidget = Key<Bool>("enableLockScreenWeatherWidget", default: true)
static let enableLockScreenFocusWidget = Key<Bool>("enableLockScreenFocusWidget", default: true)
static let enableLockScreenReminderWidget = Key<Bool>("enableLockScreenReminderWidget", default: true)
static let enableLockScreenTimerWidget = Key<Bool>("enableLockScreenTimerWidget", default: true)
static let lockScreenWeatherRefreshInterval = Key<TimeInterval>("lockScreenWeatherRefreshInterval", default: 30 * 60)
static let lockScreenWeatherShowsLocation = Key<Bool>("lockScreenWeatherShowsLocation", default: true)
static let lockScreenWeatherShowsSunrise = Key<Bool>("lockScreenWeatherShowsSunrise", default: true)
static let lockScreenWeatherWidgetStyle = Key<LockScreenWeatherWidgetStyle>("lockScreenWeatherWidgetStyle", default: .inline)
static let lockScreenWeatherTemperatureUnit = Key<LockScreenWeatherTemperatureUnit>("lockScreenWeatherTemperatureUnit", default: .celsius)
static let lockScreenWeatherShowsAQI = Key<Bool>("lockScreenWeatherShowsAQI", default: true)
static let lockScreenWeatherAQIScale = Key<LockScreenWeatherAirQualityScale>("lockScreenWeatherAQIScale", default: .us)
static let lockScreenWeatherUsesGaugeTint = Key<Bool>("lockScreenWeatherUsesGaugeTint", default: false)
static let lockScreenWeatherProviderSource = Key<LockScreenWeatherProviderSource>("lockScreenWeatherProviderSource", default: .openMeteo)
static let lockScreenWeatherVerticalOffset = Key<Double>("lockScreenWeatherVerticalOffset", default: 0)
static let lockScreenMusicVerticalOffset = Key<Double>("lockScreenMusicVerticalOffset", default: 0)
static let lockScreenMusicPanelWidth = Key<Double>("lockScreenMusicPanelWidth", default: 350)
static let lockScreenMusicAlbumParallaxEnabled = Key<Bool>("lockScreenMusicAlbumParallaxEnabled", default: false)
static let lockScreenTimerVerticalOffset = Key<Double>("lockScreenTimerVerticalOffset", default: 0)
static let lockScreenTimerWidgetWidth = Key<Double>("lockScreenTimerWidgetWidth", default: 350)
static let lockScreenGlassStyle = Key<LockScreenGlassStyle>("lockScreenGlassStyle", default: .liquid)
static let lockScreenGlassCustomizationMode = Key<LockScreenGlassCustomizationMode>(
"lockScreenGlassCustomizationMode",
default: .standard
)
static let lockScreenTimerGlassStyle = Key<LockScreenGlassStyle>("lockScreenTimerGlassStyle", default: .frosted)
static let lockScreenTimerGlassCustomizationMode = Key<LockScreenGlassCustomizationMode>(
"lockScreenTimerGlassCustomizationMode",
default: .standard
)
static let lockScreenMusicLiquidGlassVariant = Key<LiquidGlassVariant>(
"lockScreenMusicLiquidGlassVariant",
default: .defaultVariant
)
static let lockScreenTimerLiquidGlassVariant = Key<LiquidGlassVariant>(
"lockScreenTimerLiquidGlassVariant",
default: .defaultVariant
)
static let lockScreenShowAppIcon = Key<Bool>("lockScreenShowAppIcon", default: false)
static let lockScreenPanelShowsBorder = Key<Bool>("lockScreenPanelShowsBorder", default: false)
static let lockScreenMusicUsesEnhancedLiquidBorder = Key<Bool>(
"lockScreenMusicUsesEnhancedLiquidBorder",
default: true
)
static let lockScreenPanelUsesBlur = Key<Bool>("lockScreenPanelUsesBlur", default: true)
static let lockScreenMusicMergedAirPlayOutput = Key<Bool>("lockScreenMusicMergedAirPlayOutput", default: true)
static let lockScreenMusicFullscreenArtworkEnabled = Key<Bool>("lockScreenMusicFullscreenArtworkEnabled", default: true)
static let lockScreenMusicFullscreenVideoArtwork = Key<Bool>("lockScreenMusicFullscreenVideoArtwork", default: true)
static let lockScreenTimerWidgetUsesBlur = Key<Bool>("lockScreenTimerWidgetUsesBlur", default: false)
static let lockScreenReminderChipStyle = Key<LockScreenReminderChipStyle>("lockScreenReminderChipStyle", default: .eventColor)
static let lockScreenReminderWidgetHorizontalAlignment = Key<String>("lockScreenReminderWidgetHorizontalAlignment", default: "center")
static let lockScreenReminderWidgetVerticalOffset = Key<Double>("lockScreenReminderWidgetVerticalOffset", default: 0)
static let lockScreenShowCalendarEvent = Key<Bool>("lockScreenShowCalendarEvent", default: true)
static let lockScreenCalendarEventLookaheadWindow = Key<String>("lockScreenCalendarEventLookaheadWindow", default: "3h")
static let lockScreenCalendarSelectionMode = Key<String>("lockScreenCalendarSelectionMode", default: "all")
static let lockScreenSelectedCalendarIDs = Key<Set<String>>("lockScreenSelectedCalendarIDs", default: [])
static let lockScreenShowCalendarCountdown = Key<Bool>("lockScreenShowCalendarCountdown", default: true)
static let lockScreenShowCalendarEventEntireDuration = Key<Bool>("lockScreenShowCalendarEventEntireDuration", default: true)
static let lockScreenShowCalendarEventAfterStartEnabled = Key<Bool>("lockScreenShowCalendarEventAfterStartEnabled", default: false)
static let lockScreenShowCalendarEventAfterStartWindow = Key<String>("lockScreenShowCalendarEventAfterStartWindow", default: "5m")
static let lockScreenShowCalendarTimeRemaining = Key<Bool>("lockScreenShowCalendarTimeRemaining", default: true)
static let lockScreenShowCalendarStartTimeAfterBegins = Key<Bool>("lockScreenShowCalendarStartTimeAfterBegins", default: true)
static let lockScreenWeatherWidgetRowOrder = Key<String>("lockScreenWeatherWidgetRowOrder", default: "weather_calendar_focus")
// MARK: Third-party Calendar Integration
static let enableThirdPartyCalendarApp = Key<Bool>("enableThirdPartyCalendarApp", default: false)
static let selectedCalendarApp = Key<ThirdPartyCalendarApp>("selectedCalendarApp", default: .fantastical)
static let fantasticalDefaultView = Key<FantasticalViewStyle>("fantasticalDefaultView", default: .mini)
// MARK: Battery
static let showPowerStatusNotifications = Key<Bool>("showPowerStatusNotifications", default: true)
static let showBatteryIndicator = Key<Bool>("showBatteryIndicator", default: BatteryActivityManager.shared.hasBattery())
static let showBatteryPercentage = Key<Bool>("showBatteryPercentage", default: true)
static let showPowerStatusIcons = Key<Bool>("showPowerStatusIcons", default: true)
static let playLowBatteryAlertSound = Key<Bool>("playLowBatteryAlertSound", default: true)
static let lockScreenBatteryShowsBatteryGauge = Key<Bool>(
"lockScreenWeatherShowsBatteryGauge",
default: BatteryActivityManager.shared.hasBattery()
)
static let lockScreenBatteryUsesLaptopSymbol = Key<Bool>("lockScreenWeatherBatteryUsesLaptopSymbol", default: true)
static let lockScreenBatteryShowsCharging = Key<Bool>("lockScreenWeatherShowsCharging", default: true)
static let lockScreenBatteryShowsChargingPercentage = Key<Bool>("lockScreenWeatherShowsChargingPercentage", default: true)
static let lockScreenBatteryShowsBluetooth = Key<Bool>("lockScreenWeatherShowsBluetooth", default: true)
// MARK: Downloads
static let enableDownloadListener = Key<Bool>("enableDownloadListener", default: true)
static let enableSafariDownloads = Key<Bool>("enableSafariDownloads", default: true)
static let selectedDownloadIndicatorStyle = Key<DownloadIndicatorStyle>("selectedDownloadIndicatorStyle", default: DownloadIndicatorStyle.progress)
static let selectedDownloadIconStyle = Key<DownloadIconStyle>("selectedDownloadIconStyle", default: DownloadIconStyle.onlyAppIcon)
// MARK: HUD
static let inlineHUD = Key<Bool>("inlineHUD", default: true)
static let progressBarStyle = Key<ProgressBarStyle>("progressBarStyle", default: .hierarchical)
// Legacy support - keeping for backward compatibility
static let enableGradient = Key<Bool>("enableGradient", default: false)
static let systemEventIndicatorShadow = Key<Bool>("systemEventIndicatorShadow", default: false)
static let systemEventIndicatorUseAccent = Key<Bool>("systemEventIndicatorUseAccent", default: false)
static let showProgressPercentages = Key<Bool>("showProgressPercentages", default: true)
// MARK: Shelf
static let dynamicShelf = Key<Bool>("dynamicShelf", default: true)
static let openShelfByDefault = Key<Bool>("openShelfByDefault", default: true)
static let quickShareProvider = Key<String>("quickShareProvider", default: "AirDrop")
static let localSendSelectedDeviceID = Key<String>("localSendSelectedDeviceID", default: "")
static let localSendDevicePickerGlassMode = Key<LockScreenGlassCustomizationMode>("localSendDevicePickerGlassMode", default: .standard)
static let localSendDevicePickerLiquidGlassVariant = Key<LiquidGlassVariant>("localSendDevicePickerLiquidGlassVariant", default: .v11)
static let copyOnDrag = Key<Bool>("copyOnDrag", default: false)
static let autoRemoveShelfItems = Key<Bool>("autoRemoveShelfItems", default: false)
static let expandedDragDetection = Key<Bool>("expandedDragDetection", default: true)
// MARK: Calendar
static let calendarSelectionState = Key<CalendarSelectionState>("calendarSelectionState", default: .all)
static let showFullEventTitles = Key<Bool>("showFullEventTitles", default: false)
static let autoScrollToNextEvent = Key<Bool>("autoScrollToNextEvent", default: true)
// MARK: Fullscreen Media Detection
static let alwaysHideInFullscreen = Key<Bool>("alwaysHideInFullscreen", default: false)
static let hideNotchOption = Key<HideNotchOption>("hideNotchOption", default: .nowPlayingOnly)
// MARK: Wobble Animation
static let enableWobbleAnimation = Key<Bool>("enableWobbleAnimation", default: false)
// MARK: Media Controller
static let mediaController = Key<MediaControllerType>("mediaController", default: defaultMediaController)
static let spotifySPDCCookie = Key<String>("spotifySPDCCookie", default: "")
static let spotifyAuthAccessToken = Key<String>("spotifyAuthAccessToken", default: "")
static let spotifyAuthAccessTokenExpiration = Key<Double>("spotifyAuthAccessTokenExpiration", default: 0)
static let spotifyAuthLastValidatedAt = Key<Double>("spotifyAuthLastValidatedAt", default: 0)
// MARK: Bluetooth Audio Devices
static let showBluetoothDeviceConnections = Key<Bool>("showBluetoothDeviceConnections", default: true)
static let useColorCodedBatteryDisplay = Key<Bool>("useColorCodedBatteryDisplay", default: true)
static let useColorCodedVolumeDisplay = Key<Bool>("useColorCodedVolumeDisplay", default: true)
static let useSmoothColorGradient = Key<Bool>("useSmoothColorGradient", default: true)
static let useCircularBluetoothBatteryIndicator = Key<Bool>("useCircularBluetoothBatteryIndicator", default: true)
static let showBluetoothBatteryPercentageText = Key<Bool>("showBluetoothBatteryPercentageText", default: false)
static let showBluetoothDeviceNameMarquee = Key<Bool>("showBluetoothDeviceNameMarquee", default: false)
static let useBluetoothHUD3DIcon = Key<Bool>("useBluetoothHUD3DIcon", default: true)
// MARK: Stats Feature
static let enableStatsFeature = Key<Bool>("enableStatsFeature", default: false)
static let autoStartStatsMonitoring = Key<Bool>("autoStartStatsMonitoring", default: true)
static let statsStopWhenNotchCloses = Key<Bool>("statsStopWhenNotchCloses", default: true)
static let statsUpdateInterval = Key<Double>("statsUpdateInterval", default: 1.0)
static let showCpuGraph = Key<Bool>("showCpuGraph", default: true)
static let showMemoryGraph = Key<Bool>("showMemoryGraph", default: true)
static let showGpuGraph = Key<Bool>("showGpuGraph", default: true)
static let showNetworkGraph = Key<Bool>("showNetworkGraph", default: false)
static let showDiskGraph = Key<Bool>("showDiskGraph", default: false)
static let cpuTemperatureUnit = Key<LockScreenWeatherTemperatureUnit>("cpuTemperatureUnit", default: .celsius)
// MARK: Terminal Feature
static let enableTerminalFeature = Key<Bool>("enableTerminalFeature", default: false)
static let terminalShellPath = Key<String>("terminalShellPath", default: "/bin/zsh")
static let terminalFontFamily = Key<String>("terminalFontFamily", default: "")
static let terminalFontSize = Key<Double>("terminalFontSize", default: 12.0)
static let terminalOpacity = Key<Double>("terminalOpacity", default: 1.0)
static let terminalMaxHeightFraction = Key<Double>("terminalMaxHeightFraction", default: 0.4)
static let terminalCursorStyle = Key<String>("terminalCursorStyle", default: "blinkBlock")
static let terminalScrollbackLines = Key<Int>("terminalScrollbackLines", default: 1000)
static let terminalOptionAsMeta = Key<Bool>("terminalOptionAsMeta", default: true)
static let terminalMouseReporting = Key<Bool>("terminalMouseReporting", default: true)
static let terminalBoldAsBright = Key<Bool>("terminalBoldAsBright", default: true)
static let terminalBackgroundColor = Key<Color>("terminalBackgroundColor", default: .black)
static let terminalForegroundColor = Key<Color>("terminalForegroundColor", default: .white)
static let terminalCursorColor = Key<Color>("terminalCursorColor", default: Color(.selectedControlColor))
static let terminalStickyMode = Key<Bool>("terminalStickyMode", default: false)
// MARK: Timer Feature
static let enableTimerFeature = Key<Bool>("enableTimerFeature", default: true)
static let timerDisplayMode = Key<TimerDisplayMode>("timerDisplayMode", default: .tab)
static let timerPresets = Key<[TimerPreset]>("timerPresets", default: TimerPreset.defaultPresets)
static let showTimerPresetsInNotchTab = Key<Bool>("showTimerPresetsInNotchTab", default: true)
static let timerIconColorMode = Key<TimerIconColorMode>("timerIconColorMode", default: .adaptive)
static let timerSolidColor = Key<Color>("timerSolidColor", default: .blue)
static let timerShowsCountdown = Key<Bool>("timerShowsCountdown", default: true)
static let timerShowsLabel = Key<Bool>("timerShowsLabel", default: false)
static let timerShowsProgress = Key<Bool>("timerShowsProgress", default: true)
static let timerProgressStyle = Key<TimerProgressStyle>("timerProgressStyle", default: .bar)
static let mirrorSystemTimer = Key<Bool>("mirrorSystemTimer", default: true)
// MARK: Reminder Live Activity
static let enableReminderLiveActivity = Key<Bool>("enableReminderLiveActivity", default: true)
static let reminderPresentationStyle = Key<ReminderPresentationStyle>("reminderPresentationStyle", default: .ringCountdown)
static let reminderLeadTime = Key<Int>("reminderLeadTime", default: 5)
static let reminderSneakPeekDuration = Key<Double>("reminderSneakPeekDuration", default: 5)
static let timerControlWindowEnabled = Key<Bool>("timerControlWindowEnabled", default: true)
// MARK: ColorPicker Feature
static let enableColorPickerFeature = Key<Bool>("enableColorPickerFeature", default: true)
static let showColorFormats = Key<Bool>("showColorFormats", default: true)
static let colorPickerDisplayMode = Key<ColorPickerDisplayMode>("colorPickerDisplayMode", default: .panel)
static let colorHistorySize = Key<Int>("colorHistorySize", default: 10)
static let showColorPickerIcon = Key<Bool>("showColorPickerIcon", default: true)
// MARK: Clipboard Feature
static let enableClipboardManager = Key<Bool>("enableClipboardManager", default: true)
static let clipboardHistorySize = Key<Int>("clipboardHistorySize", default: 3)
static let showClipboardIcon = Key<Bool>("showClipboardIcon", default: true)
static let clipboardDisplayMode = Key<ClipboardDisplayMode>("clipboardDisplayMode", default: .panel)
// MARK: Screen Assistant Feature
static let enableScreenAssistant = Key<Bool>("enableScreenAssistant", default: true)
static let screenAssistantDisplayMode = Key<ScreenAssistantDisplayMode>("screenAssistantDisplayMode", default: .panel)
static let geminiApiKey = Key<String>("geminiApiKey", default: "")
static let openaiApiKey = Key<String>("openaiApiKey", default: "")
static let claudeApiKey = Key<String>("claudeApiKey", default: "")
static let groqApiKey = Key<String>("groqApiKey", default: "")
static let selectedAIProvider = Key<AIModelProvider>("selectedAIProvider", default: .gemini)
static let selectedAIModel = Key<AIModel?>("selectedAIModel", default: nil)
static let enableThinkingMode = Key<Bool>("enableThinkingMode", default: false)
static let localModelEndpoint = Key<String>("localModelEndpoint", default: "http://localhost:11434")
// MARK: Third-Party Extensions
static let enableThirdPartyExtensions = Key<Bool>("enableThirdPartyExtensions", default: true)
static let enableExtensionLiveActivities = Key<Bool>("enableExtensionLiveActivities", default: true)
static let enableExtensionLockScreenWidgets = Key<Bool>("enableExtensionLockScreenWidgets", default: true)
static let enableExtensionNotchExperiences = Key<Bool>("enableExtensionNotchExperiences", default: true)
static let enableExtensionNotchTabs = Key<Bool>("enableExtensionNotchTabs", default: true)
static let enableExtensionNotchMinimalisticOverrides = Key<Bool>("enableExtensionNotchMinimalisticOverrides", default: true)
static let enableExtensionNotchInteractiveWebViews = Key<Bool>("enableExtensionNotchInteractiveWebViews", default: true)
static let extensionAuthorizationEntries = Key<[ExtensionAuthorizationEntry]>("extensionAuthorizationEntries", default: [])
static let extensionRateLimitRecords = Key<[ExtensionRateLimitRecord]>("extensionRateLimitRecords", default: [])
static let extensionDiagnosticsLoggingEnabled = Key<Bool>("extensionDiagnosticsLoggingEnabled", default: true)
static let extensionLiveActivityCapacity = Key<Int>("extensionLiveActivityCapacity", default: 4)
static let extensionLockScreenWidgetCapacity = Key<Int>("extensionLockScreenWidgetCapacity", default: 4)
static let extensionNotchExperienceCapacity = Key<Int>("extensionNotchExperienceCapacity", default: 2)
static let enableExtensionFileSharing = Key<Bool>("enableExtensionFileSharing", default: true)
// MARK: Keyboard Shortcuts
static let enableShortcuts = Key<Bool>("enableShortcuts", default: true)
// MARK: System HUD Feature
static let enableSystemHUD = Key<Bool>("enableSystemHUD", default: true)
static let enableVolumeHUD = Key<Bool>("enableVolumeHUD", default: true)
static let enableBrightnessHUD = Key<Bool>("enableBrightnessHUD", default: true)
static let enableKeyboardBacklightHUD = Key<Bool>("enableKeyboardBacklightHUD", default: true)
static let systemHUDSensitivity = Key<Int>("systemHUDSensitivity", default: 5)
static let playVolumeChangeFeedback = Key<Bool>("playVolumeChangeFeedback", default: false)
// MARK: Custom OSD Window Feature
static let enableCustomOSD = Key<Bool>("enableCustomOSD", default: false)
static let enableVerticalHUD = Key<Bool>("enableVerticalHUD", default: false)
static let enableCircularHUD = Key<Bool>("enableCircularHUD", default: false)
static let verticalHUDPosition = Key<String>("verticalHUDPosition", default: "right") // "left" or "right"
// Vertical HUD Customization
static let verticalHUDShowValue = Key<Bool>("verticalHUDShowValue", default: true)
static let verticalHUDInteractive = Key<Bool>("verticalHUDInteractive", default: true)
static let verticalHUDHeight = Key<CGFloat>("verticalHUDHeight", default: 160)
static let verticalHUDWidth = Key<CGFloat>("verticalHUDWidth", default: 36)
static let verticalHUDPadding = Key<CGFloat>("verticalHUDPadding", default: 24)
static let verticalHUDUseAccentColor = Key<Bool>("verticalHUDUseAccentColor", default: false)
static let verticalHUDMaterial = Key<OSDMaterial>("verticalHUDMaterial", default: .frosted)
static let verticalHUDLiquidGlassCustomizationMode = Key<LockScreenGlassCustomizationMode>(
"verticalHUDLiquidGlassCustomizationMode",
default: .standard
)
static let verticalHUDLiquidGlassVariant = Key<LiquidGlassVariant>(
"verticalHUDLiquidGlassVariant",
default: .defaultVariant
)
// Circular HUD Customization
static let circularHUDShowValue = Key<Bool>("circularHUDShowValue", default: true)
static let circularHUDSize = Key<CGFloat>("circularHUDSize", default: 65)
static let circularHUDStrokeWidth = Key<CGFloat>("circularHUDStrokeWidth", default: 4)
static let circularHUDUseAccentColor = Key<Bool>("circularHUDUseAccentColor", default: true)
// MARK: Third-Party DDC Integration
static let enableThirdPartyDDCIntegration = Key<Bool>("enableThirdPartyDDCIntegration", default: false)
static let thirdPartyDDCProvider = Key<ThirdPartyDDCProvider>("thirdPartyDDCProvider", default: .betterDisplay)
static let enableExternalVolumeControlListener = Key<Bool>("enableExternalVolumeControlListener", default: false)
static let didMigrateThirdPartyDDCIntegration = Key<Bool>("didMigrateThirdPartyDDCIntegration", default: false)
// Legacy keys retained for migration/backward compatibility
static let enableBetterDisplayIntegration = Key<Bool>("enableBetterDisplayIntegration", default: false)
static let enableLunarIntegration = Key<Bool>("enableLunarIntegration", default: false)
static let hasSeenOSDAlphaWarning = Key<Bool>("hasSeenOSDAlphaWarning", 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 osdLiquidGlassCustomizationMode = Key<LockScreenGlassCustomizationMode>(
"osdLiquidGlassCustomizationMode",
default: .standard
)
static let osdLiquidGlassVariant = Key<LiquidGlassVariant>(
"osdLiquidGlassVariant",
default: .defaultVariant
)
static let osdIconColorStyle = Key<OSDIconColorStyle>("osdIconColorStyle", default: .white)
// MARK: Screen Recording Detection Feature
static let enableScreenRecordingDetection = Key<Bool>("enableScreenRecordingDetection", default: true)
static let showRecordingIndicator = Key<Bool>("showRecordingIndicator", default: true)
// Polling removed - now uses event-driven private API detection (CGSIsScreenWatcherPresent)
// static let enableScreenRecordingPolling = Key<Bool>("enableScreenRecordingPolling", default: false)
// MARK: Focus / Do Not Disturb Detection
static let enableDoNotDisturbDetection = Key<Bool>("enableDoNotDisturbDetection", default: true)
static let focusMonitoringMode = Key<FocusMonitoringMode>("focusMonitoringMode", default: .withoutDevTools)
static let showDoNotDisturbIndicator = Key<Bool>("showDoNotDisturbIndicator", default: true)
static let showDoNotDisturbLabel = Key<Bool>("showDoNotDisturbLabel", default: true)
static let focusIndicatorNonPersistent = Key<Bool>("focusIndicatorNonPersistent", default: false)
// MARK: Privacy Indicators (Camera & Microphone Detection)
static let enableCameraDetection = Key<Bool>("enableCameraDetection", default: true)
static let enableMicrophoneDetection = Key<Bool>("enableMicrophoneDetection", default: true)
// MARK: Lock Screen Features
static let enableLockScreenLiveActivity = Key<Bool>("enableLockScreenLiveActivity", default: true)
static let enableLockSounds = Key<Bool>("enableLockSounds", default: true)
// MARK: Caps Lock Indicator
static let enableCapsLockIndicator = Key<Bool>("enableCapsLockIndicator", default: true)
static let capsLockIndicatorUseGreenColor = Key<Bool>("capsLockIndicatorUseGreenColor", default: false) // Legacy toggle
static let capsLockIndicatorTintMode = Key<CapsLockIndicatorTintMode>("capsLockIndicatorTintMode", default: .white)
static let didMigrateCapsLockTintMode = Key<Bool>("didMigrateCapsLockTintMode", default: false)
static let showCapsLockLabel = Key<Bool>("showCapsLockLabel", default: false)
// MARK: ImageService
static let didClearLegacyURLCacheV1 = Key<Bool>("didClearLegacyURLCacheV1", default: false)
// MARK: Minimalistic UI Mode
static let enableMinimalisticUI = Key<Bool>("enableMinimalisticUI", default: false)
// MARK: Lyrics Feature
static let enableLyrics = Key<Bool>("enableLyrics", default: false)
// MARK: Notes Feature
static let enableNotes = Key<Bool>("enableNotes", default: false)
static let enableNotePinning = Key<Bool>("enableNotePinning", default: true)
static let enableNoteSearch = Key<Bool>("enableNoteSearch", default: false)
static let enableNoteColorFiltering = Key<Bool>("enableNoteColorFiltering", default: false)
static let enableCreateFromClipboard = Key<Bool>("enableCreateFromClipboard", default: true)
static let enableNoteCharCount = Key<Bool>("enableNoteCharCount", default: true)
static let savedNotes = Key<[NoteItem]>("savedNotes", default: [])
// Helper to determine the default media controller based on macOS version
static var defaultMediaController: MediaControllerType {
if #available(macOS 15.4, *) {
return .appleMusic
} else {
return .nowPlaying
}
}
// Migration helper to convert from legacy enableGradient Boolean to new ProgressBarStyle enum
static func migrateProgressBarStyle() {
// Check if migration is needed by seeing if the old Boolean was set to gradient
let wasGradientEnabled = Defaults[.enableGradient]
// Only migrate if we're still using the default hierarchical value but gradient was enabled
if wasGradientEnabled && Defaults[.progressBarStyle] == .hierarchical {
Defaults[.progressBarStyle] = .gradient
}
}
static func migrateMusicAuxControls() {
if Defaults[.didMigrateMusicAuxControls] == false {
if Defaults[.showMediaOutputControl] {
Defaults[.musicAuxRightControl] = .mediaOutput
}
Defaults[.didMigrateMusicAuxControls] = true
}
normalizeMusicAuxControls()
}
static func migrateCapsLockTintMode() {
guard Defaults[.didMigrateCapsLockTintMode] == false else { return }
let legacyGreen = Defaults[.capsLockIndicatorUseGreenColor]
Defaults[.capsLockIndicatorTintMode] = legacyGreen ? .green : .white
Defaults[.didMigrateCapsLockTintMode] = true
}
static func migrateMusicControlSlots() {
guard Defaults[.didMigrateMusicControlSlots] == false else { return }
let allowMediaOutput = Defaults[.showMediaOutputControl]
let baseLayout: [MusicControlButton]
if Defaults[.showShuffleAndRepeat] {
var slots = MusicControlButton.defaultLayout
let left = MusicControlButton(auxiliaryControl: Defaults[.musicAuxLeftControl])
let right = MusicControlButton(auxiliaryControl: Defaults[.musicAuxRightControl])
slots[0] = left
slots[4] = right
baseLayout = slots
} else {
baseLayout = MusicControlButton.minimalLayout
}
Defaults[.musicControlSlots] = baseLayout.normalized(allowingMediaOutput: allowMediaOutput)
Defaults[.didMigrateMusicControlSlots] = true
}
static func migrateThirdPartyDDCIntegration() {
if Defaults[.didMigrateThirdPartyDDCIntegration] == false {
let legacyBetterDisplayEnabled = Defaults[.enableBetterDisplayIntegration]
let legacyLunarEnabled = Defaults[.enableLunarIntegration]
if legacyBetterDisplayEnabled || legacyLunarEnabled {
Defaults[.enableThirdPartyDDCIntegration] = true
Defaults[.thirdPartyDDCProvider] = (legacyLunarEnabled && !legacyBetterDisplayEnabled) ? .lunar : .betterDisplay
}
Defaults[.didMigrateThirdPartyDDCIntegration] = true
}
syncLegacyThirdPartyDDCKeys()
}
static func syncLegacyThirdPartyDDCKeys() {
let isIntegrationEnabled = Defaults[.enableThirdPartyDDCIntegration]
let selectedProvider = Defaults[.thirdPartyDDCProvider]
Defaults[.enableBetterDisplayIntegration] = isIntegrationEnabled && selectedProvider == .betterDisplay
Defaults[.enableLunarIntegration] = isIntegrationEnabled && selectedProvider == .lunar
}
private static func normalizeMusicAuxControls() {
guard Defaults[.musicAuxLeftControl] == Defaults[.musicAuxRightControl] else { return }
let current = Defaults[.musicAuxLeftControl]
let fallback = MusicAuxiliaryControl.alternative(excluding: current)
Defaults[.musicAuxRightControl] = fallback
}
}