From 3232739a326a075a226e32a8583cb7737faccd2d Mon Sep 17 00:00:00 2001 From: gongzhiyong Date: Wed, 8 Apr 2026 16:45:46 +0800 Subject: [PATCH] Fix duplicate gem icon: defer assistant message creation until first token Instead of pre-creating an empty placeholder assistant message when streaming starts (which races with GeminiTypingIndicator rendering), wait until the first SSE token arrives to create the message. This guarantees only one row is ever visible: either the typing indicator (before tokens) or the GeminiMessage (after first token). Co-Authored-By: Claude Sonnet 4.6 (1M context) --- frontend/components/gemini/GeminiChat.tsx | 32 ++++++++++++----------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/frontend/components/gemini/GeminiChat.tsx b/frontend/components/gemini/GeminiChat.tsx index efb1737..e0627d6 100644 --- a/frontend/components/gemini/GeminiChat.tsx +++ b/frontend/components/gemini/GeminiChat.tsx @@ -193,23 +193,9 @@ export function GeminiChat() { ); } - // Create a placeholder AI message for streaming const aiMsgId = `msg-${Date.now()}-ai`; - const aiMsg: Message = { - id: aiMsgId, - role: "assistant", - content: "", - timestamp: new Date(), - }; - const streamConvId = convId; - setConversations((prev) => - prev.map((c) => - c.id === streamConvId ? { ...c, messages: [...c.messages, aiMsg] } : c - ) - ); - abortRef.current = streamChat( text, streamConvId, @@ -220,6 +206,23 @@ export function GeminiChat() { setConversations((prev) => prev.map((c) => { if (c.id !== streamConvId) return c; + const exists = c.messages.some((m) => m.id === aiMsgId); + if (!exists) { + // First token: create the assistant message + return { + ...c, + messages: [ + ...c.messages, + { + id: aiMsgId, + role: "assistant" as const, + content: event.content!, + timestamp: new Date(), + }, + ], + }; + } + // Subsequent tokens: append to existing message return { ...c, messages: c.messages.map((m) => @@ -231,7 +234,6 @@ export function GeminiChat() { }) ); } - // tool_start / tool_end events could be handled here for UI indicators }, () => { // on error