diff --git a/src/app/(dashboard)/dashboard/providers/page.tsx b/src/app/(dashboard)/dashboard/providers/page.tsx
index e509322fa6..0921de4317 100644
--- a/src/app/(dashboard)/dashboard/providers/page.tsx
+++ b/src/app/(dashboard)/dashboard/providers/page.tsx
@@ -123,6 +123,7 @@ export default function ProvidersPage() {
missingCount: number;
} | null>(null);
const [repairingEnv, setRepairingEnv] = useState(false);
+ const [searchQuery, setSearchQuery] = useState("");
const notify = useNotificationStore();
const t = useTranslations("providers");
const tc = useTranslations("common");
@@ -392,27 +393,32 @@ export default function ProvidersPage() {
const oauthProviderEntries = filterConfiguredProviderEntries(
buildMergedOAuthProviderEntries(OAUTH_PROVIDERS, FREE_PROVIDERS, getProviderStats),
- showConfiguredOnly
+ showConfiguredOnly,
+ searchQuery
);
const apiKeyProviderEntries = filterConfiguredProviderEntries(
buildStaticProviderEntries("apikey", getProviderStats),
- showConfiguredOnly
+ showConfiguredOnly,
+ searchQuery
);
const webCookieProviderEntries = filterConfiguredProviderEntries(
buildStaticProviderEntries("web-cookie", getProviderStats),
- showConfiguredOnly
+ showConfiguredOnly,
+ searchQuery
);
const searchProviderEntries = filterConfiguredProviderEntries(
buildStaticProviderEntries("search", getProviderStats),
- showConfiguredOnly
+ showConfiguredOnly,
+ searchQuery
);
const audioProviderEntries = filterConfiguredProviderEntries(
buildStaticProviderEntries("audio", getProviderStats),
- showConfiguredOnly
+ showConfiguredOnly,
+ searchQuery
);
const compatibleProviderEntries = filterConfiguredProviderEntries(
@@ -439,7 +445,8 @@ export default function ProvidersPage() {
toggleAuthType: "apikey" as const,
})),
],
- showConfiguredOnly
+ showConfiguredOnly,
+ searchQuery
);
if (loading) {
@@ -453,6 +460,31 @@ export default function ProvidersPage() {
return (
+ {/* Search Bar */}
+
+
+
+ search
+
+ setSearchQuery(e.target.value)}
+ placeholder={t("searchProviders")}
+ aria-label={t("searchProviders")}
+ className="pl-10 pr-10"
+ />
+ {searchQuery && (
+
+ )}
+
+
+
{/* Expiration Banner */}
{expirations?.summary &&
(expirations.summary.expired > 0 || expirations.summary.expiringSoon > 0) && (
diff --git a/src/app/(dashboard)/dashboard/providers/providerPageUtils.ts b/src/app/(dashboard)/dashboard/providers/providerPageUtils.ts
index d2f75617db..9b09433757 100644
--- a/src/app/(dashboard)/dashboard/providers/providerPageUtils.ts
+++ b/src/app/(dashboard)/dashboard/providers/providerPageUtils.ts
@@ -69,11 +69,26 @@ export function buildStaticProviderEntries(
export function filterConfiguredProviderEntries
(
entries: ProviderEntry[],
- showConfiguredOnly: boolean
+ showConfiguredOnly: boolean,
+ searchQuery?: string
): ProviderEntry[] {
- if (!showConfiguredOnly) return entries;
+ let filtered = entries;
- return entries.filter((entry) => Number(entry.stats?.total || 0) > 0);
+ if (showConfiguredOnly) {
+ filtered = filtered.filter((entry) => Number(entry.stats?.total || 0) > 0);
+ }
+
+ if (searchQuery && searchQuery.trim()) {
+ const query = searchQuery.trim().toLowerCase();
+ filtered = filtered.filter((entry) => {
+ const provider = entry.provider as Record;
+ const name = String(provider.name || "").toLowerCase();
+ const id = entry.providerId.toLowerCase();
+ return name.includes(query) || id.includes(query);
+ });
+ }
+
+ return filtered;
}
export function resolveDashboardProviderInfo(
diff --git a/src/i18n/messages/ar.json b/src/i18n/messages/ar.json
index 9d00bc6591..ec00176783 100644
--- a/src/i18n/messages/ar.json
+++ b/src/i18n/messages/ar.json
@@ -1635,6 +1635,7 @@
"errorOccurred": "حدث خطأ. يرجى المحاولة مرة أخرى.",
"modelStatus": "حالة النموذج",
"showConfiguredOnly": "Configured only",
+ "searchProviders": "البحث عن موفري الخدمة...",
"allModelsOperational": "جميع الموديلات شغالة",
"modelsWithIssues": "{count} الطراز (النماذج) الذي به مشكلات",
"allModelsNormal": "جميع النماذج تستجيب بشكل طبيعي.",
diff --git a/src/i18n/messages/bg.json b/src/i18n/messages/bg.json
index de2071474a..2c1c0d0bd3 100644
--- a/src/i18n/messages/bg.json
+++ b/src/i18n/messages/bg.json
@@ -1635,6 +1635,7 @@
"errorOccurred": "Възникна грешка. Моля, опитайте отново.",
"modelStatus": "Статус на модела",
"showConfiguredOnly": "Configured only",
+ "searchProviders": "Търсете доставчици...",
"allModelsOperational": "Всички модели работещи",
"modelsWithIssues": "{count} модел(а) с проблеми",
"allModelsNormal": "Всички модели реагират нормално.",
diff --git a/src/i18n/messages/cs.json b/src/i18n/messages/cs.json
index 1b8b4c6384..1a81088677 100644
--- a/src/i18n/messages/cs.json
+++ b/src/i18n/messages/cs.json
@@ -1635,6 +1635,7 @@
"errorOccurred": "Došlo k chybě. Zkuste to prosím znovu.",
"modelStatus": "Status modelu",
"showConfiguredOnly": "Configured only",
+ "searchProviders": "Hledat poskytovatele...",
"allModelsOperational": "Všechny modely funkční",
"modelsWithIssues": "{count} model(y) s problémy",
"allModelsNormal": "Všechny modely reagují normálně.",
diff --git a/src/i18n/messages/da.json b/src/i18n/messages/da.json
index fcf0034fd3..ae2cdaa225 100644
--- a/src/i18n/messages/da.json
+++ b/src/i18n/messages/da.json
@@ -1635,6 +1635,7 @@
"errorOccurred": "Der opstod en fejl. Prøv venligst igen.",
"modelStatus": "Modelstatus",
"showConfiguredOnly": "Configured only",
+ "searchProviders": "Søg efter udbydere...",
"allModelsOperational": "Alle modeller i drift",
"modelsWithIssues": "{count} model(er) med problemer",
"allModelsNormal": "Alle modeller reagerer normalt.",
diff --git a/src/i18n/messages/de.json b/src/i18n/messages/de.json
index d60916651f..df2d6f7970 100644
--- a/src/i18n/messages/de.json
+++ b/src/i18n/messages/de.json
@@ -1635,6 +1635,7 @@
"errorOccurred": "Es ist ein Fehler aufgetreten. Bitte versuchen Sie es erneut.",
"modelStatus": "Modellstatus",
"showConfiguredOnly": "Configured only",
+ "searchProviders": "Anbieter suchen...",
"allModelsOperational": "Alle Modelle betriebsbereit",
"modelsWithIssues": "{count} Modell(e) mit Problemen",
"allModelsNormal": "Alle Modelle reagieren normal.",
diff --git a/src/i18n/messages/en.json b/src/i18n/messages/en.json
index 4ed9b281af..e68d5d0e37 100644
--- a/src/i18n/messages/en.json
+++ b/src/i18n/messages/en.json
@@ -1682,6 +1682,7 @@
"errorOccurred": "An error occurred. Please try again.",
"modelStatus": "Model Status",
"showConfiguredOnly": "Configured only",
+ "searchProviders": "Search providers...",
"allModelsOperational": "All models operational",
"modelsWithIssues": "{count} model(s) with issues",
"allModelsNormal": "All models are responding normally.",
diff --git a/src/i18n/messages/es.json b/src/i18n/messages/es.json
index 73b4ec07a7..c6416c544a 100644
--- a/src/i18n/messages/es.json
+++ b/src/i18n/messages/es.json
@@ -1635,6 +1635,7 @@
"errorOccurred": "Se produjo un error. Por favor inténtalo de nuevo.",
"modelStatus": "Estado del modelo",
"showConfiguredOnly": "Configured only",
+ "searchProviders": "Buscar proveedores...",
"allModelsOperational": "Todos los modelos operativos.",
"modelsWithIssues": "{count} modelo(s) con problemas",
"allModelsNormal": "Todos los modelos están respondiendo normalmente.",
diff --git a/src/i18n/messages/fi.json b/src/i18n/messages/fi.json
index 08be63cae5..13db6ac4c9 100644
--- a/src/i18n/messages/fi.json
+++ b/src/i18n/messages/fi.json
@@ -1635,6 +1635,7 @@
"errorOccurred": "Tapahtui virhe. Yritä uudelleen.",
"modelStatus": "Mallin tila",
"showConfiguredOnly": "Configured only",
+ "searchProviders": "Hae palveluntarjoajia...",
"allModelsOperational": "Kaikki mallit toimivat",
"modelsWithIssues": "{count} malli(t), joissa on ongelmia",
"allModelsNormal": "Kaikki mallit reagoivat normaalisti.",
diff --git a/src/i18n/messages/fr.json b/src/i18n/messages/fr.json
index 1c2a0f7542..9d4ca65198 100644
--- a/src/i18n/messages/fr.json
+++ b/src/i18n/messages/fr.json
@@ -1635,6 +1635,7 @@
"errorOccurred": "Une erreur s'est produite. Veuillez réessayer.",
"modelStatus": "Statut du modèle",
"showConfiguredOnly": "Configured only",
+ "searchProviders": "Rechercher des fournisseurs...",
"allModelsOperational": "Tous les modèles opérationnels",
"modelsWithIssues": "{count} modèle(s) présentant des problèmes",
"allModelsNormal": "Tous les modèles répondent normalement.",
diff --git a/src/i18n/messages/he.json b/src/i18n/messages/he.json
index d1b90146f3..a1cb3b2089 100644
--- a/src/i18n/messages/he.json
+++ b/src/i18n/messages/he.json
@@ -1635,6 +1635,7 @@
"errorOccurred": "אירעה שגיאה. אנא נסה שוב.",
"modelStatus": "סטטוס דגם",
"showConfiguredOnly": "Configured only",
+ "searchProviders": "חיפוש ספקים...",
"allModelsOperational": "כל הדגמים מבצעיים",
"modelsWithIssues": "{count} דגמים עם בעיות",
"allModelsNormal": "כל הדגמים מגיבים כרגיל.",
diff --git a/src/i18n/messages/hi.json b/src/i18n/messages/hi.json
index 64ce703ef5..641b19cfbc 100644
--- a/src/i18n/messages/hi.json
+++ b/src/i18n/messages/hi.json
@@ -1635,6 +1635,7 @@
"errorOccurred": "एक त्रुटि हुई. कृपया पुन: प्रयास करें।",
"modelStatus": "मॉडल स्थिति",
"showConfiguredOnly": "Configured only",
+ "searchProviders": "प्रदाता खोजें...",
"allModelsOperational": "सभी मॉडल क्रियाशील",
"modelsWithIssues": "मुद्दों के साथ {count} मॉडल",
"allModelsNormal": "सभी मॉडल सामान्य रूप से प्रतिक्रिया दे रहे हैं।",
diff --git a/src/i18n/messages/hu.json b/src/i18n/messages/hu.json
index 151d8f8832..452ea02973 100644
--- a/src/i18n/messages/hu.json
+++ b/src/i18n/messages/hu.json
@@ -1635,6 +1635,7 @@
"errorOccurred": "Hiba történt. Kérjük, próbálja újra.",
"modelStatus": "Modell állapota",
"showConfiguredOnly": "Configured only",
+ "searchProviders": "Keressen szolgáltatók között...",
"allModelsOperational": "Minden modell működőképes",
"modelsWithIssues": "{count} problémákkal küzdő modell(ek).",
"allModelsNormal": "Minden modell normálisan reagál.",
diff --git a/src/i18n/messages/id.json b/src/i18n/messages/id.json
index f0d07de0f1..261bb5c121 100644
--- a/src/i18n/messages/id.json
+++ b/src/i18n/messages/id.json
@@ -1635,6 +1635,7 @@
"errorOccurred": "Terjadi kesalahan. Silakan coba lagi.",
"modelStatus": "Status Model",
"showConfiguredOnly": "Configured only",
+ "searchProviders": "Penyedia pencarian...",
"allModelsOperational": "Semua model beroperasi",
"modelsWithIssues": "{count} model yang bermasalah",
"allModelsNormal": "Semua model merespons secara normal.",
diff --git a/src/i18n/messages/it.json b/src/i18n/messages/it.json
index 3d73d0e614..21882de75f 100644
--- a/src/i18n/messages/it.json
+++ b/src/i18n/messages/it.json
@@ -1635,6 +1635,7 @@
"errorOccurred": "Si è verificato un errore. Per favore riprova.",
"modelStatus": "Stato del modello",
"showConfiguredOnly": "Configured only",
+ "searchProviders": "Cerca fornitori...",
"allModelsOperational": "Tutti i modelli operativi",
"modelsWithIssues": "{count} modelli con problemi",
"allModelsNormal": "Tutti i modelli rispondono normalmente.",
diff --git a/src/i18n/messages/ja.json b/src/i18n/messages/ja.json
index e0c278c5f4..8e379b5a9b 100644
--- a/src/i18n/messages/ja.json
+++ b/src/i18n/messages/ja.json
@@ -1635,6 +1635,7 @@
"errorOccurred": "エラーが発生しました。もう一度試してください。",
"modelStatus": "モデルステータス",
"showConfiguredOnly": "Configured only",
+ "searchProviders": "プロバイダーを検索...",
"allModelsOperational": "全モデル稼働中",
"modelsWithIssues": "{count} モデルに問題があります",
"allModelsNormal": "全機種正常に反応しております。",
diff --git a/src/i18n/messages/ko.json b/src/i18n/messages/ko.json
index b32c5bcbee..331431b22a 100644
--- a/src/i18n/messages/ko.json
+++ b/src/i18n/messages/ko.json
@@ -1635,6 +1635,7 @@
"errorOccurred": "오류가 발생했습니다. 다시 시도해 주세요.",
"modelStatus": "모델현황",
"showConfiguredOnly": "Configured only",
+ "searchProviders": "공급자 검색...",
"allModelsOperational": "모든 모델 작동 가능",
"modelsWithIssues": "문제가 있는 {count} 모델",
"allModelsNormal": "모든 모델이 정상적으로 반응하고 있습니다.",
diff --git a/src/i18n/messages/ms.json b/src/i18n/messages/ms.json
index 9638bf1b21..da47bc129f 100644
--- a/src/i18n/messages/ms.json
+++ b/src/i18n/messages/ms.json
@@ -1635,6 +1635,7 @@
"errorOccurred": "Ralat berlaku. Sila cuba lagi.",
"modelStatus": "Status Model",
"showConfiguredOnly": "Configured only",
+ "searchProviders": "Cari pembekal...",
"allModelsOperational": "Semua model beroperasi",
"modelsWithIssues": "{count} model dengan isu",
"allModelsNormal": "Semua model bertindak balas secara normal.",
diff --git a/src/i18n/messages/nl.json b/src/i18n/messages/nl.json
index 3f74d1db21..a167ecc822 100644
--- a/src/i18n/messages/nl.json
+++ b/src/i18n/messages/nl.json
@@ -1635,6 +1635,7 @@
"errorOccurred": "Er is een fout opgetreden. Probeer het opnieuw.",
"modelStatus": "Modelstatus",
"showConfiguredOnly": "Configured only",
+ "searchProviders": "Zoekaanbieders...",
"allModelsOperational": "Alle modellen operationeel",
"modelsWithIssues": "{count} model(len) met problemen",
"allModelsNormal": "Alle modellen reageren normaal.",
diff --git a/src/i18n/messages/no.json b/src/i18n/messages/no.json
index bad36d619a..6f365f5c30 100644
--- a/src/i18n/messages/no.json
+++ b/src/i18n/messages/no.json
@@ -1635,6 +1635,7 @@
"errorOccurred": "Det oppsto en feil. Vennligst prøv igjen.",
"modelStatus": "Modellstatus",
"showConfiguredOnly": "Configured only",
+ "searchProviders": "Søk etter leverandører...",
"allModelsOperational": "Alle modeller i drift",
"modelsWithIssues": "{count} modell(er) med problemer",
"allModelsNormal": "Alle modellene reagerer normalt.",
diff --git a/src/i18n/messages/phi.json b/src/i18n/messages/phi.json
index 3499fcee65..5ead5bd771 100644
--- a/src/i18n/messages/phi.json
+++ b/src/i18n/messages/phi.json
@@ -1635,6 +1635,7 @@
"errorOccurred": "May naganap na error. Pakisubukang muli.",
"modelStatus": "Katayuan ng Modelo",
"showConfiguredOnly": "Configured only",
+ "searchProviders": "Maghanap ng mga provider...",
"allModelsOperational": "Ang lahat ng mga modelo ay gumagana",
"modelsWithIssues": "{count} (mga) modelong may mga isyu",
"allModelsNormal": "Ang lahat ng mga modelo ay tumutugon nang normal.",
diff --git a/src/i18n/messages/pl.json b/src/i18n/messages/pl.json
index 17e5605fa5..e31063ad8b 100644
--- a/src/i18n/messages/pl.json
+++ b/src/i18n/messages/pl.json
@@ -1635,6 +1635,7 @@
"errorOccurred": "Wystąpił błąd. Spróbuj ponownie.",
"modelStatus": "Stan modelu",
"showConfiguredOnly": "Configured only",
+ "searchProviders": "Wyszukaj dostawców...",
"allModelsOperational": "Wszystkie modele sprawne",
"modelsWithIssues": "{count} modele z problemami",
"allModelsNormal": "Wszystkie modele reagują normalnie.",
diff --git a/src/i18n/messages/pt-BR.json b/src/i18n/messages/pt-BR.json
index a2ec852344..25dfc0dc76 100644
--- a/src/i18n/messages/pt-BR.json
+++ b/src/i18n/messages/pt-BR.json
@@ -1687,6 +1687,7 @@
"errorOccurred": "Ocorreu um erro. Tente novamente.",
"modelStatus": "Status dos Modelos",
"showConfiguredOnly": "Configured only",
+ "searchProviders": "Buscar provedores...",
"allModelsOperational": "Todos os modelos operacionais",
"modelsWithIssues": "{count} modelo(s) com problemas",
"allModelsNormal": "Todos os modelos estão respondendo normalmente.",
diff --git a/src/i18n/messages/pt.json b/src/i18n/messages/pt.json
index 6ab9e0282b..eb553354fc 100644
--- a/src/i18n/messages/pt.json
+++ b/src/i18n/messages/pt.json
@@ -1687,6 +1687,7 @@
"errorOccurred": "Ocorreu um erro. Por favor, tente novamente.",
"modelStatus": "Status do modelo",
"showConfiguredOnly": "Configured only",
+ "searchProviders": "Pesquisar fornecedores...",
"allModelsOperational": "Todos os modelos operacionais",
"modelsWithIssues": "{count} modelo(s) com problemas",
"allModelsNormal": "Todos os modelos estão respondendo normalmente.",
diff --git a/src/i18n/messages/ro.json b/src/i18n/messages/ro.json
index 4f364bdbf8..b1ea5431d8 100644
--- a/src/i18n/messages/ro.json
+++ b/src/i18n/messages/ro.json
@@ -1635,6 +1635,7 @@
"errorOccurred": "A apărut o eroare. Vă rugăm să încercați din nou.",
"modelStatus": "Stare model",
"showConfiguredOnly": "Configured only",
+ "searchProviders": "Căutați furnizori...",
"allModelsOperational": "Toate modelele sunt operaționale",
"modelsWithIssues": "{count} model(e) cu probleme",
"allModelsNormal": "Toate modelele răspund normal.",
diff --git a/src/i18n/messages/ru.json b/src/i18n/messages/ru.json
index 6496d7e8ba..b94939cf71 100644
--- a/src/i18n/messages/ru.json
+++ b/src/i18n/messages/ru.json
@@ -1659,6 +1659,7 @@
"errorOccurred": "Произошла ошибка. Пожалуйста, попробуйте еще раз.",
"modelStatus": "Статус модели",
"showConfiguredOnly": "Только настроенные",
+ "searchProviders": "Поиск поставщиков...",
"allModelsOperational": "Все модели в рабочем состоянии",
"modelsWithIssues": "{count} модели с проблемами",
"allModelsNormal": "Все модели реагируют нормально.",
diff --git a/src/i18n/messages/sk.json b/src/i18n/messages/sk.json
index 8cbf8424da..00040ffa2c 100644
--- a/src/i18n/messages/sk.json
+++ b/src/i18n/messages/sk.json
@@ -1635,6 +1635,7 @@
"errorOccurred": "Vyskytla sa chyba. Skúste to znova.",
"modelStatus": "Stav modelu",
"showConfiguredOnly": "Configured only",
+ "searchProviders": "Hľadať poskytovateľov...",
"allModelsOperational": "Všetky modely funkčné",
"modelsWithIssues": "{count} modelov s problémami",
"allModelsNormal": "Všetky modely reagujú normálne.",
diff --git a/src/i18n/messages/sv.json b/src/i18n/messages/sv.json
index 6f617db5f8..1bc1a51503 100644
--- a/src/i18n/messages/sv.json
+++ b/src/i18n/messages/sv.json
@@ -1635,6 +1635,7 @@
"errorOccurred": "Ett fel uppstod. Försök igen.",
"modelStatus": "Modellstatus",
"showConfiguredOnly": "Configured only",
+ "searchProviders": "Sök efter leverantörer...",
"allModelsOperational": "Alla modeller i drift",
"modelsWithIssues": "{count} modell(er) med problem",
"allModelsNormal": "Alla modeller svarar normalt.",
diff --git a/src/i18n/messages/th.json b/src/i18n/messages/th.json
index 4c832554d3..afaade7a46 100644
--- a/src/i18n/messages/th.json
+++ b/src/i18n/messages/th.json
@@ -1635,6 +1635,7 @@
"errorOccurred": "เกิดข้อผิดพลาด โปรดลองอีกครั้ง",
"modelStatus": "สถานะโมเดล",
"showConfiguredOnly": "Configured only",
+ "searchProviders": "ค้นหาผู้ให้บริการ...",
"allModelsOperational": "ใช้งานได้ทุกรุ่น",
"modelsWithIssues": "{count} โมเดลที่มีปัญหา",
"allModelsNormal": "ทุกรุ่นตอบสนองได้ปกติ",
diff --git a/src/i18n/messages/tr.json b/src/i18n/messages/tr.json
index 5db4e7afc9..0f37e847af 100644
--- a/src/i18n/messages/tr.json
+++ b/src/i18n/messages/tr.json
@@ -1635,6 +1635,7 @@
"errorOccurred": "Bir hata oluştu. Lütfen tekrar deneyin.",
"modelStatus": "Model Durumu",
"showConfiguredOnly": "Configured only",
+ "searchProviders": "Sağlayıcı ara...",
"allModelsOperational": "Tüm modeller çalışır durumda",
"modelsWithIssues": "{count} modelde sorun var",
"allModelsNormal": "Tüm modeller normal şekilde yanıt veriyor.",
diff --git a/src/i18n/messages/uk-UA.json b/src/i18n/messages/uk-UA.json
index 61eeb1ee77..a72e4c5334 100644
--- a/src/i18n/messages/uk-UA.json
+++ b/src/i18n/messages/uk-UA.json
@@ -1635,6 +1635,7 @@
"errorOccurred": "Сталася помилка. Спробуйте ще раз.",
"modelStatus": "Статус моделі",
"showConfiguredOnly": "Configured only",
+ "searchProviders": "Пошук постачальників...",
"allModelsOperational": "Всі моделі робочі",
"modelsWithIssues": "{count} моделі з проблемами",
"allModelsNormal": "Усі моделі реагують нормально.",
diff --git a/src/i18n/messages/vi.json b/src/i18n/messages/vi.json
index 74b92b525f..fcc7df55c6 100644
--- a/src/i18n/messages/vi.json
+++ b/src/i18n/messages/vi.json
@@ -1635,6 +1635,7 @@
"errorOccurred": "Đã xảy ra lỗi. Vui lòng thử lại.",
"modelStatus": "Tình trạng mẫu",
"showConfiguredOnly": "Configured only",
+ "searchProviders": "Tìm kiếm nhà cung cấp...",
"allModelsOperational": "Tất cả các mô hình hoạt động",
"modelsWithIssues": "{count} mô hình có vấn đề",
"allModelsNormal": "Tất cả các model đều phản hồi bình thường.",
diff --git a/src/i18n/messages/zh-CN.json b/src/i18n/messages/zh-CN.json
index 5f6f7e9655..e820d2e8d1 100644
--- a/src/i18n/messages/zh-CN.json
+++ b/src/i18n/messages/zh-CN.json
@@ -1731,6 +1731,7 @@
"errorOccurred": "发生错误。请再试一次。",
"modelStatus": "模型状态",
"showConfiguredOnly": "仅显示已配置",
+ "searchProviders": "搜索提供商...",
"allModelsOperational": "所有模型运行正常",
"modelsWithIssues": "{count} 有问题的模型",
"allModelsNormal": "所有模型当前响应正常。",
diff --git a/tests/unit/providers-page-utils.test.ts b/tests/unit/providers-page-utils.test.ts
index 11bf8e49fc..f98ca74422 100644
--- a/tests/unit/providers-page-utils.test.ts
+++ b/tests/unit/providers-page-utils.test.ts
@@ -76,6 +76,112 @@ test("configured-only filter keeps only providers with saved connections", () =>
assert.equal(providerPageUtils.filterConfiguredProviderEntries(entries, false).length, 3);
});
+test("search filter matches provider name and id case-insensitively", () => {
+ const entries = [
+ {
+ providerId: "claude",
+ provider: { id: "claude", name: "Claude" },
+ stats: { total: 2 },
+ displayAuthType: "oauth",
+ toggleAuthType: "oauth",
+ },
+ {
+ providerId: "openai",
+ provider: { id: "openai", name: "OpenAI" },
+ stats: { total: 1 },
+ displayAuthType: "oauth",
+ toggleAuthType: "oauth",
+ },
+ {
+ providerId: "gemini",
+ provider: { id: "gemini", name: "Google Gemini" },
+ stats: { total: 1 },
+ displayAuthType: "oauth",
+ toggleAuthType: "oauth",
+ },
+ ];
+
+ const byName = providerPageUtils.filterConfiguredProviderEntries(entries, false, "claude");
+ assert.deepEqual(
+ byName.map((e) => e.providerId),
+ ["claude"]
+ );
+
+ const byNameCaseInsensitive = providerPageUtils.filterConfiguredProviderEntries(
+ entries,
+ false,
+ "OPENAI"
+ );
+ assert.deepEqual(
+ byNameCaseInsensitive.map((e) => e.providerId),
+ ["openai"]
+ );
+
+ const byPartialName = providerPageUtils.filterConfiguredProviderEntries(entries, false, "google");
+ assert.deepEqual(
+ byPartialName.map((e) => e.providerId),
+ ["gemini"]
+ );
+
+ const byId = providerPageUtils.filterConfiguredProviderEntries(entries, false, "gem");
+ assert.deepEqual(
+ byId.map((e) => e.providerId),
+ ["gemini"]
+ );
+
+ const noMatch = providerPageUtils.filterConfiguredProviderEntries(entries, false, "xyz");
+ assert.equal(noMatch.length, 0);
+
+ const emptySearch = providerPageUtils.filterConfiguredProviderEntries(entries, false, "");
+ assert.equal(emptySearch.length, 3);
+
+ const whitespaceSearch = providerPageUtils.filterConfiguredProviderEntries(entries, false, " ");
+ assert.equal(whitespaceSearch.length, 3);
+});
+
+test("search and configured-only filters work together", () => {
+ const entries = [
+ {
+ providerId: "claude",
+ provider: { id: "claude", name: "Claude" },
+ stats: { total: 2 },
+ displayAuthType: "oauth",
+ toggleAuthType: "oauth",
+ },
+ {
+ providerId: "openai",
+ provider: { id: "openai", name: "OpenAI" },
+ stats: { total: 0 },
+ displayAuthType: "oauth",
+ toggleAuthType: "oauth",
+ },
+ {
+ providerId: "gemini",
+ provider: { id: "gemini", name: "Google Gemini" },
+ stats: { total: 1 },
+ displayAuthType: "oauth",
+ toggleAuthType: "oauth",
+ },
+ ];
+
+ const configuredAndSearched = providerPageUtils.filterConfiguredProviderEntries(
+ entries,
+ true,
+ "claude"
+ );
+ assert.deepEqual(
+ configuredAndSearched.map((e) => e.providerId),
+ ["claude"]
+ );
+
+ const configuredButNoMatch = providerPageUtils.filterConfiguredProviderEntries(
+ entries,
+ true,
+ "openai"
+ );
+ assert.equal(configuredButNoMatch.length, 0);
+});
+
test("configured-only preference parser only enables explicit true values", () => {
assert.equal(providerPageStorage.parseConfiguredOnlyPreference("true"), true);
assert.equal(providerPageStorage.parseConfiguredOnlyPreference("false"), false);