diff --git a/components/canvas/CanvasChatPanel.tsx b/components/canvas/CanvasChatPanel.tsx index 0ed74c8..89bdf50 100644 --- a/components/canvas/CanvasChatPanel.tsx +++ b/components/canvas/CanvasChatPanel.tsx @@ -166,9 +166,12 @@ export function CanvasChatPanel({ chatId, initialMessages = [], onClose, onOpenF const editableDiv = textareaRef.current as HTMLDivElement | null; if (!editableDiv || !editableDiv.isContentEditable) return; - // 检查是否已存在该 URL 的标签 + // 检查 DOM 中是否已存在该 URL 的标签(如果存在则不重复添加) const existing = editableDiv.querySelector(`[data-attachment-url="${url}"]`); - if (existing) return; + if (existing) { + console.log('[CanvasChat] 标签已存在于 DOM 中,跳过:', url.substring(0, 50)); + return; + } const tagSpan = document.createElement('span'); tagSpan.contentEditable = 'false'; @@ -194,32 +197,39 @@ export function CanvasChatPanel({ chatId, initialMessages = [], onClose, onOpenF } else { editableDiv.appendChild(tagSpan); } + + console.log('[CanvasChat] 已插入图片标签:', url.substring(0, 50)); }, []); // 🆕 监听 pendingChatAttachments,当用户点击"添加到对话"按钮时添加标签 useEffect(() => { if (pendingChatAttachments.length > 0) { - // 只添加还没在附件中的图片 - setAttachments(prev => { - const existingUrls = new Set(prev.map(a => a.url)); - const toAdd = pendingChatAttachments.filter(a => !existingUrls.has(a.url)); - if (toAdd.length > 0) { - // 插入图片标签到编辑器 - toAdd.forEach((att, i) => { - insertImageTagInEditor(att.url, att.name, prev.length + i); - }); - // 聚焦到输入框 - setTimeout(() => { - (textareaRef.current as HTMLDivElement | null)?.focus(); - }, 0); - return [...prev, ...toAdd]; - } - return prev; - }); + // 获取当前附件列表 + const currentAttachments = [...attachments]; + const existingUrls = new Set(currentAttachments.map(a => a.url)); + + // 过滤出需要添加的图片(不在当前附件列表中的) + const toAdd = pendingChatAttachments.filter(a => !existingUrls.has(a.url)); + + if (toAdd.length > 0) { + // 插入图片标签到编辑器 + toAdd.forEach((att, i) => { + insertImageTagInEditor(att.url, att.name, currentAttachments.length + i); + }); + + // 更新附件列表 + setAttachments([...currentAttachments, ...toAdd]); + + // 聚焦到输入框 + setTimeout(() => { + (textareaRef.current as HTMLDivElement | null)?.focus(); + }, 0); + } + // 清空待添加列表 clearPendingChatAttachments(); } - }, [pendingChatAttachments, insertImageTagInEditor, clearPendingChatAttachments]); + }, [pendingChatAttachments, attachments, insertImageTagInEditor, clearPendingChatAttachments]); // 获取模型列表 const { data: modelsData } = useSWR<{ @@ -737,7 +747,13 @@ export function CanvasChatPanel({ chatId, initialMessages = [], onClose, onOpenF e.target.value = ''; }; - // 移除附件 + // 移除附件(通过 URL 删除,更可靠) + const removeAttachmentByUrl = (url: string) => { + setAttachments(prev => prev.filter(a => a.url !== url)); + console.log('[CanvasChat] 已移除附件:', url.substring(0, 50)); + }; + + // 移除附件(通过索引删除,保留兼容性) const removeAttachment = (index: number) => { setAttachments(prev => prev.filter((_, i) => i !== index)); }; @@ -1062,11 +1078,12 @@ export function CanvasChatPanel({ chatId, initialMessages = [], onClose, onOpenF if (range.collapsed && range.startOffset === 0) { // 检查前一个兄弟节点是否是图片标签 const prevSibling = range.startContainer.previousSibling as HTMLElement; - if (prevSibling && prevSibling.getAttribute?.('data-attachment-index')) { + if (prevSibling && prevSibling.getAttribute?.('data-attachment-url')) { e.preventDefault(); - e.stopPropagation(); // 🆕 阻止事件冒泡,防止触发全局删除 - const index = parseInt(prevSibling.getAttribute('data-attachment-index') || '0'); - removeAttachment(index); + e.stopPropagation(); // 阻止事件冒泡,防止触发全局删除 + // 使用 URL 来删除附件,更可靠 + const url = prevSibling.getAttribute('data-attachment-url') || ''; + removeAttachmentByUrl(url); prevSibling.remove(); } }