feat(*): 画布创作 - 支持文本多模态生成并优化媒体节点、连接线与视频预览

Fixes #59
This commit is contained in:
ddCat
2026-07-31 19:44:02 +08:00
parent 8ce97f267b
commit 14da5e8a19
29 changed files with 231 additions and 111 deletions

View File

@@ -1,7 +1,15 @@
export function fitNodeSize(width: number, height: number, maxWidth = 640, maxHeight = 640) {
import { CanvasNodeType, type CanvasNodeData } from "@/types/canvas";
export const MEDIA_NODE_MIN_SIZE = { width: 300, height: 220 } as const;
export const VIDEO_NODE_MAX_SIZE = { width: 480, height: 480 } as const;
export function fitNodeSize(width: number, height: number, maxWidth = 640, maxHeight = 640, minWidth = MEDIA_NODE_MIN_SIZE.width, minHeight = MEDIA_NODE_MIN_SIZE.height) {
const w = Math.max(1, width);
const h = Math.max(1, height);
const scale = Math.min(1, maxWidth / w, maxHeight / h);
// 媒体节点既要保留原始比例,也要给生成状态、操作按钮留下稳定的可读空间。
const preferredScale = Math.min(1, maxWidth / w, maxHeight / h);
const minimumScale = Math.max(minWidth / w, minHeight / h);
const scale = Math.max(preferredScale, minimumScale);
return { width: w * scale, height: h * scale };
}
@@ -16,5 +24,23 @@ export function nodeSizeFromRatio(size: string, baseWidth: number, baseHeight: n
if (!Number.isFinite(width) || !Number.isFinite(height) || width <= 0 || height <= 0) return null;
const ratio = width / Math.max(1, height);
if (ratio < 0.25 || ratio > 4) return { width: baseWidth, height: baseHeight };
return ratio >= baseWidth / baseHeight ? { width: baseWidth, height: baseWidth / ratio } : { width: baseHeight * ratio, height: baseHeight };
const candidateSize = ratio >= baseWidth / baseHeight ? { width: baseWidth, height: baseWidth / ratio } : { width: baseHeight * ratio, height: baseHeight };
return fitNodeSize(candidateSize.width, candidateSize.height, baseWidth, baseHeight);
}
export function ensureMediaNodeMinimumSize(node: CanvasNodeData) {
if (node.type !== CanvasNodeType.Image && node.type !== CanvasNodeType.Video) return node;
if (node.width >= MEDIA_NODE_MIN_SIZE.width && node.height >= MEDIA_NODE_MIN_SIZE.height) return node;
const scale = Math.max(1, MEDIA_NODE_MIN_SIZE.width / Math.max(1, node.width), MEDIA_NODE_MIN_SIZE.height / Math.max(1, node.height));
const width = node.width * scale;
const height = node.height * scale;
return {
...node,
position: {
x: node.position.x + node.width / 2 - width / 2,
y: node.position.y + node.height / 2 - height / 2,
},
width,
height,
};
}