fix(notes): ghost edges (#2970)

* fix(notes): ghost edges

* fix deployed state fallback

* fallback

* remove UI level checks

* annotation missing from autoconnect source check
This commit is contained in:
Vikhyath Mondreti
2026-01-24 11:20:28 -08:00
committed by GitHub
parent 8574e6c71f
commit 8429040921
3 changed files with 45 additions and 54 deletions

View File

@@ -81,6 +81,7 @@ import { useWorkflowDiffStore } from '@/stores/workflow-diff/store'
import { useWorkflowRegistry } from '@/stores/workflows/registry/store'
import { getUniqueBlockName, prepareBlockState } from '@/stores/workflows/utils'
import { useWorkflowStore } from '@/stores/workflows/workflow/store'
import type { BlockState } from '@/stores/workflows/workflow/types'
/** Lazy-loaded components for non-critical UI that can load after initial render */
const LazyChat = lazy(() =>
@@ -535,8 +536,7 @@ const WorkflowContent = React.memo(() => {
return edgesToFilter.filter((edge) => {
const sourceBlock = blocks[edge.source]
const targetBlock = blocks[edge.target]
if (!sourceBlock || !targetBlock) return false
return !isAnnotationOnlyBlock(sourceBlock.type) && !isAnnotationOnlyBlock(targetBlock.type)
return Boolean(sourceBlock && targetBlock)
})
}, [edges, isShowingDiff, isDiffReady, diffAnalysis, blocks])
@@ -1097,6 +1097,13 @@ const WorkflowContent = React.memo(() => {
[collaborativeBatchRemoveEdges]
)
const isAutoConnectSourceCandidate = useCallback((block: BlockState): boolean => {
if (!block.enabled) return false
if (block.type === 'response') return false
if (isAnnotationOnlyBlock(block.type)) return false
return true
}, [])
/** Finds the closest block to a position for auto-connect. */
const findClosestOutput = useCallback(
(newNodePosition: { x: number; y: number }): BlockData | null => {
@@ -1109,8 +1116,7 @@ const WorkflowContent = React.memo(() => {
position: { x: number; y: number }
distanceSquared: number
} | null>((acc, [id, block]) => {
if (!block.enabled) return acc
if (block.type === 'response') return acc
if (!isAutoConnectSourceCandidate(block)) return acc
const node = nodeIndex.get(id)
if (!node) return acc
@@ -1140,7 +1146,7 @@ const WorkflowContent = React.memo(() => {
position: closest.position,
}
},
[blocks, getNodes, getNodeAnchorPosition, isPointInLoopNode]
[blocks, getNodes, getNodeAnchorPosition, isPointInLoopNode, isAutoConnectSourceCandidate]
)
/** Determines the appropriate source handle based on block type. */
@@ -1208,7 +1214,8 @@ const WorkflowContent = React.memo(() => {
position: { x: number; y: number }
distanceSquared: number
} | null>((acc, block) => {
if (block.type === 'response') return acc
const blockState = blocks[block.id]
if (!blockState || !isAutoConnectSourceCandidate(blockState)) return acc
const distanceSquared =
(block.position.x - targetPosition.x) ** 2 + (block.position.y - targetPosition.y) ** 2
if (!acc || distanceSquared < acc.distanceSquared) {
@@ -1225,7 +1232,7 @@ const WorkflowContent = React.memo(() => {
}
: undefined
},
[]
[blocks, isAutoConnectSourceCandidate]
)
/**
@@ -1241,8 +1248,6 @@ const WorkflowContent = React.memo(() => {
position: { x: number; y: number },
targetBlockId: string,
options: {
blockType: string
enableTriggerMode?: boolean
targetParentId?: string | null
existingChildBlocks?: { id: string; type: string; position: { x: number; y: number } }[]
containerId?: string
@@ -1250,17 +1255,6 @@ const WorkflowContent = React.memo(() => {
): Edge | undefined => {
if (!autoConnectRef.current) return undefined
// Don't auto-connect starter or annotation-only blocks
if (options.blockType === 'starter' || isAnnotationOnlyBlock(options.blockType)) {
return undefined
}
// Check if target is a trigger block
const targetBlockConfig = getBlock(options.blockType)
const isTargetTrigger =
options.enableTriggerMode || targetBlockConfig?.category === 'triggers'
if (isTargetTrigger) return undefined
// Case 1: Adding block inside a container with existing children
if (options.existingChildBlocks && options.existingChildBlocks.length > 0) {
const closestBlock = findClosestBlockInSet(options.existingChildBlocks, position)
@@ -1368,7 +1362,6 @@ const WorkflowContent = React.memo(() => {
const name = getUniqueBlockName(baseName, blocks)
const autoConnectEdge = tryCreateAutoConnectEdge(position, id, {
blockType: data.type,
targetParentId: null,
})
@@ -1439,8 +1432,6 @@ const WorkflowContent = React.memo(() => {
.map((b) => ({ id: b.id, type: b.type, position: b.position }))
const autoConnectEdge = tryCreateAutoConnectEdge(relativePosition, id, {
blockType: data.type,
enableTriggerMode: data.enableTriggerMode,
targetParentId: containerInfo.loopId,
existingChildBlocks,
containerId: containerInfo.loopId,
@@ -1469,8 +1460,6 @@ const WorkflowContent = React.memo(() => {
if (checkTriggerConstraints(data.type)) return
const autoConnectEdge = tryCreateAutoConnectEdge(position, id, {
blockType: data.type,
enableTriggerMode: data.enableTriggerMode,
targetParentId: null,
})
@@ -1526,7 +1515,6 @@ const WorkflowContent = React.memo(() => {
const name = getUniqueBlockName(baseName, blocks)
const autoConnectEdge = tryCreateAutoConnectEdge(basePosition, id, {
blockType: type,
targetParentId: null,
})
@@ -1562,8 +1550,6 @@ const WorkflowContent = React.memo(() => {
const name = getUniqueBlockName(baseName, blocks)
const autoConnectEdge = tryCreateAutoConnectEdge(basePosition, id, {
blockType: type,
enableTriggerMode,
targetParentId: null,
})
@@ -2364,24 +2350,6 @@ const WorkflowContent = React.memo(() => {
if (!sourceNode || !targetNode) return
// Prevent connections to/from annotation-only blocks (non-executable)
if (
isAnnotationOnlyBlock(sourceNode.data?.type) ||
isAnnotationOnlyBlock(targetNode.data?.type)
) {
return
}
// Prevent incoming connections to trigger blocks (webhook, schedule, etc.)
if (targetNode.data?.config?.category === 'triggers') {
return
}
// Prevent incoming connections to starter blocks (still keep separate for backward compatibility)
if (targetNode.data?.type === 'starter') {
return
}
// Get parent information (handle container start node case)
const sourceParentId =
blocks[sourceNode.id]?.data?.parentId ||
@@ -2787,7 +2755,6 @@ const WorkflowContent = React.memo(() => {
.map((b) => ({ id: b.id, type: b.type, position: b.position }))
const autoConnectEdge = tryCreateAutoConnectEdge(relativePositionBefore, node.id, {
blockType: node.data?.type || '',
targetParentId: potentialParentId,
existingChildBlocks,
containerId: potentialParentId,

View File

@@ -352,7 +352,7 @@ export function SocketProvider({ children, user }: SocketProviderProps) {
})
})
useWorkflowStore.setState({
useWorkflowStore.getState().replaceWorkflowState({
blocks: workflowState.blocks || {},
edges: workflowState.edges || [],
loops: workflowState.loops || {},