Skip to content

wasm demo: persist files and NVS settings in IndexedDB - #1900

Closed
MitchBradley wants to merge 11 commits into
mainfrom
WasmPersistFs
Closed

MitchBradley wants to merge 11 commits into
mainfrom
WasmPersistFs

Conversation

@MitchBradley

@MitchBradley MitchBradley commented Oct 6, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

The wasm demo kept LocalFS, the SD card and NVS only in Emscripten's in-memory FS, so every edit, upload and $ setting was lost on reload. This makes them persist per browser, and adds file transfer controls to the demo page.

  • Persistence: /native_localfs, /native_sd and /native_nvs are IDBFS mounts, restored before fluidnc_start(). The build links -lidbfs.js and exports FS.
  • Seed files now live as real files under demo/seed-localfs/ and demo/seed-sd/, each listed in that directory's manifest.json, and are fetched at boot. This is cherry-picked from the esp32p4 branch (75afbc31) and replaces the embedded TEST_CONFIG_YAML/TEST_GCODE strings. A seed file is staged only if it's missing from the restored filesystem, so user copies override it while seed files added in later deploys still appear. Seeds are written as raw bytes with FS.writeFile, so binary files are safe.
  • Saving: after each WebUI file operation, on a 3 s timer (FluidNC's own writes such as $CD=, $ settings and terminal edits never pass through the page), and on pagehide.
  • Reset files and settings button: reloads with ?reset, and the new page deletes the databases before opening them (the current page holds them open).
  • Upload / download controls for LocalFS or SD that work with no WebUI loaded (or with the Tablet UI). They use FS.writeFile/FS.readFile, so binary files are not truncated at a NUL.
  • WebUI file requests that arrive during startup stay queued until the restore finishes.
  • Binary files over the WebUI file bridge: a fluidnc-fs-request write may carry bytes (an ArrayBuffer) instead of content, and a read may set binary: true to get an ArrayBuffer back. Both use FS.writeFile/FS.readFile, with the path checked by a new fluidnc_fs_resolve() that applies the same safe_path() rule as the other entry points. The string forms are unchanged, so existing text-only bridges (FigUI) keep working. The WebUI 3 bridge (WebUI-mm) uses the binary forms.
  • Realtime commands skip the command queue: a fluidnc-shim-command that is a single realtime byte (feed hold, cycle start, reset, ?, overrides) is sent at once and answered immediately. Before this, it waited behind any command in flight, including a $SD/Run, whose ok arrives only when the job ends, so pause and reset from WebUI 3 did nothing until the job had finished.
  • WebUI menu: adds WebUI 2 (MitchBradley/ESP3D-WEBUI) and relabels the entries by version (FigUI, WebUI 2, WebUI 3). Every entry now loads that project's latest release instead of a pinned tag, so the demo keeps up with each WebUI's releases with no change here. An entry works once that project publishes a release that includes the bridge.
  • "From LocalFS" WebUI entry: loads the WebUI the way FluidNC's web server finds it on a controller: LocalFS index.html, else index.html.gz. Whatever build is uploaded there persists, so any WebUI can be tried without a GitHub release. $HTTP/IndexFile (Serve multiple WebUIs from HTTP/UIDir at /ui/<name>/ #1885) isn't supported yet, because the WASM build leaves out WebUI/, where that setting is defined.
  • Removes the unused fluidnc_lock_readonly(), whose rationale was that nothing persists.

If IndexedDB is unavailable (e.g. some private windows), the page logs a warning and behaves as before.

Test plan

  • pio run -e wasm builds
  • Tested in a browser by hand: file download via FigUI (with the bridge fixes in Add FluidNC WASM demo bridge support figamore/FigUI#29)
  • Edit config.yaml, change a $ setting, upload a file, reload: all persist
  • Reset files and settings restores the built-in copies
  • Upload and download a binary file (image or .gz) through the WebUI 3 bridge; it arrives intact
  • Pause and reset during an SD job take effect at once (WebUI 3 in the demo)
  • "From LocalFS" loads an uploaded index.html.gz, and it persists across reloads
  • Fresh browser profile: all four seed files (config.yaml, preferences.json, square.nc, Rotor.nc) appear

🤖 Generated with Claude Code

Copilot AI balanced review requested due to automatic review settings October 6, 2026 23:42

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Reset can silently retain data, and immediate reloads can interrupt an unfinished persistence sync.

Review effort: Balanced
Findings: 2 Medium severity · 3 Low severity

Open (5)
What changed in this PR

Adds browser persistence and file-transfer controls to the WebAssembly demo.

Changes:

  • Mounts LocalFS, SD, and NVS through IndexedDB-backed IDBFS.
  • Adds upload, download, reset, and conditional seed-file handling.
  • Removes obsolete read-only locking.
File Description
FluidNC/​wasm/​wasm_fs_bridge.cpp Removes obsolete read-only support.
FluidNC/​wasm/​pio_toolchain_wasm.py Links IDBFS and exports FS.
FluidNC/​wasm/​demo/​index.html Implements persistence and file controls.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread FluidNC/wasm/demo/index.html Outdated
Comment thread FluidNC/wasm/demo/index.html Outdated
Comment thread FluidNC/wasm/demo/index.html Outdated
Comment thread FluidNC/wasm/demo/index.html Outdated
Comment thread FluidNC/wasm/pio_toolchain_wasm.py Outdated

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment thread FluidNC/wasm/demo/index.html Outdated
Comment thread FluidNC/wasm/demo/index.html Outdated

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Startup can reorder realtime commands, and concurrent tabs can overwrite persisted files with stale IDBFS snapshots.

2 open findings
2 resolved since last review
Previously missed (2)

In code that hasn't changed since last review

Low severity Mark async operation messages as live status regions

FluidNC/​wasm/​demo/​index.html:52

These spans receive asynchronous reset/upload success and error messages, but they are not live regions, so screen-reader users are not notified when the operation completes or fails. Mark both status containers with role="status" so their updates are announced without moving focus.

Low severity Correct documentation placement for filesystem functions

FluidNC/​wasm/​wasm_fs_bridge.cpp:252

The existing “Returns the file's content” paragraph now immediately precedes fluidnc_fs_resolve(), even though it documents fluidnc_fs_read(). Move that paragraph below the new resolver so each exported function's documentation describes the function that follows it.

🧠 Review effort: Balanced

Comment thread FluidNC/wasm/demo/index.html
Comment thread FluidNC/wasm/demo/index.html Outdated

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Persistence acknowledgements, multi-tab fallback behavior, and unpinned executable WebUI releases present unresolved reliability and security risks.

2 open findings
2 resolved since last review
Previously missed (1)

In code that hasn't changed since last review

Low severity Keep manifest contents description accurate as files change

FluidNC/​wasm/​README.md:60

This description is incomplete: the manifests currently seed config.yaml, preferences.json, square.nc, and Rotor.nc, not just a config and one sample job. Describe the contents generically so the documentation remains correct as manifests change.

🧠 Review effort: Balanced

Comment thread FluidNC/wasm/demo/index.html
Comment thread FluidNC/wasm/demo/index.html Outdated

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Seed deletions can reappear after reload, and WebUI mutations are acknowledged before persistence finishes.

1 open finding
2 resolved since last review
Previously missed (1)

In code that hasn't changed since last review

Medium severity Persist built-in file deletions across reloads

FluidNC/​wasm/​demo/​index.html:720

Deleting any built-in seed file is not persistent: IDBFS restores the deletion, then this missing-path check recreates the file on every reload. That also makes the “Reset files and settings” button unnecessary for restoring deleted built-ins. Track which seed paths have already been introduced (or persist tombstones) so only genuinely new deploy seeds are filled while user deletions remain deleted.

🧠 Review effort: Balanced

Comment thread FluidNC/wasm/demo/index.html Outdated
Comment on lines +573 to +575
if (msg.op !== 'list' && msg.op !== 'read') {
savePersistentFs();
}

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in daba1a4. handleFsRequest() now waits for savePersistentFs() before answering a write, delete or mkdir, as the page's own upload does. A failed save still answers ok, since the operation itself succeeded in memory, and logs a console warning. The answer goes to the frame that asked. One correction to the scenario: a WebUI reload only reloads the iframe and doesn't abort the save, which runs in the demo page. The case this fixes is closing the demo tab right after a change.

MitchBradley and others added 11 commits October 9, 2026 08:50
The demo's LocalFS, SD card and NVS directories lived only in MEMFS, so
every edit, upload and $ setting was lost on reload. Mount IDBFS on
/native_localfs, /native_sd and /native_nvs and restore them before
fluidnc_start(). Built-in seed files are staged only when missing, so
user copies override them while seed files added in later deploys still
appear. Saves happen after each WebUI file operation, on a 3 s timer
(writes FluidNC makes itself never pass through the page) and on
pagehide.

The demo page also gets:
- a "Reset files and settings" button, which reloads with ?reset so the
  databases are deleted before anything opens them
- upload and download controls for LocalFS/SD that work with no WebUI
  loaded, written with FS.writeFile/readFile so binary files survive

Removes fluidnc_lock_readonly(), unused and premised on nothing
persisting.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Replaces the embedded TEST_CONFIG_YAML/TEST_GCODE JS string constants
with plain files under demo/seed-localfs//seed-sd/, fetched client-side
at boot (seedFs() in index.html). No build step needed: Netlify and
serve.py already serve everything under demo/ as static files, and
same-origin fetch() isn't affected by the COOP/COEP headers required for
SharedArrayBuffer. Each directory's manifest.json lists what's there,
since a static host can't be asked to list a directory's contents itself.

Cherry-picked from 75afbc31 (esp32p4 port branch) and adapted to the
IDBFS persistence: seedFs() stages only files missing from the restored
filesystem, and fetches bytes and writes them with FS.writeFile instead
of going through stageFile(), whose C-string content argument would
truncate a binary seed file at its first NUL. A missing manifest or
failed fetch is logged and skipped rather than blocking startup.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Reset only counts a database as deleted on onsuccess. While another
  demo tab holds it open (onblocked), say so and keep waiting; on
  onerror, report that saved data was kept instead of carrying on as if
  the reset worked.
- savePersistentFs() now returns a promise that settles once the save,
  including any pass a concurrent call queued, is done. The upload
  handler awaits it before telling the user to reload, and reports when
  the files could not be saved.
- The upload status names the volume the files were written to, not
  whatever the selector says after the upload.
- Fix a missing "as on" in the intro text and a comment naming a
  nonexistent persistFs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The fluidnc-fs-request 'write'/'read' ops passed file content as a C
string through fluidnc_stage_file/fluidnc_fs_read, so a binary file
(image, .gz) was truncated or mangled. A write may now carry `bytes`
(ArrayBuffer) instead of `content`, and a read may set `binary: true` to
get an ArrayBuffer back; both go through FS.writeFile/FS.readFile, with
the path validated by a new fluidnc_fs_resolve() using the same
safe_path() check as every other entry point. The string forms are
unchanged for existing text-only callers.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A 'fluidnc-shim-command' was always queued behind whatever was in
flight. A $SD/Run's ok only arrives when the job ends (Error::Deferred),
so feed hold, cycle start, reset and overrides sent from a WebUI that
uses shim-command (WebUI 3) waited until the job had finished. A
single realtime byte is now sent at once, bare, and answered
immediately, as raw 'fluidnc-shim-send' realtime bytes already were and
as FluidNC's web server handles them on real hardware.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Adds WebUI 2 (MitchBradley/ESP3D-WEBUI, built from the revamp branch,
which includes the WASM bridge in www/js/wasmbridge.js). The existing
entries are relabeled by WebUI version: "WebUI 3 (michmela44)" rather
than "ESP3D-WEBUI (michmela44)".

Every GitHub entry now loads that project's latest release instead of a
pinned tag, so the demo keeps up with each WebUI's releases with no
change here; an entry works once a release that includes the bridge is
published. The comment describing the bridge is updated: it predated
the FigUI and WebUI 2 bridges.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Addresses the Copilot review of 20f4d54:
- A realtime byte (shim-command or raw shim-send) that arrived before
  the WASM module had started was dropped, and a shim-command one was
  still answered ok. They are now held and sent, in order, as soon as
  the module is ready, ahead of the queued line sends, and a
  shim-command one is answered only once it has actually been sent.
  They can't wait in shimSendQueue, which waits for an ok they never
  get.
- The seed-files comment listed only config.yaml and one sample job and
  misspelled the directories; it now describes seed-localfs/ and
  seed-sd/ without listing their contents.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
"From LocalFS (index.html / index.html.gz)" loads the WebUI the way
FluidNC's own web server finds it on a controller (WebUIServer.cpp:
handle_root -> myStreamFile("index.html")): LocalFS index.html, else
index.html.gz. Whatever WebUI build is uploaded there persists across
visits like a controller's own, and any build can be tried without a
GitHub release. If neither file exists, the status line says so.

