The Cardputer draws its path lines as chips

The Cardputer's panel is drawn in MeshTerm's own Terminus, which has the
powerline chevrons, yet its paths came out as arrows: the chips-or-arrows
verdict asked the terminal that started the app about its font, and on
the device that terminal is no one's, so the answer was unknown. In the
emulator it was worse, since the verdict was whatever the host terminal
could draw, a question about the wrong glass.

A handheld names its glyph inventory (Platform.font), and every frame is
folded down to that font anyway, so the inventory now answers: after the
MESHTERM_POWERLINE override, before any terminal probe. Terminus has
U+E0B0 and U+E0B1, so the Cardputer draws chips. The PicoCalc's 512-glyph
console font has neither, so its paths stay arrows, now said outright
rather than reached by not knowing. Diagnostics reads
"core (platform:cardputer-zero)". The verdict is bound on a platform
switch and its memo cleared there.

Checked by rasterizing gallery screens through the emulator's VT and
Terminus at 53x14; not yet on the device.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Jean-Pierre MartineauandClaude Opus 5.5 committed 2026-10-06 02:16:44 -04:00
1 parent b9585e9b59
commit c07f15f4ec
4 files changed
+122 -11

No files matched your search

+3 -1
View File
@@ -764,7 +764,9 @@ branch on the platform per frame, and never `from meshterm.platforms import PLAT
(`platforms.drawn_by_meshterm`), so nothing offers to move to another terminal. Its glyph contract is
`fontset.CARDPUTER_ZERO_CODEPOINTS`, named by `Platform.font` as the PicoCalc's is; the fold
derives everything from the platform's font (accents, emoji, the `?` net) and quantizes
colour only where `truecolor` is off.
colour only where `truecolor` is off. The path line's chips-or-arrows verdict reads the
same font (`termfont`, after `MESHTERM_POWERLINE` and before any terminal probe): Terminus
has the chevrons, so the Cardputer draws chips; the PicoCalc's 512 glyphs don't, so arrows.
- **The Cardputer's exit must fit its launcher's grace.** A held Esc there is the
launcher's own way out (SIGTERM at 3 s, SIGKILL 3 s later, letting go or not), which is
where hold-to-quit's timing comes from (see **Navigation**). MeshTerm's whole exit was
+61 -9
View File
@@ -33,11 +33,18 @@ nothing — which makes this an *out-of-band* detection problem: identify the te
read its configuration, and match the configured face against fonts known to carry the
powerline block.
Three sources of truth, in confidence order:
Four sources of truth, in confidence order:
* **An explicit override** — ``MESHTERM_POWERLINE`` (``1``/``full``, ``core``,
``0``/``off``) always wins, the same gate pattern as ``MESHTERM_FULL_WIDTH``. The
user knows their glass better than any probe.
* **A handheld's own font.** Where the platform names a glyph contract
(:attr:`~meshterm.platforms.Platform.font`), every frame is folded down to that font
at the render boundary, whatever terminal started the app — so the inventory is the
answer, and asking the terminal would be asking the wrong glass. The Cardputer's panel
is Terminus, which carries the core chevrons, so its paths are chips; the PicoCalc's
512-glyph console font has no room for them, so its paths stay arrows. Nothing is
probed: :data:`~meshterm.ui.fontset.FONTS` already says what each font can draw.
* **The configured font**, resolved per terminal: Windows Terminal (``WT_SESSION`` +
``WT_PROFILE_ID`` → the profile's ``font.face`` in ``settings.json``), VS Code's
integrated terminal (``TERM_PROGRAM=vscode`` → ``terminal.integrated.fontFamily``,
@@ -80,6 +87,8 @@ from functools import lru_cache
from pathlib import Path
from ..core import win32dll
from ..platforms import Platform, on_platform
from .fontset import FONTS
#: Coverage levels a verdict (or a recommended font) can carry, strongest first.
FULL = "full"
@@ -196,7 +205,8 @@ class PowerlineSupport:
Attributes:
level: :data:`FULL`, :data:`CORE`, :data:`NONE`, or :data:`UNKNOWN`.
source: What decided it — ``"env"`` (the override), ``"font:<terminal>"``
source: What decided it — ``"env"`` (the override), ``"platform:<name>"`` (a
handheld's own font inventory), ``"font:<terminal>"``
(a configured face matched, or definitively didn't), ``"renderer:<terminal>"``
(the terminal draws the glyphs itself), ``"ssh"`` (the font lives on the far
client, unknowable), or ``"unknown"``.
@@ -576,13 +586,37 @@ def _renderer_backed(environ: Mapping[str, str]) -> str | None:
return None
#: The glyphs the path widget draws at ``core``: the solid triangle every seam is, and the
#: thin one that joins two fills the eye can't tell apart (:mod:`~meshterm.ui.pathline`).
_CORE_GLYPHS = (0xE0B0, 0xE0B1)
#: What ``full`` adds that the widget uses: the rounded caps a path's square ends become.
_FULL_GLYPHS = (0xE0B4, 0xE0B6)
def _inventory_coverage(inventory: frozenset[int]) -> str:
"""The coverage level a known glyph inventory earns — no guess, it is all listed."""
if not all(code in inventory for code in _CORE_GLYPHS):
return NONE
return FULL if all(code in inventory for code in _FULL_GLYPHS) else CORE
def _powerline_support(
environ: Mapping[str, str] | None = None,
*,
cwd: Path | None = None,
conhost_probe: Callable[[], str | None] = _conhost_face,
handheld: tuple[str, frozenset[int]] | None = None,
) -> PowerlineSupport:
"""The uncached verdict (see :func:`powerline_support` for the ladder)."""
"""The uncached verdict (see :func:`powerline_support` for the ladder).
Args:
environ: The environment to inspect (defaults to ``os.environ``).
cwd: Where the VS Code workspace walk starts (defaults to the process cwd).
conhost_probe: The classic-console face reader (injectable for tests).
handheld: The platform's name and glyph inventory where a handheld's own font
draws the screen, or ``None`` on a terminal.
"""
env = os.environ if environ is None else environ
override = (env.get("MESHTERM_POWERLINE") or "").strip().lower()
if override in {"0", "off", "no", "none", "false"}:
@@ -591,6 +625,9 @@ def _powerline_support(
return PowerlineSupport(FULL, "env")
if override == "core":
return PowerlineSupport(CORE, "env")
if handheld is not None:
name, inventory = handheld
return PowerlineSupport(_inventory_coverage(inventory), f"platform:{name}")
detected = detect_terminal_font(env, cwd=cwd, conhost_probe=conhost_probe)
matched = match_recommended(detected.face) if detected else None
@@ -610,16 +647,17 @@ def _powerline_support(
def powerline_support() -> PowerlineSupport:
"""The session's powerline verdict, decided once and cached.
The ladder: the ``MESHTERM_POWERLINE`` override, then the configured font matched
against :data:`RECOMMENDED_FONTS`, then a renderer that draws the glyphs itself,
then an honest ``none`` (face read, no match, no fallback) or ``unknown`` (ssh, or
a terminal we can't identify). Cached because the environment and settings files
don't change mid-session; tests exercise :func:`_powerline_support` directly.
The ladder: the ``MESHTERM_POWERLINE`` override, then a handheld platform's own font
inventory, then the configured font matched against :data:`RECOMMENDED_FONTS`, then
a renderer that draws the glyphs itself, then an honest ``none`` (face read, no
match, no fallback) or ``unknown`` (ssh, or a terminal we can't identify). Cached
because the environment and settings files don't change mid-session, and cleared on
a platform switch (:func:`_bind`); tests exercise :func:`_powerline_support` directly.
Returns:
The :class:`PowerlineSupport` verdict.
"""
return _powerline_support(os.environ)
return _powerline_support(os.environ, handheld=_HANDHELD_FONT)
def powerline_enabled() -> bool:
@@ -640,6 +678,20 @@ def powerline_full() -> bool:
return powerline_support().level == FULL
#: The active handheld's name and glyph inventory, or ``None`` where a terminal draws
#: whatever it is sent. Bound at platform-switch time (:func:`_bind`).
_HANDHELD_FONT: tuple[str, frozenset[int]] | None = None
@on_platform
def _bind(platform: Platform) -> None:
"""Bind the handheld font the verdict reads, and drop the verdict the last one made."""
global _HANDHELD_FONT
inventory = FONTS.get(platform.font)
_HANDHELD_FONT = (platform.name, inventory) if inventory is not None else None
powerline_support.cache_clear()
# --- installed-font scan ----------------------------------------------------------
+19 -1
View File
@@ -14,6 +14,7 @@ import random
import pytest
import meshterm.ui.pathline as pathline
from meshterm.platforms import CARDPUTER_ZERO, set_platform
from meshterm.ui import oklab
from meshterm.ui.pathline import (
_DIM_BG,
@@ -44,7 +45,7 @@ from meshterm.ui.pathline import (
path_line,
with_action_mark,
)
from meshterm.ui.theme import node_style
from meshterm.ui.theme import fold_text, node_style
from meshterm.ui.widgets import path_text
@@ -158,6 +159,23 @@ def test_auto_mode_follows_the_terminal_verdict(monkeypatch: pytest.MonkeyPatch)
assert PathLine(hops).text().plain == "a → b"
def test_the_cardputer_draws_chips_in_glyphs_its_font_has(monkeypatch: pytest.MonkeyPatch) -> None:
"""The Cardputer's own font has the chevrons, so its paths are chips that fold clean.
Its verdict is read off its font (:mod:`~meshterm.ui.termfont`), and the render
boundary folds whatever that font lacks to ``?`` — so every mark the chip language
spends (both seams, both cracks, the elision, our star) must come through unchanged.
"""
monkeypatch.delenv("MESHTERM_POWERLINE")
set_platform(CARDPUTER_ZERO)
hops = [PathHop(SELF_GLYPH, you=True), PathHop("Hub", key="3d"), elision_hop()]
line = PathLine([*hops, PathHop("a"), PathHop("b")]).text() # auto: the font decides
drawn = [line, cut_to(line, 12), cut_mark(line, 1, ELIDE_HEAD)]
ink = "".join(text.plain for text in drawn)
assert {POWERLINE_SEP, POWERLINE_THIN, CRACK_TAIL, CRACK_HEAD, "⋯", SELF_GLYPH} <= set(ink)
assert fold_text(ink) == ink
def test_the_cursor_is_a_hop_of_its_own_in_either_mode() -> None:
"""The composer's insertion point is a slot *in* the route, not a gap between hops.
+39
View File
@@ -12,6 +12,10 @@ from __future__ import annotations
import json
from pathlib import Path
import pytest
from meshterm.platforms import CARDPUTER_ZERO, PICOCALC_LYRA, REGULAR, set_platform
from meshterm.ui.fontset import CARDPUTER_ZERO_CODEPOINTS, FONT_CODEPOINTS
from meshterm.ui.termfont import (
CORE,
FULL,
@@ -30,6 +34,7 @@ from meshterm.ui.termfont import (
installed_recommended,
match_recommended,
normalize_face,
powerline_support,
primary_family,
)
@@ -248,6 +253,40 @@ def test_powerline_support_kitty_ssh_and_unknown() -> None:
assert (lost.level, lost.source) == (UNKNOWN, "unknown")
def test_powerline_support_a_handheld_answers_from_its_own_font() -> None:
"""A handheld's font draws the screen, so its inventory answers and no terminal does.
The Cardputer's Terminus carries the core chevrons and no rounded caps; the PicoCalc's
512-glyph console font has neither, so a kitty that started it changes nothing. The
override still outranks the font, as it outranks every probe.
"""
kitty = {"TERM": "xterm-kitty"} # a terminal that would otherwise earn core
cardputer = _powerline_support(kitty, handheld=("cardputer-zero", CARDPUTER_ZERO_CODEPOINTS))
assert (cardputer.level, cardputer.source) == (CORE, "platform:cardputer-zero")
picocalc = _powerline_support(kitty, handheld=("picocalc-lyra", FONT_CODEPOINTS))
assert (picocalc.level, picocalc.source) == (NONE, "platform:picocalc-lyra")
capped = CARDPUTER_ZERO_CODEPOINTS | {0xE0B4, 0xE0B6}
assert _powerline_support({}, handheld=("capped", capped)).level == FULL
pinned = {"MESHTERM_POWERLINE": "0"}
assert _powerline_support(pinned, handheld=("cardputer-zero", CARDPUTER_ZERO_CODEPOINTS)) == (
_powerline_support(pinned)
)
def test_powerline_support_follows_a_platform_switch(monkeypatch: pytest.MonkeyPatch) -> None:
"""The memoized verdict is dropped on a switch, so it never answers for the last glass."""
monkeypatch.delenv("MESHTERM_POWERLINE", raising=False)
monkeypatch.setenv("TERM", "xterm-kitty")
monkeypatch.delenv("WT_SESSION", raising=False)
monkeypatch.delenv("TERM_PROGRAM", raising=False)
set_platform(CARDPUTER_ZERO)
assert powerline_support().source == "platform:cardputer-zero"
set_platform(PICOCALC_LYRA)
assert powerline_support().source == "platform:picocalc-lyra"
set_platform(REGULAR)
assert powerline_support().source == "renderer:kitty"
def test_installed_recommended_never_raises() -> None:
"""Scanning the machine for installed fonts never raises.