Problem Statement
System managers overseeing thousands of active validator nodes need a unified, comprehensive fleet overview. The current dashboard lists nodes in a paginated table (50 per page), requiring operators to click through 20+ pages to assess fleet health. This paginated approach makes it impossible to spot spatial patterns (e.g., a regional outage affecting a contiguous block of nodes) or to compare performance metrics across the fleet at a glance. A high-density canvas grid visualization is needed that renders all nodes as color-coded cells in a single view.
Technical Bounds & Invariants
- Maximum fleet size: 10,000 nodes rendered simultaneously
- Cell size: 12px x 12px with 2px gap (14px per cell pitch) — ~1,400 cells visible per viewport at 1920x1080
- Color coding: 5 status levels (Active=green, Warning=yellow, Critical=red, Slashed=purple, Offline=gray)
- Interaction: hover shows tooltip with node name + key metrics, click opens detail panel
- Frame rate: 60 FPS during hover/scroll, 30 FPS during animated transitions (status change pulses)
Codebase Navigation Guide
- Primary target:
/src/components/dashboard/FleetCanvasGrid.tsx
- Node data hook:
/src/hooks/useFleetData.ts — returns FleetNode[] with id, status, metrics
- Current (slow) table:
/src/components/dashboard/NodeTable.tsx
- Tooltip component:
/src/components/shared/Tooltip.tsx
Step-by-Step Resolution Blueprint
- Build a
<canvas>-based grid component using useRef for the canvas element and requestAnimationFrame for the render loop; initial render draws all 10,000 cells as filled rectangles using ctx.fillRect(x, y, 12, 12) with color determined by node status via a lookup map
- Implement an
onMouseMove handler that: (a) converts mouse coordinates to cell indices via col = floor(mouseX / 14), row = floor(mouseY / 14), index = row * cols + col, (b) highlights the hovered cell by drawing a 2px white border around it (without clearing the full canvas — use ctx.clearRect only on the affected cell area), (c) shows a tooltip positioned near the cursor with the node's display name, status, and 3 key metrics (uptime %, stake, last attestation)
- Add a color legend below the grid showing the 5 status colors with labels and a count of nodes in each status; clicking a legend label filters the grid to show only nodes with that status
- Implement sort and group modes via a dropdown: (a)
Sort by stake — lay out cells left-to-right, top-to-bottom sorted by stake amount (highest at top-left), (b) Group by data center — cells are arranged in horizontal bands separated by a 4px gap, each band labeled with the data center name, (c) Group by status — cells grouped by status, showing clusters of red/green/gray
- Add a
requestAnimationFrame delta-time throttle: on each frame, check performance.now() - lastDraw > 16 (60 FPS target); if the JS thread is busy and frames are being skipped, reduce cell resolution by rendering every other cell (skip pattern) to maintain responsiveness
- Add a search input that filters nodes by name or ID; as the user types, animate non-matching cells with a fade-to-30%-opacity transition over 200ms using
ctx.globalAlpha interpolation across frames
- Write a performance test using
page.evaluate to: (a) render 10,000 nodes, (b) measure frame time via requestAnimationFrame callback timestamps for 100 frames, (c) assert p95 frame time < 16ms (60 FPS), (d) measure hover tooltip latency — assert < 5ms from mousemove to tooltip visible
Problem Statement
System managers overseeing thousands of active validator nodes need a unified, comprehensive fleet overview. The current dashboard lists nodes in a paginated table (50 per page), requiring operators to click through 20+ pages to assess fleet health. This paginated approach makes it impossible to spot spatial patterns (e.g., a regional outage affecting a contiguous block of nodes) or to compare performance metrics across the fleet at a glance. A high-density canvas grid visualization is needed that renders all nodes as color-coded cells in a single view.
Technical Bounds & Invariants
Codebase Navigation Guide
/src/components/dashboard/FleetCanvasGrid.tsx/src/hooks/useFleetData.ts— returnsFleetNode[]with id, status, metrics/src/components/dashboard/NodeTable.tsx/src/components/shared/Tooltip.tsxStep-by-Step Resolution Blueprint
<canvas>-based grid component usinguseReffor the canvas element andrequestAnimationFramefor the render loop; initial render draws all 10,000 cells as filled rectangles usingctx.fillRect(x, y, 12, 12)with color determined by node status via a lookup maponMouseMovehandler that: (a) converts mouse coordinates to cell indices viacol = floor(mouseX / 14),row = floor(mouseY / 14),index = row * cols + col, (b) highlights the hovered cell by drawing a 2px white border around it (without clearing the full canvas — usectx.clearRectonly on the affected cell area), (c) shows a tooltip positioned near the cursor with the node's display name, status, and 3 key metrics (uptime %, stake, last attestation)Sort by stake— lay out cells left-to-right, top-to-bottom sorted by stake amount (highest at top-left), (b)Group by data center— cells are arranged in horizontal bands separated by a 4px gap, each band labeled with the data center name, (c)Group by status— cells grouped by status, showing clusters of red/green/grayrequestAnimationFramedelta-time throttle: on each frame, checkperformance.now() - lastDraw > 16(60 FPS target); if the JS thread is busy and frames are being skipped, reduce cell resolution by rendering every other cell (skip pattern) to maintain responsivenessctx.globalAlphainterpolation across framespage.evaluateto: (a) render 10,000 nodes, (b) measure frame time viarequestAnimationFramecallback timestamps for 100 frames, (c) assert p95 frame time < 16ms (60 FPS), (d) measure hover tooltip latency — assert < 5ms from mousemove to tooltip visible