The graph holds still while the arrivals scroll past it
Message paths laid its whole body out as one strip — quote, fan, captions, then every arrival — and let the frame scroll it under the selection. Walk far enough down a busy message and the picture the routes are being compared against slides off the top of the dialog, which is the one thing the reader was looking at (JP, 2026-09-04). The head is pinned now and only the list moves, the app-wide windowed-list pattern the node detail's Routes tab already runs: the quote and the fan take their lines, the arrivals window into what is left with faint `↑ n more` / `↓ n more` edge markers, and the fan compresses its own lane pitch before the list would lose its window — floored at two arrivals and a marker, because comparing one arrival against another is what this dialog is for. PgUp/PgDn now page the selection by a windowful of arrivals rather than by body lines, and both they and the F-lane's chips say so only while there is more than one arrival to move between. The variable-height fit both screens need moves to `ListWindow.fit_blocks`, beside the single-line `fit` it is the sibling of; node detail drops its own copy and its hand-kept top/page state for the shared window. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
6fc4e7afc9
commit
0eb9845cd9
5 files changed
+237
-88
No files matched your search
@@ -30,7 +30,9 @@ def _screen(arrivals: list[Arrival], **kwargs) -> MessagePathsScreen:
|
||||
summary="heard twice", source="Alice",
|
||||
)
|
||||
defaults.update(kwargs)
|
||||
return MessagePathsScreen(message, arrivals, **defaults)
|
||||
screen = MessagePathsScreen(message, arrivals, **defaults)
|
||||
screen.note_viewport(40) # the frame records this before every real paint
|
||||
return screen
|
||||
|
||||
|
||||
def _arrivals() -> list[Arrival]:
|
||||
@@ -144,7 +146,7 @@ def test_paths_screen_graph_geometry_holds_still_across_the_selection() -> None:
|
||||
screen = _screen(arrivals)
|
||||
shapes = []
|
||||
for _ in range(len(arrivals)):
|
||||
shapes.append(_plain(screen._graph_lines(76)))
|
||||
shapes.append(_plain(screen._graph_lines(76, 15)))
|
||||
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
|
||||
@@ -329,3 +331,56 @@ def test_a_routed_frame_with_no_path_draws_no_graph_and_says_why() -> None:
|
||||
body = _plain(screen.render_body(72))
|
||||
assert "route not carried" in body
|
||||
assert "origin →" not in body, "no graph, so nothing for the caption to label"
|
||||
|
||||
|
||||
def _many(n: int) -> list[Arrival]:
|
||||
"""``n`` arrivals, each on its own two-hop route, oldest first."""
|
||||
now = utcnow()
|
||||
return [
|
||||
Arrival(when=now + timedelta(seconds=i), hops=(f"{i:02x}{i:02x}", "77aa"), snr=1.0)
|
||||
for i in range(n)
|
||||
]
|
||||
|
||||
|
||||
def test_the_arrival_list_windows_and_the_graph_stays_put() -> None:
|
||||
"""Only the arrivals scroll: the quote, the fan and its captions are pinned chrome.
|
||||
|
||||
Walking to the last arrival slides the window under them — the body still fits the
|
||||
dialog's budget, and the picture the routes are being compared against never leaves it.
|
||||
"""
|
||||
screen = _screen(_many(12))
|
||||
screen.note_viewport(24)
|
||||
lines = screen.render_body(72)
|
||||
assert len(lines) <= 24 # nothing scrolls off: the list took what the head left
|
||||
body = _plain(lines)
|
||||
assert "“on my way”" in body and "white = selected path" in body
|
||||
assert "↓" in body and "more" in body # the edge marker counts the hidden arrivals
|
||||
assert "PgUp/PgDn scroll" in screen.footer_hint # paging advertised only when needed
|
||||
|
||||
for _ in range(11):
|
||||
screen.handle("down")
|
||||
lines = screen.render_body(72)
|
||||
body = _plain(lines)
|
||||
assert len(lines) <= 24
|
||||
assert "white = selected path" in body, "the graph is pinned, not scrolled away"
|
||||
assert "↑" in body and "more" in body # rows now hidden above instead
|
||||
assert screen.cursor_line() is not None
|
||||
|
||||
|
||||
def test_paging_moves_the_selection_by_a_windowful() -> None:
|
||||
"""PgDn steps the pick by the rows the window actually carried, not by body lines."""
|
||||
screen = _screen(_many(12))
|
||||
screen.note_viewport(24)
|
||||
screen.render_body(72)
|
||||
page = screen._list.page
|
||||
assert 1 <= page < 12
|
||||
screen.handle("pagedown")
|
||||
assert screen._index == page
|
||||
|
||||
|
||||
def test_a_short_list_advertises_no_paging() -> None:
|
||||
"""Every arrival on screen means the pager moves nothing, and the hint says nothing."""
|
||||
screen = _screen(_arrivals())
|
||||
screen.render_body(72)
|
||||
assert "PgUp/PgDn" not in screen.footer_hint
|
||||
assert "more" not in _plain(screen.render_body(72))
|
||||
@@ -37,7 +37,7 @@ from meshterm.ui.node_detail_screen import (
|
||||
)
|
||||
from meshterm.ui.pathgraph import DST_NODE, SRC_NODE
|
||||
from meshterm.ui.pathline import CRACK_HEAD, CRACK_TAIL, SELF_GLYPH, PathHop, PathLine
|
||||
from meshterm.ui.tui.screen import CANCEL
|
||||
from meshterm.ui.tui.screen import CANCEL, ListWindow
|
||||
from meshterm.ui.widgets import highlighted_hash, route_graph_style, tab_strip
|
||||
|
||||
US = "aaaaaaaaaaaa"
|
||||
@@ -1009,10 +1009,11 @@ def test_node_detail_route_list_windows_inside_the_page() -> None:
|
||||
def test_fit_blocks_walks_wrapped_rows_into_view() -> None:
|
||||
"""The variable-height fit keeps whole blocks, spends marker lines only when rows hide,
|
||||
and walks the window down to the cursor's row."""
|
||||
screen = _screen()
|
||||
top, count = screen._fit_blocks([2, 2, 2, 2], 5, 3) # cursor on the last 2-line row
|
||||
window = ListWindow()
|
||||
top, count = window.fit_blocks([2, 2, 2, 2], 5, 3) # cursor on the last 2-line row
|
||||
assert top + count == 4 and top == 2 # slid to the tail; the last two rows fit
|
||||
top, count = screen._fit_blocks([1, 1], 5, 0)
|
||||
assert window.page == count # the settled capacity is the paging stride
|
||||
top, count = window.fit_blocks([1, 1], 5, 0)
|
||||
assert (top, count) == (0, 2) # everything fits: no window, no markers
|
||||
|
||||
|
||||
|
||||
Reference in new issue
Block a user