diff --git a/docs/screenshots/splash/logo.53.png b/docs/screenshots/splash/logo.53.png new file mode 100644 index 0000000..686f979 Binary files /dev/null and b/docs/screenshots/splash/logo.53.png differ diff --git a/docs/screenshots/splash/logo.71.png b/docs/screenshots/splash/logo.71.png new file mode 100644 index 0000000..8fb1e07 Binary files /dev/null and b/docs/screenshots/splash/logo.71.png differ diff --git a/scripts/README.md b/scripts/README.md index 34fbe4d..abb9431 100644 --- a/scripts/README.md +++ b/scripts/README.md @@ -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//` | +| `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//`. 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..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). diff --git a/scripts/screenshots.py b/scripts/screenshots.py index e63e315..c151c3e 100644 --- a/scripts/screenshots.py +++ b/scripts/screenshots.py @@ -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//.png`` (``DIR`` is ``docs/screenshots`` by default). +The script also draws each wordmark of the startup splash (``meshterm/assets/splash``) as +``DIR/splash/logo..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