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
import { Avatar } from '@heroui/react'
import React from 'react'
import { Avatar } from '../../base/Avatar'
import { generateColorFromChar, getFirstCharacter, getForegroundColor } from './utils'
interface ProviderAvatarProps {

View File

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

View File

@ -1,6 +1,6 @@
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> = {
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 type { Model } from '@renderer/types'
import type { AvatarProps } from 'antd'
import { Avatar } from 'antd'
import { first } from 'lodash'
import type { FC } from 'react'
@ -12,21 +12,10 @@ interface Props {
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 (
<Avatar
src={getModelLogo(model?.id || '')}
style={{
width: size,
height: size,
minWidth: size,
minHeight: size,
display: 'flex',
alignItems: 'center',
justifyContent: 'center'
}}
{...props}
className={className}>
<Avatar src={getModelLogo(model?.id || '')} {...props} className={classNames}>
{first(model?.name)}
</Avatar>
)

View File

@ -11,6 +11,7 @@ import {
ReloadOutlined
} from '@ant-design/icons'
import { Button } from '@cherrystudio/ui'
import { Avatar } from '@cherrystudio/ui'
import { usePreference } from '@data/hooks/usePreference'
import { loggerService } from '@logger'
import WindowControls from '@renderer/components/WindowControls'
@ -25,7 +26,7 @@ import { useTimer } from '@renderer/hooks/useTimer'
import type { MinAppType } from '@renderer/types'
import { delay } from '@renderer/utils'
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 { useEffect, useMemo, useRef, useState } from 'react'
import { useTranslation } from 'react-i18next'
@ -550,7 +551,7 @@ const MinappPopupContainer: React.FC = () => {
<EmptyView>
<Avatar
src={currentAppInfo?.logo}
size={80}
className="h-20 w-20"
style={{ border: '1px solid var(--color-border)', marginTop: -150 }}
/>
<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 { getModelUniqId } from '@renderer/services/ModelService'
import type { Model, Provider } from '@renderer/types'
import { matchKeywordsInString } from '@renderer/utils'
import { getFancyProviderName } from '@renderer/utils/naming'
import type { SelectProps } from 'antd'
import { Avatar, Select } from 'antd'
import { Select } from 'antd'
import { sortBy } from 'lodash'
import type { BaseSelectRef } from 'rc-select'
import { memo, useCallback, useMemo } from 'react'
@ -107,7 +108,7 @@ const ModelSelector = ({
} else {
return (
<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>
</div>
)

View File

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

View File

@ -1,12 +1,12 @@
import { Center, ColFlex, RowFlex } from '@cherrystudio/ui'
import { Avatar, EmojiAvatar } from '@cherrystudio/ui'
import { cacheService } from '@data/CacheService'
import { usePreference } from '@data/hooks/usePreference'
import DefaultAvatar from '@renderer/assets/images/avatar.png'
import EmojiAvatar from '@renderer/components/Avatar/EmojiAvatar'
import useAvatar from '@renderer/hooks/useAvatar'
import ImageStorage from '@renderer/services/ImageStorage'
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 { useTranslation } from 'react-i18next'
import styled from 'styled-components'

View File

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

View File

@ -53,7 +53,7 @@ const ProviderLogoPicker: FC<Props> = ({ onProviderClick }) => {
{filteredProviders.map(({ id, name, logo }) => (
<Tooltip key={id} title={name} placement="top" mouseLeaveDelay={0}>
<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>
</Tooltip>
))}

View File

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

View File

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

View File

@ -1,4 +1,5 @@
import { Button } from '@cherrystudio/ui'
import { Avatar } from '@cherrystudio/ui'
import AiProvider from '@renderer/aiCore'
import { Navbar, NavbarCenter } from '@renderer/components/app/Navbar'
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 { TerminalConfig } 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 type { FC } from 'react'
import { useCallback, useEffect, useMemo, useState } from 'react'
@ -363,7 +364,7 @@ const CodeToolsPage: FC = () => {
key={provider.id}
style={{ color: 'var(--color-text)', display: 'flex', alignItems: 'center', gap: 4 }}
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)}
<ArrowUpRight size={14} />
</Link>

View File

@ -1,3 +1,4 @@
import { Avatar } from '@cherrystudio/ui'
import { ActionIconButton } from '@renderer/components/Buttons'
import ModelTagsWithLabel from '@renderer/components/ModelTagsWithLabel'
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 type { FileType, Model } from '@renderer/types'
import { getFancyProviderName } from '@renderer/utils'
import { Avatar, Tooltip } from 'antd'
import { Tooltip } from 'antd'
import { useLiveQuery } from 'dexie-react-hooks'
import { first, sortBy } from 'lodash'
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 }} />,
icon: (
<Avatar src={getModelLogo(m.id)} size={20}>
<Avatar src={getModelLogo(m.id)} className="w-5 h-5">
{first(m.name)}
</Avatar>
),
@ -171,7 +172,7 @@ const MentionModelsButton: FC<Props> = ({
),
description: <ModelTagsWithLabel model={m} showLabel={false} size={10} style={{ opacity: 0.8 }} />,
icon: (
<Avatar src={getModelLogo(m.id)} size={20}>
<Avatar src={getModelLogo(m.id)} className="w-5 h-5">
{first(m.name)}
</Avatar>
),

View File

@ -1,8 +1,9 @@
import '@xyflow/react/dist/style.css'
import { RobotOutlined, UserOutlined } from '@ant-design/icons'
import { Avatar } from '@cherrystudio/ui'
import { EmojiAvatar } from '@cherrystudio/ui'
import { usePreference } from '@data/hooks/usePreference'
import EmojiAvatar from '@renderer/components/Avatar/EmojiAvatar'
import ModelAvatar from '@renderer/components/Avatar/ModelAvatar'
import { getModelLogo } from '@renderer/config/models'
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 { Controls, Handle, MiniMap, ReactFlow, ReactFlowProvider } 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 type { FC } 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} />
}
} else {
avatar = <Avatar icon={<UserOutlined />} style={{ backgroundColor: 'var(--color-info)' }} />
avatar = <Avatar icon={<UserOutlined />} className="bg-info" />
}
} else if (nodeType === 'assistant') {
borderColor = 'var(--color-primary)'
@ -94,11 +95,11 @@ const CustomNode: FC<{ data: any }> = ({ data }) => {
<Avatar
src={modelLogo}
icon={!modelLogo ? <RobotOutlined /> : undefined}
style={{ backgroundColor: 'var(--color-primary)' }}
className="bg-primary"
/>
)
} 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 EmojiAvatar from '@renderer/components/Avatar/EmojiAvatar'
import { APP_NAME, AppLogo, isLocalAi } from '@renderer/config/env'
import { getModelLogo } from '@renderer/config/models'
import { useTheme } from '@renderer/context/ThemeProvider'
@ -13,7 +13,6 @@ import { newMessagesActions } from '@renderer/store/newMessage'
import type { Message } from '@renderer/types/newMessage'
import { isEmoji, removeLeadingEmoji } from '@renderer/utils'
import { getMainTextContent } from '@renderer/utils/messageUtils/find'
import { Avatar } from 'antd'
import { CircleChevronDown } from 'lucide-react'
import { type FC, useCallback, useEffect, useRef, useState } from 'react'
import { useTranslation } from 'react-i18next'
@ -227,8 +226,9 @@ const MessageAnchorLine: FC<MessageLineProps> = ({ messages }) => {
{message.role === 'assistant' ? (
<MessageItemAvatar
src={avatarSource}
size={size}
style={{
width: size,
height: size,
border: isLocalAi ? '1px solid var(--color-border-soft)' : 'none',
filter: theme === 'dark' ? 'invert(0.05)' : undefined
}}
@ -246,7 +246,7 @@ const MessageAnchorLine: FC<MessageLineProps> = ({ messages }) => {
{avatar}
</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 { RowFlex } from '@cherrystudio/ui'
import { Avatar, AvatarGroup, RowFlex } from '@cherrystudio/ui'
import { usePreference } from '@data/hooks/usePreference'
import ModelAvatar from '@renderer/components/Avatar/ModelAvatar'
import Scrollbar from '@renderer/components/Scrollbar'
import { getModelLogo } from '@renderer/config/models'
import type { Model } from '@renderer/types'
import { AssistantMessageStatus, type Message } from '@renderer/types/newMessage'
import { lightbulbSoftVariants } from '@renderer/utils/motionVariants'
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 type { FC } from 'react'
import { memo, useCallback } from 'react'
@ -83,7 +85,26 @@ const MessageGroupModelList: FC<MessageGroupModelListProps> = ({ messages, selec
<ModelsContainer $displayMode={foldDisplayMode}>
{isCompact ? (
/* 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 */
<Segmented

View File

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

View File

@ -1,3 +1,4 @@
import { Avatar } from '@cherrystudio/ui'
import { loggerService } from '@logger'
import { DEFAULT_MIN_APPS } from '@renderer/config/minapps'
import { useMinappPopup } from '@renderer/hooks/useMinappPopup'
@ -5,7 +6,6 @@ import { useMinapps } from '@renderer/hooks/useMinapps'
import { useNavbarPosition } from '@renderer/hooks/useNavbar'
import TabsService from '@renderer/services/TabsService'
import { getWebviewLoaded, onWebviewStateChange, setWebviewLoaded } from '@renderer/utils/webviewStateManager'
import { Avatar } from 'antd'
import type { WebviewTag } from 'electron'
import type { FC } from 'react'
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
@ -187,7 +187,7 @@ const MinAppPage: FC = () => {
</ToolbarWrapper>
{!isReady && (
<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 }} />
</LoadingMask>
)}

View File

@ -1,9 +1,9 @@
import { Avatar } from '@cherrystudio/ui'
import { loggerService } from '@logger'
import WebviewContainer from '@renderer/components/MinApp/WebviewContainer'
import { useSettings } from '@renderer/hooks/useSettings'
import type { MinAppType } from '@renderer/types'
import { getWebviewLoaded, setWebviewLoaded } from '@renderer/utils/webviewStateManager'
import { Avatar } from 'antd'
import type { WebviewTag } from 'electron'
import type { FC } from 'react'
import { useCallback, useEffect, useRef, useState } from 'react'
@ -110,7 +110,7 @@ const MinAppFullPageView: FC<Props> = ({ app }) => {
{!isReady && (
<LoadingMask>
<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 }} />
</LoadingOverlay>
</LoadingMask>

View File

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

View File

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

View File

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

View File

@ -1,5 +1,6 @@
import { PlusOutlined } from '@ant-design/icons'
import { Button } from '@cherrystudio/ui'
import { Avatar } from '@cherrystudio/ui'
import { useCache } from '@data/hooks/useCache'
import { usePreference } from '@data/hooks/usePreference'
import { loggerService } from '@logger'
@ -16,7 +17,7 @@ import FileManager from '@renderer/services/FileManager'
import { translateText } from '@renderer/services/TranslateService'
import type { TokenFluxPainting } from '@renderer/types'
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 { Info } from 'lucide-react'
import type { FC } from 'react'
@ -381,7 +382,11 @@ const TokenFluxPage: FC<{ Options: string[] }> = ({ Options }) => {
<SettingTitle style={{ marginBottom: 8 }}>{t('common.provider')}</SettingTitle>
<SettingHelpLink target="_blank" href="https://tokenflux.ai">
{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>
</ProviderTitleContainer>
@ -392,7 +397,11 @@ const TokenFluxPage: FC<{ Options: string[] }> = ({ Options }) => {
{providerOptions.map((provider) => (
<Select.Option value={provider.value} key={provider.value}>
<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}
</SelectOptionContainer>
</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`
display: flex;
justify-content: space-between;

View File

@ -1,6 +1,7 @@
import { PlusOutlined } from '@ant-design/icons'
import { RowFlex } from '@cherrystudio/ui'
import { Button } from '@cherrystudio/ui'
import { Avatar } from '@cherrystudio/ui'
import { useCache } from '@data/hooks/useCache'
import AiProvider from '@renderer/aiCore'
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 FileManager from '@renderer/services/FileManager'
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 type { FC } from 'react'
import { useEffect, useState } from 'react'
@ -367,9 +368,9 @@ const ZhipuPage: FC<{ Options: string[] }> = ({ Options }) => {
{t('paintings.paint_course')}
</SettingHelpLink>
<ProviderLogo
shape="square"
radius="md"
src={getProviderLogo(zhipuProvider.id)}
size={16}
className="h-4 w-4"
style={{ marginLeft: 5 }}
/>
</div>
@ -378,7 +379,7 @@ const ZhipuPage: FC<{ Options: string[] }> = ({ Options }) => {
{providerOptions.map((provider) => (
<Select.Option value={provider.value} key={provider.value}>
<SelectOptionContainer>
<ProviderLogo shape="square" src={getProviderLogo(provider.value || '')} size={16} />
<ProviderLogo radius="md" src={getProviderLogo(provider.value || '')} className="h-4 w-4" />
{provider.label}
</SelectOptionContainer>
</Select.Option>

View File

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

View File

@ -1,12 +1,13 @@
import { ExportOutlined } from '@ant-design/icons'
import { Flex } from '@cherrystudio/ui'
import { Button } from '@cherrystudio/ui'
import { Avatar } from '@cherrystudio/ui'
import { ApiKeyListPopup } from '@renderer/components/Popups/ApiKeyListPopup'
import { getPreprocessProviderLogo, PREPROCESS_PROVIDER_CONFIG } from '@renderer/config/preprocessProviders'
import { usePreprocessProvider } from '@renderer/hooks/usePreprocess'
import type { PreprocessProvider } from '@renderer/types'
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 { List } from 'lucide-react'
import type { FC } from 'react'
@ -73,7 +74,11 @@ const PreprocessProviderSettings: FC<Props> = ({ provider: _provider }) => {
<>
<SettingTitle>
<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>
{officialWebsite && preprocessProviderConfig?.websites && (

View File

@ -1,6 +1,7 @@
import { Flex, RowFlex } 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 { useCallback, useMemo } from 'react'
import { useTranslation } from 'react-i18next'
@ -25,7 +26,7 @@ const UserSelector: React.FC<UserSelectorProps> = ({ currentUser, uniqueUsers, o
(userId: string, userName: string) => {
return (
<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)}
</Avatar>
<span>{userName}</span>

View File

@ -1,5 +1,6 @@
import { Flex } from '@cherrystudio/ui'
import { Button } from '@cherrystudio/ui'
import { Avatar } from '@cherrystudio/ui'
import ExpandableText from '@renderer/components/ExpandableText'
import ModelIdWithTags from '@renderer/components/ModelIdWithTags'
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 type { Model, Provider } from '@renderer/types'
import { Tooltip } from 'antd'
import { Avatar } from 'antd'
import { ChevronRight, Minus, Plus } from 'lucide-react'
import React, { memo, useCallback, useMemo, useState } from 'react'
import { useTranslation } from 'react-i18next'
@ -202,7 +202,11 @@ const ModelListItem: React.FC<ModelListItemProps> = memo(({ model, provider, onA
boxShadow: 'none'
}}
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} />,
extra: model.description && <ExpandableText text={model.description} />,
ext: '.model',

View File

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

View File

@ -1,5 +1,6 @@
import { RowFlex } from '@cherrystudio/ui'
import { Button } from '@cherrystudio/ui'
import { Avatar } from '@cherrystudio/ui'
import { FreeTrialModelTag } from '@renderer/components/FreeTrialModelTag'
import { type HealthResult, HealthStatusIndicator } from '@renderer/components/HealthStatusIndicator'
import ModelIdWithTags from '@renderer/components/ModelIdWithTags'
@ -7,7 +8,7 @@ import { getModelLogo } from '@renderer/config/models'
import type { Model } from '@renderer/types'
import type { ModelWithStatus } from '@renderer/types/healthCheck'
import { maskApiKey } from '@renderer/utils/api'
import { Avatar, Tooltip } from 'antd'
import { Tooltip } from 'antd'
import { Bolt, Minus } from 'lucide-react'
import React, { memo } from 'react'
import { useTranslation } from 'react-i18next'
@ -37,7 +38,7 @@ const ModelListItem: React.FC<ModelListItemProps> = ({ ref, model, modelStatus,
return (
<ListItem ref={ref}>
<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()}
</Avatar>
<ModelIdWithTags

View File

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