892 Commits
Author SHA1 Message Date
rich 233dfc813c Merge upstream MeshTerm 0.11.5
CI / lint (push) Waiting to run
CI / test macos-latest py3.14 (push) Waiting to run
CI / test ubuntu-24.04 py3.10 (push) Waiting to run
CI / test ubuntu-latest py3.11 (push) Waiting to run
CI / test ubuntu-latest py3.12 (push) Waiting to run
CI / test ubuntu-latest py3.13 (push) Waiting to run
CI / test ubuntu-latest py3.14 (push) Waiting to run
CI / test windows-latest py3.10 (push) Waiting to run
CI / test windows-latest py3.14 (push) Waiting to run
CI / build (push) Waiting to run
Issue trailers / check (push) Waiting to run
Bring in the 65 upstream commits since 0.11.1, with ^K lock and the self chip.
Both sides kept their features. React gives way to Lock in two places. On the
PicoCalc lane, Lock keeps the Shift half of F2 and React is ^E only. In the
hint, ^E react is the first atom to go when ^K lock has no room.

The long-message code now refills the compose line with MentionEditor, which
replaced LineEditor upstream.
2026-10-22 16:53:37 -04:00
Jean-Pierre MartineauandClaude Opus 5.5 0d09a364cd MeshTerm 0.11.5
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-22 03:15:55 -04:00
Jean-Pierre MartineauandClaude Opus 5.5 544156968d The Courier outbox filters as you type again, and a wide footer keeps Esc
Typing narrows the outbox again, and its footer says so. With the scroll
atom, the footer of an entry row is wider than 72 cells, which JP
accepted. The footer line no longer cuts a hint that is too wide at the
edge, which took Esc with it: it drops atoms instead, as the splash
already did. The outbox drops "Del delete" first.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-22 01:09:55 -04:00
Jean-Pierre MartineauandClaude Opus 5.5 53ffb984cb The Courier outbox shows each message and pans instead of cutting
Each row now carries a third column, MESSAGE, after TARGET and TRIGGER
(or OUTCOME). Nothing is shortened to fit: the rows and their headers pan
together with ←→, and the footer says so while the table runs past the
edge. The outbox no longer filters as you type, which keeps the footer
within 72 cells.

A column header that pans now pins with its section heading, as a
header that does not pan always did.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-22 00:52:40 -04:00
Jean-Pierre MartineauandClaude Opus 5.5 0b931e7cc4 The Courier outbox is a TARGET and TRIGGER table, and Enter opens the message
Each section of the outbox now has column headers: TARGET and TRIGGER for
waiting entries, TARGET and OUTCOME for finished ones. The rows no longer
show the message. Enter on a waiting entry opens it to edit (the courier
holds the entry while the prompt is open, so the old text cannot go out
meanwhile), and Enter on a finished one shows it. Del deletes an entry
after a red confirm, and ^R sends a waiting entry now; on the PicoCalc
they ride F3 as Send, with Delete behind it. On a narrow screen a long
name gives cells to the trigger and breaks off on its crack.

