The phone branch (portrait/landscape canvas from the window, the phone's
own keyboard, manifest and icons, a one-row bar, mobiletest) next to the
no-threads bundle selection and the Wi-Fi/https chooser messaging. Both
bundles share the one page, so the http copy gets the phone layout too.
Checked after the merge: host build, smoke, webtest (tiles, location,
Store, backups), nodetest (15/15 incl. the location privacy rule),
mobiletest (iPhone 14, Pixel 7, iPad); on the no-threads bundle over
http://<LAN IP>: the browser tour, the mock-node tour, and the mobile tour
(layout + keyboard pass; Bluetooth is not offered there, by design).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- the canvas is sized from the space below the bar at about one CSS pixel per
canvas pixel, whole device pixels where possible (crisp): iPhone 14 390x616,
Pixel 7 360x692, iPad 540x686; landscape by height and the window's aspect
(664x358, 734x330, 720x506). A computer keeps the framed 800x480. Same
large-screen layouts, which size themselves from the display.
- a turn restarts the UI in the other layout only when the aspect flips and
the width changes (a keyboard is no turn), never while typing
- the phone keyboard: no focus ring or tab hotkey letters for it
(CAP_NATIVE_KEYBOARD in the nav-keys block), keeps up while tapping the UI
mid-edit; IME composition followed live
- the page: slim one-row bar (slimmer on its side), safe areas, link help as
an overlay, home-screen icons (icons/app-icon.svg + render.mjs, maskable)
- tests: web/build.sh mobiletest (iPhone 14, Pixel 7, iPad): fill > 90 %,
UTF-8 + emoji through the hidden field, IME composition, send, turn and
back with the message kept, chooser, manifest. Labels found by text
(wadamesh.findText) so taps work at any size; the UTF-8 storage check
searches bytes. webtest's phone step checks the portrait canvas.
- WEB_BUILD.md "Phones and tablets"; the stray docs/tmp shots removed
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The http copy of the page (it can reach a node's plain ws://) is never
cross-origin isolated, so it has no SharedArrayBuffer and no pthreads.
web/build.sh web-nothreads (also built by web) makes a second bundle with
WADA_NO_THREADS: the FreeRTOS tasks are Emscripten fibers (Asyncify) run
after each UI step, every wait a yield point (freertos_coop.cpp); delay,
yield and esp_timer follow; HTTP from a task is an async fetch it waits
for. The page loads nt/wadamesh.js when it is not isolated (serve.py
serves nt/), and never shows developer hints off localhost.
Chooser: on http, Wi-Fi with a link to the https copy for USB, Bluetooth
and the location; on https, "Open the Wi-Fi version" opens the http copy
with the address (#wifi=...), and a ws:// connect that fails there as mixed
content (or times out) explains it with the same button.
Checked against http://<LAN IP>: the browser tour (persistence, tiles,
Store, backups) and the mock-node tour pass on the no-threads bundle.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Store: the firmware's own catalog, install and language code over
wadaHttpRequest; http://firmware.wadamesh.com/apps/... becomes the
same-origin fw/apps/... (serve.py and the nginx snippet proxy exactly those
paths). The update check, OTA images and reports stay unreachable
(CAP_FW_UPDATE_CHECK 0); the Store badge fetches the catalog on its own.
CAP_STORE_DOWNLOADS keeps the baked apps and languages as the offline first
load and turns the Apps tab and language downloads on. Other servers
(wada.net) are fetched directly: CORS decides, no open proxy. Installs and
app data live in the backend's IndexedDB data dir.
Backups: the stand-in exports and imports the device's backup file (no
private key); CAP_FILE_HANDOFF downloads an export and imports a file picked
on the computer through the UI's own confirm + import. A restart waits for
the node commands an import queued. The bar's More menu backs up and
restores the whole data dir of this browser as one .tar.
webtest: a Store app installed via the proxy survives a reload and runs; a
backup goes out (download) and into a fresh profile (file picker); a
browser backup restores the app and the settings into another one.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Location: the web board has CAP_GPS 1. web/platform/WebLocation.* is its
SensorManager + LocationProvider, fed by navigator.geolocation (page shell,
only while the UI's GPS is on, the prompt deferred to a tap) or WADA_GPS on
the host, so the GPS switch, status, map centring, distances and
wada.sys.gps (with speed/course) work unchanged. The position reaches a
connected node (CMD_SET_ADVERT_LATLON, displaced by the Location privacy
radius) only while the node's advert-location setting is "share"; the demo
moves its cast to the visitor's location.
Map tiles: wadaHttpRequest serves the firmware's default tile server only:
a synchronous emscripten_fetch from the tile_fetch pthread to the
same-origin tiles/ path (serve.py proxies it; nginx location for the site),
plain HTTP natively. WiFi reports connected while the browser is online.
Fixes the static-init order of the host VFS (the UI's own tiles LittleFS
copied an empty impl).
Polish: Lua apps that need the board's own radio carry "radio": "own" in the
catalog (baked under CAP_RADIO_TOOLS, greyed in the Store); mouse wheel and
trackpad scrolling; Settings > Wi-Fi / Bluetooth explain the page bar
(CAP_WIFI_SETUP / CAP_BT_SETUP); no Download mode (CAP_USB_FLASH); no
receiver settings on Settings > GPS (CAP_GPS_RECEIVER).
Tests: webtest checks the map centres on Playwright's Ghent location and
shows tiles; nodetest checks the location privacy rule against the mock node.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
pthreads for the UI's background tasks, LVGL + the UI loop on the browser main
thread (emscripten_set_main_loop), IDBFS at /data loaded before main() and
persisted after writes settle and on hide/pagehide; Reboot persists and
reloads. Page shell in the site's Night palette: Demo badge, Connect
placeholder, Reset demo data, Fullscreen; canvas scaled with aspect kept,
crisp at whole device pixels; pointer = finger, keyboard = CardKB codes.
serve.py and nginx-snippet.conf send COOP/COEP; without isolation the page
says so. Release -O2 (wasm brotli 1.14 MB), web-debug variant with -g.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>