mirror of
https://github.com/garrytan/gstack.git
synced 2026-05-20 19:29:56 +08:00
fix: preserve optimistic UI during tab switch on first message
When the user sends a message and the server assigns it to a new tab (because Chrome's active tab changed), switchChatTab() was blowing away the optimistic user bubble and thinking dots with a welcome screen. Now preserves the current DOM if we're mid-send with a thinking indicator. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -381,10 +381,13 @@ async function pollChat() {
|
|||||||
}
|
}
|
||||||
const data = await resp.json();
|
const data = await resp.json();
|
||||||
|
|
||||||
// Detect tab switch from server — swap chat context
|
// Detect tab switch from server — swap chat context.
|
||||||
|
// IMPORTANT: return before cleaning up thinking dots — the agent may be
|
||||||
|
// processing on the NEW tab while the OLD tab is idle. Removing the
|
||||||
|
// thinking indicator here would kill the optimistic UI before the switch.
|
||||||
if (data.activeTabId !== undefined && data.activeTabId !== sidebarActiveTabId) {
|
if (data.activeTabId !== undefined && data.activeTabId !== sidebarActiveTabId) {
|
||||||
switchChatTab(data.activeTabId);
|
switchChatTab(data.activeTabId);
|
||||||
return; // switchChatTab triggers a fresh poll
|
return; // switchChatTab triggers a fresh poll on the correct tab
|
||||||
}
|
}
|
||||||
|
|
||||||
// First successful poll — hide loading spinner
|
// First successful poll — hide loading spinner
|
||||||
@@ -409,8 +412,9 @@ async function pollChat() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Clean up orphaned thinking indicators after replay.
|
// Clean up orphaned thinking indicators after replay.
|
||||||
// Only show "(session ended)" if there's actually a thinking spinner
|
// Only remove if we're on the CORRECT tab and the agent is truly idle.
|
||||||
// to clean up (not on every idle poll, which would spam the chat).
|
// Don't clean up during tab switches — the agent may be processing on
|
||||||
|
// the new tab while the old tab shows idle.
|
||||||
const thinking = document.getElementById('agent-thinking');
|
const thinking = document.getElementById('agent-thinking');
|
||||||
if (thinking && data.agentStatus !== 'processing') {
|
if (thinking && data.agentStatus !== 'processing') {
|
||||||
thinking.remove();
|
thinking.remove();
|
||||||
@@ -437,10 +441,21 @@ function switchChatTab(newTabId) {
|
|||||||
|
|
||||||
sidebarActiveTabId = newTabId;
|
sidebarActiveTabId = newTabId;
|
||||||
|
|
||||||
// Restore saved chat for new tab, or show welcome
|
// Restore saved chat for new tab, or carry over current DOM if we're
|
||||||
|
// mid-message (the server may have switched tabs because the user's
|
||||||
|
// Chrome tab changed, but we still want to show the optimistic UI).
|
||||||
if (chatDomByTab[newTabId]) {
|
if (chatDomByTab[newTabId]) {
|
||||||
chatMessages.innerHTML = chatDomByTab[newTabId];
|
chatMessages.innerHTML = chatDomByTab[newTabId];
|
||||||
chatLineCount = chatLineCountByTab[newTabId] || 0;
|
chatLineCount = chatLineCountByTab[newTabId] || 0;
|
||||||
|
// Reset agent state for restored tab
|
||||||
|
agentContainer = null;
|
||||||
|
agentTextEl = null;
|
||||||
|
agentText = '';
|
||||||
|
} else if (lastOptimisticMsg && document.getElementById('agent-thinking')) {
|
||||||
|
// We're mid-send with optimistic UI — keep it, don't blow it away.
|
||||||
|
// The poll for the new tab will pick up the entries and sync naturally.
|
||||||
|
chatLineCount = 0;
|
||||||
|
// agentContainer/agentTextEl are already set from sendMessage()
|
||||||
} else {
|
} else {
|
||||||
chatMessages.innerHTML = `
|
chatMessages.innerHTML = `
|
||||||
<div class="chat-welcome" id="chat-welcome">
|
<div class="chat-welcome" id="chat-welcome">
|
||||||
@@ -449,13 +464,12 @@ function switchChatTab(newTabId) {
|
|||||||
<p class="muted">Each tab has its own conversation.</p>
|
<p class="muted">Each tab has its own conversation.</p>
|
||||||
</div>`;
|
</div>`;
|
||||||
chatLineCount = 0;
|
chatLineCount = 0;
|
||||||
|
// Reset agent state for fresh tab
|
||||||
|
agentContainer = null;
|
||||||
|
agentTextEl = null;
|
||||||
|
agentText = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset agent state for this tab
|
|
||||||
agentContainer = null;
|
|
||||||
agentTextEl = null;
|
|
||||||
agentText = '';
|
|
||||||
|
|
||||||
// Immediately poll the new tab's chat
|
// Immediately poll the new tab's chat
|
||||||
pollChat();
|
pollChat();
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user