/*
* 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 .
*/
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
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 = 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)
}
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("menubarIcon", default: true)
static let showOnAllDisplays = Key("showOnAllDisplays", default: false)
static let automaticallySwitchDisplay = Key("automaticallySwitchDisplay", default: true)
static let releaseName = Key("releaseName", default: "Seychelles")
static let hideDynamicIslandFromScreenCapture = Key("hideDynamicIslandFromScreenCapture", default: false)
// MARK: Behavior
static let minimumHoverDuration = Key("minimumHoverDuration", default: 0.3)
static let enableHaptics = Key("enableHaptics", default: true)
static let openNotchOnHover = Key("openNotchOnHover", default: true)
static let extendHoverArea = Key("extendHoverArea", default: false)
static let externalDisplayStyle = Key(
"externalDisplayStyle",
default: .notch
)
static let hideNonNotchUntilHover = Key("hideNonNotchUntilHover", default: false)
static let notchHeightMode = Key(
"notchHeightMode",
default: WindowHeightMode.matchRealNotchSize
)
static let nonNotchHeightMode = Key(
"nonNotchHeightMode",
default: WindowHeightMode.matchMenuBar
)
static let nonNotchHeight = Key("nonNotchHeight", default: 32)
static let notchHeight = Key("notchHeight", default: 32)
static let openNotchWidth = Key("openNotchWidth", default: 640)
//static let openLastTabByDefault = Key("openLastTabByDefault", default: false)
// MARK: Appearance
static let showEmojis = Key("showEmojis", default: false)
//static let alwaysShowTabs = Key("alwaysShowTabs", default: true)
static let showMirror = Key("showMirror", default: false)
static let mirrorShape = Key("mirrorShape", default: MirrorShapeEnum.rectangle)
static let selectedCameraID = Key("selectedCameraID", default: "")
static let settingsIconInNotch = Key("settingsIconInNotch", default: true)
static let lightingEffect = Key("lightingEffect", default: true)
static let accentColor = Key("accentColor", default: Color.blue)
static let enableShadow = Key("enableShadow", default: true)
static let cornerRadiusScaling = Key("cornerRadiusScaling", default: true)
static let useModernCloseAnimation = Key("useModernCloseAnimation", default: true)
static let showNotHumanFace = Key("showNotHumanFace", default: false)
static let customIdleAnimations = Key<[CustomIdleAnimation]>("customIdleAnimations", default: [])
static let selectedIdleAnimation = Key("selectedIdleAnimation", default: nil)
static let animationTransformOverrides = Key<[String: AnimationTransformConfig]>("animationTransformOverrides", default: [:])
static let tileShowLabels = Key("tileShowLabels", default: false)
static let showCalendar = Key("showCalendar", default: true)
static let hideCompletedReminders = Key("hideCompletedReminders", default: true)
static let hideAllDayEvents = Key("hideAllDayEvents", default: false)
static let sliderColor = Key(
"sliderUseAlbumArtColor",
default: SliderColorEnum.white
)
static let playerColorTinting = Key("playerColorTinting", default: true)
static let useMusicVisualizer = Key("useMusicVisualizer", default: true)
static let customVisualizers = Key<[CustomVisualizer]>("customVisualizers", default: [])
static let selectedVisualizer = Key("selectedVisualizer", default: nil)
static let customAppIcons = Key<[CustomAppIcon]>("customAppIcons", default: [])
static let selectedAppIconID = Key("selectedAppIconID", default: nil)
// MARK: Gestures
static let enableGestures = Key("enableGestures", default: true)
static let closeGestureEnabled = Key("closeGestureEnabled", default: true)
static let gestureSensitivity = Key("gestureSensitivity", default: 200.0)
static let enableHorizontalMusicGestures = Key("enableHorizontalMusicGestures", default: true)
static let musicGestureBehavior = Key("musicGestureBehavior", default: .track)
static let reverseSwipeGestures = Key("reverseSwipeGestures", default: false)
static let reverseScrollGestures = Key("reverseScrollGestures", default: false)
// MARK: Media playback
static let coloredSpectrogram = Key("coloredSpectrogram", default: true)
static let enableRealTimeWaveform = Key("enableRealTimeWaveform", default: false)
static let enableSneakPeek = Key("enableSneakPeek", default: false)
static let sneakPeekStyles = Key("sneakPeekStyles", default: .standard)
static let showSneakPeekOnTrackChange = Key("showSneakPeekOnTrackChange", default: true)
static let enableFullscreenMediaDetection = Key("enableFullscreenMediaDetection", default: true)
static let parallaxEffectIntensity = Key("parallaxEffectIntensity", default: 6.0)
static let waitInterval = Key("waitInterval", default: 3)
static let showShuffleAndRepeat = Key("showShuffleAndRepeat", default: true)
static let showMediaOutputControl = Key("showMediaOutputControl", default: true)
static let musicAuxLeftControl = Key("musicAuxLeftControl", default: .shuffle)
static let musicAuxRightControl = Key("musicAuxRightControl", default: .repeatMode)
static let didMigrateMusicAuxControls = Key("didMigrateMusicAuxControls", default: false)
static let musicControlSlots = Key<[MusicControlButton]>("musicControlSlots", default: MusicControlButton.defaultLayout)
static let didMigrateMusicControlSlots = Key("didMigrateMusicControlSlots", default: false)
static let musicSkipBehavior = Key("musicSkipBehavior", default: .track)
static let musicControlWindowEnabled = Key("musicControlWindowEnabled", default: false)
static let showStandardMediaControls = Key("showStandardMediaControls", default: true)
static let autoHideInactiveNotchMediaPlayer = Key("autoHideInactiveNotchMediaPlayer", default: true)
static let cachedMusicLiveActivityPreference = Key("cachedMusicLiveActivityPreference", default: nil)
static let cachedLockScreenMediaWidgetPreference = Key("cachedLockScreenMediaWidgetPreference", default: nil)
static let cachedMusicControlWindowPreference = Key("cachedMusicControlWindowPreference", default: nil)
// Enable lock screen media widget (shows the standalone panel when screen is locked)
static let enableLockScreenMediaWidget = Key("enableLockScreenMediaWidget", default: true)
static let enableLockScreenWeatherWidget = Key("enableLockScreenWeatherWidget", default: true)
static let enableLockScreenFocusWidget = Key("enableLockScreenFocusWidget", default: true)
static let enableLockScreenReminderWidget = Key("enableLockScreenReminderWidget", default: true)
static let enableLockScreenTimerWidget = Key("enableLockScreenTimerWidget", default: true)
static let lockScreenWeatherRefreshInterval = Key("lockScreenWeatherRefreshInterval", default: 30 * 60)
static let lockScreenWeatherShowsLocation = Key("lockScreenWeatherShowsLocation", default: true)
static let lockScreenWeatherShowsSunrise = Key("lockScreenWeatherShowsSunrise", default: true)
static let lockScreenWeatherWidgetStyle = Key("lockScreenWeatherWidgetStyle", default: .inline)
static let lockScreenWeatherTemperatureUnit = Key("lockScreenWeatherTemperatureUnit", default: .celsius)
static let lockScreenWeatherShowsAQI = Key("lockScreenWeatherShowsAQI", default: true)
static let lockScreenWeatherAQIScale = Key("lockScreenWeatherAQIScale", default: .us)
static let lockScreenWeatherUsesGaugeTint = Key("lockScreenWeatherUsesGaugeTint", default: false)
static let lockScreenWeatherProviderSource = Key("lockScreenWeatherProviderSource", default: .openMeteo)
static let lockScreenWeatherVerticalOffset = Key("lockScreenWeatherVerticalOffset", default: 0)
static let lockScreenMusicVerticalOffset = Key("lockScreenMusicVerticalOffset", default: 0)
static let lockScreenMusicPanelWidth = Key("lockScreenMusicPanelWidth", default: 350)
static let lockScreenMusicAlbumParallaxEnabled = Key("lockScreenMusicAlbumParallaxEnabled", default: false)
static let lockScreenTimerVerticalOffset = Key("lockScreenTimerVerticalOffset", default: 0)
static let lockScreenTimerWidgetWidth = Key("lockScreenTimerWidgetWidth", default: 350)
static let lockScreenGlassStyle = Key("lockScreenGlassStyle", default: .liquid)
static let lockScreenGlassCustomizationMode = Key(
"lockScreenGlassCustomizationMode",
default: .standard
)
static let lockScreenTimerGlassStyle = Key("lockScreenTimerGlassStyle", default: .frosted)
static let lockScreenTimerGlassCustomizationMode = Key(
"lockScreenTimerGlassCustomizationMode",
default: .standard
)
static let lockScreenMusicLiquidGlassVariant = Key(
"lockScreenMusicLiquidGlassVariant",
default: .defaultVariant
)
static let lockScreenTimerLiquidGlassVariant = Key(
"lockScreenTimerLiquidGlassVariant",
default: .defaultVariant
)
static let lockScreenShowAppIcon = Key("lockScreenShowAppIcon", default: false)
static let lockScreenPanelShowsBorder = Key("lockScreenPanelShowsBorder", default: false)
static let lockScreenMusicUsesEnhancedLiquidBorder = Key(
"lockScreenMusicUsesEnhancedLiquidBorder",
default: true
)
static let lockScreenPanelUsesBlur = Key("lockScreenPanelUsesBlur", default: true)
static let lockScreenMusicMergedAirPlayOutput = Key("lockScreenMusicMergedAirPlayOutput", default: true)
static let lockScreenTimerWidgetUsesBlur = Key("lockScreenTimerWidgetUsesBlur", default: false)
static let lockScreenReminderChipStyle = Key("lockScreenReminderChipStyle", default: .eventColor)
static let lockScreenReminderWidgetHorizontalAlignment = Key("lockScreenReminderWidgetHorizontalAlignment", default: "center")
static let lockScreenReminderWidgetVerticalOffset = Key("lockScreenReminderWidgetVerticalOffset", default: 0)
static let lockScreenShowCalendarEvent = Key("lockScreenShowCalendarEvent", default: true)
static let lockScreenCalendarEventLookaheadWindow = Key("lockScreenCalendarEventLookaheadWindow", default: "3h")
static let lockScreenCalendarSelectionMode = Key("lockScreenCalendarSelectionMode", default: "all")
static let lockScreenSelectedCalendarIDs = Key>("lockScreenSelectedCalendarIDs", default: [])
static let lockScreenShowCalendarCountdown = Key("lockScreenShowCalendarCountdown", default: true)
static let lockScreenShowCalendarEventEntireDuration = Key("lockScreenShowCalendarEventEntireDuration", default: true)
static let lockScreenShowCalendarEventAfterStartEnabled = Key("lockScreenShowCalendarEventAfterStartEnabled", default: false)
static let lockScreenShowCalendarEventAfterStartWindow = Key("lockScreenShowCalendarEventAfterStartWindow", default: "5m")
static let lockScreenShowCalendarTimeRemaining = Key("lockScreenShowCalendarTimeRemaining", default: true)
static let lockScreenShowCalendarStartTimeAfterBegins = Key("lockScreenShowCalendarStartTimeAfterBegins", default: true)
static let lockScreenWeatherWidgetRowOrder = Key("lockScreenWeatherWidgetRowOrder", default: "weather_calendar_focus")
// MARK: Third-party Calendar Integration
static let enableThirdPartyCalendarApp = Key("enableThirdPartyCalendarApp", default: false)
static let selectedCalendarApp = Key("selectedCalendarApp", default: .fantastical)
static let fantasticalDefaultView = Key("fantasticalDefaultView", default: .mini)
// MARK: Battery
static let showPowerStatusNotifications = Key("showPowerStatusNotifications", default: true)
static let showBatteryIndicator = Key("showBatteryIndicator", default: BatteryActivityManager.shared.hasBattery())
static let showBatteryPercentage = Key("showBatteryPercentage", default: true)
static let showPowerStatusIcons = Key("showPowerStatusIcons", default: true)
static let playLowBatteryAlertSound = Key("playLowBatteryAlertSound", default: true)
static let lockScreenBatteryShowsBatteryGauge = Key(
"lockScreenWeatherShowsBatteryGauge",
default: BatteryActivityManager.shared.hasBattery()
)
static let lockScreenBatteryUsesLaptopSymbol = Key("lockScreenWeatherBatteryUsesLaptopSymbol", default: true)
static let lockScreenBatteryShowsCharging = Key("lockScreenWeatherShowsCharging", default: true)
static let lockScreenBatteryShowsChargingPercentage = Key("lockScreenWeatherShowsChargingPercentage", default: true)
static let lockScreenBatteryShowsBluetooth = Key("lockScreenWeatherShowsBluetooth", default: true)
// MARK: Downloads
static let enableDownloadListener = Key("enableDownloadListener", default: true)
static let enableSafariDownloads = Key("enableSafariDownloads", default: true)
static let selectedDownloadIndicatorStyle = Key("selectedDownloadIndicatorStyle", default: DownloadIndicatorStyle.progress)
static let selectedDownloadIconStyle = Key("selectedDownloadIconStyle", default: DownloadIconStyle.onlyAppIcon)
// MARK: HUD
static let inlineHUD = Key("inlineHUD", default: true)
static let progressBarStyle = Key("progressBarStyle", default: .hierarchical)
// Legacy support - keeping for backward compatibility
static let enableGradient = Key("enableGradient", default: false)
static let systemEventIndicatorShadow = Key("systemEventIndicatorShadow", default: false)
static let systemEventIndicatorUseAccent = Key("systemEventIndicatorUseAccent", default: false)
static let showProgressPercentages = Key("showProgressPercentages", default: true)
// MARK: Shelf
static let dynamicShelf = Key("dynamicShelf", default: true)
static let openShelfByDefault = Key("openShelfByDefault", default: true)
static let quickShareProvider = Key("quickShareProvider", default: "AirDrop")
static let localSendSelectedDeviceID = Key("localSendSelectedDeviceID", default: "")
static let localSendDevicePickerGlassMode = Key("localSendDevicePickerGlassMode", default: .standard)
static let localSendDevicePickerLiquidGlassVariant = Key("localSendDevicePickerLiquidGlassVariant", default: .v11)
static let copyOnDrag = Key("copyOnDrag", default: false)
static let autoRemoveShelfItems = Key("autoRemoveShelfItems", default: false)
static let expandedDragDetection = Key("expandedDragDetection", default: true)
// MARK: Calendar
static let calendarSelectionState = Key("calendarSelectionState", default: .all)
static let showFullEventTitles = Key("showFullEventTitles", default: false)
static let autoScrollToNextEvent = Key("autoScrollToNextEvent", default: true)
// MARK: Fullscreen Media Detection
static let alwaysHideInFullscreen = Key("alwaysHideInFullscreen", default: false)
static let hideNotchOption = Key("hideNotchOption", default: .nowPlayingOnly)
// MARK: Wobble Animation
static let enableWobbleAnimation = Key("enableWobbleAnimation", default: false)
// MARK: Media Controller
static let mediaController = Key("mediaController", default: defaultMediaController)
// MARK: Bluetooth Audio Devices
static let showBluetoothDeviceConnections = Key("showBluetoothDeviceConnections", default: true)
static let useColorCodedBatteryDisplay = Key("useColorCodedBatteryDisplay", default: true)
static let useColorCodedVolumeDisplay = Key("useColorCodedVolumeDisplay", default: true)
static let useSmoothColorGradient = Key("useSmoothColorGradient", default: true)
static let useCircularBluetoothBatteryIndicator = Key("useCircularBluetoothBatteryIndicator", default: true)
static let showBluetoothBatteryPercentageText = Key("showBluetoothBatteryPercentageText", default: false)
static let showBluetoothDeviceNameMarquee = Key("showBluetoothDeviceNameMarquee", default: false)
static let useBluetoothHUD3DIcon = Key("useBluetoothHUD3DIcon", default: true)
// MARK: Stats Feature
static let enableStatsFeature = Key("enableStatsFeature", default: false)
static let autoStartStatsMonitoring = Key("autoStartStatsMonitoring", default: true)
static let statsStopWhenNotchCloses = Key("statsStopWhenNotchCloses", default: true)
static let statsUpdateInterval = Key("statsUpdateInterval", default: 1.0)
static let showCpuGraph = Key("showCpuGraph", default: true)
static let showMemoryGraph = Key("showMemoryGraph", default: true)
static let showGpuGraph = Key("showGpuGraph", default: true)
static let showNetworkGraph = Key("showNetworkGraph", default: false)
static let showDiskGraph = Key("showDiskGraph", default: false)
static let cpuTemperatureUnit = Key("cpuTemperatureUnit", default: .celsius)
// MARK: Terminal Feature
static let enableTerminalFeature = Key("enableTerminalFeature", default: false)
static let terminalShellPath = Key("terminalShellPath", default: "/bin/zsh")
static let terminalFontFamily = Key("terminalFontFamily", default: "")
static let terminalFontSize = Key("terminalFontSize", default: 12.0)
static let terminalOpacity = Key("terminalOpacity", default: 1.0)
static let terminalMaxHeightFraction = Key("terminalMaxHeightFraction", default: 0.4)
static let terminalCursorStyle = Key("terminalCursorStyle", default: "blinkBlock")
static let terminalScrollbackLines = Key("terminalScrollbackLines", default: 1000)
static let terminalOptionAsMeta = Key("terminalOptionAsMeta", default: true)
static let terminalMouseReporting = Key("terminalMouseReporting", default: true)
static let terminalBoldAsBright = Key("terminalBoldAsBright", default: true)
static let terminalBackgroundColor = Key("terminalBackgroundColor", default: .black)
static let terminalForegroundColor = Key("terminalForegroundColor", default: .white)
static let terminalCursorColor = Key("terminalCursorColor", default: Color(.selectedControlColor))
static let terminalStickyMode = Key("terminalStickyMode", default: false)
// MARK: Timer Feature
static let enableTimerFeature = Key("enableTimerFeature", default: true)
static let timerDisplayMode = Key("timerDisplayMode", default: .tab)
static let timerPresets = Key<[TimerPreset]>("timerPresets", default: TimerPreset.defaultPresets)
static let showTimerPresetsInNotchTab = Key("showTimerPresetsInNotchTab", default: true)
static let timerIconColorMode = Key("timerIconColorMode", default: .adaptive)
static let timerSolidColor = Key("timerSolidColor", default: .blue)
static let timerShowsCountdown = Key("timerShowsCountdown", default: true)
static let timerShowsLabel = Key("timerShowsLabel", default: false)
static let timerShowsProgress = Key("timerShowsProgress", default: true)
static let timerProgressStyle = Key("timerProgressStyle", default: .bar)
static let mirrorSystemTimer = Key("mirrorSystemTimer", default: true)
// MARK: Reminder Live Activity
static let enableReminderLiveActivity = Key("enableReminderLiveActivity", default: true)
static let reminderPresentationStyle = Key("reminderPresentationStyle", default: .ringCountdown)
static let reminderLeadTime = Key("reminderLeadTime", default: 5)
static let reminderSneakPeekDuration = Key("reminderSneakPeekDuration", default: 5)
static let timerControlWindowEnabled = Key("timerControlWindowEnabled", default: true)
// MARK: ColorPicker Feature
static let enableColorPickerFeature = Key("enableColorPickerFeature", default: true)
static let showColorFormats = Key("showColorFormats", default: true)
static let colorPickerDisplayMode = Key("colorPickerDisplayMode", default: .panel)
static let colorHistorySize = Key("colorHistorySize", default: 10)
static let showColorPickerIcon = Key("showColorPickerIcon", default: true)
// MARK: Clipboard Feature
static let enableClipboardManager = Key("enableClipboardManager", default: true)
static let clipboardHistorySize = Key("clipboardHistorySize", default: 3)
static let showClipboardIcon = Key("showClipboardIcon", default: true)
static let clipboardDisplayMode = Key("clipboardDisplayMode", default: .panel)
// MARK: Screen Assistant Feature
static let enableScreenAssistant = Key("enableScreenAssistant", default: true)
static let screenAssistantDisplayMode = Key("screenAssistantDisplayMode", default: .panel)
static let geminiApiKey = Key("geminiApiKey", default: "")
static let openaiApiKey = Key("openaiApiKey", default: "")
static let claudeApiKey = Key("claudeApiKey", default: "")
static let groqApiKey = Key("groqApiKey", default: "")
static let selectedAIProvider = Key("selectedAIProvider", default: .gemini)
static let selectedAIModel = Key("selectedAIModel", default: nil)
static let enableThinkingMode = Key("enableThinkingMode", default: false)
static let localModelEndpoint = Key("localModelEndpoint", default: "http://localhost:11434")
// MARK: Third-Party Extensions
static let enableThirdPartyExtensions = Key("enableThirdPartyExtensions", default: true)
static let enableExtensionLiveActivities = Key("enableExtensionLiveActivities", default: true)
static let enableExtensionLockScreenWidgets = Key("enableExtensionLockScreenWidgets", default: true)
static let enableExtensionNotchExperiences = Key("enableExtensionNotchExperiences", default: true)
static let enableExtensionNotchTabs = Key("enableExtensionNotchTabs", default: true)
static let enableExtensionNotchMinimalisticOverrides = Key("enableExtensionNotchMinimalisticOverrides", default: true)
static let enableExtensionNotchInteractiveWebViews = Key("enableExtensionNotchInteractiveWebViews", default: true)
static let extensionAuthorizationEntries = Key<[ExtensionAuthorizationEntry]>("extensionAuthorizationEntries", default: [])
static let extensionRateLimitRecords = Key<[ExtensionRateLimitRecord]>("extensionRateLimitRecords", default: [])
static let extensionDiagnosticsLoggingEnabled = Key("extensionDiagnosticsLoggingEnabled", default: true)
static let extensionLiveActivityCapacity = Key("extensionLiveActivityCapacity", default: 4)
static let extensionLockScreenWidgetCapacity = Key("extensionLockScreenWidgetCapacity", default: 4)
static let extensionNotchExperienceCapacity = Key("extensionNotchExperienceCapacity", default: 2)
static let enableExtensionFileSharing = Key("enableExtensionFileSharing", default: true)
// MARK: Keyboard Shortcuts
static let enableShortcuts = Key("enableShortcuts", default: true)
// MARK: System HUD Feature
static let enableSystemHUD = Key("enableSystemHUD", default: true)
static let enableVolumeHUD = Key("enableVolumeHUD", default: true)
static let enableBrightnessHUD = Key("enableBrightnessHUD", default: true)
static let enableKeyboardBacklightHUD = Key("enableKeyboardBacklightHUD", default: true)
static let systemHUDSensitivity = Key("systemHUDSensitivity", default: 5)
static let playVolumeChangeFeedback = Key("playVolumeChangeFeedback", default: false)
// MARK: Custom OSD Window Feature
static let enableCustomOSD = Key("enableCustomOSD", default: false)
static let enableVerticalHUD = Key("enableVerticalHUD", default: false)
static let enableCircularHUD = Key("enableCircularHUD", default: false)
static let verticalHUDPosition = Key("verticalHUDPosition", default: "right") // "left" or "right"
// Vertical HUD Customization
static let verticalHUDShowValue = Key("verticalHUDShowValue", default: true)
static let verticalHUDInteractive = Key("verticalHUDInteractive", default: true)
static let verticalHUDHeight = Key("verticalHUDHeight", default: 160)
static let verticalHUDWidth = Key("verticalHUDWidth", default: 36)
static let verticalHUDPadding = Key("verticalHUDPadding", default: 24)
static let verticalHUDUseAccentColor = Key("verticalHUDUseAccentColor", default: false)
static let verticalHUDMaterial = Key("verticalHUDMaterial", default: .frosted)
static let verticalHUDLiquidGlassCustomizationMode = Key(
"verticalHUDLiquidGlassCustomizationMode",
default: .standard
)
static let verticalHUDLiquidGlassVariant = Key(
"verticalHUDLiquidGlassVariant",
default: .defaultVariant
)
// Circular HUD Customization
static let circularHUDShowValue = Key