Node-to-node volume heatmap + volume-coloured edges (#525)#542
Conversation
) Adds a pair-level volumetric view to the network diagram, in both analysis mode and the monitor snapshot dialog. The diagram shows topology; nothing showed how *much* each pair talks. A matrix is the right shape for that. - Shared volume colour scale (utils/volumeColor.ts): one log-scaled, perceptually-tuned blue ramp, light/dark aware, replacing the two copy-pasted linear `trafficColor` helpers in ClusterGraph and CountryMapView. Ramps validated against both surfaces. Domain is fitted to the on-screen range so the full gradient is spent on real values. - VolumeHeatmap: src×dst matrix on a canvas (the grid is O(N²) but sparse, so cost scales with conversations, not N²). Fits to the container, with a zoom control and fullscreen for dense captures. Axis labels are IPs (unique, unlike identity labels); clicking one opens host details. - Volume-coloured diagram edges via a "Color edges by: Protocol | Volume" toggle on NetworkGraph, sharing the heatmap's scale and a gradient legend. Switching repaints in place without re-running the layout. - Wired into NetworkDiagramPage (analysis) and SnapshotDetailModal (the monitor Capture Timeline diagram), with cross-filter highlighting. - Deletes MonitorNetworkDiagram.tsx, dead since #321 replaced it with the snapshot-modal flow. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
Warning Review limit reached
Next review available in: 51 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Organization UI Review profile: ASSERTIVE Plan: Pro Plus Run ID: 📒 Files selected for processing (1)
📝 WalkthroughWalkthroughAdds a shared dark-mode logarithmic volume scale, volume-colored network edges, a node-to-node canvas heatmap, cross-filtering and fullscreen controls, integration across analysis and monitor diagrams, and expanded visualization documentation. ChangesVolume visualization
Estimated code review effort: 4 (Complex) | ~60 minutes Possibly related issues
Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Code Review
This pull request unifies traffic volume visualization across the application by introducing a shared, theme-aware logarithmic color scale utility (volumeColor.ts) and a useResolvedDark hook. These are integrated into cluster graphs, country maps, and network topology diagrams. The PR also adds a new VolumeHeatmap component for node-to-node volume matrices, a VolumeLegend component, and options to color diagram edges by volume, while removing the obsolete MonitorNetworkDiagram.tsx file. Review feedback highlights a duplication of CSS rules for .tp-heatmap-fullscreen-over-modal at the end of VolumeHeatmap.css that should be removed to improve maintainability.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
There was a problem hiding this comment.
Actionable comments posted: 9
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@frontend/src/components/intelligence/ClusterGraph/ClusterGraph.tsx`:
- Around line 341-346: Fit each intelligence volume color ramp to its displayed
positive range. In
frontend/src/components/intelligence/ClusterGraph/ClusterGraph.tsx:341-346, add
the smallest positive cluster volume to the node data and pass it to volumeRatio
and makeVolumeColor. In
frontend/src/components/intelligence/ClusterGraph/CountryMapView.tsx:125-131,
compute separate smallest positive country and city volumes and pass the
appropriate minimum to makeVolumeColor.
In `@frontend/src/components/intelligence/ClusterGraph/CountryMapView.tsx`:
- Around line 124-126: Update the traffic legend in CountryMapView to avoid
claiming that dark blue represents more traffic, since the dark-mode ramp uses
brighter blue for higher volume. Use theme-neutral wording or the existing
shared gradient legend while leaving the color calculation unchanged.
In `@frontend/src/components/monitor/SnapshotDetailModal/SnapshotDetailModal.tsx`:
- Around line 272-291: Update the Escape handling in the SnapshotDetailModal
keydown handler to dismiss the selected NodeDetails first when
isHeatmapFullscreen and selectedNode are both active. Clear selectedNode before
exiting fullscreen, while preserving the existing fullscreen Escape behavior
when no node is selected and the snapshot-stepping guard afterward; follow the
precedence used by NetworkDiagramPage.
In `@frontend/src/components/network/VolumeHeatmap/VolumeHeatmap.tsx`:
- Around line 400-440: Replace the raw Bootstrap buttons in the heatmap controls
with the SGDS React Button component imported from `@govtechsg/sgds-react`. Update
the zoom-out, zoom-in, Fit, and Clear selection controls while preserving their
existing handlers, disabled states, labels, titles, and styling intent.
- Around line 100-107: Update the edge aggregation loop in VolumeHeatmap to
normalize each source/target pair as an unordered pair and aggregate its bytes,
packets, and conversations once. After aggregation, populate both directed cell
keys with the same totals, including reverse-direction cells even when no
reverse edge exists.
- Around line 446-458: Update the heatmap interaction around the canvas and its
handlers (`handleMove`, `handleClick`) to provide keyboard access: make the
control focusable, expose an appropriate grid/cell accessibility model, and
support arrow-key cell navigation plus Enter activation for inspecting or
selecting pairs and host labels. Preserve the existing mouse interactions while
keeping focus and active-cell state synchronized with the visual heatmap.
In `@frontend/src/pages/NetworkDiagram/NetworkDiagramPage.tsx`:
- Around line 658-666: Add explicit aria-label values matching each icon-only
Button’s title and mark every nested decorative i element with
aria-hidden="true": update NetworkDiagramPage.tsx at lines 658-666 and 680, and
SnapshotDetailModal.tsx at lines 645-657 and 671.
- Around line 582-584: Explicitly associate each edge-color form label with its
select: in frontend/src/pages/NetworkDiagram/NetworkDiagramPage.tsx lines
582-584, add htmlFor="nd-edge-color" to the Form.Label and id="nd-edge-color" to
the Form.Select; apply the corresponding htmlFor="sd-edge-color" and
id="sd-edge-color" pairing in
frontend/src/components/monitor/SnapshotDetailModal/SnapshotDetailModal.tsx
lines 548-550.
In `@frontend/src/utils/useResolvedDark.ts`:
- Around line 18-23: Update the system-mode branch in useResolvedDark so it
immediately synchronizes sysDark from mq.matches when the effect runs, before
subscribing to future change events. Preserve the existing handler and cleanup
behavior, ensuring re-entering system mode reflects the current OS preference
without waiting for another event.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: a02d01d3-ec27-47fa-8e40-4f3917572a47
📒 Files selected for processing (17)
docs/features/network-intelligence.rstdocs/features/network-visualization.rstfrontend/src/components/intelligence/ClusterGraph/ClusterGraph.tsxfrontend/src/components/intelligence/ClusterGraph/CountryMapView.tsxfrontend/src/components/monitor/MonitorNetworkDiagram/MonitorNetworkDiagram.tsxfrontend/src/components/monitor/SnapshotDetailModal/SnapshotDetailModal.tsxfrontend/src/components/network/NetworkGraph/NetworkGraph.tsxfrontend/src/components/network/VolumeHeatmap/VolumeHeatmap.cssfrontend/src/components/network/VolumeHeatmap/VolumeHeatmap.tsxfrontend/src/components/network/VolumeHeatmap/heatmapLayout.tsfrontend/src/components/network/VolumeHeatmap/index.tsfrontend/src/components/network/VolumeLegend/VolumeLegend.cssfrontend/src/components/network/VolumeLegend/VolumeLegend.tsxfrontend/src/components/network/VolumeLegend/index.tsfrontend/src/pages/NetworkDiagram/NetworkDiagramPage.tsxfrontend/src/utils/useResolvedDark.tsfrontend/src/utils/volumeColor.ts
💤 Files with no reviewable changes (1)
- frontend/src/components/monitor/MonitorNetworkDiagram/MonitorNetworkDiagram.tsx
There was a problem hiding this comment.
Caution
Inline review comments failed to post. This is likely due to GitHub's internal server error or limits when posting large numbers of comments. If you are seeing this consistently it is likely a permissions issue. Please check "Moderation" -> "Code review limits" under your organization settings.
Actionable comments posted: 9
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@frontend/src/components/intelligence/ClusterGraph/ClusterGraph.tsx`:
- Around line 341-346: Fit each intelligence volume color ramp to its displayed
positive range. In
frontend/src/components/intelligence/ClusterGraph/ClusterGraph.tsx:341-346, add
the smallest positive cluster volume to the node data and pass it to volumeRatio
and makeVolumeColor. In
frontend/src/components/intelligence/ClusterGraph/CountryMapView.tsx:125-131,
compute separate smallest positive country and city volumes and pass the
appropriate minimum to makeVolumeColor.
In `@frontend/src/components/intelligence/ClusterGraph/CountryMapView.tsx`:
- Around line 124-126: Update the traffic legend in CountryMapView to avoid
claiming that dark blue represents more traffic, since the dark-mode ramp uses
brighter blue for higher volume. Use theme-neutral wording or the existing
shared gradient legend while leaving the color calculation unchanged.
In `@frontend/src/components/monitor/SnapshotDetailModal/SnapshotDetailModal.tsx`:
- Around line 272-291: Update the Escape handling in the SnapshotDetailModal
keydown handler to dismiss the selected NodeDetails first when
isHeatmapFullscreen and selectedNode are both active. Clear selectedNode before
exiting fullscreen, while preserving the existing fullscreen Escape behavior
when no node is selected and the snapshot-stepping guard afterward; follow the
precedence used by NetworkDiagramPage.
In `@frontend/src/components/network/VolumeHeatmap/VolumeHeatmap.tsx`:
- Around line 400-440: Replace the raw Bootstrap buttons in the heatmap controls
with the SGDS React Button component imported from `@govtechsg/sgds-react`. Update
the zoom-out, zoom-in, Fit, and Clear selection controls while preserving their
existing handlers, disabled states, labels, titles, and styling intent.
- Around line 100-107: Update the edge aggregation loop in VolumeHeatmap to
normalize each source/target pair as an unordered pair and aggregate its bytes,
packets, and conversations once. After aggregation, populate both directed cell
keys with the same totals, including reverse-direction cells even when no
reverse edge exists.
- Around line 446-458: Update the heatmap interaction around the canvas and its
handlers (`handleMove`, `handleClick`) to provide keyboard access: make the
control focusable, expose an appropriate grid/cell accessibility model, and
support arrow-key cell navigation plus Enter activation for inspecting or
selecting pairs and host labels. Preserve the existing mouse interactions while
keeping focus and active-cell state synchronized with the visual heatmap.
In `@frontend/src/pages/NetworkDiagram/NetworkDiagramPage.tsx`:
- Around line 658-666: Add explicit aria-label values matching each icon-only
Button’s title and mark every nested decorative i element with
aria-hidden="true": update NetworkDiagramPage.tsx at lines 658-666 and 680, and
SnapshotDetailModal.tsx at lines 645-657 and 671.
- Around line 582-584: Explicitly associate each edge-color form label with its
select: in frontend/src/pages/NetworkDiagram/NetworkDiagramPage.tsx lines
582-584, add htmlFor="nd-edge-color" to the Form.Label and id="nd-edge-color" to
the Form.Select; apply the corresponding htmlFor="sd-edge-color" and
id="sd-edge-color" pairing in
frontend/src/components/monitor/SnapshotDetailModal/SnapshotDetailModal.tsx
lines 548-550.
In `@frontend/src/utils/useResolvedDark.ts`:
- Around line 18-23: Update the system-mode branch in useResolvedDark so it
immediately synchronizes sysDark from mq.matches when the effect runs, before
subscribing to future change events. Preserve the existing handler and cleanup
behavior, ensuring re-entering system mode reflects the current OS preference
without waiting for another event.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: a02d01d3-ec27-47fa-8e40-4f3917572a47
📒 Files selected for processing (17)
docs/features/network-intelligence.rstdocs/features/network-visualization.rstfrontend/src/components/intelligence/ClusterGraph/ClusterGraph.tsxfrontend/src/components/intelligence/ClusterGraph/CountryMapView.tsxfrontend/src/components/monitor/MonitorNetworkDiagram/MonitorNetworkDiagram.tsxfrontend/src/components/monitor/SnapshotDetailModal/SnapshotDetailModal.tsxfrontend/src/components/network/NetworkGraph/NetworkGraph.tsxfrontend/src/components/network/VolumeHeatmap/VolumeHeatmap.cssfrontend/src/components/network/VolumeHeatmap/VolumeHeatmap.tsxfrontend/src/components/network/VolumeHeatmap/heatmapLayout.tsfrontend/src/components/network/VolumeHeatmap/index.tsfrontend/src/components/network/VolumeLegend/VolumeLegend.cssfrontend/src/components/network/VolumeLegend/VolumeLegend.tsxfrontend/src/components/network/VolumeLegend/index.tsfrontend/src/pages/NetworkDiagram/NetworkDiagramPage.tsxfrontend/src/utils/useResolvedDark.tsfrontend/src/utils/volumeColor.ts
💤 Files with no reviewable changes (1)
- frontend/src/components/monitor/MonitorNetworkDiagram/MonitorNetworkDiagram.tsx
🛑 Comments failed to post (9)
frontend/src/components/intelligence/ClusterGraph/ClusterGraph.tsx (1)
341-346: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Fit every intelligence volume ramp to its displayed positive range.
Both views accept the one-byte default minimum, compressing real traffic values into only part of the logarithmic ramp.
frontend/src/components/intelligence/ClusterGraph/ClusterGraph.tsx#L341-L346: add the smallest positive cluster volume to node data and pass it tovolumeRatioandmakeVolumeColor.frontend/src/components/intelligence/ClusterGraph/CountryMapView.tsx#L125-L131: compute separate smallest positive country and city volumes and pass them tomakeVolumeColor.📍 Affects 2 files
frontend/src/components/intelligence/ClusterGraph/ClusterGraph.tsx#L341-L346(this comment)frontend/src/components/intelligence/ClusterGraph/CountryMapView.tsx#L125-L131🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@frontend/src/components/intelligence/ClusterGraph/ClusterGraph.tsx` around lines 341 - 346, Fit each intelligence volume color ramp to its displayed positive range. In frontend/src/components/intelligence/ClusterGraph/ClusterGraph.tsx:341-346, add the smallest positive cluster volume to the node data and pass it to volumeRatio and makeVolumeColor. In frontend/src/components/intelligence/ClusterGraph/CountryMapView.tsx:125-131, compute separate smallest positive country and city volumes and pass the appropriate minimum to makeVolumeColor.frontend/src/components/intelligence/ClusterGraph/CountryMapView.tsx (1)
124-126: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Update the legend for the dark-mode ramp.
Higher volume becomes brighter blue in dark mode, so the existing “Dark blue = more traffic” legend states the opposite. Use theme-neutral wording or the shared gradient legend.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@frontend/src/components/intelligence/ClusterGraph/CountryMapView.tsx` around lines 124 - 126, Update the traffic legend in CountryMapView to avoid claiming that dark blue represents more traffic, since the dark-mode ramp uses brighter blue for higher volume. Use theme-neutral wording or the existing shared gradient legend while leaving the color calculation unchanged.frontend/src/components/monitor/SnapshotDetailModal/SnapshotDetailModal.tsx (1)
272-291: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Fix Escape-key precedence to dismiss
NodeDetailsfirst.When the heatmap is fullscreen and a node is selected, pressing Escape currently closes the heatmap fullscreen rather than dismissing the
NodeDetailsmodal. Because this event handler runs in the capture phase and callsstopPropagation(), it starves theNodeDetailsmodal of the Escape key event, leaving the modal open over the collapsed view.Clear the
selectedNodefirst before exiting fullscreen, similar to the logic correctly implemented inNetworkDiagramPage.🐛 Proposed fix
const handler = (e: KeyboardEvent) => { // Heatmap fullscreen owns Escape while it is open — without this the key // falls through to the dialog and closes the whole thing instead of just // leaving fullscreen. Capture phase, so it runs before the modal's handler. if (e.key === 'Escape' && isHeatmapFullscreen) { e.preventDefault(); e.stopPropagation(); + if (selectedNode) { + setSelectedNode(null); + return; + } setIsHeatmapFullscreen(false); return; } // Snapshot stepping would be disorienting while the matrix is fullscreen. if (isHeatmapFullscreen) return; if (e.key === 'ArrowLeft' && diagramIndex > 0) { setDiagramSnapshotId(sorted[diagramIndex - 1].id); } else if (e.key === 'ArrowRight' && diagramIndex < sorted.length - 1) { setDiagramSnapshotId(sorted[diagramIndex + 1].id); } }; window.addEventListener('keydown', handler, true); return () => window.removeEventListener('keydown', handler, true); - }, [activeTab, diagramIndex, sorted, isHeatmapFullscreen]); + }, [activeTab, diagramIndex, sorted, isHeatmapFullscreen, selectedNode]);📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.// Heatmap fullscreen owns Escape while it is open — without this the key // falls through to the dialog and closes the whole thing instead of just // leaving fullscreen. Capture phase, so it runs before the modal's handler. if (e.key === 'Escape' && isHeatmapFullscreen) { e.preventDefault(); e.stopPropagation(); if (selectedNode) { setSelectedNode(null); return; } setIsHeatmapFullscreen(false); return; } // Snapshot stepping would be disorienting while the matrix is fullscreen. if (isHeatmapFullscreen) return; if (e.key === 'ArrowLeft' && diagramIndex > 0) { setDiagramSnapshotId(sorted[diagramIndex - 1].id); } else if (e.key === 'ArrowRight' && diagramIndex < sorted.length - 1) { setDiagramSnapshotId(sorted[diagramIndex + 1].id); } }; window.addEventListener('keydown', handler, true); return () => window.removeEventListener('keydown', handler, true); }, [activeTab, diagramIndex, sorted, isHeatmapFullscreen, selectedNode]);🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@frontend/src/components/monitor/SnapshotDetailModal/SnapshotDetailModal.tsx` around lines 272 - 291, Update the Escape handling in the SnapshotDetailModal keydown handler to dismiss the selected NodeDetails first when isHeatmapFullscreen and selectedNode are both active. Clear selectedNode before exiting fullscreen, while preserving the existing fullscreen Escape behavior when no node is selected and the snapshot-stepping guard afterward; follow the precedence used by NetworkDiagramPage.frontend/src/components/network/VolumeHeatmap/VolumeHeatmap.tsx (3)
100-107: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Mirror pair totals across both matrix directions.
Only
source → targetis populated, leaving the reverse cell empty even thoughtotalByteshas no per-direction breakdown. Normalize each unordered pair, aggregate it once, then expose the same total at both directed cell keys.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@frontend/src/components/network/VolumeHeatmap/VolumeHeatmap.tsx` around lines 100 - 107, Update the edge aggregation loop in VolumeHeatmap to normalize each source/target pair as an unordered pair and aggregate its bytes, packets, and conversations once. After aggregation, populate both directed cell keys with the same totals, including reverse-direction cells even when no reverse edge exists.
400-440: 📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win
Use SGDS buttons for the heatmap controls.
Replace the raw Bootstrap zoom, fit, and clear buttons with
Buttonfrom@govtechsg/sgds-react.As per coding guidelines, “Always use SGDS components from
@govtechsg/sgds-reactbefore building custom UI components.” <coding_guidelines>🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@frontend/src/components/network/VolumeHeatmap/VolumeHeatmap.tsx` around lines 400 - 440, Replace the raw Bootstrap buttons in the heatmap controls with the SGDS React Button component imported from `@govtechsg/sgds-react`. Update the zoom-out, zoom-in, Fit, and Clear selection controls while preserving their existing handlers, disabled states, labels, titles, and styling intent.Source: Coding guidelines
446-458: 🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift
Provide keyboard interaction for the heatmap.
The canvas is a non-focusable
role="img"with mouse-only handlers, so keyboard users cannot inspect cells, select pairs, or activate host labels. Add a focusable grid interaction model with arrow keys and Enter, or an equivalent accessible control/list representation.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@frontend/src/components/network/VolumeHeatmap/VolumeHeatmap.tsx` around lines 446 - 458, Update the heatmap interaction around the canvas and its handlers (`handleMove`, `handleClick`) to provide keyboard access: make the control focusable, expose an appropriate grid/cell accessibility model, and support arrow-key cell navigation plus Enter activation for inspecting or selecting pairs and host labels. Preserve the existing mouse interactions while keeping focus and active-cell state synchronized with the visual heatmap.frontend/src/pages/NetworkDiagram/NetworkDiagramPage.tsx (2)
582-584: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Explicitly associate the form label with the select element.
Screen readers may not correctly announce the label for the
<Form.Select>because it is not explicitly linked to the<Form.Label>. Provide anidto the select element and reference it with thehtmlForattribute on the label.
frontend/src/pages/NetworkDiagram/NetworkDiagramPage.tsx#L582-L584: AddhtmlFor="nd-edge-color"to the<Form.Label>andid="nd-edge-color"to the<Form.Select>.frontend/src/components/monitor/SnapshotDetailModal/SnapshotDetailModal.tsx#L548-L550: AddhtmlFor="sd-edge-color"to the<Form.Label>andid="sd-edge-color"to the<Form.Select>.📍 Affects 2 files
frontend/src/pages/NetworkDiagram/NetworkDiagramPage.tsx#L582-L584(this comment)frontend/src/components/monitor/SnapshotDetailModal/SnapshotDetailModal.tsx#L548-L550🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@frontend/src/pages/NetworkDiagram/NetworkDiagramPage.tsx` around lines 582 - 584, Explicitly associate each edge-color form label with its select: in frontend/src/pages/NetworkDiagram/NetworkDiagramPage.tsx lines 582-584, add htmlFor="nd-edge-color" to the Form.Label and id="nd-edge-color" to the Form.Select; apply the corresponding htmlFor="sd-edge-color" and id="sd-edge-color" pairing in frontend/src/components/monitor/SnapshotDetailModal/SnapshotDetailModal.tsx lines 548-550.
658-666: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Ensure icon buttons have an accessible name and decorative icons are hidden.
Icon-only buttons naturally rely on their
titleattribute for pointer tooltips, but should also include an explicitaria-labelfor reliable screen reader announcement. Additionally, decorative icons inside buttons should be marked witharia-hidden="true". Based on learnings, interactive elements using decorative icons should provide proper accessible labels and hide the icons from assistive technologies.
frontend/src/pages/NetworkDiagram/NetworkDiagramPage.tsx#L658-L666: Addaria-labelto the<Button>(matching itstitle) andaria-hidden="true"to the nested<i>element.frontend/src/pages/NetworkDiagram/NetworkDiagramPage.tsx#L680-L680: Addaria-hidden="true"to the<i>element.frontend/src/components/monitor/SnapshotDetailModal/SnapshotDetailModal.tsx#L645-L657: Addaria-labelto the<Button>(matching itstitle) andaria-hidden="true"to the nested<i>element.frontend/src/components/monitor/SnapshotDetailModal/SnapshotDetailModal.tsx#L671-L671: Addaria-hidden="true"to the<i>element.📍 Affects 2 files
frontend/src/pages/NetworkDiagram/NetworkDiagramPage.tsx#L658-L666(this comment)frontend/src/pages/NetworkDiagram/NetworkDiagramPage.tsx#L680-L680frontend/src/components/monitor/SnapshotDetailModal/SnapshotDetailModal.tsx#L645-L657frontend/src/components/monitor/SnapshotDetailModal/SnapshotDetailModal.tsx#L671-L671🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@frontend/src/pages/NetworkDiagram/NetworkDiagramPage.tsx` around lines 658 - 666, Add explicit aria-label values matching each icon-only Button’s title and mark every nested decorative i element with aria-hidden="true": update NetworkDiagramPage.tsx at lines 658-666 and 680, and SnapshotDetailModal.tsx at lines 645-657 and 671.Source: Learnings
frontend/src/utils/useResolvedDark.ts (1)
18-23: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Synchronize the OS preference when re-entering system mode.
If the OS theme changes while
lightordarkis selected, switching back tosystemreturns stalesysDarkuntil another media-query event occurs.Proposed fix
useEffect(() => { if (themeMode !== 'system') return; const mq = window.matchMedia('(prefers-color-scheme: dark)'); + setSysDark(mq.matches); const handler = (e: MediaQueryListEvent) => setSysDark(e.matches);📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.useEffect(() => { if (themeMode !== 'system') return; const mq = window.matchMedia('(prefers-color-scheme: dark)'); setSysDark(mq.matches); const handler = (e: MediaQueryListEvent) => setSysDark(e.matches); mq.addEventListener('change', handler); return () => mq.removeEventListener('change', handler);🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@frontend/src/utils/useResolvedDark.ts` around lines 18 - 23, Update the system-mode branch in useResolvedDark so it immediately synchronizes sysDark from mq.matches when the effect runs, before subscribing to future change events. Preserve the existing handler and cleanup behavior, ensuring re-entering system mode reflects the current OS preference without waiting for another event.
The .tp-heatmap-fullscreen-over-modal rules were refactored into a shared .nd-css-fullscreen block but the original standalone block was left at the end of the file. Per gemini-code-assist review on #542. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Closes #525.
What
Adds a pair-level volumetric view to the network diagram — the diagram shows who talks to whom, but nothing showed how much. A src×dst matrix is the right shape for "which pair is loudest".
Available in both analysis mode and the monitor snapshot dialog (Capture Timeline → a snapshot → Network Diagram tab).
Changes
utils/volumeColor.ts) — one log-scaled blue ramp, light/dark aware, replacing the two copy-pasted lineartrafficColorhelpers inClusterGraphandCountryMapView(an acceptance criterion). Ramps validated against both surfaces; domain is fitted to the on-screen range so the full gradient lands on real values rather than a floor nothing reaches.VolumeHeatmap/) — src×dst matrix rendered to a canvas. The grid is O(N²) (500 hosts = 250k cells, which would freeze a DOM table) but sparse, so cost scales with conversation count, not N². Fits to the container, with a zoom control and fullscreen for dense captures. Axis labels are IPs (unique, unlike identity/device labels); clicking one opens host details.NetworkGraph, sharing the heatmap's scale + a gradient legend. Switching repaints in place without re-running the layout. Resolves the protocol-vs-volume conflict deliberately (one channel at a time), per the issue.MonitorNetworkDiagram.tsx— dead since feat: network monitor insights, node roles, subnets, annotations & per-snapshot details #321 replaced it with the snapshot-modal flow.Design decisions (confirmed with the issue author)
totalBytes. Directionality: directed src→dst, symmetric-by-necessity until per-direction counters (feat: directional biflow counters (fwd/rev bytes+packets, duration, TCP flags) on conversations #427) exist.One correction to the issue's premise: a cell and an edge can't mean the exact same number, because the diagram splits a pair into one edge per protocol while a cell is the pair total — they agree only for single-protocol pairs. Documented rather than papered over.
Verification
npx tsc --noEmitclean; new files lint clean (NetworkDiagramPagehas 2 pre-existinggraphCardRefref-in-render errors, confirmed onmain).docker compose up -d --buildsucceeds; app serves and the feature is in the shipped bundle.Docs updated:
network-intelligence.rst(shared scale) andnetwork-visualization.rst(heatmap, edge modes, both entry points).🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
Documentation