From 0cc791a71e4bdfebd0dab55bd228f34990b23f3f Mon Sep 17 00:00:00 2001 From: Your Name Date: Tue, 3 Feb 2026 16:59:23 +0800 Subject: [PATCH] fix: delay image placeholder until text shows Guard the auto image-loading placeholder so it starts only after a visible text reply and cancel pending timers when the image arrives. --- components/chat.tsx | 31 ++++++++++++++++++++++++++++++- components/messages.tsx | 10 +++++++++- 2 files changed, 39 insertions(+), 2 deletions(-) diff --git a/components/chat.tsx b/components/chat.tsx index 9bea097..7d52058 100644 --- a/components/chat.tsx +++ b/components/chat.tsx @@ -72,6 +72,7 @@ export function Chat({ // 🆕 跟踪是否正在生成图片(用于占位图显示) const [isGeneratingImageFlag, setIsGeneratingImageFlag] = useState(false); + const imagePlaceholderTimerRef = useRef | null>(null); // 🆕 存储通过 data-appendMessage 添加的消息,避免与 useChat 内部状态冲突 const [appendedMessages, setAppendedMessages] = useState([]); @@ -100,6 +101,16 @@ export function Chat({ } }, [pendingGeneration, initialMessages, completeGeneration, router]); + // 清理占位图定时器 + useEffect(() => { + return () => { + if (imagePlaceholderTimerRef.current) { + clearTimeout(imagePlaceholderTimerRef.current); + imagePlaceholderTimerRef.current = null; + } + }; + }, []); + const { mutate } = useSWRConfig(); // Handle browser back/forward navigation @@ -303,11 +314,25 @@ export function Chat({ ); if (hasGeneratingMarker) { console.log("[Chat] 🖼️ Detected GENERATING_IMAGE marker, will show placeholder after delay"); + const hasDisplayText = messageData.parts?.some((part: any) => { + if (part.type !== "text" || !part.text) return false; + const cleanedText = part.text.replace(/\n*\n*/g, "").trim(); + return cleanedText.length > 0; + }); + if (!hasDisplayText) { + console.log("[Chat] ⚠️ Marker message has no display text, skip image placeholder timer"); + return; + } // 🆕 延迟显示占位图,让用户先看到 AI 的文字回复 // 消息会立即添加到 appendedMessages 并显示,3 秒后再显示动态占位图 - setTimeout(() => { + if (imagePlaceholderTimerRef.current) { + clearTimeout(imagePlaceholderTimerRef.current); + imagePlaceholderTimerRef.current = null; + } + imagePlaceholderTimerRef.current = setTimeout(() => { console.log("[Chat] 🖼️ Now showing placeholder after 3s delay"); setIsGeneratingImageFlag(true); + imagePlaceholderTimerRef.current = null; }, 3000); // 延迟 3 秒,让用户有时间阅读文字 } @@ -326,6 +351,10 @@ export function Chat({ if (hasImage) { console.log("[Chat] 🎉 Image generated, hiding placeholder"); + if (imagePlaceholderTimerRef.current) { + clearTimeout(imagePlaceholderTimerRef.current); + imagePlaceholderTimerRef.current = null; + } setIsGeneratingImageFlag(false); } diff --git a/components/messages.tsx b/components/messages.tsx index 9db1658..6ecb5c7 100644 --- a/components/messages.tsx +++ b/components/messages.tsx @@ -472,6 +472,14 @@ function PureMessages({ (part: any) => part.type === "text" && part.text?.includes("") ) ); + const hasAssistantTextReplyWithContent = messages.some(msg => + msg.role === "assistant" && msg.parts?.some((part: any) => { + if (part.type !== "text" || !part.text) return false; + if (!part.text.includes("")) return false; + const cleanedText = part.text.replace(/\n*\n*/g, "").trim(); + return cleanedText.length > 0; + }) + ); if (showVideo) return ; if (showGemini) return ; @@ -482,7 +490,7 @@ function PureMessages({ if (isAutoMode && userMessageHasImage) { // 🆕 只用 isGeneratingImageFlag 来控制是否显示生图占位图 // isGeneratingImageFlag 由 chat.tsx 控制:延迟 3 秒后设置为 true,收到图片后设置为 false - if (isGeneratingImageFlag) { + if (isGeneratingImageFlag && hasAssistantTextReplyWithContent) { return ; } // 否则显示思考动画(在等待 AI 分析或延迟期间)