diff --git a/components/canvas/CanvasChatPanel.tsx b/components/canvas/CanvasChatPanel.tsx index e2ceaca..5444b7e 100644 --- a/components/canvas/CanvasChatPanel.tsx +++ b/components/canvas/CanvasChatPanel.tsx @@ -130,6 +130,38 @@ function extractImageUrls(text: string): string[] { return urls; } +function extractUserTextPrompt(message: ChatMessage): string { + const parts = (message as any)?.parts || []; + const textFromParts = parts + .filter((part: any) => part?.type === "text" && typeof part.text === "string") + .map((part: any) => + part.text + .replace( + /【注意:图片中有标注(箭头、圆圈、文字等)标记的区域是需要重点关注或修改的部分,请根据标注进行处理】\n?/g, + "" + ) + .trim() + ) + .filter(Boolean) + .join("\n") + .trim(); + + if (textFromParts) return textFromParts; + + const content = (message as any)?.content; + return typeof content === "string" ? content.trim() : ""; +} + +function findNearestUserPrompt(messages: ChatMessage[], assistantIndex: number): string { + for (let i = assistantIndex - 1; i >= 0; i -= 1) { + const candidate = messages[i]; + if (candidate?.role !== "user") continue; + const prompt = extractUserTextPrompt(candidate); + if (prompt) return prompt; + } + return ""; +} + // 🆕 拾取物品的附件类型扩展 interface PickedObjectAttachment extends Attachment { pickedObject?: { @@ -667,10 +699,11 @@ export function CanvasChatPanel({ chatId, initialMessages = [], onClose, onOpenF } // 只处理新消息 - messages.forEach(async (message) => { + messages.forEach(async (message, messageIndex) => { if (message.role === 'assistant' && !processedMessageIds.current.has(message.id)) { const content = message.parts?.map((p: any) => p.text || '').join('') || ''; const imageUrls = extractImageUrls(content); + const generationPrompt = findNearestUserPrompt(messages, messageIndex); if (imageUrls.length > 0) { processedMessageIds.current.add(message.id); @@ -703,10 +736,12 @@ export function CanvasChatPanel({ chatId, initialMessages = [], onClose, onOpenF data: { src: url, status: 'completed', - prompt: '对话生成', + prompt: generationPrompt || '对话生成', metadata: { originalWidth: dimensions.originalWidth, originalHeight: dimensions.originalHeight, + generationPrompt: generationPrompt || undefined, + sourceMessageId: message.id, }, }, connections: [], diff --git a/components/canvas/nodes/ImageNode.tsx b/components/canvas/nodes/ImageNode.tsx index f4562af..99dc60a 100644 --- a/components/canvas/nodes/ImageNode.tsx +++ b/components/canvas/nodes/ImageNode.tsx @@ -83,6 +83,30 @@ const sanitizeFileName = (name: string) => { return safe || "image"; }; +const GENERIC_CANVAS_PROMPTS = new Set([ + "对话生成", + "图片生成", + "AI生成", + "智能生成", + "自动生成", +]); + +const normalizePromptText = (value: unknown): string => { + return typeof value === "string" ? value.trim() : ""; +}; + +const resolveRegeneratePromptSeed = (data: ImageNodeData): string => { + const metadataPrompt = normalizePromptText(data.metadata?.generationPrompt); + if (metadataPrompt) return metadataPrompt; + + const nodePrompt = normalizePromptText(data.prompt); + if (nodePrompt && !GENERIC_CANVAS_PROMPTS.has(nodePrompt)) { + return nodePrompt; + } + + return nodePrompt; +}; + const COMMON_ASPECT_RATIOS = [ "1:1", "2:3", @@ -284,9 +308,9 @@ export function ImageNode({ node, isSelected }: ImageNodeProps) { useEffect(() => { if (promptDialogOpen) { - setRegeneratePrompt(data.prompt || ""); + setRegeneratePrompt(resolveRegeneratePromptSeed(data)); } - }, [data.prompt, promptDialogOpen]); + }, [data, promptDialogOpen]); useEffect(() => { if (notePanelOpen) { @@ -790,7 +814,7 @@ export function ImageNode({ node, isSelected }: ImageNodeProps) { return; } if (action === "prompt-regenerate") { - setRegeneratePrompt(data.prompt || ""); + setRegeneratePrompt(resolveRegeneratePromptSeed(data)); setPromptDialogOpen(true); return; }