From 059a2e06d5c6352e7b211c23936406f02828f3b5 Mon Sep 17 00:00:00 2001 From: Bryan Van Deusen Date: Mon, 9 Mar 2026 22:05:49 -0400 Subject: [PATCH] Queue concurrent prompts instead of dropping or conflicting MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Chat store: when sendMessage() is called while streaming, push to a per-conversation queue and return. When the stream ends the next queued message fires automatically via setTimeout(0) to keep the call stack clean. clearQueue() and queuedCount exposed for UI consumption. Queue is cleaned up on conversation delete. ChatView/WorkspaceView: remove the streaming guard from the local sendMessage() functions and the :disabled on the textarea so users can type and submit freely while streaming. Input placeholder changes to "Type to queue next message…" during streaming. A small "⏳ N queued ×" chip appears below the streaming bubble showing queue depth with a cancel button. DashboardChatInput: disable input, attach button, and send button during streaming. The dashboard creates a fresh conversation per message so in-conversation queuing doesn't apply — locking the input is the correct UX here. Placeholder updates to "Generating response…" during streaming. Co-Authored-By: Claude Sonnet 4.6 --- .../src/components/DashboardChatInput.vue | 12 ++-- frontend/src/stores/chat.ts | 58 ++++++++++++++++++- frontend/src/views/ChatView.vue | 40 ++++++++++++- frontend/src/views/WorkspaceView.vue | 33 ++++++++++- 4 files changed, 130 insertions(+), 13 deletions(-) diff --git a/frontend/src/components/DashboardChatInput.vue b/frontend/src/components/DashboardChatInput.vue index 4780f15..cf5fea9 100644 --- a/frontend/src/components/DashboardChatInput.vue +++ b/frontend/src/components/DashboardChatInput.vue @@ -126,7 +126,7 @@ defineExpose({ focus }); + +

{ @keydown="onInputKeydown" @input="autoResize" :placeholder="inputPlaceholder" - :disabled="store.streaming || !store.chatReady" + :disabled="!store.chatReady" rows="1" > + +

{ ref="inputEl" v-model="messageInput" class="chat-input" - placeholder="Message the agent... (Enter to send)" + :placeholder="chatStore.streaming ? 'Type to queue next message… (Enter to queue)' : 'Message the agent… (Enter to send)'" rows="1" - :disabled="chatStore.streaming" @keydown="onInputKeydown" @input="autoResize" > @@ -625,4 +631,25 @@ details[open] .thinking-summary::before { max-height: 300px; overflow-y: auto; } +.queued-indicator { + display: flex; + align-items: center; + gap: 0.4rem; + font-size: 0.8rem; + color: var(--color-text-muted); + padding: 0.3rem 0.75rem; + margin: 0.25rem 0; +} +.queued-cancel { + background: none; + border: none; + cursor: pointer; + color: var(--color-text-muted); + font-size: 1rem; + line-height: 1; + padding: 0 0.2rem; +} +.queued-cancel:hover { + color: var(--color-text); +}