refactor(canvas): 生成配置 - 收敛插件工作流并移除系统模型分支

This commit is contained in:
ddCat committed 2026-08-26 16:28:45 +08:00
1 parent 9bc5512140
commit 61b332583c
15 files changed
+227 -246

No files matched your search

@@ -401,7 +401,9 @@ export function generationWorkflowMetadata(config: AiConfig): Pick<CanvasNodeMet
export function buildGenerationConfig(config: AiConfig, node: CanvasNodeData | undefined, mode: CanvasNodeGenerationMode, requirements?: ModelRequirements): AiConfig {
// 只有独立 Config 节点读取工作流元数据;普通图片/视频/音频节点始终按基础模型生成。
const workflowProvider = mode !== "text" && node?.type === CanvasNodeType.Config ? resolveCanvasWorkflowProvider(node.metadata) : "model";
const workflowProvider = mode !== "text" && node?.type === CanvasNodeType.Config
? resolveCanvasWorkflowProvider(node.metadata) === "comfyui" ? "comfyui" : "runninghub"
: "model";
const defaultModel = mode === "image" ? config.imageModel : mode === "video" ? config.videoModel : mode === "audio" ? config.audioModel : config.textModel;
const fallbackModel = mode === "image" ? defaultConfig.imageModel : mode === "video" ? defaultConfig.videoModel : mode === "audio" ? defaultConfig.audioModel : defaultConfig.textModel;
const storedModel = resolveCanvasGenerationModel(config, node?.metadata?.model, mode);
@@ -542,12 +544,13 @@ export function supportsVideoReferenceAudio(config: AiConfig) {
}
export function resetInterruptedGeneration(nodes: CanvasNodeData[]) {
const configWidth = NODE_DEFAULT_SIZE[CanvasNodeType.Config].width;
const configHeight = NODE_DEFAULT_SIZE[CanvasNodeType.Config].height;
return nodes.map((node) => {
const mediaNode = ensureMediaNodeMinimumSize(node);
const resizedNode =
mediaNode.type === CanvasNodeType.Config && mediaNode.height < configHeight
? { ...mediaNode, height: configHeight }
mediaNode.type === CanvasNodeType.Config && (mediaNode.width < configWidth || mediaNode.height < configHeight)
? { ...mediaNode, width: Math.max(mediaNode.width, configWidth), height: Math.max(mediaNode.height, configHeight) }
: mediaNode.type === CanvasNodeType.Script && mediaNode.height < NODE_DEFAULT_SIZE[CanvasNodeType.Script].height
? { ...mediaNode, height: NODE_DEFAULT_SIZE[CanvasNodeType.Script].height }
: mediaNode;
@@ -1,24 +1,14 @@
import { Clapperboard, Folder, FolderOpen, Image as ImageIcon, Layers3, Music2, Palette, PanelTop, Pencil, Type, UploadCloud, UserRound, Video, Workflow } from "lucide-react";
import { Folder, FolderOpen, Layers3, Palette, UploadCloud, UserRound, Workflow } from "lucide-react";
import { getNodeIcon, getNodeLabel } from "@/lib/canvas/node-registry";
import { registerAddNodeMenuCommands, type AddNodeMenuCommand } from "@/lib/canvas/tool-registry";
import { PortraitClearanceIcon } from "@/components/canvas/portrait-clearance/portrait-clearance-icon";
import { PORTRAIT_CLEARANCE_NODE_TYPE, PORTRAIT_CLEARANCE_PLUGIN_ID } from "@/lib/portrait-clearance/contracts";
import { CanvasNodeType, type CanvasNodeTypeId } from "@/types/canvas";
import { CanvasNodeType } from "@/types/canvas";
/** 真正创建节点的命令,文案与图标统一取自节点注册表。 */
function nodeCommand(type: CanvasNodeType, rest: Omit<AddNodeMenuCommand, "id" | "label" | "icon" | "section">): AddNodeMenuCommand {
return { id: type, label: getNodeLabel(type), icon: getNodeIcon(type), section: "node", ...rest };
}
/**
* 扩展节点命令:落在独立的「展示与加工」分区,不挤占上方调好的四列创作网格。
* 创建逻辑一律是 createNode(type),所以统一走 onAddExtensionNode 而不是各开一个 handler。
*/
function extensionCommand(type: CanvasNodeTypeId, defaultOrder: number): AddNodeMenuCommand {
return { id: type, label: getNodeLabel(type), icon: getNodeIcon(type), section: "extension", defaultOrder, run: (ctx) => ctx.handlers.onAddExtensionNode(type) };
}
export const addNodeMenuCommands: AddNodeMenuCommand[] = [
// 项目级动作不占用节点网格,保证 8 个节点稳定保持四列两行。
{ id: "style", label: "项目画风", icon: <Palette />, section: "project", defaultOrder: 10, applicable: (ctx) => !ctx.isProjectLinked, run: (ctx) => ctx.handlers.onChooseStyle() },
@@ -33,25 +23,8 @@ export const addNodeMenuCommands: AddNodeMenuCommand[] = [
// 导演台落在节点分区,但它开的是导演工作台、不是某种画布节点,故不走注册表。
{ id: "director", label: "导演台", icon: <Layers3 />, badge: "3D", section: "node", defaultOrder: 70, applicable: (ctx) => ctx.workspaceMode !== "simple", run: (ctx) => ctx.handlers.onOpenDirector() },
nodeCommand(CanvasNodeType.Audio, { defaultOrder: 80, applicable: (ctx) => ctx.workspaceMode !== "simple", run: (ctx) => ctx.handlers.onAddAudio() }),
// 展示与加工(扩展节点)
extensionCommand(CanvasNodeType.Markdown, 10),
extensionCommand(CanvasNodeType.Svg, 20),
extensionCommand(CanvasNodeType.Html, 30),
extensionCommand(CanvasNodeType.Panorama, 40),
extensionCommand(CanvasNodeType.Compare, 50),
extensionCommand(CanvasNodeType.Chart, 60),
extensionCommand(CanvasNodeType.ColorGrade, 70),
// 云端和本地工作流共用独立配置节点,不进入基础模型节点的渠道选择。
{ id: "workflow", label: "工作流", icon: <Workflow />, section: "workflow", defaultOrder: 10, applicable: (ctx) => ctx.workspaceMode !== "simple", run: (ctx) => ctx.handlers.onAddWorkflow() },
{
id: PORTRAIT_CLEARANCE_NODE_TYPE,
label: getNodeLabel(PORTRAIT_CLEARANCE_NODE_TYPE),
icon: <PortraitClearanceIcon />,
section: "extension",
defaultOrder: 80,
applicable: (ctx) => Boolean(ctx.enabledPluginIds?.has(PORTRAIT_CLEARANCE_PLUGIN_ID)),
run: (ctx) => ctx.handlers.onAddExtensionNode(PORTRAIT_CLEARANCE_NODE_TYPE),
},
// 导入资源
{ id: "upload", label: "上传文件", icon: <UploadCloud />, section: "resource", defaultOrder: 10, run: (ctx) => ctx.handlers.onUpload() },
{ id: "project-character", label: "添加角色卡", icon: <UserRound />, section: "resource", defaultOrder: 20, applicable: (ctx) => ctx.isProjectLinked, run: (ctx) => ctx.handlers.onOpenProjectCharacters() },
@@ -185,7 +185,7 @@ export type AddNodeMenuCommand = {
icon: ReactNode;
badge?: string;
// extension:展示与加工类扩展节点。单独一区,避免挤散 node 区调好的四列网格。
section: "node" | "extension" | "workflow" | "project" | "resource";
section: "node" | "workflow" | "project" | "resource";
defaultOrder: number;
applicable?: (ctx: AddNodeMenuContext) => boolean;
run: (ctx: AddNodeMenuContext) => void;