12 Commits
Author SHA1 Message Date
Kaj SchittecatandClaude Opus 5 b15d422bdc touch: beta_68 — Lua SDK gains the map, lists, packet delivery and discovery
The four capabilities that kept third-party apps a sketch of a built-in one:

* wada.map      the firmware's own tiles, projection and cache inside an app
                page, with its own capped pool so it never evicts the Map tab's
* wada.ui.list  the missing "pick one of N" widget; rows are real buttons, so
                keyboard and trackball nav walk them for free
* on_packet     each frame delivered once instead of polling a 16-deep ring,
                which sampled rather than observed
* wada.mesh.discover  the active zero-hop probe, behind its own permission
                because it spends every neighbour's airtime, not just ours

Plus the surface those need to be useful: packet identity reported only where
the frame actually carries it, exact micro-degree coordinates (Lua is built
LUA_32BITS, so its floats were quietly costing a metre), altitude and satellite
time, wada.geo, wada.ui.input, named and one-shot timers, http_post, windowed
fs.read, and wada.sys.env on a hardware gate rather than the memory one.

Fixes:
* Both ESP32-P4 targets could not link. g_wifi_last_disc_reason was defined in
  src/main.cpp, which the IDF builds never compile, so all nine S3 envs stayed
  green while Tanmatsu and T-Display P4 were dead.
* Map zoom level was invisible in +/- buttons mode; the readout was hidden with
  the slider it was anchored to.
* Hungarian and Dutch held each other's "No SD card" translation.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-21 21:50:01 +02:00
Kaj SchittecatandClaude Opus 5 25c76a3798 i18n: the audit could not see strings reached through a table, and one was missing
pisti87 asked whether the obsolete entries in the language files will be removed
(#262). Measuring it first was the right move, because the tool that would have
driven that cleanup was wrong.

source_keys() only ever matched TR("literal"). Strings reached indirectly, as in
TR(kSettingsCats[c].label), were invisible to it, so every settings-category name
looked unreferenced: About, Backups, Language, MQTT bridge, App permissions and
the rest. Anyone pruning the .lang files on that output would have deleted live
translations for some of the most visible labels in the UI.

The audit now also pulls literals out of any table whose name appears inside a
TR(...) subscript. Deliberately greedy: over-collecting keeps a translation alive,
under-collecting deletes one.

That immediately found a real gap it had been hiding. "App permissions", the
settings category added with the permissions page, was missing from all 13
languages and the old audit reported everything as covered. Added, builtin
regenerated, published as language v15.

Also adds --obsolete, the reverse check pisti87 actually needs: rows in each .lang
file that no TR() key matches. It reports and never deletes, and says plainly that
these are candidates to check rather than a delete list, for the reason above.
Numbers now: 950 keys reachable, hu.lang carries 1044 rows, 94 unreferenced. The
earlier figure was 119, so 25 of those were the false positives just fixed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-21 19:19:21 +02:00
Kaj SchittecatandClaude Opus 5 8cc4a2b981 apps: add 2048 to the Store (pisti87, #265)
Community submission, reviewed before publishing rather than taken on trust.

The whole API surface it touches is ui.label / ui.canvas / ui.colors, sys.random,
timer.every(100) and two store keys for the high score. No network, no mesh
transmit, no filesystem, no dynamic code loading (load/loadstring/require/dofile),
no _G, metatable, debug or coroutine tricks, and no long-string or numeric-escape
obfuscation. Persistence is one bounded value. Nothing in it can reach past its
own window, so it needs no permission grant.

It also uses the real lifecycle contract — an app table with on_open/on_tick/
on_input/on_close, returned from the chunk — which is worth noting because the
published SDK page told people to do it a different way until today. He got it
right by reading the shipped apps.

Parse-checked against the firmware's own vendored Lua 5.4.7 rather than a system
interpreter, so the syntax is verified by the same parser the device runs. Not
run on hardware here; pisti87 reports it working on a T-Deck Plus.

deploy-apps.sh now also verifies that every apps.json entry has its
<id>/<ver>/<id>.lua and .json on disk. An entry pointing at a missing file lists
in the Store and then fails to install with nothing on screen to explain why —
the same failure the language check already prevents.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-13 20:56:44 +02:00
Kaj SchittecatandClaude Opus 5 f6b005e1be apps: SDK Test 1.1 -- fix the entry point (1.0 rendered nothing)
My fault, and a good demonstration of why the bench test existed: 1.0 defined
app.start(), which the host never calls. The Lua app contract is on_open(w, h) /
on_input / on_tick / on_close, so 1.0 loaded cleanly and drew an empty screen --
no error, nothing to see.

Also takes the body width from on_open's argument instead of assuming 300 px, so
the rows wrap correctly on a Pager or a Tanmatsu rather than only on a T-Deck.

Shipped as 1.1 in its own directory: version dirs are immutable, and the catalog
bump is what makes a device fetch it at all.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-13 14:20:46 +02:00
Kaj SchittecatandClaude Opus 5 e51db4350e apps: publish "SDK Test" to the store
A bench tool for the extended Lua SDK, in the store so it can be installed from
the device instead of pulling the SD card -- and so anyone with a supported board
can check the SDK on hardware we do not own.

It reports what it finds rather than asserting silently: capabilities, clock,
battery, GPS, a filesystem round trip, and two things that must FAIL --
a second write inside the rate limit ("REFUSED as designed") and a
'../identity' path ("REJECTED"). A traversal that succeeded would print
"LEAKED - BUG", which is the one line nobody should ever see.

It also has a button that really transmits to Public, so the consent flow can be
walked end to end: first tap refused + prompt, Allow, send, then refused again
inside the 5 s airtime limit. The description says it transmits, in those words,
because a store listing should not surprise anyone with a radio.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-13 14:07:48 +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