From 507a653d81bff03548577a286cfa8beee521f741 Mon Sep 17 00:00:00 2001 From: Konv Suu <2583695112@qq.com> Date: Fri, 5 Sep 2025 12:33:43 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E5=AF=B9=E9=BD=90=E6=A8=A1=E5=9E=8B?= =?UTF-8?q?=E8=AE=BE=E7=BD=AE=E4=B8=AD=20avatar=20=E7=9A=84=E6=A0=B7?= =?UTF-8?q?=E5=BC=8F=20(#9829)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: 对齐模型设置中 avatar 的样式 * update * update * fix: 修复上传弹出两次文件夹的问题 * update --- .../src/components/ProviderAvatar.tsx | 144 ++++++++++++++++++ .../components/ProviderLogoPicker/index.tsx | 12 +- src/renderer/src/config/providers.ts | 2 +- .../ProviderSettings/AddProviderPopup.tsx | 24 +-- .../ProviderSettings/ProviderList.tsx | 59 ++----- 5 files changed, 178 insertions(+), 63 deletions(-) create mode 100644 src/renderer/src/components/ProviderAvatar.tsx diff --git a/src/renderer/src/components/ProviderAvatar.tsx b/src/renderer/src/components/ProviderAvatar.tsx new file mode 100644 index 0000000000..82ad032532 --- /dev/null +++ b/src/renderer/src/components/ProviderAvatar.tsx @@ -0,0 +1,144 @@ +import { PoeLogo } from '@renderer/components/Icons' +import { getProviderLogo } from '@renderer/config/providers' +import { Provider } from '@renderer/types' +import { generateColorFromChar, getFirstCharacter, getForegroundColor } from '@renderer/utils' +import { Avatar } from 'antd' +import React from 'react' +import styled from 'styled-components' + +interface ProviderAvatarPrimitiveProps { + providerId: string + providerName: string + logoSrc?: string + size?: number + className?: string + style?: React.CSSProperties +} + +interface ProviderAvatarProps { + provider: Provider + customLogos?: Record + size?: number + className?: string + style?: React.CSSProperties +} + +const ProviderSvgLogo = styled.div` + width: 100%; + height: 100%; + + display: flex; + align-items: center; + justify-content: center; + border: 0.5px solid var(--color-border); + border-radius: 100%; + + & > svg { + width: 80%; + height: 80%; + } +` + +const ProviderLogo = styled(Avatar)` + width: 100%; + height: 100%; + border: 0.5px solid var(--color-border); +` + +export const ProviderAvatarPrimitive: React.FC = ({ + providerId, + providerName, + logoSrc, + size, + className, + style +}) => { + if (providerId === 'poe') { + return ( + + + + ) + } + + if (logoSrc) { + return ( + + ) + } + + const backgroundColor = generateColorFromChar(providerName) + const color = providerName ? getForegroundColor(backgroundColor) : 'white' + + return ( + + {getFirstCharacter(providerName)} + + ) +} + +export const ProviderAvatar: React.FC = ({ + provider, + customLogos = {}, + className, + style, + size +}) => { + const systemLogoSrc = getProviderLogo(provider.id) + if (systemLogoSrc) { + return ( + + ) + } + + const customLogo = customLogos[provider.id] + if (customLogo) { + if (customLogo === 'poe') { + return ( + + ) + } + + return ( + + ) + } + + return ( + + ) +} diff --git a/src/renderer/src/components/ProviderLogoPicker/index.tsx b/src/renderer/src/components/ProviderLogoPicker/index.tsx index b18b2b47d7..630d4f7c9e 100644 --- a/src/renderer/src/components/ProviderLogoPicker/index.tsx +++ b/src/renderer/src/components/ProviderLogoPicker/index.tsx @@ -1,4 +1,5 @@ import { SearchOutlined } from '@ant-design/icons' +import { ProviderAvatarPrimitive } from '@renderer/components/ProviderAvatar' import { PROVIDER_LOGO_MAP } from '@renderer/config/providers' import { getProviderLabel } from '@renderer/i18n/label' import { Input, Tooltip } from 'antd' @@ -48,10 +49,10 @@ const ProviderLogoPicker: FC = ({ onProviderClick }) => { /> - {filteredProviders.map(({ id, logo, name }) => ( + {filteredProviders.map(({ id, name, logo }) => ( handleProviderClick(e, id)}> - {name} + ))} @@ -86,11 +87,12 @@ const LogoGrid = styled.div` const LogoItem = styled.div` width: 52px; height: 52px; + border-radius: 100%; + overflow: hidden; display: flex; align-items: center; justify-content: center; cursor: pointer; - border-radius: 8px; transition: all 0.2s ease; background: var(--color-background-soft); border: 0.5px solid var(--color-border); @@ -102,8 +104,8 @@ const LogoItem = styled.div` } img { - width: 32px; - height: 32px; + width: 100%; + height: 100%; object-fit: contain; user-select: none; -webkit-user-drag: none; diff --git a/src/renderer/src/config/providers.ts b/src/renderer/src/config/providers.ts index e1037c52de..f28a88c7c7 100644 --- a/src/renderer/src/config/providers.ts +++ b/src/renderer/src/config/providers.ts @@ -661,7 +661,7 @@ export const PROVIDER_LOGO_MAP: AtLeast = { vertexai: VertexAIProviderLogo, 'new-api': NewAPIProviderLogo, 'aws-bedrock': AwsProviderLogo, - poe: 'svg' // use svg icon component + poe: 'poe' // use svg icon component } as const export function getProviderLogo(providerId: string) { diff --git a/src/renderer/src/pages/settings/ProviderSettings/AddProviderPopup.tsx b/src/renderer/src/pages/settings/ProviderSettings/AddProviderPopup.tsx index 586271e902..b011d243c7 100644 --- a/src/renderer/src/pages/settings/ProviderSettings/AddProviderPopup.tsx +++ b/src/renderer/src/pages/settings/ProviderSettings/AddProviderPopup.tsx @@ -1,5 +1,6 @@ import { loggerService } from '@logger' import { Center, VStack } from '@renderer/components/Layout' +import { ProviderAvatarPrimitive } from '@renderer/components/ProviderAvatar' import ProviderLogoPicker from '@renderer/components/ProviderLogoPicker' import { TopView } from '@renderer/components/TopView' import { PROVIDER_LOGO_MAP } from '@renderer/config/providers' @@ -143,7 +144,6 @@ const PopupContainer: React.FC = ({ provider, resolve }) => { }) setLogo(tempUrl) } - setDropdownOpen(false) } catch (error: any) { window.message.error(error.message) @@ -152,7 +152,8 @@ const PopupContainer: React.FC = ({ provider, resolve }) => { {t('settings.general.image_upload')} ), - onClick: () => { + onClick: (e: any) => { + e.stopPropagation() uploadRef.current?.click() } }, @@ -215,7 +216,9 @@ const PopupContainer: React.FC = ({ provider, resolve }) => { }} placement="bottom"> {logo ? ( - + + + ) : ( {getInitials()} @@ -258,16 +261,17 @@ const PopupContainer: React.FC = ({ provider, resolve }) => { ) } -const ProviderLogo = styled.img` +const ProviderLogo = styled.div` cursor: pointer; width: 60px; height: 60px; - border-radius: 12px; - object-fit: contain; + border-radius: 100%; + overflow: hidden; + display: flex; + align-items: center; + justify-content: center; + transition: opacity 0.3s ease; - background-color: var(--color-background-soft); - padding: 5px; - border: 0.5px solid var(--color-border); &:hover { opacity: 0.8; } @@ -277,7 +281,7 @@ const ProviderInitialsLogo = styled.div` cursor: pointer; width: 60px; height: 60px; - border-radius: 12px; + border-radius: 100%; display: flex; align-items: center; justify-content: center; diff --git a/src/renderer/src/pages/settings/ProviderSettings/ProviderList.tsx b/src/renderer/src/pages/settings/ProviderSettings/ProviderList.tsx index f04f62def6..f64e09d496 100644 --- a/src/renderer/src/pages/settings/ProviderSettings/ProviderList.tsx +++ b/src/renderer/src/pages/settings/ProviderSettings/ProviderList.tsx @@ -5,22 +5,14 @@ import { type DraggableVirtualListRef, useDraggableReorder } from '@renderer/components/DraggableList' -import { DeleteIcon, EditIcon, PoeLogo } from '@renderer/components/Icons' -import { getProviderLogo } from '@renderer/config/providers' +import { DeleteIcon, EditIcon } from '@renderer/components/Icons' +import { ProviderAvatar } from '@renderer/components/ProviderAvatar' import { useAllProviders, useProviders } from '@renderer/hooks/useProvider' import { useTimer } from '@renderer/hooks/useTimer' import ImageStorage from '@renderer/services/ImageStorage' import { isSystemProvider, Provider, ProviderType } from '@renderer/types' -import { - generateColorFromChar, - getFancyProviderName, - getFirstCharacter, - getForegroundColor, - matchKeywordsInModel, - matchKeywordsInProvider, - uuid -} from '@renderer/utils' -import { Avatar, Button, Dropdown, Input, MenuProps, Tag } from 'antd' +import { getFancyProviderName, matchKeywordsInModel, matchKeywordsInProvider, uuid } from '@renderer/utils' +import { Button, Dropdown, Input, MenuProps, Tag } from 'antd' import { GripVertical, PlusIcon, Search, UserPen } from 'lucide-react' import { FC, startTransition, useCallback, useEffect, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' @@ -280,36 +272,6 @@ const ProviderList: FC = () => { } } - const getProviderAvatar = (provider: Provider, size: number = 25) => { - // 特殊处理一下svg格式 - if (isSystemProvider(provider)) { - switch (provider.id) { - case 'poe': - return - } - } - - const logoSrc = getProviderLogo(provider.id) - if (logoSrc) { - return - } - - const customLogo = providerLogos[provider.id] - if (customLogo) { - return - } - - // generate color for custom provider - const backgroundColor = generateColorFromChar(provider.name) - const color = provider.name ? getForegroundColor(backgroundColor) : 'white' - - return ( - - {getFirstCharacter(provider.name)} - - ) - } - const filteredProviders = providers.filter((provider) => { const keywords = searchText.toLowerCase().split(/\s+/).filter(Boolean) const isProviderMatch = matchKeywordsInProvider(keywords, provider) @@ -382,7 +344,14 @@ const ProviderList: FC = () => { - {getProviderAvatar(provider)} + {getFancyProviderName(provider)} {provider.enabled && ( @@ -466,10 +435,6 @@ const DragHandle = styled.div` } ` -const ProviderLogo = styled(Avatar)` - border: 0.5px solid var(--color-border); -` - const ProviderItemName = styled.div` margin-left: 10px; font-weight: 500;