mirror of
https://github.com/danny-avila/LibreChat.git
synced 2025-12-17 00:40:14 +01:00
💬 fix: Temporary Chat PR's broken components and improved UI (#5705)
* 💬 fix: Temporary Chat PR's broken components and improved UI * 💬 fix: bring back hover effect on AudioRecorder button * style: adjust position of Mention component popover * refactor: PromptsCommand typing and style position * refactor: virtualize mention UI --------- Co-authored-by: Danny Avila <danny@librechat.ai>
This commit is contained in:
parent
63afb317c6
commit
70e410f38b
11 changed files with 196 additions and 86 deletions
|
|
@ -1,4 +1,5 @@
|
|||
import { useState, useRef, useEffect, useMemo, memo, useCallback } from 'react';
|
||||
import { AutoSizer, List } from 'react-virtualized';
|
||||
import { useSetRecoilState, useRecoilValue } from 'recoil';
|
||||
import { PermissionTypes, Permissions } from 'librechat-data-provider';
|
||||
import type { TPromptGroup } from 'librechat-data-provider';
|
||||
|
|
@ -42,6 +43,8 @@ const PopoverContainer = memo(
|
|||
},
|
||||
);
|
||||
|
||||
const ROW_HEIGHT = 40;
|
||||
|
||||
function PromptsCommand({
|
||||
index,
|
||||
textAreaRef,
|
||||
|
|
@ -63,8 +66,10 @@ function PromptsCommand({
|
|||
const mappedArray = data.map((group) => ({
|
||||
id: group._id,
|
||||
value: group.command ?? group.name,
|
||||
label: `${group.command ? `/${group.command} - ` : ''}${group.name}: ${
|
||||
group.oneliner?.length ? group.oneliner : group.productionPrompt?.prompt ?? ''
|
||||
label: `${group.command != null && group.command ? `/${group.command} - ` : ''}${
|
||||
group.name
|
||||
}: ${
|
||||
(group.oneliner?.length ?? 0) > 0 ? group.oneliner : group.productionPrompt?.prompt ?? ''
|
||||
}`,
|
||||
icon: <CategoryIcon category={group.category ?? ''} className="h-5 w-5" />,
|
||||
}));
|
||||
|
|
@ -85,12 +90,12 @@ function PromptsCommand({
|
|||
const [variableGroup, setVariableGroup] = useState<TPromptGroup | null>(null);
|
||||
const setShowPromptsPopover = useSetRecoilState(store.showPromptsPopoverFamily(index));
|
||||
|
||||
const prompts = useMemo(() => data?.promptGroups ?? [], [data]);
|
||||
const promptsMap = useMemo(() => data?.promptsMap ?? {}, [data]);
|
||||
const prompts = useMemo(() => data?.promptGroups, [data]);
|
||||
const promptsMap = useMemo(() => data?.promptsMap, [data]);
|
||||
|
||||
const { open, setOpen, searchValue, setSearchValue, matches } = useCombobox({
|
||||
value: '',
|
||||
options: prompts,
|
||||
options: prompts ?? [],
|
||||
});
|
||||
|
||||
const handleSelect = useCallback(
|
||||
|
|
@ -107,22 +112,20 @@ function PromptsCommand({
|
|||
removeCharIfLast(textAreaRef.current, commandChar);
|
||||
}
|
||||
|
||||
const isValidPrompt = mention && promptsMap && promptsMap[mention.id];
|
||||
|
||||
if (!isValidPrompt) {
|
||||
const group = promptsMap?.[mention.id];
|
||||
if (!group) {
|
||||
return;
|
||||
}
|
||||
|
||||
const group = promptsMap[mention.id];
|
||||
const hasVariables = detectVariables(group.productionPrompt?.prompt ?? '');
|
||||
if (group && hasVariables) {
|
||||
if (hasVariables) {
|
||||
if (e && e.key === 'Tab') {
|
||||
e.preventDefault();
|
||||
}
|
||||
setVariableGroup(group);
|
||||
setVariableDialogOpen(true);
|
||||
return;
|
||||
} else if (group) {
|
||||
} else {
|
||||
submitPrompt(group.productionPrompt?.prompt ?? '');
|
||||
}
|
||||
},
|
||||
|
|
@ -154,6 +157,37 @@ function PromptsCommand({
|
|||
return null;
|
||||
}
|
||||
|
||||
const rowRenderer = ({
|
||||
index,
|
||||
key,
|
||||
style,
|
||||
}: {
|
||||
index: number;
|
||||
key: string;
|
||||
style: React.CSSProperties;
|
||||
}) => {
|
||||
const mention = matches[index] as PromptOption;
|
||||
return (
|
||||
<MentionItem
|
||||
index={index}
|
||||
type="prompt"
|
||||
key={key}
|
||||
style={style}
|
||||
onClick={() => {
|
||||
if (timeoutRef.current) {
|
||||
clearTimeout(timeoutRef.current);
|
||||
}
|
||||
timeoutRef.current = null;
|
||||
handleSelect(mention);
|
||||
}}
|
||||
name={mention.label ?? ''}
|
||||
icon={mention.icon}
|
||||
description={mention.description}
|
||||
isActive={index === activeIndex}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<PopoverContainer
|
||||
index={index}
|
||||
|
|
@ -161,7 +195,7 @@ function PromptsCommand({
|
|||
variableGroup={variableGroup}
|
||||
setVariableDialogOpen={setVariableDialogOpen}
|
||||
>
|
||||
<div className="absolute bottom-16 z-10 w-full space-y-2">
|
||||
<div className="absolute bottom-14 z-10 w-full space-y-2">
|
||||
<div className="popover border-token-border-light rounded-2xl border bg-surface-tertiary-alt p-2 shadow-lg">
|
||||
<input
|
||||
// The user expects focus to transition to the input field when the popover is opened
|
||||
|
|
@ -213,24 +247,23 @@ function PromptsCommand({
|
|||
}
|
||||
|
||||
if (!isLoading && open) {
|
||||
return (matches as PromptOption[]).map((mention, index) => (
|
||||
<MentionItem
|
||||
index={index}
|
||||
type="prompt"
|
||||
key={`${mention.value}-${index}`}
|
||||
onClick={() => {
|
||||
if (timeoutRef.current) {
|
||||
clearTimeout(timeoutRef.current);
|
||||
}
|
||||
timeoutRef.current = null;
|
||||
handleSelect(mention);
|
||||
}}
|
||||
name={mention.label ?? ''}
|
||||
icon={mention.icon}
|
||||
description={mention.description}
|
||||
isActive={index === activeIndex}
|
||||
/>
|
||||
));
|
||||
return (
|
||||
<div className="max-h-40">
|
||||
<AutoSizer disableHeight>
|
||||
{({ width }) => (
|
||||
<List
|
||||
width={width}
|
||||
overscanRowCount={5}
|
||||
rowHeight={ROW_HEIGHT}
|
||||
rowCount={matches.length}
|
||||
rowRenderer={rowRenderer}
|
||||
scrollToIndex={activeIndex}
|
||||
height={Math.min(matches.length * ROW_HEIGHT, 160)}
|
||||
/>
|
||||
)}
|
||||
</AutoSizer>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
})()}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue