From c6180e86105566c2aea47136defdb8505982182e Mon Sep 17 00:00:00 2001 From: Your Name Date: Sun, 1 Feb 2026 02:49:52 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E6=99=BA=E8=83=BD?= =?UTF-8?q?=E8=87=AA=E5=8A=A8=E6=A8=A1=E5=BC=8F=E5=8D=A0=E4=BD=8D=E5=9B=BE?= =?UTF-8?q?=E6=8F=90=E5=89=8D=E6=B6=88=E5=A4=B1=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 问题: - appendMessage 放入队列延迟处理导致 GENERATING_IMAGE 标记无法被及时检测 - status 变成 ready 时占位图消失,但图片还在生成中 解决方案: - 添加 isGeneratingImageFlag 状态来跟踪图片生成进度 - 在收到包含 GENERATING_IMAGE 标记的消息时立即设置标志 - 在收到包含图片的消息时清除标志 - 将标志传递给 Messages 组件用于占位图显示判断 Co-authored-by: Cursor --- components/chat.tsx | 98 +++++++++++++++++++++++++++-------------- components/messages.tsx | 11 +++-- 2 files changed, 72 insertions(+), 37 deletions(-) diff --git a/components/chat.tsx b/components/chat.tsx index 429c2a4..5ecf348 100644 --- a/components/chat.tsx +++ b/components/chat.tsx @@ -69,6 +69,12 @@ export function Chat({ // 🆕 标记是否已经尝试刷新过(避免无限刷新) const hasTriedRefreshRef = useRef(false); + // 🆕 存储待处理的 appendMessage(避免与 useChat 内部状态冲突) + const pendingAppendMessagesRef = useRef([]); + + // 🆕 跟踪是否正在生成图片(用于占位图显示) + const [isGeneratingImageFlag, setIsGeneratingImageFlag] = useState(false); + // 🆕 当检测到恢复的 pendingGeneration 时,刷新页面获取最新消息 useEffect(() => { if (pendingGeneration && !hasTriedRefreshRef.current) { @@ -280,48 +286,44 @@ export function Chat({ setUsage(dataPart.data); } - // 处理后端返回的自定义消息(用于图片/视频生成的非流式响应) + // 🆕 处理后端返回的自定义消息(用于图片/视频生成的非流式响应) + // 注意:不要在这里调用 setMessages,因为会与 useChat 内部状态冲突 + // 改为存储到待处理队列,在 onFinish 时统一处理 if (dataPart.type === "data-appendMessage") { try { - // dataPart.data 是 JSON 字符串 const messageData = typeof dataPart.data === 'string' ? JSON.parse(dataPart.data) : dataPart.data; - console.log("[Chat] Received appendMessage:", messageData); + console.log("[Chat] Received appendMessage (queued):", messageData); + + // 🆕 检测是否包含 GENERATING_IMAGE 标记 + const hasGeneratingMarker = messageData.parts?.some( + (part: any) => part.type === "text" && part.text?.includes("") + ); + if (hasGeneratingMarker) { + console.log("[Chat] 🖼️ Detected GENERATING_IMAGE marker, showing placeholder"); + setIsGeneratingImageFlag(true); + } + + // 🆕 检测是否包含图片(生成完成) + const hasImage = messageData.parts?.some( + (part: any) => part.type === "text" && part.text && ( + part.text.includes("![thumb|") || + part.text.includes("![thumb|||") || + part.text.includes("![Generated Image") + ) + ); + if (hasImage) { + console.log("[Chat] 🎉 Image generated, hiding placeholder"); + setIsGeneratingImageFlag(false); + } + + // 存储到待处理队列 + pendingAppendMessagesRef.current.push(messageData); // 🆕 收到消息,清除持久化状态 completeGeneration(); - - setMessages((prevMessages) => { - console.log("[Chat] 📋 setMessages 调用前:", prevMessages.map(m => ({ - id: m.id?.substring(0, 8), - role: m.role - }))); - - if (prevMessages.some(m => m.id === messageData.id)) { - console.log("[Chat] ⚠️ 消息已存在,跳过"); - return prevMessages; - } - - // 构造 ChatMessage 对象 - // 注意:这里需要确保符合 ChatMessage 类型定义 - const newMessage: any = { - id: messageData.id, - role: messageData.role, - content: messageData.parts?.[0]?.text || "", - createdAt: new Date(), - parts: messageData.parts || [], - attachments: [], - }; - - const newMessages = [...prevMessages, newMessage]; - console.log("[Chat] 📋 setMessages 调用后:", newMessages.map(m => ({ - id: m.id?.substring(0, 8), - role: m.role - }))); - return newMessages; - }); } catch (e) { console.error("[Chat] Failed to parse data-appendMessage:", e); } @@ -331,6 +333,35 @@ export function Chat({ mutate(unstable_serialize(getChatHistoryPaginationKey)); // 🆕 生成完成,清除持久化状态 completeGeneration(); + + // 🆕 处理待处理的 appendMessage(在流结束后安全地添加) + if (pendingAppendMessagesRef.current.length > 0) { + console.log("[Chat] Processing pending appendMessages:", pendingAppendMessagesRef.current.length); + const pendingMessages = [...pendingAppendMessagesRef.current]; + pendingAppendMessagesRef.current = []; + + // 使用 setTimeout 延迟处理,确保 useChat 内部状态已稳定 + setTimeout(() => { + setMessages((prevMessages) => { + let newMessages = [...prevMessages]; + for (const messageData of pendingMessages) { + if (!newMessages.some(m => m.id === messageData.id)) { + const newMessage: any = { + id: messageData.id, + role: messageData.role, + content: messageData.parts?.[0]?.text || "", + createdAt: new Date(), + parts: messageData.parts || [], + attachments: [], + }; + newMessages = [...newMessages, newMessage]; + } + } + console.log("[Chat] After processing pending messages:", newMessages.length); + return newMessages; + }); + }, 100); + } }, onError: (error) => { if (error instanceof ChatSDKError) { @@ -1066,6 +1097,7 @@ export function Chat({ isProcessingAngleEdit={isProcessingAngleEdit} pendingGeneration={pendingGeneration} imageCount={imageParams?.imageCount || 1} + isGeneratingImageFlag={isGeneratingImageFlag} />
diff --git a/components/messages.tsx b/components/messages.tsx index f651863..12bdd61 100644 --- a/components/messages.tsx +++ b/components/messages.tsx @@ -48,6 +48,7 @@ type MessagesProps = { isProcessingAngleEdit?: boolean; pendingGeneration?: PendingGeneration | null; // 🆕 持久化的生成状态 imageCount?: number; // 🆕 用户选择的图片数量 + isGeneratingImageFlag?: boolean; // 🆕 正在生成图片的标志(来自 Chat 组件) }; function PureMessages({ @@ -72,6 +73,7 @@ function PureMessages({ isProcessingAngleEdit = false, pendingGeneration, imageCount = 1, + isGeneratingImageFlag = false, }: MessagesProps) { const { containerRef: messagesContainerRef, @@ -226,8 +228,9 @@ function PureMessages({ setShowThinkingWithDelay(false); }, delayTime); return () => clearTimeout(timer); - } else if (isGeneratingImage) { + } else if (isGeneratingImage || isGeneratingImageFlag) { // 收到 GENERATING_IMAGE 标记(对话消息已发送),继续显示占位图等待图片 + // 使用 isGeneratingImageFlag 来解决消息延迟添加时标记检测不到的问题 setShowThinkingWithDelay(true); } else if (status === "streaming" || status === "submitted") { // 正在处理中(分析意图或流式传输),显示占位图 @@ -251,7 +254,7 @@ function PureMessages({ setShowThinkingWithDelay(false); } } - }, [status, messages, isGeminiModel, isQianwenVideoModel, isVolcanoVisionModel, isVideoModel, isAlibabaImageModel, selectedModelId]); + }, [status, messages, isGeminiModel, isQianwenVideoModel, isVolcanoVisionModel, isVideoModel, isAlibabaImageModel, selectedModelId, isGeneratingImageFlag]); // 🆕 监听新消息添加,当用户发送带图片的消息或添加加载消息时,强制滚动到底部 const prevMessagesLengthRef = useRef(messages.length); @@ -411,8 +414,8 @@ function PureMessages({ // 🆕 智能自动模式占位图 if (isAutoMode && userMessageHasImage) { - // 如果已经收到了 GENERATING_IMAGE 标记,显示生图占位图 - if (hasGeneratingMarker) { + // 如果已经收到了 GENERATING_IMAGE 标记 或 isGeneratingImageFlag 为 true,显示生图占位图 + if (hasGeneratingMarker || isGeneratingImageFlag) { return ; } // 否则显示思考动画