The $HTTP/IndexFile setting (PR #1885), which the firmware tries first,
is not supported yet: it is defined in WebUI/WebUIServer.cpp, which the
WASM build leaves out.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Addresses the Copilot review of 797d00c:
- Only one tab saves to IndexedDB. Each tab saved its whole in-memory
  copy, one way, so a second tab could put back stale files over the
  first one's changes. The first tab now holds a Web Lock for as long as
  it is open; any other tab restores the saved files and runs, but never
  saves, and a notice says so. An already-open tab doesn't take over
  when the lock frees (its copy may be stale); a tab opened or reloaded
  afterwards does. The lock is waited for briefly, so reloading the
  saving tab doesn't lose it to its own unloading page.
- Realtime bytes keep their arrival order during startup: everything
  before the startup flush is held, and a byte arriving while any are
  held joins the end of the list instead of being sent ahead of them.
- The upload and reset status messages, and the new notice, are
  role="status", so screen readers announce them.
- fluidnc_fs_resolve() moved above fluidnc_fs_read()'s doc comment,
  which it had separated from its function.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Addresses the Copilot review of 30814ee:
- Without navigator.locks, a tab can't know it is the only one, so it
  no longer saves (every tab saving would bring back the stale-tab
  overwrites) and a notice says why. In practice every browser that can
  run the demo, which needs cross-origin isolation, has Web Locks.
- README: the seed files are described without listing their contents
  (it said "config.yaml and a sample job").

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Addresses the Copilot review of ac3c6b1. A WebUI write, delete or
mkdir was answered at once and saved to IndexedDB afterwards, so closing
the tab right after a change the WebUI had reported as done could lose
it (the pagehide save may not finish). handleFsRequest() now waits for
the save before answering, as the page's own upload already did. A
failed save still answers ok, since the operation itself succeeded in
memory, and logs a warning. The answer goes to the frame that asked,
even if the WebUI was switched meanwhile.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@MitchBradley

Copy link
Copy Markdown
Collaborator Author

Superseded by #1913. That PR includes this one's IndexedDB persistence and seed files, and replaces its WebUI bridge features with something simpler: the wasm module now runs FluidNC's own web server, so WebUIs run in the demo unmodified, with no bridge code.

MitchBradley added a commit that referenced this pull request Oct 12, 2026
The demo now behaves like a board on the network.  A WebUI runs only by
browsing to FluidNC, which serves it -- or its built-in file manager --
from LocalFS; the demo needs no WebUI-specific code.

- An address bar preset to fluidnc.local, with Back/Forward/Reload:
  paths work as on a board (/?forcefallback=yes, /files?path=/,
  /command?plain=...), only <$Hostname>.local and the device's IP
  resolve, and other names, https: and other ports fail as a browser
  would report it.  ?browse=<address> opens the demo at an address, and a
  navigation that escapes the device frame is sent back into it.
- Install WebUI: fetches a project's latest GitHub release (through
  webui-proxy, which now returns the release's index.html.gz unchanged and
  which serve.py now also implements for local use) or a local build, and
  uploads it to LocalFS as index.html.gz through FluidNC's /files route,
  removing any old index.html/index.html.gz.  As after an upload to a
  board, the user browses to FluidNC to run it.
