`ruff format .` across the repo: 153 files, no behaviour change. This is the second gate CONTRIBUTING.md promises, and until now it reported 154 files needing reformat — so a first contributor ran the command the guide told them to run and could not tell their own diff from the background. Landed on its own, with nothing else in it, because a whole-repo reformat is only safe to skip past in blame if it is provably nothing but layout. It was checked that way rather than assumed: every changed file was formatted in memory and compared by full `ast.dump()`, and 152 of 153 come out structurally identical (the one exception was a docstring opening on a quote character, fixed in the commit before this one). The string and comment token streams were diffed separately, so a quote flip or an edited comment could not hide inside a re-layout — five quote-style normalisations and sixteen implicit-concatenation joins, all value-identical, and one commented-out line gaining the space after its `#`. `meshterm specimen` is byte-identical on both platforms, and the suite is 1888 green, gallery gate and `test_theme16` included. What actually changed, in order of how much of the diff it is: wrapped calls and signatures rejoin onto one line where they fit inside the hundred columns; the magic trailing comma expands what it is there to expand; complex slices gain their spaces (`[len(noise) :]`); a few redundant parens go. The tables that are laid out to be read were fenced in the previous commit and are untouched here. The aligned trailing-comment column is gone from about ninety lanes that were not fenced — including the `heat.*` ladder in `MESH_THEME`, which cannot be fenced where it sits. Every comment stays on the entry it describes, so nothing there reads wrong; it just no longer lines up. Add this commit to `.git-blame-ignore-revs`, which follows immediately. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
175 lines
7.0 KiB
Python
175 lines
7.0 KiB
Python
"""The visual-language specimen: every mark, icon, colour and fold on one screen.
|
|
|
|
``meshterm specimen`` prints this through the platform's real machinery — the active
|
|
theme, the ``glyph()`` icon funnel, the render-boundary fold, the heat and SNR scales —
|
|
so what it shows *is* what the platform draws, not a mock-up of it. On the PicoCalc
|
|
console it doubles as the font/palette acceptance card (all nine P3 marks, the node
|
|
glyphs, braille, the 16 programmed slots); on a desktop terminal it shows the same
|
|
content in the regular platform's emoji-and-truecolor dress. Sized to the PicoCalc
|
|
floor: every line ≤ 53 cells, the whole card ≤ 24 rows.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from rich.console import RenderableType
|
|
from rich.text import Text
|
|
|
|
from ..platforms import get_platform
|
|
from .packet_viewer import KIND_ICONS, PAYLOAD_ICONS
|
|
from .theme import _VT_SLOTS, fold_text, glyph, name_style, snr_style
|
|
from .tui.fkeys import FPair, lane_text
|
|
from .widgets import _recency_style, channel_glyph, name_chip
|
|
|
|
#: Demo identities for the name-colour row: ``(name, key)``. The keys are synthetic, and
|
|
#: chosen so their first bytes land in three different sectors of the hue wheel — on the
|
|
#: console that means three different palette slots (see ``theme._NODE_SLOT_HEXES``).
|
|
_DEMO_NAMES = (("Alice", "a1b2"), ("Lakeside", "3d63"), ("Lounge", "cc10"))
|
|
|
|
|
|
def _palette_row() -> Text:
|
|
"""The 16 slots as filled blocks, dim bank then bright bank.
|
|
|
|
On PicoCalc the blocks address the console's real slots (``color(N)``), so the row
|
|
shows exactly what ``setvtrgb`` programmed; on the regular platform they carry the
|
|
design's RGB values instead — a preview of the same palette in truecolor.
|
|
"""
|
|
row = Text()
|
|
for slot, _, hex_ in _VT_SLOTS:
|
|
style = f"color({slot})" if not get_platform().truecolor else hex_
|
|
row.append("██", style=style)
|
|
return row
|
|
|
|
|
|
def _ages_row() -> Text:
|
|
"""The heat scale over its own anchors — one age just inside each of the seven steps."""
|
|
row = Text()
|
|
anchors = (
|
|
("now", 0),
|
|
("20m", 1200),
|
|
("3h", 10800),
|
|
("2d", 172800),
|
|
("2w", 1209600),
|
|
("3mo", 7776000),
|
|
("never", None),
|
|
)
|
|
for label, secs in anchors:
|
|
if row.plain:
|
|
row.append(" ")
|
|
row.append(label, style=_recency_style(secs))
|
|
return row
|
|
|
|
|
|
def specimen_lines() -> list[RenderableType]:
|
|
"""The specimen card, one renderable per line (print through the themed console)."""
|
|
platform = get_platform()
|
|
lines: list[RenderableType] = []
|
|
lines.append(Text.assemble(("Specimen", "accent"), (" · ", "muted"), platform.name))
|
|
lines.append(Text())
|
|
lines.append(_palette_row())
|
|
legend = Text("stock palette · 8 muted · 9-14 node hues · 15 you", style="muted")
|
|
lines.append(legend)
|
|
lines.append(Text())
|
|
|
|
status = Text("status ")
|
|
for mark, style in (("✓", "ok"), ("✗", "err"), ("⚠", "warn"), ("●", "err"), ("○", "muted")):
|
|
status.append(mark + " ", style=style)
|
|
lines.append(status)
|
|
|
|
# The node-type marks in the theme's own ``type.*`` entries — the same styles every
|
|
# typed node in the app is drawn in (ui.widgets._NODE_GLYPHS), so this row *is* the
|
|
# marker language rather than a restatement of it. The star and the unknown ring have
|
|
# no type of their own: they take the ``you`` white and ``muted`` grey they carry
|
|
# everywhere else.
|
|
nodes = Text("nodes ")
|
|
nodes.append("★ ", style="you")
|
|
for mark, style in (
|
|
("● ", "type.node"),
|
|
("▲ ", "type.repeater"),
|
|
("■ ", "type.room"),
|
|
("◉ ", "type.sensor"),
|
|
("○", "muted"),
|
|
):
|
|
nodes.append(mark, style=style)
|
|
lines.append(nodes)
|
|
|
|
classes = Text("classes ")
|
|
for icon in KIND_ICONS.values():
|
|
classes.append(glyph(icon) + " ", style="brand")
|
|
classes.append(" payloads ", style="muted")
|
|
for typename in ("REQ", "RESPONSE", "TEXT_MSG", "GRP_TXT", "GRP_DATA", "ANON_REQ"):
|
|
classes.append(glyph(PAYLOAD_ICONS[typename]) + " ")
|
|
for typename in ("PATH", "TRACE", "MULTIPART", "CONTROL"):
|
|
classes.append(glyph(PAYLOAD_ICONS[typename]) + " ")
|
|
lines.append(classes)
|
|
|
|
channels = Text("channels ")
|
|
channels.append(channel_glyph("#public", None) + " name-derived ", style="brand")
|
|
channels.append(glyph("🌐") + " public ")
|
|
channels.append(glyph("🔒") + " private")
|
|
lines.append(channels)
|
|
|
|
concepts = Text("concepts ")
|
|
for icon in ("📡", "🕒", "🔄", "💾", "📂", "🔑", "🗑", "✎", "⚡", "⭐", "📤", "📨"):
|
|
concepts.append(glyph(icon) + " ")
|
|
for icon in ("🔔", "🔕", "📱", "🔗", "🏆", "⌨", "📖", "💰"):
|
|
concepts.append(glyph(icon) + " ")
|
|
lines.append(concepts)
|
|
|
|
lines.append(Text("keys ⌫ ⇧ … ↕ ↔ ↻ ⌖ ❯ ▸"))
|
|
lines.append(Text())
|
|
|
|
names = Text("names ")
|
|
for name, key in _DEMO_NAMES:
|
|
names.append(name + " ", style=name_style(name, key))
|
|
names.append("me", style="you")
|
|
lines.append(names)
|
|
|
|
# The same names as chips — the chat's sender labels, drawn through the path line's
|
|
# own segment, so this row is also a one-hop sample of the route language: hue in the
|
|
# fill, our end the star, a node nobody can place in the keyless grey.
|
|
chips = Text("chips ")
|
|
for name, key in _DEMO_NAMES[:2]:
|
|
chips.append_text(name_chip(name, key))
|
|
chips.append(" ")
|
|
chips.append_text(name_chip("", you=True))
|
|
chips.append(" ")
|
|
chips.append_text(name_chip("·"))
|
|
lines.append(chips)
|
|
|
|
lines.append(Text("heard ").append_text(_ages_row()))
|
|
|
|
snr = Text("snr ")
|
|
for reading in (8.0, -2.0, -12.0):
|
|
snr.append(f"{reading:+.1f} ", style=snr_style(reading))
|
|
lines.append(snr)
|
|
|
|
# What the render boundary makes of stored text: accents fold on PicoCalc (the raw
|
|
# form would be tofu there — the console font has no accented Latin), Cyrillic is
|
|
# font-native and passes through on both platforms.
|
|
folded = fold_text("café Montréal")
|
|
lines.append(Text.assemble("text ", (folded, "brand"), (" · ", "muted"), "привет мир"))
|
|
lines.append(Text())
|
|
|
|
chart = Text("chart ")
|
|
chart.append("⡀⡄⡆⣆⣦⣶⣷⣿", style="snr.good")
|
|
chart.append("⣶⣤⣀", style="snr.ok")
|
|
chart.append("⡀⠄⠂", style="snr.bad")
|
|
chart.append(" braille", style="muted")
|
|
lines.append(chart)
|
|
|
|
# The F-key lane, on the one platform that draws it: both fills, plus a dim slot and
|
|
# an unassigned one, so the card shows every state a chip can be in. Built through
|
|
# lane_text itself — this is the acceptance screen, so it must be the real renderer.
|
|
if platform.footer_fkeys:
|
|
lane = (
|
|
FPair("Region", "home"),
|
|
FPair("You", "locate", enabled=False),
|
|
None,
|
|
FPair("Page ↓", "pagedown", "Bottom", "end"),
|
|
FPair("Page ↑", "pageup", "Top", "home"),
|
|
)
|
|
lines.append(Text())
|
|
lines.append(lane_text(lane))
|
|
lines.append(lane_text(lane, shifted=True))
|
|
return lines
|