mirror of
https://github.com/langgenius/dify.git
synced 2026-05-03 00:48:04 +08:00
Merge remote-tracking branch 'origin/main' into feat/model-plugins-implementing
This commit is contained in:
@ -1,308 +1,114 @@
|
||||
import { fireEvent, render, screen, waitFor } from '@testing-library/react'
|
||||
import Avatar from '../index'
|
||||
import { render, screen } from '@testing-library/react'
|
||||
import { Avatar } from '../index'
|
||||
|
||||
describe('Avatar', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
})
|
||||
|
||||
// Rendering tests - verify component renders correctly in different states
|
||||
describe('Rendering', () => {
|
||||
it('should render img element with correct alt and src when avatar URL is provided', () => {
|
||||
const avatarUrl = 'https://example.com/avatar.jpg'
|
||||
const props = { name: 'John Doe', avatar: avatarUrl }
|
||||
|
||||
render(<Avatar {...props} />)
|
||||
it('should render img element when avatar URL is provided', () => {
|
||||
render(<Avatar name="John Doe" avatar="https://example.com/avatar.jpg" />)
|
||||
|
||||
const img = screen.getByRole('img', { name: 'John Doe' })
|
||||
expect(img).toBeInTheDocument()
|
||||
expect(img).toHaveAttribute('src', avatarUrl)
|
||||
expect(img).toHaveAttribute('src', 'https://example.com/avatar.jpg')
|
||||
})
|
||||
|
||||
it('should render fallback div with uppercase initial when avatar is null', () => {
|
||||
const props = { name: 'alice', avatar: null }
|
||||
|
||||
render(<Avatar {...props} />)
|
||||
it('should render fallback with uppercase initial when avatar is null', () => {
|
||||
render(<Avatar name="alice" avatar={null} />)
|
||||
|
||||
expect(screen.queryByRole('img')).not.toBeInTheDocument()
|
||||
expect(screen.getByText('A')).toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
|
||||
// Props tests - verify all props are applied correctly
|
||||
describe('Props', () => {
|
||||
describe('size prop', () => {
|
||||
it.each([
|
||||
{ size: undefined, expected: '30px', label: 'default (30px)' },
|
||||
{ size: 50, expected: '50px', label: 'custom (50px)' },
|
||||
])('should apply $label size to img element', ({ size, expected }) => {
|
||||
const props = { name: 'Test', avatar: 'https://example.com/avatar.jpg', size }
|
||||
it('should render both image and fallback when avatar is provided', () => {
|
||||
render(<Avatar name="John" avatar="https://example.com/avatar.jpg" />)
|
||||
|
||||
render(<Avatar {...props} />)
|
||||
|
||||
expect(screen.getByRole('img')).toHaveStyle({
|
||||
width: expected,
|
||||
height: expected,
|
||||
fontSize: expected,
|
||||
lineHeight: expected,
|
||||
})
|
||||
})
|
||||
|
||||
it('should apply size to fallback div when avatar is null', () => {
|
||||
const props = { name: 'Test', avatar: null, size: 40 }
|
||||
|
||||
render(<Avatar {...props} />)
|
||||
|
||||
const textElement = screen.getByText('T')
|
||||
const outerDiv = textElement.parentElement as HTMLElement
|
||||
expect(outerDiv).toHaveStyle({ width: '40px', height: '40px' })
|
||||
})
|
||||
})
|
||||
|
||||
describe('className prop', () => {
|
||||
it('should merge className with default avatar classes on img', () => {
|
||||
const props = {
|
||||
name: 'Test',
|
||||
avatar: 'https://example.com/avatar.jpg',
|
||||
className: 'custom-class',
|
||||
}
|
||||
|
||||
render(<Avatar {...props} />)
|
||||
|
||||
const img = screen.getByRole('img')
|
||||
expect(img).toHaveClass('custom-class')
|
||||
expect(img).toHaveClass('shrink-0', 'flex', 'items-center', 'rounded-full', 'bg-primary-600')
|
||||
})
|
||||
|
||||
it('should merge className with default avatar classes on fallback div', () => {
|
||||
const props = {
|
||||
name: 'Test',
|
||||
avatar: null,
|
||||
className: 'my-custom-class',
|
||||
}
|
||||
|
||||
render(<Avatar {...props} />)
|
||||
|
||||
const textElement = screen.getByText('T')
|
||||
const outerDiv = textElement.parentElement as HTMLElement
|
||||
expect(outerDiv).toHaveClass('my-custom-class')
|
||||
expect(outerDiv).toHaveClass('shrink-0', 'flex', 'items-center', 'rounded-full', 'bg-primary-600')
|
||||
})
|
||||
})
|
||||
|
||||
describe('textClassName prop', () => {
|
||||
it('should apply textClassName to the initial text element', () => {
|
||||
const props = {
|
||||
name: 'Test',
|
||||
avatar: null,
|
||||
textClassName: 'custom-text-class',
|
||||
}
|
||||
|
||||
render(<Avatar {...props} />)
|
||||
|
||||
const textElement = screen.getByText('T')
|
||||
expect(textElement).toHaveClass('custom-text-class')
|
||||
expect(textElement).toHaveClass('scale-[0.4]', 'text-center', 'text-white')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
// State Management tests - verify useState and useEffect behavior
|
||||
describe('State Management', () => {
|
||||
it('should switch to fallback when image fails to load', async () => {
|
||||
const props = { name: 'John', avatar: 'https://example.com/broken.jpg' }
|
||||
render(<Avatar {...props} />)
|
||||
const img = screen.getByRole('img')
|
||||
|
||||
fireEvent.error(img)
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.queryByRole('img')).not.toBeInTheDocument()
|
||||
})
|
||||
expect(screen.getByText('J')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('should reset error state when avatar URL changes', async () => {
|
||||
const initialProps = { name: 'John', avatar: 'https://example.com/broken.jpg' }
|
||||
const { rerender } = render(<Avatar {...initialProps} />)
|
||||
const img = screen.getByRole('img')
|
||||
|
||||
// First, trigger error
|
||||
fireEvent.error(img)
|
||||
await waitFor(() => {
|
||||
expect(screen.queryByRole('img')).not.toBeInTheDocument()
|
||||
})
|
||||
expect(screen.getByText('J')).toBeInTheDocument()
|
||||
|
||||
rerender(<Avatar name="John" avatar="https://example.com/new-avatar.jpg" />)
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByRole('img')).toBeInTheDocument()
|
||||
})
|
||||
expect(screen.queryByText('J')).not.toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('should not reset error state if avatar becomes null', async () => {
|
||||
const initialProps = { name: 'John', avatar: 'https://example.com/broken.jpg' }
|
||||
const { rerender } = render(<Avatar {...initialProps} />)
|
||||
|
||||
// Trigger error
|
||||
fireEvent.error(screen.getByRole('img'))
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText('J')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
rerender(<Avatar name="John" avatar={null} />)
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.queryByRole('img')).not.toBeInTheDocument()
|
||||
})
|
||||
expect(screen.getByRole('img')).toBeInTheDocument()
|
||||
expect(screen.getByText('J')).toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
|
||||
// Event Handlers tests - verify onError callback behavior
|
||||
describe('Event Handlers', () => {
|
||||
it('should call onError with true when image fails to load', () => {
|
||||
const onErrorMock = vi.fn()
|
||||
const props = {
|
||||
name: 'John',
|
||||
avatar: 'https://example.com/broken.jpg',
|
||||
onError: onErrorMock,
|
||||
}
|
||||
render(<Avatar {...props} />)
|
||||
describe('Size variants', () => {
|
||||
it.each([
|
||||
{ size: 'xxs' as const, expectedClass: 'size-4' },
|
||||
{ size: 'xs' as const, expectedClass: 'size-5' },
|
||||
{ size: 'sm' as const, expectedClass: 'size-6' },
|
||||
{ size: 'md' as const, expectedClass: 'size-8' },
|
||||
{ size: 'lg' as const, expectedClass: 'size-9' },
|
||||
{ size: 'xl' as const, expectedClass: 'size-10' },
|
||||
{ size: '2xl' as const, expectedClass: 'size-12' },
|
||||
{ size: '3xl' as const, expectedClass: 'size-16' },
|
||||
])('should apply $expectedClass for size="$size"', ({ size, expectedClass }) => {
|
||||
const { container } = render(<Avatar name="Test" avatar={null} size={size} />)
|
||||
|
||||
fireEvent.error(screen.getByRole('img'))
|
||||
|
||||
expect(onErrorMock).toHaveBeenCalledTimes(1)
|
||||
expect(onErrorMock).toHaveBeenCalledWith(true)
|
||||
const root = container.firstElementChild as HTMLElement
|
||||
expect(root).toHaveClass(expectedClass)
|
||||
})
|
||||
|
||||
it('should call onError with false when image loads successfully', () => {
|
||||
const onErrorMock = vi.fn()
|
||||
const props = {
|
||||
name: 'John',
|
||||
avatar: 'https://example.com/avatar.jpg',
|
||||
onError: onErrorMock,
|
||||
}
|
||||
render(<Avatar {...props} />)
|
||||
it('should default to md size when size is not specified', () => {
|
||||
const { container } = render(<Avatar name="Test" avatar={null} />)
|
||||
|
||||
fireEvent.load(screen.getByRole('img'))
|
||||
|
||||
expect(onErrorMock).toHaveBeenCalledTimes(1)
|
||||
expect(onErrorMock).toHaveBeenCalledWith(false)
|
||||
})
|
||||
|
||||
it('should not throw when onError is not provided', async () => {
|
||||
const props = { name: 'John', avatar: 'https://example.com/broken.jpg' }
|
||||
render(<Avatar {...props} />)
|
||||
|
||||
expect(() => fireEvent.error(screen.getByRole('img'))).not.toThrow()
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText('J')).toBeInTheDocument()
|
||||
})
|
||||
const root = container.firstElementChild as HTMLElement
|
||||
expect(root).toHaveClass('size-8')
|
||||
})
|
||||
})
|
||||
|
||||
describe('className prop', () => {
|
||||
it('should merge className with avatar variant classes on root', () => {
|
||||
const { container } = render(
|
||||
<Avatar name="Test" avatar={null} className="custom-class" />,
|
||||
)
|
||||
|
||||
const root = container.firstElementChild as HTMLElement
|
||||
expect(root).toHaveClass('custom-class')
|
||||
expect(root).toHaveClass('rounded-full', 'bg-primary-600')
|
||||
})
|
||||
})
|
||||
|
||||
// Edge Cases tests - verify handling of unusual inputs
|
||||
describe('Edge Cases', () => {
|
||||
it('should handle empty string name gracefully', () => {
|
||||
const props = { name: '', avatar: null }
|
||||
const { container } = render(<Avatar name="" avatar={null} />)
|
||||
|
||||
const { container } = render(<Avatar {...props} />)
|
||||
|
||||
// Note: Using querySelector here because empty name produces no visible text,
|
||||
// making semantic queries (getByRole, getByText) impossible
|
||||
const textElement = container.querySelector('.text-white') as HTMLElement
|
||||
expect(textElement).toBeInTheDocument()
|
||||
expect(textElement.textContent).toBe('')
|
||||
const fallback = container.querySelector('.text-white') as HTMLElement
|
||||
expect(fallback).toBeInTheDocument()
|
||||
expect(fallback.textContent).toBe('')
|
||||
})
|
||||
|
||||
it.each([
|
||||
{ name: '中文名', expected: '中', label: 'Chinese characters' },
|
||||
{ name: '123User', expected: '1', label: 'number' },
|
||||
])('should display first character when name starts with $label', ({ name, expected }) => {
|
||||
const props = { name, avatar: null }
|
||||
|
||||
render(<Avatar {...props} />)
|
||||
render(<Avatar name={name} avatar={null} />)
|
||||
|
||||
expect(screen.getByText(expected)).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('should handle empty string avatar as falsy value', () => {
|
||||
const props = { name: 'Test', avatar: '' as string | null }
|
||||
|
||||
render(<Avatar {...props} />)
|
||||
render(<Avatar name="Test" avatar={'' as string | null} />)
|
||||
|
||||
expect(screen.queryByRole('img')).not.toBeInTheDocument()
|
||||
expect(screen.getByText('T')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('should handle undefined className and textClassName', () => {
|
||||
const props = { name: 'Test', avatar: null }
|
||||
|
||||
render(<Avatar {...props} />)
|
||||
|
||||
const textElement = screen.getByText('T')
|
||||
const outerDiv = textElement.parentElement as HTMLElement
|
||||
expect(outerDiv).toHaveClass('shrink-0', 'flex', 'items-center', 'rounded-full', 'bg-primary-600')
|
||||
})
|
||||
|
||||
it.each([
|
||||
{ size: 0, expected: '0px', label: 'zero' },
|
||||
{ size: 1000, expected: '1000px', label: 'very large' },
|
||||
])('should handle $label size value', ({ size, expected }) => {
|
||||
const props = { name: 'Test', avatar: null, size }
|
||||
|
||||
render(<Avatar {...props} />)
|
||||
|
||||
const textElement = screen.getByText('T')
|
||||
const outerDiv = textElement.parentElement as HTMLElement
|
||||
expect(outerDiv).toHaveStyle({ width: expected, height: expected })
|
||||
})
|
||||
})
|
||||
|
||||
// Combined props tests - verify props work together correctly
|
||||
describe('Combined Props', () => {
|
||||
it('should apply all props correctly when used together', () => {
|
||||
const onErrorMock = vi.fn()
|
||||
const props = {
|
||||
name: 'Test User',
|
||||
avatar: 'https://example.com/avatar.jpg',
|
||||
size: 64,
|
||||
className: 'custom-avatar',
|
||||
onError: onErrorMock,
|
||||
}
|
||||
describe('onLoadingStatusChange', () => {
|
||||
it('should render image when avatar and onLoadingStatusChange are provided', () => {
|
||||
render(
|
||||
<Avatar
|
||||
name="John"
|
||||
avatar="https://example.com/avatar.jpg"
|
||||
onLoadingStatusChange={vi.fn()}
|
||||
/>,
|
||||
)
|
||||
|
||||
render(<Avatar {...props} />)
|
||||
|
||||
const img = screen.getByRole('img')
|
||||
expect(img).toHaveAttribute('alt', 'Test User')
|
||||
expect(img).toHaveAttribute('src', 'https://example.com/avatar.jpg')
|
||||
expect(img).toHaveStyle({ width: '64px', height: '64px' })
|
||||
expect(img).toHaveClass('custom-avatar')
|
||||
|
||||
// Trigger load to verify onError callback
|
||||
fireEvent.load(img)
|
||||
expect(onErrorMock).toHaveBeenCalledWith(false)
|
||||
expect(screen.getByRole('img')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('should apply all fallback props correctly when used together', () => {
|
||||
const props = {
|
||||
name: 'Fallback User',
|
||||
avatar: null,
|
||||
size: 48,
|
||||
className: 'fallback-custom',
|
||||
textClassName: 'custom-text-style',
|
||||
}
|
||||
it('should not render image when avatar is null even with onLoadingStatusChange', () => {
|
||||
const onStatusChange = vi.fn()
|
||||
render(
|
||||
<Avatar name="John" avatar={null} onLoadingStatusChange={onStatusChange} />,
|
||||
)
|
||||
|
||||
render(<Avatar {...props} />)
|
||||
|
||||
const textElement = screen.getByText('F')
|
||||
const outerDiv = textElement.parentElement as HTMLElement
|
||||
expect(outerDiv).toHaveClass('fallback-custom')
|
||||
expect(outerDiv).toHaveStyle({ width: '48px', height: '48px' })
|
||||
expect(textElement).toHaveClass('custom-text-style')
|
||||
expect(screen.queryByRole('img')).not.toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import type { Meta, StoryObj } from '@storybook/nextjs-vite'
|
||||
import Avatar from '.'
|
||||
import { Avatar } from '.'
|
||||
|
||||
const meta = {
|
||||
title: 'Base/Data Display/Avatar',
|
||||
@ -7,12 +7,12 @@ const meta = {
|
||||
parameters: {
|
||||
docs: {
|
||||
description: {
|
||||
component: 'Initials or image-based avatar used across contacts and member lists. Falls back to the first letter when the image fails to load.',
|
||||
component: 'Initials or image-based avatar built on Base UI. Falls back to the first letter when the image fails to load.',
|
||||
},
|
||||
source: {
|
||||
language: 'tsx',
|
||||
code: `
|
||||
<Avatar name="Alex Doe" avatar="https://cloud.dify.ai/logo/logo.svg" size={40} />
|
||||
<Avatar name="Alex Doe" avatar="https://i.pravatar.cc/96?u=avatar-default" size="xl" />
|
||||
`.trim(),
|
||||
},
|
||||
},
|
||||
@ -20,8 +20,8 @@ const meta = {
|
||||
tags: ['autodocs'],
|
||||
args: {
|
||||
name: 'Alex Doe',
|
||||
avatar: 'https://cloud.dify.ai/logo/logo.svg',
|
||||
size: 40,
|
||||
avatar: 'https://i.pravatar.cc/96?u=avatar-default',
|
||||
size: 'xl',
|
||||
},
|
||||
} satisfies Meta<typeof Avatar>
|
||||
|
||||
@ -40,23 +40,20 @@ export const WithFallback: Story = {
|
||||
source: {
|
||||
language: 'tsx',
|
||||
code: `
|
||||
<Avatar name="Fallback" avatar={null} size={40} />
|
||||
<Avatar name="Fallback" avatar={null} size="xl" />
|
||||
`.trim(),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
export const CustomSizes: Story = {
|
||||
export const AllSizes: Story = {
|
||||
render: args => (
|
||||
<div className="flex items-end gap-4">
|
||||
{[24, 32, 48, 64].map(size => (
|
||||
{(['xxs', 'xs', 'sm', 'md', 'lg', 'xl', '2xl', '3xl'] as const).map(size => (
|
||||
<div key={size} className="flex flex-col items-center gap-2">
|
||||
<Avatar {...args} size={size} avatar="https://i.pravatar.cc/96?u=size-test" />
|
||||
<span className="text-xs text-text-tertiary">
|
||||
{size}
|
||||
px
|
||||
</span>
|
||||
<span className="text-xs text-text-tertiary">{size}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
@ -66,7 +63,7 @@ export const CustomSizes: Story = {
|
||||
source: {
|
||||
language: 'tsx',
|
||||
code: `
|
||||
{[24, 32, 48, 64].map(size => (
|
||||
{(['xxs', 'xs', 'sm', 'md', 'lg', 'xl', '2xl', '3xl'] as const).map(size => (
|
||||
<Avatar key={size} name="Size Test" size={size} avatar="https://i.pravatar.cc/96?u=size-test" />
|
||||
))}
|
||||
`.trim(),
|
||||
@ -74,3 +71,16 @@ export const CustomSizes: Story = {
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
export const AllFallbackSizes: Story = {
|
||||
render: args => (
|
||||
<div className="flex items-end gap-4">
|
||||
{(['xxs', 'xs', 'sm', 'md', 'lg', 'xl', '2xl', '3xl'] as const).map(size => (
|
||||
<div key={size} className="flex flex-col items-center gap-2">
|
||||
<Avatar {...args} size={size} avatar={null} name="Alex" />
|
||||
<span className="text-xs text-text-tertiary">{size}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
),
|
||||
}
|
||||
|
||||
@ -1,64 +1,52 @@
|
||||
'use client'
|
||||
import { useEffect, useState } from 'react'
|
||||
import type { ImageLoadingStatus } from '@base-ui/react/avatar'
|
||||
import { Avatar as BaseAvatar } from '@base-ui/react/avatar'
|
||||
import { cn } from '@/utils/classnames'
|
||||
|
||||
const SIZES = {
|
||||
'xxs': { root: 'size-4', text: 'text-[7px]' },
|
||||
'xs': { root: 'size-5', text: 'text-[8px]' },
|
||||
'sm': { root: 'size-6', text: 'text-[10px]' },
|
||||
'md': { root: 'size-8', text: 'text-xs' },
|
||||
'lg': { root: 'size-9', text: 'text-sm' },
|
||||
'xl': { root: 'size-10', text: 'text-base' },
|
||||
'2xl': { root: 'size-12', text: 'text-xl' },
|
||||
'3xl': { root: 'size-16', text: 'text-2xl' },
|
||||
} as const
|
||||
|
||||
export type AvatarSize = keyof typeof SIZES
|
||||
|
||||
export type AvatarProps = {
|
||||
name: string
|
||||
avatar: string | null
|
||||
size?: number
|
||||
size?: AvatarSize
|
||||
className?: string
|
||||
textClassName?: string
|
||||
onError?: (x: boolean) => void
|
||||
onLoadingStatusChange?: (status: ImageLoadingStatus) => void
|
||||
}
|
||||
const Avatar = ({
|
||||
|
||||
const BASE_CLASS = 'relative inline-flex shrink-0 select-none items-center justify-center overflow-hidden rounded-full bg-primary-600'
|
||||
|
||||
export const Avatar = ({
|
||||
name,
|
||||
avatar,
|
||||
size = 30,
|
||||
size = 'md',
|
||||
className,
|
||||
textClassName,
|
||||
onError,
|
||||
onLoadingStatusChange,
|
||||
}: AvatarProps) => {
|
||||
const avatarClassName = 'shrink-0 flex items-center rounded-full bg-primary-600'
|
||||
const style = { width: `${size}px`, height: `${size}px`, fontSize: `${size}px`, lineHeight: `${size}px` }
|
||||
const [imgError, setImgError] = useState(false)
|
||||
|
||||
const handleError = () => {
|
||||
setImgError(true)
|
||||
onError?.(true)
|
||||
}
|
||||
|
||||
// after uploaded, api would first return error imgs url: '.../files//file-preview/...'. Then return the right url, Which caused not show the avatar
|
||||
useEffect(() => {
|
||||
if (avatar && imgError)
|
||||
setImgError(false)
|
||||
}, [avatar])
|
||||
|
||||
if (avatar && !imgError) {
|
||||
return (
|
||||
<img
|
||||
className={cn(avatarClassName, className)}
|
||||
style={style}
|
||||
alt={name}
|
||||
src={avatar}
|
||||
onError={handleError}
|
||||
onLoad={() => onError?.(false)}
|
||||
/>
|
||||
)
|
||||
}
|
||||
const sizeConfig = SIZES[size]
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cn(avatarClassName, className)}
|
||||
style={style}
|
||||
>
|
||||
<div
|
||||
className={cn(textClassName, 'scale-[0.4] text-center text-white')}
|
||||
style={style}
|
||||
>
|
||||
{name && name[0].toLocaleUpperCase()}
|
||||
</div>
|
||||
</div>
|
||||
<BaseAvatar.Root className={cn(BASE_CLASS, sizeConfig.root, className)}>
|
||||
{avatar && (
|
||||
<BaseAvatar.Image
|
||||
src={avatar}
|
||||
alt={name}
|
||||
className="absolute inset-0 size-full object-cover"
|
||||
onLoadingStatusChange={onLoadingStatusChange}
|
||||
/>
|
||||
)}
|
||||
<BaseAvatar.Fallback className={cn('font-medium text-white', sizeConfig.text)}>
|
||||
{name?.[0]?.toLocaleUpperCase()}
|
||||
</BaseAvatar.Fallback>
|
||||
</BaseAvatar.Root>
|
||||
)
|
||||
}
|
||||
|
||||
export default Avatar
|
||||
|
||||
@ -23,7 +23,7 @@ import { submitHumanInputForm as submitHumanInputFormService } from '@/service/w
|
||||
import { TransferMethod } from '@/types/app'
|
||||
import { cn } from '@/utils/classnames'
|
||||
import { formatBooleanInputs } from '@/utils/model-config'
|
||||
import Avatar from '../../avatar'
|
||||
import { Avatar } from '../../avatar'
|
||||
import Chat from '../chat'
|
||||
import { useChat } from '../chat/hooks'
|
||||
import { getLastAnswer, isValidGeneratedAnswer } from '../utils'
|
||||
@ -351,7 +351,7 @@ const ChatWrapper = () => {
|
||||
<Avatar
|
||||
avatar={initUserVariables.avatar_url}
|
||||
name={initUserVariables.name || 'user'}
|
||||
size={40}
|
||||
size="xl"
|
||||
/>
|
||||
)
|
||||
: undefined
|
||||
|
||||
@ -23,7 +23,7 @@ import {
|
||||
import { submitHumanInputForm as submitHumanInputFormService } from '@/service/workflow'
|
||||
import { TransferMethod } from '@/types/app'
|
||||
import { cn } from '@/utils/classnames'
|
||||
import Avatar from '../../avatar'
|
||||
import { Avatar } from '../../avatar'
|
||||
import Chat from '../chat'
|
||||
import { useChat } from '../chat/hooks'
|
||||
import { getLastAnswer, isValidGeneratedAnswer } from '../utils'
|
||||
@ -337,7 +337,7 @@ const ChatWrapper = () => {
|
||||
<Avatar
|
||||
avatar={initUserVariables.avatar_url}
|
||||
name={initUserVariables.name || 'user'}
|
||||
size={40}
|
||||
size="xl"
|
||||
/>
|
||||
)
|
||||
: undefined
|
||||
|
||||
Reference in New Issue
Block a user