The label column is the widest tool title plus two cells of gap, which
leaves the muted description 47 to work in. All 22 of them ran past it —
"Act on the device itself — clock, backup, identity key, reboot, reset"
needed 94 columns of terminal to say what 67 could. The row scrolled
under ←→ rather than lying, but a description you have to scroll is a
description nobody reads.
Rewritten to fit: the subject moves into the title's shadow (the row
already says Device actions, so the description just lists them), the
parenthetical asides go, and the trailing clause that repeated the
obvious goes with them. Widest row is now exactly 72.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The menu asks "What would you like to do?" and then grouped its rows by
subject, which is not an answer to that. Mesh had grown to seven of the
nineteen rows — live watching, passive browsing, and one transmit action
in the same bucket — and Tools said nothing at all, since everything in
the menu is a tool.
So each section now names a doing, and holds what shares it:
Message Chat · Channels · Courier · Contacts
Watch Dashboard · Live feed · Watchtower · Time machine
Explore Map · Mesh walk · Trace target · Trace path · Trophy case
This node Device info · Device configuration · Device actions · Send advert
Other nodes Repeater admin · TX optimize
Four rows moved for a reason. Contacts is the address book the three
features above it pick a recipient from, not a mesh view. Time machine is
the past tense of the Dashboard, and was stranded in the grab-bag. Send
advert is your own node announcing itself — a device action, filed with
the mesh views only because it was about the mesh. And Repeater admin and
TX optimize are the same job — someone else's radio, over the mesh, with
credentials — which the old Device-management/Tools split hid.
The two walks join the topology views they walk over, with the boards
that score them underneath: see a node, walk to it, get scored. Nothing
is renamed (a menu row still names the screen it opens) and no section is
now larger than five rows or smaller than two.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A firmware-less radio bridge holds its channels only in RAM, so every one is
lost when the bridge restarts — the Channels page came up empty each session
even after you added channels. MeshTerm read channels live from the device and
never remembered them.
Add a durable, per-device channel store (<config_dir>/channels.json, keyed by
the device's public key) that records every channel written through MeshTerm.
On connect, reconcile() replays any remembered channel the device isn't
reporting into a free slot — filling gaps, never overwriting an occupied slot.
The gate is provenance, not device type: only channels you wrote through
MeshTerm are recorded, so a firmware radio you never edit through MeshTerm keeps
an empty record and does a single identity probe on connect (untouched). A
bridge, where every channel is necessarily created through MeshTerm, gets its
whole set back. Every mutation (create/join/import/edit/reorder/clear) now flows
through a single write_channel() boundary that keeps the store faithful.
Verified end to end against the live SPI bridge: write a channel, clear it off
the device to mimic a restart, reconnect in a fresh session, and reconcile
restores it with the right key.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add TCP alongside serial and BLE as a first-class companion transport, end to
end: discovery/connection/selection learn a tcp: stable_id and host:port target,
--tcp and TCP config profiles reach a WiFi board or network proxy, and a network
companion -- which isn't discoverable -- is rebuilt into the picker from its
remembered endpoint.
Device picker gains the network-device affordances:
- "Add a network device" prompts for host:port, smoke-tests it, and remembers it.
The row is marked "- experimental" in the cautionary hue.
- Delete on a network row opens a chromeless Cancel/Remove confirm (Cancel left,
Remove right and default: Enter removes, Esc cancels), then DeviceStore.forget
prunes it and re-draws the list. Serial/BLE rows stay inert.
- The "Del remove" footer atom now follows the highlight, surfacing on the box's
bottom border only while the cursor sits on a deletable row; the box is sized
to the fullest footer so it never widens mid-navigation.
Folds in the accompanying courier, channels, and config refinements built along
the way. Screens stay readable at 72 cols; full suite green (745 tests).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Four small UX gaps closed together:
- Dashboard's footer hint was missing Home/End even though the screen
already handled them.
- The packet viewer's paging keys were reversed relative to the list
that opens it: PgUp/PgDn walked packets while the dashboard feed
walks with up/down. Swapped so up/down page through packets (matching
the feed) and PgUp/PgDn scroll a tall body (matching every other
screen's scroll keys).
- A raw "packet" entry (an overheard RX_LOG_DATA frame) now shows its
parsed payload class and route, and — for a channel-text frame naming
a channel we hold the key for — its decrypted text. The AES/HMAC
logic lives in core/channels.py as pure, unit-tested crypto
(decrypt_channel_text), independent of the vendored meshcore
library's own reader internals; the dashboard fetches the device's
channel secrets once at open time and hands them down to each
PacketViewer it opens. Raw-field dumps now skip fields already folded
into a flavoured row instead of repeating them.
- The Time Machine's whole-mesh charts (packets/day, nodes/day, rhythm)
gained the same mirrored numeric y-axis the dashboard's activity
chart already had. That gutter-drawing logic moved into
braillechart.axis_chart, a shared widget, and the dashboard's own
activity chart was refactored to use it too instead of hand-rolling
its own copy.
meshterm/ui/channels.py's _read_slots is now public (read_channel_slots)
since the dashboard opener needs it too.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Chat, Channels, and Courier move into their own Messaging section at
the top; Mesh keeps the views over the mesh itself - Dashboard, Nodes,
Map, Atlas, Watchtower, Send advert - dropping from nine rows to six.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Messaging becomes Mesh and takes in Nodes (between Channels and Map —
the "who's out there" view) and Send advert (last in section: its one
action, after its views), so everything mesh-wide lives under one
heading. Device becomes Device management and keeps the box-level
tools; Diagnostics becomes Tools.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Tools gain an icon attribute — menu-only, so result windows and the
CLI keep the plain title — and the menu loop composes it into the
aligned name lane. Single-codepoint, emoji-presentation glyphs only,
so the two-cell width holds across terminals: 💬 Chat, 📻 Channels,
🌍 Map, 👥 Nodes, 📡 Send advert, 🔧 configuration, 🔨 actions,
📋 info, 🎯/👣 the traces, 🎧 Monitor, 📶 TX optimize, 🚪 Quit.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Enforce one voice across every screen: column and section headers are
UPPERCASE, menu items are Capitalized buttons, and short descriptions
drop their trailing period (with real ellipses instead of "...").
Tools gain a display `title` separate from their kebab-case CLI name,
so a menu item now reads exactly like the screen it opens ("Device
configuration" opens Device configuration) and result windows inherit
the same name. Every sub-screen backs out via a "Back" row below a
blank separator (the config editor was missing both; its Close is now
Back, spelling out the discard when changes are staged), while the main
menu and device picker keep "Quit". Panel and dialog titles now wear a
brighter tint of their border colour via new title.* theme styles.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Rename the package directory, imports, brand strings, env-var prefixes,
console-script entry point, and the config/data dir (~/.config/meshterm).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>