diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/workflow.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/workflow.tsx index ecec650641..704ee74133 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/workflow.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/workflow.tsx @@ -19,7 +19,6 @@ import { useShallow } from 'zustand/react/shallow' import { useSession } from '@/lib/auth/auth-client' import type { OAuthConnectEventDetail } from '@/lib/copilot/tools/client/other/oauth-request-access' import type { OAuthProvider } from '@/lib/oauth' -import { DEFAULT_HORIZONTAL_SPACING } from '@/lib/workflows/autolayout/constants' import { BLOCK_DIMENSIONS, CONTAINER_DIMENSIONS } from '@/lib/workflows/blocks/block-dimensions' import { TriggerUtils } from '@/lib/workflows/triggers/triggers' import { useWorkspacePermissionsContext } from '@/app/workspace/[workspaceId]/providers/workspace-permissions-provider' @@ -89,6 +88,26 @@ const logger = createLogger('Workflow') const DEFAULT_PASTE_OFFSET = { x: 50, y: 50 } +/** + * Gets the center of the current viewport in flow coordinates + */ +function getViewportCenter( + screenToFlowPosition: (pos: { x: number; y: number }) => { x: number; y: number } +): { x: number; y: number } { + const flowContainer = document.querySelector('.react-flow') + if (!flowContainer) { + return screenToFlowPosition({ + x: window.innerWidth / 2, + y: window.innerHeight / 2, + }) + } + const rect = flowContainer.getBoundingClientRect() + return screenToFlowPosition({ + x: rect.width / 2, + y: rect.height / 2, + }) +} + /** * Calculates the offset to paste blocks at viewport center */ @@ -125,14 +144,7 @@ function calculatePasteOffset( ) const clipboardCenter = { x: (minX + maxX) / 2, y: (minY + maxY) / 2 } - const flowContainer = document.querySelector('.react-flow') - if (!flowContainer) return DEFAULT_PASTE_OFFSET - - const rect = flowContainer.getBoundingClientRect() - const viewportCenter = screenToFlowPosition({ - x: rect.width / 2, - y: rect.height / 2, - }) + const viewportCenter = getViewportCenter(screenToFlowPosition) return { x: viewportCenter.x - clipboardCenter.x, @@ -1312,65 +1324,13 @@ const WorkflowContent = React.memo(() => { if (!type) return if (type === 'connectionBlock') return - // Calculate smart position - to the right of existing root-level blocks - const calculateSmartPosition = (): { x: number; y: number } => { - // Get all root-level blocks (no parentId) - const rootBlocks = Object.values(blocks).filter((b) => !b.data?.parentId) + const basePosition = getViewportCenter(screenToFlowPosition) - if (rootBlocks.length === 0) { - // No blocks yet, use viewport center - return screenToFlowPosition({ - x: window.innerWidth / 2, - y: window.innerHeight / 2, - }) - } - - // Find the rightmost block - let maxRight = Number.NEGATIVE_INFINITY - let rightmostBlockY = 0 - for (const block of rootBlocks) { - const blockWidth = - block.type === 'loop' || block.type === 'parallel' - ? block.data?.width || CONTAINER_DIMENSIONS.DEFAULT_WIDTH - : BLOCK_DIMENSIONS.FIXED_WIDTH - const blockRight = block.position.x + blockWidth - if (blockRight > maxRight) { - maxRight = blockRight - rightmostBlockY = block.position.y - } - } - - // Position to the right with autolayout spacing - const position = { - x: maxRight + DEFAULT_HORIZONTAL_SPACING, - y: rightmostBlockY, - } - - // Ensure position doesn't overlap any container - let container = isPointInLoopNode(position) - while (container) { - position.x = - container.loopPosition.x + container.dimensions.width + DEFAULT_HORIZONTAL_SPACING - container = isPointInLoopNode(position) - } - - return position - } - - const basePosition = calculateSmartPosition() - - // Special handling for container nodes (loop or parallel) if (type === 'loop' || type === 'parallel') { const id = crypto.randomUUID() const baseName = type === 'loop' ? 'Loop' : 'Parallel' const name = getUniqueBlockName(baseName, blocks) - const autoConnectEdge = tryCreateAutoConnectEdge(basePosition, id, { - blockType: type, - targetParentId: null, - }) - - // Add the container node with default dimensions and auto-connect edge addBlock( id, type, @@ -1383,7 +1343,7 @@ const WorkflowContent = React.memo(() => { }, undefined, undefined, - autoConnectEdge + undefined ) return @@ -1395,24 +1355,13 @@ const WorkflowContent = React.memo(() => { return } - // Check trigger constraints first if (checkTriggerConstraints(type)) return - // Create a new block with a unique ID const id = crypto.randomUUID() - // Prefer semantic default names for triggers; then ensure unique numbering centrally const defaultTriggerName = TriggerUtils.getDefaultTriggerName(type) const baseName = defaultTriggerName || blockConfig.name const name = getUniqueBlockName(baseName, blocks) - const autoConnectEdge = tryCreateAutoConnectEdge(basePosition, id, { - blockType: type, - enableTriggerMode, - targetParentId: null, - }) - - // Add the block to the workflow with auto-connect edge - // Enable trigger mode if this is a trigger-capable block from the triggers tab addBlock( id, type, @@ -1421,7 +1370,7 @@ const WorkflowContent = React.memo(() => { undefined, undefined, undefined, - autoConnectEdge, + undefined, enableTriggerMode ) } @@ -1438,11 +1387,7 @@ const WorkflowContent = React.memo(() => { screenToFlowPosition, blocks, addBlock, - tryCreateAutoConnectEdge, - isPointInLoopNode, effectivePermissions.canEdit, - addNotification, - activeWorkflowId, checkTriggerConstraints, ])