Add a true force-directed network graph view (force_network.js) that users switch to via the existing "Network Mode" radio buttons. Currently both radio options render TimeArcs with different coloring. After this change, selecting "Force Layout" renders a node-link network diagram.
Exports:
renderForceNetwork(config)- Render the network graph into#chartSVGcleanupForceNetwork()- Stop simulation, remove elementsisForceNetworkActive()- Check if network view is activeupdateForceNetworkVisibility(visibleAttacks)- Filter edges/nodes by legend selection
Data aggregation (internal):
aggregateLinksForNetwork(links)- Collapse per-minute links into per-pair totals with dominant attack type, total count, minute countbuildNetworkNodes(edges)- Derive nodes from edges with degree, totalTraffic, dominant attack
Force simulation:
d3.forceLink- distance inversely proportional to connection weightd3.forceManyBody- repulsion (-300 strength, 500 max distance)d3.forceCenter- center graph in viewportd3.forceCollide- prevent node overlap (radius + 4px padding)- Alpha decay 0.02, velocity decay 0.3
Rendering:
- SVG structure:
g.network-container > g.network-edges + g.network-nodes - Edges:
<line>colored by dominant attack, width by log-scaled count (0.5-6px) - Nodes:
<g>with<circle>sized by sqrt(degree) (5-30px radius), colored by dominant non-normal attack, white stroke;<text>label below - Simulation tick updates positions each frame
Interactions:
- Drag: d3.drag on nodes, pins during drag, releases on end
- Hover: highlight connected edges (opacity 0.9), dim others (0.1); dim unconnected nodes (0.15); show tooltip with IP, degree, traffic, attack breakdown
- Click: sticky highlight (click again or background to deselect)
- Zoom/Pan: d3.zoom on SVG (scale 0.2-5x), transforms
g.network-container
Resize handling:
- Update forceCenter target, reheat simulation slightly (alpha 0.3)
-
Add import (top, with existing imports):
import { renderForceNetwork, cleanupForceNetwork, isForceNetworkActive, updateForceNetworkVisibility } from './force_network.js';
-
Replace radio handler (lines 237-244) with mode-switching logic:
timearcs → force_layout: cleanup TimeArcs, hide TimeArcs-specific UI (bifocal bar, axis, brush controls, compression slider), callrenderForceNetwork()with current data/links/colors/legend callbacksforce_layout → timearcs: callcleanupForceNetwork(), restore TimeArcs UI, callrender(originalData)- Same mode: existing
updateLabelMode()behavior
-
Add UI toggle helpers:
hideTimeArcsUI()- hide bifocal bar, axis-top, brush status, compression slidershowTimeArcsUI()- restore those elements
-
Guard in
applyAttackFilter()(~line 972): ifisForceNetworkActive(), callupdateForceNetworkVisibility(visibleAttacks)instead of re-rendering TimeArcs -
Guard in resize handler (~line 457): if
isForceNetworkActive(), skip TimeArcs resize logic
Add classes for network elements:
.network-edge- fill none, pointer-events stroke, opacity transition.network-node circle- cursor grab/grabbing, opacity transition.node-label- 9px, gray, no pointer events.network-node.dimmed,.network-edge.dimmed- low opacity.network-edge.highlighted- high opacity
No structural changes required. Optionally rename "Force Layout" label to "Network Graph" for clarity.
- Serve directory:
python -m http.server 8000 - Open
http://localhost:8000/attack-network.html - Load CSV data (or let default CSV load)
- Verify TimeArcs renders normally
- Click "Force Layout" / "Network Graph" radio button
- Verify: TimeArcs-specific UI hides, force-directed graph appears with nodes and edges
- Test: node dragging, hover tooltips, click highlight, zoom/pan
- Test: legend filtering dims/hides edges and nodes
- Switch back to "TimeArcs" - verify TimeArcs re-renders correctly
- Test window resize in both modes