diff --git a/components/canvas/CanvasChatPanel.tsx b/components/canvas/CanvasChatPanel.tsx
index 2dc5fe4..1ab713b 100644
--- a/components/canvas/CanvasChatPanel.tsx
+++ b/components/canvas/CanvasChatPanel.tsx
@@ -121,7 +121,42 @@ export function CanvasChatPanel({ chatId, initialMessages = [], onClose, onOpenF
n => selectedNodeIds.includes(n.id) && n.type === 'image' && (n.data as any)?.src
);
- // 当选中画布中的图片时,自动添加到附件
+ // 🆕 辅助函数:在 contenteditable 中插入图片标签
+ const insertImageTagInEditor = useCallback((url: string, name: string, index: number) => {
+ const editableDiv = textareaRef.current as HTMLDivElement | null;
+ if (!editableDiv || !editableDiv.isContentEditable) return;
+
+ // 检查是否已存在该 URL 的标签
+ const existing = editableDiv.querySelector(`[data-attachment-url="${url}"]`);
+ if (existing) return;
+
+ const tagSpan = document.createElement('span');
+ tagSpan.contentEditable = 'false';
+ tagSpan.setAttribute('data-attachment-index', String(index));
+ tagSpan.setAttribute('data-attachment-url', url);
+ tagSpan.className = 'inline-flex items-center gap-1 bg-muted/60 hover:bg-muted rounded-full pl-1 pr-2 py-0.5 mx-0.5 align-middle cursor-default select-none';
+ const shortName = name.length > 10 ? name.substring(0, 8) + '...' : name;
+ tagSpan.innerHTML = `
+
+ ${shortName}
+ `;
+
+ // 在光标位置插入,或添加到末尾
+ const selection = window.getSelection();
+ if (selection && selection.rangeCount > 0 && editableDiv.contains(selection.anchorNode)) {
+ const range = selection.getRangeAt(0);
+ range.deleteContents();
+ range.insertNode(tagSpan);
+ range.setStartAfter(tagSpan);
+ range.setEndAfter(tagSpan);
+ selection.removeAllRanges();
+ selection.addRange(range);
+ } else {
+ editableDiv.appendChild(tagSpan);
+ }
+ }, []);
+
+ // 当选中画布中的图片时,自动添加到附件并插入标签
useEffect(() => {
if (selectedImageNodes.length > 0) {
const newAttachments: Attachment[] = selectedImageNodes.map(node => {
@@ -138,12 +173,16 @@ export function CanvasChatPanel({ chatId, initialMessages = [], onClose, onOpenF
const existingUrls = new Set(prev.map(a => a.url));
const toAdd = newAttachments.filter(a => !existingUrls.has(a.url));
if (toAdd.length > 0) {
+ // 插入图片标签到编辑器
+ toAdd.forEach((att, i) => {
+ insertImageTagInEditor(att.url, att.name, prev.length + i);
+ });
return [...prev, ...toAdd];
}
return prev;
});
}
- }, [selectedImageNodes.length, selectedNodeIds]);
+ }, [selectedImageNodes.length, selectedNodeIds, insertImageTagInEditor]);
// 获取模型列表
const { data: modelsData } = useSWR<{
@@ -534,12 +573,20 @@ export function CanvasChatPanel({ chatId, initialMessages = [], onClose, onOpenF
firstImageUrl = imageUrl;
}
- // 添加到附件列表
- setAttachments(prev => [...prev, {
- url: imageUrl,
- name: file.name,
- contentType: file.type,
- }]);
+ // 添加到附件列表并在编辑器中插入标签
+ setAttachments(prev => {
+ const newIndex = prev.length;
+ // 在编辑器中插入图片标签
+ setTimeout(() => {
+ insertImageTagInEditor(imageUrl, file.name, newIndex);
+ (textareaRef.current as HTMLDivElement | null)?.focus();
+ }, 0);
+ return [...prev, {
+ url: imageUrl,
+ name: file.name,
+ contentType: file.type,
+ }];
+ });
// 立即将图片添加到画布
const existingNodes = nodes.length;
@@ -877,52 +924,59 @@ export function CanvasChatPanel({ chatId, initialMessages = [], onClose, onOpenF
- {/* 底部输入区域 - Redesigned */}
+ {/* 底部输入区域 - 混合输入(图片标签+文字可自由混合) */}