refactor(web): move avatar to base ui (#34889)

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
This commit is contained in:
yyh 2026-04-10 12:44:05 +08:00 committed by GitHub
parent b90fe73c96
commit f42c1b68a4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
22 changed files with 22 additions and 27 deletions

View File

@ -2,7 +2,7 @@
import type { Area } from 'react-easy-crop' import type { Area } from 'react-easy-crop'
import type { OnImageInput } from '@/app/components/base/app-icon-picker/ImageInput' import type { OnImageInput } from '@/app/components/base/app-icon-picker/ImageInput'
import type { AvatarProps } from '@/app/components/base/avatar' import type { AvatarProps } from '@/app/components/base/ui/avatar'
import type { ImageFile } from '@/types/app' import type { ImageFile } from '@/types/app'
import { RiDeleteBin5Line, RiPencilLine } from '@remixicon/react' import { RiDeleteBin5Line, RiPencilLine } from '@remixicon/react'
import * as React from 'react' import * as React from 'react'
@ -10,10 +10,10 @@ import { useCallback, useState } from 'react'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import ImageInput from '@/app/components/base/app-icon-picker/ImageInput' import ImageInput from '@/app/components/base/app-icon-picker/ImageInput'
import getCroppedImg from '@/app/components/base/app-icon-picker/utils' import getCroppedImg from '@/app/components/base/app-icon-picker/utils'
import { Avatar } from '@/app/components/base/avatar'
import Button from '@/app/components/base/button' import Button from '@/app/components/base/button'
import Divider from '@/app/components/base/divider' import Divider from '@/app/components/base/divider'
import { useLocalFileUploader } from '@/app/components/base/image-uploader/hooks' import { useLocalFileUploader } from '@/app/components/base/image-uploader/hooks'
import { Avatar } from '@/app/components/base/ui/avatar'
import { Dialog, DialogContent } from '@/app/components/base/ui/dialog' import { Dialog, DialogContent } from '@/app/components/base/ui/dialog'
import { toast } from '@/app/components/base/ui/toast' import { toast } from '@/app/components/base/ui/toast'
import { DISABLE_UPLOAD_IMAGE_AS_ICON } from '@/config' import { DISABLE_UPLOAD_IMAGE_AS_ICON } from '@/config'

View File

@ -6,9 +6,9 @@ import {
import { Fragment } from 'react' import { Fragment } from 'react'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import { resetUser } from '@/app/components/base/amplitude/utils' import { resetUser } from '@/app/components/base/amplitude/utils'
import { Avatar } from '@/app/components/base/avatar'
import { LogOut01 } from '@/app/components/base/icons/src/vender/line/general' import { LogOut01 } from '@/app/components/base/icons/src/vender/line/general'
import PremiumBadge from '@/app/components/base/premium-badge' import PremiumBadge from '@/app/components/base/premium-badge'
import { Avatar } from '@/app/components/base/ui/avatar'
import { useProviderContext } from '@/context/provider-context' import { useProviderContext } from '@/context/provider-context'
import { useRouter } from '@/next/navigation' import { useRouter } from '@/next/navigation'
import { useLogout, useUserProfile } from '@/service/use-common' import { useLogout, useUserProfile } from '@/service/use-common'

View File

@ -10,9 +10,9 @@ import {
import * as React from 'react' import * as React from 'react'
import { useEffect, useRef } from 'react' import { useEffect, useRef } from 'react'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import { Avatar } from '@/app/components/base/avatar'
import Button from '@/app/components/base/button' import Button from '@/app/components/base/button'
import Loading from '@/app/components/base/loading' import Loading from '@/app/components/base/loading'
import { Avatar } from '@/app/components/base/ui/avatar'
import { toast } from '@/app/components/base/ui/toast' import { toast } from '@/app/components/base/ui/toast'
import { useLanguage } from '@/app/components/header/account-setting/model-provider-page/hooks' import { useLanguage } from '@/app/components/header/account-setting/model-provider-page/hooks'
import { setPostLoginRedirect } from '@/app/signin/utils/post-login-redirect' import { setPostLoginRedirect } from '@/app/signin/utils/post-login-redirect'

View File

@ -5,12 +5,12 @@ import { RiAddCircleFill, RiArrowRightSLine, RiOrganizationChart } from '@remixi
import { useDebounce } from 'ahooks' import { useDebounce } from 'ahooks'
import { useCallback, useEffect, useRef, useState } from 'react' import { useCallback, useEffect, useRef, useState } from 'react'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import { Avatar } from '@/app/components/base/ui/avatar'
import { useSelector } from '@/context/app-context' import { useSelector } from '@/context/app-context'
import { SubjectType } from '@/models/access-control' import { SubjectType } from '@/models/access-control'
import { useSearchForWhiteListCandidates } from '@/service/access-control' import { useSearchForWhiteListCandidates } from '@/service/access-control'
import { cn } from '@/utils/classnames' import { cn } from '@/utils/classnames'
import useAccessControlStore from '../../../../context/access-control-store' import useAccessControlStore from '../../../../context/access-control-store'
import { Avatar } from '../../base/avatar'
import Button from '../../base/button' import Button from '../../base/button'
import Checkbox from '../../base/checkbox' import Checkbox from '../../base/checkbox'
import Input from '../../base/input' import Input from '../../base/input'

View File

@ -3,10 +3,10 @@ import type { AccessControlAccount, AccessControlGroup } from '@/models/access-c
import { RiAlertFill, RiCloseCircleFill, RiLockLine, RiOrganizationChart } from '@remixicon/react' import { RiAlertFill, RiCloseCircleFill, RiLockLine, RiOrganizationChart } from '@remixicon/react'
import { useCallback, useEffect } from 'react' import { useCallback, useEffect } from 'react'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import { Avatar } from '@/app/components/base/ui/avatar'
import { AccessMode } from '@/models/access-control' import { AccessMode } from '@/models/access-control'
import { useAppWhiteListSubjects } from '@/service/access-control' import { useAppWhiteListSubjects } from '@/service/access-control'
import useAccessControlStore from '../../../../context/access-control-store' import useAccessControlStore from '../../../../context/access-control-store'
import { Avatar } from '../../base/avatar'
import Loading from '../../base/loading' import Loading from '../../base/loading'
import Tooltip from '../../base/tooltip' import Tooltip from '../../base/tooltip'
import AddMemberOrGroupDialog from './add-member-or-group-pop' import AddMemberOrGroupDialog from './add-member-or-group-pop'

View File

@ -90,7 +90,7 @@ vi.mock('@/app/components/base/chat/chat', () => ({
}, },
})) }))
vi.mock('@/app/components/base/avatar', () => ({ vi.mock('@/app/components/base/ui/avatar', () => ({
Avatar: ({ name }: { name: string }) => <div data-testid="avatar">{name}</div>, Avatar: ({ name }: { name: string }) => <div data-testid="avatar">{name}</div>,
})) }))

