The capture also draws the two wordmarks of the splash

scripts/screenshots.py draws logo.71.ans and logo.53.ans, through the
splash's own loader and the emulator's Terminus raster, in the Campbell
colours, into docs/screenshots/splash/logo.<width>.png at one image
pixel per font pixel. The website takes its splash from there, so it
always shows the art that the app shows.

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-16 03:35:43 -04:00
1 parent 68280533a7
commit f8a9ce1cb8
4 files changed
+73 -5

No files matched your search

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

+8 -3
View File
@@ -6,7 +6,7 @@ These are tools for the repository. None of them is in the wheel.
|---|---|
| `basemap-doctor.py` | Finds why a user's map has no basemap: certificates, DNS, proxy, or colour depth |
| `cardputer-zero/` | `build-deb.sh` builds the Debian package for M5Stack's app store. Run it on the Cardputer Zero or on another arm64 Debian 13 system. `store/` holds the listing for the store (`app-builder.json`, which `czdev publish` reads), its icon, and its screenshots. `screenshot.py` saves the display of a Cardputer as a 320×170 PNG over SSH, as the store wants a screenshot. `store-publish.py` sends a package to the store with M5Stack's `czdev`, with two corrections. The release workflow runs it after each release. `launcher-entry.sh` puts a source checkout in the Cardputer Zero's app launcher. `remove` takes it out. `meshterm.png` is the package's icon: the website's logo, at the launcher's 100×100 size. `meshterm-dev.png` is its negative, for the checkout's entry, so that the two entries look different. `type-text.py` types a pasted string (an API key, a Wi-Fi password) on the Cardputer's keyboard from another computer, over SSH |
| `screenshots.py` | Makes the screenshots of the website from the demo mesh (`--demo`): the main menu, the chat, the paths of a message, and the map, for the terminal, the Cardputer Zero, and the PicoCalc. They are the same at each run. The result goes to `docs/screenshots/<set>/` |
| `screenshots.py` | Makes the screenshots of the website from the demo mesh (`--demo`): the main menu, the chat, the paths of a message, and the map, for the terminal, the Cardputer Zero, and the PicoCalc. They are the same at each run. The result goes to `docs/screenshots/<set>/`. It also draws the two wordmarks of the splash into `docs/screenshots/splash/`, which the website uses as its splash |
| `picocalc-lyra/` | Setup of the Calculinux device: console font, palette, Wi-Fi. `xiao-radio/` is the kit for the XIAO radio (firmware build, flashing, UART setup) |
| `uconsole/` | The uConsole's always-on SPI bridge service. MeshTerm can also control that radio directly, with no script (refer to `docs/devices/uconsole.md`) |
@@ -67,5 +67,10 @@ Emoji, which Windows has. On another system, change `TEXT_FONTS` and `EMOJI_FONT
top of the script. The first run downloads the tiles of the map. The later runs use the
tile cache in the temporary directory.
After a change to a screen, run the script again, then rebuild the website (refer to the
README of the `meshterm.net` repository).
The script also draws each wordmark of the startup splash (`meshterm/assets/splash/*.ans`)
as `docs/screenshots/splash/logo.<width>.png`, one image pixel for each pixel of the font,
in the Campbell colours. The website takes its splash from there: the wide mark on a wide
screen, and the narrow mark on a phone. `--only splash` draws only these two images.
After a change to a screen or to the art, run the script again, then rebuild the website
(refer to the README of the `meshterm.net` repository).
+65 -2
View File
@@ -9,6 +9,10 @@ an 80×24 terminal), ``cardputer-zero``, and ``picocalc-lyra``. Each run uses ``
the paths of a message, and the map, and it saves each screen as a PNG. The result is
``DIR/<set>/<screen>.png`` (``DIR`` is ``docs/screenshots`` by default).
The script also draws each wordmark of the startup splash (``meshterm/assets/splash``) as
``DIR/splash/logo.<width>.png``, one image pixel for each pixel of the font: the website
shows them as its splash, the wide mark on a wide screen and the narrow mark on a phone.
The screens are the same at each run, for four reasons:
* The demo writes the same history at each start.
@@ -53,6 +57,18 @@ ROOT = Path(__file__).resolve().parent.parent
#: The sets, in the order on the website. The terminal set is the default there.
SETS = ("terminal", "cardputer-zero", "picocalc-lyra")
#: The name of the wordmarks in ``--only``. They need no run of MeshTerm.
SPLASH = "splash"
#: The 16 colours of the wordmarks: the Campbell scheme of Windows Terminal, which the
#: art was drawn in and the website uses.
CAMPBELL = (
(12, 12, 12), (197, 15, 31), (19, 161, 14), (193, 156, 0),
(0, 55, 218), (136, 23, 152), (58, 150, 221), (204, 204, 204),
(118, 118, 118), (231, 72, 86), (22, 198, 12), (249, 241, 165),
(59, 120, 255), (180, 0, 158), (97, 214, 214), (242, 242, 242),
) # fmt: skip
#: The grid of the terminal set: a standard terminal.
TERMINAL_GRID = (80, 24)
@@ -74,7 +90,7 @@ def main(argv: list[str]) -> int:
"""Run one child process for each set, then report what each one wrote."""
parser = argparse.ArgumentParser(description=__doc__.splitlines()[0])
parser.add_argument("--out", type=Path, default=ROOT / "docs" / "screenshots")
parser.add_argument("--only", nargs="*", choices=SETS, default=list(SETS))
parser.add_argument("--only", nargs="*", choices=(*SETS, SPLASH), default=[*SETS, SPLASH])
parser.add_argument("--child", choices=SETS, help=argparse.SUPPRESS)
args = parser.parse_args(argv)
if args.child:
@@ -85,7 +101,10 @@ def main(argv: list[str]) -> int:
# directory of its own, so it gets the font directory of this machine by name.
fonts = os.environ.get("MESHTERM_EMULATOR_FONTS") or str(font_dir())
status = 0
for name in args.only:
if SPLASH in args.only:
for path in _draw_splash(args.out / SPLASH):
print(f"{SPLASH}: {path}", flush=True)
for name in (name for name in args.only if name in SETS):
home = Path(tempfile.gettempdir()) / "meshterm-screenshots" / name
env = {
**os.environ,
@@ -270,6 +289,50 @@ def _tour(capture: Capture, type_text: Callable[[str], None], save: Callable[[st
# --------------------------------------------------------------------------- drawing
def _draw_splash(target: Path) -> list[Path]:
"""Draw each wordmark of the splash in the emulator's font, one image pixel per pixel.
The marks are the ANSI art that the startup splash shows, read through the same loader
(:func:`meshterm.ui.logo.load_logo`, widest first). The emulator's raster draws them
in Terminus 6×12, the font of the handhelds. Its cell has the shape of the VGA cell
that the art was drawn in, so the art keeps its proportions.
Returns:
The paths of the images, widest mark first.
"""
from PIL import Image
from meshterm.emulator.font import CELL_H, CELL_W, find_font
from meshterm.emulator.raster import Raster
from meshterm.emulator.vt import Terminal
from meshterm.ui.logo import load_logo, logo_width
target.mkdir(parents=True, exist_ok=True)
font = find_font()
written: list[Path] = []
limit: int | None = None
while rows := load_logo(limit):
width = logo_width(rows)
terminal = Terminal(width, len(rows), palette=CAMPBELL)
terminal.feed("\r\n".join(f"{row}\x1b[0m" for row in rows))
raster = Raster(
terminal,
font,
default_fg=CAMPBELL[7],
default_bg=CAMPBELL[0],
width=width * CELL_W,
height=len(rows) * CELL_H,
top_left=True,
)
raster.redraw()
size = (raster.width, raster.height)
path = target / f"logo.{width}.png"
Image.frombytes("RGB", size, bytes(raster.pixels)).save(path, optimize=True)
written.append(path)
limit = width - 1
return written
def _draw(name: str, grid, palette): # noqa: ANN001, ANN202 - cells, colours, an Image
"""The image of one screen, in the look of its set."""
from meshterm.emulator.devices import DEVICES