Each browser profile and origin keeps its own sync id (browsers that synced
under the shared id keep it), so every copy gets every message it has not
had. iOS: open the keyboard from touchend too, and a Keyboard button when the
UI edits a field without the phone keyboard up.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Like meshcomod: a short notice in the chooser, Continue opens
wifi.app.wadamesh.com with the address, which connects straight away and
says where the visitor is.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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>
Work in progress, handed to the web-mobile branch:
- the canvas size is the board's resolution, picked at boot from the window
(WebDisplay runtime size, host --size WxH); a phone held upright gets a
480-wide portrait canvas on the large-screen portrait layouts; turning
the phone restarts the UI in the other layout (data kept)
- the phone's own keyboard: a hidden textarea opened inside the tap's
gesture, its edits forwarded as UTF-8 text + Backspace/Enter through the
CardKB path (cardKbUiKey passes BLE_KEY_TEXT through); CAP_NATIVE_KEYBOARD
keeps the UI's own keys down; the screen slides above the keyboard
- viewport/safe-area/overscroll, web app manifest, More menu (Reset too),
bar stays on one row, chooser notes for iOS and the http/https copies,
a mixed-content ws:// failure explains the http copy
- web/scripts/mobile-tour.mjs (iPhone 14, Pixel 7)
Known open: the mobile tour's UTF-8 storage check fails (the bubble shows
the text; the stored history does not match the needle; on desktop,
Playwright's insertText of a non-ASCII char sends no keydown and is lost).
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>
A node on a network this computer cannot route to (Kaj's V4 on another VLAN)
never answers a WebSocket at all, so the page sat on "Connecting" with the
demo's placeholder name and nothing loaded. The WebSocket now gives up after
6 s and retries; after two misses the bar says "Can't reach your node" with
what to check for that link (same network, address, cable, Bluetooth) and a
Back to the demo button.
Also: --pre-js files are now LINK_DEPENDS, so editing the transport JS relinks
wadamesh.js (it silently stayed stale before).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Connect opens a chooser with the transports the browser has (Web Serial and
Web Bluetooth on Chrome / Edge, WebSocket to the node's port 8765 everywhere,
with a note where some are missing). The choice is kept and the UI reboots
into it; each backend has its own data dir (/data/demo, /data/node-<key>) so
demo and node data never mix; a reload reconnects; Disconnect returns to the
demo. The bar shows Demo / Connecting / Connected: <node> over <link> /
Reconnecting, and Reconnect / Pair again when the browser needs a click.
Transports retry a lost link and reopen a granted USB port on plug-in. A node
skips the demo's setup wizard. A link lost mid-handshake no longer reports
ready. web/build.sh nodetest: the flow end to end against the mock WS 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>