Commit Graph
6 Commits
Author SHA1 Message Date
Jean-Pierre MartineauandClaude Opus 4.8 733a68857c Contacts: rename the mislabeled Nodes feature; sharpen node vs contact
The "Nodes" menu was a contacts roster mislabeled — it lists the device's
contacts (get_contacts), which upstream meshcore calls "contact" too. Rename
throughout, no CLI alias:

- tools/nodes.py -> tools/contacts.py (ContactsTool, `meshterm contacts`)
- ui/nodes_screen.py -> ui/contacts_screen.py (ContactsScreen/open_contacts)
- ui/nodelist.py -> ui/contactlist.py (ContactListScreen/ContactRow)
- widgets: NodesSort->ContactsSort, nodes_table->contacts_table

Courier's recipient is a contact you address, so its prose and CLI arg
(`courier queue CONTACT ...`) follow; persisted node_key/node_name stay.

The reception/hardware layer keeps "node" — HeardNode, heard_nodes(),
observations.node, trace_hops.node, map/atlas participants, node types, admin
targets, and the Time Machine's reception picker (its "Nodes" heading stands).
CLAUDE.md's lexicon is rewritten to the refined definitions: node = any mesh
radio; contact = a node your device knows (discovered or added).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-19 11:42:11 -04:00
Jean-Pierre MartineauandClaude Fable 5 93c1937de7 Courier recipient picker: the shared node list, Enter commits
The queueing flow's inline glyph rows give way to the app's one node
list: full NAME/HEARD/PKTS/KEY lanes, key-hued names, recency-heat
ages, PKTS from the recorder's overheard tallies, and the same
Ctrl+arrow sort ring and type-to-filter as the Nodes screen and the
Time Machine picker. It opens on the heard column freshest-first (the
old fixed order, now just a default), name sorts A→Z, and Enter
resolves the highlighted Contact into the flow; the footer spells the
grammar out with Esc cancelling the step.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-19 01:13:34 -04:00
Jean-Pierre MartineauandClaude Fable 5 98aa6ef7dc Courier outbox: live rows, live sections
The outbox used to freeze at whatever it showed when it opened —
"retry in ~4 m" would still read 4 m ten minutes later, and a delivery
only moved to Finished after some unrelated keypress rebuilt the list.
Entry rows become callable titles recomputed on every repaint (the 1 s
app baseline), so countdowns and ages tick for real; a new
CourierOutboxScreen fingerprints the store's shape once a second from
an opener ticker and recomposes the sections in place only when an
entry appeared, finished, or was cleared — keeping the highlight on
its entry, the node-list rebuild idiom. No store callback needed: the
courier's own pass cadence is 30 s, so a 1 s shape check lands every
transition promptly.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-19 01:11:51 -04:00
Jean-Pierre MartineauandClaude Opus 4.8 583be7c4f7 Compose fields: share one UTF-8 byte gauge, and put it on the courier entry
Lift the chat compose bar's byte budget into a reusable gauge and reuse it
wherever a field feeds a size-capped LoRa packet.

- prompt.py gains byte_counter/byte_style and the DM/CHANNEL byte limits, plus
  a byte_limit option on TextScreen: a field so limited rides the shared
  used/limit gauge (right-pinned, green→yellow→orange→red) and blocks Enter
  once the text tops the cap, with a "Too long by n bytes" note. Ui.text and
  session.text thread byte_limit through.
- chat.py drops its private copies and calls the shared byte_counter; the
  limits now come from prompt.py.
- The courier's "Message for …" entry passes byte_limit=DM_BYTE_LIMIT so a
  queued message is gauged and capped the same way a live chat message is.

Also fixes a courier scheduling bug uncovered here: the "When it's next heard"
row carried the value None, indistinguishable from session.select's own cancel
sentinel — backing out of the picker silently queued the message. It now
carries its own WHEN_HEARD sentinel, so Esc cancels and the row means no
schedule.

Full suite green (806).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 21:05:23 -04:00
Jean-Pierre MartineauandClaude Opus 4.8 965783d276 Network (TCP) companions: connect, remember, and manage from the picker
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>
2026-07-14 02:38:11 -04:00
Jean-Pierre MartineauandClaude Fable 5 d75a9aa2db Courier: a store-and-forward outbox that delivers when the node shows up
Queue a direct message for a node that isn't reachable right now and a
session-long background loop delivers it when the recipient is next
heard — or at a scheduled local time ("next 07:00", "in 3 h", any
HH:MM). Each attempt is an ordinary chat send (it lands in the
conversation history with its ack state, exactly as if typed); failures
back off exponentially and, after the first shot, wait until the node
is heard again, so an absent node is never hammered on faith. One
attempt per pass keeps a backlog draining politely, and after five
unacknowledged tries the courier gives up and says so. Outcomes raise
Watchtower alerts, so the header's badge lights when an overnight
delivery finally lands.

The outbox persists in courier.json (a queued message must survive
restarts or the promise is hollow), a Mesh-section screen carries the
queueing flow, per-entry Send now / Cancel, and the finished history,
and a scripted `courier <node> <text…> [--at HH:MM]` queues without
transmitting anything.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-11 05:52:51 -04:00