diff --git a/VoiceInk/PowerMode/PowerModeConfig.swift b/VoiceInk/PowerMode/PowerModeConfig.swift index 6e66400..3cb2a6a 100644 --- a/VoiceInk/PowerMode/PowerModeConfig.swift +++ b/VoiceInk/PowerMode/PowerModeConfig.swift @@ -16,11 +16,10 @@ struct PowerModeConfig: Codable, Identifiable, Equatable { var isAutoSendEnabled: Bool = false var isEnabled: Bool = true - // Custom coding keys to handle migration from selectedWhisperModel enum CodingKeys: String, CodingKey { case id, name, emoji, appConfigs, urlConfigs, isAIEnhancementEnabled, selectedPrompt, selectedLanguage, useScreenCapture, selectedAIProvider, selectedAIModel, isAutoSendEnabled, isEnabled - case selectedWhisperModel // Old key - case selectedTranscriptionModelName // New key + case selectedWhisperModel + case selectedTranscriptionModelName } init(id: UUID = UUID(), name: String, emoji: String, appConfigs: [AppConfig]? = nil, @@ -92,7 +91,6 @@ struct PowerModeConfig: Codable, Identifiable, Equatable { } } -// App configuration struct AppConfig: Codable, Identifiable, Equatable { let id: UUID var bundleIdentifier: String @@ -109,10 +107,9 @@ struct AppConfig: Codable, Identifiable, Equatable { } } -// Simple URL configuration struct URLConfig: Codable, Identifiable, Equatable { let id: UUID - var url: String // Simple URL like "google.com" + var url: String init(id: UUID = UUID(), url: String) { self.id = id @@ -135,7 +132,6 @@ class PowerModeManager: ObservableObject { private init() { loadConfigurations() - // Set the active configuration from saved ID if let activeConfigIdString = UserDefaults.standard.string(forKey: activeConfigIdKey), let activeConfigId = UUID(uuidString: activeConfigIdString) { activeConfiguration = configurations.first { $0.id == activeConfigId } @@ -180,7 +176,6 @@ class PowerModeManager: ObservableObject { } } - // Get configuration for a specific URL func getConfigurationForURL(_ url: String) -> PowerModeConfig? { let cleanedURL = cleanURL(url) @@ -198,7 +193,6 @@ class PowerModeManager: ObservableObject { return nil } - // Get configuration for an application bundle ID func getConfigurationForApp(_ bundleId: String) -> PowerModeConfig? { for config in configurations.filter({ $0.isEnabled }) { if let appConfigs = config.appConfigs { @@ -210,7 +204,6 @@ class PowerModeManager: ObservableObject { return nil } - // Enable a configuration func enableConfiguration(with id: UUID) { if let index = configurations.firstIndex(where: { $0.id == id }) { configurations[index].isEnabled = true @@ -218,7 +211,6 @@ class PowerModeManager: ObservableObject { } } - // Disable a configuration func disableConfiguration(with id: UUID) { if let index = configurations.firstIndex(where: { $0.id == id }) { configurations[index].isEnabled = false @@ -226,12 +218,10 @@ class PowerModeManager: ObservableObject { } } - // Get all enabled configurations var enabledConfigurations: [PowerModeConfig] { return configurations.filter { $0.isEnabled } } - // Add app configuration func addAppConfig(_ appConfig: AppConfig, to config: PowerModeConfig) { if var updatedConfig = configurations.first(where: { $0.id == config.id }) { var configs = updatedConfig.appConfigs ?? [] @@ -241,7 +231,6 @@ class PowerModeManager: ObservableObject { } } - // Remove app configuration 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 }) @@ -249,7 +238,6 @@ class PowerModeManager: ObservableObject { } } - // Add URL configuration func addURLConfig(_ urlConfig: URLConfig, to config: PowerModeConfig) { if var updatedConfig = configurations.first(where: { $0.id == config.id }) { var configs = updatedConfig.urlConfigs ?? [] @@ -259,7 +247,6 @@ class PowerModeManager: ObservableObject { } } - // Remove URL configuration 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 }) @@ -267,7 +254,6 @@ class PowerModeManager: ObservableObject { } } - // Clean URL for comparison func cleanURL(_ url: String) -> String { return url.lowercased() .replacingOccurrences(of: "https://", with: "") @@ -276,19 +262,16 @@ class PowerModeManager: ObservableObject { .trimmingCharacters(in: .whitespacesAndNewlines) } - // Set active configuration func setActiveConfiguration(_ config: PowerModeConfig?) { activeConfiguration = config UserDefaults.standard.set(config?.id.uuidString, forKey: activeConfigIdKey) self.objectWillChange.send() } - // Get current active configuration var currentActiveConfiguration: PowerModeConfig? { return activeConfiguration } - // Get all available configurations in order func getAllAvailableConfigurations() -> [PowerModeConfig] { return configurations } diff --git a/VoiceInk/PowerMode/PowerModeConfigView.swift b/VoiceInk/PowerMode/PowerModeConfigView.swift index 7a1b6e9..bbf6c05 100644 --- a/VoiceInk/PowerMode/PowerModeConfigView.swift +++ b/VoiceInk/PowerMode/PowerModeConfigView.swift @@ -35,7 +35,6 @@ struct ConfigurationView: View { // New state for screen capture toggle @State private var useScreenCapture = false - // NEW: Auto-send toggle state @State private var isAutoSendEnabled = false // State for prompt editing (similar to EnhancementSettingsView) @@ -171,21 +170,9 @@ struct ConfigurationView: View { } } - // Enhanced Emoji Picker with Custom Emoji Support - // if isShowingEmojiPicker { // <<< This conditional block will be removed - // EmojiPickerView( - // selectedEmoji: $selectedEmoji, - // isPresented: $isShowingEmojiPicker - // ) - // .padding(.horizontal) - // } - - // SECTION 1: TRIGGERS VStack(spacing: 16) { - // Section Header SectionHeader(title: "When to Trigger") - // Applications Subsection VStack(alignment: .leading, spacing: 12) { HStack { Text("Applications") @@ -257,7 +244,6 @@ struct ConfigurationView: View { Divider() - // Websites Subsection VStack(alignment: .leading, spacing: 12) { Text("Websites") .font(.subheadline) @@ -328,12 +314,9 @@ struct ConfigurationView: View { .background(CardBackground(isSelected: false)) .padding(.horizontal) - // SECTION 2: TRANSCRIPTION VStack(spacing: 16) { - // Section Header SectionHeader(title: "Transcription") - // Whisper Model Selection Subsection if whisperState.usableModels.isEmpty { Text("No transcription models available. Please connect to a cloud service or download a local model in the AI Models tab.") .font(.subheadline) @@ -342,7 +325,6 @@ struct ConfigurationView: View { .frame(maxWidth: .infinity, alignment: .center) .background(CardBackground(isSelected: false)) } else { - // Create a simple binding that uses current model if nil let modelBinding = Binding( get: { selectedTranscriptionModelName ?? whisperState.usableModels.first?.name @@ -366,12 +348,10 @@ struct ConfigurationView: View { } } - // Language Selection Subsection if let selectedModel = effectiveModelName, let modelInfo = whisperState.allAvailableModels.first(where: { $0.name == selectedModel }), modelInfo.isMultilingualModel { - // Create a simple binding that uses UserDefaults language if nil let languageBinding = Binding( get: { selectedLanguage ?? UserDefaults.standard.string(forKey: "SelectedLanguage") ?? "auto" @@ -400,7 +380,6 @@ struct ConfigurationView: View { } else if let selectedModel = effectiveModelName, let modelInfo = whisperState.allAvailableModels.first(where: { $0.name == selectedModel }), !modelInfo.isMultilingualModel { - // Silently set to English without showing UI let _ = { selectedLanguage = "en" }() } } @@ -408,16 +387,13 @@ struct ConfigurationView: View { .background(CardBackground(isSelected: false)) .padding(.horizontal) - // SECTION 3: AI ENHANCEMENT VStack(spacing: 16) { - // Section Header SectionHeader(title: "AI Enhancement") Toggle("Enable AI Enhancement", isOn: $isAIEnhancementEnabled) .frame(maxWidth: .infinity, alignment: .leading) .onChange(of: isAIEnhancementEnabled) { oldValue, newValue in if newValue { - // When enabling AI enhancement, set default values if none are selected if selectedAIProvider == nil { selectedAIProvider = aiService.selectedProvider.rawValue } @@ -429,21 +405,18 @@ struct ConfigurationView: View { Divider() - // AI Provider Selection - Match style with Whisper model selection - // Create a binding for the provider selection that falls back to global settings let providerBinding = Binding( get: { if let providerName = selectedAIProvider, let provider = AIProvider(rawValue: providerName) { return provider } - // Just return the global provider without modifying state return aiService.selectedProvider }, set: { newValue in - selectedAIProvider = newValue.rawValue // Update local state for UI responsiveness - aiService.selectedProvider = newValue // Update global AI service state - selectedAIModel = nil // Reset selected model when provider changes + selectedAIProvider = newValue.rawValue + aiService.selectedProvider = newValue + selectedAIModel = nil } ) @@ -470,9 +443,7 @@ struct ConfigurationView: View { } .labelsHidden() .onChange(of: selectedAIProvider) { oldValue, newValue in - // When provider changes, ensure we have a valid model for that provider if let provider = newValue.flatMap({ AIProvider(rawValue: $0) }) { - // Set default model for this provider selectedAIModel = provider.defaultModel } } @@ -480,7 +451,6 @@ struct ConfigurationView: View { } } - // AI Model Selection - Match style with whisper language selection let providerName = selectedAIProvider ?? aiService.selectedProvider.rawValue if let provider = AIProvider(rawValue: providerName), provider != .custom { @@ -496,18 +466,15 @@ struct ConfigurationView: View { .italic() .frame(maxWidth: .infinity, alignment: .leading) } else { - // Create binding that falls back to current model for the selected provider let modelBinding = Binding( get: { if let model = selectedAIModel, !model.isEmpty { return model } - // Just return the current model without modifying state return aiService.currentModel }, set: { newModelValue in - selectedAIModel = newModelValue // Update local state - // Update the model in AIService for the current provider + selectedAIModel = newModelValue aiService.selectModel(newModelValue) } ) @@ -539,7 +506,6 @@ struct ConfigurationView: View { } - // Enhancement Prompts Section (reused from EnhancementSettingsView) VStack(alignment: .leading, spacing: 12) { Text("Enhancement Prompt") .font(.headline) @@ -576,7 +542,6 @@ struct ConfigurationView: View { .background(CardBackground(isSelected: false)) .padding(.horizontal) - // SECTION 4: ADVANCED VStack(spacing: 16) { SectionHeader(title: "Advanced") @@ -595,7 +560,6 @@ struct ConfigurationView: View { .background(CardBackground(isSelected: false)) .padding(.horizontal) - // Save Button VoiceInkButton( title: mode.isAdding ? "Add New Power Mode" : "Save Changes", action: saveConfiguration, diff --git a/VoiceInk/PowerMode/PowerModePopover.swift b/VoiceInk/PowerMode/PowerModePopover.swift index 06a5c6f..ba9a4c5 100644 --- a/VoiceInk/PowerMode/PowerModePopover.swift +++ b/VoiceInk/PowerMode/PowerModePopover.swift @@ -1,6 +1,5 @@ import SwiftUI -// Power Mode Popover for recorder views struct PowerModePopover: View { @ObservedObject var powerModeManager = PowerModeManager.shared @State private var selectedConfig: PowerModeConfig? @@ -18,13 +17,10 @@ struct PowerModePopover: View { ScrollView { VStack(alignment: .leading, spacing: 4) { - // "Disable" option if a power mode is active if powerModeManager.activeConfiguration != nil { Button(action: { powerModeManager.setActiveConfiguration(nil) selectedConfig = nil - // Here we might want to revert to a default state, - // but for now, we'll just deactivate the power mode. }) { HStack { Text("Disable Power Mode") @@ -40,7 +36,6 @@ struct PowerModePopover: View { .buttonStyle(.plain) } - // Custom Configurations ForEach(powerModeManager.configurations) { config in PowerModeRow( config: config, @@ -48,7 +43,6 @@ struct PowerModePopover: View { action: { powerModeManager.setActiveConfiguration(config) selectedConfig = config - // Apply configuration immediately applySelectedConfiguration() } ) @@ -63,12 +57,10 @@ struct PowerModePopover: View { .background(Color.black) .environment(\.colorScheme, .dark) .onAppear { - // Set the initially selected configuration selectedConfig = powerModeManager.activeConfiguration } } - // Helper function to apply the selected configuration private func applySelectedConfiguration() { Task { if let config = selectedConfig { @@ -78,7 +70,6 @@ struct PowerModePopover: View { } } -// Row view for each power mode in the popover struct PowerModeRow: View { let config: PowerModeConfig let isSelected: Bool @@ -87,7 +78,6 @@ struct PowerModeRow: View { var body: some View { Button(action: action) { HStack(spacing: 8) { - // Always use the emoji from the configuration Text(config.emoji) .font(.system(size: 14)) diff --git a/VoiceInk/PowerMode/PowerModeSessionManager.swift b/VoiceInk/PowerMode/PowerModeSessionManager.swift index debe5f7..eb274b2 100644 --- a/VoiceInk/PowerMode/PowerModeSessionManager.swift +++ b/VoiceInk/PowerMode/PowerModeSessionManager.swift @@ -1,19 +1,16 @@ import Foundation import AppKit -// Represents the state of the application that can be modified by a Power Mode. -// This struct captures the settings that will be temporarily overridden. struct ApplicationState: Codable { var isEnhancementEnabled: Bool var useScreenCaptureContext: Bool - var selectedPromptId: String? // Storing as String for Codable simplicity + var selectedPromptId: String? var selectedAIProvider: String? var selectedAIModel: String? var selectedLanguage: String? var transcriptionModelName: String? } -// Represents an active Power Mode session. struct PowerModeSession: Codable { let id: UUID let startTime: Date @@ -29,7 +26,6 @@ class PowerModeSessionManager { private var enhancementService: AIEnhancementService? private init() { - // Attempt to recover a session on startup in case of a crash. recoverSession() } @@ -38,15 +34,12 @@ class PowerModeSessionManager { self.enhancementService = enhancementService } - // Begins a new Power Mode session. It captures the current state, - // applies the new configuration, and saves the session. func beginSession(with config: PowerModeConfig) async { guard let whisperState = whisperState, let enhancementService = enhancementService else { print("SessionManager not configured.") return } - // 1. Capture the current application state. let originalState = ApplicationState( isEnhancementEnabled: enhancementService.isEnhancementEnabled, useScreenCaptureContext: enhancementService.useScreenCaptureContext, @@ -57,7 +50,6 @@ class PowerModeSessionManager { transcriptionModelName: whisperState.currentTranscriptionModel?.name ) - // 2. Create and save the session. let newSession = PowerModeSession( id: UUID(), startTime: Date(), @@ -65,22 +57,17 @@ class PowerModeSessionManager { ) saveSession(newSession) - // 3. Apply the new configuration's settings. await applyConfiguration(config) } - // Ends the current Power Mode session and restores the original state. func endSession() async { guard let session = loadSession() else { return } - // Restore the original state from the session. await restoreState(session.originalState) - // Clear the session from UserDefaults. clearSession() } - // Applies the settings from a PowerModeConfig. private func applyConfiguration(_ config: PowerModeConfig) async { guard let enhancementService = enhancementService else { return } @@ -117,7 +104,6 @@ class PowerModeSessionManager { } } - // Restores the application state from a saved state object. private func restoreState(_ state: ApplicationState) async { guard let enhancementService = enhancementService else { return } @@ -149,7 +135,6 @@ class PowerModeSessionManager { } } - // Handles the logic for switching transcription models. private func handleModelChange(to newModel: any TranscriptionModel) async { guard let whisperState = whisperState else { return } @@ -162,13 +147,11 @@ class PowerModeSessionManager { do { try await whisperState.loadModel(localModel) } catch { - // Log error appropriately print("Power Mode: Failed to load local model '\(localModel.name)': \(error)") } } case .parakeet: await whisperState.cleanupModelResources() - // Parakeet models are loaded on demand, so we only need to clean up. default: await whisperState.cleanupModelResources() @@ -183,8 +166,6 @@ class PowerModeSessionManager { } } - // MARK: - UserDefaults Persistence - private func saveSession(_ session: PowerModeSession) { do { let data = try JSONEncoder().encode(session) diff --git a/VoiceInk/PowerMode/PowerModeValidator.swift b/VoiceInk/PowerMode/PowerModeValidator.swift index a8f863b..fc547b1 100644 --- a/VoiceInk/PowerMode/PowerModeValidator.swift +++ b/VoiceInk/PowerMode/PowerModeValidator.swift @@ -41,20 +41,15 @@ struct PowerModeValidator { self.powerModeManager = powerModeManager } - /// Validates a power mode configuration when the user tries to save it. - /// This validation only happens at save time, not during editing. func validateForSave(config: PowerModeConfig, mode: ConfigurationMode) -> [PowerModeValidationError] { var errors: [PowerModeValidationError] = [] - // Validate name if config.name.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty { errors.append(.emptyName) } - // Check for duplicate name let isDuplicateName = powerModeManager.configurations.contains { existingConfig in if case .edit(let editConfig) = mode, existingConfig.id == editConfig.id { - // Skip checking against itself when editing return false } return existingConfig.name == config.name @@ -64,17 +59,14 @@ struct PowerModeValidator { errors.append(.duplicateName(config.name)) } - // For all modes, check that there's at least one trigger if (config.appConfigs == nil || config.appConfigs?.isEmpty == true) && (config.urlConfigs == nil || config.urlConfigs?.isEmpty == true) { errors.append(.noTriggers) } - // Check for duplicate app configurations if let appConfigs = config.appConfigs { for appConfig in appConfigs { for existingConfig in powerModeManager.configurations { - // Skip checking against itself when editing if case .edit(let editConfig) = mode, existingConfig.id == editConfig.id { continue } @@ -87,11 +79,9 @@ struct PowerModeValidator { } } - // Check for duplicate website configurations if let urlConfigs = config.urlConfigs { for urlConfig in urlConfigs { for existingConfig in powerModeManager.configurations { - // Skip checking against itself when editing if case .edit(let editConfig) = mode, existingConfig.id == editConfig.id { continue } @@ -108,7 +98,6 @@ struct PowerModeValidator { } } -// Alert extension for showing validation errors extension View { func powerModeValidationAlert( errors: [PowerModeValidationError], diff --git a/VoiceInk/PowerMode/PowerModeView.swift b/VoiceInk/PowerMode/PowerModeView.swift index d8fe164..3cf42b7 100644 --- a/VoiceInk/PowerMode/PowerModeView.swift +++ b/VoiceInk/PowerMode/PowerModeView.swift @@ -13,7 +13,6 @@ extension View { } } -// Configuration Mode Enum enum ConfigurationMode: Hashable { case add case edit(PowerModeConfig) @@ -30,18 +29,16 @@ enum ConfigurationMode: Hashable { } } - // Implement hash(into:) to conform to Hashable func hash(into hasher: inout Hasher) { switch self { case .add: - hasher.combine(0) // Use a unique value for add + hasher.combine(0) case .edit(let config): - hasher.combine(1) // Use a unique value for edit + hasher.combine(1) hasher.combine(config.id) } } - // Implement == to conform to Equatable (required by Hashable) static func == (lhs: ConfigurationMode, rhs: ConfigurationMode) -> Bool { switch (lhs, rhs) { case (.add, .add): @@ -54,16 +51,13 @@ enum ConfigurationMode: Hashable { } } -// Configuration Type enum ConfigurationType { case application case website } -// Common Emojis for selection let commonEmojis = ["🏢", "🏠", "💼", "🎮", "📱", "📺", "🎵", "📚", "✏️", "🎨", "🧠", "⚙️", "💻", "🌐", "📝", "📊", "🔍", "💬", "📈", "🔧"] -// Main Power Mode View with Navigation struct PowerModeView: View { @StateObject private var powerModeManager = PowerModeManager.shared @EnvironmentObject private var enhancementService: AIEnhancementService @@ -74,7 +68,6 @@ struct PowerModeView: View { var body: some View { NavigationStack(path: $navigationPath) { VStack(spacing: 0) { - // Header Section with proper macOS styling VStack(spacing: 12) { HStack { VStack(alignment: .leading, spacing: 4) { @@ -83,8 +76,7 @@ struct PowerModeView: View { .font(.system(size: 28, weight: .bold, design: .default)) .foregroundColor(.primary) - // InfoTip for Power Mode - InfoTip( + InfoTip( title: "What is Power Mode?", message: "Automatically apply custom configurations based on the app/website you are using", learnMoreURL: "https://www.youtube.com/watch?v=-xFLvgNs_Iw" @@ -98,7 +90,6 @@ struct PowerModeView: View { Spacer() - // Add button in header for better macOS UX Button(action: { configurationMode = .add navigationPath.append(configurationMode!) @@ -122,18 +113,15 @@ struct PowerModeView: View { .padding(.top, 20) .padding(.bottom, 16) - // Separator Rectangle() .fill(Color(NSColor.separatorColor)) .frame(height: 1) .padding(.horizontal, 24) - // Main Content Area GeometryReader { geometry in ScrollView { VStack(spacing: 0) { if powerModeManager.configurations.isEmpty { - // Empty State - Centered and symmetric VStack(spacing: 24) { Spacer() .frame(height: geometry.size.height * 0.2) @@ -148,7 +136,7 @@ struct PowerModeView: View { .font(.system(size: 20, weight: .medium)) .foregroundColor(.primary) - Text("Create your first power mode to enhance your productivity\nwith context-aware AI assistance") + Text("Create your first power mode to enhance your VoiceInk experience\nwith context-aware speech-to-text AI transcription tool") .font(.system(size: 14)) .foregroundColor(.secondary) .multilineTextAlignment(.center) @@ -161,7 +149,6 @@ struct PowerModeView: View { .frame(maxWidth: .infinity) .frame(minHeight: geometry.size.height) } else { - // Configurations Grid with symmetric padding VStack(spacing: 0) { PowerModeConfigurationsGrid( powerModeManager: powerModeManager, @@ -173,7 +160,6 @@ struct PowerModeView: View { .padding(.horizontal, 24) .padding(.vertical, 20) - // Bottom spacing for visual balance Spacer() .frame(height: 40) } diff --git a/VoiceInk/PowerMode/PowerModeViewComponents.swift b/VoiceInk/PowerMode/PowerModeViewComponents.swift index 2ebfae9..aaee294 100644 --- a/VoiceInk/PowerMode/PowerModeViewComponents.swift +++ b/VoiceInk/PowerMode/PowerModeViewComponents.swift @@ -1,7 +1,5 @@ import SwiftUI -// Supporting Views -// VoiceInk's consistent button component struct VoiceInkButton: View { let title: String let action: () -> Void @@ -91,10 +89,8 @@ struct ConfigurationRow: View { @EnvironmentObject var enhancementService: AIEnhancementService @EnvironmentObject var whisperState: WhisperState - // How many app icons to show at maximum private let maxAppIconsToShow = 5 - // Data properties private var selectedPrompt: CustomPrompt? { guard let promptId = config.selectedPrompt, let uuid = UUID(uuidString: promptId) else { return nil } @@ -147,51 +143,46 @@ struct ConfigurationRow: View { var body: some View { VStack(spacing: 0) { - // Top row: Emoji, Name, and App/Website counts HStack(spacing: 12) { - // Left: Emoji/Icon - ZStack { - Circle() - .fill(Color(NSColor.controlBackgroundColor)) - .frame(width: 40, height: 40) - - Text(config.emoji) - .font(.system(size: 20)) - } + ZStack { + Circle() + .fill(Color(NSColor.controlBackgroundColor)) + .frame(width: 40, height: 40) + + Text(config.emoji) + .font(.system(size: 20)) + } - // Middle: Name and badge - VStack(alignment: .leading, spacing: 3) { - HStack(spacing: 6) { - Text(config.name) - .font(.system(size: 15, weight: .semibold)) + VStack(alignment: .leading, spacing: 3) { + HStack(spacing: 6) { + Text(config.name) + .font(.system(size: 15, weight: .semibold)) + } + + HStack(spacing: 12) { + if appCount > 0 { + HStack(spacing: 4) { + Image(systemName: "app.fill") + .font(.system(size: 10)) + Text(appText) + .font(.caption2) + } } - // Display app and website counts - HStack(spacing: 12) { - if appCount > 0 { - HStack(spacing: 4) { - Image(systemName: "app.fill") - .font(.system(size: 10)) - Text(appText) - .font(.caption2) - } - } - - if websiteCount > 0 { - HStack(spacing: 4) { - Image(systemName: "globe") - .font(.system(size: 10)) - Text(websiteText) - .font(.caption2) - } + if websiteCount > 0 { + HStack(spacing: 4) { + Image(systemName: "globe") + .font(.system(size: 10)) + Text(websiteText) + .font(.caption2) } } - .foregroundColor(.secondary) } + .foregroundColor(.secondary) + } Spacer() - // Right: Toggle Switch Toggle("", isOn: $config.isEnabled) .toggleStyle(SwitchToggleStyle(tint: .accentColor)) .labelsHidden() @@ -199,14 +190,11 @@ struct ConfigurationRow: View { .padding(.vertical, 12) .padding(.horizontal, 14) - // Only add divider and settings row if we have settings if selectedModel != nil || selectedLanguage != nil || config.isAIEnhancementEnabled { Divider() .padding(.horizontal, 16) - // Settings badges in specified order HStack(spacing: 8) { - // 1. Voice Model badge if let model = selectedModel, model != "Default" { HStack(spacing: 4) { Image(systemName: "waveform") @@ -224,7 +212,6 @@ struct ConfigurationRow: View { ) } - // 2. Language badge if let language = selectedLanguage, language != "Default" { HStack(spacing: 4) { Image(systemName: "globe") @@ -242,12 +229,10 @@ struct ConfigurationRow: View { ) } - // 3. AI Model badge if specified (moved before AI Enhancement) if config.isAIEnhancementEnabled, let modelName = config.selectedAIModel, !modelName.isEmpty { HStack(spacing: 4) { Image(systemName: "cpu") .font(.system(size: 10)) - // Display a shortened version of the model name if it's too long (increased limit) Text(modelName.count > 20 ? String(modelName.prefix(18)) + "..." : modelName) .font(.caption) } @@ -261,9 +246,7 @@ struct ConfigurationRow: View { ) } - // 4. AI Enhancement badge if config.isAIEnhancementEnabled { - // Context Awareness badge (moved before AI Enhancement) if config.useScreenCapture { HStack(spacing: 4) { Image(systemName: "camera.viewfinder") @@ -321,7 +304,6 @@ struct ConfigurationRow: View { } } -// App Icon View Component struct PowerModeAppIcon: View { let bundleId: String