2.5.0 #10

Merged
rune merged 41 commits from 2.5.0 into main 2026-08-03 08:33:23 +02:00
Showing only changes of commit 37734232f5 - Show all commits
@@ -25,7 +25,6 @@ import SwiftUI
struct CombineConversationsSheet: View { struct CombineConversationsSheet: View {
@Environment(\.dismiss) var dismiss @Environment(\.dismiss) var dismiss
@Environment(ChatViewModel.self) private var chatViewModel
let conversations: [Conversation] let conversations: [Conversation]
var onCompleted: (Conversation) -> Void var onCompleted: (Conversation) -> Void
@@ -37,6 +36,8 @@ struct CombineConversationsSheet: View {
@State private var errorMessage: String? @State private var errorMessage: String?
@State private var mergeModel: ModelInfo? @State private var mergeModel: ModelInfo?
@State private var mergeProvider: Settings.Provider @State private var mergeProvider: Settings.Provider
@State private var mergeModels: [ModelInfo] = []
@State private var isLoadingMergeModels = false
@State private var showModelPicker = false @State private var showModelPicker = false
private let settings = SettingsService.shared private let settings = SettingsService.shared
@@ -50,11 +51,8 @@ struct CombineConversationsSheet: View {
} }
private var mergeModelLabel: String? { private var mergeModelLabel: String? {
if let mergeModel { guard let mergeModel else { return nil }
return "\(mergeProvider.displayName) / \(mergeModel.name)" return "\(mergeProvider.displayName) / \(mergeModel.name)"
}
guard let model = settings.defaultModel, !model.isEmpty else { return nil }
return "\(settings.defaultProvider.displayName) / \(model)"
} }
private var isValid: Bool { private var isValid: Bool {
@@ -125,12 +123,37 @@ struct CombineConversationsSheet: View {
Label("No model selected", systemImage: "exclamationmark.triangle.fill") Label("No model selected", systemImage: "exclamationmark.triangle.fill")
.font(.caption).foregroundStyle(.orange) .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 showModelPicker = true
} }
.buttonStyle(.link) .buttonStyle(.link)
.font(.caption) .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) .frame(minWidth: 520, idealWidth: 560, minHeight: 460, idealHeight: 520)
.task { .task {
if chatViewModel.availableModels.count <= 10 { await loadMergeModels()
await chatViewModel.loadAvailableModels() if let defaultModel = settings.defaultModel {
} mergeModel = mergeModels.first(where: { $0.id == defaultModel })
if mergeModel == nil, let defaultModel = settings.defaultModel {
mergeModel = chatViewModel.availableModels.first(where: { $0.id == defaultModel })
} }
} }
.sheet(isPresented: $showModelPicker) { .sheet(isPresented: $showModelPicker) {
ModelSelectorView( ModelSelectorView(
models: chatViewModel.availableModels, models: mergeModels,
selectedModel: mergeModel, selectedModel: mergeModel,
onSelect: { model in onSelect: { model in
mergeModel = model mergeModel = model
mergeProvider = chatViewModel.inferProviderPublic(from: model.id) ?? mergeProvider
showModelPicker = false 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() { private func combine() {
isProcessing = true isProcessing = true
errorMessage = nil errorMessage = nil