diff --git a/web/css/linker-main.css b/web/css/linker-main.css index 08ed1c8..9d9b6f9 100644 --- a/web/css/linker-main.css +++ b/web/css/linker-main.css @@ -630,12 +630,6 @@ top: 18px; z-index: 2; } -.ml-missing-row-sources .ml-missing-source-dot:nth-child(3):nth-last-child(2) { - left: 22px; -} -.ml-missing-row-sources .ml-missing-source-dot:nth-child(4):last-child { - left: 52px; -} .ml-missing-row-sources .ml-missing-source-dot:nth-child(3):last-child { left: 37px; } @@ -3350,6 +3344,20 @@ font-weight: 500; color: #d8d8dd; } +.ml-options-source-icon { + display: inline-flex; + align-items: center; + justify-content: center; + width: 16px; + height: 16px; + flex: 0 0 16px; + color: currentColor; +} +.ml-options-source-icon svg { + display: block; + width: 16px; + height: 16px; +} .ml-options-toggle-control { display: inline-flex; align-items: center; diff --git a/web/linker.js b/web/linker.js index 0bc46ec..b166bd1 100644 --- a/web/linker.js +++ b/web/linker.js @@ -298,19 +298,71 @@ class LinkerManagerDialog extends ComfyDialog { return labels[source] || source; } + getSearchSourceDefinitions() { + return [ + { + source: 'local', + storageKey: 'modelLinker.searchSource.localEnabled', + tooltip: 'Searches bundled known-model data before online providers.' + }, + { + source: 'huggingface', + storageKey: 'modelLinker.searchSource.huggingFaceEnabled', + tooltip: 'Searches Hugging Face when Everything is selected.' + }, + { + source: 'civitai', + storageKey: 'modelLinker.searchSource.civitaiEnabled', + tooltip: 'Searches CivitAI when Everything is selected.' + }, + { + source: 'civarchive', + storageKey: 'modelLinker.searchSource.civArchiveEnabled', + tooltip: 'Searches CivArchive when Everything is selected.' + }, + { + source: 'lora_manager_archive', + storageKey: 'modelLinker.searchSource.loraManagerArchiveEnabled', + tooltip: 'Searches the local LoRA Manager archive when Everything is selected.' + } + ]; + } + + getSearchSourceDefinition(source) { + return this.getSearchSourceDefinitions().find(def => def.source === source) || null; + } + + isSearchSourceEnabled(source) { + if (!source || source === 'all') return true; + const definition = this.getSearchSourceDefinition(source); + if (!definition) return true; + return localStorage.getItem(definition.storageKey) !== 'false'; + } + + isSearchSourceUsable(source) { + return this.isSourceAvailable(source) && this.isSearchSourceEnabled(source); + } + + getEnabledSearchSources() { + const sources = this.getSearchSourceDefinitions() + .filter(def => this.isSearchSourceUsable(def.source)) + .map(def => def.source); + return sources.length ? sources : ['local']; + } + + getSearchSourceEnabledMap() { + return this.getSearchSourceDefinitions().reduce((enabled, def) => { + enabled[def.source] = this.isSearchSourceEnabled(def.source); + return enabled; + }, {}); + } + getSearchSourcesForSelection(selectedSource, missing = {}) { if (selectedSource !== 'all') { - return this.isSourceAvailable(selectedSource) ? [selectedSource] : []; + return this.isSearchSourceUsable(selectedSource) ? [selectedSource] : []; } - const sources = ['local', 'huggingface', 'civitai']; - if (this.isSourceAvailable('civarchive')) { - sources.push('civarchive'); - } - if (this.isSourceAvailable('lora_manager_archive')) { - sources.push('lora_manager_archive'); - } - return sources; + return this.getEnabledSearchSources(); } setSourceProgress(state, source, patch = {}, missing = null) { @@ -504,6 +556,10 @@ class LinkerManagerDialog extends ComfyDialog { const state = this.getSearchState(missing); const selectEl = container.querySelector(`#search-source-select-${missing.node_id}-${missing.widget_index}`); if (selectEl) { + const options = this.getSearchSourceOptions(); + if (!options.some(option => option.value === state.selectedSource)) { + state.selectedSource = 'all'; + } this.setDropdownValue(selectEl, state.selectedSource, this.getSearchSourceLabel(state.selectedSource)); } } @@ -2824,13 +2880,7 @@ class LinkerManagerDialog extends ComfyDialog { } getSearchSourceOptions() { - const sources = ['all', 'local', 'huggingface', 'civitai']; - if (this.isSourceAvailable('civarchive')) { - sources.push('civarchive'); - } - if (this.isSourceAvailable('lora_manager_archive')) { - sources.push('lora_manager_archive'); - } + const sources = ['all', ...this.getEnabledSearchSources()]; return sources.map(source => ({ value: source, label: this.getSearchSourceLabel(source) @@ -3122,6 +3172,7 @@ class LinkerManagerDialog extends ComfyDialog { const civitai_candidate_limit = Number.isFinite(civitaiCandidateLimitRaw) ? Math.min(20, Math.max(1, civitaiCandidateLimitRaw)) : 5; + const search_source_enabled = this.getSearchSourceEnabledMap(); return { civitai_key: localStorage.getItem('modelLinker.civitaiApiKey') || '', @@ -3133,7 +3184,8 @@ class LinkerManagerDialog extends ComfyDialog { hf_use_api_search: localStorage.getItem('modelLinker.hfUseApiSearch') !== 'false', hf_use_comfy_org_fallback: localStorage.getItem('modelLinker.hfUseComfyOrgFallback') !== 'false', hf_use_brave_fallback: localStorage.getItem('modelLinker.hfUseBraveFallback') !== 'false', - civitai_candidate_limit + civitai_candidate_limit, + search_source_enabled }; } @@ -3156,6 +3208,29 @@ class LinkerManagerDialog extends ComfyDialog { this.contentElement.style.overflowY = 'hidden'; const tokens = this.getStoredTokens(); + const sourceDefaultsRows = this.getSearchSourceDefinitions() + .filter(def => this.isSourceAvailable(def.source)) + .map(def => { + const iconName = this.getSearchSourceIconName(def.source); + const checked = tokens.search_source_enabled?.[def.source] !== false; + const inputId = `ml-options-source-${def.source.replace(/_/g, '-')}`; + return ` + + `; + }) + .join(''); this.contentElement.innerHTML = `