A per-hop median's path squares only the route's own ends

Each hop row drew its link as a whole route, square at both ends, so every
hop read as a walk that began and ended there. A hop is one link of the
walk: only the first hop's start and the last hop's finish are the route's
own ends. Every other end is a node the route runs on through, and now
wears the notch or the point (a bare → in arrow mode), the marks a path
line already uses for "this goes on".

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Jean-Pierre MartineauandClaude Opus 5.5 committed 2026-10-04 22:32:02 -04:00
1 parent ad036a89aa
commit ff5315ffcf
2 files changed
+48 -10

No files matched your search

+23 -4
View File
@@ -1203,7 +1203,8 @@ class TraceScreen(Screen):
Each hop reads ``n origin → destination +4.5 dB meter``, the link drawn through
THE path widget as the route lane above draws it: names, no hash after them (an
unnamed node shows its hash, the only identity it has), our own end the bare
``★``. Where every row fits whole the hops are a four-column table, one line each.
``★``, and only the route's own two ends square (see :meth:`_hop_path`). Where
every row fits whole the hops are a four-column table, one line each.
Where any doesn't, every row takes two — all alike, so the column of readings
never jumps between lines as the eye runs down it: the hop number and its path on
the first, the reading and its meter flush right on the second. A path still
@@ -1214,7 +1215,11 @@ class TraceScreen(Screen):
the rows are drawn at the shift the footer and the next press agree on.
"""
numbers = [Text(str(agg.index), style="muted") for agg in stats.hop_snrs]
paths = [self._hop_path(agg, hash_bytes) for agg in stats.hop_snrs]
last = len(stats.hop_snrs) - 1
paths = [
self._hop_path(agg, hash_bytes, from_origin=i == 0, to_destination=i == last)
for i, agg in enumerate(stats.hop_snrs)
]
readings = [
Text(f"{agg.median_snr:+.1f} dB", style=snr_style(agg.median_snr))
for agg in stats.hop_snrs
@@ -1259,8 +1264,20 @@ class TraceScreen(Screen):
lines.append(render_to_ansi(second, width, no_wrap=True))
return lines
def _hop_path(self, agg: HopAggregate, hash_bytes: int | None) -> Text:
"""One hop's link, ``origin → destination``, as the route lane draws its nodes."""
def _hop_path(
self,
agg: HopAggregate,
hash_bytes: int | None,
*,
from_origin: bool,
to_destination: bool,
) -> Text:
"""One hop's link, ``origin → destination``, as the route lane draws its nodes.
A hop is one link of the walk, so only the route's own two ends are drawn square —
the first hop's start, the last hop's finish. Every other end is a node the route
runs on through, and wears the chevron (the arrow form, a bare ``→``) that says so.
"""
ends = [
None if not node or node == self._device_label else node
for node in (agg.origin, agg.destination)
@@ -1272,6 +1289,8 @@ class TraceScreen(Screen):
self_name=self._device_label,
hash_bytes=hash_bytes,
bare_self=True,
from_origin=from_origin,
to_destination=to_destination,
).text()
def _trace_log(self) -> RenderableType:
+25 -6
View File
@@ -389,14 +389,31 @@ def test_hop_rows_name_their_link_on_one_line_where_it_fits() -> None:
"""Each hop is ``n origin → destination reading meter`` — names, never a hash."""
rows = _hop_rows(_hop_rows_screen(), 100)
assert len(rows) == 3 # one line a hop
assert rows[1].startswith("1 Lakeside → Mont-Royal Summit Relay")
assert "-3.5 dB" in rows[1] and _BAR_FULL in rows[1]
assert rows[0].startswith("0 ★ → Lakeside") # our end is the bare star
assert not any("(3d" in row or "(f2" in row for row in rows)
# Only the route's own ends are bare; every end it runs on through keeps its arrow.
assert rows[0].startswith("0 ★ → Lakeside → ") # our end is the bare star
assert rows[1].startswith("1 → Lakeside → Mont-Royal Summit Relay → ")
assert rows[2].startswith("2 → Mont-Royal Summit Relay → ★ ")
# A four-column table: every reading ends in the same column.
assert len({row.index(" dB") for row in rows}) == 1
def test_hop_rows_square_only_the_routes_own_ends(powerline) -> None: # noqa: ANN001
"""As chips, the first hop opens square and the last closes square — nothing else.
A hop is one link of the walk, so its far ends are nodes the route runs on through:
those wear the notch and the point, the marks a path line uses for "this goes on".
"""
from meshterm.ui.pathline import POWERLINE_SEP
powerline(True)
rows = _hop_rows(_hop_rows_screen(), 50) # chips are wider: every hop folds
paths = [row[2:].rstrip() for row in rows[::2]]
assert [p.startswith(POWERLINE_SEP) for p in paths] == [False, True, True]
assert [p.endswith(POWERLINE_SEP) for p in paths] == [True, True, False]
def test_hop_rows_fold_under_their_path_and_slide_where_it_does_not() -> None:
"""Too narrow for a row, every hop takes two lines; ←→ slide the paths alone.
@@ -407,7 +424,7 @@ def test_hop_rows_fold_under_their_path_and_slide_where_it_does_not() -> None:
screen = _hop_rows_screen()
rows = _hop_rows(screen, 30)
assert len(rows) == 6 # two lines a hop, all alike
assert rows[2].startswith("1 Lakeside → Mont-Royal Su")
assert rows[2].startswith("1 → Lakeside → Mont-Royal")
assert rows[2].rstrip().endswith("…") # cut where it runs on
assert "-3.5 dB" in rows[3] and cell_len(rows[3].rstrip()) == 30 # flush right
assert "←→ scroll" in screen.footer_hint
@@ -415,13 +432,15 @@ def test_hop_rows_fold_under_their_path_and_slide_where_it_does_not() -> None:
screen.handle("down") # the action cursor pins the page…
screen.handle("right")
assert screen.cursor_line() is None # …and sliding the paths lets it go again
screen.handle("right")
rows = _hop_rows(screen, 30)
assert rows[2].startswith("1 …") and rows[2].rstrip().endswith("Summit Relay")
assert rows[0].startswith("0 ★ → Lakeside") # a path in view whole never moves
assert rows[2].startswith("1 …") and rows[2].rstrip().endswith("Summit Relay →")
assert rows[0].startswith("0 ★ → Lakeside →") # a path in view whole never moves
screen.handle("right") # already at the tail: clamped
assert _hop_rows(screen, 30)[2] == rows[2]
screen.handle("left")
assert _hop_rows(screen, 30)[2].startswith("1 Lakeside")
screen.handle("left")
assert _hop_rows(screen, 30)[2].startswith("1 → Lakeside")
_hop_rows(screen, 100) # wide enough again: nothing to slide, nothing advertised
assert "←→ scroll" not in screen.footer_hint