diff --git a/core/settings.py b/core/settings.py index 3cd17bb..75898f6 100644 --- a/core/settings.py +++ b/core/settings.py @@ -30,6 +30,7 @@ SETTINGS_SCHEMA: List[Dict[str, Any]] = [ { "serverKey": "auto_fill_base_model", "localKey": "ModelResolver.autoFillBaseModel", "type": "boolean", "default": True }, { "serverKey": "auto_fill_subfolder", "localKey": "ModelResolver.autoFillSubfolder", "type": "boolean", "default": True }, { "serverKey": "auto_refresh_comfy_models_after_apply", "localKey": "ModelResolver.autoRefreshComfyModelsAfterApply", "type": "boolean", "default": True }, + { "serverKey": "auto_open_on_missing", "localKey": "ModelResolver.autoOpenOnMissing", "type": "boolean", "default": False }, { "serverKey": "workflow_hash_metadata_enabled", "localKey": "ModelResolver.workflowHashMetadataEnabled", "type": "boolean", "default": True }, { "serverKey": "workflow_dependency_marker_enabled", "localKey": "ModelResolver.workflowDependencyMarkerEnabled", "type": "boolean", "default": False }, { "serverKey": "download_backend", "localKey": "ModelResolver.downloadBackend", "type": "backend", "default": "python" }, @@ -192,6 +193,9 @@ def normalize_settings(settings: Optional[Mapping[str, Any]]) -> Dict[str, Any]: data["auto_refresh_comfy_models_after_apply"] = bool_setting( data.get("auto_refresh_comfy_models_after_apply"), True ) + data["auto_open_on_missing"] = bool_setting( + data.get("auto_open_on_missing"), False + ) data["download_path_mode"] = normalize_download_path_mode( data.get("download_path_mode"), data.get("auto_fill_subfolder") ) diff --git a/tests/test_downloads_tab_workflow_route.mjs b/tests/test_downloads_tab_workflow_route.mjs index 78267e5..26d8eac 100644 --- a/tests/test_downloads_tab_workflow_route.mjs +++ b/tests/test_downloads_tab_workflow_route.mjs @@ -9,6 +9,14 @@ const queueMethodsSource = fs.readFileSync( path.join(projectRoot, 'web/resolver/actions/queue_methods.js'), 'utf8' ); +const modelResolverSource = fs.readFileSync( + path.join(projectRoot, 'web/resolver/model_resolver.js'), + 'utf8' +); +const optionsMethodsSource = fs.readFileSync( + path.join(projectRoot, 'web/resolver/views/options_methods.js'), + 'utf8' +); const resolveDownloadMethodsSource = fs.readFileSync( path.join(projectRoot, 'web/resolver/actions/resolve_download_methods.js'), 'utf8' @@ -662,6 +670,41 @@ test('active download refresh preserves existing cards for hover and tooltip sta assert.match(queueMethodsSource, /patchDownloadsPanelElement\(currentElement, nextElement\)/); }); +test('automatic opening for missing models is disabled by default', () => { + const isAutoOpenEnabled = eval(`(${extractMethod(modelResolverSource, 'isAutoOpenEnabled')})`); + const previousLocalStorageDescriptor = Object.getOwnPropertyDescriptor(globalThis, 'localStorage'); + const setStoredValue = value => { + Object.defineProperty(globalThis, 'localStorage', { + configurable: true, + value: { getItem: () => value }, + }); + }; + + try { + setStoredValue(null); + assert.equal(isAutoOpenEnabled(), false); + + setStoredValue('false'); + assert.equal(isAutoOpenEnabled(), false); + + setStoredValue('true'); + assert.equal(isAutoOpenEnabled(), true); + } finally { + if (previousLocalStorageDescriptor) { + Object.defineProperty(globalThis, 'localStorage', previousLocalStorageDescriptor); + } else delete globalThis.localStorage; + } +}); + +test('automatic opening remains conditional on unresolved models', () => { + assert.match( + modelResolverSource, + /if \(data\.total_missing > 0\) \{[\s\S]*?this\.openResolverManager\(\);/ + ); + assert.match(optionsMethodsSource, /id="mr-options-auto-open-on-missing"/); + assert.match(optionsMethodsSource, /auto_open_on_missing: Boolean\(autoOpenOnMissingInput\?\.checked\)/); +}); + test('active workflow label prefers selected ComfyUI workflow tab name over route hash', () => { const cleanWorkflowLabel = eval(`(${extractMethod(queueMethodsSource, 'cleanWorkflowLabel')})`); const getWorkflowLabelFromActiveTabElement = eval(`(${extractMethod(queueMethodsSource, 'getWorkflowLabelFromActiveTabElement')})`); diff --git a/tests/test_settings.py b/tests/test_settings.py index 6dd323c..8d2434f 100644 --- a/tests/test_settings.py +++ b/tests/test_settings.py @@ -24,6 +24,14 @@ class SettingsTests(unittest.TestCase): ] ) + def test_auto_open_on_missing_defaults_to_disabled(self): + self.assertFalse(normalize_settings({})["auto_open_on_missing"]) + self.assertTrue( + normalize_settings({"auto_open_on_missing": "true"})[ + "auto_open_on_missing" + ] + ) + def test_sanitize_folder_name(self): self.assertEqual(sanitize_folder_name("valid_name"), "valid_name") self.assertEqual(sanitize_folder_name("name/with\\slashes"), "name_with_slashes") diff --git a/web/resolver/model_resolver.js b/web/resolver/model_resolver.js index 2ff2fbb..50f8483 100644 --- a/web/resolver/model_resolver.js +++ b/web/resolver/model_resolver.js @@ -1066,7 +1066,7 @@ export class ModelResolver { * Check if auto-open preference is enabled */ isAutoOpenEnabled() { - return localStorage.getItem('ModelResolver.autoOpenOnMissing') !== 'false'; + return localStorage.getItem('ModelResolver.autoOpenOnMissing') === 'true'; } diff --git a/web/resolver/views/options_methods.js b/web/resolver/views/options_methods.js index d249004..1b0c40f 100644 --- a/web/resolver/views/options_methods.js +++ b/web/resolver/views/options_methods.js @@ -409,6 +409,15 @@ export const optionsMethods = { + + + Open when a workflow has missing models ? + + + + + + Embed workflow hashes ? @@ -900,6 +909,7 @@ export const optionsMethods = { const autoFillBaseModelInput = this.contentElement.querySelector('#mr-options-auto-fill-base-model'); const autoFillSubfolderInput = this.contentElement.querySelector('#mr-options-auto-fill-subfolder'); const autoRefreshComfyModelsInput = this.contentElement.querySelector('#mr-options-auto-refresh-comfy-models'); + const autoOpenOnMissingInput = this.contentElement.querySelector('#mr-options-auto-open-on-missing'); const workflowHashMetadataInput = this.contentElement.querySelector('#mr-options-workflow-hash-metadata'); const workflowDependencyMarkerInput = this.contentElement.querySelector('#mr-options-workflow-dependency-marker'); const addDependencyNodeBtn = this.contentElement.querySelector('#mr-options-add-dependency-node'); @@ -1027,6 +1037,7 @@ export const optionsMethods = { autoFillBaseModelInput, autoFillSubfolderInput, autoRefreshComfyModelsInput, + autoOpenOnMissingInput, workflowHashMetadataInput, workflowDependencyMarkerInput, downloadBackendInput, @@ -3425,6 +3436,7 @@ export const optionsMethods = { auto_fill_base_model: Boolean(autoFillBaseModelInput?.checked), auto_fill_subfolder: Boolean(autoFillSubfolderInput?.checked), auto_refresh_comfy_models_after_apply: Boolean(autoRefreshComfyModelsInput?.checked), + auto_open_on_missing: Boolean(autoOpenOnMissingInput?.checked), workflow_hash_metadata_enabled: Boolean(workflowHashMetadataInput?.checked), workflow_dependency_marker_enabled: Boolean(workflowDependencyMarkerInput?.checked), download_backend: downloadBackend,