mirror of
https://github.com/simstudioai/sim.git
synced 2026-02-11 15:14:53 -05:00
feat(autolayout): add snap-to-grid support (#3031)
* feat(autolayout): add snap-to-grid support * fix(autolayout): recalculate dimensions after grid snapping * fix(autolayout): correct dimension calculation and propagate gridSize
This commit is contained in:
@@ -35,8 +35,7 @@ const AutoLayoutRequestSchema = z.object({
|
||||
})
|
||||
.optional()
|
||||
.default({}),
|
||||
// Optional: if provided, use these blocks instead of loading from DB
|
||||
// This allows using blocks with live measurements from the UI
|
||||
gridSize: z.number().min(0).max(50).optional(),
|
||||
blocks: z.record(z.any()).optional(),
|
||||
edges: z.array(z.any()).optional(),
|
||||
loops: z.record(z.any()).optional(),
|
||||
@@ -53,7 +52,6 @@ export async function POST(request: NextRequest, { params }: { params: Promise<{
|
||||
const { id: workflowId } = await params
|
||||
|
||||
try {
|
||||
// Get the session
|
||||
const session = await getSession()
|
||||
if (!session?.user?.id) {
|
||||
logger.warn(`[${requestId}] Unauthorized autolayout attempt for workflow ${workflowId}`)
|
||||
@@ -62,7 +60,6 @@ export async function POST(request: NextRequest, { params }: { params: Promise<{
|
||||
|
||||
const userId = session.user.id
|
||||
|
||||
// Parse request body
|
||||
const body = await request.json()
|
||||
const layoutOptions = AutoLayoutRequestSchema.parse(body)
|
||||
|
||||
@@ -70,7 +67,6 @@ export async function POST(request: NextRequest, { params }: { params: Promise<{
|
||||
userId,
|
||||
})
|
||||
|
||||
// Fetch the workflow to check ownership/access
|
||||
const accessContext = await getWorkflowAccessContext(workflowId, userId)
|
||||
const workflowData = accessContext?.workflow
|
||||
|
||||
@@ -79,7 +75,6 @@ export async function POST(request: NextRequest, { params }: { params: Promise<{
|
||||
return NextResponse.json({ error: 'Workflow not found' }, { status: 404 })
|
||||
}
|
||||
|
||||
// Check if user has permission to update this workflow
|
||||
const canUpdate =
|
||||
accessContext?.isOwner ||
|
||||
(workflowData.workspaceId
|
||||
@@ -94,8 +89,6 @@ export async function POST(request: NextRequest, { params }: { params: Promise<{
|
||||
return NextResponse.json({ error: 'Access denied' }, { status: 403 })
|
||||
}
|
||||
|
||||
// Use provided blocks/edges if available (with live measurements from UI),
|
||||
// otherwise load from database
|
||||
let currentWorkflowData: NormalizedWorkflowData | null
|
||||
|
||||
if (layoutOptions.blocks && layoutOptions.edges) {
|
||||
@@ -125,6 +118,7 @@ export async function POST(request: NextRequest, { params }: { params: Promise<{
|
||||
y: layoutOptions.padding?.y ?? DEFAULT_LAYOUT_PADDING.y,
|
||||
},
|
||||
alignment: layoutOptions.alignment,
|
||||
gridSize: layoutOptions.gridSize,
|
||||
}
|
||||
|
||||
const layoutResult = applyAutoLayout(
|
||||
|
||||
@@ -1,108 +0,0 @@
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { type NextRequest, NextResponse } from 'next/server'
|
||||
import { z } from 'zod'
|
||||
import { generateRequestId } from '@/lib/core/utils/request'
|
||||
import { applyAutoLayout } from '@/lib/workflows/autolayout'
|
||||
import {
|
||||
DEFAULT_HORIZONTAL_SPACING,
|
||||
DEFAULT_LAYOUT_PADDING,
|
||||
DEFAULT_VERTICAL_SPACING,
|
||||
} from '@/lib/workflows/autolayout/constants'
|
||||
|
||||
const logger = createLogger('YamlAutoLayoutAPI')
|
||||
|
||||
const AutoLayoutRequestSchema = z.object({
|
||||
workflowState: z.object({
|
||||
blocks: z.record(z.any()),
|
||||
edges: z.array(z.any()),
|
||||
loops: z.record(z.any()).optional().default({}),
|
||||
parallels: z.record(z.any()).optional().default({}),
|
||||
}),
|
||||
options: z
|
||||
.object({
|
||||
spacing: z
|
||||
.object({
|
||||
horizontal: z.number().optional(),
|
||||
vertical: z.number().optional(),
|
||||
})
|
||||
.optional(),
|
||||
alignment: z.enum(['start', 'center', 'end']).optional(),
|
||||
padding: z
|
||||
.object({
|
||||
x: z.number().optional(),
|
||||
y: z.number().optional(),
|
||||
})
|
||||
.optional(),
|
||||
})
|
||||
.optional(),
|
||||
})
|
||||
|
||||
export async function POST(request: NextRequest) {
|
||||
const requestId = generateRequestId()
|
||||
|
||||
try {
|
||||
const body = await request.json()
|
||||
const { workflowState, options } = AutoLayoutRequestSchema.parse(body)
|
||||
|
||||
logger.info(`[${requestId}] Applying auto layout`, {
|
||||
blockCount: Object.keys(workflowState.blocks).length,
|
||||
edgeCount: workflowState.edges.length,
|
||||
})
|
||||
|
||||
const autoLayoutOptions = {
|
||||
horizontalSpacing: options?.spacing?.horizontal ?? DEFAULT_HORIZONTAL_SPACING,
|
||||
verticalSpacing: options?.spacing?.vertical ?? DEFAULT_VERTICAL_SPACING,
|
||||
padding: {
|
||||
x: options?.padding?.x ?? DEFAULT_LAYOUT_PADDING.x,
|
||||
y: options?.padding?.y ?? DEFAULT_LAYOUT_PADDING.y,
|
||||
},
|
||||
alignment: options?.alignment ?? 'center',
|
||||
}
|
||||
|
||||
const layoutResult = applyAutoLayout(
|
||||
workflowState.blocks,
|
||||
workflowState.edges,
|
||||
autoLayoutOptions
|
||||
)
|
||||
|
||||
if (!layoutResult.success || !layoutResult.blocks) {
|
||||
logger.error(`[${requestId}] Auto layout failed:`, {
|
||||
error: layoutResult.error,
|
||||
})
|
||||
return NextResponse.json(
|
||||
{
|
||||
success: false,
|
||||
errors: [layoutResult.error || 'Unknown auto layout error'],
|
||||
},
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
|
||||
logger.info(`[${requestId}] Auto layout completed successfully:`, {
|
||||
success: true,
|
||||
blockCount: Object.keys(layoutResult.blocks).length,
|
||||
})
|
||||
|
||||
const transformedResponse = {
|
||||
success: true,
|
||||
workflowState: {
|
||||
blocks: layoutResult.blocks,
|
||||
edges: workflowState.edges,
|
||||
loops: workflowState.loops || {},
|
||||
parallels: workflowState.parallels || {},
|
||||
},
|
||||
}
|
||||
|
||||
return NextResponse.json(transformedResponse)
|
||||
} catch (error) {
|
||||
logger.error(`[${requestId}] Auto layout failed:`, error)
|
||||
|
||||
return NextResponse.json(
|
||||
{
|
||||
success: false,
|
||||
errors: [error instanceof Error ? error.message : 'Unknown auto layout error'],
|
||||
},
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user