Skip to content

fix(admin): DOM dropdown fallback so selects work in the desktop webview embed - #608

Merged
badbread merged 1 commit into
mainfrom
fix/admin-embed-dropdowns
Aug 9, 2026
Merged

fix(admin): DOM dropdown fallback so selects work in the desktop webview embed#608
badbread merged 1 commit into
mainfrom
fix/admin-embed-dropdowns

Conversation

@badbread

@badbread badbread commented Aug 9, 2026

Copy link
Copy Markdown
Owner

The bug

The desktop client embeds the web admin console (services/api/src/admin.html) in a webview_windows webview that runs in visual-hosting / composited mode. That mode cannot display the OS-drawn popup of a native <select>, so clicking any dropdown in the embedded server-settings console does nothing. All ~53 <select> elements are affected. In a normal browser they work fine; only the desktop embed is broken.

The fix: an embed-only DOM-dropdown shim

Rather than rewrite 53 selects, this adds one small, reusable shim to admin.html:

  • Activates only when embedded. It gates on the existing isEmbedded() helper (body.embedded, set by bootSSO from ?embedded=1 and/or #embed=1) that the rest of the file already uses. In a normal browser the shim is completely inert and native selects stay native.
  • Event delegation, not per-select wiring. A single capture-phase mousedown/keydown interceptor on document covers selects added dynamically (camera editor, notifications pane, wizard) for free.
  • Replaces only the popup. On opening a select in embed mode it preventDefault()s the dead native popup and renders a DOM overlay of that select's options, positioned under the control (flips above / clamps into the viewport). The native <select> stays in the DOM: its closed state paints fine and holds the value.
  • Zero-change integration. Choosing an option marks the option selected and dispatches real bubbling input + change events, so every existing onchange= handler and .value read keeps working untouched.
  • Closes on selection, outside click, Esc, scroll and resize. Keyboard open/nav/commit supported. Respects disabled selects and disabled options. Renders <optgroup> labels (none in the file today, but handled).
  • Styled with the console's existing tokens (--surface2, --border2, --accent-soft, --accent, --shadow-lg).

Verification

  • Extracted the inline <script> and ran node --check (passes).
  • Built crumb-api on the dev box with the modified admin.html (green; include_str! embed intact).
  • Exercised the shim in a browser via a harness using the exact CSS+JS: basic open, preventDefault of the native popup, option select firing change and updating .value, preselected-value highlight, disabled option not selectable, disabled select not opening, optgroup rendering, outside-click / Esc / scroll close, keyboard open+nav+commit, and confirmed it is fully inert (no preventDefault, no overlay) when body.embedded is absent.

How to verify in a plain browser

Load http://<host>:8080/admin?embedded=1#embed=1, then open any dropdown in Server Settings. It should open as a DOM list (not the native popup), and picking an option should update the setting (the underlying change handler fires and persists). Loading /admin without the embed flag keeps native selects unchanged.

…iew embed

The desktop client embeds the web admin console in a composited/visual-hosting
webview that cannot paint the OS-drawn popup of a native <select>, so clicking
any dropdown in the embedded console does nothing. All ~53 selects were affected;
in a normal browser they work fine.

Add one small, reusable shim to admin.html that activates only when the console
is embedded (body.embedded, set by bootSSO from ?embedded=1 / #embed=1 - the same
isEmbedded() signal the rest of the file already uses). It installs a single
delegated capture-phase mousedown/keydown interceptor on document, so it covers
dynamically-added selects (camera editor, notifications, wizard) with no
per-select wiring. On opening a select in embed mode it preventDefault()s the
dead native popup and renders a DOM overlay listing that select's options,
positioned under the control (flipping above / clamping into the viewport).

Choosing an option marks the option selected and dispatches real bubbling input
and change events, so every existing onchange= handler and .value read keeps
working unchanged. The native <select> stays in the DOM (its closed state paints
fine and holds the value); only the popup is replaced. Closes on selection,
outside click, Esc, scroll and resize; supports keyboard open/nav/commit;
respects disabled selects and disabled options; renders optgroup labels. In a
normal browser isEmbedded() is false and the whole shim is inert.

Signed-off-by: badbread <badbread@users.noreply.github.com>
@badbread
badbread merged commit e874a1c into main Aug 9, 2026
7 checks passed
@badbread
badbread deleted the fix/admin-embed-dropdowns branch August 9, 2026 18:39
badbread added a commit that referenced this pull request Aug 9, 2026
Bring the [0.2.0] UNRELEASED section current with the final PRs merged after the
previous changelog pass: the release version-drift guard (#605), the
fresh-install-audit fixes (#606), the Android playback-wall intensity batching
(#607), and the desktop-webview embedded-dropdown fix (#608). Added to Fixed and
to All merged changes. Still UNRELEASED; dating the header is the release step.

Signed-off-by: badbread <badbread@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant