feat: refine node deletion logic to protect only start nodes

This commit is contained in:
mlogclub
2026-06-28 21:24:54 +08:00
parent c7555d67c5
commit 77dda5fd9c
6 changed files with 13 additions and 7 deletions
+1 -1
Submodule docs updated: fe29cfb3cc...b6d08c0964
@@ -64,7 +64,7 @@ export function useFlowgramEditorProps({
}, },
canDeleteNode: (_ctx, node) => { canDeleteNode: (_ctx, node) => {
const type = String(node.flowNodeType ?? "") const type = String(node.flowNodeType ?? "")
return type !== "start" && type !== "end" return type !== "start"
}, },
canDeleteLine: () => !readonly, canDeleteLine: () => !readonly,
onContentChange: (ctx) => { onContentChange: (ctx) => {
@@ -42,8 +42,8 @@ export function buildFlowgramNodeRegistries(nodeSpecs: AIWorkflowNodeSpec[]): Wo
meta: { meta: {
defaultExpanded: true, defaultExpanded: true,
isStart: spec.type === "start", isStart: spec.type === "start",
deleteDisable: spec.type === "start" || spec.type === "end", deleteDisable: spec.type === "start",
copyDisable: spec.type === "start" || spec.type === "end", copyDisable: spec.type === "start",
defaultPorts: defaultPortsForNodeType(spec.type), defaultPorts: defaultPortsForNodeType(spec.type),
}, },
formMeta: { formMeta: {
@@ -208,7 +208,9 @@ function WorkflowEditorInner({
useEffect(() => { useEffect(() => {
const disposable = selectService.onSelectionChanged(() => { const disposable = selectService.onSelectionChanged(() => {
const selectedNode = selectService.selectedNodes[0] const selectedNode = selectService.selectedNodes.length === 1
? selectService.selectedNodes[0]
: null
onSelectNode(selectedNode?.id ?? "") onSelectNode(selectedNode?.id ?? "")
}) })
return () => disposable.dispose() return () => disposable.dispose()
@@ -252,7 +252,7 @@ describe("workflow definition mutations", () => {
}) })
}) })
it("deletes normal nodes and related edges while keeping start and end protected", async () => { it("deletes non-start nodes and related edges while keeping start protected", async () => {
const { deleteWorkflowNode } = await loadModule() const { deleteWorkflowNode } = await loadModule()
const definition = { const definition = {
schemaVersion: 2, schemaVersion: 2,
@@ -270,6 +270,10 @@ describe("workflow definition mutations", () => {
const protectedDefinition = deleteWorkflowNode(definition, "start_1") const protectedDefinition = deleteWorkflowNode(definition, "start_1")
assert.deepEqual(protectedDefinition, definition) assert.deepEqual(protectedDefinition, definition)
const withoutEnd = deleteWorkflowNode(definition, "end_1")
assert.deepEqual(withoutEnd.nodes.map((node) => node.id), ["start_1", "reply_1"])
assert.deepEqual(withoutEnd.edges, [workflowEdge("start_1", "reply_1")])
}) })
it("upserts and deletes condition branches in node config", async () => { it("upserts and deletes condition branches in node config", async () => {
@@ -199,7 +199,7 @@ export function deleteWorkflowNode(
nodeId: string nodeId: string
): AIWorkflowDefinition { ): AIWorkflowDefinition {
const node = definition.nodes.find((item) => item.id === nodeId) const node = definition.nodes.find((item) => item.id === nodeId)
if (!node || node.type === "start" || node.type === "end") { if (!node || node.type === "start") {
return definition return definition
} }
return { return {