import Foundation struct PowerModeConfig: Codable, Identifiable, Equatable { var id: UUID var name: String var emoji: String var appConfigs: [AppConfig]? var urlConfigs: [URLConfig]? var isAIEnhancementEnabled: Bool var selectedPrompt: String? var selectedTranscriptionModelName: String? var selectedLanguage: String? var useScreenCapture: Bool var selectedAIProvider: String? var selectedAIModel: String? var isAutoSendEnabled: Bool = false var isEnabled: Bool = true enum CodingKeys: String, CodingKey { case id, name, emoji, appConfigs, urlConfigs, isAIEnhancementEnabled, selectedPrompt, selectedLanguage, useScreenCapture, selectedAIProvider, selectedAIModel, isAutoSendEnabled, isEnabled case selectedWhisperModel case selectedTranscriptionModelName } init(id: UUID = UUID(), name: String, emoji: String, appConfigs: [AppConfig]? = nil, urlConfigs: [URLConfig]? = nil, isAIEnhancementEnabled: Bool, selectedPrompt: String? = nil, selectedTranscriptionModelName: String? = nil, selectedLanguage: String? = nil, useScreenCapture: Bool = false, selectedAIProvider: String? = nil, selectedAIModel: String? = nil, isAutoSendEnabled: Bool = false, isEnabled: Bool = true) { self.id = id self.name = name self.emoji = emoji self.appConfigs = appConfigs self.urlConfigs = urlConfigs self.isAIEnhancementEnabled = isAIEnhancementEnabled self.selectedPrompt = selectedPrompt self.useScreenCapture = useScreenCapture self.isAutoSendEnabled = isAutoSendEnabled self.selectedAIProvider = selectedAIProvider ?? UserDefaults.standard.string(forKey: "selectedAIProvider") self.selectedAIModel = selectedAIModel self.selectedTranscriptionModelName = selectedTranscriptionModelName ?? UserDefaults.standard.string(forKey: "CurrentTranscriptionModel") self.selectedLanguage = selectedLanguage ?? UserDefaults.standard.string(forKey: "SelectedLanguage") ?? "en" self.isEnabled = isEnabled } init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) id = try container.decode(UUID.self, forKey: .id) name = try container.decode(String.self, forKey: .name) emoji = try container.decode(String.self, forKey: .emoji) appConfigs = try container.decodeIfPresent([AppConfig].self, forKey: .appConfigs) urlConfigs = try container.decodeIfPresent([URLConfig].self, forKey: .urlConfigs) isAIEnhancementEnabled = try container.decode(Bool.self, forKey: .isAIEnhancementEnabled) selectedPrompt = try container.decodeIfPresent(String.self, forKey: .selectedPrompt) selectedLanguage = try container.decodeIfPresent(String.self, forKey: .selectedLanguage) useScreenCapture = try container.decode(Bool.self, forKey: .useScreenCapture) selectedAIProvider = try container.decodeIfPresent(String.self, forKey: .selectedAIProvider) selectedAIModel = try container.decodeIfPresent(String.self, forKey: .selectedAIModel) isAutoSendEnabled = try container.decodeIfPresent(Bool.self, forKey: .isAutoSendEnabled) ?? false isEnabled = try container.decodeIfPresent(Bool.self, forKey: .isEnabled) ?? true if let newModelName = try container.decodeIfPresent(String.self, forKey: .selectedTranscriptionModelName) { selectedTranscriptionModelName = newModelName } else if let oldModelName = try container.decodeIfPresent(String.self, forKey: .selectedWhisperModel) { selectedTranscriptionModelName = oldModelName } else { selectedTranscriptionModelName = nil } } func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(id, forKey: .id) try container.encode(name, forKey: .name) try container.encode(emoji, forKey: .emoji) try container.encodeIfPresent(appConfigs, forKey: .appConfigs) try container.encodeIfPresent(urlConfigs, forKey: .urlConfigs) try container.encode(isAIEnhancementEnabled, forKey: .isAIEnhancementEnabled) try container.encodeIfPresent(selectedPrompt, forKey: .selectedPrompt) try container.encodeIfPresent(selectedLanguage, forKey: .selectedLanguage) try container.encode(useScreenCapture, forKey: .useScreenCapture) try container.encodeIfPresent(selectedAIProvider, forKey: .selectedAIProvider) try container.encodeIfPresent(selectedAIModel, forKey: .selectedAIModel) try container.encode(isAutoSendEnabled, forKey: .isAutoSendEnabled) try container.encodeIfPresent(selectedTranscriptionModelName, forKey: .selectedTranscriptionModelName) try container.encode(isEnabled, forKey: .isEnabled) } static func == (lhs: PowerModeConfig, rhs: PowerModeConfig) -> Bool { lhs.id == rhs.id } } struct AppConfig: Codable, Identifiable, Equatable { let id: UUID var bundleIdentifier: String var appName: String init(id: UUID = UUID(), bundleIdentifier: String, appName: String) { self.id = id self.bundleIdentifier = bundleIdentifier self.appName = appName } static func == (lhs: AppConfig, rhs: AppConfig) -> Bool { lhs.id == rhs.id } } struct URLConfig: Codable, Identifiable, Equatable { let id: UUID var url: String init(id: UUID = UUID(), url: String) { self.id = id self.url = url } static func == (lhs: URLConfig, rhs: URLConfig) -> Bool { lhs.id == rhs.id } } class PowerModeManager: ObservableObject { static let shared = PowerModeManager() @Published var configurations: [PowerModeConfig] = [] @Published var activeConfiguration: PowerModeConfig? private let configKey = "powerModeConfigurationsV2" private let activeConfigIdKey = "activeConfigurationId" private init() { loadConfigurations() if let activeConfigIdString = UserDefaults.standard.string(forKey: activeConfigIdKey), let activeConfigId = UUID(uuidString: activeConfigIdString) { activeConfiguration = configurations.first { $0.id == activeConfigId } } else { activeConfiguration = nil } } private func loadConfigurations() { if let data = UserDefaults.standard.data(forKey: configKey), let configs = try? JSONDecoder().decode([PowerModeConfig].self, from: data) { configurations = configs } } func saveConfigurations() { if let data = try? JSONEncoder().encode(configurations) { UserDefaults.standard.set(data, forKey: configKey) } } func addConfiguration(_ config: PowerModeConfig) { if !configurations.contains(where: { $0.id == config.id }) { configurations.append(config) saveConfigurations() } } func removeConfiguration(with id: UUID) { configurations.removeAll { $0.id == id } saveConfigurations() } func getConfiguration(with id: UUID) -> PowerModeConfig? { return configurations.first { $0.id == id } } func updateConfiguration(_ config: PowerModeConfig) { if let index = configurations.firstIndex(where: { $0.id == config.id }) { configurations[index] = config saveConfigurations() } } func getConfigurationForURL(_ url: String) -> PowerModeConfig? { let cleanedURL = cleanURL(url) for config in configurations.filter({ $0.isEnabled }) { if let urlConfigs = config.urlConfigs { for urlConfig in urlConfigs { let configURL = cleanURL(urlConfig.url) if cleanedURL.contains(configURL) { return config } } } } return nil } func getConfigurationForApp(_ bundleId: String) -> PowerModeConfig? { for config in configurations.filter({ $0.isEnabled }) { if let appConfigs = config.appConfigs { if appConfigs.contains(where: { $0.bundleIdentifier == bundleId }) { return config } } } return nil } func getWildcardConfiguration() -> PowerModeConfig? { for config in configurations.filter({ $0.isEnabled }) { if let urlConfigs = config.urlConfigs { if urlConfigs.contains(where: { $0.url == "*" }) { return config } } } return nil } func enableConfiguration(with id: UUID) { if let index = configurations.firstIndex(where: { $0.id == id }) { configurations[index].isEnabled = true saveConfigurations() } } func disableConfiguration(with id: UUID) { if let index = configurations.firstIndex(where: { $0.id == id }) { configurations[index].isEnabled = false saveConfigurations() } } var enabledConfigurations: [PowerModeConfig] { return configurations.filter { $0.isEnabled } } func addAppConfig(_ appConfig: AppConfig, to config: PowerModeConfig) { if var updatedConfig = configurations.first(where: { $0.id == config.id }) { var configs = updatedConfig.appConfigs ?? [] configs.append(appConfig) updatedConfig.appConfigs = configs updateConfiguration(updatedConfig) } } func removeAppConfig(_ appConfig: AppConfig, from config: PowerModeConfig) { if var updatedConfig = configurations.first(where: { $0.id == config.id }) { updatedConfig.appConfigs?.removeAll(where: { $0.id == appConfig.id }) updateConfiguration(updatedConfig) } } func addURLConfig(_ urlConfig: URLConfig, to config: PowerModeConfig) { if var updatedConfig = configurations.first(where: { $0.id == config.id }) { var configs = updatedConfig.urlConfigs ?? [] configs.append(urlConfig) updatedConfig.urlConfigs = configs updateConfiguration(updatedConfig) } } func removeURLConfig(_ urlConfig: URLConfig, from config: PowerModeConfig) { if var updatedConfig = configurations.first(where: { $0.id == config.id }) { updatedConfig.urlConfigs?.removeAll(where: { $0.id == urlConfig.id }) updateConfiguration(updatedConfig) } } func cleanURL(_ url: String) -> String { return url.lowercased() .replacingOccurrences(of: "https://", with: "") .replacingOccurrences(of: "http://", with: "") .replacingOccurrences(of: "www.", with: "") .trimmingCharacters(in: .whitespacesAndNewlines) } func setActiveConfiguration(_ config: PowerModeConfig?) { activeConfiguration = config UserDefaults.standard.set(config?.id.uuidString, forKey: activeConfigIdKey) self.objectWillChange.send() } var currentActiveConfiguration: PowerModeConfig? { return activeConfiguration } func getAllAvailableConfigurations() -> [PowerModeConfig] { return configurations } func isEmojiInUse(_ emoji: String) -> Bool { return configurations.contains { $0.emoji == emoji } } }