diff --git a/web/app/components/workflow-app/hooks/use-nodes-sync-draft.ts b/web/app/components/workflow-app/hooks/use-nodes-sync-draft.ts index 5d131fba5b..f7debd1030 100644 --- a/web/app/components/workflow-app/hooks/use-nodes-sync-draft.ts +++ b/web/app/components/workflow-app/hooks/use-nodes-sync-draft.ts @@ -37,7 +37,15 @@ export const useNodesSyncDraft = () => { if (appId) { const nodes = getNodes() - // Allow empty workflows - sync restrictions removed to support empty workflow editing + + // Prevent syncing empty workflow if React Flow isn't properly initialized + // Check multiple indicators of uninitialized state: + // 1. Empty nodes with default viewport (x=0, y=0, zoom=1) + // 2. Empty edges array along with empty nodes + if (nodes.length === 0 && edges.length === 0 && x === 0 && y === 0 && zoom === 1) { + // This appears to be an uninitialized React Flow state, skip sync to prevent data loss + return null + } const features = featuresStore!.getState().features const producedNodes = produce(nodes, (draft) => { diff --git a/web/app/components/workflow-app/hooks/use-workflow-refresh-draft.ts b/web/app/components/workflow-app/hooks/use-workflow-refresh-draft.ts index c944e10c4c..b45ab16c14 100644 --- a/web/app/components/workflow-app/hooks/use-workflow-refresh-draft.ts +++ b/web/app/components/workflow-app/hooks/use-workflow-refresh-draft.ts @@ -19,7 +19,13 @@ export const useWorkflowRefreshDraft = () => { } = workflowStore.getState() setIsSyncingWorkflowDraft(true) fetchWorkflowDraft(`/apps/${appId}/workflows/draft`).then((response) => { - handleUpdateWorkflowCanvas(response.graph as WorkflowDataUpdater) + // Ensure we have a valid workflow structure with viewport + const workflowData: WorkflowDataUpdater = { + nodes: response.graph?.nodes || [], + edges: response.graph?.edges || [], + viewport: response.graph?.viewport || { x: 0, y: 0, zoom: 1 }, + } + handleUpdateWorkflowCanvas(workflowData) setSyncWorkflowDraftHash(response.hash) setEnvSecrets((response.environment_variables || []).filter(env => env.value_type === 'secret').reduce((acc, env) => { acc[env.id] = env.value diff --git a/web/app/components/workflow/hooks/use-workflow-interactions.ts b/web/app/components/workflow/hooks/use-workflow-interactions.ts index d8653a5942..857998e8d8 100644 --- a/web/app/components/workflow/hooks/use-workflow-interactions.ts +++ b/web/app/components/workflow/hooks/use-workflow-interactions.ts @@ -329,7 +329,10 @@ export const useWorkflowUpdate = () => { edges: initialEdges(edges, nodes), }, } as any) - setViewport(viewport) + + // Only set viewport if it exists and is valid + if (viewport && typeof viewport.x === 'number' && typeof viewport.y === 'number' && typeof viewport.zoom === 'number') + setViewport(viewport) }, [eventEmitter, reactflow]) return { diff --git a/web/app/components/workflow/nodes/trigger-webhook/components/generic-table.tsx b/web/app/components/workflow/nodes/trigger-webhook/components/generic-table.tsx index 1abc431ced..17479f16a2 100644 --- a/web/app/components/workflow/nodes/trigger-webhook/components/generic-table.tsx +++ b/web/app/components/workflow/nodes/trigger-webhook/components/generic-table.tsx @@ -177,7 +177,7 @@ const GenericTable: FC = ({ 'h-6 rounded-none bg-transparent px-0 text-text-secondary', 'hover:bg-transparent focus-visible:bg-transparent group-hover/simple-select:bg-transparent', )} - optionWrapClassName="w-26 min-w-26 z-[5] -ml-3" + optionWrapClassName="w-26 min-w-26 z-[60] -ml-3" notClearable /> )