Small feature changes and bug fixes
This commit is contained in:
@@ -14,14 +14,23 @@ struct ConversationListView: View {
|
||||
@State private var conversations: [Conversation] = []
|
||||
@State private var selectedConversations: Set<UUID> = []
|
||||
@State private var isSelecting = false
|
||||
@State private var useSemanticSearch = false
|
||||
@State private var semanticResults: [Conversation] = []
|
||||
@State private var isSearching = false
|
||||
private let settings = SettingsService.shared
|
||||
var onLoad: ((Conversation) -> Void)?
|
||||
|
||||
private var filteredConversations: [Conversation] {
|
||||
if searchText.isEmpty {
|
||||
return conversations
|
||||
}
|
||||
return conversations.filter {
|
||||
$0.name.lowercased().contains(searchText.lowercased())
|
||||
|
||||
if useSemanticSearch && settings.embeddingsEnabled {
|
||||
return semanticResults
|
||||
} else {
|
||||
return conversations.filter {
|
||||
$0.name.lowercased().contains(searchText.lowercased())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,6 +88,11 @@ struct ConversationListView: View {
|
||||
.foregroundStyle(.secondary)
|
||||
TextField("Search conversations...", text: $searchText)
|
||||
.textFieldStyle(.plain)
|
||||
.onChange(of: searchText) {
|
||||
if useSemanticSearch && settings.embeddingsEnabled && !searchText.isEmpty {
|
||||
performSemanticSearch()
|
||||
}
|
||||
}
|
||||
if !searchText.isEmpty {
|
||||
Button { searchText = "" } label: {
|
||||
Image(systemName: "xmark.circle.fill")
|
||||
@@ -86,6 +100,25 @@ struct ConversationListView: View {
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
}
|
||||
|
||||
if settings.embeddingsEnabled {
|
||||
Divider()
|
||||
.frame(height: 16)
|
||||
Toggle("Semantic", isOn: $useSemanticSearch)
|
||||
.toggleStyle(.switch)
|
||||
.controlSize(.small)
|
||||
.onChange(of: useSemanticSearch) {
|
||||
if useSemanticSearch && !searchText.isEmpty {
|
||||
performSemanticSearch()
|
||||
}
|
||||
}
|
||||
.help("Use AI-powered semantic search instead of keyword matching")
|
||||
}
|
||||
|
||||
if isSearching {
|
||||
ProgressView()
|
||||
.controlSize(.small)
|
||||
}
|
||||
}
|
||||
.padding(10)
|
||||
.background(.quaternary.opacity(0.5), in: RoundedRectangle(cornerRadius: 8))
|
||||
@@ -231,6 +264,52 @@ struct ConversationListView: View {
|
||||
}
|
||||
}
|
||||
|
||||
private func performSemanticSearch() {
|
||||
guard !searchText.isEmpty else {
|
||||
semanticResults = []
|
||||
return
|
||||
}
|
||||
|
||||
isSearching = true
|
||||
|
||||
Task {
|
||||
do {
|
||||
// Use user's selected provider, or fall back to best available
|
||||
guard let provider = EmbeddingService.shared.getSelectedProvider() else {
|
||||
Log.api.warning("No embedding providers available - skipping semantic search")
|
||||
await MainActor.run {
|
||||
isSearching = false
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Generate embedding for search query
|
||||
let embedding = try await EmbeddingService.shared.generateEmbedding(
|
||||
text: searchText,
|
||||
provider: provider
|
||||
)
|
||||
|
||||
// Search conversations
|
||||
let results = try DatabaseService.shared.searchConversationsBySemantic(
|
||||
queryEmbedding: embedding,
|
||||
limit: 20
|
||||
)
|
||||
|
||||
await MainActor.run {
|
||||
semanticResults = results.map { $0.0 }
|
||||
isSearching = false
|
||||
Log.ui.info("Semantic search found \(results.count) results using \(provider.displayName)")
|
||||
}
|
||||
} catch {
|
||||
await MainActor.run {
|
||||
semanticResults = []
|
||||
isSearching = false
|
||||
Log.ui.error("Semantic search failed: \(error)")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func exportConversation(_ conversation: Conversation) {
|
||||
guard let (_, loadedMessages) = try? DatabaseService.shared.loadConversation(id: conversation.id),
|
||||
!loadedMessages.isEmpty else {
|
||||
|
||||
Reference in New Issue
Block a user