2024-06-27 17:34:48 -04:00
|
|
|
import { useSetRecoilState } from 'recoil';
|
2024-06-21 12:34:28 -04:00
|
|
|
import { useCallback, useMemo } from 'react';
|
2024-06-27 17:34:48 -04:00
|
|
|
import { PermissionTypes, Permissions } from 'librechat-data-provider';
|
2024-06-25 03:02:38 -04:00
|
|
|
import type { SetterOrUpdater } from 'recoil';
|
2024-06-27 17:34:48 -04:00
|
|
|
import useHasAccess from '~/hooks/Roles/useHasAccess';
|
|
|
|
|
import store from '~/store';
|
2024-06-25 03:02:38 -04:00
|
|
|
|
|
|
|
|
/** Event Keys that shouldn't trigger a command */
|
|
|
|
|
const invalidKeys = {
|
|
|
|
|
Escape: true,
|
|
|
|
|
Backspace: true,
|
|
|
|
|
Enter: true,
|
|
|
|
|
};
|
2024-06-21 12:34:28 -04:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Utility function to determine if a command should trigger.
|
|
|
|
|
*/
|
|
|
|
|
const shouldTriggerCommand = (
|
|
|
|
|
textAreaRef: React.RefObject<HTMLTextAreaElement>,
|
|
|
|
|
commandChar: string,
|
|
|
|
|
) => {
|
|
|
|
|
const text = textAreaRef.current?.value;
|
|
|
|
|
if (!(text && text[text.length - 1] === commandChar)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const startPos = textAreaRef.current?.selectionStart;
|
|
|
|
|
if (!startPos) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const isAtStart = startPos === 1;
|
|
|
|
|
const isPrecededBySpace = textAreaRef.current?.value.charAt(startPos - 2) === ' ';
|
|
|
|
|
|
|
|
|
|
const shouldTrigger = isAtStart || isPrecededBySpace;
|
|
|
|
|
return shouldTrigger;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Custom hook for handling key up events with command triggers.
|
|
|
|
|
*/
|
|
|
|
|
const useHandleKeyUp = ({
|
2024-06-27 17:34:48 -04:00
|
|
|
index,
|
2024-06-21 12:34:28 -04:00
|
|
|
textAreaRef,
|
2024-06-25 03:02:38 -04:00
|
|
|
setShowPlusPopover,
|
|
|
|
|
setShowMentionPopover,
|
2024-06-21 12:34:28 -04:00
|
|
|
}: {
|
2024-06-27 17:34:48 -04:00
|
|
|
index: number;
|
2024-06-21 12:34:28 -04:00
|
|
|
textAreaRef: React.RefObject<HTMLTextAreaElement>;
|
2024-06-25 03:02:38 -04:00
|
|
|
setShowPlusPopover: SetterOrUpdater<boolean>;
|
|
|
|
|
setShowMentionPopover: SetterOrUpdater<boolean>;
|
2024-06-21 12:34:28 -04:00
|
|
|
}) => {
|
2024-06-27 17:34:48 -04:00
|
|
|
const hasAccess = useHasAccess({
|
|
|
|
|
permissionType: PermissionTypes.PROMPTS,
|
|
|
|
|
permission: Permissions.USE,
|
|
|
|
|
});
|
|
|
|
|
const setShowPromptsPopover = useSetRecoilState(store.showPromptsPopoverFamily(index));
|
2024-06-21 12:34:28 -04:00
|
|
|
const handleAtCommand = useCallback(() => {
|
|
|
|
|
if (shouldTriggerCommand(textAreaRef, '@')) {
|
|
|
|
|
setShowMentionPopover(true);
|
|
|
|
|
}
|
|
|
|
|
}, [textAreaRef, setShowMentionPopover]);
|
|
|
|
|
|
2024-06-25 03:02:38 -04:00
|
|
|
const handlePlusCommand = useCallback(() => {
|
|
|
|
|
if (shouldTriggerCommand(textAreaRef, '+')) {
|
|
|
|
|
setShowPlusPopover(true);
|
|
|
|
|
}
|
|
|
|
|
}, [textAreaRef, setShowPlusPopover]);
|
2024-06-21 12:34:28 -04:00
|
|
|
|
2024-06-27 17:34:48 -04:00
|
|
|
const handlePromptsCommand = useCallback(() => {
|
|
|
|
|
if (!hasAccess) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (shouldTriggerCommand(textAreaRef, '/')) {
|
|
|
|
|
setShowPromptsPopover(true);
|
|
|
|
|
}
|
|
|
|
|
}, [textAreaRef, hasAccess, setShowPromptsPopover]);
|
|
|
|
|
|
2024-06-21 12:34:28 -04:00
|
|
|
const commandHandlers = useMemo(
|
|
|
|
|
() => ({
|
|
|
|
|
'@': handleAtCommand,
|
2024-06-25 03:02:38 -04:00
|
|
|
'+': handlePlusCommand,
|
2024-06-27 17:34:48 -04:00
|
|
|
'/': handlePromptsCommand,
|
2024-06-21 12:34:28 -04:00
|
|
|
}),
|
2024-06-27 17:34:48 -04:00
|
|
|
[handleAtCommand, handlePlusCommand, handlePromptsCommand],
|
2024-06-21 12:34:28 -04:00
|
|
|
);
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Main key up handler.
|
|
|
|
|
*/
|
|
|
|
|
const handleKeyUp = useCallback(
|
|
|
|
|
(event: React.KeyboardEvent<HTMLTextAreaElement>) => {
|
|
|
|
|
const text = textAreaRef.current?.value;
|
|
|
|
|
if (!text) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-25 03:02:38 -04:00
|
|
|
if (invalidKeys[event.key]) {
|
2024-06-21 12:34:28 -04:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const lastChar = text[text.length - 1];
|
|
|
|
|
const handler = commandHandlers[lastChar];
|
|
|
|
|
|
|
|
|
|
if (handler) {
|
|
|
|
|
handler();
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
[textAreaRef, commandHandlers],
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
return handleKeyUp;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export default useHandleKeyUp;
|