// Draggable node with avatar function Node({ node, selected, onSelect, onDrag, onDragEnd, onStartConnect, onFinishConnect, onDelete, dragging, worldZoom, underAttack }){ const ref = React.useRef(null); const moveState = React.useRef(null); const onPointerDown = (e) => { if (e.target.closest('.connect-handle')) return; if (e.target.closest('.delete-btn')) return; e.stopPropagation(); onSelect(node.id); const startX = e.clientX, startY = e.clientY; const ox = node.x, oy = node.y; moveState.current = { startX, startY, ox, oy, moved:false }; e.currentTarget.setPointerCapture(e.pointerId); }; const onPointerMove = (e) => { if (!moveState.current) return; const { startX, startY, ox, oy } = moveState.current; const dx = (e.clientX - startX) / worldZoom; const dy = (e.clientY - startY) / worldZoom; if (Math.abs(dx)+Math.abs(dy) > 2) moveState.current.moved = true; onDrag(node.id, ox+dx, oy+dy); }; const onPointerUp = (e) => { if (!moveState.current) return; const moved = moveState.current.moved; moveState.current = null; onDragEnd(node.id, moved); try { e.currentTarget.releasePointerCapture(e.pointerId); } catch(_){} }; const handleConnectDown = (e) => { e.stopPropagation(); e.preventDefault(); onStartConnect(node.id, e); }; const handleFinishUp = (e) => { // If currently connecting, finish here. Parent handles routing on pointerup too. onFinishConnect(node.id); }; const isBranch = node.variant === 'branch'; const shapeClass = isBranch ? 'circle' : (node.shape || 'circle'); const avatarStyle = { background: node.color || '#fff' }; let inner; if (isBranch){ // Branch nodes are just a colored dot with an optional emoji. No image, // no fallback initial — keeps them visually "minor" vs full cards. inner = node.emoji ?