librelad 5e06e77d7f fix(forms): one bad select no longer kills every dropdown after it
Reported: dropdowns dead on the app config page only, in a private window
(so not cache), with the served frontend confirmed identical to source.

The enhancer ran in a bare forEach with no try/catch anywhere, and
build() inserts its wrapper via `select.parentNode.insertBefore(...)`.
A detached select makes that a null deref, and one throw abandoned the
rest of the pass — every select AFTER it silently stayed native. The
MutationObserver callback had the same exposure for the remaining
mutation records in a batch.

The app config page is the one that can produce a detached select: it
builds its category panels in an async loop, so the observer can see a
node a later render already replaced. That matches "only app config".

Worse than losing the theme: if the throw landed after build() added
.custom-select-native, the select was left opacity:0 / pointer-events:
none behind a button with no listeners — a dropdown that looks right and
does nothing.

Now: detached/unconnected selects are skipped (they get enhanced when
their subtree is attached and the observer fires again), every
enhancement is individually guarded, a failed one is rolled back so it
can never be left invisible, and failures console.warn with the field
name instead of vanishing.

Verified with jsdom against the real file: detached nodes skipped without
throwing, a failure mid-pass leaves later selects working, and the
survivors open and set their value.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-12 22:40:26 +01:00
..