From 0d6ab1ee7679c4544e3fb8d7b2b312c2147dc7ea Mon Sep 17 00:00:00 2001 From: "@suet-kei.chan" Date: Sun, 17 May 2026 21:19:26 +0200 Subject: [PATCH 1/2] fix: dispatch scroll event on view switch to fix empty file list --- src/nmcfiles.ts | 63 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/src/nmcfiles.ts b/src/nmcfiles.ts index 72093246..2ec2476c 100644 --- a/src/nmcfiles.ts +++ b/src/nmcfiles.ts @@ -245,6 +245,69 @@ if (document.readyState === 'loading') { setupPendingShare() } +/** + * Fix for empty screen when switching between grid and list view. + * + * Root cause: the .files-list element is the actual scroll container. When the + * view class changes (list to grid or vice versa), the virtual scroll does not + * automatically recalculate visible rows for the new item sizes, leaving a blank + * screen until the user scrolls. Dispatching a synthetic scroll event on the + * .files-list element triggers the recalculation without changing scroll position. + */ +function setupGridViewScrollFix(): void { + let filesListEl: Element | null = null + let classObserver: MutationObserver | null = null + + /** + * Forces the virtual scroll to recalculate after a view switch. + * + * The actual scroll container is the .files-list element itself (scrollTop > 0 + * when scrolled, confirmed via DOM inspection). We dispatch a scroll event on it + * so the virtual scroll recalculates visible rows at the current scroll position. + * setTimeout ensures CSS has fully reflowed before the event fires. + * @param scrollContainer The .files-list element to dispatch the scroll event on. + */ + function forceVirtualScrollUpdate(scrollContainer: Element): void { + setTimeout(() => { + scrollContainer.dispatchEvent(new Event('scroll', { bubbles: true })) + }, 0) + } + + /** + * Attaches a class mutation observer to the files list element. + * @param el The files list element to observe. + */ + function attachClassObserver(el: Element): void { + let prevIsGrid = el.classList.contains('files-list--grid') + classObserver?.disconnect() + classObserver = new MutationObserver(() => { + const isGrid = el.classList.contains('files-list--grid') + if (isGrid === prevIsGrid) return + prevIsGrid = isGrid + + // Wait one frame for Vue to finish applying the new layout + requestAnimationFrame(() => forceVirtualScrollUpdate(el)) + }) + classObserver.observe(el, { attributes: true, attributeFilter: ['class'] }) + } + + const domObserver = new MutationObserver(() => { + const el = document.querySelector('.files-list') + if (el && el !== filesListEl) { + filesListEl = el + attachClassObserver(el) + } + }) + + domObserver.observe(document.body, { childList: true, subtree: true }) +} + +if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', setupGridViewScrollFix) +} else { + setupGridViewScrollFix() +} + window.addEventListener('DOMContentLoaded', function() { const breadcrumb = document.querySelector('.breadcrumb') const empty = document.querySelector('.files-list__empty') From af1cbf8ef981ebba1a6b40f7fced165e40bd0c32 Mon Sep 17 00:00:00 2001 From: "@suet-kei.chan" Date: Sun, 17 May 2026 21:24:38 +0200 Subject: [PATCH 2/2] fix: dispatch scroll event on view switch to fix empty file list MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The .files-list element is the actual scroll container (confirmed via DOM inspection — scrollTop > 0 on .files-list when scrolled, not on window or #app-content-vue). When switching between list and grid view, the virtual scroll does not automatically recalculate visible rows for the new item sizes, leaving a blank screen until the user manually scrolls. Fix: watch for files-list--grid class changes on .files-list via MutationObserver, then dispatch a synthetic scroll event on the same element via setTimeout so CSS has fully reflowed before the event fires. This triggers the virtual scroll to recalculate visible rows at the current position without changing the user's scroll position. --- src/nmcfiles.ts | 45 ++++++--------------------------------------- 1 file changed, 6 insertions(+), 39 deletions(-) diff --git a/src/nmcfiles.ts b/src/nmcfiles.ts index 2ec2476c..ecda7768 100644 --- a/src/nmcfiles.ts +++ b/src/nmcfiles.ts @@ -245,58 +245,25 @@ if (document.readyState === 'loading') { setupPendingShare() } -/** - * Fix for empty screen when switching between grid and list view. - * - * Root cause: the .files-list element is the actual scroll container. When the - * view class changes (list to grid or vice versa), the virtual scroll does not - * automatically recalculate visible rows for the new item sizes, leaving a blank - * screen until the user scrolls. Dispatching a synthetic scroll event on the - * .files-list element triggers the recalculation without changing scroll position. - */ +/** Fixes empty file list when switching between grid and list view. */ function setupGridViewScrollFix(): void { let filesListEl: Element | null = null let classObserver: MutationObserver | null = null - /** - * Forces the virtual scroll to recalculate after a view switch. - * - * The actual scroll container is the .files-list element itself (scrollTop > 0 - * when scrolled, confirmed via DOM inspection). We dispatch a scroll event on it - * so the virtual scroll recalculates visible rows at the current scroll position. - * setTimeout ensures CSS has fully reflowed before the event fires. - * @param scrollContainer The .files-list element to dispatch the scroll event on. - */ - function forceVirtualScrollUpdate(scrollContainer: Element): void { - setTimeout(() => { - scrollContainer.dispatchEvent(new Event('scroll', { bubbles: true })) - }, 0) - } + const domObserver = new MutationObserver(() => { + const el = document.querySelector('.files-list') + if (!el || el === filesListEl) return + filesListEl = el - /** - * Attaches a class mutation observer to the files list element. - * @param el The files list element to observe. - */ - function attachClassObserver(el: Element): void { let prevIsGrid = el.classList.contains('files-list--grid') classObserver?.disconnect() classObserver = new MutationObserver(() => { const isGrid = el.classList.contains('files-list--grid') if (isGrid === prevIsGrid) return prevIsGrid = isGrid - - // Wait one frame for Vue to finish applying the new layout - requestAnimationFrame(() => forceVirtualScrollUpdate(el)) + setTimeout(() => el.dispatchEvent(new Event('scroll', { bubbles: true })), 0) }) classObserver.observe(el, { attributes: true, attributeFilter: ['class'] }) - } - - const domObserver = new MutationObserver(() => { - const el = document.querySelector('.files-list') - if (el && el !== filesListEl) { - filesListEl = el - attachClassObserver(el) - } }) domObserver.observe(document.body, { childList: true, subtree: true })