Browse Source
- Add WorkflowPreview component with non-interactive React Flow - Simple colored rectangles represent different node types - Supports fitView for automatic scaling within container - Uses ReactFlowProvider for standalone usagehandoff-20260429-1057
1 changed files with 120 additions and 0 deletions
@ -0,0 +1,120 @@ |
|||
"use client"; |
|||
|
|||
import { useMemo } from "react"; |
|||
import { |
|||
ReactFlow, |
|||
ReactFlowProvider, |
|||
Node, |
|||
Edge, |
|||
} from "@xyflow/react"; |
|||
import "@xyflow/react/dist/style.css"; |
|||
|
|||
// Node type to color mapping - using minimap colors from WorkflowCanvas
|
|||
const NODE_COLORS: Record<string, string> = { |
|||
imageInput: "#22c55e", // green-500
|
|||
annotation: "#eab308", // yellow-500
|
|||
prompt: "#3b82f6", // blue-500
|
|||
nanoBanana: "#f97316", // orange-500
|
|||
generateVideo: "#a855f7", // purple-500
|
|||
llmGenerate: "#06b6d4", // cyan-500
|
|||
splitGrid: "#ec4899", // pink-500
|
|||
output: "#6b7280", // gray-500
|
|||
}; |
|||
|
|||
// Simple preview node component - just a colored rectangle
|
|||
function PreviewNode({ data }: { data: { nodeType: string } }) { |
|||
const color = NODE_COLORS[data.nodeType] || "#6b7280"; |
|||
|
|||
return ( |
|||
<div |
|||
style={{ |
|||
width: "100%", |
|||
height: "100%", |
|||
backgroundColor: color, |
|||
borderRadius: 4, |
|||
opacity: 0.8, |
|||
}} |
|||
/> |
|||
); |
|||
} |
|||
|
|||
const previewNodeTypes = { |
|||
preview: PreviewNode, |
|||
}; |
|||
|
|||
interface WorkflowPreviewProps { |
|||
workflow: { |
|||
nodes: Node[]; |
|||
edges: Edge[]; |
|||
}; |
|||
className?: string; |
|||
} |
|||
|
|||
function WorkflowPreviewInner({ workflow, className = "" }: WorkflowPreviewProps) { |
|||
// Transform workflow nodes to preview nodes
|
|||
const previewNodes = useMemo(() => { |
|||
return workflow.nodes.map((node) => ({ |
|||
id: node.id, |
|||
type: "preview", |
|||
position: node.position, |
|||
data: { nodeType: node.type || "unknown" }, |
|||
// Scale down the node sizes for preview
|
|||
style: { |
|||
width: ((node.style?.width as number) || 300) * 0.15, |
|||
height: ((node.style?.height as number) || 280) * 0.15, |
|||
}, |
|||
})); |
|||
}, [workflow.nodes]); |
|||
|
|||
// Use the workflow edges as-is (simplified rendering)
|
|||
const previewEdges = useMemo(() => { |
|||
return workflow.edges.map((edge) => ({ |
|||
id: edge.id, |
|||
source: edge.source, |
|||
target: edge.target, |
|||
// Simple edge style
|
|||
style: { stroke: "#525252", strokeWidth: 1 }, |
|||
type: "default", |
|||
})); |
|||
}, [workflow.edges]); |
|||
|
|||
return ( |
|||
<div className={`w-full h-full ${className}`}> |
|||
<ReactFlow |
|||
nodes={previewNodes} |
|||
edges={previewEdges} |
|||
nodeTypes={previewNodeTypes} |
|||
// Non-interactive mode
|
|||
nodesDraggable={false} |
|||
nodesConnectable={false} |
|||
elementsSelectable={false} |
|||
panOnDrag={false} |
|||
zoomOnScroll={false} |
|||
zoomOnPinch={false} |
|||
zoomOnDoubleClick={false} |
|||
preventScrolling={false} |
|||
// Fit the preview to container
|
|||
fitView={true} |
|||
fitViewOptions={{ padding: 0.2 }} |
|||
// Hide attribution and controls
|
|||
proOptions={{ hideAttribution: true }} |
|||
// Minimal styling
|
|||
className="bg-transparent" |
|||
// No default edge options needed
|
|||
defaultEdgeOptions={{ |
|||
type: "default", |
|||
animated: false, |
|||
}} |
|||
/> |
|||
</div> |
|||
); |
|||
} |
|||
|
|||
// Wrap with ReactFlowProvider for standalone usage
|
|||
export function WorkflowPreview(props: WorkflowPreviewProps) { |
|||
return ( |
|||
<ReactFlowProvider> |
|||
<WorkflowPreviewInner {...props} /> |
|||
</ReactFlowProvider> |
|||
); |
|||
} |
|||
Loading…
Reference in new issue