- The Upload/Download panel goes through FluidNC too: /files and /upload
  for uploads, their JSON for listings, WebDAV /flash and /sd for
  downloads.
- Files and NVS settings persist across reloads in IndexedDB (one saving
  tab, coordinated with Web Locks), seeded from seed-localfs/ and
  seed-sd/, with a button to reset to the built-in copies.

This supersedes #1900, whose persistence and seeding are included here;
its WebUI bridge features are replaced by the above.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
MitchBradley added a commit that referenced this pull request Oct 12, 2026
* wasm: run FluidNC's own web server inside the wasm module

The wasm build now includes the real WebUI/ web server -- HTTP routes,
WSChannel and WebDAV on unmodified ESPAsyncWebServer -- so a browser can
use it exactly as it uses a controller.  A web page cannot listen on a
socket, so the sockets come from JS instead:

- BrowserAsyncTCP/: the AsyncTCP API that ESPAsyncWebServer builds on,
  over numbered virtual connections that JS opens, feeds and closes with
  fluidnc_vconn_open/send/close; output returns through
  self.fluidncOnVconnData/Close.  All callbacks run on one async_tcp
  thread, as on lwIP's task on ESP32.
- VirtualNet.cpp: the network module -- always up, at 192.168.0.1 (the
  access-point address) and <$Hostname>.local -- providing ESP800/ESP111
  and the $I and ESP420 network lines.
