feat: 画布图片节点添加图片标注功能

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Your Name 2026-02-01 17:12:30 +08:00
parent 94640002d0
commit 59a81f3c0e
4 changed files with 73 additions and 4 deletions

View File

@ -456,6 +456,10 @@ export function CanvasChatPanel({ chatId, initialMessages = [], onClose, onOpenF
}
}
// 🆕 检查是否有标注的图片
const annotatedNodes = selectedImageNodes.filter(node => (node.data as any)?.hasAnnotations);
const hasAnnotatedImages = annotatedNodes.length > 0;
// 添加文件部分(作为 file 类型的 part)
effectiveAttachments.forEach((a) => {
parts.push({
@ -463,11 +467,19 @@ export function CanvasChatPanel({ chatId, initialMessages = [], onClose, onOpenF
mediaType: a.contentType,
name: a.name,
url: a.url,
hasAnnotations: hasAnnotatedImages, // 🆕 标记图片是否有标注
});
});
// 添加文本部分
const textContent = input.trim() || (effectiveAttachments.length > 0 ? "请处理这张图片" : "");
let textContent = input.trim() || (effectiveAttachments.length > 0 ? "请处理这张图片" : "");
// 🆕 如果有标注的图片,在文本中添加提示
if (hasAnnotatedImages && textContent) {
textContent = `【注意:图片中有标注(箭头、圆圈、文字等)标记的区域是需要重点关注或修改的部分,请根据标注进行处理】\n${textContent}`;
console.log('[CanvasChat] 检测到标注图片,已添加标注提示');
}
if (textContent) {
parts.push({ type: "text" as const, text: textContent });
}

View File

@ -2,7 +2,7 @@
import React, { useState, useRef, useEffect } from "react";
import { createPortal } from "react-dom";
import { ImagePlus, Loader2, AlertCircle, MoreVertical, Scissors, Sparkles, Expand, Maximize2, Move3D, Layers, Download, Trash2, RefreshCw } from "lucide-react";
import { ImagePlus, Loader2, AlertCircle, MoreVertical, Scissors, Sparkles, Expand, Maximize2, Move3D, Layers, Download, Trash2, RefreshCw, Pencil } from "lucide-react";
import { Button } from "@/components/ui/button";
import {
DropdownMenu,
@ -27,6 +27,7 @@ import type { CanvasNode, ImageNodeData } from "@/types/canvas";
import { cn } from "@/lib/utils";
import { AngleEditDialog } from "@/components/angle-edit-dialog";
import { SceneFusionDialog } from "@/components/scene-fusion-dialog";
import { ImageAnnotator } from "@/components/image-annotator";
interface ImageNodeProps {
node: CanvasNode;
@ -43,6 +44,7 @@ export function ImageNode({ node, isSelected }: ImageNodeProps) {
const [resizeWidth, setResizeWidth] = useState(1024);
const [resizeHeight, setResizeHeight] = useState(1024);
const [isUploading, setIsUploading] = useState(false);
const [annotatorOpen, setAnnotatorOpen] = useState(false);
// 文件输入的 ref
const fileInputRef = useRef<HTMLInputElement>(null);
@ -136,6 +138,7 @@ export function ImageNode({ node, isSelected }: ImageNodeProps) {
'resize': '尺寸调整',
'angle': '角度编辑',
'scene-fusion': '详情替换',
'annotate': '图片标注',
};
return names[action] || action;
};
@ -160,6 +163,10 @@ export function ImageNode({ node, isSelected }: ImageNodeProps) {
setResizeDialogOpen(true);
return;
}
if (action === 'annotate') {
setAnnotatorOpen(true);
return;
}
const actionName = getActionName(action);
@ -263,6 +270,26 @@ export function ImageNode({ node, isSelected }: ImageNodeProps) {
});
};
// 图片标注完成回调
const handleAnnotateComplete = (annotatedImageUrl: string) => {
setAnnotatorOpen(false);
// 更新当前节点,标记为已标注图片
// 标注后的图片会带有视觉标记(箭头、圆圈、文字等)
// 这些标记指示了用户希望 AI 重点关注/修改的区域
updateNode(node.id, {
data: {
...data,
src: annotatedImageUrl,
hasAnnotations: true, // 🆕 标记为已标注
originalSrc: data.originalSrc || data.src, // 保留原始图片
prompt: data.prompt ? `${data.prompt} (已标注)` : '已标注图片',
},
});
toast.success('标注已保存!发送对话时将重点处理标注区域');
};
// 尺寸调整执行
const handleResize = async () => {
if (!data.src) return;
@ -617,6 +644,18 @@ export function ImageNode({ node, isSelected }: ImageNodeProps) {
<span>调整</span>
</Button>
{/* 图片标注 */}
<Button
variant="ghost"
size="sm"
className={`h-9 px-3 text-xs gap-1.5 rounded-full hover:bg-gray-100 ${data.hasAnnotations ? 'text-primary' : ''}`}
onClick={() => handleAction('annotate')}
title="标注图片重点区域"
>
<Pencil className="h-4 w-4" />
<span>标注</span>
</Button>
{/* 更多操作 */}
<DropdownMenu>
<DropdownMenuTrigger asChild>
@ -647,6 +686,15 @@ export function ImageNode({ node, isSelected }: ImageNodeProps) {
</div>,
document.body
)}
{/* 图片标注器 */}
{annotatorOpen && data.src && (
<ImageAnnotator
imageUrl={data.src}
onConfirm={handleAnnotateComplete}
onCancel={() => setAnnotatorOpen(false)}
/>
)}
</>
);
}

View File

@ -57,6 +57,7 @@ const EDIT_TEMPLATES: Record<
negative: string;
extractors: Record<string, RegExp>;
defaults: Record<string, string>;
useOriginalPrompt?: boolean; // 🆕 是否使用原始提示词
}
> = {
remove_background: {
@ -178,6 +179,13 @@ const LLM_OPTIMIZER_SYSTEM_PROMPT = `你是一个专业的AI图片编辑提示
3. **细节要求**:包含颜色、材质、光影、位置等具体参数
4. **自然过渡**:修改区域与周围环境自然融合
## 图片标注处理(重要!)
如果用户消息中提到"图片中有标注"或"标注区域",说明用户已经在图片上画了标记(箭头、圆圈、文字等)来指示需要修改的区域:
- **优先处理标注区域**:将标注标记的区域作为主要编辑目标
- **理解标注意图**:箭头指向的位置、圆圈圈住的区域、文字说明的内容都是用户想要修改的部分
- **强调标注**:在提示词中明确提到"按照图片中的标注/标记指示进行修改"
- **保护非标注区域**:标注以外的区域应保持完全不变
## 输出格式(严格JSON)
{
"optimizedPrompt": "精准的图片编辑指令",
@ -403,7 +411,7 @@ export function optimizePrompt(params: {
friendlyMessage = friendlyTemplate.template;
// 填充友好消息中的变量
for (const [key, regex] of Object.entries(template.extractors)) {
for (const [key, regex] of Object.entries(template.extractors) as [string, RegExp][]) {
const match = originalPrompt.match(regex);
let value = match ? match[1].trim() : template.defaults[key] || "";
value = value.replace(/[,。!?,.!?]/g, "").trim();
@ -414,7 +422,7 @@ export function optimizePrompt(params: {
}
// 填充剩余的默认值
for (const [key, defaultValue] of Object.entries(template.defaults)) {
for (const [key, defaultValue] of Object.entries(template.defaults) as [string, string][]) {
friendlyMessage = friendlyMessage.replace(`{${key}}`, defaultValue);
}
}

View File

@ -40,6 +40,7 @@ export interface ImageNodeData {
// 图片编辑相关
originalSrc?: string;
editHistory?: ImageEditAction[];
hasAnnotations?: boolean; // 🆕 标记图片是否有用户标注
}
// 视频节点数据