View File

@ -7,11 +7,11 @@ import {
useCallback, useCallback,
useMemo, useMemo,
} from 'react' } from 'react'
import { Avatar } from '@/app/components/base/avatar'
import Chat from '@/app/components/base/chat/chat' import Chat from '@/app/components/base/chat/chat'
import { useChat } from '@/app/components/base/chat/chat/hooks' import { useChat } from '@/app/components/base/chat/chat/hooks'
import { getLastAnswer } from '@/app/components/base/chat/utils' import { getLastAnswer } from '@/app/components/base/chat/utils'
import { useFeatures } from '@/app/components/base/features/hooks' import { useFeatures } from '@/app/components/base/features/hooks'
import { Avatar } from '@/app/components/base/ui/avatar'
import { ModelFeatureEnum } from '@/app/components/header/account-setting/model-provider-page/declarations' import { ModelFeatureEnum } from '@/app/components/header/account-setting/model-provider-page/declarations'
import { useAppContext } from '@/context/app-context' import { useAppContext } from '@/context/app-context'
import { useDebugConfigurationContext } from '@/context/debug-configuration' import { useDebugConfigurationContext } from '@/context/debug-configuration'

View File

@ -3,11 +3,11 @@ import type { ChatConfig, ChatItem, OnSend } from '@/app/components/base/chat/ty
import type { FileEntity } from '@/app/components/base/file-uploader/types' import type { FileEntity } from '@/app/components/base/file-uploader/types'
import { memo, useCallback, useImperativeHandle, useMemo } from 'react' import { memo, useCallback, useImperativeHandle, useMemo } from 'react'
import { useStore as useAppStore } from '@/app/components/app/store' import { useStore as useAppStore } from '@/app/components/app/store'
import { Avatar } from '@/app/components/base/avatar'
import Chat from '@/app/components/base/chat/chat' import Chat from '@/app/components/base/chat/chat'
import { useChat } from '@/app/components/base/chat/chat/hooks' import { useChat } from '@/app/components/base/chat/chat/hooks'
import { getLastAnswer, isValidGeneratedAnswer } from '@/app/components/base/chat/utils' import { getLastAnswer, isValidGeneratedAnswer } from '@/app/components/base/chat/utils'
import { useFeatures } from '@/app/components/base/features/hooks' import { useFeatures } from '@/app/components/base/features/hooks'
import { Avatar } from '@/app/components/base/ui/avatar'
import { ModelFeatureEnum } from '@/app/components/header/account-setting/model-provider-page/declarations' import { ModelFeatureEnum } from '@/app/components/header/account-setting/model-provider-page/declarations'
import { useAppContext } from '@/context/app-context' import { useAppContext } from '@/context/app-context'
import { useDebugConfigurationContext } from '@/context/debug-configuration' import { useDebugConfigurationContext } from '@/context/debug-configuration'

View File

@ -11,6 +11,7 @@ import AppIcon from '@/app/components/base/app-icon'
import InputsForm from '@/app/components/base/chat/chat-with-history/inputs-form' import InputsForm from '@/app/components/base/chat/chat-with-history/inputs-form'
import SuggestedQuestions from '@/app/components/base/chat/chat/answer/suggested-questions' import SuggestedQuestions from '@/app/components/base/chat/chat/answer/suggested-questions'
import { Markdown } from '@/app/components/base/markdown' import { Markdown } from '@/app/components/base/markdown'
import { Avatar } from '@/app/components/base/ui/avatar'
import { InputVarType } from '@/app/components/workflow/types' import { InputVarType } from '@/app/components/workflow/types'
import { import {
AppSourceType, AppSourceType,
@ -23,7 +24,6 @@ import { submitHumanInputForm as submitHumanInputFormService } from '@/service/w
import { TransferMethod } from '@/types/app' import { TransferMethod } from '@/types/app'
import { cn } from '@/utils/classnames' import { cn } from '@/utils/classnames'
import { formatBooleanInputs } from '@/utils/model-config' import { formatBooleanInputs } from '@/utils/model-config'
import { Avatar } from '../../avatar'
import Chat from '../chat' import Chat from '../chat'
import { useChat } from '../chat/hooks' import { useChat } from '../chat/hooks'
import { getLastAnswer, isValidGeneratedAnswer } from '../utils' import { getLastAnswer, isValidGeneratedAnswer } from '../utils'

View File

@ -12,6 +12,7 @@ import SuggestedQuestions from '@/app/components/base/chat/chat/answer/suggested
import InputsForm from '@/app/components/base/chat/embedded-chatbot/inputs-form' import InputsForm from '@/app/components/base/chat/embedded-chatbot/inputs-form'
import LogoAvatar from '@/app/components/base/logo/logo-embedded-chat-avatar' import LogoAvatar from '@/app/components/base/logo/logo-embedded-chat-avatar'
import { Markdown } from '@/app/components/base/markdown' import { Markdown } from '@/app/components/base/markdown'
import { Avatar } from '@/app/components/base/ui/avatar'
import { InputVarType } from '@/app/components/workflow/types' import { InputVarType } from '@/app/components/workflow/types'
import { import {
AppSourceType, AppSourceType,
@ -23,7 +24,6 @@ import {
import { submitHumanInputForm as submitHumanInputFormService } from '@/service/workflow' import { submitHumanInputForm as submitHumanInputFormService } from '@/service/workflow'
import { TransferMethod } from '@/types/app' import { TransferMethod } from '@/types/app'
import { cn } from '@/utils/classnames' import { cn } from '@/utils/classnames'
import { Avatar } from '../../avatar'
import Chat from '../chat' import Chat from '../chat'
import { useChat } from '../chat/hooks' import { useChat } from '../chat/hooks'
import { getLastAnswer, isValidGeneratedAnswer } from '../utils' import { getLastAnswer, isValidGeneratedAnswer } from '../utils'

View File

@ -1,5 +1,5 @@
import { render, screen } from '@testing-library/react' import { render, screen } from '@testing-library/react'
import { Avatar } from '../index' import { Avatar } from '..'
describe('Avatar', () => { describe('Avatar', () => {
describe('Rendering', () => { describe('Rendering', () => {

View File

@ -36,7 +36,7 @@ function AvatarRoot({
return ( return (
<BaseAvatar.Root <BaseAvatar.Root
className={cn( className={cn(
'relative inline-flex shrink-0 select-none items-center justify-center overflow-hidden rounded-full bg-primary-600', 'relative inline-flex shrink-0 items-center justify-center overflow-hidden rounded-full bg-primary-600 select-none',
avatarSizeClasses[size].root, avatarSizeClasses[size].root,
className, className,
)} )}
@ -53,7 +53,7 @@ function AvatarImage({
}: AvatarImageProps) { }: AvatarImageProps) {
return ( return (
<BaseAvatar.Image <BaseAvatar.Image
className={cn('absolute inset-0 size-full object-cover', className)} className={cn('inset-0 absolute size-full object-cover', className)}
{...props} {...props}
/> />
) )

View File

@ -4,13 +4,13 @@ import { useDebounceFn } from 'ahooks'
import * as React from 'react' import * as React from 'react'
import { useCallback, useMemo, useState } from 'react' import { useCallback, useMemo, useState } from 'react'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import { Avatar } from '@/app/components/base/avatar'
import Input from '@/app/components/base/input' import Input from '@/app/components/base/input'
import { import {
PortalToFollowElem, PortalToFollowElem,
PortalToFollowElemContent, PortalToFollowElemContent,
PortalToFollowElemTrigger, PortalToFollowElemTrigger,
} from '@/app/components/base/portal-to-follow-elem' } from '@/app/components/base/portal-to-follow-elem'
import { Avatar } from '@/app/components/base/ui/avatar'
import { useSelector as useAppContextWithSelector } from '@/context/app-context' import { useSelector as useAppContextWithSelector } from '@/context/app-context'
import { DatasetPermission } from '@/models/datasets' import { DatasetPermission } from '@/models/datasets'
import { cn } from '@/utils/classnames' import { cn } from '@/utils/classnames'

View File

@ -4,9 +4,9 @@ import type { MouseEventHandler, ReactNode } from 'react'
import { useState } from 'react' import { useState } from 'react'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import { resetUser } from '@/app/components/base/amplitude/utils' import { resetUser } from '@/app/components/base/amplitude/utils'
import { Avatar } from '@/app/components/base/avatar'
import PremiumBadge from '@/app/components/base/premium-badge' import PremiumBadge from '@/app/components/base/premium-badge'
import ThemeSwitcher from '@/app/components/base/theme-switcher' import ThemeSwitcher from '@/app/components/base/theme-switcher'
import { Avatar } from '@/app/components/base/ui/avatar'
import { DropdownMenu, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLinkItem, DropdownMenuSeparator, DropdownMenuTrigger } from '@/app/components/base/ui/dropdown-menu' import { DropdownMenu, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLinkItem, DropdownMenuSeparator, DropdownMenuTrigger } from '@/app/components/base/ui/dropdown-menu'
import { ACCOUNT_SETTING_TAB } from '@/app/components/header/account-setting/constants' import { ACCOUNT_SETTING_TAB } from '@/app/components/header/account-setting/constants'
import { IS_CLOUD_EDITION } from '@/config' import { IS_CLOUD_EDITION } from '@/config'

View File

@ -2,7 +2,7 @@
import type { InvitationResult } from '@/models/common' import type { InvitationResult } from '@/models/common'
import { useState } from 'react' import { useState } from 'react'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import { Avatar } from '@/app/components/base/avatar' import { Avatar } from '@/app/components/base/ui/avatar'
import { Tooltip, TooltipContent, TooltipTrigger } from '@/app/components/base/ui/tooltip' import { Tooltip, TooltipContent, TooltipTrigger } from '@/app/components/base/ui/tooltip'
import { NUM_INFINITE } from '@/app/components/billing/config' import { NUM_INFINITE } from '@/app/components/billing/config'
import { Plan } from '@/app/components/billing/type' import { Plan } from '@/app/components/billing/type'

View File

@ -3,9 +3,9 @@ import type { FC } from 'react'
import * as React from 'react' import * as React from 'react'
import { useMemo, useState } from 'react' import { useMemo, useState } from 'react'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import { Avatar } from '@/app/components/base/avatar'
import Input from '@/app/components/base/input' import Input from '@/app/components/base/input'
import { PortalToFollowElem, PortalToFollowElemContent, PortalToFollowElemTrigger } from '@/app/components/base/portal-to-follow-elem' import { PortalToFollowElem, PortalToFollowElemContent, PortalToFollowElemTrigger } from '@/app/components/base/portal-to-follow-elem'
import { Avatar } from '@/app/components/base/ui/avatar'
import { useMembers } from '@/service/use-common' import { useMembers } from '@/service/use-common'
import { cn } from '@/utils/classnames' import { cn } from '@/utils/classnames'

View File

@ -3,7 +3,7 @@ import type { Member } from '@/models/common'
import { RiCloseCircleFill, RiErrorWarningFill } from '@remixicon/react' import { RiCloseCircleFill, RiErrorWarningFill } from '@remixicon/react'
import * as React from 'react' import * as React from 'react'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import { Avatar } from '@/app/components/base/avatar' import { Avatar } from '@/app/components/base/ui/avatar'
import { cn } from '@/utils/classnames' import { cn } from '@/utils/classnames'
type Props = { type Props = {

View File

@ -4,8 +4,8 @@ import type { Recipient } from '@/app/components/workflow/nodes/human-input/type
import type { Member } from '@/models/common' import type { Member } from '@/models/common'
import { useMemo } from 'react' import { useMemo } from 'react'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import { Avatar } from '@/app/components/base/avatar'
import Input from '@/app/components/base/input' import Input from '@/app/components/base/input'
import { Avatar } from '@/app/components/base/ui/avatar'
import { cn } from '@/utils/classnames' import { cn } from '@/utils/classnames'
const i18nPrefix = 'nodes.humanInput' const i18nPrefix = 'nodes.humanInput'

View File

@ -1,7 +1,7 @@
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import { Avatar } from '@/app/components/base/avatar'
import Button from '@/app/components/base/button' import Button from '@/app/components/base/button'
import { Triangle } from '@/app/components/base/icons/src/public/education' import { Triangle } from '@/app/components/base/icons/src/public/education'
import { Avatar } from '@/app/components/base/ui/avatar'
import { useAppContext } from '@/context/app-context' import { useAppContext } from '@/context/app-context'
import { useRouter } from '@/next/navigation' import { useRouter } from '@/next/navigation'
import { useLogout } from '@/service/use-common' import { useLogout } from '@/service/use-common'

View File

@ -1560,11 +1560,6 @@
"count": 1 "count": 1
} }
}, },
"app/components/base/avatar/index.tsx": {
"tailwindcss/enforce-consistent-class-order": {
"count": 2
}
},
"app/components/base/badge.tsx": { "app/components/base/badge.tsx": {
"tailwindcss/enforce-consistent-class-order": { "tailwindcss/enforce-consistent-class-order": {
"count": 2 "count": 2

View File

@ -171,7 +171,7 @@ export default antfu(
}, },
{ {
name: 'dify/base-ui-primitives', name: 'dify/base-ui-primitives',
files: ['app/components/base/ui/**/*.tsx', 'app/components/base/avatar/**/*.tsx'], files: ['app/components/base/ui/**/*.tsx'],
rules: { rules: {
'react-refresh/only-export-components': 'off', 'react-refresh/only-export-components': 'off',
}, },