Commit Graph
687 Commits
Author SHA1 Message Date
Jean-Pierre MartineauandClaude Opus 5.5 c3c18b1c85 Formatting for the map's memory budget and its tests
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-04 17:16:08 -04:00
Jean-Pierre MartineauandClaude Opus 5.5 db6370407a A map moved faster than it can draw no longer freezes the PicoCalc
Panning or zooming the map quickly on the PicoCalc could stop the whole app
for many seconds. A watchdog run on the device found two causes behind it,
plus the port walk fixed in the previous commit.

Memory. A decoded tile holds 1 to 4.6 MB of RAM on the device (65 bytes a
point), not the 0.9 MB the code assumed. The source kept up to 24 of them,
counted as tiles, and the map screen kept 8 or more of its own on top, so a
few pans and a zoom out filled a 104 MB device. Swap is on the SD card, so
the process then stopped while the kernel read its code back in: 16,000
major page faults and a 15 s freeze in one run. Now the source holds decoded
tiles to a byte budget, one eighth of the machine's RAM (12.8 MB on the
PicoCalc, at most 128 MB on a desktop), weighed by mvt.resident_bytes. That
estimate was fitted against tracemalloc on the device's 157 cached tiles: 1%
under to 9% over there, and 6% under to 21% over on a 64-bit desktop. The
screen keeps only the tiles in view, and takes any tile already in RAM
straight from the source on the paint (BasemapSource.resident), with no trip
through a thread. The prefetcher warms the disk cache (BasemapSource.warm)
instead of filling the RAM with guesses, and passes over any tile already
written down. Tile loads take turns, two at a time, and a tile the view has
left before its turn comes is dropped, not loaded.

CPU. fill_polygon asked every edge about every scanline, which at z7 means
coastlines with tens of thousands of edges: 6.8 s of a 7.6 s frame. Each
edge is now filed under only the rows it crosses, and draws the same dots
(checked against the old fill on 3,000 random polygons, and in a test).
_draw_tile also tests each ring's bounds before projecting it, and skips a
ring wholly outside the view. That changes no dot: 1,500 frames from the
device's tiles match rendering with no culling at all. The one frame that
differed from HEAD was HEAD's own building cull, which dropped a footprint
whose fill rounds onto the canvas edge.

Measured on the PicoCalc with the profile-picocalc map_burst tour (keys sent
at 10 a second, from a pinned z10 view, real radio and database). The new
code ran first, so the baseline had the warmer tile cache:

  pans, catch-up after a burst     HEAD 1.0-1.8 s      now 0.4-0.6 s
  longest silence, whole tour      HEAD 11.6 s         now 0.6 s
  whole-process freezes            HEAD 8.0, 3.1, 2.1, 11.6 s
                                   now one, 1.6 s (idle pages swapped back in)
  swap at its peak                 HEAD 25 MB          now 7 MB
  warm raster, z7 / z8 / z9        HEAD 7.6 / 1.9 / 1.8 s
                                   now 0.57 / 0.29 / 0.81 s
  warm raster, z10 / z13           HEAD 0.49 / 0.56 s  now 0.26 / 0.26 s

Still open: z9 spends 0.23 s drawing lines; a prefetch decode of a heavy z7
tile is still 1.3 to 1.7 s on a worker thread; and pages swapped out while
the map sits idle can still cost a second or two to come back.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-04 17:15:19 -04:00
Jean-Pierre MartineauandClaude Opus 5.5 3095e3b236 Every page with a highlight edge-scrolls, with no code of its own
Edge scroll reached only the eight screens that answered cursor_at_edge, one
gate each; the trace screen, the autocomplete prompt and anything built
tomorrow went without. Now no screen says where its edges are. An arrow goes
to the screen like any key, and the next paint reads the answer off the page:
a highlight on the same line, drawn the same, moved nothing, so the page
takes the line instead. Comparing the drawn line, not just its index, keeps a
list that scrolls in its own window from looking stuck. Arrows pressed faster
than the screen paints are judged together. All a screen supplies is its
highlight's line in cursor_line, which keeping it in view asked for already.
The eight gates and row_at_edge are gone.

The snap-back's arrow is now the one pointing at the off-screen highlight,
read from where the paint left it, so it no longer needs to know which edge
the page went past. Home and End take the page to its ends after the screen
has handled them, without assuming the highlight followed.

