diff --git a/oxlint-suppressions.json b/oxlint-suppressions.json index f98f7a16fa9..ba83af1b8c9 100644 --- a/oxlint-suppressions.json +++ b/oxlint-suppressions.json @@ -1691,11 +1691,6 @@ "count": 1 } }, - "web/app/components/base/logo/dify-logo.tsx": { - "react/only-export-components": { - "count": 2 - } - }, "web/app/components/base/markdown-blocks/__tests__/paragraph.spec.tsx": { "jsx_a11y/anchor-is-valid": { "count": 1 diff --git a/web/app/(humanInputLayout)/form/[token]/__tests__/form.spec.tsx b/web/app/(humanInputLayout)/form/[token]/__tests__/form.spec.tsx index 147af155fff..01ce7d01c43 100644 --- a/web/app/(humanInputLayout)/form/[token]/__tests__/form.spec.tsx +++ b/web/app/(humanInputLayout)/form/[token]/__tests__/form.spec.tsx @@ -120,7 +120,7 @@ vi.mock('@/app/components/base/loading', () => ({ vi.mock('@/app/components/base/logo/dify-logo', () => ({ __esModule: true, - default: () =>
dify-logo
, + DifyLogo: () =>
dify-logo
, })) vi.mock('@/app/components/base/app-icon', () => ({ diff --git a/web/app/(humanInputLayout)/form/[token]/branding-footer.tsx b/web/app/(humanInputLayout)/form/[token]/branding-footer.tsx index 8c26efbfdc5..739c97e09dd 100644 --- a/web/app/(humanInputLayout)/form/[token]/branding-footer.tsx +++ b/web/app/(humanInputLayout)/form/[token]/branding-footer.tsx @@ -1,5 +1,5 @@ import { useTranslation } from 'react-i18next' -import DifyLogo from '@/app/components/base/logo/dify-logo' +import { DifyLogo } from '@/app/components/base/logo/dify-logo' type BrandingFooterProps = { removeWebappBrand?: boolean @@ -20,7 +20,7 @@ const BrandingFooter = ({ removeWebappBrand, replaceWebappLogo }: BrandingFooter {replaceWebappLogo ? ( logo ) : ( - + )} diff --git a/web/app/account/(commonLayout)/header.tsx b/web/app/account/(commonLayout)/header.tsx index 18cfb9763fc..f056bb32601 100644 --- a/web/app/account/(commonLayout)/header.tsx +++ b/web/app/account/(commonLayout)/header.tsx @@ -3,7 +3,7 @@ import { Button } from '@langgenius/dify-ui/button' import { useSuspenseQuery } from '@tanstack/react-query' import { useCallback } from 'react' import { useTranslation } from 'react-i18next' -import DifyLogo from '@/app/components/base/logo/dify-logo' +import { DifyLogo } from '@/app/components/base/logo/dify-logo' import { systemFeaturesQueryOptions } from '@/features/system-features/client' import Link from '@/next/link' import { useRouter } from '@/next/navigation' diff --git a/web/app/components/base/chat/chat-with-history/sidebar/index.tsx b/web/app/components/base/chat/chat-with-history/sidebar/index.tsx index f62ec1d4ab5..609a6f76198 100644 --- a/web/app/components/base/chat/chat-with-history/sidebar/index.tsx +++ b/web/app/components/base/chat/chat-with-history/sidebar/index.tsx @@ -17,7 +17,7 @@ import ActionButton from '@/app/components/base/action-button' import AppIcon from '@/app/components/base/app-icon' import List from '@/app/components/base/chat/chat-with-history/sidebar/list' import RenameModal from '@/app/components/base/chat/chat-with-history/sidebar/rename-modal' -import DifyLogo from '@/app/components/base/logo/dify-logo' +import { DifyLogo } from '@/app/components/base/logo/dify-logo' import MenuDropdown from '@/app/components/share/text-generation/menu-dropdown' import { systemFeaturesQueryOptions } from '@/features/system-features/client' import { useChatWithHistoryContext } from '../context' @@ -177,7 +177,7 @@ const Sidebar = ({ isPanel }: Props) => { className="block h-5 w-auto" /> ) : ( - + )} )} diff --git a/web/app/components/base/chat/embedded-chatbot/header/index.tsx b/web/app/components/base/chat/embedded-chatbot/header/index.tsx index ada6a182a43..9d64d7fa235 100644 --- a/web/app/components/base/chat/embedded-chatbot/header/index.tsx +++ b/web/app/components/base/chat/embedded-chatbot/header/index.tsx @@ -9,7 +9,7 @@ import { useTranslation } from 'react-i18next' import ActionButton from '@/app/components/base/action-button' import ViewFormDropdown from '@/app/components/base/chat/embedded-chatbot/inputs-form/view-form-dropdown' import Divider from '@/app/components/base/divider' -import DifyLogo from '@/app/components/base/logo/dify-logo' +import { DifyLogo } from '@/app/components/base/logo/dify-logo' import { systemFeaturesQueryOptions } from '@/features/system-features/client' import { isClient } from '@/utils/client' import { useEmbeddedChatbotContext } from '../context' @@ -105,7 +105,7 @@ const Header: FC = ({ className="block h-5 w-auto" /> ) : ( - + )} )} diff --git a/web/app/components/base/chat/embedded-chatbot/index.tsx b/web/app/components/base/chat/embedded-chatbot/index.tsx index d2b23db31a7..d67dc55f1c6 100644 --- a/web/app/components/base/chat/embedded-chatbot/index.tsx +++ b/web/app/components/base/chat/embedded-chatbot/index.tsx @@ -7,7 +7,7 @@ import { useTranslation } from 'react-i18next' import ChatWrapper from '@/app/components/base/chat/embedded-chatbot/chat-wrapper' import Header from '@/app/components/base/chat/embedded-chatbot/header' import Loading from '@/app/components/base/loading' -import DifyLogo from '@/app/components/base/logo/dify-logo' +import { DifyLogo } from '@/app/components/base/logo/dify-logo' import LogoHeader from '@/app/components/base/logo/logo-embedded-chat-header' import { systemFeaturesQueryOptions } from '@/features/system-features/client' import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints' @@ -95,7 +95,7 @@ const Chatbot = () => { className="block h-5 w-auto" /> ) : ( - + )} )} diff --git a/web/app/components/base/logo/__tests__/dify-logo.spec.tsx b/web/app/components/base/logo/__tests__/dify-logo.spec.tsx new file mode 100644 index 00000000000..95b6dad028c --- /dev/null +++ b/web/app/components/base/logo/__tests__/dify-logo.spec.tsx @@ -0,0 +1,16 @@ +import { render, screen } from '@testing-library/react' +import { DifyLogo } from '../dify-logo' + +describe('DifyLogo', () => { + it('uses the provided alternative text as its accessible name', () => { + const { container, rerender } = render() + + expect(screen.getByRole('img', { name: 'Dify' })).toHaveAttribute('src', '/logo/logo.svg') + + rerender() + + const decorativeLogo = container.querySelector('img') + expect(decorativeLogo).toHaveAttribute('alt', '') + expect(screen.queryByRole('img')).not.toBeInTheDocument() + }) +}) diff --git a/web/app/components/base/logo/dify-logo.tsx b/web/app/components/base/logo/dify-logo.tsx index 99d479fa156..f94b9112ec1 100644 --- a/web/app/components/base/logo/dify-logo.tsx +++ b/web/app/components/base/logo/dify-logo.tsx @@ -1,47 +1,44 @@ -'use client' -import type { FC } from 'react' +import type { VariantProps } from 'class-variance-authority' +import type { ComponentProps } from 'react' import { cn } from '@langgenius/dify-ui/cn' -import useTheme from '@/hooks/use-theme' +import { cva } from 'class-variance-authority' import { basePath } from '@/utils/var' -export type LogoStyle = 'default' | 'monochromeWhite' +const difyLogoVariants = cva( + 'block object-contain [html[data-theme=dark]_&]:brightness-0 [html[data-theme=dark]_&]:invert', + { + variants: { + size: { + small: 'h-4 w-9', + medium: 'h-[22px] w-12', + large: 'h-7 w-16', + }, + }, + defaultVariants: { + size: 'medium', + }, + }, +) -export const logoPathMap: Record = { - default: '/logo/logo.svg', - monochromeWhite: '/logo/logo-monochrome-white.svg', -} +export type DifyLogoProps = Omit< + ComponentProps<'img'>, + 'alt' | 'height' | 'size' | 'src' | 'width' +> & + VariantProps & { + alt: string + } -export type LogoSize = 'large' | 'medium' | 'small' - -export const logoSizeMap: Record = { - large: 'w-16 h-7', - medium: 'w-12 h-[22px]', - small: 'w-9 h-4', -} - -type DifyLogoProps = { - style?: LogoStyle - size?: LogoSize - className?: string - alt?: string -} - -const DifyLogo: FC = ({ - style = 'default', - size = 'medium', - className, - alt = 'Dify', -}) => { - const { theme } = useTheme() - const themedStyle = theme === 'dark' && style === 'default' ? 'monochromeWhite' : style +export function DifyLogo({ alt, className, size, ...props }: DifyLogoProps) { + const classes = cn(difyLogoVariants({ size, className })) return ( {alt} ) } - -export default DifyLogo diff --git a/web/app/components/billing/pricing/header.tsx b/web/app/components/billing/pricing/header.tsx index d436754b5fa..c6e1ed1d0dc 100644 --- a/web/app/components/billing/pricing/header.tsx +++ b/web/app/components/billing/pricing/header.tsx @@ -3,7 +3,7 @@ import { cn } from '@langgenius/dify-ui/cn' import { DialogDescription, DialogTitle } from '@langgenius/dify-ui/dialog' import * as React from 'react' import { useTranslation } from 'react-i18next' -import DifyLogo from '../../base/logo/dify-logo' +import { DifyLogo } from '../../base/logo/dify-logo' import styles from './header.module.css' type HeaderProps = { @@ -18,7 +18,7 @@ const Header = ({ onClose }: HeaderProps) => {
) : ( - + )} ) diff --git a/web/app/components/header/account-about/index.tsx b/web/app/components/header/account-about/index.tsx index e4c497c5f3b..a9d326c5f89 100644 --- a/web/app/components/header/account-about/index.tsx +++ b/web/app/components/header/account-about/index.tsx @@ -6,7 +6,7 @@ import { RiCloseLine } from '@remixicon/react' import { useSuspenseQuery } from '@tanstack/react-query' import dayjs from 'dayjs' import { useTranslation } from 'react-i18next' -import DifyLogo from '@/app/components/base/logo/dify-logo' +import { DifyLogo } from '@/app/components/base/logo/dify-logo' import { systemFeaturesQueryOptions } from '@/features/system-features/client' import Link from '@/next/link' @@ -48,7 +48,7 @@ export default function AccountAbout({ langGeniusVersionInfo, onCancel }: IAccou alt="logo" /> ) : ( - + )}
diff --git a/web/app/components/main-nav/index.tsx b/web/app/components/main-nav/index.tsx index 45127955827..ee077f1d8ac 100644 --- a/web/app/components/main-nav/index.tsx +++ b/web/app/components/main-nav/index.tsx @@ -7,7 +7,7 @@ import { useAtomValue } from 'jotai' import { useMemo } from 'react' import { useTranslation } from 'react-i18next' import Badge from '@/app/components/base/badge' -import DifyLogo from '@/app/components/base/logo/dify-logo' +import { DifyLogo } from '@/app/components/base/logo/dify-logo' import EnvNav from '@/app/components/header/env-nav' import StepByStepTourMount from '@/app/components/step-by-step-tour/mount' import { langGeniusVersionInfoAtom } from '@/context/version-state' diff --git a/web/app/components/plugins/marketplace/description/index.tsx b/web/app/components/plugins/marketplace/description/index.tsx index c28d2b28ebd..05fb6b16dad 100644 --- a/web/app/components/plugins/marketplace/description/index.tsx +++ b/web/app/components/plugins/marketplace/description/index.tsx @@ -5,7 +5,7 @@ import { motion, useMotionValue, useSpring, useTransform } from 'motion/react' import { useEffect, useLayoutEffect, useRef } from 'react' import { useLocale, useTranslation } from '#i18n' import Divider from '@/app/components/base/divider' -import DifyLogo from '@/app/components/base/logo/dify-logo' +import { DifyLogo } from '@/app/components/base/logo/dify-logo' import { SubmitRequestDropdown } from '@/app/components/plugins/plugin-page/nav-operations' import PluginTypeSwitch from '../plugin-type-switch' import SearchBoxWrapper from '../search-box/search-box-wrapper' @@ -231,7 +231,7 @@ const Description = ({
- + {tCommon(($) => $['mainNav.marketplace'])} diff --git a/web/app/components/share/text-generation/text-generation-sidebar.tsx b/web/app/components/share/text-generation/text-generation-sidebar.tsx index 36fb5769061..d5fa0831f8d 100644 --- a/web/app/components/share/text-generation/text-generation-sidebar.tsx +++ b/web/app/components/share/text-generation/text-generation-sidebar.tsx @@ -12,7 +12,7 @@ import { useTranslation } from 'react-i18next' import SavedItems from '@/app/components/app/text-generate/saved-items' import AppIcon from '@/app/components/base/app-icon' import Badge from '@/app/components/base/badge' -import DifyLogo from '@/app/components/base/logo/dify-logo' +import { DifyLogo } from '@/app/components/base/logo/dify-logo' import { appDefaultIconBackground } from '@/config' import { AccessMode } from '@/models/access-control' import MenuDropdown from './menu-dropdown' @@ -223,7 +223,7 @@ const TextGenerationSidebar: FC = ({ ) : customConfig?.replace_webapp_logo ? ( logo ) : ( - + )}
)} diff --git a/web/app/device/_header.tsx b/web/app/device/_header.tsx index f5990151629..fe555ad4314 100644 --- a/web/app/device/_header.tsx +++ b/web/app/device/_header.tsx @@ -1,6 +1,7 @@ 'use client' import { useSuspenseQuery } from '@tanstack/react-query' import Divider from '@/app/components/base/divider' +import { DifyLogo } from '@/app/components/base/logo/dify-logo' import LocaleMenu from '@/app/signin/_locale-menu' import { useLocale } from '@/context/i18n' import { systemFeaturesQueryOptions } from '@/features/system-features/client' @@ -8,10 +9,6 @@ import { setLocaleOnClient } from '@/i18n-config' import { languages } from '@/i18n-config/language' import dynamic from '@/next/dynamic' -const DifyLogo = dynamic(() => import('@/app/components/base/logo/dify-logo'), { - ssr: false, - loading: () =>
, -}) const ThemeSelector = dynamic(() => import('@/app/components/base/theme-selector'), { ssr: false, loading: () =>
, @@ -30,7 +27,7 @@ const Header = () => { alt="logo" /> ) : ( - + )}
{ }} >
- +
diff --git a/web/app/signin/_header.tsx b/web/app/signin/_header.tsx index 44a8cd4364a..ed824b6a2e4 100644 --- a/web/app/signin/_header.tsx +++ b/web/app/signin/_header.tsx @@ -1,6 +1,7 @@ 'use client' import { useSuspenseQuery } from '@tanstack/react-query' import Divider from '@/app/components/base/divider' +import { DifyLogo } from '@/app/components/base/logo/dify-logo' import { useLocale } from '@/context/i18n' import { systemFeaturesQueryOptions } from '@/features/system-features/client' import { setLocaleOnClient } from '@/i18n-config' @@ -8,11 +9,6 @@ import { languages } from '@/i18n-config/language' import dynamic from '@/next/dynamic' import LocaleMenu from './_locale-menu' -// Avoid rendering the logo and theme selector on the server -const DifyLogo = dynamic(() => import('@/app/components/base/logo/dify-logo'), { - ssr: false, - loading: () =>
, -}) const ThemeSelector = dynamic(() => import('@/app/components/base/theme-selector'), { ssr: false, loading: () =>
, @@ -31,7 +27,7 @@ const Header = () => { alt="logo" /> ) : ( - + )}
- - - - - - - - - - -