diff --git a/VoiceInk/PowerMode/PowerModeConfigView.swift b/VoiceInk/PowerMode/PowerModeConfigView.swift index 23a1ef9..55ca34f 100644 --- a/VoiceInk/PowerMode/PowerModeConfigView.swift +++ b/VoiceInk/PowerMode/PowerModeConfigView.swift @@ -5,7 +5,6 @@ struct ConfigurationView: View { let powerModeManager: PowerModeManager @EnvironmentObject var enhancementService: AIEnhancementService @EnvironmentObject var aiService: AIService - @Environment(\.dismiss) private var dismiss @Environment(\.presentationMode) private var presentationMode @FocusState private var isNameFieldFocused: Bool @@ -225,8 +224,7 @@ struct ConfigurationView: View { Spacer() } .padding() - .background(Color(.windowBackgroundColor).opacity(0.2)) - .cornerRadius(8) + .background(CardBackground(isSelected: false)) } else { // Grid of selected apps that wraps to next line LazyVGrid(columns: [GridItem(.adaptive(minimum: 50, maximum: 55), spacing: 10)], spacing: 10) { @@ -302,8 +300,7 @@ struct ConfigurationView: View { Spacer() } .padding() - .background(Color(.windowBackgroundColor).opacity(0.2)) - .cornerRadius(8) + .background(CardBackground(isSelected: false)) } else { // Grid of website tags that wraps to next line LazyVGrid(columns: [GridItem(.adaptive(minimum: 100, maximum: 160), spacing: 10)], spacing: 10) { @@ -339,10 +336,7 @@ struct ConfigurationView: View { } } .padding() - .background( - RoundedRectangle(cornerRadius: 12) - .fill(Color(.windowBackgroundColor).opacity(0.4)) - ) + .background(CardBackground(isSelected: false)) .padding(.horizontal) } @@ -358,8 +352,7 @@ struct ConfigurationView: View { .foregroundColor(.secondary) .padding() .frame(maxWidth: .infinity, alignment: .center) - .background(Color(.windowBackgroundColor).opacity(0.2)) - .cornerRadius(8) + .background(CardBackground(isSelected: false)) } else { // Create a simple binding that uses current model if nil let modelBinding = Binding( @@ -420,17 +413,11 @@ struct ConfigurationView: View { let modelInfo = whisperState.allAvailableModels.first(where: { $0.name == selectedModel }), !modelInfo.isMultilingualModel { // Silently set to English without showing UI - EmptyView() - .onAppear { - selectedLanguage = "en" - } + let _ = { selectedLanguage = "en" }() } } .padding() - .background( - RoundedRectangle(cornerRadius: 12) - .fill(Color(.windowBackgroundColor).opacity(0.4)) - ) + .background(CardBackground(isSelected: false)) .padding(.horizontal) // SECTION 3: AI ENHANCEMENT @@ -515,8 +502,8 @@ struct ConfigurationView: View { .font(.subheadline) .foregroundColor(.secondary) - if provider == .ollama && aiService.availableModels.isEmpty { - Text("No models available") + if aiService.availableModels.isEmpty { + Text(provider == .openRouter ? "No models loaded" : "No models available") .foregroundColor(.secondary) .italic() .frame(maxWidth: .infinity, alignment: .leading) @@ -537,7 +524,7 @@ struct ConfigurationView: View { } ) - let models = provider == .ollama ? aiService.availableModels : provider.availableModels + let models = provider == .openRouter ? aiService.availableModels : (provider == .ollama ? aiService.availableModels : provider.availableModels) Picker("", selection: modelBinding) { ForEach(models, id: \.self) { model in @@ -545,7 +532,19 @@ struct ConfigurationView: View { } } .labelsHidden() - + + if provider == .openRouter { + Button(action: { + Task { + await aiService.fetchOpenRouterModels() + } + }) { + Image(systemName: "arrow.clockwise") + } + .buttonStyle(.borderless) + .help("Refresh models") + } + Spacer() } } @@ -586,10 +585,7 @@ struct ConfigurationView: View { } } .padding() - .background( - RoundedRectangle(cornerRadius: 12) - .fill(Color(.windowBackgroundColor).opacity(0.4)) - ) + .background(CardBackground(isSelected: false)) .padding(.horizontal) // Save Button diff --git a/VoiceInk/Services/AIService.swift b/VoiceInk/Services/AIService.swift index 21d939f..191044e 100644 --- a/VoiceInk/Services/AIService.swift +++ b/VoiceInk/Services/AIService.swift @@ -174,7 +174,7 @@ class AIService: ObservableObject { private let userDefaults = UserDefaults.standard private lazy var ollamaService = OllamaService() - private var openRouterModels: [String] = [] + @Published private var openRouterModels: [String] = [] var connectedProviders: [AIProvider] { AIProvider.allCases.filter { provider in @@ -223,6 +223,7 @@ class AIService: ObservableObject { } loadSavedModelSelections() + loadSavedOpenRouterModels() } private func loadSavedModelSelections() { @@ -234,6 +235,16 @@ class AIService: ObservableObject { } } + private func loadSavedOpenRouterModels() { + if let savedModels = userDefaults.array(forKey: "openRouterModels") as? [String] { + openRouterModels = savedModels + } + } + + private func saveOpenRouterModels() { + userDefaults.set(openRouterModels, forKey: "openRouterModels") + } + func selectModel(_ model: String) { guard !model.isEmpty else { return } @@ -499,6 +510,7 @@ class AIService: ObservableObject { logger.error("Failed to fetch OpenRouter models: Invalid HTTP response") await MainActor.run { self.openRouterModels = [] + self.saveOpenRouterModels() self.objectWillChange.send() } return @@ -509,6 +521,7 @@ class AIService: ObservableObject { logger.error("Failed to parse OpenRouter models JSON") await MainActor.run { self.openRouterModels = [] + self.saveOpenRouterModels() self.objectWillChange.send() } return @@ -517,6 +530,7 @@ class AIService: ObservableObject { let models = dataArray.compactMap { $0["id"] as? String } await MainActor.run { self.openRouterModels = models.sorted() + self.saveOpenRouterModels() // Save to UserDefaults if self.selectedProvider == .openRouter && self.currentModel == self.selectedProvider.defaultModel && !models.isEmpty { self.selectModel(models.sorted().first!) } @@ -528,12 +542,15 @@ class AIService: ObservableObject { logger.error("Error fetching OpenRouter models: \(error.localizedDescription)") await MainActor.run { self.openRouterModels = [] + self.saveOpenRouterModels() self.objectWillChange.send() } } + } } extension Notification.Name { static let aiProviderKeyChanged = Notification.Name("aiProviderKeyChanged") } + diff --git a/VoiceInk/Views/APIKeyManagementView.swift b/VoiceInk/Views/APIKeyManagementView.swift index 06ff60e..5f300d3 100644 --- a/VoiceInk/Views/APIKeyManagementView.swift +++ b/VoiceInk/Views/APIKeyManagementView.swift @@ -52,17 +52,41 @@ struct APIKeyManagementView: View { .onChange(of: aiService.selectedProvider) { oldValue, newValue in if aiService.selectedProvider == .ollama { checkOllamaConnection() - } else if aiService.selectedProvider == .openRouter { - Task { - await aiService.fetchOpenRouterModels() - } } } - // Model Selection - only show for standard providers with available models - if !aiService.availableModels.isEmpty && - aiService.selectedProvider != .ollama && - aiService.selectedProvider != .custom { + // Model Selection + if aiService.selectedProvider == .openRouter { + HStack { + if aiService.availableModels.isEmpty { + Text("No models loaded") + .foregroundColor(.secondary) + } else { + Picker("Model", selection: Binding( + get: { aiService.currentModel }, + set: { aiService.selectModel($0) } + )) { + ForEach(aiService.availableModels, id: \.self) { model in + Text(model).tag(model) + } + } + } + + + + Button(action: { + Task { + await aiService.fetchOpenRouterModels() + } + }) { + Image(systemName: "arrow.clockwise") + } + .buttonStyle(.borderless) + .help("Refresh models") + } + } else if !aiService.availableModels.isEmpty && + aiService.selectedProvider != .ollama && + aiService.selectedProvider != .custom { HStack { Picker("Model", selection: Binding( get: { aiService.currentModel }, @@ -72,18 +96,6 @@ struct APIKeyManagementView: View { Text(model).tag(model) } } - - if aiService.selectedProvider == .openRouter { - Button(action: { - Task { - await aiService.fetchOpenRouterModels() - } - }) { - Image(systemName: "arrow.clockwise") - } - .buttonStyle(.borderless) - .help("Refresh models") - } } } @@ -440,10 +452,6 @@ struct APIKeyManagementView: View { .onAppear { if aiService.selectedProvider == .ollama { checkOllamaConnection() - } else if aiService.selectedProvider == .openRouter { - Task { - await aiService.fetchOpenRouterModels() - } } } }