Files
imagegen-tools/src/lib/canvas/node-factory.ts
T

170 lines
3.8 KiB
TypeScript

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<HistoryItem, "size">) {
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,
};
}