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.
This commit is contained in:
Your Name 2026-02-03 16:59:23 +08:00
parent b4c18bd554
commit 0cc791a71e
2 changed files with 39 additions and 2 deletions

View File

@ -72,6 +72,7 @@ export function Chat({
// 🆕 跟踪是否正在生成图片(用于占位图显示)
const [isGeneratingImageFlag, setIsGeneratingImageFlag] = useState(false);
const imagePlaceholderTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
// 🆕 存储通过 data-appendMessage 添加的消息,避免与 useChat 内部状态冲突
const [appendedMessages, setAppendedMessages] = useState<ChatMessage[]>([]);
@ -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*<!-- GENERATING_IMAGE -->\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);
}

View File

@ -472,6 +472,14 @@ function PureMessages({
(part: any) => part.type === "text" && part.text?.includes("<!-- GENERATING_IMAGE -->")
)
);
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("<!-- GENERATING_IMAGE -->")) return false;
const cleanedText = part.text.replace(/\n*<!-- GENERATING_IMAGE -->\n*/g, "").trim();
return cleanedText.length > 0;
})
);
if (showVideo) return <QianwenVideoLoading />;
if (showGemini) return <GeminiImageLoading imageCount={imageCount} />;
@ -482,7 +490,7 @@ function PureMessages({
if (isAutoMode && userMessageHasImage) {
// 🆕 只用 isGeneratingImageFlag 来控制是否显示生图占位图
// isGeneratingImageFlag 由 chat.tsx 控制:延迟 3 秒后设置为 true,收到图片后设置为 false
if (isGeneratingImageFlag) {
if (isGeneratingImageFlag && hasAssistantTextReplyWithContent) {
return <ImageGenerationLoading imageCount={imageCount} maxImages={6} providerType="alibaba-image" modelName="智能处理" />;
}
// 否则显示思考动画(在等待 AI 分析或延迟期间)