Node detail: light route labels through coalesced hop ids

The Routes tab mutes every label off the selected route, testing
membership against the route's raw draw hops. But the path graph folds a
short hop into the wide id it can only be among the drawn routes (a
1-byte trace hop beside the same relay's full contact id), so a route
recorded at the short width greyed the very marker its white line rides
through — TSFCT SUTTON-680M's bf→8e→3d route muted YUL-Cartierville.
Decide membership in the graph's own id space instead, by running the
layers through the same coalesce the widget applies.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Jean-Pierre MartineauandClaude Fable 5 committed 2026-07-26 17:26:39 -04:00
1 parent ceab414ca9
commit 18b1e034fd
2 files changed
+37 -1

No files matched your search

+6 -1
View File
@@ -75,6 +75,7 @@ from .pathgraph import (
LabelOf,
LabelRgbOf,
PathLayer,
_coalesce_prefixes,
bidir_clusters,
render_path_graph,
)
@@ -649,7 +650,11 @@ class NodeDetailScreen(Screen):
# A node keeps its name hue only while it sits on the selected route (its endpoints
# always do); every other node in the fan fades its label to the same grey its line
# went, so the picture reads as *this* route rather than the whole tangle.
on_route = set(rv.routes[sel].draw) | {SRC_NODE, DST_NODE}
# Membership is decided in the graph's own id space: the widget folds a short hop
# into the wide id it can only be among these routes (a 1-byte trace hop beside the
# same relay's full contact id), so the raw draw hops would grey the very marker the
# selected line rides through.
on_route = set(_coalesce_prefixes(layers)[sel].hops) | {SRC_NODE, DST_NODE}
base_rgb = rv.label_rgb_of
assert base_rgb is not None
+31
View File
@@ -572,6 +572,37 @@ def test_node_detail_enter_on_a_route_row_opens_its_trace() -> None:
assert resolved == ["trace"] and screen.selected_spec() == "a1,f2,a1"
def test_route_labels_light_through_a_coalesced_hop() -> None:
"""A route drawn from a short hop id lights the wide marker the graph folds it into.
Route evidence reaches the page at mixed hash widths — a 1-byte trace hop (``3d``)
beside the same relay's full id on a sibling route. The graph coalesces the short id
into the one wide marker, so the label muting must test membership against the *drawn*
ids: keyed on the raw draw hops, the selected route's own relay rendered muted while
the white line rode straight through it (the TSFCT SUTTON-680M report).
"""
lit: list[str] = []
def base_rgb(node: str) -> tuple[int, int, int]:
lit.append(node) # the stage's muting wrapper only consults us for on-route nodes
return (200, 200, 200)
routes = _RoutesView(
routes=[
_Route(draw=("bf61f2fb1d9e", "3d63c6429436"), spec="s0", row=Text("wide")),
_Route(draw=("bf61f2fb1d9e", "3d"), spec="s1", row=Text("short")),
],
glyph_of=lambda n: ("●", "#ffffff"),
label_of=lambda n: n[:2],
label_rgb_of=base_rgb,
)
screen = _screen(routes=routes, tabs=[_Tab("Routes", "routes")])
screen.note_viewport(30)
screen.handle("down") # select the short-hop route
screen.render_body(72)
assert "3d63c6429436" in lit # the relay it rides through keeps its hue
def test_node_detail_route_list_windows_inside_the_page() -> None:
"""With more routes than fit, the list windows with edge markers — the graph and the
action rows never leave the screen, however many routes a busy node has."""