From a69bc3a264e8c25a0a8e14b87933ebeae91c6cd9 Mon Sep 17 00:00:00 2001 From: nesquena-hermes Date: Thu, 16 Jul 2026 01:15:11 +0000 Subject: [PATCH] fix(#6105): close the drawer-over-picker stacking class via z-index, not per-site guards MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Codex found the body-reparented picker (z-200, later body child) could be revealed UNDER a full-screen drawer/panel via multiple paths (swipe, breakpoint resize, profile-switch). Rather than guard each open-site (whack-a-mole + my resize guard over-fired on keyboard reflows), fix the class once: .model-dropdown--floating gets z-index:190 — above the composer-mobile-config-panel (z-180) it opens from, below the mobile sidebar + workspace panel (both z-200) — so any drawer/panel that opens covers the picker, matching master's stacking intent. Reverted the two boot.js guards. Co-authored-by webtecnica. --- static/boot.js | 22 ---------------------- static/style.css | 11 +++++++++-- 2 files changed, 9 insertions(+), 24 deletions(-) diff --git a/static/boot.js b/static/boot.js index 7b32820b..0a8f7774 100644 --- a/static/boot.js +++ b/static/boot.js @@ -436,11 +436,6 @@ function _openMobileSidebarFromGesture(){ if(_isDesktopWidth())return; const sidebar=document.querySelector('.sidebar'); if(!sidebar)return; - // #6105/#6080: the phone model picker is reparented to at z-index:200, - // the same level as the sidebar — as a later body child it would paint ON TOP - // of the drawer. Close it first (which also restores it into the footer) so a - // left-edge swipe reveals the session drawer cleanly, not an orphaned popover. - try{if(typeof closeModelDropdown==='function')closeModelDropdown();}catch(_){} try{if(typeof _syncMobileSidebarPanelFromMainView==='function')_syncMobileSidebarPanelFromMainView();}catch(_){} const layout=document.querySelector('.layout'); if(layout)layout.classList.remove('sidebar-collapsed'); @@ -2482,23 +2477,6 @@ function applyEmptyStateSuggestionPref(){ } window.addEventListener('resize',()=>{ - // #6105/#6080: the floating phone model picker is a child at z-index:200, - // the same level as the mobile sidebar drawer and the workspace panel. A direct - // landscape/tablet→phone resize can re-reveal an already-open drawer/panel - // *underneath* the picker (no click fires, so outside-click-close doesn't run). - // Close + restore the picker before we re-sync drawer state, but only when a - // phone-width layout will actually surface one of those z-200 siblings — so an - // ordinary keyboard/URL-bar reflow doesn't dismiss the picker mid-use. - try{ - if(typeof closeModelDropdown==='function' && !_isDesktopWidth()){ - const dd=document.getElementById('composerModelDropdown'); - if(dd&&dd.classList.contains('open')){ - const drawerOpen=!!document.querySelector('.sidebar.mobile-open'); - const panelOpen=(typeof _workspacePanelMode!=='undefined'&&_workspacePanelMode&&_workspacePanelMode!=='closed'); - if(drawerOpen||panelOpen) closeModelDropdown(); - } - } - }catch(_){} _syncWorkspacePanelInlineWidth(); syncWorkspacePanelState(); if(!window.visualViewport) _forceMobileViewportReflow(); diff --git a/static/style.css b/static/style.css index d4620e57..d6d8f505 100644 --- a/static/style.css +++ b/static/style.css @@ -3253,8 +3253,15 @@ Geist Contrast skin — both establish a fixed containing block that would otherwise resolve `fixed` against the FOOTER, dropping the menu below the fold). Same idiom as #profileDropdown. JS owns left/top/width/maxHeight; this - rule only flips positioning and relaxes the desktop width clamps. */ -.model-dropdown.model-dropdown--floating{position:fixed;left:0;bottom:auto;min-width:0;} + rule only flips positioning and relaxes the desktop width clamps. + z-index:190 is deliberate: ABOVE the .composer-mobile-config-panel (z-180) it + can be opened from, but BELOW the full-screen mobile sidebar drawer and + workspace panel (both z-200) — so whenever one of those surfaces opens (by + swipe, breakpoint resize, profile-switch, or any future path) it covers the + picker, exactly as it did on master when the picker lived inside the composer's + lower stacking context. This single stacking rule closes the whole "drawer + opened behind the floating picker" class without per-open-site guards. */ +.model-dropdown.model-dropdown--floating{position:fixed;left:0;bottom:auto;min-width:0;z-index:190;} .model-scope-note{position:sticky;top:0;z-index:2;padding:9px 14px;border-bottom:1px solid var(--border);color:var(--text);font-size:11px;font-weight:650;line-height:1.4;background:color-mix(in srgb,var(--surface) 82%,var(--accent-bg));box-shadow:0 1px 0 rgba(0,0,0,.12);} .model-group{padding:8px 14px 4px;font-size:10px;font-weight:700;letter-spacing:.04em;color:var(--muted);text-transform:uppercase;border-top:1px solid var(--border2);margin-top:2px;}.model-group.collapsible{cursor:pointer;user-select:none}.model-group.collapsible:before{content:'\25b6';display:inline-block;font-size:8px;margin-right:5px;transition:transform .15s ease}.model-group.collapsible.open:before{transform:rotate(90deg)}.model-group-body:empty{display:none} .model-group.sub{padding:4px 14px 2px 22px;font-size:9px;border-top:none;margin-top:0;letter-spacing:.03em;color:var(--muted);background:transparent;}.model-group.sub.collapsible:before{content:'\25b6';display:inline-block;font-size:7px;margin-right:4px;transition:transform .15s ease}.model-group.sub.collapsible.open:before{transform:rotate(90deg)}.model-group-body.sub{padding-left:8px;border-left:1px solid var(--border);}