The node page shows its map preview again, except on a short frame

A contact's page has enough action rows that, on a 24-row terminal, they left
the preview under its five-row floor, and it stepped aside everywhere. It now
keeps its floor and the page scrolls. Only a short frame (the Cardputer's
11-row viewport) leaves it out.

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
This commit is contained in:
Jean-Pierre MartineauandClaude Haiku 5.5 committed 2026-10-11 09:34:53 -04:00
1 parent ad8792f34b
commit 28529d6b62
2 files changed
+51 -18

No files matched your search

+18 -18
View File
@@ -23,7 +23,7 @@ screen is the node itself, in full:
faint ``…`` marks at the edges, the same as a long path line scrolls on the Routes
tab. Then, when the node has advertised a location, a static basemap preview (refer
to :class:`~meshterm.ui.minimap.MiniMap`) centred on the node. The preview grows into
the rows of the viewport that remain.
the rows of the viewport that remain. A short frame (the Cardputer) does not show it.
The actions of the tab: ``Open full map`` opens the full map, centred here, with its
find filter set to this node, so the node is lit among the others. Then
@@ -884,7 +884,7 @@ class NodeDetailScreen(Screen):
if tab is not None:
if tab.kind == "info":
budget = viewport - len(lines) - action_lines - 1 # the line of the rule
lines.extend(self._info_stage(width, budget))
lines.extend(self._info_stage(width, budget, short=short))
else:
route_blocks = [
self._route_row_lines(route, i == self._row_index, width)
@@ -936,17 +936,18 @@ class NodeDetailScreen(Screen):
self._scroll_total = max(1, len(lines))
return lines
def _info_stage(self, width: int, budget: int) -> list[str]:
def _info_stage(self, width: int, budget: int, *, short: bool = False) -> list[str]:
"""The block of vitals and, with a fix, the location preview grown to fit.
``budget`` is the number of visible lines that remain for the full stage. The rows
of vitals are never truncated. The preview changes its size instead: it takes the
lines that the vitals and its caption leave, up to ``_MAP_MAX_ROWS``. When that is
less than ``_MAP_MIN_ROWS``, the preview is not shown at all (a short frame keeps
its header and actions visible instead). Each row, but not the key, hangs under
itself when it wraps. The key keeps its one line and scrolls (refer to
:meth:`_key_line`). Thus the row count that sets the size of the preview cannot
change while the user scrolls through a long key.
lines that the vitals and its caption leave, between ``_MAP_MIN_ROWS`` and
``_MAP_MAX_ROWS``. When the vitals leave less than the minimum, the preview keeps
its minimum and the page scrolls. On a ``short`` frame, the preview is not shown at
all, because its minimum alone pushes the header or the actions off the screen.
Each row, but not the key, hangs under itself when it wraps. The key keeps its one
line and scrolls (refer to :meth:`_key_line`). Thus the row count that sets the
size of the preview cannot change while the user scrolls through a long key.
"""
lines: list[str] = [""] * tab_air() # the space of the stage under the strip, if any
for label, value in self._info_rows:
@@ -961,17 +962,16 @@ class NodeDetailScreen(Screen):
indent=_LABEL_LANE,
)
)
if self._minimap is not None:
# A short frame has no rows for the preview. The 14 rows of the Cardputer leave it
# two, and its minimum pushes the identity header and the actions off the screen.
if self._minimap is not None and not short:
caption = 1 if self._map_caption is not None else 0
room = budget - len(lines) - 1 - caption # minus the blank line above it
# Below its minimum, the preview is not shown. It does not push the identity
# header and the actions off a short frame (the 14 rows of the Cardputer leave
# it two).
if room >= _MAP_MIN_ROWS:
lines.append("")
lines.extend(self._minimap.render(width, min(_MAP_MAX_ROWS, room)))
if self._map_caption is not None:
lines.extend(render_lines(self._map_caption, width, no_wrap=True))
rows = max(_MAP_MIN_ROWS, min(_MAP_MAX_ROWS, room))
lines.append("")
lines.extend(self._minimap.render(width, rows))
if self._map_caption is not None:
lines.extend(render_lines(self._map_caption, width, no_wrap=True))
return lines
def _key_line(self, label: str, value: Text, width: int) -> str:
+33
View File
@@ -1369,6 +1369,39 @@ def test_node_detail_screen_tabs_switch_the_stage() -> None:
assert screen.picocalc_lyra_lane[2].label == "Info"
def test_node_detail_preview_keeps_its_minimum_except_on_a_short_frame() -> None:
"""The preview shows at its minimum when the actions leave less. A short frame has none.
A contact page has many action rows, so on a terminal of 24 rows they leave the preview
less than its minimum. The preview keeps its minimum and the page scrolls. The 11-row
viewport of the Cardputer is a short frame, and there the preview is not shown.
"""
mini = MiniMap(
_FakeSession(),
_OfflineSource(),
14,
center_lat=45.5,
center_lon=-73.6,
zoom=13,
markers=[MapMarker(label="Hub", lat=45.5, lon=-73.6, key="3d63aa")],
)
actions = [_Action(f"a{i}", "⚙", "", f"Action {i}") for i in range(8)]
screen = _screen(
minimap=mini,
map_caption=Text("Hub · centred here", style="faint"),
info_actions=actions,
)
screen.note_viewport(21)
body = _plain(screen.render_body(72)).splitlines()
caption = next(i for i, line in enumerate(body) if "centred here" in line)
vitals = next(i for i, line in enumerate(body) if line.startswith("packets"))
assert caption - vitals - 2 >= 5 # the rows between the blank line and the caption
assert len(body) > 21 # the preview did not shrink under its minimum: the page scrolls
screen.note_viewport(11)
assert "centred here" not in _plain(screen.render_body(53))
def test_node_detail_single_tab_hides_the_switch_hint() -> None:
"""A page with only one tab does not have the ←→ tab atom in its footer."""
screen = _screen(tabs=[_Tab("Info", "info")]) # our node: no Routes tab