F-lane: You was standing in for a list end it isn't

The mesh walk's You had been living behind Page up, in the pager's Shift
bank, because that bank is where a screen's Home lands by default. But You
is not an end of the link list -- it drops the whole trail and refocuses our
own node -- so it had to pretend to be Top, and Bottom had to stay blank to
keep the pretence, and the walk was the one scrolling screen whose Home
didn't scroll.

So You takes a slot of the screen's own (F3, which is what F1-F3 are for),
and the pager's Shift bank goes back to meaning what it means everywhere
else: F10 Top, F9 Bottom, the two ends of the list. Home and End on a
keyboard follow the same correction and now move the highlight.

Its chord moves with it: locate is ^U, for you, on the map as well as here.
The letter is the mnemonic of the action rather than one screen's word for
it -- ^L only ever read as "locate" from inside the map's code, and the
action is the same one on both screens, so it should not have been two
mnemonics waiting to happen.

You dims once the focus is already us and no find is narrowing the list --
there is nowhere to come back from.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Jean-Pierre MartineauandClaude Opus 5 committed 2026-08-09 11:37:38 -04:00
1 parent 4466d75932
commit 1eda585c50
6 files changed
+80 -32

No files matched your search

+1 -1
View File
@@ -219,7 +219,7 @@ branch on the platform per frame, and never `from meshterm.platforms import PLAT
thing that just isn't available this paint. Dimming is presentational — `handle` stays
the authority and no-ops. The lane is also the *only* affordance advertisement here, so
anything the desktop reaches by a chord or a bare letter (a list's `^PgUp/^PgDn` section
jumps, the Time Machine's `w`, the map's `^L`, a row's `Del`) earns a slot — otherwise
jumps, the Time Machine's `w`, the map's `^U`, a row's `Del`) earns a slot — otherwise
it is undiscoverable on the device.
- `meshterm specimen` prints the whole visual language through the real funnels — the
acceptance card on-device, a preview under `--platform picocalc` on the desktop.
+6 -6
View File
@@ -14,8 +14,8 @@ render_map`. Keys:
* ``PgUp`` / ``PgDn`` zoom in / out,
* ``Home`` recenters and refits to the dense core of the nodes — the *region* the mesh
covers, and the same default view the map opens on,
* ``Ctrl+L`` recenters on **your own node**, keeping the zoom you chose and clearing any
find (the lane's ``You`` chip; its Shift half also zooms in close),
* ``^U`` (for *you*) recenters on **your own node**, keeping the zoom you chose and
clearing any find (the lane's ``You`` chip; its Shift half also zooms in close),
* **typing finds nodes**: every letter key feeds a live name filter — matching nodes keep
bright labels while the rest dim to context, ``Enter`` frames the matches, ``Backspace``
edits, and ``Esc`` clears the filter (a second ``Esc`` leaves the map). This is why no
@@ -224,7 +224,7 @@ class MapScreen(Screen):
editing keys, so the typed text is always visible somewhere fixed.
The line spends its whole 72-cell budget, so the two view-jump keys share one atom
(``Home/^L region/you``) and ``⇧ fine`` — a refinement of a key the line already
(``Home/^U region/you``) and ``⇧ fine`` — a refinement of a key the line already
names, and the only atom here that documents a *modifier* rather than a binding —
is the one that gives way to make room for them. The basemap's state used to hang
off the end of this line as a suffix; it is a status atom, not a key, so it moved
@@ -233,7 +233,7 @@ class MapScreen(Screen):
"""
if self._filter:
return f"find: {self._filter}▏ · Enter frame · ⌫ erase · Esc clear"
return "↑↓←→ pan · PgUp/PgDn zoom · Home/^L region/you · type to find · Esc back"
return "↑↓←→ pan · PgUp/PgDn zoom · Home/^U region/you · type to find · Esc back"
def consume_edge_scrub(self) -> int:
"""Right-edge columns the session should force-repaint on the next paint (0 = none).
@@ -520,7 +520,7 @@ class MapScreen(Screen):
active filter first, the map itself only once the filter is clear.
Three actions reframe the view, and each has both a key and an F-key chip:
``home`` the whole region, ``locate`` (Ctrl+L) our own node — ``locate_zoom``
``home`` the whole region, ``locate`` (^U, for *you*) our own node — ``locate_zoom``
the Shift-bank variant that also homes in — and ``frame`` the find matches,
which is what Enter already does while a query is being typed, kept as a
separate action so the lane can name it on a platform that draws no hint line.
@@ -589,7 +589,7 @@ class MapScreen(Screen):
return next((m for m in self._markers if m.is_self), None)
def _locate(self, vp: Viewport, *, zoom_in: bool = False) -> None:
"""Recentre on our own node (``Ctrl+L`` / the ``You`` chip), clearing any find.
"""Recentre on our own node (``^U`` / the ``You`` chip), clearing any find.
Plain ``You`` keeps the zoom the user chose — pressing it twice does the same
thing twice, and pairing it with one Zoom + is a single extra press. Its Shift
+4 -1
View File
@@ -58,11 +58,14 @@ from .select import Choice, ReorderScreen, SelectScreen, Separator
#: genuine third-level character arrives as some *other* glyph and never matches this table.
#: ``m``/``i``/``h`` are not free: prompt_toolkit spells Enter, Tab and Backspace as
#: ``Keys.ControlM``/``ControlI``/``ControlH``, so claiming them here would rebind those keys.
#: A chord's letter is the mnemonic of the *action*, not of one screen's word for it —
#: ``locate`` is ^U for **you** (JP, 2026-08-09), the same key on the map and in the mesh
#: walk, because what it names on both is our own node.
_CTRL_LETTER_CHORDS: dict[str, str] = {
"c": "quit",
"l": "locate",
"p": "paths",
"r": "retry",
"u": "locate",
"v": "paste_clipboard",
}
+28 -13
View File
@@ -213,20 +213,24 @@ class WalkScreen(Screen):
@property
def fkey_lane(self):
"""The shared pager over the link list, with ``You`` behind Page ↑ and nothing behind ↓.
"""``You`` on the screen's own F3, the shared pager and its two ends on F4/F5.
The walk's Home does not scroll to a top — it drops the whole trail and puts the
focus back on our own node — so the Shift companion that would say *Top* says
``You`` instead. ``End`` is bound to nothing here, so its companion stays blank:
a trail has a beginning (us) but no far end to jump to. Both nav slots need rows
to move through, which a leaf node in a sparse graph may not have.
``You`` is not an end of the link list — it drops the whole trail and puts the
focus back on our own node — so it never belonged behind the pager, where it had
to stand in for *Top* and leave *Bottom* blank to keep the pretence. It takes a
slot of the screen's own instead (JP, 2026-08-09), which is what F1-F3 are for,
and the pager's Shift bank goes back to meaning here what it means everywhere
else: the two ends of the list this screen scrolls. ^U reaches the same action on
a keyboard, the same chord the map spends on our node.
Both nav slots need rows to move through, which a leaf node in a sparse graph may
not have; ``You`` needs somewhere to come back *from* — a walked trail, or a find
narrowing the list — and dims once the focus is already us.
"""
from .tui.fkeys import FPair, default_lane
paging = len(self._rows()) > 1
lane = list(default_lane(nav=paging))
lane[3] = FPair("Page ↓", "pagedown", enabled=paging)
lane[4] = FPair("Page ↑", "pageup", "You", "home", enabled=paging)
lane = list(default_lane(nav=len(self._rows()) > 1))
lane[2] = FPair("You", "locate", enabled=len(self._trail) > 1 or bool(self._filter))
return lane
def __init__(
@@ -364,10 +368,15 @@ class WalkScreen(Screen):
"""Walking keys — or the live find query while one is being typed."""
if self._filter:
return f"find: {self._filter}▏ · ↑↓ move · Enter focus · ⌫ erase · Esc clear"
return "↑↓ move · Enter focus · ⌫ back · Home you · type to find · Esc back"
return "↑↓ move · Enter focus · ⌫ back · ^U you · type to find · Esc back"
def handle(self, action: str, data: str = "") -> None:
"""Move the highlight, walk, back up, find, or dismiss."""
"""Move the highlight, walk, back up, find, or dismiss.
Home/End are the list's own ends here, as on every other scrolling screen; the jump
back to our own node is ``locate`` (^U, and the lane's F3), which is not a place in
this list at all — see :attr:`fkey_lane`.
"""
rows = self._rows()
if action == "escape":
if self._filter:
@@ -395,7 +404,13 @@ class WalkScreen(Screen):
elif len(self._trail) > 1:
self._trail.pop()
self._index = 0
elif action in ("home", "ctrl_home"):
elif action in ("home", "ctrl_home") and rows:
self._index = 0
elif action in ("end", "ctrl_end") and rows:
self._index = len(rows) - 1
elif action == "locate":
# ^U (and F3): abandon the walk rather than move within it — the trail goes
# back to just us and any find narrowing the list is dropped with it.
self._trail = [self._topo.self_id]
self._filter = ""
self._index = 0
+7 -3
View File
@@ -212,13 +212,17 @@ def test_dialogs_draw_no_lane_at_all() -> None:
def test_map_locate_is_reachable_from_both_control_keys() -> None:
"""``locate`` is bound as a chord app-wide, so both Ctrl keys and F2 reach the same action."""
"""``locate`` is bound as a chord app-wide, so both Ctrl keys and F2 reach the same action.
The letter is the mnemonic of the action — ^U for *you* — so it is the same chord on
every screen that can point at our own node, not one screen's initial.
"""
from prompt_toolkit.keys import Keys
from meshterm.ui.tui.session import _CTRL_LETTER_CHORDS, _KEY_ACTIONS
assert _CTRL_LETTER_CHORDS["l"] == "locate" # the right-Ctrl rescue's half
assert _KEY_ACTIONS[Keys.ControlL] == "locate" # the ordinary binding, generated from it
assert _CTRL_LETTER_CHORDS["u"] == "locate" # the right-Ctrl rescue's half
assert _KEY_ACTIONS[Keys.ControlU] == "locate" # the ordinary binding, generated from it
def test_host_battery_reads_the_sysfs_supply(tmp_path, monkeypatch) -> None:
+34 -8
View File
@@ -305,13 +305,22 @@ def test_walk_trail_names_carry_their_node_hues() -> None:
)
def test_walk_home_refocuses_us() -> None:
"""Home resets the walk to our own node from anywhere."""
def test_walk_locate_refocuses_us_and_home_end_walk_the_list() -> None:
"""^U resets the walk to our own node from anywhere; Home/End are the list's ends."""
topo = _topo()
screen = _screen(topo)
screen.render_body(80)
screen.handle("enter") # us → YUL
rows = screen._rows()
assert len(rows) > 1, "the fixture's YUL needs a list to move within"
# Home/End move the highlight — they no longer abandon the walk.
screen.handle("end")
assert screen._index == len(rows) - 1 and screen._trail[-1] != topo.self_id
screen.handle("home")
assert screen._index == 0 and screen._trail[-1] != topo.self_id
screen.handle("locate")
assert screen._trail == [topo.self_id]
@@ -348,8 +357,8 @@ def test_walk_echoes_the_find_query_above_the_matches_it_narrows() -> None:
set_platform(REGULAR)
def test_walk_fkey_lane_says_you_where_the_shared_lane_would_say_top() -> None:
"""Home drops the trail rather than scrolling, and End is bound to nothing at all."""
def test_walk_fkey_lane_gives_you_its_own_slot_over_the_pagers_ends() -> None:
"""``You`` is a verb of this screen (F3), not an end of the list the pager scrolls."""
from meshterm.ui.tui.fkeys import action_for
screen = _screen(_topo())
@@ -357,11 +366,28 @@ def test_walk_fkey_lane_says_you_where_the_shared_lane_would_say_top() -> None:
lane = screen.fkey_lane
assert [pair.label if pair else None for pair in lane] == [
None, None, None, "Page ↓", "Page ↑",
None, None, "You", "Page ↓", "Page ↑",
]
# The jump behind Page ↑ goes back to us; the one behind Page ↓ doesn't exist here.
assert lane[4].opp_label == "You" and action_for(lane, 10) == "home"
assert lane[3].opp_label == "" and action_for(lane, 9) is None
assert action_for(lane, 3) == "locate"
assert lane[2].opp_label == "" # nothing is the opposite of going home
# And the pager's Shift bank means what it means everywhere else: the list's two ends.
assert lane[4].opp_label == "Top" and action_for(lane, 10) == "home"
assert lane[3].opp_label == "Bottom" and action_for(lane, 9) == "end"
def test_walk_you_dims_once_the_focus_is_already_us() -> None:
"""Dim says *a thing here, just not right now* — there is nowhere to come back from."""
screen = _screen(_topo())
screen.render_body(80)
assert screen.fkey_lane[2].enabled is False # opens on us, trail of one
screen.handle("enter") # walked away: now there is
assert screen.fkey_lane[2].enabled is True
screen.handle("locate")
assert screen.fkey_lane[2].enabled is False
screen.handle("text", "a") # a find narrows the list off our own neighbours too
assert screen.fkey_lane[2].enabled is True
def test_walk_came_from_anchors_west_and_the_fan_stays_east() -> None: