import type { ReactNode } from "react"; import { Brush, Camera, Copy, FileText, Grid2x2, Lock, LockOpen, Maximize2, PencilLine, Scissors, SlidersHorizontal, Smile, Sparkles, Upload, ZoomIn } from "lucide-react"; import type { CanvasNodeData } from "@/types/canvas"; export type ImageNodeActionToolId = "copyPrompt" | "reversePrompt" | "replace" | "resize" | "annotation" | "maskEdit" | "emotion" | "portraitTexture" | "crop" | "split" | "upscale" | "superResolve" | "angle" | "view"; export type ImageQuickToolId = "info" | "delete" | "saveAsset" | "download" | "edit" | ImageNodeActionToolId; export type ImageToolHandlers = { onUpload: (node: CanvasNodeData) => void; onToggleFreeResize: (node: CanvasNodeData) => void; onAnnotate: (node: CanvasNodeData) => void; onMaskEdit: (node: CanvasNodeData) => void; onEmotion: (node: CanvasNodeData) => void; onPortraitTexture: (node: CanvasNodeData) => void; onCrop: (node: CanvasNodeData) => void; onSplit: (node: CanvasNodeData) => void; onUpscale: (node: CanvasNodeData) => void; onSuperResolve: (node: CanvasNodeData) => void; onAngle: (node: CanvasNodeData) => void; onViewImage: (node: CanvasNodeData) => void; onCopyPrompt: (node: CanvasNodeData) => void; onReversePrompt: (node: CanvasNodeData) => void; }; export type ImageToolDefinition = { id: ImageNodeActionToolId; defaultVisible: boolean; panelLabel: string; label: string | ((node: CanvasNodeData) => string); title: string | ((node: CanvasNodeData) => string); icon: (node: CanvasNodeData) => ReactNode; active?: (node: CanvasNodeData) => boolean; run: (node: CanvasNodeData, handlers: ImageToolHandlers) => void; }; export const IMAGE_QUICK_TOOLS_STORAGE_KEY = "canvas-image-quick-tools-v13"; const defaultBaseToolIds: ImageQuickToolId[] = ["info", "delete", "saveAsset", "download", "edit"]; export const imageToolDefinitions: ImageToolDefinition[] = [ { id: "copyPrompt", defaultVisible: true, panelLabel: "复制提示词", label: "复制提示词", title: "复制生成该图片的提示词", icon: () => , run: (node, handlers) => handlers.onCopyPrompt(node), }, { id: "reversePrompt", defaultVisible: true, panelLabel: "反推提示词", label: "反推提示词", title: "创建反推提示词的文本和配置节点", icon: () => , run: (node, handlers) => handlers.onReversePrompt(node), }, { id: "replace", defaultVisible: true, panelLabel: "替换图片", label: "替换图片", title: "替换图片", icon: () => , run: (node, handlers) => handlers.onUpload(node), }, { id: "resize", defaultVisible: false, panelLabel: "锁比例", label: (node) => (node.metadata?.freeResize ? "自由比例" : "锁比例"), title: (node) => (node.metadata?.freeResize ? "切换为等比缩放" : "切换为自由比例"), icon: (node) => (node.metadata?.freeResize ? : ), active: (node) => Boolean(node.metadata?.freeResize), run: (node, handlers) => handlers.onToggleFreeResize(node), }, { id: "annotation", defaultVisible: true, panelLabel: "标注", label: "标注", title: "在图片上绘制标记并保存为新节点", icon: () => , run: (node, handlers) => handlers.onAnnotate(node), }, { id: "maskEdit", defaultVisible: true, panelLabel: "局部编辑", label: "局部编辑", title: "添加蒙版遮罩后局部修改", icon: () => , run: (node, handlers) => handlers.onMaskEdit(node), }, { id: "emotion", defaultVisible: true, panelLabel: "表情与情绪", label: "情绪", title: "调整人物表情与情绪", icon: () => , run: (node, handlers) => handlers.onEmotion(node), }, { id: "portraitTexture", defaultVisible: true, panelLabel: "人物质感调节", label: "人物质感", title: "调节人景融合、光影、皮肤、纹理与锐度", icon: () => , run: (node, handlers) => handlers.onPortraitTexture(node), }, { id: "crop", defaultVisible: true, panelLabel: "剪裁", label: "剪裁", title: "剪裁并生成新节点", icon: () => , run: (node, handlers) => handlers.onCrop(node), }, { id: "split", defaultVisible: true, panelLabel: "切图", label: "切图", title: "按行列切分图片", icon: () => , run: (node, handlers) => handlers.onSplit(node), }, { id: "upscale", defaultVisible: true, panelLabel: "放大", label: "放大", title: "放大图片分辨率", icon: () => , run: (node, handlers) => handlers.onUpscale(node), }, { id: "superResolve", defaultVisible: false, panelLabel: "超分", label: "超分", title: "AI 超分", icon: () => , run: (node, handlers) => handlers.onSuperResolve(node), }, { id: "angle", defaultVisible: true, panelLabel: "多视角", label: "多视角", title: "生成不同观察视角", icon: () => , run: (node, handlers) => handlers.onAngle(node), }, { id: "view", defaultVisible: true, panelLabel: "查看大图", label: "查看大图", title: "查看图片详情", icon: () => , run: (node, handlers) => handlers.onViewImage(node), }, ]; export const defaultImageQuickToolIds: ImageQuickToolId[] = ["info", "download", "maskEdit", "emotion", "portraitTexture", "crop", "angle"]; export function isImageQuickToolId(value: string): value is ImageQuickToolId { return defaultBaseToolIds.some((id) => id === value) || imageToolDefinitions.some((tool) => tool.id === value); } export function buildImageToolbarTools(node: CanvasNodeData, handlers: ImageToolHandlers) { return imageToolDefinitions.map((tool) => ({ id: tool.id, label: resolveToolText(tool.label, node), title: resolveToolText(tool.title, node), icon: tool.icon(node), active: tool.active?.(node), onClick: () => tool.run(node, handlers), })); } export function normalizeImageQuickToolIds(value: unknown[]) { const allIds: ImageQuickToolId[] = [...defaultBaseToolIds, ...imageToolDefinitions.map((tool) => tool.id)]; const ids = new Set(allIds); return allIds.filter((id) => value.includes(id) && ids.has(id)); } export function readImageQuickToolsConfig(value: unknown): ImageQuickToolId[] { return Array.isArray(value) ? normalizeImageQuickToolIds(value).slice(0, 7) : defaultImageQuickToolIds; } function resolveToolText(value: string | ((node: CanvasNodeData) => string), node: CanvasNodeData) { return typeof value === "function" ? value(node) : value; }