OpenRouter: match both id/text in select2 (#4237) * OpenRouter: match both id/text in select2 * Update public/scripts/utils.js Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

e1a55408b60e068b648f2d0c53992aa26e357a75

Cohee <18619528+Cohee1207@users.noreply.github.com>

Signed
3 files changed, +54 -1Ignore whitespace
public/scripts/openai.js+2 -0
@@ -61,6 +61,7 @@ import {
61 parseJsonFile,61 parseJsonFile,
62 resetScrollHeight,62 resetScrollHeight,
63 stringFormat,63 stringFormat,
64 textValueMatcher,
64 uuidv4,65 uuidv4,
65} from './utils.js';66} from './utils.js';
66import { countTokensOpenAIAsync, getTokenizerModel } from './tokenizers.js';67import { countTokensOpenAIAsync, getTokenizerModel } from './tokenizers.js';
@@ -6348,6 +6349,7 @@ export function initOpenAI() {
6348 searchInputCssClass: 'text_pole',6349 searchInputCssClass: 'text_pole',
6349 width: '100%',6350 width: '100%',
6350 templateResult: getOpenRouterModelTemplate,6351 templateResult: getOpenRouterModelTemplate,
6352 matcher: textValueMatcher,
6351 });6353 });
6352 $('#model_aimlapi_select').select2({6354 $('#model_aimlapi_select').select2({
6353 placeholder: t`Select a model`,6355 placeholder: t`Select a model`,
public/scripts/textgen-models.js+2 -1
@@ -7,7 +7,7 @@ import { renderTemplateAsync } from './templates.js';
7import { POPUP_TYPE, callGenericPopup } from './popup.js';7import { POPUP_TYPE, callGenericPopup } from './popup.js';
8import { t } from './i18n.js';8import { t } from './i18n.js';
9import { accountStorage } from './util/AccountStorage.js';9import { accountStorage } from './util/AccountStorage.js';
10import { localizePagination, PAGINATION_TEMPLATE } from './utils.js';10import { localizePagination, PAGINATION_TEMPLATE, textValueMatcher } from './utils.js';
1111
12let mancerModels = [];12let mancerModels = [];
13let togetherModels = [];13let togetherModels = [];
@@ -1005,6 +1005,7 @@ export function initTextGenModels() {
1005 searchInputCssClass: 'text_pole',1005 searchInputCssClass: 'text_pole',
1006 width: '100%',1006 width: '100%',
1007 templateResult: getOpenRouterModelTemplate,1007 templateResult: getOpenRouterModelTemplate,
1008 matcher: textValueMatcher,
1008 });1009 });
1009 $('#vllm_model').select2({1010 $('#vllm_model').select2({
1010 placeholder: t`Select a model`,1011 placeholder: t`Select a model`,
public/scripts/utils.js+50 -0
@@ -2483,3 +2483,53 @@ export function clearInfoBlock(target) {
2483 infoBlock.innerHTML = '';2483 infoBlock.innerHTML = '';
2484 }2484 }
2485}2485}
2486
2487/**
2488 * Provides a matcher function for select2 that matches both the text and value of options.
2489 * @param {import('select2').SearchOptions} params
2490 * @param {import('select2').OptGroupData|import('select2').OptionData} data
2491 * @return {import('select2').OptGroupData|import('select2').OptionData|null}
2492 */
2493export function textValueMatcher(params, data) {
2494 // Always return the object if there is nothing to compare
2495 if (params.term == null || params.term.trim() === '') {
2496 return data;
2497 }
2498
2499 // Do a recursive check for options with children
2500 if (data.children && data.children.length > 0) {
2501 // Clone the data object if there are children
2502 // This is required as we modify the object to remove any non-matches
2503 const match = $.extend(true, {}, data);
2504
2505 // Check each child of the option
2506 for (let c = data.children.length - 1; c >= 0; c--) {
2507 const child = data.children[c];
2508
2509 const matches = textValueMatcher(params, child);
2510
2511 // If there wasn't a match, remove the object in the array
2512 if (matches == null) {
2513 match.children.splice(c, 1);
2514 }
2515 }
2516
2517 // If any children matched, return the new object
2518 if (match.children.length > 0) {
2519 return match;
2520 }
2521
2522 // If there were no matching children, check just the plain object
2523 return textValueMatcher(params, match);
2524 }
2525
2526 const textMatch = compareIgnoreCaseAndAccents(data.text, params.term, (a, b) => a.indexOf(b) > -1);
2527 const valueMatch = data.element instanceof HTMLOptionElement && compareIgnoreCaseAndAccents(data.element.value, params.term, (a, b) => a.indexOf(b) > -1);
2528
2529 if (textMatch || valueMatch) {
2530 return data;
2531 }
2532
2533 // If it doesn't contain the term, don't return anything
2534 return null;
2535}