import type { CanvasConfigNodeMetadata, CanvasImageNodeMetadata, CanvasNode, CanvasNodeType, CanvasPosition, } from "@/lib/canvas/types"; import type { HistoryItem } from "@/lib/image-workflow"; export const DEFAULT_NODE_WIDTH = 280; export const DEFAULT_NODE_HEIGHT = 170; export const DEFAULT_IMAGE_WIDTH = 320; export const DEFAULT_IMAGE_HEIGHT = 240; export const defaultCanvasConfig: CanvasConfigNodeMetadata = { model: "gpt-image-2-2k", size: "1024x1024", quality: "high", outputFormat: "png", preserveIdentity: true, }; export function createCanvasNode( type: CanvasNodeType, position: CanvasPosition, ): CanvasNode { const id = crypto.randomUUID(); if (type === "prompt") { return { id, type, title: "提示词", position, width: DEFAULT_NODE_WIDTH, height: DEFAULT_NODE_HEIGHT, metadata: { prompt: "描述你想生成的画面...", }, }; } if (type === "config") { return { id, type, title: "生成配置", position, width: DEFAULT_NODE_WIDTH, height: DEFAULT_NODE_HEIGHT, metadata: { ...defaultCanvasConfig }, }; } return { id, type, title: "参考图片", position, width: DEFAULT_IMAGE_WIDTH, height: DEFAULT_IMAGE_HEIGHT, metadata: { imageUrl: "", mode: "reference", status: "idle", }, }; } export function createImageNodeFromHistory( item: HistoryItem, position: CanvasPosition, ): CanvasNode { const size = getImageNodeSize(item); return { id: crypto.randomUUID(), type: "image", title: "历史图片", position, width: size.width, height: size.height, metadata: { historyId: item.id, imageUrl: item.imageUrl, mode: "reference", prompt: item.prompt, model: item.model, size: item.size, quality: item.quality, outputFormat: item.outputFormat, status: "success", }, }; } export function createImageNodeFromUpload( file: File, imageUrl: string, position: CanvasPosition, ): CanvasNode { return { id: crypto.randomUUID(), type: "image", title: file.name || "上传图片", position, width: DEFAULT_IMAGE_WIDTH, height: DEFAULT_IMAGE_HEIGHT, metadata: { imageUrl, mode: "reference", status: "success", }, }; } export function createImageResultNode( result: HistoryItem, position: CanvasPosition, ): CanvasNode { const size = getImageNodeSize(result); const metadata: CanvasImageNodeMetadata = { imageUrl: result.imageUrl, mode: "result", historyId: result.id, prompt: result.prompt, model: result.model, size: result.size, quality: result.quality, outputFormat: result.outputFormat, status: "success", }; return { id: crypto.randomUUID(), type: "image", title: "生成结果", position, width: size.width, height: size.height, metadata, }; } function getImageNodeSize(item: Pick) { const match = /^(\d+)x(\d+)$/.exec(item.size || ""); if (!match) { return { width: DEFAULT_IMAGE_WIDTH, height: DEFAULT_IMAGE_HEIGHT }; } const naturalWidth = Number(match[1]); const naturalHeight = Number(match[2]); if (!naturalWidth || !naturalHeight) { return { width: DEFAULT_IMAGE_WIDTH, height: DEFAULT_IMAGE_HEIGHT }; } const maxSide = 360; const minSide = 180; const scale = Math.min(maxSide / naturalWidth, maxSide / naturalHeight, 1); const imageWidth = Math.max(minSide, Math.round(naturalWidth * scale)); const imageHeight = Math.max(minSide, Math.round(naturalHeight * scale)); const padding = 16; const headerHeight = 34; const gap = 12; return { width: imageWidth + padding * 2, height: imageHeight + padding * 2 + headerHeight + gap, }; }