feat: 画布聊天自动使用最近图片

当用户发送文字请求但没有附带图片时,自动按以下优先级获取图片:
1. 画布中选中的图片
2. 画布中最近添加的图片
3. 历史对话中的图片

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Your Name 2026-02-01 16:07:34 +08:00
parent 1f26ec911c
commit ddf49cb329

View File

@ -356,6 +356,43 @@ export function CanvasChatPanel({ chatId, initialMessages = [], onClose, onOpenF
// 标记是否已经为当前画布设置过标题(避免重复调用)
const hasSetCanvasTitle = useRef(false);
// 获取最近的图片 URL(用于自动附加)
const getRecentImageUrl = useCallback((): string | null => {
// 1. 优先使用画布中选中的图片
if (selectedImageNodes.length > 0) {
return (selectedImageNodes[0].data as any)?.src || null;
}
// 2. 其次使用画布中最近添加的图片
const imageNodes = nodes.filter(n => n.type === 'image' && (n.data as any)?.src);
if (imageNodes.length > 0) {
// 返回最后一个添加的图片(假设数组末尾是最新的)
return (imageNodes[imageNodes.length - 1].data as any)?.src || null;
}
// 3. 最后从历史对话中查找图片
for (let i = messages.length - 1; i >= 0; i--) {
const msg = messages[i];
// 检查用户消息的附件
if (msg.role === 'user' && (msg as any).attachments?.length > 0) {
const attachment = (msg as any).attachments[0];
if (attachment.url && attachment.contentType?.startsWith('image/')) {
return attachment.url;
}
}
// 检查用户消息的 parts
if (msg.role === 'user' && (msg as any).parts) {
for (const part of (msg as any).parts) {
if (part.type === 'file' && part.url && part.mediaType?.startsWith('image/')) {
return part.url;
}
}
}
}
return null;
}, [selectedImageNodes, nodes, messages]);
// 处理发送(图片已在上传时添加到画布,这里只发送消息)
const handleSend = useCallback(async () => {
if (!input.trim() && attachments.length === 0) return;
@ -367,8 +404,23 @@ export function CanvasChatPanel({ chatId, initialMessages = [], onClose, onOpenF
// 构建 parts 数组:文件在前,文本在后(与 multimodal-input 一致)
const parts: any[] = [];
// 🆕 如果没有附件,自动使用最近的图片
let effectiveAttachments = [...attachments];
if (effectiveAttachments.length === 0 && input.trim()) {
const recentImageUrl = getRecentImageUrl();
if (recentImageUrl) {
// 自动添加最近的图片作为附件
effectiveAttachments.push({
url: recentImageUrl,
name: '最近图片',
contentType: 'image/jpeg',
});
console.log('[CanvasChat] 自动使用最近图片:', recentImageUrl);
}
}
// 添加文件部分(作为 file 类型的 part)
attachments.forEach((a) => {
effectiveAttachments.forEach((a) => {
parts.push({
type: "file" as const,
mediaType: a.contentType,
@ -378,7 +430,7 @@ export function CanvasChatPanel({ chatId, initialMessages = [], onClose, onOpenF
});
// 添加文本部分
const textContent = input.trim() || (attachments.length > 0 ? "请处理这张图片" : "");
const textContent = input.trim() || (effectiveAttachments.length > 0 ? "请处理这张图片" : "");
if (textContent) {
parts.push({ type: "text" as const, text: textContent });
}
@ -391,7 +443,7 @@ export function CanvasChatPanel({ chatId, initialMessages = [], onClose, onOpenF
setInput("");
setAttachments([]);
}, [input, attachments, selectedModelId, sendMessage]);
}, [input, attachments, selectedModelId, sendMessage, getRecentImageUrl]);
// 处理文件选择 - 上传后立即添加到画布
const handleFileSelect = async (e: React.ChangeEvent<HTMLInputElement>) => {