The remote CLI opts out (edge_scrolls = False): the line it keeps in view is
its prompt, and its arrows recall history. The autocomplete prompt now names
its highlighted suggestion, so a short box follows it; an empty select list's
"no matches" line stands in for the highlight; and the reorder list's Home
and End jump to its first and last rows, as on every list.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-04 17:15:15 -04:00
Jean-Pierre MartineauandClaude Opus 5.5 bdbf65312a The serial link check walks the ports off the event loop
Every two seconds the liveness watcher asks whether the companion's serial
port is still there, and that question is a walk over /sys/class/tty: about
30 ms of small reads on an idle PicoCalc. It ran on the event loop. Each read
hands the GIL back and has to wait its turn to take it again, and while a
worker thread is busy (a map frame being drawn, a tile being decoded) that
turn is up to 5 ms away, hundreds of times over. A freeze watchdog on the
device caught the main thread inside this walk in 20 of the 21 stack samples
taken while the map was stalled, for up to 1.2 s at a time.

It now runs in asyncio.to_thread, as the device picker's and the reconnect
dialog's walks already did; the worker pays the wait and the screen doesn't.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-04 17:15:03 -04:00
Jean-Pierre MartineauandClaude Opus 5.5 61d7463ddf A connection that won't open says why, wherever it fails
The September messages reached the device picker and the command line, but
three other places still lost them. The reconnect dialog retried every
failure in silence, so a device that came back and refused (its port taken
by the MeshCore app, a stale pairing) kept the spinner up forever; from the
second failure in a row it now shows the reason under the spinner, and goes
back to waiting if the device is unplugged again. A device named on the
command line skipped the picker, and a failed start was dropped unlogged; it
is now shown before the menu opens. And a failed open left the dead device
behind, so is_connected said yes and every later tool failed with "not
connected": it is dropped, and the next tool tries again and says why.

TCP failures are named one by one (host not found, nothing on the port, no
route, no answer, hung up) instead of all reading "no response from a
MeshCore companion". A failure MeshTerm has no name for is no longer called
"not a MeshCore device": it is said in its own words, its traceback is
logged once, and the dialog says where the log file is. The tool-failure
dialog stops reading an error's brackets as markup, which had silently
deleted "[org.bluez.Error.Failed]" from Linux Bluetooth errors.

docs/guide/connecting.md lists the TCP messages and where each message
appears.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-04 14:43:53 -04:00
Jean-Pierre MartineauandClaude Opus 5.5 58909d1373 meshterm emulate shows either handheld's display in a window
`meshterm emulate cardputer-zero --mock` and `meshterm emulate picocalc-lyra --mock`
open a desktop window drawing MeshTerm as that device's own screen draws it. An
EmulatedDevice (emulator/devices.py) holds what differs per handheld: the panel, a
Linux console's top-left grid and bold-as-bright 4-bit palette for the PicoCalc, the
lane keys drawn under the Cardputer's panel, and how each keyboard spells Shift with
an F-key (the PicoCalc's F6-F10, the Cardputer's xterm-shifted F4-F8).

It is not a strict emulator, and the docs say so wherever they offer it: MeshTerm runs
on the desktop as itself, and the window mimics the display's and keyboard's
constraints, never the CPU or the OS. Inside it the platform resolves with own_display
(platforms.drawn_by_meshterm), so nothing offers to move to another terminal.

Global options typed anywhere (--mock, --port, --db...) reach the emulated MeshTerm.
The command refuses --json like specimen. The Terminus fetch moved into the package as
`meshterm emulate --fetch-fonts` (with --archive for a copy fetched another way),
replacing scripts/cardputer-zero/fetch-terminus.py. A build without Tk says to install
with pip or pipx instead of failing.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-04 14:19:22 -04:00
Jean-Pierre MartineauandClaude Opus 5.5 50ea38faea The console host is meshterm.emulator
"Host" already meant two things here: a TCP companion's network host,
and battery="host" for this machine itself. And with a PicoCalc preview
coming, the package is less a console host than an emulator of a
handheld's display: its panel, grid, font, colours and keys. So it is
meshterm.emulator, Platform.console_host is own_display, its desktop
window is window.py (EmulatorWindow) and its device front end
framebuffer.py, since "sim" and "device" now mean the radio simulator and
the device being emulated. MESHTERM_HOST_FONTS is MESHTERM_EMULATOR_FONTS.

