From 2d7c404ebb728f5af5dc3f921da27fbf44e68d10 Mon Sep 17 00:00:00 2001 From: Will Miao Date: Mon, 4 May 2026 20:26:13 +0800 Subject: [PATCH] fix(recipes): preserve scroll position on filter, search, and folder-driven reloads MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Five entry points that trigger recipe page reloads were not passing preserveScroll: true, causing the page to snap back to top after filtering, searching, or navigating folders — especially painful with hundreds of recipes. - RecipePageControls.resetAndReload() → refreshVirtualScroll() now passes { preserveScroll: true } (sidebar folder clicks/drag moves) - FilterManager applyFilters/clearAllFilters → loadRecipes(true) changed to loadRecipes({ preserveScroll: true }) - SearchManager performSearch → loadRecipes(true) changed to loadRecipes({ preserveScroll: true }) - SettingsManager reloadContent → loadRecipes() changed to loadRecipes({ preserveScroll: true }) The normalizeLoadRecipesOptions boolean path always forces preserveScroll: false — the object form is required to pass it. --- static/js/managers/FilterManager.js | 4 ++-- static/js/managers/SearchManager.js | 2 +- static/js/managers/SettingsManager.js | 2 +- static/js/recipes.js | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/static/js/managers/FilterManager.js b/static/js/managers/FilterManager.js index 1b8956c8..9af4d58e 100644 --- a/static/js/managers/FilterManager.js +++ b/static/js/managers/FilterManager.js @@ -599,7 +599,7 @@ export class FilterManager { // Call the appropriate manager's load method based on page type if (this.currentPage === 'recipes' && window.recipeManager) { - await window.recipeManager.loadRecipes(true); + await window.recipeManager.loadRecipes({ preserveScroll: true }); } else if (this.currentPage === 'loras' || this.currentPage === 'embeddings' || this.currentPage === 'checkpoints') { // For models page, reset the page and reload await getModelApiClient().loadMoreWithVirtualScroll(true, false); @@ -682,7 +682,7 @@ export class FilterManager { // Reload data using the appropriate method for the current page if (this.currentPage === 'recipes' && window.recipeManager) { - await window.recipeManager.loadRecipes(true); + await window.recipeManager.loadRecipes({ preserveScroll: true }); } else if (this.currentPage === 'loras' || this.currentPage === 'checkpoints' || this.currentPage === 'embeddings') { await getModelApiClient().loadMoreWithVirtualScroll(true, true); } diff --git a/static/js/managers/SearchManager.js b/static/js/managers/SearchManager.js index 60c0f28a..8796878f 100644 --- a/static/js/managers/SearchManager.js +++ b/static/js/managers/SearchManager.js @@ -301,7 +301,7 @@ export class SearchManager { // Call the appropriate manager's load method based on page type if (this.currentPage === 'recipes' && window.recipeManager) { - window.recipeManager.loadRecipes(true); // true to reset pagination + window.recipeManager.loadRecipes({ preserveScroll: true }); } else if (this.currentPage === 'loras' || this.currentPage === 'embeddings' || this.currentPage === 'checkpoints') { // For models page, reset the page and reload getModelApiClient().loadMoreWithVirtualScroll(true, false); diff --git a/static/js/managers/SettingsManager.js b/static/js/managers/SettingsManager.js index 11b091a0..b531ebca 100644 --- a/static/js/managers/SettingsManager.js +++ b/static/js/managers/SettingsManager.js @@ -2863,7 +2863,7 @@ export class SettingsManager { await resetAndReload(false); } else if (this.currentPage === 'recipes') { // Reload the recipes without updating folders - await window.recipeManager.loadRecipes(); + await window.recipeManager.loadRecipes({ preserveScroll: true }); } else if (this.currentPage === 'checkpoints') { // Reload the checkpoints without updating folders await resetAndReload(false); diff --git a/static/js/recipes.js b/static/js/recipes.js index 76e79854..68d5719b 100644 --- a/static/js/recipes.js +++ b/static/js/recipes.js @@ -19,7 +19,7 @@ class RecipePageControls { } async resetAndReload() { - refreshVirtualScroll(); + await refreshVirtualScroll({ preserveScroll: true }); } async refreshModels(fullRebuild = false) {