From d26b0a788b2bb964c3b804cfa323c6072393cb38 Mon Sep 17 00:00:00 2001 From: Dariusz L Date: Thu, 10 Sep 2026 01:01:41 +0200 Subject: [PATCH] Add refresh animations for busy option/search controls --- web/resolver/search/search_panel.js | 18 ++++++++++------ web/resolver/views/options_methods.js | 30 ++++++++++++++++++++------- 2 files changed, 35 insertions(+), 13 deletions(-) diff --git a/web/resolver/search/search_panel.js b/web/resolver/search/search_panel.js index 4c27761..9943921 100644 --- a/web/resolver/search/search_panel.js +++ b/web/resolver/search/search_panel.js @@ -1277,18 +1277,24 @@ export const searchPanelMethods = { container.querySelectorAll?.('.mr-search-progress-retry').forEach(button => { if (button.dataset.mlRetrySearchBound === '1') return; button.dataset.mlRetrySearchBound = '1'; - button.addEventListener('click', (event) => { + button.addEventListener('click', async (event) => { event.preventDefault(); event.stopPropagation(); const source = button.dataset.source || ''; const progress = state?.sourceProgress?.[source]; if (!source || !progress?.retryable || state?.activeSearchRunId) return; button.disabled = true; - this.searchOnline(missing, { - workflowKey, - source, - forceSearch: true - }); + const refreshAnimation = this.startRefreshButtonAnimation(button); + try { + await this.searchOnline(missing, { + workflowKey, + source, + forceSearch: true + }); + } finally { + refreshAnimation?.cancel(); + button.disabled = false; + } }); }); }, diff --git a/web/resolver/views/options_methods.js b/web/resolver/views/options_methods.js index d94e1c3..36184bb 100644 --- a/web/resolver/views/options_methods.js +++ b/web/resolver/views/options_methods.js @@ -1177,10 +1177,26 @@ export const optionsMethods = { const shortSha = (value) => value ? String(value).slice(0, 8) : ''; + const refreshButtons = new Set([ + modelListCheckBtn, baseModelsCheckBtn, hfIndexRefreshBtn, + metadataSizeAuditBtn, aria2CheckBtn, civitaiKeyCheckBtn, + civitaiSessionCheckBtn, hfTokenCheckBtn, braveKeyCheckBtn, + clearAllCacheBtn + ]); + const refreshAnimations = new Map(); + const setControlsBusyState = (buttons, busy, cancelBtn = null) => { const btnList = Array.isArray(buttons) ? buttons : [buttons]; btnList.forEach(btn => { - if (btn) btn.disabled = busy; + if (!btn) return; + btn.disabled = busy; + if (!refreshButtons.has(btn)) return; + refreshAnimations.get(btn)?.cancel(); + refreshAnimations.delete(btn); + btn.classList.toggle('mr-is-refreshing', busy); + if (busy) { + refreshAnimations.set(btn, this.startRefreshButtonAnimation(btn)); + } }); if (cancelBtn) { cancelBtn.hidden = !busy; @@ -2244,7 +2260,7 @@ export const optionsMethods = { }; const checkAria2Status = async ({ useUnsavedPath = true } = {}) => { - if (aria2CheckBtn) aria2CheckBtn.disabled = true; + setControlsBusyState(aria2CheckBtn, true); setAria2Status(''); renderAria2Summary({ backend: 'aria2' }, 'checking'); try { @@ -2276,7 +2292,7 @@ export const optionsMethods = { running: false }); } finally { - if (aria2CheckBtn) aria2CheckBtn.disabled = false; + setControlsBusyState(aria2CheckBtn, false); } }; @@ -2405,7 +2421,7 @@ export const optionsMethods = { } try { - if (button) button.disabled = true; + setControlsBusyState(button, true); setElementStatusText(statusEl, 'Checking...', 'is-pending'); const data = await this.fetchJson(endpoint, { method: 'POST', @@ -2420,7 +2436,7 @@ export const optionsMethods = { console.error('Model Resolver: Credential check error:', error); setElementStatusText(statusEl, error.message || 'Check failed', 'is-invalid'); } finally { - if (button) button.disabled = false; + setControlsBusyState(button, false); } }; @@ -2699,7 +2715,7 @@ export const optionsMethods = { const clearAllCachesFromOptions = async () => { try { - if (clearAllCacheBtn) clearAllCacheBtn.disabled = true; + setControlsBusyState(clearAllCacheBtn, true); if (clearAllCacheStatus) clearAllCacheStatus.textContent = 'Clearing frontend and backend cache...'; await this.clearAllResolverCaches(); if (clearAllCacheStatus) clearAllCacheStatus.textContent = 'Frontend and backend cache cleared.'; @@ -2712,7 +2728,7 @@ export const optionsMethods = { if (clearAllCacheStatus) clearAllCacheStatus.textContent = error.message || 'Failed to clear cache.'; this.showNotification('Cache clear failed', 'error'); } finally { - if (clearAllCacheBtn) clearAllCacheBtn.disabled = false; + setControlsBusyState(clearAllCacheBtn, false); } };