Message paths: hold the fan's shape still across the selection

The dialog ranked layers by selection (priority 3 for the picked path, 2 for the
rest), so every ↑↓ re-elected the spine and re-laid the whole graph out. Rank by
first-heard order instead and move `emphasis`, the way the node detail's Routes
tab already does — the geometry is fixed by evidence, only the white lane moves.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Jean-Pierre MartineauandClaude Opus 5 committed 2026-07-26 18:12:46 -04:00
1 parent 18b1e034fd
commit 8fdd7572bd
2 files changed
+30 -2

No files matched your search

+9 -2
View File
@@ -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,
+21
View File
@@ -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)