diff --git a/src/App.tsx b/src/App.tsx index 15ef3eb..6492a03 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -23,13 +23,15 @@ import { useTheme } from './lib/themeContext' import { ContextMenu, ContextMenuContent, - ContextMenuGroup, ContextMenuItem, - ContextMenuLabel, + ContextMenuSub, + ContextMenuSubContent, + ContextMenuSubTrigger, ContextMenuTrigger, + ContextMenuGroup } from "@/components/ui/context-menu" import { Button } from '@/components/ui/button' -import { Moon, Sun } from 'lucide-react' +import { Code2, Moon, ScrollText, Sparkles, Sun, Variable } from 'lucide-react' // Generate short unique node ids when missing const genId = () => `node_${Math.random().toString(36).slice(2, 9)}` @@ -118,14 +120,13 @@ export default function App() { const click = contextTarget ?? lastClickRef.current const clientX = click?.clientX ?? window.innerWidth / 2 const clientY = click?.clientY ?? window.innerHeight / 2 - const rect = wrapperRef.current?.getBoundingClientRect() - const point = rect ? { x: clientX - rect.left, y: clientY - rect.top } : { x: clientX, y: clientY } - let position = point + let position: { x: number; y: number } try { - // project to flow coords when possible - position = rfInstance.project ? rfInstance.project(point) : point - } catch (e) { - // ignore and use raw point + // Convert screen (client) coordinates to flow coordinates — same position as where the context menu opened + const screenToFlow = rfInstance.screenToFlowPosition ?? rfInstance.project + position = screenToFlow.call(rfInstance, { x: clientX, y: clientY }) + } catch { + position = { x: clientX, y: clientY } } position = snapToGrid(position.x, position.y) @@ -154,7 +155,7 @@ export default function App() { lastClickRef.current = null setContextTarget(null) }, - [rfInstance, setNodes] + [rfInstance, setNodes, contextTarget] ) const deleteNode = React.useCallback((id: string | undefined) => { @@ -182,46 +183,64 @@ export default function App() {
- - - - + nodes={nodes} + edges={edges} + onNodesChange={onNodesChange} + onEdgesChange={onEdgesChange} + onConnect={onConnect} + nodeTypes={nodeTypes} + edgeTypes={edgeTypes} + defaultEdgeOptions={{ type: 'animated' }} + colorMode={theme as ColorMode} + snapToGrid + snapGrid={SNAP_GRID} + fitView + onInit={onInit} + > + + + +
- + {contextTarget?.type === 'node' ? ( - deleteNode(contextTarget.nodeId)}>Delete + + deleteNode(contextTarget.nodeId)}>Delete + ) : ( - <> - - New Node - createNode('config')}>Config - createNode('render')}>Renderer - createNode('variable')}>Variable - createNode('function')}>Function - - + + + Create node + + + createNode('config')}> + + Config + + createNode('render')}> + + Renderer + + createNode('variable')}> + + Variable + + createNode('function')}> + + Function + + + + + )} - + ) } diff --git a/src/styles.css b/src/styles.css index 30f6ec6..33b4bee 100644 --- a/src/styles.css +++ b/src/styles.css @@ -44,11 +44,12 @@ body { @keyframes edge-flow { from { - stroke-dashoffset: 0; - } - to { stroke-dashoffset: 14; } + + to { + stroke-dashoffset: 0; + } } /* Resize control: snappy drag, no transition or selection delay */