From 3f09f2b8f106e10769ba3fe7b6bfa748be432053 Mon Sep 17 00:00:00 2001 From: Luckyu_js <11670186+luckyu-js@user.noreply.gitee.com> Date: Mon, 13 Jul 2026 19:08:35 +0800 Subject: [PATCH] =?UTF-8?q?feat(annotate):=20=E6=8E=A5=E5=85=A5=E7=BC=96?= =?UTF-8?q?=E8=BE=91=E5=B7=A5=E5=85=B7=E5=AE=BF=E4=B8=BB=E4=B8=8E=E8=8A=82?= =?UTF-8?q?=E7=82=B9=E8=8F=9C=E5=8D=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - nodeToolStore / ImageEditTool / ImageEditSession / ActiveImageEditHost 注册 annotate - MediaEditActionDropdown 标签 - GenerateImageNode 与 ImageInputNode 编辑菜单新增"标注"入口、图标与激活判定 Co-Authored-By: Claude Opus 4.8 --- src/components/canvas/ActiveImageEditHost.tsx | 1 + .../media/MediaEditActionDropdown.tsx | 4 ++- .../media/image-edit/ImageEditSession.tsx | 3 ++ src/components/media/image-edit/types.ts | 2 +- src/components/nodes/GenerateImageNode.tsx | 30 +++++++++++++++++-- src/components/nodes/ImageInputNode.tsx | 25 ++++++++++++++-- src/store/nodeToolStore.ts | 3 +- 7 files changed, 60 insertions(+), 8 deletions(-) diff --git a/src/components/canvas/ActiveImageEditHost.tsx b/src/components/canvas/ActiveImageEditHost.tsx index 4a46b910..a9311123 100644 --- a/src/components/canvas/ActiveImageEditHost.tsx +++ b/src/components/canvas/ActiveImageEditHost.tsx @@ -18,6 +18,7 @@ const IMAGE_EDIT_TOOLS: ReadonlySet = new Set([ "outpainting", "inpainting", "splitGrid", + "annotate", ]); function asImageDimensions(value: unknown): { width: number; height: number } | null { diff --git a/src/components/media/MediaEditActionDropdown.tsx b/src/components/media/MediaEditActionDropdown.tsx index 81b4ed5f..89f7badf 100644 --- a/src/components/media/MediaEditActionDropdown.tsx +++ b/src/components/media/MediaEditActionDropdown.tsx @@ -4,7 +4,7 @@ import type { ReactNode } from "react"; import type { MenuProps } from "antd"; import type { NodeActionCapsuleAction } from "@/components/nodes/NodeActionCapsule"; -export type MediaEditMenuTool = "edit" | "high-definition" | "outpainting" | "inpainting" | "crop"; +export type MediaEditMenuTool = "edit" | "high-definition" | "outpainting" | "inpainting" | "crop" | "annotate"; interface MediaEditActionDropdownOptions { label: string; @@ -46,6 +46,8 @@ export function getMediaEditMenuLabel(tool: MediaEditMenuTool) { return "擦除"; case "crop": return "裁剪"; + case "annotate": + return "标注"; default: return "编辑"; } diff --git a/src/components/media/image-edit/ImageEditSession.tsx b/src/components/media/image-edit/ImageEditSession.tsx index 586eebff..c004855f 100644 --- a/src/components/media/image-edit/ImageEditSession.tsx +++ b/src/components/media/image-edit/ImageEditSession.tsx @@ -4,6 +4,7 @@ import { CropEditSession } from "./CropEditSession"; import { InpaintingEditSession } from "./InpaintingEditSession"; import { OutpaintingEditSession } from "./OutpaintingEditSession"; import { SplitGridEditSession } from "./SplitGridEditSession"; +import { AnnotationEditSession } from "./AnnotationEditSession"; import type { ImageEditSessionProps } from "./types"; export function ImageEditSession({ @@ -35,6 +36,8 @@ export function ImageEditSession({ return ; case "splitGrid": return splitGridSelection ? : null; + case "annotate": + return ; default: return null; } diff --git a/src/components/media/image-edit/types.ts b/src/components/media/image-edit/types.ts index 24b5fd9d..259d0371 100644 --- a/src/components/media/image-edit/types.ts +++ b/src/components/media/image-edit/types.ts @@ -2,7 +2,7 @@ import type { RefObject } from "react"; import type { ProviderModel } from "@/lib/providers/types"; import type { SplitGridSelection } from "@/components/media/SplitGridCapsuleMenu"; -export type ImageEditTool = "crop" | "outpainting" | "inpainting" | "splitGrid"; +export type ImageEditTool = "crop" | "outpainting" | "inpainting" | "splitGrid" | "annotate"; export interface ImageEditSessionCommonProps { nodeId: string; diff --git a/src/components/nodes/GenerateImageNode.tsx b/src/components/nodes/GenerateImageNode.tsx index 6ffb0a84..5eedf37f 100644 --- a/src/components/nodes/GenerateImageNode.tsx +++ b/src/components/nodes/GenerateImageNode.tsx @@ -64,6 +64,14 @@ function GenerateImageEditMenuIcon({ tool, className = "h-5 w-5" }: { tool: Medi ); } + if (tool === "annotate") { + return ( + + + + + ); + } return ( @@ -297,6 +305,10 @@ export function GenerateImageNodeView({ id, data, selected, renderInputHandle = openNodeTool("inpainting"); }, [openNodeTool]); + const startAnnotationTool = useCallback(() => { + openNodeTool("annotate"); + }, [openNodeTool]); + const handleEditMenuPrimaryClick = useCallback((event: React.MouseEvent) => { event.preventDefault(); event.stopPropagation(); @@ -312,9 +324,13 @@ export function GenerateImageNodeView({ id, data, selected, renderInputHandle = startInpaintingTool(); return; } + if (activeEditMenuTool === "annotate") { + startAnnotationTool(); + return; + } setActiveEditMenuTool("crop"); startCropTool(); - }, [activeEditMenuTool, handleCreateHighDefinitionNode, startCropTool, startInpaintingTool, startOutpaintingTool]); + }, [activeEditMenuTool, handleCreateHighDefinitionNode, startAnnotationTool, startCropTool, startInpaintingTool, startOutpaintingTool]); const handleCarouselPrevious = useCallback(async () => { const history = nodeData.imageHistory || []; @@ -437,7 +453,8 @@ export function GenerateImageNodeView({ id, data, selected, renderInputHandle = activeNodeTool === "crop" || activeNodeTool === "outpainting" || activeNodeTool === "inpainting" || - activeNodeTool === "splitGrid" + activeNodeTool === "splitGrid" || + activeNodeTool === "annotate" ? activeNodeTool : null; const isEditingImage = Boolean(activeImageEditTool); @@ -472,6 +489,11 @@ export function GenerateImageNodeView({ id, data, selected, renderInputHandle = icon: , label: t("imageInput.crop"), }, + { + key: "annotate", + icon: , + label: "标注", + }, ], menuOnClick: ({ key, domEvent }) => { domEvent.preventDefault(); @@ -495,6 +517,10 @@ export function GenerateImageNodeView({ id, data, selected, renderInputHandle = setActiveEditMenuTool("crop"); startCropTool(); } + if (key === "annotate") { + setActiveEditMenuTool("annotate"); + startAnnotationTool(); + } }, }), { diff --git a/src/components/nodes/ImageInputNode.tsx b/src/components/nodes/ImageInputNode.tsx index b80b665a..269d65b0 100644 --- a/src/components/nodes/ImageInputNode.tsx +++ b/src/components/nodes/ImageInputNode.tsx @@ -63,7 +63,8 @@ type ImageToolbarIconKind = | "multiAngle" | "outpainting" | "inpainting" - | "highDefinition"; + | "highDefinition" + | "annotate"; function ImageToolbarIcon({ kind }: { kind: ImageToolbarIconKind }) { const commonProps = { @@ -97,6 +98,8 @@ function ImageToolbarIcon({ kind }: { kind: ImageToolbarIconKind }) { return ; case "highDefinition": return ; + case "annotate": + return ; } } @@ -107,7 +110,9 @@ function getImageInputEditMenuIcon(tool: MediaEditMenuTool) { ? "crop" : tool === "inpainting" ? "inpainting" - : "outpainting"; + : tool === "annotate" + ? "annotate" + : "outpainting"; return ; } @@ -144,7 +149,8 @@ export function ImageInputNodeView({ id, data, selected, renderInputHandle = fal activeNodeTool === "crop" || activeNodeTool === "outpainting" || activeNodeTool === "inpainting" || - activeNodeTool === "splitGrid" + activeNodeTool === "splitGrid" || + activeNodeTool === "annotate" ? activeNodeTool : null; const isEditingImage = Boolean(activeImageEditTool); @@ -324,6 +330,10 @@ export function ImageInputNodeView({ id, data, selected, renderInputHandle = fal openImageEditTool("inpainting"); return; } + if (activeEditMenuTool === "annotate") { + openImageEditTool("annotate"); + return; + } setActiveEditMenuTool("crop"); openImageEditTool("crop"); }, [activeEditMenuTool, handleCreateHighDefinitionNode, openImageEditTool]); @@ -422,6 +432,11 @@ export function ImageInputNodeView({ id, data, selected, renderInputHandle = fal icon: , label: t("imageInput.crop"), }, + { + key: "annotate", + icon: , + label: "标注", + }, ], menuOnClick: ({ key, domEvent }) => { domEvent.preventDefault(); @@ -445,6 +460,10 @@ export function ImageInputNodeView({ id, data, selected, renderInputHandle = fal setActiveEditMenuTool("crop"); openImageEditTool("crop"); } + if (key === "annotate") { + setActiveEditMenuTool("annotate"); + openImageEditTool("annotate"); + } }, }), { diff --git a/src/store/nodeToolStore.ts b/src/store/nodeToolStore.ts index 9b4f42c3..278cc699 100644 --- a/src/store/nodeToolStore.ts +++ b/src/store/nodeToolStore.ts @@ -8,7 +8,8 @@ export type NodeToolType = | "crop" | "splitGrid" | "outpainting" - | "inpainting"; + | "inpainting" + | "annotate"; interface NodeToolState { activeNodeId: string | null;