Skip to content

Commit bbaf7e9

Browse files
improvement(autolayout): simplify code to use fixed block widths, height + refactor (#2112)
* improvement(autolayout): simplify code to use fixed block widths, height + refactor * change to aliased imports
1 parent c80827f commit bbaf7e9

File tree

16 files changed

+766
-752
lines changed

16 files changed

+766
-752
lines changed

apps/sim/app/api/workflows/[id]/autolayout/route.ts

Lines changed: 13 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,11 @@ import { getSession } from '@/lib/auth'
44
import { createLogger } from '@/lib/logs/console/logger'
55
import { generateRequestId } from '@/lib/utils'
66
import { applyAutoLayout } from '@/lib/workflows/autolayout'
7+
import {
8+
DEFAULT_HORIZONTAL_SPACING,
9+
DEFAULT_LAYOUT_PADDING,
10+
DEFAULT_VERTICAL_SPACING,
11+
} from '@/lib/workflows/autolayout/constants'
712
import {
813
loadWorkflowFromNormalizedTables,
914
type NormalizedWorkflowData,
@@ -15,24 +20,18 @@ export const dynamic = 'force-dynamic'
1520
const logger = createLogger('AutoLayoutAPI')
1621

1722
const AutoLayoutRequestSchema = z.object({
18-
strategy: z
19-
.enum(['smart', 'hierarchical', 'layered', 'force-directed'])
20-
.optional()
21-
.default('smart'),
22-
direction: z.enum(['horizontal', 'vertical', 'auto']).optional().default('auto'),
2323
spacing: z
2424
.object({
25-
horizontal: z.number().min(100).max(1000).optional().default(400),
26-
vertical: z.number().min(50).max(500).optional().default(200),
27-
layer: z.number().min(200).max(1200).optional().default(600),
25+
horizontal: z.number().min(100).max(1000).optional(),
26+
vertical: z.number().min(50).max(500).optional(),
2827
})
2928
.optional()
3029
.default({}),
3130
alignment: z.enum(['start', 'center', 'end']).optional().default('center'),
3231
padding: z
3332
.object({
34-
x: z.number().min(50).max(500).optional().default(200),
35-
y: z.number().min(50).max(500).optional().default(200),
33+
x: z.number().min(50).max(500).optional(),
34+
y: z.number().min(50).max(500).optional(),
3635
})
3736
.optional()
3837
.default({}),
@@ -68,8 +67,6 @@ export async function POST(request: NextRequest, { params }: { params: Promise<{
6867
const layoutOptions = AutoLayoutRequestSchema.parse(body)
6968

7069
logger.info(`[${requestId}] Processing autolayout request for workflow ${workflowId}`, {
71-
strategy: layoutOptions.strategy,
72-
direction: layoutOptions.direction,
7370
userId,
7471
})
7572

@@ -121,20 +118,18 @@ export async function POST(request: NextRequest, { params }: { params: Promise<{
121118
}
122119

123120
const autoLayoutOptions = {
124-
horizontalSpacing: layoutOptions.spacing?.horizontal || 550,
125-
verticalSpacing: layoutOptions.spacing?.vertical || 200,
121+
horizontalSpacing: layoutOptions.spacing?.horizontal ?? DEFAULT_HORIZONTAL_SPACING,
122+
verticalSpacing: layoutOptions.spacing?.vertical ?? DEFAULT_VERTICAL_SPACING,
126123
padding: {
127-
x: layoutOptions.padding?.x || 150,
128-
y: layoutOptions.padding?.y || 150,
124+
x: layoutOptions.padding?.x ?? DEFAULT_LAYOUT_PADDING.x,
125+
y: layoutOptions.padding?.y ?? DEFAULT_LAYOUT_PADDING.y,
129126
},
130127
alignment: layoutOptions.alignment,
131128
}
132129

133130
const layoutResult = applyAutoLayout(
134131
currentWorkflowData.blocks,
135132
currentWorkflowData.edges,
136-
currentWorkflowData.loops || {},
137-
currentWorkflowData.parallels || {},
138133
autoLayoutOptions
139134
)
140135

@@ -156,16 +151,13 @@ export async function POST(request: NextRequest, { params }: { params: Promise<{
156151

157152
logger.info(`[${requestId}] Autolayout completed successfully in ${elapsed}ms`, {
158153
blockCount,
159-
strategy: layoutOptions.strategy,
160154
workflowId,
161155
})
162156

163157
return NextResponse.json({
164158
success: true,
165159
message: `Autolayout applied successfully to ${blockCount} blocks`,
166160
data: {
167-
strategy: layoutOptions.strategy,
168-
direction: layoutOptions.direction,
169161
blockCount,
170162
elapsed: `${elapsed}ms`,
171163
layoutedBlocks: layoutResult.blocks,

apps/sim/app/api/yaml/autolayout/route.ts

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,11 @@ import { z } from 'zod'
33
import { createLogger } from '@/lib/logs/console/logger'
44
import { generateRequestId } from '@/lib/utils'
55
import { applyAutoLayout } from '@/lib/workflows/autolayout'
6+
import {
7+
DEFAULT_HORIZONTAL_SPACING,
8+
DEFAULT_LAYOUT_PADDING,
9+
DEFAULT_VERTICAL_SPACING,
10+
} from '@/lib/workflows/autolayout/constants'
611

712
const logger = createLogger('YamlAutoLayoutAPI')
813

@@ -15,13 +20,10 @@ const AutoLayoutRequestSchema = z.object({
1520
}),
1621
options: z
1722
.object({
18-
strategy: z.enum(['smart', 'hierarchical', 'layered', 'force-directed']).optional(),
19-
direction: z.enum(['horizontal', 'vertical', 'auto']).optional(),
2023
spacing: z
2124
.object({
2225
horizontal: z.number().optional(),
2326
vertical: z.number().optional(),
24-
layer: z.number().optional(),
2527
})
2628
.optional(),
2729
alignment: z.enum(['start', 'center', 'end']).optional(),
@@ -45,24 +47,21 @@ export async function POST(request: NextRequest) {
4547
logger.info(`[${requestId}] Applying auto layout`, {
4648
blockCount: Object.keys(workflowState.blocks).length,
4749
edgeCount: workflowState.edges.length,
48-
strategy: options?.strategy || 'smart',
4950
})
5051

5152
const autoLayoutOptions = {
52-
horizontalSpacing: options?.spacing?.horizontal || 550,
53-
verticalSpacing: options?.spacing?.vertical || 200,
53+
horizontalSpacing: options?.spacing?.horizontal ?? DEFAULT_HORIZONTAL_SPACING,
54+
verticalSpacing: options?.spacing?.vertical ?? DEFAULT_VERTICAL_SPACING,
5455
padding: {
55-
x: options?.padding?.x || 150,
56-
y: options?.padding?.y || 150,
56+
x: options?.padding?.x ?? DEFAULT_LAYOUT_PADDING.x,
57+
y: options?.padding?.y ?? DEFAULT_LAYOUT_PADDING.y,
5758
},
58-
alignment: options?.alignment || 'center',
59+
alignment: options?.alignment ?? 'center',
5960
}
6061

6162
const layoutResult = applyAutoLayout(
6263
workflowState.blocks,
6364
workflowState.edges,
64-
workflowState.loops || {},
65-
workflowState.parallels || {},
6665
autoLayoutOptions
6766
)
6867

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/hooks/use-block-dimensions.ts

Lines changed: 3 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@ import { useEffect, useRef } from 'react'
22
import { useUpdateNodeInternals } from 'reactflow'
33
import { useWorkflowStore } from '@/stores/workflows/workflow/store'
44

5+
// Re-export for backwards compatibility
6+
export { BLOCK_DIMENSIONS } from '@/lib/blocks/block-dimensions'
7+
58
interface BlockDimensions {
69
width: number
710
height: number
@@ -13,24 +16,6 @@ interface UseBlockDimensionsOptions {
1316
dependencies: React.DependencyList
1417
}
1518

16-
/**
17-
* Shared block dimension constants
18-
*/
19-
export const BLOCK_DIMENSIONS = {
20-
FIXED_WIDTH: 250,
21-
HEADER_HEIGHT: 40,
22-
MIN_HEIGHT: 100,
23-
24-
// Workflow blocks
25-
WORKFLOW_CONTENT_PADDING: 16,
26-
WORKFLOW_ROW_HEIGHT: 29,
27-
28-
// Note blocks
29-
NOTE_CONTENT_PADDING: 14,
30-
NOTE_MIN_CONTENT_HEIGHT: 20,
31-
NOTE_BASE_CONTENT_HEIGHT: 60,
32-
} as const
33-
3419
/**
3520
* Hook to manage deterministic block dimensions without ResizeObserver.
3621
* Calculates dimensions based on content structure and updates the store.

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/hooks/use-node-utilities.ts

Lines changed: 23 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,10 @@
11
import { useCallback } from 'react'
22
import { useReactFlow } from 'reactflow'
3+
import { BLOCK_DIMENSIONS, CONTAINER_DIMENSIONS } from '@/lib/blocks/block-dimensions'
34
import { createLogger } from '@/lib/logs/console/logger'
45

56
const logger = createLogger('NodeUtilities')
67

7-
const DEFAULT_CONTAINER_WIDTH = 500
8-
const DEFAULT_CONTAINER_HEIGHT = 300
9-
108
/**
119
* Hook providing utilities for node position, hierarchy, and dimension calculations
1210
*/
@@ -27,40 +25,43 @@ export function useNodeUtilities(blocks: Record<string, any>) {
2725
const getBlockDimensions = useCallback(
2826
(blockId: string): { width: number; height: number } => {
2927
const block = blocks[blockId]
30-
if (!block) return { width: 250, height: 100 }
28+
if (!block) {
29+
return { width: BLOCK_DIMENSIONS.FIXED_WIDTH, height: BLOCK_DIMENSIONS.MIN_HEIGHT }
30+
}
3131

3232
if (isContainerType(block.type)) {
3333
return {
34-
width: block.data?.width ? Math.max(block.data.width, 400) : DEFAULT_CONTAINER_WIDTH,
35-
height: block.data?.height ? Math.max(block.data.height, 200) : DEFAULT_CONTAINER_HEIGHT,
34+
width: block.data?.width
35+
? Math.max(block.data.width, CONTAINER_DIMENSIONS.MIN_WIDTH)
36+
: CONTAINER_DIMENSIONS.DEFAULT_WIDTH,
37+
height: block.data?.height
38+
? Math.max(block.data.height, CONTAINER_DIMENSIONS.MIN_HEIGHT)
39+
: CONTAINER_DIMENSIONS.DEFAULT_HEIGHT,
3640
}
3741
}
3842

3943
// Workflow block nodes have fixed visual width
40-
const width = 250
44+
const width = BLOCK_DIMENSIONS.FIXED_WIDTH
4145

4246
// Prefer deterministic height published by the block component; fallback to estimate
4347
let height = block.height
4448

4549
if (!height) {
4650
// Estimate height for workflow blocks before ResizeObserver measures them
47-
// Block structure: header (40px) + content area with subblocks
51+
// Block structure: header + content area with subblocks
4852
// Each subblock row is approximately 29px (14px text + 8px gap + padding)
49-
const headerHeight = 40
50-
const subblockRowHeight = 29
51-
const contentPadding = 16 // p-[8px] top and bottom = 16px total
52-
53-
// Estimate number of visible subblock rows
54-
// This is a rough estimate - actual rendering may vary
5553
const estimatedRows = 3 // Conservative estimate for typical blocks
5654
const hasErrorRow = block.type !== 'starter' && block.type !== 'response' ? 1 : 0
5755

58-
height = headerHeight + contentPadding + (estimatedRows + hasErrorRow) * subblockRowHeight
56+
height =
57+
BLOCK_DIMENSIONS.HEADER_HEIGHT +
58+
BLOCK_DIMENSIONS.WORKFLOW_CONTENT_PADDING +
59+
(estimatedRows + hasErrorRow) * BLOCK_DIMENSIONS.WORKFLOW_ROW_HEIGHT
5960
}
6061

6162
return {
6263
width,
63-
height: Math.max(height, 100),
64+
height: Math.max(height, BLOCK_DIMENSIONS.MIN_HEIGHT),
6465
}
6566
},
6667
[blocks, isContainerType]
@@ -205,9 +206,9 @@ export function useNodeUtilities(blocks: Record<string, any>) {
205206
const absolutePos = getNodeAbsolutePosition(n.id)
206207
const rect = {
207208
left: absolutePos.x,
208-
right: absolutePos.x + (n.data?.width || DEFAULT_CONTAINER_WIDTH),
209+
right: absolutePos.x + (n.data?.width || CONTAINER_DIMENSIONS.DEFAULT_WIDTH),
209210
top: absolutePos.y,
210-
bottom: absolutePos.y + (n.data?.height || DEFAULT_CONTAINER_HEIGHT),
211+
bottom: absolutePos.y + (n.data?.height || CONTAINER_DIMENSIONS.DEFAULT_HEIGHT),
211212
}
212213

213214
return (
@@ -222,8 +223,8 @@ export function useNodeUtilities(blocks: Record<string, any>) {
222223
// Return absolute position so callers can compute relative placement correctly
223224
loopPosition: getNodeAbsolutePosition(n.id),
224225
dimensions: {
225-
width: n.data?.width || DEFAULT_CONTAINER_WIDTH,
226-
height: n.data?.height || DEFAULT_CONTAINER_HEIGHT,
226+
width: n.data?.width || CONTAINER_DIMENSIONS.DEFAULT_WIDTH,
227+
height: n.data?.height || CONTAINER_DIMENSIONS.DEFAULT_HEIGHT,
227228
},
228229
}))
229230

@@ -247,8 +248,8 @@ export function useNodeUtilities(blocks: Record<string, any>) {
247248
*/
248249
const calculateLoopDimensions = useCallback(
249250
(nodeId: string): { width: number; height: number } => {
250-
const minWidth = DEFAULT_CONTAINER_WIDTH
251-
const minHeight = DEFAULT_CONTAINER_HEIGHT
251+
const minWidth = CONTAINER_DIMENSIONS.DEFAULT_WIDTH
252+
const minHeight = CONTAINER_DIMENSIONS.DEFAULT_HEIGHT
252253

253254
// Match styling in subflow-node.tsx:
254255
// - Header section: 50px total height

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/utils/auto-layout-utils.ts

Lines changed: 10 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
11
import { createLogger } from '@/lib/logs/console/logger'
2+
import {
3+
DEFAULT_HORIZONTAL_SPACING,
4+
DEFAULT_LAYOUT_PADDING,
5+
DEFAULT_VERTICAL_SPACING,
6+
} from '@/lib/workflows/autolayout/constants'
27
import { useWorkflowStore } from '@/stores/workflows/workflow/store'
38

49
const logger = createLogger('AutoLayoutUtils')
@@ -7,12 +12,9 @@ const logger = createLogger('AutoLayoutUtils')
712
* Auto layout options interface
813
*/
914
export interface AutoLayoutOptions {
10-
strategy?: 'smart' | 'hierarchical' | 'layered' | 'force-directed'
11-
direction?: 'horizontal' | 'vertical' | 'auto'
1215
spacing?: {
1316
horizontal?: number
1417
vertical?: number
15-
layer?: number
1618
}
1719
alignment?: 'start' | 'center' | 'end'
1820
padding?: {
@@ -21,24 +23,6 @@ export interface AutoLayoutOptions {
2123
}
2224
}
2325

24-
/**
25-
* Default auto layout options
26-
*/
27-
const DEFAULT_AUTO_LAYOUT_OPTIONS = {
28-
strategy: 'smart' as const,
29-
direction: 'auto' as const,
30-
spacing: {
31-
horizontal: 550,
32-
vertical: 200,
33-
layer: 550,
34-
},
35-
alignment: 'center' as const,
36-
padding: {
37-
x: 150,
38-
y: 150,
39-
},
40-
}
41-
4226
/**
4327
* Apply auto layout and update store
4428
* Standalone utility for use outside React context (event handlers, tools, etc.)
@@ -69,17 +53,14 @@ export async function applyAutoLayoutAndUpdateStore(
6953

7054
// Merge with default options
7155
const layoutOptions = {
72-
strategy: options.strategy || DEFAULT_AUTO_LAYOUT_OPTIONS.strategy,
73-
direction: options.direction || DEFAULT_AUTO_LAYOUT_OPTIONS.direction,
7456
spacing: {
75-
horizontal: options.spacing?.horizontal || DEFAULT_AUTO_LAYOUT_OPTIONS.spacing.horizontal,
76-
vertical: options.spacing?.vertical || DEFAULT_AUTO_LAYOUT_OPTIONS.spacing.vertical,
77-
layer: options.spacing?.layer || DEFAULT_AUTO_LAYOUT_OPTIONS.spacing.layer,
57+
horizontal: options.spacing?.horizontal ?? DEFAULT_HORIZONTAL_SPACING,
58+
vertical: options.spacing?.vertical ?? DEFAULT_VERTICAL_SPACING,
7859
},
79-
alignment: options.alignment || DEFAULT_AUTO_LAYOUT_OPTIONS.alignment,
60+
alignment: options.alignment ?? 'center',
8061
padding: {
81-
x: options.padding?.x || DEFAULT_AUTO_LAYOUT_OPTIONS.padding.x,
82-
y: options.padding?.y || DEFAULT_AUTO_LAYOUT_OPTIONS.padding.y,
62+
x: options.padding?.x ?? DEFAULT_LAYOUT_PADDING.x,
63+
y: options.padding?.y ?? DEFAULT_LAYOUT_PADDING.y,
8364
},
8465
}
8566

0 commit comments

Comments
 (0)