Files
AutoMetabuilder/frontend/autometabuilder/components/workflow/WorkflowCanvas.tsx
copilot-swe-agent[bot] 6e9ff896e7 Phase 9: Implement atomic hooks-based workflow canvas components
- Created custom hooks: useWorkflowGraph, useWorkflowPlugins, usePluginSearch, useTabNavigation
- Decomposed canvas logic into: useCanvasNodes, useCanvasEdges, useCanvasDragDrop
- Built atomic node components: NodeHeader, NodeBody, NodePorts
- Created canvas UI components: CanvasInfoPanel, CanvasHintPanel
- Split builder into: LoadingState, ErrorState, WorkflowBuilderHeader, WorkflowBuilderTabs, WorkflowBuilderContent
- Added React Flow for n8n-style visual canvas with drag-and-drop
- All components now under 100 LOC following PROMPT.md guidelines

Co-authored-by: johndoe6345789 <224850594+johndoe6345789@users.noreply.github.com>
2026-01-10 12:25:36 +00:00

91 lines
2.3 KiB
TypeScript

"use client";
import ReactFlow, {
Background,
BackgroundVariant,
Controls,
MiniMap,
Panel,
ReactFlowProvider,
} from "reactflow";
import "reactflow/dist/style.css";
import { Box } from "@mui/material";
import { WorkflowGraph, WorkflowPluginMap, Node, Edge } from "../../lib/types";
import WorkflowNode from "./WorkflowNode";
import CanvasInfoPanel from "./CanvasInfoPanel";
import CanvasHintPanel from "./CanvasHintPanel";
import { useWorkflowCanvas } from "../../hooks/useWorkflowCanvas";
const nodeTypes = {
workflow: WorkflowNode,
};
type WorkflowCanvasProps = {
graph: WorkflowGraph;
plugins: WorkflowPluginMap;
onNodesChange?: (nodes: Node[]) => void;
onEdgesChange?: (edges: Edge[]) => void;
t: (key: string, fallback?: string) => string;
};
function WorkflowCanvasInner(props: WorkflowCanvasProps) {
const { graph, plugins, onNodesChange, onEdgesChange, t } = props;
const {
nodes,
edges,
onNodesChangeInternal,
onEdgesChangeInternal,
onConnect,
onDragOver,
onDrop,
reactFlowWrapper,
} = useWorkflowCanvas(graph, plugins, t, onNodesChange, onEdgesChange);
return (
<Box
ref={reactFlowWrapper}
sx={{ width: "100%", height: "600px", position: "relative" }}
>
<ReactFlow
nodes={nodes}
edges={edges}
onNodesChange={onNodesChangeInternal}
onEdgesChange={onEdgesChangeInternal}
onConnect={onConnect}
onDragOver={onDragOver}
onDrop={onDrop}
nodeTypes={nodeTypes}
fitView
attributionPosition="bottom-left"
>
<Background variant={BackgroundVariant.Dots} gap={16} size={1} />
<Controls />
<MiniMap
nodeStrokeWidth={3}
zoomable
pannable
style={{
backgroundColor: "var(--color-panel-bg)",
border: "1px solid var(--color-border-muted)",
}}
/>
<Panel position="top-left">
<CanvasInfoPanel nodeCount={nodes.length} edgeCount={edges.length} t={t} />
</Panel>
<Panel position="top-right">
<CanvasHintPanel t={t} />
</Panel>
</ReactFlow>
</Box>
);
}
export default function WorkflowCanvas(props: WorkflowCanvasProps) {
return (
<ReactFlowProvider>
<WorkflowCanvasInner {...props} />
</ReactFlowProvider>
);
}