fix(forkchoice): redesign viz node weight display#328
fix(forkchoice): redesign viz node weight display#328MegaRedHand merged 6 commits intolambdaclass:mainfrom
Conversation
Greptile SummaryThis PR replaces the previous variable-radius node rendering in the fork-choice visualizer with fixed-size ring nodes whose inner circle fills from the bottom proportional to Confidence Score: 4/5Safe to merge; both findings are P2 visual/defensive issues that do not affect correctness or functionality. No P0 or P1 issues found. Two P2s: tooltip can render "undefined" as denominator when validator_count is absent, and the fill-mask rect uses a paint-over technique whose corner pixels protrude ~2.4 px outside the outer ring. Both are currently invisible/benign but worth noting. crates/net/rpc/static/fork_choice.html — fill-mask corner protrusion and tooltip undefined guard
|
| Filename | Overview |
|---|---|
| crates/net/rpc/static/fork_choice.html | Replaces variable-radius nodes with fixed-size ring+fill animation; two minor P2 issues: tooltip can display "undefined" denominator and the fill-mask rect corners slightly protrude outside the outer ring due to paint-over rather than clip-path approach. |
Flowchart
%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[fetchAndRender poll] --> B[buildLayout]
B --> C["flatNodes: {...d.data, x, y, _color, _ratio}"]
C --> D{Node in DOM?}
D -- New --> E[nodeEnter: append g]
E --> E1[append node-inner circle fill=_color]
E1 --> E2[append fill-mask rect height=1-ratio×2R]
E2 --> E3[append node-outer ring stroke=_color]
E3 --> E4[append node-hit transparent]
E4 --> E5[append text label]
D -- Existing --> F[nodeMerged]
F --> F1[transition fill-mask height duration=500ms]
F1 --> F2[delay 500ms flip node-inner fill + node-outer stroke duration=100ms]
F --> G{hoveredRoot set?}
G -- Yes --> H[refresh tooltip innerHTML]
G -- No --> I[done]
H --> I
Prompt To Fix All With AI
This is a comment left during a code review.
Path: crates/net/rpc/static/fork_choice.html
Line: 342
Comment:
**Tooltip shows `undefined` when `validator_count` is absent**
`${total}` in the template literal renders as the string `"undefined"` if `total` is `undefined`, producing `weight: X/undefined (0%)` in the tooltip. The `pct` guard prevents `NaN` in the percentage, but the denominator in the fraction is still unguarded.
```suggestion
`<span class="tt-label">weight:</span> ${d.weight}${total != null ? `/${total} (${pct}%)` : ''}`;
```
How can I resolve this? If you propose a fix, please make it concise.
---
This is a comment left during a code review.
Path: crates/net/rpc/static/fork_choice.html
Line: 457-462
Comment:
**`fill-mask` rect corners protrude outside the outer ring**
The mask rect spans `±INNER_RADIUS` (±13 px), so its corners sit at distance √(13²+13²) ≈ 18.4 px from center — outside `NODE_RADIUS` (16 px) by ~2.4 px. This is invisible today because `.fill-mask` fill (`#1a1a2e`) matches the page background, but it is a fragile paint-over rather than a true clip. Any theme change or scrollable container with a different background will expose small dark squares at the diagonal corners of each partially-filled node.
Using an SVG `<clipPath>` with a circle of radius `INNER_RADIUS` applied to the rect would make this robust without a visual change.
How can I resolve this? If you propose a fix, please make it concise.Reviews (1): Last reviewed commit: "Merge branch 'main' into improve-node-we..." | Re-trigger Greptile
| nodeEnter.append("rect") | ||
| .attr("class", "fill-mask") | ||
| .attr("x", -INNER_RADIUS) | ||
| .attr("width", INNER_RADIUS * 2) | ||
| .attr("y", -INNER_RADIUS) | ||
| .attr("height", d => (1 - d._ratio) * INNER_RADIUS * 2); |
There was a problem hiding this comment.
fill-mask rect corners protrude outside the outer ring
The mask rect spans ±INNER_RADIUS (±13 px), so its corners sit at distance √(13²+13²) ≈ 18.4 px from center — outside NODE_RADIUS (16 px) by ~2.4 px. This is invisible today because .fill-mask fill (#1a1a2e) matches the page background, but it is a fragile paint-over rather than a true clip. Any theme change or scrollable container with a different background will expose small dark squares at the diagonal corners of each partially-filled node.
Using an SVG <clipPath> with a circle of radius INNER_RADIUS applied to the rect would make this robust without a visual change.
Prompt To Fix With AI
This is a comment left during a code review.
Path: crates/net/rpc/static/fork_choice.html
Line: 457-462
Comment:
**`fill-mask` rect corners protrude outside the outer ring**
The mask rect spans `±INNER_RADIUS` (±13 px), so its corners sit at distance √(13²+13²) ≈ 18.4 px from center — outside `NODE_RADIUS` (16 px) by ~2.4 px. This is invisible today because `.fill-mask` fill (`#1a1a2e`) matches the page background, but it is a fragile paint-over rather than a true clip. Any theme change or scrollable container with a different background will expose small dark squares at the diagonal corners of each partially-filled node.
Using an SVG `<clipPath>` with a circle of radius `INNER_RADIUS` applied to the rect would make this robust without a visual change.
How can I resolve this? If you propose a fix, please make it concise.
Motivation
Closes #150 .
In the original fork choice viz, node size scaled with
weight, so a high-weight block could grow large enough to obscure neighboring block roots. This PR replaces it with fixed-size nodes whose inner circle fills from the bottom proportional toweight / validator_count.Description
Every node renders at the same size; the visible inner fill is the weight ratio. The fill animates first, then the role color (head, safe-target, justified, finalized) flips after the fill settles. The tooltip's
weightline now readscount/total (pct%).How to Use
1. Start a local devnet
Or start a node manually:
2. Open the visualization
Navigate to
http://localhost:5054/lean/v0/fork_choice/uiin your browser.The page will start polling automatically and render the fork tree as blocks are produced.
What to look for
Screenshots
Live local devnet
Forked tree (mocked data)
Test plan
make fmt,make lint,make test— no regressions.