️ refactor: Optimize Rendering Performance for Icons, Conversations (#5234)

* refactor: HoverButtons and Fork components to use explicit props

* refactor: improve typing for Fork Component

* fix: memoize SpecIcon to avoid unnecessary re-renders

* feat: introduce URLIcon component and update SpecIcon for improved icon handling

* WIP: optimizing icons

* refactor: simplify modelLabel assignment in Message components

* refactor: memoize ConvoOptions component to optimize rendering performance
This commit is contained in:
Danny Avila 2025-01-09 15:40:10 -05:00 committed by GitHub
parent 687ab32bd3
commit 0f95604a67
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
19 changed files with 206 additions and 171 deletions

View file

@ -1,10 +1,11 @@
import React, { memo } from 'react';
import { memo, useMemo } from 'react';
import type { IconMapProps } from '~/common';
import { icons } from '~/components/Chat/Menus/Endpoints/Icons';
import { URLIcon } from '~/components/Endpoints/URLIcon';
interface ConvoIconURLProps {
iconURL?: string;
modelLabel?: string;
modelLabel?: string | null;
endpointIconURL?: string;
assistantName?: string;
agentName?: string;
@ -38,33 +39,26 @@ const ConvoIconURL: React.FC<ConvoIconURLProps> = ({
agentName,
context,
}) => {
let Icon: (
const Icon: (
props: IconMapProps & {
context?: string;
iconURL?: string;
},
) => React.JSX.Element;
const isURL = !!(iconURL && (iconURL.includes('http') || iconURL.startsWith('/images/')));
if (!isURL) {
Icon = icons[iconURL] ?? icons.unknown;
} else {
Icon = () => (
<div
) => React.JSX.Element = useMemo(() => icons[iconURL] ?? icons.unknown, [iconURL]);
const isURL = useMemo(
() => !!(iconURL && (iconURL.includes('http') || iconURL.startsWith('/images/'))),
[iconURL],
);
if (isURL) {
return (
<URLIcon
iconURL={iconURL}
altName={modelLabel}
className={classMap[context ?? 'default'] ?? classMap.default}
style={styleMap[context ?? 'default'] ?? styleMap.default}
>
<img
src={iconURL}
alt={modelLabel}
style={styleImageMap[context ?? 'default'] ?? styleImageMap.default}
className="object-cover"
/>
</div>
containerStyle={styleMap[context ?? 'default'] ?? styleMap.default}
imageStyle={styleImageMap[context ?? 'default'] ?? styleImageMap.default}
/>
);
return <Icon context={context} />;
}
return (

View file

@ -1,6 +1,6 @@
import { EModelEndpoint, isAssistantsEndpoint, alternateName } from 'librechat-data-provider';
import UnknownIcon from '~/components/Chat/Menus/Endpoints/UnknownIcon';
import { memo } from 'react';
import { Feather } from 'lucide-react';
import { EModelEndpoint, isAssistantsEndpoint, alternateName } from 'librechat-data-provider';
import {
Plugin,
GPTIcon,
@ -13,10 +13,16 @@ import {
AzureMinimalIcon,
CustomMinimalIcon,
} from '~/components/svg';
import UnknownIcon from '~/components/Chat/Menus/Endpoints/UnknownIcon';
import { IconProps } from '~/common';
import { cn } from '~/utils';
type EndpointIcon = {
icon: React.ReactNode | React.JSX.Element;
bg?: string;
name?: string | null;
};
function getOpenAIColor(_model: string | null | undefined) {
const model = _model?.toLowerCase() ?? '';
if (model && /\bo1\b/i.test(model)) {
@ -116,7 +122,9 @@ const MessageEndpointIcon: React.FC<IconProps> = (props) => {
name: endpoint,
};
const endpointIcons = {
const endpointIcons: {
[key: string]: EndpointIcon | undefined;
} = {
[EModelEndpoint.assistants]: assistantsIcon,
[EModelEndpoint.agents]: agentsIcon,
[EModelEndpoint.azureAssistants]: assistantsIcon,
@ -189,7 +197,9 @@ const MessageEndpointIcon: React.FC<IconProps> = (props) => {
};
let { icon, bg, name } =
endpoint && endpointIcons[endpoint] ? endpointIcons[endpoint] : endpointIcons.default;
endpoint != null && endpoint && endpointIcons[endpoint]
? endpointIcons[endpoint] ?? {}
: (endpointIcons.default as EndpointIcon);
if (iconURL && endpointIcons[iconURL]) {
({ icon, bg, name } = endpointIcons[iconURL]);
@ -201,9 +211,9 @@ const MessageEndpointIcon: React.FC<IconProps> = (props) => {
return (
<div
title={name}
title={name ?? ''}
style={{
background: bg || 'transparent',
background: bg != null ? bg || 'transparent' : 'transparent',
width: size,
height: size,
}}
@ -222,4 +232,4 @@ const MessageEndpointIcon: React.FC<IconProps> = (props) => {
);
};
export default MessageEndpointIcon;
export default memo(MessageEndpointIcon);

View file

@ -0,0 +1,21 @@
import React, { memo } from 'react';
export const URLIcon = memo(
({
iconURL,
altName,
containerStyle = { width: '20', height: '20' },
imageStyle = { width: '100%', height: '100%' },
className = 'icon-xl mr-1 shrink-0 overflow-hidden rounded-full',
}: {
iconURL: string;
altName?: string | null;
className?: string;
containerStyle?: React.CSSProperties;
imageStyle?: React.CSSProperties;
}) => (
<div className={className} style={containerStyle}>
<img src={iconURL} alt={altName ?? ''} style={imageStyle} className="object-cover" />
</div>
),
);