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:
parent
b4c18bd554
commit
0cc791a71e
@ -72,6 +72,7 @@ export function Chat({
|
|||||||
|
|
||||||
// 🆕 跟踪是否正在生成图片(用于占位图显示)
|
// 🆕 跟踪是否正在生成图片(用于占位图显示)
|
||||||
const [isGeneratingImageFlag, setIsGeneratingImageFlag] = useState(false);
|
const [isGeneratingImageFlag, setIsGeneratingImageFlag] = useState(false);
|
||||||
|
const imagePlaceholderTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
||||||
|
|
||||||
// 🆕 存储通过 data-appendMessage 添加的消息,避免与 useChat 内部状态冲突
|
// 🆕 存储通过 data-appendMessage 添加的消息,避免与 useChat 内部状态冲突
|
||||||
const [appendedMessages, setAppendedMessages] = useState<ChatMessage[]>([]);
|
const [appendedMessages, setAppendedMessages] = useState<ChatMessage[]>([]);
|
||||||
@ -100,6 +101,16 @@ export function Chat({
|
|||||||
}
|
}
|
||||||
}, [pendingGeneration, initialMessages, completeGeneration, router]);
|
}, [pendingGeneration, initialMessages, completeGeneration, router]);
|
||||||
|
|
||||||
|
// 清理占位图定时器
|
||||||
|
useEffect(() => {
|
||||||
|
return () => {
|
||||||
|
if (imagePlaceholderTimerRef.current) {
|
||||||
|
clearTimeout(imagePlaceholderTimerRef.current);
|
||||||
|
imagePlaceholderTimerRef.current = null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
const { mutate } = useSWRConfig();
|
const { mutate } = useSWRConfig();
|
||||||
|
|
||||||
// Handle browser back/forward navigation
|
// Handle browser back/forward navigation
|
||||||
@ -303,11 +314,25 @@ export function Chat({
|
|||||||
);
|
);
|
||||||
if (hasGeneratingMarker) {
|
if (hasGeneratingMarker) {
|
||||||
console.log("[Chat] 🖼️ Detected GENERATING_IMAGE marker, will show placeholder after delay");
|
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 的文字回复
|
// 🆕 延迟显示占位图,让用户先看到 AI 的文字回复
|
||||||
// 消息会立即添加到 appendedMessages 并显示,3 秒后再显示动态占位图
|
// 消息会立即添加到 appendedMessages 并显示,3 秒后再显示动态占位图
|
||||||
setTimeout(() => {
|
if (imagePlaceholderTimerRef.current) {
|
||||||
|
clearTimeout(imagePlaceholderTimerRef.current);
|
||||||
|
imagePlaceholderTimerRef.current = null;
|
||||||
|
}
|
||||||
|
imagePlaceholderTimerRef.current = setTimeout(() => {
|
||||||
console.log("[Chat] 🖼️ Now showing placeholder after 3s delay");
|
console.log("[Chat] 🖼️ Now showing placeholder after 3s delay");
|
||||||
setIsGeneratingImageFlag(true);
|
setIsGeneratingImageFlag(true);
|
||||||
|
imagePlaceholderTimerRef.current = null;
|
||||||
}, 3000); // 延迟 3 秒,让用户有时间阅读文字
|
}, 3000); // 延迟 3 秒,让用户有时间阅读文字
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -326,6 +351,10 @@ export function Chat({
|
|||||||
|
|
||||||
if (hasImage) {
|
if (hasImage) {
|
||||||
console.log("[Chat] 🎉 Image generated, hiding placeholder");
|
console.log("[Chat] 🎉 Image generated, hiding placeholder");
|
||||||
|
if (imagePlaceholderTimerRef.current) {
|
||||||
|
clearTimeout(imagePlaceholderTimerRef.current);
|
||||||
|
imagePlaceholderTimerRef.current = null;
|
||||||
|
}
|
||||||
setIsGeneratingImageFlag(false);
|
setIsGeneratingImageFlag(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -472,6 +472,14 @@ function PureMessages({
|
|||||||
(part: any) => part.type === "text" && part.text?.includes("<!-- GENERATING_IMAGE -->")
|
(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 (showVideo) return <QianwenVideoLoading />;
|
||||||
if (showGemini) return <GeminiImageLoading imageCount={imageCount} />;
|
if (showGemini) return <GeminiImageLoading imageCount={imageCount} />;
|
||||||
@ -482,7 +490,7 @@ function PureMessages({
|
|||||||
if (isAutoMode && userMessageHasImage) {
|
if (isAutoMode && userMessageHasImage) {
|
||||||
// 🆕 只用 isGeneratingImageFlag 来控制是否显示生图占位图
|
// 🆕 只用 isGeneratingImageFlag 来控制是否显示生图占位图
|
||||||
// isGeneratingImageFlag 由 chat.tsx 控制:延迟 3 秒后设置为 true,收到图片后设置为 false
|
// isGeneratingImageFlag 由 chat.tsx 控制:延迟 3 秒后设置为 true,收到图片后设置为 false
|
||||||
if (isGeneratingImageFlag) {
|
if (isGeneratingImageFlag && hasAssistantTextReplyWithContent) {
|
||||||
return <ImageGenerationLoading imageCount={imageCount} maxImages={6} providerType="alibaba-image" modelName="智能处理" />;
|
return <ImageGenerationLoading imageCount={imageCount} maxImages={6} providerType="alibaba-image" modelName="智能处理" />;
|
||||||
}
|
}
|
||||||
// 否则显示思考动画(在等待 AI 分析或延迟期间)
|
// 否则显示思考动画(在等待 AI 分析或延迟期间)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user