diff --git a/meshterm/ui/livefeed_screen.py b/meshterm/ui/livefeed_screen.py index 48eaefb..7612054 100644 --- a/meshterm/ui/livefeed_screen.py +++ b/meshterm/ui/livefeed_screen.py @@ -12,7 +12,7 @@ graph. The feed's job is to say what arrived and how well it was heard; Enter sa it got here. Where the width allows, a **scope** lane follows the readings: the region a flood was sent -into (``harbour``), ``scoped · 3fa1`` for a region nobody here has named, ``unscoped`` for a +into (``harbour``), ``unknown scope 3fa1`` for a region nobody here has named, ``unscoped`` for a plain flood, and nothing for a direct frame, which no repeater region-filters (see :func:`_lanes_for` for which widths hold it, and why the PicoCalc's never does). @@ -161,7 +161,7 @@ _FEED_LABEL_MIN_WIDTH = ( ) #: The scope lane's width: the longest thing :func:`~meshterm.ui.widgets.scope_text` draws -#: for an unnamed scope (``scoped · 3fa1``), so the one reading that carries a code keeps +#: for an unnamed scope (``unknown scope 3fa1``), so the one reading that carries a code keeps #: it whole. A named region longer than this ellipsizes — its whole name is on the #: viewer's ``route`` row, one keypress away, like everything else a lane cuts. _FEED_SCOPE_WIDTH = 13 @@ -892,7 +892,7 @@ class LiveFeedScreen(Screen): read from, and only a flood has one; the lane is left blank for everything else — a direct frame is never region-filtered, so a word there would claim a meaning it lacks. The lane is drawn ``bare``: its heading already says ``SCOPE``, so a known - region reads as its name alone, while ``unscoped`` and ``scoped · 3fa1`` stay the + region reads as its name alone, while ``unscoped`` and ``unknown scope 3fa1`` stay the words they are everywhere (:func:`~meshterm.ui.widgets.scope_text`). """ if entry.kind != "packet" or not isinstance(entry.raw, dict): diff --git a/meshterm/ui/message_paths_screen.py b/meshterm/ui/message_paths_screen.py index 10ba218..725385b 100644 --- a/meshterm/ui/message_paths_screen.py +++ b/meshterm/ui/message_paths_screen.py @@ -192,7 +192,7 @@ class MessagePathsScreen(Screen): status atoms chain (``Message paths · scope harbour``) and it costs the body no line: on the PicoCalc's 26 rows a line spent here is a lane of the fan or a row of the list. The words are :func:`~meshterm.ui.widgets.scope_text`'s, plain — a title - carries no styling — so ``unscoped`` and ``scoped · 3fa1`` read here exactly as they + carries no styling — so ``unscoped`` and ``unknown scope 3fa1`` read here exactly as they do on the packet viewer's ``route`` row. """ atom = scope_text(scope).plain diff --git a/meshterm/ui/packet_viewer.py b/meshterm/ui/packet_viewer.py index ddb4dda..60c0be5 100644 --- a/meshterm/ui/packet_viewer.py +++ b/meshterm/ui/packet_viewer.py @@ -85,7 +85,7 @@ def unnamed_scope(raw: dict | None) -> Scope | None: """A frame's scope read without any region names — the fallback when none are at hand. Still says everything the frame itself says: a plain flood is ``unscoped``, and a - scoped one is ``scoped · ``. Only the *naming* needs the known regions (see + scoped one is ``unknown scope ``. Only the *naming* needs the known regions (see :meth:`~meshterm.core.region_store.RegionStore.scope_of`), so a viewer opened without a store never draws less than the truth, only less than the whole of it. """ @@ -999,7 +999,7 @@ class PacketViewer(Screen): A flood reads ``flood`` and then its scope as a chained atom (:func:`~meshterm.ui.widgets.scope_text`) — ``flood · scope harbour``, - ``flood · scoped · 3fa1``, ``flood · unscoped`` — rather than the library's + ``flood · unknown scope 3fa1``, ``flood · unscoped`` — rather than the library's ``tc flood``, which named the wire mechanism (transport codes) and left the reader to know that it meant *scoped*, and to which region nobody could say. A scoped and an unscoped flood are one routing; the scope is what differs, so it diff --git a/meshterm/ui/widgets.py b/meshterm/ui/widgets.py index ce8d2cf..d5ec8c8 100644 --- a/meshterm/ui/widgets.py +++ b/meshterm/ui/widgets.py @@ -102,7 +102,8 @@ def scope_text(scope: Scope | None, *, bare: bool = False) -> Text: * ``scope yul`` — a scoped flood whose region is known, the name in the ``scope`` style (a region is not a node, so it never takes a node hue); - * ``scoped · 3fa1`` — a scoped flood no known name reproduces: the code is shown so + * ``unknown scope 3fa1`` (``? 3fa1`` bare) — a scoped flood no known name + reproduces: the code is shown so two frames can still be told to share a region, ``muted`` because it names nothing; * ``unscoped`` — a plain flood, ``muted``: the ordinary case, stated not stressed. @@ -125,9 +126,11 @@ def scope_text(scope: Scope | None, *, bare: bool = False) -> Text: text.append("scope ", style="muted") text.append(scope.region, style="scope") elif scope.scoped: - text.append("scoped", style="muted") + # No `·` inside the reading: a title chains its status atoms with `·`, and + # ``scoped · 3fa1`` there read as two atoms where it is one fact. + text.append("?" if bare else "unknown scope", style="muted") if scope.code: - text.append(f" · {scope.code}", style="muted") + text.append(f" {scope.code}", style="muted") else: text.append("unscoped", style="muted") return text diff --git a/tests/test_scope_display.py b/tests/test_scope_display.py index ff687b8..91d5605 100644 --- a/tests/test_scope_display.py +++ b/tests/test_scope_display.py @@ -105,14 +105,14 @@ def test_the_viewer_names_the_region_a_flood_was_scoped_to() -> None: def test_the_viewer_shows_an_unnamed_scopes_code_and_a_plain_flood_as_unscoped() -> None: """A scope no known name reproduces keeps its code; a plain flood reads unscoped.""" assert _route_row(_scoped("elsewhere"), _knows("harbour")).endswith( - f"flood · scoped · {_code('elsewhere')}" + f"flood · unknown scope {_code('elsewhere')}" ) assert _route_row(_FLOOD, _knows("harbour")).endswith("flood · unscoped") def test_the_viewer_without_a_store_still_tells_scoped_from_unscoped() -> None: """No names at hand is less of the truth, never a wrong one: the code stands in.""" - assert _route_row(_scoped("harbour")).endswith(f"flood · scoped · {_code('harbour')}") + assert _route_row(_scoped("harbour")).endswith(f"flood · unknown scope {_code('harbour')}") assert _route_row(_FLOOD).endswith("flood · unscoped") @@ -182,7 +182,7 @@ def test_the_feed_carries_a_scope_lane_on_a_72_column_terminal() -> None: header, *rows = _stripped(screen.render_body(68)) assert header.split()[-1] == "SCOPE" assert rows[0].rstrip().endswith("harbour") - assert rows[1].rstrip().endswith(f"scoped · {_code('elsewhere')}") + assert rows[1].rstrip().endswith(f"? {_code('elsewhere')}") assert rows[2].rstrip().endswith("unscoped") assert rows[3].rstrip().endswith("dBm") # the direct frame states no scope for line in (header, *rows): @@ -293,7 +293,9 @@ def test_the_paths_dialog_states_the_scope_once_in_its_title() -> None: assert _paths_screen(Scope("scoped", "harbour", "3fa1")).title == ( "Message paths · scope harbour" ) - assert _paths_screen(Scope("unknown", None, "3fa1")).title == "Message paths · scoped · 3fa1" + assert ( + _paths_screen(Scope("unknown", None, "3fa1")).title == "Message paths · unknown scope 3fa1" + ) assert _paths_screen(UNSCOPED).title == "Message paths · unscoped" assert _paths_screen(None).title == "Message paths" # …and never per arrival row: the body does not repeat it.