diff --git a/CLAUDE.md b/CLAUDE.md index 6e8c306..166c01e 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -647,7 +647,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 `^U`, a row's `Del`) earns a slot — otherwise + jumps, the Time Machine's `w`, the map's `^Y`, 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. diff --git a/meshterm/ui/map_screen.py b/meshterm/ui/map_screen.py index 35ed6ed..abd38a4 100644 --- a/meshterm/ui/map_screen.py +++ b/meshterm/ui/map_screen.py @@ -15,7 +15,7 @@ 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, -* ``^U`` (for *you*) recenters on **your own node**, keeping the zoom you chose and +* ``^Y`` (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, ``Backspace`` edits, ``^Enter`` frames the @@ -287,7 +287,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/^U region/you``) and ``⇧ fine`` — a refinement of a key the line already + (``Home/^Y 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 @@ -296,7 +296,7 @@ class MapScreen(Screen): """ if self._filter: return f"find: {self._filter}▏ · ^Enter frame · ⌫ erase · Enter/Esc clear" - return "↑↓←→ pan · PgUp/PgDn zoom · Home/^U region/you · type to find · Esc back" + return "↑↓←→ pan · PgUp/PgDn zoom · Home/^Y 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). @@ -958,7 +958,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`` (^U, for *you*) our own node — ``locate_zoom`` + ``home`` the whole region, ``locate`` (^Y, for *you*) our own node — ``locate_zoom`` the Shift-bank variant that also homes in — and ``frame`` (^Enter) the find matches. ``clear_find`` drops the query without moving the view, which is also what plain **Enter** does. @@ -1041,7 +1041,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 (``^U`` / the ``You`` chip), clearing any find. + """Recentre on our own node (``^Y`` / 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 diff --git a/meshterm/ui/tui/fkeys.py b/meshterm/ui/tui/fkeys.py index 9c93f2c..d25cfdc 100644 --- a/meshterm/ui/tui/fkeys.py +++ b/meshterm/ui/tui/fkeys.py @@ -229,9 +229,9 @@ _HINT_KEY_ACTIONS: dict[str, str] = { } #: Marks a word as *some* key notation even where it isn't one this module can map: a -#: chord (``^U``), a shifted key (``⇧Tab``), or a bare glyph key (``⌫``). An atom whose +#: chord (``^Y``), a shifted key (``⇧Tab``), or a bare glyph key (``⌫``). An atom whose #: verb half carries one of these names more than the keys it opened with, so it is never -#: dropped on the strength of the ones it did name (the map's ``Home/^U region/you``). +#: dropped on the strength of the ones it did name (the map's ``Home/^Y region/you``). _KEYISH = ("^", "⇧", "⌫", "↑", "↓", "←", "→") diff --git a/meshterm/ui/tui/session.py b/meshterm/ui/tui/session.py index f649fec..bf90e5d 100644 --- a/meshterm/ui/tui/session.py +++ b/meshterm/ui/tui/session.py @@ -64,8 +64,8 @@ from .spinner import spinner_interval #: ``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. +#: ``locate`` is ^Y for **you** (JP, 2026-08-09; ^U until 2026-10-01), the same key on the +#: map and in the mesh walk, because what it names on both is our own node. #: #: Two of these are app-wide rather than a screen's: ``to_menu`` (^W) unwinds the whole #: navigation stack back to the main menu, and ``quit`` (^Q, alongside the older ^C) asks @@ -85,9 +85,9 @@ _CTRL_LETTER_CHORDS: dict[str, str] = { # ^S is XOFF's key, and safe here for the same reason ^Q is not XON (see above): raw # mode clears IXON/IXOFF, so the terminal never eats it to freeze the screen. "s": "reveal", - "u": "locate", "v": "paste_clipboard", "w": "to_menu", + "y": "locate", } #: Maps prompt_toolkit keys to the normalized action names screens understand. diff --git a/meshterm/ui/walk_screen.py b/meshterm/ui/walk_screen.py index fbc28e2..c7d8cc8 100644 --- a/meshterm/ui/walk_screen.py +++ b/meshterm/ui/walk_screen.py @@ -46,7 +46,7 @@ window, drawn whole while whole fits and *cropped* at either edge the walk conti past — a chip sheared on its own half block, the way every path row in the app that outgrows its lane is cut. At rest the window sits at the tail, so the focus and the steps just taken are in view and it is the walk's start that is cropped; **←→ slide -it** to read back over a long walk. **^U** refocuses our own node. +it** to read back over a long walk. **^Y** refocuses our own node. **Typing finds** — a global filter over every node in the graph, islands included — and narrows the canvas's fan to matching neighbours as it goes, the focus and the came-from node holding through it; Enter teleports the focus to the highlighted match (the trail @@ -239,7 +239,7 @@ class WalkScreen(Screen): 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 + else: the two ends of the list this screen scrolls. ^Y 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 @@ -285,7 +285,7 @@ class WalkScreen(Screen): self._self_label = self_label self._prefix_bytes = prefix_bytes #: The walked trail of canonical ids; the focus is its last entry. Walking - #: appends, ⌫ pops, ^U resets to us, a find teleport restarts it. + #: appends, ⌫ pops, ^Y resets to us, a find teleport restarts it. self._trail: list[str] = [topo.self_id] #: Hops the breadcrumb line is scrolled off its *tail* end (see #: :meth:`_trail_text`). 0 is the resting state — the focus flush right — and @@ -431,7 +431,7 @@ class WalkScreen(Screen): atoms = ["↑↓ move"] if scrolls: atoms.append("←→ trail") - atoms += ["Enter focus", "⌫ back", "^U you"] + atoms += ["Enter focus", "⌫ back", "^Y you"] if not scrolls: atoms.append("type to find") atoms.append("Esc back") @@ -441,7 +441,7 @@ class WalkScreen(Screen): """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 + back to our own node is ``locate`` (^Y, and the lane's F3), which is not a place in this list at all — see :attr:`fkey_lane`. """ rows = self._rows() @@ -491,7 +491,7 @@ class WalkScreen(Screen): elif action == "right": self._trail_scroll = max(0, self._trail_scroll - _HSCROLL_STEP) elif action == "locate": - # ^U (and F3): abandon the walk rather than move within it — the trail goes + # ^Y (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 = "" @@ -644,7 +644,7 @@ class WalkScreen(Screen): costs the window a cell, so the crop is measured only once both are known, else the line would draw a cell past the row. Sliding stops where the head comes into view rather than running the line off the edge, and any change to the trail itself - resets it: a walk, a step back, a teleport and ^U all end with the focus in view, + resets it: a walk, a step back, a teleport and ^Y all end with the focus in view, which is where the next move is read from. """ self._trail_width = width diff --git a/tests/test_picocalc_chrome.py b/tests/test_picocalc_chrome.py index 88200eb..5fd5073 100644 --- a/tests/test_picocalc_chrome.py +++ b/tests/test_picocalc_chrome.py @@ -359,15 +359,15 @@ 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. - The letter is the mnemonic of the action — ^U for *you* — so it is the same chord on + The letter is the mnemonic of the action — ^Y 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["u"] == "locate" # the right-Ctrl rescue's half - assert _KEY_ACTIONS[Keys.ControlU] == "locate" # the ordinary binding, generated from it + assert _CTRL_LETTER_CHORDS["y"] == "locate" # the right-Ctrl rescue's half + assert _KEY_ACTIONS[Keys.ControlY] == "locate" # the ordinary binding, generated from it def test_host_battery_reads_the_sysfs_supply(tmp_path, monkeypatch) -> None: @@ -458,7 +458,7 @@ def test_an_atom_the_lane_only_half_covers_stands() -> None: # The atom documents the arrows too, and no slot ever claims those. assert strip_lane_atoms("↑↓ PgUp/PgDn scroll", DEFAULT_LANE) == "↑↓ PgUp/PgDn scroll" # A second key riding in the verb half keeps the atom whole (the map's region/you). - assert strip_lane_atoms("Home/^U region/you", DEFAULT_LANE) == "Home/^U region/you" + assert strip_lane_atoms("Home/^Y region/you", DEFAULT_LANE) == "Home/^Y region/you" def test_a_dimmed_chip_still_covers_its_atom() -> None: diff --git a/tests/test_walk.py b/tests/test_walk.py index 0e0e649..edaefcf 100644 --- a/tests/test_walk.py +++ b/tests/test_walk.py @@ -367,7 +367,7 @@ def test_walk_trail_names_carry_their_node_hues() -> None: 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.""" + """^Y resets the walk to our own node from anywhere; Home/End are the list's ends.""" # A hub of spokes off one of our neighbours: a walked-away focus with a list to move in. topo = MeshTopology(US, contacts=[Hub]) hub = topo.canonical(Hub.public_key)