Add usermod web UI injection mechanism (/um.js)#5741
Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
WalkthroughWLED adds usermod UI-injection hooks, aggregates generated JavaScript into a new ChangesUsermod UI injection
Estimated code review effort: 3 (Moderate) | ~20 minutes Sequence Diagram(s)sequenceDiagram
participant Browser
participant WLEDServer
participant UsermodManager
participant Usermods
Browser->>WLEDServer: GET /um.js
WLEDServer->>UsermodManager: addUIInjectCode(dest)
UsermodManager->>Usermods: addUIInjectCode(dest)
Usermods-->>UsermodManager: JavaScript injection code
UsermodManager-->>WLEDServer: Aggregated JavaScript
WLEDServer-->>Browser: application/javascript response
sequenceDiagram
participant WLEDUI
participant WLEDServer
participant umInject
WLEDUI->>WLEDUI: Receive json.info.u
WLEDUI->>WLEDServer: Load /um.js once
WLEDServer-->>WLEDUI: Define umInject(s)
WLEDUI->>WLEDUI: Render state with readState(s)
WLEDUI->>umInject: Safely invoke umInject(s)
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@wled00/data/index.js`:
- Line 1624: Wrap the umInject(s) call in the usermod injection check within a
try...catch so exceptions from injected JavaScript cannot escape readState().
Preserve the existing function-type guard, and handle the caught error locally
without disrupting requestJson() or triggering its retry behavior.
- Line 1823: Update the usermod injection check in the surrounding dashboard
initialization to use logical checks instead of optional chaining, preserving
the condition that usermod data exists. Call loadUmInject with the current state
object s so it does not perform a redundant fetch during script loading.
- Around line 232-240: Update loadUmInject to accept the current state s, and in
its script onload handler invoke umInject(s) directly when available instead of
calling requestJson(). Wrap the usermod invocation in try/catch so injected
exceptions are contained and do not interrupt readState(); regenerate the web UI
headers by running npm run build after editing.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 6472d9b7-57e0-4787-90fd-dcd4ebb1361c
📒 Files selected for processing (4)
wled00/data/index.jswled00/fcn_declare.hwled00/um_manager.cppwled00/wled_server.cpp
- umInjectSafe() wraps umInject() in try/catch so an exception in usermod-provided JS cannot abort readState() and trigger requestJson()'s retry loop - pass the current state into loadUmInject(s) and run the first injection directly on script load instead of refetching /json/si - use json.info && json.info.u to match the surrounding style
…Safe shield, no redundant fetch)
|
I guess that's 3 of us working on this feature now! I would vote against this approach entirely, though. Usermod UI assembly and injection should be done at compile time, so it can also leverage the zipping and minification features. Runtime assembly also opens a number of memory management problems that can be entirely avoided. I've been working on a manifest-based system, though I haven't got to the main UI reparse points yet. I've pushed it up what's available now as PR #5742 . Maybe we could integrate the main UI injection hook to run at build time? |
|
Usermod needs to inject variable elements at runtime. So... I'm going to watch closely how you'll manage to do that at compile time. On the other hand, nothing is prohibiting usermod to include pre-minimised and compressed web page header and (then) register a web request handler right now. For the later see my Multi relay usermod. The footprint and complexity of this approach is minimal and all that is needed additionally are some support JS functions and DOM objects so that usermod developer can leverage some work. |
Adds a small, usermod-agnostic mechanism that lets usermods inject their own elements into the main web UI without patching
index.js— based on the design proposed by @blazoncek.How it works
virtual void addUIInjectCode(Print &dest)and print plain JavaScript./um.js(no-store), wrapping all usermods' output infunction umInject(s){...}./um.jsonce (loadUmInject(), triggered when a state response carriesinfo.u, i.e. usermods are present) and callsumInject(s)at the end of everyreadState(s)— so injected elements survive UI re-renders (e.g.populateSegments()), for both fetch and WebSocket updates./um.jsserves an empty function body and the UI is unchanged; if the script fails to load,onerrorlogs and the UI continues normally.Deltas from the original proposal
umInject(s)receives the freshly applied state object — injected UI usually renders usermod data carried in the state (viaaddToJsonState()), and passing it avoids a second fetch.loadUmInject()is guarded against double-insertion of the script tag.onloadtriggers arequestJson()so the first injection runs with state available (the script finishes loading after the initial render).WLED_ENABLE_UM_UI_INJECTcapability macro next to the virtual, so external usermods can#ifdef-guard their override and keep compiling against WLED bases that don't have the mechanism (yet).Tested
Verified on physical hardware — a QuinLED Dig-Next-2 (ESP32) — with the PowerManager usermod serving a segment-card menu through this mechanism: injection runs after every render (fetch and WS), re-renders keep injected elements, and stock behavior is untouched when no usermod provides inject code.
esp32devbuilds clean; generatedhtml_*.hfiles are intentionally not included.What this allows in practice — a usermod-provided menu living right on the segment card, with zero PowerManager-specific code in
index.js:One practical note for usermod authors that surfaced during testing: injected code runs on the main UI page, where only its globals (
d,gId,requestJson, ...) exist — settings-page helpers likecE()are not available.🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
/um.jsendpoint to serve dynamic usermod UI enhancements.Bug Fixes