feat: Quality-of-Life Chat/Edit-Message Enhancements (#5194)

* fix: rendering error for mermaid flowchart syntax

* feat: add submit button ref and enable submit on Ctrl+Enter in EditMessage component

* feat: add save button and keyboard shortcuts for saving and canceling in EditMessage component

* feat: collapse chat on max height

* refactor: implement scrollable detection for textarea on key down events and initial render

* feat: add regenerate button for error handling in HoverButtons, closes #3658

* feat: add functionality to edit latest user message with the up arrow key when the input is empty
This commit is contained in:
Danny Avila 2025-01-06 22:47:24 -05:00 committed by GitHub
parent b01c744eb8
commit 8aa1e731ca
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
22 changed files with 242 additions and 66 deletions

View file

@ -5,7 +5,7 @@ import { useForm } from 'react-hook-form';
import { useUpdateMessageMutation } from 'librechat-data-provider/react-query';
import type { TEditProps } from '~/common';
import { useChatContext, useAddedChatContext } from '~/Providers';
import { TextareaAutosize } from '~/components/ui';
import { TextareaAutosize, TooltipAnchor } from '~/components/ui';
import { cn, removeFocusRings } from '~/utils';
import { useLocalize } from '~/hooks';
import Container from './Container';
@ -21,6 +21,8 @@ const EditMessage = ({
setSiblingIdx,
}: TEditProps) => {
const { addedIndex } = useAddedChatContext();
const saveButtonRef = useRef<HTMLButtonElement | null>(null);
const submitButtonRef = useRef<HTMLButtonElement | null>(null);
const { getMessages, setMessages, conversation } = useChatContext();
const [latestMultiMessage, setLatestMultiMessage] = useRecoilState(
store.latestMessageFamily(addedIndex),
@ -127,6 +129,14 @@ const EditMessage = ({
const handleKeyDown = useCallback(
(e: React.KeyboardEvent<HTMLTextAreaElement>) => {
if (e.key === 'Enter' && (e.ctrlKey || e.metaKey)) {
e.preventDefault();
submitButtonRef.current?.click();
}
if (e.key === 's' && (e.ctrlKey || e.metaKey)) {
e.preventDefault();
saveButtonRef.current?.click();
}
if (e.key === 'Escape') {
e.preventDefault();
enterEdit(true);
@ -165,25 +175,42 @@ const EditMessage = ({
/>
</div>
<div className="mt-2 flex w-full justify-center text-center">
<button
className="btn btn-primary relative mr-2"
disabled={
isSubmitting || (endpoint === EModelEndpoint.google && !message.isCreatedByUser)
<TooltipAnchor
description="Ctrl + Enter / ⌘ + Enter"
render={
<button
ref={submitButtonRef}
className="btn btn-primary relative mr-2"
disabled={
isSubmitting || (endpoint === EModelEndpoint.google && !message.isCreatedByUser)
}
onClick={handleSubmit(resubmitMessage)}
>
{localize('com_ui_save_submit')}
</button>
}
onClick={handleSubmit(resubmitMessage)}
>
{localize('com_ui_save_submit')}
</button>
<button
className="btn btn-secondary relative mr-2"
disabled={isSubmitting}
onClick={handleSubmit(updateMessage)}
>
{localize('com_ui_save')}
</button>
<button className="btn btn-neutral relative" onClick={() => enterEdit(true)}>
{localize('com_ui_cancel')}
</button>
/>
<TooltipAnchor
description="Shift + Enter"
render={
<button
ref={saveButtonRef}
className="btn btn-secondary relative mr-2"
disabled={isSubmitting}
onClick={handleSubmit(updateMessage)}
>
{localize('com_ui_save')}
</button>
}
/>
<TooltipAnchor
description="Esc"
render={
<button className="btn btn-neutral relative" onClick={() => enterEdit(true)}>
{localize('com_ui_cancel')}
</button>
}
/>
</div>
</Container>
);