diff --git a/ui/desktop/src/components/settings/providers/ProviderGrid.tsx b/ui/desktop/src/components/settings/providers/ProviderGrid.tsx index 6efe13bbc..b1c34bb82 100644 --- a/ui/desktop/src/components/settings/providers/ProviderGrid.tsx +++ b/ui/desktop/src/components/settings/providers/ProviderGrid.tsx @@ -10,7 +10,8 @@ import { acpDeleteCustomProvider, acpUpdateCustomProviderFromRequest, } from '../../../acp/providers'; -import { Plus } from 'lucide-react'; +import { Plus, Search } from 'lucide-react'; +import { Input } from '../../ui/input'; import { Dialog, DialogContent, DialogHeader, DialogTitle } from '../../ui/dialog'; import CustomProviderForm from './modal/subcomponents/forms/CustomProviderForm'; import { SwitchModelModal } from '../models/subcomponents/SwitchModelModal'; @@ -43,6 +44,14 @@ const i18n = defineMessages({ id: 'providerGrid.chooseModel', defaultMessage: 'Choose Model', }, + searchPlaceholder: { + id: 'providerGrid.searchPlaceholder', + defaultMessage: 'Search providers...', + }, + noMatch: { + id: 'providerGrid.noMatch', + defaultMessage: 'No providers match "{query}"', + }, }); const GridLayout = memo(function GridLayout({ children }: { children: React.ReactNode }) { @@ -97,6 +106,7 @@ function ProviderCards({ onModelSelected?: (model?: string) => void; }) { const intl = useIntl(); + const [searchQuery, setSearchQuery] = useState(''); const [configuringProvider, setConfiguringProvider] = useState(null); const [showCustomProviderModal, setShowCustomProviderModal] = useState(false); const [showSwitchModelModal, setShowSwitchModelModal] = useState(false); @@ -232,6 +242,8 @@ function ProviderCards({ [refreshProviders] ); + const query = searchQuery.trim().toLowerCase(); + const providerCards = useMemo(() => { // providers needs to be an array const providersArray = Array.isArray(providers) ? providers : []; @@ -239,7 +251,14 @@ function ProviderCards({ const sortedProviders = [...providersArray].sort((a, b) => a.metadata.display_name.localeCompare(b.metadata.display_name) ); - const cards = sortedProviders.map((provider) => ( + const filteredProviders = query + ? sortedProviders.filter( + (provider) => + provider.metadata.display_name.toLowerCase().includes(query) || + provider.metadata.description.toLowerCase().includes(query) + ) + : sortedProviders; + const cards = filteredProviders.map((provider) => ( 0 && + providerCards.length === 1 && + (Array.isArray(providers) ? providers.length : 0) > 0; const initialData = editingProvider && { engine: editingProvider.config.engine, @@ -277,7 +307,25 @@ function ProviderCards({ : intl.formatMessage(i18n.addProviderTitle); return ( <> - {providerCards} +
+
+ + setSearchQuery(e.target.value)} + placeholder={intl.formatMessage(i18n.searchPlaceholder)} + className="pl-9" + data-testid="provider-search-input" + /> +
+
+ {providerCards} + {hasNoMatches && ( +
+ {intl.formatMessage(i18n.noMatch, { query: searchQuery.trim() })} +
+ )} @@ -330,14 +378,12 @@ export default function ProviderGrid({ onModelSelected?: (model?: string) => void; }) { return ( - - - + ); } diff --git a/ui/desktop/src/i18n/messages/de.json b/ui/desktop/src/i18n/messages/de.json index ac830aee7..1818e3273 100644 --- a/ui/desktop/src/i18n/messages/de.json +++ b/ui/desktop/src/i18n/messages/de.json @@ -50,15 +50,15 @@ "appsView.noAppsTitle": { "defaultMessage": "Keine Apps verfügbar" }, - "appsView.retry": { - "defaultMessage": "Erneut versuchen" - }, "appsView.retiredChatApp": { "defaultMessage": "Chat app retired" }, "appsView.retiredChatAppDetail": { "defaultMessage": "We removed this feature because MCP sampling is no longer supported." }, + "appsView.retry": { + "defaultMessage": "Erneut versuchen" + }, "appsView.title": { "defaultMessage": "Apps" }, @@ -2816,6 +2816,12 @@ "providerGrid.fromTemplateOrManual": { "defaultMessage": "Aus Vorlage oder manuelle Einrichtung" }, + "providerGrid.noMatch": { + "defaultMessage": "Keine Anbieter entsprechen \"{query}\"" + }, + "providerGrid.searchPlaceholder": { + "defaultMessage": "Anbieter suchen..." + }, "providerLogo.alt": { "defaultMessage": "{providerName}-Logo" }, diff --git a/ui/desktop/src/i18n/messages/en.json b/ui/desktop/src/i18n/messages/en.json index b44b61ad9..6fd6870aa 100644 --- a/ui/desktop/src/i18n/messages/en.json +++ b/ui/desktop/src/i18n/messages/en.json @@ -2816,6 +2816,12 @@ "providerGrid.fromTemplateOrManual": { "defaultMessage": "From template or manual setup" }, + "providerGrid.noMatch": { + "defaultMessage": "No providers match \"{query}\"" + }, + "providerGrid.searchPlaceholder": { + "defaultMessage": "Search providers..." + }, "providerLogo.alt": { "defaultMessage": "{providerName} logo" }, diff --git a/ui/desktop/src/i18n/messages/es.json b/ui/desktop/src/i18n/messages/es.json index 682ced012..e35cc012e 100644 --- a/ui/desktop/src/i18n/messages/es.json +++ b/ui/desktop/src/i18n/messages/es.json @@ -2816,6 +2816,12 @@ "providerGrid.fromTemplateOrManual": { "defaultMessage": "Desde plantilla o configuración manual" }, + "providerGrid.noMatch": { + "defaultMessage": "Ningún proveedor coincide con \"{query}\"" + }, + "providerGrid.searchPlaceholder": { + "defaultMessage": "Buscar proveedores..." + }, "providerLogo.alt": { "defaultMessage": "Logotipo de {providerName}" }, diff --git a/ui/desktop/src/i18n/messages/fr.json b/ui/desktop/src/i18n/messages/fr.json index ce067a71e..30708f441 100644 --- a/ui/desktop/src/i18n/messages/fr.json +++ b/ui/desktop/src/i18n/messages/fr.json @@ -50,15 +50,15 @@ "appsView.noAppsTitle": { "defaultMessage": "Aucune application disponible" }, - "appsView.retry": { - "defaultMessage": "Réessayer" - }, "appsView.retiredChatApp": { "defaultMessage": "Chat app retired" }, "appsView.retiredChatAppDetail": { "defaultMessage": "We removed this feature because MCP sampling is no longer supported." }, + "appsView.retry": { + "defaultMessage": "Réessayer" + }, "appsView.title": { "defaultMessage": "Applications" }, @@ -2816,6 +2816,12 @@ "providerGrid.fromTemplateOrManual": { "defaultMessage": "À partir d'un modèle ou d'une configuration manuelle" }, + "providerGrid.noMatch": { + "defaultMessage": "Aucun fournisseur ne correspond à \"{query}\"" + }, + "providerGrid.searchPlaceholder": { + "defaultMessage": "Rechercher des fournisseurs..." + }, "providerLogo.alt": { "defaultMessage": "Logo {providerName}" }, diff --git a/ui/desktop/src/i18n/messages/hi.json b/ui/desktop/src/i18n/messages/hi.json index 9efb28104..df1382bd8 100644 --- a/ui/desktop/src/i18n/messages/hi.json +++ b/ui/desktop/src/i18n/messages/hi.json @@ -2816,6 +2816,12 @@ "providerGrid.fromTemplateOrManual": { "defaultMessage": "टेम्प्लेट या मैन्युअल सेटअप से" }, + "providerGrid.noMatch": { + "defaultMessage": "\"{query}\" से मेल खाने वाला कोई प्रदाता नहीं" + }, + "providerGrid.searchPlaceholder": { + "defaultMessage": "प्रदाता खोजें..." + }, "providerLogo.alt": { "defaultMessage": "{providerName} लोगो" }, diff --git a/ui/desktop/src/i18n/messages/id.json b/ui/desktop/src/i18n/messages/id.json index 3f366765a..68bc48efd 100644 --- a/ui/desktop/src/i18n/messages/id.json +++ b/ui/desktop/src/i18n/messages/id.json @@ -50,15 +50,15 @@ "appsView.noAppsTitle": { "defaultMessage": "Tidak ada aplikasi yang tersedia" }, - "appsView.retry": { - "defaultMessage": "Coba lagi" - }, "appsView.retiredChatApp": { "defaultMessage": "Chat app retired" }, "appsView.retiredChatAppDetail": { "defaultMessage": "We removed this feature because MCP sampling is no longer supported." }, + "appsView.retry": { + "defaultMessage": "Coba lagi" + }, "appsView.title": { "defaultMessage": "Aplikasi" }, @@ -2816,6 +2816,12 @@ "providerGrid.fromTemplateOrManual": { "defaultMessage": "Dari templat atau penyiapan manual" }, + "providerGrid.noMatch": { + "defaultMessage": "Tidak ada penyedia yang cocok dengan \"{query}\"" + }, + "providerGrid.searchPlaceholder": { + "defaultMessage": "Cari penyedia..." + }, "providerLogo.alt": { "defaultMessage": "Logo {providerName}" }, diff --git a/ui/desktop/src/i18n/messages/it.json b/ui/desktop/src/i18n/messages/it.json index 60738d9ce..eee0676e2 100644 --- a/ui/desktop/src/i18n/messages/it.json +++ b/ui/desktop/src/i18n/messages/it.json @@ -50,15 +50,15 @@ "appsView.noAppsTitle": { "defaultMessage": "Nessuna app disponibile" }, - "appsView.retry": { - "defaultMessage": "Riprova" - }, "appsView.retiredChatApp": { "defaultMessage": "Chat app retired" }, "appsView.retiredChatAppDetail": { "defaultMessage": "We removed this feature because MCP sampling is no longer supported." }, + "appsView.retry": { + "defaultMessage": "Riprova" + }, "appsView.title": { "defaultMessage": "App" }, @@ -2816,6 +2816,12 @@ "providerGrid.fromTemplateOrManual": { "defaultMessage": "Da modello o configurazione manuale" }, + "providerGrid.noMatch": { + "defaultMessage": "Nessun provider corrisponde a \"{query}\"" + }, + "providerGrid.searchPlaceholder": { + "defaultMessage": "Cerca provider..." + }, "providerLogo.alt": { "defaultMessage": "Logo {providerName}" }, diff --git a/ui/desktop/src/i18n/messages/ja.json b/ui/desktop/src/i18n/messages/ja.json index 3ac77bffb..6fb948ee3 100644 --- a/ui/desktop/src/i18n/messages/ja.json +++ b/ui/desktop/src/i18n/messages/ja.json @@ -2816,6 +2816,12 @@ "providerGrid.fromTemplateOrManual": { "defaultMessage": "テンプレートまたは手動設定から" }, + "providerGrid.noMatch": { + "defaultMessage": "「{query}」に一致するプロバイダーはありません" + }, + "providerGrid.searchPlaceholder": { + "defaultMessage": "プロバイダーを検索..." + }, "providerLogo.alt": { "defaultMessage": "{providerName}のロゴ" }, diff --git a/ui/desktop/src/i18n/messages/ko.json b/ui/desktop/src/i18n/messages/ko.json index b35dfe012..a2642cdbd 100644 --- a/ui/desktop/src/i18n/messages/ko.json +++ b/ui/desktop/src/i18n/messages/ko.json @@ -2816,6 +2816,12 @@ "providerGrid.fromTemplateOrManual": { "defaultMessage": "템플릿 또는 수동 설정으로 시작" }, + "providerGrid.noMatch": { + "defaultMessage": "\"{query}\"와 일치하는 제공업체가 없습니다" + }, + "providerGrid.searchPlaceholder": { + "defaultMessage": "제공업체 검색..." + }, "providerLogo.alt": { "defaultMessage": "{providerName} 로고" }, diff --git a/ui/desktop/src/i18n/messages/ms.json b/ui/desktop/src/i18n/messages/ms.json index a68ff81e7..0500ee537 100644 --- a/ui/desktop/src/i18n/messages/ms.json +++ b/ui/desktop/src/i18n/messages/ms.json @@ -50,15 +50,15 @@ "appsView.noAppsTitle": { "defaultMessage": "Tiada apl tersedia" }, - "appsView.retry": { - "defaultMessage": "Cuba semula" - }, "appsView.retiredChatApp": { "defaultMessage": "Chat app retired" }, "appsView.retiredChatAppDetail": { "defaultMessage": "We removed this feature because MCP sampling is no longer supported." }, + "appsView.retry": { + "defaultMessage": "Cuba semula" + }, "appsView.title": { "defaultMessage": "Apl" }, @@ -2816,6 +2816,12 @@ "providerGrid.fromTemplateOrManual": { "defaultMessage": "Daripada templat atau persediaan manual" }, + "providerGrid.noMatch": { + "defaultMessage": "Tiada penyedia yang sepadan dengan \"{query}\"" + }, + "providerGrid.searchPlaceholder": { + "defaultMessage": "Cari penyedia..." + }, "providerLogo.alt": { "defaultMessage": "Logo {providerName}" }, diff --git a/ui/desktop/src/i18n/messages/pt.json b/ui/desktop/src/i18n/messages/pt.json index f5bf0ef24..c2e1e51be 100644 --- a/ui/desktop/src/i18n/messages/pt.json +++ b/ui/desktop/src/i18n/messages/pt.json @@ -50,15 +50,15 @@ "appsView.noAppsTitle": { "defaultMessage": "Nenhuma aplicação disponível" }, - "appsView.retry": { - "defaultMessage": "Tentar novamente" - }, "appsView.retiredChatApp": { "defaultMessage": "Chat app retired" }, "appsView.retiredChatAppDetail": { "defaultMessage": "We removed this feature because MCP sampling is no longer supported." }, + "appsView.retry": { + "defaultMessage": "Tentar novamente" + }, "appsView.title": { "defaultMessage": "Aplicações" }, @@ -2816,6 +2816,12 @@ "providerGrid.fromTemplateOrManual": { "defaultMessage": "A partir de um modelo ou configuração manual" }, + "providerGrid.noMatch": { + "defaultMessage": "Nenhum fornecedor corresponde a \"{query}\"" + }, + "providerGrid.searchPlaceholder": { + "defaultMessage": "Pesquisar fornecedores..." + }, "providerLogo.alt": { "defaultMessage": "Logótipo de {providerName}" }, diff --git a/ui/desktop/src/i18n/messages/ru.json b/ui/desktop/src/i18n/messages/ru.json index b36fcb274..bab350342 100644 --- a/ui/desktop/src/i18n/messages/ru.json +++ b/ui/desktop/src/i18n/messages/ru.json @@ -2816,6 +2816,12 @@ "providerGrid.fromTemplateOrManual": { "defaultMessage": "Из шаблона или вручную" }, + "providerGrid.noMatch": { + "defaultMessage": "Нет провайдеров, соответствующих \"{query}\"" + }, + "providerGrid.searchPlaceholder": { + "defaultMessage": "Поиск провайдеров..." + }, "providerLogo.alt": { "defaultMessage": "Логотип {providerName}" }, diff --git a/ui/desktop/src/i18n/messages/tr.json b/ui/desktop/src/i18n/messages/tr.json index 3a93602bc..6e89f0a6a 100644 --- a/ui/desktop/src/i18n/messages/tr.json +++ b/ui/desktop/src/i18n/messages/tr.json @@ -2816,6 +2816,12 @@ "providerGrid.fromTemplateOrManual": { "defaultMessage": "Şablondan veya manuel kurulumdan" }, + "providerGrid.noMatch": { + "defaultMessage": "\"{query}\" ile eşleşen sağlayıcı yok" + }, + "providerGrid.searchPlaceholder": { + "defaultMessage": "Sağlayıcı ara..." + }, "providerLogo.alt": { "defaultMessage": "{providerName} logosu" }, diff --git a/ui/desktop/src/i18n/messages/vi.json b/ui/desktop/src/i18n/messages/vi.json index 4fcdfb1cf..bf0a4e5a4 100644 --- a/ui/desktop/src/i18n/messages/vi.json +++ b/ui/desktop/src/i18n/messages/vi.json @@ -50,15 +50,15 @@ "appsView.noAppsTitle": { "defaultMessage": "Không có ứng dụng nào" }, - "appsView.retry": { - "defaultMessage": "Thử lại" - }, "appsView.retiredChatApp": { "defaultMessage": "Chat app retired" }, "appsView.retiredChatAppDetail": { "defaultMessage": "We removed this feature because MCP sampling is no longer supported." }, + "appsView.retry": { + "defaultMessage": "Thử lại" + }, "appsView.title": { "defaultMessage": "Ứng dụng" }, @@ -2816,6 +2816,12 @@ "providerGrid.fromTemplateOrManual": { "defaultMessage": "Từ mẫu hoặc thiết lập thủ công" }, + "providerGrid.noMatch": { + "defaultMessage": "Không có nhà cung cấp nào khớp với \"{query}\"" + }, + "providerGrid.searchPlaceholder": { + "defaultMessage": "Tìm kiếm nhà cung cấp..." + }, "providerLogo.alt": { "defaultMessage": "Logo {providerName}" }, diff --git a/ui/desktop/src/i18n/messages/zh-CN.json b/ui/desktop/src/i18n/messages/zh-CN.json index 8b2801e8b..4e84c7146 100644 --- a/ui/desktop/src/i18n/messages/zh-CN.json +++ b/ui/desktop/src/i18n/messages/zh-CN.json @@ -2816,6 +2816,12 @@ "providerGrid.fromTemplateOrManual": { "defaultMessage": "使用模板或手动设置" }, + "providerGrid.noMatch": { + "defaultMessage": "没有匹配 \"{query}\" 的提供商" + }, + "providerGrid.searchPlaceholder": { + "defaultMessage": "搜索提供商..." + }, "providerLogo.alt": { "defaultMessage": "{providerName} 徽标" }, diff --git a/ui/desktop/src/i18n/messages/zh-TW.json b/ui/desktop/src/i18n/messages/zh-TW.json index 60a1a5ed6..5c42b2432 100644 --- a/ui/desktop/src/i18n/messages/zh-TW.json +++ b/ui/desktop/src/i18n/messages/zh-TW.json @@ -50,15 +50,15 @@ "appsView.noAppsTitle": { "defaultMessage": "沒有可用的應用程式" }, - "appsView.retry": { - "defaultMessage": "重試" - }, "appsView.retiredChatApp": { "defaultMessage": "Chat app retired" }, "appsView.retiredChatAppDetail": { "defaultMessage": "We removed this feature because MCP sampling is no longer supported." }, + "appsView.retry": { + "defaultMessage": "重試" + }, "appsView.title": { "defaultMessage": "應用程式" }, @@ -2816,6 +2816,12 @@ "providerGrid.fromTemplateOrManual": { "defaultMessage": "從範本或手動設定" }, + "providerGrid.noMatch": { + "defaultMessage": "沒有符合 \"{query}\" 的提供者" + }, + "providerGrid.searchPlaceholder": { + "defaultMessage": "搜尋提供者..." + }, "providerLogo.alt": { "defaultMessage": "{providerName} 標誌" },