🎨 feat: Create Avatars of Initials Locally (#1869)

This commit is contained in:
Danny Avila 2024-02-23 09:23:29 -05:00 committed by GitHub
parent 4012dea4ab
commit 5f6d1f3db0
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 477 additions and 32 deletions

View file

@ -27,6 +27,8 @@
},
"homepage": "https://librechat.ai",
"dependencies": {
"@dicebear/collection": "^7.0.4",
"@dicebear/core": "^7.0.4",
"@headlessui/react": "^1.7.13",
"@radix-ui/react-accordion": "^1.1.2",
"@radix-ui/react-alert-dialog": "^1.0.2",

View file

@ -12,6 +12,7 @@ import {
CustomMinimalIcon,
} from '~/components/svg';
import { useAuthContext } from '~/hooks/AuthContext';
import useAvatar from '~/hooks/Messages/useAvatar';
import { IconProps } from '~/common';
import { cn } from '~/utils';
@ -27,6 +28,8 @@ const Icon: React.FC<IconProps> = (props) => {
assistantName,
} = props;
const avatarSrc = useAvatar(user);
if (isCreatedByUser) {
const username = user?.name || 'User';
@ -39,14 +42,7 @@ const Icon: React.FC<IconProps> = (props) => {
}}
className={cn('relative flex items-center justify-center', props.className ?? '')}
>
<img
className="rounded-sm"
src={
user?.avatar ||
`https://api.dicebear.com/6.x/initials/svg?seed=${username}&fontFamily=Verdana&fontSize=36`
}
alt="avatar"
/>
<img className="rounded-sm" src={user?.avatar || avatarSrc} alt="avatar" />
</div>
);
} else {

View file

@ -7,6 +7,7 @@ import { useGetUserBalance, useGetStartupConfig } from 'librechat-data-provider/
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 { useLocalize } from '~/hooks';
@ -28,9 +29,12 @@ function NavLinks() {
const [showSettings, setShowSettings] = useState(false);
const [showFiles, setShowFiles] = useRecoilState(store.showFiles);
let conversation;
const activeConvo = useRecoilValue(store.conversationByIndex(0));
const globalConvo = useRecoilValue(store.conversation) ?? ({} as TConversation);
const avatarSrc = useAvatar(user);
let conversation: TConversation | null | undefined;
if (location.state?.from?.pathname.includes('/chat')) {
conversation = globalConvo;
} else {
@ -68,16 +72,7 @@ function NavLinks() {
>
<div className="-ml-0.9 -mt-0.8 h-8 w-7 flex-shrink-0">
<div className="relative flex">
<img
className="rounded-full"
src={
user?.avatar ||
`https://api.dicebear.com/6.x/initials/svg?seed=${
user?.name || 'User'
}&fontFamily=Verdana&fontSize=36`
}
alt=""
/>
<img className="rounded-full" src={user?.avatar || avatarSrc} alt="" />
</div>
</div>
<div

View file

@ -1,3 +1,4 @@
export { default as useAvatar } from './useAvatar';
export { default as useProgress } from './useProgress';
export { default as useMessageHelpers } from './useMessageHelpers';
export { default as useMessageScrolling } from './useMessageScrolling';

View file

@ -0,0 +1,50 @@
import { useState, useEffect } from 'react';
import { createAvatar } from '@dicebear/core';
import { initials } from '@dicebear/collection';
import type { TUser } from 'librechat-data-provider';
const useAvatar = (user: TUser | undefined) => {
const [avatarSrc, setAvatarSrc] = useState('');
useEffect(() => {
if (avatarSrc.length) {
return;
}
if (user?.avatar) {
return;
}
if (!user?.username) {
return;
}
const generateAvatar = async () => {
if (!user) {
return;
}
const { username } = user;
const avatar = createAvatar(initials, {
seed: username,
fontFamily: ['Verdana'],
fontSize: 36,
});
try {
const avatarDataUri = await avatar.toDataUri();
setAvatarSrc(avatarDataUri);
} catch (error) {
console.error('Failed to generate avatar:', error);
setAvatarSrc('');
}
};
generateAvatar();
}, [user, avatarSrc.length]);
return avatarSrc;
};
export default useAvatar;