diff --git a/frontend/src/stores/chat.ts b/frontend/src/stores/chat.ts index 7d2e83f..76c1f23 100644 --- a/frontend/src/stores/chat.ts +++ b/frontend/src/stores/chat.ts @@ -109,6 +109,28 @@ export const useChatStore = defineStore("chat", () => { } } + // Drain the next queued message for a conversation, if conditions are met. + // Called both at stream-end and after fetchConversation, so orphaned queue + // messages (e.g. from a navigation away mid-stream) are picked up on return. + function _tryDrainQueue(convId: number) { + const queue = convQueues.value[convId]; + if (!queue?.length) return; + if (isStreamingConv(convId)) return; // stream-end will drain naturally + if (currentConversation.value?.id !== convId) return; // not our conversation + const next = queue.shift()!; + _saveQueue(convId); + setTimeout(() => sendMessage( + next.content, + next.contextNoteId, + next.includeNoteIds, + next.think, + next.contextNoteTitle, + next.excludeNoteIds, + next.ragProjectId, + next.workspaceProjectId, + ), 0); + } + function clearQueue() { const id = currentConversation.value?.id; if (id) { @@ -158,6 +180,9 @@ export const useChatStore = defineStore("chat", () => { `/api/chat/conversations/${id}` ); _loadQueue(id); + // Drain any messages that were queued but never sent because the user + // navigated away before the previous stream finished. + _tryDrainQueue(id); } catch (e) { useToastStore().show("Failed to load conversation", "error"); throw e; @@ -431,23 +456,9 @@ export const useChatStore = defineStore("chat", () => { s.pendingTool = null; } - // Process next queued message, if any. - // Use setTimeout so this frame resolves before the next send begins. - const queue = convQueues.value[convId]; - if (queue?.length && currentConversation.value?.id === convId) { - const next = queue.shift()!; - _saveQueue(convId); - setTimeout(() => sendMessage( - next.content, - next.contextNoteId, - next.includeNoteIds, - next.think, - next.contextNoteTitle, - next.excludeNoteIds, - next.ragProjectId, - next.workspaceProjectId, - ), 0); - } + // Process next queued message if this is still the active conversation. + // If the user has navigated away, _tryDrainQueue will fire on fetchConversation. + _tryDrainQueue(convId); } async function reconnectIfGenerating(convId: number): Promise {