Our chip had our hue as a light fill under dark text. It now has the fill that each known sender gets, with a white label, and the PicoCalc can draw that too: white on the dim twin of the hue. The hop flag is white_ink now. A chat chip with no key keeps its grey fill, and its label is black. On the PicoCalc it is black on the light grey of the keyless chip. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
202 lines
8.0 KiB
Python
202 lines
8.0 KiB
Python
# SPDX-License-Identifier: Apache-2.0
|
|
"""The visual-language specimen: all the marks, icons, colours, and folds on one screen.
|
|
|
|
``meshterm specimen`` prints this card through the real functions of the platform: the
|
|
active theme, the ``glyph()`` function that all icons go through, the fold at the render
|
|
boundary, and the heat and SNR scales. Thus what the card shows is what the platform
|
|
draws, not a model of it. On the PicoCalc console, the card is also the acceptance card
|
|
for the font and the palette (all nine P3 marks, the node glyphs, braille, and the 16
|
|
programmed slots). On a desktop terminal, it shows the same content with the emoji and
|
|
the truecolor of the regular platform. The card fits the limits of the PicoCalc: each
|
|
line is 53 cells or less, and the full card is 24 rows or less.
|
|
"""
|
|
|
|
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, active_deck
|
|
from .widgets import (
|
|
ORIGIN,
|
|
_recency_style,
|
|
author_chip,
|
|
channel_glyph,
|
|
mention_chip,
|
|
name_chip,
|
|
self_chip,
|
|
)
|
|
|
|
#: Demonstration identities for the row of name colours: ``(name, key)``. The keys are
|
|
#: synthetic. We chose them so that their first bytes are in three different sectors of
|
|
#: the hue wheel. On the console, these are three different palette slots (refer to
|
|
#: ``theme._NODE_SLOT_HEXES``).
|
|
_DEMO_NAMES = (("Alice", "a1b2"), ("Lakeside", "3d63"), ("Lounge", "cc10"))
|
|
|
|
#: The key of our own node in the specimen, which gives the hue of its chat chip.
|
|
_DEMO_SELF_KEY = "5e7f"
|
|
|
|
|
|
def _palette_row() -> Text:
|
|
"""The 16 slots as filled blocks: first the dim bank, then the bright bank.
|
|
|
|
On the PicoCalc, the blocks use the real slots of the console (``color(N)``). Thus the
|
|
row shows exactly what ``setvtrgb`` programmed. On the regular platform, the blocks
|
|
use the RGB values of the design instead. They are 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 at its own anchors: one age a little 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, with one renderable for each line.
|
|
|
|
Print the lines 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 marks of the node types, in the theme's own ``type.*`` entries. The app draws
|
|
# each node that has a type in these same styles (ui.widgets.NODE_GLYPHS). Thus this
|
|
# row is the marker language itself, not a copy of it. The star and the unknown ring
|
|
# have no type of their own. They use the ``you`` white and the ``muted`` grey, the
|
|
# same as everywhere else in the app.
|
|
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, as the chat draws them. A sender label closes on the point,
|
|
# because a message goes on from its author. The mention at the end opens on the
|
|
# notch, because the node that it names is where the message goes, and it has no ``@``.
|
|
# The path line's own segment draws them, so this row is also a one-hop sample of the
|
|
# route language: the hue in the fill, and black on grey for a node that nobody can
|
|
# identify. Our own messages go under ``★ me``, the one chip with a white label, on
|
|
# the fill of our hue.
|
|
chips = Text("chips ")
|
|
for name, key in _DEMO_NAMES[:2]:
|
|
chips.append_text(name_chip(name, key, role=ORIGIN))
|
|
chips.append(" ")
|
|
chips.append_text(self_chip("me", _DEMO_SELF_KEY, role=ORIGIN))
|
|
chips.append(" ")
|
|
chips.append_text(author_chip("·", None))
|
|
chips.append(" ")
|
|
name, key = _DEMO_NAMES[2]
|
|
chips.append_text(mention_chip(name, key))
|
|
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 does to stored text. On the PicoCalc, accents fold, because
|
|
# the console font has no accented Latin letters. There, the raw form shows as tofu.
|
|
# Cyrillic is in the font, so it 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 a platform that draws one: both fills, a dim slot, and an
|
|
# unassigned slot. Thus the card shows each state that a chip can have. The platform's
|
|
# own deck builds the lane, because this is the acceptance screen, and it must use the
|
|
# real renderer.
|
|
deck = active_deck()
|
|
if deck is not None:
|
|
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(deck.lane_text(lane))
|
|
lines.append(deck.lane_text(lane, shifted=True))
|
|
return lines
|