The package's own description opens on what it is not: nothing runs the
handheld's processor or operating system; it mimics the display's
constraints so the window obeys the device's.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-04 14:03:55 -04:00
Jean-Pierre MartineauandClaude Opus 5.5 8b742ea270 The device picker's rescan tests wait for scans, not for 50 ms
The fake splash ran the rescan for a fixed 50 ms and assumed several
rounds would fit. On Windows a 1 ms sleep lasts a whole ~15 ms timer tick
and each scan goes through a worker thread, so under a full run the
window sometimes held a single round and "a device going away" never got
its second scan. The fake now runs until the script's rounds, and a few
more, have actually been scanned, with a 10 s deadline that fails with
its own message rather than as a missing redraw.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-04 13:27:25 -04:00
Jean-Pierre MartineauandClaude Opus 5.5 b6e7cd5c16 The handhelds are named for their variant: picocalc-lyra and cardputer-zero
M5Stack makes three Cardputers and only the Zero runs Linux; the PicoCalc
takes several cores and only the Luckfox Lyra runs MeshTerm. So the bare
names were ambiguous, and every reference to the two devices now carries
the variant: the --platform values and MESHTERM_PLATFORM, the Platform
constants (PICOCALC_LYRA, CARDPUTER_ZERO), their lane decks and every
screen's picocalc_lyra_lane / cardputer_zero_lane, the font inventories,
the lane chips' theme styles, the scripts/ folders, the two chrome test
files, and the docs and CLAUDE.md that name them.

There is no alias: --platform picocalc is refused with the three real
choices listed, rather than falling back to the desktop layout. The
PicoCalc is auto-detected from its device tree, so it needs nothing.
What names hardware or a user's data keeps its name: the PicoCalc
keyboard's input-device name, its battery's sysfs supply, and the serial
profile the Lyra setup script writes. .gitignore follows the firmware's
folder, and the changelog keeps the words each release used.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-04 11:52:41 -04:00
Jean-Pierre MartineauandClaude Opus 5.5 22398128f5 An SPI radio's GPIO chip is found by its label, not trusted by its number
The AIO's pins sit on whichever /dev/gpiochip carries the Raspberry Pi's
40-pin header, and that number is no fact about the board: chip 0 on a
uConsole's CM4, chip 4 on a CM5 under its early kernels, chip 0 once
Raspberry Pi aliased the RP1 there, and another number again on some
later kernels. gpio_chip now defaults to -1, which asks each chip for its
label and takes the one called pinctrl-rp1, pinctrl-bcm2711 or
pinctrl-bcm2835, falling back to chip 0 where none is; a number in the
profile still pins it. The chip is settled once before the node starts,
so the node, every error and the lsof hint all name the same one.

The uConsole guide says CM4 and CM5 both work, that ClockworkPi's own
A-04, A-06 and R-01 cores are not supported, and explains which chip and
why; config.example.toml and the configuration guide say the same, and
the guide's test record now names the CM5 it has run on since 09-24.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-04 11:36:44 -04:00
Jean-Pierre Martineau b884e2b468 Merge branch 'main' into cardputer-zero 2026-10-04 02:59:32 -04:00
Jean-Pierre MartineauandClaude Opus 5.5 fb2ead3cc3 Charts keep a three-cell y-axis gutter; the Rhythm drops its slice width when narrow
A chart's y-axis labels got exactly the cells their widest mark needed,
so the gutter grew under the reader as a busy minute took the peak from
9 to 10 to 100, sliding the whole chart sideways. Every gutter now gets
at least three cells (AXIS_LABEL_MIN), which hold every mark below a
thousand and every compacted one, so it stays put.

