Titles speak one language: Sentence case, no emoji, — for subjects
Every screen and dialog title now follows the same three rules. Titles
are sentence-cased ("Trace — YUL-Poly", "Dashboard — mesh overview",
"Nodes"), never bare lowercase ("trace · x", "nodes", "command line ·
x", "path width", "admin login" and a dozen siblings). An em dash
introduces a title's subject or qualifier while middle dots stay for
chained status atoms ("Map · z12 · 34 nodes · 2 km across"), so the two
separators mean different things instead of being used at random. And
no title carries an emoji — the icon belongs to the menu row; a
content-sized floating dialog whose title emoji the terminal draws a
cell narrower than Rich measures leaves the border short (the bug the
Send advert popup already documented), so the Watchtower, Courier,
Time machine, identity-key, reboot, sync, restore, location, factory
reset, and advert dialogs all drop theirs.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
1 parent
241be536d8
commit
d1c8ed73a0
16 files changed
+64
-62
No files matched your search
@@ -384,7 +384,7 @@ class AtlasScreen(Screen):
|
||||
def _compose_title(self, links: list[Link], matches: list[str]) -> str:
|
||||
"""The status title: counts, the zoom level, and the find tally when active."""
|
||||
nodes = len(self._layout_nodes(links))
|
||||
title = f"Mesh Atlas · {nodes} nodes · {len(links)} links"
|
||||
title = f"Mesh atlas · {nodes} nodes · {len(links)} links"
|
||||
if self._zoom > _ZOOM_MIN:
|
||||
title += f" · {self._zoom:.1f}×"
|
||||
if self._filter:
|
||||
|
||||
@@ -490,7 +490,7 @@ async def _stage_advert_cadence(
|
||||
else:
|
||||
prompt = "How often this node announces itself to neighbours in range:"
|
||||
selected = await ctx.ui.select(
|
||||
"📡 Flood advert" if flood else "📡 Direct advert",
|
||||
"Flood advert" if flood else "Direct advert",
|
||||
items,
|
||||
prompt=prompt,
|
||||
default=current,
|
||||
@@ -515,7 +515,7 @@ async def _stage_location(
|
||||
choice = await ctx.ui.dialog(
|
||||
f"Advertised location: {_format_coords(lat, lon)}",
|
||||
[("Pick on map", "map"), ("Type coordinates", "type"), ("Clear", "clear")],
|
||||
title="📍 Location",
|
||||
title="Location",
|
||||
)
|
||||
if choice is None:
|
||||
return
|
||||
@@ -834,7 +834,7 @@ async def _reboot(ctx: "AppContext", device: "Device", snapshot: dict) -> bool:
|
||||
choice = await ctx.ui.dialog(
|
||||
"Reboot the device now?",
|
||||
[("Cancel", None), ("Reboot", "reboot")],
|
||||
title="🔄 Reboot device",
|
||||
title="Reboot device",
|
||||
default=1,
|
||||
danger=True,
|
||||
)
|
||||
@@ -895,7 +895,7 @@ async def _sync_clock(ctx: "AppContext", device: "Device", snapshot: dict) -> No
|
||||
choice = await ctx.ui.dialog(
|
||||
prompt,
|
||||
[("Cancel", None), ("Sync", "sync")],
|
||||
title="🕒 Sync clock",
|
||||
title="Sync clock",
|
||||
default=1,
|
||||
)
|
||||
if choice == "sync":
|
||||
@@ -948,7 +948,7 @@ async def _restore_now(ctx: "AppContext", device: "Device", snapshot: dict) -> b
|
||||
choice = await ctx.ui.dialog(
|
||||
"Apply the backup now, or preview the changes first?",
|
||||
[("Cancel", None), ("Preview", "preview"), ("Apply", "apply")],
|
||||
title="📂 Restore from backup",
|
||||
title="Restore from backup",
|
||||
default=1,
|
||||
)
|
||||
if choice == "preview":
|
||||
@@ -956,7 +956,7 @@ async def _restore_now(ctx: "AppContext", device: "Device", snapshot: dict) -> b
|
||||
choice = await ctx.ui.dialog(
|
||||
"Apply these changes to the device?",
|
||||
[("Cancel", None), ("Apply", "apply")],
|
||||
title="📂 Restore from backup",
|
||||
title="Restore from backup",
|
||||
default=1,
|
||||
)
|
||||
if choice != "apply":
|
||||
@@ -973,7 +973,7 @@ async def _identity_key_menu(ctx: "AppContext", device: "Device", snapshot: dict
|
||||
its snapshot.
|
||||
"""
|
||||
choice = await ctx.ui.select(
|
||||
"🔐 Identity key",
|
||||
"Identity key",
|
||||
[
|
||||
*menu_rows(
|
||||
[
|
||||
@@ -994,7 +994,7 @@ async def _identity_key_menu(ctx: "AppContext", device: "Device", snapshot: dict
|
||||
"The private key IS the node's identity — anyone who sees it can impersonate "
|
||||
"this node. Show it on screen?",
|
||||
[("Cancel", None), ("Show key", "show")],
|
||||
title="🔐 Show private key",
|
||||
title="Show private key",
|
||||
default=1,
|
||||
danger=True,
|
||||
)
|
||||
@@ -1024,7 +1024,7 @@ async def _identity_key_menu(ctx: "AppContext", device: "Device", snapshot: dict
|
||||
"Importing a key permanently overwrites this device's identity. Contacts and "
|
||||
"messages keyed to the old identity will no longer match it.",
|
||||
"IMPORT",
|
||||
title="🔐 Import private key",
|
||||
title="Import private key",
|
||||
)
|
||||
if not confirmed:
|
||||
return False
|
||||
@@ -1043,7 +1043,7 @@ async def _factory_reset(ctx: "AppContext", device: "Device", snapshot: dict) ->
|
||||
"This erases EVERYTHING on the device — identity, contacts, channels, and every "
|
||||
"setting — and cannot be undone.",
|
||||
"RESET",
|
||||
title="⚠ Factory reset",
|
||||
title="Factory reset",
|
||||
)
|
||||
if not confirmed:
|
||||
return False
|
||||
|
||||
@@ -117,7 +117,7 @@ async def open_courier(ctx: "AppContext") -> Optional[dict[str, Any]]:
|
||||
while True:
|
||||
items = _menu_items(ctx, store.entries())
|
||||
menu = SelectScreen(
|
||||
"📨 Courier — store-and-forward outbox",
|
||||
"Courier — store-and-forward outbox",
|
||||
items,
|
||||
default=cursor,
|
||||
wrap=False,
|
||||
@@ -225,7 +225,7 @@ async def _queue_flow(ctx: "AppContext", contacts: list[Contact]) -> None:
|
||||
"No contacts available — connect a device that knows some nodes first.",
|
||||
style="muted",
|
||||
),
|
||||
title="queue a message",
|
||||
title="Queue a message",
|
||||
)
|
||||
return
|
||||
|
||||
@@ -246,7 +246,7 @@ async def _queue_flow(ctx: "AppContext", contacts: list[Contact]) -> None:
|
||||
row.append(f" heard {_format_age(secs)}", style="muted")
|
||||
items.append(Choice(row, contact))
|
||||
contact = await session.select(
|
||||
"✉ Courier — recipient",
|
||||
"Courier — recipient",
|
||||
items,
|
||||
prompt="The message waits in the outbox until this node can take it:",
|
||||
)
|
||||
@@ -265,7 +265,7 @@ async def _queue_flow(ctx: "AppContext", contacts: list[Contact]) -> None:
|
||||
if key is None:
|
||||
await session.message_dialog(
|
||||
Text(f"{contact.name!r} has no usable key to address.", style="err"),
|
||||
title="queue a message",
|
||||
title="Queue a message",
|
||||
)
|
||||
return
|
||||
ctx.courier_store.queue(key, contact.name, text, not_before=not_before)
|
||||
@@ -325,14 +325,14 @@ async def _entry_actions(ctx: "AppContext", ident: int) -> None:
|
||||
f"{'s' if message.attempts != 1 else ''}",
|
||||
style="muted",
|
||||
)
|
||||
await session.message_dialog(body, title=f"📨 {message.node_name}")
|
||||
await session.message_dialog(body, title=message.node_name)
|
||||
return
|
||||
items = [
|
||||
Choice("📤 Send now — one forced attempt", "send"),
|
||||
Choice(Text.assemble(("✗ ", "err"), "Cancel this message"), "cancel"),
|
||||
]
|
||||
picked = await session.select(
|
||||
f"📨 {message.node_name} — “{_shorten(message.text, 28)}”",
|
||||
f"{message.node_name} — “{_shorten(message.text, 28)}”",
|
||||
items, filterable=False,
|
||||
footer_hint="↑↓ move · Enter select · Esc back",
|
||||
)
|
||||
@@ -344,7 +344,7 @@ async def _entry_actions(ctx: "AppContext", ident: int) -> None:
|
||||
outcome = await ctx.courier.attempt_now(ident)
|
||||
except Exception as exc: # noqa: BLE001 - surface the failure, keep the queue
|
||||
await session.message_dialog(
|
||||
Text(f"send failed: {exc}", style="err"), title="courier"
|
||||
Text(f"send failed: {exc}", style="err"), title="Courier"
|
||||
)
|
||||
return
|
||||
notes = {
|
||||
@@ -362,5 +362,5 @@ async def _entry_actions(ctx: "AppContext", ident: int) -> None:
|
||||
"gone": Text("this entry is no longer queued", style="muted"),
|
||||
}
|
||||
await session.message_dialog(
|
||||
notes.get(outcome, Text(outcome)), title="courier"
|
||||
notes.get(outcome, Text(outcome)), title="Courier"
|
||||
)
|
||||
@@ -155,7 +155,7 @@ class DashboardScreen(Screen):
|
||||
so it can attempt to decrypt an overheard channel-text packet.
|
||||
"""
|
||||
super().__init__()
|
||||
self.title = "dashboard · mesh overview"
|
||||
self.title = "Dashboard — mesh overview"
|
||||
self._session = session
|
||||
self._resolve = resolve
|
||||
self._activity = activity
|
||||
|
||||
@@ -415,7 +415,7 @@ class LocationPickScreen(MapScreen):
|
||||
"""A live status title: the coordinates under the crosshair and the scale."""
|
||||
base = super()._title(vp)
|
||||
scale = base.rsplit("·", 1)[-1].strip()
|
||||
return f"set location · {vp.center_lat:.5f}, {vp.center_lon:.5f} · z{vp.zoom} · {scale}"
|
||||
return f"Set location · {vp.center_lat:.5f}, {vp.center_lon:.5f} · z{vp.zoom} · {scale}"
|
||||
|
||||
def handle(self, action: str, data: str = "") -> None:
|
||||
"""Commit the centre on Enter; ``Home`` returns to the initial spot; else map keys."""
|
||||
|
||||
@@ -49,7 +49,7 @@ class NodesScreen(Screen):
|
||||
sort: The initial sort; mutated in place as the user presses the arrows.
|
||||
"""
|
||||
super().__init__()
|
||||
self.title = "nodes"
|
||||
self.title = "Nodes"
|
||||
self._self_name = self_name
|
||||
self._self_key = self_key
|
||||
self._contacts = contacts
|
||||
|
||||
@@ -137,7 +137,7 @@ class PathComposerScreen(Screen):
|
||||
them, the *fetch neighbours* row appears.
|
||||
"""
|
||||
super().__init__()
|
||||
self.title = f"compose path · {target_label}" if target_label else "compose path"
|
||||
self.title = f"Compose path — {target_label}" if target_label else "Compose path"
|
||||
self._target_id = target_id
|
||||
self._target_hash = (target_hash or "").lower().removeprefix("0x")
|
||||
self._target_label = target_label
|
||||
|
||||
@@ -61,7 +61,7 @@ class RemoteCliScreen(Screen):
|
||||
session: The running TUI session (for repaints).
|
||||
"""
|
||||
super().__init__()
|
||||
self.title = f"command line · {node_label}"
|
||||
self.title = f"Command line — {node_label}"
|
||||
self._node_label = node_label
|
||||
self._send = send
|
||||
self._session = session
|
||||
|
||||
@@ -126,7 +126,7 @@ async def _login(ctx: "AppContext", device: "Device", node: Contact) -> bool:
|
||||
+ "try again to enter a new one.",
|
||||
style="err",
|
||||
),
|
||||
title="admin login",
|
||||
title="Admin login",
|
||||
)
|
||||
return False
|
||||
ctx.admin_store.remember(node, password)
|
||||
@@ -175,14 +175,14 @@ async def _admin_session(
|
||||
elif choice == _ADVERT:
|
||||
await _simple_action(
|
||||
ctx, device, node, "advert",
|
||||
title="📡 Send advert",
|
||||
title="Send advert",
|
||||
prompt=f"Ask {node.name} to announce itself (flood) now?",
|
||||
commit="Send",
|
||||
)
|
||||
elif choice == _CLOCK:
|
||||
await _simple_action(
|
||||
ctx, device, node, "clock sync",
|
||||
title="🕒 Sync clock",
|
||||
title="Sync clock",
|
||||
prompt=f"Set {node.name}'s clock from this companion's time?",
|
||||
commit="Sync",
|
||||
)
|
||||
@@ -191,7 +191,7 @@ async def _admin_session(
|
||||
elif choice == _REBOOT:
|
||||
await _simple_action(
|
||||
ctx, device, node, "reboot",
|
||||
title="🔄 Reboot node",
|
||||
title="Reboot node",
|
||||
prompt=f"Reboot {node.name} now? It drops off the mesh while booting.",
|
||||
commit="Reboot",
|
||||
danger=True,
|
||||
@@ -375,7 +375,7 @@ async def _apply(
|
||||
if i < total:
|
||||
await asyncio.sleep(ctx.settings.trace_cooldown_s)
|
||||
|
||||
aborted = await _run_under_dialog(ctx, f"applying · {node.name}", work)
|
||||
aborted = await _run_under_dialog(ctx, f"Applying — {node.name}", work)
|
||||
ctx.repo.finish_run(
|
||||
run_id, "error" if aborted else "ok",
|
||||
{"applied": applied, "staged_left": len(pending)},
|
||||
@@ -387,7 +387,7 @@ async def _apply(
|
||||
summary.append(" (aborted — the rest stay staged)", style="warn")
|
||||
await ctx.ui.session.message_dialog(
|
||||
Text("\n").join([summary, Text(), *outcomes]) if outcomes else summary,
|
||||
title=f"apply · {node.name}",
|
||||
title=f"Apply — {node.name}",
|
||||
)
|
||||
return applied
|
||||
|
||||
@@ -420,7 +420,7 @@ async def _read_all(ctx: "AppContext", device: "Device", node: Contact) -> None:
|
||||
if i < len(specs):
|
||||
await asyncio.sleep(ctx.settings.trace_cooldown_s)
|
||||
|
||||
aborted = await _run_under_dialog(ctx, f"reading · {node.name}", work)
|
||||
aborted = await _run_under_dialog(ctx, f"Reading — {node.name}", work)
|
||||
ctx.repo.finish_run(
|
||||
run_id, "error" if aborted else "ok", {"read": read, "asked": len(specs)}
|
||||
)
|
||||
@@ -517,7 +517,7 @@ async def _change_password(ctx: "AppContext", device: "Device", node: Contact) -
|
||||
confirmed = await ctx.ui.dialog(
|
||||
f"Change {node.name}'s admin password now? You will need the new one everywhere.",
|
||||
[("Cancel", None), ("Change", "go")],
|
||||
title="🔐 Admin password",
|
||||
title="Admin password",
|
||||
default=1,
|
||||
danger=True,
|
||||
)
|
||||
@@ -538,7 +538,7 @@ async def _change_password(ctx: "AppContext", device: "Device", node: Contact) -
|
||||
)
|
||||
else:
|
||||
body = Text(reply.strip(), style="err")
|
||||
await ctx.ui.session.message_dialog(body, title="admin password")
|
||||
await ctx.ui.session.message_dialog(body, title="Admin password")
|
||||
|
||||
|
||||
# --- the command line ---------------------------------------------------------------
|
||||
|
||||
@@ -165,7 +165,7 @@ class TimeMachineScreen(Screen):
|
||||
|
||||
def _set_title(self) -> None:
|
||||
name, _delta = _WINDOWS[self._window_index]
|
||||
self.title = f"⏳ {self._label} · {name}"
|
||||
self.title = f"{self._label} · {name}"
|
||||
|
||||
def handle(self, action: str, data: str = "") -> None:
|
||||
"""Scroll, cycle the window, or dismiss."""
|
||||
@@ -742,7 +742,7 @@ async def open_timemachine(ctx: "AppContext") -> None:
|
||||
"MeshTerm listens. Come back after the mesh has talked a while.",
|
||||
style="muted",
|
||||
),
|
||||
title="⏳ Time machine",
|
||||
title="Time machine",
|
||||
)
|
||||
return
|
||||
# Stored names first, the contact resolver filling the blanks (the app-wide
|
||||
@@ -770,7 +770,7 @@ async def open_timemachine(ctx: "AppContext") -> None:
|
||||
)
|
||||
)
|
||||
picked = await session.select(
|
||||
"⏳ Time machine — pick a subject",
|
||||
"Time machine — pick a subject",
|
||||
items,
|
||||
prompt="Everything the recorder ever heard, explorable:",
|
||||
)
|
||||
|
||||
+16
-14
@@ -148,7 +148,7 @@ class TracingDialog(Screen):
|
||||
"""Build the dialog.
|
||||
|
||||
Args:
|
||||
title: Heading for the dialog border (e.g. ``tracing · YUL-Poly``).
|
||||
title: Heading for the dialog border (e.g. ``Tracing — YUL-Poly``).
|
||||
spinner: The spinner to animate (shared with the owner's ticker).
|
||||
on_abort: Invoked when the user asks to abort (idempotent expected).
|
||||
"""
|
||||
@@ -289,7 +289,7 @@ class TraceScreen(Screen):
|
||||
the screen never claims a route the radio wasn't given.
|
||||
"""
|
||||
super().__init__()
|
||||
self.title = f"trace · {target}" if mode == "target" else "trace path"
|
||||
self.title = f"Trace — {target}" if mode == "target" else "Trace path"
|
||||
self._target = target
|
||||
self._mode = mode
|
||||
self._flight_label = target if mode == "target" else "path"
|
||||
@@ -364,7 +364,7 @@ class TraceScreen(Screen):
|
||||
"""
|
||||
total = max(1, int(self._sample_count()))
|
||||
dialog = TracingDialog(
|
||||
f"tracing · {self._flight_label}", spinner=self._spinner, on_abort=self.cancel
|
||||
f"Tracing — {self._flight_label}", spinner=self._spinner, on_abort=self.cancel
|
||||
)
|
||||
self._flight = dialog
|
||||
self._session.push(dialog)
|
||||
@@ -1047,7 +1047,7 @@ async def _open_session(ctx: "AppContext", target: Optional[str]) -> int:
|
||||
f"{target!r} isn't a known contact or a hex key prefix, so a forced "
|
||||
"path can't end at it. Trace it device-routed, or pick a contact.",
|
||||
),
|
||||
title="no destination hash",
|
||||
title="No destination hash",
|
||||
)
|
||||
|
||||
async def fetch_neighbours_via(repeater: Contact, repeater_id: str) -> bool:
|
||||
@@ -1083,7 +1083,7 @@ async def _open_session(ctx: "AppContext", target: Optional[str]) -> int:
|
||||
)
|
||||
spinner = Spinner()
|
||||
dialog = TracingDialog(
|
||||
f"neighbours · {repeater.name}", spinner=spinner, on_abort=lambda: None
|
||||
f"Fetch neighbours — {repeater.name}", spinner=spinner, on_abort=lambda: None
|
||||
)
|
||||
dialog.show_last = False # a fetch has no streaming replies to echo
|
||||
dialog.status = f"logging in to {repeater.name}…"
|
||||
@@ -1138,7 +1138,7 @@ async def _open_session(ctx: "AppContext", target: Optional[str]) -> int:
|
||||
run_id, "error", {"error": str(error) or type(error).__name__}
|
||||
)
|
||||
await session.message_dialog(
|
||||
Text(str(error), style="err"), title="fetch neighbours"
|
||||
Text(str(error), style="err"), title="Fetch neighbours"
|
||||
)
|
||||
return False
|
||||
ctx.repo.record_neighbours(run_id, repeater_id, entries)
|
||||
@@ -1154,7 +1154,7 @@ async def _open_session(ctx: "AppContext", target: Optional[str]) -> int:
|
||||
"it relearns neighbours from received adverts, so ask again later.",
|
||||
style="muted",
|
||||
),
|
||||
title="fetch neighbours",
|
||||
title="Fetch neighbours",
|
||||
)
|
||||
return False
|
||||
return True
|
||||
@@ -1251,7 +1251,7 @@ async def _open_session(ctx: "AppContext", target: Optional[str]) -> int:
|
||||
items.append(Choice(title=title, value=w))
|
||||
picked = await session.run_screen(
|
||||
SelectScreen(
|
||||
"path width",
|
||||
"Path width",
|
||||
items,
|
||||
prompt="Forced hops are addressed by this many leading key bytes.",
|
||||
default=width_bytes,
|
||||
@@ -1288,7 +1288,7 @@ async def _open_session(ctx: "AppContext", target: Optional[str]) -> int:
|
||||
items.append(Choice(title=title, value=n))
|
||||
picked = await session.run_screen(
|
||||
SelectScreen(
|
||||
"sample count",
|
||||
"Sample count",
|
||||
items,
|
||||
prompt=f"One Trace action runs this many traces, {pace:g} s apart.",
|
||||
default=sample_count,
|
||||
@@ -1367,7 +1367,9 @@ async def _open_session(ctx: "AppContext", target: Optional[str]) -> int:
|
||||
ctx.profile_name,
|
||||
)
|
||||
spinner = Spinner()
|
||||
dialog = TracingDialog(f"probing · {target_label}", spinner=spinner, on_abort=lambda: None)
|
||||
dialog = TracingDialog(
|
||||
f"Probing — {target_label}", spinner=spinner, on_abort=lambda: None
|
||||
)
|
||||
dialog.status = f"path 1/{len(candidates)} · one trace each"
|
||||
|
||||
def on_result(index: int, done: int, result: TraceResult) -> None:
|
||||
@@ -1431,7 +1433,7 @@ async def _open_session(ctx: "AppContext", target: Optional[str]) -> int:
|
||||
run_id, "error", {"error": str(error) or type(error).__name__}
|
||||
)
|
||||
await session.message_dialog(
|
||||
Text(f"probe failed: {error}", style="err"), title="path probe"
|
||||
Text(f"probe failed: {error}", style="err"), title="Path probe"
|
||||
)
|
||||
return None
|
||||
best = outcomes[0] if outcomes else None
|
||||
@@ -1460,7 +1462,7 @@ async def _open_session(ctx: "AppContext", target: Optional[str]) -> int:
|
||||
await session.message_dialog(
|
||||
Text("no observed evidence involving this target yet — run a trace or "
|
||||
"let monitoring accumulate paths first.", style="muted"),
|
||||
title="explore paths",
|
||||
title="Explore paths",
|
||||
)
|
||||
return None
|
||||
|
||||
@@ -1481,7 +1483,7 @@ async def _open_session(ctx: "AppContext", target: Optional[str]) -> int:
|
||||
items.extend(back_rows(("back", None)))
|
||||
picked = await session.run_screen(
|
||||
SelectScreen(
|
||||
f"reach {target_label} · scenarios",
|
||||
f"Explore paths — {target_label}",
|
||||
items,
|
||||
prompt="Choose the outbound leg — the return mirrors it.",
|
||||
footer_hint="↑↓ move · Enter adopt/probe · Esc back",
|
||||
@@ -1512,7 +1514,7 @@ async def _open_session(ctx: "AppContext", target: Optional[str]) -> int:
|
||||
result_items.append(Choice(title="Keep current path", value=None)) # the exit: no adoption
|
||||
adopted = await session.run_screen(
|
||||
SelectScreen(
|
||||
f"probe results · {target_label}",
|
||||
f"Probe results — {target_label}",
|
||||
result_items,
|
||||
footer_hint="↑↓ move · Enter adopt path · Esc keep current",
|
||||
wrap=False,
|
||||
|
||||
@@ -126,7 +126,7 @@ class TxSweepScreen(Screen):
|
||||
pick_samples: Floats the per-level sample-count dialog.
|
||||
"""
|
||||
super().__init__()
|
||||
self.title = f"tx optimize · {admin_label} → {target_label}"
|
||||
self.title = f"TX optimize — {admin_label} → {target_label}"
|
||||
self._admin_label = admin_label
|
||||
self._target_label = target_label
|
||||
self._device_label = device_label
|
||||
@@ -671,7 +671,7 @@ async def open_tx_optimize(
|
||||
]
|
||||
picked = await session.run_screen(
|
||||
SelectScreen(
|
||||
"sweep range",
|
||||
"Sweep range",
|
||||
items,
|
||||
prompt="The TX window the sweep explores:",
|
||||
default=(screen.tx_min, screen.tx_max),
|
||||
@@ -710,7 +710,7 @@ async def open_tx_optimize(
|
||||
]
|
||||
picked = await session.run_screen(
|
||||
SelectScreen(
|
||||
"coarse step",
|
||||
"Coarse step",
|
||||
items,
|
||||
prompt="The coarse sweep measures the window at this spacing, then refines.",
|
||||
default=screen.step,
|
||||
@@ -735,7 +735,7 @@ async def open_tx_optimize(
|
||||
]
|
||||
picked = await session.run_screen(
|
||||
SelectScreen(
|
||||
"samples per level",
|
||||
"Samples per level",
|
||||
items,
|
||||
prompt=f"Each TX level is measured with this many traces, {pace:g} s apart.",
|
||||
default=screen.samples,
|
||||
@@ -779,7 +779,7 @@ async def open_tx_optimize(
|
||||
"The saved password was cleared; sweep again to enter a new one.",
|
||||
style="err",
|
||||
),
|
||||
title="admin login",
|
||||
title="Admin login",
|
||||
)
|
||||
return False
|
||||
ctx.admin_store.remember(admin_node, password)
|
||||
@@ -831,7 +831,7 @@ async def open_tx_optimize(
|
||||
|
||||
spinner = Spinner()
|
||||
flight = TracingDialog(
|
||||
f"sweeping · {admin_node.name} → {target_label}",
|
||||
f"Sweeping — {admin_node.name} → {target_label}",
|
||||
spinner=spinner,
|
||||
on_abort=screen.cancel,
|
||||
)
|
||||
|
||||
@@ -88,7 +88,7 @@ async def open_watchtower(ctx: "AppContext") -> Optional[dict[str, Any]]:
|
||||
while True:
|
||||
items = _menu_items(store.alerts(), store.watched(), store.new_node_alerts)
|
||||
menu = SelectScreen(
|
||||
"🚨 Watchtower — alerts & watched nodes",
|
||||
"Watchtower — alerts & watched nodes",
|
||||
items,
|
||||
default=cursor,
|
||||
wrap=False,
|
||||
@@ -206,7 +206,7 @@ async def _pick_node(ctx: "AppContext", contacts: list[Contact]) -> None:
|
||||
if contacts
|
||||
else "No contacts available — connect a device that knows some nodes first."
|
||||
)
|
||||
await session.message_dialog(Text(message, style="muted"), title="watch a node")
|
||||
await session.message_dialog(Text(message, style="muted"), title="Watch a node")
|
||||
return
|
||||
|
||||
def recency(pair: tuple[str, Contact]) -> float:
|
||||
@@ -221,7 +221,7 @@ async def _pick_node(ctx: "AppContext", contacts: list[Contact]) -> None:
|
||||
row.append(f" heard {_format_age(_age_seconds(contact.last_seen))}", style="muted")
|
||||
items.append(Choice(row, (key, contact)))
|
||||
picked = await session.select(
|
||||
"⭐ Watch a node",
|
||||
"Watch a node",
|
||||
items,
|
||||
prompt="Silence and SNR rules will watch it from now on:",
|
||||
)
|
||||
|
||||
+1
-1
@@ -127,7 +127,7 @@ def test_atlas_renders_graph_and_overview_panel() -> None:
|
||||
assert "Homestead" in body and "YUL-Cartierville" in body and "Alice" in body
|
||||
assert "trace 1" in body and "packet 1" in body
|
||||
assert "rings = hops out" in body # the overview hint row
|
||||
assert "Mesh Atlas · 3 nodes · 2 links" == screen.title
|
||||
assert "Mesh atlas · 3 nodes · 2 links" == screen.title
|
||||
|
||||
|
||||
def test_atlas_empty_graph_renders_guidance() -> None:
|
||||
|
||||
@@ -218,7 +218,7 @@ def test_location_picker_draws_a_live_crosshair_without_keeping_it() -> None:
|
||||
body = _plain(screen.render_body(80))
|
||||
assert "⌖" in body and "45.5" in body # crosshair + its live coordinates
|
||||
assert screen._markers == markers # not accumulated across frames
|
||||
assert "set location" in screen._title(screen._viewport)
|
||||
assert "Set location" in screen._title(screen._viewport)
|
||||
|
||||
|
||||
def test_location_picker_without_nodes_or_initial_shows_the_world() -> None:
|
||||
|
||||
@@ -396,7 +396,7 @@ def test_composer_path_mode_opens_us_to_us_without_auto() -> None:
|
||||
the device to route to.
|
||||
"""
|
||||
screen = _composer(_topo(), target=False)
|
||||
assert screen.title == "compose path" # no target to name
|
||||
assert screen.title == "Compose path" # no target to name
|
||||
preview = screen._route_preview()
|
||||
assert preview.plain == "Us (aa) → Us (aa)" # endpoints carry our hash
|
||||
faint = sum(
|
||||
|
||||
Reference in new issue
Block a user