The message field has the colours of the chat's compose line: a white
prompt mark and white text.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-22 00:22:05 -04:00
Jean-Pierre MartineauandClaude Opus 5.5 1b30f353c6 A cut in the text beside a chip ends on an ellipsis, not on a stray crack
When a row was a chip followed by text (a Courier entry: the recipient,
then what it waits for) and the text ran past the screen, the cut looked
back for a chip fill, found the chip far to the left, and drew a half
block in its colour at the right edge. The crack now forms only inside
the path that the row stamps, so a cut in plain text gets the ellipsis.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-22 00:22:04 -04:00
Jean-Pierre MartineauandClaude Opus 5.5 284c2718c6 MeshTerm 0.11.4
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-20 00:24:12 -04:00
Jean-Pierre MartineauandClaude Opus 5.5 8f3f07276b Rewrap a docstring line that ruff found too long
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-20 00:01:45 -04:00
Jean-Pierre MartineauandClaude Haiku 5.5 8dff5bbb29 The selected chat message whitens its author chip and its unknown mentions
The author chip of the selected message's group now turns its label white,
as the message's text does, and goes back when the selection leaves the
group. A mention of an unknown node turns white too: its grey fill already
says the node is unknown, and white reads better on it than black.

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
2026-10-19 23:59:45 -04:00
Jean-Pierre MartineauandClaude Haiku 5.5 7264a71104 The mention chips of a selected chat message go white with its text
In a channel, a DM, or a room, the selected message's text is white, and
now its mention chips follow: the label turns white and the fill stays, so
the chips still tell one node from the next. Arrow mode whitens the label
the same way. A mention that no key can place keeps its grey, as on every
selected row.

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
2026-10-19 23:48:59 -04:00
Jean-Pierre MartineauandClaude Haiku 5.5 59273a6b29 A locked author's sender chip carries the padlock, as ours carries the star
In a channel, a DM, or a room, the chip of a sender that is a locked
contact reads 🔒 Name (⚿ on the PicoCalc), in the place of the ★ on our
own chip. The transcript cache now compares the locks, so a ^K or a lock
from the node page redraws the chips on the next paint.

The padlock is one constant, marks.LOCK_ICON, shared with the Contacts list.

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
2026-10-19 23:40:32 -04:00
Jean-Pierre MartineauandClaude Haiku 5.5 793bc26436 ^K on the Contacts list locks or unlocks the highlighted contact
The same key as in a conversation. The row gains or loses its padlock in
place, with no confirm, and the next ^K reverses it. On the handhelds, Lock
(or Unlock) rides Shift+F2, as in a channel and a DM.

The hint names ^K only where it fits without dropping another atom. That
rule now lives in one helper, menus.splice_if_room, which the archive
preview uses too.

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
2026-10-19 23:27:05 -04:00
Jean-Pierre MartineauandClaude Haiku 5.5 daa26be5df ^K in a conversation locks the author of the selected message
In a channel, a DM, or a room, ^K toggles the lock of the contact that wrote
the selected message, so the archive sweep never takes it. The hint says
^K lock or ^K unlock, but only where it fits without dropping another atom,
and the status line confirms what the key did.

On the handhelds, Lock (or Unlock) rides Shift+F2 in a channel and a DM. A
room keeps Log in there, so the Author page that a conversation opens now
carries the lock row, and no other management verb.

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
2026-10-19 23:18:57 -04:00
Jean-Pierre MartineauandClaude Haiku 5.5 60a2df727a Shift+Delete in the archive preview locks a contact as well as sparing it
Delete keeps a contact out of this sweep only. Shift+Delete removes the row
the same way and stages a lock, so no later sweep takes the contact. The
locks are written on Apply with the archive; Esc and Back ask before they
discard them. A preview left with only locks applies them without the amber
confirm.

The footer names ⇧Del lock only when the hint still fits 72 cells, and a
contact row now says Enter open, since Enter opens its node page. On the
handhelds, Lock rides Shift+F3 behind the Delete chip.

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
2026-10-19 18:14:39 -04:00
Jean-Pierre MartineauandClaude Opus 5.5 87bf929ac4 The Cardputer's battery gauge stays on through the Cap's noisy bus
On battery with the Cap on, 79% of the gauge reads failed, in groups of
up to 110 s. The poll read 3 times in 11 s and then waited 20 s, so it
missed the good periods, and after 7 failed polls the gauge went away.

- After a failed read, the next read comes after 5.5 s, not 20 s.
- The last good reading stays for 10 minutes instead of 7 polls.
- A change of the charging flag waits for a second read that agrees,
  because a garbled read said Charging while the pack discharged.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-19 03:36:55 -04:00
