Node page: the PicoCalc trades the strip's air for stage rows

The blank line over the tab strip and the one under it are desktop
luxury; on the handheld both go to the stage - the route graph's
ceiling, the location preview's growth room. Bound at platform-switch
time alongside the borderless frame, never branched per paint.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Jean-Pierre MartineauandClaude Fable 5 committed 2026-08-08 19:11:45 -04:00
1 parent 6a79ec5b5e
commit 3b075149ce
1 file changed
+20 -3
+20 -3
View File
@@ -73,6 +73,7 @@ from rich.text import Text
from ..core.geo import EARTH_RADIUS_KM, usable_fix
from ..core.models import NODE_TYPE_LABELS, Contact, utcnow
from ..platforms import Platform, on_platform
from .mapcanvas import RGB
from .minimap import MiniMap
from .pathgraph import (
@@ -107,6 +108,20 @@ if TYPE_CHECKING: # pragma: no cover - typing only
#: Our own node's marker glyph and hue, matching the map/mesh-walk star.
_SELF_GLYPH = ("★", "#facc15")
#: Blank rows of air around the tab strip — one above and one under on the desktop; none
#: on the PicoCalc (JP, 2026-08-08), where rows are the scarce resource and both lines go
#: to the stage instead (the route graph's ceiling, the location preview's growth room).
#: Rides the same frugality signal as the borderless frame, and is bound at
#: platform-switch time like every platform-derived constant — never branched per paint.
_TAB_AIR = 1
@on_platform
def _bind_tab_air(platform: Platform) -> None:
"""Bind the strip's air to the platform (runs now and on every switch)."""
global _TAB_AIR
_TAB_AIR = 1 if platform.frame_border else 0
#: The inline location preview's row bounds: it grows into whatever the Info tab's
#: viewport spares (the vitals and action rows are short), floored so a cramped terminal
#: still shows a recognisable neighbourhood and capped so a tall one doesn't become all map.
@@ -659,7 +674,7 @@ class NodeDetailScreen(Screen):
lines: list[str] = []
lines.extend(render_lines(self._header, width))
if self._tabs:
lines.append("")
lines.extend([""] * _TAB_AIR)
lines.extend(
render_lines(
tab_strip([t.name for t in self._tabs], self._tab_index, width),
@@ -692,7 +707,9 @@ class NodeDetailScreen(Screen):
for i, route in enumerate(self._routes.routes if self._routes else [])
]
if not route_blocks:
lines.append("") # the bare note pays for its own air under the strip
# The bare note pays for its own air under the strip — where the
# platform affords the strip any air at all.
lines.extend([""] * _TAB_AIR)
budget = viewport - len(lines) - action_lines - 1 # the rule's line
lines.extend(self._routes_stage(width, budget, route_blocks))
# The rule closes the stage, so the drawn view and the rows below it read as
@@ -773,7 +790,7 @@ class NodeDetailScreen(Screen):
:meth:`_key_line`), so a row count the preview sizes against can't move as a reader
walks a long key.
"""
lines: list[str] = [""] # the stage's one line of air under the strip
lines: list[str] = [""] * _TAB_AIR # the stage's air under the strip, where afforded
for label, value in self._info_rows:
if label == _KEY_LABEL:
lines.append(self._key_line(label, value, width))