From 07af3db2ca81184ddd24c90fce98f9affa0d030a Mon Sep 17 00:00:00 2001 From: Doxa Maps Date: Fri, 2 Oct 2026 05:18:54 +0000 Subject: [PATCH] check:nav-reachable-mobile polls the rail instead of reading it once at 600 ms MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit plugin-studio's first frame after the ☰ tap lands at ~1.3 s on a phone, so the single read at 600 ms caught the rail mid-slide (x = -4) and failed although it opened (measured on 443d2efda and f4066c36f alike). The gate now reads every 50 ms for up to 2.5 s and passes on the first fully-on-screen frame. "On screen" also checks the right edge: in RTL the closed rail parks at x = innerWidth, which x >= 0 alone counted as open. --- internal/check-nav-reachable-mobile.mjs | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/internal/check-nav-reachable-mobile.mjs b/internal/check-nav-reachable-mobile.mjs index acc737c35..2121ef002 100644 --- a/internal/check-nav-reachable-mobile.mjs +++ b/internal/check-nav-reachable-mobile.mjs @@ -178,7 +178,12 @@ for (const t of targets) { n.getBoundingClientRect().width > 0) if (!btn) return { clicked: false } btn.click() - return new Promise(res => setTimeout(() => { + // POLLED, not read once. A slow page (plugin-studio: its first frame after the tap lands at ~1.3 s on a phone) + // was caught MID-SLIDE by a single read at 600 ms (x = -4) and failed although the rail opened. Read every + // 50 ms for up to 2.5 s and pass on the first fully-on-screen frame. "On screen" is the rail INSIDE the + // viewport on both edges: in RTL the closed rail parks at the RIGHT (x = innerWidth), which x >= 0 alone + // would count as open. + const readRail = () => { const s2 = new Set(); let rail = null ;(function w(n) { if (!n || s2.has(n)) return; s2.add(n) @@ -187,8 +192,16 @@ for (const t of targets) { for (const c of (n.children || [])) w(c) })(document.body) const r = rail ? rail.getBoundingClientRect() : null - res({ clicked: true, railX: r ? Math.round(r.x) : null, railOnScreen: !!r && r.x >= 0 && r.width > 0 }) - }, 600)) + return { railX: r ? Math.round(r.x) : null, railOnScreen: !!r && r.width > 0 && r.x >= 0 && r.right <= window.innerWidth + 1 } + } + return new Promise(res => { + const t0 = performance.now() + ;(function poll() { + const now = readRail() + if (now.railOnScreen || performance.now() - t0 > 2500) return res({ clicked: true, ...now, ms: Math.round(performance.now() - t0) }) + setTimeout(poll, 50) + })() + }) }) if (!after.clicked || !after.railOnScreen) {