- env:wasm uses Arduino-Emulator, like the posix envs.  Library builds
  are switched to emcc too (previously only project sources were), FS is
  exported to JS, and wasm/IPAddress.h defers to Arduino-Emulator's.
- ESPAsyncWebServer's chunked in-flight pacing is off for this env: with
  instant acks it stalls responses whose source is briefly empty, which
  truncated /command replies such as [ESP400] (upstream fix proposed in
  ESP32Async/ESPAsyncWebServer#488).
- The postMessage WebUI bridge this replaces is removed: ShimChannel.cpp,
  wasm_fs_bridge.cpp and FwInfo.cpp.
- vconn_smoke_test.mjs: a headless Node test of HTTP, upload and
  WebSocket over virtual connections.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* wasm demo: a virtual network from the page to FluidNC's web server

A page can't listen on a socket, so these carry a WebUI's traffic to the
web server inside the wasm module over its virtual connections.  A WebUI
loaded from FluidNC then talks ordinary HTTP and WebSocket, unmodified.

- vnet-sw.js: a Service Worker (scope device/) that relays every
  same-origin request from the device frame -- /, /command, /files,
  /upload, /sd/... -- to the demo page and streams the response back.  It
  decodes gzip (browsers don't decode a Response a worker builds), adds the
  COEP header the isolated page requires of a nested document, injects the
  WebSocket stand-in into HTML, and keeps redirects inside its scope.
- vnet-ws-shim.js: replaces window.WebSocket in the device frame, since
  Service Workers can't see WebSockets.  It treats the device's names and
  the page's own host (on any port) as the device, keeps same-device links
  and window.open inside the frame, and reports in-page URL changes.
- vnet-host.js: on the demo page, the HTTP/1.1 and WebSocket client over
  fluidnc_vconn_*, with a cookie jar (browsers drop Set-Cookie from
  worker-built responses), and helpers for device names and URLs.  Its
  createClient() core has no DOM dependencies.
- vnet_client_test.mjs: drives that client against the wasm server in
  Node -- gzip, cookies, chunked replies, uploads, the WebUI install
  sequence, and a WebSocket session.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* wasm demo: run WebUIs the way a controller does

The demo now behaves like a board on the network.  A WebUI runs only by
browsing to FluidNC, which serves it -- or its built-in file manager --
from LocalFS; the demo needs no WebUI-specific code.

- An address bar preset to fluidnc.local, with Back/Forward/Reload:
  paths work as on a board (/?forcefallback=yes, /files?path=/,
  /command?plain=...), only <$Hostname>.local and the device's IP
  resolve, and other names, https: and other ports fail as a browser
  would report it.  ?browse=<address> opens the demo at an address, and a
  navigation that escapes the device frame is sent back into it.
- Install WebUI: fetches a project's latest GitHub release (through
  webui-proxy, which now returns the release's index.html.gz unchanged and
  which serve.py now also implements for local use) or a local build, and
  uploads it to LocalFS as index.html.gz through FluidNC's /files route,
  removing any old index.html/index.html.gz.  As after an upload to a
  board, the user browses to FluidNC to run it.
- The Upload/Download panel goes through FluidNC too: /files and /upload
  for uploads, their JSON for listings, WebDAV /flash and /sd for
  downloads.
- Files and NVS settings persist across reloads in IndexedDB (one saving
  tab, coordinated with Web Locks), seeded from seed-localfs/ and
  seed-sd/, with a button to reset to the built-in copies.

This supersedes #1900, whose persistence and seeding are included here;
its WebUI bridge features are replaced by the above.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* wasm demo: address review findings

- vnet-sw.js: never route a frame's requests to another tab's FluidNC.
  The frame-to-page mapping is lost when the browser stops the idle
  worker; it used to fall back to any demo page.  It now recovers by
  asking the frame for its page's token (vnet-ws-shim.js answers) and the
  pages which one owns it, and without an answer guesses only when a
  single demo page is open -- otherwise the request fails with 503.
- vnet-host.js: a Content-Length or chunked body cut short by the server
  closing is now an error, not a short success; only close-delimited
  bodies end at close.  A WebSocket closed while connect() is still
  waiting for the server no longer opens an upgrade afterwards.
- index.html: the WebUI installer checks each delete and fails if an old
  index.html survives, since FluidNC would keep serving it.
- IPAddress.h: comment named the old env:wasm_webui.
- vnet_unit_test.mjs: tests for the above against a fake module and fake
  browser clients (they fail on the previous code); the integration test
  also checks the delete replies.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* wasm demo: path-scoped cookie jar; network-order loopback address

- vnet-host.js: the cookie jar now follows RFC 6265 path rules instead of
  keeping one cookie per name.  A cookie belongs to its Path (or the
  directory of the request that set it), is sent only on requests at or
  under that path, most specific first, and Max-Age=0 or a past Expires
  removes it.  FluidNC's sessionId is set for "/", and the HTTP/UIDir
  WebUIs of #1885 set a uiSession cookie for "/ui/<name>/" that must reach
  only that WebUI; sending one UI's uiSession everywhere defeated that
  isolation.
- BrowserAsyncTCP: keep the client and local addresses in network byte
  order, as lwIP does and IPAddress(uint32_t) expects; the log showed
  requests "from 1.0.0.127".
- vnet_unit_test.mjs: cookie-path tests (they fail on the previous jar).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* env:wasm: link the no-op posix mDNS

#1906 made WebUIServer and TelnetServer register mDNS services
unconditionally and added a no-op posix/Mdns.cpp so native builds still
link.  env:wasm takes only selected posix/ files, so it needs this one
too, or WebUIServer's Mdns::add/remove are undefined at link time.  (On a
base before #1906 the file doesn't exist yet and the filter line matches
nothing.)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* wasm demo: give the device frame the device's own URLs; log traffic

The Service Worker served the device frame under device/, and the prefix
showed: a WebUI at /ui/webui2/ saw /device/ui/webui2/ as its own path.
WebUI 2's HTTP/UIDir support derives its base from location.pathname, so
it fell back to root-UI behavior and saved its preferences to LocalFS.
Navigations to a bare "/" also escaped the scope to the demo page.

The worker now covers the whole site and routes each request by who made
it: frame navigations and requests from nested windows go to FluidNC with
exactly their paths; the demo page, its workers and top-level tabs use the
network.  The device frame therefore sees the same URLs as on a
controller -- /, /ui/<name>/, /#/dashboard and the reload helper pages all
behave as they would on a board.  The frame guard and Location rewriting
are gone, the old device/-scoped registration is removed, and the demo
must be served from the root of its origin.

The virtual network's traffic is now logged to the DevTools console: a
line per HTTP exchange (method, path, status, bytes each way, time; the
demo page's own requests marked "(demo)") and per WebSocket open/close
with message and byte counts.  FluidNCVnet.logFrames = true adds each
WebSocket message; FluidNCVnet.log = false silences it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Demo: style the controls and frame borders

The demo's buttons, drop-downs and address bar get a larger
sans-serif font, padding and rounded corners: blue action buttons,
grey navigation buttons, and a red outline on Reset, whose
explanation moves into its tooltip.  The terminal and WebUI frames get
a heavier border.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* wasm demo: install a WebUI to /ui/<name>/ as well as LocalFS

"Install WebUI to" chooses LocalFS index.html.gz, as before, or
/ui/<name> (HTTP/UIDir), with a name defaulting to the project's (figui,
webui2, webui3) or the local file's.  The latter installs through
FluidNC's HTTP interface as a browser on the LAN would: a WebDAV DELETE
of any old index.html, which would be served in preference, then
PUT /ui/<name>/index.html.gz, which creates the directory.  The name
localfs is FluidNC's for the LocalFS root, so it installs there.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
@MitchBradley
MitchBradley deleted the WasmPersistFs branch October 12, 2026 06:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants