Skip to content

Commit 767db1c

Browse files
fix(autolayout): edits coalesced for same request diffs (#3724)
* fix(autolayout): edits coalesced for same request diffs * address comments * address edge signature gen * perf improvement
1 parent 288aa08 commit 767db1c

File tree

8 files changed

+803
-91
lines changed

8 files changed

+803
-91
lines changed

apps/sim/lib/copilot/tools/server/workflow/edit-workflow/index.ts

Lines changed: 7 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import {
77
type BaseServerTool,
88
type ServerToolContext,
99
} from '@/lib/copilot/tools/server/base-tool'
10-
import { applyTargetedLayout } from '@/lib/workflows/autolayout'
10+
import { applyTargetedLayout, getTargetedLayoutImpact } from '@/lib/workflows/autolayout'
1111
import {
1212
DEFAULT_HORIZONTAL_SPACING,
1313
DEFAULT_VERTICAL_SPACING,
@@ -233,29 +233,18 @@ export const editWorkflowServerTool: BaseServerTool<EditWorkflowParams, unknown>
233233
// Persist the workflow state to the database
234234
const finalWorkflowState = validation.sanitizedState || modifiedWorkflowState
235235

236-
// Identify blocks that need layout by comparing against the pre-operation
237-
// state. New blocks and blocks inserted into subflows (position reset to
238-
// 0,0) need repositioning. Extracted blocks are excluded — their handler
239-
// already computed valid absolute positions from the container offset.
240-
const preOperationBlockIds = new Set(Object.keys(workflowState.blocks || {}))
241-
const blocksNeedingLayout = Object.keys(finalWorkflowState.blocks).filter((id) => {
242-
if (!preOperationBlockIds.has(id)) return true
243-
const prevParent = workflowState.blocks[id]?.data?.parentId ?? null
244-
const currParent = finalWorkflowState.blocks[id]?.data?.parentId ?? null
245-
if (prevParent === currParent) return false
246-
// Parent changed — only needs layout if position was reset to (0,0)
247-
// by insert_into_subflow. extract_from_subflow computes absolute
248-
// positions directly, so those blocks don't need repositioning.
249-
const pos = finalWorkflowState.blocks[id]?.position
250-
return pos?.x === 0 && pos?.y === 0
236+
const { layoutBlockIds, shiftSourceBlockIds } = getTargetedLayoutImpact({
237+
before: workflowState,
238+
after: finalWorkflowState,
251239
})
252240

253241
let layoutedBlocks = finalWorkflowState.blocks
254242

255-
if (blocksNeedingLayout.length > 0) {
243+
if (layoutBlockIds.length > 0 || shiftSourceBlockIds.length > 0) {
256244
try {
257245
layoutedBlocks = applyTargetedLayout(finalWorkflowState.blocks, finalWorkflowState.edges, {
258-
changedBlockIds: blocksNeedingLayout,
246+
changedBlockIds: layoutBlockIds,
247+
shiftSourceBlockIds,
259248
horizontalSpacing: DEFAULT_HORIZONTAL_SPACING,
260249
verticalSpacing: DEFAULT_VERTICAL_SPACING,
261250
})

0 commit comments

Comments
 (0)