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:
Garry Tan
2026-04-04 07:19:52 -07:00
parent 40dcb34192
commit e48f6b28ff
+24 -10
View File
@@ -381,10 +381,13 @@ async function pollChat() {
}
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) {
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
@@ -409,8 +412,9 @@ async function pollChat() {
}
// Clean up orphaned thinking indicators after replay.
// Only show "(session ended)" if there's actually a thinking spinner
// to clean up (not on every idle poll, which would spam the chat).
// Only remove if we're on the CORRECT tab and the agent is truly idle.
// 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');
if (thinking && data.agentStatus !== 'processing') {
thinking.remove();
@@ -437,10 +441,21 @@ function switchChatTab(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]) {
chatMessages.innerHTML = chatDomByTab[newTabId];
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 {
chatMessages.innerHTML = `
<div class="chat-welcome" id="chat-welcome">
@@ -449,13 +464,12 @@ function switchChatTab(newTabId) {
<p class="muted">Each tab has its own conversation.</p>
</div>`;
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
pollChat();
}