diff --git a/client/src/components/Input/ModelSelect/Plugins.tsx b/client/src/components/Input/ModelSelect/Plugins.tsx index a7e128701f..d352da8424 100644 --- a/client/src/components/Input/ModelSelect/Plugins.tsx +++ b/client/src/components/Input/ModelSelect/Plugins.tsx @@ -5,7 +5,7 @@ import { useAvailablePluginsQuery } from 'librechat-data-provider/react-query'; import type { TPlugin } from 'librechat-data-provider'; import type { TModelSelectProps } from '~/common'; import { SelectDropDown, MultiSelectDropDown, SelectDropDownPop, Button } from '~/components/ui'; -import { useSetOptions, useAuthContext, useMediaQuery } from '~/hooks'; +import { useSetOptions, useAuthContext, useMediaQuery, useLocalize } from '~/hooks'; import { cn, cardStyle } from '~/utils/'; import store from '~/store'; @@ -26,6 +26,7 @@ export default function Plugins({ showAbove, popover = false, }: TModelSelectProps) { + const localize = useLocalize(); const { data: allPlugins } = useAvailablePluginsQuery(); const [visible, setVisibility] = useState(true); const [availableTools, setAvailableTools] = useRecoilState(store.availableTools); @@ -84,7 +85,7 @@ export default function Plugins({ type="button" className={cn( cardStyle, - 'min-w-4 z-40 flex h-[40px] flex-none items-center justify-center px-3 hover:bg-white focus:ring-0 focus:ring-offset-0 dark:hover:bg-gray-700', + 'z-40 flex h-[40px] min-w-4 flex-none items-center justify-center px-3 hover:bg-white focus:ring-0 focus:ring-offset-0 dark:hover:bg-gray-700', )} onClick={() => setVisibility((prev) => !prev)} > @@ -100,7 +101,7 @@ export default function Plugins({ setValue={setOption('model')} availableValues={models} showAbove={showAbove} - className={cn(cardStyle, 'min-w-60 z-40 flex w-64 sm:w-48', visible ? '' : 'hidden')} + className={cn(cardStyle, 'z-40 flex w-64 min-w-60 sm:w-48', visible ? '' : 'hidden')} /> ); diff --git a/client/src/components/Input/ModelSelect/PluginsByIndex.tsx b/client/src/components/Input/ModelSelect/PluginsByIndex.tsx index f7d52f9231..fe88847345 100644 --- a/client/src/components/Input/ModelSelect/PluginsByIndex.tsx +++ b/client/src/components/Input/ModelSelect/PluginsByIndex.tsx @@ -11,7 +11,7 @@ import { MultiSelectPop, Button, } from '~/components/ui'; -import { useSetIndexOptions, useAuthContext, useMediaQuery } from '~/hooks'; +import { useSetIndexOptions, useAuthContext, useMediaQuery, useLocalize } from '~/hooks'; import { cn, cardStyle } from '~/utils/'; import store from '~/store'; @@ -32,6 +32,7 @@ export default function PluginsByIndex({ showAbove, popover = false, }: TModelSelectProps) { + const localize = useLocalize(); const { data: allPlugins } = useAvailablePluginsQuery(); const [visible, setVisibility] = useState(true); const [availableTools, setAvailableTools] = useRecoilState(store.availableTools); @@ -92,7 +93,7 @@ export default function PluginsByIndex({ type="button" className={cn( cardStyle, - 'min-w-4 z-40 flex h-[40px] flex-none items-center justify-center px-3 hover:bg-white focus:ring-0 focus:ring-offset-0 dark:hover:bg-gray-700', + 'z-40 flex h-[40px] min-w-4 flex-none items-center justify-center px-3 hover:bg-white focus:ring-0 focus:ring-offset-0 dark:hover:bg-gray-700', )} onClick={() => setVisibility((prev) => !prev)} > @@ -120,6 +121,7 @@ export default function PluginsByIndex({ optionValueKey="pluginKey" showAbove={false} showLabel={false} + searchPlaceholder={localize('com_ui_select_search_plugin')} /> )} diff --git a/client/src/components/ui/MultiSearch.tsx b/client/src/components/ui/MultiSearch.tsx index d8a762af63..ce7bd4fa0a 100644 --- a/client/src/components/ui/MultiSearch.tsx +++ b/client/src/components/ui/MultiSearch.tsx @@ -49,6 +49,14 @@ export default function MultiSearch({ */ function defaultGetStringKey(node: unknown): string { if (typeof node === 'string') { + // BUGFIX: Detect psedeo separators and make sure they don't appear in the list when filtering items + // it makes sure (for the most part) that the model name starts and ends with dashes + // The long-term fix here would be to enable seperators (model groupings) but there's no + // feature mocks for such a thing yet + if (node.startsWith('---') && node.endsWith('---')) { + return ''; + } + return node.toUpperCase(); } // This should be a noop, but it's here for redundancy diff --git a/client/src/components/ui/MultiSelectDropDown.tsx b/client/src/components/ui/MultiSelectDropDown.tsx index c821db1c03..13065ed4d4 100644 --- a/client/src/components/ui/MultiSelectDropDown.tsx +++ b/client/src/components/ui/MultiSelectDropDown.tsx @@ -18,6 +18,7 @@ export type TMultiSelectDropDownProps = { containerClassName?: string; isSelected: (value: string) => boolean; className?: string; + searchPlaceholder?: string; optionValueKey?: string; }; @@ -32,6 +33,7 @@ function MultiSelectDropDown({ containerClassName, isSelected, className, + searchPlaceholder, optionValueKey = 'value', }: TMultiSelectDropDownProps) { const [isOpen, setIsOpen] = useState(false); @@ -44,10 +46,13 @@ function MultiSelectDropDown({ setIsOpen(true); }; - // Detemine if we should to convert this component into a searchable select. If we have enough elements, a search // input will appear near the top of the menu, allowing correct filtering of different model menu items. This will // reset once the component is unmounted (as per a normal search) - const [filteredValues, searchRender] = useMultiSearch(availableValues); + const [filteredValues, searchRender] = useMultiSearch( + availableValues, + searchPlaceholder, + (option) => (option.name || '').toUpperCase(), + ); const hasSearchRender = Boolean(searchRender); const options = hasSearchRender ? filteredValues : availableValues; diff --git a/client/src/components/ui/MultiSelectPop.tsx b/client/src/components/ui/MultiSelectPop.tsx index 3abf1838f1..729d93fef4 100644 --- a/client/src/components/ui/MultiSelectPop.tsx +++ b/client/src/components/ui/MultiSelectPop.tsx @@ -18,6 +18,7 @@ type SelectDropDownProps = { isSelected: (value: string) => boolean; className?: string; optionValueKey?: string; + searchPlaceholder?: string; }; function MultiSelectPop({ @@ -30,6 +31,7 @@ function MultiSelectPop({ containerClassName, isSelected, optionValueKey = 'value', + searchPlaceholder, }: SelectDropDownProps) { // const localize = useLocalize(); @@ -37,7 +39,11 @@ function MultiSelectPop({ const excludeIds = ['select-plugin', 'plugins-label', 'selected-plugins']; // Detemine if we should to convert this component into a searchable select - const [filteredValues, searchRender] = useMultiSearch(availableValues); + const [filteredValues, searchRender] = useMultiSearch( + availableValues, + searchPlaceholder, + (option) => (option.name || '').toUpperCase(), + ); const hasSearchRender = Boolean(searchRender); const options = hasSearchRender ? filteredValues : availableValues; diff --git a/client/src/localization/languages/Eng.tsx b/client/src/localization/languages/Eng.tsx index cff9b405ca..0d98d83a73 100644 --- a/client/src/localization/languages/Eng.tsx +++ b/client/src/localization/languages/Eng.tsx @@ -59,6 +59,7 @@ export default { com_ui_model: 'Model', com_ui_select_model: 'Select a model', com_ui_select_search_model: 'Search model by name', + com_ui_select_search_plugin: 'Search plugin by name', com_ui_use_prompt: 'Use prompt', com_ui_prev: 'Prev', com_ui_next: 'Next',