import { memo, useMemo, type ReactElement } from "react"; import type { NodeMeshState } from "@fusion/core"; export interface MeshTopologyProps { nodes: NodeMeshState[]; className?: string; } const STATUS_COLORS: Record = { online: "var(--success, var(--color-success))", offline: "var(--text-dim)", connecting: "var(--color-warning)", error: "var(--color-error)", }; const NODE_RADIUS = 28; const LABEL_OFFSET = 12; const MIN_VIEWBOX_SIZE = 300; const MAX_REMOTE_DISTANCE = 120; function MeshTopologyInner({ nodes, className }: MeshTopologyProps): ReactElement { const localNode = useMemo(() => nodes.find((n) => n.nodeType === "local") ?? nodes[0], [nodes]); const remoteNodes = useMemo(() => nodes.filter((n) => n.nodeId !== localNode?.nodeId), [nodes, localNode?.nodeId]); const viewBoxSize = useMemo(() => MIN_VIEWBOX_SIZE + (Math.max(0, remoteNodes.length - 4) * 20), [remoteNodes.length]); const centerX = viewBoxSize / 2; const centerY = viewBoxSize / 2; const nodePositions = useMemo(() => { const positions = new Map(); if (localNode) { positions.set(localNode.nodeId, { x: centerX, y: centerY, node: localNode }); } if (remoteNodes.length > 0) { const distance = Math.min(MAX_REMOTE_DISTANCE, (viewBoxSize / 2) - NODE_RADIUS - 10); const angleStep = (2 * Math.PI) / remoteNodes.length; const startAngle = -Math.PI / 2; remoteNodes.forEach((node, index) => { const angle = startAngle + index * angleStep; positions.set(node.nodeId, { node, x: centerX + distance * Math.cos(angle), y: centerY + distance * Math.sin(angle), }); }); } return positions; }, [centerX, centerY, localNode, remoteNodes, viewBoxSize]); const links = useMemo(() => { const seen = new Set(); const result: Array<{ key: string; from: { x: number; y: number }; to: { x: number; y: number } }> = []; for (const node of nodes) { const from = nodePositions.get(node.nodeId); if (!from) continue; for (const peer of node.knownPeers) { const to = nodePositions.get(peer.peerNodeId); if (!to) continue; const key = [node.nodeId, peer.peerNodeId].sort().join("::"); if (seen.has(key)) continue; seen.add(key); result.push({ key, from, to }); } } return result; }, [nodePositions, nodes]); if (nodes.length === 0) { return

No nodes to display

; } return (
{links.map((link) => ( ))} {Array.from(nodePositions.values()).map(({ node, x, y }) => ( {node.nodeName.length > 12 ? `${node.nodeName.slice(0, 10)}…` : node.nodeName} {node.nodeType === "local" ? "L" : "R"} ))}
Online
Offline
Connecting
Error
{links.length === 0 &&

Peer-to-peer discovery data unavailable.

}
); } export const MeshTopology = memo(MeshTopologyInner);