refactor(frontend): extract search source policies

This commit is contained in:
Dariusz L
2026-08-02 13:34:02 +02:00
parent c2aaf3b7e6
commit 57e6f8c61d
4 changed files with 157 additions and 177 deletions
+13 -40
View File
@@ -1,46 +1,19 @@
import fs from 'node:fs';
import path from 'node:path';
import test from 'node:test';
import assert from 'node:assert/strict';
import { searchSourceMethods } from '../web/resolver/search/search_source_methods.js';
const projectRoot = path.resolve(import.meta.dirname, '..');
const searchPanelMethodsSource = fs.readFileSync(
path.join(projectRoot, 'web/resolver/search/search_panel.js'),
'utf8'
);
function extractMethod(source, methodName, paramsPattern = '[^)]*') {
const signatureRegex = new RegExp(`\\n\\s+(async\\s+)?${methodName}\\s*\\(${paramsPattern}\\)\\s*\\{`);
const match = signatureRegex.exec(source);
assert.ok(match, `Could not find ${methodName}`);
const isAsync = Boolean(match[1]);
const parenStart = source.indexOf('(', match.index);
const parenEnd = source.indexOf(')', parenStart);
const params = source.slice(parenStart + 1, parenEnd);
const braceStart = source.indexOf('{', parenEnd);
let depth = 0;
for (let i = braceStart; i < source.length; i += 1) {
const char = source[i];
if (char === '{') depth += 1;
if (char === '}') depth -= 1;
if (depth === 0) {
return `${isAsync ? 'async ' : ''}function ${methodName}(${params}) ${source.slice(braceStart, i + 1)}`;
}
}
throw new Error(`Could not parse ${methodName}`);
}
const getSearchSourceLabel = eval(`(${extractMethod(searchPanelMethodsSource, 'getSearchSourceLabel')})`);
const getSearchSourceDefinitions = eval(`(${extractMethod(searchPanelMethodsSource, 'getSearchSourceDefinitions')})`);
const getSearchResultKeysForSources = eval(`(${extractMethod(searchPanelMethodsSource, 'getSearchResultKeysForSources')})`);
const getHashLookupSourcesForSearchSources = eval(`(${extractMethod(searchPanelMethodsSource, 'getHashLookupSourcesForSearchSources')})`);
const clearSearchResultsForSources = eval(`(${extractMethod(searchPanelMethodsSource, 'clearSearchResultsForSources')})`);
const isSearchSourceEnabled = eval(`(${extractMethod(searchPanelMethodsSource, 'isSearchSourceEnabled')})`);
const getSearchSourceDefinition = eval(`(${extractMethod(searchPanelMethodsSource, 'getSearchSourceDefinition')})`);
const getSearchSourceEnabledMap = eval(`(${extractMethod(searchPanelMethodsSource, 'getSearchSourceEnabledMap')})`);
const getEnabledSearchSources = eval(`(${extractMethod(searchPanelMethodsSource, 'getEnabledSearchSources')})`);
const getSearchSourcesForSelection = eval(`(${extractMethod(searchPanelMethodsSource, 'getSearchSourcesForSelection')})`);
const {
getSearchSourceLabel,
getSearchSourceDefinitions,
getSearchResultKeysForSources,
getHashLookupSourcesForSearchSources,
clearSearchResultsForSources,
isSearchSourceEnabled,
getSearchSourceDefinition,
getSearchSourceEnabledMap,
getEnabledSearchSources,
getSearchSourcesForSelection,
} = searchSourceMethods;
function installStorage(values = {}) {
const previous = Object.getOwnPropertyDescriptor(globalThis, 'localStorage');
+2
View File
@@ -12,6 +12,7 @@ import { optionsMethods } from "./views/options_methods.js";
import { tabsLoadedMethods } from "./views/tabs_loaded_methods.js";
import { downloadTargetMethods } from "./search/download_target_methods.js";
import { searchPanelMethods } from "./search/search_panel.js";
import { searchSourceMethods } from "./search/search_source_methods.js";
import { queueMethods } from "./actions/queue_methods.js";
import { queueStorageMethods } from "./actions/queue_storage_methods.js";
import { downloadProgressMethods } from "./actions/download_progress_methods.js";
@@ -333,6 +334,7 @@ function applyDialogMethods(...sources) {
}
applyDialogMethods(
searchSourceMethods,
searchPanelMethods,
modelInfoMethods,
renderFormatMethods,
-137
View File
@@ -1005,143 +1005,6 @@ export const searchPanelMethods = {
return this.hasRenderableSearchState?.(state || {}) || this.hasSearchResults(state?.results || {});
},
getSearchResultKeysForSources(sources = []) {
const normalized = new Set((Array.isArray(sources) ? sources : [sources])
.map(source => String(source || '').trim())
.filter(Boolean));
if (normalized.has('all')) {
return ['popular', 'model_list', 'huggingface', 'civitai', 'civarchive', 'lora_manager_archive'];
}
const keys = new Set();
for (const source of normalized) {
if (source === 'local') {
keys.add('popular');
keys.add('model_list');
} else if (source) {
keys.add(source);
}
}
return Array.from(keys);
},
getHashLookupSourcesForSearchSources(sources = []) {
const normalized = new Set((Array.isArray(sources) ? sources : [sources])
.map(source => String(source || '').trim())
.filter(Boolean));
const hashSources = ['huggingface', 'civitai', 'civarchive'];
if (normalized.has('all')) return new Set(hashSources);
return new Set(hashSources.filter(source => normalized.has(source)));
},
clearSearchResultsForSources(results = {}, sources = []) {
const nextResults = {
popular: results.popular || null,
model_list: results.model_list || null,
huggingface: results.huggingface || null,
civitai: results.civitai || null,
civarchive: results.civarchive || null,
lora_manager_archive: results.lora_manager_archive || null,
custom: Array.isArray(results.custom) ? results.custom : [],
local_hash_matches: Array.isArray(results.local_hash_matches) ? results.local_hash_matches : []
};
for (const key of this.getSearchResultKeysForSources(sources)) {
if (key in nextResults) nextResults[key] = null;
}
const hashSourcesToClear = this.getHashLookupSourcesForSearchSources(sources);
if (hashSourcesToClear.size) {
nextResults.local_hash_matches = nextResults.local_hash_matches.filter(match => {
const source = String(match?.hash_lookup_source || '').trim();
return source && !hashSourcesToClear.has(source);
});
}
return nextResults;
},
/**
* Convert source ids to readable labels
*/
getSearchSourceLabel(source) {
const labels = {
all: 'Everything',
local: 'Local Database',
huggingface: 'HuggingFace',
civitai: 'CivitAI',
civarchive: 'CivArchive',
lora_manager_archive: 'LoRA Manager Archive',
custom: 'Custom URL'
};
return labels[source] || source;
},
getSearchSourceDefinitions() {
return [
{
source: 'local',
storageKey: 'ModelResolver.searchSource.localEnabled',
tooltip: 'Searches bundled known-model data before online providers.'
},
{
source: 'huggingface',
storageKey: 'ModelResolver.searchSource.huggingFaceEnabled',
tooltip: 'Searches Hugging Face when Everything is selected.'
},
{
source: 'civitai',
storageKey: 'ModelResolver.searchSource.civitaiEnabled',
tooltip: 'Searches CivitAI when Everything is selected.'
},
{
source: 'civarchive',
storageKey: 'ModelResolver.searchSource.civArchiveEnabled',
tooltip: 'Searches CivArchive when Everything is selected.'
},
{
source: 'lora_manager_archive',
storageKey: 'ModelResolver.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.isSearchSourceUsable(selectedSource) ? [selectedSource] : [];
}
return this.getEnabledSearchSources();
},
setSourceProgress(state, source, patch = {}, missing = null, { workflowKey = this.getWorkflowScopedQueueKey() } = {}) {
state.sourceProgress = {
...(state.sourceProgress || {}),
@@ -0,0 +1,142 @@
import { safeStorage } from "../utils/html_utils.js";
const localStorage = safeStorage;
export const searchSourceMethods = {
getSearchResultKeysForSources(sources = []) {
const normalized = new Set((Array.isArray(sources) ? sources : [sources])
.map(source => String(source || '').trim())
.filter(Boolean));
if (normalized.has('all')) {
return ['popular', 'model_list', 'huggingface', 'civitai', 'civarchive', 'lora_manager_archive'];
}
const keys = new Set();
for (const source of normalized) {
if (source === 'local') {
keys.add('popular');
keys.add('model_list');
} else if (source) {
keys.add(source);
}
}
return Array.from(keys);
},
getHashLookupSourcesForSearchSources(sources = []) {
const normalized = new Set((Array.isArray(sources) ? sources : [sources])
.map(source => String(source || '').trim())
.filter(Boolean));
const hashSources = ['huggingface', 'civitai', 'civarchive'];
if (normalized.has('all')) return new Set(hashSources);
return new Set(hashSources.filter(source => normalized.has(source)));
},
clearSearchResultsForSources(results = {}, sources = []) {
const nextResults = {
popular: results.popular || null,
model_list: results.model_list || null,
huggingface: results.huggingface || null,
civitai: results.civitai || null,
civarchive: results.civarchive || null,
lora_manager_archive: results.lora_manager_archive || null,
custom: Array.isArray(results.custom) ? results.custom : [],
local_hash_matches: Array.isArray(results.local_hash_matches) ? results.local_hash_matches : []
};
for (const key of this.getSearchResultKeysForSources(sources)) {
if (key in nextResults) nextResults[key] = null;
}
const hashSourcesToClear = this.getHashLookupSourcesForSearchSources(sources);
if (hashSourcesToClear.size) {
nextResults.local_hash_matches = nextResults.local_hash_matches.filter(match => {
const source = String(match?.hash_lookup_source || '').trim();
return source && !hashSourcesToClear.has(source);
});
}
return nextResults;
},
/**
* Convert source ids to readable labels
*/
getSearchSourceLabel(source) {
const labels = {
all: 'Everything',
local: 'Local Database',
huggingface: 'HuggingFace',
civitai: 'CivitAI',
civarchive: 'CivArchive',
lora_manager_archive: 'LoRA Manager Archive',
custom: 'Custom URL'
};
return labels[source] || source;
},
getSearchSourceDefinitions() {
return [
{
source: 'local',
storageKey: 'ModelResolver.searchSource.localEnabled',
tooltip: 'Searches bundled known-model data before online providers.'
},
{
source: 'huggingface',
storageKey: 'ModelResolver.searchSource.huggingFaceEnabled',
tooltip: 'Searches Hugging Face when Everything is selected.'
},
{
source: 'civitai',
storageKey: 'ModelResolver.searchSource.civitaiEnabled',
tooltip: 'Searches CivitAI when Everything is selected.'
},
{
source: 'civarchive',
storageKey: 'ModelResolver.searchSource.civArchiveEnabled',
tooltip: 'Searches CivArchive when Everything is selected.'
},
{
source: 'lora_manager_archive',
storageKey: 'ModelResolver.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.isSearchSourceUsable(selectedSource) ? [selectedSource] : [];
}
return this.getEnabledSearchSources();
},
};