mirror of
https://github.com/CherryHQ/cherry-studio.git
synced 2025-12-23 18:10:26 +08:00
refactor: remove ProviderAvatar component and related files
- Deleted the ProviderAvatar component and its associated utility functions and stories to streamline the codebase. - Updated index.ts to remove the export of ProviderAvatar, ensuring a cleaner component structure.
This commit is contained in:
parent
d470fd8b88
commit
6c71b92d1d
@ -1,93 +0,0 @@
|
||||
// Original path: src/renderer/src/components/ProviderAvatar.tsx
|
||||
import React from 'react'
|
||||
|
||||
import { Avatar } from '../../base/Avatar'
|
||||
import { generateColorFromChar, getFirstCharacter, getForegroundColor } from './utils'
|
||||
|
||||
interface ProviderAvatarProps {
|
||||
providerId: string
|
||||
providerName: string
|
||||
logoSrc?: string
|
||||
size?: 'sm' | 'md' | 'lg' | number
|
||||
className?: string
|
||||
style?: React.CSSProperties
|
||||
renderCustomLogo?: (providerId: string) => React.ReactNode
|
||||
}
|
||||
|
||||
export const ProviderAvatar: React.FC<ProviderAvatarProps> = ({
|
||||
providerId,
|
||||
providerName,
|
||||
logoSrc,
|
||||
size = 'md',
|
||||
className = '',
|
||||
style,
|
||||
renderCustomLogo
|
||||
}) => {
|
||||
// Convert numeric size to HeroUI size props
|
||||
const getAvatarSize = () => {
|
||||
if (typeof size === 'number') {
|
||||
// For custom numeric sizes, we'll use style override
|
||||
return 'md'
|
||||
}
|
||||
return size
|
||||
}
|
||||
|
||||
const getCustomStyle = () => {
|
||||
const baseStyle: React.CSSProperties = {
|
||||
...style
|
||||
}
|
||||
|
||||
if (typeof size === 'number') {
|
||||
baseStyle.width = `${size}px`
|
||||
baseStyle.height = `${size}px`
|
||||
}
|
||||
|
||||
return baseStyle
|
||||
}
|
||||
|
||||
// Check if custom logo renderer is provided for special providers
|
||||
if (renderCustomLogo) {
|
||||
const customLogo = renderCustomLogo(providerId)
|
||||
if (customLogo) {
|
||||
return (
|
||||
<div
|
||||
className={`flex items-center justify-center rounded-full border border-gray-200 dark:border-gray-700 ${className}`}
|
||||
style={getCustomStyle()}>
|
||||
<div className="w-4/5 h-4/5 flex items-center justify-center">{customLogo}</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// If logo source is provided, render image avatar
|
||||
if (logoSrc) {
|
||||
return (
|
||||
<Avatar
|
||||
src={logoSrc}
|
||||
size={getAvatarSize()}
|
||||
className={`border border-gray-200 dark:border-gray-700 ${className}`}
|
||||
style={getCustomStyle()}
|
||||
imgProps={{ draggable: false }}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
// Default: generate avatar with first character and background color
|
||||
const backgroundColor = generateColorFromChar(providerName)
|
||||
const color = providerName ? getForegroundColor(backgroundColor) : 'white'
|
||||
|
||||
return (
|
||||
<Avatar
|
||||
name={getFirstCharacter(providerName)}
|
||||
size={getAvatarSize()}
|
||||
className={`border border-gray-200 dark:border-gray-700 ${className}`}
|
||||
style={{
|
||||
backgroundColor,
|
||||
color,
|
||||
...getCustomStyle()
|
||||
}}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export default ProviderAvatar
|
||||
@ -1,37 +0,0 @@
|
||||
// Utility functions for ProviderAvatar component
|
||||
|
||||
export function generateColorFromChar(char: string): string {
|
||||
const seed = char.charCodeAt(0)
|
||||
const a = 1664525
|
||||
const c = 1013904223
|
||||
const m = Math.pow(2, 32)
|
||||
|
||||
let r = (a * seed + c) % m
|
||||
let g = (a * r + c) % m
|
||||
let b = (a * g + c) % m
|
||||
|
||||
r = Math.floor((r / m) * 256)
|
||||
g = Math.floor((g / m) * 256)
|
||||
b = Math.floor((b / m) * 256)
|
||||
|
||||
const toHex = (n: number) => n.toString(16).padStart(2, '0')
|
||||
return `#${toHex(r)}${toHex(g)}${toHex(b)}`
|
||||
}
|
||||
|
||||
export function getFirstCharacter(str: string): string {
|
||||
for (const char of str) {
|
||||
return char
|
||||
}
|
||||
return ''
|
||||
}
|
||||
|
||||
export function getForegroundColor(backgroundColor: string): string {
|
||||
// Simple luminance calculation
|
||||
const hex = backgroundColor.replace('#', '')
|
||||
const r = parseInt(hex.substring(0, 2), 16) / 255
|
||||
const g = parseInt(hex.substring(2, 4), 16) / 255
|
||||
const b = parseInt(hex.substring(4, 6), 16) / 255
|
||||
|
||||
const luminance = 0.2126 * r + 0.7152 * g + 0.0722 * b
|
||||
return luminance > 0.179 ? '#000000' : '#FFFFFF'
|
||||
}
|
||||
@ -21,7 +21,6 @@ export { default as Ellipsis } from './display/Ellipsis'
|
||||
export { default as ExpandableText } from './display/ExpandableText'
|
||||
export { default as ListItem } from './display/ListItem'
|
||||
export { default as MaxContextCount } from './display/MaxContextCount'
|
||||
export { ProviderAvatar } from './display/ProviderAvatar'
|
||||
export { default as ThinkingEffect } from './display/ThinkingEffect'
|
||||
|
||||
// Layout Components
|
||||
|
||||
@ -1,210 +0,0 @@
|
||||
import type { Meta, StoryObj } from '@storybook/react'
|
||||
|
||||
import { ProviderAvatar } from '../../../src/components/display/ProviderAvatar'
|
||||
|
||||
// 定义 Story 的元数据
|
||||
const meta: Meta<typeof ProviderAvatar> = {
|
||||
title: 'Display/ProviderAvatar',
|
||||
component: ProviderAvatar,
|
||||
parameters: {
|
||||
layout: 'centered'
|
||||
},
|
||||
tags: ['autodocs'],
|
||||
argTypes: {
|
||||
size: {
|
||||
control: { type: 'range', min: 16, max: 128, step: 4 },
|
||||
description: '头像尺寸',
|
||||
defaultValue: 40
|
||||
},
|
||||
providerId: {
|
||||
control: 'text',
|
||||
description: '提供商 ID'
|
||||
},
|
||||
providerName: {
|
||||
control: 'text',
|
||||
description: '提供商名称'
|
||||
},
|
||||
logoSrc: {
|
||||
control: 'text',
|
||||
description: '图片 Logo 地址'
|
||||
},
|
||||
className: {
|
||||
control: 'text',
|
||||
description: '自定义类名'
|
||||
}
|
||||
}
|
||||
} satisfies Meta<typeof ProviderAvatar>
|
||||
|
||||
export default meta
|
||||
type Story = StoryObj<typeof meta>
|
||||
|
||||
// 基础用法:文字头像
|
||||
export const Default: Story = {
|
||||
args: {
|
||||
providerId: 'openai',
|
||||
providerName: 'OpenAI',
|
||||
size: 40
|
||||
}
|
||||
}
|
||||
|
||||
// 带图片的头像
|
||||
export const WithImage: Story = {
|
||||
args: {
|
||||
providerId: 'custom',
|
||||
providerName: 'Custom Provider',
|
||||
logoSrc: 'https://via.placeholder.com/150',
|
||||
size: 40
|
||||
}
|
||||
}
|
||||
|
||||
// 不同尺寸展示
|
||||
export const Sizes: Story = {
|
||||
render: (args) => (
|
||||
<div className="flex items-center gap-4">
|
||||
<ProviderAvatar {...args} providerName="Small" size="sm" />
|
||||
<ProviderAvatar {...args} providerName="Medium" size="md" />
|
||||
<ProviderAvatar {...args} providerName="Large" size="lg" />
|
||||
<ProviderAvatar {...args} providerName="24px" size={24} />
|
||||
<ProviderAvatar {...args} providerName="48px" size={48} />
|
||||
<ProviderAvatar {...args} providerName="72px" size={72} />
|
||||
</div>
|
||||
),
|
||||
args: {
|
||||
providerId: 'size-demo'
|
||||
}
|
||||
}
|
||||
|
||||
// 不同首字母的颜色生成
|
||||
export const ColorGeneration: Story = {
|
||||
args: {
|
||||
providerId: 'azure',
|
||||
providerName: 'Azure',
|
||||
size: 40
|
||||
},
|
||||
render: (args) => (
|
||||
<div className="flex items-center gap-4">
|
||||
<ProviderAvatar {...args} providerId="azure" providerName="Azure" size={40} />
|
||||
<ProviderAvatar {...args} providerId="anthropic" providerName="Anthropic" size={40} />
|
||||
<ProviderAvatar {...args} providerId="baidu" providerName="Baidu" size={40} />
|
||||
<ProviderAvatar {...args} providerId="google" providerName="Google" size={40} />
|
||||
<ProviderAvatar {...args} providerId="meta" providerName="Meta" size={40} />
|
||||
<ProviderAvatar {...args} providerId="openai" providerName="OpenAI" size={40} />
|
||||
<ProviderAvatar {...args} providerId="perplexity" providerName="Perplexity" size={40} />
|
||||
<ProviderAvatar {...args} providerId="zhipu" providerName="智谱" size={40} />
|
||||
<ProviderAvatar {...args} providerId="alibaba" providerName="阿里云" size={40} />
|
||||
<ProviderAvatar {...args} providerId="tencent" providerName="腾讯云" size={40} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
// 自定义 SVG Logo
|
||||
export const WithCustomSvg: Story = {
|
||||
args: {
|
||||
providerId: 'custom-svg',
|
||||
providerName: 'Custom SVG',
|
||||
size: 40,
|
||||
renderCustomLogo: () => (
|
||||
<svg viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M12 2L2 7L12 12L22 7L12 2Z" />
|
||||
<path d="M2 17L12 22L22 17L12 12L2 17Z" />
|
||||
<path d="M2 12L12 17L22 12L12 7L2 12Z" />
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// 混合展示
|
||||
export const Mixed: Story = {
|
||||
args: {
|
||||
providerId: 'text',
|
||||
providerName: 'Text Avatar',
|
||||
size: 40
|
||||
},
|
||||
render: (args) => (
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: '16px' }}>
|
||||
<ProviderAvatar {...args} providerId="text" providerName="Text Avatar" size={40} />
|
||||
<ProviderAvatar
|
||||
{...args}
|
||||
providerId="image"
|
||||
providerName="Image Avatar"
|
||||
logoSrc="https://via.placeholder.com/150/0000FF/FFFFFF?text=IMG"
|
||||
size={40}
|
||||
/>
|
||||
<ProviderAvatar
|
||||
{...args}
|
||||
providerId="svg"
|
||||
providerName="SVG Avatar"
|
||||
size={40}
|
||||
renderCustomLogo={() => (
|
||||
<svg viewBox="0 0 24 24" fill="#FF6B6B">
|
||||
<circle cx="12" cy="12" r="10" />
|
||||
</svg>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
// 空值处理
|
||||
export const EmptyValues: Story = {
|
||||
args: {
|
||||
providerId: 'empty',
|
||||
providerName: '',
|
||||
size: 40
|
||||
},
|
||||
render: (args) => (
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: '16px' }}>
|
||||
<div>
|
||||
<p style={{ margin: '0 0 8px 0', fontSize: '12px', color: '#666' }}>空名称</p>
|
||||
<ProviderAvatar {...args} providerId="empty" providerName="" size={40} />
|
||||
</div>
|
||||
<div>
|
||||
<p style={{ margin: '0 0 8px 0', fontSize: '12px', color: '#666' }}>正常显示</p>
|
||||
<ProviderAvatar {...args} providerId="normal" providerName="Normal" size={40} />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
// 自定义样式
|
||||
export const CustomStyle: Story = {
|
||||
args: {
|
||||
providerId: 'custom-style',
|
||||
providerName: 'Custom',
|
||||
size: 40,
|
||||
style: {
|
||||
border: '2px solid #FF6B6B',
|
||||
boxShadow: '0 4px 8px rgba(0,0,0,0.1)'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 响应式网格展示
|
||||
export const ResponsiveGrid: Story = {
|
||||
args: {
|
||||
providerId: 'provider-a',
|
||||
providerName: 'Provider A',
|
||||
size: 48
|
||||
},
|
||||
render: (args) => (
|
||||
<div
|
||||
style={{
|
||||
display: 'grid',
|
||||
gridTemplateColumns: 'repeat(auto-fill, minmax(60px, 1fr))',
|
||||
gap: '16px',
|
||||
width: '400px'
|
||||
}}>
|
||||
{['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L'].map((letter) => (
|
||||
<div key={letter} style={{ textAlign: 'center' }}>
|
||||
<ProviderAvatar
|
||||
{...args}
|
||||
providerId={`provider-${letter.toLowerCase()}`}
|
||||
providerName={`Provider ${letter}`}
|
||||
size={48}
|
||||
/>
|
||||
<div style={{ fontSize: '12px', marginTop: '4px', color: '#666' }}>{letter}</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@ -1,10 +1,9 @@
|
||||
import { Avatar } from '@cherrystudio/ui'
|
||||
import { Avatar, cn } 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 React from 'react'
|
||||
import styled from 'styled-components'
|
||||
|
||||
interface ProviderAvatarPrimitiveProps {
|
||||
providerId: string
|
||||
@ -23,28 +22,6 @@ interface ProviderAvatarProps {
|
||||
style?: React.CSSProperties
|
||||
}
|
||||
|
||||
const ProviderSvgLogo = styled.div`
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border: 0.5px solid var(--color-border);
|
||||
border-radius: 100%;
|
||||
|
||||
& > svg {
|
||||
width: 80%;
|
||||
height: 80%;
|
||||
}
|
||||
`
|
||||
|
||||
const ProviderLogo = styled(Avatar)`
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border: 0.5px solid var(--color-border);
|
||||
`
|
||||
|
||||
export const ProviderAvatarPrimitive: React.FC<ProviderAvatarPrimitiveProps> = ({
|
||||
providerId,
|
||||
providerName,
|
||||
@ -53,33 +30,42 @@ export const ProviderAvatarPrimitive: React.FC<ProviderAvatarPrimitiveProps> = (
|
||||
className,
|
||||
style
|
||||
}) => {
|
||||
// Special handling for Poe provider
|
||||
if (providerId === 'poe') {
|
||||
return (
|
||||
<ProviderSvgLogo className={className} style={style}>
|
||||
<PoeLogo fontSize={size} />
|
||||
</ProviderSvgLogo>
|
||||
<div
|
||||
className={cn(
|
||||
'flex items-center justify-center rounded-full',
|
||||
'border-[0.5px] border-[var(--color-border)]',
|
||||
className
|
||||
)}
|
||||
style={{ width: size, height: size, ...style }}>
|
||||
<PoeLogo fontSize={size ? size * 0.8 : undefined} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
// If logo source is provided, render image avatar
|
||||
if (logoSrc) {
|
||||
return (
|
||||
<ProviderLogo
|
||||
draggable="false"
|
||||
radius="full"
|
||||
<Avatar
|
||||
src={logoSrc}
|
||||
className={className}
|
||||
radius="full"
|
||||
className={cn('border-[0.5px] border-[var(--color-border)]', className)}
|
||||
style={{ width: size, height: size, ...style }}
|
||||
imgProps={{ draggable: false }}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
// Default: generate avatar with first character and background color
|
||||
const backgroundColor = generateColorFromChar(providerName)
|
||||
const color = providerName ? getForegroundColor(backgroundColor) : 'white'
|
||||
|
||||
return (
|
||||
<ProviderLogo
|
||||
<Avatar
|
||||
radius="full"
|
||||
className={className}
|
||||
className={cn('border-[0.5px] border-[var(--color-border)]', className)}
|
||||
style={{
|
||||
width: size,
|
||||
height: size,
|
||||
@ -88,7 +74,7 @@ export const ProviderAvatarPrimitive: React.FC<ProviderAvatarPrimitiveProps> = (
|
||||
...style
|
||||
}}>
|
||||
{getFirstCharacter(providerName)}
|
||||
</ProviderLogo>
|
||||
</Avatar>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user