diff --git a/app/page.tsx b/app/page.tsx index 8f5129010b..89bbf285ff 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -1,3 +1,3 @@ export default function Home() { - return
Hello World
+ return
Go to /w/1
} diff --git a/app/w/[id]/workflow.tsx b/app/w/[id]/workflow.tsx index a7079c3415..69fe2fe95d 100644 --- a/app/w/[id]/workflow.tsx +++ b/app/w/[id]/workflow.tsx @@ -1,241 +1,132 @@ 'use client' -import { useState, useCallback, useEffect } from 'react' -import { BlockConfig } from '../components/blocks/types/block' -import { WorkflowBlock } from '../components/blocks/components/workflow-block/workflow-block' +import { useState, useCallback } from 'react' +import ReactFlow, { + Background, + Controls, + NodeProps, + NodeTypes, + EdgeTypes, + Connection, + Edge, + addEdge, + useNodesState, + useEdgesState, + XYPosition, + useReactFlow, + ReactFlowProvider, +} from 'reactflow' +import 'reactflow/dist/style.css' import { getBlock } from '../components/blocks/configs' -import { CoordinateTransformer } from '../lib/coordinates' +import { WorkflowBlock } from '../components/blocks/components/workflow-block/workflow-block' -interface WorkflowBlock { - id: string +// Types +interface WorkflowNodeData { type: string - position: { x: number; y: number } - config: BlockConfig + config: any // Consider adding a proper type for config + name: string } -const ZOOM_SPEED = 0.005 -const MIN_ZOOM = 0.5 -const MAX_ZOOM = 2 -const CANVAS_SIZE = 5000 +// Node Components +const WorkflowNode = ({ + data, + id, + xPos, + yPos, +}: NodeProps) => ( + +) -export default function Workflow() { - const [blocks, setBlocks] = useState([]) - const [zoom, setZoom] = useState(1) - const [pan, setPan] = useState({ x: 0, y: 0 }) - const [isPanning, setIsPanning] = useState(false) - const [startPanPos, setStartPanPos] = useState({ x: 0, y: 0 }) +const nodeTypes: NodeTypes = { + workflowBlock: WorkflowNode, +} - // Initialize pan position after mount - useEffect(() => { - const { width, height } = CoordinateTransformer.getViewportDimensions() - setPan({ - x: (width - CANVAS_SIZE) / 2, - y: (height - CANVAS_SIZE) / 2, - }) - }, []) +// Main Canvas Component +function WorkflowCanvas() { + // State + const [nodes, setNodes, onNodesChange] = useNodesState([]) + const [edges, setEdges, onEdgesChange] = useEdgesState([]) + const { project } = useReactFlow() - const constrainPan = useCallback( - (newPan: { x: number; y: number }, currentZoom: number) => { - const { width, height } = CoordinateTransformer.getViewportDimensions() - - // Calculate the scaled canvas size - const scaledCanvasWidth = CANVAS_SIZE * currentZoom - const scaledCanvasHeight = CANVAS_SIZE * currentZoom - - // Calculate the maximum allowed pan values - const minX = Math.min(0, width - scaledCanvasWidth) - const minY = Math.min(0, height - scaledCanvasHeight) - - return { - x: Math.min(0, Math.max(minX, newPan.x)), - y: Math.min(0, Math.max(minY, newPan.y)), - } - }, - [] + // Handlers + const onConnect = useCallback( + (connection: Connection) => setEdges((eds) => addEdge(connection, eds)), + [setEdges] ) - const handleDragOver = (e: React.DragEvent) => { - e.preventDefault() - e.dataTransfer.dropEffect = 'copy' - } + const onDrop = useCallback( + (event: React.DragEvent) => { + event.preventDefault() - const handleDrop = (e: React.DragEvent) => { - e.preventDefault() + try { + const reactFlowBounds = event.currentTarget.getBoundingClientRect() + const position = project({ + x: event.clientX - reactFlowBounds.left, + y: event.clientY - reactFlowBounds.top, + }) - try { - const { type } = JSON.parse(e.dataTransfer.getData('application/json')) - const blockConfig = getBlock(type) + const { type } = JSON.parse( + event.dataTransfer.getData('application/json') + ) + const blockConfig = getBlock(type) - if (!blockConfig) { - console.error('Invalid block type:', type) - return - } + if (!blockConfig) { + console.error('Invalid block type:', type) + return + } - const canvasElement = e.currentTarget as HTMLElement - const dropPoint = { - x: e.clientX, - y: e.clientY, - } - - // Convert drop coordinates to canvas space - const canvasPoint = CoordinateTransformer.viewportToCanvas( - CoordinateTransformer.clientToViewport(dropPoint), - canvasElement - ) - - setBlocks((prev) => [ - ...prev, - { + const newNode = { id: crypto.randomUUID(), - type, - position: canvasPoint, - config: blockConfig, - }, - ]) - } catch (err) { - console.error('Error dropping block:', err) - } - } + type: 'workflowBlock', + position, + data: { + type, + config: blockConfig, + name: `${blockConfig.toolbar.title} ${ + nodes.filter((n) => n.data.type === type).length + 1 + }`, + }, + } - const handleWheel = useCallback( - (e: React.WheelEvent) => { - if (e.ctrlKey || e.metaKey) { - e.preventDefault() - const delta = -e.deltaY * ZOOM_SPEED - setZoom((prevZoom) => { - if ( - (prevZoom >= MAX_ZOOM && delta > 0) || - (prevZoom <= MIN_ZOOM && delta < 0) - ) { - return prevZoom - } - const newZoom = Math.min( - MAX_ZOOM, - Math.max(MIN_ZOOM, prevZoom + delta) - ) - setPan((prevPan) => constrainPan(prevPan, newZoom)) - return newZoom - }) - } else { - setPan((prevPan) => - constrainPan( - { - x: prevPan.x - e.deltaX, - y: prevPan.y - e.deltaY, - }, - zoom - ) - ) + setNodes((nds) => [...nds, newNode]) + } catch (err) { + console.error('Error dropping block:', err) } }, - [constrainPan, zoom] - ) - - const handleMouseDown = useCallback( - (e: React.MouseEvent) => { - if (e.button === 1 || e.button === 0) { - setIsPanning(true) - const viewportPoint = CoordinateTransformer.clientToViewport({ - x: e.clientX, - y: e.clientY, - }) - setStartPanPos({ - x: viewportPoint.x - pan.x, - y: viewportPoint.y - pan.y, - }) - } - }, - [pan] - ) - - const handleMouseMove = useCallback( - (e: React.MouseEvent) => { - if (isPanning) { - const viewportPoint = CoordinateTransformer.clientToViewport({ - x: e.clientX, - y: e.clientY, - }) - setPan((prevPan) => - constrainPan( - { - x: viewportPoint.x - startPanPos.x, - y: viewportPoint.y - startPanPos.y, - }, - zoom - ) - ) - } - }, - [isPanning, startPanPos, zoom, constrainPan] - ) - - const handleMouseUp = useCallback(() => { - setIsPanning(false) - }, []) - - useEffect(() => { - const preventDefaultZoom = (e: WheelEvent) => { - if (e.ctrlKey || e.metaKey) { - e.preventDefault() - } - } - - document.addEventListener('wheel', preventDefaultZoom, { passive: false }) - return () => document.removeEventListener('wheel', preventDefaultZoom) - }, []) - - const updateBlockPosition = useCallback( - (id: string, newPosition: { x: number; y: number }) => { - setBlocks((prevBlocks) => - prevBlocks.map((block) => - block.id === id ? { ...block, position: newPosition } : block - ) - ) - }, - [] + [project, nodes, setNodes] ) return ( -
-
+ e.preventDefault()} + fitView + maxZoom={1} + panOnScroll > - {blocks.map((block, index) => { - const typeCount = blocks - .slice(0, index + 1) - .filter((b) => b.type === block.type).length - - return ( - - ) - })} -
+ +
) } + +export default function Workflow() { + return ( + + + + ) +} diff --git a/app/w/components/blocks/components/toolbar-block/toolbar-block.tsx b/app/w/components/blocks/components/toolbar-block/toolbar-block.tsx index 2dd80470ef..ce9c4b5f48 100644 --- a/app/w/components/blocks/components/toolbar-block/toolbar-block.tsx +++ b/app/w/components/blocks/components/toolbar-block/toolbar-block.tsx @@ -10,6 +10,7 @@ export function ToolbarBlock({ config }: ToolbarBlockProps) { 'application/json', JSON.stringify({ type: config.type }) ) + e.dataTransfer.effectAllowed = 'move' } return ( diff --git a/app/w/components/blocks/components/workflow-block/components/connection/connection-point.tsx b/app/w/components/blocks/components/workflow-block/components/connection/connection-point.tsx index b75afb908e..40bc10bde6 100644 --- a/app/w/components/blocks/components/workflow-block/components/connection/connection-point.tsx +++ b/app/w/components/blocks/components/workflow-block/components/connection/connection-point.tsx @@ -1,55 +1,132 @@ import { cn } from '@/lib/utils' import { CoordinateTransformer } from '@/app/w/lib/coordinates' +import { useState, useCallback, useEffect } from 'react' interface ConnectionPointProps { position: 'top' | 'bottom' } +interface ConnectionLine { + start: { x: number; y: number } + end: { x: number; y: number } +} + export function ConnectionPoint({ position }: ConnectionPointProps) { - const handleClick = (e: React.MouseEvent) => { + const [isDrawing, setIsDrawing] = useState(false) + const [line, setLine] = useState(null) + + const handleMouseDown = useCallback((e: React.MouseEvent) => { + e.stopPropagation() + const canvasElement = e.currentTarget.closest( '[style*="transform"]' ) as HTMLElement if (!canvasElement) return - const elementPosition = CoordinateTransformer.getElementCanvasPosition( - e.currentTarget, - canvasElement - ) - - // Test distance calculation - const testPoint = { - x: e.clientX + 100, - y: e.clientY + 100, + // Get raw client coordinates + const startPoint = { + x: e.clientX, + y: e.clientY, } - const distance = CoordinateTransformer.getTransformedDistance( - { x: e.clientX, y: e.clientY }, - testPoint, + // Use the same conversion pattern as drag and drop + const canvasPoint = CoordinateTransformer.viewportToCanvas( + CoordinateTransformer.clientToViewport(startPoint), canvasElement ) - console.log({ - viewport: CoordinateTransformer.getViewportDimensions(), - canvasTransform: CoordinateTransformer.getCanvasTransform(canvasElement), - elementPosition, - transformedDistance: distance, + setIsDrawing(true) + setLine({ + start: canvasPoint, + end: canvasPoint, }) - } + }, []) + + const handleMouseMove = useCallback( + (e: MouseEvent) => { + if (!isDrawing || !line) return + + const canvasElement = document.querySelector( + '[style*="transform"]' + ) as HTMLElement + if (!canvasElement) return + + // Convert current mouse position to canvas coordinates + const currentPoint = CoordinateTransformer.viewportToCanvas( + CoordinateTransformer.clientToViewport({ + x: e.clientX, + y: e.clientY, + }), + canvasElement + ) + + setLine((prev) => + prev + ? { + ...prev, + end: currentPoint, + } + : null + ) + }, + [isDrawing, line] + ) + + const handleMouseUp = useCallback(() => { + setIsDrawing(false) + setLine(null) + }, []) + + // Add and remove global mouse event listeners + useEffect(() => { + if (isDrawing) { + document.addEventListener('mousemove', handleMouseMove) + document.addEventListener('mouseup', handleMouseUp) + return () => { + document.removeEventListener('mousemove', handleMouseMove) + document.removeEventListener('mouseup', handleMouseUp) + } + } + }, [isDrawing, handleMouseMove, handleMouseUp]) return ( -
+
+ {line && ( + + + )} - /> + ) } diff --git a/app/w/components/blocks/components/workflow-block/workflow-block.tsx b/app/w/components/blocks/components/workflow-block/workflow-block.tsx index 74b569cbc3..57d730cb90 100644 --- a/app/w/components/blocks/components/workflow-block/workflow-block.tsx +++ b/app/w/components/blocks/components/workflow-block/workflow-block.tsx @@ -1,131 +1,53 @@ import { Card } from '@/components/ui/card' import { BlockConfig, SubBlockConfig } from '../../types/block' -import { cn } from '@/lib/utils' import { SubBlock } from './components/sub-block/sub-block' -import { useCallback, useState, MouseEvent, useEffect } from 'react' import { ConnectionPoint } from './components/connection/connection-point' +import { Handle, Position } from 'reactflow' -export interface WorkflowBlockProps { +interface WorkflowBlockProps { id: string type: string position: { x: number; y: number } config: BlockConfig name: string - onPositionUpdate: (id: string, position: { x: number; y: number }) => void - zoom: number } -function groupSubBlocks(subBlocks: SubBlockConfig[]) { - const rows: SubBlockConfig[][] = [] - let currentRow: SubBlockConfig[] = [] - let currentRowWidth = 0 +export function WorkflowBlock({ id, type, config, name }: WorkflowBlockProps) { + const { toolbar, workflow } = config - subBlocks.forEach((block) => { - const blockWidth = block.layout === 'half' ? 0.5 : 1 + function groupSubBlocks(subBlocks: SubBlockConfig[]) { + const rows: SubBlockConfig[][] = [] + let currentRow: SubBlockConfig[] = [] + let currentRowWidth = 0 - if (currentRowWidth + blockWidth > 1) { - rows.push([...currentRow]) - currentRow = [block] - currentRowWidth = blockWidth - } else { - currentRow.push(block) - currentRowWidth += blockWidth + subBlocks.forEach((block) => { + const blockWidth = block.layout === 'half' ? 0.5 : 1 + if (currentRowWidth + blockWidth > 1) { + rows.push([...currentRow]) + currentRow = [block] + currentRowWidth = blockWidth + } else { + currentRow.push(block) + currentRowWidth += blockWidth + } + }) + + if (currentRow.length > 0) { + rows.push(currentRow) } - }) - if (currentRow.length > 0) { - rows.push(currentRow) + return rows } - return rows -} - -export function WorkflowBlock({ - id, - type, - position, - config, - name, - onPositionUpdate, - zoom, -}: WorkflowBlockProps) { - const [isDragging, setIsDragging] = useState(false) - const [dragOffset, setDragOffset] = useState({ x: 0, y: 0 }) - - const handleMouseDown = useCallback( - (e: MouseEvent) => { - // Don't handle drag if clicking on a connection point - if ((e.target as HTMLElement).closest('[data-connection-point]')) { - return - } - - e.stopPropagation() - setIsDragging(true) - - // Account for the sidebar width (344px) and control bar height (56px) - const sidebarWidth = 344 // 72px (sidebar) + 272px (toolbar) - const controlBarHeight = 56 - - const rect = e.currentTarget.getBoundingClientRect() - setDragOffset({ - x: (e.clientX - sidebarWidth) / zoom - position.x, - y: (e.clientY - controlBarHeight) / zoom - position.y, - }) - }, - [zoom, position] - ) - - const handleMouseMove = useCallback( - (e: MouseEvent) => { - if (isDragging) { - e.stopPropagation() - - // Account for the sidebar width and control bar height - const sidebarWidth = 344 - const controlBarHeight = 56 - - const newX = (e.clientX - sidebarWidth) / zoom - dragOffset.x - const newY = (e.clientY - controlBarHeight) / zoom - dragOffset.y - - onPositionUpdate(id, { x: newX, y: newY }) - } - }, - [id, isDragging, dragOffset, onPositionUpdate, zoom] - ) - - const handleMouseUp = useCallback(() => { - setIsDragging(false) - }, []) - - // Add event listeners to handle dragging outside the block - useEffect(() => { - if (isDragging) { - document.addEventListener('mousemove', handleMouseMove as any) - document.addEventListener('mouseup', handleMouseUp) - return () => { - document.removeEventListener('mousemove', handleMouseMove as any) - document.removeEventListener('mouseup', handleMouseUp) - } - } - }, [isDragging, handleMouseMove, handleMouseUp]) - - const { toolbar, workflow } = config const subBlockRows = groupSubBlocks(workflow.subBlocks) return ( - - + +
(
@@ -155,7 +76,11 @@ export function WorkflowBlock({ ))}
- + ) } diff --git a/package-lock.json b/package-lock.json index f5539299da..b020cc9b76 100644 --- a/package-lock.json +++ b/package-lock.json @@ -24,6 +24,7 @@ "next": "15.1.3", "react": "^18.2.0", "react-dom": "^18.2.0", + "reactflow": "^11.11.4", "tailwind-merge": "^2.6.0", "tailwindcss-animate": "^1.0.7" }, @@ -1831,6 +1832,108 @@ "integrity": "sha512-A9+lCBZoaMJlVKcRBz2YByCG+Cp2t6nAnMnNba+XiWxnj6r4JUFqfsgwocMBZU9LPtdxC6wB56ySYpc7LQIoJg==", "license": "MIT" }, + "node_modules/@reactflow/background": { + "version": "11.3.14", + "resolved": "https://registry.npmjs.org/@reactflow/background/-/background-11.3.14.tgz", + "integrity": "sha512-Gewd7blEVT5Lh6jqrvOgd4G6Qk17eGKQfsDXgyRSqM+CTwDqRldG2LsWN4sNeno6sbqVIC2fZ+rAUBFA9ZEUDA==", + "license": "MIT", + "dependencies": { + "@reactflow/core": "11.11.4", + "classcat": "^5.0.3", + "zustand": "^4.4.1" + }, + "peerDependencies": { + "react": ">=17", + "react-dom": ">=17" + } + }, + "node_modules/@reactflow/controls": { + "version": "11.2.14", + "resolved": "https://registry.npmjs.org/@reactflow/controls/-/controls-11.2.14.tgz", + "integrity": "sha512-MiJp5VldFD7FrqaBNIrQ85dxChrG6ivuZ+dcFhPQUwOK3HfYgX2RHdBua+gx+40p5Vw5It3dVNp/my4Z3jF0dw==", + "license": "MIT", + "dependencies": { + "@reactflow/core": "11.11.4", + "classcat": "^5.0.3", + "zustand": "^4.4.1" + }, + "peerDependencies": { + "react": ">=17", + "react-dom": ">=17" + } + }, + "node_modules/@reactflow/core": { + "version": "11.11.4", + "resolved": "https://registry.npmjs.org/@reactflow/core/-/core-11.11.4.tgz", + "integrity": "sha512-H4vODklsjAq3AMq6Np4LE12i1I4Ta9PrDHuBR9GmL8uzTt2l2jh4CiQbEMpvMDcp7xi4be0hgXj+Ysodde/i7Q==", + "license": "MIT", + "dependencies": { + "@types/d3": "^7.4.0", + "@types/d3-drag": "^3.0.1", + "@types/d3-selection": "^3.0.3", + "@types/d3-zoom": "^3.0.1", + "classcat": "^5.0.3", + "d3-drag": "^3.0.0", + "d3-selection": "^3.0.0", + "d3-zoom": "^3.0.0", + "zustand": "^4.4.1" + }, + "peerDependencies": { + "react": ">=17", + "react-dom": ">=17" + } + }, + "node_modules/@reactflow/minimap": { + "version": "11.7.14", + "resolved": "https://registry.npmjs.org/@reactflow/minimap/-/minimap-11.7.14.tgz", + "integrity": "sha512-mpwLKKrEAofgFJdkhwR5UQ1JYWlcAAL/ZU/bctBkuNTT1yqV+y0buoNVImsRehVYhJwffSWeSHaBR5/GJjlCSQ==", + "license": "MIT", + "dependencies": { + "@reactflow/core": "11.11.4", + "@types/d3-selection": "^3.0.3", + "@types/d3-zoom": "^3.0.1", + "classcat": "^5.0.3", + "d3-selection": "^3.0.0", + "d3-zoom": "^3.0.0", + "zustand": "^4.4.1" + }, + "peerDependencies": { + "react": ">=17", + "react-dom": ">=17" + } + }, + "node_modules/@reactflow/node-resizer": { + "version": "2.2.14", + "resolved": "https://registry.npmjs.org/@reactflow/node-resizer/-/node-resizer-2.2.14.tgz", + "integrity": "sha512-fwqnks83jUlYr6OHcdFEedumWKChTHRGw/kbCxj0oqBd+ekfs+SIp4ddyNU0pdx96JIm5iNFS0oNrmEiJbbSaA==", + "license": "MIT", + "dependencies": { + "@reactflow/core": "11.11.4", + "classcat": "^5.0.4", + "d3-drag": "^3.0.0", + "d3-selection": "^3.0.0", + "zustand": "^4.4.1" + }, + "peerDependencies": { + "react": ">=17", + "react-dom": ">=17" + } + }, + "node_modules/@reactflow/node-toolbar": { + "version": "1.3.14", + "resolved": "https://registry.npmjs.org/@reactflow/node-toolbar/-/node-toolbar-1.3.14.tgz", + "integrity": "sha512-rbynXQnH/xFNu4P9H+hVqlEUafDCkEoCy0Dg9mG22Sg+rY/0ck6KkrAQrYrTgXusd+cEJOMK0uOOFCK2/5rSGQ==", + "license": "MIT", + "dependencies": { + "@reactflow/core": "11.11.4", + "classcat": "^5.0.3", + "zustand": "^4.4.1" + }, + "peerDependencies": { + "react": ">=17", + "react-dom": ">=17" + } + }, "node_modules/@swc/counter": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz", @@ -1846,6 +1949,265 @@ "tslib": "^2.8.0" } }, + "node_modules/@types/d3": { + "version": "7.4.3", + "resolved": "https://registry.npmjs.org/@types/d3/-/d3-7.4.3.tgz", + "integrity": "sha512-lZXZ9ckh5R8uiFVt8ogUNf+pIrK4EsWrx2Np75WvF/eTpJ0FMHNhjXk8CKEx/+gpHbNQyJWehbFaTvqmHWB3ww==", + "license": "MIT", + "dependencies": { + "@types/d3-array": "*", + "@types/d3-axis": "*", + "@types/d3-brush": "*", + "@types/d3-chord": "*", + "@types/d3-color": "*", + "@types/d3-contour": "*", + "@types/d3-delaunay": "*", + "@types/d3-dispatch": "*", + "@types/d3-drag": "*", + "@types/d3-dsv": "*", + "@types/d3-ease": "*", + "@types/d3-fetch": "*", + "@types/d3-force": "*", + "@types/d3-format": "*", + "@types/d3-geo": "*", + "@types/d3-hierarchy": "*", + "@types/d3-interpolate": "*", + "@types/d3-path": "*", + "@types/d3-polygon": "*", + "@types/d3-quadtree": "*", + "@types/d3-random": "*", + "@types/d3-scale": "*", + "@types/d3-scale-chromatic": "*", + "@types/d3-selection": "*", + "@types/d3-shape": "*", + "@types/d3-time": "*", + "@types/d3-time-format": "*", + "@types/d3-timer": "*", + "@types/d3-transition": "*", + "@types/d3-zoom": "*" + } + }, + "node_modules/@types/d3-array": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.1.tgz", + "integrity": "sha512-Y2Jn2idRrLzUfAKV2LyRImR+y4oa2AntrgID95SHJxuMUrkNXmanDSed71sRNZysveJVt1hLLemQZIady0FpEg==", + "license": "MIT" + }, + "node_modules/@types/d3-axis": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-axis/-/d3-axis-3.0.6.tgz", + "integrity": "sha512-pYeijfZuBd87T0hGn0FO1vQ/cgLk6E1ALJjfkC0oJ8cbwkZl3TpgS8bVBLZN+2jjGgg38epgxb2zmoGtSfvgMw==", + "license": "MIT", + "dependencies": { + "@types/d3-selection": "*" + } + }, + "node_modules/@types/d3-brush": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-brush/-/d3-brush-3.0.6.tgz", + "integrity": "sha512-nH60IZNNxEcrh6L1ZSMNA28rj27ut/2ZmI3r96Zd+1jrZD++zD3LsMIjWlvg4AYrHn/Pqz4CF3veCxGjtbqt7A==", + "license": "MIT", + "dependencies": { + "@types/d3-selection": "*" + } + }, + "node_modules/@types/d3-chord": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-chord/-/d3-chord-3.0.6.tgz", + "integrity": "sha512-LFYWWd8nwfwEmTZG9PfQxd17HbNPksHBiJHaKuY1XeqscXacsS2tyoo6OdRsjf+NQYeB6XrNL3a25E3gH69lcg==", + "license": "MIT" + }, + "node_modules/@types/d3-color": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.3.tgz", + "integrity": "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==", + "license": "MIT" + }, + "node_modules/@types/d3-contour": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-contour/-/d3-contour-3.0.6.tgz", + "integrity": "sha512-BjzLgXGnCWjUSYGfH1cpdo41/hgdWETu4YxpezoztawmqsvCeep+8QGfiY6YbDvfgHz/DkjeIkkZVJavB4a3rg==", + "license": "MIT", + "dependencies": { + "@types/d3-array": "*", + "@types/geojson": "*" + } + }, + "node_modules/@types/d3-delaunay": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-delaunay/-/d3-delaunay-6.0.4.tgz", + "integrity": "sha512-ZMaSKu4THYCU6sV64Lhg6qjf1orxBthaC161plr5KuPHo3CNm8DTHiLw/5Eq2b6TsNP0W0iJrUOFscY6Q450Hw==", + "license": "MIT" + }, + "node_modules/@types/d3-dispatch": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-dispatch/-/d3-dispatch-3.0.6.tgz", + "integrity": "sha512-4fvZhzMeeuBJYZXRXrRIQnvUYfyXwYmLsdiN7XXmVNQKKw1cM8a5WdID0g1hVFZDqT9ZqZEY5pD44p24VS7iZQ==", + "license": "MIT" + }, + "node_modules/@types/d3-drag": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@types/d3-drag/-/d3-drag-3.0.7.tgz", + "integrity": "sha512-HE3jVKlzU9AaMazNufooRJ5ZpWmLIoc90A37WU2JMmeq28w1FQqCZswHZ3xR+SuxYftzHq6WU6KJHvqxKzTxxQ==", + "license": "MIT", + "dependencies": { + "@types/d3-selection": "*" + } + }, + "node_modules/@types/d3-dsv": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@types/d3-dsv/-/d3-dsv-3.0.7.tgz", + "integrity": "sha512-n6QBF9/+XASqcKK6waudgL0pf/S5XHPPI8APyMLLUHd8NqouBGLsU8MgtO7NINGtPBtk9Kko/W4ea0oAspwh9g==", + "license": "MIT" + }, + "node_modules/@types/d3-ease": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-3.0.2.tgz", + "integrity": "sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==", + "license": "MIT" + }, + "node_modules/@types/d3-fetch": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@types/d3-fetch/-/d3-fetch-3.0.7.tgz", + "integrity": "sha512-fTAfNmxSb9SOWNB9IoG5c8Hg6R+AzUHDRlsXsDZsNp6sxAEOP0tkP3gKkNSO/qmHPoBFTxNrjDprVHDQDvo5aA==", + "license": "MIT", + "dependencies": { + "@types/d3-dsv": "*" + } + }, + "node_modules/@types/d3-force": { + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/@types/d3-force/-/d3-force-3.0.10.tgz", + "integrity": "sha512-ZYeSaCF3p73RdOKcjj+swRlZfnYpK1EbaDiYICEEp5Q6sUiqFaFQ9qgoshp5CzIyyb/yD09kD9o2zEltCexlgw==", + "license": "MIT" + }, + "node_modules/@types/d3-format": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-format/-/d3-format-3.0.4.tgz", + "integrity": "sha512-fALi2aI6shfg7vM5KiR1wNJnZ7r6UuggVqtDA+xiEdPZQwy/trcQaHnwShLuLdta2rTymCNpxYTiMZX/e09F4g==", + "license": "MIT" + }, + "node_modules/@types/d3-geo": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@types/d3-geo/-/d3-geo-3.1.0.tgz", + "integrity": "sha512-856sckF0oP/diXtS4jNsiQw/UuK5fQG8l/a9VVLeSouf1/PPbBE1i1W852zVwKwYCBkFJJB7nCFTbk6UMEXBOQ==", + "license": "MIT", + "dependencies": { + "@types/geojson": "*" + } + }, + "node_modules/@types/d3-hierarchy": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/@types/d3-hierarchy/-/d3-hierarchy-3.1.7.tgz", + "integrity": "sha512-tJFtNoYBtRtkNysX1Xq4sxtjK8YgoWUNpIiUee0/jHGRwqvzYxkq0hGVbbOGSz+JgFxxRu4K8nb3YpG3CMARtg==", + "license": "MIT" + }, + "node_modules/@types/d3-interpolate": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz", + "integrity": "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==", + "license": "MIT", + "dependencies": { + "@types/d3-color": "*" + } + }, + "node_modules/@types/d3-path": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.1.0.tgz", + "integrity": "sha512-P2dlU/q51fkOc/Gfl3Ul9kicV7l+ra934qBFXCFhrZMOL6du1TM0pm1ThYvENukyOn5h9v+yMJ9Fn5JK4QozrQ==", + "license": "MIT" + }, + "node_modules/@types/d3-polygon": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-polygon/-/d3-polygon-3.0.2.tgz", + "integrity": "sha512-ZuWOtMaHCkN9xoeEMr1ubW2nGWsp4nIql+OPQRstu4ypeZ+zk3YKqQT0CXVe/PYqrKpZAi+J9mTs05TKwjXSRA==", + "license": "MIT" + }, + "node_modules/@types/d3-quadtree": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-quadtree/-/d3-quadtree-3.0.6.tgz", + "integrity": "sha512-oUzyO1/Zm6rsxKRHA1vH0NEDG58HrT5icx/azi9MF1TWdtttWl0UIUsjEQBBh+SIkrpd21ZjEv7ptxWys1ncsg==", + "license": "MIT" + }, + "node_modules/@types/d3-random": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/d3-random/-/d3-random-3.0.3.tgz", + "integrity": "sha512-Imagg1vJ3y76Y2ea0871wpabqp613+8/r0mCLEBfdtqC7xMSfj9idOnmBYyMoULfHePJyxMAw3nWhJxzc+LFwQ==", + "license": "MIT" + }, + "node_modules/@types/d3-scale": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.8.tgz", + "integrity": "sha512-gkK1VVTr5iNiYJ7vWDI+yUFFlszhNMtVeneJ6lUTKPjprsvLLI9/tgEGiXJOnlINJA8FyA88gfnQsHbybVZrYQ==", + "license": "MIT", + "dependencies": { + "@types/d3-time": "*" + } + }, + "node_modules/@types/d3-scale-chromatic": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@types/d3-scale-chromatic/-/d3-scale-chromatic-3.1.0.tgz", + "integrity": "sha512-iWMJgwkK7yTRmWqRB5plb1kadXyQ5Sj8V/zYlFGMUBbIPKQScw+Dku9cAAMgJG+z5GYDoMjWGLVOvjghDEFnKQ==", + "license": "MIT" + }, + "node_modules/@types/d3-selection": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/@types/d3-selection/-/d3-selection-3.0.11.tgz", + "integrity": "sha512-bhAXu23DJWsrI45xafYpkQ4NtcKMwWnAC/vKrd2l+nxMFuvOT3XMYTIj2opv8vq8AO5Yh7Qac/nSeP/3zjTK0w==", + "license": "MIT" + }, + "node_modules/@types/d3-shape": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.7.tgz", + "integrity": "sha512-VLvUQ33C+3J+8p+Daf+nYSOsjB4GXp19/S/aGo60m9h1v6XaxjiT82lKVWJCfzhtuZ3yD7i/TPeC/fuKLLOSmg==", + "license": "MIT", + "dependencies": { + "@types/d3-path": "*" + } + }, + "node_modules/@types/d3-time": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.4.tgz", + "integrity": "sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==", + "license": "MIT" + }, + "node_modules/@types/d3-time-format": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@types/d3-time-format/-/d3-time-format-4.0.3.tgz", + "integrity": "sha512-5xg9rC+wWL8kdDj153qZcsJ0FWiFt0J5RB6LYUNZjwSnesfblqrI/bJ1wBdJ8OQfncgbJG5+2F+qfqnqyzYxyg==", + "license": "MIT" + }, + "node_modules/@types/d3-timer": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.2.tgz", + "integrity": "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==", + "license": "MIT" + }, + "node_modules/@types/d3-transition": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/@types/d3-transition/-/d3-transition-3.0.9.tgz", + "integrity": "sha512-uZS5shfxzO3rGlu0cC3bjmMFKsXv+SmZZcgp0KD22ts4uGXp5EVYGzu/0YdwZeKmddhcAccYtREJKkPfXkZuCg==", + "license": "MIT", + "dependencies": { + "@types/d3-selection": "*" + } + }, + "node_modules/@types/d3-zoom": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@types/d3-zoom/-/d3-zoom-3.0.8.tgz", + "integrity": "sha512-iqMC4/YlFCSlO8+2Ii1GGGliCAY4XdeG748w5vQUbevlbDu0zSjH/+jojorQVBK/se0j6DUFNPBGSqD3YWYnDw==", + "license": "MIT", + "dependencies": { + "@types/d3-interpolate": "*", + "@types/d3-selection": "*" + } + }, + "node_modules/@types/geojson": { + "version": "7946.0.15", + "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.15.tgz", + "integrity": "sha512-9oSxFzDCT2Rj6DfcHF8G++jxBKS7mBqXl5xrRW+Kbvjry6Uduya2iiwqHPhVXpasAVMBYKkEPGgKhd3+/HZ6xA==", + "license": "MIT" + }, "node_modules/@types/node": { "version": "20.17.11", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.11.tgz", @@ -2072,6 +2434,12 @@ "url": "https://polar.sh/cva" } }, + "node_modules/classcat": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/classcat/-/classcat-5.0.5.tgz", + "integrity": "sha512-JhZUT7JFcQy/EzW605k/ktHtncoo9vnyW/2GspNYwFlN1C/WmjuV/xtS04e9SOkL2sTdw0VAZ2UGCcQ9lR6p6w==", + "license": "MIT" + }, "node_modules/client-only": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", @@ -2653,6 +3021,111 @@ "devOptional": true, "license": "MIT" }, + "node_modules/d3-color": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz", + "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-dispatch": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-3.0.1.tgz", + "integrity": "sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-drag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-3.0.0.tgz", + "integrity": "sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==", + "license": "ISC", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-selection": "3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-ease": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz", + "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-interpolate": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz", + "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==", + "license": "ISC", + "dependencies": { + "d3-color": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-selection": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz", + "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-timer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz", + "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-transition": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-3.0.1.tgz", + "integrity": "sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==", + "license": "ISC", + "dependencies": { + "d3-color": "1 - 3", + "d3-dispatch": "1 - 3", + "d3-ease": "1 - 3", + "d3-interpolate": "1 - 3", + "d3-timer": "1 - 3" + }, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "d3-selection": "2 - 3" + } + }, + "node_modules/d3-zoom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-3.0.0.tgz", + "integrity": "sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==", + "license": "ISC", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-drag": "2 - 3", + "d3-interpolate": "1 - 3", + "d3-selection": "2 - 3", + "d3-transition": "2 - 3" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/detect-libc": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz", @@ -3518,6 +3991,24 @@ } } }, + "node_modules/reactflow": { + "version": "11.11.4", + "resolved": "https://registry.npmjs.org/reactflow/-/reactflow-11.11.4.tgz", + "integrity": "sha512-70FOtJkUWH3BAOsN+LU9lCrKoKbtOPnz2uq0CV2PLdNSwxTXOhCbsZr50GmZ+Rtw3jx8Uv7/vBFtCGixLfd4Og==", + "license": "MIT", + "dependencies": { + "@reactflow/background": "11.3.14", + "@reactflow/controls": "11.2.14", + "@reactflow/core": "11.11.4", + "@reactflow/minimap": "11.7.14", + "@reactflow/node-resizer": "2.2.14", + "@reactflow/node-toolbar": "1.3.14" + }, + "peerDependencies": { + "react": ">=17", + "react-dom": ">=17" + } + }, "node_modules/read-cache": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", @@ -4092,6 +4583,15 @@ } } }, + "node_modules/use-sync-external-store": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.4.0.tgz", + "integrity": "sha512-9WXSPC5fMv61vaupRkCKCxsPxBocVnwakBEkMIHHpkTTg6icbJtg6jzgtLDm4bl3cSHAca52rYWih0k4K3PfHw==", + "license": "MIT", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -4215,6 +4715,34 @@ "engines": { "node": ">= 14" } + }, + "node_modules/zustand": { + "version": "4.5.6", + "resolved": "https://registry.npmjs.org/zustand/-/zustand-4.5.6.tgz", + "integrity": "sha512-ibr/n1hBzLLj5Y+yUcU7dYw8p6WnIVzdJbnX+1YpaScvZVF2ziugqHs+LAmHw4lWO9c/zRj+K1ncgWDQuthEdQ==", + "license": "MIT", + "dependencies": { + "use-sync-external-store": "^1.2.2" + }, + "engines": { + "node": ">=12.7.0" + }, + "peerDependencies": { + "@types/react": ">=16.8", + "immer": ">=9.0.6", + "react": ">=16.8" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "immer": { + "optional": true + }, + "react": { + "optional": true + } + } } } } diff --git a/package.json b/package.json index f07eb73765..8ed0c502e7 100644 --- a/package.json +++ b/package.json @@ -25,6 +25,7 @@ "next": "15.1.3", "react": "^18.2.0", "react-dom": "^18.2.0", + "reactflow": "^11.11.4", "tailwind-merge": "^2.6.0", "tailwindcss-animate": "^1.0.7" },