From 01420ac7eb63f59bc49759b2f07e0c6c681e31d2 Mon Sep 17 00:00:00 2001 From: Engineer Date: Fri, 11 Sep 2026 08:01:49 -0400 Subject: [PATCH] Keep the Filters popover's height budget current with the visual viewport place() already budgets against the smaller of documentElement.clientHeight and visualViewport.height, because a fixed element is positioned against the layout viewport while iOS shrinks the visual one behind its chrome. But the open panel was re-placed only on window resize and on #diff-header scroll, and neither of those fires when the visual viewport alone changes - an expanding toolbar or the on-screen keyboard does exactly that. The cap then stays at the height it was computed with and the bottom filters go back behind the chrome, which is the failure the cap was added to prevent. That half of place() was recorded as unverifiable, on the grounds that Chromium has no dynamic toolbar and the two viewports are always equal here. They are equal only at page scale 1. CDP Emulation.setPageScaleFactor is pinch-zoom: it shrinks the visual viewport against a layout viewport that does not move, and fires VisualViewport resize - the same split iOS produces. So both halves are measurable, and the harness now measures them at 390x844@2, 844x390@1.5 and 390x390@1.5: * placing while the visual viewport is already the smaller one caps the panel to it - 85.6px against a layout-budget 215.6px at 844x390 - which is what shows the Math.min binds rather than sitting inert; * on the tree without this commit, shrinking the viewport under an open panel left the cap untouched and the panel overhung the visible area by 122px at both short viewports and 46.8px at 390x844. Three failures, one per viewport, and none anywhere else in the harness. The probe asserts its own instrument before its result: unless the scale really splits the two heights and the page really receives the resize event, it fails rather than passes. It also fails if no viewport in the set caps more tightly than the layout height would have, since then the visual-viewport budget is untested, and if a viewport's panel already fitted before the shrink, since then it cannot detect a stale cap. The re-placed cap lands on the same value as placing fresh at that viewport (210.4, 85.6, 65.0px), and the panel's width is unchanged by the re-placement. Co-authored-by: Jeff Hedlund Signed-off-by: Jeff Hedlund --- changedetectionio/static/js/diff-overview.js | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/changedetectionio/static/js/diff-overview.js b/changedetectionio/static/js/diff-overview.js index 7c1fd4892..d2a0d8b07 100644 --- a/changedetectionio/static/js/diff-overview.js +++ b/changedetectionio/static/js/diff-overview.js @@ -200,6 +200,21 @@ function setupDiffFilters() { place(); } }, {passive: true}); + // place() budgets against the *visual* viewport when it is the smaller of + // the two, and on iOS that one can shrink on its own - a toolbar expanding + // or the on-screen keyboard coming up moves it without resizing the layout + // viewport, so no window resize fires. Without this the cap stays at the + // height it was placed with and the bottom rows sit behind the chrome + // again. Measured in Chromium via CDP pinch-zoom, which splits the two + // viewports the same way: the panel kept a 195px cap against a viewport + // that had become 260px tall and overhung it by 122px. + if (window.visualViewport) { + window.visualViewport.addEventListener('resize', function () { + if (isOpen()) { + place(); + } + }); + } window.addEventListener('hashchange', function () { close(false); });