feat: add EmojiAvatar component and update component exports

- Introduced a new EmojiAvatar component for enhanced avatar functionality.
- Updated index.ts to export the new EmojiAvatar alongside existing components.
- Removed the old display/EmojiAvatar component to streamline the codebase.
- Adjusted various components to utilize the new Avatar structure and styling.
This commit is contained in:
MyPrototypeWhat 2025-09-29 17:47:17 +08:00
parent ef4bede062
commit 99962b740c
36 changed files with 225 additions and 194 deletions

View File

@ -0,0 +1,43 @@
import { cn } from '@heroui/react'
import React, { memo } from 'react'
interface EmojiAvatarProps {
children: string
size?: number
fontSize?: number
onClick?: React.MouseEventHandler<HTMLDivElement>
className?: string
style?: React.CSSProperties
}
const EmojiAvatar = ({
children,
size = 31,
fontSize,
onClick,
className,
style
}: EmojiAvatarProps) => (
<div
onClick={onClick}
className={cn(
'flex items-center justify-center',
'bg-background-soft border-border',
'rounded-[20%] cursor-pointer',
'transition-opacity hover:opacity-80',
'border-[0.5px]',
className
)}
style={{
width: size,
height: size,
fontSize: fontSize ?? size * 0.5,
...style
}}>
{children}
</div>
)
EmojiAvatar.displayName = 'EmojiAvatar'
export default memo(EmojiAvatar)

View File

@ -0,0 +1,26 @@
import type { AvatarProps as HeroUIAvatarProps } from '@heroui/react'
import { Avatar as HeroUIAvatar, AvatarGroup as HeroUIAvatarGroup, cn } from '@heroui/react'
import EmojiAvatar from './EmojiAvatar'
export interface AvatarProps extends Omit<HeroUIAvatarProps, 'size'> {
size?: 'xs' | 'sm' | 'md' | 'lg'
}
const Avatar = (props: AvatarProps) => {
const { size, className = '', ...rest } = props
const isExtraSmall = size === 'xs'
const resolvedSize = isExtraSmall ? undefined : size
const mergedClassName = cn(isExtraSmall && 'w-6 h-6 text-tiny', 'shadow-lg', className)
return <HeroUIAvatar size={resolvedSize} className={mergedClassName} {...rest} />
}
Avatar.displayName = 'Avatar'
const AvatarGroup = HeroUIAvatarGroup
AvatarGroup.displayName = 'AvatarGroup'
export { Avatar, AvatarGroup, EmojiAvatar }

View File

@ -1,35 +0,0 @@
// Original path: src/renderer/src/components/Avatar/EmojiAvatar.tsx
import { memo } from 'react'
interface EmojiAvatarProps {
children: string
size?: number
fontSize?: number
onClick?: React.MouseEventHandler<HTMLDivElement>
className?: string
style?: React.CSSProperties
ref?: React.RefObject<HTMLDivElement>
}
const EmojiAvatar = ({ children, size = 31, fontSize, onClick, className = '', style, ref }: EmojiAvatarProps) => {
const computedFontSize = fontSize ?? size * 0.5
return (
<div
ref={ref}
onClick={onClick}
className={`flex items-center justify-center rounded-[20%] border border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-gray-800 cursor-pointer transition-opacity hover:opacity-80 ${className}`}
style={{
width: `${size}px`,
height: `${size}px`,
fontSize: `${computedFontSize}px`,
...style
}}>
{children}
</div>
)
}
EmojiAvatar.displayName = 'EmojiAvatar'
export default memo(EmojiAvatar)

View File

