mirror of
https://github.com/danny-avila/LibreChat.git
synced 2025-12-19 09:50:15 +01:00
🧭 refactor: Modernize Nav/Header (#7094)
* refactor: streamline model preset handling in conversation setup * refactor: integrate navigation and location hooks in chat functions and event handlers, prevent cache from fetching on final event handling * fix: prevent adding code interpreter non-image output to file list on message attachment event, fix all unhandled edge cases when this is done (treating the file download as an image attachment, undefined fields, message tokenCount issues, use of `startsWith` on undefined "text") although it is now prevent altogether * chore: remove unused jailbreak prop from MinimalIcon component in EndpointIcon * feat: add new SVG icons (MobileSidebar, Sidebar, XAIcon), fix: xAI styling in dark vs. light modes, adjust styling of Landing icons * fix: open conversation in new tab on navigation with ctrl/meta key * refactor: update Nav & Header to use close/open sidebar buttons, as well as redesign "New Chat"/"Bookmarks" buttons to the top of the Nav, matching the latest design of ChatGPT for simplicity and to free up space * chore: remove unused isToggleHovering state and simplify opacity logic in Nav component * style: match mobile nav to mobile header
This commit is contained in:
parent
c0ebb434a6
commit
550c7cc68a
37 changed files with 361 additions and 298 deletions
|
|
@ -25,6 +25,7 @@ import store, { useGetEphemeralAgent } from '~/store';
|
|||
import { getArtifactsMode } from '~/utils/artifacts';
|
||||
import { getEndpointField, logger } from '~/utils';
|
||||
import useUserKey from '~/hooks/Input/useUserKey';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
|
||||
const logChatRequest = (request: Record<string, unknown>) => {
|
||||
logger.log('=====================================\nAsk function called with:');
|
||||
|
|
@ -69,6 +70,7 @@ export default function useChatFunctions({
|
|||
const codeArtifacts = useRecoilValue(store.codeArtifacts);
|
||||
const includeShadcnui = useRecoilValue(store.includeShadcnui);
|
||||
const customPromptMode = useRecoilValue(store.customPromptMode);
|
||||
const navigate = useNavigate();
|
||||
const resetLatestMultiMessage = useResetRecoilState(store.latestMessageFamily(index + 1));
|
||||
const setShowStopButton = useSetRecoilState(store.showStopButtonByIndex(index));
|
||||
const setFilesToDelete = useSetFilesToDelete();
|
||||
|
|
@ -146,6 +148,7 @@ export default function useChatFunctions({
|
|||
parentMessageId = Constants.NO_PARENT;
|
||||
currentMessages = [];
|
||||
conversationId = null;
|
||||
navigate('/c/new');
|
||||
}
|
||||
|
||||
const targetParentMessageId = isRegenerate ? messageId : latestMessage?.parentMessageId;
|
||||
|
|
|
|||
|
|
@ -23,10 +23,10 @@ export default function useChatHelpers(index = 0, paramId?: string) {
|
|||
const { conversation, setConversation } = useCreateConversationAtom(index);
|
||||
const { conversationId } = conversation ?? {};
|
||||
|
||||
const queryParam = paramId === 'new' ? paramId : conversationId ?? paramId ?? '';
|
||||
const queryParam = paramId === 'new' ? paramId : (conversationId ?? paramId ?? '');
|
||||
|
||||
/* Messages: here simply to fetch, don't export and use `getMessages()` instead */
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
|
||||
const { data: _messages } = useGetMessagesByConvoId(conversationId ?? '', {
|
||||
enabled: isAuthenticated,
|
||||
});
|
||||
|
|
@ -41,7 +41,7 @@ export default function useChatHelpers(index = 0, paramId?: string) {
|
|||
const setMessages = useCallback(
|
||||
(messages: TMessage[]) => {
|
||||
queryClient.setQueryData<TMessage[]>([QueryKeys.messages, queryParam], messages);
|
||||
if (queryParam === 'new') {
|
||||
if (queryParam === 'new' && conversationId && conversationId !== 'new') {
|
||||
queryClient.setQueryData<TMessage[]>([QueryKeys.messages, conversationId], messages);
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { memo } from 'react';
|
||||
import { EModelEndpoint, KnownEndpoints } from 'librechat-data-provider';
|
||||
import { CustomMinimalIcon } from '~/components/svg';
|
||||
import { CustomMinimalIcon, XAIcon } from '~/components/svg';
|
||||
import { IconContext } from '~/common';
|
||||
import { cn } from '~/utils';
|
||||
|
||||
|
|
@ -20,7 +20,6 @@ const knownEndpointAssets = {
|
|||
[KnownEndpoints.shuttleai]: '/assets/shuttleai.png',
|
||||
[KnownEndpoints['together.ai']]: '/assets/together.png',
|
||||
[KnownEndpoints.unify]: '/assets/unify.webp',
|
||||
[KnownEndpoints.xai]: '/assets/xai.svg',
|
||||
};
|
||||
|
||||
const knownEndpointClasses = {
|
||||
|
|
@ -29,9 +28,6 @@ const knownEndpointClasses = {
|
|||
},
|
||||
[KnownEndpoints.xai]: {
|
||||
[IconContext.landing]: 'p-2',
|
||||
[IconContext.menuItem]: 'bg-white',
|
||||
[IconContext.message]: 'bg-white',
|
||||
[IconContext.nav]: 'bg-white',
|
||||
},
|
||||
};
|
||||
|
||||
|
|
@ -72,6 +68,18 @@ function UnknownIcon({
|
|||
|
||||
const currentEndpoint = endpoint.toLowerCase();
|
||||
|
||||
if (currentEndpoint === KnownEndpoints.xai) {
|
||||
return (
|
||||
<XAIcon
|
||||
className={getKnownClass({
|
||||
currentEndpoint,
|
||||
context: context,
|
||||
className,
|
||||
})}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
if (iconURL) {
|
||||
return <img className={className} src={iconURL} alt={`${endpoint} Icon`} />;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ export default function useAttachmentHandler(queryClient?: QueryClient) {
|
|||
return ({ data }: { data: TAttachment; submission: EventSubmission }) => {
|
||||
const { messageId } = data;
|
||||
|
||||
if (queryClient) {
|
||||
if (queryClient && !data?.filepath?.startsWith('/api/files')) {
|
||||
queryClient.setQueryData([QueryKeys.files], (oldData: TAttachment[] | undefined) => {
|
||||
return [data, ...(oldData || [])];
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { v4 } from 'uuid';
|
||||
import { useCallback, useRef } from 'react';
|
||||
import { useSetRecoilState } from 'recoil';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import { useParams, useNavigate, useLocation } from 'react-router-dom';
|
||||
import { useQueryClient } from '@tanstack/react-query';
|
||||
import {
|
||||
QueryKeys,
|
||||
|
|
@ -172,6 +172,8 @@ export default function useEventHandlers({
|
|||
const { announcePolite } = useLiveAnnouncer();
|
||||
const applyAgentTemplate = useApplyNewAgentTemplate();
|
||||
const setAbortScroll = useSetRecoilState(store.abortScroll);
|
||||
const navigate = useNavigate();
|
||||
const location = useLocation();
|
||||
|
||||
const lastAnnouncementTimeRef = useRef(Date.now());
|
||||
const { conversationId: paramId } = useParams();
|
||||
|
|
@ -421,6 +423,7 @@ export default function useEventHandlers({
|
|||
announcePolite,
|
||||
setConversation,
|
||||
resetLatestMessage,
|
||||
applyAgentTemplate,
|
||||
],
|
||||
);
|
||||
|
||||
|
|
@ -449,12 +452,20 @@ export default function useEventHandlers({
|
|||
announcePolite({ message: getAllContentText(responseMessage) });
|
||||
|
||||
/* Update messages; if assistants endpoint, client doesn't receive responseMessage */
|
||||
let finalMessages: TMessage[] = [];
|
||||
if (runMessages) {
|
||||
setMessages([...runMessages]);
|
||||
finalMessages = [...runMessages];
|
||||
} else if (isRegenerate && responseMessage) {
|
||||
setMessages([...messages, responseMessage]);
|
||||
finalMessages = [...messages, responseMessage];
|
||||
} else if (requestMessage != null && responseMessage != null) {
|
||||
setMessages([...messages, requestMessage, responseMessage]);
|
||||
finalMessages = [...messages, requestMessage, responseMessage];
|
||||
}
|
||||
if (finalMessages.length > 0) {
|
||||
setMessages(finalMessages);
|
||||
queryClient.setQueryData<TMessage[]>(
|
||||
[QueryKeys.messages, conversation.conversationId],
|
||||
finalMessages,
|
||||
);
|
||||
}
|
||||
|
||||
const isNewConvo = conversation.conversationId !== submissionConvo.conversationId;
|
||||
|
|
@ -476,8 +487,8 @@ export default function useEventHandlers({
|
|||
}
|
||||
|
||||
if (setConversation && isAddedRequest !== true) {
|
||||
if (window.location.pathname === '/c/new') {
|
||||
window.history.pushState({}, '', '/c/' + conversation.conversationId);
|
||||
if (location.pathname === '/c/new') {
|
||||
navigate(`/c/${conversation.conversationId}`, { replace: true });
|
||||
}
|
||||
|
||||
setConversation((prevState) => {
|
||||
|
|
@ -502,16 +513,18 @@ export default function useEventHandlers({
|
|||
setIsSubmitting(false);
|
||||
},
|
||||
[
|
||||
genTitle,
|
||||
queryClient,
|
||||
getMessages,
|
||||
setMessages,
|
||||
setCompleted,
|
||||
isAddedRequest,
|
||||
announcePolite,
|
||||
setConversation,
|
||||
setIsSubmitting,
|
||||
setShowStopButton,
|
||||
setCompleted,
|
||||
getMessages,
|
||||
announcePolite,
|
||||
genTitle,
|
||||
setConversation,
|
||||
isAddedRequest,
|
||||
setIsSubmitting,
|
||||
setMessages,
|
||||
queryClient,
|
||||
location.pathname,
|
||||
navigate,
|
||||
],
|
||||
);
|
||||
|
||||
|
|
@ -599,7 +612,7 @@ export default function useEventHandlers({
|
|||
setIsSubmitting(false);
|
||||
return;
|
||||
},
|
||||
[setMessages, paramId, setIsSubmitting, setCompleted, newConversation],
|
||||
[setCompleted, setMessages, paramId, newConversation, setIsSubmitting, getMessages],
|
||||
);
|
||||
|
||||
const abortConversation = useCallback(
|
||||
|
|
@ -698,7 +711,15 @@ export default function useEventHandlers({
|
|||
setIsSubmitting(false);
|
||||
}
|
||||
},
|
||||
[token, setIsSubmitting, finalHandler, cancelHandler, setMessages, newConversation],
|
||||
[
|
||||
finalHandler,
|
||||
newConversation,
|
||||
setIsSubmitting,
|
||||
token,
|
||||
cancelHandler,
|
||||
getMessages,
|
||||
setMessages,
|
||||
],
|
||||
);
|
||||
|
||||
return {
|
||||
|
|
|
|||
|
|
@ -22,8 +22,8 @@ import {
|
|||
getEndpointField,
|
||||
buildDefaultConvo,
|
||||
getDefaultEndpoint,
|
||||
getModelSpecPreset,
|
||||
getDefaultModelSpec,
|
||||
getModelSpecIconURL,
|
||||
updateLastSelectedModel,
|
||||
} from '~/utils';
|
||||
import { useDeleteFilesMutation, useGetEndpointsQuery, useGetStartupConfig } from '~/data-provider';
|
||||
|
|
@ -231,11 +231,7 @@ const useNewConvo = (index = 0) => {
|
|||
(startupConfig.interface?.modelSelect ?? true) !== true) &&
|
||||
defaultModelSpec
|
||||
) {
|
||||
preset = {
|
||||
...defaultModelSpec.preset,
|
||||
iconURL: getModelSpecIconURL(defaultModelSpec),
|
||||
spec: defaultModelSpec.name,
|
||||
} as TConversation;
|
||||
preset = getModelSpecPreset(defaultModelSpec);
|
||||
}
|
||||
|
||||
if (conversation.conversationId === 'new' && !modelsData) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue