100 Commits
Author SHA1 Message Date
Kaj SchittecatandClaude Opus 5 b5a70f43a5 docs: how to submit an app to the store, and what the safety check is
There was no written path for contributing a Lua app. pisti87 wrote a 2048 game
and ended up asking another contributor to upload it for him, in the comments of
an unrelated issue about map panning — which is the symptom of a missing
procedure, not of anyone doing something wrong.

deploy/apps/README.md is now the canonical process: file layout, the immutable
version-directory rule, the apps.json catalog row, and how to open the PR. It
also says an issue with the .lua attached is an acceptable way in, because
requiring git fluency would cost us apps from people who can clearly write them.

Both this and the site's Apps section state up front that every submission is
reviewed and safety-checked before it is added, and what is actually looked for:
anything touching node identity, keys or channel secrets (the one that gets a
hard no), flash-write patterns that trigger GC and stall both cores, blocking the
shared UI/mesh loop, unbounded memory on a 2 MB V4, and unrequested transmits.
Those criteria come from bugs this firmware has actually shipped, so they are
concrete rather than boilerplate.

Written to be inviting about it — the review exists because apps run on other
people's radios, not to gatekeep, and a rough app that works beats a perfect one
that never gets sent.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-12 11:41:51 +02:00
Kaj SchittecatandClaude Opus 5 d41bcd3c0f i18n: register the three new scope strings, and fix a key that could never match
The #259 popup change introduced TR("Scope "), TR("my region") and
TR("another region"). Two problems, both caught by re-running the audit rather
than by eye:

  - none of the 13 language files had rows for them, which would have reopened
    the drift closed in d4ade2e. Added to all 13 as empty rows (the format's
    own untranslated marker), so translators see the gap and English falls
    through meanwhile.
  - the key was "Scope " with a trailing space. TR() strips icon-glyph prefixes,
    NOT trailing whitespace, so the lookup would never have matched a "Scope"
    row and that label was permanently English in every language. The space now
    lives in the format string where it belongs.

audit-lang.py: 0 missing and 0 unsafe across all 13.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-12 09:16:38 +02:00
Kaj SchittecatandClaude Opus 5 d192bf53db fix: never trust a translation as a printf format string (#258)
pisti87's T-Deck rebooted every time he logged into a repeater, but only in
Hungarian. The login clock-skew warning does:

  snprintf(msg, n, TR("Device clock differs from \"%s\" by %lu min%s"),
           name, minutes, suffix);

and the Hungarian row reordered the conversions to "%lu ... %s ... %s". Varargs
are positional, so snprintf read the name POINTER as an unsigned long and then
took the minute count -- the integer 3 -- as a char* and dereferenced address 3.
Instant panic, every login, Hungarian only. English fit the declared order, so
it never showed there.

Fixing the four bad Hungarian rows is not sufficient: TR() returns a format
string and translations come from .lang files that users download or hand-write,
so any file can crash any device. TR() now compares the ordered conversion
signatures of key and translation and falls back to the English key on a
mismatch -- the key IS the call site's format string, so it is always correct.
The scan runs only for keys containing '%', which is a small minority.

Also fixed the four rows (three were Hungarian-only crashes or dropped values),
bumped hu to v11, and taught audit-lang.py to fail the build on a mismatch so a
future translation PR cannot reintroduce this. Unit-checked that the audit
detects the original bad row and accepts the repaired one.

Reported by pisti87.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-12 08:14:05 +02:00
Kaj SchittecatandClaude Opus 5 32725edf11 docs: explain the save chip on the command centre
The floppy-disk icon and time in the traffic card's legend row read as a clock,
and nothing on the page said otherwise. Document what it actually is — when
chat history last reached storage — and all five states, including the -ND
suffix that marks a store which stopped saving and the red FAIL / amber
migrating cases, with the pointer to About -> Chat store for the detail.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-11 21:17:39 +02:00
Kaj SchittecatandClaude Opus 5 c00734cfe8 apps: publish the pending language updates (de v9, nl v9, hu v10)
langs.json had not moved since the v8 publish, but four commits changed
language content after it: pisti87's Hungarian v9, the glyph swap that
followed, the 48 placeholder rows, and pisti87's German, Dutch and Hungarian
translations of those rows. The device asks langs.json for the version, then
fetches the immutable /apps/lang/<ver>/<code>.lang copy — so with the catalog
still reporting v8 for everything, every one of those updates was invisible on
device. That is the "it downloads an old version" report.

Publish the immutable v9 and v10 snapshots and bump the three languages that
changed. The other ten stay at v8: they only gained empty placeholder rows,
which fall back to English either way, so bumping them would cost every device
a download for no visible change.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-11 11:17:46 +02:00
956bc9faeb i18n: German, Dutch and Hungarian translations for the 48 new UI strings (pisti87)
pisti87 translated the entire missing-key set for three languages and posted
them as 143 line comments on d4ade2e rather than a PR, so they were sitting
unmerged. Harvested by mapping each comment's diff position back to the
placeholder row it annotates — all 143 matched an empty row exactly, no
guesswork.

de 48/48, nl 48/48, hu 46/46 (two of his 47 Hungarian comments were variants of
the same string; 'Copy blocked: migration guard unavailable' is still open).
Version headers bumped so devices pick the files up; i18n_builtin.h regenerated.

Co-Authored-By: pisti87 <pisti87@users.noreply.github.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-11 08:55:27 +02:00
Kaj SchittecatandClaude Opus 5 d4ade2e616 i18n: add the 48 UI strings no language file had, as empty rows for translators
The firmware asked for these keys and no .lang file carried them, so they
rendered English in all 13 languages with nothing to show a translator that
they were missing. Mostly strings that shipped with a feature whose PR did not
touch the language files: the SD arbitration and migration warnings, the
Wi-Fi/BLE coexistence messages, the keyboard-navigation vocabulary
(Up/Down/Left/Right/Select/Scroll) and section names (General, Clock & time,
Sensors, Screen, Home, Messages, Keys, Compact, Snake).

They go in with an EMPTY translation, which is the format's own way of saying
'not translated yet': the runtime loader requires a non-empty value
(`if (*p && tab[1])`) and gen-lang-builtin.py requires `if k and v`, so both
skip these rows and the English falls through exactly as before. Confirmed:
i18n_builtin.h regenerates byte-identical. No `# ver:` bump for the same
reason — device behaviour is unchanged, so there is nothing to re-download.

Also fixes the audit's own row parser to match the loader: a line is a comment
only when it has NO tab. Testing for a leading '#' hid every key starting with
LVGL recolor markup ("#7A7F87 Wardrive: …#"), the exact class the loader has a
comment warning about.

Every language is now at 0 missing keys.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-10 10:24:56 +02:00
Kaj SchittecatandClaude Fable 5 e33bd41f40 touch: hu v9 — swap the one glyph the fonts lack (U+201E low quote)
Audited every non-ASCII codepoint in the v9 Hungarian file against the
extras-font ranges: full coverage (incl. Hungarian ő/ű, Latin Extended-A)
except a single „ low opening quote used once — swapped for the covered “
rather than regenerating three fonts for one glyph. Add U+201E to the
extras --symbols on the next font regen.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-09 15:20:14 +02:00
Kaj SchittecatandClaude Fable 5 785a2df4e2 touch: Hungarian translation update v9 (pisti87) + lang store no longer downgrades (#247, #232)
hu.lang: 43 improved translations + 21 newly covered strings from pisti87,
applied as ver 9; builtin table regenerated.

Lang store (#247): the update check treated ANY version difference as
'update available' — a side-loaded NEWER file than the catalog got
DOWNGRADED on Update. Now numeric: offer only when the catalog is newer
(non-numeric versions keep the old inequality).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-09 15:14:48 +02:00
Kaj SchittecatandClaude Opus 4.8 ad6446b312 apps: publish the v8 language files at their immutable path
The Store fetches a language from /apps/lang/<ver>/<code>.lang, not from the
mutable copy at the top of the directory -- so bumping langs.json to v8 without
creating lang/8/ would have offered every device an Update that 404s.

Also normalised the "ver" spacing in langs.json back to the file's own style.
The device parser skips whitespace after the colon so both forms work, but
there is no reason for the file to be inconsistent with itself.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-05 00:54:43 +02:00
Kaj SchittecatandClaude Opus 4.8 dbff43dbd7 beta_59 prep: language files v8, the SDK docs page, and device-status corrections
The audit found three keys the Store work added or renamed since v7 -- Store,
the out-of-memory message and the Languages hint -- untranslated in all 13
languages. Filled, using each language's own word for the Use button so the
hint matches the control it points at, and bumped the files to v8. Every
language is back to full coverage at 856 keys.

New deploy/site/sdk.html: the wada.* reference. The beta ships a public API and
had no user-facing documentation for it, so nobody outside the repo could write
an app. Covers the lifecycle, all seven tables, the app format, side-loading and
how to publish, plus the two constraints that surprise people -- the mesh is
read-only in v1 and http_get is plain HTTP because TLS does not fit in the heap
left after Wi-Fi associates.

Site device list: the Attaky Core and the T-Display P4 are fully supported now
(Kaj's call), so their not-hardware-verified caveats are gone. Dropped the "one
of the two primary development boards" wording, which stopped being true once
the P4 joined the test loop. The terms section named only the T-Deck and Heltec
V4 as flashable when the flasher offers ten boards; it now points at the list on
the page instead of going stale again.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-05 00:37:22 +02:00
Kaj SchittecatandClaude Opus 4.8 cac3a44074 i18n: scan by literal, not by function name — 103 more wrapped incl. Remove channel (v7)
'Remove channel' was built by mk_full(), a local row helper my scanner did
not know about. Listing the functions that take UI text has the same blind
spot as grepping for the strings themselves: the one helper nobody wrote
down keeps its labels English forever.

So the scanner gained a --wide mode that inverts the question — every prose
literal in the file is suspect unless it is consumed by a known NON-UI sink
(logging, strcmp, paths, Lua glue, format-only args). That surfaced the
whole family of local builders: mk_full, mk_btn, mk_label, mk_ta, mk_switch,
setupHeader, setupBtn, fmActionBtn, ccToggle, make_launcher,
setAddChannelError. --fix now wraps prose in ANY argument of those helpers
rather than a hardcoded index, so their differing signatures don't matter.

103 calls wrapped: the channel sheet (Remove channel, Delete chat), every
contact action (Open chat, Ping, Message, Telemetry, Trace SNR, Admin,
Range test, Sightline, Block, Favorite, Reset path), the Profile/Radio
settings rows, auto-add and experimental switches, the add/join channel
menu and its errors, file-manager actions, control-center chips, the
launcher tiles and the whole first-boot wizard. 40 new keys translated in
all 13 languages; units and protocol tokens (MHz, SF, CR, TX, AF, BT, GPS,
DND, Wi-Fi) map to themselves. Files at ver 7, both tools green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-04 17:58:50 +02:00
Kaj SchittecatandClaude Opus 4.8 15415fd0f5 i18n: enumerate every user-visible literal instead of grepping for them (49 wrapped, v6)
My earlier sweeps searched for strings I could think of, which is why you
kept finding more. scripts/build/i18n-scan-unwrapped.py now parses every
call that takes user text — LVGL setters (label/checkbox/dropdown/roller/
placeholder/tab/table/msgbox), showAlert, showConfirm, appPageBegin,
createSettingsModal and the settings row helpers — splits the argument list
and reports any human-readable literal not wrapped in TR(). It found 55;
49 got wrapped (--fix does it mechanically), 6 skipped as brand/technical
(WADAMESH, CPU, PSK, OK, and two unit readouts). It now reports zero.

The 33 newly reachable keys are translated in all 13 languages: the Flood/
Zero-hop scope buttons, Share QR, Search contacts, Play / Set as
notification sound / Set as wallpaper, Probe now, On map, Install update,
Save update bin to SD, Choose .wav from files, Power, Clear history, the
Bluetooth/MQTT/Logs/System info sheet titles, both dropdown option lists,
the size preset list, Import, the URL bar, and Snake's New game.

Two format bugs behind the last stragglers: keys whose text begins with
LVGL recolor markup ('#7A7F87 Wardrive: ...#') were dropped as comment
lines by BOTH the device loader and the audit — a line is a comment only
when it has no tab, fixed in both. And several rows had been written with
literal '\xE2\x80\xA6' text where the file needs the real character; the
loader only unescapes \n \t \\, so those keys never matched.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-04 17:46:00 +02:00
Kaj SchittecatandClaude Opus 4.8 7a034d303a lang: fix the download for real (PSRAM->SD + short reads), and say why before restarting
Three real bugs behind the endless 'Download failed', found by putting a
diagnostic on the device instead of guessing:

1. HTTP read quit early. The loop treated a gap in the stream as
   end-of-data, so a 49 KB file came back as 16 KB (n=16492 measured) and,
   worse, that truncated buffer was returned as success. It now reads to
   Content-Length, gives stalls a 4 s progress grace, and FAILS on a short
   read instead of handing back half a file.
2. SD writes from PSRAM died after the first 4 KB (wr=4096, measured twice).
   Writes now go through a small internal-RAM bounce buffer, which is what
   every proven-good SD writer in this firmware already does. Full 49344
   bytes written and installed, confirmed on the bench.
3. A truncated write still stamped the new '# ver:' (it is the file's FIRST
   line) while the appended rows were lost — so the tab claimed v4, offered
   no Update, and showed English. Downloads are atomic now: temp file,
   verify on-card size, then rename over the target. A failure leaves the
   old file and its old version untouched.

Plus: if a /lang write fails, an identical control write to /apps proves
whether the card is healthy, and if so /lang is rebuilt and the install
retried — self-healing the FAT damage that mid-write resets can leave.

Language changes now show 'Restarting to apply the language...' and reboot
on a timer, so the notice actually paints. showAlert() followed immediately
by rebootDevice() never rendered a frame — the device just went dark under
your finger. Applies to Update, Use, and the built-in picker alike.

The boot self-heal no longer toasts its failures (that was the 'download
failed' that greeted you when merely opening the store). Files at ver 5.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-04 17:28:06 +02:00
Kaj SchittecatandClaude Opus 4.8 535471613d lang: chunked SD write fixes the false 'Download failed', audit sees glyph-prefixed TR, 16 more keys (v4)
The update download was actually succeeding: one 48 KB f.write on FAT can
return a short count without error, so the file landed while the code
reported failure — which also suppressed the reboot-to-apply (Kaj saw the
new version only after re-entering the tab and switching languages by
hand). The write is chunked with a real completion check now, and
s_langdl_ok is volatile.

The audit's TR() extractor required the argument to start with a quote, so
every TR(LV_SYMBOL_X "  text") call — all the sheet rows wrapped in the
previous commit — was invisible to it, and 'full coverage' was reported
while 16 keys had no rows anywhere: the chat/channel sheet actions (Mark
as read, Share secret, Region & scope, Chat icon, Delete history, Log in
again, Reset path), contacts overflow (Search, Auto-add settings, Blocked
list), URL menu (Open in web, Create QR), power menu (Power off, Download
mode), plus '(device behind)' and 'Copy internal data to SD'. The regex
now accepts the macro prefix and strips glyphs exactly like runtime TR().
All 16 translated in all 13 languages; files at ver 4, audit green at 731
keys per file.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-04 16:48:50 +02:00
Kaj SchittecatandClaude Opus 4.8 6a5738946f i18n: wrap the bare-literal sheets and buttons — months-old translations finally render
The channel/chat long-press sheets (Mark as read, Share secret, Region &
scope, Chat icon, Delete history, Log in again, Reset path, Blocked users),
the contacts overflow menu, the URL menu, the power menu rows, five confirm
buttons and four page labels were built from glyph-concatenated string
literals that never passed through TR() — so their translations, which have
existed in the table (now the .lang files) for months, never rendered. All
wrapped now; TR() strips the glyph prefix and rebuilds the label, which is
exactly the machinery it grew for. Only five keys were genuinely new
(Install, Map, Save report, Set, Stop) — translated in all 13 files, bumped
to ver 3 (immutable /apps/lang/3/), audit fully green.

Also fixes the audit appender's substring presence check: '© OpenStreetMap'
contains 'Map', which silently skipped the new Map row in every file.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-04 16:41:59 +02:00
Kaj SchittecatandClaude Opus 4.8 7ec5e92414 lang: language files are versioned like apps — Update button, immutable URLs
Each .lang carries a '# ver:' header (missing = v1) and langs.json carries
the catalog ver. An installed file whose ver differs from the catalog shows
a blue Update on its row (sub line shows 'code.lang vN'); updating the
ACTIVE language reboots on success to apply the fresh overlay, updating an
inactive one just refreshes the row. Downloads go to the immutable
/apps/lang/<ver>/<code>.lang path so the day-long edge cache can never
serve a stale file after a translation bump — same lesson as the flasher's
mutable-latest cache bite. The boot self-heal resolves the ver from the
catalog on the worker (flat-path fallback) and never reboots on its own.
Canonical files bumped to ver 2 (the full-coverage sweep).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-04 16:28:42 +02:00
Kaj SchittecatandClaude Opus 4.8 911d665be5 i18n: the baked-in translation table is gone — languages are files, full sweep included
The 727-row / 13-column kI18n table no longer compiles into the firmware.
TR() now consults only the loaded .lang overlay (bsearch) and falls back to
English. deploy/apps/lang/*.lang is the canonical translation source —
translators edit those files and PR them; scripts/build/i18n-audit.py
(replacing the retired generator) verifies every TR() key in the source is
covered by every language file, byte-accurate against C escapes. Reclaims
~400 KB of flash on every board: T-Deck 85.1->75.3%, V4 81.4->71.6%,
Pager 87.7->77.8%.

The sweep: 94 keys had no translations at all — everything Remote/web
access, the P4 antenna dialogs, history-limit texts, DND, keyboard-light
toasts, GPS/clock states and all the new Store/Language strings. All are
translated in all 13 languages now; the audit reports full coverage (707
keys per file).

Migration: a built-in language picked before this claims its file code at
boot; if the file is missing the download self-heals in the background
(that boot runs English, the next applies it) and retries every boot until
it lands. Wi-Fi-less devices stay English until they get one chance to
fetch — the honest cost of un-baking 400 KB.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-04 16:21:18 +02:00
Kaj SchittecatandClaude Opus 4.8 f3f421398c store: three tabs (Apps / Built-in / Languages) + languages as downloadable files
The Lua Store is now a segmented three-tab page. Apps keeps the catalog and
sideloaded scripts; Built-in holds the show/hide switches; Languages is new.
Opening is instant: the card re-listing that ran synchronously on open now
runs on the net worker (request/done flags like the catalog), and the app
cache pre-warms at boot. Card text no longer runs under the action button —
title and description both stop at the button column.

Languages as files: every translation column of the i18n table exports to a
.lang file (scripts/build/gen-lang-files.py -> apps/lang/<code>.lang on the
firmware host + langs.json catalog). The device downloads them from the
Languages tab into <data>/lang/, and at boot the active file loads into
PSRAM and overlays TR() lookups via binary search — the file wins, then the
built-in ui_lang column, then English, so a partial or hand-edited file
degrades gracefully. The file's '# base:' header names that fallback column.
Users can drop their own .lang files on the card (or edit a downloaded one)
for languages the firmware never shipped; switching reboots to apply, same
as the Settings picker (which now also clears the file overlay). Prefs v48
appends the active file code, trailing per the schema law.

This is stage one of un-baking translations from the image: the mechanism,
catalog and files ship now while the built-in table stays as the fallback;
the flash win lands when the table columns are dropped after field proof.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-04 15:40:35 +02:00
Kaj SchittecatandClaude Opus 4.8 2791a1aa12 lua: drop the two invented apps — the catalog is conversions of existing apps only
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-04 14:01:01 +02:00
Kaj SchittecatandClaude Opus 4.8 c2161b4650 lua: delete the native RF Monitor + Airtime — the Lua Store versions replace them
Kaj confirmed the 1.3 ports stand on their own, so the built-in versions
are gone, not gated: ~320 lines of monitor page + helpers removed from
UITask.cpp, ChannelUtil.cpp/.h deleted, and every reference with them —
drawer tiles, APPACT_MONITOR/APPACT_AIRTIME actions, dispatch cases, the
popup-registry row, anyPopupOpen and tab-lock checks, the Store's built-in
hide toggles, and their doc-capture shot (Lua apps stay out of the tour;
the tour itself gets revisited separately).

Also new in the catalog: Nodes (contacts by last-heard with freshness
colours) and Breakout (canvas + per-frame tick + drag-to-move paddle).
Five apps now. V4 reclaims ~4 KB, T-Deck ~6 KB.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-04 13:57:28 +02:00
Kaj SchittecatandClaude Opus 4.8 ea58ff3d9a lua: scrollable app pages + real line heights; Monitor scrolls, Airtime gets a Reset button
Two API gaps the ports exposed: wada.ui.scroll(true) makes the app body
scroll vertically (games leave it off so swipes still steer), and
wada.ui.text_h(size) returns the actual font line height — the overlapping
labels came from stacking rows on guessed heights.

Monitor 1.3 lays every row out from measured heights, scrolls, and shows
the full 16-entry heard ring. Airtime 1.3 replaces tap-to-reset with a
Reset button top-right (a tap was too easy to trigger by accident).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-04 13:45:53 +02:00
Kaj SchittecatandClaude Opus 4.8 8f174c63b7 lua: chart matches the built-in styling + Y-axis labels; app layouts stop overflowing
wada.ui.chart now uses the same recipe as the built-in Monitor/Airtime
charts (panel fill, 1px dark border, radius 6, dim grid, no point dots,
2px series) and gains chart:axis(ticks, gutter) for LVGL's Y value labels
— which draw to the LEFT of the chart, so the apps inset it by the gutter.
Labels gain label:width(px) to wrap instead of running off the screen.

Monitor 1.2: dBm axis, 72-point history, wrapped metrics/feed rows.
Airtime 1.2: % axis, and the chart height is now whatever is left after
the detail block is reserved — the text that fell off the bottom was the
chart claiming a fixed height on short screens.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-04 13:41:19 +02:00
Kaj SchittecatandClaude Opus 4.8 fd4659f4ce lua: wada.ui.chart + full radio stats, and RF Monitor/Airtime rebuilt to parity (1.1)
Adds the primitive the ports were missing: wada.ui.chart (two shifting
series, line or bar, settable range) plus rx_events/rx_dropped/tx_pkts/
freq/bw/sf/duty_pct on wada.mesh.stats.

RF Monitor 1.1: live RSSI + noise-floor chart, peak with tap-to-reset,
link-margin grade, RX/min, radio params, colour-coded heard feed, narrow
and wide layouts. Airtime 1.1: live + average utilization bars, peak,
rx/tx air split, duty ceiling, tx budget, packet counters, tap to restart
the window. Native versions stay compiled in until these are field-proven.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-04 13:34:06 +02:00
Kaj SchittecatandClaude Opus 4.8 47ec8a2293 lua: install button flips to Remove without reopening + the two ports are labelled SDK examples
The Store trusted a directory re-listing taken immediately after writing the
files; on FAT that can lag, leaving the row reading 'Get' until reopen. The
install result now updates the installed list directly (rescan still runs).

The RF Monitor / Airtime ports lacked the native tools' depth on device, so
they ship as 'RX Log (example)' / 'Air Bars (example)' — wada.mesh demos for
app authors. The native versions remain the real tools; full-parity ports are
future work, not a day-one swap.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-04 13:29:10 +02:00
Kaj SchittecatandClaude Opus 4.8 14d5b06560 lua: Phase 3 — wada.mesh + wada.net, RF Monitor + Airtime as catalog apps, IDF wiring
wada.mesh: read-only contacts/rx_log/stats/self via thin UITask bridges
(no mesh types cross into the host TU). wada.net.http_get: async on the
existing net worker, http-only, 32 KB cap, one in flight per app, callback
delivered on the UI thread under the guarded pcall. RF Monitor and Airtime
rewritten as Lua catalog apps (live on the VPS with snake); their native
versions deliberately stay compiled in for beta_59 until the ports are
field-proven. lib/lua joined both IDF component builds (P4 verified on
RISC-V). Catalog source tracked at deploy/apps/.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-04 13:18:12 +02:00
Kaj Schittecat 077bc12f24 chore: refresh Mesh America catalog for beta_50 2026-07-29 15:11:46 +02:00
Kaj SchittecatandClaude Opus 4.8 57f206220d meshamerica: add LilyGo T-Lora Pager tile (both radio variants)
The pager folds into Mesh America's existing 'LilyGo T-Lora Pager' tile
(exact name match). Generator now supports per-radio 'variants' — emits one
gui firmware entry per build under a single tile, [LR1121]/[SX1262] title
prefix matching their stock pager labelling. Stable (beta_46) bins verified.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-19 20:26:02 +02:00
Kaj SchittecatandClaude Opus 4.8 756bb9f821 site: group device selector by manufacturer + add search + P4-LCD card
Rework the homepage device picker into collapsible per-manufacturer boxes
(LilyGo / Heltec / Elecrow / RAKwireless / Nicolai Electronics / Attaky) with
a live search that filters by device name and manufacturer and auto-expands
matching groups. Add the T-Display P4 TFT-LCD (HI8561) card, wired to a
client-built ESP32-P4 manifest + the immutable per-tag BETA archive bin (no
feed change needed), and relabel the existing P4 card (AMOLED). All existing
install-button + download + channel-toggle wiring preserved.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-19 19:30:40 +02:00
Kaj SchittecatandClaude Opus 4.8 99423e5bf8 touch: beta_47 — Discover app (find/list/map nearby nodes + wardriving), Attaky release wiring, P4 LCD
- New Discover app: active NODE_DISCOVER sweep -> signal-ranked nearby list, tap-to-add-contact, GPS wardriving log (SD CSV) + signal-coloured coverage overlay on the map. Board-agnostic.
- T-Display P4 TFT-LCD (HI8561) variant support (WADA_P4_LCD build; AMOLED bin untouched).
- Wire the Attaky Core board (#158, @attakygit) into the release matrix + web flasher.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-19 17:45:05 +02:00
Kaj Schittecat cab0c81208 chore: refresh Mesh America catalog for beta_46 2026-07-19 16:24:58 +02:00
Kaj Schittecat 3fbc3e3582 chore: refresh Mesh America catalog for beta_45 2026-07-16 23:02:56 +02:00
Kaj SchittecatandClaude Opus 4.8 36e49d4d9e site: add two community videos (Signal Trek, KDHD Stuff 'WadaMesh Update')
Newest-first: Signal Trek's 'The Firmware That Finally Makes It Worth It'
(~Jul 12) on top, then KDHD Stuff's 'WadaMesh Update! Supercharge Your T-Deck'.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 22:00:42 +02:00
Kaj SchittecatandClaude Opus 4.8 ae02fb6238 release: beta_45 infra — T-LoRa Pager + T-Display P4 in the flasher matrix
- release.sh: add both T-LoRa Pager envs (LR1121 + SX1262) to the PIO build set.
- gen-flasher-meta.py: add pager (×2) + T-Display P4 manifests; P4 carries
  chipFamily ESP32-P4 (per-board now, not hardcoded S3).
- site: LilyGo T-LoRa Pager board card (both radio variants) with the
  partially-supported badge; the P4 card already landed last cut.
- release-notes/beta_45.txt (leads with @codemonkeybr's pager port).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 20:32:31 +02:00
Kaj SchittecatandClaude Opus 4.8 f029dcc744 site: per-board support-level badges + T-Display P4 board box
Every flasher board card now carries a support badge with a hover popover:
Heltec V4 + TFT and the T-Deck are 'Fully supported' (the two primary dev
boards); ThinkNode M9, RAK Tap V2, Heltec V4-R8, Tanmatsu and the new
T-Display P4 are 'Partially supported' — the popover spells out that the
core (LoRa mesh communication, chat, phone-app link) works while the rest
is still settling. New T-Display P4 card wired to the beta channel
(manifest-tdisplay-p4.json), same new-board pattern as the M9/RAK cards.

NOTICE: extend the ES8311 attribution to cover the P4's P4Audio.cpp
(esp-bsp-derived register sequence, re-based onto Arduino Wire).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 19:24:55 +02:00
Kaj SchittecatandClaude Opus 4.8 a87ebe1e81 site: tidy the per-board download link — two-line 'Download .bin' + version caption
The first cut used an inline-flex link that wrapped and scattered the icon, text
and version across the narrow card. Restructure as a clean teal 'Download .bin'
row with the version as a small muted caption underneath; drop the double spacing
(the .board flex gap already separates it from Connect).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-14 12:11:27 +02:00
Kaj SchittecatandClaude Opus 4.8 69ffe71bfe site: standalone flasher — per-board "download the .bin" links (find them where Connect is)
Community feedback: the only .bin download was buried in the Launcher path, so
people couldn't find a download at all. Add a compact "or download the .bin" link
to every standalone board card, right under its Connect button. Each follows the
Stable/Beta toggle (beta-only boards stay pinned to latest-beta) and shows the
exact version that feed serves (both channel tags fetched once so every link is
accurate). Downloads the full merged image — the same build Connect flashes.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-14 12:03:43 +02:00
Kaj SchittecatandClaude Opus 4.8 e617bf306c site: Launcher flasher page — Stable/Beta toggle + "you're downloading version X" box
Community feedback (gubbinsgalore): on the Launcher install page there was no way
to tell which version the "Download .bin" gives you (it's the version-less
wadamesh-tdeck.bin), and the Stable/Beta channel toggle only existed on the
Standalone page — so Launcher users were pinned to stable and couldn't grab a
beta app image.

- Add the same Stable/Beta toggle to the Launcher page (the existing wmSetChannel
  JS already drives dl-tdeck + keeps both toggles in sync).
- Add a version box under the download, auto-filled from version.json and updating
  with the toggle: "You're downloading version <tag> · <channel>".

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-14 11:49:31 +02:00
Kaj SchittecatandClaude Opus 4.8 687eb3865c touch: add Heltec WiFi LoRa 32 V4-R8 board (8 MB octal PSRAM + Expansion Kit V2 SD)
The V4-R8 uses an ESP32-S3R8 (8 MB OCTAL PSRAM) which claims GPIO33-37, so
Heltec moved every control signal off them and the Expansion Kit V2 rebuilt the
display/touch/SD block onto a hardware-SPI bus:
  Vext 36->40 (active-LOW) . GPS_EN 34->42 . LED 35->46 . ADC_Ctrl removed
  TFT (LovyanGFX HW-SPI): MOSI=15 SCK=16 MISO=45 CS=47 DC=48 RST=21 BL=44
  touch (CHSC6x, shares board I2C 17/18) . micro-SD (shared TFT SPI, CS=3)

The build defines HELTEC_LORA_V4_TFT too, so it reuses all the V4 touch-UI code;
HELTEC_LORA_V4_R8 gates only the deltas. 8 MB enables the on-device web browser
(CAP_WEB_BROWSER) and the SD card (CAP_SD). Also wires the board into the release
+ web-flasher pipeline (release.sh, gen-flasher-meta.py, the flasher picker).

UNTESTED — no V4-R8 hardware yet. Pin sources: Meshtastic heltec_v4_r8 variant,
Heltec V4-R8 pinmap, and the Expansion_board_V2.03 schematic. All three touch
envs build clean; V4/T-Deck unregressed. On-device TODO: CHSC6x shared-Wire on
17/18, SD_CS=3, and the V2 PAM8904 buzzer.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-14 11:27:40 +02:00
Kaj SchittecatandClaude Opus 4.8 c22c76b704 docs: add Web browser section + screenshots to the site docs
New "Web browser" section in deploy/site/docs.html (and a TOC entry)
covering the on-device text browser and tappable chat links
("Open in web" / "Create QR"), with three new device screenshots
(app_web, app_web_links, app_web_qr). Existing screenshots unchanged.

Also render the reader page inline in the DOC_CAPTURE tour: the reader
paints from the UI-update poll, which doesn't run while the tour blocks
the loop, so the tour now drives the fetch-wait + render itself before
capturing the Web app.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 18:08:43 +02:00
Kaj SchittecatandClaude Opus 4.8 8a2825e7fd docs: cache-bust the map screenshot URL past the Cloudflare edge
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 17:25:40 +02:00
Kaj SchittecatandClaude Opus 4.8 5f40653e58 docs: restore the previous (better) map screenshot
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>
2026-07-12 17:23:44 +02:00
Kaj SchittecatandClaude Opus 4.8 d787d77365 docs: web control panel + Remote/VNC guide, refreshed device shots
- 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>
2026-07-12 17:18:20 +02:00
Kaj Schittecat 9da367d0bc chore: refresh Mesh America catalog for beta_40 2026-07-11 12:28:49 +02:00
Kaj Schittecat 326d34aaec site: add BFGNeil community video (newest first) 2026-07-10 13:05:02 +02:00
Kaj SchittecatandClaude Fable 5 cb08116eae flasher/site: ThinkNode M9 + RAK WisMesh Tap V2 web-flash support
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>
2026-07-09 13:55:35 +02:00
Kaj Schittecat 8718e49827 chore: refresh Mesh America catalog for beta_35 2026-07-07 09:50:26 +02:00
Kaj Schittecat 5d9a184314 chore: refresh Mesh America catalog for beta_33 2026-07-05 23:50:32 +02:00
Kaj Schittecat 816045bb6b chore: refresh Mesh America catalog for beta_31 2026-07-05 21:13:27 +02:00
Kaj SchittecatandClaude Opus 4.8 5623237951 site: dismissible donate banner -> support the MeshCore founders
Sticky top banner asking would-be donors to help the MeshCore founders instead — links to the blog post (Learn more) and the givealittle donation page (Donate). Dismissible (x + localStorage), and it shifts the corner FAB rails down while shown so they don't collide with the close button.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-04 15:22:37 +02:00