diff --git a/components/canvas/CanvasChatPanel.tsx b/components/canvas/CanvasChatPanel.tsx index db43d5419..ef0c69d51 100644 --- a/components/canvas/CanvasChatPanel.tsx +++ b/components/canvas/CanvasChatPanel.tsx @@ -2676,13 +2676,14 @@ export function CanvasChatPanel({ chatId, initialMessages = [], onClose, onOpenF const x = 100 + (existingNodes % 3) * (dimensions.width + spacing); const y = 100 + Math.floor(existingNodes / 3) * (dimensions.height + spacing); - addNode({ + const nodeId = addNode({ type: 'image', position: { x, y }, size: { width: dimensions.width, height: dimensions.height }, data: { src: url, status: 'completed', + title: '图片', prompt: generationPrompt || '对话生成', metadata: { originalWidth: dimensions.originalWidth, @@ -2693,6 +2694,7 @@ export function CanvasChatPanel({ chatId, initialMessages = [], onClose, onOpenF }, connections: [], }); + void autoNameGeneratedImageNode(nodeId, url); addedImageCount += 1; } } @@ -2736,7 +2738,7 @@ export function CanvasChatPanel({ chatId, initialMessages = [], onClose, onOpenF } } }); - }, [messages, addNode, nodes]); + }, [messages, addNode, nodes, autoNameGeneratedImageNode]); // 为历史节点补全可编辑的原始提示词(旧数据里可能只有“对话生成”占位文案) useEffect(() => { @@ -2789,6 +2791,7 @@ export function CanvasChatPanel({ chatId, initialMessages = [], onClose, onOpenF // 标记是否已经为当前画布设置过标题(避免重复调用) const hasSetCanvasTitle = useRef(false); + const imageAutoTitleCacheRef = useRef>(new Map()); // 🆕 Agent模式上下文(画布图片布局) const buildAutoContext = useCallback(() => { @@ -4437,6 +4440,60 @@ export function CanvasChatPanel({ chatId, initialMessages = [], onClose, onOpenF } }, [chatId]); + const autoNameGeneratedImageNode = useCallback( + async (nodeId: string, imageUrl: string) => { + if (!nodeId || !imageUrl) return; + + const normalizeAutoTitle = (rawTitle: string) => { + const compact = String(rawTitle || "").replace(/\s+/g, "").trim(); + if (!compact || compact === "新画布" || compact === "未命名画布" || compact.includes("画布")) { + return "图片"; + } + return compact.slice(0, 12); + }; + + const applyNodeTitle = (title: string) => { + const storeState = useCanvasStore.getState(); + const currentNode = storeState.getNode(nodeId); + if (!currentNode) return; + const currentData = ((currentNode.data as any) || {}) as Record; + storeState.updateNode(nodeId, { + data: { + ...currentData, + title, + metadata: { + ...(currentData.metadata || {}), + autoTitle: title, + }, + }, + }); + }; + + const cachedTitle = imageAutoTitleCacheRef.current.get(imageUrl); + if (cachedTitle) { + applyNodeTitle(cachedTitle); + return; + } + + try { + const response = await fetch('/api/canvas/auto-title', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ imageUrl }), + }); + + const data = await response.json().catch(() => ({})); + const title = normalizeAutoTitle(String(data?.title || "")); + imageAutoTitleCacheRef.current.set(imageUrl, title); + applyNodeTitle(title); + } catch (error) { + console.warn('[CanvasChat] 自动命名图片失败:', error); + applyNodeTitle("图片"); + } + }, + [], + ); + const uploadImageFileAndAddToCanvas = useCallback(async ( file: File, nodeIndex: number