mirror of
https://github.com/danny-avila/LibreChat.git
synced 2025-12-16 16:30:15 +01:00
Revert "⌨️ feat: Add Shift-Key Shortcuts for Instant Conversation Actions (#10732)"
This reverts commit 41c0a96d39.
This commit is contained in:
parent
dcd9273700
commit
03ced7a894
4 changed files with 43 additions and 176 deletions
|
|
@ -6,7 +6,7 @@ import { useToastContext, useMediaQuery } from '@librechat/client';
|
||||||
import type { TConversation } from 'librechat-data-provider';
|
import type { TConversation } from 'librechat-data-provider';
|
||||||
import { useUpdateConversationMutation } from '~/data-provider';
|
import { useUpdateConversationMutation } from '~/data-provider';
|
||||||
import EndpointIcon from '~/components/Endpoints/EndpointIcon';
|
import EndpointIcon from '~/components/Endpoints/EndpointIcon';
|
||||||
import { useNavigateToConvo, useLocalize, useShiftKey } from '~/hooks';
|
import { useNavigateToConvo, useLocalize } from '~/hooks';
|
||||||
import { useGetEndpointsQuery } from '~/data-provider';
|
import { useGetEndpointsQuery } from '~/data-provider';
|
||||||
import { NotificationSeverity } from '~/common';
|
import { NotificationSeverity } from '~/common';
|
||||||
import { ConvoOptions } from './ConvoOptions';
|
import { ConvoOptions } from './ConvoOptions';
|
||||||
|
|
@ -31,7 +31,6 @@ export default function Conversation({ conversation, retainView, toggleNav }: Co
|
||||||
const updateConvoMutation = useUpdateConversationMutation(currentConvoId ?? '');
|
const updateConvoMutation = useUpdateConversationMutation(currentConvoId ?? '');
|
||||||
const activeConvos = useRecoilValue(store.allConversationsSelector);
|
const activeConvos = useRecoilValue(store.allConversationsSelector);
|
||||||
const isSmallScreen = useMediaQuery('(max-width: 768px)');
|
const isSmallScreen = useMediaQuery('(max-width: 768px)');
|
||||||
const isShiftHeld = useShiftKey();
|
|
||||||
const { conversationId, title = '' } = conversation;
|
const { conversationId, title = '' } = conversation;
|
||||||
|
|
||||||
const [titleInput, setTitleInput] = useState(title || '');
|
const [titleInput, setTitleInput] = useState(title || '');
|
||||||
|
|
@ -195,9 +194,8 @@ export default function Conversation({ conversation, retainView, toggleNav }: Co
|
||||||
className={cn(
|
className={cn(
|
||||||
'mr-2 flex origin-left',
|
'mr-2 flex origin-left',
|
||||||
isPopoverActive || isActiveConvo
|
isPopoverActive || isActiveConvo
|
||||||
? 'pointer-events-auto scale-x-100 opacity-100'
|
? 'pointer-events-auto max-w-[28px] scale-x-100 opacity-100'
|
||||||
: 'pointer-events-none max-w-0 scale-x-0 opacity-0 group-focus-within:pointer-events-auto group-focus-within:max-w-[60px] group-focus-within:scale-x-100 group-focus-within:opacity-100 group-hover:pointer-events-auto group-hover:max-w-[60px] group-hover:scale-x-100 group-hover:opacity-100',
|
: 'pointer-events-none max-w-0 scale-x-0 opacity-0 group-focus-within:pointer-events-auto group-focus-within:max-w-[28px] group-focus-within:scale-x-100 group-focus-within:opacity-100 group-hover:pointer-events-auto group-hover:max-w-[28px] group-hover:scale-x-100 group-hover:opacity-100',
|
||||||
(isPopoverActive || isActiveConvo) && (isShiftHeld ? 'max-w-[60px]' : 'max-w-[28px]'),
|
|
||||||
)}
|
)}
|
||||||
// Removing aria-hidden to fix accessibility issue: ARIA hidden element must not be focusable or contain focusable elements
|
// Removing aria-hidden to fix accessibility issue: ARIA hidden element must not be focusable or contain focusable elements
|
||||||
// but not sure what its original purpose was, so leaving the property commented out until it can be cleared safe to delete.
|
// but not sure what its original purpose was, so leaving the property commented out until it can be cleared safe to delete.
|
||||||
|
|
|
||||||
|
|
@ -1,19 +1,15 @@
|
||||||
import { useState, useId, useRef, memo, useCallback, useMemo } from 'react';
|
import { useState, useId, useRef, memo, useCallback, useMemo } from 'react';
|
||||||
import * as Ariakit from '@ariakit/react';
|
import * as Ariakit from '@ariakit/react';
|
||||||
import { useParams, useNavigate } from 'react-router-dom';
|
import { useParams, useNavigate } from 'react-router-dom';
|
||||||
import { QueryKeys } from 'librechat-data-provider';
|
|
||||||
import { useQueryClient } from '@tanstack/react-query';
|
|
||||||
import { DropdownPopup, Spinner, useToastContext } from '@librechat/client';
|
import { DropdownPopup, Spinner, useToastContext } from '@librechat/client';
|
||||||
import { Ellipsis, Share2, CopyPlus, Archive, Pen, Trash } from 'lucide-react';
|
import { Ellipsis, Share2, CopyPlus, Archive, Pen, Trash } from 'lucide-react';
|
||||||
import type { MouseEvent } from 'react';
|
import type { MouseEvent } from 'react';
|
||||||
import type { TMessage } from 'librechat-data-provider';
|
|
||||||
import {
|
import {
|
||||||
useDuplicateConversationMutation,
|
useDuplicateConversationMutation,
|
||||||
useDeleteConversationMutation,
|
|
||||||
useGetStartupConfig,
|
useGetStartupConfig,
|
||||||
useArchiveConvoMutation,
|
useArchiveConvoMutation,
|
||||||
} from '~/data-provider';
|
} from '~/data-provider';
|
||||||
import { useLocalize, useNavigateToConvo, useNewConvo, useShiftKey } from '~/hooks';
|
import { useLocalize, useNavigateToConvo, useNewConvo } from '~/hooks';
|
||||||
import { NotificationSeverity } from '~/common';
|
import { NotificationSeverity } from '~/common';
|
||||||
import { useChatContext } from '~/Providers';
|
import { useChatContext } from '~/Providers';
|
||||||
import DeleteButton from './DeleteButton';
|
import DeleteButton from './DeleteButton';
|
||||||
|
|
@ -38,8 +34,6 @@ function ConvoOptions({
|
||||||
isActiveConvo: boolean;
|
isActiveConvo: boolean;
|
||||||
}) {
|
}) {
|
||||||
const localize = useLocalize();
|
const localize = useLocalize();
|
||||||
const queryClient = useQueryClient();
|
|
||||||
const isShiftHeld = useShiftKey();
|
|
||||||
const { index } = useChatContext();
|
const { index } = useChatContext();
|
||||||
const { data: startupConfig } = useGetStartupConfig();
|
const { data: startupConfig } = useGetStartupConfig();
|
||||||
const { navigateToConvo } = useNavigateToConvo(index);
|
const { navigateToConvo } = useNavigateToConvo(index);
|
||||||
|
|
@ -58,28 +52,6 @@ function ConvoOptions({
|
||||||
|
|
||||||
const archiveConvoMutation = useArchiveConvoMutation();
|
const archiveConvoMutation = useArchiveConvoMutation();
|
||||||
|
|
||||||
const deleteMutation = useDeleteConversationMutation({
|
|
||||||
onSuccess: () => {
|
|
||||||
if (currentConvoId === conversationId || currentConvoId === 'new') {
|
|
||||||
newConversation();
|
|
||||||
navigate('/c/new', { replace: true });
|
|
||||||
}
|
|
||||||
retainView();
|
|
||||||
showToast({
|
|
||||||
message: localize('com_ui_convo_delete_success'),
|
|
||||||
severity: NotificationSeverity.SUCCESS,
|
|
||||||
showIcon: true,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
onError: () => {
|
|
||||||
showToast({
|
|
||||||
message: localize('com_ui_convo_delete_error'),
|
|
||||||
severity: NotificationSeverity.ERROR,
|
|
||||||
showIcon: true,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const duplicateConversation = useDuplicateConversationMutation({
|
const duplicateConversation = useDuplicateConversationMutation({
|
||||||
onSuccess: (data) => {
|
onSuccess: (data) => {
|
||||||
navigateToConvo(data.conversation);
|
navigateToConvo(data.conversation);
|
||||||
|
|
@ -105,7 +77,6 @@ function ConvoOptions({
|
||||||
|
|
||||||
const isDuplicateLoading = duplicateConversation.isLoading;
|
const isDuplicateLoading = duplicateConversation.isLoading;
|
||||||
const isArchiveLoading = archiveConvoMutation.isLoading;
|
const isArchiveLoading = archiveConvoMutation.isLoading;
|
||||||
const isDeleteLoading = deleteMutation.isLoading;
|
|
||||||
|
|
||||||
const shareHandler = useCallback(() => {
|
const shareHandler = useCallback(() => {
|
||||||
setShowShareDialog(true);
|
setShowShareDialog(true);
|
||||||
|
|
@ -115,70 +86,47 @@ function ConvoOptions({
|
||||||
setShowDeleteDialog(true);
|
setShowDeleteDialog(true);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const handleInstantDelete = useCallback(
|
const handleArchiveClick = useCallback(async () => {
|
||||||
(e: MouseEvent) => {
|
const convoId = conversationId ?? '';
|
||||||
e.stopPropagation();
|
if (!convoId) {
|
||||||
const convoId = conversationId ?? '';
|
return;
|
||||||
if (!convoId) {
|
}
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const messages = queryClient.getQueryData<TMessage[]>([QueryKeys.messages, convoId]);
|
archiveConvoMutation.mutate(
|
||||||
const thread_id = messages?.[messages.length - 1]?.thread_id;
|
{ conversationId: convoId, isArchived: true },
|
||||||
const endpoint = messages?.[messages.length - 1]?.endpoint;
|
{
|
||||||
|
onSuccess: () => {
|
||||||
deleteMutation.mutate({ conversationId: convoId, thread_id, endpoint, source: 'button' });
|
setAnnouncement(localize('com_ui_convo_archived'));
|
||||||
},
|
setTimeout(() => {
|
||||||
[conversationId, deleteMutation, queryClient],
|
setAnnouncement('');
|
||||||
);
|
}, 10000);
|
||||||
|
if (currentConvoId === convoId || currentConvoId === 'new') {
|
||||||
const handleArchiveClick = useCallback(
|
newConversation();
|
||||||
async (e?: MouseEvent) => {
|
navigate('/c/new', { replace: true });
|
||||||
e?.stopPropagation();
|
}
|
||||||
const convoId = conversationId ?? '';
|
retainView();
|
||||||
if (!convoId) {
|
setIsPopoverActive(false);
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
archiveConvoMutation.mutate(
|
|
||||||
{ conversationId: convoId, isArchived: true },
|
|
||||||
{
|
|
||||||
onSuccess: () => {
|
|
||||||
setAnnouncement(localize('com_ui_convo_archived'));
|
|
||||||
setTimeout(() => {
|
|
||||||
setAnnouncement('');
|
|
||||||
}, 10000);
|
|
||||||
|
|
||||||
if (currentConvoId === convoId || currentConvoId === 'new') {
|
|
||||||
newConversation();
|
|
||||||
navigate('/c/new', { replace: true });
|
|
||||||
}
|
|
||||||
|
|
||||||
retainView();
|
|
||||||
setIsPopoverActive(false);
|
|
||||||
},
|
|
||||||
onError: () => {
|
|
||||||
showToast({
|
|
||||||
message: localize('com_ui_archive_error'),
|
|
||||||
severity: NotificationSeverity.ERROR,
|
|
||||||
showIcon: true,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
);
|
onError: () => {
|
||||||
},
|
showToast({
|
||||||
[
|
message: localize('com_ui_archive_error'),
|
||||||
conversationId,
|
severity: NotificationSeverity.ERROR,
|
||||||
currentConvoId,
|
showIcon: true,
|
||||||
archiveConvoMutation,
|
});
|
||||||
navigate,
|
},
|
||||||
newConversation,
|
},
|
||||||
retainView,
|
);
|
||||||
setIsPopoverActive,
|
}, [
|
||||||
showToast,
|
conversationId,
|
||||||
localize,
|
currentConvoId,
|
||||||
],
|
archiveConvoMutation,
|
||||||
);
|
navigate,
|
||||||
|
newConversation,
|
||||||
|
retainView,
|
||||||
|
setIsPopoverActive,
|
||||||
|
showToast,
|
||||||
|
localize,
|
||||||
|
]);
|
||||||
|
|
||||||
const handleDuplicateClick = useCallback(() => {
|
const handleDuplicateClick = useCallback(() => {
|
||||||
duplicateConversation.mutate({
|
duplicateConversation.mutate({
|
||||||
|
|
@ -250,44 +198,6 @@ function ConvoOptions({
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
const buttonClassName = cn(
|
|
||||||
'inline-flex h-7 w-7 items-center justify-center rounded-md border-none p-0 text-sm font-medium ring-ring-primary transition-all duration-200 ease-in-out focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:opacity-50',
|
|
||||||
isActiveConvo === true || isPopoverActive
|
|
||||||
? 'opacity-100'
|
|
||||||
: 'opacity-0 focus:opacity-100 group-focus-within:opacity-100 group-hover:opacity-100 data-[open]:opacity-100',
|
|
||||||
);
|
|
||||||
|
|
||||||
if (isShiftHeld) {
|
|
||||||
return (
|
|
||||||
<div className="flex items-center gap-0.5">
|
|
||||||
<button
|
|
||||||
aria-label={localize('com_ui_archive')}
|
|
||||||
className={cn(buttonClassName, 'hover:bg-surface-hover')}
|
|
||||||
onClick={handleArchiveClick}
|
|
||||||
disabled={isArchiveLoading}
|
|
||||||
>
|
|
||||||
{isArchiveLoading ? (
|
|
||||||
<Spinner className="size-4" />
|
|
||||||
) : (
|
|
||||||
<Archive className="icon-md text-text-secondary" aria-hidden={true} />
|
|
||||||
)}
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
aria-label={localize('com_ui_delete')}
|
|
||||||
className={cn(buttonClassName, 'hover:bg-surface-hover')}
|
|
||||||
onClick={handleInstantDelete}
|
|
||||||
disabled={isDeleteLoading}
|
|
||||||
>
|
|
||||||
{isDeleteLoading ? (
|
|
||||||
<Spinner className="size-4" />
|
|
||||||
) : (
|
|
||||||
<Trash className="icon-md text-text-secondary" aria-hidden={true} />
|
|
||||||
)}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<span className="sr-only" aria-live="polite" aria-atomic="true">
|
<span className="sr-only" aria-live="polite" aria-atomic="true">
|
||||||
|
|
|
||||||
|
|
@ -1,2 +1 @@
|
||||||
export * from './useLazyEffect';
|
export * from './useLazyEffect';
|
||||||
export { default as useShiftKey } from './useShiftKey';
|
|
||||||
|
|
|
||||||
|
|
@ -1,40 +0,0 @@
|
||||||
import { useState, useEffect } from 'react';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Hook to track whether the shift key is currently being held down
|
|
||||||
* @returns boolean indicating if shift key is pressed
|
|
||||||
*/
|
|
||||||
export default function useShiftKey(): boolean {
|
|
||||||
const [isShiftHeld, setIsShiftHeld] = useState(false);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const handleKeyDown = (e: KeyboardEvent) => {
|
|
||||||
if (e.key === 'Shift') {
|
|
||||||
setIsShiftHeld(true);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleKeyUp = (e: KeyboardEvent) => {
|
|
||||||
if (e.key === 'Shift') {
|
|
||||||
setIsShiftHeld(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Reset shift state when window loses focus
|
|
||||||
const handleBlur = () => {
|
|
||||||
setIsShiftHeld(false);
|
|
||||||
};
|
|
||||||
|
|
||||||
window.addEventListener('keydown', handleKeyDown);
|
|
||||||
window.addEventListener('keyup', handleKeyUp);
|
|
||||||
window.addEventListener('blur', handleBlur);
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
window.removeEventListener('keydown', handleKeyDown);
|
|
||||||
window.removeEventListener('keyup', handleKeyUp);
|
|
||||||
window.removeEventListener('blur', handleBlur);
|
|
||||||
};
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return isShiftHeld;
|
|
||||||
}
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue