Fix zoom display regression: split viewport change concerns

Pass popoverVisible flag from graph-canvas tick loop to the
onViewportChange callback. The handler always updates zoomDisplay
(so NavigationControls shows correct zoom %) but only increments
viewportVersion (triggering popover repositioning) when a node is
actually selected or hovered. This avoids 60fps React reconciliation
during plain panning while keeping the zoom indicator responsive.
This commit is contained in:
Vorflux AI 2026-03-27 18:37:06 +00:00
parent 52c0a615c1
commit 76f5eaa446
3 changed files with 19 additions and 14 deletions

View file

@ -234,17 +234,14 @@ export const GraphCanvas = memo<ExtendedGraphCanvasProps>(function GraphCanvas({
return
renderNeeded.current = false
// Report viewport changes so popover positions update.
// Only fire when a popover is actually visible (selected or hovered node)
// to avoid triggering React re-renders at 60fps during plain panning.
// Report viewport changes so zoom display and popover positions update.
const vpChanged =
vp.panX !== prevVpX || vp.panY !== prevVpY || vp.zoom !== prevVpZoom
if (
(vpChanged || simActive) &&
(cur.selectedNodeId || cur.hoveredNodeId) &&
cb.current.onViewportChange
) {
cb.current.onViewportChange(vp.zoom)
if ((vpChanged || simActive) && cb.current.onViewportChange) {
cb.current.onViewportChange(
vp.zoom,
!!(cur.selectedNodeId || cur.hoveredNodeId),
)
}
if (vpChanged) {
prevVpX = vp.panX

View file

@ -170,10 +170,18 @@ export function MemoryGraph({
// Drag end handled by InputHandler
}, [])
const handleViewportChange = useCallback((zoom: number) => {
setZoomDisplay(Math.round(zoom * 100))
setViewportVersion((v) => v + 1)
}, [])
const handleViewportChange = useCallback(
(zoom: number, popoverVisible: boolean) => {
setZoomDisplay(Math.round(zoom * 100))
// Only increment viewportVersion (which triggers popover repositioning
// via activePopoverPosition useMemo) when a popover is actually visible.
// This avoids 60fps React reconciliation during plain panning/zooming.
if (popoverVisible) {
setViewportVersion((v) => v + 1)
}
},
[],
)
// Navigation
const handleAutoFit = useCallback(() => {

View file

@ -163,7 +163,7 @@ export interface GraphCanvasProps {
onNodeClick: (nodeId: string | null) => void
onNodeDragStart: (nodeId: string) => void
onNodeDragEnd: () => void
onViewportChange?: (zoom: number) => void
onViewportChange?: (zoom: number, popoverVisible: boolean) => void
canvasRef?: React.RefObject<HTMLCanvasElement | null>
simulation?: import("./canvas/simulation").ForceSimulation
viewportRef?: React.RefObject<