fix: Electron drag mask resolves by DOM order, not z-index

The home header carried -webkit-app-region: drag after <main> in the
tree, re-adding drag over the graph chrome row's no-drag carve-out —
Electron's mask applies carve-outs only to regions added EARLIER in the
DOM walk, so the gear/search/coverage/import buttons dragged the window.

Fix: a dedicated 52px drag-mask div placed FIRST in the home page DOM;
every carved control (chrome row, node detail card, toggle) comes later
and subtracts correctly. The header loses its app-region entirely — the
mask routes the pixels, the header stays a pure pointer-events container.
Retires the .lh-float-drag CSS class.

Verified live in the Electron dev shell by the owner: chrome row,
sidebar close button, logo, and window drag all work.
This commit is contained in:
amosblomqvist
2026-09-17 18:33:59 +03:00
parent 6e08d77ec2
commit 9863674a76
2 changed files with 20 additions and 10 deletions
+4 -8
View File
@@ -313,15 +313,15 @@ body {
in the plain browser — it keys off <html class="lh-electron">, which
SidebarProvider sets after mount when it detects the Electron shell.
- .lh-drag window-drag region (must sit on pointer-receiving els)
- .lh-drag window-drag region — on a dedicated mask div in
home.tsx placed FIRST in the page DOM (the mask
resolves by DOM order, not z-index: no-drag subtracts
only regions added earlier in the tree)
- .lh-no-drag carve-out for interactive elements inside a drag band
- .lh-lights-gutter the sidebar LH band's traffic-light gutter: lights
end at x≈68, the wordmark starts at 80px (5rem)
- .lh-closed-toggle same gutter for the sidebar toggle when the sidebar
is closed — the lights are static, the toggle moves
- .lh-float-drag the home header strip floats pointer-events-none over
the graph; only in Electron does it claim events and
become the drag surface for that page
───────────────────────────────────────────────────────────────────────── */
.lh-electron .lh-drag {
-webkit-app-region: drag;
@@ -337,7 +337,3 @@ body {
.lh-electron .lh-closed-toggle {
margin-left: 92px;
}
.lh-electron .lh-float-drag {
pointer-events: auto;
-webkit-app-region: drag;
}
+16 -2
View File
@@ -51,6 +51,17 @@ export function Home() {
return (
<div className="relative h-dvh">
{/* Window-drag strip (merged chrome, owner 2026-09-17): a dedicated
mask element, deliberately FIRST in the DOM. Electron's app-region
mask resolves by DOM order — a no-drag element subtracts only drag
regions that came EARLIER in the tree — not by z-index, so the
header's old inline drag region (after <main>) re-added itself over
the graph chrome row's carve-out and the buttons dragged the window.
Placed first, every carved control (chrome row, node detail card,
the toggle) comes later and subtracts correctly. Pure mask: it
never needs to receive events — the mask routes the pixels. */}
<div className="lh-drag absolute inset-x-0 top-0 h-13" aria-hidden="true" />
{/* Graph — the main view, full-bleed (owner, graph-view round 1): no
padding on any side; the canvas runs from the sidebar to the right
edge and from the bottom to the top, under everything else. */}
@@ -59,8 +70,11 @@ export function Home() {
</main>
{/* Header floats over the graph — pointer-events-none except the
toggle, so the canvas stays draggable/hoverable around it. */}
<header className="lh-float-drag pointer-events-none absolute inset-x-0 top-0 flex items-center gap-2 px-3 py-2">
toggle, so the canvas stays draggable/hoverable around it. Carries
no app-region of its own: the strip's drag comes from the mask
div above, and the toggle's lh-no-drag subtracts from it (it is
later in the tree, so the subtraction sticks). */}
<header className="pointer-events-none absolute inset-x-0 top-0 flex items-center gap-2 px-3 py-2">
<div className="pointer-events-auto">
<SidebarToggle />
</div>