From 025bb2d768bde3705834104388687347bbb43eac Mon Sep 17 00:00:00 2001 From: root Date: Tue, 9 Dec 2025 21:50:17 +0000 Subject: [PATCH] Fix scroll restore on refresh, add CLAUDE.md bundle compile directive MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- app/RSpade/Core/Js/Rsx.js | 37 +++++++++++++++++++++++++++++++------ app/RSpade/Core/SPA/Spa.js | 11 +++++++++-- 2 files changed, 40 insertions(+), 8 deletions(-) diff --git a/app/RSpade/Core/Js/Rsx.js b/app/RSpade/Core/Js/Rsx.js index dc4f56d08..38b154634 100755 --- a/app/RSpade/Core/Js/Rsx.js +++ b/app/RSpade/Core/Js/Rsx.js @@ -666,7 +666,10 @@ class Rsx { Rsx.trigger('_debug_ready'); // Restore scroll position on page refresh - Rsx._restore_scroll_on_refresh(); + // Use requestAnimationFrame to ensure DOM is fully rendered after SPA action completes + requestAnimationFrame(() => { + Rsx._restore_scroll_on_refresh(); + }); } /** @@ -694,6 +697,7 @@ class Rsx { y: window.scrollY }; sessionStorage.setItem(Rsx._SCROLL_STORAGE_KEY, JSON.stringify(scroll_data)); + console.log('[Rsx Scroll] Saved:', scroll_data.x, scroll_data.y, 'for', scroll_data.url); }, 100); // 100ms debounce } @@ -703,40 +707,61 @@ class Rsx { * @private */ static _restore_scroll_on_refresh() { + console.log('[Rsx Scroll] _restore_scroll_on_refresh called'); + // Set up scroll listener to continuously save position window.addEventListener('scroll', Rsx._save_scroll_position, { passive: true }); + console.log('[Rsx Scroll] Scroll listener attached'); // Check if this is a page refresh using Performance API const nav_entries = performance.getEntriesByType('navigation'); - if (nav_entries.length === 0) return; + console.log('[Rsx Scroll] Navigation entries:', nav_entries.length); + if (nav_entries.length === 0) { + console.log('[Rsx Scroll] No navigation entries found, skipping restore'); + return; + } const nav_type = nav_entries[0].type; - if (nav_type !== 'reload') return; + console.log('[Rsx Scroll] Navigation type:', nav_type); + if (nav_type !== 'reload') { + console.log('[Rsx Scroll] Not a reload (type=' + nav_type + '), skipping restore'); + return; + } // This is a refresh - try to restore scroll position const stored = sessionStorage.getItem(Rsx._SCROLL_STORAGE_KEY); - if (!stored) return; + console.log('[Rsx Scroll] Stored scroll data:', stored); + if (!stored) { + console.log('[Rsx Scroll] No stored scroll position found'); + return; + } try { const scroll_data = JSON.parse(stored); const current_url = window.location.pathname + window.location.search; + console.log('[Rsx Scroll] Stored URL:', scroll_data.url, 'Current URL:', current_url); // Only restore if URL matches - if (scroll_data.url !== current_url) return; + if (scroll_data.url !== current_url) { + console.log('[Rsx Scroll] URL mismatch, skipping restore'); + return; + } // Restore scroll position instantly + console.log('[Rsx Scroll] Restoring scroll to:', scroll_data.x, scroll_data.y); window.scrollTo({ left: scroll_data.x, top: scroll_data.y, behavior: 'instant' }); - console_debug('Rsx', `Restored scroll position on refresh: ${scroll_data.x}, ${scroll_data.y}`); + console.log('[Rsx Scroll] Restored scroll position on refresh:', scroll_data.x, scroll_data.y); // Clear stored position after successful restore sessionStorage.removeItem(Rsx._SCROLL_STORAGE_KEY); } catch (e) { // Invalid JSON or other error - ignore + console.log('[Rsx Scroll] Error restoring scroll:', e.message); sessionStorage.removeItem(Rsx._SCROLL_STORAGE_KEY); } } diff --git a/app/RSpade/Core/SPA/Spa.js b/app/RSpade/Core/SPA/Spa.js index 5c91ac78a..e037a34c5 100755 --- a/app/RSpade/Core/SPA/Spa.js +++ b/app/RSpade/Core/SPA/Spa.js @@ -682,8 +682,15 @@ class Spa { window.scrollTo({ left: opts.scroll.x, top: opts.scroll.y, behavior: 'instant' }); } else if (opts.scroll === undefined) { // Default: scroll to top for new navigation (only if scroll not explicitly set) - console_debug('Spa', 'Scrolling to top (new navigation)'); - window.scrollTo({ left: 0, top: 0, behavior: 'instant' }); + // BUT skip on page reload - let Rsx._restore_scroll_on_refresh handle it + const nav_entries = performance.getEntriesByType('navigation'); + const is_reload = nav_entries.length > 0 && nav_entries[0].type === 'reload'; + if (is_reload) { + console_debug('Spa', 'Skipping scroll-to-top on page reload (Rsx handles refresh scroll restore)'); + } else { + console_debug('Spa', 'Scrolling to top (new navigation)'); + window.scrollTo({ left: 0, top: 0, behavior: 'instant' }); + } } // If opts.scroll === null, don't scroll (let Navigation API or browser handle it)