refactor(web): share dropdown search matching
This commit is contained in:
@@ -0,0 +1,37 @@
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import test from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import { matchesSearchText, normalizeSearchToken } from '../web/resolver/utils/search_utils.js';
|
||||
import { baseModelAliasMethods } from '../web/resolver/search/base_model_alias_methods.js';
|
||||
|
||||
const projectRoot = path.resolve(import.meta.dirname, '..');
|
||||
const downloadTargetMethodsSource = fs.readFileSync(
|
||||
path.join(projectRoot, 'web/resolver/search/download_target_methods.js'),
|
||||
'utf8'
|
||||
);
|
||||
const resolveDownloadMethodsSource = fs.readFileSync(
|
||||
path.join(projectRoot, 'web/resolver/actions/resolve_download_methods.js'),
|
||||
'utf8'
|
||||
);
|
||||
|
||||
test('search text matching preserves raw, normalized, and empty-query behavior', () => {
|
||||
assert.equal(matchesSearchText('Checkpoints Check Points', ''), true);
|
||||
assert.equal(matchesSearchText('Flux.1 D', 'flux'), true);
|
||||
assert.equal(matchesSearchText('Flux.1 D', 'flux1d'), true);
|
||||
assert.equal(matchesSearchText('Flux.1 D', 'flux-1-d'), true);
|
||||
assert.equal(matchesSearchText('Flux.1 D', 'sdxl'), false);
|
||||
assert.equal(matchesSearchText('Flux.1 D', '---'), false);
|
||||
});
|
||||
|
||||
test('base model token normalization uses the shared search token contract', () => {
|
||||
const dialog = { ...baseModelAliasMethods };
|
||||
for (const value of ['', ' Flux.1 D ', 'SDXL 1.0', 'Pony XL']) {
|
||||
assert.equal(dialog.normalizeBaseModelToken(value), normalizeSearchToken(value));
|
||||
}
|
||||
});
|
||||
|
||||
test('download category and base model dropdowns use the shared matcher', () => {
|
||||
assert.match(downloadTargetMethodsSource, /matchesSearchText\(`\$\{option\.value\} \$\{option\.label\}`, filter\)/);
|
||||
assert.match(resolveDownloadMethodsSource, /matchesSearchText\(`\$\{option\.value\} \$\{option\.label\}`, filter\)/);
|
||||
});
|
||||
@@ -6,6 +6,7 @@ import { normalizeSha256 } from "../utils/hash_utils.js";
|
||||
import { normalizePathIdentity } from "../utils/html_utils.js";
|
||||
import { bindEventOnce, syncElementAttributes } from "../utils/dom_patch_utils.js";
|
||||
import { bindDownloadActionHandlers } from "../utils/download_action_handlers.js";
|
||||
import { matchesSearchText } from "../utils/search_utils.js";
|
||||
import { normalizeSourceKey } from "../utils/source_labels.js";
|
||||
const log = createModuleLogger('resolve_download_methods');
|
||||
|
||||
@@ -3359,7 +3360,6 @@ export const resolveDownloadMethods = {
|
||||
|
||||
const renderBaseOptions = (filterText = '') => {
|
||||
const filter = String(filterText || '').trim().toLowerCase();
|
||||
const normalizedFilter = this.normalizeBaseModelToken?.(filter) || filter.replace(/[^a-z0-9]+/g, '');
|
||||
const options = this.getKnownBaseModelOptions()
|
||||
.map(option => {
|
||||
const label = option.value === 'auto'
|
||||
@@ -3367,14 +3367,7 @@ export const resolveDownloadMethods = {
|
||||
: option.label;
|
||||
return { ...option, label };
|
||||
})
|
||||
.filter(option => {
|
||||
if (!filter) return true;
|
||||
const searchText = `${option.value} ${option.label}`.toLowerCase();
|
||||
const normalizedSearchText = this.normalizeBaseModelToken?.(searchText)
|
||||
|| searchText.replace(/[^a-z0-9]+/g, '');
|
||||
return searchText.includes(filter)
|
||||
|| (normalizedFilter && normalizedSearchText.includes(normalizedFilter));
|
||||
});
|
||||
.filter(option => matchesSearchText(`${option.value} ${option.label}`, filter));
|
||||
baseList.innerHTML = options
|
||||
.map(option => {
|
||||
const label = option.label;
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { normalizeSearchToken } from '../utils/search_utils.js';
|
||||
|
||||
export const baseModelAliasMethods = {
|
||||
getBaseModelAliases() {
|
||||
const baseModelsList = this.baseModels?.base_models;
|
||||
@@ -22,9 +24,7 @@ export const baseModelAliasMethods = {
|
||||
},
|
||||
|
||||
normalizeBaseModelToken(value = '') {
|
||||
return String(value || '')
|
||||
.toLowerCase()
|
||||
.replace(/[^a-z0-9]+/g, '');
|
||||
return normalizeSearchToken(value);
|
||||
},
|
||||
|
||||
getBaseModelTokenVariants(value = '') {
|
||||
|
||||
@@ -4,6 +4,7 @@ import { createFloatingTreePicker } from "../utils/tree_picker.js";
|
||||
import { normalizeDownloadCategoryValue } from "../utils/category_utils.js";
|
||||
import { safeStorage } from "../utils/html_utils.js";
|
||||
import { getSha256Field } from "../utils/hash_utils.js";
|
||||
import { matchesSearchText } from "../utils/search_utils.js";
|
||||
const localStorage = safeStorage;
|
||||
const invalidWindowsPathCharacters = new RegExp(
|
||||
`[<>:"|?*${Array.from({ length: 32 }, (_, code) => String.fromCharCode(code)).join('')}]+`,
|
||||
@@ -2856,19 +2857,12 @@ export const downloadTargetMethods = {
|
||||
const populateCategoryOptions = (filterText = '') => {
|
||||
if (!categoryListEl) return;
|
||||
const filter = String(filterText || '').trim().toLowerCase();
|
||||
const normalizedFilter = filter.replace(/[^a-z0-9]+/g, '');
|
||||
const options = this.getDownloadCategoryOptions(this.getDropdownValue(categoryEl) || 'checkpoints')
|
||||
.map(category => ({
|
||||
value: category,
|
||||
label: this.getCategoryDisplayName(category)
|
||||
}))
|
||||
.filter(option => {
|
||||
if (!filter) return true;
|
||||
const searchText = `${option.value} ${option.label}`.toLowerCase();
|
||||
const normalizedSearchText = searchText.replace(/[^a-z0-9]+/g, '');
|
||||
return searchText.includes(filter)
|
||||
|| (normalizedFilter && normalizedSearchText.includes(normalizedFilter));
|
||||
});
|
||||
.filter(option => matchesSearchText(`${option.value} ${option.label}`, filter));
|
||||
renderOptions(categoryListEl, options, (value, label) => {
|
||||
this.setDropdownValue(categoryEl, value, label);
|
||||
subfolderEl.value = '';
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
export function normalizeSearchToken(value = '') {
|
||||
return String(value || '')
|
||||
.toLowerCase()
|
||||
.replace(/[^a-z0-9]+/g, '');
|
||||
}
|
||||
|
||||
export function matchesSearchText(value = '', query = '') {
|
||||
const filter = String(query || '').trim().toLowerCase();
|
||||
if (!filter) return true;
|
||||
|
||||
const searchText = String(value || '').toLowerCase();
|
||||
const normalizedFilter = normalizeSearchToken(filter);
|
||||
return searchText.includes(filter)
|
||||
|| Boolean(
|
||||
normalizedFilter
|
||||
&& normalizeSearchToken(searchText).includes(normalizedFilter)
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user