Narrow wordmark for narrow screens; move the art out of the code

JP drew a 53-cell MeshTerm at the original's height. It gets the same
colouring: the vertical ramp — white, yellow, light red, red, light
black — with every box-drawing character, shadow row and letter bevel
alike, in the light black.

The art moves to meshterm/assets/. It was sitting in meshterm/ui/ among
the modules, which is the wrong place for a file nothing imports; it
still ships inside the package, since the splash reads it at runtime and
an installed venv has no repo to read from. scripts/colour-logo.py paints
a .txt into its .ansi, so the palette is stated once in code instead of
retyped into escape sequences by hand, and re-styling stays a text edit.

Which mark is drawn is a question about the *screen*, not the platform: a
53-column console and a desktop terminal dragged narrow have the same
problem. load_logo(cols) answers it — widest that fits — and
compose_startup asks, since it is the only place that knows the width.
Below the smallest mark it returns nothing at all; no banner beats a torn
one. A test now draws the real wordmark at each platform's width, which
is the gate the splash never had (it is the one screen the gallery
doesn't cover, which is how a 71-cell mark shipped to a 53-cell console).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Jean-Pierre MartineauandClaude Opus 5 committed 2026-08-07 21:53:28 -04:00
1 parent 5aeb034c06
commit eaafc87403
8 files changed
+183 -18

No files matched your search

+73
View File
@@ -0,0 +1,73 @@
#!/usr/bin/env python3
"""Colour a plain-text wordmark into the pre-coloured ANSI the splash draws.
The art itself is hand-drawn and lives in ``meshterm/assets`` as a ``.txt``; this paints
it and writes the matching ``.ansi`` beside it, so the colour scheme is stated once here
rather than re-typed into every escape sequence by hand.
python scripts/colour-logo.py # repaint every asset/*.txt
python scripts/colour-logo.py logo_53 # just that one
The scheme is a vertical ramp over the letter rows — white, yellow, light red, red, light
black, hottest at the top — with every box-drawing character (the drop shadow and the
letters' own bevels) in the light black regardless of its row. Each colour is chosen so the
PicoCalc console's 16-slot quantizer lands it on exactly one palette slot (15, 11, 9, 1, 8)
while it stays a warm fade in truecolour on the desktop; see ``theme._VT_SLOTS``.
"""
from __future__ import annotations
import sys
from pathlib import Path
ASSETS = Path(__file__).resolve().parent.parent / "meshterm" / "assets"
#: Letter-row colours, hottest first. A mark with more rows than this reuses the last one.
ROW_RGB = ("255;255;255", "251;217;80", "248;113;113", "185;28;28", "80;93;111")
#: Every non-block character — the drop shadow and the bevels — takes this.
SHADOW_RGB = "80;93;111"
#: The letter body. Everything else in the art is shadow.
BLOCK = "█"
def colour(art: str) -> str:
"""Paint one plain-text wordmark, returning the pre-coloured ANSI."""
lines = art.split("\n")
if lines and lines[-1] == "":
lines.pop()
out_lines = []
for row, line in enumerate(lines):
hue = ROW_RGB[min(row, len(ROW_RGB) - 1)]
out, current = [], None
for ch in line:
if ch == " ":
if current is not None:
out.append("\x1b[0m")
current = None
out.append(" ")
continue
want = hue if ch == BLOCK else SHADOW_RGB
if want != current:
out.append(f"\x1b[38;2;{want}m")
current = want
out.append(ch)
if current is not None:
out.append("\x1b[0m")
out_lines.append("".join(out))
return "\n".join(out_lines) + "\n"
def main() -> None:
"""Repaint the named marks, or every ``.txt`` in the assets folder."""
names = sys.argv[1:] or sorted(p.stem for p in ASSETS.glob("*.txt"))
for name in names:
source = ASSETS / f"{name}.txt"
target = ASSETS / f"{name}.ansi"
target.write_text(colour(source.read_text(encoding="utf-8")), encoding="utf-8")
print(f"{source.name} -> {target.name}")
if __name__ == "__main__":
main()