🪟 fix+feat: General UI Enhancements (#2619)

* feat: Minor design changes to mimic OpenAI's latest login page

* fix: Optimize ThemeSelector for mobile

* fix: Use a svg for the logo for transperency in dark mode

* feat: Update styles for Registration

* feat: Update error colors for login & registration

* fix: remove medium font

* wip: Dropdown menu

* feat: Update dropdown to match ChatGPT

* feat: Improve rounding and padding

* feat: Add UI Updates to RequestPasswordReset, PasswordRest and increase width for theme dropdown

* fix: Modify the My Files modal's width to not touch the screen

* feat: fix scrolling for dropdown, and make border width lighter

* feat: Match popup menu design to OpenAI (p1/2)

* fix+feat: fix dark mode, add user email, add lighter borders

* fix: Add border color on focus of chat input.

* feat: Move Export Conversation to a seperate button (testing)

* fix: Properly center Login, Registration, Reset Password Flow

* fix: Border colors on dark mode for settings modal

* feat: Improve wording for settings menu

* fix: Optimize settings modal for mobile and fix height for modal

* feat: Optimize for desktop

* fix: make TooltipTrigger asChild of button, improve settings mobile responsiveness

* feat: Handle dropdowns properly
TODO: Make height dynamic, fix dark mode colors

* fix: input styles
fix: make endpoint icon smaller

* feat: Update UI to Match ChatGPT Style

- Updated the dropdown styles to match the aesthetic of ChatGPT.
- Decreased spacing within the conversation area for cleanliness.
- Replaced the current archive icon with the ChatGPT's icon.

* fix: fix colors for EditMenuButton & ArchiveButton for dark mode and light mode

* fix: ui fixes

* fix: Fix Conversation UI Bugs

* fix: transparency of HoverToggle to make buttons not visible

* fix: dark mode HoverToggle & compress menu item spacing

* fix: responsiveness of export icon

* fix: first mentionitem is set to always be highlighted

* fix: improve hover state to text instead of bg

* feat: Update icons to ChatGPT Style

* fix: dark mode hover for PanelFileCell

* fix: change navlinks z-index to 100

* fix: hover states for DataTable

* feat: Move ExportButton to seperate component

* chore: remove unused imports
This commit is contained in:
Anirudh 2024-05-10 03:16:16 +05:30 committed by GitHub
parent d73ea8e1f2
commit 8f20fb28e5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
43 changed files with 716 additions and 469 deletions

View file

@ -9,7 +9,7 @@ const Logout = forwardRef(() => {
return (
<button
className="flex w-full cursor-pointer items-center gap-3 px-3 py-3 text-sm text-black transition-colors duration-200 hover:bg-gray-100 dark:text-white dark:hover:bg-gray-700"
className="group group flex w-full cursor-pointer items-center gap-2 rounded p-2.5 text-sm transition-colors duration-200 hover:bg-gray-500/10 focus:ring-0 dark:text-white dark:hover:bg-gray-600"
onClick={() => logout()}
>
<LogOutIcon />

View file

@ -6,17 +6,21 @@ interface Props {
text: string;
clickHandler?: () => void;
className?: string;
disabled?: boolean;
}
const NavLink: FC<Props> = forwardRef<HTMLAnchorElement, Props>((props, ref) => {
const { svg, text, clickHandler, className = '' } = props;
const { svg, text, clickHandler, disabled, className = '' } = props;
const defaultProps: {
className: string;
onClick?: () => void;
} = {
className: cn(
'flex cursor-pointer items-center gap-3 rounded-md py-3 px-3 text-sm text-white transition-colors duration-200 hover:bg-gray-500/10',
'flex gap-2 rounded p-2.5 text-sm cursor-pointer focus:ring-0 group items-center transition-colors duration-200 hover:bg-gray-500/10 dark:text-white dark:hover:bg-gray-600',
className,
{
'opacity-50 pointer-events-none': disabled,
},
),
};

View file

@ -1,6 +1,6 @@
import { useLocation } from 'react-router-dom';
import { Fragment, useState, memo } from 'react';
import { Download, FileText } from 'lucide-react';
import { FileText } from 'lucide-react';
import { Menu, Transition } from '@headlessui/react';
import { useRecoilValue, useRecoilState } from 'recoil';
import { useGetUserBalance, useGetStartupConfig } from 'librechat-data-provider/react-query';
@ -8,7 +8,6 @@ import type { TConversation } from 'librechat-data-provider';
import FilesView from '~/components/Chat/Input/Files/FilesView';
import { useAuthContext } from '~/hooks/AuthContext';
import useAvatar from '~/hooks/Messages/useAvatar';
import { ExportModal } from './ExportConversation';
import { LinkIcon, GearIcon } from '~/components';
import { UserIcon } from '~/components/svg';
import { useLocalize } from '~/hooks';
@ -26,7 +25,6 @@ function NavLinks() {
const balanceQuery = useGetUserBalance({
enabled: !!isAuthenticated && startupConfig?.checkBalance,
});
const [showExports, setShowExports] = useState(false);
const [showSettings, setShowSettings] = useState(false);
const [showFiles, setShowFiles] = useRecoilState(store.showFiles);
@ -42,34 +40,15 @@ function NavLinks() {
conversation = activeConvo;
}
const exportable =
conversation &&
conversation.conversationId &&
conversation.conversationId !== 'new' &&
conversation.conversationId !== 'search';
const clickHandler = () => {
if (exportable) {
setShowExports(true);
}
};
return (
<>
<Menu as="div" className="group relative">
{({ open }) => (
<>
{startupConfig?.checkBalance &&
balanceQuery.data &&
!isNaN(parseFloat(balanceQuery.data)) && (
<div className="m-1 ml-3 whitespace-nowrap text-left text-sm text-black dark:text-gray-200">
{`Balance: ${parseFloat(balanceQuery.data).toFixed(2)}`}
</div>
)}
<Menu.Button
className={cn(
'group-ui-open:bg-gray-100 dark:group-ui-open:bg-gray-700 duration-350 mt-text-sm mb-1 flex h-11 w-full items-center gap-2 rounded-lg px-3 py-3 text-sm transition-colors hover:bg-gray-100 dark:hover:bg-gray-700',
open ? 'bg-gray-100 dark:bg-gray-700' : '',
'group-ui-open:bg-gray-100 dark:group-ui-open:bg-gray-700 duration-350 mt-text-sm flex h-auto w-full items-center gap-2 rounded-lg p-2 text-sm transition-colors hover:bg-gray-100 dark:hover:bg-gray-800',
open ? 'bg-gray-100 dark:bg-gray-800' : '',
)}
data-testid="nav-user"
>
@ -93,7 +72,7 @@ function NavLinks() {
</div>
</div>
<div
className="mt-2 grow overflow-hidden text-ellipsis whitespace-nowrap text-left text-black dark:text-white"
className="mt-2 grow overflow-hidden text-ellipsis whitespace-nowrap text-left text-black dark:text-gray-100"
style={{ marginTop: '0', marginLeft: '0' }}
>
{user?.name || user?.username || localize('com_nav_user')}
@ -109,24 +88,23 @@ function NavLinks() {
leaveFrom="translate-y-0 opacity-100"
leaveTo="translate-y-2 opacity-0"
>
<Menu.Items className="absolute bottom-full left-0 z-20 mb-1 mt-1 w-full translate-y-0 overflow-hidden rounded-lg bg-white py-1.5 opacity-100 outline-none dark:bg-gray-800">
<Menu.Items className="absolute bottom-full left-0 z-[100] mb-1 mt-1 w-full translate-y-0 overflow-hidden rounded-lg border border-gray-300 bg-white p-1.5 opacity-100 shadow-lg outline-none dark:border-gray-600 dark:bg-gray-700">
<div className="text-token-text-secondary ml-3 mr-2 py-2 text-sm" role="none">
{user?.email || localize('com_nav_user')}
</div>
<div className="my-1.5 h-px bg-black/10 dark:bg-white/10" role="none" />
{startupConfig?.checkBalance &&
balanceQuery.data &&
!isNaN(parseFloat(balanceQuery.data)) && (
<>
<div className="text-token-text-secondary ml-3 mr-2 py-2 text-sm">
{`Balance: ${parseFloat(balanceQuery.data).toFixed(2)}`}
</div>
<div className="my-1.5 h-px bg-black/10 dark:bg-white/10" role="none" />
</>
)}
<Menu.Item as="div">
<NavLink
className={cn(
'flex w-full cursor-pointer items-center gap-3 rounded-none px-3 py-3 text-sm text-black transition-colors duration-200 hover:bg-gray-100 dark:text-white dark:hover:bg-gray-700',
exportable
? 'cursor-pointer text-black dark:text-white'
: 'cursor-not-allowed text-black/50 dark:text-white/50',
)}
svg={() => <Download size={16} />}
text={localize('com_nav_export_conversation')}
clickHandler={clickHandler}
/>
</Menu.Item>
<div className="my-1 h-px bg-black/20 dark:bg-white/20" role="none" />
<Menu.Item as="div">
<NavLink
className="flex w-full cursor-pointer items-center gap-3 rounded-none px-3 py-3 text-sm text-black transition-colors duration-200 hover:bg-gray-100 dark:text-white dark:hover:bg-gray-700"
svg={() => <FileText className="icon-md" />}
text={localize('com_nav_my_files')}
clickHandler={() => setShowFiles(true)}
@ -135,7 +113,6 @@ function NavLinks() {
{startupConfig?.helpAndFaqURL !== '/' && (
<Menu.Item as="div">
<NavLink
className="flex w-full cursor-pointer items-center gap-3 rounded-none px-3 py-3 text-sm text-black transition-colors duration-200 hover:bg-gray-100 dark:text-white dark:hover:bg-gray-700"
svg={() => <LinkIcon />}
text={localize('com_nav_help_faq')}
clickHandler={() => window.open(startupConfig?.helpAndFaqURL, '_blank')}
@ -144,13 +121,12 @@ function NavLinks() {
)}
<Menu.Item as="div">
<NavLink
className="flex w-full cursor-pointer items-center gap-3 rounded-none px-3 py-3 text-sm text-black transition-colors duration-200 hover:bg-gray-100 dark:text-white dark:hover:bg-gray-700"
svg={() => <GearIcon className="icon-md" />}
text={localize('com_nav_settings')}
clickHandler={() => setShowSettings(true)}
/>
</Menu.Item>
<div className="my-1 h-px bg-black/20 bg-white/20" role="none" />
<div className="my-1.5 h-px bg-black/10 dark:bg-white/10" role="none" />
<Menu.Item as="div">
<Logout />
</Menu.Item>
@ -159,9 +135,6 @@ function NavLinks() {
</>
)}
</Menu>
{showExports && (
<ExportModal open={showExports} onOpenChange={setShowExports} conversation={conversation} />
)}
{showFiles && <FilesView open={showFiles} onOpenChange={setShowFiles} />}
{showSettings && <Settings open={showSettings} onOpenChange={setShowSettings} />}
</>

View file

@ -16,8 +16,8 @@ export default function Settings({ open, onOpenChange }: TDialogProps) {
<Dialog open={open} onOpenChange={onOpenChange}>
<DialogContent
className={cn(
'shadow-2xl md:min-h-[373px] md:w-[680px]',
isSmallScreen ? 'top-20 -translate-y-0' : '',
'overflow-hidden shadow-2xl md:min-h-[373px] md:w-[680px]',
isSmallScreen ? 'top-5 -translate-y-0' : '',
)}
>
<DialogHeader>
@ -25,19 +25,19 @@ export default function Settings({ open, onOpenChange }: TDialogProps) {
{localize('com_nav_settings')}
</DialogTitle>
</DialogHeader>
<div className="px-6">
<div className="max-h-[373px] overflow-auto px-6 md:min-h-[373px] md:w-[680px]">
<Tabs.Root
defaultValue={SettingsTabValues.GENERAL}
className="flex flex-col gap-10 md:flex-row"
orientation="vertical"
orientation="horizontal"
>
<Tabs.List
aria-label="Settings"
role="tablist"
aria-orientation="vertical"
aria-orientation="horizontal"
className={cn(
'min-w-auto -ml-[8px] flex flex-shrink-0 flex-col',
isSmallScreen ? 'flex-row rounded-lg bg-gray-200 p-1 dark:bg-gray-700' : '',
'min-w-auto max-w-auto -ml-[8px] flex flex-shrink-0 flex-col flex-wrap overflow-auto sm:max-w-none',
isSmallScreen ? 'flex-row rounded-lg bg-gray-200 p-1 dark:bg-gray-800' : '',
)}
style={{ outline: 'none' }}
>
@ -112,11 +112,13 @@ export default function Settings({ open, onOpenChange }: TDialogProps) {
{localize('com_nav_setting_account')}
</Tabs.Trigger>
</Tabs.List>
<General />
<Messages />
<Beta />
<Data />
<Account />
<div className="h-screen max-h-[373px] overflow-auto sm:w-full sm:max-w-none">
<General />
<Messages />
<Beta />
<Data />
<Account />
</div>
</Tabs.Root>
</div>
</DialogContent>

View file

@ -22,10 +22,10 @@ function Account({ onCheckedChange }: { onCheckedChange?: (value: boolean) => vo
<Tabs.Content
value={SettingsTabValues.ACCOUNT}
role="tabpanel"
className="w-full md:min-h-[300px]"
className="w-full md:min-h-[271px]"
>
<div className="flex flex-col gap-3 text-sm text-gray-600 dark:text-gray-50">
<div className="border-b pb-3 last-of-type:border-b-0 dark:border-gray-700">
<div className="border-b pb-3 last-of-type:border-b-0 dark:border-gray-600">
<Avatar />
</div>
<div className="flex items-center justify-between">
@ -39,7 +39,7 @@ function Account({ onCheckedChange }: { onCheckedChange?: (value: boolean) => vo
/>
</div>
</div>
<div className="border-b pb-3 last-of-type:border-b-0 dark:border-gray-700"></div>
<div className="border-b pb-3 last-of-type:border-b-0 dark:border-gray-600"></div>
</Tabs.Content>
);
}

View file

@ -9,13 +9,13 @@ function Beta() {
<Tabs.Content
value={SettingsTabValues.BETA}
role="tabpanel"
className="w-full md:min-h-[300px]"
className="w-full md:min-h-[271px]"
>
<div className="flex flex-col gap-3 text-sm text-gray-600 dark:text-gray-50">
<div className="border-b pb-3 last-of-type:border-b-0 dark:border-gray-700">
<div className="border-b pb-3 last-of-type:border-b-0 dark:border-gray-600">
<ModularChat />
</div>
<div className="border-b pb-3 last-of-type:border-b-0 dark:border-gray-700">
<div className="border-b pb-3 last-of-type:border-b-0 dark:border-gray-600">
<LaTeXParsing />
</div>
</div>

View file

@ -100,18 +100,18 @@ function Data() {
<Tabs.Content
value={SettingsTabValues.DATA}
role="tabpanel"
className="w-full md:min-h-[300px]"
className="w-full md:min-h-[271px]"
ref={dataTabRef}
>
<div className="flex flex-col gap-3 text-sm text-gray-600 dark:text-gray-50">
<div className="border-b pb-3 last-of-type:border-b-0 dark:border-gray-700">
<div className="border-b pb-3 last-of-type:border-b-0 dark:border-gray-600">
<ImportConversations />
</div>
<div className="border-b pb-3 last-of-type:border-b-0 dark:border-gray-700">
<div className="border-b pb-3 last-of-type:border-b-0 dark:border-gray-600">
<RevokeKeysButton all={true} />
</div>
<div className="border-b pb-3 last-of-type:border-b-0 dark:border-gray-700">
<div className="border-b pb-3 last-of-type:border-b-0 dark:border-gray-600">
<ClearChatsButton
confirmClear={confirmClearConvos}
onClick={clearConvos}

View file

@ -33,7 +33,9 @@ export const ThemeSelector = ({
value={theme}
onChange={onChange}
options={themeOptions}
width={150}
width={220}
position={'left'}
maxHeight="200px"
testId="theme-selector"
/>
</div>
@ -103,7 +105,13 @@ export const LangSelector = ({
return (
<div className="flex items-center justify-between">
<div> {localize('com_nav_language')} </div>
<Dropdown value={langcode} onChange={onChange} options={languageOptions} />
<Dropdown
value={langcode}
onChange={onChange}
position={'left'}
maxHeight="271px"
options={languageOptions}
/>
</div>
);
};
@ -142,26 +150,26 @@ function General() {
<Tabs.Content
value={SettingsTabValues.GENERAL}
role="tabpanel"
className="w-full md:min-h-[300px]"
className="w-full md:min-h-[271px]"
ref={contentRef}
>
<div className="flex flex-col gap-3 text-sm text-gray-600 dark:text-gray-50">
<div className="border-b pb-3 last-of-type:border-b-0 dark:border-gray-700">
<div className="border-b pb-3 last-of-type:border-b-0 dark:border-gray-600">
<ThemeSelector theme={theme} onChange={changeTheme} />
</div>
<div className="border-b pb-3 last-of-type:border-b-0 dark:border-gray-700">
<div className="border-b pb-3 last-of-type:border-b-0 dark:border-gray-600">
<LangSelector langcode={selectedLang} onChange={changeLang} />
</div>
<div className="border-b pb-3 last-of-type:border-b-0 dark:border-gray-700">
<div className="border-b pb-3 last-of-type:border-b-0 dark:border-gray-600">
<AutoScrollSwitch />
</div>
<div className="border-b pb-3 last-of-type:border-b-0 dark:border-gray-700">
<div className="border-b pb-3 last-of-type:border-b-0 dark:border-gray-600">
<HideSidePanelSwitch />
</div>
<div className="border-b pb-3 last-of-type:border-b-0 dark:border-gray-700">
<div className="border-b pb-3 last-of-type:border-b-0 dark:border-gray-600">
<ArchivedChats />
</div>
{/* <div className="border-b pb-3 last-of-type:border-b-0 dark:border-gray-700">
{/* <div className="border-b pb-3 last-of-type:border-b-0 dark:border-gray-600">
</div> */}
</div>
</Tabs.Content>

View file

@ -18,7 +18,7 @@ export const ForkSettings = () => {
return (
<>
<div className="border-b pb-3 last-of-type:border-b-0 dark:border-gray-700">
<div className="border-b pb-3 last-of-type:border-b-0 dark:border-gray-600">
<div className="flex items-center justify-between">
<div> {localize('com_ui_fork_change_default')} </div>
<Dropdown
@ -26,11 +26,13 @@ export const ForkSettings = () => {
onChange={setForkSetting}
options={forkOptions}
width={200}
position={'left'}
maxHeight="199px"
testId="fork-setting-dropdown"
/>
</div>
</div>
<div className="border-b pb-3 last-of-type:border-b-0 dark:border-gray-700">
<div className="border-b pb-3 last-of-type:border-b-0 dark:border-gray-600">
<div className="flex items-center justify-between">
<div> {localize('com_ui_fork_default')} </div>
<Switch
@ -42,7 +44,7 @@ export const ForkSettings = () => {
/>
</div>
</div>
<div className="border-b pb-3 last-of-type:border-b-0 dark:border-gray-700">
<div className="border-b pb-3 last-of-type:border-b-0 dark:border-gray-600">
<div className="flex items-center justify-between">
<div> {localize('com_ui_fork_split_target_setting')} </div>
<Switch

View file

@ -7,16 +7,12 @@ import { ForkSettings } from './ForkSettings';
function Messages() {
return (
<Tabs.Content
value={SettingsTabValues.MESSAGES}
role="tabpanel"
className="w-full md:min-h-[300px]"
>
<Tabs.Content value={SettingsTabValues.MESSAGES} role="tabpanel" className="md: w-full">
<div className="flex flex-col gap-3 text-sm text-gray-600 dark:text-gray-50">
<div className="border-b pb-3 last-of-type:border-b-0 dark:border-gray-700">
<div className="border-b pb-3 last-of-type:border-b-0 dark:border-gray-600">
<SendMessageKeyEnter />
</div>
<div className="border-b pb-3 last-of-type:border-b-0 dark:border-gray-700">
<div className="border-b pb-3 last-of-type:border-b-0 dark:border-gray-600">
<ShowCodeSwitch />
</div>
<ForkSettings />