The Time Machine's Rhythm heading ended on its slice width ("20-min
slices"), which on the PicoCalc's and Cardputer's 53 columns wrapped the
heading onto a second row. Where the whole heading would wrap, the slice
width goes; the axis under the chart already marks the day's hours.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-04 02:59:31 -04:00
Jean-Pierre Martineau ac38bd28bc Merge branch 'main' into cardputer-zero 2026-10-04 02:34:36 -04:00
Jean-Pierre MartineauandClaude Opus 5.5 a8ce329758 The handhelds' dashboard drops "one minute per dot column"
The activity heading's aside ended "one minute per dot column". The
PicoCalc and the Cardputer draw braille as solid tiles, so there are no
dots there to count, and on 53 columns the words cost the heading its
line. Where braille is solid the aside is "every packet heard" alone;
the axis under the chart still marks the minutes. The desktop keeps it.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-04 02:34:36 -04:00
Jean-Pierre Martineau fb828d6be0 Merge branch 'main' into cardputer-zero
# Conflicts:
#	tests/test_timemachine.py
2026-10-04 02:28:14 -04:00
Jean-Pierre MartineauandClaude Opus 5.5 00e05e1b8c The scope and window chips are the word alone, without a triangle
On the PicoCalc's lane (and the Cardputer's, which deals the same) the
scope chip read "▸ yul" and the Time Machine's window chip "▸ 30 d". The
triangle took two of a chip's six cells to say what its place on the lane
already says, and cut a region's name to four. Both chips are now the
view a press goes to and nothing else, so a name gets all six cells.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-04 02:27:11 -04:00
Jean-Pierre Martineau ffad401f07 Merge branch 'main' into cardputer-zero 2026-10-04 01:34:05 -04:00
Jean-Pierre MartineauandClaude Opus 5.5 c7b3998a62 Edge scroll: an arrow past the last row a highlight can reach scrolls the page
On a page taller than the screen, whatever sat above the first selectable
row or below the last could scroll away for good: the frame always pulled
the highlight into view, and nothing up there could take it. Now, with the
highlight on the first row, up scrolls the page a line instead, and on the
last, down does, until the page's real top or bottom is in view. Home and
End take the page to its ends with the highlight.

The highlight may leave the screen meanwhile, on purpose. The snap-back
brings it home: while it is out of view, the other arrow, Enter or Delete
only bring it back, and do nothing else, so nothing runs that cannot be
seen. Any other key ends the edge scroll.

It lives in Screen.edge_scroll, asked before a screen's own handle; select
and reorder lists, the node page, chat, message paths, the path composer,
records and the TX sweep answer cursor_at_edge. CLAUDE.md names the rule.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-04 01:34:01 -04:00
Jean-Pierre MartineauandClaude Opus 5.5 cd9e81e69c On the Cardputer the node page's title bar wears the node itself
On a short frame the bar reads the node's type glyph and its name in its
own colours, "▲ Hilltop-Repeater" where it said "Node — Hilltop-Repeater",
so the identity line under the bar said the same thing twice and is gone.
The Routes tab drops the rule under the graph there too, and both freed
rows go to the graph: its ceiling is what the chrome and the list's window
leave, so a busy fan spreads into them. A sparse fan keeps its compact
pitch, as the graph always does. Taller frames are unchanged.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-04 01:15:06 -04:00
Jean-Pierre MartineauandClaude Opus 5.5 acc1341883 An activity chart that ends a row shortens to fit
The channel list's activity sparkline is the row's last column, and
where the row was too wide for the screen, the PicoCalc's above all,
the row's ellipsis cut it off mid-chart. The row now takes the render
width and draws the chart in whatever cells the lanes before it leave,
dropping its oldest buckets so now stays on the right; with no cell
left it is not drawn, and the ACTIVITY heading shortens and then goes
with it, so MSGS is never cut instead.

A row like that declares itself fitted, so the highlight draws it
fitted too rather than as a full line for the arrows to slide. And a
row exactly as wide as its list no longer claims to overflow by a cell.
CLAUDE.md states the rule for any chart that ends a row.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-03 20:17:33 -04:00
Jean-Pierre MartineauandClaude Opus 5.5 a8bec6f4cc An activity chart that ends a row shortens to fit
The channel list's activity sparkline is the row's last column, and
where the row was too wide for the screen, the PicoCalc's above all,
the row's ellipsis cut it off mid-chart. The row now takes the render
width and draws the chart in whatever cells the lanes before it leave,
dropping its oldest buckets so now stays on the right; with no cell
left it is not drawn, and the ACTIVITY heading shortens and then goes
with it, so MSGS is never cut instead.

A row like that declares itself fitted, so the highlight draws it
fitted too rather than as a full line for the arrows to slide. And a
row exactly as wide as its list no longer claims to overflow by a cell.
CLAUDE.md states the rule for any chart that ends a row.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-03 20:17:33 -04:00
Jean-Pierre MartineauandClaude Opus 5.5 149eb0cd90 The chat list's columns say LAST and MESSAGE; the live feed keeps TIME
The conversation list now reads CONVERSATION, NEW, LAST, MESSAGE: LAST
over the last message's age, MESSAGE over the message, shortening to MSG
on a narrow terminal. The live feed's timestamp lane is TIME again, and
the channel list keeps NEW.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-03 19:44:28 -04:00
Jean-Pierre MartineauandClaude Opus 5.5 51de872f32 The chat list's columns say LAST and MESSAGE; the live feed keeps TIME
The conversation list now reads CONVERSATION, NEW, LAST, MESSAGE: LAST
over the last message's age, MESSAGE over the message, shortening to MSG
on a narrow terminal. The live feed's timestamp lane is TIME again, and
the channel list keeps NEW.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-03 19:44:26 -04:00
Jean-Pierre MartineauandClaude Opus 5.5 fedd5f9608 Column headings say NEW and WHEN, not UNREAD and TIME
The chat picker's unread count is headed NEW and its last-message age
WHEN; the channel list's unread count is NEW; the live feed's timestamp
lane is WHEN. Short words over a badge and an age, which UNREAD outgrew
by a cell and had to borrow the gap for. The command line's columns keep
their names: they are a documented contract of their own.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-03 17:13:21 -04:00
Jean-Pierre MartineauandClaude Opus 5.5 17f57b1bbc Column headings say NEW and WHEN, not UNREAD and TIME
The chat picker's unread count is headed NEW and its last-message age
WHEN; the channel list's unread count is NEW; the live feed's timestamp
lane is WHEN. Short words over a badge and an age, which UNREAD outgrew
by a cell and had to borrow the gap for. The command line's columns keep
their names: they are a documented contract of their own.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-03 17:13:14 -04:00
Jean-Pierre MartineauandClaude Opus 5.5 fedb56bf9e On the Cardputer the header folds into the title bar
The Cardputer's 14 rows had a whole one spent on the header. It has no
header row now: its unread and Watchtower badges and the battery ride the
title bar's right end, after the way out, so they sit in the top-right
corner they always had, one row up, and every screen gets that row back.
The way out gives way first and then the title, never the status. The
wordmark moves to the main menu's title, in the header's own colours, and
the activity pulse goes; the dashboard draws the mesh's activity in full.

A tall dialog now draws over the bar, eleven rows instead of ten, but
never over the lane, which is the dialog's own and names keys nowhere
else. Where a box covers the bar, the bar's ends either side of it are
blanked, since a stray "7%" beside the box would read as the battery.

JP chose the corner placement and the dialog height from rendered
mockups. The PicoCalc and the desktop are unchanged.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-03 17:00:22 -04:00
Jean-Pierre Martineau 032e686beb Merge branch 'main' into cardputer-zero 2026-10-02 03:13:56 -04:00
Jean-Pierre MartineauandClaude Opus 5.5 b7fe257e2a The quit confirm keeps the chip that asked: F3 again leaves
On the PicoCalc the main menu's middle chip, Quit?, opens the quit
confirm, and the confirm's own lane was empty, so leaving took a move
to the Quit button and Enter. The confirm now carries Quit! on that same
F3, sending the same quit, which with the question up is the leaving, as
a second ^Q is. A button dialog can take a lane for that one case; every
other prompt keeps its empty one.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-02 03:11:20 -04:00
Jean-Pierre Martineau 4371b7e5d3 Merge branch 'main' into cardputer-zero 2026-10-02 02:52:13 -04:00
Jean-Pierre MartineauandClaude Opus 5.5 c988a2374e Section headings are a dark grey, not the frame's accent
A ── Label ── heading drawn in the border's own colour read as part of
the frame it sits in. Grouped lists' headings and the chat's day
dividers now wear their own theme style, heading: a bold dark grey on
the desktop, slot 8 on the PicoCalc. The frame keeps the accent.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-02 02:52:13 -04:00
Jean-Pierre MartineauandClaude Opus 5.5 bdfae433b5 On a short frame, every route graph's node-type key steps aside
The message paths' fan already gave its legend's row to the list below
a 16-row viewport. The node detail page's routes, the packet viewer and
a record's Route tab now do the same, so on the Cardputer's 11 rows no
path graph spends one on the key. The PicoCalc and a desktop terminal
keep it. Tests that paint these screens record a viewport first, as the
frame does.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-02 02:40:03 -04:00
Jean-Pierre Martineau 388b29615c Merge branch 'main' into cardputer-zero 2026-10-02 02:13:50 -04:00
Jean-Pierre MartineauandClaude Opus 5.5 5d56b45368 The node-type legend says room, not room server
Beside its square the shorter word is unmistakable, and it brings the
whole key to 52 cells, so the legend under a path graph or the contacts
table is one line on the PicoCalc and the Cardputer instead of two. The
type's name stays room server everywhere else, the CLI's answers
included.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-02 02:13:37 -04:00
Jean-Pierre Martineau a2e2500efe Merge branch 'main' into cardputer-zero
# Conflicts:
#	tests/test_chat.py
2026-10-02 00:58:32 -04:00
Jean-Pierre MartineauandClaude Opus 5.5 810232cfb6 A link written without a scheme is found too, by www. or a real TLD
meshterm.net/map and www.example.org are links now, as surely as their
https forms: a host starting www., or ending in a top-level domain IANA
has delegated, read from the registry itself, kept verbatim in
assets/tlds.txt. So file.txt stays text, txt being no TLD, and so do a
decimal, an abbreviation, an address, and a path. A scheme-less link
opens, and its QR code encodes, as https://. Link finding moves out of
the chat screen into core/links.py.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-02 00:55:59 -04:00
Jean-Pierre MartineauandClaude Opus 5.5 cd68498feb On the Cardputer, a link's code is on ^U, as on the desktop
Its 14 rows can't spare a code under every message that carries a link,
so it keeps url_codes off and reaches a link's code the desktop's way,
on the share screen. Its braille code is small enough that a link and its
code share the panel. A test pins all three: no code under the message,
QR on the lane's Shift bank, and the share screen inside 53x14.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-02 00:18:33 -04:00
Jean-Pierre Martineau 8755adc6f8 Merge branch 'main' into cardputer-zero
# Conflicts:
#	CLAUDE.md
#	meshterm/ui/chat.py
#	meshterm/ui/qr.py
#	meshterm/ui/walk_screen.py
#	tests/test_chat.py
#	tests/test_gallery.py
2026-10-02 00:15:20 -04:00
Jean-Pierre MartineauandClaude Opus 5.5 c6c80b4c1f A chat link is never cut where the screen can hold it, and ^U shows its code
A URL too long for a message's hanging lane steps out to the timestamp's
column on a line of its own, whole, since a terminal opens a link, and a
reader copies one, only while it sits on one line. Only a URL wider than
the screen still folds. ^U on a picked message opens its links on the
share screen, one code at a time, and with several, the arrows step
through them, the URL line flanked by a lit or dim arrow each way. On the
PicoCalc the lane carries it as QR, behind Day down.

CLAUDE.md also states the shortcut rule: a bare letter only on a screen
that takes no typing, a Ctrl chord everywhere letters are text.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-01 23:07:51 -04:00
Jean-Pierre MartineauandClaude Opus 5.5 f847ebd637 Locating our own node is ^Y, not ^U
Y for you, on the map and in the mesh walk alike.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-01 23:05:06 -04:00
Jean-Pierre MartineauandClaude Opus 5.5 71edf6a937 The map runs right up to the panel's side borders
The bordered frame pads its body one column in from each side border,
which is air for a row of text and two columns of drawing given up on
the map. A screen can now be flush (Screen.flush), and the map is: its
body is cols - 2 wide on the desktop, base_body_size agrees through the
shared panel_inset, and the braille edge scrub drops to the one border
column. The borderless platforms already drew the full width.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-01 23:03:01 -04:00
Jean-Pierre MartineauandClaude Opus 5.5 3c75374379 The map runs right up to the panel's side borders
The bordered frame pads its body one column in from each side border,
which is air for a row of text and two columns of drawing given up on
the map. A screen can now be flush (Screen.flush), and the map is: its
body is cols - 2 wide on the desktop, base_body_size agrees through the
shared panel_inset, and the braille edge scrub drops to the one border
column. The borderless platforms already drew the full width.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-01 22:46:39 -04:00
Jean-Pierre MartineauandClaude Opus 5.5 e66a5619dc Locating our own node is ^Y, not ^U
Y for you, on the map and in the mesh walk alike.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-01 22:10:19 -04:00
Jean-Pierre MartineauandClaude Opus 5.5 9b215172d0 On the PicoCalc, a chat message's links hang as QR codes under it
Every URL in a channel or direct message, any scheme (a meshcore://
share as surely as a web link), now gets a braille code under the
message, hung at the body's indent. Several sit side by side and wrap
onto a new band when the line is full, so a link read on the handheld
opens on a phone instead of being typed out. Platform.url_codes keeps
it to the PicoCalc: half-block codes would swamp a desktop transcript,
and the Cardputer's 14 rows can't spare 9 under every link.

A picked message now keeps its whole block in view, codes included,
rather than only its first row, so walking the pick down onto a link
no longer leaves its code below the fold.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-01 00:37:33 -04:00
Jean-Pierre MartineauandClaude Opus 5.5 ec850d8e93 On the PicoCalc, a chat message's links hang as QR codes under it
Every URL in a channel or direct message, any scheme (a meshcore://
share as surely as a web link), now gets a braille code under the
message, hung at the body's indent. Several sit side by side and wrap
onto a new band when the line is full, so a link read on the handheld
opens on a phone instead of being typed out. Platform.url_codes keeps
it to the PicoCalc: half-block codes would swamp a desktop transcript,
and the Cardputer's 14 rows can't spare 9 under every link.

A picked message now keeps its whole block in view, codes included,
rather than only its first row, so walking the pick down onto a link
no longer leaves its code below the fold.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-01 00:36:50 -04:00
Jean-Pierre MartineauandClaude Opus 5.5 28bd59cf1b The Cardputer draws braille as solid tiles, and its QR codes in braille
The console host now draws every braille cell over Terminus's dotted
ones as solid 3x3 tiles, the same tiles as the PicoCalc's font build,
so the map and the charts draw unbroken lines and Platform.solid_braille
holds here too. Every QR code is then braille, one module a dot: the
share screen fits the 14-row frame instead of being cut, a channel
share with its link under it, a contact card with its link a page down.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-01 00:26:55 -04:00
Jean-Pierre Martineau e31d24e12b Merge branch 'main' into cardputer-zero 2026-10-01 00:22:54 -04:00
Jean-Pierre MartineauandClaude Opus 5.5 6840138eb4 On the PicoCalc, every QR code is braille, one module a dot
Braille was a fallback for codes too big for half blocks; on the
PicoCalc it is now the grid every code is drawn on, eight modules to a
cell, share screens and page fences alike. A channel share is 23 cells
by 12 rows there, a contact card 29 by 15, each with its link under it.
The regular platform keeps half blocks, its fonts' braille being dotted.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-30 22:55:01 -04:00
Jean-Pierre MartineauandClaude Opus 5.5 edacbd0740 On the PicoCalc, a QR code too big for half blocks steps down to braille
Now that the PicoCalc's fonts draw braille as solid tiles, a braille
cell is a true 2x4 pixel grid, so it can hold a QR code at half the
module size. The fit ladder tries every half-block fit first (a lighter
error level and a narrower quiet zone cost a camera nothing on a
screen), then the same fits in braille. That puts the contact card on
the 53x26 panel whole: the standard code in 29 cells by 15 rows, with
its link under it, where half blocks had to send the link a page down.

Platform.solid_braille says where braille is solid; desktop fonts draw
it dotted, so the regular platform never offers it. Codes are cached
per (data, error level) since the share screen refits every paint.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-30 22:32:43 -04:00
Jean-Pierre MartineauandClaude Opus 5.5 a62bf9ffdb The Cardputer's lane chips are laid out as the PicoCalc's
Key caption, nine cells a chip, two between, at the same columns (JP); only the
captions, F4-F8 in both banks, and the colours are the Cardputer's own.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-30 21:34:13 -04:00
Jean-Pierre MartineauandClaude Opus 5.5 a5b53122ad Two message-paths tests record a viewport before painting, as the frame does
Unset, the viewport reads as one row, which is now a short frame that leaves the
graph's caption out, and the caption is what these two check.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-30 21:17:41 -04:00