Files
ai-agent/web/app/dashboard/ai-workflows/_components/flowgram-node-registries.tsx
T

113 lines
3.1 KiB
TypeScript

import { Field, type WorkflowNodeRegistry, useNodeRender } from "@flowgram.ai/free-layout-editor"
import type { AIWorkflowNodeSpec } from "@/lib/api/admin"
import { WorkflowConditionNodeContent } from "./workflow-condition-node-content"
import { WorkflowNodeCard } from "./workflow-node-card"
import { WorkflowSimpleNodeContent } from "./workflow-simple-node-content"
export function buildFlowgramNodeRegistries(nodeSpecs: AIWorkflowNodeSpec[]): WorkflowNodeRegistry[] {
const seen = new Set<string>()
const specs = nodeSpecs.length > 0
? nodeSpecs
: [
{
type: "start",
title: "开始",
description: "流程入口",
riskLevel: "low" as const,
interruptible: false,
requiresConfirmationPredecessor: false,
},
{
type: "end",
title: "结束",
description: "流程结束",
riskLevel: "low" as const,
interruptible: false,
requiresConfirmationPredecessor: false,
},
]
return specs
.filter((spec) => {
if (!spec.type || seen.has(spec.type)) {
return false
}
seen.add(spec.type)
return true
})
.map((spec) => ({
type: spec.type,
meta: {
defaultExpanded: true,
isStart: spec.type === "start",
deleteDisable: spec.type === "start" || spec.type === "end",
copyDisable: spec.type === "start" || spec.type === "end",
defaultPorts: defaultPortsForNodeType(spec.type),
},
formMeta: {
render: () => (
<FlowgramNodeForm
nodeType={spec.type}
fallbackTitle={spec.title || spec.type}
spec={spec}
/>
),
},
}))
}
function FlowgramNodeForm({
nodeType,
fallbackTitle,
spec,
}: {
nodeType: string
fallbackTitle: string
spec?: AIWorkflowNodeSpec
}) {
const { node, selected } = useNodeRender()
const nodeId = String(node.id ?? "")
return (
<Field<string> name="title">
{({ field }) => (
<WorkflowNodeCard
nodeType={nodeType}
title={field.value || fallbackTitle}
spec={spec}
selected={selected}
width={nodeType === "condition" ? "wide" : "normal"}
>
{nodeType === "condition" ? (
<Field<Record<string, unknown>> name="config">
{({ field: configField }) => (
<WorkflowConditionNodeContent
configValue={configField.value}
nodeId={nodeId}
onChange={configField.onChange}
/>
)}
</Field>
) : (
<WorkflowSimpleNodeContent spec={spec} />
)}
</WorkflowNodeCard>
)}
</Field>
)
}
function defaultPortsForNodeType(type: string) {
if (type === "start") {
return [{ type: "output" as const }]
}
if (type === "end") {
return [{ type: "input" as const }]
}
if (type === "condition") {
return [{ type: "input" as const }]
}
return [{ type: "input" as const }, { type: "output" as const }]
}