()
| 315 | } |
| 316 | |
| 317 | function frame() { |
| 318 | rafRef.current = requestAnimationFrame(frame); |
| 319 | // Idle while the panel is hidden (the shell keeps visited tabs |
| 320 | // mounted); pending work resumes on the first visible frame. |
| 321 | if (!canvas.offsetParent) return; |
| 322 | const sim = simRef.current; |
| 323 | const simActive = sim ? sim.isActive() : false; |
| 324 | if (simActive) { |
| 325 | sim?.tick(); |
| 326 | // Track the focused node while the layout is still moving. |
| 327 | const followId = followIdRef.current; |
| 328 | if (followId && sim) { |
| 329 | const node = sim.nodes.find((n) => n.id === followId); |
| 330 | if (node) { |
| 331 | const camera = cameraRef.current; |
| 332 | camera.x += (node.x - camera.x) * 0.18; |
| 333 | camera.y += (node.y - camera.y) * 0.18; |
| 334 | } |
| 335 | } else if (followFitRef.current && sim) { |
| 336 | // Keep the whole graph framed as it spreads out and settles. |
| 337 | fitCameraToNodes(sizeRef.current, sim, cameraRef.current, true); |
| 338 | } |
| 339 | } |
| 340 | if (simActive || needsRenderRef.current) { |
| 341 | needsRenderRef.current = false; |
| 342 | render(); |
| 343 | } |
| 344 | } |
| 345 | rafRef.current = requestAnimationFrame(frame); |
| 346 | |
| 347 | function onWheel(event: WheelEvent) { |
nothing calls this directly
no test coverage detected