diff --git a/client/src/common/types.ts b/client/src/common/types.ts index a2ab5c88db..2fc9164b71 100644 --- a/client/src/common/types.ts +++ b/client/src/common/types.ts @@ -178,3 +178,17 @@ export type TUserContext = { export type TAuthConfig = { loginRedirect: string; }; + +export type IconProps = { + size?: number; + isCreatedByUser?: boolean; + button?: boolean; + model?: string; + message?: boolean; + className?: string; + endpoint?: string | null; + error?: boolean; + chatGptLabel?: string; + modelLabel?: string; + jailbreak?: boolean; +}; diff --git a/client/src/components/Conversations/Conversation.jsx b/client/src/components/Conversations/Conversation.jsx index f301444c9c..f4aaf392fa 100644 --- a/client/src/components/Conversations/Conversation.jsx +++ b/client/src/components/Conversations/Conversation.jsx @@ -3,7 +3,7 @@ import { useRecoilState, useSetRecoilState } from 'recoil'; import { useUpdateConversationMutation } from 'librechat-data-provider'; import RenameButton from './RenameButton'; import DeleteButton from './DeleteButton'; -import ConvoIcon from '../svg/ConvoIcon'; +import { MinimalIcon } from '~/components/Endpoints'; import { useConversations, useConversation } from '~/hooks'; import store from '~/store'; @@ -66,6 +66,14 @@ export default function Conversation({ conversation, retainView }) { updateConvoMutation.mutate({ conversationId, title: titleInput }); }; + const icon = MinimalIcon({ + size: 20, + endpoint: conversation.endpoint, + model: conversation.model, + error: false, + className: 'mr-0', + }); + useEffect(() => { if (updateConvoMutation.isSuccess) { refreshConversations(); @@ -97,7 +105,7 @@ export default function Conversation({ conversation, retainView }) { return ( clickHandler()} {...aProps}> - + {icon}
{renaming === true ? ( = (props) => { + const { size = 30, isCreatedByUser, button, model = true, endpoint, error, jailbreak } = props; + + const { user } = useAuthContext(); + + if (isCreatedByUser) { + const username = user?.name || 'User'; + + return ( +
+ avatar +
+ ); + } else { + const endpointIcons = { + azureOpenAI: { + icon: , + bg: 'linear-gradient(0.375turn, #61bde2, #4389d0)', + name: 'ChatGPT', + }, + openAI: { + icon: , + bg: + typeof model === 'string' && model.toLowerCase().startsWith('gpt-4') + ? '#AB68FF' + : '#19C37D', + name: 'ChatGPT', + }, + gptPlugins: { + icon: , + bg: `rgba(69, 89, 164, ${button ? 0.75 : 1})`, + name: 'Plugins', + }, + google: { icon: Palm Icon, name: 'PaLM2' }, + anthropic: { icon: , bg: '#d09a74', name: 'Claude' }, + bingAI: { + icon: jailbreak ? ( + Bing Icon + ) : ( + Sydney Icon + ), + name: jailbreak ? 'Sydney' : 'BingAI', + }, + chatGPTBrowser: { + icon: , + bg: + typeof model === 'string' && model.toLowerCase().startsWith('gpt-4') + ? '#AB68FF' + : `rgba(0, 163, 255, ${button ? 0.75 : 1})`, + name: 'ChatGPT', + }, + null: { icon: , bg: 'grey', name: 'N/A' }, + default: { icon: , bg: 'grey', name: 'UNKNOWN' }, + }; + + const { icon, bg, name } = endpointIcons[endpoint] || endpointIcons.default; + + return ( +
+ {icon} + {error && ( + + ! + + )} +
+ ); + } +}; + +export default Icon; diff --git a/client/src/components/Endpoints/MinimalIcon.tsx b/client/src/components/Endpoints/MinimalIcon.tsx new file mode 100644 index 0000000000..d35161e560 --- /dev/null +++ b/client/src/components/Endpoints/MinimalIcon.tsx @@ -0,0 +1,58 @@ +import React from 'react'; +import { + AzureMinimalIcon, + OpenAIMinimalIcon, + ChatGPTMinimalIcon, + PluginMinimalIcon, + BingAIMinimalIcon, + PaLMinimalIcon, + AnthropicMinimalIcon, +} from '~/components/svg'; +import { cn } from '~/utils'; +import { IconProps } from '~/common'; + +const MinimalIcon: React.FC = (props) => { + const { size = 30, error } = props; + + let endpoint = 'default'; // Default value for endpoint + + if (typeof props.endpoint === 'string') { + endpoint = props.endpoint; + } + + const endpointIcons = { + azureOpenAI: { icon: , name: props.chatGptLabel || 'ChatGPT' }, + openAI: { icon: , name: props.chatGptLabel || 'ChatGPT' }, + gptPlugins: { icon: , name: 'Plugins' }, + google: { icon: , name: props.modelLabel || 'PaLM2' }, + anthropic: { icon: , name: props.modelLabel || 'Claude' }, + bingAI: { icon: , name: 'BingAI' }, + chatGPTBrowser: { icon: , name: 'ChatGPT' }, + default: { icon: , name: 'UNKNOWN' }, + }; + + const { icon, name } = endpointIcons[endpoint]; + + return ( +
+ {icon} + {error && ( + + ! + + )} +
+ ); +}; + +export default MinimalIcon; diff --git a/client/src/components/Endpoints/getIcon.jsx b/client/src/components/Endpoints/getIcon.jsx deleted file mode 100644 index 5480812ec5..0000000000 --- a/client/src/components/Endpoints/getIcon.jsx +++ /dev/null @@ -1,114 +0,0 @@ -import { Plugin, GPTIcon, AnthropicIcon } from '~/components/svg'; -import { useAuthContext } from '~/hooks'; -import { cn } from '~/utils'; - -const getIcon = (props) => { - const { size = 30, isCreatedByUser, button, model, message = true } = props; - // eslint-disable-next-line react-hooks/rules-of-hooks - const { user } = useAuthContext(); - - if (isCreatedByUser) { - return ( -
- avatar -
- ); - } else if (!isCreatedByUser) { - const { endpoint, error } = props; - - let icon, bg, name; - if (endpoint === 'azureOpenAI') { - const { chatGptLabel } = props; - icon = ; - bg = 'linear-gradient(0.375turn, #61bde2, #4389d0)'; - name = chatGptLabel || 'ChatGPT'; - } else if (endpoint === 'openAI' || (endpoint === 'gptPlugins' && message)) { - const { chatGptLabel } = props; - icon = ; - bg = - model && model.toLowerCase().startsWith('gpt-4') - ? '#AB68FF' - : chatGptLabel - ? `rgba(16, 163, 127, ${button ? 0.75 : 1})` - : `rgba(16, 163, 127, ${button ? 0.75 : 1})`; - name = chatGptLabel || 'ChatGPT'; - } else if (endpoint === 'gptPlugins' && !message) { - icon = ; - bg = `rgba(69, 89, 164, ${button ? 0.75 : 1})`; - name = 'Plugins'; - } else if (endpoint === 'google') { - const { modelLabel } = props; - icon = Palm Icon; - name = modelLabel || 'PaLM2'; - } else if (endpoint === 'anthropic') { - const { modelLabel } = props; - icon = ; - bg = '#d09a74'; - name = modelLabel || 'Claude'; - } else if (endpoint === 'bingAI') { - const { jailbreak } = props; - if (jailbreak) { - icon = Bing Icon; - name = 'Sydney'; - } else { - icon = Sydney Icon; - name = 'BingAI'; - } - } else if (endpoint === 'chatGPTBrowser') { - icon = ; - bg = - model && model.toLowerCase().startsWith('gpt-4') - ? '#AB68FF' - : `rgba(0, 163, 255, ${button ? 0.75 : 1})`; - name = 'ChatGPT'; - } else if (endpoint === null) { - icon = ; - bg = 'grey'; - name = 'N/A'; - } else { - icon = ; - bg = 'grey'; - name = 'UNKNOWN'; - } - - return ( -
- {icon} - {error && ( - - ! - - )} -
- ); - } -}; - -export default getIcon; diff --git a/client/src/components/Endpoints/index.ts b/client/src/components/Endpoints/index.ts index a9769a8648..90b93d2a99 100644 --- a/client/src/components/Endpoints/index.ts +++ b/client/src/components/Endpoints/index.ts @@ -1,4 +1,5 @@ -export { default as getIcon } from './getIcon'; +export { default as Icon } from './Icon'; +export { default as MinimalIcon } from './MinimalIcon'; export { default as EndpointSettings } from './EndpointSettings'; export { default as EditPresetDialog } from './EditPresetDialog'; export { default as SaveAsPresetDialog } from './SaveAsPresetDialog'; diff --git a/client/src/components/Input/EndpointMenu/EndpointItem.tsx b/client/src/components/Input/EndpointMenu/EndpointItem.tsx index fc68a01b71..b816ceb232 100644 --- a/client/src/components/Input/EndpointMenu/EndpointItem.tsx +++ b/client/src/components/Input/EndpointMenu/EndpointItem.tsx @@ -2,7 +2,7 @@ import { useState } from 'react'; import { useRecoilValue } from 'recoil'; import { Settings } from 'lucide-react'; import { DropdownMenuRadioItem } from '~/components'; -import { getIcon } from '~/components/Endpoints'; +import { Icon } from '~/components/Endpoints'; import { SetKeyDialog } from '../SetKeyDialog'; import { useLocalize } from '~/hooks'; @@ -21,7 +21,7 @@ export default function ModelItem({ const [isDialogOpen, setDialogOpen] = useState(false); const endpointsConfig = useRecoilValue(store.endpointsConfig); - const icon = getIcon({ + const icon = Icon({ size: 20, endpoint, error: false, diff --git a/client/src/components/Input/EndpointMenu/EndpointMenu.jsx b/client/src/components/Input/EndpointMenu/EndpointMenu.jsx index c59d905b46..2372e3b4c4 100644 --- a/client/src/components/Input/EndpointMenu/EndpointMenu.jsx +++ b/client/src/components/Input/EndpointMenu/EndpointMenu.jsx @@ -3,7 +3,7 @@ import { Trash2 } from 'lucide-react'; import { useState, useEffect } from 'react'; import { useRecoilValue, useRecoilState } from 'recoil'; import { useDeletePresetMutation, useCreatePresetMutation } from 'librechat-data-provider'; -import { getIcon, EditPresetDialog } from '~/components/Endpoints'; +import { Icon, EditPresetDialog } from '~/components/Endpoints'; import EndpointItems from './EndpointItems'; import PresetItems from './PresetItems'; import FileUpload from './FileUpload'; @@ -136,10 +136,9 @@ export default function NewConversationMenu() { deletePresetsMutation.mutate({ arg: preset }); }; - const icon = getIcon({ + const icon = Icon({ size: 32, ...conversation, - isCreatedByUser: false, error: false, button: true, }); diff --git a/client/src/components/Input/EndpointMenu/PresetItem.tsx b/client/src/components/Input/EndpointMenu/PresetItem.tsx index 47ac0fb9f9..a3f7686379 100644 --- a/client/src/components/Input/EndpointMenu/PresetItem.tsx +++ b/client/src/components/Input/EndpointMenu/PresetItem.tsx @@ -1,7 +1,7 @@ import type { TPresetItemProps } from '~/common'; import type { TPreset } from 'librechat-data-provider'; import { DropdownMenuRadioItem, EditIcon, TrashIcon } from '~/components'; -import { getIcon } from '~/components/Endpoints'; +import { Icon } from '~/components/Endpoints'; export default function PresetItem({ preset = {} as TPreset, @@ -11,7 +11,7 @@ export default function PresetItem({ }: TPresetItemProps) { const { endpoint } = preset; - const icon = getIcon({ + const icon = Icon({ size: 20, endpoint: preset?.endpoint, model: preset?.model, diff --git a/client/src/components/Messages/Message.tsx b/client/src/components/Messages/Message.tsx index f84ee5ade9..bc4c62561d 100644 --- a/client/src/components/Messages/Message.tsx +++ b/client/src/components/Messages/Message.tsx @@ -8,7 +8,7 @@ import { SubRow, Plugin, MessageContent } from './Content'; import MultiMessage from './MultiMessage'; import HoverButtons from './HoverButtons'; import SiblingSwitch from './SiblingSwitch'; -import { getIcon } from '~/components/Endpoints'; +import { Icon } from '~/components/Endpoints'; import { useMessageHandler, useConversation } from '~/hooks'; import type { TMessageProps } from '~/common'; import { cn } from '~/utils'; @@ -90,7 +90,7 @@ export default function Message({ titleclass: '', }; - const icon = getIcon({ + const icon = Icon({ ...conversation, ...message, model: message?.model ?? conversation?.model, diff --git a/client/src/components/svg/AnthropicMinimalIcon.tsx b/client/src/components/svg/AnthropicMinimalIcon.tsx new file mode 100644 index 0000000000..ea44063cd0 --- /dev/null +++ b/client/src/components/svg/AnthropicMinimalIcon.tsx @@ -0,0 +1,20 @@ +import React from 'react'; + +export default function AzureMinimalistIcon() { + return ( + + + + ); +} diff --git a/client/src/components/svg/AzureMinimalIcon.tsx b/client/src/components/svg/AzureMinimalIcon.tsx new file mode 100644 index 0000000000..21797d6871 --- /dev/null +++ b/client/src/components/svg/AzureMinimalIcon.tsx @@ -0,0 +1,23 @@ +/* eslint-disable indent */ +import React from 'react'; + +export default function AzureMinimalIcon() { + return ( + + + + + + ); +} diff --git a/client/src/components/svg/BingAIMinimalIcon.tsx b/client/src/components/svg/BingAIMinimalIcon.tsx new file mode 100644 index 0000000000..db1d87d4b4 --- /dev/null +++ b/client/src/components/svg/BingAIMinimalIcon.tsx @@ -0,0 +1,22 @@ +import React from 'react'; + +export default function BingAIMinimalIcon() { + return ( + + + + + + ); +} diff --git a/client/src/components/svg/ChatGPTMinimalIcon.tsx b/client/src/components/svg/ChatGPTMinimalIcon.tsx new file mode 100644 index 0000000000..d593268a1a --- /dev/null +++ b/client/src/components/svg/ChatGPTMinimalIcon.tsx @@ -0,0 +1,23 @@ +import React from 'react'; + +export default function ChatGPTMinimalIcon() { + return ( + + + + + + + + ); +} diff --git a/client/src/components/svg/OpenAIMinimalIcon.tsx b/client/src/components/svg/OpenAIMinimalIcon.tsx new file mode 100644 index 0000000000..983a53d26d --- /dev/null +++ b/client/src/components/svg/OpenAIMinimalIcon.tsx @@ -0,0 +1,20 @@ +import React from 'react'; + +export default function OpenAIMinimalIcon() { + return ( + + + + ); +} diff --git a/client/src/components/svg/PaLMinimalIcon.tsx b/client/src/components/svg/PaLMinimalIcon.tsx new file mode 100644 index 0000000000..d69d24cc42 --- /dev/null +++ b/client/src/components/svg/PaLMinimalIcon.tsx @@ -0,0 +1,26 @@ +import React from 'react'; + +export default function PaLMinimalIcon() { + return ( + + + + + + + + + + ); +} diff --git a/client/src/components/svg/PluginMinimalIcon.tsx b/client/src/components/svg/PluginMinimalIcon.tsx new file mode 100644 index 0000000000..88f2e12e19 --- /dev/null +++ b/client/src/components/svg/PluginMinimalIcon.tsx @@ -0,0 +1,21 @@ +import React from 'react'; + +export default function PluginMinimalIcon() { + return ( + + + + + ); +} diff --git a/client/src/components/svg/index.ts b/client/src/components/svg/index.ts index b4967aac25..674ad72ab4 100644 --- a/client/src/components/svg/index.ts +++ b/client/src/components/svg/index.ts @@ -23,3 +23,10 @@ export { default as LinkIcon } from './LinkIcon'; export { default as DotsIcon } from './DotsIcon'; export { default as GearIcon } from './GearIcon'; export { default as TrashIcon } from './TrashIcon'; +export { default as AzureMinimalIcon } from './AzureMinimalIcon'; +export { default as OpenAIMinimalIcon } from './OpenAIMinimalIcon'; +export { default as ChatGPTMinimalIcon } from './ChatGPTMinimalIcon'; +export { default as PluginMinimalIcon } from './PluginMinimalIcon'; +export { default as BingAIMinimalIcon } from './BingAIMinimalIcon'; +export { default as PaLMinimalIcon } from './PaLMinimalIcon'; +export { default as AnthropicMinimalIcon } from './AnthropicMinimalIcon';