mirror of
https://github.com/danny-avila/LibreChat.git
synced 2025-12-20 02:10:15 +01:00
⬆️ refactor: Improve Text Commands (#3152)
* refactor(useMentions): separate usage of `useSelectMention` * refactor: separate handleKeyUp logic from useTextarea * fix(Mention): cleanup blur timer * refactor(handleKeyUp): improve command handling, prevent unintended re-trigger * chore: remove console log * chore: temporarily comment plus command
This commit is contained in:
parent
b2b469bd3d
commit
24467dd626
7 changed files with 121 additions and 38 deletions
|
|
@ -2,6 +2,7 @@ export { default as useUserKey } from './useUserKey';
|
|||
export { default as useDebounce } from './useDebounce';
|
||||
export { default as useTextarea } from './useTextarea';
|
||||
export { default as useCombobox } from './useCombobox';
|
||||
export { default as useHandleKeyUp } from './useHandleKeyUp';
|
||||
export { default as useRequiresKey } from './useRequiresKey';
|
||||
export { default as useMultipleKeys } from './useMultipleKeys';
|
||||
export { default as useSpeechToText } from './useSpeechToText';
|
||||
|
|
|
|||
93
client/src/hooks/Input/useHandleKeyUp.ts
Normal file
93
client/src/hooks/Input/useHandleKeyUp.ts
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
import { useCallback, useMemo } from 'react';
|
||||
import { useSetRecoilState } from 'recoil';
|
||||
import store from '~/store';
|
||||
|
||||
/**
|
||||
* 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;
|
||||
if (shouldTrigger) {
|
||||
// Blurring helps prevent the command from firing twice.
|
||||
textAreaRef.current.blur();
|
||||
}
|
||||
return shouldTrigger;
|
||||
};
|
||||
|
||||
/**
|
||||
* Custom hook for handling key up events with command triggers.
|
||||
*/
|
||||
const useHandleKeyUp = ({
|
||||
index,
|
||||
textAreaRef,
|
||||
}: {
|
||||
index: number;
|
||||
textAreaRef: React.RefObject<HTMLTextAreaElement>;
|
||||
}) => {
|
||||
const setShowMentionPopover = useSetRecoilState(store.showMentionPopoverFamily(index));
|
||||
|
||||
const handleAtCommand = useCallback(() => {
|
||||
if (shouldTriggerCommand(textAreaRef, '@')) {
|
||||
setShowMentionPopover(true);
|
||||
}
|
||||
}, [textAreaRef, setShowMentionPopover]);
|
||||
|
||||
// const handlePlusCommand = useCallback(() => {
|
||||
// if (shouldTriggerCommand(textAreaRef, '+')) {
|
||||
// console.log('+ command triggered');
|
||||
// }
|
||||
// }, [textAreaRef]);
|
||||
|
||||
const commandHandlers = useMemo(
|
||||
() => ({
|
||||
'@': handleAtCommand,
|
||||
// '+': handlePlusCommand,
|
||||
}),
|
||||
[handleAtCommand],
|
||||
// [handleAtCommand, handlePlusCommand],
|
||||
);
|
||||
|
||||
/**
|
||||
* Main key up handler.
|
||||
*/
|
||||
const handleKeyUp = useCallback(
|
||||
(event: React.KeyboardEvent<HTMLTextAreaElement>) => {
|
||||
const text = textAreaRef.current?.value;
|
||||
if (!text) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.key === 'Escape') {
|
||||
return;
|
||||
}
|
||||
|
||||
const lastChar = text[text.length - 1];
|
||||
const handler = commandHandlers[lastChar];
|
||||
|
||||
if (handler) {
|
||||
handler();
|
||||
}
|
||||
},
|
||||
[textAreaRef, commandHandlers],
|
||||
);
|
||||
|
||||
return handleKeyUp;
|
||||
};
|
||||
|
||||
export default useHandleKeyUp;
|
||||
|
|
@ -11,7 +11,6 @@ import useAssistantListMap from '~/hooks/Assistants/useAssistantListMap';
|
|||
import { mapEndpoints, getPresetTitle } from '~/utils';
|
||||
import { EndpointIcon } from '~/components/Endpoints';
|
||||
import { useGetPresetsQuery } from '~/data-provider';
|
||||
import useSelectMention from './useSelectMention';
|
||||
|
||||
const defaultInterface = getConfigDefaults().interface;
|
||||
|
||||
|
|
@ -85,13 +84,6 @@ export default function useMentions({ assistantMap }: { assistantMap: TAssistant
|
|||
[startupConfig],
|
||||
);
|
||||
|
||||
const { onSelectMention } = useSelectMention({
|
||||
modelSpecs,
|
||||
endpointsConfig,
|
||||
presets,
|
||||
assistantMap,
|
||||
});
|
||||
|
||||
const options: MentionOption[] = useMemo(() => {
|
||||
const mentions = [
|
||||
...(modelSpecs?.length > 0 ? modelSpecs : []).map((modelSpec) => ({
|
||||
|
|
@ -156,8 +148,10 @@ export default function useMentions({ assistantMap }: { assistantMap: TAssistant
|
|||
|
||||
return {
|
||||
options,
|
||||
presets,
|
||||
modelSpecs,
|
||||
modelsConfig,
|
||||
onSelectMention,
|
||||
endpointsConfig,
|
||||
assistantListMap,
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import debounce from 'lodash/debounce';
|
||||
import { useEffect, useRef, useCallback } from 'react';
|
||||
import { useRecoilValue, useRecoilState } from 'recoil';
|
||||
import { isAssistantsEndpoint } from 'librechat-data-provider';
|
||||
import { useRecoilValue, useSetRecoilState, useRecoilState } from 'recoil';
|
||||
import type { TEndpointOption } from 'librechat-data-provider';
|
||||
import type { KeyboardEvent } from 'react';
|
||||
import { forceResize, insertTextAtCursor, getAssistantName } from '~/utils';
|
||||
|
|
@ -40,8 +40,6 @@ export default function useTextarea({
|
|||
setFilesLoading,
|
||||
setShowBingToneSetting,
|
||||
} = useChatContext();
|
||||
|
||||
const setShowMentionPopover = useSetRecoilState(store.showMentionPopoverFamily(index));
|
||||
const [activePrompt, setActivePrompt] = useRecoilState(store.activePromptByIndex(index));
|
||||
|
||||
const { conversationId, jailbreak, endpoint = '', assistant_id } = conversation || {};
|
||||
|
|
@ -148,23 +146,6 @@ export default function useTextarea({
|
|||
assistantMap,
|
||||
]);
|
||||
|
||||
const handleKeyUp = useCallback(() => {
|
||||
const text = textAreaRef.current?.value;
|
||||
if (!(text && text[text.length - 1] === '@')) {
|
||||
return;
|
||||
}
|
||||
|
||||
const startPos = textAreaRef.current?.selectionStart;
|
||||
if (!startPos) {
|
||||
return;
|
||||
}
|
||||
|
||||
const isAtStart = startPos === 1;
|
||||
const isPrecededBySpace = textAreaRef.current?.value.charAt(startPos - 2) === ' ';
|
||||
|
||||
setShowMentionPopover(isAtStart || isPrecededBySpace);
|
||||
}, [textAreaRef, setShowMentionPopover]);
|
||||
|
||||
const handleKeyDown = useCallback(
|
||||
(e: KeyEvent) => {
|
||||
if (e.key === 'Enter' && isSubmitting) {
|
||||
|
|
@ -244,7 +225,6 @@ export default function useTextarea({
|
|||
return {
|
||||
textAreaRef,
|
||||
handlePaste,
|
||||
handleKeyUp,
|
||||
handleKeyDown,
|
||||
handleCompositionStart,
|
||||
handleCompositionEnd,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue