feat: enhance flowgram editor with condition node handling, port normalization, and UI improvements

This commit is contained in:
mlogclub
2026-06-28 00:08:55 +08:00
parent 0d568ebf87
commit 2bde314820
5 changed files with 394 additions and 111 deletions
@@ -166,13 +166,16 @@ export function createWorkflowNodeFromSpec(
position: WorkflowNodePosition
): AIWorkflowDefinition["nodes"][number] {
const id = uniqueNodeId(existingNodes, spec.type)
const defaultConfig = spec.type === "condition"
? { branches: [{ id: "default", name: "默认分支", targetNodeId: "", default: true }] }
: {}
return {
id,
type: spec.type,
meta: { position },
data: {
title: spec.title || spec.type,
config: {},
config: defaultConfig,
inputsValues: spec.defaultInputs ?? {},
},
}
@@ -247,6 +250,68 @@ export function deleteConditionBranch(
})
}
export function normalizeConditionPortsForFlowgram(
definition: AIWorkflowDefinition
): AIWorkflowDefinition {
return {
...definition,
nodes: definition.nodes.map((node) => {
if (node.type !== "condition") {
return node
}
const config = normalizeNodeConfig(node.data?.config)
const branches = ensureConditionBranches(config.branches ?? [])
return {
...node,
data: {
...(node.data ?? {}),
config: { ...config, branches },
portKeys: branches.map((branch) => branch.id),
ports: branches.map((branch) => branch.id),
},
}
}),
edges: definition.edges.map((edge) => {
const source = definition.nodes.find((node) => node.id === edge.sourceNodeID)
if (!source || source.type !== "condition" || edge.sourcePortID) {
return edge
}
const branch = findConditionBranchForTarget(source, edge.targetNodeID)
return branch ? { ...edge, sourcePortID: branch.id } : edge
}),
}
}
export function syncConditionBranchTargetsFromEdges(
definition: AIWorkflowDefinition
): AIWorkflowDefinition {
return {
...definition,
nodes: definition.nodes.map((node) => {
if (node.type !== "condition") {
return node
}
const config = normalizeNodeConfig(node.data?.config)
const branches = ensureConditionBranches(config.branches ?? [])
const nextBranches = branches.map((branch) => {
const edge = definition.edges.find((item) => (
item.sourceNodeID === node.id && item.sourcePortID === branch.id
))
return edge ? { ...branch, targetNodeId: edge.targetNodeID } : branch
})
return {
...node,
data: {
...(node.data ?? {}),
config: { ...config, branches: nextBranches },
portKeys: nextBranches.map((branch) => branch.id),
ports: nextBranches.map((branch) => branch.id),
},
}
}),
}
}
export function normalizeNodeConfig(config: unknown): WorkflowNodeConfig {
if (!config || typeof config !== "object" || Array.isArray(config)) {
return {}
@@ -263,6 +328,31 @@ export function normalizeNodeConfig(config: unknown): WorkflowNodeConfig {
} as WorkflowNodeConfig
}
function ensureConditionBranches(branches: WorkflowConditionBranch[]) {
if (branches.some((branch) => branch.default)) {
return orderConditionBranches(branches)
}
return orderConditionBranches([
...branches,
{ id: "default", name: "默认分支", targetNodeId: "", default: true },
])
}
function orderConditionBranches(branches: WorkflowConditionBranch[]) {
return [
...branches.filter((branch) => !branch.default),
...branches.filter((branch) => branch.default).slice(0, 1),
]
}
function findConditionBranchForTarget(
node: AIWorkflowDefinition["nodes"][number],
targetNodeId: string
) {
const branches = ensureConditionBranches(normalizeNodeConfig(node.data?.config).branches ?? [])
return branches.find((branch) => branch.targetNodeId === targetNodeId)
}
export function createConditionBranchID(existingBranches: WorkflowConditionBranch[]) {
const existingIDs = new Set(existingBranches.map((branch) => branch.id))
for (let index = 1; index < 10000; index++) {