mirror of
https://github.com/danny-avila/LibreChat.git
synced 2025-12-26 05:08:50 +01:00
📶 fix: Mobile Stylings (#2639)
* chore: remove unused mobile nav * fix: mobile nav fix for 'more' and 'archive' buttons div * refactor(useTextarea): rewrite handleKeyUp for backwards compatibility refactor(useTextarea): rewrite handleKeyUp for backwards compatibility * experimental: add processing delay to azure streams for better performance/UX * experiemental: adjust gpt-3 azureDelay * fix: perplexity titles
This commit is contained in:
parent
b6d6343f54
commit
3c5fa40435
7 changed files with 49 additions and 105 deletions
|
|
@ -26,11 +26,11 @@ const {
|
|||
createContextHandlers,
|
||||
} = require('./prompts');
|
||||
const { encodeAndFormat } = require('~/server/services/Files/images/encode');
|
||||
const { isEnabled, sleep } = require('~/server/utils');
|
||||
const { handleOpenAIErrors } = require('./tools/util');
|
||||
const spendTokens = require('~/models/spendTokens');
|
||||
const { createLLM, RunManager } = require('./llm');
|
||||
const ChatGPTClient = require('./ChatGPTClient');
|
||||
const { isEnabled } = require('~/server/utils');
|
||||
const { summaryBuffer } = require('./memory');
|
||||
const { runTitleChain } = require('./chains');
|
||||
const { tokenSplit } = require('./document');
|
||||
|
|
@ -1079,11 +1079,8 @@ ${convo}
|
|||
...opts,
|
||||
});
|
||||
|
||||
/* hacky fixes for Mistral AI API:
|
||||
- Re-orders system message to the top of the messages payload, as not allowed anywhere else
|
||||
- If there is only one message and it's a system message, change the role to user
|
||||
*/
|
||||
if (opts.baseURL.includes('https://api.mistral.ai/v1') && modelOptions.messages) {
|
||||
/* Re-orders system message to the top of the messages payload, as not allowed anywhere else */
|
||||
if (opts.baseURL.includes('api.mistral.ai') && modelOptions.messages) {
|
||||
const { messages } = modelOptions;
|
||||
|
||||
const systemMessageIndex = messages.findIndex((msg) => msg.role === 'system');
|
||||
|
|
@ -1094,10 +1091,16 @@ ${convo}
|
|||
}
|
||||
|
||||
modelOptions.messages = messages;
|
||||
}
|
||||
|
||||
if (messages.length === 1 && messages[0].role === 'system') {
|
||||
modelOptions.messages[0].role = 'user';
|
||||
}
|
||||
/* If there is only one message and it's a system message, change the role to user */
|
||||
if (
|
||||
(opts.baseURL.includes('api.mistral.ai') || opts.baseURL.includes('api.perplexity.ai')) &&
|
||||
modelOptions.messages &&
|
||||
modelOptions.messages.length === 1 &&
|
||||
modelOptions.messages[0]?.role === 'system'
|
||||
) {
|
||||
modelOptions.messages[0].role = 'user';
|
||||
}
|
||||
|
||||
if (this.options.addParams && typeof this.options.addParams === 'object') {
|
||||
|
|
@ -1151,6 +1154,7 @@ ${convo}
|
|||
}
|
||||
});
|
||||
|
||||
const azureDelay = this.modelOptions.model?.includes('gpt-4') ? 30 : 17;
|
||||
for await (const chunk of stream) {
|
||||
const token = chunk.choices[0]?.delta?.content || '';
|
||||
intermediateReply += token;
|
||||
|
|
@ -1159,6 +1163,10 @@ ${convo}
|
|||
stream.controller.abort();
|
||||
break;
|
||||
}
|
||||
|
||||
if (this.azure) {
|
||||
await sleep(azureDelay);
|
||||
}
|
||||
}
|
||||
|
||||
if (!UnexpectedRoleError) {
|
||||
|
|
|
|||
|
|
@ -138,6 +138,7 @@ export default function Conversation({ conversation, retainView, toggleNav, isLa
|
|||
onRename={onRename}
|
||||
renameHandler={renameHandler}
|
||||
appendLabel={true}
|
||||
className="group m-1.5 flex w-full cursor-pointer items-center gap-2 rounded p-2.5 text-sm hover:bg-gray-200 focus-visible:bg-gray-200 focus-visible:outline-0 radix-disabled:pointer-events-none radix-disabled:opacity-50 dark:hover:bg-gray-600 dark:focus-visible:bg-gray-600"
|
||||
/>
|
||||
<DeleteButton
|
||||
conversationId={conversationId}
|
||||
|
|
@ -152,7 +153,7 @@ export default function Conversation({ conversation, retainView, toggleNav, isLa
|
|||
conversationId={conversationId}
|
||||
retainView={retainView}
|
||||
shouldArchive={true}
|
||||
icon={<ArchiveIcon className="w-full hover:text-gray-400" />}
|
||||
icon={<ArchiveIcon className="hover:text-gray-400" />}
|
||||
/>
|
||||
</HoverToggle>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ interface RenameButtonProps {
|
|||
renameHandler: (e: MouseEvent<HTMLButtonElement>) => void;
|
||||
onRename: (e: MouseEvent<HTMLButtonElement>) => void;
|
||||
appendLabel?: boolean;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export default function RenameButton({
|
||||
|
|
@ -14,15 +15,13 @@ export default function RenameButton({
|
|||
renameHandler,
|
||||
onRename,
|
||||
appendLabel = false,
|
||||
className = '',
|
||||
}: RenameButtonProps): ReactElement {
|
||||
const localize = useLocalize();
|
||||
const handler = renaming ? onRename : renameHandler;
|
||||
|
||||
return (
|
||||
<button
|
||||
className="group m-1.5 flex w-full cursor-pointer items-center gap-2 rounded p-2.5 text-sm hover:bg-gray-200 focus-visible:bg-gray-200 focus-visible:outline-0 radix-disabled:pointer-events-none radix-disabled:opacity-50 dark:hover:bg-gray-600 dark:focus-visible:bg-gray-600"
|
||||
onClick={handler}
|
||||
>
|
||||
<button className={className} onClick={handler}>
|
||||
{renaming ? (
|
||||
<CheckMark />
|
||||
) : appendLabel ? (
|
||||
|
|
|
|||
|
|
@ -1,73 +0,0 @@
|
|||
import React from 'react';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import type { Dispatch, SetStateAction } from 'react';
|
||||
import { useLocalize, useNewConvo } from '~/hooks';
|
||||
import store from '~/store';
|
||||
|
||||
export default function MobileNav({
|
||||
setNavVisible,
|
||||
}: {
|
||||
setNavVisible: Dispatch<SetStateAction<boolean>>;
|
||||
}) {
|
||||
const localize = useLocalize();
|
||||
const { newConversation } = useNewConvo(0);
|
||||
const conversation = useRecoilValue(store.conversationByIndex(0));
|
||||
const { title = 'New Chat' } = conversation || {};
|
||||
|
||||
return (
|
||||
<div className="text-token-primary border-token-border-medium bg-token-surface-primary sticky top-0 z-10 flex min-h-[40px] items-center border-b bg-white dark:bg-gray-800 dark:text-white md:hidden">
|
||||
<button
|
||||
type="button"
|
||||
data-testid="mobile-header-new-chat-button"
|
||||
className="inline-flex h-10 w-10 items-center justify-center rounded-md hover:text-gray-800 focus:outline-none focus:ring-2 focus:ring-inset focus:ring-white active:opacity-50 dark:hover:text-white"
|
||||
onClick={() =>
|
||||
setNavVisible((prev) => {
|
||||
localStorage.setItem('navVisible', JSON.stringify(!prev));
|
||||
return !prev;
|
||||
})
|
||||
}
|
||||
>
|
||||
<span className="sr-only">{localize('com_nav_open_sidebar')}</span>
|
||||
<svg
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
className="icon-md"
|
||||
>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
d="M3 8C3 7.44772 3.44772 7 4 7H20C20.5523 7 21 7.44772 21 8C21 8.55228 20.5523 9 20 9H4C3.44772 9 3 8.55228 3 8ZM3 16C3 15.4477 3.44772 15 4 15H14C14.5523 15 15 15.4477 15 16C15 16.5523 14.5523 17 14 17H4C3.44772 17 3 16.5523 3 16Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
<h1 className="flex-1 text-center text-base font-normal">
|
||||
{title || localize('com_ui_new_chat')}
|
||||
</h1>
|
||||
<button
|
||||
type="button"
|
||||
className="inline-flex h-10 w-10 items-center justify-center rounded-md hover:text-gray-800 focus:outline-none focus:ring-2 focus:ring-inset focus:ring-white active:opacity-50 dark:hover:text-white"
|
||||
onClick={() => newConversation()}
|
||||
>
|
||||
<svg
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
className="icon-md"
|
||||
>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
d="M16.7929 2.79289C18.0118 1.57394 19.9882 1.57394 21.2071 2.79289C22.4261 4.01184 22.4261 5.98815 21.2071 7.20711L12.7071 15.7071C12.5196 15.8946 12.2652 16 12 16H9C8.44772 16 8 15.5523 8 15V12C8 11.7348 8.10536 11.4804 8.29289 11.2929L16.7929 2.79289ZM19.7929 4.20711C19.355 3.7692 18.645 3.7692 18.2071 4.2071L10 12.4142V14H11.5858L19.7929 5.79289C20.2308 5.35499 20.2308 4.64501 19.7929 4.20711ZM6 5C5.44772 5 5 5.44771 5 6V18C5 18.5523 5.44772 19 6 19H18C18.5523 19 19 18.5523 19 18V14C19 13.4477 19.4477 13 20 13C20.5523 13 21 13.4477 21 14V18C21 19.6569 19.6569 21 18 21H6C4.34315 21 3 19.6569 3 18V6C3 4.34314 4.34315 3 6 3H10C10.5523 3 11 3.44771 11 4C11 4.55228 10.5523 5 10 5H6Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -2,7 +2,6 @@ export * from './ExportConversation';
|
|||
export * from './SettingsTabs/';
|
||||
export { default as ClearConvos } from './ClearConvos';
|
||||
export { default as Logout } from './Logout';
|
||||
export { default as MobileNav } from './MobileNav';
|
||||
export { default as Nav } from './Nav';
|
||||
export { default as NavLink } from './NavLink';
|
||||
export { default as NavLinks } from './NavLinks';
|
||||
|
|
|
|||
|
|
@ -14,6 +14,11 @@ import store from '~/store';
|
|||
|
||||
type KeyEvent = KeyboardEvent<HTMLTextAreaElement>;
|
||||
|
||||
const keyMap = {
|
||||
50: '2',
|
||||
192: '@',
|
||||
};
|
||||
|
||||
export default function useTextarea({
|
||||
textAreaRef,
|
||||
submitButtonRef,
|
||||
|
|
@ -138,24 +143,30 @@ export default function useTextarea({
|
|||
|
||||
const handleKeyUp = useCallback(
|
||||
(e: KeyEvent) => {
|
||||
let isMention = false;
|
||||
if (e.key === '@' || e.key === '2') {
|
||||
const text = textAreaRef.current?.value;
|
||||
isMention = !!(text && text[text.length - 1] === '@');
|
||||
let normalizedKey = e.key;
|
||||
|
||||
if (!normalizedKey || normalizedKey === 'Unidentified') {
|
||||
normalizedKey = keyMap[e.keyCode] || normalizedKey;
|
||||
}
|
||||
|
||||
if (isMention) {
|
||||
const startPos = textAreaRef.current?.selectionStart;
|
||||
const isAtStart = startPos === 1;
|
||||
const isPrecededBySpace =
|
||||
startPos && textAreaRef.current?.value.charAt(startPos - 2) === ' ';
|
||||
|
||||
if (isAtStart || isPrecededBySpace) {
|
||||
setShowMentionPopover(true);
|
||||
} else {
|
||||
setShowMentionPopover(false);
|
||||
}
|
||||
if (normalizedKey !== '@' && normalizedKey !== '2') {
|
||||
return;
|
||||
}
|
||||
|
||||
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],
|
||||
);
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import { useGetSearchEnabledQuery } from 'librechat-data-provider/react-query';
|
|||
import type { ContextType } from '~/common';
|
||||
import { useAuthContext, useAssistantsMap, useFileMap } from '~/hooks';
|
||||
import { AssistantsMapContext, FileMapContext } from '~/Providers';
|
||||
import { Nav, MobileNav } from '~/components/Nav';
|
||||
import { Nav } from '~/components/Nav';
|
||||
import store from '~/store';
|
||||
|
||||
export default function Root() {
|
||||
|
|
@ -45,7 +45,6 @@ export default function Root() {
|
|||
<div className="relative z-0 flex h-full w-full overflow-hidden">
|
||||
<Nav navVisible={navVisible} setNavVisible={setNavVisible} />
|
||||
<div className="relative flex h-full max-w-full flex-1 flex-col overflow-hidden">
|
||||
<MobileNav setNavVisible={setNavVisible} />
|
||||
<Outlet context={{ navVisible, setNavVisible } satisfies ContextType} />
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue