fix(chat): delay image placeholder until agent analysis stage is visible
Some checks failed
Lint / build (20) (push) Has been cancelled
Playwright Tests / test (push) Has been cancelled

This commit is contained in:
Your Name 2026-03-02 22:36:26 +08:00
parent 7ce2857aff
commit 6967fd6683
2 changed files with 33 additions and 13 deletions

View File

@ -115,6 +115,21 @@ function estimateTypewriterDurationMs(text: string): number {
return Math.min(9000, Math.max(1200, length * 22));
}
function extractDisplayTextFromParts(parts?: Array<any>): string {
if (!Array.isArray(parts) || parts.length === 0) return "";
return parts
.filter((part) => part?.type === "text" && typeof part.text === "string")
.map((part) =>
String(part.text)
.replace(/\n*<!-- GENERATING_IMAGE -->\n*/g, "")
.replace(/\n*<!-- IMAGE_GENERATION_START -->\n*/g, "")
.replace(/\n*<!-- IMAGE_GENERATION_END -->\n*/g, "")
.trim()
)
.join("\n")
.trim();
}
function sortMessagesByCreatedAt(messages: ChatMessage[]): ChatMessage[] {
return [...messages].sort((a, b) => {
const timeA = (a as any).createdAt ? new Date((a as any).createdAt).getTime() : 0;
@ -242,6 +257,7 @@ export function Chat({
const [imageGenerationStartedAt, setImageGenerationStartedAt] = useState<number | null>(null);
const imageGenerationPhaseRef = useRef<ImageGenerationPhase>("idle");
const imagePlaceholderTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
const latestAssistantDisplayTextRef = useRef<string>("");
const startedGenerationMessageIdRef = useRef<string | null>(null);
// 🆕 存储通过 data-appendMessage 添加的消息,避免与 useChat 内部状态冲突
@ -486,6 +502,7 @@ export function Chat({
api: "/api/chat",
fetch: fetchWithErrorHandlers,
prepareSendMessagesRequest(request) {
latestAssistantDisplayTextRef.current = "";
console.log("[Chat] Preparing request, videoParams:", videoParamsRef.current);
console.log("[Chat] Preparing request, imageParams:", imageParamsRef.current);
console.log("[Chat] Is video model:", isVideoModelRef.current);
@ -536,6 +553,10 @@ export function Chat({
.trim();
return cleanedText.length === 0;
});
const displayTextFromMessage = extractDisplayTextFromParts(messageData.parts);
if (displayTextFromMessage) {
latestAssistantDisplayTextRef.current = displayTextFromMessage;
}
if (hasImageGenEndMarker && isMarkerOnlyMessage) {
console.log("[Chat] 🖼️ Image generation entered finalizing stage");
@ -548,28 +569,26 @@ export function Chat({
}
if (hasImageGenStartMarker && isMarkerOnlyMessage) {
console.log("[Chat] 🖼️ Image generation started, show placeholder");
const delayText = latestAssistantDisplayTextRef.current;
const delayMs = delayText
? estimateTypewriterDurationMs(delayText) + 240
: 1200;
console.log("[Chat] 🖼️ Image generation start marker received, delay processing placeholder:", delayMs);
if (imagePlaceholderTimerRef.current) {
clearTimeout(imagePlaceholderTimerRef.current);
imagePlaceholderTimerRef.current = null;
}
updateImageGenerationPhase("processing");
updateImageGenerationPhase("queued");
imagePlaceholderTimerRef.current = setTimeout(() => {
updateImageGenerationPhase("processing");
imagePlaceholderTimerRef.current = null;
}, delayMs);
return; // 不渲染标记消息
}
if (hasLegacyGeneratingMarker) {
console.log("[Chat] 🖼️ Detected legacy marker, will show placeholder after delay");
const displayText = messageData.parts
?.filter((part: any) => part.type === "text" && typeof part.text === "string")
.map((part: any) =>
String(part.text)
.replace(/\n*<!-- GENERATING_IMAGE -->\n*/g, "")
.replace(/\n*<!-- IMAGE_GENERATION_START -->\n*/g, "")
.replace(/\n*<!-- IMAGE_GENERATION_END -->\n*/g, "")
.trim()
)
.join("\n")
.trim() || "";
const displayText = displayTextFromMessage;
const hasDisplayText = displayText.length > 0;
const placeholderDelayMs = hasDisplayText
? estimateTypewriterDurationMs(displayText) + 240

View File

@ -676,6 +676,7 @@ function PureMessages({
if (
autoImagePlaceholderReady &&
isGeneratingImageFlag &&
imageGenerationPhase !== "queued" &&
hasAssistantTextReplyWithContent &&
!hasGenerationEnd
) {