Skip to content

Safari/webkit browsers show 2D transitions for 1D segments ( correctly hidden on PC) #5498

Description

@remixmark

What happened?

You get an amended version of transitions when using the desktop browser (chrome) UI vs. the mobile UI on an apple device.

To Reproduce Bug

Check the transitions list on a mobile device (viewing the mobile/device toolbar in chrome doesn't work) vs the list provided on a computer.

Expected Behavior

The full list or transitions should be available on the desktop version of the UI.

Install Method

Binary from WLED.me

What version of WLED?

16.0.0-beta

Which microcontroller/board are you seeing the problem on?

ESP32

Relevant log/trace output

Anything else?

No response

Code of Conduct

  • I agree to follow this project's Code of Conduct

Activity

  1. DedeHai commented on Apr 12, 2026

    @DedeHai
    Collaborator

    screenshots please.

  2. added
    cannot reproduceDevelopers are not able reproduce. Might be fixed already, or report is missing important details
    on Apr 12, 2026
  3. remixmark commented on Apr 12, 2026

    @remixmark
    Author
    Image Image
  4. DedeHai commented on Apr 12, 2026

    @DedeHai
    Collaborator

    one shows 2D options, one shows 1D options. same setup? which browser?

  5. remixmark commented on Apr 12, 2026

    @remixmark
    Author

    Chrome browser.

    Where do you see & adjust what dimension the transition is?

  6. BobLoeffler68 commented on Apr 15, 2026

    @BobLoeffler68
    Contributor

    Where do you see & adjust what dimension the transition is?

    He is talking about the dimensions of the LED device. If it's a 1D strip, it will have the 8 transition options. But if it's a 2D matrix, it will have a lot more transition options. So the device in your first screenshot is a 2D device (ip 192.168.86.111) and the second screenshot is for a 1D device.

  7. remixmark commented on Apr 15, 2026

    @remixmark
    Author

    Why does the mobile UI show 2D options and the desktop web UI show 1D options?

  8. DedeHai commented on Apr 15, 2026

    @DedeHai
    Collaborator

    now asking the important questions :)

    I tested what you proposed in your opening statement, and I see 2D options on both or 1D option on both.

  9. remixmark commented on Apr 15, 2026

    @remixmark
    Author

    I don't understand this statement, "and I see 2D options on both or 1D option on both." Can you clarify what you're saying for me?

  10. DedeHai commented on Apr 15, 2026

    @DedeHai
    Collaborator

    if I connect to an ESP that has a 2D setup, I see the 2D options for transitions, on both mobile and PC.

  11. remixmark commented on Apr 15, 2026

    @remixmark
    Author

    Hrmm, I'm connecting to the same controller on both mobile and desktop yet I get different transition options.

    I have zero 2D LED's, is there somewhere I can force WLED to only show me the 1D options?

  12. remixmark commented on Apr 15, 2026

    @remixmark
    Author

    Also, what is the process to get the transition to be applied to a playlist? I change it, then when the playlist starts a new preset it goes back to the default transition.

    Image
  13. DedeHai commented on Apr 15, 2026

    @DedeHai
    Collaborator

    @coderabbitai is there any reason the UI would show the options for 2D transistions on a setup that does not have 2D enabled?

  14. DedeHai commented on Apr 15, 2026

    @DedeHai
    Collaborator

    @remixmark github is to track bugs, if you have useability questions please move those to the forum or discord.

  15. 19 remaining items

  16. coderabbitai commented on Apr 16, 2026

    @coderabbitai
    Contributor
    🧩 Analysis chain
  17. DedeHai commented on Apr 16, 2026

    @DedeHai
    Collaborator

    @coderabbitai please give me the full code analysis, you got cut off once more

  18. coderabbitai commented on Apr 16, 2026

    @coderabbitai
    Contributor
    🧩 Analysis chain
  19. softhack007 commented on Apr 16, 2026

    @softhack007
    Member

    Lines 664–668 of index.js call o.style.display='none' on individual <option> elements, which iOS Safari/WebKit silently ignores because it renders <select> as a native picker.

    @coderabbitai please double-check your statement. Is this based on known difference between IOS / safari and chrome or firefox? Is hiding a select option via "display:none" allowed according to CSS and HTML standards?

  20. coderabbitai commented on Apr 16, 2026

    @coderabbitai
    Contributor

    @softhack007 — good catch, and thank you for pushing on this. My previous statement was directionally correct for iOS but oversimplified the broader picture. Here's the accurate, complete answer:

    1. Is it allowed by CSS/HTML standards?

    The answer is: the spec explicitly does not guarantee it works. The HTML specification notes that user agents may "ignore certain CSS properties" on <option> elements, as they are often implemented as platform-native controls. So applying display:none to an <option> is not formally prohibited, but the spec provides no guarantee that browsers must honour it. This is a long-standing gap in the specification.

    2. Is it a known iOS/Safari-specific problem, or broader?

    It is broader than just iOS. The behaviour is inconsistent across all major browsers and has been for years — the inconsistency is just most severe and most predictable on iOS:

    • The CSS display:none is said to be fully supported on Safari for iOS per MDN browser-compat data, while it does not actually work on <option> HTML elements — the option is displayed on Safari for iOS regardless.
    • You cannot toggle display on <option> elements in Safari (including macOS Safari). This is part of a long and inconsistent tradition of Safari restricting CSS styling functionality on form elements, as it believes the visual language of interactive elements should be consistent with the OS.
    • Both Chrome and Safari on iOS devices display <option> elements that have display:none (or Bootstrap's d-none class) — confirming this is an iOS platform issue affecting all browsers on that platform, since all iOS browsers must use WebKit.
    • Setting display:none does not remove an element from the DOM, it just hides it from the visual media — and since <option> elements are "still bound to the parent select element as an option," browsers may still show and allow selecting them.

    Summary for the WLED case:

    My earlier statement that "iOS WebKit ignores display:none on <option>" was correct, but incomplete. More precisely:

    Platform display:none on <option>
    iOS Safari / iOS Chrome ❌ Does not work (confirmed)
    macOS Safari ❌ Does not work
    Desktop Chrome Inconsistent (has varied across versions)
    Desktop Firefox Generally works

    The HTML spec gives browsers explicit latitude to ignore CSS on form controls, so this behaviour is not a standards violation — it is undefined territory. The only reliable, cross-browser approach is DOM removal (replaceChildren() or option.remove()), as described in the fix I proposed earlier. That fix remains correct and is actually more broadly beneficial than just "fixing the iOS issue."


    🧠 Learnings used
    Learnt from: softhack007
    Repo: wled/WLED PR: 0
    File: :0-0
    Timestamp: 2026-03-08T00:57:36.134Z
    Learning: In WLED (wled00/cfg.cpp), `deserializeConfig()` is called with an empty JsonObject when cfg.json doesn't exist on fresh install. Any value read without the ArduinoJSON `|` fallback operator or CJSON macro will override correct constructor defaults with 0/null. Known affected values: `strip.setTargetFps(hw_led["fps"])` at line 179 (sets FPS to 0/unlimited instead of WLED_FPS=42). Fix: `strip.setTargetFps(hw_led["fps"] | WLED_FPS)`. The gamma issue (gammaCorrectCol/Bri) had the same root cause and was fixed in commit d1d9dec402 (Jan 2026) using inline `| default` fallbacks.
    

    Learnt from: DedeHai
    Repo: wled/WLED PR: 0
    File: :0-0
    Timestamp: 2026-04-15T15:54:12.393Z
    Learning: In WLED (wled00/FX_fcn.cpp), when `ledmap.json` (index 0) contains `width` or `height` fields, setting `isMatrix = true` and updating `Segment::maxWidth`/`maxHeight` is intentional and correct behaviour — a ledmap with those fields is by definition a 2D setup. Do not flag this as an unexpected or unintended trigger for 2D mode in future reviews.
    

    Learnt from: DedeHai
    Repo: wled/WLED PR: 4889
    File: wled00/json.cpp:310-310
    Timestamp: 2026-03-21T18:12:09.437Z
    Learning: In WLED's `deserializeSegment()` (wled00/json.cpp), the blend mode field `seg.blendMode` is intentionally written without a post-read clamp (`getVal(elem["bm"], seg.blendMode)`). Out-of-range or unsupported blend mode values are handled safely in `WS2812FX::blendSegment()` (wled00/FX_fcn.cpp), which defaults to mode 0 for any unsupported value via a bounds check against the `BLENDMODES` constant. Do not flag the missing clamp in deserializeSegment as a bug in future reviews.
    

    Learnt from: DedeHai
    Repo: wled/WLED PR: 4798
    File: wled00/FX.cpp:7531-7533
    Timestamp: 2025-08-26T11:51:21.817Z
    Learning: In WLED PR `#4798`, DedeHai confirmed that certain gamma-related calls in FX.cpp/FX_fcn.cpp/particle systems are intentional for effect-level shaping (e.g., brightness curves, TV sim, Pride 2015 pre-mix), distinct from final output gamma. Do not flag or remove these in future reviews; add comments when feasible to clarify intent.
    

    Learnt from: softhack007
    Repo: wled/WLED PR: 0
    File: :0-0
    Timestamp: 2026-04-10T16:10:42.477Z
    Learning: In WLED (wled/WLED), the segments vector (`strip.segments`) is highly volatile: preset loads, effect changes, and JSON state updates in json.cpp can add/remove/reorder segments and change their active status at any time. Segments have no stable persistent identifier — only their ordinal index in the vector, which also changes. Code that tries to maintain a discovery-time binding from an external protocol (e.g., Espalexa/Alexa) to a specific segment using ordinal-among-active-segments as a key will silently map to wrong segments after any preset load. A correct implementation must store the init-time segment-index-to-device mapping explicitly and guard against the segment at that index changing identity after a vector rebuild.
    

    Learnt from: BobLoeffler68
    Repo: wled/WLED PR: 5440
    File: usermods/user_fx/user_fx.cpp:1304-1313
    Timestamp: 2026-03-25T07:03:35.475Z
    Learning: In WLED `mode_dissolveplus` (usermods/user_fx/user_fx.cpp), using `hw_random16(SEGLEN)` to select the survivor pixel index is correct and safe for this 1D-only effect. The 0xFFFF unmapped-entry concern from the physical bus mapping does not apply to 1D segments because virtual indices 0..SEGLEN-1 always map to valid physical LEDs without gaps. Do not flag this as a bug in future reviews of 1D effects.
    

    Learnt from: softhack007
    Repo: wled/WLED PR: 0
    File: :0-0
    Timestamp: 2026-03-25T12:25:11.610Z
    Learning: In WLED's `Segment::startTransition()` (wled00/FX_fcn.cpp:284), when `isInTransition()` is true and `_t->_oldSegment` already exists, the function silently returns without updating `_t->_start` or `_t->_bri`. This causes a bug where rapid successive on/off toggles during a non-FADE blending transition (e.g., fairy dust) leave the transition clock stale: by the time of the second re-trigger, elapsed time may already exceed `_dur`, so `updateTransitionProgress()` sets `_progress = 0xFFFF` immediately on the next service tick and `stopTransition()` fires — the blending effect never plays. The fix is to always reset `_t->_start = millis()`, `_t->_dur = dur`, and `_t->_bri = currentBri()` (current visible brightness) in the `isInTransition()` branch, regardless of whether `_oldSegment` exists.
    

    Learnt from: softhack007
    Repo: wled/WLED PR: 5482
    File: wled00/wled.h:579-579
    Timestamp: 2026-04-08T21:25:44.777Z
    Learning: In WLED PR `#5482` (wled00/wled.h), the `denyWsecUpload` flag is confirmed to default to `false`. The complementary defence against malformed wsec.json content (bootloop/brick risk from malformed JSON) is handled by PR `#5484`, which hardens JSON file reading/parsing at load time. Do not suggest moving `denyWsecUpload` default to `true` or replacing JSON validation with upload blocking — both mechanisms serve different purposes and should coexist.
    

    Learnt from: DedeHai
    Repo: wled/WLED PR: 4939
    File: wled00/FX_fcn.cpp:1176-1187
    Timestamp: 2025-09-16T18:08:42.848Z
    Learning: In WLED finalizeInit() bus creation (wled00/FX_fcn.cpp), intentionally allowing memory overruns when bus configurations exceed MAX_LED_MEMORY is a deliberate design choice. The trade-off prioritizes creating buses with reduced LED counts over completely failing to create buses, which would cause no LED output and UI failures. This approach forces users to update configurations after migrating to version 0.16 while maintaining basic functionality.
    

    Learnt from: softhack007
    Repo: wled/WLED PR: 0
    File: :0-0
    Timestamp: 2026-03-08T00:57:36.134Z
    Learning: In WLED (wled00/cfg.cpp), `deserializeConfigFromFS()` only sets `needsSave=true` (triggering a write of cfg.json) when usermod settings are present in the JSON (lines 758-761). On a fresh install with no usermods, `needsSave=false` so `serializeConfigToFS()` is never called at first boot. Correct defaults are never written to cfg.json until the user manually saves. Structural fix: set `needsSave = needsSave || !cfgExists` where `cfgExists = WLED_FS.exists(s_cfg_json)` checked before reading the file.
    

    Learnt from: DedeHai
    Repo: wled/WLED PR: 0
    File: :0-0
    Timestamp: 2026-04-16T05:47:40.778Z
    Learning: In WLED (wled00/data/index.js), the `#bs` select element hides 2D blend/transition options via `option.style.display='none'`. iOS Safari (and all iOS browsers, which use WebKit) ignores `display:none` on `<option>` elements because the native iOS picker renders all DOM-present options regardless of their CSS. The correct fix is to remove 2D options from the DOM entirely using `replaceChildren()` with a pre-snapshot array (`bsOpts`), rather than toggling `style.display`. The snapshot should be taken lazily on the first `loadInfo()` call, when all options are still present in the HTML.
    

    Learnt from: softhack007
    Repo: wled/WLED PR: 0
    File: :0-0
    Timestamp: 2025-12-28T14:06:48.772Z
    Learning: In WLED post-commit ee9ac947, the rendering pipeline uses per-segment buffers and per-pixel bus updates. Unmapped (0xFFFF) mapping entries are now skipped in WS2812FX::show() (no “clear to black”), which can leave physical gap LEDs with stale/random colors unless they are explicitly cleared. This is a behavior change from pre-0.16 where a full physical buffer was effectively refreshed each frame.
    

    Learnt from: willmmiles
    Repo: wled/WLED PR: 5462
    File: wled00/json.cpp:1189-1198
    Timestamp: 2026-03-30T15:32:08.847Z
    Learning: In WLED's `respondModeData()` (wled00/json.cpp), the 256-character `lineBuffer` limit for effect descriptor strings (getModeData) is an intentional constraint that matches the same 256-char limit used ~6 other places in the codebase. It is not new to this PR and should not be flagged as a regression. Lifting it requires a future refactor (better type system or dynamic buffer).
    

    Learnt from: softhack007
    Repo: wled/WLED PR: 0
    File: :0-0
    Timestamp: 2026-03-29T20:58:20.394Z
    Learning: In WLED PR `#5048`, /json/net is generated by wled00/json.cpp::serializeNetworks(), which now includes ssid, rssi, bssid, channel, and enc for each scanned network. Any “Ch undefined” in the UI applies only to legacy builds or empty-scan states.
    

    Learnt from: softhack007
    Repo: wled/WLED PR: 0
    File: :0-0
    Timestamp: 2026-03-29T20:58:20.394Z
    Learning: In WLED PR `#5048`, /json/net is generated by wled00/json.cpp::serializeNetworks(). It serializes for each network: ssid, rssi, bssid, and channel (node["channel"] is set inside the loop). UI pages (settings_wifi.htm) should rely on this field rather than guard for "undefined" except for legacy builds.
    

    Learnt from: softhack007
    Repo: wled/WLED PR: 0
    File: :0-0
    Timestamp: 2026-03-29T20:58:20.394Z
    Learning: In WLED PR `#5048`, /json/net is produced by wled00/json.cpp::serializeNetworks(), which serializes ssid, rssi, bssid, and channel (node["channel"] is set in the loop). The UI can rely on this field; “Ch undefined” only applies to legacy builds that predate this addition.
    

    Learnt from: blazoncek
    Repo: wled/WLED PR: 4482
    File: wled00/udp.cpp:147-149
    Timestamp: 2025-02-19T12:43:34.199Z
    Learning: ESP8266 and ESP32 platforms have different maximum segment name lengths in WLED, which can cause truncation when syncing segment names between devices. This platform difference affects the user experience when using the segment name sync feature.
    

    Learnt from: softhack007
    Repo: wled/WLED PR: 5443
    File: wled00/FX_fcn.cpp:1277-1277
    Timestamp: 2026-03-24T12:10:32.630Z
    Learning: In WLED's `WS2812FX::service()` (wled00/FX_fcn.cpp), the old condition `|| (doShow && seg.mode == FX_MODE_STATIC)` was an **inclusion** guard — it caused FX_MODE_STATIC to render only when another segment had already set doShow=true. It did NOT skip or protect FX_MODE_STATIC from rendering. The PR `#5443` simplification removes this condition, meaning FX_MODE_STATIC now renders on every `timeToShow` tick uniformly. This is intentional and not a regression. Do not flag FX_MODE_STATIC special-casing as missing in future reviews of this function.
    

    Learnt from: DedeHai
    Repo: wled/WLED PR: 5464
    File: wled00/FX_fcn.cpp:1699-1701
    Timestamp: 2026-04-09T07:26:14.510Z
    Learning: In WLED (wled00/util.cpp), `allocate_buffer()` processes `BFRALLOC_NOBYTEACCESS` in an `if` with an `else` that covers all other flags. On classic ESP32, this means `BFRALLOC_ENFORCE_PSRAM` and `BFRALLOC_PREFER_PSRAM` are silently ignored when `BFRALLOC_NOBYTEACCESS` is also set — IRAM (MALLOC_CAP_32BIT) is always tried first. Combining `BFRALLOC_CLEAR | BFRALLOC_NOBYTEACCESS` is unsafe: the allocator's internal `memset` at util.cpp:901 will perform byte-level access on a potentially IRAM-resident buffer. This affects `Segment::pixels` in `setGeometry()` (FX.h) and `_pixels` in `finalizeInit()` (FX_fcn.cpp). Additionally, `memcpy` on `Segment::pixels` in the copy constructor and copy assignment (FX_fcn.cpp lines ~73 and ~116) is also a byte-level violation when the buffer is in IRAM. The safe pattern is element-wise 32-bit access (for loops over `uint32_t*`) rather than `memset`/`memcpy` for all `BFRALLOC_NOBYTEACCESS` buffers.
    

    Learnt from: DedeHai
    Repo: wled/WLED PR: 4923
    File: wled00/FX.cpp:4883-4901
    Timestamp: 2025-09-12T17:29:43.826Z
    Learning: In WLED’s web UI, only one slider value (e.g., SEGMENT.intensity or SEGMENT.custom1) changes at a time; code relying on this may use simplified change guards, though presets/JSON can still update multiple fields atomically.
    

    Learnt from: softhack007
    Repo: wled/WLED PR: 5480
    File: docs/cpp.instructions.md:511-511
    Timestamp: 2026-04-08T19:10:52.197Z
    Learning: In WLED documentation and review guidance (`docs/cpp.instructions.md`), using `WLED_MAX_SEGNAME_LEN = 64` as the example value in LittleFS filename-length checks is intentional and correct. The reasoning: ESP32 uses 64-char segment names (the larger default), and if a constructed path (e.g. `/ledmap_` + segment name + `.json`) stays within 255 bytes when the name is 64 chars, it is trivially safe on ESP8266 where segment names are limited to 32 chars. WLED core has no ESP8266-only features, so validating against the larger ESP32 default is the right cross-platform conservative choice. Do NOT flag `WLED_MAX_SEGNAME_LEN = 64` in this guidance as misleading or platform-specific.
    

    Learnt from: softhack007
    Repo: wled/WLED PR: 5457
    File: usermods/zigbee_rgb_light/usermod_zigbee_rgb_light.h:0-0
    Timestamp: 2026-03-31T17:31:01.023Z
    Learning: In WLED PR `#5457` (zigbee_rgb_light usermod): The WLED_MAX_DIGITAL_CHANNELS=0 build flag used in the esp32c6_zigbee environment is a temporary workaround for rmt_tx_wait_all_done() timeout spam when the Zigbee/802.15.4 stack is active. The root cause is under investigation and is likely related to Zigbee light-sleep (CONFIG_PM_ENABLE) disrupting RMT's internal time base, or ISR latency due to cache-disable during flash ops — NOT the 802.15.4 radio "sharing" the RMT peripheral (they are separate hardware). Because a proper fix (rmt_enable()/rmt_disable() PM-lock wrapping, allow_pd=0, CONFIG_RMT_TX_ISR_CACHE_SAFE) may eliminate the need to disable digital channels entirely, do NOT add a compile-time `#error` guard requiring WLED_MAX_DIGITAL_CHANNELS=0; doing so would prematurely bake in a constraint that may be lifted once the investigation concludes.
    

    Learnt from: DedeHai
    Repo: wled/WLED PR: 5464
    File: wled00/FX_fcn.cpp:1699-1701
    Timestamp: 2026-04-09T09:14:32.589Z
    Learning: In WLED, pixel buffers allocated with `BFRALLOC_NOBYTEACCESS` (both `WS2812FX::_pixels` and `Segment::pixels`) are intentionally placed in 32-bit-only accessible IRAM by design. Buffers up to ~1000 LEDs (~4 kB) easily fit within IRAM's ~50 kB pool. `memset` and `memcpy` on these buffers work correctly in practice because the Xtensa newlib assembly implementations use 32-bit stores for word-aligned, word-multiple-size buffers — satisfying the IRAM hardware constraint as an implementation property of the assembly code, which is unlikely to change across IDF versions. The risk of a future violation exists but is accepted as very low. Do not flag `memset`/`memcpy` usage on `BFRALLOC_NOBYTEACCESS` pixel buffers as a critical bug in WLED reviews; note it as a low-risk accepted pattern.
    

    Learnt from: DedeHai
    Repo: wled/WLED PR: 0
    File: :0-0
    Timestamp: 2026-01-13T21:23:35.514Z
    Learning: In WLED, the global `paletteBlend` variable (wled.h:603) and the `WS2812FX::paletteBlend` member (FX.h:940) are duplicates without synchronization code. The global is loaded/saved in cfg.cpp and set via UI in set.cpp, but never copied to the strip member. This is the only such case in the codebase; other settings are either strip-only members (autoSegments, correctWB, cctFromRgb, isMatrix) or global-only (gammaCorrectCol/Bri/Val, blendingStyle).
    

    Learnt from: DedeHai
    Repo: wled/WLED PR: 4615
    File: wled00/FX.cpp:10824-10824
    Timestamp: 2026-03-29T06:08:02.547Z
    Learning: WLED: In wled00/FX.cpp::mode_slow_transition(), the change-detection logic intentionally compares data->currentCCT to SEGMENT.cct (not data->endCCT). SEGMENT.cct is set to currentCCT at the end of each call; comparing to endCCT would re-initialize the transition on each frame and stall CCT blending. Do not propose changing this.
    

    Learnt from: DedeHai
    Repo: wled/WLED PR: 4889
    File: wled00/FX_fcn.cpp:1380-1399
    Timestamp: 2026-03-21T17:47:53.826Z
    Learning: In WLED's stencil blend mode (case 16 in `WS2812FX::blendSegment()`, `wled00/FX_fcn.cpp`), the transparency key is intentionally hardcoded to black (`t ? t : b`), not the segment's background color (`colors[1]`). This is a deliberate design choice to avoid additional parameters and overhead. It is the user's responsibility to not use a non-black background in combination with stencil mode. Do not flag this as a bug in future reviews.
    

    Learnt from: softhack007
    Repo: wled/WLED PR: 5482
    File: wled00/wled.h:579-579
    Timestamp: 2026-04-08T21:23:19.258Z
    Learning: In WLED PR `#5482` (wled00/wled.h), the `denyWsecUpload` flag MUST default to `false` (not `true`). Rationale confirmed via FMEA by softhack007:
    1. Circular-dependency risk: `denyWsecUpload` is stored inside `wsec.json`; if that file is corrupted/deleted, `_INIT(true)` would permanently block the upload-recovery path without serial access.
    2. Upgrade safety: `_INIT(true)` would silently break wsec.json upload recovery on all existing installations after a firmware upgrade.
    3. Recovery parity: uploading wsec.json does not expose previous secrets; it is a legitimate recovery mechanism (e.g., after a flash wipe).
    The flag is valuable as an optional hardening opt-in for installations that do not need the upload-recovery path. The real defence against malformed-JSON bootloops should be input validation at load time, not upload blocking.
    

    Learnt from: softhack007
    Repo: wled/WLED PR: 5048
    File: wled00/set.cpp:551-555
    Timestamp: 2026-03-27T21:00:25.902Z
    Learning: In WLED PR `#5048` (wled00/set.cpp lines 551-555), the CONFIG_IDF_TARGET_ESP32C5 block that unconditionally forces ntpEnabled = false is an intentional **temporary hotfix** by softhack007 for a known ESP32-C5 crash: `assert failed: udp_new_ip_type udp.c:1278 (Required to lock TCPIP core functionality!)`. Do not flag this as a permanent design issue; the TODO comment in the code already notes it should be resolved properly once the underlying IDF/TCPIP bug on C5 is fixed. A future permanent solution should use a target capability flag rather than overwriting the user's setting.
    

    Learnt from: DedeHai
    Repo: wled/WLED PR: 4939
    File: wled00/data/index.js:2406-2413
    Timestamp: 2025-09-28T09:53:42.670Z
    Learning: In WLED, code paths that appear unreachable may actually be safety fallbacks for low-heap conditions. These fallbacks are intentional defensive programming measures to ensure UI functionality is preserved when system resources are constrained.
    
  21. softhack007 commented on Apr 16, 2026

    @softhack007
    Member

    @coderabbitai

    1. please check if settings_leds.htm or other settings pages are affected by the same problem

    2. is there a standard compliant (and simple) method to reliably remove entries from a selection list?

  22. coderabbitai commented on Apr 16, 2026

    @coderabbitai
    Contributor
    🧩 Analysis chain

    @softhack007

    Here’s the full check and the answer to both points:

    Are settings_leds.htm or other settings pages affected?

    • Scan summary:

      • No settings page toggles visibility of via style.display.
      • settings_leds.htm uses the standard .disabled = true/false approach on elements and never sets option.style.display. This is iOS‑safe (visible but unselectable).
      • No other settings_*.htm file uses option.style.display either.
    • File/line references from the scan:

      • settings_leds.htm uses helpers that operate via the disabled attribute on options (e.g., disable/enable with sel.querySelectorAll('option[...]').forEach(o => o.disabled = true/false)). There are no occurrences of option.style.display in settings_leds.htm (see script output around Lines 940–1010 and the surrounding matches).
      • The only place using style.display on in the whole UI is in wled00/data/index.js where 2D transition options for #bs are shown/hidden. That’s the area we already discussed as problematic on iOS.

    Conclusion: Only the index.js #bs dropdown is affected. settings_leds.htm and the rest of the settings pages are not impacted by the iOS/WebKit limitation.

    A standard‑compliant, simple way to reliably remove entries from a selection list

    Use DOM removal/rebuild (not CSS) so all browsers, including iOS WebKit, only render the allowed elements.

    Pattern you can drop in anywhere:

    • Snapshot once, then filter and rebuild
      • Snapshot original options once (first time you need to filter) so you can swap between filtered and full lists later.
      • Replace children with only the allowed options.
      • Preserve the current selection if it’s still present; otherwise, fall back to a safe default.

    Example:

    // one-time snapshot when the page first learns device capabilities
    let selectAllOptions = null;
    
    function snapshotOptionsOnce(selectEl) {
      if (!selectAllOptions) {
        // Deep clones preserve text, value, dataset, disabled, etc.
        selectAllOptions = Array.from(selectEl.options).map(o => o.cloneNode(true));
      }
    }
    
    function setSelectAllowed(selectEl, predicate, { fallbackValue = "" } = {}) {
      snapshotOptionsOnce(selectEl);
    
      // Build the new list from the snapshot
      const kept = selectAllOptions
        .filter(predicate)                // e.g., o => is2D || o.dataset.type !== "2D"
        .map(o => o.cloneNode(true));     // never move originals out of the snapshot
    
      // Replace options atomically
      selectEl.replaceChildren(...kept);
    
      // Restore selection if possible, else fall back
      const prev = selectEl.value;
      if (!Array.from(selectEl.options).some(o => o.value === prev)) {
        // preferred fallback, else first option if missing
        const fb = Array.from(selectEl.options).some(o => o.value === String(fallbackValue))
          ? String(fallbackValue)
          : (selectEl.options[0]?.value ?? "");
        selectEl.value = fb;
      } else {
        selectEl.value = prev;
      }
    }

    Usage for the #bs dropdown (exactly what you need today):

    // Example: hide all 2D-only transitions when not a matrix
    const bs = gId('bs');
    setSelectAllowed(bs, o => isM || o.dataset.type !== '2D', { fallbackValue: 0 /* Fade */ });

    Notes:

    • This is standards‑compliant (pure DOM operations) and works uniformly on iOS, Android, macOS, Windows, Chrome, Safari, Firefox, etc.
    • If merely preventing selection (while still showing the items) is acceptable, using o.disabled = true is also standards‑compliant and iOS‑safe. But it does not remove items from the iOS native picker UI; they’ll still be listed (greyed out). For fully removing from the list, prefer DOM removal as above.

    If you want, I can prep a minimal patch for index.js that switches the current option.style.display approach on #bs to this replaceChildren() pattern.


    🏁 Script executed:

    #!/bin/bash
    set -e
    
    echo "== List settings pages =="
    fd -p -i --full-path 'wled00/data/settings*.htm' | sort
    
    echo
    echo "== Any occurrences of option.style.display / style.display applied to option across data/ =="
    rg -nPI --no-heading '(querySelectorAll\s*\(\s*[\'"]option|<option\b).*?style\.display|style\.display.*option' wled00/data | sed -n '1,160p'
    
    echo
    echo "== All style.display assignments in JS/HTM under data/ =="
    rg -nPI --no-heading '\.style\.display\s*=' wled00/data | sed -n '1,200p'
    
    echo
    echo "== settings_leds.htm: show lines around option manipulation =="
    # Show a reasonable window around potential helper functions and option logic
    rg -nPI --no-heading 'querySelectorAll\s*\(\s*[\'"]option|disabled\s*=' wled00/data/settings_leds.htm -n -C3 | sed -n '1,240p'
    
    echo
    echo "== settings_leds.htm: dump 940-1010 to capture helper defs and usage =="
    sed -n '940,1010p' wled00/data/settings_leds.htm
    
    echo
    echo "== Other settings pages: scan for option handling / style.display =="
    for f in $(fd -p -i --full-path 'wled00/data/settings*.htm'); do
      echo "--- $f ---"
      rg -nPI --no-heading 'querySelectorAll\s*\(\s*[\'"]option|<option\b|style\.display\s*=' "$f" -n -C2 || true
    done
    
    echo
    echo "== index.js: confirm the `#bs` option show/hide block =="
    rg -nPI --no-heading 'option\[data-type="2D"\]|gId\([\'"]bs[\'"]\)|isM\s*=' wled00/data/index.js -n -C3 | sed -n '1,240p'
    

    Length of output: 760


    🏁 Script executed:

    #!/bin/bash
    set -e
    
    echo "== Settings pages present =="
    fd -p -i --full-path 'wled00/data/settings*.htm' | sort || true
    
    echo
    echo "== All occurrences of style.display assignments under data/ (with context) =="
    rg -nP --no-heading '\.style\s*\.\s*display\s*=' wled00/data -C3 || true
    
    echo
    echo "== Any lines that both mention <option> and style.display (HTML/JS) =="
    rg -nP --no-heading '(?:<option\b.*?style\s*=)|(?:option[^\n]{0,200}\.style\s*\.\s*display\s*=)|(?:style\s*\.\s*display[^\n]{0,200}option)' wled00/data -C2 || true
    
    echo
    echo "== All places that programmatically iterate over <option> elements =="
    rg -nP --no-heading 'querySelectorAll\s*\(\s*[\"\']option|\.options\b|new\s+Option\s*\(' wled00/data -C2 || true
    
    echo
    echo "== settings.htm: show any option-handling (disabled/display) =="
    rg -nP --no-heading 'querySelectorAll\s*\(\s*[\"\']option|disabled\s*=|style\s*\.\s*display\s*=' wled00/data/settings.htm -C3 || true
    echo
    echo "== settings.htm: list selects/options snippet =="
    rg -nN --no-heading '<select|<option' wled00/data/settings.htm -n -C2 | sed -n '1,200p' || true
    
    echo
    echo "== index.js: confirm `#bs` and data-type=2D logic =="
    rg -nP --no-heading 'gId\s*\(\s*[\"\']bs[\"\']\s*\)|data-type\s*=\s*[\"\']2D[\"\']|isM\s*=' wled00/data/index.js -C3 || true
    

    Length of output: 52116


    @softhack007

    Scope check (settings pages)

    • Affected: Only wled00/data/index.js uses style.display on elements:
      • Line 665: gId('bs').querySelectorAll('option[data-type="2D"]').forEach(o => { o.style.display = 'none'; });
      • Line 668: ... o.style.display = '';
    • Not affected: All settings pages (settings_*.htm) toggle containers or messages via style.display, not individual s. Where options are altered, they use .disabled (which is iOS‑safe). Example:
      • wled00/data/settings_leds.htm:337 — gId("dig"+n+"w").querySelector("[data-opt=CCT]").disabled = !hasCCT(t);

    Conclusion: The iOS/WebKit “can’t hide <option>” problem is confined to the #bs dropdown in index.js; settings pages are not impacted.

    Standard‑compliant, simple way to remove entries from a <select>

    • Do not rely on CSS. Remove/rebuild the option nodes in the DOM. This is fully standard (HTML/DOM) and works across Safari/WebKit (including iOS), Chrome, and Firefox.

    Drop‑in helper:

    let snapshotAll = null;
    
    function setSelectAllowed(selectEl, predicate, { fallbackValue = "0" } = {}) {
      if (!snapshotAll) snapshotAll = Array.from(selectEl.options).map(o => o.cloneNode(true));
      const prev = selectEl.value;
    
      const kept = snapshotAll
        .filter(predicate)               // e.g. (o) => isMatrix || o.dataset.type !== "2D"
        .map(o => o.cloneNode(true));    // clone so snapshot stays intact
    
      selectEl.replaceChildren(...kept);
    
      // restore selection if possible, otherwise fall back
      if (!Array.from(selectEl.options).some(o => o.value === prev)) {
        selectEl.value = Array.from(selectEl.options).some(o => o.value === String(fallbackValue))
          ? String(fallbackValue)
          : (selectEl.options[0]?.value ?? "");
      } else {
        selectEl.value = prev;
      }
    }

    Usage for #bs in index.js:

    const bs = gId('bs');
    setSelectAllowed(bs, o => isM || o.dataset.type !== '2D', { fallbackValue: 0 }); // 0 = Fade

    Note

    • If it’s acceptable to show but not allow selection, using o.disabled = true is also standards‑compliant and iOS‑safe — but it won’t hide items in iOS’ native picker UI. DOM removal (above) is the reliable “hide” behavior.

    🧠 Learnings used
    Learnt from: softhack007
    Repo: wled/WLED PR: 0
    File: :0-0
    Timestamp: 2026-03-08T00:57:36.134Z
    Learning: In WLED (wled00/cfg.cpp), `deserializeConfig()` is called with an empty JsonObject when cfg.json doesn't exist on fresh install. Any value read without the ArduinoJSON `|` fallback operator or CJSON macro will override correct constructor defaults with 0/null. Known affected values: `strip.setTargetFps(hw_led["fps"])` at line 179 (sets FPS to 0/unlimited instead of WLED_FPS=42). Fix: `strip.setTargetFps(hw_led["fps"] | WLED_FPS)`. The gamma issue (gammaCorrectCol/Bri) had the same root cause and was fixed in commit d1d9dec402 (Jan 2026) using inline `| default` fallbacks.
    

    Learnt from: DedeHai
    Repo: wled/WLED PR: 0
    File: :0-0
    Timestamp: 2026-04-15T15:54:12.393Z
    Learning: In WLED (wled00/FX_fcn.cpp), when `ledmap.json` (index 0) contains `width` or `height` fields, setting `isMatrix = true` and updating `Segment::maxWidth`/`maxHeight` is intentional and correct behaviour — a ledmap with those fields is by definition a 2D setup. Do not flag this as an unexpected or unintended trigger for 2D mode in future reviews.
    

    Learnt from: DedeHai
    Repo: wled/WLED PR: 4798
    File: wled00/FX.cpp:7531-7533
    Timestamp: 2025-08-26T11:51:21.817Z
    Learning: In WLED PR `#4798`, DedeHai confirmed that certain gamma-related calls in FX.cpp/FX_fcn.cpp/particle systems are intentional for effect-level shaping (e.g., brightness curves, TV sim, Pride 2015 pre-mix), distinct from final output gamma. Do not flag or remove these in future reviews; add comments when feasible to clarify intent.
    

    Learnt from: DedeHai
    Repo: wled/WLED PR: 4889
    File: wled00/json.cpp:310-310
    Timestamp: 2026-03-21T18:12:09.437Z
    Learning: In WLED's `deserializeSegment()` (wled00/json.cpp), the blend mode field `seg.blendMode` is intentionally written without a post-read clamp (`getVal(elem["bm"], seg.blendMode)`). Out-of-range or unsupported blend mode values are handled safely in `WS2812FX::blendSegment()` (wled00/FX_fcn.cpp), which defaults to mode 0 for any unsupported value via a bounds check against the `BLENDMODES` constant. Do not flag the missing clamp in deserializeSegment as a bug in future reviews.
    

    Learnt from: softhack007
    Repo: wled/WLED PR: 0
    File: :0-0
    Timestamp: 2026-04-10T16:10:42.477Z
    Learning: In WLED (wled/WLED), the segments vector (`strip.segments`) is highly volatile: preset loads, effect changes, and JSON state updates in json.cpp can add/remove/reorder segments and change their active status at any time. Segments have no stable persistent identifier — only their ordinal index in the vector, which also changes. Code that tries to maintain a discovery-time binding from an external protocol (e.g., Espalexa/Alexa) to a specific segment using ordinal-among-active-segments as a key will silently map to wrong segments after any preset load. A correct implementation must store the init-time segment-index-to-device mapping explicitly and guard against the segment at that index changing identity after a vector rebuild.
    

    Learnt from: BobLoeffler68
    Repo: wled/WLED PR: 5440
    File: usermods/user_fx/user_fx.cpp:1304-1313
    Timestamp: 2026-03-25T07:03:35.475Z
    Learning: In WLED `mode_dissolveplus` (usermods/user_fx/user_fx.cpp), using `hw_random16(SEGLEN)` to select the survivor pixel index is correct and safe for this 1D-only effect. The 0xFFFF unmapped-entry concern from the physical bus mapping does not apply to 1D segments because virtual indices 0..SEGLEN-1 always map to valid physical LEDs without gaps. Do not flag this as a bug in future reviews of 1D effects.
    

    Learnt from: softhack007
    Repo: wled/WLED PR: 0
    File: :0-0
    Timestamp: 2026-03-25T12:25:11.610Z
    Learning: In WLED's `Segment::startTransition()` (wled00/FX_fcn.cpp:284), when `isInTransition()` is true and `_t->_oldSegment` already exists, the function silently returns without updating `_t->_start` or `_t->_bri`. This causes a bug where rapid successive on/off toggles during a non-FADE blending transition (e.g., fairy dust) leave the transition clock stale: by the time of the second re-trigger, elapsed time may already exceed `_dur`, so `updateTransitionProgress()` sets `_progress = 0xFFFF` immediately on the next service tick and `stopTransition()` fires — the blending effect never plays. The fix is to always reset `_t->_start = millis()`, `_t->_dur = dur`, and `_t->_bri = currentBri()` (current visible brightness) in the `isInTransition()` branch, regardless of whether `_oldSegment` exists.
    

    Learnt from: softhack007
    Repo: wled/WLED PR: 5482
    File: wled00/wled.h:579-579
    Timestamp: 2026-04-08T21:25:44.777Z
    Learning: In WLED PR `#5482` (wled00/wled.h), the `denyWsecUpload` flag is confirmed to default to `false`. The complementary defence against malformed wsec.json content (bootloop/brick risk from malformed JSON) is handled by PR `#5484`, which hardens JSON file reading/parsing at load time. Do not suggest moving `denyWsecUpload` default to `true` or replacing JSON validation with upload blocking — both mechanisms serve different purposes and should coexist.
    

    Learnt from: DedeHai
    Repo: wled/WLED PR: 4939
    File: wled00/FX_fcn.cpp:1176-1187
    Timestamp: 2025-09-16T18:08:42.848Z
    Learning: In WLED finalizeInit() bus creation (wled00/FX_fcn.cpp), intentionally allowing memory overruns when bus configurations exceed MAX_LED_MEMORY is a deliberate design choice. The trade-off prioritizes creating buses with reduced LED counts over completely failing to create buses, which would cause no LED output and UI failures. This approach forces users to update configurations after migrating to version 0.16 while maintaining basic functionality.
    

    Learnt from: softhack007
    Repo: wled/WLED PR: 0
    File: :0-0
    Timestamp: 2026-03-08T00:57:36.134Z
    Learning: In WLED (wled00/cfg.cpp), `deserializeConfigFromFS()` only sets `needsSave=true` (triggering a write of cfg.json) when usermod settings are present in the JSON (lines 758-761). On a fresh install with no usermods, `needsSave=false` so `serializeConfigToFS()` is never called at first boot. Correct defaults are never written to cfg.json until the user manually saves. Structural fix: set `needsSave = needsSave || !cfgExists` where `cfgExists = WLED_FS.exists(s_cfg_json)` checked before reading the file.
    

    Learnt from: DedeHai
    Repo: wled/WLED PR: 0
    File: :0-0
    Timestamp: 2026-04-16T05:47:40.778Z
    Learning: In WLED (wled00/data/index.js), the `#bs` select element hides 2D blend/transition options via `option.style.display='none'`. iOS Safari (and all iOS browsers, which use WebKit) ignores `display:none` on `<option>` elements because the native iOS picker renders all DOM-present options regardless of their CSS. The correct fix is to remove 2D options from the DOM entirely using `replaceChildren()` with a pre-snapshot array (`bsOpts`), rather than toggling `style.display`. The snapshot should be taken lazily on the first `loadInfo()` call, when all options are still present in the HTML.
    

    Learnt from: softhack007
    Repo: wled/WLED PR: 0
    File: :0-0
    Timestamp: 2025-12-28T14:06:48.772Z
    Learning: In WLED post-commit ee9ac947, the rendering pipeline uses per-segment buffers and per-pixel bus updates. Unmapped (0xFFFF) mapping entries are now skipped in WS2812FX::show() (no “clear to black”), which can leave physical gap LEDs with stale/random colors unless they are explicitly cleared. This is a behavior change from pre-0.16 where a full physical buffer was effectively refreshed each frame.
    

    Learnt from: willmmiles
    Repo: wled/WLED PR: 5462
    File: wled00/json.cpp:1189-1198
    Timestamp: 2026-03-30T15:32:08.847Z
    Learning: In WLED's `respondModeData()` (wled00/json.cpp), the 256-character `lineBuffer` limit for effect descriptor strings (getModeData) is an intentional constraint that matches the same 256-char limit used ~6 other places in the codebase. It is not new to this PR and should not be flagged as a regression. Lifting it requires a future refactor (better type system or dynamic buffer).
    

    Learnt from: softhack007
    Repo: wled/WLED PR: 0
    File: :0-0
    Timestamp: 2026-03-29T20:58:20.394Z
    Learning: In WLED PR `#5048`, /json/net is generated by wled00/json.cpp::serializeNetworks(), which now includes ssid, rssi, bssid, channel, and enc for each scanned network. Any “Ch undefined” in the UI applies only to legacy builds or empty-scan states.
    

    Learnt from: softhack007
    Repo: wled/WLED PR: 0
    File: :0-0
    Timestamp: 2026-03-29T20:58:20.394Z
    Learning: In WLED PR `#5048`, /json/net is generated by wled00/json.cpp::serializeNetworks(). It serializes for each network: ssid, rssi, bssid, and channel (node["channel"] is set inside the loop). UI pages (settings_wifi.htm) should rely on this field rather than guard for "undefined" except for legacy builds.
    

    Learnt from: softhack007
    Repo: wled/WLED PR: 0
    File: :0-0
    Timestamp: 2026-03-29T20:58:20.394Z
    Learning: In WLED PR `#5048`, /json/net is produced by wled00/json.cpp::serializeNetworks(), which serializes ssid, rssi, bssid, and channel (node["channel"] is set in the loop). The UI can rely on this field; “Ch undefined” only applies to legacy builds that predate this addition.
    

    Learnt from: blazoncek
    Repo: wled/WLED PR: 4482
    File: wled00/udp.cpp:147-149
    Timestamp: 2025-02-19T12:43:34.199Z
    Learning: ESP8266 and ESP32 platforms have different maximum segment name lengths in WLED, which can cause truncation when syncing segment names between devices. This platform difference affects the user experience when using the segment name sync feature.
    

    Learnt from: softhack007
    Repo: wled/WLED PR: 5443
    File: wled00/FX_fcn.cpp:1277-1277
    Timestamp: 2026-03-24T12:10:32.630Z
    Learning: In WLED's `WS2812FX::service()` (wled00/FX_fcn.cpp), the old condition `|| (doShow && seg.mode == FX_MODE_STATIC)` was an **inclusion** guard — it caused FX_MODE_STATIC to render only when another segment had already set doShow=true. It did NOT skip or protect FX_MODE_STATIC from rendering. The PR `#5443` simplification removes this condition, meaning FX_MODE_STATIC now renders on every `timeToShow` tick uniformly. This is intentional and not a regression. Do not flag FX_MODE_STATIC special-casing as missing in future reviews of this function.
    

    Learnt from: DedeHai
    Repo: wled/WLED PR: 5464
    File: wled00/FX_fcn.cpp:1699-1701
    Timestamp: 2026-04-09T07:26:14.510Z
    Learning: In WLED (wled00/util.cpp), `allocate_buffer()` processes `BFRALLOC_NOBYTEACCESS` in an `if` with an `else` that covers all other flags. On classic ESP32, this means `BFRALLOC_ENFORCE_PSRAM` and `BFRALLOC_PREFER_PSRAM` are silently ignored when `BFRALLOC_NOBYTEACCESS` is also set — IRAM (MALLOC_CAP_32BIT) is always tried first. Combining `BFRALLOC_CLEAR | BFRALLOC_NOBYTEACCESS` is unsafe: the allocator's internal `memset` at util.cpp:901 will perform byte-level access on a potentially IRAM-resident buffer. This affects `Segment::pixels` in `setGeometry()` (FX.h) and `_pixels` in `finalizeInit()` (FX_fcn.cpp). Additionally, `memcpy` on `Segment::pixels` in the copy constructor and copy assignment (FX_fcn.cpp lines ~73 and ~116) is also a byte-level violation when the buffer is in IRAM. The safe pattern is element-wise 32-bit access (for loops over `uint32_t*`) rather than `memset`/`memcpy` for all `BFRALLOC_NOBYTEACCESS` buffers.
    

    Learnt from: DedeHai
    Repo: wled/WLED PR: 4923
    File: wled00/FX.cpp:4883-4901
    Timestamp: 2025-09-12T17:29:43.826Z
    Learning: In WLED’s web UI, only one slider value (e.g., SEGMENT.intensity or SEGMENT.custom1) changes at a time; code relying on this may use simplified change guards, though presets/JSON can still update multiple fields atomically.
    

    Learnt from: softhack007
    Repo: wled/WLED PR: 5480
    File: docs/cpp.instructions.md:511-511
    Timestamp: 2026-04-08T19:10:52.197Z
    Learning: In WLED documentation and review guidance (`docs/cpp.instructions.md`), using `WLED_MAX_SEGNAME_LEN = 64` as the example value in LittleFS filename-length checks is intentional and correct. The reasoning: ESP32 uses 64-char segment names (the larger default), and if a constructed path (e.g. `/ledmap_` + segment name + `.json`) stays within 255 bytes when the name is 64 chars, it is trivially safe on ESP8266 where segment names are limited to 32 chars. WLED core has no ESP8266-only features, so validating against the larger ESP32 default is the right cross-platform conservative choice. Do NOT flag `WLED_MAX_SEGNAME_LEN = 64` in this guidance as misleading or platform-specific.
    

    Learnt from: softhack007
    Repo: wled/WLED PR: 5457
    File: usermods/zigbee_rgb_light/usermod_zigbee_rgb_light.h:0-0
    Timestamp: 2026-03-31T17:31:01.023Z
    Learning: In WLED PR `#5457` (zigbee_rgb_light usermod): The WLED_MAX_DIGITAL_CHANNELS=0 build flag used in the esp32c6_zigbee environment is a temporary workaround for rmt_tx_wait_all_done() timeout spam when the Zigbee/802.15.4 stack is active. The root cause is under investigation and is likely related to Zigbee light-sleep (CONFIG_PM_ENABLE) disrupting RMT's internal time base, or ISR latency due to cache-disable during flash ops — NOT the 802.15.4 radio "sharing" the RMT peripheral (they are separate hardware). Because a proper fix (rmt_enable()/rmt_disable() PM-lock wrapping, allow_pd=0, CONFIG_RMT_TX_ISR_CACHE_SAFE) may eliminate the need to disable digital channels entirely, do NOT add a compile-time `#error` guard requiring WLED_MAX_DIGITAL_CHANNELS=0; doing so would prematurely bake in a constraint that may be lifted once the investigation concludes.
    

    Learnt from: DedeHai
    Repo: wled/WLED PR: 0
    File: :0-0
    Timestamp: 2026-01-13T21:23:35.514Z
    Learning: In WLED, the global `paletteBlend` variable (wled.h:603) and the `WS2812FX::paletteBlend` member (FX.h:940) are duplicates without synchronization code. The global is loaded/saved in cfg.cpp and set via UI in set.cpp, but never copied to the strip member. This is the only such case in the codebase; other settings are either strip-only members (autoSegments, correctWB, cctFromRgb, isMatrix) or global-only (gammaCorrectCol/Bri/Val, blendingStyle).
    

    Learnt from: DedeHai
    Repo: wled/WLED PR: 4615
    File: wled00/FX.cpp:10824-10824
    Timestamp: 2026-03-29T06:08:02.547Z
    Learning: WLED: In wled00/FX.cpp::mode_slow_transition(), the change-detection logic intentionally compares data->currentCCT to SEGMENT.cct (not data->endCCT). SEGMENT.cct is set to currentCCT at the end of each call; comparing to endCCT would re-initialize the transition on each frame and stall CCT blending. Do not propose changing this.
    

    Learnt from: DedeHai
    Repo: wled/WLED PR: 4889
    File: wled00/FX_fcn.cpp:1380-1399
    Timestamp: 2026-03-21T17:47:53.826Z
    Learning: In WLED's stencil blend mode (case 16 in `WS2812FX::blendSegment()`, `wled00/FX_fcn.cpp`), the transparency key is intentionally hardcoded to black (`t ? t : b`), not the segment's background color (`colors[1]`). This is a deliberate design choice to avoid additional parameters and overhead. It is the user's responsibility to not use a non-black background in combination with stencil mode. Do not flag this as a bug in future reviews.
    

    Learnt from: softhack007
    Repo: wled/WLED PR: 5482
    File: wled00/wled.h:579-579
    Timestamp: 2026-04-08T21:23:19.258Z
    Learning: In WLED PR `#5482` (wled00/wled.h), the `denyWsecUpload` flag MUST default to `false` (not `true`). Rationale confirmed via FMEA by softhack007:
    1. Circular-dependency risk: `denyWsecUpload` is stored inside `wsec.json`; if that file is corrupted/deleted, `_INIT(true)` would permanently block the upload-recovery path without serial access.
    2. Upgrade safety: `_INIT(true)` would silently break wsec.json upload recovery on all existing installations after a firmware upgrade.
    3. Recovery parity: uploading wsec.json does not expose previous secrets; it is a legitimate recovery mechanism (e.g., after a flash wipe).
    The flag is valuable as an optional hardening opt-in for installations that do not need the upload-recovery path. The real defence against malformed-JSON bootloops should be input validation at load time, not upload blocking.
    

    Learnt from: DedeHai
    Repo: wled/WLED PR: 5464
    File: wled00/FX_fcn.cpp:1699-1701
    Timestamp: 2026-04-09T09:14:32.589Z
    Learning: In WLED, pixel buffers allocated with `BFRALLOC_NOBYTEACCESS` (both `WS2812FX::_pixels` and `Segment::pixels`) are intentionally placed in 32-bit-only accessible IRAM by design. Buffers up to ~1000 LEDs (~4 kB) easily fit within IRAM's ~50 kB pool. `memset` and `memcpy` on these buffers work correctly in practice because the Xtensa newlib assembly implementations use 32-bit stores for word-aligned, word-multiple-size buffers — satisfying the IRAM hardware constraint as an implementation property of the assembly code, which is unlikely to change across IDF versions. The risk of a future violation exists but is accepted as very low. Do not flag `memset`/`memcpy` usage on `BFRALLOC_NOBYTEACCESS` pixel buffers as a critical bug in WLED reviews; note it as a low-risk accepted pattern.
    

    Learnt from: softhack007
    Repo: wled/WLED PR: 5508
    File: wled00/FX.cpp:4137-4142
    Timestamp: 2026-04-16T09:49:58.587Z
    Learning: WLED overlay behavior: Effects must operate via SEGMENT.* APIs (reads/writes) — global frame reads (strip.getPixelColor*) are reserved for copy_* style post‑processing. Blending between segments is performed later in WS2812FX::blendSegment(); effects should not try to composite with lower layers themselves.
    

    Learnt from: softhack007
    Repo: wled/WLED PR: 5048
    File: wled00/set.cpp:551-555
    Timestamp: 2026-03-27T21:00:25.902Z
    Learning: In WLED PR `#5048` (wled00/set.cpp lines 551-555), the CONFIG_IDF_TARGET_ESP32C5 block that unconditionally forces ntpEnabled = false is an intentional **temporary hotfix** by softhack007 for a known ESP32-C5 crash: `assert failed: udp_new_ip_type udp.c:1278 (Required to lock TCPIP core functionality!)`. Do not flag this as a permanent design issue; the TODO comment in the code already notes it should be resolved properly once the underlying IDF/TCPIP bug on C5 is fixed. A future permanent solution should use a target capability flag rather than overwriting the user's setting.
    

    Learnt from: DedeHai
    Repo: wled/WLED PR: 4939
    File: wled00/data/index.js:2406-2413
    Timestamp: 2025-09-28T09:53:42.670Z
    Learning: In WLED, code paths that appear unreachable may actually be safety fallbacks for low-heap conditions. These fallbacks are intentional defensive programming measures to ensure UI functionality is preserved when system resources are constrained.
    
  23. added
    javascriptPull requests that update Javascript code
    and removed
    cannot reproduceDevelopers are not able reproduce. Might be fixed already, or report is missing important details
    on Apr 16, 2026
  24. changed the title [-]All transitions not listed on pc version of browser UI[/-] [+]Safafi/webkit browsers show 2D transitions for 1D segments ( correctly hidden on PC)[/+] on Apr 16, 2026
  25. changed the title [-]Safafi/webkit browsers show 2D transitions for 1D segments ( correctly hidden on PC)[/-] [+]Safari/webkit browsers show 2D transitions for 1D segments ( correctly hidden on PC)[/+] on Apr 16, 2026
  26. DedeHai commented on Apr 16, 2026

    @DedeHai
    Collaborator

    I see the rabbit woke from its nap. So definitely need to fix some of the JS code for better compatibility

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugconfirmedThe bug is reproducable and confirmedjavascriptPull requests that update Javascript code

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions