mirror of
https://github.com/danny-avila/LibreChat.git
synced 2025-12-17 08:50:15 +01:00
🔧 fix(menu): Menu Item Filter Improvements (#2153)
* small-fix: Ensure that fake seperators in model lists do not show in search * Ensure Plugin search uses correct placeholder and key filtering in search
This commit is contained in:
parent
30f6d90cfe
commit
f521040784
6 changed files with 33 additions and 9 deletions
|
|
@ -5,7 +5,7 @@ import { useAvailablePluginsQuery } from 'librechat-data-provider/react-query';
|
||||||
import type { TPlugin } from 'librechat-data-provider';
|
import type { TPlugin } from 'librechat-data-provider';
|
||||||
import type { TModelSelectProps } from '~/common';
|
import type { TModelSelectProps } from '~/common';
|
||||||
import { SelectDropDown, MultiSelectDropDown, SelectDropDownPop, Button } from '~/components/ui';
|
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 { cn, cardStyle } from '~/utils/';
|
||||||
import store from '~/store';
|
import store from '~/store';
|
||||||
|
|
||||||
|
|
@ -26,6 +26,7 @@ export default function Plugins({
|
||||||
showAbove,
|
showAbove,
|
||||||
popover = false,
|
popover = false,
|
||||||
}: TModelSelectProps) {
|
}: TModelSelectProps) {
|
||||||
|
const localize = useLocalize();
|
||||||
const { data: allPlugins } = useAvailablePluginsQuery();
|
const { data: allPlugins } = useAvailablePluginsQuery();
|
||||||
const [visible, setVisibility] = useState<boolean>(true);
|
const [visible, setVisibility] = useState<boolean>(true);
|
||||||
const [availableTools, setAvailableTools] = useRecoilState(store.availableTools);
|
const [availableTools, setAvailableTools] = useRecoilState(store.availableTools);
|
||||||
|
|
@ -84,7 +85,7 @@ export default function Plugins({
|
||||||
type="button"
|
type="button"
|
||||||
className={cn(
|
className={cn(
|
||||||
cardStyle,
|
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)}
|
onClick={() => setVisibility((prev) => !prev)}
|
||||||
>
|
>
|
||||||
|
|
@ -100,7 +101,7 @@ export default function Plugins({
|
||||||
setValue={setOption('model')}
|
setValue={setOption('model')}
|
||||||
availableValues={models}
|
availableValues={models}
|
||||||
showAbove={showAbove}
|
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')}
|
||||||
/>
|
/>
|
||||||
<MultiSelectDropDown
|
<MultiSelectDropDown
|
||||||
value={conversation.tools || []}
|
value={conversation.tools || []}
|
||||||
|
|
@ -109,7 +110,8 @@ export default function Plugins({
|
||||||
availableValues={availableTools}
|
availableValues={availableTools}
|
||||||
optionValueKey="pluginKey"
|
optionValueKey="pluginKey"
|
||||||
showAbove={showAbove}
|
showAbove={showAbove}
|
||||||
className={cn(cardStyle, 'min-w-60 z-50 w-64 sm:w-48', visible ? '' : 'hidden')}
|
className={cn(cardStyle, 'z-50 w-64 min-w-60 sm:w-48', visible ? '' : 'hidden')}
|
||||||
|
searchPlaceholder={localize('com_ui_select_search_plugin')}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ import {
|
||||||
MultiSelectPop,
|
MultiSelectPop,
|
||||||
Button,
|
Button,
|
||||||
} from '~/components/ui';
|
} from '~/components/ui';
|
||||||
import { useSetIndexOptions, useAuthContext, useMediaQuery } from '~/hooks';
|
import { useSetIndexOptions, useAuthContext, useMediaQuery, useLocalize } from '~/hooks';
|
||||||
import { cn, cardStyle } from '~/utils/';
|
import { cn, cardStyle } from '~/utils/';
|
||||||
import store from '~/store';
|
import store from '~/store';
|
||||||
|
|
||||||
|
|
@ -32,6 +32,7 @@ export default function PluginsByIndex({
|
||||||
showAbove,
|
showAbove,
|
||||||
popover = false,
|
popover = false,
|
||||||
}: TModelSelectProps) {
|
}: TModelSelectProps) {
|
||||||
|
const localize = useLocalize();
|
||||||
const { data: allPlugins } = useAvailablePluginsQuery();
|
const { data: allPlugins } = useAvailablePluginsQuery();
|
||||||
const [visible, setVisibility] = useState<boolean>(true);
|
const [visible, setVisibility] = useState<boolean>(true);
|
||||||
const [availableTools, setAvailableTools] = useRecoilState(store.availableTools);
|
const [availableTools, setAvailableTools] = useRecoilState(store.availableTools);
|
||||||
|
|
@ -92,7 +93,7 @@ export default function PluginsByIndex({
|
||||||
type="button"
|
type="button"
|
||||||
className={cn(
|
className={cn(
|
||||||
cardStyle,
|
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)}
|
onClick={() => setVisibility((prev) => !prev)}
|
||||||
>
|
>
|
||||||
|
|
@ -120,6 +121,7 @@ export default function PluginsByIndex({
|
||||||
optionValueKey="pluginKey"
|
optionValueKey="pluginKey"
|
||||||
showAbove={false}
|
showAbove={false}
|
||||||
showLabel={false}
|
showLabel={false}
|
||||||
|
searchPlaceholder={localize('com_ui_select_search_plugin')}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
|
||||||
|
|
@ -49,6 +49,14 @@ export default function MultiSearch({
|
||||||
*/
|
*/
|
||||||
function defaultGetStringKey(node: unknown): string {
|
function defaultGetStringKey(node: unknown): string {
|
||||||
if (typeof node === '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();
|
return node.toUpperCase();
|
||||||
}
|
}
|
||||||
// This should be a noop, but it's here for redundancy
|
// This should be a noop, but it's here for redundancy
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ export type TMultiSelectDropDownProps = {
|
||||||
containerClassName?: string;
|
containerClassName?: string;
|
||||||
isSelected: (value: string) => boolean;
|
isSelected: (value: string) => boolean;
|
||||||
className?: string;
|
className?: string;
|
||||||
|
searchPlaceholder?: string;
|
||||||
optionValueKey?: string;
|
optionValueKey?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -32,6 +33,7 @@ function MultiSelectDropDown({
|
||||||
containerClassName,
|
containerClassName,
|
||||||
isSelected,
|
isSelected,
|
||||||
className,
|
className,
|
||||||
|
searchPlaceholder,
|
||||||
optionValueKey = 'value',
|
optionValueKey = 'value',
|
||||||
}: TMultiSelectDropDownProps) {
|
}: TMultiSelectDropDownProps) {
|
||||||
const [isOpen, setIsOpen] = useState(false);
|
const [isOpen, setIsOpen] = useState(false);
|
||||||
|
|
@ -44,10 +46,13 @@ function MultiSelectDropDown({
|
||||||
setIsOpen(true);
|
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
|
// 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)
|
// reset once the component is unmounted (as per a normal search)
|
||||||
const [filteredValues, searchRender] = useMultiSearch<TPlugin[]>(availableValues);
|
const [filteredValues, searchRender] = useMultiSearch<TPlugin[]>(
|
||||||
|
availableValues,
|
||||||
|
searchPlaceholder,
|
||||||
|
(option) => (option.name || '').toUpperCase(),
|
||||||
|
);
|
||||||
const hasSearchRender = Boolean(searchRender);
|
const hasSearchRender = Boolean(searchRender);
|
||||||
const options = hasSearchRender ? filteredValues : availableValues;
|
const options = hasSearchRender ? filteredValues : availableValues;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ type SelectDropDownProps = {
|
||||||
isSelected: (value: string) => boolean;
|
isSelected: (value: string) => boolean;
|
||||||
className?: string;
|
className?: string;
|
||||||
optionValueKey?: string;
|
optionValueKey?: string;
|
||||||
|
searchPlaceholder?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
function MultiSelectPop({
|
function MultiSelectPop({
|
||||||
|
|
@ -30,6 +31,7 @@ function MultiSelectPop({
|
||||||
containerClassName,
|
containerClassName,
|
||||||
isSelected,
|
isSelected,
|
||||||
optionValueKey = 'value',
|
optionValueKey = 'value',
|
||||||
|
searchPlaceholder,
|
||||||
}: SelectDropDownProps) {
|
}: SelectDropDownProps) {
|
||||||
// const localize = useLocalize();
|
// const localize = useLocalize();
|
||||||
|
|
||||||
|
|
@ -37,7 +39,11 @@ function MultiSelectPop({
|
||||||
const excludeIds = ['select-plugin', 'plugins-label', 'selected-plugins'];
|
const excludeIds = ['select-plugin', 'plugins-label', 'selected-plugins'];
|
||||||
|
|
||||||
// Detemine if we should to convert this component into a searchable select
|
// Detemine if we should to convert this component into a searchable select
|
||||||
const [filteredValues, searchRender] = useMultiSearch<TPlugin[]>(availableValues);
|
const [filteredValues, searchRender] = useMultiSearch<TPlugin[]>(
|
||||||
|
availableValues,
|
||||||
|
searchPlaceholder,
|
||||||
|
(option) => (option.name || '').toUpperCase(),
|
||||||
|
);
|
||||||
const hasSearchRender = Boolean(searchRender);
|
const hasSearchRender = Boolean(searchRender);
|
||||||
const options = hasSearchRender ? filteredValues : availableValues;
|
const options = hasSearchRender ? filteredValues : availableValues;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -59,6 +59,7 @@ export default {
|
||||||
com_ui_model: 'Model',
|
com_ui_model: 'Model',
|
||||||
com_ui_select_model: 'Select a model',
|
com_ui_select_model: 'Select a model',
|
||||||
com_ui_select_search_model: 'Search model by name',
|
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_use_prompt: 'Use prompt',
|
||||||
com_ui_prev: 'Prev',
|
com_ui_prev: 'Prev',
|
||||||
com_ui_next: 'Next',
|
com_ui_next: 'Next',
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue