- New "Web" app: on-device HTTPS text browser (strip HTML to readable text,
tappable links, back/fwd/refresh, no proxy). Gated to 8 MB boards via
CAP_WEB_BROWSER; hidden on the 2 MB V4 (can't do the TLS handshake).
- Tappable URLs in chat -> Open in web (8 MB) / Create QR (all boards).
- Drawn vector globe icon for the Web tile.
- Fix: French AZERTY on-screen keyboard lost its spacebar/OK/kbd-switch row
(a stray "" mid-map terminated the LVGL button map early). Restored.
- V4 web fetch resilience (192->96->48->24 KB buffer fallback).
- Home signal box: "tap for details" cue back at the top.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The DOC_CAPTURE tour re-captured the map from the live device, which showed a
less representative view. Keep the earlier curated map shot (tiles + markers).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- New "Remote & web app" docs section covering the browser control panel
(Chats / Contacts / Discovered / Settings / Terminal), Screen mirror (VNC)
and Remote UI, with a 7-shot phone gallery of the real web app.
- Refreshed all device screenshots via the DOC_CAPTURE tour (now also
captures the Remote screen) and added the MQTT bridge settings page.
- New automated web-capture harness (scripts/doc/web-demo/): extracts the
real page from firmware, serves it with mock data, screenshots via Playwright.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The browser control panel gains full Chats / Contacts / Discovered / Settings
tabs (search, sort, filter, per-contact actions, notifications + live refresh,
ACK/repeats, add-from-discovered, live radio settings, quick-command picker).
Heltec V4 map tiles: fixed the "only the top renders" bug on the 2 MB-PSRAM
board with a persistent tile-buffer pool + viewport culling (no more per-render
128 KB churn/fragmentation). Adds an opt-in "Tile debug overlay" (Map options).
Also: V4 physical-keyboard input in remote/VNC, clearer remote-access screen,
complete Terminal help on device + web, WADAMESH header + status icons.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Remote mode showed a black screen on 2 MB-PSRAM boards (Heltec V4): the
mirror ring/shadow were too big to allocate. Size the ring to one frame
and drop the remote render to 400x240 so the whole footprint is smaller
than VNC's (which already worked). Add a touch-and-hold (3 s) exit for
keyboard-less touch boards and an Exit button on the web page.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Stream the live LVGL UI to a phone/desktop browser over the WS companion
server (VNC app) plus a headless REMOTE mode that renders the UI at a
browser-friendly resolution. Non-blocking per-client TX buffering, a
core-0 stream task guarded by a recursive mutex, an on-page Rotate
orientation toggle, and a small 480x288 render upscaled in the browser.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Tanmatsu (ESP32-P4) now bakes FIRMWARE_VERSION="wadamesh <tag>" like the S3
release path, and the app-store publish passes the exact beta tag into the IDF
build — so the About page shows the real beta number instead of the MeshCore
default "v1.16.0-touch".
- beta_40 stable release notes: rolls up beta_36 through beta_40.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Fixes:
- Card-less devices (Heltec V4) froze for seconds: a full contacts rewrite to
SPIFFS on every advert refresh triggered a multi-second GC in the mesh loop.
Coalesce advert-driven saves (a contact-set change still saves promptly); SD
boards unchanged. Flush on reboot/power-off so nothing is lost.
- Long translations (FR/DE/...) overflowed fixed-width labels: new
uiFitLabelWidth() shrinks the font to fit one line instead of clipping/wrapping
- applied to the message menu, confirm dialogs, and app-drawer tiles.
- Home signal graph: "tap for details" hint moved to bottom-right so it no longer
overlaps the "Sig" chip in longer languages.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Persistence (Leon P / MrWeavis, beta_38):
- Deleting a message or clearing a chat now persists synchronously, so it
survives an immediate hardware reset instead of "coming back" (the tombstone
used to ride the 30 s lazy SD flush, which a reset beats).
- The history file is now written compactly — only the used records,
oldest-first, instead of all ~5000 ring slots every time. A typical chat
writes ~48 KB instead of ~1.2 MB, so the synchronous delete/clear/reboot
flush no longer hitches the UI (and normal message flushes are lighter too).
Old history files still load (the reader derives its slot count from size).
Emoji picker:
- Baked the 14 glyphs that were listed but never in the font (umbrella, coffee,
pizza, cake, rocket, camera, phone, laptop, calendar, antenna, e-mail, house,
car, watch); they rendered as the notdef box.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Audited every user-visible string sink across the touch UI and closed the
gaps that had accumulated since the language table was last touched:
- TR() now strips a leading LVGL-symbol glyph (+ spaces) off the lookup key
and rebuilds the label, so icon-prefixed menu/sheet rows ("<glyph> Copy")
translate instead of silently falling back to English.
- Wrapped 95 bare literal sinks (settingsRowLabel / showAlert / showConfirm /
lv_label_set_text / dropdown / placeholder / checkbox) in TR(); unwrapped 29
format-only strings that never should have been keys.
- Added 307 missing rows to kI18n[] covering all 11 non-English languages
(NL DE FR ES IT RU UK BG SR EL PT-BR); keys byte-identical, printf
placeholders and %-order preserved, Serbian in Cyrillic.
Builds green on T-Deck + Heltec V4; Tanmatsu app 3,169,296 B (107 KB under
the AppFS 50-page load ceiling).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
One page with every supported board, its install path, release channel
and maturity, plus the open hardware requests and the channel model.
The README board list now covers all five targets and links there.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Both new S3 boards are installable from the browser now: release.sh
builds and ships their bins per channel, gen-flasher-meta emits their
esp-web-tools manifests, and the site gets install cards for both.
The cards are pinned to the beta feed (with a note) until the first
stable promote that includes their bins; the wmSetChannel comment says
how to unpin then. beta_38's beta feed and GitHub release carry the
new bins retroactively.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The P4's internal FFat 'locfd' has a broken FAT metadata layer: open/
read/write work but f_stat/exists()/size() return garbage - and WHICH
garbage shifts with the build. At -Og the exists()-gated identity and
NodePrefs loads worked by luck; the -Os switch flipped the lie and the
device booted with a fresh identity and default name, and profile
changes never survived a reboot (the loads failed, not the saves).
Chat history was unaffected because it already lives on SD_MMC.
- DataStore::useSdMmcStorage(): full-store adoption of the card
(/meshcomod root, identity store included), mirroring the T-Deck's
useSdStorage(). The old card-root contacts3/channels2/adv_blobs from
the secondary-FS era are renamed under /meshcomod.
- Tanmatsu boot: one-time rescue of identity + prefs off FFat by OPEN
and READ probing (never exists()/size() on that FS), then adopt the
card. FFat remains the no-card fallback only.
- DataStore::loadPrefs() probes candidates by opening and reading a
byte instead of exists() - truthful on the P4, identical elsewhere.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Since the beta_35 lazy history writer, message-ring flushes are spaced
up to 30 s apart on the deep SD ring. Every on-device reboot/power path
compensates by calling persistHistoryNow() first, but the companion
protocol's CMD_REBOOT (the phone app's reboot button) went straight to
board.reboot(), dropping everything since the last flush - the newest,
just-read messages, which reads as 'my channel and DM messages get
deleted after a manual reboot' (Leon P, T-Deck under Launcher, beta_35
through beta_37).
persistHistoryNow() is now an AbstractUITask hook (default no-op, so
non-touch companions are unchanged) and CMD_REBOOT calls it before
rebooting, matching the power-menu contract.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The app has been compiling at debug optimization since the port began,
costing ~300 KB of flash-mapped footprint against the launcher's hard
50-page (3,276,800 B) app-load ceiling. At -Os current main is
2,973,520 B, ~296 KB under the ceiling. Boot-verified on the device:
app starts, C6 esp-hosted link up, Wi-Fi associates.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Users who flipped 'Store data on SD' before beta_36 ran with the toggle
IGNORED (the flag never survived a reboot), so their identity, prefs and
contacts kept living on SPIFFS. When beta_36 made the flag finally take
effect, useSdStorage() pointed the store at an EMPTY card, the identity
store minted a brand-new node, and the user 'lost' their profile. The
data was never gone - it was orphaned on internal flash.
Two-part fix:
- Boot: adopting the card while SPIFFS holds an identity the card lacks
now copies every SPIFFS file into SD:/meshcomod first (kv files
flatten to the card's SdNvsPrefs layout). If the identity file fails
to land, the device stays on SPIFFS for the boot instead of adopting
a card without it.
- Settings -> Storage gains 'Copy internal data to SD' for the users
already bitten (their card now holds a fresh identity the boot-time
no-clobber gate rightly won't touch): confirmation, overwrite-copy of
everything from internal flash, force the SD pref on, reboot.
Workaround until this ships: toggle 'Store data on SD' OFF and reboot -
the device reads internal flash again and the old profile returns.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
With Delete the menu carries up to 7 actions; a single column no longer
fit the 240-px screens without scrolling. Two per row (wider 200-px card,
odd last button on its own row) keeps the whole menu on one screen.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- The chat jump buttons are bare arrows flush against the right edge now
(28x36 transparent touch target, accent-colored glyph) instead of 40 px
filled circles; they run at full opacity while the list scrolls and dim
to 50 percent one second after the last scroll activity. The Tanmatsu
keeps its F-key chip styling and geometry.
- The long-press message menu gains a Delete row: the message is
tombstoned in the ring (thread tag blanked, so every walker skips the
slot and live ring indexes stay valid) and both the chat and the thread
list refresh.
- The chat/channel settings sheet (in-chat gear and list long-press)
gains a 'Delete history' row for both DMs and channels: confirmation
prompt, then every message of the thread is tombstoned. The thread row,
channel membership and contact stay; only the conversation is wiped.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The app has been building at debug optimization since the port began.
At -Og the AppFS image sat 2.2 KB OVER the launcher's 50-page load
ceiling with the chat virtualization in; at -Os it is 2,980,688 bytes,
296 KB under the ceiling, retiring the size-locked-platform problem
(and likely speeding the UI up). Needs one on-device boot check before
anything ships with it - the P4 has never run an -Os build.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- chatVirtReset no longer queues lv_obj_del_async on the divider: the
empty-thread paths lv_obj_clean two lines later freed it first and the
deferred lv_obj_del then ran on freed memory (confirmed UAF, plus a
cross-panel variant when both panels are open). Downstream cleans own
the widget; the reset only nulls the pointer now.
- Compact rows register bubbleLongPressMenuCb for LV_EVENT_LONG_PRESSED
(the only event the callback acts on) instead of PRESSED/RELEASED/
PRESS_LOST/DELETE, all of which it filtered out - the per-message menu
was completely dead in compact mode.
- Bubble mode gets tap-to-resend back on failed sends (CLICKED handler
plus the 'tap to resend' footer), matching the compact path and the
pre-virtualization behavior.
- need_layout gains a content-generation guard (endpoint ring slots): at
ring capacity n stays constant while content rotates, and equal-count
thread switches reused stale offsets - rows rendered the next message's
text at the previous message's position. The dead dirty_timeline rescue
(cleared before its async consumer could read it) is removed.
- New chatVirtTryAppendLayout fast path: pure appends extend offsets for
the new tail instead of re-measuring the whole thread (the full measure
is ~100 ms of UI stall per received message on a 5000-message SD ring).
- lv_async_call failure no longer wedges the render busy flag; the nav
focus fallback validates s_nav_objs[0] before focusing it (bubbles now
recycle on scroll-end, so slot 0 can be one tick stale); dead
chatVirtClearAllChildren/DeleteMsgsChild pair removed; stray
FIRMWARE_BUILD_DATE bump reverted.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The control-center sizing lost its '#elif HAS_TDECK_GT911' line, which
put the 200 px set_size inside the Tanmatsu arm (shrinking its card
from 384 to 200) and dropped the T-Deck into the portrait else-arm.
Restored as TDECK || M9. Also removed the unreachable duplicate lines
after the M9_KEY_HOME return and fixed 'hole the d-pad' on the lock
screen hint.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The bare ')' under #if CAP_LARGE_SCREEN broke the Tanmatsu (P4) build,
the only target that compiles that arm. The power-menu #else arm went
back to the original card_h = 206 so T-Deck/V4 keep their exact layout.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- PIN_USER_BTN: -1 -> 0 (enable BOOT key on GPIO0)
- Short press (<1s): toggle screen on/off (no hard-lock)
- Long press (>=1s): open power menu (Power off / Reboot / Cancel)
- Add ARDUINO_USB_CDC_ON_BOOT=1 (stable USB CDC COM port)
- boards/rak_tap_v2.json: use_1200bps_touch=false, wait_for_upload_port=false
(manual download mode via BOOT+RESET, esptool upload on COM7)
- Remove unused boards/wiscore_rak3312.json
- Remove LovyanGFX from Heltec lib_deps (accidental regression in baseline)
Signed-off-by: Ethan-chen1234-zy <ethan.chen@rakwireless.com>
A flash/reset landing mid-I2C-transaction can wedge the GT911 (the S3
resets, the touch controller keeps running) into repeating a stale
pressed frame. The driver's keep-prev-state-on-no-data poll then latched
an eternal press: real touches ignored, trackball clicks eaten (a live
touch outranks the click injection), idle timeout never firing, keyboard
lock/unlock still working - a dead-looking device that survived every
reboot and reflash because only a full power cycle resets the GT911.
Watchdogs in gt911Poll() now force a release when status reads fail
mid-press, when no frame arrives for 250 ms while pressed (a real held
finger produces a frame every GT911 scan cycle), or when one bit-exact
point stays pressed for 10 s (a human touch always jitters) - that last
point is then ignored until it moves or a real lift frame arrives. A
jitter-y phantom can still need a power cycle, but the common wedge
modes self-heal in under a second.
Init is no longer one-shot: the boot probe retries every 500 ms for
~6 s before caching failure, so a GT911 still resetting at the first
attempt no longer costs the session its touch AND keyboard (the kb
poll task only starts once touch inits).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Viewport-based message virtualization for open chats: only the visible
window of LVGL widgets is materialised while layout offsets cover the
full thread (up to msgCap). Supports bubble and compact IRC modes, day
separators, jump to oldest/latest, and compressed LVGL scroll coords
for very long histories.
Finger drags advance layout scroll 1:1 even when coords are compressed,
so long histories scroll fluidly without micro-drags that trigger bubble
long-press menus.
Includes LVGL crash hardening from soak testing: defer refreshChatDetail
and bubble reflow off the indev/refresh path, sync-delete during async
render, size chatVirtEnsureMsgIdx to msgCap for the SD deep ring, and
open-to-bottom scroll that does not rely on premature scroll_bottom.
Fixes#126
Signed-off-by: Steve Glenner <steveglenner@hotmail.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
It is align_to'd against the clock's final translated coords each tick; the
tall-bar top-row translate on top of that pushed its upper half out of the
bar (clipped) inside a chat.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Typing (and thus the active layout) only matters in an open conversation;
everywhere else the language name was permanent bar clutter next to the clock.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The EN/DE indicator sat at a fixed -182 right offset while the clock's left
edge reaches past that in 12-hour mode ('7:45 PM'), overlapping on the T-Deck
bar. The Tanmatsu branch already parked it align_to the clock; un-gate that so
all boards track the clock wherever it lands (12/24h width, charging slide,
centred hide-name mode).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Adds the node's live GPS position as the first row of the chat quick-reply
picker: with a fix it shows the coordinates and a tap inserts them into the
composer at the cursor (same no-auto-send contract as the macro rows, so the
operator can add context before sending); without a live fix the row is
greyed out and disabled. Sharing where you are is one of the most common
things typed into a mesh chat, and hand-copying coordinates from the GPS
screen was the only way to do it.
Signed-off-by: Yaz <yazutsu@users.noreply.github.com>
Ensure the tile-fetch worker is running before arming a message-ring flush,
fall back to a synchronous write if spawn fails, write ui_msgs_v1.bin via a
.tmp rename for crash-safe replacement, and retry when the worker snapshot is
missing instead of treating the flush as successful.
Signed-off-by: Steve Glenner <steveglenner@hotmail.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Follow-up to the keyboard-backlight brightness control (issue #84). The Off
preset used to set the brightness percent to 0 while leaving the backlight
MODE untouched, inventing a second dark state: the keyboard went dark but the
control-center chip - which cycles and shows the mode - kept saying on/auto.
Off now switches the mode off, the same axis the CC chip shows, so the two
surfaces can never disagree. The brightness slider becomes 1-100: dark is
only expressible via the mode, and the on/auto modes always glow visibly at
the chosen level. The stored percent is left untouched by Off, so re-enabling
the backlight restores the previous brightness; a stored 0 from the previous
build is clamped to 1 at boot.
Signed-off-by: Yaz <yazutsu@users.noreply.github.com>
- ALT-accent picker on the Tanmatsu physical keyboard: hold ALT + letter
opens the accent popup with the first variant highlighted, the letter
cycles (auto-repeat walks the options), releasing ALT (its own scancode
event) inserts the highlighted choice. Popup is nav-skipped/non-clickable
so keypad-nav focus can't steal the cycle key, SC()-scaled, align-centred.
- Double-tap-space language cycle ported to the Tanmatsu typing path
(navPump), and keyboardLayoutsApply now updates the active layout even
with no on-screen keyboard object (was a permanent no-op there).
- Fonts: LV_USE_FONT_COMPRESSED on; extras_font_12/14/16 regenerated
compressed (identical text glyphs; redundant mono emoji dropped — the
colour imgfont covers emoji) freeing ~340 KB; new compressed Latin-accent
extras_lat_20/24/28 spliced into the Large/Huge fallback chain
(Tanmatsu-only) so umlauts size-match the scaled UI. Tanmatsu app.bin =
3,269,440 B, UNDER the known-booting 3,271,856 B (50-page AppFS ceiling).
- TEMP: TDECK_KEYCODE_PROBE toast (build-flag gated, inert in releases)
for mapping the T-Deck alt key.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>