From baa20348cee56cf9f5b1a1e5bd3636e1a9297bb5 Mon Sep 17 00:00:00 2001 From: yyh Date: Thu, 15 Oct 2026 13:47:36 +0800 Subject: [PATCH 1/2] refactor(web): centralize AppIcon business data and Avatar rendering --- oxlint-suppressions.json | 2 +- .../form/[token]/loaded-form-content.tsx | 8 +- .../(commonLayout)/account-page/index.tsx | 10 +- .../app-sidebar/app-detail-section.tsx | 5 +- .../__tests__/app-info-trigger.spec.tsx | 24 +--- .../app-sidebar/app-info/app-info-header.tsx | 21 +--- .../app-sidebar/app-info/app-info-trigger.tsx | 5 +- .../app-sidebar/dataset-info/index.tsx | 8 +- .../built-in-access-points/web-app-card.tsx | 10 +- .../environment-web-app-card.tsx | 14 +-- .../config/agent/agent-tools/index.tsx | 7 +- .../dataset-config/card-item/index.tsx | 8 +- .../dataset-config/select-dataset/index.tsx | 14 +-- .../app/create-app-dialog/app-card/index.tsx | 15 +-- .../workflow-source-popover.tsx | 19 +-- .../apps/__tests__/app-card.spec.tsx | 15 +-- web/app/components/apps/app-card/index.tsx | 12 +- ...import-from-marketplace-template-modal.tsx | 15 ++- web/app/components/apps/starred-app-card.tsx | 11 +- .../base/app-icon/__tests__/index.spec.tsx | 15 +++ .../base/app-icon/index.stories.tsx | 98 +++------------ web/app/components/base/app-icon/index.tsx | 113 +++++++----------- .../chat/chat-with-history/chat-wrapper.tsx | 16 +-- .../chat-with-history/header-in-mobile.tsx | 9 +- .../chat/chat-with-history/header/index.tsx | 8 +- .../chat/chat-with-history/sidebar/index.tsx | 8 +- .../chat/embedded-chatbot/chat-wrapper.tsx | 16 +-- .../__tests__/image-confirmation.spec.tsx | 9 ++ web/app/components/base/icon-picker/index.tsx | 15 +-- .../base/linked-apps-panel/index.tsx | 8 +- .../plugins/component-picker-block/hooks.tsx | 8 +- .../list/template-card/content.tsx | 8 +- .../details/__tests__/index.spec.tsx | 2 +- .../list/template-card/details/index.tsx | 18 +-- .../list/template-card/edit-pipeline-info.tsx | 10 +- .../datasets/create/step-three/index.tsx | 9 +- .../__tests__/dataset-card-header.spec.tsx | 4 - .../components/dataset-card-header.tsx | 8 +- .../datasets/rename-modal/index.tsx | 10 +- .../installed-app-navigation/app-nav-item.tsx | 12 +- .../components/explore/learn-dify/item.tsx | 16 +-- .../explore/try-app/app-info/index.tsx | 8 +- .../components/explore/try-app/app/chat.tsx | 9 +- .../explore/try-app/app/text-generation.tsx | 6 +- .../try-app/preview/agent-app-preview.tsx | 9 +- .../goto-anything/actions/agent.tsx | 17 +-- .../components/goto-anything/actions/app.tsx | 15 +-- .../plugins/card/base/card-icon.tsx | 35 +++--- .../creator-profile/creation-card.tsx | 16 ++- .../marketplace/templates/template-card.tsx | 10 +- .../app-selector/app-picker.tsx | 11 +- .../app-selector/app-trigger.tsx | 13 +- .../tool-selector/components/tool-item.tsx | 7 +- .../__tests__/index.spec.tsx | 26 ---- .../share/text-generation/info-modal.tsx | 6 +- .../text-generation-sidebar.tsx | 6 +- web/app/components/tools/mcp/modal.tsx | 51 +++++--- web/app/components/workflow/block-icon.tsx | 7 +- .../__tests__/agent-selector.spec.tsx | 4 - .../block-selector/agent-selector.tsx | 8 +- .../components/agent-roster-field.tsx | 18 +-- .../workflow/nodes/agent-v2/node.tsx | 17 +-- .../agent/__tests__/integration.spec.tsx | 6 +- .../components/__tests__/tool-icon.spec.tsx | 12 -- .../nodes/agent/components/tool-icon.tsx | 7 +- .../components/dataset-item.tsx | 8 +- .../nodes/knowledge-retrieval/node.tsx | 9 +- .../__tests__/navigation.spec.tsx | 20 ++-- .../__tests__/access-surface-cards.spec.tsx | 4 + .../access/components/web-app-access-card.tsx | 14 +-- .../components/workflow-references-table.tsx | 25 ++-- .../publish-bar/publish-impact-details.tsx | 23 ++-- .../preview/__tests__/preview-avatar.spec.tsx | 29 +++-- .../components/preview/preview-chat.tsx | 17 +-- .../logs/components/source-icon.tsx | 27 ++--- .../agent-v2/agent-detail/navigation.tsx | 16 +-- .../roster/components/agent-form-fields.tsx | 2 +- .../roster/components/agent-roster-list.tsx | 18 +-- .../agent-workflow-references-dropdown.tsx | 23 +--- web/features/home/continue-work/item.tsx | 8 +- web/features/home/template-card.tsx | 15 +-- .../skills/__tests__/detail-page.fixture.tsx | 4 - web/features/skills/__tests__/page.spec.tsx | 4 - web/features/skills/detail/skill-metadata.tsx | 50 ++++---- 84 files changed, 361 insertions(+), 922 deletions(-) create mode 100644 web/app/components/base/app-icon/__tests__/index.spec.tsx diff --git a/oxlint-suppressions.json b/oxlint-suppressions.json index 4f4e5f4c12f7eb..45ec787871a6c4 100644 --- a/oxlint-suppressions.json +++ b/oxlint-suppressions.json @@ -2967,7 +2967,7 @@ }, "web/app/components/workflow/nodes/agent/__tests__/integration.spec.tsx": { "typescript/no-explicit-any": { - "count": 17 + "count": 16 } }, "web/app/components/workflow/nodes/agent/default.ts": { diff --git a/web/app/(humanInputLayout)/form/[token]/loaded-form-content.tsx b/web/app/(humanInputLayout)/form/[token]/loaded-form-content.tsx index cd116c1a75d40d..85743c20b72401 100644 --- a/web/app/(humanInputLayout)/form/[token]/loaded-form-content.tsx +++ b/web/app/(humanInputLayout)/form/[token]/loaded-form-content.tsx @@ -73,13 +73,7 @@ const LoadedFormContent = ({ return (
- +
{site.title}
diff --git a/web/app/account/(commonLayout)/account-page/index.tsx b/web/app/account/(commonLayout)/account-page/index.tsx index a8f85b1489e657..771cf585890c79 100644 --- a/web/app/account/(commonLayout)/account-page/index.tsx +++ b/web/app/account/(commonLayout)/account-page/index.tsx @@ -1,7 +1,6 @@ 'use client' import type { AppPartial } from '@dify/contracts/api/console/apps/types.gen' import type { IItem } from '@/app/components/header/account-setting/collapse' -import { zIconType } from '@dify/contracts/api/console/apps/zod.gen' import { Button } from '@langgenius/dify-ui/button' import { useQuery, useSuspenseQuery } from '@tanstack/react-query' import { useState } from 'react' @@ -61,17 +60,10 @@ export default function AccountPage() { if (!userProfile) return null const renderAppItem = (item: AccountAppItem) => { - const appIconType = zIconType.safeParse(item.icon_type).data ?? null return (
- +
{item.name}
diff --git a/web/app/components/app-sidebar/app-detail-section.tsx b/web/app/components/app-sidebar/app-detail-section.tsx index 32dfd53fbdf178..5f0e5eab6d61f3 100644 --- a/web/app/components/app-sidebar/app-detail-section.tsx +++ b/web/app/components/app-sidebar/app-detail-section.tsx @@ -185,11 +185,8 @@ function AppDetailContent({ appId, expand }: { appId: string; expand: boolean }) expand={expand} appName={appTitle} modeLabel={appDetail ? getAppModeLabel(appDetail.mode, t) : undefined} - iconType={appDetail?.icon_type} - icon={appDetail?.icon ?? undefined} - background={appDetail?.icon_background} - imageUrl={appDetail?.icon_url} operationGroups={[]} + icon={appDetail} /> )}
diff --git a/web/app/components/app-sidebar/app-info/__tests__/app-info-trigger.spec.tsx b/web/app/components/app-sidebar/app-info/__tests__/app-info-trigger.spec.tsx index 08f3da735580e7..ba63d3a1973b34 100644 --- a/web/app/components/app-sidebar/app-info/__tests__/app-info-trigger.spec.tsx +++ b/web/app/components/app-sidebar/app-info/__tests__/app-info-trigger.spec.tsx @@ -31,24 +31,6 @@ vi.mock('@/context/permission-state', async () => { return createPermissionStateModuleMock(() => mockConsoleState.current) }) -vi.mock('../../../base/app-icon', () => ({ - default: ({ - size, - icon, - background, - }: { - size: string - icon: string - background: string - iconType?: string - imageUrl?: string - }) => ( - - {icon} - - ), -})) - const defaultAppPermissionKeys = [ AppACLPermission.Edit, AppACLPermission.ImportExportDSL, @@ -96,7 +78,7 @@ describe('AppInfoTrigger', () => { }) render() - expect(screen.getByText('๐Ÿค–')).toHaveAttribute('data-size', 'medium') + expect(screen.getByText('๐Ÿค–')).toBeInTheDocument() expect(screen.getByText('My Chatbot')).toBeInTheDocument() expect(screen.getByText('app.types.advanced')).toBeInTheDocument() expect(screen.getByText('My Chatbot').closest('button')).toBeNull() @@ -107,10 +89,10 @@ describe('AppInfoTrigger', () => { expect(props.exportCheck).not.toHaveBeenCalled() }) - it('renders only the medium app icon when collapsed', () => { + it('renders only the app icon when collapsed', () => { render() - expect(screen.getByText('๐Ÿค–')).toHaveAttribute('data-size', 'medium') + expect(screen.getByText('๐Ÿค–')).toBeInTheDocument() expect(screen.queryByText('Test App')).not.toBeInTheDocument() expect(screen.queryByRole('button')).not.toBeInTheDocument() }) diff --git a/web/app/components/app-sidebar/app-info/app-info-header.tsx b/web/app/components/app-sidebar/app-info/app-info-header.tsx index 1dd742b6873586..36136124ec86ba 100644 --- a/web/app/components/app-sidebar/app-info/app-info-header.tsx +++ b/web/app/components/app-sidebar/app-info/app-info-header.tsx @@ -1,13 +1,11 @@ -import type { ComponentProps } from 'react' +import type { AppIconProps } from '../../base/app-icon' import type { Operation } from './app-operations' import { cn } from '@langgenius/dify-ui/cn' import AppIcon from '../../base/app-icon' import AppOperations from './app-operations' -type AppInfoHeaderProps = Pick< - ComponentProps, - 'iconType' | 'icon' | 'background' | 'imageUrl' -> & { +type AppInfoHeaderProps = { + icon: AppIconProps['icon'] expand: boolean appName: string modeLabel?: string @@ -18,10 +16,7 @@ export default function AppInfoHeader({ expand, appName, modeLabel, - iconType, icon, - background, - imageUrl, operationGroups, }: AppInfoHeaderProps) { return ( @@ -32,15 +27,7 @@ export default function AppInfoHeader({ )} >
- +
{expand && (
diff --git a/web/app/components/app-sidebar/app-info/app-info-trigger.tsx b/web/app/components/app-sidebar/app-info/app-info-trigger.tsx index 6d97d49e0ece84..ac08c9ff877f50 100644 --- a/web/app/components/app-sidebar/app-info/app-info-trigger.tsx +++ b/web/app/components/app-sidebar/app-info/app-info-trigger.tsx @@ -116,11 +116,8 @@ const AppInfoTrigger = ({ expand={expand} appName={appDetail.name} modeLabel={modeLabel} - iconType={appDetail.icon_type} - icon={appDetail.icon ?? undefined} - background={appDetail.icon_background} - imageUrl={appDetail.icon_url} operationGroups={[mainOperations, destructiveOperations, workflowConversionOperations]} + icon={appDetail} /> ) } diff --git a/web/app/components/app-sidebar/dataset-info/index.tsx b/web/app/components/app-sidebar/dataset-info/index.tsx index d6af45bd61e15f..32e70c8a39af61 100644 --- a/web/app/components/app-sidebar/dataset-info/index.tsx +++ b/web/app/components/app-sidebar/dataset-info/index.tsx @@ -38,13 +38,7 @@ const DatasetInfo = ({ expand }: DatasetInfoProps) => { )} >
- +
{!expand && {dataset.name}} {expand && ( diff --git a/web/app/components/app/access-point/built-in-access-points/web-app-card.tsx b/web/app/components/app/access-point/built-in-access-points/web-app-card.tsx index 47699bfb05bda7..724a989abcd2f6 100644 --- a/web/app/components/app/access-point/built-in-access-points/web-app-card.tsx +++ b/web/app/components/app/access-point/built-in-access-points/web-app-card.tsx @@ -173,15 +173,7 @@ export function WebAppAccessPointCard({ description={t(($) => $['studio.accessPoint.webAppDescription'], { ns: 'deployments', })} - icon={ - - } + icon={} status={status} statusLabel={statusLabel} highlighted={highlighted} diff --git a/web/app/components/app/access-point/deployed-environment-access-points/environment-web-app-card.tsx b/web/app/components/app/access-point/deployed-environment-access-points/environment-web-app-card.tsx index cc8f0a2a08cf89..774dd349d0209b 100644 --- a/web/app/components/app/access-point/deployed-environment-access-points/environment-web-app-card.tsx +++ b/web/app/components/app/access-point/deployed-environment-access-points/environment-web-app-card.tsx @@ -166,19 +166,7 @@ export function EnvironmentWebAppCard({ description={t(($) => $['studio.accessPoint.webAppDescription'], { ns: 'deployments', })} - icon={ - appInfo ? ( - - ) : ( - 'i-ri-robot-2-line' - ) - } + icon={appInfo ? : 'i-ri-robot-2-line'} status={status} statusLabel={statusLabel} highlighted={highlighted} diff --git a/web/app/components/app/configuration/config/agent/agent-tools/index.tsx b/web/app/components/app/configuration/config/agent/agent-tools/index.tsx index 7336c90330af72..95703f7be31dc1 100644 --- a/web/app/components/app/configuration/config/agent/agent-tools/index.tsx +++ b/web/app/components/app/configuration/config/agent/agent-tools/index.tsx @@ -230,8 +230,11 @@ const AgentTools: FC = () => { )}
diff --git a/web/app/components/app/configuration/dataset-config/card-item/index.tsx b/web/app/components/app/configuration/dataset-config/card-item/index.tsx index 0e491e6de234ca..25d37b257382aa 100644 --- a/web/app/components/app/configuration/dataset-config/card-item/index.tsx +++ b/web/app/components/app/configuration/dataset-config/card-item/index.tsx @@ -61,13 +61,7 @@ const Item: FC = ({ config, onSave, onRemove, readonly = false, edita )} >
- +
{config.name}
diff --git a/web/app/components/app/configuration/dataset-config/select-dataset/index.tsx b/web/app/components/app/configuration/dataset-config/select-dataset/index.tsx index 1c4446670b0b20..28c65f75d8eba8 100644 --- a/web/app/components/app/configuration/dataset-config/select-dataset/index.tsx +++ b/web/app/components/app/configuration/dataset-config/select-dataset/index.tsx @@ -154,19 +154,7 @@ const SelectDataSet: FC = ({ >
- +
{ const { app: appBasicInfo } = app const appName = appBasicInfo?.name ?? '' const appMode = appBasicInfo?.mode ?? '' - const appIconType = - appBasicInfo?.icon_type === 'image' || - appBasicInfo?.icon_type === 'emoji' || - appBasicInfo?.icon_type === 'link' - ? appBasicInfo.icon_type - : null + const canViewApp = deploymentEdition === 'CLOUD' const handleShowTryAppPanel = useCallback(() => { trackEvent('preview_template', { @@ -54,13 +49,7 @@ const AppCard = ({ app, canCreate, onCreate, onPreview }: AppCardProps) => { >
- + ) diff --git a/web/app/components/apps/__tests__/app-card.spec.tsx b/web/app/components/apps/__tests__/app-card.spec.tsx index ceafce9e625043..40e9a3360d9aae 100644 --- a/web/app/components/apps/__tests__/app-card.spec.tsx +++ b/web/app/components/apps/__tests__/app-card.spec.tsx @@ -1,6 +1,6 @@ import type { AppPartial } from '@dify/contracts/api/console/apps/types.gen' import type { InstalledAppListResponse } from '@dify/contracts/api/console/installed-apps/types.gen' -import { fireEvent, screen, waitFor, within } from '@testing-library/react' +import { fireEvent, isInaccessible, screen, waitFor, within } from '@testing-library/react' import userEvent from '@testing-library/user-event' import * as React from 'react' import { STEP_BY_STEP_TOUR_TARGETS } from '@/app/components/step-by-step-tour/target-registry' @@ -580,22 +580,19 @@ describe('AppCard', () => { }) it('should render app icon', () => { - // AppIcon component renders the emoji icon from app data - const { container } = render() + render() const emojiIcon = screen.getByText(mockApp.icon!) - const imageIcon = container.querySelector('img') - expect(emojiIcon || imageIcon).toBeTruthy() - expect(emojiIcon).toHaveAttribute('aria-hidden', 'true') + expect(isInaccessible(emojiIcon)).toBe(true) }) - it('should treat a redundant image icon as decorative', () => { + it('should keep an image fallback decorative beside the app name', () => { const imageApp = createMockApp({ icon_type: 'image', icon_url: 'https://example.com/app-icon.png', }) - const { container } = render() + render() - expect(container.querySelector('img')).toHaveAttribute('alt', '') + expect(isInaccessible(screen.getByText('๐Ÿค–'))).toBe(true) }) it('should render app type icon', () => { diff --git a/web/app/components/apps/app-card/index.tsx b/web/app/components/apps/app-card/index.tsx index eb739cea2d1c49..9ab466664b3cf7 100644 --- a/web/app/components/apps/app-card/index.tsx +++ b/web/app/components/apps/app-card/index.tsx @@ -2,7 +2,6 @@ import type { AppPartial } from '@dify/contracts/api/console/apps/types.gen' import type { WorkflowOnlineUser } from '@/models/app' -import { zIconType } from '@dify/contracts/api/console/apps/zod.gen' import { cn } from '@langgenius/dify-ui/cn' import { useSuspenseQuery } from '@tanstack/react-query' import { useAtomValue } from 'jotai' @@ -117,7 +116,7 @@ export const AppCard = memo( }, [app.id, onlineUsers]) const appNameId = useId() const appDescriptionId = useId() - const appIconType = zIconType.safeParse(app.icon_type).data ?? null + const appHref = getRedirectionPath(app, maintainerPermissionOptions) const appCardClassName = cn( 'inline-flex h-full w-full touch-manipulation flex-col rounded-xl outline-hidden', @@ -130,14 +129,7 @@ export const AppCard = memo( <>
- +
diff --git a/web/app/components/apps/starred-app-card.tsx b/web/app/components/apps/starred-app-card.tsx index 08bd7adc6dae6e..3d43c79e30f418 100644 --- a/web/app/components/apps/starred-app-card.tsx +++ b/web/app/components/apps/starred-app-card.tsx @@ -1,7 +1,6 @@ 'use client' import type { AppPartial } from '@dify/contracts/api/console/apps/types.gen' -import { zIconType } from '@dify/contracts/api/console/apps/zod.gen' import { cn } from '@langgenius/dify-ui/cn' import { useSuspenseQuery } from '@tanstack/react-query' import { useAtomValue } from 'jotai' @@ -36,7 +35,6 @@ export const StarredAppCard = memo( const { data: systemFeatures } = useSuspenseQuery(systemFeaturesQueryOptions()) const isRbacEnabled = systemFeatures.rbac_enabled const isPreviewOnly = hasOnlyAppPreviewPermission(app.permission_keys) - const appIconType = zIconType.safeParse(app.icon_type).data ?? null const editTimeText = useMemo(() => { const timestamp = app.updated_at || app.created_at @@ -65,14 +63,7 @@ export const StarredAppCard = memo( const cardContent = ( <>
- + { + render() + expect(screen.getByText('๐Ÿค–')).toBeInTheDocument() + expect(screen.queryByRole('img')).not.toBeInTheDocument() +}) + +it('does not use an uploaded file ID as a URL or emoji when its resolved URL is missing', () => { + const { container } = render() + expect(screen.getByText('๐Ÿค–')).toBeInTheDocument() + expect(container.querySelector('img')).not.toBeInTheDocument() + expect(screen.queryByText('uploaded-file-id')).not.toBeInTheDocument() +}) diff --git a/web/app/components/base/app-icon/index.stories.tsx b/web/app/components/base/app-icon/index.stories.tsx index 2638626b5a67a5..85d0800848da71 100644 --- a/web/app/components/base/app-icon/index.stories.tsx +++ b/web/app/components/base/app-icon/index.stories.tsx @@ -1,10 +1,7 @@ import type { Meta, StoryObj } from '@storybook/nextjs-vite' -import type { ComponentProps } from 'react' -import { fn } from 'storybook/test' +import type { AppIconProps } from '.' import AppIcon from '.' -const onChooseIcon = fn() - const meta = { title: 'Base/General/AppIcon', component: AppIcon, @@ -12,16 +9,15 @@ const meta = { docs: { description: { component: - 'Reusable avatar for applications and workflows. Supports emoji or uploaded imagery, rounded-sm mode, edit overlays, and multiple sizes.', + 'Application identity display built on Avatar. Sources are emoji or image URLs; editing belongs to IconPicker.', }, }, }, tags: ['autodocs'], args: { - icon: '๐Ÿงญ', - background: '#FFEAD5', + icon: { icon_type: 'emoji', icon: '๐Ÿงญ', icon_background: '#FFEAD5' }, size: 'medium', - rounded: false, + shape: 'square', }, } satisfies Meta @@ -32,96 +28,36 @@ export const Default: Story = { render: (args) => (
- +
), - parameters: { - docs: { - source: { - language: 'tsx', - code: ` - - - `.trim(), - }, - }, - }, } export const Sizes: Story = { render: (args) => { - const sizes: Array['size']> = [ - 'xs', - 'tiny', - 'small', - 'medium', - 'large', - 'xl', - 'xxl', - ] + const sizes: AppIconProps['size'][] = ['xs', 'tiny', 'small', 'medium', 'large', 'xl', 'xxl'] return (
{sizes.map((size) => (
- + {size}
))}
) }, - parameters: { - docs: { - source: { - language: 'tsx', - code: ` -{(['xs','tiny','small','medium','large','xl','xxl'] as const).map(size => ( - -))} - `.trim(), - }, - }, - }, } -export const WithEditOverlay: Story = { - render: (args) => ( -
- - -
- ), - parameters: { - docs: { - source: { - language: 'tsx', - code: ` - - `.trim(), - }, - }, +export const ImageFallback: Story = { + args: { + icon: { icon_type: 'image', icon_url: 'data:image/png;base64,broken' }, + fallback: { content: '๐Ÿ“™', background: '#FFF4ED' }, + decorative: false, + label: 'Knowledge base', }, } diff --git a/web/app/components/base/app-icon/index.tsx b/web/app/components/base/app-icon/index.tsx index 84b9fd8e06b77b..660d8a03559328 100644 --- a/web/app/components/base/app-icon/index.tsx +++ b/web/app/components/base/app-icon/index.tsx @@ -1,98 +1,69 @@ 'use client' -import type { FC } from 'react' -import type { AppIconType } from '@/types/app' + +import type { RecommendedAppInfoResponse } from '@dify/contracts/api/console/explore/types.gen' +import type { ReactNode } from 'react' +import { Avatar, AvatarFallback, AvatarImage } from '@langgenius/dify-ui/avatar' import { cn } from '@langgenius/dify-ui/cn' import { cva } from 'class-variance-authority' -import * as React from 'react' import { resolveEmoji } from '@/utils/emoji' -type AppIconProps = { +type AppIconData = Pick & + Partial> + +export type AppIconProps = { + icon?: AppIconData | null + fallback?: { content: ReactNode; background?: string | null } size?: 'xs' | 'tiny' | 'small' | 'medium' | 'large' | 'xl' | 'xxl' - rounded?: boolean - iconType?: AppIconType | null - icon?: string - background?: string | null - imageUrl?: string | null - decorative?: boolean + shape?: 'square' | 'circle' className?: string - innerIcon?: React.ReactNode - showEditIcon?: boolean -} +} & ({ decorative?: true; label?: never } | { decorative: false; label: string }) + const appIconVariants = cva( - 'relative flex shrink-0 grow-0 items-center justify-center overflow-hidden border-[0.5px] border-divider-regular leading-none', + 'flex shrink-0 grow-0 border-[0.5px] border-divider-regular leading-none', { variants: { size: { xs: 'size-4 rounded-sm text-xs', tiny: 'size-6 rounded-md text-base', small: 'size-8 rounded-lg text-xl', - medium: 'h-9 w-9 rounded-[10px] text-[22px]', - large: 'h-10 w-10 rounded-[10px] text-[24px]', - xl: 'h-12 w-12 rounded-xl text-[28px]', - xxl: 'h-14 w-14 rounded-2xl text-[32px]', + medium: 'size-9 rounded-[10px] text-[22px]', + large: 'size-10 rounded-[10px] text-2xl', + xl: 'size-12 rounded-xl text-[28px]', + xxl: 'size-14 rounded-2xl text-[32px]', }, - rounded: { - true: 'rounded-full', + shape: { + square: '', + circle: 'rounded-full', }, }, - defaultVariants: { - size: 'medium', - rounded: false, - }, }, ) -const EditIconVariants = cva('i-ri-edit-line text-text-primary-on-surface', { - variants: { - size: { - xs: 'size-3', - tiny: 'size-3.5', - small: 'size-5', - medium: 'size-5.5', - large: 'size-6', - xl: 'size-7', - xxl: 'size-8', - }, - }, - defaultVariants: { - size: 'medium', - }, -}) -const AppIcon: FC = ({ - size = 'medium', - rounded = false, - iconType, + +export default function AppIcon({ icon, - background, - imageUrl, - decorative = false, + fallback, + size = 'medium', + shape = 'square', + decorative = true, + label, className, - innerIcon, - showEditIcon = false, -}) => { - const isValidImageIcon = iconType === 'image' && imageUrl - const Icon = resolveEmoji(icon) +}: AppIconProps) { + const isImage = icon?.icon_type === 'image' || icon?.icon_type === 'link' + const src = icon?.icon_type === 'link' ? icon.icon : icon?.icon_url + const emoji = !icon?.icon_type || icon.icon_type === 'emoji' ? icon?.icon : undefined + const background = (!isImage && icon?.icon_background) || fallback?.background || '#FFEAD5' return ( - - {isValidImageIcon ? ( - {decorative - ) : ( - innerIcon || Icon - )} - {showEditIcon && ( - + + {emoji ? resolveEmoji(emoji) : (fallback?.content ?? '๐Ÿค–')} + + {isImage && } + ) } - -export default React.memo(AppIcon) diff --git a/web/app/components/base/chat/chat-with-history/chat-wrapper.tsx b/web/app/components/base/chat/chat-with-history/chat-wrapper.tsx index c9061e303eca6a..ad58c3be4a852c 100644 --- a/web/app/components/base/chat/chat-with-history/chat-wrapper.tsx +++ b/web/app/components/base/chat/chat-with-history/chat-wrapper.tsx @@ -370,13 +370,7 @@ const ChatWrapper = () => { return (
- +
@@ -389,13 +383,7 @@ const ChatWrapper = () => { } return (
- +
{
{!currentConversationId && ( <> - +
{appData?.site.title}
diff --git a/web/app/components/base/chat/chat-with-history/header/index.tsx b/web/app/components/base/chat/chat-with-history/header/index.tsx index d48d07ee79c383..cc0050fba6be3c 100644 --- a/web/app/components/base/chat/chat-with-history/header/index.tsx +++ b/web/app/components/base/chat/chat-with-history/header/index.tsx @@ -102,13 +102,7 @@ const Header = ({ toggleButtonRef }: { toggleButtonRef?: Ref