You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
wasm demo: persist files and NVS settings in IndexedDB - #1900
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-requestwrite 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.
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.
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.
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.
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.
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.
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>
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.
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: 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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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./native_localfs,/native_sdand/native_nvsare IDBFS mounts, restored beforefluidnc_start(). The build links-lidbfs.jsand exportsFS.demo/seed-localfs/anddemo/seed-sd/, each listed in that directory'smanifest.json, and are fetched at boot. This is cherry-picked from the esp32p4 branch (75afbc31) and replaces the embeddedTEST_CONFIG_YAML/TEST_GCODEstrings. 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 withFS.writeFile, so binary files are safe.$CD=,$settings and terminal edits never pass through the page), and onpagehide.?reset, and the new page deletes the databases before opening them (the current page holds them open).FS.writeFile/FS.readFile, so binary files are not truncated at a NUL.fluidnc-fs-requestwritemay carrybytes(an ArrayBuffer) instead ofcontent, and areadmay setbinary: trueto get an ArrayBuffer back. Both useFS.writeFile/FS.readFile, with the path checked by a newfluidnc_fs_resolve()that applies the samesafe_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.fluidnc-shim-commandthat 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, whoseokarrives only when the job ends, so pause and reset from WebUI 3 did nothing until the job had finished.index.html, elseindex.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 outWebUI/, where that setting is defined.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 wasmbuildsconfig.yaml, change a$setting, upload a file, reload: all persist.gz) through the WebUI 3 bridge; it arrives intactindex.html.gz, and it persists across reloadsconfig.yaml,preferences.json,square.nc,Rotor.nc) appear🤖 Generated with Claude Code