diff --git a/tests/test_downloads_tab_workflow_route.mjs b/tests/test_downloads_tab_workflow_route.mjs index 6d0bbb1..e9e9697 100644 --- a/tests/test_downloads_tab_workflow_route.mjs +++ b/tests/test_downloads_tab_workflow_route.mjs @@ -2150,6 +2150,10 @@ test('content-preserving Missing Models refresh patches the browser instead of c missingBrowserMethodsSource, 'setupMissingModelsVirtualizer' ); + const destroyMissingModelsVirtualizer = extractMethod( + missingBrowserMethodsSource, + 'destroyMissingModelsVirtualizer' + ); const displayMissingModels = extractMethod( missingBrowserMethodsSource, 'displayMissingModels' @@ -2175,6 +2179,18 @@ test('content-preserving Missing Models refresh patches the browser instead of c setupMissingModelsVirtualizer, /rowsHost\.innerHTML/ ); + assert.match( + destroyMissingModelsVirtualizer, + /removeEventListener\('scroll', state\.onScroll\)/ + ); + assert.match( + destroyMissingModelsVirtualizer, + /resizeObserver\?\.disconnect\?\./ + ); + assert.match( + destroyMissingModelsVirtualizer, + /_mrMissingVirtualState = null/ + ); assert.doesNotMatch( patchMissingModelsBrowserElement, /currentBrowser\.replaceWith\(nextBrowser\)/ @@ -2197,7 +2213,7 @@ test('content-preserving Missing Models refresh patches the browser instead of c ); assert.match( displayMissingModels, - /if \(!browserPatched\) \{\s*container\.innerHTML = browserHtml;/ + /if \(!browserPatched\) \{\s*this\.destroyMissingModelsVirtualizer\(container\);\s*container\.innerHTML = browserHtml;/ ); }); diff --git a/web/resolver/views/missing_browser_methods.js b/web/resolver/views/missing_browser_methods.js index 0d319b0..6317b00 100644 --- a/web/resolver/views/missing_browser_methods.js +++ b/web/resolver/views/missing_browser_methods.js @@ -514,6 +514,26 @@ export const missingBrowserMethods = { list?._mrMissingVirtualState?.update?.(force); }, + destroyMissingModelsVirtualizer(container) { + const list = container?.matches?.('.mr-missing-list') + ? container + : container?.querySelector?.('.mr-missing-list'); + const state = list?._mrMissingVirtualState; + if (!state) return false; + + if (state.onScroll) { + list.removeEventListener('scroll', state.onScroll); + } + state.resizeObserver?.disconnect?.(); + if (state.animationFrame) { + cancelAnimationFrame(state.animationFrame); + state.animationFrame = null; + } + state.destroyed = true; + list._mrMissingVirtualState = null; + return true; + }, + getMissingBrowserVirtualRowHeight(list = null) { const target = list instanceof HTMLElement ? list @@ -607,6 +627,9 @@ export const missingBrowserMethods = { } const existingState = list._mrMissingVirtualState; + if (existingState && existingState.rowsHost !== rowsHost) { + this.destroyMissingModelsVirtualizer(list); + } const state = existingState && existingState.rowsHost === rowsHost ? existingState : { @@ -620,6 +643,8 @@ export const missingBrowserMethods = { end: -1, resizeObserver: null, onScroll: null, + animationFrame: null, + destroyed: false, update: null }; @@ -629,7 +654,7 @@ export const missingBrowserMethods = { state.virtualScroll.style.height = `${Math.max(0, sortedMissingModels.length * state.rowHeight)}px`; state.update = (force = false) => { - if (!list.isConnected || !rowsHost.isConnected) return; + if (state.destroyed || !list.isConnected || !rowsHost.isConnected) return; const rowHeight = state.rowHeight || MISSING_ROW_FALLBACK_HEIGHT; const header = list.querySelector('.mr-missing-list-head'); @@ -2428,12 +2453,14 @@ export const missingBrowserMethods = { } if (!hasAnyModelsToDisplay && activeCount === 0) { + this.destroyMissingModelsVirtualizer(container); container.innerHTML = this.renderStatusMessage('All models are available! No missing models found.', 'success'); return; } // If no missing models but downloads are active, show a waiting message if (rawMissingCount === 0 && activeCount > 0 && !resolvedModels.length) { + this.destroyMissingModelsVirtualizer(container); container.innerHTML = this.renderStatusMessage( `${activeCount} download${activeCount > 1 ? 's' : ''} in progress. Local matches will refresh when complete.`, 'info' @@ -2455,6 +2482,7 @@ export const missingBrowserMethods = { const keepCurrentBrowser = preserveBrowser && Boolean(container.querySelector('.mr-missing-browser')); if (!keepCurrentBrowser) { + this.destroyMissingModelsVirtualizer(container); container.innerHTML = this.renderStatusMessage('Loading model folders...', 'info'); } @@ -2574,6 +2602,7 @@ export const missingBrowserMethods = { const browserPatched = options.preserveBrowser && this.patchMissingModelsBrowserElement(container, browserHtml); if (!browserPatched) { + this.destroyMissingModelsVirtualizer(container); container.innerHTML = browserHtml; } this.wireMissingModelsBrowser(container, data, sortedMissingModels);