scripts/video.py runs the demo mesh headless in the terminal set of the screenshots, types a tour of less than two minutes, and records the screen 30 times a second. It paints each different screen in parallel, encodes the video with ffmpeg, and writes demo.mp4, its poster, and its play card into the website checkout's src/. The camera stops while the tour waits for something slow, so the video jumps over the wait.
screenshots.py gives the video its app runner, its environment, and an RGB painter. The terminal screenshots stay pixel-identical. The glossary gains "video frame".
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Measured against a real Montreal database: most messages arrive twice,
through the two repeaters a companion hears, on paths that share their
start and split near the end, with 3-byte hashes. The demo did five or
six short copies through five neighbours.
Our node now hears only two new rooftop repeaters, Villeray and
Rosemont (the terrain hides the big ones), companions lose 7 dB at street
level, and each sender has its own hash width (mostly 3 bytes). Now 91%
of messages arrive twice and 83% of paths use 3-byte hashes. Noor moves
to Côte-des-Neiges to stay in reach, and the traces leave through
Villeray or Rosemont.
The capture finds the Message paths message by its text (a key press
can be lost while the transcript draws) and selects the second path,
since the first is often a straight line.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The release skill runs scripts/screenshots.py after the version bump,
so the README, the website's three sets, and its splash show the new
version, and the images ride in the release commit and its tag. The
release commit now stages by name, because other sessions share the
tree.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
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>
The four captures in the README are now the terminal set from the demo
mesh: the main menu, the chat, the paths of a message, and the map. The
old dashboard, map, trace, and path images, which showed real nodes,
are gone. docs/README.md says where the set folders come from.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
scripts/screenshots.py runs MeshTerm --demo in the emulator's session,
with no window, and types its way to the main menu, the chat on Public,
the paths of a message, and the map. It saves each screen for the
terminal (80x24, drawn in Cascadia Mono with its own box, braille, and
Powerline shapes), the Cardputer Zero, and the PicoCalc (the emulator's
own raster, at 3x), into docs/screenshots/<set>/.
Two runs give the same pixels: the clock is pinned to 11:42 on Thursday
15 October 2026 in Montréal (time-machine and TZ), and the demo device
plays no background traffic during a capture (PLAY_BACKGROUND). Gabriel
in Beloeil joins the demo, so the message shown in Message paths
arrives on five paths through Mont-St-Hilaire, and the map opens at z10.
Pillow, fontTools, and time-machine join the dev extras.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The demo's conversations are plain English about ordinary things: the
mesh, the meetup, a bike ride, the weather, a family dinner. The props
are gone: no poutine, smoked meat, or tuque, and Poutine Patrol and
Bixi Bandit are now Steve K. and Nico R. Our node is Alex Base. The
room, the sensors, two channels (#cycling, Family), and the regions
(south-shore, west-island, mtl-west...) have English names.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The virtual clock of the demo script stands for 11:42 on a weekday, not
19:30. Today's lines move into the morning: Mathilde arrives at 10:05,
the smoked meat run for noon is planned at 11:20, and the dinner on
Famille moves to yesterday. A test checks that each line is sent while
its sender is heard on the mesh.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
meshterm --demo runs the simulator in a synthetic Montréal mesh: 14
repeaters on the high points (Mont-Royal to Mont-St-Hilaire), rooms,
sensors, and about twenty companions, all invented. A log-distance
link model decides who hears whom, so floods arrive on several real
paths and traces walk real routes.
At each start the demo writes its own directory (<config>/demo) again:
300 days of adverts and a dense week of traffic, bilingual channel,
direct, and room conversations whose packets decrypt, traces with
their trophy-case records, neighbour tables, a joined room, admin
passwords, regions, and Watchtower alerts. Every value comes from a
fixed seed and an age before now, so each run gives the same data.
The device plays the live traffic from the same seeds.
The repository writers that stamp now take an optional time, and
record_observations stores a batch in one commit. The test world of
--mock is unchanged.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Chat's Channels, Rooms, and Direct headings and the trophy case's
discipline headings drop their emoji, for a cleaner look. The trophy
marks stay on the delete list's rows and the record page's header.
section_heading no longer strips icons, since no heading has one.
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
"Section spacing" (section_spacing_min_rows, in the Display group)
replaces "Author spacing" and "Spacing from". It is the smallest
terminal that gets the line: any height, 20, 24, 30, or 40 rows, or
never (default 24). Each section heading of a list gets the line, and
the chat keeps it between authors and before a new day.
The frame records the terminal rows on the screen before the body
renders (note_terminal_rows), and Screen.section_spacing is the one
test. section_heading marks its row as spaced, and the select list
adds the line, never at the top, after a blank line, or under a
heading. The hand spacers before headings are gone, except the seam
between the region table and its actions, which is two lists.
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
So they pop a bit more: #f5e5a8, pale and low in chroma, which keeps it
well clear of warn's amber (0.12 apart in OKLab) and title.warn's (0.085).
The PicoCalc draws its nearest slot, the bright yellow that warn uses.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
A section heading, in a grouped list or as a chat's day divider, was the
dark grey of faint. It now wears title.border, the colour of the title
in the frame around it: bold #d4d4d4, and white on the PicoCalc. It is
still never the border's own grey.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
#7c7c7c went too far. The border now sits halfway in lightness between
that and the old #a2a2a2, and on the PicoCalc it is back on the light
grey slot, the nearest one.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Every node-type mark is white now: ● ▲ ■ ◉ were pink, violet, white,
and orange, and the shape already says the type. Each keeps its own
type.* style, so one can take a colour back in one place; the unknown ○
keeps its grey. On the map a crowd in one cell used to glow toward
white, which a white marker cannot do, so that signal is gone.
The border grey goes down to #7c7c7c, which JP picked on a ladder. That
sits almost on faint, so the title bar's live clip arrow is now lit like
the title instead of taking the border's grey. On the PicoCalc the
border is the dark grey slot.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
A plain border, the one with no caution tier, was the indigo accent: one
more colour on every screen, and it meant nothing. It is now its own
theme style, border, in the grey of muted, with title.border and
hint.border beside it; the amber and red tiers keep their colours. That
covers the base frame, every dialog, the handhelds' title bar, the tab
strip, and the CLI's panels. The title bar's clip arrow for a direction
with nothing more now dims to faint, since the border and muted are the
same grey. On the PicoCalc the border takes the light grey slot.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The Tailwind slate greys were cold. Each one is now r=g=b at its own
OKLab lightness, so every contrast stays where it was: the theme's
chrome, the path chips, the map's roads, labels and buildings, the heat
tail, and the emulator window. test_neutral_greys fails any #rrggbb
literal that reads as a grey and has a tint.
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
MeshTerm runs the same on the Cardputer Zero (C154) and the Cardputer Zero
Lite (C155): the Lite only leaves out a camera, motion sensors, and the
microSD card, none of which MeshTerm uses. The device manual, the README,
the manuals index, and the About page now say that both models are supported,
and the manual says that the Lite needs a card with M5Stack's system first.
M5Stack does not support hot-swapping a Cap on the Cardputer Zero, the Cap
LoRa-1262 included, and says that it may cause the device to malfunction.
The manual now puts that caution before its steps for the Cap, with M5Stack's
other caution (the antenna goes on before the power), and the steps turn the
handheld off first. It also says that MeshTerm switching the header off when
it quits does not make a Cap safe to remove. The About page says the same in
its own voice, and gains a section on the Cardputer Zero next to the uConsole
and the PicoCalc.
The README shows the Cardputer Zero's start screen (the store's capture,
three times its size) under the PicoCalc photo, and its quick start shows the
emulator command. The pip commands with the spi extra install from GitHub,
because mesh-term is not on PyPI yet. The platform comment and CLAUDE.md no
longer say that the hardware is not in hand: only a flag selects the platform
because the device-tree model is the CM0's generic string.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
MeshTerm is in M5Stack's app store since 0.11.0 (CardputerZero/packages#176).
The release skill now treats the store PR as part of the release: it is not
done until that PR merges, and the Discord announcement tells Cardputer Zero
owners to update from the store. The device guide no longer says that the
store may not have MeshTerm yet.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The part of a route that the user does not compose (a mirrored return
leg, the return home) drew every hop as the same dark slate chip. Each
faded chip now has the colours of its own node at FADE_CHROMA of their
chroma, at the same lightness, so it still says which node it is. The
PicoCalc has no colour of less chroma: there a faded hop keeps its label
colour and drops its fill. Arrows keep faint.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The star of our node had the hue of our key, set from the self-info at run
time. It is now the white of "you" on each surface: the map, the routes,
the lists, the legends, and the chat. The run-time colour, the device-state
hook, and the style generation of the render cache are removed. A faded
star keeps its grey.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
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>
The star of our node was the yellow of the map. SELF_MARK now names a
theme style, you.mark, and the theme writes our key's hue into it when the
device-state cache reads the self-info. The context makes that connection,
so the services still do not import the UI. The map, the routes, the lists,
the legends, and the chat all draw it, and the render cache keys on a style
generation so no old bytes stay. A faded star keeps its grey.
In a chat, a sender or a mention with no key had the light blue keyless
chip. It is now quiet: the grey of plain text on that grey darkened by the
chip rule. Routes keep the keyless chip, arrow mode keeps node.unknown, and
the PicoCalc keeps the keyless pair.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The point of our chip did not read well. The mark at the left of the time
is now the star, in the hue of our key: the colour of the mention chip in
the body. It is the same on each platform, with chips or without them. The
Messages list uses the same star at the left of the age.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The notch and the point together were too heavy. The mark is now one cell,
the point of our chip in its fill, then a space before the time. Without
chips, it is ">" in our hue.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The star at the left of the time becomes two cells in the fill of our
chip: the notch, then the point, with no label between them. Thus the mark
has the colour of the mention chip in the body. Without chips, it is ">>"
in our hue. The Messages list puts the same mark at the left of the age.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Our messages go under "★ Name" on the turned-over chip, as before the last
change. A mention of us keeps its chip without a star, and its message
keeps the star at the left of the time.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Our sender chip is our name, turned over, with the star on the page to its
right. A mention of us has the turned-over chip and no star: the message
that names us gets the star at the left of its time, so every time moves
two cells right, and the Messages list puts it at the left of the row's
age. A mention chip drops its @ where chips are drawn.
The compose line draws each complete @[Name] as its chip and treats it as
one atom: the arrows step over it, and Backspace or Delete removes all of
it. A reply inserts its mention at the cursor, so one message can name
several nodes. The prompt and the typed text are white.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The Discord announcement now carries a star ask under the download
link, with the reason. The release page's ask opens on a heart and
closes on a thank-you.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Two preferences in the Display group: "Author spacing" (auto, on, off;
default auto) and "Spacing from" (the smallest terminal, in rows, that gets
the blank line with auto; default 24). The test reads the real rows of the
terminal before each paint, so a small desktop window drops the line as the
Cardputer Zero does, and a change applies at the next paint. The transcript
cache stores the spacing, so a change draws the lines again. The CLI
manual's sample of `preferences show` is the real output again.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The key parsers now return the key in the form that the user gave. Each
device takes the form that it keeps: a firmware companion expands a seed
to the 64 bytes that it accepts, and the node of an SPI radio saves the
seed as it is, the same form as a key that it makes itself. A seed is the
more complete form, and the radio library signs with it through PyNaCl.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The transcript asked for a gap between sender groups, but an empty Text
renders as no line at all, so the gap never showed. A blank line now
closes each author's messages, also before a new day's divider, where
the platform has 20 rows or more. The 14 rows of the Cardputer Zero keep
the transcript dense. A cached slice that starts with the gap pins its
day divider one line down.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
A channel, a direct chat, a room, and the Messages list show our messages
under "★ Name" instead of the bare star. The chip has our key's hue turned
over: dark ink on a light fill, where each other sender is light on dark.
A mention of our name takes the same chip, as "★ @Name". The PicoCalc
cannot draw a light background, so there the chip keeps the normal
colours and the star marks it. Path lines keep the bare star.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Show private key drew its warning and no key in the menu since 0.10.0.
On the uConsole and the Cardputer Zero, MeshTerm now writes an imported
key into the node's identity.key and restarts the node. An import also
reads a file (hex text or raw bytes), expands a 32-byte seed, refuses a
key of the wrong length before the confirmation, and names a firmware
that has its import turned off. The run log no longer stores the key of
config import-key (a migration removes the stored ones), and an exported
key file is written with mode 0600. Refer to #26.
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
czdev starts its branch from the fork's default branch, and the store's
check compares the PR branch with the store's main directly. After
M5Stack removed every app on 2026-10-12, the fork was 10 commits behind,
so the 0.11.0 PR (#176) carried all 40 removed apps as new files, and
the check failed on the first of them. store-publish.py now calls
GitHub's merge-upstream for the fork first, and publishes nothing if
that fails. PR #176 was repaired by hand with "Update branch".
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The page no longer warns that the handheld is not supported. It now gives
three ways to install MeshTerm: M5Stack's app store first, then the same
package from a GitHub release, then the source code for developers. The
README, the docs index, and the hardware guide say the same, and the
hardware guide says that the launcher selects the platform.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Greying every name that two keys ever used was too strict: a reflash with an
erase makes a new key, and 34 names on this mesh have one, JP's own among
them. Smog went grey this morning because of two keys silent since September.
The newest key now gets the name, and the name is grey only when another key
used it within name_overlap_days (a new Display preference, "Shared names",
default 7) of it: two live claimants. A contact that was never heard never
wins. On this machine's real data, LOG.IC 3R goes to its active key 47368a7c,
Smog to fa331c83, and 8 of 883 names stay grey.
The stored names come with their times from the one scan node_names() already
ran (node_names_with_times), so opening a chat costs no second scan.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
A contact's page has enough action rows that, on a 24-row terminal, they left
the preview under its five-row floor, and it stepped aside everywhere. It now
keeps its floor and the page scrolls. Only a short frame (the Cardputer's
11-row viewport) leaves it out.
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
A name is a label any key can claim, and a channel message carries only its
sender's name. When two nodes use one name, the resolver used to let the last
contact win, which handed one node's hue, map, and page to a message that
nothing tied to it: a second "LOG.IC 3R" with a new key took the first one's
green and location. Such a name now resolves to None, like any unplaced name.
A contact's full key and the stored id of the same node still count as one.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
In a channel, a DM, or a room, ^O on a selected message opens the node page
of its author, the same page the Contacts list opens: our own for a message
of ours, the contact the author's key finds otherwise (ContactPages, the twin
of the map's Locator). It opens to look, without the management verbs, which
belong to the list a contact is in. The hint names ^O only where it acts; the
handheld chip is Author, on Shift+F3 behind Map, since F1-F3 are full.
The room's login moves from ^O to ^G.
The Unreleased changelog now also says where the Map chip went, and covers
the shared lane and the route-end chips.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
F1-F3 hold what the three conversations share, in the hint's order: Paths,
QR, Map. The Shift bank holds each screen's own verbs: ^R on Shift+F1 (Retry
in a DM and a room, Resend in a channel) and the room's Log in on Shift+F2.
F4/F5 stay the pager with Latest/Oldest behind Shift.
Day ↑ and Day ↓ are gone from the lane; the desktop keeps the day jump on
^PgUp/^PgDn.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The Messages list's preview draws its author as an origin chip (no colon: the
point says it) and its mentions as destination chips, as the transcript does.
The packet viewer's from rows close on the point and its to row opens on the
notch, around the via chain that already opens and closes on both. The live
feed's addressed subject is an origin chip and a destination chip facing each
other across one cell of page (widgets.route_ends): a seam would claim the two
are neighbours. A pair too wide for the lane cracks instead of eliding. The
courier's recipient is a destination chip, in the hue of its key while it
waits and muted once finished.
Arrow mode reads exactly as before everywhere. A lone hop there carries no
PATH_INK stamp, so a highlighted list row whitens it like any other name.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>