diff --git a/meshterm/ui/message_paths_screen.py b/meshterm/ui/message_paths_screen.py index b9fd50f..2cffc13 100644 --- a/meshterm/ui/message_paths_screen.py +++ b/meshterm/ui/message_paths_screen.py @@ -278,15 +278,22 @@ class MessagePathsScreen(Screen): (:func:`~meshterm.ui.widgets.route_graph_style`): endpoints named, relays their own map marker where the type is known, each labelled by its first hash byte in the node's own hue. + + Layout rank is fixed by first-heard order (the first path heard is always the spine), + exactly as the node detail's Routes tab fixes it by evidence order, so the fan's + geometry holds still as ↑↓ walk the arrivals — only the emphasis (which lane draws + white and on top) follows the pick. """ selected = self._arrivals[self._index].hops + paths = self._paths() layers = [ PathLayer( hops=path, color=_EDGE_SELECTED if path == selected else _EDGE_UNUSED, - priority=3 if path == selected else 2, + priority=len(paths) - i, + emphasis=1 if path == selected else 0, ) - for path in self._paths() + for i, path in enumerate(paths) ] glyph_of, label_of, label_rgb_of = route_graph_style( resolve=self._resolve, self_name=self._self_name, source=self._source, diff --git a/tests/test_message_paths_screen.py b/tests/test_message_paths_screen.py index 7421ef4..ed559bb 100644 --- a/tests/test_message_paths_screen.py +++ b/tests/test_message_paths_screen.py @@ -96,6 +96,27 @@ def test_paths_screen_draws_selected_path_white_over_gray() -> None: assert "38;2;255;255;255" in raw2 and "38;2;110;110;110" in raw2 +def test_paths_screen_graph_geometry_holds_still_across_the_selection() -> None: + """↑↓ repaint the fan's colours, never its shape — the Routes tab's rule. + + Layout rank is first-heard order, so only ``emphasis`` follows the pick; the drawn + glyphs/lanes are byte-identical from every selection. + """ + now = utcnow() + arrivals = [ + Arrival(when=now, hops=("3d63",), snr=4.0), + Arrival(when=now + timedelta(seconds=2), hops=("a1b2", "77aa"), snr=-2.0), + Arrival(when=now + timedelta(seconds=4), hops=("a1b2", "5c5c", "77aa"), snr=1.0), + ] + screen = _screen(arrivals) + shapes = [] + for _ in range(len(arrivals)): + shapes.append(_plain(screen._graph_lines(76))) + screen.handle("down") + assert len(set(shapes)) == 1, "the graph's shape moved when the selection did" + assert shapes[0].count("\n") > 1 # a genuine multi-lane fan, not one flat line + + def test_paths_screen_marks_a_repeater_relay_with_its_triangle() -> None: """A relay whose type resolves to a repeater draws ▲, not the generic dot.""" screen = _screen(_arrivals(), type_of=lambda h: 2 if h == "3d63" else None)