refactor(frontend): extract search source policies
This commit is contained in:
@@ -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');
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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();
|
||||
},
|
||||
};
|
||||
Reference in New Issue
Block a user