diff --git a/web/resolver/resolver_dialog.js b/web/resolver/resolver_dialog.js index 81028f4..4e12503 100644 --- a/web/resolver/resolver_dialog.js +++ b/web/resolver/resolver_dialog.js @@ -66,6 +66,8 @@ export class ResolverManagerDialog extends ComfyDialog { this._dragging = false; this._dragStart = null; this._analysisProgressToken = null; + this._workflowDataLoadToken = null; + this._loadedModelsLoadToken = null; this._locateAnimationFrame = null; this._viewportClampFrame = null; this.activeWorkflowRouteKey = this.getActiveWorkflowRouteKey(); diff --git a/web/resolver/search/download_target_methods.js b/web/resolver/search/download_target_methods.js index c6254e0..7c689b9 100644 --- a/web/resolver/search/download_target_methods.js +++ b/web/resolver/search/download_target_methods.js @@ -704,6 +704,8 @@ export const downloadTargetMethods = { this.downloadSubfolders.clear(); this.downloadTargetSelections?.clear(); this._analysisProgressToken = null; + this._workflowDataLoadToken = null; + this._loadedModelsLoadToken = null; await this.ensureCapabilitiesLoaded(); await this.ensureBaseModelsLoaded(); diff --git a/web/resolver/shell/lifecycle_graph_methods.js b/web/resolver/shell/lifecycle_graph_methods.js index 1992f59..28055d9 100644 --- a/web/resolver/shell/lifecycle_graph_methods.js +++ b/web/resolver/shell/lifecycle_graph_methods.js @@ -116,6 +116,14 @@ export const lifecycleGraphMethods = { async loadWorkflowData(workflow = null, { force = false } = {}) { if (!this.contentElement) return; + const loadToken = `missing-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`; + this._workflowDataLoadToken = loadToken; + const shouldRenderMissingModels = () => ( + this.activeTab === 'missing' && + this._workflowDataLoadToken === loadToken && + this.contentElement + ); + // Show loading state try { // Use provided workflow, or get current workflow from ComfyUI @@ -124,8 +132,12 @@ export const lifecycleGraphMethods = { } if (!workflow) { - this._analysisProgressToken = null; - this.contentElement.innerHTML = '

No workflow loaded. Please load a workflow first.

'; + if (this._workflowDataLoadToken === loadToken) { + this._analysisProgressToken = null; + } + if (shouldRenderMissingModels()) { + this.contentElement.innerHTML = '

No workflow loaded. Please load a workflow first.

'; + } return; } this.syncWorkflowScopedQueue(workflow); @@ -140,19 +152,23 @@ export const lifecycleGraphMethods = { this.cachedWorkflowSignature === workflowSignature && this.cachedAnalysisData ) { - this.displayMissingModels(this.contentElement, this.cachedAnalysisData); - this.reconnectActiveDownloads(); + if (shouldRenderMissingModels()) { + this.displayMissingModels(this.contentElement, this.cachedAnalysisData); + this.reconnectActiveDownloads(); + } return; } const analysisId = `an-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`; this._analysisProgressToken = analysisId; - this.contentElement.innerHTML = this.renderAnalysisProgress({ - status: 'starting', - message: 'Starting analysis...', - current: 0, - total: 0 - }); + if (shouldRenderMissingModels()) { + this.contentElement.innerHTML = this.renderAnalysisProgress({ + status: 'starting', + message: 'Starting analysis...', + current: 0, + total: 0 + }); + } // Call analyze endpoint const progressPromise = this.pollAnalysisProgress(analysisId, analysisId); @@ -161,7 +177,9 @@ export const lifecycleGraphMethods = { headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ workflow, analysis_id: analysisId }) }); - this._analysisProgressToken = null; + if (this._analysisProgressToken === analysisId) { + this._analysisProgressToken = null; + } await progressPromise; if (!response.ok) { @@ -169,18 +187,24 @@ export const lifecycleGraphMethods = { } const data = await response.json(); - this.cachedWorkflowSignature = workflowSignature; - this.cachedAnalysisData = data; - this.saveAnalysisCacheForActiveWorkflow(); - this.displayMissingModels(this.contentElement, data); + if (this._workflowDataLoadToken === loadToken) { + this.cachedWorkflowSignature = workflowSignature; + this.cachedAnalysisData = data; + this.saveAnalysisCacheForActiveWorkflow(); + } + if (shouldRenderMissingModels()) { + this.displayMissingModels(this.contentElement, data); - // Reconnect any active downloads to their new progress divs - this.reconnectActiveDownloads(); + // Reconnect any active downloads to their new progress divs + this.reconnectActiveDownloads(); + } } catch (error) { - this._analysisProgressToken = null; + if (this._workflowDataLoadToken === loadToken) { + this._analysisProgressToken = null; + } console.error('Model Resolver: Error loading workflow data:', error); - if (this.contentElement) { + if (shouldRenderMissingModels()) { this.contentElement.innerHTML = `

Error: ${error.message}

`; } } diff --git a/web/resolver/shell/workflow_state_methods.js b/web/resolver/shell/workflow_state_methods.js index 3d4279b..b9e4d6b 100644 --- a/web/resolver/shell/workflow_state_methods.js +++ b/web/resolver/shell/workflow_state_methods.js @@ -306,6 +306,9 @@ export const workflowStateMethods = { this.cachedAnalysisData = null; this.cachedLoadedModelsSignature = null; this.cachedLoadedModelsData = null; + this._analysisProgressToken = null; + this._workflowDataLoadToken = null; + this._loadedModelsLoadToken = null; for (const [missingSearchKey, state] of this.searchResultCache.entries()) { if (!this.hasBackgroundSearchJob?.(key, missingSearchKey, state.activeSearchRunId)) { state.activeSearchRunId = null; diff --git a/web/resolver/utils/render_format_methods.js b/web/resolver/utils/render_format_methods.js index ee5251b..1d52624 100644 --- a/web/resolver/utils/render_format_methods.js +++ b/web/resolver/utils/render_format_methods.js @@ -162,10 +162,18 @@ export const renderFormatMethods = { while (this._analysisProgressToken === token) { try { const response = await api.fetchApi(`/model_resolver/analyze-progress/${analysisId}`); - if (response.ok && this.contentElement && this._analysisProgressToken === token) { + if ( + response.ok && + this.contentElement && + this._analysisProgressToken === token && + this.activeTab === 'missing' + ) { const progress = await response.json(); this.contentElement.innerHTML = this.renderAnalysisProgress(progress); if (progress.status === 'completed' || progress.status === 'error') { + if (this._analysisProgressToken === token) { + this._analysisProgressToken = null; + } return; } } diff --git a/web/resolver/views/missing_browser_methods.js b/web/resolver/views/missing_browser_methods.js index 9d5dbe1..4bdf850 100644 --- a/web/resolver/views/missing_browser_methods.js +++ b/web/resolver/views/missing_browser_methods.js @@ -685,7 +685,6 @@ export const missingBrowserMethods = { // Skip rendering if active tab is not "missing" if (this.activeTab !== 'missing') { - container.innerHTML = ''; return; } diff --git a/web/resolver/views/tabs_loaded_methods.js b/web/resolver/views/tabs_loaded_methods.js index 3f6f88e..053b1a3 100644 --- a/web/resolver/views/tabs_loaded_methods.js +++ b/web/resolver/views/tabs_loaded_methods.js @@ -38,6 +38,12 @@ export const tabsLoadedMethods = { this.activeTab = nextTab; this.persistActiveTab(this.activeTab); + if (this.activeTab !== 'missing') { + this._analysisProgressToken = null; + } + if (this.activeTab !== 'loaded') { + this._loadedModelsLoadToken = null; + } this.hideTooltip(); this.animateTabContentTransition(); this.updateTabButtonStates(); @@ -67,10 +73,20 @@ export const tabsLoadedMethods = { async loadLoadedModels(workflow = null, { force = false } = {}) { if (!this.contentElement) return; + const loadToken = `loaded-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`; + this._loadedModelsLoadToken = loadToken; + const shouldRenderLoadedModels = () => ( + this.activeTab === 'loaded' && + this._loadedModelsLoadToken === loadToken && + this.contentElement + ); + try { workflow = workflow || this.getCurrentWorkflow(); if (!workflow) { - this.contentElement.innerHTML = '

No workflow loaded. Please load a workflow first.

'; + if (shouldRenderLoadedModels()) { + this.contentElement.innerHTML = '

No workflow loaded. Please load a workflow first.

'; + } return; } this.syncWorkflowScopedQueue(workflow); @@ -82,11 +98,15 @@ export const tabsLoadedMethods = { this.cachedLoadedModelsSignature === workflowSignature && this.cachedLoadedModelsData ) { - this.displayLoadedModels(this.contentElement, this.cachedLoadedModelsData); + if (shouldRenderLoadedModels()) { + this.displayLoadedModels(this.contentElement, this.cachedLoadedModelsData); + } return; } - this.contentElement.innerHTML = '

Loading loaded models...

'; + if (shouldRenderLoadedModels()) { + this.contentElement.innerHTML = '

Loading loaded models...

'; + } const response = await api.fetchApi('/model_resolver/loaded', { method: 'POST', @@ -99,14 +119,18 @@ export const tabsLoadedMethods = { } const data = await response.json(); - this.cachedLoadedModelsSignature = workflowSignature; - this.cachedLoadedModelsData = data; - this.saveLoadedModelsCacheForActiveWorkflow(); - this.displayLoadedModels(this.contentElement, data); + if (this._loadedModelsLoadToken === loadToken) { + this.cachedLoadedModelsSignature = workflowSignature; + this.cachedLoadedModelsData = data; + this.saveLoadedModelsCacheForActiveWorkflow(); + } + if (shouldRenderLoadedModels()) { + this.displayLoadedModels(this.contentElement, data); + } } catch (error) { console.error('Model Resolver: Error loading loaded models:', error); - if (this.contentElement) { + if (shouldRenderLoadedModels()) { this.contentElement.innerHTML = `

Error: ${error.message}

`; } }