mirror of
https://github.com/simstudioai/sim.git
synced 2026-01-14 09:27:58 -05:00
Compare commits
4 Commits
fix/copilo
...
fix/dups
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
79621d788e | ||
|
|
67686eac07 | ||
|
|
96a2979bee | ||
|
|
c1a92d3be9 |
@@ -356,6 +356,9 @@ const WorkflowContent = React.memo(() => {
|
||||
/** Stores source node/handle info when a connection drag starts for drop-on-block detection. */
|
||||
const connectionSourceRef = useRef<{ nodeId: string; handleId: string } | null>(null)
|
||||
|
||||
/** Tracks whether onConnect successfully handled the connection (ReactFlow pattern). */
|
||||
const connectionCompletedRef = useRef(false)
|
||||
|
||||
/** Stores start positions for multi-node drag undo/redo recording. */
|
||||
const multiNodeDragStartRef = useRef<Map<string, { x: number; y: number; parentId?: string }>>(
|
||||
new Map()
|
||||
@@ -2214,7 +2217,8 @@ const WorkflowContent = React.memo(() => {
|
||||
)
|
||||
|
||||
/**
|
||||
* Captures the source handle when a connection drag starts
|
||||
* Captures the source handle when a connection drag starts.
|
||||
* Resets connectionCompletedRef to track if onConnect handles this connection.
|
||||
*/
|
||||
const onConnectStart = useCallback((_event: any, params: any) => {
|
||||
const handleId: string | undefined = params?.handleId
|
||||
@@ -2223,6 +2227,7 @@ const WorkflowContent = React.memo(() => {
|
||||
nodeId: params?.nodeId,
|
||||
handleId: params?.handleId,
|
||||
}
|
||||
connectionCompletedRef.current = false
|
||||
}, [])
|
||||
|
||||
/** Handles new edge connections with container boundary validation. */
|
||||
@@ -2283,6 +2288,7 @@ const WorkflowContent = React.memo(() => {
|
||||
isInsideContainer: true,
|
||||
},
|
||||
})
|
||||
connectionCompletedRef.current = true
|
||||
return
|
||||
}
|
||||
|
||||
@@ -2311,6 +2317,7 @@ const WorkflowContent = React.memo(() => {
|
||||
}
|
||||
: undefined,
|
||||
})
|
||||
connectionCompletedRef.current = true
|
||||
}
|
||||
},
|
||||
[addEdge, getNodes, blocks]
|
||||
@@ -2319,8 +2326,9 @@ const WorkflowContent = React.memo(() => {
|
||||
/**
|
||||
* Handles connection drag end. Detects if the edge was dropped over a block
|
||||
* and automatically creates a connection to that block's target handle.
|
||||
* Only creates a connection if ReactFlow didn't already handle it (e.g., when
|
||||
* dropping on the block body instead of a handle).
|
||||
*
|
||||
* Uses connectionCompletedRef to check if onConnect already handled this connection
|
||||
* (ReactFlow pattern for distinguishing handle-to-handle vs handle-to-body drops).
|
||||
*/
|
||||
const onConnectEnd = useCallback(
|
||||
(event: MouseEvent | TouchEvent) => {
|
||||
@@ -2332,6 +2340,12 @@ const WorkflowContent = React.memo(() => {
|
||||
return
|
||||
}
|
||||
|
||||
// If onConnect already handled this connection, skip (handle-to-handle case)
|
||||
if (connectionCompletedRef.current) {
|
||||
connectionSourceRef.current = null
|
||||
return
|
||||
}
|
||||
|
||||
// Get cursor position in flow coordinates
|
||||
const clientPos = 'changedTouches' in event ? event.changedTouches[0] : event
|
||||
const flowPosition = screenToFlowPosition({
|
||||
@@ -2342,12 +2356,7 @@ const WorkflowContent = React.memo(() => {
|
||||
// Find node under cursor
|
||||
const targetNode = findNodeAtPosition(flowPosition)
|
||||
|
||||
// Create connection if valid target found AND edge doesn't already exist
|
||||
// ReactFlow's onConnect fires first when dropping on a handle, so we check
|
||||
// if that connection already exists to avoid creating duplicates.
|
||||
// IMPORTANT: We must read directly from the store (not React state) because
|
||||
// the store update from ReactFlow's onConnect may not have triggered a
|
||||
// React re-render yet when this callback runs (typically 1-2ms later).
|
||||
// Create connection if valid target found (handle-to-body case)
|
||||
if (targetNode && targetNode.id !== source.nodeId) {
|
||||
const currentEdges = useWorkflowStore.getState().edges
|
||||
const edgeAlreadyExists = currentEdges.some(
|
||||
|
||||
@@ -24,7 +24,9 @@ export function hasWorkflowChanged(
|
||||
deployedState: WorkflowState | null
|
||||
): boolean {
|
||||
// If no deployed state exists, then the workflow has changed
|
||||
if (!deployedState) return true
|
||||
if (!deployedState) {
|
||||
return true
|
||||
}
|
||||
|
||||
// 1. Compare edges (connections between blocks)
|
||||
const currentEdges = currentState.edges || []
|
||||
|
||||
@@ -197,9 +197,10 @@ export function normalizeEdge(edge: Edge): NormalizedEdge {
|
||||
}
|
||||
|
||||
/**
|
||||
* Sorts edges for consistent comparison
|
||||
* Sorts and deduplicates edges for consistent comparison.
|
||||
* Deduplication handles legacy data that may contain duplicate edges.
|
||||
* @param edges - Array of edges to sort
|
||||
* @returns Sorted array of normalized edges
|
||||
* @returns Sorted array of unique normalized edges
|
||||
*/
|
||||
export function sortEdges(
|
||||
edges: Array<{
|
||||
@@ -214,7 +215,13 @@ export function sortEdges(
|
||||
target: string
|
||||
targetHandle?: string | null
|
||||
}> {
|
||||
return [...edges].sort((a, b) =>
|
||||
const uniqueEdges = new Map<string, (typeof edges)[number]>()
|
||||
for (const edge of edges) {
|
||||
const key = `${edge.source}-${edge.sourceHandle ?? 'null'}-${edge.target}-${edge.targetHandle ?? 'null'}`
|
||||
uniqueEdges.set(key, edge)
|
||||
}
|
||||
|
||||
return Array.from(uniqueEdges.values()).sort((a, b) =>
|
||||
`${a.source}-${a.sourceHandle}-${a.target}-${a.targetHandle}`.localeCompare(
|
||||
`${b.source}-${b.sourceHandle}-${b.target}-${b.targetHandle}`
|
||||
)
|
||||
|
||||
@@ -498,8 +498,6 @@ export const useWorkflowStore = create<WorkflowStore>()(
|
||||
const currentEdges = get().edges
|
||||
const newEdges = [...currentEdges]
|
||||
const existingEdgeIds = new Set(currentEdges.map((e) => e.id))
|
||||
// Track existing connections to prevent duplicates (same source->target)
|
||||
const existingConnections = new Set(currentEdges.map((e) => `${e.source}->${e.target}`))
|
||||
|
||||
for (const edge of edges) {
|
||||
// Skip if edge ID already exists
|
||||
@@ -508,10 +506,6 @@ export const useWorkflowStore = create<WorkflowStore>()(
|
||||
// Skip self-referencing edges
|
||||
if (edge.source === edge.target) continue
|
||||
|
||||
// Skip if connection already exists (same source and target)
|
||||
const connectionKey = `${edge.source}->${edge.target}`
|
||||
if (existingConnections.has(connectionKey)) continue
|
||||
|
||||
// Skip if would create a cycle
|
||||
if (wouldCreateCycle([...newEdges], edge.source, edge.target)) continue
|
||||
|
||||
@@ -525,7 +519,6 @@ export const useWorkflowStore = create<WorkflowStore>()(
|
||||
data: edge.data || {},
|
||||
})
|
||||
existingEdgeIds.add(edge.id)
|
||||
existingConnections.add(connectionKey)
|
||||
}
|
||||
|
||||
const blocks = get().blocks
|
||||
|
||||
Reference in New Issue
Block a user