sim
sim copied to clipboard
fix(subflow): prevent auto-connect across subflow edges with keyboard shortcut block additions, make positioning for auto-drop smarter
Summary
- prevent auto-connect across subflow edges with keyboard shortcut block additions, we had validation for the drag case but not for keyboard shortcuts so in random edge cases it would connect blocks inside subflows to blocks outside
- make positioning for auto-drop smarter, position similar to copilot
- stronger typing
Type of Change
- [x] Bug fix
Testing
Tested manually
Checklist
- [x] Code follows project style guidelines
- [x] Self-reviewed my changes
- [x] Tests added/updated and passing
- [x] No new warnings introduced
- [x] I confirm that I have read and agree to the terms outlined in the Contributor License Agreement (CLA)
The latest updates on your projects. Learn more about Vercel for GitHub.
Greptile Summary
This PR refactors the auto-connect logic into a centralized tryCreateAutoConnectEdge function that handles three distinct cases: blocks inside containers with existing children, blocks in empty containers, and root-level blocks. The refactoring consolidates previously duplicated logic from drag-and-drop and keyboard shortcut handlers.
Key improvements:
- Prevented cross-subflow edge creation for keyboard shortcuts by adding validation in
tryCreateAutoConnectEdge(lines 710-714) - Improved block positioning for keyboard shortcuts with
calculateSmartPosition()that places blocks to the right of existing blocks with proper spacing, similar to copilot behavior - Enhanced type safety by replacing
anytype casts withSubflowNodeDatatype (lines 526, 624, 1254, 1259, 1867, 1872) - Replaced magic numbers with constants from
BLOCK_DIMENSIONSandCONTAINER_DIMENSIONS - Extracted trigger constraint validation into reusable
checkTriggerConstraintsfunction
Issue found:
- Cross-container edge validation on line 712 only checks one direction (inside→root) but not the reverse (root→inside). While
findClosestOutputprovides some protection, a bidirectional check would be more robust.
Confidence Score: 4/5
- This PR is safe to merge with one logical issue that should be addressed
- The refactoring significantly improves code organization and maintainability by consolidating duplicated auto-connect logic. The type safety improvements eliminate
anytypes, and the smart positioning enhances UX. However, the cross-container edge prevention logic has an incomplete check that could theoretically allow invalid edges in edge cases (thoughfindClosestOutputprovides partial protection). The issue should be fixed to ensure complete correctness. - Pay close attention to
apps/sim/app/workspace/[workspaceId]/w/[workflowId]/workflow.tsxline 712 - fix the cross-container edge validation
Important Files Changed
| Filename | Overview |
|---|---|
| apps/sim/app/workspace/[workspaceId]/w/[workflowId]/workflow.tsx | Refactored auto-connect logic into centralized tryCreateAutoConnectEdge function that properly prevents cross-subflow edges; added smart positioning for keyboard shortcuts; improved type safety by replacing any with SubflowNodeData |
Sequence Diagram
sequenceDiagram
participant User
participant Toolbar
participant WorkflowContent
participant tryCreateAutoConnectEdge
participant findClosestOutput
participant Store
User->>Toolbar: Press keyboard shortcut to add block
Toolbar->>WorkflowContent: dispatch 'add-block-from-toolbar' event
WorkflowContent->>WorkflowContent: calculateSmartPosition()
Note over WorkflowContent: Find rightmost root-level block<br/>Position to the right with spacing<br/>Ensure position doesn't overlap containers
WorkflowContent->>WorkflowContent: checkTriggerConstraints(type)
alt Trigger constraint violated
WorkflowContent->>User: Show error notification
end
WorkflowContent->>tryCreateAutoConnectEdge: tryCreateAutoConnectEdge(position, id, options)
alt Auto-connect disabled or special block type
tryCreateAutoConnectEdge-->>WorkflowContent: undefined
else Case 1: Inside container with children
tryCreateAutoConnectEdge->>tryCreateAutoConnectEdge: findClosestBlockInSet(existingChildren)
tryCreateAutoConnectEdge->>tryCreateAutoConnectEdge: createEdgeObject(closestBlock, newBlock)
tryCreateAutoConnectEdge-->>WorkflowContent: edge
else Case 2: Inside empty container
tryCreateAutoConnectEdge->>tryCreateAutoConnectEdge: getContainerStartHandle(containerId)
tryCreateAutoConnectEdge->>tryCreateAutoConnectEdge: createEdgeObject(container, newBlock)
tryCreateAutoConnectEdge-->>WorkflowContent: edge
else Case 3: Root level
tryCreateAutoConnectEdge->>findClosestOutput: findClosestOutput(position)
findClosestOutput->>findClosestOutput: isPointInLoopNode(position)
findClosestOutput->>findClosestOutput: Filter blocks with same parentId
findClosestOutput-->>tryCreateAutoConnectEdge: closestBlock
alt Cross-container edge detected
tryCreateAutoConnectEdge-->>WorkflowContent: undefined
else Valid same-level connection
tryCreateAutoConnectEdge->>tryCreateAutoConnectEdge: createEdgeObject(closestBlock, newBlock)
tryCreateAutoConnectEdge-->>WorkflowContent: edge
end
end
WorkflowContent->>Store: addBlock(id, type, name, position, ..., autoConnectEdge)
Store->>Store: Update workflow state
Store-->>User: Block added with auto-connect edge