Footer hints: one grammar, Esc always last, one key notation
The key hints now follow one sentence shape across every screen:
navigation keys first, action keys next, Esc last — the quit dialog and
TX apply dialog had Esc leading. The Esc verb is now systematic: "Esc
back" leaves a screen, "Esc close" dismisses a read-only floating view
(result windows and the share/key popups switch over), "Esc cancel"
abandons a prompt, "Esc keep" leaves a value picker unchanged. Bare
hints fill in their missing verbs ("↑↓ move", the map's and composer's
naked "Esc"), the confirm prompt says "Enter select" like its button
siblings, and key notation unifies on the compact forms the app already
half-used: ^R/^End for Ctrl chords (chat's "Ctrl-R" included) and ⌫ for
backspace (the map/atlas "Bksp").
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
1 parent
d1c8ed73a0
commit
84a2c45f12
14 files changed
+23
-22
No files matched your search
@@ -184,7 +184,7 @@ class AtlasScreen(Screen):
|
||||
def footer_hint(self) -> str: # type: ignore[override]
|
||||
"""Camera and find hints; the find query takes the row over while active."""
|
||||
if self._filter:
|
||||
return f"find: {self._filter}▏ · Enter select · Bksp erase · Esc clear"
|
||||
return f"find: {self._filter}▏ · Enter select · ⌫ erase · Esc clear"
|
||||
return "↑↓←→ pan · PgUp/PgDn zoom · Tab walk · Enter info · type to find · Esc"
|
||||
|
||||
def handle(self, action: str, data: str = "") -> None:
|
||||
|
||||
@@ -739,7 +739,7 @@ async def _show_share(
|
||||
Text(""),
|
||||
Text(url, style="accent"),
|
||||
)
|
||||
await ctx.ui.view(body, title=f"Share {name}", footer_hint="Esc back")
|
||||
await ctx.ui.view(body, title=f"Share {name}", footer_hint="Esc close")
|
||||
|
||||
|
||||
async def _show_key(ctx: "AppContext", slot: ChannelSlot) -> None:
|
||||
@@ -775,7 +775,7 @@ async def _show_key(ctx: "AppContext", slot: ChannelSlot) -> None:
|
||||
blocks.append(Text())
|
||||
blocks.append(Text(label, style="muted"))
|
||||
blocks.append(value)
|
||||
await ctx.ui.view(Group(*blocks), title=f"Key — {slot.name}", footer_hint="Esc back")
|
||||
await ctx.ui.view(Group(*blocks), title=f"Key — {slot.name}", footer_hint="Esc close")
|
||||
|
||||
|
||||
async def _open_chat(ctx: "AppContext", slot: ChannelSlot) -> None:
|
||||
|
||||
+2
-2
@@ -167,7 +167,7 @@ class ChatScreen(Screen):
|
||||
"""Key hint, reflecting whether a message is picked for reply (channels only)."""
|
||||
if not self._is_channel:
|
||||
if any(m.outbound and m.acked is False for m in self._messages):
|
||||
return "Enter send · Ctrl-R retry failed · ↑↓/PgUp scroll · ^End latest · Esc back"
|
||||
return "Enter send · ^R retry failed · ↑↓/PgUp scroll · ^End latest · Esc back"
|
||||
return "Enter send · ↑↓/PgUp scroll · ^End latest · Esc back"
|
||||
if self._selected is not None:
|
||||
return "Enter reply (@mention) · ↑↓ pick · ^End/Esc cancel"
|
||||
@@ -736,7 +736,7 @@ class ChatScreen(Screen):
|
||||
message.acked = False
|
||||
self._status = f"retry failed: {exc}"
|
||||
else:
|
||||
self._status = "" if message.acked else "still no ack — Ctrl-R to retry"
|
||||
self._status = "" if message.acked else "still no ack — ^R to retry"
|
||||
finally:
|
||||
self._sending = False
|
||||
self._stick = True
|
||||
|
||||
@@ -816,7 +816,7 @@ async def _show_contact_card(ctx: "AppContext", snapshot: dict) -> None:
|
||||
Text(""),
|
||||
Text(url, style="accent"),
|
||||
)
|
||||
await ctx.ui.view(body, title=f"Share {name}", footer_hint="Esc back")
|
||||
await ctx.ui.view(body, title=f"Share {name}", footer_hint="Esc close")
|
||||
|
||||
|
||||
async def _reboot(ctx: "AppContext", device: "Device", snapshot: dict) -> bool:
|
||||
|
||||
@@ -290,7 +290,7 @@ async def _pick_schedule(ctx: "AppContext", name: str):
|
||||
]
|
||||
picked = await session.select(
|
||||
f"When should {name} get it?", items, filterable=False,
|
||||
footer_hint="↑↓ move · Enter choose · Esc cancel",
|
||||
footer_hint="↑↓ move · Enter select · Esc cancel",
|
||||
)
|
||||
if picked == "custom":
|
||||
while True:
|
||||
|
||||
@@ -125,9 +125,9 @@ class MapScreen(Screen):
|
||||
editing keys, so the typed text is always visible somewhere fixed.
|
||||
"""
|
||||
if self._filter:
|
||||
base = f"find: {self._filter}▏ · Enter frame · Bksp erase · Esc clear"
|
||||
base = f"find: {self._filter}▏ · Enter frame · ⌫ erase · Esc clear"
|
||||
else:
|
||||
base = "↑↓←→ pan · ⇧ fine · PgUp/PgDn zoom · Home reset · type to find · Esc"
|
||||
base = "↑↓←→ pan · ⇧ fine · PgUp/PgDn zoom · Home reset · type to find · Esc back"
|
||||
if self._pending:
|
||||
return f"{base} · [muted]loading {len(self._pending)} tiles…[/muted]"
|
||||
if not self._source.available:
|
||||
|
||||
+1
-1
@@ -422,7 +422,7 @@ async def _menu_loop(ctx: AppContext, session: TuiSession) -> None:
|
||||
buttons,
|
||||
title="Quit MeshTerm",
|
||||
default=len(buttons) - 1, # highlight Quit
|
||||
footer_hint="Esc cancel · Enter quit",
|
||||
footer_hint="Enter quit · Esc cancel",
|
||||
prompt_style="warn",
|
||||
button_style="selected",
|
||||
button_idle_style="muted",
|
||||
|
||||
@@ -98,7 +98,7 @@ class PathComposerScreen(Screen):
|
||||
:data:`~meshterm.ui.tui.screen.CANCEL`.
|
||||
"""
|
||||
|
||||
footer_hint = "↑↓ · Enter add · type to filter · ⌫ remove · Esc"
|
||||
footer_hint = "↑↓ move · Enter add · type to filter · ⌫ remove · Esc cancel"
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
|
||||
@@ -1255,7 +1255,7 @@ async def _open_session(ctx: "AppContext", target: Optional[str]) -> int:
|
||||
items,
|
||||
prompt="Forced hops are addressed by this many leading key bytes.",
|
||||
default=width_bytes,
|
||||
footer_hint="↑↓ · Enter set · Esc keep",
|
||||
footer_hint="↑↓ move · Enter set · Esc keep",
|
||||
filterable=False,
|
||||
wrap=False,
|
||||
)
|
||||
@@ -1292,7 +1292,7 @@ async def _open_session(ctx: "AppContext", target: Optional[str]) -> int:
|
||||
items,
|
||||
prompt=f"One Trace action runs this many traces, {pace:g} s apart.",
|
||||
default=sample_count,
|
||||
footer_hint="↑↓ · Enter set · Esc keep",
|
||||
footer_hint="↑↓ move · Enter set · Esc keep",
|
||||
filterable=False,
|
||||
wrap=False,
|
||||
)
|
||||
|
||||
@@ -332,7 +332,7 @@ class ConfirmScreen(Screen):
|
||||
title: str,
|
||||
*,
|
||||
default: bool = True,
|
||||
footer_hint: str = "←→/Y/N choose · Enter accept · Esc cancel",
|
||||
footer_hint: str = "←→/Y/N choose · Enter select · Esc cancel",
|
||||
) -> None:
|
||||
"""Build a confirm prompt.
|
||||
|
||||
|
||||
@@ -268,7 +268,7 @@ class ScrollScreen(Screen):
|
||||
renderable: RenderableType,
|
||||
*,
|
||||
title: str = "",
|
||||
footer_hint: str = "↑↓ PgUp/PgDn scroll · Esc back",
|
||||
footer_hint: str = "↑↓ PgUp/PgDn scroll · Esc close",
|
||||
floating: bool = True,
|
||||
) -> None:
|
||||
"""Wrap a renderable in a dismissable, scrollable screen.
|
||||
|
||||
@@ -550,7 +550,7 @@ class TuiSession:
|
||||
screen = ScrollScreen(
|
||||
renderable,
|
||||
title=title,
|
||||
footer_hint=footer_hint or "↑↓ PgUp/PgDn scroll · Esc back",
|
||||
footer_hint=footer_hint or "↑↓ PgUp/PgDn scroll · Esc close",
|
||||
)
|
||||
await self.run_screen(screen)
|
||||
|
||||
|
||||
@@ -675,7 +675,7 @@ async def open_tx_optimize(
|
||||
items,
|
||||
prompt="The TX window the sweep explores:",
|
||||
default=(screen.tx_min, screen.tx_max),
|
||||
footer_hint="↑↓ · Enter set · Esc keep",
|
||||
footer_hint="↑↓ move · Enter set · Esc keep",
|
||||
filterable=False,
|
||||
wrap=False,
|
||||
)
|
||||
@@ -714,7 +714,7 @@ async def open_tx_optimize(
|
||||
items,
|
||||
prompt="The coarse sweep measures the window at this spacing, then refines.",
|
||||
default=screen.step,
|
||||
footer_hint="↑↓ · Enter set · Esc keep",
|
||||
footer_hint="↑↓ move · Enter set · Esc keep",
|
||||
filterable=False,
|
||||
wrap=False,
|
||||
)
|
||||
@@ -739,7 +739,7 @@ async def open_tx_optimize(
|
||||
items,
|
||||
prompt=f"Each TX level is measured with this many traces, {pace:g} s apart.",
|
||||
default=screen.samples,
|
||||
footer_hint="↑↓ · Enter set · Esc keep",
|
||||
footer_hint="↑↓ move · Enter set · Esc keep",
|
||||
filterable=False,
|
||||
wrap=False,
|
||||
)
|
||||
@@ -804,7 +804,7 @@ async def open_tx_optimize(
|
||||
[("Cancel", "cancel"), ("Apply", "apply")],
|
||||
title="Apply winner",
|
||||
default=1,
|
||||
footer_hint="Esc cancel · Enter apply",
|
||||
footer_hint="Enter apply · Esc cancel",
|
||||
)
|
||||
if choice != "apply":
|
||||
return
|
||||
|
||||
+3
-2
@@ -668,8 +668,9 @@ def test_chat_screen_shows_delivery_glyphs() -> None:
|
||||
# static hourglass; resolved ones show ✅ / ❌.
|
||||
assert "✅" in joined and "❌" in joined
|
||||
assert Spinner.BRAILLE[0] in joined and "⏳" not in joined
|
||||
# A failed message advertises the retry shortcut in the footer hint.
|
||||
assert "Ctrl-R" in screen.footer_hint
|
||||
# A failed message advertises the retry shortcut in the footer hint (the app-wide
|
||||
# compact ^-notation for Ctrl chords).
|
||||
assert "^R" in screen.footer_hint
|
||||
|
||||
|
||||
def test_wrapped_body_hangs_under_the_first_line() -> None:
|
||||
|
||||
Reference in new issue
Block a user