From 947dbd8854865e963580747a40bf0ab7508c7d27 Mon Sep 17 00:00:00 2001 From: twwu Date: Thu, 17 Jul 2025 14:00:29 +0800 Subject: [PATCH] chore(apps): move app list components to components folder --- web/app/(commonLayout)/apps/assets/add.svg | 3 - .../(commonLayout)/apps/assets/chat-solid.svg | 4 - web/app/(commonLayout)/apps/assets/chat.svg | 3 - .../apps/assets/completion-solid.svg | 4 - .../(commonLayout)/apps/assets/completion.svg | 3 - .../(commonLayout)/apps/assets/discord.svg | 3 - web/app/(commonLayout)/apps/assets/github.svg | 17 -- .../(commonLayout)/apps/assets/link-gray.svg | 3 - web/app/(commonLayout)/apps/assets/link.svg | 3 - .../apps/assets/right-arrow.svg | 3 - web/app/(commonLayout)/apps/layout.tsx | 12 - web/app/(commonLayout)/apps/page.tsx | 28 +-- web/app/(commonLayout)/list.module.css | 217 ------------------ .../components/app/type-selector/index.tsx | 76 +++--- .../apps/app-card.tsx} | 4 +- web/app/components/apps/empty.tsx | 35 +++ web/app/components/apps/footer.tsx | 46 ++++ .../apps/hooks/use-apps-query-state.ts | 0 .../apps/hooks/use-dsl-drag-drop.ts | 2 +- web/app/components/apps/index.tsx | 22 ++ .../Apps.tsx => components/apps/list.tsx} | 29 +-- .../apps/new-app-card.tsx} | 21 +- web/app/components/base/app-icon/index.tsx | 3 +- web/next.config.js | 5 +- web/package.json | 4 +- web/pnpm-lock.yaml | 114 +++++++++ 26 files changed, 284 insertions(+), 380 deletions(-) delete mode 100644 web/app/(commonLayout)/apps/assets/add.svg delete mode 100644 web/app/(commonLayout)/apps/assets/chat-solid.svg delete mode 100644 web/app/(commonLayout)/apps/assets/chat.svg delete mode 100644 web/app/(commonLayout)/apps/assets/completion-solid.svg delete mode 100644 web/app/(commonLayout)/apps/assets/completion.svg delete mode 100644 web/app/(commonLayout)/apps/assets/discord.svg delete mode 100644 web/app/(commonLayout)/apps/assets/github.svg delete mode 100644 web/app/(commonLayout)/apps/assets/link-gray.svg delete mode 100644 web/app/(commonLayout)/apps/assets/link.svg delete mode 100644 web/app/(commonLayout)/apps/assets/right-arrow.svg delete mode 100644 web/app/(commonLayout)/apps/layout.tsx delete mode 100644 web/app/(commonLayout)/list.module.css rename web/app/{(commonLayout)/apps/AppCard.tsx => components/apps/app-card.tsx} (99%) create mode 100644 web/app/components/apps/empty.tsx create mode 100644 web/app/components/apps/footer.tsx rename web/app/{(commonLayout) => components}/apps/hooks/use-apps-query-state.ts (100%) rename web/app/{(commonLayout) => components}/apps/hooks/use-dsl-drag-drop.ts (97%) create mode 100644 web/app/components/apps/index.tsx rename web/app/{(commonLayout)/apps/Apps.tsx => components/apps/list.tsx} (92%) rename web/app/{(commonLayout)/apps/NewAppCard.tsx => components/apps/new-app-card.tsx} (94%) diff --git a/web/app/(commonLayout)/apps/assets/add.svg b/web/app/(commonLayout)/apps/assets/add.svg deleted file mode 100644 index 9958e855aa..0000000000 --- a/web/app/(commonLayout)/apps/assets/add.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/web/app/(commonLayout)/apps/assets/chat-solid.svg b/web/app/(commonLayout)/apps/assets/chat-solid.svg deleted file mode 100644 index a793e982c0..0000000000 --- a/web/app/(commonLayout)/apps/assets/chat-solid.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/web/app/(commonLayout)/apps/assets/chat.svg b/web/app/(commonLayout)/apps/assets/chat.svg deleted file mode 100644 index 0971349a53..0000000000 --- a/web/app/(commonLayout)/apps/assets/chat.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/web/app/(commonLayout)/apps/assets/completion-solid.svg b/web/app/(commonLayout)/apps/assets/completion-solid.svg deleted file mode 100644 index a9dc7e3dc1..0000000000 --- a/web/app/(commonLayout)/apps/assets/completion-solid.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/web/app/(commonLayout)/apps/assets/completion.svg b/web/app/(commonLayout)/apps/assets/completion.svg deleted file mode 100644 index 34af4417fe..0000000000 --- a/web/app/(commonLayout)/apps/assets/completion.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/web/app/(commonLayout)/apps/assets/discord.svg b/web/app/(commonLayout)/apps/assets/discord.svg deleted file mode 100644 index 9f22a1ab59..0000000000 --- a/web/app/(commonLayout)/apps/assets/discord.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/web/app/(commonLayout)/apps/assets/github.svg b/web/app/(commonLayout)/apps/assets/github.svg deleted file mode 100644 index f03798b5e1..0000000000 --- a/web/app/(commonLayout)/apps/assets/github.svg +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - - - - - - - - - - diff --git a/web/app/(commonLayout)/apps/assets/link-gray.svg b/web/app/(commonLayout)/apps/assets/link-gray.svg deleted file mode 100644 index a293cfcf53..0000000000 --- a/web/app/(commonLayout)/apps/assets/link-gray.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/web/app/(commonLayout)/apps/assets/link.svg b/web/app/(commonLayout)/apps/assets/link.svg deleted file mode 100644 index 2926c28b16..0000000000 --- a/web/app/(commonLayout)/apps/assets/link.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/web/app/(commonLayout)/apps/assets/right-arrow.svg b/web/app/(commonLayout)/apps/assets/right-arrow.svg deleted file mode 100644 index a2c1cedf95..0000000000 --- a/web/app/(commonLayout)/apps/assets/right-arrow.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/web/app/(commonLayout)/apps/layout.tsx b/web/app/(commonLayout)/apps/layout.tsx deleted file mode 100644 index 10d04a4188..0000000000 --- a/web/app/(commonLayout)/apps/layout.tsx +++ /dev/null @@ -1,12 +0,0 @@ -'use client' - -import useDocumentTitle from '@/hooks/use-document-title' -import { useTranslation } from 'react-i18next' - -export default function DatasetsLayout({ children }: { children: React.ReactNode }) { - const { t } = useTranslation() - useDocumentTitle(t('common.menus.apps')) - return (<> - {children} - ) -} diff --git a/web/app/(commonLayout)/apps/page.tsx b/web/app/(commonLayout)/apps/page.tsx index 3f617d41c9..25b6d55d11 100644 --- a/web/app/(commonLayout)/apps/page.tsx +++ b/web/app/(commonLayout)/apps/page.tsx @@ -1,32 +1,8 @@ -'use client' -import { useTranslation } from 'react-i18next' -import { RiDiscordFill, RiGithubFill } from '@remixicon/react' -import Link from 'next/link' -import style from '../list.module.css' -import Apps from './Apps' -import { useEducationInit } from '@/app/education-apply/hooks' -import { useGlobalPublicStore } from '@/context/global-public-context' +import Apps from '@/app/components/apps' const AppList = () => { - const { t } = useTranslation() - useEducationInit() - const { systemFeatures } = useGlobalPublicStore() return ( -
- - {!systemFeatures.branding.enabled &&
-

{t('app.join')}

-

{t('app.communityIntro')}

-
- - - - - - -
-
} -
+ ) } diff --git a/web/app/(commonLayout)/list.module.css b/web/app/(commonLayout)/list.module.css deleted file mode 100644 index c4d3aec29f..0000000000 --- a/web/app/(commonLayout)/list.module.css +++ /dev/null @@ -1,217 +0,0 @@ -.listItem { - @apply col-span-1 bg-white border-2 border-solid border-transparent rounded-xl shadow-xs min-h-[160px] flex flex-col transition-all duration-200 ease-in-out cursor-pointer hover:shadow-lg; -} - -.listItem.newItemCard { - @apply outline outline-1 outline-gray-200 -outline-offset-1 hover:shadow-sm hover:bg-white; - background-color: rgba(229, 231, 235, 0.5); -} - -.listItem.selectable { - @apply relative bg-gray-50 outline outline-1 outline-gray-200 -outline-offset-1 shadow-none hover:bg-none hover:shadow-none hover:outline-primary-200 transition-colors; -} - -.listItem.selectable * { - @apply relative; -} - -.listItem.selectable::before { - content: ""; - @apply absolute top-0 left-0 block w-full h-full rounded-lg pointer-events-none opacity-0 transition-opacity duration-200 ease-in-out hover:opacity-100; - background: linear-gradient(0deg, - rgba(235, 245, 255, 0.5), - rgba(235, 245, 255, 0.5)), - #ffffff; -} - -.listItem.selectable:hover::before { - @apply opacity-100; -} - -.listItem.selected { - @apply border-primary-600 hover:border-primary-600 border-2; -} - -.listItem.selected::before { - @apply opacity-100; -} - -.appIcon { - @apply flex items-center justify-center w-8 h-8 bg-pink-100 rounded-lg grow-0 shrink-0; -} - -.appIcon.medium { - @apply w-9 h-9; -} - -.appIcon.large { - @apply w-10 h-10; -} - -.newItemIcon { - @apply flex items-center justify-center w-8 h-8 transition-colors duration-200 ease-in-out border border-gray-200 rounded-lg hover:bg-white grow-0 shrink-0; -} - -.listItem:hover .newItemIcon { - @apply bg-gray-50 border-primary-100; -} - -.newItemCard .newItemIcon { - @apply bg-gray-100; -} - -.newItemCard:hover .newItemIcon { - @apply bg-white; -} - -.selectable .newItemIcon { - @apply bg-gray-50; -} - -.selectable:hover .newItemIcon { - @apply bg-primary-50; -} - -.newItemIconImage { - @apply grow-0 shrink-0 block w-4 h-4 bg-center bg-contain transition-colors duration-200 ease-in-out; - color: #1f2a37; -} - -.listItem:hover .newIconImage { - @apply text-primary-600; -} - -.newItemIconAdd { - background-image: url("./apps/assets/add.svg"); -} - -/* .newItemIconChat { - background-image: url("~@/app/components/base/icons/assets/public/header-nav/studio/Robot.svg"); -} - -.selected .newItemIconChat { - background-image: url("~@/app/components/base/icons/assets/public/header-nav/studio/Robot-Active.svg"); -} */ - -.newItemIconComplete { - background-image: url("./apps/assets/completion.svg"); -} - -.listItemTitle { - @apply flex pt-[14px] px-[14px] pb-3 h-[66px] items-center gap-3 grow-0 shrink-0; -} - -.listItemHeading { - @apply relative h-8 text-sm font-medium leading-8 grow; -} - -.listItemHeadingContent { - @apply absolute top-0 left-0 w-full h-full overflow-hidden text-ellipsis whitespace-nowrap; -} - -.actionIconWrapper { - @apply hidden h-8 w-8 p-2 rounded-md border-none hover:bg-gray-100 !important; -} - -.listItem:hover .actionIconWrapper { - @apply !inline-flex; -} - -.deleteDatasetIcon { - @apply hidden grow-0 shrink-0 basis-8 w-8 h-8 rounded-lg transition-colors duration-200 ease-in-out bg-white border border-gray-200 hover:bg-gray-100 bg-center bg-no-repeat; - background-size: 16px; - background-image: url('~@/assets/delete.svg'); -} - -.listItem:hover .deleteDatasetIcon { - @apply block; -} - -.listItemDescription { - @apply mb-3 px-[14px] h-9 text-xs leading-normal text-gray-500 line-clamp-2; -} - -.listItemDescription.noClip { - @apply line-clamp-none; -} - -.listItemFooter { - @apply flex items-center flex-wrap min-h-[42px] px-[14px] pt-2 pb-[10px]; -} - -.listItemFooter.datasetCardFooter { - @apply flex items-center gap-4 text-xs text-gray-500; -} - -.listItemStats { - @apply flex items-center gap-1; -} - -.listItemFooterIcon { - @apply block w-3 h-3 bg-center bg-contain; -} - -.solidChatIcon { - background-image: url("./apps/assets/chat-solid.svg"); -} - -.solidCompletionIcon { - background-image: url("./apps/assets/completion-solid.svg"); -} - -.newItemCardHeading { - @apply transition-colors duration-200 ease-in-out; -} - -.listItem:hover .newItemCardHeading { - @apply text-primary-600; -} - -.listItemLink { - @apply inline-flex items-center gap-1 text-xs text-gray-400 transition-colors duration-200 ease-in-out; -} - -.listItem:hover .listItemLink { - @apply text-primary-600; -} - -.linkIcon { - @apply block w-[13px] h-[13px] bg-center bg-contain; - background-image: url("./apps/assets/link.svg"); -} - -.linkIcon.grayLinkIcon { - background-image: url("./apps/assets/link-gray.svg"); -} - -.listItem:hover .grayLinkIcon { - background-image: url("./apps/assets/link.svg"); -} - -.rightIcon { - @apply block w-[13px] h-[13px] bg-center bg-contain; - background-image: url("./apps/assets/right-arrow.svg"); -} - -.socialMediaLink { - @apply flex items-center justify-center w-8 h-8 cursor-pointer hover:opacity-80 transition-opacity duration-200 ease-in-out; -} - -.socialMediaIcon { - @apply block w-6 h-6 bg-center bg-contain; -} - -/* #region new app dialog */ -.newItemCaption { - @apply inline-flex items-center mb-2 text-sm font-medium; -} - -/* #endregion new app dialog */ - -.unavailable { - @apply opacity-50; -} - -.listItem:hover .unavailable { - @apply opacity-100; -} diff --git a/web/app/components/app/type-selector/index.tsx b/web/app/components/app/type-selector/index.tsx index a57bac20db..99a76d7ac7 100644 --- a/web/app/components/app/type-selector/index.tsx +++ b/web/app/components/app/type-selector/index.tsx @@ -65,6 +65,44 @@ const AppTypeSelector = ({ value, onChange }: AppSelectorProps) => { export default AppTypeSelector +type AppTypeIconProps = { + type: AppMode + style?: React.CSSProperties + className?: string + wrapperClassName?: string +} + +export const AppTypeIcon = React.memo(({ type, className, wrapperClassName, style }: AppTypeIconProps) => { + const wrapperClassNames = cn('inline-flex h-5 w-5 items-center justify-center rounded-md border border-divider-regular', wrapperClassName) + const iconClassNames = cn('h-3.5 w-3.5 text-components-avatar-shape-fill-stop-100', className) + if (type === 'chat') { + return
+ +
+ } + if (type === 'agent-chat') { + return
+ +
+ } + if (type === 'advanced-chat') { + return
+ +
+ } + if (type === 'workflow') { + return
+ +
+ } + if (type === 'completion') { + return
+ +
+ } + return null +}) + function AppTypeSelectTrigger({ values }: { values: AppSelectorProps['value'] }) { const { t } = useTranslation() if (!values || values.length === 0) { @@ -108,44 +146,6 @@ function AppTypeSelectorItem({ checked, type, onClick }: AppTypeSelectorItemProp } -type AppTypeIconProps = { - type: AppMode - style?: React.CSSProperties - className?: string - wrapperClassName?: string -} - -export function AppTypeIcon({ type, className, wrapperClassName, style }: AppTypeIconProps) { - const wrapperClassNames = cn('inline-flex h-5 w-5 items-center justify-center rounded-md border border-divider-regular', wrapperClassName) - const iconClassNames = cn('h-3.5 w-3.5 text-components-avatar-shape-fill-stop-100', className) - if (type === 'chat') { - return
- -
- } - if (type === 'agent-chat') { - return
- -
- } - if (type === 'advanced-chat') { - return
- -
- } - if (type === 'workflow') { - return
- -
- } - if (type === 'completion') { - return
- -
- } - return null -} - type AppTypeLabelProps = { type: AppMode className?: string diff --git a/web/app/(commonLayout)/apps/AppCard.tsx b/web/app/components/apps/app-card.tsx similarity index 99% rename from web/app/(commonLayout)/apps/AppCard.tsx rename to web/app/components/apps/app-card.tsx index bdb9f3abe4..5bbd8d17f6 100644 --- a/web/app/(commonLayout)/apps/AppCard.tsx +++ b/web/app/components/apps/app-card.tsx @@ -1,8 +1,8 @@ 'use client' +import React, { useCallback, useEffect, useMemo, useState } from 'react' import { useContext, useContextSelector } from 'use-context-selector' import { useRouter } from 'next/navigation' -import { useCallback, useEffect, useMemo, useState } from 'react' import { useTranslation } from 'react-i18next' import { RiBuildingLine, RiGlobalLine, RiLockLine, RiMoreFill, RiVerifiedBadgeLine } from '@remixicon/react' import cn from '@/utils/classnames' @@ -497,4 +497,4 @@ const AppCard = ({ app, onRefresh }: AppCardProps) => { ) } -export default AppCard +export default React.memo(AppCard) diff --git a/web/app/components/apps/empty.tsx b/web/app/components/apps/empty.tsx new file mode 100644 index 0000000000..e6b52294a2 --- /dev/null +++ b/web/app/components/apps/empty.tsx @@ -0,0 +1,35 @@ +import React from 'react' +import { useTranslation } from 'react-i18next' + +const DefaultCards = React.memo(() => { + const renderArray = Array.from({ length: 36 }) + return ( + <> + { + renderArray.map((_, index) => ( +
+ )) + } + + ) +}) + +const Empty = () => { + const { t } = useTranslation() + + return ( + <> + +
+ + {t('app.newApp.noAppsFound')} + +
+ + ) +} + +export default React.memo(Empty) diff --git a/web/app/components/apps/footer.tsx b/web/app/components/apps/footer.tsx new file mode 100644 index 0000000000..7bee272342 --- /dev/null +++ b/web/app/components/apps/footer.tsx @@ -0,0 +1,46 @@ +import React from 'react' +import Link from 'next/link' +import { RiDiscordFill, RiGithubFill } from '@remixicon/react' +import { useTranslation } from 'react-i18next' + +type CustomLinkProps = { + href: string + children: React.ReactNode +} + +const CustomLink = React.memo(({ + href, + children, +}: CustomLinkProps) => { + return ( + + {children} + + ) +}) + +const Footer = () => { + const { t } = useTranslation() + + return ( +
+

{t('app.join')}

+

{t('app.communityIntro')}

+
+ + + + + + +
+
+ ) +} + +export default React.memo(Footer) diff --git a/web/app/(commonLayout)/apps/hooks/use-apps-query-state.ts b/web/app/components/apps/hooks/use-apps-query-state.ts similarity index 100% rename from web/app/(commonLayout)/apps/hooks/use-apps-query-state.ts rename to web/app/components/apps/hooks/use-apps-query-state.ts diff --git a/web/app/(commonLayout)/apps/hooks/use-dsl-drag-drop.ts b/web/app/components/apps/hooks/use-dsl-drag-drop.ts similarity index 97% rename from web/app/(commonLayout)/apps/hooks/use-dsl-drag-drop.ts rename to web/app/components/apps/hooks/use-dsl-drag-drop.ts index 96942ec54e..dda5773062 100644 --- a/web/app/(commonLayout)/apps/hooks/use-dsl-drag-drop.ts +++ b/web/app/components/apps/hooks/use-dsl-drag-drop.ts @@ -2,7 +2,7 @@ import { useEffect, useState } from 'react' type DSLDragDropHookProps = { onDSLFileDropped: (file: File) => void - containerRef: React.RefObject + containerRef: React.RefObject enabled?: boolean } diff --git a/web/app/components/apps/index.tsx b/web/app/components/apps/index.tsx new file mode 100644 index 0000000000..68748439c7 --- /dev/null +++ b/web/app/components/apps/index.tsx @@ -0,0 +1,22 @@ +'use client' +import { useEducationInit } from '@/app/education-apply/hooks' +import { useGlobalPublicStore } from '@/context/global-public-context' +import List from './list' +import Footer from './footer' + +const Apps = () => { + const { systemFeatures } = useGlobalPublicStore() + + useEducationInit() + + return ( +
+ + {!systemFeatures.branding.enabled && ( +
+ )} +
+ ) +} + +export default Apps diff --git a/web/app/(commonLayout)/apps/Apps.tsx b/web/app/components/apps/list.tsx similarity index 92% rename from web/app/(commonLayout)/apps/Apps.tsx rename to web/app/components/apps/list.tsx index 9dc8c16097..359eaeabd4 100644 --- a/web/app/(commonLayout)/apps/Apps.tsx +++ b/web/app/components/apps/list.tsx @@ -15,8 +15,8 @@ import { RiMessage3Line, RiRobot3Line, } from '@remixicon/react' -import AppCard from './AppCard' -import NewAppCard from './NewAppCard' +import AppCard from './app-card' +import NewAppCard from './new-app-card' import useAppsQueryState from './hooks/use-apps-query-state' import { useDSLDragDrop } from './hooks/use-dsl-drag-drop' import type { AppListResponse } from '@/models/app' @@ -31,6 +31,7 @@ import { useStore as useTagStore } from '@/app/components/base/tag-management/st import TagFilter from '@/app/components/base/tag-management/filter' import CheckboxWithLabel from '@/app/components/datasets/create/website/base/checkbox-with-label' import dynamic from 'next/dynamic' +import Empty from './empty' const TagManagementModal = dynamic(() => import('@/app/components/base/tag-management'), { ssr: false, @@ -63,7 +64,7 @@ const getKey = ( return null } -const Apps = () => { +const List = () => { const { t } = useTranslation() const router = useRouter() const { isCurrentWorkspaceEditor, isCurrentWorkspaceDatasetOperator } = useAppContext() @@ -215,7 +216,7 @@ const Apps = () => { :
{isCurrentWorkspaceEditor && } - +
} {isCurrentWorkspaceEditor && ( @@ -254,22 +255,4 @@ const Apps = () => { ) } -export default Apps - -function NoAppsFound() { - const { t } = useTranslation() - function renderDefaultCard() { - const defaultCards = Array.from({ length: 36 }, (_, index) => ( -
- )) - return defaultCards - } - return ( - <> - {renderDefaultCard()} -
- {t('app.newApp.noAppsFound')} -
- - ) -} +export default List diff --git a/web/app/(commonLayout)/apps/NewAppCard.tsx b/web/app/components/apps/new-app-card.tsx similarity index 94% rename from web/app/(commonLayout)/apps/NewAppCard.tsx rename to web/app/components/apps/new-app-card.tsx index 2761a257e9..451d2ae326 100644 --- a/web/app/(commonLayout)/apps/NewAppCard.tsx +++ b/web/app/components/apps/new-app-card.tsx @@ -1,6 +1,6 @@ 'use client' -import { useMemo, useState } from 'react' +import React, { useMemo, useState } from 'react' import { useRouter, useSearchParams, @@ -25,17 +25,14 @@ const CreateFromDSLModal = dynamic(() => import('@/app/components/app/create-fro export type CreateAppCardProps = { className?: string onSuccess?: () => void + ref: React.RefObject } -const CreateAppCard = ( - { - ref, - className, - onSuccess, - }: CreateAppCardProps & { - ref: React.RefObject; - }, -) => { +const CreateAppCard = ({ + ref, + className, + onSuccess, +}: CreateAppCardProps) => { const { t } = useTranslation() const { onPlanInfoChanged } = useProviderContext() const searchParams = useSearchParams() @@ -129,5 +126,5 @@ const CreateAppCard = ( } CreateAppCard.displayName = 'CreateAppCard' -export default CreateAppCard -export { CreateAppCard } + +export default React.memo(CreateAppCard) diff --git a/web/app/components/base/app-icon/index.tsx b/web/app/components/base/app-icon/index.tsx index 003d929c8c..b4724ca5de 100644 --- a/web/app/components/base/app-icon/index.tsx +++ b/web/app/components/base/app-icon/index.tsx @@ -1,5 +1,6 @@ 'use client' +import React from 'react' import type { FC } from 'react' import { init } from 'emoji-mart' import data from '@emoji-mart/data' @@ -71,4 +72,4 @@ const AppIcon: FC = ({ } -export default AppIcon +export default React.memo(AppIcon) diff --git a/web/next.config.js b/web/next.config.js index 9ce1b35644..00793bf26a 100644 --- a/web/next.config.js +++ b/web/next.config.js @@ -12,6 +12,9 @@ const withMDX = require('@next/mdx')({ // providerImportSource: "@mdx-js/react", }, }) +const withBundleAnalyzer = require('@next/bundle-analyzer')({ + enabled: process.env.ANALYZE === 'true', +}) // the default url to prevent parse url error when running jest const hasSetWebPrefix = process.env.NEXT_PUBLIC_WEB_PREFIX @@ -66,4 +69,4 @@ const nextConfig = { output: 'standalone', } -module.exports = withMDX(nextConfig) +module.exports = withBundleAnalyzer(withMDX(nextConfig)) diff --git a/web/package.json b/web/package.json index 9099d3ed36..b9577c6e55 100644 --- a/web/package.json +++ b/web/package.json @@ -36,7 +36,8 @@ "test:watch": "jest --watch", "storybook": "storybook dev -p 6006", "build-storybook": "storybook build", - "preinstall": "npx only-allow pnpm" + "preinstall": "npx only-allow pnpm", + "analyze": "ANALYZE=true pnpm build" }, "dependencies": { "@babel/runtime": "^7.22.3", @@ -58,6 +59,7 @@ "@mdx-js/loader": "^3.1.0", "@mdx-js/react": "^3.1.0", "@monaco-editor/react": "^4.6.0", + "@next/bundle-analyzer": "^15.4.1", "@next/mdx": "~15.3.5", "@octokit/core": "^6.1.2", "@octokit/request-error": "^6.1.5", diff --git a/web/pnpm-lock.yaml b/web/pnpm-lock.yaml index 746b2b3e72..4da2578bdb 100644 --- a/web/pnpm-lock.yaml +++ b/web/pnpm-lock.yaml @@ -75,6 +75,9 @@ importers: '@monaco-editor/react': specifier: ^4.6.0 version: 4.7.0(monaco-editor@0.52.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@next/bundle-analyzer': + specifier: ^15.4.1 + version: 15.4.1 '@next/mdx': specifier: ~15.3.5 version: 15.3.5(@mdx-js/loader@3.1.0(acorn@8.14.1)(webpack@5.99.5(esbuild@0.25.0)(uglify-js@3.19.3)))(@mdx-js/react@3.1.0(@types/react@19.1.8)(react@19.1.0)) @@ -1277,6 +1280,10 @@ packages: resolution: {integrity: sha512-mepCf/e9+SKYy1d02/UkvSy6+6MoyXhVxP8lLDfA7BPE1X1d4dR0sZznmbM8/XVJ1GPM+Svnx7Xj6ZweByWUkw==} engines: {node: '>17.0.0'} + '@discoveryjs/json-ext@0.5.7': + resolution: {integrity: sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==} + engines: {node: '>=10.0.0'} + '@emnapi/core@1.4.0': resolution: {integrity: sha512-H+N/FqT07NmLmt6OFFtDfwe8PNygprzBikrEMyQfgqSmT0vzE515Pz7R8izwB9q/zsH/MA64AKoul3sA6/CzVg==} @@ -2224,6 +2231,9 @@ packages: '@napi-rs/wasm-runtime@0.2.8': resolution: {integrity: sha512-OBlgKdX7gin7OIq4fadsjpg+cp2ZphvAIKucHsNfTdJiqdOmOEwQd/bHi0VwNrcw5xpBJyUw6cK/QilCqy1BSg==} + '@next/bundle-analyzer@15.4.1': + resolution: {integrity: sha512-O5R3iPLR3/oQWFIXl+Mnd02IyhvWBterTlXcceIGw29QHWL/gjvyO0eIVEvrJPS7zzE6/NSu1TiSVgi8mxotlw==} + '@next/env@15.3.5': resolution: {integrity: sha512-7g06v8BUVtN2njAX/r8gheoVffhiKFVt4nx74Tt6G4Hqw9HCLYQVx/GkH2qHvPtAHZaUNZ0VXAa0pQP6v1wk7g==} @@ -2455,6 +2465,9 @@ packages: webpack-plugin-serve: optional: true + '@polka/url@1.0.0-next.29': + resolution: {integrity: sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==} + '@react-aria/focus@3.20.1': resolution: {integrity: sha512-lgYs+sQ1TtBrAXnAdRBQrBo0/7o5H6IrfDxec1j+VRpcXL0xyk0xPq+m3lZp8typzIghqDgpnKkJ5Jf4OrzPIw==} peerDependencies: @@ -4561,6 +4574,9 @@ packages: dayjs@1.11.13: resolution: {integrity: sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==} + debounce@1.2.1: + resolution: {integrity: sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug==} + debug@3.2.7: resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==} peerDependencies: @@ -4734,6 +4750,9 @@ packages: resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} engines: {node: '>= 0.4'} + duplexer@0.1.2: + resolution: {integrity: sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==} + echarts-for-react@3.0.2: resolution: {integrity: sha512-DRwIiTzx8JfwPOVgGttDytBqdp5VzCSyMRIxubgU/g2n9y3VLUmF2FK7Icmg/sNVkv4+rktmrLN9w22U2yy3fA==} peerDependencies: @@ -5551,6 +5570,10 @@ packages: graphemer@1.4.0: resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} + gzip-size@6.0.0: + resolution: {integrity: sha512-ax7ZYomf6jqPTQ4+XCpUGyXKHk5WweS+e05MBO4/y3WJ5RkmPXNKvX+bx1behVILVwr6JSQvZAku021CHPXG3Q==} + engines: {node: '>=10'} + hachure-fill@0.5.2: resolution: {integrity: sha512-3GKBOn+m2LX9iq+JC1064cSFprJY4jL1jCXTcpnfER5HYE2l/4EfWSGzkPa/ZDBmYI0ZOEj5VHV/eKnPGkHuOg==} @@ -5955,6 +5978,10 @@ packages: resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==} engines: {node: '>=12'} + is-plain-object@5.0.0: + resolution: {integrity: sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==} + engines: {node: '>=0.10.0'} + is-regex@1.2.1: resolution: {integrity: sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==} engines: {node: '>= 0.4'} @@ -6739,6 +6766,10 @@ packages: monaco-editor@0.52.2: resolution: {integrity: sha512-GEQWEZmfkOGLdd3XK8ryrfWz3AIP8YymVXiPHEdewrUq7mh0qrKrfHLNCXcbB6sTnMLnOZ3ztSiKcciFUkIJwQ==} + mrmime@2.0.1: + resolution: {integrity: sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==} + engines: {node: '>=10'} + ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} @@ -6925,6 +6956,10 @@ packages: resolution: {integrity: sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==} engines: {node: '>=12'} + opener@1.5.2: + resolution: {integrity: sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==} + hasBin: true + optionator@0.9.4: resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} engines: {node: '>= 0.8.0'} @@ -7919,6 +7954,10 @@ packages: simple-swizzle@0.2.2: resolution: {integrity: sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==} + sirv@2.0.4: + resolution: {integrity: sha512-94Bdh3cC2PKrbgSOUqTiGPWVZeSiXfKOVZNJniWoqrWrRkB1CJzBU3NEbiTsPcYy1lDsANA/THzS+9WBiy5nfQ==} + engines: {node: '>= 10'} + sisteransi@1.0.5: resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} @@ -8280,6 +8319,10 @@ packages: resolution: {integrity: sha512-khrZo4buq4qVmsGzS5yQjKe/WsFvV8fGfOjDQN0q4iy9FjRfPWRgTFrU8u1R2iu/SfWLhY9WnCi4Jhdrcbtg+g==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + totalist@3.0.1: + resolution: {integrity: sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==} + engines: {node: '>=6'} + tr46@0.0.3: resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} @@ -8707,6 +8750,11 @@ packages: resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==} engines: {node: '>=12'} + webpack-bundle-analyzer@4.10.1: + resolution: {integrity: sha512-s3P7pgexgT/HTUSYgxJyn28A+99mmLq4HsJepMPzu0R8ImJc52QNqaFYW1Z2z2uIb1/J3eYgaAWVpaC+v/1aAQ==} + engines: {node: '>= 10.13.0'} + hasBin: true + webpack-code-inspector-plugin@0.18.3: resolution: {integrity: sha512-3782rsJhBnRiw0IpR6EqnyGDQoiSq0CcGeLJ52rZXlszYCe8igXtcujq7OhI0byaivWQ1LW7sXKyMEoVpBhq0w==} @@ -8798,6 +8846,18 @@ packages: resolution: {integrity: sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==} engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + ws@7.5.10: + resolution: {integrity: sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==} + engines: {node: '>=8.3.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: ^5.0.2 + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + ws@8.18.1: resolution: {integrity: sha512-RKW2aJZMXeMxVpnZ6bck+RswznaxmzdULiBr6KY7XkTnW8uvt0iT9H5DkHUChXrc+uurzwa0rVI16n/Xzjdz1w==} engines: {node: '>=10.0.0'} @@ -9817,6 +9877,8 @@ snapshots: '@dagrejs/graphlib@2.2.4': {} + '@discoveryjs/json-ext@0.5.7': {} + '@emnapi/core@1.4.0': dependencies: '@emnapi/wasi-threads': 1.0.1 @@ -10883,6 +10945,13 @@ snapshots: '@tybys/wasm-util': 0.9.0 optional: true + '@next/bundle-analyzer@15.4.1': + dependencies: + webpack-bundle-analyzer: 4.10.1 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + '@next/env@15.3.5': {} '@next/eslint-plugin-next@15.3.5': @@ -11058,6 +11127,8 @@ snapshots: type-fest: 4.39.1 webpack-hot-middleware: 2.26.1 + '@polka/url@1.0.0-next.29': {} + '@react-aria/focus@3.20.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@react-aria/interactions': 3.24.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0) @@ -13608,6 +13679,8 @@ snapshots: dayjs@1.11.13: {} + debounce@1.2.1: {} + debug@3.2.7: dependencies: ms: 2.1.3 @@ -13756,6 +13829,8 @@ snapshots: es-errors: 1.3.0 gopd: 1.2.0 + duplexer@0.1.2: {} + echarts-for-react@3.0.2(echarts@5.6.0)(react@19.1.0): dependencies: echarts: 5.6.0 @@ -14961,6 +15036,10 @@ snapshots: graphemer@1.4.0: {} + gzip-size@6.0.0: + dependencies: + duplexer: 0.1.2 + hachure-fill@0.5.2: {} happy-dom@17.4.4: @@ -15435,6 +15514,8 @@ snapshots: is-plain-obj@4.1.0: {} + is-plain-object@5.0.0: {} + is-regex@1.2.1: dependencies: call-bound: 1.0.4 @@ -16699,6 +16780,8 @@ snapshots: monaco-editor@0.52.2: {} + mrmime@2.0.1: {} + ms@2.1.3: {} mz@2.7.0: @@ -16911,6 +16994,8 @@ snapshots: is-docker: 2.2.1 is-wsl: 2.2.0 + opener@1.5.2: {} + optionator@0.9.4: dependencies: deep-is: 0.1.4 @@ -18132,6 +18217,12 @@ snapshots: dependencies: is-arrayish: 0.3.2 + sirv@2.0.4: + dependencies: + '@polka/url': 1.0.0-next.29 + mrmime: 2.0.1 + totalist: 3.0.1 + sisteransi@1.0.5: {} size-sensor@1.0.2: {} @@ -18517,6 +18608,8 @@ snapshots: dependencies: eslint-visitor-keys: 3.4.3 + totalist@3.0.1: {} + tr46@0.0.3: optional: true @@ -18971,6 +19064,25 @@ snapshots: webidl-conversions@7.0.0: {} + webpack-bundle-analyzer@4.10.1: + dependencies: + '@discoveryjs/json-ext': 0.5.7 + acorn: 8.14.1 + acorn-walk: 8.3.4 + commander: 7.2.0 + debounce: 1.2.1 + escape-string-regexp: 4.0.0 + gzip-size: 6.0.0 + html-escaper: 2.0.2 + is-plain-object: 5.0.0 + opener: 1.5.2 + picocolors: 1.1.1 + sirv: 2.0.4 + ws: 7.5.10 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + webpack-code-inspector-plugin@0.18.3: dependencies: code-inspector-core: 0.18.3 @@ -19117,6 +19229,8 @@ snapshots: imurmurhash: 0.1.4 signal-exit: 3.0.7 + ws@7.5.10: {} + ws@8.18.1: {} xml-name-validator@4.0.0: {}