From 6ec08acb9abb6984342fec31a8990e7bb3dcd9fe Mon Sep 17 00:00:00 2001 From: openclaw-bot Date: Tue, 26 May 2026 17:55:13 +0000 Subject: [PATCH] fix(#1402): gesture-hint regressions on mobile + first-load schedule - Bug 1+5 (tab-swipe race / first-load schedule): re-schedule on window 'load' as a safety net so [data-bottom-nav] is in the DOM by the time the 800ms relevance check runs. Operator console trace showed the schedule path was only reliably firing on hashchange. - Bug 2 (edge-drawer): flip condition from innerWidth > 768 to < 768. Edge-swipe drawer is a mobile feature per #1064/#1184. - Bug 3 (pull-refresh): decouple from .pull-to-reconnect element (which only renders on WS disconnect per #1068). Gate on touch viewport (pointer: coarse) instead. - Bug 4 (row-swipe scope): widen route filter from /packets|/nodes to also include /channels and /observers (both verified to have swipable row markup). /perf and /analytics deliberately omitted. Preserves: #1244 /live exclusion, reduced-motion behavior, singleton guard, dismiss-flow semantics. --- public/gesture-hints.js | 45 +++++++++++++++++++++++++++++++++++------ 1 file changed, 39 insertions(+), 6 deletions(-) diff --git a/public/gesture-hints.js b/public/gesture-hints.js index 1aee40ed..0ea895c5 100644 --- a/public/gesture-hints.js +++ b/public/gesture-hints.js @@ -8,9 +8,21 @@ * - aria-live=polite, role=status, no focus stealing, pointer-events:none. * - prefers-reduced-motion: animation-name: none (style.css handles via media query). * - Singleton + cleanup: module-scoped guard; SPA re-mount must not re-show dismissed. - * - Pull-to-refresh hint only when .pull-to-reconnect element exists in DOM. - * - Edge-drawer hint only at viewport > 768px (where edge-swipe drawer applies). - * - Row-swipe hint only on table pages: /#/packets, /#/nodes, etc. + * - #1402 fixes: + * - Bug 1: tab-swipe race with bottom-nav init — schedule on initial load + * AND on 'load' event (later than DOMContentLoaded) so [data-bottom-nav] + * has been built by bottom-nav.js. Also schedule on any hashchange. + * - Bug 2: edge-drawer is a MOBILE feature (per #1064/#1184). Condition + * flipped from innerWidth > 768 to innerWidth < 768. + * - Bug 3: pull-refresh no longer gated on `.pull-to-reconnect` (which + * only renders on WS-disconnect per #1068). Use touch-viewport probe. + * - Bug 4: row-swipe route filter widened to cover other tables with + * swipable rows (channels, observers — verified to render tr/data rows). + * - Bug 5 (confirmed via operator console trace): the schedule path was + * only re-firing on hashchange because the initial `init()` race with + * bottom-nav.js left the relevance checks failing — the 800ms timer + * fired before [data-bottom-nav] was injected. Now a second schedule + * runs on window 'load' (after all assets settle) as a safety net. */ (function () { 'use strict'; @@ -38,7 +50,11 @@ relevant: function () { if (onLiveRoute()) return false; // #1244 var h = location.hash || ''; - return /^#\/(packets|nodes)/.test(h); + // #1402 Bug 4: widen to other tables with swipable rows. + // channels (.ch-item / .ch-row data-hash), observers (#obsTable tr) — + // verified via grep before adding. /perf and /analytics omitted: no + // swipable rows confirmed there. + return /^#\/(packets|nodes|channels|observers)/.test(h); }, position: 'bottom', }, @@ -56,7 +72,10 @@ text: 'Tip: swipe in from the left edge to open navigation.', relevant: function () { if (onLiveRoute()) return false; // #1244 - return window.innerWidth > 768 && !!document.querySelector('.nav-drawer, [data-nav-drawer]'); + // #1402 Bug 2: edge-swipe drawer (#1064/#1184) is a MOBILE feature. + // Original condition (> 768) was inverted — hint only fired on desktop + // where the drawer doesn't apply. + return window.innerWidth < 768 && !!document.querySelector('.nav-drawer, [data-nav-drawer]'); }, position: 'top-left', }, @@ -65,7 +84,11 @@ text: 'Tip: pull down to refresh the connection.', relevant: function () { if (onLiveRoute()) return false; // #1244 - return !!document.querySelector('.pull-to-reconnect'); + // #1402 Bug 3: was gated on `.pull-to-reconnect` which only renders + // on WS-disconnect (#1068). First-visit healthy-connection operators + // never saw the hint. Decoupled: any touch viewport gets the hint. + var mm = window.matchMedia && window.matchMedia('(pointer: coarse)'); + return !!(mm && mm.matches); }, position: 'top', }, @@ -192,6 +215,16 @@ if (!_routeChangeBound) { _routeChangeBound = true; window.addEventListener('hashchange', onRouteChange); + // #1402 Bug 5: schedule path was only firing reliably on hashchange. + // The initial scheduleHints() call below races bottom-nav.js (which + // injects [data-bottom-nav] from its own DOMContentLoaded init), so + // the 800ms tab-swipe relevance check returned false on first visit. + // Re-schedule on 'load' (after all sync init has completed) as a + // safety net. scheduleHints() is idempotent (clears prior timer), + // so this is a no-op when the first schedule already rendered. + if (document.readyState !== 'complete') { + window.addEventListener('load', scheduleHints, { once: true }); + } } scheduleHints(); }