Skip to content

fix: prevent dependency graph from going blank when clicking nodes#238

Merged
DevanshuNEU merged 5 commits into
OpenCodeIntel:mainfrom
DevanshuNEU:fix/dependency-graph-click-bug
Feb 3, 2026
Merged

fix: prevent dependency graph from going blank when clicking nodes#238
DevanshuNEU merged 5 commits into
OpenCodeIntel:mainfrom
DevanshuNEU:fix/dependency-graph-click-bug

Conversation

@DevanshuNEU

@DevanshuNEU DevanshuNEU commented Feb 3, 2026

Copy link
Copy Markdown
Collaborator

Problem

When clicking on certain nodes in the dependency graph (especially when clicking on dependent files in the ImpactPanel that aren't currently visible), the entire graph would go blank.

Root Cause

When a user clicks on a file in the ImpactPanel that's not in the visibleNodeIds set (e.g., when only top 15 files are shown), the selectedNodeId gets set to a non-visible file. This caused all other visible nodes to get state = 'dimmed' even though the selected node wasn't displayed, breaking the visual state.

Fix

  1. Added effectiveSelectedId safety check - Only applies selection highlighting if the selected node is actually visible in the current view
  2. Smart handlePanelFileClick - When clicking on a non-visible dependent, auto-enables "Show All" mode to make the file visible before selecting it
  3. Applied to both modes - Fixed both clustered (directory grouping) and non-clustered graph views
  4. Edge styling fix - Edges also use the effective selected ID to prevent incorrect dimming

Testing

  • Build passes
  • Click on files in the main graph view
  • Click on dependents in the ImpactPanel when "Show All" is off
  • Toggle between clustered/non-clustered modes
  • Verify graph doesn't go blank in any scenario

Summary by CodeRabbit

  • Style

    • Updated dimmed-node visuals for clearer contrast and consistency across light/dark themes.
  • Improvements

    • Clicking a hidden selection now makes the item visible before selecting.
    • Only selected items and their direct dependents/connected edges are highlighted; elements no longer dim or rely on hover for dimming.
    • More robust layout and visibility handling to prevent missing/invalid elements and ensure reliable view fitting.
    • Panel open/closed state now persists across selections; rendering forces reliable refresh when visibility changes.

- Added safety check: only apply selection highlighting if selected node is visible
- Fixed handlePanelFileClick to auto-enable 'show all' when clicking non-visible dependents
- Applied fix to both clustered and non-clustered graph modes
- Prevents edge dimming when selected node isn't in visible set
@vercel

vercel Bot commented Feb 3, 2026

Copy link
Copy Markdown

@DevanshuNEU is attempting to deploy a commit to the Dev's projects Team on Vercel.

A member of the Team first needs to authorize it.

Root cause: dimmed state had border-zinc-800 (same as background) + opacity-40
making nodes essentially invisible when selecting a file.

Changes:
- GraphNode: dark:border-zinc-600 dark:bg-zinc-800/80 opacity-50
- DirectoryNode: same fix for cluster mode

Now dimmed nodes are visible but clearly de-emphasized.
@coderabbitai

coderabbitai Bot commented Feb 3, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

Updated dimmed-state styles for DirectoryNode and GraphNode; reworked DependencyGraph layout/visibility/selection flows to only highlight visible nodes/edges, added guards for missing layout data, a renderKey remount trigger, tab-visibility listener, and deferred selection when clicking non-visible files; ImpactPanel no longer resets collapsible open state on file change.

Changes

Cohort / File(s) Summary
Dimmed State Styling
frontend/src/components/DependencyGraph/DirectoryNode.tsx, frontend/src/components/DependencyGraph/GraphNode.tsx
Replaced CSS classes for the "dimmed" visual state: adjusted border/background colors and opacity in light/dark modes. No control-flow changes.
Layout, Visibility & Selection Logic
frontend/src/components/DependencyGraph/index.tsx
Added guards for empty nodes and missing dagre positions, create edges only when endpoints exist, fallback node positioning, introduced renderKey to force remounts, tab-visibility listener to trigger fitView, effectiveSelectedId/effectiveSelectedIdCluster to limit highlighting to visible items, simplified highlighting (no dim-on-hover), and updated handlePanelFileClick to enable showAll and defer selection for invisible files.
Collapsible State Persistence
frontend/src/components/DependencyGraph/ImpactPanel.tsx
Removed effect that reset CollapsibleSection isOpen when defaultOpen or files changed so open/closed state now persists after mount.

Sequence Diagram(s)

sequenceDiagram
    participant User as User
    participant Panel as Panel (file list)
    participant Graph as DependencyGraph (ReactFlow)
    participant Layout as Dagre/Layout
    participant Browser as Browser (visibility)

    User->>Panel: click file
    Panel->>Graph: handlePanelFileClick(fileId)
    alt file not visible
        Graph->>Graph: set showAll = true
        Graph->>Browser: add visibility listener
        Browser-->>Graph: visibilitychange (visible)
        Graph->>Graph: update renderKey (force remount)
    end
    Graph->>Layout: getLayoutedElements(nodes, edges)
    Layout-->>Graph: positions (or fallback)
    Graph->>Graph: compute effectiveSelectedId / effectiveSelectedIdCluster
    Graph->>Graph: highlight selected nodes & connected edges
    Graph->>Graph: fitView (with adjusted delay)
    Graph-->>User: rendered focused view
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Poem

🐰 I hopped the graph with nimble feet,
Softened borders, colors neat,
If a file hides beyond my sight,
I nudge the map — it springs to light,
A little hop, and all's in sight. 🥕

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title directly addresses the main issue fixed in this PR - preventing the dependency graph from going blank when clicking nodes, which matches the root cause analysis and primary fix.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Comment @coderabbitai help to get the list of available commands and usage tips.

Quick fix for demo - instead of dimming non-selected nodes (which was
causing them to become invisible), we now just highlight the selected
node and its dependents while keeping others at default visibility.

Changes:
- Removed 'dimmed' state assignment in both normal and cluster modes
- Removed edge dimming
- Added guards to dagre layout function for edge cases
- Kept selected/direct/transitive highlighting working

This is a simpler UX that works reliably for the demo.
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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@frontend/src/components/DependencyGraph/index.tsx`:
- Around line 412-432: handlePanelFileClick can select a file that’s not
actually rendered because its parent directory is collapsed; update the handler
to detect and expand the file’s directory before selecting/zooming: inside
handlePanelFileClick (which currently references visibleNodeIds, showAll,
setShowAll, setSelectedNodeId, nodes, fitView) check the file’s parent directory
collapse state (e.g. collapsedDirs or isDirCollapsed(parentId)) and if
collapsed, call the directory-expand action (e.g. setCollapsed(parentId, false)
or onToggleDirectory(parentId)) — if showAll is false also setShowAll(true) —
then after a short delay (like the existing setTimeout) call
setSelectedNodeId(fileId) and fitView for the node; otherwise proceed as before.
Ensure you reference the actual state/updater names used for directory collapse
and parent lookup when implementing.
🧹 Nitpick comments (2)
frontend/src/components/DependencyGraph/index.tsx (2)

141-156: Clear the visibility-change timeout to avoid stray fitView calls.

setTimeout isn’t cleared if the component unmounts or visibility toggles rapidly. A small cleanup prevents queued fitView calls after teardown.

🧹 Proposed cleanup
  useEffect(() => {
    const handleVisibilityChange = () => {
      if (document.visibilityState === 'visible') {
        // Force re-render by updating key
        setRenderKey(k => k + 1)
        // Also trigger fitView after a short delay
-        setTimeout(() => {
+        const timeoutId = window.setTimeout(() => {
          fitView({ padding: 0.2, duration: 200 })
-        }, 100)
+        }, 100)
+        // Clear any pending fitView if visibility changes again quickly
+        return () => window.clearTimeout(timeoutId)
      }
    }

    document.addEventListener('visibilitychange', handleVisibilityChange)
    return () => document.removeEventListener('visibilitychange', handleVisibilityChange)
  }, [fitView])

384-391: Avoid stacked fitView timeouts on rapid state changes.

This effect can queue multiple timeouts if dependencies update quickly (panel toggles, filtering), which can cause jitter. Consider cancelling the prior timeout.

🧹 Proposed cleanup
  useEffect(() => {
-    if (nodes.length > 0) {
-      const minZoom = nodes.length > 20 ? 0.5 : 0.3
-      // Delay to allow container resize when panel opens/closes
-      setTimeout(() => fitView({ padding: 0.2, duration: 300, minZoom }), 150)
-    }
+    if (nodes.length === 0) return
+    const minZoom = nodes.length > 20 ? 0.5 : 0.3
+    // Delay to allow container resize when panel opens/closes
+    const timeoutId = window.setTimeout(() => {
+      fitView({ padding: 0.2, duration: 300, minZoom })
+    }, 150)
+    return () => window.clearTimeout(timeoutId)
  }, [nodes.length, selectedNodeId, showAll, showTests, clusterByDir, expandedDirs, fitView])

Comment thread frontend/src/components/DependencyGraph/index.tsx
The useEffect was resetting isOpen to defaultOpen whenever files changed,
which caused the section to immediately close after clicking to open it.
@vercel

vercel Bot commented Feb 3, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
opencodeintel Ready Ready Preview, Comment Feb 3, 2026 9:42pm

@DevanshuNEU DevanshuNEU merged commit 56450f1 into OpenCodeIntel:main Feb 3, 2026
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant