Decouple merge model picker from the main chat's active provider
The merge model picker was reusing chatViewModel.availableModels, which only ever holds whichever provider the main chat window currently has active — fine for the main chat's own switcher (where provider and model change together via the header), wrong for an independent one-off picker like this. If your active chat was on Anthropic, that's all you could pick from here regardless of what other providers you have configured. Added its own provider menu (mirroring HeaderView's) and an independent model list fetched via ProviderRegistry for whichever provider is selected, so OpenRouter, Anthropic, OpenAI, etc. are all genuinely selectable regardless of what the main chat is doing.
This commit is contained in:
@@ -25,7 +25,6 @@ import SwiftUI
|
||||
|
||||
struct CombineConversationsSheet: View {
|
||||
@Environment(\.dismiss) var dismiss
|
||||
@Environment(ChatViewModel.self) private var chatViewModel
|
||||
|
||||
let conversations: [Conversation]
|
||||
var onCompleted: (Conversation) -> Void
|
||||
@@ -37,6 +36,8 @@ struct CombineConversationsSheet: View {
|
||||
@State private var errorMessage: String?
|
||||
@State private var mergeModel: ModelInfo?
|
||||
@State private var mergeProvider: Settings.Provider
|
||||
@State private var mergeModels: [ModelInfo] = []
|
||||
@State private var isLoadingMergeModels = false
|
||||
@State private var showModelPicker = false
|
||||
|
||||
private let settings = SettingsService.shared
|
||||
@@ -50,11 +51,8 @@ struct CombineConversationsSheet: View {
|
||||
}
|
||||
|
||||
private var mergeModelLabel: String? {
|
||||
if let mergeModel {
|
||||
return "\(mergeProvider.displayName) / \(mergeModel.name)"
|
||||
}
|
||||
guard let model = settings.defaultModel, !model.isEmpty else { return nil }
|
||||
return "\(settings.defaultProvider.displayName) / \(model)"
|
||||
guard let mergeModel else { return nil }
|
||||
return "\(mergeProvider.displayName) / \(mergeModel.name)"
|
||||
}
|
||||
|
||||
private var isValid: Bool {
|
||||
@@ -125,12 +123,37 @@ struct CombineConversationsSheet: View {
|
||||
Label("No model selected", systemImage: "exclamationmark.triangle.fill")
|
||||
.font(.caption).foregroundStyle(.orange)
|
||||
}
|
||||
Button("Change…") {
|
||||
|
||||
Menu {
|
||||
ForEach(ProviderRegistry.shared.configuredProviders, id: \.self) { p in
|
||||
Button {
|
||||
switchMergeProvider(to: p)
|
||||
} label: {
|
||||
HStack {
|
||||
Image(systemName: p.iconName)
|
||||
Text(p.displayName)
|
||||
if p == mergeProvider { Image(systemName: "checkmark") }
|
||||
}
|
||||
}
|
||||
}
|
||||
} label: {
|
||||
Text(mergeProvider.displayName)
|
||||
}
|
||||
.menuStyle(.borderlessButton)
|
||||
.fixedSize()
|
||||
.font(.caption)
|
||||
.disabled(isProcessing || isLoadingMergeModels)
|
||||
|
||||
Button("Change Model…") {
|
||||
showModelPicker = true
|
||||
}
|
||||
.buttonStyle(.link)
|
||||
.font(.caption)
|
||||
.disabled(isProcessing)
|
||||
.disabled(isProcessing || isLoadingMergeModels || mergeModels.isEmpty)
|
||||
|
||||
if isLoadingMergeModels {
|
||||
ProgressView().controlSize(.mini)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -173,26 +196,46 @@ struct CombineConversationsSheet: View {
|
||||
}
|
||||
.frame(minWidth: 520, idealWidth: 560, minHeight: 460, idealHeight: 520)
|
||||
.task {
|
||||
if chatViewModel.availableModels.count <= 10 {
|
||||
await chatViewModel.loadAvailableModels()
|
||||
}
|
||||
if mergeModel == nil, let defaultModel = settings.defaultModel {
|
||||
mergeModel = chatViewModel.availableModels.first(where: { $0.id == defaultModel })
|
||||
await loadMergeModels()
|
||||
if let defaultModel = settings.defaultModel {
|
||||
mergeModel = mergeModels.first(where: { $0.id == defaultModel })
|
||||
}
|
||||
}
|
||||
.sheet(isPresented: $showModelPicker) {
|
||||
ModelSelectorView(
|
||||
models: chatViewModel.availableModels,
|
||||
models: mergeModels,
|
||||
selectedModel: mergeModel,
|
||||
onSelect: { model in
|
||||
mergeModel = model
|
||||
mergeProvider = chatViewModel.inferProviderPublic(from: model.id) ?? mergeProvider
|
||||
showModelPicker = false
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private func switchMergeProvider(to newProvider: Settings.Provider) {
|
||||
guard newProvider != mergeProvider else { return }
|
||||
mergeProvider = newProvider
|
||||
mergeModel = nil
|
||||
mergeModels = []
|
||||
Task { await loadMergeModels() }
|
||||
}
|
||||
|
||||
private func loadMergeModels() async {
|
||||
guard let provider = ProviderRegistry.shared.getProvider(for: mergeProvider) else {
|
||||
mergeModels = []
|
||||
return
|
||||
}
|
||||
isLoadingMergeModels = true
|
||||
defer { isLoadingMergeModels = false }
|
||||
do {
|
||||
mergeModels = try await provider.listModels()
|
||||
} catch {
|
||||
Log.api.error("Failed to load models for merge provider \(mergeProvider.rawValue): \(error.localizedDescription)")
|
||||
mergeModels = []
|
||||
}
|
||||
}
|
||||
|
||||
private func combine() {
|
||||
isProcessing = true
|
||||
errorMessage = nil
|
||||
|
||||
Reference in New Issue
Block a user