From fac40ef09b17159cd08cc278bc505d8599c859dd Mon Sep 17 00:00:00 2001 From: dovvnloading Date: Tue, 28 Jul 2026 15:22:56 -0400 Subject: [PATCH] R8a: composer must never overlap the minimap or the token counter The composer island's width formula only ever reserved clearance from the minimap (a thin margin, not real space), and never accounted for the token counter at all beyond a 7px vertical gap - a real but razor-thin non-overlap that a font/padding change, or the composer picking up a second line, could erase. At common window widths the composer could visibly paint over both corners' chrome. Fixed with three independent, structural guarantees instead of one tuned distance: 1. The composer's layer now reserves REAL horizontal space on both sides (left:230px for the token counter's measured footprint, right:232px for the minimap's) and centers its card WITHIN that narrowed box, not the full viewport - centering can no longer land it in territory either neighbor already owns. 2. bottom:64px still gives the token-counter side a second, independent vertical guarantee on top of that. 3. Both the token counter and the minimap now sit on a new --gl-z-canvas-hud tier (21) that outranks the composer's app-layer tier (20) regardless of position - so even if the geometry above were ever wrong, neither can be painted over. Verified live at the app's real minimum window width (960px, per graphlink_desktop.py's own min_size) and at the width the collision was first reported at, with real measured pixel gaps on both sides, not just an absence of overlap. --- web_ui/src/app/styles.css | 58 +++++++++++++++++++++++++++++---------- 1 file changed, 44 insertions(+), 14 deletions(-) diff --git a/web_ui/src/app/styles.css b/web_ui/src/app/styles.css index 2738751..7022333 100644 --- a/web_ui/src/app/styles.css +++ b/web_ui/src/app/styles.css @@ -15,8 +15,8 @@ * Everything that floats above the canvas must take a value from here. Numbers * are spaced so a tier can be inserted without renumbering its neighbours. */ :root { - --gl-z-canvas-chrome: 5; /* persistent canvas HUD chrome (token counter) */ --gl-z-app-layer: 20; /* app-bar popovers, plugin picker, pin overlay */ + --gl-z-canvas-hud: 21; /* persistent canvas HUD (token counter, minimap) - must never be paintable-over by the composer or any app-layer chrome, on purpose, not just a reserved-space accident */ --gl-z-search: 22; /* search overlay, above the other app layers */ --gl-z-notification: 30; /* banner must beat app layers, not dialogs */ --gl-z-scrim: 40; /* modal scrim + the dialog it dims for */ @@ -238,6 +238,18 @@ body, inset as every other piece of canvas chrome, rather than a value that happened to be close but came from a different system. */ margin: var(--gl-chrome-inset) !important; + /* R8a: the composer island used to be able to visually cover the + minimap's bottom-right corner (its React Flow-supplied z-index:5 lost + to the composer's app-layer:20 wherever they geometrically + overlapped) - the same class of bug the token counter had on the + opposite corner. .app-composer-layer's own width now reserves real + space on BOTH sides so overlap shouldn't happen in practice - but + "should never be able to" means this can't depend solely on getting + that geometry right, so both HUD elements ALSO outrank every + app-layer surface here, independent of position. !important for the + same reason as the margin above: RF's own stylesheet sets a z-index + on this exact element and loads after ours. */ + z-index: var(--gl-z-canvas-hud) !important; background-color: var(--gl-surface-node-body); border: 1px solid var(--gl-surface-border); border-radius: 8px; @@ -1137,26 +1149,42 @@ body, docked flush at the bottom with no visual container of its own. It's now a floating island - a sibling of app-token-counter-layer/ app-notification-layer inside app-canvas-region - so the canvas renders - full-bleed behind it. bottom:64px (not the other layers' usual 16px) is - deliberate: at the narrow end of the supported window range a centered - card can sit close to app-token-counter-layer's footprint horizontally, - so the clearance is enforced vertically instead (the card's whole box - stays above the token counter's top edge everywhere), which doesn't - depend on how close the two ever get side to side. pointer-events:none - on the layer + pointer-events:auto on the card keeps the canvas - clickable/draggable everywhere the card doesn't visually cover it. */ + full-bleed behind it. + + It shares the bottom-left and bottom-right corners with two other real + pieces of chrome (the token counter, the minimap), so its box reserves + REAL space on both sides rather than relying on a thin, easy-to-erode + margin: `left: 230px` clears the token counter's measured footprint + (16px inset + ~193px content + a real gap, not a bare few px), `right: + 232px` clears the minimap's footprint (16px margin + 200px + 16px) the + same way. The card centers WITHIN that narrowed box via flex, not the + full viewport, so centering can never land it in territory either + neighbor already owns - a structural guarantee, not a tuned distance + that happens to work today. bottom:64px on top of that gives the + token-counter side a SECOND, independent vertical guarantee (its ~41px + height plus the 16px inset it shares with this layer still leaves real + clearance even if the horizontal reservation above were ever wrong). + And on both sides, --gl-z-canvas-hud gives the token counter and the + minimap themselves a THIRD guarantee: outranking every app-layer + surface regardless of position (see .scene-minimap's and + .app-token-counter-layer's own comments) - "should never be able to" + means no single mechanism carries that promise alone. pointer- + events:none on the layer + pointer-events:auto on the card keeps the + canvas clickable/draggable everywhere the card doesn't visually cover + it. */ .app-composer-layer { position: absolute; - left: 50%; + left: 230px; + right: 232px; bottom: 64px; - transform: translateX(-50%); - width: min(clamp(480px, calc(100% - 220px), 760px), calc(100% - 24px)); + display: flex; + justify-content: center; z-index: var(--gl-z-app-layer); pointer-events: none; } .app-composer-layer > .composer-dock { - width: 100%; + width: min(clamp(480px, calc(100% - 24px), 760px), calc(100vw - 48px)); pointer-events: auto; } @@ -1488,7 +1516,9 @@ body, position: absolute; bottom: var(--gl-chrome-inset); left: var(--gl-chrome-inset); - z-index: var(--gl-z-canvas-chrome); + /* R8a: outranks the composer/app-layer tier, same reasoning as the + minimap - see .scene-minimap's own comment. */ + z-index: var(--gl-z-canvas-hud); } .token-counter {