On a bordered frame the map's credit moves into the bottom rule, out of the map

The credit was stamped over the right end of the drawing's own last row on both
platforms. That is the traditional corner and it is where a frame with no bottom
border has to put it, but where there *is* a bottom border there is already a rule
under the body doing nothing except closing the box — and a caption set into it
costs the picture nothing at all. JP, looking at the desktop map: "that way it's
not in the map."

So a screen may now hand the frame a bottom_caption, which _panel_box sets into the
bottom rule right-justified through Rich's own subtitle machinery — landing as
"└──… © OpenStreetMap ─┘", one rule cell before the corner, the mirror of the way a
title sits in the top rule. The rule's other occupant keeps its place: the clip
arrows still lead, centred when they are alone and joined by the caption, right, when
both are wanted, because dropping either would be the frame deciding something
neither screen asked of it. The caption joins the base box memo's key as well — the
credit collapses on a keystroke that need not change a single body line, and a memo
blind to it would go on serving a rule the reader has already grown out of.

The map asks attribution for both halves and tests nothing itself: rule_caption gives
the caption where the platform's frame has a rule, map_body stamps the drawing where
it hasn't, and exactly one of them draws. Which it is, is bound once at platform-switch
time rather than asked per paint.

Nothing about the PicoCalc changes — same strings, same corner, same collapse, and the
minimap still stamps on both platforms, having no frame of its own to set anything
into. JP read that form on the device's own panel on 13 September and signed it off as
it stands; the docstring now says so, so nobody goes tidying it.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
Jean-Pierre MartineauandClaude Fable 5.1 committed 2026-09-13 13:47:28 -04:00
1 parent 8916544bfb
commit 8a1bb875a7
9 files changed
+348 -73

No files matched your search

+12 -10
View File
@@ -25,16 +25,18 @@ allowed to change behaviour, not just add to it.
build actually installed — at the root of the archive, and the same three files ride
beside the downloads on the GitHub release page.
- **The map credits OpenStreetMap on the map.** The full-screen map is arrived at with
`© OpenMapTiles · Data from OpenStreetMap` in its bottom-right corner — the corner a
paper map puts it in — and the first pan, zoom, reframe or find keystroke collapses it
to `© OpenStreetMap`, which is a complete credit in its own right, so nothing is ever
taken back. It costs no chrome: no title atom, no footer character, no body row, just
fifteen cells of the drawing's own bottom line that a keypress can pan out from under.
The node page's location preview carries the short form throughout, being a picture
rather than a place. The licence and its URL live where they can be read — the About
page, which also now names **OpenMapTiles**, as OpenFreeMap asks and neither the page
nor the README did.
- **The map credits OpenStreetMap in its own bottom-right corner.** The full-screen map is
arrived at with `© OpenMapTiles · Data from OpenStreetMap` there — the corner a paper map
puts it in — and the first pan, zoom, reframe or find keystroke collapses it to
`© OpenStreetMap`, which is a complete credit in its own right, so nothing is ever taken
back. It costs no chrome at all: no title atom, no footer character, no body row. On a
desktop terminal it is set into the panel's bottom border rule, right-justified, the way
a title sits in the top one, so the drawing keeps every cell it had; on the PicoCalc,
whose frame has no bottom rule to set anything into, it rides the right end of the map's
own last row, where a pan can move the ground out from under it. The node page's location
preview carries the short form throughout, being a picture rather than a place. The
licence and its URL live where they can be read — the About page, which also now names
**OpenMapTiles**, as OpenFreeMap asks and neither the page nor the README did.
- **Every Python file names its licence with an SPDX identifier** on its first line (second, after a shebang), and `tests/test_spdx.py` keeps a new file from landing without one. Apache-2.0 does not require per-file headers, but a file lifted out of the tree carried no marking at all.
+11 -4
View File
@@ -22,10 +22,17 @@ never `git push` unless explicitly asked in that moment.
shebang). `tests/test_spdx.py` is the gate. Anything under another licence lives beside
its own text and says so in its header — the GPL-2.0-only console-font script, the
MeshCore MIT notice beside the XIAO patch — and never enters the distributed package.
- **The map credits OpenStreetMap on the map** (`ui/attribution.py`): the full OpenFreeMap
line in the drawing's bottom-right corner on arrival, `© OpenStreetMap` there after the
first keystroke, `muted`, overprinting the cells it sits in. Never a title atom, never a
footer character, never a key. The licence and URL live on the About page.
- **The map credits OpenStreetMap in the map's own bottom-right corner**
(`ui/attribution.py`): the full OpenFreeMap line on arrival, `© OpenStreetMap` after the
first keystroke, `muted`, on both platforms and in the same words. *Which* corner is the
frame's, not the drawing's, wherever the frame has one — **regular** sets the credit into
the panel's **bottom border rule**, right-justified, one rule cell before the corner, the
way a title sits in the top rule (`Screen.bottom_caption` → `frame._panel_box`), so the
map's cells are untouched; **picocalc**, whose frame has no bottom rule at all, stamps it
over the right end of the drawing's last row instead, overprinting what it covers. The
minimap always stamps. The split is bound once via `platforms.on_platform`, never asked
per frame. Never a title atom, never a footer character, never a key. The licence and URL
live on the About page.
- The name and the wordmark are trademarks reserved in `NOTICE`, which Apache §4(d) makes
every fork carry; `NOTICE` holds only what must travel with a redistribution.
+4 -2
View File
@@ -487,9 +487,11 @@ under the [Open Database License (ODbL)](https://www.openstreetmap.org/copyright
are decoded with a small built-in reader for the
[Mapbox Vector Tile](https://github.com/mapbox/vector-tile-spec) format.
Both map surfaces carry the credit themselves: the full-screen map opens with
Both map surfaces carry the credit themselves. The full-screen map opens with
`© OpenMapTiles · Data from OpenStreetMap` in its bottom-right corner and collapses it to
`© OpenStreetMap` once you pan, zoom or type; the node page's location preview shows the
`© OpenStreetMap` once you pan, zoom or type — set into the panel's bottom border rule on
a desktop terminal, so the drawing itself keeps every cell, and on the map's own last row
on the PicoCalc, whose frame has no bottom rule. The node page's location preview shows the
short form throughout. The About page inside the app spells out the licence URL.
### Bundled font
+86 -11
View File
@@ -1,5 +1,5 @@
# SPDX-License-Identifier: Apache-2.0
"""The basemap's credit — OpenStreetMap's name, stamped in the corner of the drawing.
"""The basemap's credit — OpenStreetMap's name, in the corner of the map's own frame.
The street map is rendered from OpenStreetMap data, served as vector tiles by
OpenFreeMap on the unmodified OpenMapTiles schema. All three want naming, and the
@@ -30,11 +30,21 @@ nice if you do."* — so :data:`CREDIT_FULL` is that line less its optional half
That is the whole design, and it is built to cost the map almost nothing:
* **The credit rides the map, not the chrome.** It is stamped over the right end of the
drawing's own bottom row, which is the corner the guideline calls traditional. No
title atom, no footer character, no body line: the title is a status line already
chaining four atoms and the footer hint spends its whole 72-cell budget, and a credit
bolted to either would be paid for on every screen of every visit.
* **The credit rides the map's own frame, never a line of its own.** No title atom, no
footer character, no body row: the title is a status line already chaining four atoms
and the footer hint spends its whole 72-cell budget, and a credit bolted to either
would be paid for on every screen of every visit. *Where* it rides is the one thing
that differs by platform, and only because the platforms' frames differ. A bordered
frame has a **bottom border rule**, so the credit sits in it the way a title sits in
the top rule — right-justified, muted, one rule cell before the corner — and the
drawing itself is left alone (JP, 2026-09-13: *"that way it's not in the map"*). The
PicoCalc's frame has no bottom rule at all — a borderless title bar above, the F-key
lane below, and body rows in between — so there the credit is **stamped over the right
end of the drawing's own bottom row**, which is still the corner the guideline calls
traditional, and costs the map fifteen cells of ground a pan can move out from under.
That form was read on the device's own panel and signed off as it stands (JP,
2026-09-13), so its glyphs, its position and its collapse are settled: leave them alone.
:func:`rule_caption` and :func:`stamp` are the two forms; :func:`map_body` picks.
* **It arrives whole and shrinks once used.** A map the reader has not touched yet shows
:data:`CREDIT_FULL`, so the attribution is never something you have to interact to
see; the first pan, zoom, reframe or find keystroke collapses it to
@@ -51,11 +61,12 @@ That is the whole design, and it is built to cost the map almost nothing:
unadvertised key is an undiscoverable one, so a reveal would have had to buy a chip on
a five-slot F-key lane that is already full.
The mark **wins the cells it sits in**: it is spliced over the rendered row, so a street,
a braille dot or a node label under it is overprinted rather than shifted. The guideline
requires the attribution to be "legible and understandable", which a credit a passing
label could shred is not — and the map is pannable, so ground hidden under fifteen cells
of one row is a keypress away, while the credit is not allowed to be.
Where it *is* stamped on the drawing — the PicoCalc's map, and the location preview on
both platforms — the mark **wins the cells it sits in**: it is spliced over the rendered
row, so a street, a braille dot or a node label under it is overprinted rather than
shifted. The guideline requires the attribution to be "legible and understandable", which
a credit a passing label could shred is not — and the map is pannable, so ground hidden
under fifteen cells of one row is a keypress away, while the credit is not allowed to be.
Its style is ``muted``: this is chrome drawn on the map, not content on it. It must not
be mistaken for a node, so it takes neither the ``you`` white nor any key-derived node
@@ -67,6 +78,7 @@ from __future__ import annotations
from rich.cells import cell_len
from rich.text import Text
from ..platforms import Platform, on_platform
from .tui.render import crop_cells, render_to_ansi
#: The whole credit, shown until the reader first touches the map. OpenFreeMap's required
@@ -84,6 +96,18 @@ CREDIT_SHORT = "© OpenStreetMap"
#: never reads as the tail of a street name it happens to land beside.
_GAP = 1
#: Whether this platform's frame has a bottom border rule for the credit to sit in, rather
#: than the map having to give up cells of its own bottom row. Bound once per platform
#: switch (:func:`_bind`) so no paint ever asks the platform this question itself.
_HAS_RULE = True
@on_platform
def _bind(platform: Platform) -> None:
"""Settle where the credit goes when the platform is chosen, not when a frame is drawn."""
global _HAS_RULE
_HAS_RULE = platform.frame_border
def credit(*, full: bool) -> Text:
"""The credit as a styled run — :data:`CREDIT_FULL` or :data:`CREDIT_SHORT`.
@@ -147,3 +171,54 @@ def stamp(lines: list[str], width: int, *, full: bool, left: Text | None = None)
row.pad_right(max(0, keep - cell_len(row.plain)))
row.append_text(mark)
return [*lines[:-1], render_to_ansi(row, width, no_wrap=True)]
def rule_caption(*, full: bool) -> str:
"""The credit for the frame's bottom border rule — ``""`` where the frame draws none.
A bordered frame already has a rule under the body doing nothing but closing the box,
and a caption in it is the cheapest place the credit can possibly go: it takes no cell
of the drawing and no cell of any line the app was otherwise using. The frame renders
it right-justified through Rich's own subtitle machinery
(:func:`~meshterm.ui.tui.frame._panel_box`), so it lands as ``──── © OpenStreetMap ─╯``
— one rule cell before the corner, exactly as a title sits in the top rule.
Empty on the PicoCalc, whose frame has no bottom rule to sit in: there the credit is
stamped on the drawing instead (:func:`stamp`). Both callers ask unconditionally and
one of them gets nothing, so neither has to know which platform it is on.
Args:
full: Whether the credit is still in its whole form (see :func:`credit`).
Returns:
The caption, or ``""`` where this platform's frame has no rule for it.
"""
return (CREDIT_FULL if full else CREDIT_SHORT) if _HAS_RULE else ""
def map_body(lines: list[str], width: int, *, full: bool, left: Text | None = None) -> list[str]:
"""The full-screen map's rows, credited wherever this platform's frame cannot do it.
The counterpart to :func:`rule_caption`, and the reason the map's ``render_body`` needs
no platform test of its own: exactly one of the two marks the map, and this is the one
that draws nothing where the frame's bottom rule is carrying the credit instead.
``left`` (the find query echo) is drawn either way, because it belongs to the map
rather than to the credit — though in practice only the rule-less platform asks for it,
that being the same platform whose footer is the F-key lane and so has nowhere else to
put the query.
Args:
lines: The rendered map rows.
width: The row width in cells.
full: Whether the credit is still in its whole form (see :func:`credit`).
left: The find query echo for the bottom row, or ``None``.
Returns:
A new list of rows (the input's own list where there is nothing to draw).
"""
if not _HAS_RULE:
return stamp(lines, width, full=full, left=left)
if left is None or not lines or width <= 0:
return lines
return [*lines[:-1], render_to_ansi(crop_cells(left, 0, width), width, no_wrap=True)]
+18 -1
View File
@@ -364,7 +364,24 @@ class MapScreen(Screen):
self._ensure_prefetch(vp) # last: it reads what this paint just asked for
self.title = self._title(vp)
echo = query_text(self._filter) if self._query_echo() else None
return attribution.stamp(lines, width, full=self._untouched, left=echo)
return attribution.map_body(lines, width, full=self._untouched, left=echo)
@property
def bottom_caption(self) -> str: # type: ignore[override]
"""The basemap credit, for a frame whose bottom rule can carry it.
The other half of :func:`~meshterm.ui.attribution.map_body`, and the half that
costs the drawing nothing: where the panel has a bottom border, the credit is set
into it right-justified rather than stamped over the map's own last row (JP,
2026-09-13 — *"that way it's not in the map"*). Empty where the frame has no rule
to set it into, which is exactly where ``map_body`` stamps instead, so between
them the credit is drawn once on every platform and twice on none.
Read per paint, like the title, so the collapse to
:data:`~meshterm.ui.attribution.CREDIT_SHORT` reaches the rule on the very
keystroke that causes it.
"""
return attribution.rule_caption(full=self._untouched)
def _ensure_viewport(self, width: int) -> Viewport:
"""The viewport for a body ``width`` cells wide — built on first paint, else resized.
+30 -6
View File
@@ -143,29 +143,49 @@ def _breathing_room(body_len: int, budget: int) -> int:
def _panel_box(
title: str, visible: list[str], more_above: bool, more_below: bool, border: str
title: str,
visible: list[str],
more_above: bool,
more_below: bool,
border: str,
caption: str = "",
) -> Panel:
"""Wrap already-sliced body lines in a titled, scroll-aware panel.
The bottom rule carries up to two things, and they share one subtitle rather than
fighting over the line. The clip arrows sit centred as they always have. A
``caption`` — a screen's :attr:`~meshterm.ui.tui.screen.Screen.bottom_caption`, which
today means the map's OpenStreetMap credit — is **right-justified**, so it lands as
``──── © OpenStreetMap ─╯``: one rule cell before the corner, the mirror of the way
the title sits in the top rule, and not one cell of the body given up for it. Where
both are wanted the caption joins the arrows in the same run and the run moves right,
because the arrows are the half a reader is looking *for* and the caption is the half
that must simply be present — dropping either would be the frame deciding something
neither screen asked it to.
Args:
title: The screen's heading (empty for none).
visible: The viewport's ANSI lines, already sliced and padded to height.
more_above: Whether content continues above the slice.
more_below: Whether content continues below the slice.
border: Border style name (``"accent"`` for the focused screen).
caption: A short muted credit for the bottom rule's right end (empty for none).
Returns:
A Rich :class:`Panel` of exactly ``len(visible) + 2`` rows.
"""
body = Text.from_ansi("\n".join(visible))
subtitle = None
atoms = []
if more_above or more_below:
arrow = ("↑" if more_above else " ") + ("↓" if more_below else " ")
subtitle = f"[{hint_style(border)}]{arrow} more[/]"
atoms.append(("↑" if more_above else " ") + ("↓" if more_below else " ") + " more")
if caption:
atoms.append(caption)
subtitle = f"[{hint_style(border)}]{' · '.join(atoms)}[/]" if atoms else None
return Panel(
body,
title=f"[{title_style(border)}]{title}[/]" if title else None,
subtitle=subtitle,
subtitle_align="right" if caption else "center",
border_style=border,
padding=(0, 1),
)
@@ -314,11 +334,15 @@ def compose_base(
# flip with the physical Shift key alone) has no place in the key, so it renders
# uncached — that combination doesn't arise: the lane belongs to the borderless
# platform below.
key = (cols, rows, base.title, footer_hint, more_above, more_below, *visible)
# The caption belongs in the key like the title does: the map's credit collapses on
# a keystroke that need not change a single body line, and a memo without it would
# keep serving the rule the reader has already grown out of.
caption = base.bottom_caption
key = (cols, rows, base.title, caption, footer_hint, more_above, more_below, *visible)
if footer_lane is None and _BASE_BOX_CACHE is not None and _BASE_BOX_CACHE[0] == key:
body = _BASE_BOX_CACHE[1]
else:
panel = _panel_box(base.title, visible, more_above, more_below, "accent")
panel = _panel_box(base.title, visible, more_above, more_below, "accent", caption)
body = render_lines(Group(panel, footer_row()), cols)
if footer_lane is None:
_BASE_BOX_CACHE = (key, body)
+10
View File
@@ -92,10 +92,20 @@ class Screen:
base screen. Ignored while ``chrome`` is ``True``.
footnote: A short line drawn muted and centered *below* a chromeless base screen's
box (e.g. a copyright notice). Ignored while ``chrome`` is ``True``.
bottom_caption: A short credit or label set into the **bottom border rule**,
right-justified, the way :attr:`title` sits in the top one — for something
that belongs to the framed picture rather than to any row inside it. The map's
OpenStreetMap credit is the reason it exists (see
:mod:`meshterm.ui.attribution`): a bordered frame already draws that rule, so
a caption in it costs the body nothing at all. Read fresh every paint, so a
screen may change it as its state changes. Drawn only where the platform's
frame *has* a bottom rule — the borderless one has none, and a screen wanting
a mark there puts it in its own body instead.
"""
title: str = ""
footer_hint: str = "Esc back"
bottom_caption: str = ""
floating: bool = True
modal: bool = False
grow_only: bool = False
+52 -3
View File
@@ -68,6 +68,7 @@ from meshterm.ui.about import (
join_discord,
support_project,
)
from meshterm.ui.attribution import CREDIT_FULL, CREDIT_SHORT
from meshterm.ui.chat import ChatScreen
from meshterm.ui.config_editor import _ConfigMenu, _menu_items, config_table, has_pin
from meshterm.ui.contactlist import SORT_COLUMNS, SORT_OPENS_ASCENDING
@@ -340,9 +341,10 @@ def _map_body_rows(rows: int) -> int:
The map is the one screen that sizes its own canvas from
:meth:`~meshterm.ui.tui.session.TuiSession.base_body_size`, so the stub session has to
answer with the *body* height rather than the terminal's — otherwise the specimen draws
a canvas taller than the slice it is shown in, which cuts the bottom row (and with it
the basemap credit that rides there) and hangs a phantom "↓ more" off a map that never
scrolls. Mirrors ``base_body_size`` with no header — these specimens compose against an
a canvas taller than the slice it is shown in, which cuts the bottom row (and on the
borderless platform the basemap credit that rides there) and hangs a phantom "↓ more"
off a map that never scrolls. Mirrors ``base_body_size`` with no header — these
specimens compose against an
empty one — so it is the footer row plus either the panel's two borders or the
borderless platform's single title bar.
"""
@@ -1036,6 +1038,10 @@ _KNOWN_WIDE: set[str] = set()
_PREEXISTING_REGULAR_OVERFLOW: set[str] = set()
#: The specimens by name, for a test that wants one entry rather than the whole sweep.
_ENTRY_BY_NAME = {entry.name: entry for entry in _ENTRIES}
def _cases():
for entry in _ENTRIES:
for platform, cols, rows in _COMBOS:
@@ -1136,3 +1142,46 @@ def test_gallery_screen_fits_its_platform(
f"{entry.name} {where} line {i} has characters outside the console "
f"font: {sorted(strays)!r} in {line!r}"
)
#: The map specimens and the credit each should be carrying — the whole OpenFreeMap line
#: on the map nobody has touched, the remnant on the two that have been used.
_CREDITED: list[tuple[str, bool]] = [
("map", True),
("map_panned", False),
("map_find", False),
]
@pytest.mark.parametrize("name,full", _CREDITED)
@pytest.mark.parametrize("platform,cols,rows", _COMBOS)
def test_gallery_map_shows_the_basemap_credit_where_its_frame_puts_it(
name: str,
full: bool,
platform: Platform,
cols: int,
rows: int,
) -> None:
"""Every map specimen credits OpenStreetMap, on the surface its own frame affords.
The width gate above would be perfectly happy with a map that had quietly stopped
crediting anyone, so the specimens assert the mark itself as well: on a bordered frame
it is set into the bottom border rule, right-justified with one rule cell before the
corner, and the drawing is left alone; on the borderless one, which has no rule, it is
stamped on the drawing's own last row. See :mod:`meshterm.ui.attribution`.
"""
set_platform(platform)
screen = _ENTRY_BY_NAME[name].factory(cols, rows)
screen.note_viewport(max(1, rows - 4))
expected = CREDIT_FULL if full else CREDIT_SHORT
body = _plain(screen.render_body(cols))
composed = frame.compose_base(Text(""), screen, screen.footer_hint, cols, rows)
lines = [_plain(ln) for ln in composed.split("\n")]
if platform.frame_border:
assert "OpenStreetMap" not in body, "the credit reached the drawing on a bordered frame"
rule = next(ln for ln in reversed(lines) if "└" in ln)
assert rule.endswith(f" {expected} ─┘"), rule
else:
assert body.splitlines()[-1].endswith(expected)
assert any(expected in ln for ln in lines)
+125 -36
View File
@@ -2568,6 +2568,12 @@ def test_the_map_registers_no_cli_command() -> None:
# meshterm/ui/attribution.py): the credit sits in a corner of the map, names
# "OpenStreetMap" rather than an abbreviation, is visible without interacting, and may
# collapse once the map is used — to a form that is still a complete OSM attribution.
#
# Which corner is the *frame's*, and the two platforms' frames differ: a bordered one sets
# the credit into its bottom border rule and leaves the drawing alone, while the
# borderless one has no rule and stamps the drawing's own last row instead. So most of
# these run twice, once per platform, and assert on the surface that platform actually
# marks.
def _credited_map(cols: int = 80) -> MapScreen:
@@ -2583,6 +2589,36 @@ def _last_row(screen: MapScreen, cols: int = 80) -> str:
return _plain(screen.render_body(cols)).splitlines()[-1]
def _bottom_rule(screen: MapScreen, cols: int = 80, rows: int = 24) -> str:
"""The composed frame's bottom border rule — the line the caption is set into."""
from rich.text import Text
from meshterm.ui.tui import frame
screen.note_viewport(max(1, rows - 4))
composed = frame.compose_base(Text(""), screen, screen.footer_hint, cols, rows)
return next(
ln for ln in reversed([Text.from_ansi(x).plain for x in composed.split("\n")]) if "└" in ln
)
def _mark(screen: MapScreen, cols: int = 80) -> str:
"""Wherever this platform puts the credit: the bottom rule, else the drawing's last row."""
return _bottom_rule(screen, cols) if screen.bottom_caption else _last_row(screen, cols)
def _both_platforms():
"""The two platforms, restoring REGULAR afterwards — the credit differs across them."""
from meshterm.platforms import PICOCALC, REGULAR, set_platform
try:
for platform in (REGULAR, PICOCALC):
set_platform(platform)
yield platform
finally:
set_platform(REGULAR)
def test_credit_names_openstreetmap_in_full_never_an_abbreviation() -> None:
"""OSMF: "Attribution must be to 'OpenStreetMap'" — so neither form may abbreviate it."""
from meshterm.ui import attribution
@@ -2598,28 +2634,62 @@ def test_credit_names_openstreetmap_in_full_never_an_abbreviation() -> None:
assert "Data from OpenStreetMap" in attribution.CREDIT_FULL
def test_an_untouched_map_shows_the_whole_credit_in_its_bottom_right_corner() -> None:
def test_an_untouched_map_shows_the_whole_credit_on_every_platform() -> None:
"""The attribution may not be something you have to interact with the map to see."""
from meshterm.ui import attribution
row = _last_row(_credited_map())
assert row.endswith(attribution.CREDIT_FULL)
assert cell_len(row) <= 80
for platform in _both_platforms():
mark = _mark(_credited_map(platform.readable_cols), platform.readable_cols)
assert attribution.CREDIT_FULL in mark, platform.name
def test_a_bordered_frame_sets_the_credit_in_its_bottom_rule_and_spares_the_drawing() -> None:
"""JP, 2026-09-13: "that way it's not in the map" — the rule carries it, the map doesn't."""
from meshterm.platforms import REGULAR, set_platform
from meshterm.ui import attribution
set_platform(REGULAR)
screen = _credited_map(72)
# Right-justified with exactly one rule cell before the corner, as a title sits in the
# top rule: "└──… © OpenMapTiles · Data from OpenStreetMap ─┘".
rule = _bottom_rule(screen, 72)
assert rule.startswith("└") and rule.endswith(f" {attribution.CREDIT_FULL} ─┘")
assert cell_len(rule) == 72
# And nothing of it reached the drawing: the map's own rows are pure picture.
assert "OpenStreetMap" not in _plain(screen.render_body(72))
def test_a_frame_with_no_bottom_rule_stamps_the_drawing_instead() -> None:
"""The PicoCalc has a title bar and an F-key lane, and no rule to set a caption into."""
from meshterm.platforms import PICOCALC, REGULAR, set_platform
from meshterm.ui import attribution
try:
set_platform(PICOCALC)
screen = _credited_map(53)
assert screen.bottom_caption == "" # nowhere to put it but the map
assert _last_row(screen, 53).endswith(attribution.CREDIT_FULL)
finally:
set_platform(REGULAR)
def test_the_credit_collapses_once_the_map_is_used_and_stays_collapsed() -> None:
"""OSMF allows a collapse "automatically on map interaction such as panning … zooming"."""
from meshterm.ui import attribution
for action in ("right", "pageup", "home", "text"):
screen = _credited_map()
assert _last_row(screen).endswith(attribution.CREDIT_FULL)
screen.handle(action, "a" if action == "text" else "")
row = _last_row(screen)
assert row.endswith(attribution.CREDIT_SHORT), action
assert attribution.CREDIT_FULL not in row, action
screen.render_body(80) # a repaint never brings the whole line back
assert _last_row(screen).endswith(attribution.CREDIT_SHORT), action
for platform in _both_platforms():
cols = platform.readable_cols
for action in ("right", "pageup", "home", "text"):
screen = _credited_map(cols)
screen.render_body(cols) # the arrival paint the reader is answering
assert attribution.CREDIT_FULL in _mark(screen, cols), (platform.name, action)
screen.handle(action, "a" if action == "text" else "")
mark = _mark(screen, cols)
assert attribution.CREDIT_SHORT in mark, (platform.name, action)
assert attribution.CREDIT_FULL not in mark, (platform.name, action)
# A repaint never brings the whole line back — including through the frame's
# own composition memo, which has to key on the caption to notice.
assert attribution.CREDIT_FULL not in _mark(screen, cols), (platform.name, action)
def test_a_reopened_map_arrives_with_the_whole_credit_again() -> None:
@@ -2629,17 +2699,30 @@ def test_a_reopened_map_arrives_with_the_whole_credit_again() -> None:
used = _credited_map()
used.render_body(80) # keys do nothing until the map has a viewport to move
used.handle("right")
assert _last_row(used).endswith(attribution.CREDIT_SHORT)
assert _last_row(_credited_map()).endswith(attribution.CREDIT_FULL)
assert attribution.CREDIT_SHORT in _mark(used)
assert attribution.CREDIT_FULL in _mark(_credited_map())
def test_the_credit_costs_no_chrome_row_and_no_footer_character() -> None:
"""It rides the drawing, so the body keeps its height and the hint keeps its wording."""
screen = _credited_map()
assert len(screen.render_body(80)) == len(_credited_map().render_body(80))
assert "OpenStreetMap" not in screen.footer_hint
screen.render_body(80)
assert "OpenStreetMap" not in screen.title
"""It rides the frame, so the body keeps its height and the hint keeps its wording."""
for platform in _both_platforms():
cols = platform.readable_cols
screen = _credited_map(cols)
assert len(screen.render_body(cols)) == len(_credited_map(cols).render_body(cols))
assert "OpenStreetMap" not in screen.footer_hint
screen.render_body(cols)
assert "OpenStreetMap" not in screen.title
def test_the_bottom_rule_keeps_its_clip_arrows_when_a_caption_shares_it() -> None:
"""Neither half is dropped: the arrows lead, the caption follows, the run moves right."""
from meshterm.ui.tui.frame import _panel_box
panel = _panel_box("Map", ["body"], True, True, "accent", "© OpenStreetMap")
assert "↑↓ more · © OpenStreetMap" in (panel.subtitle or "")
assert panel.subtitle_align == "right"
# With no caption the arrows keep the centre they have always had.
assert _panel_box("Map", ["body"], False, True, "accent", "").subtitle_align == "center"
def test_the_find_query_and_the_credit_share_the_row_with_the_credit_kept_whole() -> None:
@@ -2663,25 +2746,31 @@ def test_the_find_query_and_the_credit_share_the_row_with_the_credit_kept_whole(
def test_the_location_preview_credits_the_basemap_in_its_short_form() -> None:
"""A produced work too — and a non-interactive one, so its mark must stand still."""
"""A produced work too — and a non-interactive one, so its mark must stand still.
The preview is embedded in somebody else's body, with no frame of its own to set a
caption into, so it stamps on both platforms — unlike the full-screen map.
"""
from meshterm.ui import attribution
from meshterm.ui.map_render import MapMarker
from meshterm.ui.minimap import MiniMap
preview = MiniMap(
_StubSession(72, 24),
_StubSource(),
14,
center_lat=45.5,
center_lon=-73.6,
zoom=12,
markers=[MapMarker("A", 45.5, -73.6)],
)
rows = preview.render(72, 6)
assert len(rows) == 6 # the credit takes no row of its own
last = _plain(rows).splitlines()[-1]
assert last.endswith(attribution.CREDIT_SHORT)
assert attribution.CREDIT_FULL not in last
for platform in _both_platforms():
cols = platform.readable_cols
preview = MiniMap(
_StubSession(cols, 24),
_StubSource(),
14,
center_lat=45.5,
center_lon=-73.6,
zoom=12,
markers=[MapMarker("A", 45.5, -73.6)],
)
rows = preview.render(cols, 6)
assert len(rows) == 6, platform.name # the credit takes no row of its own
last = _plain(rows).splitlines()[-1]
assert last.endswith(attribution.CREDIT_SHORT), platform.name
assert attribution.CREDIT_FULL not in last, platform.name
def test_the_credit_glyphs_are_in_the_picocalc_console_font() -> None: