From c6f599a6ce68a259c04786d8178811e43acff2d8 Mon Sep 17 00:00:00 2001 From: badbread Date: Sun, 9 Aug 2026 11:14:34 -0700 Subject: [PATCH] fix(admin): DOM dropdown fallback so selects work in the desktop webview 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 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 --- services/api/src/admin.html | 173 ++++++++++++++++++++++++++++++++++++ 1 file changed, 173 insertions(+) diff --git a/services/api/src/admin.html b/services/api/src/admin.html index 167ede55..4eefc4e7 100644 --- a/services/api/src/admin.html +++ b/services/api/src/admin.html @@ -49,6 +49,25 @@ select{appearance:none;-webkit-appearance:none;padding-right:30px; background-image:url("data:image/svg+xml;utf8,"); background-repeat:no-repeat;background-position:right 10px center;} +/* Embedded-webview popup, + so clicking a dropdown does nothing. embedSelect (see script) intercepts the + click when body.embedded is set and renders this DOM list instead. In a real + browser the shim never activates and native selects stay native. Above modals + (z 100) and toasts (z 200) so it works inside the wizard/editor dialogs too. */ +.embed-select-pop{position:fixed;z-index:9000;background:var(--surface2); + border:1px solid var(--border2);border-radius:var(--radius);box-shadow:var(--shadow-lg); + max-height:min(320px,60vh);overflow-y:auto;padding:4px;font:inherit;color:var(--text); + -webkit-user-select:none;user-select:none;} +.embed-select-opt{padding:7px 10px;border-radius:var(--radius);cursor:pointer;white-space:nowrap; + overflow:hidden;text-overflow:ellipsis;line-height:1.3;} +.embed-select-opt:hover{background:var(--accent-soft);} +.embed-select-opt.active{background:var(--accent-soft);box-shadow:inset 0 0 0 1px var(--accent-dim);} +.embed-select-opt[aria-selected="true"]{color:var(--accent);font-weight:600;} +.embed-select-opt.disabled{opacity:.4;cursor:default;background:none;box-shadow:none;} +.embed-select-grp{padding:6px 10px 3px;font-size:11px;letter-spacing:.4px; + text-transform:uppercase;color:var(--dim2);} +.embed-select-grp+.embed-select-opt{margin-top:0;} /* Segmented two-state toggle (used where a native popup shim ── + The desktop client embeds this console in a composited webview that cannot + display the OS-drawn native stays in the DOM (its closed state paints fine + and holds the value); we only replace the popup. Choosing an option sets the + option as selected and fires a real bubbling change event, so every existing + onchange= handler and .value read keeps working with zero per-select changes. + Delegation means dynamically-added selects (camera editor, notifications, + wizard) are covered for free. In a normal browser isEmbedded() is false and + this whole shim is inert — native selects stay native. */ +(function embedSelectShim() { + let pop = null; // the open overlay element, or null + let curSel = null; // the