@ -1,7 +1,7 @@
// Original path: src/renderer/src/components/ProviderAvatar.tsx // Original path: src/renderer/src/components/ProviderAvatar.tsx
import { Avatar } from '@heroui/react'
import React from 'react' import React from 'react'
import { Avatar } from '../../base/Avatar'
import { generateColorFromChar, getFirstCharacter, getForegroundColor } from './utils' import { generateColorFromChar, getFirstCharacter, getForegroundColor } from './utils'
interface ProviderAvatarProps { interface ProviderAvatarProps {

View File

@ -1,4 +1,5 @@
// Base Components // Base Components
export { Avatar, AvatarGroup, type AvatarProps,EmojiAvatar } from './base/Avatar'
export { default as Button, type ButtonProps } from './base/Button' export { default as Button, type ButtonProps } from './base/Button'
export { default as CopyButton } from './base/CopyButton' export { default as CopyButton } from './base/CopyButton'
export { default as CustomCollapse } from './base/CustomCollapse' export { default as CustomCollapse } from './base/CustomCollapse'
@ -17,7 +18,6 @@ export { getToastUtilities, type ToastUtilities } from './base/Toast'
// Display Components // Display Components
export { default as Ellipsis } from './display/Ellipsis' export { default as Ellipsis } from './display/Ellipsis'
export { default as EmojiAvatar } from './display/EmojiAvatar'
export { default as ExpandableText } from './display/ExpandableText' export { default as ExpandableText } from './display/ExpandableText'
export { default as ListItem } from './display/ListItem' export { default as ListItem } from './display/ListItem'
export { default as MaxContextCount } from './display/MaxContextCount' export { default as MaxContextCount } from './display/MaxContextCount'

View File

@ -1,6 +1,6 @@
import type { Meta, StoryObj } from '@storybook/react' import type { Meta, StoryObj } from '@storybook/react'
import EmojiAvatar from '../../../src/components/display/EmojiAvatar' import EmojiAvatar from '../../../src/components/base/Avatar/EmojiAvatar'
const meta: Meta<typeof EmojiAvatar> = { const meta: Meta<typeof EmojiAvatar> = {
title: 'Display/EmojiAvatar', title: 'Display/EmojiAvatar',

View File

@ -1,53 +0,0 @@
import React, { memo } from 'react'
import styled from 'styled-components'
interface EmojiAvatarProps {
children: string
size?: number
fontSize?: number
onClick?: React.MouseEventHandler<HTMLDivElement>
className?: string
style?: React.CSSProperties
}
const EmojiAvatar = ({
ref,
children,
size = 31,
fontSize,
onClick,
className,
style
}: EmojiAvatarProps & { ref?: React.RefObject<HTMLDivElement | null> }) => (
<StyledEmojiAvatar
ref={ref}
$size={size}
$fontSize={fontSize ?? size * 0.5}
onClick={onClick}
className={className}
style={style}>
{children}
</StyledEmojiAvatar>
)
EmojiAvatar.displayName = 'EmojiAvatar'
const StyledEmojiAvatar = styled.div<{ $size: number; $fontSize: number }>`
display: flex;
align-items: center;
justify-content: center;
background-color: var(--color-background-soft);
border: 0.5px solid var(--color-border);
border-radius: 20%;
cursor: pointer;
width: ${(props) => props.$size}px;
height: ${(props) => props.$size}px;
font-size: ${(props) => props.$fontSize}px;
transition: opacity 0.3s ease;
&:hover {
opacity: 0.8;
}
`
export default memo(EmojiAvatar)

View File

@ -1,7 +1,7 @@
import type { AvatarProps } from '@cherrystudio/ui'
import { Avatar, cn } from '@cherrystudio/ui'
import { getModelLogo } from '@renderer/config/models' import { getModelLogo } from '@renderer/config/models'
import type { Model } from '@renderer/types' import type { Model } from '@renderer/types'
import type { AvatarProps } from 'antd'
import { Avatar } from 'antd'
import { first } from 'lodash' import { first } from 'lodash'
import type { FC } from 'react' import type { FC } from 'react'
@ -12,21 +12,10 @@ interface Props {
className?: string className?: string
} }
const ModelAvatar: FC<Props> = ({ model, size, props, className }) => { const ModelAvatar: FC<Props> = ({ model, size, className, ...props }) => {
const classNames = cn('flex items-center justify-center', `h-[${size}px] w-[${size}px]`, `${className || ''}`)
return ( return (
<Avatar <Avatar src={getModelLogo(model?.id || '')} {...props} className={classNames}>
src={getModelLogo(model?.id || '')}
style={{
width: size,
height: size,
minWidth: size,
minHeight: size,
display: 'flex',
alignItems: 'center',
justifyContent: 'center'
}}
{...props}
className={className}>
{first(model?.name)} {first(model?.name)}
</Avatar> </Avatar>
) )

View File

@ -11,6 +11,7 @@ import {
ReloadOutlined ReloadOutlined
} from '@ant-design/icons' } from '@ant-design/icons'
import { Button } from '@cherrystudio/ui' import { Button } from '@cherrystudio/ui'
import { Avatar } from '@cherrystudio/ui'
import { usePreference } from '@data/hooks/usePreference' import { usePreference } from '@data/hooks/usePreference'
import { loggerService } from '@logger' import { loggerService } from '@logger'
import WindowControls from '@renderer/components/WindowControls' import WindowControls from '@renderer/components/WindowControls'
@ -25,7 +26,7 @@ import { useTimer } from '@renderer/hooks/useTimer'
import type { MinAppType } from '@renderer/types' import type { MinAppType } from '@renderer/types'
import { delay } from '@renderer/utils' import { delay } from '@renderer/utils'
import { clearWebviewState, getWebviewLoaded, setWebviewLoaded } from '@renderer/utils/webviewStateManager' import { clearWebviewState, getWebviewLoaded, setWebviewLoaded } from '@renderer/utils/webviewStateManager'
import { Alert, Avatar, Drawer, Tooltip } from 'antd' import { Alert, Drawer, Tooltip } from 'antd'
import type { WebviewTag } from 'electron' import type { WebviewTag } from 'electron'
import { useEffect, useMemo, useRef, useState } from 'react' import { useEffect, useMemo, useRef, useState } from 'react'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
@ -550,7 +551,7 @@ const MinappPopupContainer: React.FC = () => {
<EmptyView> <EmptyView>
<Avatar <Avatar
src={currentAppInfo?.logo} src={currentAppInfo?.logo}
size={80} className="h-20 w-20"
style={{ border: '1px solid var(--color-border)', marginTop: -150 }} style={{ border: '1px solid var(--color-border)', marginTop: -150 }}
/> />
<BeatLoader color="var(--color-text-2)" size={10} style={{ marginTop: 15 }} /> <BeatLoader color="var(--color-text-2)" size={10} style={{ marginTop: 15 }} />

View File

@ -1,10 +1,11 @@
import { Avatar } from '@cherrystudio/ui'
import ModelAvatar from '@renderer/components/Avatar/ModelAvatar' import ModelAvatar from '@renderer/components/Avatar/ModelAvatar'
import { getModelUniqId } from '@renderer/services/ModelService' import { getModelUniqId } from '@renderer/services/ModelService'
import type { Model, Provider } from '@renderer/types' import type { Model, Provider } from '@renderer/types'
import { matchKeywordsInString } from '@renderer/utils' import { matchKeywordsInString } from '@renderer/utils'
import { getFancyProviderName } from '@renderer/utils/naming' import { getFancyProviderName } from '@renderer/utils/naming'
import type { SelectProps } from 'antd' import type { SelectProps } from 'antd'
import { Avatar, Select } from 'antd' import { Select } from 'antd'
import { sortBy } from 'lodash' import { sortBy } from 'lodash'
import type { BaseSelectRef } from 'rc-select' import type { BaseSelectRef } from 'rc-select'
import { memo, useCallback, useMemo } from 'react' import { memo, useCallback, useMemo } from 'react'
@ -107,7 +108,7 @@ const ModelSelector = ({
} else { } else {
return ( return (
<div style={{ display: 'flex', alignItems: 'center', gap: 8 }}> <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
{showAvatar && <Avatar size={18} />} {showAvatar && <Avatar className="h-[18px] w-[18px]" />}
<span>{t('knowledge.error.model_invalid')}</span> <span>{t('knowledge.error.model_invalid')}</span>
</div> </div>
) )

View File

@ -1,5 +1,6 @@
import { PushpinOutlined } from '@ant-design/icons' import { PushpinOutlined } from '@ant-design/icons'
import { Flex } from '@cherrystudio/ui' import { Flex } from '@cherrystudio/ui'
import { Avatar } from '@cherrystudio/ui'
import { FreeTrialModelTag } from '@renderer/components/FreeTrialModelTag' import { FreeTrialModelTag } from '@renderer/components/FreeTrialModelTag'
import ModelTagsWithLabel from '@renderer/components/ModelTagsWithLabel' import ModelTagsWithLabel from '@renderer/components/ModelTagsWithLabel'
import { TopView } from '@renderer/components/TopView' import { TopView } from '@renderer/components/TopView'
@ -12,7 +13,7 @@ import type { Model, ModelType, Provider } from '@renderer/types'
import { objectEntries } from '@renderer/types' import { objectEntries } from '@renderer/types'
import { classNames, filterModelsByKeywords, getFancyProviderName } from '@renderer/utils' import { classNames, filterModelsByKeywords, getFancyProviderName } from '@renderer/utils'
import { getModelTags } from '@renderer/utils/model' import { getModelTags } from '@renderer/utils/model'
import { Avatar, Divider, Empty, Modal, Tooltip } from 'antd' import { Divider, Empty, Modal, Tooltip } from 'antd'
import { first, sortBy } from 'lodash' import { first, sortBy } from 'lodash'
import { Settings2 } from 'lucide-react' import { Settings2 } from 'lucide-react'
import React, { import React, {
@ -124,7 +125,7 @@ const PopupContainer: React.FC<Props> = ({ model, filter: baseFilter, showTagFil
</TagsContainer> </TagsContainer>
), ),
icon: ( icon: (
<Avatar src={getModelLogo(model.id || '')} size={24}> <Avatar src={getModelLogo(model.id || '')} size="xs">
{first(model.name) || 'M'} {first(model.name) || 'M'}
</Avatar> </Avatar>
), ),

View File

@ -1,12 +1,12 @@
import { Center, ColFlex, RowFlex } from '@cherrystudio/ui' import { Center, ColFlex, RowFlex } from '@cherrystudio/ui'
import { Avatar, EmojiAvatar } from '@cherrystudio/ui'
import { cacheService } from '@data/CacheService' import { cacheService } from '@data/CacheService'
import { usePreference } from '@data/hooks/usePreference' import { usePreference } from '@data/hooks/usePreference'
import DefaultAvatar from '@renderer/assets/images/avatar.png' import DefaultAvatar from '@renderer/assets/images/avatar.png'
import EmojiAvatar from '@renderer/components/Avatar/EmojiAvatar'
import useAvatar from '@renderer/hooks/useAvatar' import useAvatar from '@renderer/hooks/useAvatar'
import ImageStorage from '@renderer/services/ImageStorage' import ImageStorage from '@renderer/services/ImageStorage'
import { compressImage, isEmoji } from '@renderer/utils' import { compressImage, isEmoji } from '@renderer/utils'
import { Avatar, Dropdown, Input, Modal, Popover, Upload } from 'antd' import { Dropdown, Input, Modal, Popover, Upload } from 'antd'
import React, { useState } from 'react' import React, { useState } from 'react'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import styled from 'styled-components' import styled from 'styled-components'

View File

@ -1,8 +1,8 @@
import { Avatar } from '@cherrystudio/ui'
import { PoeLogo } from '@renderer/components/Icons' import { PoeLogo } from '@renderer/components/Icons'
import { getProviderLogo } from '@renderer/config/providers' import { getProviderLogo } from '@renderer/config/providers'
import type { Provider } from '@renderer/types' import type { Provider } from '@renderer/types'
import { generateColorFromChar, getFirstCharacter, getForegroundColor } from '@renderer/utils' import { generateColorFromChar, getFirstCharacter, getForegroundColor } from '@renderer/utils'
import { Avatar } from 'antd'
import React from 'react' import React from 'react'
import styled from 'styled-components' import styled from 'styled-components'
@ -63,7 +63,13 @@ export const ProviderAvatarPrimitive: React.FC<ProviderAvatarPrimitiveProps> = (
if (logoSrc) { if (logoSrc) {
return ( return (
<ProviderLogo draggable="false" shape="circle" src={logoSrc} className={className} style={style} size={size} /> <ProviderLogo
draggable="false"
radius="full"
src={logoSrc}
className={className}
style={{ width: size, height: size, ...style }}
/>
) )
} }
@ -72,10 +78,11 @@ export const ProviderAvatarPrimitive: React.FC<ProviderAvatarPrimitiveProps> = (
return ( return (
<ProviderLogo <ProviderLogo
size={size} radius="full"
shape="circle"
className={className} className={className}
style={{ style={{
width: size,
height: size,
backgroundColor, backgroundColor,
color, color,
...style ...style

View File

@ -53,7 +53,7 @@ const ProviderLogoPicker: FC<Props> = ({ onProviderClick }) => {
{filteredProviders.map(({ id, name, logo }) => ( {filteredProviders.map(({ id, name, logo }) => (
<Tooltip key={id} title={name} placement="top" mouseLeaveDelay={0}> <Tooltip key={id} title={name} placement="top" mouseLeaveDelay={0}>
<LogoItem onClick={(e) => handleProviderClick(e, id)}> <LogoItem onClick={(e) => handleProviderClick(e, id)}>
<ProviderAvatarPrimitive providerId={id} size={52} providerName={name} logoSrc={logo} /> <ProviderAvatarPrimitive providerId={id} style={{ width: '52px', height: '52px' }} providerName={name} logoSrc={logo} />
</LogoItem> </LogoItem>
</Tooltip> </Tooltip>
))} ))}

View File

@ -1,5 +1,5 @@
import { Avatar, EmojiAvatar } from '@cherrystudio/ui'
import { usePreference } from '@data/hooks/usePreference' import { usePreference } from '@data/hooks/usePreference'
import EmojiAvatar from '@renderer/components/Avatar/EmojiAvatar'
import { isMac } from '@renderer/config/constant' import { isMac } from '@renderer/config/constant'
import { UserAvatar } from '@renderer/config/env' import { UserAvatar } from '@renderer/config/env'
import { useTheme } from '@renderer/context/ThemeProvider' import { useTheme } from '@renderer/context/ThemeProvider'
@ -13,7 +13,7 @@ import { useSettings } from '@renderer/hooks/useSettings'
import { getSidebarIconLabel, getThemeModeLabel } from '@renderer/i18n/label' import { getSidebarIconLabel, getThemeModeLabel } from '@renderer/i18n/label'
import { isEmoji } from '@renderer/utils' import { isEmoji } from '@renderer/utils'
import { ThemeMode } from '@shared/data/preference/preferenceTypes' import { ThemeMode } from '@shared/data/preference/preferenceTypes'
import { Avatar, Tooltip } from 'antd' import { Tooltip } from 'antd'
import { import {
Code, Code,
FileSearch, FileSearch,

View File

@ -1,3 +1,4 @@
import { Avatar } from '@cherrystudio/ui'
import { loggerService } from '@logger' import { loggerService } from '@logger'
import PaddleocrLogo from '@renderer/assets/images/providers/paddleocr.png' import PaddleocrLogo from '@renderer/assets/images/providers/paddleocr.png'
import TesseractLogo from '@renderer/assets/images/providers/Tesseract.js.png' import TesseractLogo from '@renderer/assets/images/providers/Tesseract.js.png'
@ -7,7 +8,6 @@ import { useAppSelector } from '@renderer/store'
import { addOcrProvider, removeOcrProvider, setImageOcrProviderId, updateOcrProviderConfig } from '@renderer/store/ocr' import { addOcrProvider, removeOcrProvider, setImageOcrProviderId, updateOcrProviderConfig } from '@renderer/store/ocr'
import type { ImageOcrProvider, OcrProvider, OcrProviderConfig } from '@renderer/types' import type { ImageOcrProvider, OcrProvider, OcrProviderConfig } from '@renderer/types'
import { isBuiltinOcrProvider, isBuiltinOcrProviderId, isImageOcrProvider } from '@renderer/types' import { isBuiltinOcrProvider, isBuiltinOcrProviderId, isImageOcrProvider } from '@renderer/types'
import { Avatar } from 'antd'
import { FileQuestionMarkIcon, MonitorIcon } from 'lucide-react' import { FileQuestionMarkIcon, MonitorIcon } from 'lucide-react'
import { useCallback, useEffect, useState } from 'react' import { useCallback, useEffect, useState } from 'react'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
@ -72,11 +72,11 @@ export const useOcrProviders = () => {
if (isBuiltinOcrProvider(p)) { if (isBuiltinOcrProvider(p)) {
switch (p.id) { switch (p.id) {
case 'tesseract': case 'tesseract':
return <Avatar size={size} src={TesseractLogo} /> return <Avatar className={`h-[${size}px] w-[${size}px]`} src={TesseractLogo} />
case 'system': case 'system':
return <MonitorIcon size={size} /> return <MonitorIcon size={size} />
case 'paddleocr': case 'paddleocr':
return <Avatar size={size} src={PaddleocrLogo} /> return <Avatar className={`h-[${size}px] w-[${size}px]`} src={PaddleocrLogo} />
} }
} }
return <FileQuestionMarkIcon size={size} /> return <FileQuestionMarkIcon size={size} />

View File

@ -1,4 +1,5 @@
import { Button } from '@cherrystudio/ui' import { Button } from '@cherrystudio/ui'
import { Avatar } from '@cherrystudio/ui'
import AiProvider from '@renderer/aiCore' import AiProvider from '@renderer/aiCore'
import { Navbar, NavbarCenter } from '@renderer/components/app/Navbar' import { Navbar, NavbarCenter } from '@renderer/components/app/Navbar'
import ModelSelector from '@renderer/components/ModelSelector' import ModelSelector from '@renderer/components/ModelSelector'
@ -17,7 +18,7 @@ import { setIsBunInstalled } from '@renderer/store/mcp'
import type { EndpointType, Model } from '@renderer/types' import type { EndpointType, Model } from '@renderer/types'
import type { TerminalConfig } from '@shared/config/constant' import type { TerminalConfig } from '@shared/config/constant'
import { codeTools, terminalApps } from '@shared/config/constant' import { codeTools, terminalApps } from '@shared/config/constant'
import { Alert, Avatar, Checkbox, Input, Popover, Select, Space, Tooltip } from 'antd' import { Alert, Checkbox, Input, Popover, Select, Space, Tooltip } from 'antd'
import { ArrowUpRight, Download, FolderOpen, HelpCircle, Terminal, X } from 'lucide-react' import { ArrowUpRight, Download, FolderOpen, HelpCircle, Terminal, X } from 'lucide-react'
import type { FC } from 'react' import type { FC } from 'react'
import { useCallback, useEffect, useMemo, useState } from 'react' import { useCallback, useEffect, useMemo, useState } from 'react'
@ -363,7 +364,7 @@ const CodeToolsPage: FC = () => {
key={provider.id} key={provider.id}
style={{ color: 'var(--color-text)', display: 'flex', alignItems: 'center', gap: 4 }} style={{ color: 'var(--color-text)', display: 'flex', alignItems: 'center', gap: 4 }}
to={`/settings/provider?id=${provider.id}`}> to={`/settings/provider?id=${provider.id}`}>
<ProviderLogo shape="square" src={getProviderLogo(provider.id)} size={20} /> <Avatar radius="md" src={getProviderLogo(provider.id)} className="h-5 w-5 rounded-md" />
{getProviderLabel(provider.id)} {getProviderLabel(provider.id)}
<ArrowUpRight size={14} /> <ArrowUpRight size={14} />
</Link> </Link>

View File

@ -1,3 +1,4 @@
import { Avatar } from '@cherrystudio/ui'
import { ActionIconButton } from '@renderer/components/Buttons' import { ActionIconButton } from '@renderer/components/Buttons'
import ModelTagsWithLabel from '@renderer/components/ModelTagsWithLabel' import ModelTagsWithLabel from '@renderer/components/ModelTagsWithLabel'
import { type QuickPanelListItem, QuickPanelReservedSymbol, useQuickPanel } from '@renderer/components/QuickPanel' import { type QuickPanelListItem, QuickPanelReservedSymbol, useQuickPanel } from '@renderer/components/QuickPanel'
@ -7,7 +8,7 @@ import { useProviders } from '@renderer/hooks/useProvider'
import { getModelUniqId } from '@renderer/services/ModelService' import { getModelUniqId } from '@renderer/services/ModelService'
import type { FileType, Model } from '@renderer/types' import type { FileType, Model } from '@renderer/types'
import { getFancyProviderName } from '@renderer/utils' import { getFancyProviderName } from '@renderer/utils'
import { Avatar, Tooltip } from 'antd' import { Tooltip } from 'antd'
import { useLiveQuery } from 'dexie-react-hooks' import { useLiveQuery } from 'dexie-react-hooks'
import { first, sortBy } from 'lodash' import { first, sortBy } from 'lodash'
import { AtSign, CircleX, Plus } from 'lucide-react' import { AtSign, CircleX, Plus } from 'lucide-react'
@ -135,7 +136,7 @@ const MentionModelsButton: FC<Props> = ({
), ),
description: <ModelTagsWithLabel model={m} showLabel={false} size={10} style={{ opacity: 0.8 }} />, description: <ModelTagsWithLabel model={m} showLabel={false} size={10} style={{ opacity: 0.8 }} />,
icon: ( icon: (
<Avatar src={getModelLogo(m.id)} size={20}> <Avatar src={getModelLogo(m.id)} className="w-5 h-5">
{first(m.name)} {first(m.name)}
</Avatar> </Avatar>
), ),
@ -171,7 +172,7 @@ const MentionModelsButton: FC<Props> = ({
), ),
description: <ModelTagsWithLabel model={m} showLabel={false} size={10} style={{ opacity: 0.8 }} />, description: <ModelTagsWithLabel model={m} showLabel={false} size={10} style={{ opacity: 0.8 }} />,
icon: ( icon: (
<Avatar src={getModelLogo(m.id)} size={20}> <Avatar src={getModelLogo(m.id)} className="w-5 h-5">
{first(m.name)} {first(m.name)}
</Avatar> </Avatar>
), ),

View File

@ -1,8 +1,9 @@
import '@xyflow/react/dist/style.css' import '@xyflow/react/dist/style.css'
import { RobotOutlined, UserOutlined } from '@ant-design/icons' import { RobotOutlined, UserOutlined } from '@ant-design/icons'
import { Avatar } from '@cherrystudio/ui'
import { EmojiAvatar } from '@cherrystudio/ui'
import { usePreference } from '@data/hooks/usePreference' import { usePreference } from '@data/hooks/usePreference'
import EmojiAvatar from '@renderer/components/Avatar/EmojiAvatar'
import ModelAvatar from '@renderer/components/Avatar/ModelAvatar' import ModelAvatar from '@renderer/components/Avatar/ModelAvatar'
import { getModelLogo } from '@renderer/config/models' import { getModelLogo } from '@renderer/config/models'
import { useTheme } from '@renderer/context/ThemeProvider' import { useTheme } from '@renderer/context/ThemeProvider'
@ -17,7 +18,7 @@ import { getMainTextContent } from '@renderer/utils/messageUtils/find'
import type { Edge, Node, NodeTypes } from '@xyflow/react' import type { Edge, Node, NodeTypes } from '@xyflow/react'
import { Controls, Handle, MiniMap, ReactFlow, ReactFlowProvider } from '@xyflow/react' import { Controls, Handle, MiniMap, ReactFlow, ReactFlowProvider } from '@xyflow/react'
import { Position, useEdgesState, useNodesState } from '@xyflow/react' import { Position, useEdgesState, useNodesState } from '@xyflow/react'
import { Avatar, Spin, Tooltip } from 'antd' import { Spin, Tooltip } from 'antd'
import { isEqual } from 'lodash' import { isEqual } from 'lodash'
import type { FC } from 'react' import type { FC } from 'react'
import { memo, useCallback, useEffect, useMemo, useState } from 'react' import { memo, useCallback, useEffect, useMemo, useState } from 'react'
@ -77,7 +78,7 @@ const CustomNode: FC<{ data: any }> = ({ data }) => {
avatar = <Avatar src={data.userAvatar} alt={title} /> avatar = <Avatar src={data.userAvatar} alt={title} />
} }
} else { } else {
avatar = <Avatar icon={<UserOutlined />} style={{ backgroundColor: 'var(--color-info)' }} /> avatar = <Avatar icon={<UserOutlined />} className="bg-info" />
} }
} else if (nodeType === 'assistant') { } else if (nodeType === 'assistant') {
borderColor = 'var(--color-primary)' borderColor = 'var(--color-primary)'
@ -94,11 +95,11 @@ const CustomNode: FC<{ data: any }> = ({ data }) => {
<Avatar <Avatar
src={modelLogo} src={modelLogo}
icon={!modelLogo ? <RobotOutlined /> : undefined} icon={!modelLogo ? <RobotOutlined /> : undefined}
style={{ backgroundColor: 'var(--color-primary)' }} className="bg-primary"
/> />
) )
} else { } else {
avatar = <Avatar icon={<RobotOutlined />} style={{ backgroundColor: 'var(--color-primary)' }} /> avatar = <Avatar icon={<RobotOutlined />} className="bg-primary" />
} }
} }

View File

@ -1,5 +1,5 @@
import { Avatar, EmojiAvatar } from '@cherrystudio/ui'
import { usePreference } from '@data/hooks/usePreference' import { usePreference } from '@data/hooks/usePreference'
import EmojiAvatar from '@renderer/components/Avatar/EmojiAvatar'
import { APP_NAME, AppLogo, isLocalAi } from '@renderer/config/env' import { APP_NAME, AppLogo, isLocalAi } from '@renderer/config/env'
import { getModelLogo } from '@renderer/config/models' import { getModelLogo } from '@renderer/config/models'
import { useTheme } from '@renderer/context/ThemeProvider' import { useTheme } from '@renderer/context/ThemeProvider'
@ -13,7 +13,6 @@ import { newMessagesActions } from '@renderer/store/newMessage'
import type { Message } from '@renderer/types/newMessage' import type { Message } from '@renderer/types/newMessage'
import { isEmoji, removeLeadingEmoji } from '@renderer/utils' import { isEmoji, removeLeadingEmoji } from '@renderer/utils'
import { getMainTextContent } from '@renderer/utils/messageUtils/find' import { getMainTextContent } from '@renderer/utils/messageUtils/find'
import { Avatar } from 'antd'
import { CircleChevronDown } from 'lucide-react' import { CircleChevronDown } from 'lucide-react'
import { type FC, useCallback, useEffect, useRef, useState } from 'react' import { type FC, useCallback, useEffect, useRef, useState } from 'react'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
@ -227,8 +226,9 @@ const MessageAnchorLine: FC<MessageLineProps> = ({ messages }) => {
{message.role === 'assistant' ? ( {message.role === 'assistant' ? (
<MessageItemAvatar <MessageItemAvatar
src={avatarSource} src={avatarSource}
size={size}
style={{ style={{
width: size,
height: size,
border: isLocalAi ? '1px solid var(--color-border-soft)' : 'none', border: isLocalAi ? '1px solid var(--color-border-soft)' : 'none',
filter: theme === 'dark' ? 'invert(0.05)' : undefined filter: theme === 'dark' ? 'invert(0.05)' : undefined
}} }}
@ -246,7 +246,7 @@ const MessageAnchorLine: FC<MessageLineProps> = ({ messages }) => {
{avatar} {avatar}
</EmojiAvatar> </EmojiAvatar>
) : ( ) : (
<MessageItemAvatar src={avatar} size={size} /> <MessageItemAvatar src={avatar} style={{ width: size, height: size }} />
)} )}
</> </>
)} )}

View File

@ -1,13 +1,15 @@
import { ArrowsAltOutlined, ShrinkOutlined } from '@ant-design/icons' import { ArrowsAltOutlined, ShrinkOutlined } from '@ant-design/icons'
import { RowFlex } from '@cherrystudio/ui' import { Avatar, AvatarGroup, RowFlex } from '@cherrystudio/ui'
import { usePreference } from '@data/hooks/usePreference' import { usePreference } from '@data/hooks/usePreference'
import ModelAvatar from '@renderer/components/Avatar/ModelAvatar' import ModelAvatar from '@renderer/components/Avatar/ModelAvatar'
import Scrollbar from '@renderer/components/Scrollbar' import Scrollbar from '@renderer/components/Scrollbar'
import { getModelLogo } from '@renderer/config/models'
import type { Model } from '@renderer/types' import type { Model } from '@renderer/types'
import { AssistantMessageStatus, type Message } from '@renderer/types/newMessage' import { AssistantMessageStatus, type Message } from '@renderer/types/newMessage'
import { lightbulbSoftVariants } from '@renderer/utils/motionVariants' import { lightbulbSoftVariants } from '@renderer/utils/motionVariants'
import type { MultiModelFoldDisplayMode } from '@shared/data/preference/preferenceTypes' import type { MultiModelFoldDisplayMode } from '@shared/data/preference/preferenceTypes'
import { Avatar, Segmented as AntdSegmented, Tooltip } from 'antd' import { Segmented as AntdSegmented, Tooltip } from 'antd'
import { first } from 'lodash'
import { motion } from 'motion/react' import { motion } from 'motion/react'
import type { FC } from 'react' import type { FC } from 'react'
import { memo, useCallback } from 'react' import { memo, useCallback } from 'react'
@ -83,7 +85,26 @@ const MessageGroupModelList: FC<MessageGroupModelListProps> = ({ messages, selec
<ModelsContainer $displayMode={foldDisplayMode}> <ModelsContainer $displayMode={foldDisplayMode}>
{isCompact ? ( {isCompact ? (
/* Compact style display */ /* Compact style display */
<Avatar.Group className="avatar-group">{messages.map((message) => renderLabel(message))}</Avatar.Group> <AvatarGroup className="p-2" isBordered>
{messages.map((message) => {
const modelTip = message.model?.name
const isSelected = message.id === selectMessageId
return (
<Tooltip key={message.id} title={modelTip} mouseEnterDelay={0.5} mouseLeaveDelay={0}>
<Avatar
src={getModelLogo(message.model?.id || '')}
name={first(message.model?.name)}
size="xs"
isBordered={isSelected}
color={isSelected ? 'primary' : 'default'}
onClick={() => setSelectedMessage(message)}
className="shadow-lg"
/>
</Tooltip>
)
})}
</AvatarGroup>
) : ( ) : (
/* Expanded style display */ /* Expanded style display */
<Segmented <Segmented

View File

@ -1,6 +1,6 @@
import { RowFlex } from '@cherrystudio/ui' import { RowFlex } from '@cherrystudio/ui'
import { Avatar, EmojiAvatar } from '@cherrystudio/ui'
import { usePreference } from '@data/hooks/usePreference' import { usePreference } from '@data/hooks/usePreference'
import EmojiAvatar from '@renderer/components/Avatar/EmojiAvatar'
import UserPopup from '@renderer/components/Popups/UserPopup' import UserPopup from '@renderer/components/Popups/UserPopup'
import { APP_NAME, AppLogo, isLocalAi } from '@renderer/config/env' import { APP_NAME, AppLogo, isLocalAi } from '@renderer/config/env'
import { getModelLogo } from '@renderer/config/models' import { getModelLogo } from '@renderer/config/models'
@ -15,7 +15,7 @@ import { getModelName } from '@renderer/services/ModelService'
import type { Assistant, Model, Topic } from '@renderer/types' import type { Assistant, Model, Topic } from '@renderer/types'
import type { Message } from '@renderer/types/newMessage' import type { Message } from '@renderer/types/newMessage'
import { firstLetter, isEmoji, removeLeadingEmoji } from '@renderer/utils' import { firstLetter, isEmoji, removeLeadingEmoji } from '@renderer/utils'
import { Avatar, Checkbox, Tooltip } from 'antd' import { Checkbox, Tooltip } from 'antd'
import dayjs from 'dayjs' import dayjs from 'dayjs'
import { Sparkle } from 'lucide-react' import { Sparkle } from 'lucide-react'
import type { FC } from 'react' import type { FC } from 'react'
@ -86,7 +86,7 @@ const MessageHeader: FC<Props> = memo(({ assistant, model, message, topic, isGro
{isAssistantMessage ? ( {isAssistantMessage ? (
<Avatar <Avatar
src={avatarSource} src={avatarSource}
size={35} className="h-[35px] w-[35px]"
style={{ style={{
borderRadius: '25%', borderRadius: '25%',
cursor: showMinappIcon ? 'pointer' : 'default', cursor: showMinappIcon ? 'pointer' : 'default',
@ -105,7 +105,7 @@ const MessageHeader: FC<Props> = memo(({ assistant, model, message, topic, isGro
) : ( ) : (
<Avatar <Avatar
src={avatar} src={avatar}
size={35} className="h-[35px] w-[35px]"
style={{ borderRadius: '25%', cursor: 'pointer' }} style={{ borderRadius: '25%', cursor: 'pointer' }}
onClick={() => UserPopup.show()} onClick={() => UserPopup.show()}
/> />

View File

@ -1,3 +1,4 @@
import { Avatar } from '@cherrystudio/ui'
import { loggerService } from '@logger' import { loggerService } from '@logger'
import { DEFAULT_MIN_APPS } from '@renderer/config/minapps' import { DEFAULT_MIN_APPS } from '@renderer/config/minapps'
import { useMinappPopup } from '@renderer/hooks/useMinappPopup' import { useMinappPopup } from '@renderer/hooks/useMinappPopup'
@ -5,7 +6,6 @@ import { useMinapps } from '@renderer/hooks/useMinapps'
import { useNavbarPosition } from '@renderer/hooks/useNavbar' import { useNavbarPosition } from '@renderer/hooks/useNavbar'
import TabsService from '@renderer/services/TabsService' import TabsService from '@renderer/services/TabsService'
import { getWebviewLoaded, onWebviewStateChange, setWebviewLoaded } from '@renderer/utils/webviewStateManager' import { getWebviewLoaded, onWebviewStateChange, setWebviewLoaded } from '@renderer/utils/webviewStateManager'
import { Avatar } from 'antd'
import type { WebviewTag } from 'electron' import type { WebviewTag } from 'electron'
import type { FC } from 'react' import type { FC } from 'react'
import { useCallback, useEffect, useMemo, useRef, useState } from 'react' import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
@ -187,7 +187,7 @@ const MinAppPage: FC = () => {
</ToolbarWrapper> </ToolbarWrapper>
{!isReady && ( {!isReady && (
<LoadingMask> <LoadingMask>
<Avatar src={app.logo} size={60} style={{ border: '1px solid var(--color-border)' }} /> <Avatar src={app.logo} className="h-[60px] w-[60px] border border-border" />
<BeatLoader color="var(--color-text-2)" size={8} style={{ marginTop: 12 }} /> <BeatLoader color="var(--color-text-2)" size={8} style={{ marginTop: 12 }} />
</LoadingMask> </LoadingMask>
)} )}

View File

@ -1,9 +1,9 @@
import { Avatar } from '@cherrystudio/ui'
import { loggerService } from '@logger' import { loggerService } from '@logger'
import WebviewContainer from '@renderer/components/MinApp/WebviewContainer' import WebviewContainer from '@renderer/components/MinApp/WebviewContainer'
import { useSettings } from '@renderer/hooks/useSettings' import { useSettings } from '@renderer/hooks/useSettings'
import type { MinAppType } from '@renderer/types' import type { MinAppType } from '@renderer/types'
import { getWebviewLoaded, setWebviewLoaded } from '@renderer/utils/webviewStateManager' import { getWebviewLoaded, setWebviewLoaded } from '@renderer/utils/webviewStateManager'
import { Avatar } from 'antd'
import type { WebviewTag } from 'electron' import type { WebviewTag } from 'electron'
import type { FC } from 'react' import type { FC } from 'react'
import { useCallback, useEffect, useRef, useState } from 'react' import { useCallback, useEffect, useRef, useState } from 'react'
@ -110,7 +110,7 @@ const MinAppFullPageView: FC<Props> = ({ app }) => {
{!isReady && ( {!isReady && (
<LoadingMask> <LoadingMask>
<LoadingOverlay> <LoadingOverlay>
<Avatar src={app.logo} size={60} style={{ border: '1px solid var(--color-border)' }} /> <Avatar src={app.logo} className="h-[60px] w-[60px] border border-border" />
<BeatLoader color="var(--color-text-2)" size={8} style={{ marginTop: 12 }} /> <BeatLoader color="var(--color-text-2)" size={8} style={{ marginTop: 12 }} />
</LoadingOverlay> </LoadingOverlay>
</LoadingMask> </LoadingMask>

View File

@ -1,6 +1,7 @@
import { PlusOutlined, RedoOutlined } from '@ant-design/icons' import { PlusOutlined, RedoOutlined } from '@ant-design/icons'
import { Button, RowFlex } from '@cherrystudio/ui' import { Button, RowFlex } from '@cherrystudio/ui'
import { Switch } from '@cherrystudio/ui' import { Switch } from '@cherrystudio/ui'
import { Avatar } from '@cherrystudio/ui'
import { useCache } from '@data/hooks/useCache' import { useCache } from '@data/hooks/useCache'
import { usePreference } from '@data/hooks/usePreference' import { usePreference } from '@data/hooks/usePreference'
import { loggerService } from '@logger' import { loggerService } from '@logger'
@ -21,7 +22,7 @@ import { translateText } from '@renderer/services/TranslateService'
import type { FileMetadata } from '@renderer/types' import type { FileMetadata } from '@renderer/types'
import type { PaintingAction, PaintingsState } from '@renderer/types' import type { PaintingAction, PaintingsState } from '@renderer/types'
import { getErrorMessage, uuid } from '@renderer/utils' import { getErrorMessage, uuid } from '@renderer/utils'
import { Avatar, Input, InputNumber, Radio, Segmented, Select, Slider, Tooltip, Upload } from 'antd' import { Input, InputNumber, Radio, Segmented, Select, Slider, Tooltip, Upload } from 'antd'
import TextArea from 'antd/es/input/TextArea' import TextArea from 'antd/es/input/TextArea'
import { Info } from 'lucide-react' import { Info } from 'lucide-react'
import type { FC } from 'react' import type { FC } from 'react'
@ -839,10 +840,10 @@ const AihubmixPage: FC<{ Options: string[] }> = ({ Options }) => {
<SettingTitle style={{ marginBottom: 5 }}>{t('common.provider')}</SettingTitle> <SettingTitle style={{ marginBottom: 5 }}>{t('common.provider')}</SettingTitle>
<SettingHelpLink target="_blank" href={aihubmixProvider.apiHost}> <SettingHelpLink target="_blank" href={aihubmixProvider.apiHost}>
{t('paintings.learn_more')} {t('paintings.learn_more')}
<ProviderLogo <Avatar
shape="square" radius="md"
src={getProviderLogo(aihubmixProvider.id)} src={getProviderLogo(aihubmixProvider.id)}
size={16} className="h-4 w-4 border-[0.5px] border-[var(--color-border)]"
style={{ marginLeft: 5 }} style={{ marginLeft: 5 }}
/> />
</SettingHelpLink> </SettingHelpLink>
@ -852,7 +853,11 @@ const AihubmixPage: FC<{ Options: string[] }> = ({ Options }) => {
{providerOptions.map((provider) => ( {providerOptions.map((provider) => (
<Select.Option value={provider.value} key={provider.value}> <Select.Option value={provider.value} key={provider.value}>
<SelectOptionContainer> <SelectOptionContainer>
<ProviderLogo shape="square" src={getProviderLogo(provider.value || '')} size={16} /> <Avatar
radius="md"
src={getProviderLogo(provider.value || '')}
className="h-4 w-4 border-[0.5px] border-[var(--color-border)]"
/>
{provider.label} {provider.label}
</SelectOptionContainer> </SelectOptionContainer>
</Select.Option> </Select.Option>

View File

@ -1,6 +1,7 @@
import { PlusOutlined, RedoOutlined } from '@ant-design/icons' import { PlusOutlined, RedoOutlined } from '@ant-design/icons'
import { Button, RowFlex } from '@cherrystudio/ui' import { Button, RowFlex } from '@cherrystudio/ui'
import { Switch } from '@cherrystudio/ui' import { Switch } from '@cherrystudio/ui'
import { Avatar } from '@cherrystudio/ui'
import { useCache } from '@data/hooks/useCache' import { useCache } from '@data/hooks/useCache'
import DMXAPIToImg from '@renderer/assets/images/providers/DMXAPI-to-img.webp' import DMXAPIToImg from '@renderer/assets/images/providers/DMXAPI-to-img.webp'
import { Navbar, NavbarCenter, NavbarRight } from '@renderer/components/app/Navbar' import { Navbar, NavbarCenter, NavbarRight } from '@renderer/components/app/Navbar'
@ -14,7 +15,7 @@ import FileManager from '@renderer/services/FileManager'
import type { FileMetadata } from '@renderer/types' import type { FileMetadata } from '@renderer/types'
import { convertToBase64, uuid } from '@renderer/utils' import { convertToBase64, uuid } from '@renderer/utils'
import type { DmxapiPainting } from '@types' import type { DmxapiPainting } from '@types'
import { Avatar, Input, InputNumber, Segmented, Select, Tooltip } from 'antd' import { Input, InputNumber, Segmented, Select, Tooltip } from 'antd'
import TextArea from 'antd/es/input/TextArea' import TextArea from 'antd/es/input/TextArea'
import { Info } from 'lucide-react' import { Info } from 'lucide-react'
import type { FC } from 'react' import type { FC } from 'react'
@ -804,10 +805,10 @@ const DmxapiPage: FC<{ Options: string[] }> = ({ Options }) => {
<SettingHelpLink target="_blank" href={TOP_UP_URL}> <SettingHelpLink target="_blank" href={TOP_UP_URL}>
{t('paintings.top_up')} {t('paintings.top_up')}
</SettingHelpLink> </SettingHelpLink>
<ProviderLogo <Avatar
shape="square" radius="md"
src={getProviderLogo(dmxapiProvider.id)} src={getProviderLogo(dmxapiProvider.id)}
size={16} className="h-4 w-4 border-[0.5px] border-[var(--color-border)]"
style={{ marginLeft: 5 }} style={{ marginLeft: 5 }}
/> />
</div> </div>
@ -816,7 +817,11 @@ const DmxapiPage: FC<{ Options: string[] }> = ({ Options }) => {
{providerOptions.map((provider) => ( {providerOptions.map((provider) => (
<Select.Option value={provider.value} key={provider.value}> <Select.Option value={provider.value} key={provider.value}>
<SelectOptionContainer> <SelectOptionContainer>
<ProviderLogo shape="square" src={getProviderLogo(provider.value || '')} size={16} /> <Avatar
radius="md"
src={getProviderLogo(provider.value || '')}
className="h-4 w-4 border-[0.5px] border-[var(--color-border)]"
/>
{provider.label} {provider.label}
</SelectOptionContainer> </SelectOptionContainer>
</Select.Option> </Select.Option>
@ -1028,9 +1033,6 @@ const ProviderTitleContainer = styled.div`
margin-bottom: 5px; margin-bottom: 5px;
` `
const ProviderLogo = styled(Avatar)`
border: 0.5px solid var(--color-border);
`
const SelectOptionContainer = styled.div` const SelectOptionContainer = styled.div`
display: flex; display: flex;
align-items: center; align-items: center;

View File

@ -1,5 +1,6 @@
import { PlusOutlined } from '@ant-design/icons' import { PlusOutlined } from '@ant-design/icons'
import { Button } from '@cherrystudio/ui' import { Button } from '@cherrystudio/ui'
import { Avatar } from '@cherrystudio/ui'
import { useCache } from '@data/hooks/useCache' import { useCache } from '@data/hooks/useCache'
import { usePreference } from '@data/hooks/usePreference' import { usePreference } from '@data/hooks/usePreference'
import { loggerService } from '@logger' import { loggerService } from '@logger'
@ -28,7 +29,7 @@ import { translateText } from '@renderer/services/TranslateService'
import type { PaintingAction, PaintingsState } from '@renderer/types' import type { PaintingAction, PaintingsState } from '@renderer/types'
import type { FileMetadata } from '@renderer/types' import type { FileMetadata } from '@renderer/types'
import { getErrorMessage, uuid } from '@renderer/utils' import { getErrorMessage, uuid } from '@renderer/utils'
import { Avatar, Empty, InputNumber, Segmented, Select, Upload } from 'antd' import { Empty, InputNumber, Segmented, Select, Upload } from 'antd'
import TextArea from 'antd/es/input/TextArea' import TextArea from 'antd/es/input/TextArea'
import type { FC } from 'react' import type { FC } from 'react'
import React from 'react' import React from 'react'
@ -502,10 +503,10 @@ const NewApiPage: FC<{ Options: string[] }> = ({ Options }) => {
<SettingTitle style={{ marginBottom: 5 }}>{t('common.provider')}</SettingTitle> <SettingTitle style={{ marginBottom: 5 }}>{t('common.provider')}</SettingTitle>
<SettingHelpLink target="_blank" href={'https://docs.newapi.pro/apps/cherry-studio/'}> <SettingHelpLink target="_blank" href={'https://docs.newapi.pro/apps/cherry-studio/'}>
{t('paintings.learn_more')} {t('paintings.learn_more')}
<ProviderLogo <Avatar
shape="square" radius="md"
src={getProviderLogo(newApiProvider.id)} src={getProviderLogo(newApiProvider.id)}
size={16} className="h-4 w-4 border-[0.5px] border-[var(--color-border)]"
style={{ marginLeft: 5 }} style={{ marginLeft: 5 }}
/> />
</SettingHelpLink> </SettingHelpLink>
@ -518,7 +519,11 @@ const NewApiPage: FC<{ Options: string[] }> = ({ Options }) => {
{providerOptions.map((provider) => ( {providerOptions.map((provider) => (
<Select.Option value={provider.value} key={provider.value}> <Select.Option value={provider.value} key={provider.value}>
<SelectOptionContainer> <SelectOptionContainer>
<ProviderLogo shape="square" src={getProviderLogo(provider.value || '')} size={16} /> <Avatar
radius="md"
src={getProviderLogo(provider.value || '')}
className="h-4 w-4 border-[0.5px] border-[var(--color-border)]"
/>
{provider.label} {provider.label}
</SelectOptionContainer> </SelectOptionContainer>
</Select.Option> </Select.Option>
@ -787,10 +792,6 @@ const ToolbarMenu = styled.div`
gap: 6px; gap: 6px;
` `
const ProviderLogo = styled(Avatar)`
border: 0.5px solid var(--color-border);
`
// 添加新的样式组件 // 添加新的样式组件
const ModeSegmentedContainer = styled.div` const ModeSegmentedContainer = styled.div`
display: flex; display: flex;

View File

@ -1,5 +1,6 @@
import { PlusOutlined } from '@ant-design/icons' import { PlusOutlined } from '@ant-design/icons'
import { Button } from '@cherrystudio/ui' import { Button } from '@cherrystudio/ui'
import { Avatar } from '@cherrystudio/ui'
import { useCache } from '@data/hooks/useCache' import { useCache } from '@data/hooks/useCache'
import { usePreference } from '@data/hooks/usePreference' import { usePreference } from '@data/hooks/usePreference'
import { loggerService } from '@logger' import { loggerService } from '@logger'
@ -16,7 +17,7 @@ import FileManager from '@renderer/services/FileManager'
import { translateText } from '@renderer/services/TranslateService' import { translateText } from '@renderer/services/TranslateService'
import type { TokenFluxPainting } from '@renderer/types' import type { TokenFluxPainting } from '@renderer/types'
import { getErrorMessage, uuid } from '@renderer/utils' import { getErrorMessage, uuid } from '@renderer/utils'
import { Avatar, Select, Tooltip } from 'antd' import { Select, Tooltip } from 'antd'
import TextArea from 'antd/es/input/TextArea' import TextArea from 'antd/es/input/TextArea'
import { Info } from 'lucide-react' import { Info } from 'lucide-react'
import type { FC } from 'react' import type { FC } from 'react'
@ -381,7 +382,11 @@ const TokenFluxPage: FC<{ Options: string[] }> = ({ Options }) => {
<SettingTitle style={{ marginBottom: 8 }}>{t('common.provider')}</SettingTitle> <SettingTitle style={{ marginBottom: 8 }}>{t('common.provider')}</SettingTitle>
<SettingHelpLink target="_blank" href="https://tokenflux.ai"> <SettingHelpLink target="_blank" href="https://tokenflux.ai">
{t('paintings.learn_more')} {t('paintings.learn_more')}
<ProviderLogo shape="square" src={getProviderLogo('tokenflux')} size={16} style={{ marginLeft: 5 }} /> <Avatar
radius="md"
src={getProviderLogo('tokenflux')}
className="ml-[5px] h-4 w-4 border-[0.5px] border-[var(--color-border)]"
/>
</SettingHelpLink> </SettingHelpLink>
</ProviderTitleContainer> </ProviderTitleContainer>
@ -392,7 +397,11 @@ const TokenFluxPage: FC<{ Options: string[] }> = ({ Options }) => {
{providerOptions.map((provider) => ( {providerOptions.map((provider) => (
<Select.Option value={provider.value} key={provider.value}> <Select.Option value={provider.value} key={provider.value}>
<SelectOptionContainer> <SelectOptionContainer>
<ProviderLogo shape="square" src={getProviderLogo(provider.value || '')} size={16} /> <Avatar
radius="md"
src={getProviderLogo(provider.value || '')}
className="h-4 w-4 border-[0.5px] border-[var(--color-border)]"
/>
{provider.label} {provider.label}
</SelectOptionContainer> </SelectOptionContainer>
</Select.Option> </Select.Option>
@ -763,10 +772,6 @@ const InfoIcon = styled(Info)`
} }
` `
const ProviderLogo = styled(Avatar)`
border: 0.5px solid var(--color-border);
`
const ProviderTitleContainer = styled.div` const ProviderTitleContainer = styled.div`
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;

View File

@ -1,6 +1,7 @@
import { PlusOutlined } from '@ant-design/icons' import { PlusOutlined } from '@ant-design/icons'
import { RowFlex } from '@cherrystudio/ui' import { RowFlex } from '@cherrystudio/ui'
import { Button } from '@cherrystudio/ui' import { Button } from '@cherrystudio/ui'
import { Avatar } from '@cherrystudio/ui'
import { useCache } from '@data/hooks/useCache' import { useCache } from '@data/hooks/useCache'
import AiProvider from '@renderer/aiCore' import AiProvider from '@renderer/aiCore'
import { Navbar, NavbarCenter, NavbarRight } from '@renderer/components/app/Navbar' import { Navbar, NavbarCenter, NavbarRight } from '@renderer/components/app/Navbar'
@ -12,7 +13,7 @@ import { useAllProviders } from '@renderer/hooks/useProvider'
import { getProviderLabel } from '@renderer/i18n/label' import { getProviderLabel } from '@renderer/i18n/label'
import FileManager from '@renderer/services/FileManager' import FileManager from '@renderer/services/FileManager'
import { getErrorMessage, uuid } from '@renderer/utils' import { getErrorMessage, uuid } from '@renderer/utils'
import { Avatar, InputNumber, Radio, Select } from 'antd' import { InputNumber, Radio, Select } from 'antd'
import TextArea from 'antd/es/input/TextArea' import TextArea from 'antd/es/input/TextArea'
import type { FC } from 'react' import type { FC } from 'react'
import { useEffect, useState } from 'react' import { useEffect, useState } from 'react'
@ -367,9 +368,9 @@ const ZhipuPage: FC<{ Options: string[] }> = ({ Options }) => {
{t('paintings.paint_course')} {t('paintings.paint_course')}
</SettingHelpLink> </SettingHelpLink>
<ProviderLogo <ProviderLogo
shape="square" radius="md"
src={getProviderLogo(zhipuProvider.id)} src={getProviderLogo(zhipuProvider.id)}
size={16} className="h-4 w-4"
style={{ marginLeft: 5 }} style={{ marginLeft: 5 }}
/> />
</div> </div>
@ -378,7 +379,7 @@ const ZhipuPage: FC<{ Options: string[] }> = ({ Options }) => {
{providerOptions.map((provider) => ( {providerOptions.map((provider) => (
<Select.Option value={provider.value} key={provider.value}> <Select.Option value={provider.value} key={provider.value}>
<SelectOptionContainer> <SelectOptionContainer>
<ProviderLogo shape="square" src={getProviderLogo(provider.value || '')} size={16} /> <ProviderLogo radius="md" src={getProviderLogo(provider.value || '')} className="h-4 w-4" />
{provider.label} {provider.label}
</SelectOptionContainer> </SelectOptionContainer>
</Select.Option> </Select.Option>

View File

@ -2,6 +2,7 @@ import { GithubOutlined } from '@ant-design/icons'
import { RowFlex } from '@cherrystudio/ui' import { RowFlex } from '@cherrystudio/ui'
import { Switch } from '@cherrystudio/ui' import { Switch } from '@cherrystudio/ui'
import { Button } from '@cherrystudio/ui' import { Button } from '@cherrystudio/ui'
import { Avatar } from '@cherrystudio/ui'
import { usePreference } from '@data/hooks/usePreference' import { usePreference } from '@data/hooks/usePreference'
import IndicatorLight from '@renderer/components/IndicatorLight' import IndicatorLight from '@renderer/components/IndicatorLight'
import { APP_NAME, AppLogo } from '@renderer/config/env' import { APP_NAME, AppLogo } from '@renderer/config/env'
@ -15,7 +16,7 @@ import { handleSaveData } from '@renderer/store'
import { runAsyncFunction } from '@renderer/utils' import { runAsyncFunction } from '@renderer/utils'
import { UpgradeChannel } from '@shared/data/preference/preferenceTypes' import { UpgradeChannel } from '@shared/data/preference/preferenceTypes'
import { ThemeMode } from '@shared/data/preference/preferenceTypes' import { ThemeMode } from '@shared/data/preference/preferenceTypes'
import { Avatar, Progress, Radio, Row, Tag, Tooltip } from 'antd' import { Progress, Radio, Row, Tag, Tooltip } from 'antd'
import { debounce } from 'lodash' import { debounce } from 'lodash'
import { Bug, FileCheck, Globe, Mail, Rss } from 'lucide-react' import { Bug, FileCheck, Globe, Mail, Rss } from 'lucide-react'
import { BadgeQuestionMark } from 'lucide-react' import { BadgeQuestionMark } from 'lucide-react'
@ -208,7 +209,7 @@ const AboutSettings: FC = () => {
strokeColor="#67ad5b" strokeColor="#67ad5b"
/> />
)} )}
<Avatar src={AppLogo} size={80} style={{ minHeight: 80 }} /> <Avatar src={AppLogo} className="h-20 min-h-[80px] w-20" />
</AvatarWrapper> </AvatarWrapper>
<VersionWrapper> <VersionWrapper>
<Title>{APP_NAME}</Title> <Title>{APP_NAME}</Title>

View File

@ -1,12 +1,13 @@
import { ExportOutlined } from '@ant-design/icons' import { ExportOutlined } from '@ant-design/icons'
import { Flex } from '@cherrystudio/ui' import { Flex } from '@cherrystudio/ui'
import { Button } from '@cherrystudio/ui' import { Button } from '@cherrystudio/ui'
import { Avatar } from '@cherrystudio/ui'
import { ApiKeyListPopup } from '@renderer/components/Popups/ApiKeyListPopup' import { ApiKeyListPopup } from '@renderer/components/Popups/ApiKeyListPopup'
import { getPreprocessProviderLogo, PREPROCESS_PROVIDER_CONFIG } from '@renderer/config/preprocessProviders' import { getPreprocessProviderLogo, PREPROCESS_PROVIDER_CONFIG } from '@renderer/config/preprocessProviders'
import { usePreprocessProvider } from '@renderer/hooks/usePreprocess' import { usePreprocessProvider } from '@renderer/hooks/usePreprocess'
import type { PreprocessProvider } from '@renderer/types' import type { PreprocessProvider } from '@renderer/types'
import { formatApiKeys, hasObjectKey } from '@renderer/utils' import { formatApiKeys, hasObjectKey } from '@renderer/utils'
import { Avatar, Divider, Input, Tooltip } from 'antd' import { Divider, Input, Tooltip } from 'antd'
import Link from 'antd/es/typography/Link' import Link from 'antd/es/typography/Link'
import { List } from 'lucide-react' import { List } from 'lucide-react'
import type { FC } from 'react' import type { FC } from 'react'
@ -73,7 +74,11 @@ const PreprocessProviderSettings: FC<Props> = ({ provider: _provider }) => {
<> <>
<SettingTitle> <SettingTitle>
<Flex className="items-center gap-2"> <Flex className="items-center gap-2">
<ProviderLogo shape="square" src={getPreprocessProviderLogo(preprocessProvider.id)} size={16} /> <Avatar
radius="md"
src={getPreprocessProviderLogo(preprocessProvider.id)}
className="h-4 w-4 border-[0.5px] border-[var(--color-border)]"
/>
<ProviderName> {preprocessProvider.name}</ProviderName> <ProviderName> {preprocessProvider.name}</ProviderName>
{officialWebsite && preprocessProviderConfig?.websites && ( {officialWebsite && preprocessProviderConfig?.websites && (

View File

@ -1,6 +1,7 @@
import { Flex, RowFlex } from '@cherrystudio/ui' import { Flex, RowFlex } from '@cherrystudio/ui'
import { Button } from '@cherrystudio/ui' import { Button } from '@cherrystudio/ui'
import { Avatar, Select, Tooltip } from 'antd' import { Avatar } from '@cherrystudio/ui'
import { Select, Tooltip } from 'antd'
import { UserRoundPlus } from 'lucide-react' import { UserRoundPlus } from 'lucide-react'
import { useCallback, useMemo } from 'react' import { useCallback, useMemo } from 'react'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
@ -25,7 +26,7 @@ const UserSelector: React.FC<UserSelectorProps> = ({ currentUser, uniqueUsers, o
(userId: string, userName: string) => { (userId: string, userName: string) => {
return ( return (
<RowFlex className="items-center gap-2.5"> <RowFlex className="items-center gap-2.5">
<Avatar size={20} style={{ background: 'var(--color-primary)' }}> <Avatar className="h-5 w-5 bg-primary">
{getUserAvatar(userId)} {getUserAvatar(userId)}
</Avatar> </Avatar>
<span>{userName}</span> <span>{userName}</span>

View File

@ -1,5 +1,6 @@
import { Flex } from '@cherrystudio/ui' import { Flex } from '@cherrystudio/ui'
import { Button } from '@cherrystudio/ui' import { Button } from '@cherrystudio/ui'
import { Avatar } from '@cherrystudio/ui'
import ExpandableText from '@renderer/components/ExpandableText' import ExpandableText from '@renderer/components/ExpandableText'
import ModelIdWithTags from '@renderer/components/ModelIdWithTags' import ModelIdWithTags from '@renderer/components/ModelIdWithTags'
import CustomTag from '@renderer/components/Tags/CustomTag' import CustomTag from '@renderer/components/Tags/CustomTag'
@ -10,7 +11,6 @@ import FileItem from '@renderer/pages/files/FileItem'
import NewApiBatchAddModelPopup from '@renderer/pages/settings/ProviderSettings/ModelList/NewApiBatchAddModelPopup' import NewApiBatchAddModelPopup from '@renderer/pages/settings/ProviderSettings/ModelList/NewApiBatchAddModelPopup'
import type { Model, Provider } from '@renderer/types' import type { Model, Provider } from '@renderer/types'
import { Tooltip } from 'antd' import { Tooltip } from 'antd'
import { Avatar } from 'antd'
import { ChevronRight, Minus, Plus } from 'lucide-react' import { ChevronRight, Minus, Plus } from 'lucide-react'
import React, { memo, useCallback, useMemo, useState } from 'react' import React, { memo, useCallback, useMemo, useState } from 'react'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
@ -202,7 +202,11 @@ const ModelListItem: React.FC<ModelListItemProps> = memo(({ model, provider, onA
boxShadow: 'none' boxShadow: 'none'
}} }}
fileInfo={{ fileInfo={{
icon: <Avatar src={getModelLogo(model.id)}>{model?.name?.[0]?.toUpperCase()}</Avatar>, icon: (
<Avatar src={getModelLogo(model.id)} size="sm">
{model?.name?.[0]?.toUpperCase()}
</Avatar>
),
name: <ModelIdWithTags model={model} />, name: <ModelIdWithTags model={model} />,
extra: model.description && <ExpandableText text={model.description} />, extra: model.description && <ExpandableText text={model.description} />,
ext: '.model', ext: '.model',

View File

@ -64,6 +64,7 @@ const ModelListGroup: React.FC<ModelListGroupProps> = ({
indicator: ( indicator: (
<Tooltip title={t('settings.models.manage.remove_whole_group')} mouseLeaveDelay={0}> <Tooltip title={t('settings.models.manage.remove_whole_group')} mouseLeaveDelay={0}>
<Button <Button
as="span"
variant="light" variant="light"
className="toolbar-item" className="toolbar-item"
startContent={<Minus size={14} />} startContent={<Minus size={14} />}

View File

@ -1,5 +1,6 @@
import { RowFlex } from '@cherrystudio/ui' import { RowFlex } from '@cherrystudio/ui'
import { Button } from '@cherrystudio/ui' import { Button } from '@cherrystudio/ui'
import { Avatar } from '@cherrystudio/ui'
import { FreeTrialModelTag } from '@renderer/components/FreeTrialModelTag' import { FreeTrialModelTag } from '@renderer/components/FreeTrialModelTag'
import { type HealthResult, HealthStatusIndicator } from '@renderer/components/HealthStatusIndicator' import { type HealthResult, HealthStatusIndicator } from '@renderer/components/HealthStatusIndicator'
import ModelIdWithTags from '@renderer/components/ModelIdWithTags' import ModelIdWithTags from '@renderer/components/ModelIdWithTags'
@ -7,7 +8,7 @@ import { getModelLogo } from '@renderer/config/models'
import type { Model } from '@renderer/types' import type { Model } from '@renderer/types'
import type { ModelWithStatus } from '@renderer/types/healthCheck' import type { ModelWithStatus } from '@renderer/types/healthCheck'
import { maskApiKey } from '@renderer/utils/api' import { maskApiKey } from '@renderer/utils/api'
import { Avatar, Tooltip } from 'antd' import { Tooltip } from 'antd'
import { Bolt, Minus } from 'lucide-react' import { Bolt, Minus } from 'lucide-react'
import React, { memo } from 'react' import React, { memo } from 'react'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
@ -37,7 +38,7 @@ const ModelListItem: React.FC<ModelListItemProps> = ({ ref, model, modelStatus,
return ( return (
<ListItem ref={ref}> <ListItem ref={ref}>
<RowFlex className="flex-1 items-center gap-2.5"> <RowFlex className="flex-1 items-center gap-2.5">
<Avatar src={getModelLogo(model.id)} size={24}> <Avatar src={getModelLogo(model.id)} className="h-6 w-6">
{model?.name?.[0]?.toUpperCase()} {model?.name?.[0]?.toUpperCase()}
</Avatar> </Avatar>
<ModelIdWithTags <ModelIdWithTags

View File

@ -1,5 +1,6 @@
import '@renderer/assets/styles/selection-toolbar.css' import '@renderer/assets/styles/selection-toolbar.css'
import { Avatar } from '@cherrystudio/ui'
import { usePreference } from '@data/hooks/usePreference' import { usePreference } from '@data/hooks/usePreference'
import { loggerService } from '@logger' import { loggerService } from '@logger'
import { AppLogo } from '@renderer/config/env' import { AppLogo } from '@renderer/config/env'
@ -8,7 +9,6 @@ import i18n from '@renderer/i18n'
import { defaultLanguage } from '@shared/config/constant' import { defaultLanguage } from '@shared/config/constant'
import type { SelectionActionItem } from '@shared/data/preference/preferenceTypes' import type { SelectionActionItem } from '@shared/data/preference/preferenceTypes'
import { IpcChannel } from '@shared/IpcChannel' import { IpcChannel } from '@shared/IpcChannel'
import { Avatar } from 'antd'
import { ClipboardCheck, ClipboardCopy, ClipboardX, MessageSquareHeart } from 'lucide-react' import { ClipboardCheck, ClipboardCopy, ClipboardX, MessageSquareHeart } from 'lucide-react'
import { DynamicIcon } from 'lucide-react/dynamic' import { DynamicIcon } from 'lucide-react/dynamic'
import type { FC } from 'react' import type { FC } from 'react'