Skip to content

Commit 9a08c5f

Browse files
committed
fix: comprehensive graph visibility fixes
Multiple issues addressed: 1. Tab visibility - graph now re-renders when tab regains focus - Added visibilitychange event listener - Uses renderKey to force ReactFlow refresh - Calls fitView after tab becomes visible 2. Cluster mode timing - fixed race condition - Added check for clusteredData.dirNodes.length > 0 - Falls back to non-clustered mode if data not ready 3. FitView on panel open/close - Graph now refits when ImpactPanel opens/closes - Added selectedNodeId to fitView dependencies 4. Dagre layout guards - Added empty nodes guard - Added fallback positions for failed layouts - Only adds edges where both endpoints exist
1 parent 0100d5e commit 9a08c5f

1 file changed

Lines changed: 32 additions & 8 deletions

File tree

  • frontend/src/components/DependencyGraph

frontend/src/components/DependencyGraph/index.tsx

Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useEffect, useState, useCallback, useMemo } from 'react'
1+
import { useEffect, useState, useCallback, useMemo, useRef } from 'react'
22
import ReactFlow, {
33
Controls,
44
Background,
@@ -125,6 +125,7 @@ function DependencyGraphInner({ repoId, apiUrl, apiKey }: DependencyGraphProps)
125125
const [clusterByDir, setClusterByDir] = useState(false)
126126
const [expandedDirs, setExpandedDirs] = useState<Set<string>>(new Set())
127127
const [rawGraphData, setRawGraphData] = useState<any>(null)
128+
const [renderKey, setRenderKey] = useState(0) // Force re-render key
128129

129130
const { fitView } = useReactFlow()
130131
const { resolvedTheme } = useTheme()
@@ -137,6 +138,23 @@ function DependencyGraphInner({ repoId, apiUrl, apiKey }: DependencyGraphProps)
137138
if (data) setRawGraphData(data)
138139
}, [data])
139140

141+
// Handle tab visibility changes - force re-render when tab becomes visible
142+
useEffect(() => {
143+
const handleVisibilityChange = () => {
144+
if (document.visibilityState === 'visible') {
145+
// Force re-render by updating key
146+
setRenderKey(k => k + 1)
147+
// Also trigger fitView after a short delay
148+
setTimeout(() => {
149+
fitView({ padding: 0.2, duration: 200 })
150+
}, 100)
151+
}
152+
}
153+
154+
document.addEventListener('visibilitychange', handleVisibilityChange)
155+
return () => document.removeEventListener('visibilitychange', handleVisibilityChange)
156+
}, [fitView])
157+
140158
const visibleNodeIds = useMemo(() => {
141159
if (!rawGraphData || !impact.isReady) return new Set<string>()
142160

@@ -241,14 +259,17 @@ function DependencyGraphInner({ repoId, apiUrl, apiKey }: DependencyGraphProps)
241259
let flowNodes: Node[] = []
242260
let flowEdges: Edge[] = []
243261

244-
if (clusterByDir && clusteredData) {
262+
// Only use cluster mode if we have clustered data ready
263+
const useClusterMode = clusterByDir && clusteredData && clusteredData.dirNodes.length > 0
264+
265+
if (useClusterMode) {
245266
// Safety: only apply selection highlighting if the selected node is actually visible
246267
const effectiveSelectedIdCluster = selectedNodeId &&
247-
(clusteredData.visibleFiles.has(selectedNodeId) || selectedNodeId.startsWith('dir:'))
268+
(clusteredData!.visibleFiles.has(selectedNodeId) || selectedNodeId.startsWith('dir:'))
248269
? selectedNodeId : null
249270

250271
// Add directory nodes
251-
clusteredData.dirNodes.forEach(dir => {
272+
clusteredData!.dirNodes.forEach(dir => {
252273
flowNodes.push({
253274
id: dir.id,
254275
type: 'directory',
@@ -259,7 +280,7 @@ function DependencyGraphInner({ repoId, apiUrl, apiKey }: DependencyGraphProps)
259280

260281
// Add visible file nodes
261282
rawGraphData.nodes
262-
.filter((node: any) => clusteredData.visibleFiles.has(node.id))
283+
.filter((node: any) => clusteredData!.visibleFiles.has(node.id))
263284
.forEach((node: any) => {
264285
const fileName = node.label || node.id.split('/').pop()
265286
const metrics = impact.getFileMetrics(node.id)
@@ -289,7 +310,7 @@ function DependencyGraphInner({ repoId, apiUrl, apiKey }: DependencyGraphProps)
289310
})
290311

291312
// Add edges
292-
clusteredData.edges.forEach(([source, target]) => {
313+
clusteredData!.edges.forEach(([source, target]) => {
293314
flowEdges.push({
294315
id: `${source}-${target}`,
295316
source,
@@ -360,12 +381,14 @@ function DependencyGraphInner({ repoId, apiUrl, apiKey }: DependencyGraphProps)
360381
setEdges(layoutedEdges)
361382
}, [rawGraphData, impact.isReady, visibleNodeIds, selectedNodeId, selectedImpact, hoveredFileId, isDark, clusterByDir, clusteredData])
362383

384+
// Fit view when nodes change or panel opens/closes
363385
useEffect(() => {
364386
if (nodes.length > 0) {
365387
const minZoom = nodes.length > 20 ? 0.5 : 0.3
366-
setTimeout(() => fitView({ padding: 0.2, duration: 300, minZoom }), 100)
388+
// Delay to allow container resize when panel opens/closes
389+
setTimeout(() => fitView({ padding: 0.2, duration: 300, minZoom }), 150)
367390
}
368-
}, [showAll, showTests, clusterByDir, expandedDirs])
391+
}, [nodes.length, selectedNodeId, showAll, showTests, clusterByDir, expandedDirs, fitView])
369392

370393
const handleNodeClick = useCallback((_: any, node: Node) => {
371394
// Toggle directory expansion
@@ -492,6 +515,7 @@ function DependencyGraphInner({ repoId, apiUrl, apiKey }: DependencyGraphProps)
492515
<div className="flex overflow-hidden" style={{ height: '600px' }}>
493516
<div className="relative" style={{ flex: 1, height: '600px' }}>
494517
<ReactFlow
518+
key={renderKey}
495519
nodes={nodes}
496520
edges={edges}
497521
onNodesChange={onNodesChange}

0 commit comments

Comments
 (0)