Jean-Pierre MartineauandClaude Opus 5.5 bdc60481ec A serial link that dies while its port stays listed opens the reconnect dialog
When a laptop wakes from sleep, the kernel can reset the USB device and hang
up the open port, but keep the same device file. The next write fails with
EIO, and the serial transport closes itself. The serial liveness check read
only the OS port list, so it said the link was up: no reconnect dialog
opened, and each command failed with no_event_received (issue #32).

The serial check now also reads the meshcore client's is_connected flag,
which the client clears when its transport is lost, as the BLE and TCP
checks already did.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-19 00:42:19 -04:00
Jean-Pierre MartineauandClaude Opus 5.5 9d7c5340b5 MeshTerm 0.11.3
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-18 01:57:10 -04:00
Jean-Pierre MartineauandClaude Opus 5.5 1d983013d7 No log keeps a password or a secret: the log file, the run log, or the remote history
Passwords, channel secrets, pairing PINs, and private keys reached the DEBUG log and
the runs table in plain text: every chat run stored its channel secret inside the
Conversation repr, and --password, --secret, channel links, and config set device_pin
were stored as typed. meshterm.core.secrets now hides each one as <secret> by its
shape (a secret-named key, record, set operation, or dataclass field; bytes; a Secret;
a remote `password`/`set guest.password` command; name=value in text), and by value
for each secret MeshTerm has read or sent. Both log handlers and the runs table go
through it, always. The remote CLI history keeps `password ` without its value. Schema
v20 hides the secrets that older versions stored.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-18 00:20:22 -04:00
Jean-Pierre MartineauandClaude Haiku 5.5 9e579424b4 The Anonymize preference masks the home folder and addresses in the log and diagnostics
Off by default. Each masked value keeps a tidbit (j*********u, DA:xx:xx:xx:7C:7C,
192.xxx.x.180, m******e.local), so two radios stay two radios. The log masks each
line as it is written; diagnostics take --anonymize/--no-anonymize, and the page
toggles with `a` (Shift+F3 on the PicoCalc). Refs #31.

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
2026-10-17 21:30:26 -04:00
Jean-Pierre MartineauandClaude Opus 5.5 fc58c58c09 The demo mesh uses the regions of the real mesh: na, can, onqc, qc, and yul
Each region is in the one before it, as on the real mesh: na > can > onqc > qc > yul. Every repeater of Montréal, Laval, and the south shore carries the five, and Mont-St-Hilaire, outside the city, has yul but does not relay it. The #montreal channel sends in yul instead of mtl. The invented regions (mtl, mtl-west, laval, south-shore, and the others) are gone.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-17 18:23:30 -04:00
Jean-Pierre MartineauandClaude Opus 5.5 b278ccfdc1 In the demo, the TX power of a repeater changes how well it is heard
TX optimize had nothing to find in the demo: the demo device answered each trace from the world's links and ignored the power that the sweep set on a repeater. Now a relay that TX optimize tuned is heard less well on the link that it transmits into, on the simulator's curve with its peak at 19 dBm, and a reading under the decode floor loses the trace. The repeaters of the world read 22 dBm, so the sweep finds a lower power with a better signal. A relay that nobody tuned keeps the readings of the world.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-17 17:01:01 -04:00
Jean-Pierre MartineauandClaude Opus 5.5 507e3bd999 In the demo, a node that we mention answers, and our node uses three-byte paths
A channel message of our node that mentions Dave gets his answer a few seconds later, and the answer mentions our node. The answers are a table in the demo script. Our node uses three bytes for each hop, as most of the mesh does, so a trace uses two.

The video's tour now selects the two paths of a message in turn, types a question to Dave so that the mention becomes a chip, and shows his answer with our star. The trace shows its new records, and the map zooms from the city to the streets around our node.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-17 09:51:56 -04:00
Jean-Pierre MartineauandClaude Opus 5.5 37b17d8ba4 The demo's message paths look like a real mesh
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>
2026-10-16 04:30:43 -04:00
Jean-Pierre MartineauandClaude Opus 5.5 5d2648d652 MeshTerm 0.11.2
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-16 03:46:15 -04:00
Jean-Pierre MartineauandClaude Opus 5.5 51c8b69f49 Screenshots for the website, the same at each run
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>
2026-10-16 03:18:32 -04:00
Jean-Pierre MartineauandClaude Opus 5.5 058effe42d The demo talks like anyone, in English
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>
2026-10-16 01:45:14 -04:00
Jean-Pierre MartineauandClaude Opus 5.5 4397bd58ec The demo's now is 11:42
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>
2026-10-16 01:25:07 -04:00
Jean-Pierre MartineauandClaude Opus 5.5 d81f3c8618 A demo mesh over Montréal, for screenshots that stay the same
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>
2026-10-16 00:38:47 -04:00
Jean-Pierre MartineauandClaude Haiku 5.5 eb2d314551 Section headings carry no icon
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>
2026-10-15 23:56:36 -04:00
Jean-Pierre MartineauandClaude Haiku 5.5 820740c9a0 A blank line before each section is one preference, app-wide
"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>
2026-10-15 22:28:23 -04:00
rich c5a2fb3746 Merge branch 'long-messages'
CI / lint (push) Canceled after 0s
CI / test macos-latest py3.14 (push) Canceled after 0s
CI / test ubuntu-24.04 py3.10 (push) Canceled after 0s
CI / test ubuntu-latest py3.11 (push) Canceled after 0s
CI / test ubuntu-latest py3.12 (push) Canceled after 0s
CI / test ubuntu-latest py3.13 (push) Canceled after 0s
CI / test ubuntu-latest py3.14 (push) Canceled after 0s
CI / test windows-latest py3.10 (push) Canceled after 0s
CI / test windows-latest py3.14 (push) Canceled after 0s
CI / build (push) Canceled after 0s
Issue trailers / check (push) Canceled after 0s
# Conflicts:
#	docs/development/writing-style.md
#	docs/guide/features.md
#	meshterm/core/preferences.py
#	meshterm/ui/chat.py
2026-10-15 21:30:10 -04:00
richandClaude f7f0036d0f Send a long chat message as numbered parts
A message that is too long for one packet now goes out as up to
message_parts_max parts (default 3, 1 turns it off), each ending with
its part number: [1/3], [2/3], [3/3]. Each part, with its part number,
fits in the byte limit of the conversation (130 for a channel, 150 for
a direct message). A cut goes at a space, and a word with no space to
cut at is cut between characters, never inside an emoji sequence or
after a combining accent.

Channel parts wait the transmit cooldown between them, so that the
floods do not go out as a burst. A direct part waits for the
acknowledgement of the part before it, or the end of its wait. An
unacknowledged part still lets the rest go out, and ^R retries each
one. A hard failure stops the rest and puts their text back in the
compose line. The byte gauge shows the number of parts and the bytes
of the last part. Rooms are not changed.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-15 21:26:49 -04:00
richandClaude Opus 5.5 d9426166c8 Search every emoji by name in the reaction dialog
CI / lint (push) Canceled after 0s
CI / test macos-latest py3.14 (push) Canceled after 0s
CI / test ubuntu-24.04 py3.10 (push) Canceled after 0s
CI / test ubuntu-latest py3.11 (push) Canceled after 0s
CI / test ubuntu-latest py3.12 (push) Canceled after 0s
CI / test ubuntu-latest py3.13 (push) Canceled after 0s
CI / test ubuntu-latest py3.14 (push) Canceled after 0s
CI / test windows-latest py3.10 (push) Canceled after 0s
CI / test windows-latest py3.14 (push) Canceled after 0s
CI / build (push) Canceled after 0s
Issue trailers / check (push) Canceled after 0s
^E still lists the reaction_emoji preference first, under "Your reactions". Under
"All emoji" it now lists each other emoji with its Unicode name, and the list
filters as you type: "fire" finds 🔥, "heart" finds the hearts. The catalog comes
from the Unicode database of the Python that runs MeshTerm and from Rich's table of
text-default emoji, so it needs no data file.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 07:47:13 -04:00
richandClaude Opus 5.5 f3aa8e5aef Allow channel sender names of up to 31 characters
The firmware keeps a node name in 32 bytes, but the sender prefix and the @[Name]
mention matched only 20 characters. A longer name stayed in the body: the transcript
showed no sender for it, and its reactions showed as new messages. Found on hardware
with "Hazelwood r0b3.Anduril" (22 characters).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 07:47:13 -04:00
richandClaude Haiku 5.5 a323342971 Add emoji reactions in the wire format of MeshCore One
Select a message in a channel or a DM and press ^E (Shift+F2 on the handhelds) to
react with an emoji. A reaction is a usual message on the wire: MeshTerm writes the
format of MeshCore One and reads it and the two formats of meshcore-open. The
transcript folds each reaction under the message that it names, with a count, or
the names of the nodes while that message is selected. A reaction does not raise
the unread badge, and the Messages list previews it as "reacted 👍".

A reaction names its target by the sender's timestamp. Thus MeshTerm now gives the
device the timestamp of each message it sends and stores exactly that second. A
retry of a DM keeps the timestamp and counts the try number, as the protocol
specifies. The emoji that ^E offers are the reaction_emoji preference. Rooms have
no reactions.

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
2026-10-15 07:47:13 -04:00
Jean-Pierre Martineau b54a93e5c4 Revert "Section headings are a creamy yellow"
This reverts commit 963cb2ec7d.
2026-10-15 03:06:13 -04:00
Jean-Pierre MartineauandClaude Opus 5.5 963cb2ec7d Section headings are a creamy yellow
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>
2026-10-15 02:53:15 -04:00
Jean-Pierre MartineauandClaude Opus 5.5 e0e8607639 Section headings take the frame title's colour
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>
2026-10-15 00:43:53 -04:00
Jean-Pierre MartineauandClaude Opus 5.5 30329e62a1 The border is #8f8f8f
#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>
2026-10-15 00:40:35 -04:00
Jean-Pierre MartineauandClaude Opus 5.5 eeafbb18f6 Node marks are white, and the border is darker
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>
2026-10-15 00:30:12 -04:00
Jean-Pierre MartineauandClaude Opus 5.5 45697db856 Borders are grey
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>
2026-10-14 23:21:24 -04:00
Jean-Pierre MartineauandClaude Haiku 5.5 05428bd007 Every grey is neutral
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>
2026-10-14 23:08:27 -04:00
Jean-Pierre MartineauandClaude Opus 5.5 b5fc1f8930 The docs name both Cardputer Zero models and warn against hot-swapping a Cap
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>
2026-10-14 02:59:46 -04:00
Jean-Pierre MartineauandClaude Opus 5.5 7964f20889 Revert "A faded chip keeps its node's colours, with less chroma"
This reverts commit bd6df0b.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-14 00:55:11 -04:00
Jean-Pierre MartineauandClaude Opus 5.5 bd6df0b9fa A faded chip keeps its node's colours, with less chroma
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>
2026-10-14 00:44:17 -04:00
Jean-Pierre MartineauandClaude Opus 5.5 796ff94dad Our star is white
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>
2026-10-14 00:18:59 -04:00
Jean-Pierre MartineauandClaude Opus 5.5 feea5a89db In a chat, our chip is white on our fill and unknown names are black
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>
2026-10-14 00:06:28 -04:00
Jean-Pierre MartineauandClaude Opus 5.5 b680b172d7 Our star takes the hue of our key, and unknown chat names go grey
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>
2026-10-13 23:46:39 -04:00
Jean-Pierre MartineauandClaude Opus 5.5 3fdc638812 In a chat, a star in our hue marks a message that names us
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>
2026-10-13 23:23:06 -04:00
Jean-Pierre MartineauandClaude Opus 5.5 691170d342 In a chat, the point of our chip alone marks a message that names us
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>
2026-10-13 23:05:12 -04:00