diff --git a/web-app/src/hooks/useChat.ts b/web-app/src/hooks/useChat.ts index 7237f1b60..36c5119e6 100644 --- a/web-app/src/hooks/useChat.ts +++ b/web-app/src/hooks/useChat.ts @@ -49,7 +49,7 @@ export const useChat = () => { const { getProviderByName, selectedModel, selectedProvider } = useModelProvider() - const { getCurrentThread: retrieveThread, createThread } = useThreads() + const { getCurrentThread: retrieveThread, createThread, updateThreadTimestamp } = useThreads() const { getMessages, addMessage } = useMessages() const router = useRouter() @@ -65,7 +65,7 @@ export const useChat = () => { } setTools() - let unsubscribe = () => {} + let unsubscribe = () => { } listen(SystemEvent.MCP_UPDATE, setTools).then((unsub) => { // Unsubscribe from the event when the component unmounts unsubscribe = unsub @@ -111,6 +111,7 @@ export const useChat = () => { setAbortController(activeThread.id, abortController) updateStreamingContent(emptyThreadContent) addMessage(newUserThreadContent(activeThread.id, message)) + updateThreadTimestamp(activeThread.id) setPrompt('') try { if (selectedModel?.id) { @@ -133,11 +134,11 @@ export const useChat = () => { // Filter tools based on model capabilities and available tools for this thread let availableTools = selectedModel?.capabilities?.includes('tools') ? tools.filter((tool) => { - const availableToolNames = getAvailableToolsForThread( - activeThread.id - ) - return availableToolNames.includes(tool.name) - }) + const availableToolNames = getAvailableToolsForThread( + activeThread.id + ) + return availableToolNames.includes(tool.name) + }) : [] // TODO: Later replaced by Agent setup? @@ -213,6 +214,7 @@ export const useChat = () => { allowAllMCPPermissions ) addMessage(updatedMessage ?? finalContent) + updateThreadTimestamp(activeThread.id) isCompleted = !toolCalls.length // Do not create agent loop if there is no need for it @@ -236,6 +238,7 @@ export const useChat = () => { setAbortController, updateStreamingContent, addMessage, + updateThreadTimestamp, setPrompt, selectedModel, currentAssistant, diff --git a/web-app/src/hooks/useThreads.ts b/web-app/src/hooks/useThreads.ts index eac147ebe..5118974c6 100644 --- a/web-app/src/hooks/useThreads.ts +++ b/web-app/src/hooks/useThreads.ts @@ -26,6 +26,7 @@ type ThreadState = { updateCurrentThreadModel: (model: ThreadModel) => void getFilteredThreads: (searchTerm: string) => Thread[] updateCurrentThreadAssistant: (assistant: Assistant) => void + updateThreadTimestamp: (threadId: string) => void searchIndex: Fzf | null } @@ -164,7 +165,7 @@ export const useThreads = create()( id: ulid(), title: title ?? 'New Thread', model, - order: 1, // Will be set properly by setThreads + // order: 1, // Will be set properly by setThreads updated: Date.now() / 1000, assistants: assistant ? [assistant] : [], } @@ -244,6 +245,62 @@ export const useThreads = create()( const { currentThreadId, threads } = get() return currentThreadId ? threads[currentThreadId] : undefined }, + updateThreadTimestamp: (threadId) => { + set((state) => { + const thread = state.threads[threadId] + if (!thread) return state + + // If the thread is already at order 1, just update the timestamp + if (thread.order === 1) { + const updatedThread = { + ...thread, + updated: Date.now() / 1000, + } + updateThread(updatedThread) + + return { + threads: { + ...state.threads, + [threadId]: updatedThread, + }, + } + } + + // Update the thread with new timestamp and set it to order 1 (top) + const updatedThread = { + ...thread, + updated: Date.now() / 1000, + order: 1, + } + + // Update all other threads to increment their order by 1 + const updatedThreads = { ...state.threads } + Object.keys(updatedThreads).forEach(id => { + if (id !== threadId) { + const otherThread = updatedThreads[id] + updatedThreads[id] = { + ...otherThread, + order: (otherThread.order || 1) + 1, + } + // Update the backend for other threads + updateThread(updatedThreads[id]) + } + }) + + // Set the updated thread + updatedThreads[threadId] = updatedThread + + // Update the backend for the main thread + updateThread(updatedThread) + + return { + threads: updatedThreads, + searchIndex: new Fzf(Object.values(updatedThreads), { + selector: (item: Thread) => item.title, + }), + } + }) + }, }), { name: localStorageKey.threads,