Skip to content

Commit 6efe3f1

Browse files
Carly 2.0claude
andcommitted
fix(web-chat): prevent blank chat pane after assistant response
After a turn completes, the UI could briefly or permanently show blank content. Two related issues: 1. When loadChatHistory is triggered by a final event (tool turns or non-renderable final messages), chatLoading was only set inside the async function body — synchronously, but still after the void call site returned. In practice loadChatHistory sets it before the first await, but explicitly pre-setting chatLoading = true at every call site in handleTerminalChatEvent and handleChatGatewayEvent makes the loading state visible to any synchronous re-render that fires between the call and the function body running. 2. When loadChatHistory returned early because the client was disconnected (!state.client || !state.connected), any chatLoading = true that had been pre-set externally was never cleared, leaving the UI stuck in a loading state rather than showing cached messages. Together these close the window where: streaming ends → chatStream = null → chatMessages not yet updated → chatLoading still false → UI renders empty (isEmpty = true) before the history reload finishes. Fixes #67035 Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
1 parent 30e259b commit 6efe3f1

2 files changed

Lines changed: 10 additions & 3 deletions

File tree

ui/src/ui/app-gateway.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -593,7 +593,9 @@ function handleTerminalChatEvent(
593593
// replace the now-cleared streaming state.
594594
if (hadToolEvents && state === "final") {
595595
const completedRunId = runId ?? null;
596-
void loadChatHistory(host as unknown as ChatState).finally(() => {
596+
const chatStateHostForTools = host as unknown as ChatState;
597+
chatStateHostForTools.chatLoading = true;
598+
void loadChatHistory(chatStateHostForTools).finally(() => {
597599
if (completedRunId && host.chatRunId && host.chatRunId !== completedRunId) {
598600
return;
599601
}
@@ -658,11 +660,15 @@ function handleChatGatewayEvent(host: GatewayHost, payload: ChatEventPayload | u
658660
deferredReloadHost.pendingSessionMessageReloadSessionKey = null;
659661
}
660662
if (finalEventNeedsHistoryReload && !historyReloaded && !terminalEventIsForDifferentActiveRun) {
661-
void loadChatHistory(host as unknown as ChatState);
663+
const chatStateHost = host as unknown as ChatState;
664+
chatStateHost.chatLoading = true;
665+
void loadChatHistory(chatStateHost);
662666
return;
663667
}
664668
if (shouldReplayDeferredSessionMessageReload && !historyReloaded) {
665-
void loadChatHistory(host as unknown as ChatState);
669+
const chatStateHost = host as unknown as ChatState;
670+
chatStateHost.chatLoading = true;
671+
void loadChatHistory(chatStateHost);
666672
}
667673
}
668674

ui/src/ui/controllers/chat.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -386,6 +386,7 @@ function maybeResetToolStream(state: ChatState) {
386386

387387
export async function loadChatHistory(state: ChatState) {
388388
if (!state.client || !state.connected) {
389+
state.chatLoading = false;
389390
return;
390391
}
391392
const sessionKey = state.sessionKey;

0 commit comments

Comments
 (0)