introduces workspace items as combined search+chat model
This commit is contained in:
@@ -95,6 +95,7 @@ final class SybilViewModel {
|
||||
|
||||
var chats: [ChatSummary] = []
|
||||
var searches: [SearchSummary] = []
|
||||
var workspaceItems: [WorkspaceItem] = []
|
||||
|
||||
var selectedItem: SidebarSelection?
|
||||
var selectedChat: ChatDetail?
|
||||
@@ -388,40 +389,40 @@ final class SybilViewModel {
|
||||
}
|
||||
|
||||
var sidebarItems: [SidebarItem] {
|
||||
let chatItems: [SidebarItem] = chats.map { chat in
|
||||
let initiatedLabel: String?
|
||||
if let model = chat.initiatedModel?.trimmingCharacters(in: .whitespacesAndNewlines), !model.isEmpty {
|
||||
if let provider = chat.initiatedProvider {
|
||||
initiatedLabel = "\(provider.displayName) • \(model)"
|
||||
workspaceItems.map { item in
|
||||
switch item.type {
|
||||
case .chat:
|
||||
let initiatedLabel: String?
|
||||
if let model = item.initiatedModel?.trimmingCharacters(in: .whitespacesAndNewlines), !model.isEmpty {
|
||||
if let provider = item.initiatedProvider {
|
||||
initiatedLabel = "\(provider.displayName) • \(model)"
|
||||
} else {
|
||||
initiatedLabel = model
|
||||
}
|
||||
} else {
|
||||
initiatedLabel = model
|
||||
initiatedLabel = nil
|
||||
}
|
||||
} else {
|
||||
initiatedLabel = nil
|
||||
|
||||
return SidebarItem(
|
||||
selection: .chat(item.id),
|
||||
kind: .chat,
|
||||
title: chatTitle(title: item.title, messages: nil),
|
||||
updatedAt: item.updatedAt,
|
||||
initiatedLabel: initiatedLabel,
|
||||
isRunning: isChatRowRunning(item.id)
|
||||
)
|
||||
|
||||
case .search:
|
||||
return SidebarItem(
|
||||
selection: .search(item.id),
|
||||
kind: .search,
|
||||
title: searchTitle(title: item.title, query: item.query),
|
||||
updatedAt: item.updatedAt,
|
||||
initiatedLabel: "exa",
|
||||
isRunning: isSearchRowRunning(item.id)
|
||||
)
|
||||
}
|
||||
|
||||
return SidebarItem(
|
||||
selection: .chat(chat.id),
|
||||
kind: .chat,
|
||||
title: chatTitle(title: chat.title, messages: nil),
|
||||
updatedAt: chat.updatedAt,
|
||||
initiatedLabel: initiatedLabel,
|
||||
isRunning: isChatRowRunning(chat.id)
|
||||
)
|
||||
}
|
||||
|
||||
let searchItems: [SidebarItem] = searches.map { search in
|
||||
SidebarItem(
|
||||
selection: .search(search.id),
|
||||
kind: .search,
|
||||
title: searchTitle(title: search.title, query: search.query),
|
||||
updatedAt: search.updatedAt,
|
||||
initiatedLabel: "exa",
|
||||
isRunning: isSearchRowRunning(search.id)
|
||||
)
|
||||
}
|
||||
|
||||
return (chatItems + searchItems).sorted { $0.updatedAt > $1.updatedAt }
|
||||
}
|
||||
|
||||
var selectedChatSummary: ChatSummary? {
|
||||
@@ -502,6 +503,7 @@ final class SybilViewModel {
|
||||
authMode = nil
|
||||
chats = []
|
||||
searches = []
|
||||
workspaceItems = []
|
||||
selectedItem = .settings
|
||||
selectedChat = nil
|
||||
selectedSearch = nil
|
||||
@@ -671,6 +673,7 @@ final class SybilViewModel {
|
||||
setProvider(submittedProvider, model: submittedModel)
|
||||
chats.removeAll(where: { $0.id == chat.id })
|
||||
chats.insert(chat, at: 0)
|
||||
upsertWorkspaceChat(chat)
|
||||
draftKind = nil
|
||||
selectedItem = .chat(chat.id)
|
||||
selectedChat = ChatDetail(
|
||||
@@ -1034,6 +1037,7 @@ final class SybilViewModel {
|
||||
guard selectedItem == sourceSelection, draftKind == nil else {
|
||||
chats.removeAll(where: { $0.id == chat.id })
|
||||
chats.insert(chat, at: 0)
|
||||
upsertWorkspaceChat(chat)
|
||||
isCreatingSearchChat = false
|
||||
return
|
||||
}
|
||||
@@ -1045,6 +1049,7 @@ final class SybilViewModel {
|
||||
|
||||
chats.removeAll(where: { $0.id == chat.id })
|
||||
chats.insert(chat, at: 0)
|
||||
upsertWorkspaceChat(chat)
|
||||
|
||||
selectedItem = .chat(chat.id)
|
||||
selectedSearch = nil
|
||||
@@ -1148,18 +1153,16 @@ final class SybilViewModel {
|
||||
errorMessage = nil
|
||||
|
||||
do {
|
||||
async let chatsValue = client.listChats()
|
||||
async let searchesValue = client.listSearches()
|
||||
async let workspaceItemsValue = client.listWorkspaceItems()
|
||||
async let activeRunsValue = client.getActiveRuns()
|
||||
let (nextChats, nextSearches, nextActiveRuns) = try await (chatsValue, searchesValue, activeRunsValue)
|
||||
let (nextWorkspaceItems, nextActiveRuns) = try await (workspaceItemsValue, activeRunsValue)
|
||||
|
||||
chats = nextChats
|
||||
searches = nextSearches
|
||||
applyWorkspaceItems(nextWorkspaceItems)
|
||||
applyActiveRuns(nextActiveRuns)
|
||||
|
||||
SybilLog.info(
|
||||
SybilLog.app,
|
||||
"Loaded collections: \(nextChats.count) chats, \(nextSearches.count) searches"
|
||||
"Loaded collections: \(chats.count) chats, \(searches.count) searches"
|
||||
)
|
||||
|
||||
do {
|
||||
@@ -1176,7 +1179,7 @@ final class SybilViewModel {
|
||||
if case .settings = selectedItem {
|
||||
nextSelection = .settings
|
||||
} else if let currentSelection = selectedItem,
|
||||
hasSelection(currentSelection, chats: nextChats, searches: nextSearches) {
|
||||
hasSelection(currentSelection, chats: chats, searches: searches) {
|
||||
nextSelection = currentSelection
|
||||
} else {
|
||||
nextSelection = sidebarItems.first?.selection
|
||||
@@ -1248,18 +1251,16 @@ final class SybilViewModel {
|
||||
|
||||
do {
|
||||
let client = try client()
|
||||
async let chatsValue = client.listChats()
|
||||
async let searchesValue = client.listSearches()
|
||||
async let workspaceItemsValue = client.listWorkspaceItems()
|
||||
async let activeRunsValue = client.getActiveRuns()
|
||||
let (nextChats, nextSearches, nextActiveRuns) = try await (chatsValue, searchesValue, activeRunsValue)
|
||||
let (nextWorkspaceItems, nextActiveRuns) = try await (workspaceItemsValue, activeRunsValue)
|
||||
|
||||
chats = nextChats
|
||||
searches = nextSearches
|
||||
applyWorkspaceItems(nextWorkspaceItems)
|
||||
applyActiveRuns(nextActiveRuns)
|
||||
|
||||
SybilLog.info(
|
||||
SybilLog.app,
|
||||
"Refreshed collections: \(nextChats.count) chats, \(nextSearches.count) searches"
|
||||
"Refreshed collections: \(chats.count) chats, \(searches.count) searches"
|
||||
)
|
||||
errorMessage = nil
|
||||
|
||||
@@ -1277,10 +1278,10 @@ final class SybilViewModel {
|
||||
}
|
||||
|
||||
if let preferredSelection,
|
||||
hasSelection(preferredSelection, chats: nextChats, searches: nextSearches) {
|
||||
hasSelection(preferredSelection, chats: chats, searches: searches) {
|
||||
selectedItem = preferredSelection
|
||||
} else if let existing = selectedItem,
|
||||
hasSelection(existing, chats: nextChats, searches: nextSearches) {
|
||||
hasSelection(existing, chats: chats, searches: searches) {
|
||||
selectedItem = existing
|
||||
} else {
|
||||
selectedItem = sidebarItems.first?.selection
|
||||
@@ -1374,6 +1375,34 @@ final class SybilViewModel {
|
||||
serverActiveSearchIDs = Set(activeRuns.searches)
|
||||
}
|
||||
|
||||
private func applyWorkspaceItems(_ items: [WorkspaceItem]) {
|
||||
workspaceItems = items
|
||||
chats = items.compactMap(\.chatSummary)
|
||||
searches = items.compactMap(\.searchSummary)
|
||||
}
|
||||
|
||||
private func upsertWorkspaceChat(_ chat: ChatSummary, moveToFront: Bool = true) {
|
||||
upsertWorkspaceItem(WorkspaceItem(chat: chat), moveToFront: moveToFront)
|
||||
}
|
||||
|
||||
private func upsertWorkspaceSearch(_ search: SearchSummary, moveToFront: Bool = true) {
|
||||
upsertWorkspaceItem(WorkspaceItem(search: search), moveToFront: moveToFront)
|
||||
}
|
||||
|
||||
private func upsertWorkspaceItem(_ item: WorkspaceItem, moveToFront: Bool) {
|
||||
if let existingIndex = workspaceItems.firstIndex(where: { $0.type == item.type && $0.id == item.id }) {
|
||||
workspaceItems.remove(at: existingIndex)
|
||||
if moveToFront {
|
||||
workspaceItems.insert(item, at: 0)
|
||||
} else {
|
||||
workspaceItems.insert(item, at: existingIndex)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
workspaceItems.insert(item, at: 0)
|
||||
}
|
||||
|
||||
private func attachToVisibleActiveRunIfNeeded() {
|
||||
guard draftKind == nil else {
|
||||
return
|
||||
@@ -1705,6 +1734,7 @@ final class SybilViewModel {
|
||||
|
||||
chats.removeAll(where: { $0.id == created.id })
|
||||
chats.insert(created, at: 0)
|
||||
upsertWorkspaceChat(created)
|
||||
|
||||
if shouldShowCreatedChat {
|
||||
draftKind = nil
|
||||
@@ -1781,6 +1811,7 @@ final class SybilViewModel {
|
||||
}
|
||||
return existing
|
||||
}
|
||||
self.upsertWorkspaceChat(updated, moveToFront: false)
|
||||
|
||||
if self.selectedChat?.id == updated.id {
|
||||
self.selectedChat?.title = updated.title
|
||||
@@ -1918,6 +1949,7 @@ final class SybilViewModel {
|
||||
|
||||
searches.removeAll(where: { $0.id == created.id })
|
||||
searches.insert(created, at: 0)
|
||||
upsertWorkspaceSearch(created)
|
||||
|
||||
if shouldShowCreatedSearch {
|
||||
draftKind = nil
|
||||
|
||||
Reference in New Issue
Block a user