feat: add auto-fill base model toggle to control default search behavior

Adds a new 'Auto-fill model filter' setting under Search Defaults that
controls whether new searches default to 'Auto' (detecting the workflow
base model) or 'none' (Any model). The preference is persisted in
localStorage as ModelResolver.autoFillBaseModel and is respected across
search panel initialization, base model dropdown defaults, workflow state
cloning, and dirty-state checks.
This commit is contained in:
Dariusz L
2026-06-07 18:02:42 +02:00
parent 420b7e50c2
commit b7c4f47e90
4 changed files with 59 additions and 11 deletions
@@ -588,6 +588,7 @@ export const downloadTargetMethods = {
hf_use_api_search: localStorage.getItem('ModelResolver.hfUseApiSearch') !== 'false',
hf_use_comfy_org_fallback: localStorage.getItem('ModelResolver.hfUseComfyOrgFallback') !== 'false',
hf_use_brave_fallback: localStorage.getItem('ModelResolver.hfUseBraveFallback') !== 'false',
auto_fill_base_model: localStorage.getItem('ModelResolver.autoFillBaseModel') !== 'false',
civitai_candidate_limit,
search_source_enabled
};
@@ -628,6 +629,8 @@ export const downloadTargetMethods = {
localStorage.setItem('ModelResolver.hfUseComfyOrgFallback', data.hf_use_comfy_org_fallback ? 'true' : 'false');
if (data.hf_use_brave_fallback !== undefined)
localStorage.setItem('ModelResolver.hfUseBraveFallback', data.hf_use_brave_fallback ? 'true' : 'false');
if (data.auto_fill_base_model !== undefined)
localStorage.setItem('ModelResolver.autoFillBaseModel', data.auto_fill_base_model ? 'true' : 'false');
if (data.civitai_candidate_limit !== undefined)
localStorage.setItem('ModelResolver.civitaiCandidateLimit', `${data.civitai_candidate_limit}`);
+14 -6
View File
@@ -29,7 +29,7 @@ export const searchPanelMethods = {
createEmptySearchState() {
return {
selectedSource: 'all',
selectedBaseModel: 'auto',
selectedBaseModel: this.getDefaultSearchBaseModel(),
results: {
popular: null,
model_list: null,
@@ -47,6 +47,14 @@ export const searchPanelMethods = {
};
},
isAutoFillBaseModelEnabled() {
return localStorage.getItem('ModelResolver.autoFillBaseModel') !== 'false';
},
getDefaultSearchBaseModel() {
return this.isAutoFillBaseModelEnabled() ? 'auto' : 'none';
},
getKnownBaseModelOptions() {
const baseModelsList = this.baseModels?.base_models;
if (Array.isArray(baseModelsList) && baseModelsList.length > 0) {
@@ -250,7 +258,7 @@ export const searchPanelMethods = {
getSearchBaseModelContext(missing = {}) {
const state = this.getSearchState(missing);
const selected = state.selectedBaseModel || 'auto';
const selected = state.selectedBaseModel || this.getDefaultSearchBaseModel();
if (selected === 'none') return '';
if (selected === 'auto') return this.getDominantWorkflowBaseModel();
return selected;
@@ -852,7 +860,7 @@ export const searchPanelMethods = {
if (baseEl) {
const options = this.getKnownBaseModelOptions();
if (!options.some(option => option.value === state.selectedBaseModel)) {
state.selectedBaseModel = 'auto';
state.selectedBaseModel = this.getDefaultSearchBaseModel();
}
this.setDropdownValue(baseEl, state.selectedBaseModel, this.getSearchBaseModelLabel(state.selectedBaseModel));
}
@@ -870,8 +878,8 @@ export const searchPanelMethods = {
setSearchBaseModel(missing, baseModel, container) {
const state = this.getSearchState(missing);
const nextBaseModel = baseModel || 'auto';
const changed = (state.selectedBaseModel || 'auto') !== nextBaseModel;
const nextBaseModel = baseModel || this.getDefaultSearchBaseModel();
const changed = (state.selectedBaseModel || this.getDefaultSearchBaseModel()) !== nextBaseModel;
if (changed) {
this.clearSearchProgressTimers(state.activeSearchRunId);
state.results = this.createEmptySearchState().results;
@@ -1250,7 +1258,7 @@ export const searchPanelMethods = {
const searchBaseListId = `search-base-list-${missing.node_id}-${missing.widget_index}`;
const state = this.getSearchState(missing);
const selectedSource = state.selectedSource || 'all';
const selectedBaseModel = state.selectedBaseModel || 'auto';
const selectedBaseModel = state.selectedBaseModel || this.getDefaultSearchBaseModel();
const buttonText = options.buttonText
|| (this.hasSearchResultsForMissing(missing) ? 'Search Again' : 'Search');
+3 -2
View File
@@ -186,7 +186,7 @@ export const workflowStateMethods = {
}
clone.selectedSource = clone.selectedSource || 'all';
clone.selectedBaseModel = clone.selectedBaseModel || 'auto';
clone.selectedBaseModel = clone.selectedBaseModel || this.getDefaultSearchBaseModel?.() || 'auto';
clone.results = this.mergeSearchResults({}, clone.results || {});
clone.lastAttemptSources = Array.isArray(clone.lastAttemptSources)
? clone.lastAttemptSources
@@ -231,7 +231,8 @@ export const workflowStateMethods = {
isPersistableSearchState(state = {}) {
if ((state?.selectedSource || 'all') !== 'all') return true;
if ((state?.selectedBaseModel || 'auto') !== 'auto') return true;
const defaultBaseModel = this.getDefaultSearchBaseModel?.() || 'auto';
if ((state?.selectedBaseModel || defaultBaseModel) !== defaultBaseModel) return true;
if (state?.lastAttemptError) return true;
if (this.hasSearchResults(state?.results || {})) return true;
+39 -3
View File
@@ -57,6 +57,18 @@ export const optionsMethods = {
</button>
</div>
</div>
<div class="mr-options-sidebar-group">
<div class="mr-options-sidebar-label">Search</div>
<div class="mr-options-nav">
<button type="button" class="mr-options-nav-btn" data-target="mr-options-section-search">
<span class="mr-options-nav-main">
<span class="mr-options-nav-icon" aria-hidden="true">${getSvgIcon('search')}</span>
<span>Defaults</span>
</span>
<span class="mr-options-nav-meta">03</span>
</button>
</div>
</div>
<div class="mr-options-sidebar-group">
<div class="mr-options-sidebar-label">Providers</div>
<div class="mr-options-nav">
@@ -65,14 +77,14 @@ export const optionsMethods = {
<span class="mr-options-nav-icon mr-options-provider-icon mr-options-provider-icon-civitai" aria-hidden="true">${getSvgIcon('civitai')}</span>
<span>CivitAI</span>
</span>
<span class="mr-options-nav-meta">03</span>
<span class="mr-options-nav-meta">04</span>
</button>
<button type="button" class="mr-options-nav-btn" data-target="mr-options-section-hf">
<span class="mr-options-nav-main">
<span class="mr-options-nav-icon mr-options-provider-icon mr-options-provider-icon-huggingface" aria-hidden="true">${getSvgIcon('huggingface')}</span>
<span>HuggingFace</span>
</span>
<span class="mr-options-nav-meta">04</span>
<span class="mr-options-nav-meta">05</span>
</button>
</div>
</div>
@@ -84,7 +96,7 @@ export const optionsMethods = {
<span class="mr-options-nav-icon" aria-hidden="true">${getSvgIcon('database')}</span>
<span>Maintenance</span>
</span>
<span class="mr-options-nav-meta">05</span>
<span class="mr-options-nav-meta">06</span>
</button>
</div>
</div>
@@ -106,6 +118,26 @@ export const optionsMethods = {
</div>
</div>
</section>
<section id="mr-options-section-search" class="mr-options-card mr-options-section is-hidden">
<div class="mr-options-section-head">
<h4 class="mr-options-section-title">Search Defaults</h4>
</div>
<div class="mr-options-grid">
<div class="mr-options-panel">
<div class="mr-options-toggle-list">
<label class="mr-options-toggle-row">
<div class="mr-options-toggle-copy">
<span class="mr-options-toggle-title">Auto-fill model filter <span class="mr-tooltip-badge" data-tooltip="When enabled, new searches use Auto with the detected workflow base model. When disabled, new searches start with Any model, and Auto remains available in the model dropdown.">?</span></span>
</div>
<span class="mr-options-toggle-control">
<input id="mr-options-auto-fill-base-model" class="mr-options-switch-input" type="checkbox" ${tokens.auto_fill_base_model ? 'checked' : ''}>
<span class="mr-options-switch"></span>
</span>
</label>
</div>
</div>
</div>
</section>
<section id="mr-options-section-local-db" class="mr-options-card mr-options-section is-hidden">
<div class="mr-options-section-head">
<h4 class="mr-options-section-title">Local Database</h4>
@@ -369,6 +401,7 @@ export const optionsMethods = {
const hfUseApiSearchInput = this.contentElement.querySelector('#mr-options-hf-use-api-search');
const hfUseComfyOrgFallbackInput = this.contentElement.querySelector('#mr-options-hf-use-comfy-org-fallback');
const hfUseBraveFallbackInput = this.contentElement.querySelector('#mr-options-hf-use-brave-fallback');
const autoFillBaseModelInput = this.contentElement.querySelector('#mr-options-auto-fill-base-model');
const hfTokenCheckBtn = this.contentElement.querySelector('#mr-options-hf-token-check');
const hfTokenCheckStatus = this.contentElement.querySelector('#mr-options-hf-token-check-status');
const braveKeyCheckBtn = this.contentElement.querySelector('#mr-options-brave-key-check');
@@ -408,6 +441,7 @@ export const optionsMethods = {
hfUseApiSearchInput,
hfUseComfyOrgFallbackInput,
hfUseBraveFallbackInput,
autoFillBaseModelInput,
...sourceEnabledInputs,
].filter(Boolean);
@@ -935,6 +969,7 @@ export const optionsMethods = {
hf_use_api_search: Boolean(hfUseApiSearchInput?.checked),
hf_use_comfy_org_fallback: Boolean(hfUseComfyOrgFallbackInput?.checked),
hf_use_brave_fallback: Boolean(hfUseBraveFallbackInput?.checked),
auto_fill_base_model: Boolean(autoFillBaseModelInput?.checked),
brave_search_api_key: braveInput?.value || '',
search_source_enabled: sourceEnabled,
};
@@ -949,6 +984,7 @@ export const optionsMethods = {
localStorage.setItem('ModelResolver.hfUseApiSearch', newSettings.hf_use_api_search ? 'true' : 'false');
localStorage.setItem('ModelResolver.hfUseComfyOrgFallback', newSettings.hf_use_comfy_org_fallback ? 'true' : 'false');
localStorage.setItem('ModelResolver.hfUseBraveFallback', newSettings.hf_use_brave_fallback ? 'true' : 'false');
localStorage.setItem('ModelResolver.autoFillBaseModel', newSettings.auto_fill_base_model ? 'true' : 'false');
localStorage.setItem('ModelResolver.civitaiCandidateLimit', `${civitaiCandidateLimit}`);
Object.entries(sourceEnabled).forEach(([key, val]) => {
localStorage.setItem(key, val ? 'true' : 'false');