+
+ {currentFileName}
-
-
- {currentFileName}
-
-
- {!!currentFileExtension && {currentFileExtension}}
- {!!currentFileExtension && !!currentFileSize && ·}
- {!!currentFileSize && {currentFileSize}}
-
+
+ {!!currentFileExtension && {currentFileExtension}}
+ {!!currentFileExtension && !!currentFileSize && ·}
+ {!!currentFileSize && {currentFileSize}}
-
- {isFileUploading && (
-
- )}
+
+
+ {isFileUploading && (
+
+ )}
+
+
+
+ )
+ : (
+
event.preventDefault()}
+ onDrop={handleDropFile}
+ >
+
+
+
+ {t('batch.uploadDropzonePrefix')}
+ {' '}
+ {t('batch.uploadDropzoneEmphasis')}
+ {' '}
+ {t('batch.uploadDropzoneSuffix')}
+
+
+ {t('batch.uploadDropzoneDownloadPrefix')}
+ {' '}
- )
- : (
-
event.preventDefault()}
- onDrop={handleDropFile}
- >
-
-
-
- {t('batch.uploadDropzonePrefix')}
- {' '}
- {t('batch.uploadDropzoneEmphasis')}
- {' '}
- {t('batch.uploadDropzoneSuffix')}
-
-
- {t('batch.uploadDropzoneDownloadPrefix')}
- {' '}
-
-
-
-
- )}
+
+ )}
{!!previewFields.length && (
diff --git a/web/app/components/evaluation/components/batch-test-panel/input-fields/use-input-fields-actions.ts b/web/app/components/evaluation/components/batch-test-panel/input-fields/use-input-fields-actions.ts
index 8db1b0fbdd..79a20ebd10 100644
--- a/web/app/components/evaluation/components/batch-test-panel/input-fields/use-input-fields-actions.ts
+++ b/web/app/components/evaluation/components/batch-test-panel/input-fields/use-input-fields-actions.ts
@@ -3,7 +3,7 @@ import type { InputField } from './input-fields-utils'
import { useMutation } from '@tanstack/react-query'
import { useState } from 'react'
import { useTranslation } from 'react-i18next'
-import { toast } from '@/app/components/base/ui/toast'
+import { toast } from '@langgenius/dify-ui/toast'
import { upload } from '@/service/base'
import { useStartEvaluationRunMutation } from '@/service/use-evaluation'
import { formatFileSize } from '@/utils/format'
diff --git a/web/app/components/evaluation/components/conditions-section/condition-group.tsx b/web/app/components/evaluation/components/conditions-section/condition-group.tsx
index 8649ec7f1d..5896f43c60 100644
--- a/web/app/components/evaluation/components/conditions-section/condition-group.tsx
+++ b/web/app/components/evaluation/components/conditions-section/condition-group.tsx
@@ -10,7 +10,7 @@ import { cn } from '@langgenius/dify-ui/cn'
import { useMemo } from 'react'
import { useTranslation } from 'react-i18next'
import Input from '@/app/components/base/input'
-import { Button } from '@/app/components/base/ui/button'
+import { Button } from '@langgenius/dify-ui/button'
import {
Select,
SelectContent,
diff --git a/web/app/components/evaluation/components/metric-section/builtin-metric-card.tsx b/web/app/components/evaluation/components/metric-section/builtin-metric-card.tsx
index f8103aa2cc..d3f2478c05 100644
--- a/web/app/components/evaluation/components/metric-section/builtin-metric-card.tsx
+++ b/web/app/components/evaluation/components/metric-section/builtin-metric-card.tsx
@@ -5,13 +5,13 @@ import type { NodeInfo } from '@/types/evaluation'
import { cn } from '@langgenius/dify-ui/cn'
import { useState } from 'react'
import { useTranslation } from 'react-i18next'
-import { Button } from '@/app/components/base/ui/button'
+import { Button } from '@langgenius/dify-ui/button'
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
-} from '@/app/components/base/ui/dropdown-menu'
+} from '@langgenius/dify-ui/dropdown-menu'
import { useEvaluationStore } from '../../store'
import { dedupeNodeInfoList, getMetricVisual, getNodeVisual, getToneClasses } from '../metric-selector/utils'
@@ -76,37 +76,37 @@ const BuiltinMetricCard = ({
{selectedNodeInfoList.length
? selectedNodeInfoList.map((nodeInfo) => {
- const nodeVisual = getNodeVisual(nodeInfo)
- const nodeToneClasses = getToneClasses(nodeVisual.tone)
+ const nodeVisual = getNodeVisual(nodeInfo)
+ const nodeToneClasses = getToneClasses(nodeVisual.tone)
- return (
-
-
-
-
-
{nodeInfo.title}
-
+ return (
+
+
+
- )
- })
+
{nodeInfo.title}
+
+
+ )
+ })
: (
-
{t('metrics.nodesAll')}
- )}
+
{t('metrics.nodesAll')}
+ )}
{shouldShowAddNode && (
diff --git a/web/app/components/evaluation/components/metric-section/custom-metric-card.tsx b/web/app/components/evaluation/components/metric-section/custom-metric-card.tsx
index d0c04bac04..d25fd32b94 100644
--- a/web/app/components/evaluation/components/metric-section/custom-metric-card.tsx
+++ b/web/app/components/evaluation/components/metric-section/custom-metric-card.tsx
@@ -4,7 +4,7 @@ import type { EvaluationMetric, EvaluationResourceProps } from '../../types'
import { cn } from '@langgenius/dify-ui/cn'
import { useTranslation } from 'react-i18next'
import Badge from '@/app/components/base/badge'
-import { Button } from '@/app/components/base/ui/button'
+import { Button } from '@langgenius/dify-ui/button'
import { isCustomMetricConfigured, useEvaluationStore } from '../../store'
import CustomMetricEditorCard from '../custom-metric-editor'
import { getToneClasses } from '../metric-selector/utils'
diff --git a/web/app/components/evaluation/components/metric-selector/index.tsx b/web/app/components/evaluation/components/metric-selector/index.tsx
index 0f91271a9f..2e1cc89ec8 100644
--- a/web/app/components/evaluation/components/metric-selector/index.tsx
+++ b/web/app/components/evaluation/components/metric-selector/index.tsx
@@ -6,7 +6,7 @@ import { cn } from '@langgenius/dify-ui/cn'
import { useState } from 'react'
import { useTranslation } from 'react-i18next'
import Input from '@/app/components/base/input'
-import { Button } from '@/app/components/base/ui/button'
+import { Button } from '@langgenius/dify-ui/button'
import {
Popover,
PopoverContent,
@@ -68,17 +68,17 @@ const MetricSelector = ({
render={(
triggerStyle === 'text'
? (
-
- )
+
+ )
: (
-
- )
+
+ )
)}
/>
diff --git a/web/app/components/evaluation/components/pipeline/pipeline-batch-actions.tsx b/web/app/components/evaluation/components/pipeline/pipeline-batch-actions.tsx
index 2cc673aa2c..867c67a587 100644
--- a/web/app/components/evaluation/components/pipeline/pipeline-batch-actions.tsx
+++ b/web/app/components/evaluation/components/pipeline/pipeline-batch-actions.tsx
@@ -3,7 +3,7 @@
import type { EvaluationResourceProps } from '../../types'
import type { InputField } from '../batch-test-panel/input-fields/input-fields-utils'
import { useTranslation } from 'react-i18next'
-import { Button } from '@/app/components/base/ui/button'
+import { Button } from '@langgenius/dify-ui/button'
import { getEvaluationMockConfig } from '../../mock'
import { isEvaluationRunnable, useEvaluationResource } from '../../store'
import UploadRunPopover from '../batch-test-panel/input-fields/upload-run-popover'
diff --git a/web/app/components/explore/app-card/index.tsx b/web/app/components/explore/app-card/index.tsx
index 00e21de7d6..869e2a6a18 100644
--- a/web/app/components/explore/app-card/index.tsx
+++ b/web/app/components/explore/app-card/index.tsx
@@ -2,12 +2,12 @@
import type { App } from '@/models/explore'
import type { TryAppSelection } from '@/types/try-app'
import { PlusIcon } from '@heroicons/react/20/solid'
+import { Button } from '@langgenius/dify-ui/button'
import { cn } from '@langgenius/dify-ui/cn'
import { RiInformation2Line } from '@remixicon/react'
import { useTranslation } from 'react-i18next'
import { trackEvent } from '@/app/components/base/amplitude'
import AppIcon from '@/app/components/base/app-icon'
-import { Button } from '@/app/components/base/ui/button'
import { useGlobalPublicStore } from '@/context/global-public-context'
import { AppModeEnum } from '@/types/app'
import { AppTypeIcon } from '../../app/type-selector'
diff --git a/web/app/components/explore/app-list/index.tsx b/web/app/components/explore/app-list/index.tsx
index b0e20c531a..d57283b999 100644
--- a/web/app/components/explore/app-list/index.tsx
+++ b/web/app/components/explore/app-list/index.tsx
@@ -3,6 +3,7 @@
import type { CreateAppModalProps } from '@/app/components/explore/create-app-modal'
import type { App } from '@/models/explore'
import type { TryAppSelection } from '@/types/try-app'
+import { Button } from '@langgenius/dify-ui/button'
import { cn } from '@langgenius/dify-ui/cn'
import { useDebounceFn } from 'ahooks'
import { useQueryState } from 'nuqs'
@@ -12,7 +13,6 @@ import { useTranslation } from 'react-i18next'
import DSLConfirmModal from '@/app/components/app/create-from-dsl-modal/dsl-confirm-modal'
import Input from '@/app/components/base/input'
import Loading from '@/app/components/base/loading'
-import { Button } from '@/app/components/base/ui/button'
import AppCard from '@/app/components/explore/app-card'
import Banner from '@/app/components/explore/banner/banner'
import Category from '@/app/components/explore/category'
diff --git a/web/app/components/explore/banner/__tests__/banner.spec.tsx b/web/app/components/explore/banner/__tests__/banner.spec.tsx
index 069aaf02dc..c95f2532e2 100644
--- a/web/app/components/explore/banner/__tests__/banner.spec.tsx
+++ b/web/app/components/explore/banner/__tests__/banner.spec.tsx
@@ -1,6 +1,6 @@
-import type * as React from 'react'
import type { Banner as BannerType } from '@/models/app'
import { cleanup, fireEvent, render, screen } from '@testing-library/react'
+import * as React from 'react'
import { act } from 'react'
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import Banner from '../banner'
@@ -8,6 +8,13 @@ import Banner from '../banner'
const mockUseGetBanners = vi.fn()
const mockUseSelector = vi.fn()
const mockTrackEvent = vi.fn()
+let mockSelectedIndex = 0
+const mockCarouselListeners = new Set<() => void>()
+
+const setMockSelectedIndex = (index: number) => {
+ mockSelectedIndex = index
+ mockCarouselListeners.forEach(listener => listener())
+}
vi.mock('@/service/use-explore', () => ({
useGetBanners: (...args: unknown[]) => mockUseGetBanners(...args),
@@ -54,13 +61,23 @@ vi.mock('@/app/components/base/carousel', () => ({
},
},
),
- useCarousel: () => ({
- api: {
- scrollTo: vi.fn(),
- slideNodes: () => [],
- },
- selectedIndex: 0,
- }),
+ useCarousel: () => {
+ const selectedIndex = React.useSyncExternalStore(
+ (listener) => {
+ mockCarouselListeners.add(listener)
+ return () => mockCarouselListeners.delete(listener)
+ },
+ () => mockSelectedIndex,
+ )
+
+ return {
+ api: {
+ scrollTo: vi.fn(),
+ slideNodes: () => [],
+ },
+ selectedIndex,
+ }
+ },
}))
vi.mock('../banner-item', () => ({
@@ -102,7 +119,10 @@ const createMockBanner = (id: string, status: string = 'enabled', title: string
describe('Banner', () => {
beforeEach(() => {
+ vi.clearAllMocks()
vi.useFakeTimers()
+ mockSelectedIndex = 0
+ mockCarouselListeners.clear()
mockUseSelector.mockImplementation(selector => selector({
userProfile: {
id: 'account-123',
@@ -112,7 +132,6 @@ describe('Banner', () => {
afterEach(() => {
cleanup()
- vi.clearAllMocks()
vi.useRealTimers()
})
@@ -257,7 +276,7 @@ describe('Banner', () => {
expect(screen.getByTestId('carousel')).toHaveClass('rounded-2xl')
})
- it('tracks enabled banner impressions with expected payload', () => {
+ it('tracks only the current banner impression and reports the next one after slide changes', () => {
mockUseGetBanners.mockReturnValue({
data: [
createMockBanner('1', 'enabled', 'Enabled Banner 1'),
@@ -270,7 +289,7 @@ describe('Banner', () => {
render()
- expect(mockTrackEvent).toHaveBeenCalledTimes(2)
+ expect(mockTrackEvent).toHaveBeenCalledTimes(1)
expect(mockTrackEvent).toHaveBeenNthCalledWith(1, 'explore_banner_impression', expect.objectContaining({
banner_id: '1',
title: 'Enabled Banner 1',
@@ -281,6 +300,12 @@ describe('Banner', () => {
account_id: 'account-123',
event_time: expect.any(Number),
}))
+
+ act(() => {
+ setMockSelectedIndex(1)
+ })
+
+ expect(mockTrackEvent).toHaveBeenCalledTimes(2)
expect(mockTrackEvent).toHaveBeenNthCalledWith(2, 'explore_banner_impression', expect.objectContaining({
banner_id: '3',
title: 'Enabled Banner 2',
diff --git a/web/app/components/explore/banner/banner.tsx b/web/app/components/explore/banner/banner.tsx
index a320bb1974..505902195b 100644
--- a/web/app/components/explore/banner/banner.tsx
+++ b/web/app/components/explore/banner/banner.tsx
@@ -1,8 +1,9 @@
import type { FC } from 'react'
+import type { Banner as BannerType } from '@/models/app'
import * as React from 'react'
import { useEffect, useMemo, useRef, useState } from 'react'
import { trackEvent } from '@/app/components/base/amplitude'
-import { Carousel } from '@/app/components/base/carousel'
+import { Carousel, useCarousel } from '@/app/components/base/carousel'
import { useSelector } from '@/context/app-context'
import { useLocale } from '@/context/i18n'
import { useGetBanners } from '@/service/use-explore'
@@ -22,6 +23,45 @@ const LoadingState: FC = () => (
)
+type BannerImpressionTrackerProps = {
+ banners: BannerType[]
+ accountId?: string
+ language: string
+ trackedBannerIdsRef: React.MutableRefObject
>
+}
+
+const BannerImpressionTracker: FC = ({
+ banners,
+ accountId,
+ language,
+ trackedBannerIdsRef,
+}) => {
+ const { selectedIndex } = useCarousel()
+
+ useEffect(() => {
+ if (!accountId)
+ return
+
+ const currentBanner = banners[selectedIndex]
+ if (!currentBanner || trackedBannerIdsRef.current.has(currentBanner.id))
+ return
+
+ trackEvent('explore_banner_impression', {
+ banner_id: currentBanner.id,
+ title: currentBanner.content.title,
+ sort: selectedIndex + 1,
+ link: currentBanner.link,
+ page: 'explore',
+ language,
+ account_id: accountId,
+ event_time: Date.now(),
+ })
+ trackedBannerIdsRef.current.add(currentBanner.id)
+ }, [accountId, banners, language, selectedIndex, trackedBannerIdsRef])
+
+ return null
+}
+
const Banner: FC = () => {
const locale = useLocale()
const { data: banners, isLoading, isError } = useGetBanners(locale)
@@ -60,28 +100,6 @@ const Banner: FC = () => {
}
}, [])
- useEffect(() => {
- if (!accountId)
- return
-
- enabledBanners.forEach((banner, index) => {
- if (trackedBannerIdsRef.current.has(banner.id))
- return
-
- trackEvent('explore_banner_impression', {
- banner_id: banner.id,
- title: banner.content.title,
- sort: index + 1,
- link: banner.link,
- page: 'explore',
- language: locale,
- account_id: accountId,
- event_time: Date.now(),
- })
- trackedBannerIdsRef.current.add(banner.id)
- })
- }, [accountId, enabledBanners, locale])
-
if (isLoading)
return
@@ -102,6 +120,12 @@ const Banner: FC = () => {
onMouseEnter={() => setIsHovered(true)}
onMouseLeave={() => setIsHovered(false)}
>
+
{enabledBanners.map((banner, index) => (
diff --git a/web/app/components/explore/create-app-modal/index.tsx b/web/app/components/explore/create-app-modal/index.tsx
index ebfb562b0c..ebe5a79a16 100644
--- a/web/app/components/explore/create-app-modal/index.tsx
+++ b/web/app/components/explore/create-app-modal/index.tsx
@@ -1,5 +1,8 @@
'use client'
import type { AppIconType } from '@/types/app'
+import { Button } from '@langgenius/dify-ui/button'
+import { Switch } from '@langgenius/dify-ui/switch'
+import { toast } from '@langgenius/dify-ui/toast'
import { RiCloseLine } from '@remixicon/react'
import { useDebounceFn, useKeyPress } from 'ahooks'
import { noop } from 'es-toolkit/function'
@@ -9,10 +12,7 @@ import { useTranslation } from 'react-i18next'
import AppIcon from '@/app/components/base/app-icon'
import Input from '@/app/components/base/input'
import Modal from '@/app/components/base/modal'
-import Switch from '@/app/components/base/switch'
import Textarea from '@/app/components/base/textarea'
-import { Button } from '@/app/components/base/ui/button'
-import { toast } from '@/app/components/base/ui/toast'
import AppsFull from '@/app/components/billing/apps-full-in-dialog'
import { useProviderContext } from '@/context/provider-context'
import { AppModeEnum } from '@/types/app'
diff --git a/web/app/components/explore/item-operation/__tests__/index.spec.tsx b/web/app/components/explore/item-operation/__tests__/index.spec.tsx
index d54c644ab0..268fe8ebea 100644
--- a/web/app/components/explore/item-operation/__tests__/index.spec.tsx
+++ b/web/app/components/explore/item-operation/__tests__/index.spec.tsx
@@ -2,7 +2,7 @@ import { fireEvent, render, screen, waitFor } from '@testing-library/react'
import * as React from 'react'
import ItemOperation from '../index'
-vi.mock('@/app/components/base/ui/dropdown-menu', () => {
+vi.mock('@langgenius/dify-ui/dropdown-menu', () => {
const DropdownMenuContext = React.createContext<{ isOpen: boolean, setOpen: (open: boolean) => void } | null>(null)
const useDropdownMenuContext = () => {
diff --git a/web/app/components/explore/item-operation/index.tsx b/web/app/components/explore/item-operation/index.tsx
index 72bd00fa6e..0d9d57841e 100644
--- a/web/app/components/explore/item-operation/index.tsx
+++ b/web/app/components/explore/item-operation/index.tsx
@@ -1,6 +1,12 @@
'use client'
import type { FC } from 'react'
import { cn } from '@langgenius/dify-ui/cn'
+import {
+ DropdownMenu,
+ DropdownMenuContent,
+ DropdownMenuItem,
+ DropdownMenuTrigger,
+} from '@langgenius/dify-ui/dropdown-menu'
import {
RiDeleteBinLine,
RiEditLine,
@@ -9,12 +15,6 @@ import { useBoolean } from 'ahooks'
import * as React from 'react'
import { useEffect, useState } from 'react'
import { useTranslation } from 'react-i18next'
-import {
- DropdownMenu,
- DropdownMenuContent,
- DropdownMenuItem,
- DropdownMenuTrigger,
-} from '@/app/components/base/ui/dropdown-menu'
import { Pin02 } from '../../base/icons/src/vender/line/general'
import s from './style.module.css'
diff --git a/web/app/components/explore/sidebar/__tests__/index.spec.tsx b/web/app/components/explore/sidebar/__tests__/index.spec.tsx
index 0ce98f45db..cce42d6322 100644
--- a/web/app/components/explore/sidebar/__tests__/index.spec.tsx
+++ b/web/app/components/explore/sidebar/__tests__/index.spec.tsx
@@ -47,8 +47,8 @@ vi.mock('@/service/use-explore', () => ({
}),
}))
-vi.mock('@/app/components/base/ui/toast', async (importOriginal) => {
- const actual = await importOriginal()
+vi.mock('@langgenius/dify-ui/toast', async (importOriginal) => {
+ const actual = await importOriginal()
return {
...actual,
toast: {
diff --git a/web/app/components/explore/sidebar/index.tsx b/web/app/components/explore/sidebar/index.tsx
index 85602a6bcc..c6128cb560 100644
--- a/web/app/components/explore/sidebar/index.tsx
+++ b/web/app/components/explore/sidebar/index.tsx
@@ -1,10 +1,4 @@
'use client'
-import { cn } from '@langgenius/dify-ui/cn'
-import { useBoolean } from 'ahooks'
-import * as React from 'react'
-import { useState } from 'react'
-import { useTranslation } from 'react-i18next'
-import Divider from '@/app/components/base/divider'
import {
AlertDialog,
AlertDialogActions,
@@ -13,9 +7,15 @@ import {
AlertDialogContent,
AlertDialogDescription,
AlertDialogTitle,
-} from '@/app/components/base/ui/alert-dialog'
-import { ScrollArea } from '@/app/components/base/ui/scroll-area'
-import { toast } from '@/app/components/base/ui/toast'
+} from '@langgenius/dify-ui/alert-dialog'
+import { cn } from '@langgenius/dify-ui/cn'
+import { ScrollArea } from '@langgenius/dify-ui/scroll-area'
+import { toast } from '@langgenius/dify-ui/toast'
+import { useBoolean } from 'ahooks'
+import * as React from 'react'
+import { useState } from 'react'
+import { useTranslation } from 'react-i18next'
+import Divider from '@/app/components/base/divider'
import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints'
import Link from '@/next/link'
import { useSelectedLayoutSegments } from '@/next/navigation'
diff --git a/web/app/components/explore/try-app/app-info/index.tsx b/web/app/components/explore/try-app/app-info/index.tsx
index 441fabca82..a2c8c26e9f 100644
--- a/web/app/components/explore/try-app/app-info/index.tsx
+++ b/web/app/components/explore/try-app/app-info/index.tsx
@@ -1,12 +1,12 @@
'use client'
import type { FC } from 'react'
import type { TryAppInfo } from '@/service/try-app'
+import { Button } from '@langgenius/dify-ui/button'
import { cn } from '@langgenius/dify-ui/cn'
import * as React from 'react'
import { useTranslation } from 'react-i18next'
import { AppTypeIcon } from '@/app/components/app/type-selector'
import AppIcon from '@/app/components/base/app-icon'
-import { Button } from '@/app/components/base/ui/button'
import useGetRequirements from './use-get-requirements'
type Props = {
diff --git a/web/app/components/explore/try-app/index.tsx b/web/app/components/explore/try-app/index.tsx
index 644047d06f..209bde9cb6 100644
--- a/web/app/components/explore/try-app/index.tsx
+++ b/web/app/components/explore/try-app/index.tsx
@@ -2,12 +2,12 @@
'use client'
import type { FC } from 'react'
import type { App as AppType } from '@/models/explore'
+import { Button } from '@langgenius/dify-ui/button'
import * as React from 'react'
import { useState } from 'react'
import AppUnavailable from '@/app/components/base/app-unavailable'
import Loading from '@/app/components/base/loading'
import Modal from '@/app/components/base/modal/index'
-import { Button } from '@/app/components/base/ui/button'
import { IS_CLOUD_EDITION } from '@/config'
import { useGlobalPublicStore } from '@/context/global-public-context'
import { useGetTryAppInfo } from '@/service/use-try-app'
diff --git a/web/app/components/goto-anything/__tests__/index.spec.tsx b/web/app/components/goto-anything/__tests__/index.spec.tsx
index b2050ef9fb..39249e0427 100644
--- a/web/app/components/goto-anything/__tests__/index.spec.tsx
+++ b/web/app/components/goto-anything/__tests__/index.spec.tsx
@@ -3,7 +3,7 @@ import type { ActionItem, SearchResult } from '../actions/types'
import { act, render, screen, waitFor } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import * as React from 'react'
-import GotoAnything from '../index'
+import { GotoAnything } from '../index'
type TestSearchResult = Omit & {
icon?: ReactNode
diff --git a/web/app/components/goto-anything/index.tsx b/web/app/components/goto-anything/index.tsx
index f3bf40f62a..af6f0a7a9b 100644
--- a/web/app/components/goto-anything/index.tsx
+++ b/web/app/components/goto-anything/index.tsx
@@ -1,10 +1,10 @@
'use client'
import type { FC, KeyboardEvent } from 'react'
+import { Dialog, DialogContent } from '@langgenius/dify-ui/dialog'
import { Command } from 'cmdk'
import { useCallback, useEffect, useMemo, useRef } from 'react'
import { useTranslation } from 'react-i18next'
-import { Dialog, DialogContent } from '@/app/components/base/ui/dialog'
import InstallFromMarketplace from '../plugins/install-plugin/install-from-marketplace'
import { SlashCommandProvider } from './actions/commands'
import { slashCommandRegistry } from './actions/commands/registry'
@@ -22,7 +22,7 @@ type Props = {
onHide?: () => void
}
-const GotoAnything: FC = ({
+const GotoAnythingDialog: FC = ({
onHide,
}) => {
const { t } = useTranslation()
@@ -234,15 +234,10 @@ const GotoAnything: FC = ({
)
}
-/**
- * GotoAnything component with context provider
- */
-const GotoAnythingWithContext: FC = (props) => {
+export const GotoAnything: FC = (props) => {
return (
-
+
)
}
-
-export default GotoAnythingWithContext
diff --git a/web/app/components/header/account-about/index.tsx b/web/app/components/header/account-about/index.tsx
index cdab2815dc..9375497566 100644
--- a/web/app/components/header/account-about/index.tsx
+++ b/web/app/components/header/account-about/index.tsx
@@ -1,11 +1,11 @@
'use client'
import type { LangGeniusVersionResponse } from '@/models/common'
+import { Button } from '@langgenius/dify-ui/button'
import { RiCloseLine } from '@remixicon/react'
import dayjs from 'dayjs'
import { useTranslation } from 'react-i18next'
import DifyLogo from '@/app/components/base/logo/dify-logo'
import Modal from '@/app/components/base/modal'
-import { Button } from '@/app/components/base/ui/button'
import { IS_CE_EDITION } from '@/config'
import { useGlobalPublicStore } from '@/context/global-public-context'
diff --git a/web/app/components/header/account-dropdown/__tests__/compliance.spec.tsx b/web/app/components/header/account-dropdown/__tests__/compliance.spec.tsx
index 15439b157f..b4a700e22d 100644
--- a/web/app/components/header/account-dropdown/__tests__/compliance.spec.tsx
+++ b/web/app/components/header/account-dropdown/__tests__/compliance.spec.tsx
@@ -1,8 +1,8 @@
import type { ModalContextState } from '@/context/modal-context'
+import { DropdownMenu, DropdownMenuContent, DropdownMenuTrigger } from '@langgenius/dify-ui/dropdown-menu'
+import { toast } from '@langgenius/dify-ui/toast'
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import { fireEvent, render, screen, waitFor } from '@testing-library/react'
-import { DropdownMenu, DropdownMenuContent, DropdownMenuTrigger } from '@/app/components/base/ui/dropdown-menu'
-import { toast } from '@/app/components/base/ui/toast'
import { Plan } from '@/app/components/billing/type'
import { ACCOUNT_SETTING_TAB } from '@/app/components/header/account-setting/constants'
import { useModalContext } from '@/context/modal-context'
diff --git a/web/app/components/header/account-dropdown/__tests__/support.spec.tsx b/web/app/components/header/account-dropdown/__tests__/support.spec.tsx
index a826038ad0..38be78abf5 100644
--- a/web/app/components/header/account-dropdown/__tests__/support.spec.tsx
+++ b/web/app/components/header/account-dropdown/__tests__/support.spec.tsx
@@ -1,7 +1,7 @@
import type { AppContextValue } from '@/context/app-context'
-import { fireEvent, render, screen } from '@testing-library/react'
+import { DropdownMenu, DropdownMenuContent, DropdownMenuTrigger } from '@langgenius/dify-ui/dropdown-menu'
-import { DropdownMenu, DropdownMenuContent, DropdownMenuTrigger } from '@/app/components/base/ui/dropdown-menu'
+import { fireEvent, render, screen } from '@testing-library/react'
import { Plan } from '@/app/components/billing/type'
import { useAppContext } from '@/context/app-context'
import { baseProviderContextValue, useProviderContext } from '@/context/provider-context'
diff --git a/web/app/components/header/account-dropdown/compliance.tsx b/web/app/components/header/account-dropdown/compliance.tsx
index 2f79c631fb..75d7200666 100644
--- a/web/app/components/header/account-dropdown/compliance.tsx
+++ b/web/app/components/header/account-dropdown/compliance.tsx
@@ -1,11 +1,11 @@
import type { ReactNode } from 'react'
+import { Button } from '@langgenius/dify-ui/button'
+import { DropdownMenuGroup, DropdownMenuItem, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger } from '@langgenius/dify-ui/dropdown-menu'
+import { toast } from '@langgenius/dify-ui/toast'
+import { Tooltip, TooltipContent, TooltipTrigger } from '@langgenius/dify-ui/tooltip'
import { useMutation } from '@tanstack/react-query'
import { useCallback } from 'react'
import { useTranslation } from 'react-i18next'
-import { Button } from '@/app/components/base/ui/button'
-import { DropdownMenuGroup, DropdownMenuItem, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger } from '@/app/components/base/ui/dropdown-menu'
-import { toast } from '@/app/components/base/ui/toast'
-import { Tooltip, TooltipContent, TooltipTrigger } from '@/app/components/base/ui/tooltip'
import { Plan } from '@/app/components/billing/type'
import { ACCOUNT_SETTING_TAB } from '@/app/components/header/account-setting/constants'
import { useModalContext } from '@/context/modal-context'
@@ -19,12 +19,13 @@ import SparklesSoft from '../../base/icons/src/public/common/SparklesSoft'
import PremiumBadge from '../../base/premium-badge'
import { MenuItemContent } from './menu-item-content'
-enum DocName {
- SOC2_Type_I = 'SOC2_Type_I',
- SOC2_Type_II = 'SOC2_Type_II',
- ISO_27001 = 'ISO_27001',
- GDPR = 'GDPR',
-}
+const DocName = {
+ SOC2_Type_I: 'SOC2_Type_I',
+ SOC2_Type_II: 'SOC2_Type_II',
+ ISO_27001: 'ISO_27001',
+ GDPR: 'GDPR',
+} as const
+type DocName = typeof DocName[keyof typeof DocName]
type ComplianceDocActionVisualProps = {
isCurrentPlanCanDownload: boolean
@@ -61,7 +62,6 @@ function ComplianceDocActionVisual({
return (
diff --git a/web/app/components/header/account-dropdown/index.tsx b/web/app/components/header/account-dropdown/index.tsx
index 8552852b81..27a601a4ab 100644
--- a/web/app/components/header/account-dropdown/index.tsx
+++ b/web/app/components/header/account-dropdown/index.tsx
@@ -1,14 +1,14 @@
'use client'
import type { MouseEventHandler, ReactNode } from 'react'
+import { Avatar } from '@langgenius/dify-ui/avatar'
import { cn } from '@langgenius/dify-ui/cn'
+import { DropdownMenu, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLinkItem, DropdownMenuSeparator, DropdownMenuTrigger } from '@langgenius/dify-ui/dropdown-menu'
import { useState } from 'react'
import { useTranslation } from 'react-i18next'
import { resetUser } from '@/app/components/base/amplitude/utils'
import PremiumBadge from '@/app/components/base/premium-badge'
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 { ACCOUNT_SETTING_TAB } from '@/app/components/header/account-setting/constants'
import { IS_CLOUD_EDITION } from '@/config'
import { useAppContext } from '@/context/app-context'
diff --git a/web/app/components/header/account-dropdown/support.tsx b/web/app/components/header/account-dropdown/support.tsx
index 97d87ac7e1..9a7023c415 100644
--- a/web/app/components/header/account-dropdown/support.tsx
+++ b/web/app/components/header/account-dropdown/support.tsx
@@ -1,5 +1,5 @@
+import { DropdownMenuGroup, DropdownMenuItem, DropdownMenuLinkItem, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger } from '@langgenius/dify-ui/dropdown-menu'
import { useTranslation } from 'react-i18next'
-import { DropdownMenuGroup, DropdownMenuItem, DropdownMenuLinkItem, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger } from '@/app/components/base/ui/dropdown-menu'
import { toggleZendeskWindow } from '@/app/components/base/zendesk/utils'
import { Plan } from '@/app/components/billing/type'
import { SUPPORT_EMAIL_ADDRESS, ZENDESK_WIDGET_KEY } from '@/config'
diff --git a/web/app/components/header/account-dropdown/workplace-selector/__tests__/index.spec.tsx b/web/app/components/header/account-dropdown/workplace-selector/__tests__/index.spec.tsx
index 7e4a969089..28b07d114b 100644
--- a/web/app/components/header/account-dropdown/workplace-selector/__tests__/index.spec.tsx
+++ b/web/app/components/header/account-dropdown/workplace-selector/__tests__/index.spec.tsx
@@ -33,7 +33,7 @@ vi.mock('@/service/common', () => ({
switchWorkspace: vi.fn(),
}))
-vi.mock('@/app/components/base/ui/toast', () => ({
+vi.mock('@langgenius/dify-ui/toast', () => ({
default: {
notify: (args: unknown) => toastMocks.mockNotify(args),
},
@@ -45,8 +45,8 @@ vi.mock('@/app/components/base/ui/toast', () => ({
},
}))
-vi.mock('@/app/components/base/ui/select', async (importOriginal) => {
- const actual = await importOriginal()
+vi.mock('@langgenius/dify-ui/select', async (importOriginal) => {
+ const actual = await importOriginal()
return {
...actual,
diff --git a/web/app/components/header/account-dropdown/workplace-selector/index.tsx b/web/app/components/header/account-dropdown/workplace-selector/index.tsx
index bb96137fb3..ae6f6729fd 100644
--- a/web/app/components/header/account-dropdown/workplace-selector/index.tsx
+++ b/web/app/components/header/account-dropdown/workplace-selector/index.tsx
@@ -1,5 +1,4 @@
import type { Plan } from '@/app/components/billing/type'
-import { useTranslation } from 'react-i18next'
import {
Select,
SelectContent,
@@ -8,8 +7,9 @@ import {
SelectItemText,
SelectLabel,
SelectTrigger,
-} from '@/app/components/base/ui/select'
-import { toast } from '@/app/components/base/ui/toast'
+} from '@langgenius/dify-ui/select'
+import { toast } from '@langgenius/dify-ui/toast'
+import { useTranslation } from 'react-i18next'
import PlanBadge from '@/app/components/header/plan-badge'
import { useWorkspacesContext } from '@/context/workspace-context'
import { switchWorkspace } from '@/service/common'
diff --git a/web/app/components/header/account-setting/__tests__/menu-dialog.dialog.spec.tsx b/web/app/components/header/account-setting/__tests__/menu-dialog.dialog.spec.tsx
index db8aec4ec1..3ed0b70968 100644
--- a/web/app/components/header/account-setting/__tests__/menu-dialog.dialog.spec.tsx
+++ b/web/app/components/header/account-setting/__tests__/menu-dialog.dialog.spec.tsx
@@ -10,7 +10,7 @@ type DialogProps = {
let latestOnOpenChange: DialogProps['onOpenChange']
-vi.mock('@/app/components/base/ui/dialog', () => ({
+vi.mock('@langgenius/dify-ui/dialog', () => ({
Dialog: ({ children, onOpenChange }: DialogProps) => {
latestOnOpenChange = onOpenChange
return {children}
diff --git a/web/app/components/header/account-setting/api-based-extension-page/__tests__/modal.spec.tsx b/web/app/components/header/account-setting/api-based-extension-page/__tests__/modal.spec.tsx
index 8bfc1b13df..e006a6be03 100644
--- a/web/app/components/header/account-setting/api-based-extension-page/__tests__/modal.spec.tsx
+++ b/web/app/components/header/account-setting/api-based-extension-page/__tests__/modal.spec.tsx
@@ -28,7 +28,7 @@ vi.mock('@/service/common', () => ({
updateApiBasedExtension: vi.fn(),
}))
-vi.mock('@/app/components/base/ui/toast', () => ({
+vi.mock('@langgenius/dify-ui/toast', () => ({
toast: mockToast,
}))
diff --git a/web/app/components/header/account-setting/api-based-extension-page/index.tsx b/web/app/components/header/account-setting/api-based-extension-page/index.tsx
index b5d07dcfe1..bc26f63838 100644
--- a/web/app/components/header/account-setting/api-based-extension-page/index.tsx
+++ b/web/app/components/header/account-setting/api-based-extension-page/index.tsx
@@ -1,8 +1,8 @@
+import { Button } from '@langgenius/dify-ui/button'
import {
RiAddLine,
} from '@remixicon/react'
import { useTranslation } from 'react-i18next'
-import { Button } from '@/app/components/base/ui/button'
import { useModalContext } from '@/context/modal-context'
import { useApiBasedExtensions } from '@/service/use-common'
import Empty from './empty'
diff --git a/web/app/components/header/account-setting/api-based-extension-page/item.tsx b/web/app/components/header/account-setting/api-based-extension-page/item.tsx
index 5ef349f5e0..cfda9a672f 100644
--- a/web/app/components/header/account-setting/api-based-extension-page/item.tsx
+++ b/web/app/components/header/account-setting/api-based-extension-page/item.tsx
@@ -1,11 +1,5 @@
import type { FC } from 'react'
import type { ApiBasedExtension } from '@/models/common'
-import {
- RiDeleteBinLine,
- RiEditLine,
-} from '@remixicon/react'
-import { useState } from 'react'
-import { useTranslation } from 'react-i18next'
import {
AlertDialog,
AlertDialogActions,
@@ -13,8 +7,14 @@ import {
AlertDialogConfirmButton,
AlertDialogContent,
AlertDialogTitle,
-} from '@/app/components/base/ui/alert-dialog'
-import { Button } from '@/app/components/base/ui/button'
+} from '@langgenius/dify-ui/alert-dialog'
+import { Button } from '@langgenius/dify-ui/button'
+import {
+ RiDeleteBinLine,
+ RiEditLine,
+} from '@remixicon/react'
+import { useState } from 'react'
+import { useTranslation } from 'react-i18next'
import { useModalContext } from '@/context/modal-context'
import { deleteApiBasedExtension } from '@/service/common'
diff --git a/web/app/components/header/account-setting/api-based-extension-page/modal.tsx b/web/app/components/header/account-setting/api-based-extension-page/modal.tsx
index bffd6da230..b7d2e09734 100644
--- a/web/app/components/header/account-setting/api-based-extension-page/modal.tsx
+++ b/web/app/components/header/account-setting/api-based-extension-page/modal.tsx
@@ -1,12 +1,12 @@
import type { FC } from 'react'
import type { ApiBasedExtension } from '@/models/common'
+import { Button } from '@langgenius/dify-ui/button'
+import { toast } from '@langgenius/dify-ui/toast'
import { noop } from 'es-toolkit/function'
import { useState } from 'react'
import { useTranslation } from 'react-i18next'
import { BookOpen01 } from '@/app/components/base/icons/src/vender/line/education'
import Modal from '@/app/components/base/modal'
-import { Button } from '@/app/components/base/ui/button'
-import { toast } from '@/app/components/base/ui/toast'
import { useDocLink } from '@/context/i18n'
import { addApiBasedExtension, updateApiBasedExtension } from '@/service/common'
diff --git a/web/app/components/header/account-setting/data-source-page-new/card.tsx b/web/app/components/header/account-setting/data-source-page-new/card.tsx
index 9e8a54eeb2..3d875ab8fa 100644
--- a/web/app/components/header/account-setting/data-source-page-new/card.tsx
+++ b/web/app/components/header/account-setting/data-source-page-new/card.tsx
@@ -2,12 +2,6 @@ import type {
DataSourceAuth,
DataSourceCredential,
} from './types'
-import {
- memo,
- useCallback,
- useRef,
-} from 'react'
-import { useTranslation } from 'react-i18next'
import {
AlertDialog,
AlertDialogActions,
@@ -15,7 +9,13 @@ import {
AlertDialogConfirmButton,
AlertDialogContent,
AlertDialogTitle,
-} from '@/app/components/base/ui/alert-dialog'
+} from '@langgenius/dify-ui/alert-dialog'
+import {
+ memo,
+ useCallback,
+ useRef,
+} from 'react'
+import { useTranslation } from 'react-i18next'
import {
ApiKeyModal,
usePluginAuthAction,
diff --git a/web/app/components/header/account-setting/data-source-page-new/configure.tsx b/web/app/components/header/account-setting/data-source-page-new/configure.tsx
index 1cf2c9d382..712fb91415 100644
--- a/web/app/components/header/account-setting/data-source-page-new/configure.tsx
+++ b/web/app/components/header/account-setting/data-source-page-new/configure.tsx
@@ -4,6 +4,7 @@ import type {
AddOAuthButtonProps,
PluginPayload,
} from '@/app/components/plugins/plugin-auth/types'
+import { Button } from '@langgenius/dify-ui/button'
import {
RiAddLine,
} from '@remixicon/react'
@@ -19,7 +20,6 @@ import {
PortalToFollowElemContent,
PortalToFollowElemTrigger,
} from '@/app/components/base/portal-to-follow-elem'
-import { Button } from '@/app/components/base/ui/button'
import {
AddApiKeyButton,
AddOAuthButton,
diff --git a/web/app/components/header/account-setting/data-source-page-new/item.tsx b/web/app/components/header/account-setting/data-source-page-new/item.tsx
index 658d685ab2..b00db1687f 100644
--- a/web/app/components/header/account-setting/data-source-page-new/item.tsx
+++ b/web/app/components/header/account-setting/data-source-page-new/item.tsx
@@ -1,13 +1,13 @@
import type {
DataSourceCredential,
} from './types'
+import { Button } from '@langgenius/dify-ui/button'
import {
memo,
useState,
} from 'react'
import { useTranslation } from 'react-i18next'
import Input from '@/app/components/base/input'
-import { Button } from '@/app/components/base/ui/button'
import Indicator from '@/app/components/header/indicator'
import Operator from './operator'
diff --git a/web/app/components/header/account-setting/data-source-page-new/operator.tsx b/web/app/components/header/account-setting/data-source-page-new/operator.tsx
index 437fa80139..bcd3acb6b2 100644
--- a/web/app/components/header/account-setting/data-source-page-new/operator.tsx
+++ b/web/app/components/header/account-setting/data-source-page-new/operator.tsx
@@ -1,6 +1,13 @@
import type {
DataSourceCredential,
} from './types'
+import {
+ DropdownMenu,
+ DropdownMenuContent,
+ DropdownMenuItem,
+ DropdownMenuSeparator,
+ DropdownMenuTrigger,
+} from '@langgenius/dify-ui/dropdown-menu'
import {
memo,
useCallback,
@@ -8,13 +15,6 @@ import {
} from 'react'
import { useTranslation } from 'react-i18next'
import ActionButton from '@/app/components/base/action-button'
-import {
- DropdownMenu,
- DropdownMenuContent,
- DropdownMenuItem,
- DropdownMenuSeparator,
- DropdownMenuTrigger,
-} from '@/app/components/base/ui/dropdown-menu'
import { CredentialTypeEnum } from '@/app/components/plugins/plugin-auth/types'
type OperatorProps = {
diff --git a/web/app/components/header/account-setting/index.tsx b/web/app/components/header/account-setting/index.tsx
index 3ec6dea315..f356bd71b2 100644
--- a/web/app/components/header/account-setting/index.tsx
+++ b/web/app/components/header/account-setting/index.tsx
@@ -1,11 +1,11 @@
'use client'
import type { AccountSettingTab } from '@/app/components/header/account-setting/constants'
+import { Button } from '@langgenius/dify-ui/button'
import { cn } from '@langgenius/dify-ui/cn'
+import { ScrollArea } from '@langgenius/dify-ui/scroll-area'
import { useCallback, useState } from 'react'
import { useTranslation } from 'react-i18next'
import SearchInput from '@/app/components/base/search-input'
-import { Button } from '@/app/components/base/ui/button'
-import { ScrollArea } from '@/app/components/base/ui/scroll-area'
import BillingPage from '@/app/components/billing/billing-page'
import CustomPage from '@/app/components/custom/custom-page'
import {
diff --git a/web/app/components/header/account-setting/language-page/__tests__/index.spec.tsx b/web/app/components/header/account-setting/language-page/__tests__/index.spec.tsx
index 1139e953a8..c026b42f22 100644
--- a/web/app/components/header/account-setting/language-page/__tests__/index.spec.tsx
+++ b/web/app/components/header/account-setting/language-page/__tests__/index.spec.tsx
@@ -1,6 +1,6 @@
import type { UserProfileResponse } from '@/models/common'
+import { ToastHost } from '@langgenius/dify-ui/toast'
import { act, fireEvent, render, screen, waitFor, within } from '@testing-library/react'
-import { ToastHost } from '@/app/components/base/ui/toast'
import { languages } from '@/i18n-config/language'
import { updateUserProfile } from '@/service/common'
import { timezones } from '@/utils/timezone'
diff --git a/web/app/components/header/account-setting/language-page/index.tsx b/web/app/components/header/account-setting/language-page/index.tsx
index e538337d62..effd468895 100644
--- a/web/app/components/header/account-setting/language-page/index.tsx
+++ b/web/app/components/header/account-setting/language-page/index.tsx
@@ -1,10 +1,10 @@
'use client'
import type { Item } from '@/app/components/base/select'
import type { Locale } from '@/i18n-config'
+import { toast } from '@langgenius/dify-ui/toast'
import { useState } from 'react'
import { useTranslation } from 'react-i18next'
import { SimpleSelect } from '@/app/components/base/select'
-import { toast } from '@/app/components/base/ui/toast'
import { useAppContext } from '@/context/app-context'
import { useLocale } from '@/context/i18n'
import { setLocaleOnClient } from '@/i18n-config'
diff --git a/web/app/components/header/account-setting/members-page/edit-workspace-modal/__tests__/dialog.spec.tsx b/web/app/components/header/account-setting/members-page/edit-workspace-modal/__tests__/dialog.spec.tsx
index 1f3f4efff7..e134f8f93c 100644
--- a/web/app/components/header/account-setting/members-page/edit-workspace-modal/__tests__/dialog.spec.tsx
+++ b/web/app/components/header/account-setting/members-page/edit-workspace-modal/__tests__/dialog.spec.tsx
@@ -11,7 +11,7 @@ type DialogProps = {
let latestOnOpenChange: DialogProps['onOpenChange']
-vi.mock('@/app/components/base/ui/dialog', () => ({
+vi.mock('@langgenius/dify-ui/dialog', () => ({
Dialog: ({ children, onOpenChange }: DialogProps) => {
latestOnOpenChange = onOpenChange
return {children}
diff --git a/web/app/components/header/account-setting/members-page/edit-workspace-modal/__tests__/index.spec.tsx b/web/app/components/header/account-setting/members-page/edit-workspace-modal/__tests__/index.spec.tsx
index d794a75ada..e2248a44c3 100644
--- a/web/app/components/header/account-setting/members-page/edit-workspace-modal/__tests__/index.spec.tsx
+++ b/web/app/components/header/account-setting/members-page/edit-workspace-modal/__tests__/index.spec.tsx
@@ -13,7 +13,7 @@ const toastMocks = vi.hoisted(() => ({
vi.mock('@/context/app-context')
vi.mock('@/service/common')
-vi.mock('@/app/components/base/ui/toast', () => ({
+vi.mock('@langgenius/dify-ui/toast', () => ({
default: {
notify: (args: unknown) => toastMocks.mockNotify(args),
},
diff --git a/web/app/components/header/account-setting/members-page/edit-workspace-modal/index.tsx b/web/app/components/header/account-setting/members-page/edit-workspace-modal/index.tsx
index b4dbe243cf..0f6a6fb8d6 100644
--- a/web/app/components/header/account-setting/members-page/edit-workspace-modal/index.tsx
+++ b/web/app/components/header/account-setting/members-page/edit-workspace-modal/index.tsx
@@ -1,11 +1,11 @@
'use client'
+import { Button } from '@langgenius/dify-ui/button'
import { cn } from '@langgenius/dify-ui/cn'
+import { Dialog, DialogCloseButton, DialogContent, DialogTitle } from '@langgenius/dify-ui/dialog'
+import { toast } from '@langgenius/dify-ui/toast'
import { useId, useMemo, useState } from 'react'
import { useTranslation } from 'react-i18next'
import Input from '@/app/components/base/input'
-import { Button } from '@/app/components/base/ui/button'
-import { Dialog, DialogCloseButton, DialogContent, DialogTitle } from '@/app/components/base/ui/dialog'
-import { toast } from '@/app/components/base/ui/toast'
import { useAppContext } from '@/context/app-context'
import { updateWorkspaceInfo } from '@/service/common'
diff --git a/web/app/components/header/account-setting/members-page/index.tsx b/web/app/components/header/account-setting/members-page/index.tsx
index 0a3ed87e8f..f671eed94a 100644
--- a/web/app/components/header/account-setting/members-page/index.tsx
+++ b/web/app/components/header/account-setting/members-page/index.tsx
@@ -1,9 +1,9 @@
'use client'
import type { InvitationResult } from '@/models/common'
+import { Avatar } from '@langgenius/dify-ui/avatar'
+import { Tooltip, TooltipContent, TooltipTrigger } from '@langgenius/dify-ui/tooltip'
import { useState } from 'react'
import { useTranslation } from 'react-i18next'
-import { Avatar } from '@/app/components/base/ui/avatar'
-import { Tooltip, TooltipContent, TooltipTrigger } from '@/app/components/base/ui/tooltip'
import { NUM_INFINITE } from '@/app/components/billing/config'
import { Plan } from '@/app/components/billing/type'
import UpgradeBtn from '@/app/components/billing/upgrade-btn'
diff --git a/web/app/components/header/account-setting/members-page/invite-button.tsx b/web/app/components/header/account-setting/members-page/invite-button.tsx
index c4e73b8feb..c703924112 100644
--- a/web/app/components/header/account-setting/members-page/invite-button.tsx
+++ b/web/app/components/header/account-setting/members-page/invite-button.tsx
@@ -1,7 +1,7 @@
+import { Button } from '@langgenius/dify-ui/button'
import { RiUserAddLine } from '@remixicon/react'
import { useTranslation } from 'react-i18next'
import Loading from '@/app/components/base/loading'
-import { Button } from '@/app/components/base/ui/button'
import { useAppContext } from '@/context/app-context'
import { useGlobalPublicStore } from '@/context/global-public-context'
import { useWorkspacePermissions } from '@/service/use-workspace'
diff --git a/web/app/components/header/account-setting/members-page/invite-modal/__tests__/index.spec.tsx b/web/app/components/header/account-setting/members-page/invite-modal/__tests__/index.spec.tsx
index 7de1fbeccb..9ed99614e4 100644
--- a/web/app/components/header/account-setting/members-page/invite-modal/__tests__/index.spec.tsx
+++ b/web/app/components/header/account-setting/members-page/invite-modal/__tests__/index.spec.tsx
@@ -1,8 +1,8 @@
import type { InvitationResponse } from '@/models/common'
+import { toast } from '@langgenius/dify-ui/toast'
import { fireEvent, render, screen, waitFor } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import { vi } from 'vitest'
-import { toast } from '@/app/components/base/ui/toast'
import { useProviderContextSelector } from '@/context/provider-context'
import { inviteMember } from '@/service/common'
import InviteModal from '../index'
@@ -18,7 +18,7 @@ vi.mock('@/context/provider-context', () => ({
})),
}))
vi.mock('@/service/common')
-vi.mock('@/app/components/base/ui/toast', () => ({
+vi.mock('@langgenius/dify-ui/toast', () => ({
toast: {
error: mockToastError,
},
diff --git a/web/app/components/header/account-setting/members-page/invite-modal/index.tsx b/web/app/components/header/account-setting/members-page/invite-modal/index.tsx
index e90455f2d4..426d0491a2 100644
--- a/web/app/components/header/account-setting/members-page/invite-modal/index.tsx
+++ b/web/app/components/header/account-setting/members-page/invite-modal/index.tsx
@@ -1,14 +1,14 @@
'use client'
import type { RoleKey } from './role-selector'
import type { InvitationResult } from '@/models/common'
+import { Button } from '@langgenius/dify-ui/button'
import { cn } from '@langgenius/dify-ui/cn'
+import { Dialog, DialogCloseButton, DialogContent, DialogTitle } from '@langgenius/dify-ui/dialog'
+import { toast } from '@langgenius/dify-ui/toast'
import { useBoolean } from 'ahooks'
import { useCallback, useEffect, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { ReactMultiEmail } from 'react-multi-email'
-import { Button } from '@/app/components/base/ui/button'
-import { Dialog, DialogCloseButton, DialogContent, DialogTitle } from '@/app/components/base/ui/dialog'
-import { toast } from '@/app/components/base/ui/toast'
import { emailRegex } from '@/config'
import { useLocale } from '@/context/i18n'
import { useProviderContextSelector } from '@/context/provider-context'
diff --git a/web/app/components/header/account-setting/members-page/invite-modal/role-selector.tsx b/web/app/components/header/account-setting/members-page/invite-modal/role-selector.tsx
index 68cec4fe71..9c44e8b93d 100644
--- a/web/app/components/header/account-setting/members-page/invite-modal/role-selector.tsx
+++ b/web/app/components/header/account-setting/members-page/invite-modal/role-selector.tsx
@@ -1,11 +1,11 @@
import { cn } from '@langgenius/dify-ui/cn'
-import * as React from 'react'
-import { useTranslation } from 'react-i18next'
import {
Popover,
PopoverContent,
PopoverTrigger,
-} from '@/app/components/base/ui/popover'
+} from '@langgenius/dify-ui/popover'
+import * as React from 'react'
+import { useTranslation } from 'react-i18next'
import { useProviderContext } from '@/context/provider-context'
const roleI18nKeyMap = {
diff --git a/web/app/components/header/account-setting/members-page/invited-modal/index.tsx b/web/app/components/header/account-setting/members-page/invited-modal/index.tsx
index d7d48759c0..52cc69f726 100644
--- a/web/app/components/header/account-setting/members-page/invited-modal/index.tsx
+++ b/web/app/components/header/account-setting/members-page/invited-modal/index.tsx
@@ -1,9 +1,9 @@
import type { InvitationResult } from '@/models/common'
+import { Button } from '@langgenius/dify-ui/button'
+import { Dialog, DialogCloseButton, DialogContent, DialogTitle } from '@langgenius/dify-ui/dialog'
+import { Tooltip, TooltipContent, TooltipTrigger } from '@langgenius/dify-ui/tooltip'
import { useMemo } from 'react'
import { useTranslation } from 'react-i18next'
-import { Button } from '@/app/components/base/ui/button'
-import { Dialog, DialogCloseButton, DialogContent, DialogTitle } from '@/app/components/base/ui/dialog'
-import { Tooltip, TooltipContent, TooltipTrigger } from '@/app/components/base/ui/tooltip'
import { IS_CE_EDITION } from '@/config'
import InvitationLink from './invitation-link'
diff --git a/web/app/components/header/account-setting/members-page/invited-modal/invitation-link.tsx b/web/app/components/header/account-setting/members-page/invited-modal/invitation-link.tsx
index 4297099382..778d9fc8c9 100644
--- a/web/app/components/header/account-setting/members-page/invited-modal/invitation-link.tsx
+++ b/web/app/components/header/account-setting/members-page/invited-modal/invitation-link.tsx
@@ -1,10 +1,10 @@
'use client'
import type { SuccessInvitationResult } from '.'
+import { Tooltip, TooltipContent, TooltipTrigger } from '@langgenius/dify-ui/tooltip'
import copy from 'copy-to-clipboard'
import { t } from 'i18next'
import * as React from 'react'
import { useCallback, useEffect, useState } from 'react'
-import { Tooltip, TooltipContent, TooltipTrigger } from '@/app/components/base/ui/tooltip'
import s from './index.module.css'
type IInvitationLinkProps = {
diff --git a/web/app/components/header/account-setting/members-page/operation/index.tsx b/web/app/components/header/account-setting/members-page/operation/index.tsx
index dab0c84e5b..b67fb26bcd 100644
--- a/web/app/components/header/account-setting/members-page/operation/index.tsx
+++ b/web/app/components/header/account-setting/members-page/operation/index.tsx
@@ -1,16 +1,16 @@
'use client'
import type { Member } from '@/models/common'
import { cn } from '@langgenius/dify-ui/cn'
-import { memo, useMemo, useState } from 'react'
-import { useTranslation } from 'react-i18next'
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuSeparator,
DropdownMenuTrigger,
-} from '@/app/components/base/ui/dropdown-menu'
-import { toast } from '@/app/components/base/ui/toast'
+} from '@langgenius/dify-ui/dropdown-menu'
+import { toast } from '@langgenius/dify-ui/toast'
+import { memo, useMemo, useState } from 'react'
+import { useTranslation } from 'react-i18next'
import { useProviderContext } from '@/context/provider-context'
import { deleteMemberOrCancelInvitation, updateMemberRole } from '@/service/common'
diff --git a/web/app/components/header/account-setting/members-page/transfer-ownership-modal/__tests__/index.spec.tsx b/web/app/components/header/account-setting/members-page/transfer-ownership-modal/__tests__/index.spec.tsx
index 07a728d609..9e05a93c7a 100644
--- a/web/app/components/header/account-setting/members-page/transfer-ownership-modal/__tests__/index.spec.tsx
+++ b/web/app/components/header/account-setting/members-page/transfer-ownership-modal/__tests__/index.spec.tsx
@@ -15,7 +15,7 @@ const toastMocks = vi.hoisted(() => ({
vi.mock('@/context/app-context')
vi.mock('@/service/common')
vi.mock('@/service/use-common')
-vi.mock('@/app/components/base/ui/toast', () => ({
+vi.mock('@langgenius/dify-ui/toast', () => ({
default: {
notify: (args: unknown) => toastMocks.mockNotify(args),
},
diff --git a/web/app/components/header/account-setting/members-page/transfer-ownership-modal/index.tsx b/web/app/components/header/account-setting/members-page/transfer-ownership-modal/index.tsx
index 8554db341f..85a3ac3b22 100644
--- a/web/app/components/header/account-setting/members-page/transfer-ownership-modal/index.tsx
+++ b/web/app/components/header/account-setting/members-page/transfer-ownership-modal/index.tsx
@@ -1,11 +1,11 @@
+import { Button } from '@langgenius/dify-ui/button'
+import { toast } from '@langgenius/dify-ui/toast'
import { noop } from 'es-toolkit/function'
import * as React from 'react'
import { useCallback, useState } from 'react'
import { Trans, useTranslation } from 'react-i18next'
import Input from '@/app/components/base/input'
import Modal from '@/app/components/base/modal'
-import { Button } from '@/app/components/base/ui/button'
-import { toast } from '@/app/components/base/ui/toast'
import { useAppContext } from '@/context/app-context'
import { ownershipTransfer, sendOwnerEmail, verifyOwnerEmail } from '@/service/common'
import MemberSelector from './member-selector'
diff --git a/web/app/components/header/account-setting/members-page/transfer-ownership-modal/member-selector.tsx b/web/app/components/header/account-setting/members-page/transfer-ownership-modal/member-selector.tsx
index 713104fcc3..5875b4fb6a 100644
--- a/web/app/components/header/account-setting/members-page/transfer-ownership-modal/member-selector.tsx
+++ b/web/app/components/header/account-setting/members-page/transfer-ownership-modal/member-selector.tsx
@@ -1,12 +1,12 @@
'use client'
import type { FC } from 'react'
+import { Avatar } from '@langgenius/dify-ui/avatar'
import { cn } from '@langgenius/dify-ui/cn'
import * as React from 'react'
import { useMemo, useState } from 'react'
import { useTranslation } from 'react-i18next'
import Input from '@/app/components/base/input'
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'
type Props = {
diff --git a/web/app/components/header/account-setting/menu-dialog.tsx b/web/app/components/header/account-setting/menu-dialog.tsx
index f1a0307b0d..9bd2df5fa4 100644
--- a/web/app/components/header/account-setting/menu-dialog.tsx
+++ b/web/app/components/header/account-setting/menu-dialog.tsx
@@ -1,7 +1,7 @@
import type { ReactNode } from 'react'
import { cn } from '@langgenius/dify-ui/cn'
+import { Dialog, DialogContent } from '@langgenius/dify-ui/dialog'
import { useCallback } from 'react'
-import { Dialog, DialogContent } from '@/app/components/base/ui/dialog'
type DialogProps = {
className?: string
diff --git a/web/app/components/header/account-setting/model-provider-page/model-auth/add-custom-model.tsx b/web/app/components/header/account-setting/model-provider-page/model-auth/add-custom-model.tsx
index 50ea37e45e..a1e181e97e 100644
--- a/web/app/components/header/account-setting/model-provider-page/model-auth/add-custom-model.tsx
+++ b/web/app/components/header/account-setting/model-provider-page/model-auth/add-custom-model.tsx
@@ -3,6 +3,9 @@ import type {
CustomConfigurationModelFixedFields,
ModelProvider,
} from '@/app/components/header/account-setting/model-provider-page/declarations'
+import {
+ Button,
+} from '@langgenius/dify-ui/button'
import { cn } from '@langgenius/dify-ui/cn'
import {
RiAddCircleFill,
@@ -20,9 +23,6 @@ import {
PortalToFollowElemTrigger,
} from '@/app/components/base/portal-to-follow-elem'
import Tooltip from '@/app/components/base/tooltip'
-import {
- Button,
-} from '@/app/components/base/ui/button'
import { ModelModalModeEnum } from '@/app/components/header/account-setting/model-provider-page/declarations'
import ModelIcon from '../model-icon'
import { useAuth } from './hooks/use-auth'
diff --git a/web/app/components/header/account-setting/model-provider-page/model-auth/authorized/credential-item.tsx b/web/app/components/header/account-setting/model-provider-page/model-auth/authorized/credential-item.tsx
index 94b3ab2bc8..714c492137 100644
--- a/web/app/components/header/account-setting/model-provider-page/model-auth/authorized/credential-item.tsx
+++ b/web/app/components/header/account-setting/model-provider-page/model-auth/authorized/credential-item.tsx
@@ -1,5 +1,6 @@
import type { Credential } from '../../declarations'
import { cn } from '@langgenius/dify-ui/cn'
+import { Tooltip, TooltipContent, TooltipTrigger } from '@langgenius/dify-ui/tooltip'
import {
memo,
useMemo,
@@ -7,7 +8,6 @@ import {
import { useTranslation } from 'react-i18next'
import ActionButton from '@/app/components/base/action-button'
import Badge from '@/app/components/base/badge'
-import { Tooltip, TooltipContent, TooltipTrigger } from '@/app/components/base/ui/tooltip'
import Indicator from '@/app/components/header/indicator'
type CredentialItemProps = {
diff --git a/web/app/components/header/account-setting/model-provider-page/model-auth/authorized/index.tsx b/web/app/components/header/account-setting/model-provider-page/model-auth/authorized/index.tsx
index 9c0d6577ef..ca0e0d9c73 100644
--- a/web/app/components/header/account-setting/model-provider-page/model-auth/authorized/index.tsx
+++ b/web/app/components/header/account-setting/model-provider-page/model-auth/authorized/index.tsx
@@ -9,6 +9,15 @@ import type {
import type {
PortalToFollowElemOptions,
} from '@/app/components/base/portal-to-follow-elem'
+import {
+ AlertDialog,
+ AlertDialogActions,
+ AlertDialogCancelButton,
+ AlertDialogConfirmButton,
+ AlertDialogContent,
+ AlertDialogTitle,
+} from '@langgenius/dify-ui/alert-dialog'
+import { Button } from '@langgenius/dify-ui/button'
import { cn } from '@langgenius/dify-ui/cn'
import {
RiAddLine,
@@ -25,15 +34,6 @@ import {
PortalToFollowElemContent,
PortalToFollowElemTrigger,
} from '@/app/components/base/portal-to-follow-elem'
-import {
- AlertDialog,
- AlertDialogActions,
- AlertDialogCancelButton,
- AlertDialogConfirmButton,
- AlertDialogContent,
- AlertDialogTitle,
-} from '@/app/components/base/ui/alert-dialog'
-import { Button } from '@/app/components/base/ui/button'
import { useAuth } from '../hooks'
import AuthorizedItem from './authorized-item'
diff --git a/web/app/components/header/account-setting/model-provider-page/model-auth/config-model.tsx b/web/app/components/header/account-setting/model-provider-page/model-auth/config-model.tsx
index 42f4b68026..b570a5335e 100644
--- a/web/app/components/header/account-setting/model-provider-page/model-auth/config-model.tsx
+++ b/web/app/components/header/account-setting/model-provider-page/model-auth/config-model.tsx
@@ -1,3 +1,4 @@
+import { Button } from '@langgenius/dify-ui/button'
import { cn } from '@langgenius/dify-ui/cn'
import {
RiEqualizer2Line,
@@ -5,7 +6,6 @@ import {
} from '@remixicon/react'
import { memo } from 'react'
import { useTranslation } from 'react-i18next'
-import { Button } from '@/app/components/base/ui/button'
import Indicator from '@/app/components/header/indicator'
type ConfigModelProps = {
diff --git a/web/app/components/header/account-setting/model-provider-page/model-auth/config-provider.tsx b/web/app/components/header/account-setting/model-provider-page/model-auth/config-provider.tsx
index e64c5aedc0..4a268168ba 100644
--- a/web/app/components/header/account-setting/model-provider-page/model-auth/config-provider.tsx
+++ b/web/app/components/header/account-setting/model-provider-page/model-auth/config-provider.tsx
@@ -2,6 +2,9 @@ import type {
CustomConfigurationModelFixedFields,
ModelProvider,
} from '@/app/components/header/account-setting/model-provider-page/declarations'
+import {
+ Button,
+} from '@langgenius/dify-ui/button'
import {
RiEqualizer2Line,
} from '@remixicon/react'
@@ -11,9 +14,6 @@ import {
} from 'react'
import { useTranslation } from 'react-i18next'
import Tooltip from '@/app/components/base/tooltip'
-import {
- Button,
-} from '@/app/components/base/ui/button'
import { ConfigurationMethodEnum } from '@/app/components/header/account-setting/model-provider-page/declarations'
import Authorized from './authorized'
import { useCredentialStatus } from './hooks'
diff --git a/web/app/components/header/account-setting/model-provider-page/model-auth/hooks/__tests__/use-auth.spec.tsx b/web/app/components/header/account-setting/model-provider-page/model-auth/hooks/__tests__/use-auth.spec.tsx
index be9fb2c486..bb682cf8a4 100644
--- a/web/app/components/header/account-setting/model-provider-page/model-auth/hooks/__tests__/use-auth.spec.tsx
+++ b/web/app/components/header/account-setting/model-provider-page/model-auth/hooks/__tests__/use-auth.spec.tsx
@@ -21,8 +21,8 @@ const mockAddModelCredential = vi.fn()
const mockEditProviderCredential = vi.fn()
const mockEditModelCredential = vi.fn()
-vi.mock('@/app/components/base/ui/toast', async (importOriginal) => {
- const actual = await importOriginal()
+vi.mock('@langgenius/dify-ui/toast', async (importOriginal) => {
+ const actual = await importOriginal()
return {
...actual,
default: {
diff --git a/web/app/components/header/account-setting/model-provider-page/model-auth/hooks/use-auth.ts b/web/app/components/header/account-setting/model-provider-page/model-auth/hooks/use-auth.ts
index 72587e3da6..1cdb28523e 100644
--- a/web/app/components/header/account-setting/model-provider-page/model-auth/hooks/use-auth.ts
+++ b/web/app/components/header/account-setting/model-provider-page/model-auth/hooks/use-auth.ts
@@ -1,7 +1,7 @@
import type { ConfigurationMethodEnum, Credential, CustomConfigurationModelFixedFields, CustomModel, ModelModalModeEnum, ModelProvider } from '../../declarations'
+import { toast } from '@langgenius/dify-ui/toast'
import { useCallback, useRef, useState } from 'react'
import { useTranslation } from 'react-i18next'
-import { toast } from '@/app/components/base/ui/toast'
import { useModelModalHandler, useRefreshModel } from '@/app/components/header/account-setting/model-provider-page/hooks'
import { useDeleteModel } from '@/service/use-models'
import { useAuthService } from './use-auth-service'
diff --git a/web/app/components/header/account-setting/model-provider-page/model-auth/manage-custom-model-credentials.tsx b/web/app/components/header/account-setting/model-provider-page/model-auth/manage-custom-model-credentials.tsx
index 6737a6ffff..0979d4a4b7 100644
--- a/web/app/components/header/account-setting/model-provider-page/model-auth/manage-custom-model-credentials.tsx
+++ b/web/app/components/header/account-setting/model-provider-page/model-auth/manage-custom-model-credentials.tsx
@@ -2,15 +2,15 @@ import type {
CustomConfigurationModelFixedFields,
ModelProvider,
} from '@/app/components/header/account-setting/model-provider-page/declarations'
+import {
+ Button,
+} from '@langgenius/dify-ui/button'
import { cn } from '@langgenius/dify-ui/cn'
import {
memo,
useCallback,
} from 'react'
import { useTranslation } from 'react-i18next'
-import {
- Button,
-} from '@/app/components/base/ui/button'
import {
ConfigurationMethodEnum,
ModelModalModeEnum,
diff --git a/web/app/components/header/account-setting/model-provider-page/model-auth/switch-credential-in-load-balancing.tsx b/web/app/components/header/account-setting/model-provider-page/model-auth/switch-credential-in-load-balancing.tsx
index af374b8588..58ffc180dd 100644
--- a/web/app/components/header/account-setting/model-provider-page/model-auth/switch-credential-in-load-balancing.tsx
+++ b/web/app/components/header/account-setting/model-provider-page/model-auth/switch-credential-in-load-balancing.tsx
@@ -4,6 +4,7 @@ import type {
CustomModel,
ModelProvider,
} from '../declarations'
+import { Button } from '@langgenius/dify-ui/button'
import { cn } from '@langgenius/dify-ui/cn'
import { RiArrowDownSLine } from '@remixicon/react'
import {
@@ -13,7 +14,6 @@ import {
import { useTranslation } from 'react-i18next'
import Badge from '@/app/components/base/badge'
import Tooltip from '@/app/components/base/tooltip'
-import { Button } from '@/app/components/base/ui/button'
import { ConfigurationMethodEnum, ModelModalModeEnum } from '@/app/components/header/account-setting/model-provider-page/declarations'
import Indicator from '@/app/components/header/indicator'
import Authorized from './authorized'
diff --git a/web/app/components/header/account-setting/model-provider-page/model-modal/__tests__/dialog.spec.tsx b/web/app/components/header/account-setting/model-provider-page/model-modal/__tests__/dialog.spec.tsx
index 21f8d554c4..e2c934bb7c 100644
--- a/web/app/components/header/account-setting/model-provider-page/model-modal/__tests__/dialog.spec.tsx
+++ b/web/app/components/header/account-setting/model-provider-page/model-modal/__tests__/dialog.spec.tsx
@@ -31,7 +31,7 @@ vi.mock('../../model-auth', () => ({
CredentialSelector: ({ credentials }: { credentials: Credential[] }) => {`credentials:${credentials.length}`}
,
}))
-vi.mock('@/app/components/base/ui/dialog', () => ({
+vi.mock('@langgenius/dify-ui/dialog', () => ({
Dialog: ({ children, onOpenChange }: DialogProps) => {
latestDialogOnOpenChange = onOpenChange
return {children}
@@ -40,7 +40,7 @@ vi.mock('@/app/components/base/ui/dialog', () => ({
DialogCloseButton: () => ,
}))
-vi.mock('@/app/components/base/ui/alert-dialog', () => ({
+vi.mock('@langgenius/dify-ui/alert-dialog', () => ({
AlertDialog: ({ children, onOpenChange }: AlertDialogProps) => {
latestAlertDialogOnOpenChange = onOpenChange
return {children}
diff --git a/web/app/components/header/account-setting/model-provider-page/model-modal/index.tsx b/web/app/components/header/account-setting/model-provider-page/model-modal/index.tsx
index 995da8bb98..a8f004f3a2 100644
--- a/web/app/components/header/account-setting/model-provider-page/model-modal/index.tsx
+++ b/web/app/components/header/account-setting/model-provider-page/model-modal/index.tsx
@@ -9,6 +9,20 @@ import type {
FormRefObject,
FormSchema,
} from '@/app/components/base/form/types'
+import {
+ AlertDialog,
+ AlertDialogActions,
+ AlertDialogCancelButton,
+ AlertDialogConfirmButton,
+ AlertDialogContent,
+ AlertDialogTitle,
+} from '@langgenius/dify-ui/alert-dialog'
+import { Button } from '@langgenius/dify-ui/button'
+import {
+ Dialog,
+ DialogCloseButton,
+ DialogContent,
+} from '@langgenius/dify-ui/dialog'
import {
memo,
useCallback,
@@ -22,20 +36,6 @@ import AuthForm from '@/app/components/base/form/form-scenarios/auth'
import { LinkExternal02 } from '@/app/components/base/icons/src/vender/line/general'
import { Lock01 } from '@/app/components/base/icons/src/vender/solid/security'
import Loading from '@/app/components/base/loading'
-import {
- AlertDialog,
- AlertDialogActions,
- AlertDialogCancelButton,
- AlertDialogConfirmButton,
- AlertDialogContent,
- AlertDialogTitle,
-} from '@/app/components/base/ui/alert-dialog'
-import { Button } from '@/app/components/base/ui/button'
-import {
- Dialog,
- DialogCloseButton,
- DialogContent,
-} from '@/app/components/base/ui/dialog'
import {
useAuth,
useCredentialData,
diff --git a/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/__tests__/parameter-item.select.spec.tsx b/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/__tests__/parameter-item.select.spec.tsx
index ae4a68bad7..c221071b82 100644
--- a/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/__tests__/parameter-item.select.spec.tsx
+++ b/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/__tests__/parameter-item.select.spec.tsx
@@ -6,8 +6,8 @@ vi.mock('../../hooks', () => ({
useLanguage: () => 'en_US',
}))
-vi.mock('@/app/components/base/ui/select', async (importOriginal) => {
- const actual = await importOriginal()
+vi.mock('@langgenius/dify-ui/select', async (importOriginal) => {
+ const actual = await importOriginal()
return {
...actual,
diff --git a/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/__tests__/parameter-item.spec.tsx b/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/__tests__/parameter-item.spec.tsx
index 2e928c36cc..a57884df1d 100644
--- a/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/__tests__/parameter-item.spec.tsx
+++ b/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/__tests__/parameter-item.spec.tsx
@@ -11,7 +11,7 @@ vi.mock('../../hooks', () => ({
useLanguage: () => 'en_US',
}))
-vi.mock('@/app/components/base/ui/slider', () => ({
+vi.mock('@langgenius/dify-ui/slider', () => ({
Slider: ({ onValueChange }: { onValueChange: (v: number) => void }) => (
),
diff --git a/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/configuration-button.tsx b/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/configuration-button.tsx
index 7545cf6a22..598db04ee9 100644
--- a/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/configuration-button.tsx
+++ b/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/configuration-button.tsx
@@ -1,5 +1,5 @@
+import { Button } from '@langgenius/dify-ui/button'
import { useTranslation } from 'react-i18next'
-import { Button } from '@/app/components/base/ui/button'
import { ConfigurationMethodEnum } from '../declarations'
type ConfigurationButtonProps = {
diff --git a/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/index.tsx b/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/index.tsx
index 38b343bc19..6f37775052 100644
--- a/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/index.tsx
+++ b/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/index.tsx
@@ -14,16 +14,16 @@ import type {
NodeOutPutVar,
} from '@/app/components/workflow/types'
import { cn } from '@langgenius/dify-ui/cn'
-import { useMemo, useRef, useState } from 'react'
-import { useTranslation } from 'react-i18next'
-import { ArrowNarrowLeft } from '@/app/components/base/icons/src/vender/line/arrows'
-import Loading from '@/app/components/base/loading'
import {
Popover,
PopoverClose,
PopoverContent,
PopoverTrigger,
-} from '@/app/components/base/ui/popover'
+} from '@langgenius/dify-ui/popover'
+import { useMemo, useRef, useState } from 'react'
+import { useTranslation } from 'react-i18next'
+import { ArrowNarrowLeft } from '@/app/components/base/icons/src/vender/line/arrows'
+import Loading from '@/app/components/base/loading'
import { PROVIDER_WITH_PRESET_TONE, STOP_PARAMETER_RULE, TONE_LIST } from '@/config'
import { useModelParameterRules } from '@/service/use-common'
import {
diff --git a/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/parameter-item.tsx b/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/parameter-item.tsx
index 4cda97031f..f7e1962fd7 100644
--- a/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/parameter-item.tsx
+++ b/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/parameter-item.tsx
@@ -4,15 +4,15 @@ import type {
NodeOutPutVar,
} from '@/app/components/workflow/types'
import { cn } from '@langgenius/dify-ui/cn'
+import { Select, SelectContent, SelectItem, SelectItemIndicator, SelectItemText, SelectTrigger, SelectValue } from '@langgenius/dify-ui/select'
+import { Slider } from '@langgenius/dify-ui/slider'
+import { Switch } from '@langgenius/dify-ui/switch'
+import { Tooltip, TooltipContent, TooltipTrigger } from '@langgenius/dify-ui/tooltip'
import { useEffect, useMemo, useRef, useState } from 'react'
import { useTranslation } from 'react-i18next'
import PromptEditor from '@/app/components/base/prompt-editor'
import Radio from '@/app/components/base/radio'
-import Switch from '@/app/components/base/switch'
import TagInput from '@/app/components/base/tag-input'
-import { Select, SelectContent, SelectItem, SelectItemIndicator, SelectItemText, SelectTrigger, SelectValue } from '@/app/components/base/ui/select'
-import { Slider } from '@/app/components/base/ui/slider'
-import { Tooltip, TooltipContent, TooltipTrigger } from '@/app/components/base/ui/tooltip'
import { BlockEnum } from '@/app/components/workflow/types'
import { useLanguage } from '../hooks'
import { isNullOrUndefined } from '../utils'
diff --git a/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/presets-parameter.tsx b/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/presets-parameter.tsx
index c2138c1c6f..cb537ab18f 100644
--- a/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/presets-parameter.tsx
+++ b/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/presets-parameter.tsx
@@ -1,15 +1,15 @@
import type { ReactNode } from 'react'
-import { useTranslation } from 'react-i18next'
-import { Brush01 } from '@/app/components/base/icons/src/vender/solid/editor'
-import { Scales02 } from '@/app/components/base/icons/src/vender/solid/FinanceAndECommerce'
-import { Target04 } from '@/app/components/base/icons/src/vender/solid/general'
-import { Button } from '@/app/components/base/ui/button'
+import { Button } from '@langgenius/dify-ui/button'
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
-} from '@/app/components/base/ui/dropdown-menu'
+} from '@langgenius/dify-ui/dropdown-menu'
+import { useTranslation } from 'react-i18next'
+import { Brush01 } from '@/app/components/base/icons/src/vender/solid/editor'
+import { Scales02 } from '@/app/components/base/icons/src/vender/solid/FinanceAndECommerce'
+import { Target04 } from '@/app/components/base/icons/src/vender/solid/general'
import { TONE_LIST } from '@/config'
const toneI18nKeyMap = {
diff --git a/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/trigger.tsx b/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/trigger.tsx
index 06823d1a74..d9cb29abbf 100644
--- a/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/trigger.tsx
+++ b/web/app/components/header/account-setting/model-provider-page/model-parameter-modal/trigger.tsx
@@ -5,8 +5,8 @@ import type {
ModelProvider,
} from '../declarations'
import { cn } from '@langgenius/dify-ui/cn'
+import { Tooltip, TooltipContent, TooltipTrigger } from '@langgenius/dify-ui/tooltip'
import { useTranslation } from 'react-i18next'
-import { Tooltip, TooltipContent, TooltipTrigger } from '@/app/components/base/ui/tooltip'
import { useProviderContext } from '@/context/provider-context'
import ModelIcon from '../model-icon'
import ModelName from '../model-name'
diff --git a/web/app/components/header/account-setting/model-provider-page/model-selector/__tests__/popover.spec.tsx b/web/app/components/header/account-setting/model-provider-page/model-selector/__tests__/popover.spec.tsx
index e92bab1db5..d7501672f4 100644
--- a/web/app/components/header/account-setting/model-provider-page/model-selector/__tests__/popover.spec.tsx
+++ b/web/app/components/header/account-setting/model-provider-page/model-selector/__tests__/popover.spec.tsx
@@ -16,7 +16,7 @@ vi.mock('../../hooks', () => ({
}),
}))
-vi.mock('@/app/components/base/ui/popover', () => ({
+vi.mock('@langgenius/dify-ui/popover', () => ({
Popover: ({ children, onOpenChange }: PopoverProps) => {
latestOnOpenChange = onOpenChange
return {children}
diff --git a/web/app/components/header/account-setting/model-provider-page/model-selector/__tests__/popup-item.spec.tsx b/web/app/components/header/account-setting/model-provider-page/model-selector/__tests__/popup-item.spec.tsx
index 6728791120..341a9c6abc 100644
--- a/web/app/components/header/account-setting/model-provider-page/model-selector/__tests__/popup-item.spec.tsx
+++ b/web/app/components/header/account-setting/model-provider-page/model-selector/__tests__/popup-item.spec.tsx
@@ -44,7 +44,7 @@ vi.mock('@/app/components/base/tooltip', () => ({
default: ({ children }: { children: React.ReactNode }) => {children}
,
}))
-vi.mock('@/app/components/base/ui/popover', () => ({
+vi.mock('@langgenius/dify-ui/popover', () => ({
Popover: ({ children }: { children: React.ReactNode }) => {children}
,
PopoverTrigger: ({ render }: { render: React.ReactNode }) => <>{render}>,
PopoverContent: ({ children }: { children: React.ReactNode }) => {children}
,
diff --git a/web/app/components/header/account-setting/model-provider-page/model-selector/index.tsx b/web/app/components/header/account-setting/model-provider-page/model-selector/index.tsx
index 44392a70a5..9241c592f5 100644
--- a/web/app/components/header/account-setting/model-provider-page/model-selector/index.tsx
+++ b/web/app/components/header/account-setting/model-provider-page/model-selector/index.tsx
@@ -5,12 +5,12 @@ import type {
ModelFeatureEnum,
ModelItem,
} from '../declarations'
-import { useState } from 'react'
import {
Popover,
PopoverContent,
PopoverTrigger,
-} from '@/app/components/base/ui/popover'
+} from '@langgenius/dify-ui/popover'
+import { useState } from 'react'
import { useCurrentProviderAndModel } from '../hooks'
import ModelSelectorTrigger from './model-selector-trigger'
import Popup from './popup'
diff --git a/web/app/components/header/account-setting/model-provider-page/model-selector/model-selector-trigger.tsx b/web/app/components/header/account-setting/model-provider-page/model-selector/model-selector-trigger.tsx
index d43324ca12..6b9bcae8dc 100644
--- a/web/app/components/header/account-setting/model-provider-page/model-selector/model-selector-trigger.tsx
+++ b/web/app/components/header/account-setting/model-provider-page/model-selector/model-selector-trigger.tsx
@@ -5,8 +5,8 @@ import type {
ModelItem,
} from '../declarations'
import { cn } from '@langgenius/dify-ui/cn'
+import { Tooltip, TooltipContent, TooltipTrigger } from '@langgenius/dify-ui/tooltip'
import { useTranslation } from 'react-i18next'
-import { Tooltip, TooltipContent, TooltipTrigger } from '@/app/components/base/ui/tooltip'
import { useProviderContext } from '@/context/provider-context'
import {
DERIVED_MODEL_STATUS_BADGE_I18N,
diff --git a/web/app/components/header/account-setting/model-provider-page/model-selector/popup-item.tsx b/web/app/components/header/account-setting/model-provider-page/model-selector/popup-item.tsx
index e38cc07ca2..ed16cd7904 100644
--- a/web/app/components/header/account-setting/model-provider-page/model-selector/popup-item.tsx
+++ b/web/app/components/header/account-setting/model-provider-page/model-selector/popup-item.tsx
@@ -5,15 +5,15 @@ import type {
ModelItem,
} from '../declarations'
import { cn } from '@langgenius/dify-ui/cn'
-import { useCallback, useState } from 'react'
-import { useTranslation } from 'react-i18next'
-import { CreditsCoin } from '@/app/components/base/icons/src/vender/line/financeAndECommerce'
import {
Popover,
PopoverContent,
PopoverTrigger,
-} from '@/app/components/base/ui/popover'
-import { Tooltip, TooltipContent, TooltipTrigger } from '@/app/components/base/ui/tooltip'
+} from '@langgenius/dify-ui/popover'
+import { Tooltip, TooltipContent, TooltipTrigger } from '@langgenius/dify-ui/tooltip'
+import { useCallback, useState } from 'react'
+import { useTranslation } from 'react-i18next'
+import { CreditsCoin } from '@/app/components/base/icons/src/vender/line/financeAndECommerce'
import { useModalContext } from '@/context/modal-context'
import { useProviderContext } from '@/context/provider-context'
import {
diff --git a/web/app/components/header/account-setting/model-provider-page/model-selector/popup.tsx b/web/app/components/header/account-setting/model-provider-page/model-selector/popup.tsx
index b40bc53a65..017194aaf5 100644
--- a/web/app/components/header/account-setting/model-provider-page/model-selector/popup.tsx
+++ b/web/app/components/header/account-setting/model-provider-page/model-selector/popup.tsx
@@ -5,11 +5,11 @@ import type {
ModelItem,
} from '../declarations'
import type { ModelProviderQuotaGetPaid } from '@/types/model-provider'
+import { Button } from '@langgenius/dify-ui/button'
import { cn } from '@langgenius/dify-ui/cn'
import { useTheme } from 'next-themes'
import { useCallback, useMemo, useState } from 'react'
import { useTranslation } from 'react-i18next'
-import { Button } from '@/app/components/base/ui/button'
import { ACCOUNT_SETTING_TAB } from '@/app/components/header/account-setting/constants'
import checkTaskStatus from '@/app/components/plugins/install-plugin/base/check-task-status'
import useRefreshPluginList from '@/app/components/plugins/install-plugin/hooks/use-refresh-plugin-list'
diff --git a/web/app/components/header/account-setting/model-provider-page/provider-added-card/__tests__/credential-panel.spec.tsx b/web/app/components/header/account-setting/model-provider-page/provider-added-card/__tests__/credential-panel.spec.tsx
index 4f51de9b41..f07f9652f8 100644
--- a/web/app/components/header/account-setting/model-provider-page/provider-added-card/__tests__/credential-panel.spec.tsx
+++ b/web/app/components/header/account-setting/model-provider-page/provider-added-card/__tests__/credential-panel.spec.tsx
@@ -32,7 +32,7 @@ vi.mock('@/context/global-public-context', () => ({
useSystemFeaturesQuery: () => ({ data: { trial_models: ['langgenius/openai/openai'] } }),
}))
-vi.mock('@/app/components/base/ui/toast', () => ({
+vi.mock('@langgenius/dify-ui/toast', () => ({
default: { notify: mockToastNotify },
toast: {
success: (message: string) => mockToastNotify({ type: 'success', message }),
diff --git a/web/app/components/header/account-setting/model-provider-page/provider-added-card/__tests__/model-load-balancing-modal.spec.tsx b/web/app/components/header/account-setting/model-provider-page/provider-added-card/__tests__/model-load-balancing-modal.spec.tsx
index eedc04115c..c4794c9775 100644
--- a/web/app/components/header/account-setting/model-provider-page/provider-added-card/__tests__/model-load-balancing-modal.spec.tsx
+++ b/web/app/components/header/account-setting/model-provider-page/provider-added-card/__tests__/model-load-balancing-modal.spec.tsx
@@ -52,8 +52,8 @@ let mockCredentialData: CredentialData | undefined = {
current_credential_name: 'Default',
}
-vi.mock('@/app/components/base/ui/toast', async (importOriginal) => {
- const actual = await importOriginal()
+vi.mock('@langgenius/dify-ui/toast', async (importOriginal) => {
+ const actual = await importOriginal()
return {
...actual,
default: {
diff --git a/web/app/components/header/account-setting/model-provider-page/provider-added-card/__tests__/use-change-provider-priority.spec.ts b/web/app/components/header/account-setting/model-provider-page/provider-added-card/__tests__/use-change-provider-priority.spec.ts
index 57c5121014..7f6e7d393a 100644
--- a/web/app/components/header/account-setting/model-provider-page/provider-added-card/__tests__/use-change-provider-priority.spec.ts
+++ b/web/app/components/header/account-setting/model-provider-page/provider-added-card/__tests__/use-change-provider-priority.spec.ts
@@ -16,7 +16,7 @@ const mockMutationOptions = vi.fn((options: Record) => ({
...options,
}))
-vi.mock('@/app/components/base/ui/toast', () => ({
+vi.mock('@langgenius/dify-ui/toast', () => ({
default: {
notify: (...args: unknown[]) => mockNotify(...args),
},
diff --git a/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-auth-dropdown/__tests__/dialog.spec.tsx b/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-auth-dropdown/__tests__/dialog.spec.tsx
index b4ec0e09d2..85ae2d7e52 100644
--- a/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-auth-dropdown/__tests__/dialog.spec.tsx
+++ b/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-auth-dropdown/__tests__/dialog.spec.tsx
@@ -34,7 +34,7 @@ vi.mock('../use-activate-credential', () => ({
}),
}))
-vi.mock('@/app/components/base/ui/alert-dialog', () => ({
+vi.mock('@langgenius/dify-ui/alert-dialog', () => ({
AlertDialog: ({ children, onOpenChange }: AlertDialogProps) => {
latestOnOpenChange = onOpenChange
return {children}
diff --git a/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-auth-dropdown/__tests__/use-activate-credential.spec.tsx b/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-auth-dropdown/__tests__/use-activate-credential.spec.tsx
index 12acf479c0..aec9c9f691 100644
--- a/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-auth-dropdown/__tests__/use-activate-credential.spec.tsx
+++ b/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-auth-dropdown/__tests__/use-activate-credential.spec.tsx
@@ -1,6 +1,6 @@
import type { Credential, ModelProvider } from '../../../declarations'
+import { toast } from '@langgenius/dify-ui/toast'
import { act, renderHook } from '@testing-library/react'
-import { toast } from '@/app/components/base/ui/toast'
import { useActivateCredential } from '../use-activate-credential'
const mockMutate = vi.fn()
diff --git a/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-auth-dropdown/api-key-section.tsx b/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-auth-dropdown/api-key-section.tsx
index e6f5e51d61..41645677b6 100644
--- a/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-auth-dropdown/api-key-section.tsx
+++ b/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-auth-dropdown/api-key-section.tsx
@@ -1,7 +1,7 @@
import type { Credential, CustomModel, ModelProvider } from '../../declarations'
+import { Button } from '@langgenius/dify-ui/button'
import { memo } from 'react'
import { useTranslation } from 'react-i18next'
-import { Button } from '@/app/components/base/ui/button'
import CredentialItem from '../../model-auth/authorized/credential-item'
type ApiKeySectionProps = {
diff --git a/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-auth-dropdown/credits-exhausted-alert.tsx b/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-auth-dropdown/credits-exhausted-alert.tsx
index 53abe1211d..69a7f488b3 100644
--- a/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-auth-dropdown/credits-exhausted-alert.tsx
+++ b/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-auth-dropdown/credits-exhausted-alert.tsx
@@ -1,3 +1,4 @@
+import { MeterIndicator, MeterLabel, MeterRoot, MeterTrack } from '@langgenius/dify-ui/meter'
import { Trans, useTranslation } from 'react-i18next'
import { CreditsCoin } from '@/app/components/base/icons/src/vender/line/financeAndECommerce'
import { useModalContextSelector } from '@/context/modal-context'
@@ -21,7 +22,9 @@ export default function CreditsExhaustedAlert({ hasApiKeyFallback }: CreditsExha
: 'modelProvider.card.creditsExhaustedDescription'
const usedCredits = totalCredits - credits
- const usagePercent = totalCredits > 0 ? Math.min((usedCredits / totalCredits) * 100, 100) : 100
+ const hasTotal = totalCredits > 0
+ const meterValue = hasTotal ? Math.min(usedCredits, totalCredits) : 1
+ const meterMax = hasTotal ? totalCredits : 1
return (
@@ -45,11 +48,11 @@ export default function CreditsExhaustedAlert({ hasApiKeyFallback }: CreditsExha
/>
-
+
-
+
{t('modelProvider.card.usageLabel', { ns: 'common' })}
-
+
@@ -59,13 +62,10 @@ export default function CreditsExhaustedAlert({ hasApiKeyFallback }: CreditsExha
-
-
+
+
+
+
)
}
diff --git a/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-auth-dropdown/dropdown-content.tsx b/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-auth-dropdown/dropdown-content.tsx
index 415539383b..0e0f3a67b1 100644
--- a/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-auth-dropdown/dropdown-content.tsx
+++ b/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-auth-dropdown/dropdown-content.tsx
@@ -1,7 +1,5 @@
import type { Credential, ModelProvider, PreferredProviderTypeEnum } from '../../declarations'
import type { CredentialPanelState } from '../use-credential-panel-state'
-import { memo, useCallback } from 'react'
-import { useTranslation } from 'react-i18next'
import {
AlertDialog,
AlertDialogActions,
@@ -10,7 +8,9 @@ import {
AlertDialogContent,
AlertDialogDescription,
AlertDialogTitle,
-} from '@/app/components/base/ui/alert-dialog'
+} from '@langgenius/dify-ui/alert-dialog'
+import { memo, useCallback } from 'react'
+import { useTranslation } from 'react-i18next'
import { ConfigurationMethodEnum } from '../../declarations'
import { useAuth } from '../../model-auth/hooks'
import ApiKeySection from './api-key-section'
diff --git a/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-auth-dropdown/index.tsx b/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-auth-dropdown/index.tsx
index fc710ef73f..67f73f6941 100644
--- a/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-auth-dropdown/index.tsx
+++ b/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-auth-dropdown/index.tsx
@@ -1,13 +1,13 @@
import type { ModelProvider, PreferredProviderTypeEnum } from '../../declarations'
import type { CardVariant, CredentialPanelState } from '../use-credential-panel-state'
-import { memo, useCallback, useState } from 'react'
-import { useTranslation } from 'react-i18next'
-import { Button } from '@/app/components/base/ui/button'
+import { Button } from '@langgenius/dify-ui/button'
import {
Popover,
PopoverContent,
PopoverTrigger,
-} from '@/app/components/base/ui/popover'
+} from '@langgenius/dify-ui/popover'
+import { memo, useCallback, useState } from 'react'
+import { useTranslation } from 'react-i18next'
import DropdownContent from './dropdown-content'
type ModelAuthDropdownProps = {
diff --git a/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-auth-dropdown/usage-priority-section.tsx b/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-auth-dropdown/usage-priority-section.tsx
index 638f9d605b..e572ec41af 100644
--- a/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-auth-dropdown/usage-priority-section.tsx
+++ b/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-auth-dropdown/usage-priority-section.tsx
@@ -1,7 +1,7 @@
import type { UsagePriority } from '../use-credential-panel-state'
import { cn } from '@langgenius/dify-ui/cn'
+import { Tooltip, TooltipContent, TooltipTrigger } from '@langgenius/dify-ui/tooltip'
import { useTranslation } from 'react-i18next'
-import { Tooltip, TooltipContent, TooltipTrigger } from '@/app/components/base/ui/tooltip'
import { PreferredProviderTypeEnum } from '../../declarations'
type UsagePrioritySectionProps = {
@@ -34,7 +34,6 @@ export default function UsagePrioritySection({ value, disabled, onSelect }: Usag
diff --git a/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-auth-dropdown/use-activate-credential.ts b/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-auth-dropdown/use-activate-credential.ts
index 6f99c32296..db9f735e3a 100644
--- a/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-auth-dropdown/use-activate-credential.ts
+++ b/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-auth-dropdown/use-activate-credential.ts
@@ -1,7 +1,7 @@
import type { Credential, ModelProvider } from '../../declarations'
+import { toast } from '@langgenius/dify-ui/toast'
import { useCallback, useRef, useState } from 'react'
import { useTranslation } from 'react-i18next'
-import { toast } from '@/app/components/base/ui/toast'
import { useActiveProviderCredential } from '@/service/use-models'
import { useUpdateModelList, useUpdateModelProviders } from '../../hooks'
diff --git a/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-list-item.tsx b/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-list-item.tsx
index d8f0b5928c..305ef71c50 100644
--- a/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-list-item.tsx
+++ b/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-list-item.tsx
@@ -1,12 +1,12 @@
import type { ModelItem, ModelProvider } from '../declarations'
import { cn } from '@langgenius/dify-ui/cn'
+import { Switch } from '@langgenius/dify-ui/switch'
import { useQueryClient } from '@tanstack/react-query'
import { useDebounceFn } from 'ahooks'
import { memo, useCallback } from 'react'
import { useTranslation } from 'react-i18next'
import Badge from '@/app/components/base/badge'
import { Balance } from '@/app/components/base/icons/src/vender/line/financeAndECommerce'
-import Switch from '@/app/components/base/switch'
import Tooltip from '@/app/components/base/tooltip'
import { Plan } from '@/app/components/billing/type'
import { useAppContext } from '@/context/app-context'
diff --git a/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-load-balancing-configs.tsx b/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-load-balancing-configs.tsx
index ea4edace30..9b7b858c13 100644
--- a/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-load-balancing-configs.tsx
+++ b/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-load-balancing-configs.tsx
@@ -9,11 +9,11 @@ import type {
ModelProvider,
} from '../declarations'
import { cn } from '@langgenius/dify-ui/cn'
+import { Switch } from '@langgenius/dify-ui/switch'
import { useCallback, useMemo } from 'react'
import { useTranslation } from 'react-i18next'
import Badge from '@/app/components/base/badge/index'
import GridMask from '@/app/components/base/grid-mask'
-import Switch from '@/app/components/base/switch'
import Tooltip from '@/app/components/base/tooltip'
import UpgradeBtn from '@/app/components/billing/upgrade-btn'
import s from '@/app/components/custom/style.module.css'
diff --git a/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-load-balancing-modal.tsx b/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-load-balancing-modal.tsx
index 652630be67..34b9d1578f 100644
--- a/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-load-balancing-modal.tsx
+++ b/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-load-balancing-modal.tsx
@@ -1,9 +1,4 @@
import type { Credential, CustomConfigurationModelFixedFields, ModelItem, ModelLoadBalancingConfig, ModelLoadBalancingConfigEntry, ModelProvider } from '../declarations'
-import { cn } from '@langgenius/dify-ui/cn'
-import { memo, useCallback, useEffect, useMemo, useState } from 'react'
-import { useTranslation } from 'react-i18next'
-import Loading from '@/app/components/base/loading'
-import Modal from '@/app/components/base/modal'
import {
AlertDialog,
AlertDialogActions,
@@ -11,9 +6,14 @@ import {
AlertDialogConfirmButton,
AlertDialogContent,
AlertDialogTitle,
-} from '@/app/components/base/ui/alert-dialog'
-import { Button } from '@/app/components/base/ui/button'
-import { toast } from '@/app/components/base/ui/toast'
+} from '@langgenius/dify-ui/alert-dialog'
+import { Button } from '@langgenius/dify-ui/button'
+import { cn } from '@langgenius/dify-ui/cn'
+import { toast } from '@langgenius/dify-ui/toast'
+import { memo, useCallback, useEffect, useMemo, useState } from 'react'
+import { useTranslation } from 'react-i18next'
+import Loading from '@/app/components/base/loading'
+import Modal from '@/app/components/base/modal'
import { SwitchCredentialInLoadBalancing } from '@/app/components/header/account-setting/model-provider-page/model-auth'
import { useGetModelCredential, useUpdateModelLoadBalancingConfig } from '@/service/use-models'
import { ConfigurationMethodEnum, FormTypeEnum } from '../declarations'
diff --git a/web/app/components/header/account-setting/model-provider-page/provider-added-card/priority-selector.tsx b/web/app/components/header/account-setting/model-provider-page/provider-added-card/priority-selector.tsx
index e71a219dcd..a74c400035 100644
--- a/web/app/components/header/account-setting/model-provider-page/provider-added-card/priority-selector.tsx
+++ b/web/app/components/header/account-setting/model-provider-page/provider-added-card/priority-selector.tsx
@@ -1,5 +1,6 @@
import type { FC } from 'react'
import { Popover, PopoverButton, PopoverPanel, Transition } from '@headlessui/react'
+import { Button } from '@langgenius/dify-ui/button'
import { cn } from '@langgenius/dify-ui/cn'
import {
RiCheckLine,
@@ -7,7 +8,6 @@ import {
} from '@remixicon/react'
import { Fragment } from 'react'
import { useTranslation } from 'react-i18next'
-import { Button } from '@/app/components/base/ui/button'
import { PreferredProviderTypeEnum } from '../declarations'
type SelectorProps = {
diff --git a/web/app/components/header/account-setting/model-provider-page/provider-added-card/provider-card-actions.tsx b/web/app/components/header/account-setting/model-provider-page/provider-added-card/provider-card-actions.tsx
index 2269354825..861c4afa7a 100644
--- a/web/app/components/header/account-setting/model-provider-page/provider-added-card/provider-card-actions.tsx
+++ b/web/app/components/header/account-setting/model-provider-page/provider-added-card/provider-card-actions.tsx
@@ -1,11 +1,11 @@
import type { FC } from 'react'
import type { PluginDetail } from '@/app/components/plugins/types'
+import { Button } from '@langgenius/dify-ui/button'
import { cn } from '@langgenius/dify-ui/cn'
+import { Tooltip, TooltipContent, TooltipTrigger } from '@langgenius/dify-ui/tooltip'
import { useMemo } from 'react'
import { useTranslation } from 'react-i18next'
import Badge from '@/app/components/base/badge'
-import { Button } from '@/app/components/base/ui/button'
-import { Tooltip, TooltipContent, TooltipTrigger } from '@/app/components/base/ui/tooltip'
import { HeaderModals } from '@/app/components/plugins/plugin-detail-panel/detail-header/components'
import { useDetailHeaderState, usePluginOperations } from '@/app/components/plugins/plugin-detail-panel/detail-header/hooks'
import OperationDropdown from '@/app/components/plugins/plugin-detail-panel/operation-dropdown'
diff --git a/web/app/components/header/account-setting/model-provider-page/provider-added-card/quota-panel.tsx b/web/app/components/header/account-setting/model-provider-page/provider-added-card/quota-panel.tsx
index 03d7c28a88..7828dc4635 100644
--- a/web/app/components/header/account-setting/model-provider-page/provider-added-card/quota-panel.tsx
+++ b/web/app/components/header/account-setting/model-provider-page/provider-added-card/quota-panel.tsx
@@ -3,12 +3,12 @@ import type { ModelProvider } from '../declarations'
import type { Plugin } from '@/app/components/plugins/types'
import type { ModelProviderQuotaGetPaid } from '@/types/model-provider'
import { cn } from '@langgenius/dify-ui/cn'
+import { Tooltip, TooltipContent, TooltipTrigger } from '@langgenius/dify-ui/tooltip'
import { useBoolean } from 'ahooks'
import * as React from 'react'
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
import { useTranslation } from 'react-i18next'
import Loading from '@/app/components/base/loading'
-import { Tooltip, TooltipContent, TooltipTrigger } from '@/app/components/base/ui/tooltip'
import InstallFromMarketplace from '@/app/components/plugins/install-plugin/install-from-marketplace'
import { useSystemFeaturesQuery } from '@/context/global-public-context'
import useTimestamp from '@/hooks/use-timestamp'
@@ -102,7 +102,6 @@ const QuotaPanel: FC = ({
@@ -150,7 +149,6 @@ const QuotaPanel: FC = ({
({
}),
}))
-vi.mock('@/app/components/base/ui/toast', async (importOriginal) => {
- const actual = await importOriginal()
+vi.mock('@langgenius/dify-ui/toast', async (importOriginal) => {
+ const actual = await importOriginal()
return {
...actual,
toast: {
diff --git a/web/app/components/header/account-setting/model-provider-page/system-model-selector/index.tsx b/web/app/components/header/account-setting/model-provider-page/system-model-selector/index.tsx
index 8b86923391..9f993260ca 100644
--- a/web/app/components/header/account-setting/model-provider-page/system-model-selector/index.tsx
+++ b/web/app/components/header/account-setting/model-provider-page/system-model-selector/index.tsx
@@ -3,21 +3,21 @@ import type {
DefaultModel,
DefaultModelResponse,
} from '../declarations'
-import { useState } from 'react'
-import { useTranslation } from 'react-i18next'
-import { Button } from '@/app/components/base/ui/button'
+import { Button } from '@langgenius/dify-ui/button'
import {
Dialog,
DialogCloseButton,
DialogContent,
DialogTitle,
-} from '@/app/components/base/ui/dialog'
-import { toast } from '@/app/components/base/ui/toast'
+} from '@langgenius/dify-ui/dialog'
+import { toast } from '@langgenius/dify-ui/toast'
import {
Tooltip,
TooltipContent,
TooltipTrigger,
-} from '@/app/components/base/ui/tooltip'
+} from '@langgenius/dify-ui/tooltip'
+import { useState } from 'react'
+import { useTranslation } from 'react-i18next'
import { useAppContext } from '@/context/app-context'
import { useProviderContext } from '@/context/provider-context'
import { updateDefaultModel } from '@/service/common'
@@ -141,7 +141,6 @@ const SystemModel: FC = ({
diff --git a/web/app/components/header/account-setting/plugin-page/SerpapiPlugin.tsx b/web/app/components/header/account-setting/plugin-page/SerpapiPlugin.tsx
index 7308691b54..31c4ce2c3a 100644
--- a/web/app/components/header/account-setting/plugin-page/SerpapiPlugin.tsx
+++ b/web/app/components/header/account-setting/plugin-page/SerpapiPlugin.tsx
@@ -1,7 +1,7 @@
import type { Form, ValidateValue } from '../key-validator/declarations'
import type { PluginProvider } from '@/models/common'
+import { toast } from '@langgenius/dify-ui/toast'
import { useTranslation } from 'react-i18next'
-import { toast } from '@/app/components/base/ui/toast'
import { useAppContext } from '@/context/app-context'
import SerpapiLogo from '../../assets/serpapi.png'
import KeyValidator from '../key-validator'
diff --git a/web/app/components/header/account-setting/plugin-page/__tests__/SerpapiPlugin.spec.tsx b/web/app/components/header/account-setting/plugin-page/__tests__/SerpapiPlugin.spec.tsx
index 85d205713b..2055c0725d 100644
--- a/web/app/components/header/account-setting/plugin-page/__tests__/SerpapiPlugin.spec.tsx
+++ b/web/app/components/header/account-setting/plugin-page/__tests__/SerpapiPlugin.spec.tsx
@@ -32,7 +32,7 @@ const { mockToast } = vi.hoisted(() => {
return { mockToast }
})
-vi.mock('@/app/components/base/ui/toast', () => ({
+vi.mock('@langgenius/dify-ui/toast', () => ({
toast: mockToast,
}))
diff --git a/web/app/components/header/account-setting/plugin-page/__tests__/index.spec.tsx b/web/app/components/header/account-setting/plugin-page/__tests__/index.spec.tsx
index ff58fdd182..ca0c41a83b 100644
--- a/web/app/components/header/account-setting/plugin-page/__tests__/index.spec.tsx
+++ b/web/app/components/header/account-setting/plugin-page/__tests__/index.spec.tsx
@@ -14,8 +14,8 @@ vi.mock('@/context/app-context', () => ({
useAppContext: vi.fn(),
}))
-vi.mock('@/app/components/base/ui/toast', async (importOriginal) => {
- const actual = await importOriginal()
+vi.mock('@langgenius/dify-ui/toast', async (importOriginal) => {
+ const actual = await importOriginal()
return {
...actual,
diff --git a/web/app/components/plugins/install-plugin/__tests__/hooks.spec.ts b/web/app/components/plugins/install-plugin/__tests__/hooks.spec.ts
index b4171de7f0..385b9e01e1 100644
--- a/web/app/components/plugins/install-plugin/__tests__/hooks.spec.ts
+++ b/web/app/components/plugins/install-plugin/__tests__/hooks.spec.ts
@@ -2,7 +2,7 @@ import { beforeEach, describe, expect, it, vi } from 'vitest'
import { checkForUpdates, fetchReleases, handleUpload } from '../hooks'
const mockNotify = vi.fn()
-vi.mock('@/app/components/base/ui/toast', () => ({
+vi.mock('@langgenius/dify-ui/toast', () => ({
toast: Object.assign((...args: unknown[]) => mockNotify(...args), {
success: (...args: unknown[]) => mockNotify(...args),
error: (...args: unknown[]) => mockNotify(...args),
diff --git a/web/app/components/plugins/install-plugin/base/installed.tsx b/web/app/components/plugins/install-plugin/base/installed.tsx
index 088d587378..7b76767e77 100644
--- a/web/app/components/plugins/install-plugin/base/installed.tsx
+++ b/web/app/components/plugins/install-plugin/base/installed.tsx
@@ -1,10 +1,10 @@
'use client'
import type { FC } from 'react'
import type { Plugin, PluginDeclaration, PluginManifestInMarket } from '../../types'
+import { Button } from '@langgenius/dify-ui/button'
import * as React from 'react'
import { useTranslation } from 'react-i18next'
import Badge, { BadgeState } from '@/app/components/base/badge/index'
-import { Button } from '@/app/components/base/ui/button'
import Card from '../../card'
import { pluginManifestInMarketToPluginProps, pluginManifestToCardPluginProps } from '../utils'
diff --git a/web/app/components/plugins/install-plugin/hooks.ts b/web/app/components/plugins/install-plugin/hooks.ts
index 4759953e30..e7086a7fba 100644
--- a/web/app/components/plugins/install-plugin/hooks.ts
+++ b/web/app/components/plugins/install-plugin/hooks.ts
@@ -1,5 +1,5 @@
import type { GitHubRepoReleaseResponse } from '../types'
-import { toast } from '@/app/components/base/ui/toast'
+import { toast } from '@langgenius/dify-ui/toast'
import { uploadGitHub } from '@/service/plugins'
import { compareVersion, getLatestVersion } from '@/utils/semver'
diff --git a/web/app/components/plugins/install-plugin/install-bundle/steps/install.tsx b/web/app/components/plugins/install-plugin/install-bundle/steps/install.tsx
index df94331d58..a94cd8588d 100644
--- a/web/app/components/plugins/install-plugin/install-bundle/steps/install.tsx
+++ b/web/app/components/plugins/install-plugin/install-bundle/steps/install.tsx
@@ -2,12 +2,12 @@
import type { FC } from 'react'
import type { Dependency, InstallStatus, InstallStatusResponse, Plugin, VersionInfo } from '../../../types'
import type { ExposeRefs } from './install-multi'
+import { Button } from '@langgenius/dify-ui/button'
import { RiLoader2Line } from '@remixicon/react'
import * as React from 'react'
import { useCallback, useRef, useState } from 'react'
import { useTranslation } from 'react-i18next'
import Checkbox from '@/app/components/base/checkbox'
-import { Button } from '@/app/components/base/ui/button'
import { useCanInstallPluginFromMarketplace } from '@/app/components/plugins/plugin-page/use-reference-setting'
import { useMittContextSelector } from '@/context/mitt-context'
import { useInstallOrUpdate, usePluginTaskList } from '@/service/use-plugins'
diff --git a/web/app/components/plugins/install-plugin/install-bundle/steps/installed.tsx b/web/app/components/plugins/install-plugin/install-bundle/steps/installed.tsx
index 1c64e73d62..3fbf0c13ec 100644
--- a/web/app/components/plugins/install-plugin/install-bundle/steps/installed.tsx
+++ b/web/app/components/plugins/install-plugin/install-bundle/steps/installed.tsx
@@ -1,10 +1,10 @@
'use client'
import type { FC } from 'react'
import type { InstallStatus, Plugin } from '../../../types'
+import { Button } from '@langgenius/dify-ui/button'
import * as React from 'react'
import { useTranslation } from 'react-i18next'
import Badge, { BadgeState } from '@/app/components/base/badge/index'
-import { Button } from '@/app/components/base/ui/button'
import Card from '@/app/components/plugins/card'
import { MARKETPLACE_API_PREFIX } from '@/config'
import useGetIcon from '../../base/use-get-icon'
diff --git a/web/app/components/plugins/install-plugin/install-from-github/__tests__/index.spec.tsx b/web/app/components/plugins/install-plugin/install-from-github/__tests__/index.spec.tsx
index 2f35f484f0..e923de5e38 100644
--- a/web/app/components/plugins/install-plugin/install-from-github/__tests__/index.spec.tsx
+++ b/web/app/components/plugins/install-plugin/install-from-github/__tests__/index.spec.tsx
@@ -57,7 +57,7 @@ const createUpdatePayload = (overrides: Partial = {}):
// Mock external dependencies
const mockNotify = vi.fn()
-vi.mock('@/app/components/base/ui/toast', () => ({
+vi.mock('@langgenius/dify-ui/toast', () => ({
toast: Object.assign((props: { type: string, message: string }) => mockNotify(props), {
success: (message: string) => mockNotify({ type: 'success', message }),
error: (message: string) => mockNotify({ type: 'error', message }),
diff --git a/web/app/components/plugins/install-plugin/install-from-github/index.tsx b/web/app/components/plugins/install-plugin/install-from-github/index.tsx
index f1e4d1b9bf..4ac0a3aa7f 100644
--- a/web/app/components/plugins/install-plugin/install-from-github/index.tsx
+++ b/web/app/components/plugins/install-plugin/install-from-github/index.tsx
@@ -4,11 +4,11 @@ import type { PluginDeclaration, UpdateFromGitHubPayload } from '../../types'
import type { Item } from '@/app/components/base/select'
import type { InstallState } from '@/app/components/plugins/types'
import { cn } from '@langgenius/dify-ui/cn'
+import { toast } from '@langgenius/dify-ui/toast'
import * as React from 'react'
import { useCallback, useState } from 'react'
import { useTranslation } from 'react-i18next'
import Modal from '@/app/components/base/modal'
-import { toast } from '@/app/components/base/ui/toast'
import useGetIcon from '@/app/components/plugins/install-plugin/base/use-get-icon'
import { InstallStepFromGitHub } from '../../types'
import Installed from '../base/installed'
diff --git a/web/app/components/plugins/install-plugin/install-from-github/steps/loaded.tsx b/web/app/components/plugins/install-plugin/install-from-github/steps/loaded.tsx
index c044445f19..ff7630e3a2 100644
--- a/web/app/components/plugins/install-plugin/install-from-github/steps/loaded.tsx
+++ b/web/app/components/plugins/install-plugin/install-from-github/steps/loaded.tsx
@@ -1,11 +1,11 @@
'use client'
import type { Plugin, PluginDeclaration, UpdateFromGitHubPayload } from '../../../types'
+import { Button } from '@langgenius/dify-ui/button'
import { RiLoader2Line } from '@remixicon/react'
import * as React from 'react'
import { useEffect } from 'react'
import { useTranslation } from 'react-i18next'
-import { Button } from '@/app/components/base/ui/button'
import useCheckInstalled from '@/app/components/plugins/install-plugin/hooks/use-check-installed'
import { updateFromGitHub } from '@/service/plugins'
import { useInstallPackageFromGitHub, usePluginTaskList } from '@/service/use-plugins'
diff --git a/web/app/components/plugins/install-plugin/install-from-github/steps/selectPackage.tsx b/web/app/components/plugins/install-plugin/install-from-github/steps/selectPackage.tsx
index a8236b4b93..18c33def82 100644
--- a/web/app/components/plugins/install-plugin/install-from-github/steps/selectPackage.tsx
+++ b/web/app/components/plugins/install-plugin/install-from-github/steps/selectPackage.tsx
@@ -2,10 +2,10 @@
import type { PluginDeclaration, UpdateFromGitHubPayload } from '../../../types'
import type { Item } from '@/app/components/base/select'
+import { Button } from '@langgenius/dify-ui/button'
import * as React from 'react'
import { useTranslation } from 'react-i18next'
import { PortalSelect } from '@/app/components/base/select'
-import { Button } from '@/app/components/base/ui/button'
import { handleUpload } from '../../hooks'
const i18nPrefix = 'installFromGitHub'
diff --git a/web/app/components/plugins/install-plugin/install-from-github/steps/setURL.tsx b/web/app/components/plugins/install-plugin/install-from-github/steps/setURL.tsx
index 2a7012d40c..f1b149a8bf 100644
--- a/web/app/components/plugins/install-plugin/install-from-github/steps/setURL.tsx
+++ b/web/app/components/plugins/install-plugin/install-from-github/steps/setURL.tsx
@@ -1,8 +1,8 @@
'use client'
+import { Button } from '@langgenius/dify-ui/button'
import * as React from 'react'
import { useTranslation } from 'react-i18next'
-import { Button } from '@/app/components/base/ui/button'
type SetURLProps = {
repoUrl: string
diff --git a/web/app/components/plugins/install-plugin/install-from-local-package/steps/install.tsx b/web/app/components/plugins/install-plugin/install-from-local-package/steps/install.tsx
index 9ef52d7a5d..7c0a90f179 100644
--- a/web/app/components/plugins/install-plugin/install-from-local-package/steps/install.tsx
+++ b/web/app/components/plugins/install-plugin/install-from-local-package/steps/install.tsx
@@ -1,11 +1,11 @@
'use client'
import type { FC } from 'react'
import type { PluginDeclaration } from '../../../types'
+import { Button } from '@langgenius/dify-ui/button'
import { RiLoader2Line } from '@remixicon/react'
import * as React from 'react'
import { useEffect, useMemo } from 'react'
import { Trans, useTranslation } from 'react-i18next'
-import { Button } from '@/app/components/base/ui/button'
import useCheckInstalled from '@/app/components/plugins/install-plugin/hooks/use-check-installed'
import { useAppContext } from '@/context/app-context'
import { uninstallPlugin } from '@/service/plugins'
diff --git a/web/app/components/plugins/install-plugin/install-from-local-package/steps/uploading.tsx b/web/app/components/plugins/install-plugin/install-from-local-package/steps/uploading.tsx
index f9f9969817..e7c3b4cd0f 100644
--- a/web/app/components/plugins/install-plugin/install-from-local-package/steps/uploading.tsx
+++ b/web/app/components/plugins/install-plugin/install-from-local-package/steps/uploading.tsx
@@ -1,10 +1,10 @@
'use client'
import type { FC } from 'react'
import type { Dependency, PluginDeclaration } from '../../../types'
+import { Button } from '@langgenius/dify-ui/button'
import { RiLoader2Line } from '@remixicon/react'
import * as React from 'react'
import { useTranslation } from 'react-i18next'
-import { Button } from '@/app/components/base/ui/button'
import { uploadFile } from '@/service/plugins'
import Card from '../../../card'
diff --git a/web/app/components/plugins/install-plugin/install-from-marketplace/steps/install.tsx b/web/app/components/plugins/install-plugin/install-from-marketplace/steps/install.tsx
index ac8b458406..91397f3189 100644
--- a/web/app/components/plugins/install-plugin/install-from-marketplace/steps/install.tsx
+++ b/web/app/components/plugins/install-plugin/install-from-marketplace/steps/install.tsx
@@ -1,11 +1,11 @@
'use client'
import type { FC } from 'react'
import type { Plugin, PluginManifestInMarket } from '../../../types'
+import { Button } from '@langgenius/dify-ui/button'
import { RiLoader2Line } from '@remixicon/react'
import * as React from 'react'
import { useEffect, useMemo } from 'react'
import { useTranslation } from 'react-i18next'
-import { Button } from '@/app/components/base/ui/button'
import useCheckInstalled from '@/app/components/plugins/install-plugin/hooks/use-check-installed'
import { useAppContext } from '@/context/app-context'
import { useInstallPackageFromMarketPlace, usePluginDeclarationFromMarketPlace, usePluginTaskList, useUpdatePackageFromMarketPlace } from '@/service/use-plugins'
diff --git a/web/app/components/plugins/marketplace/list/card-wrapper.tsx b/web/app/components/plugins/marketplace/list/card-wrapper.tsx
index 6cab636392..9dc5bc3d78 100644
--- a/web/app/components/plugins/marketplace/list/card-wrapper.tsx
+++ b/web/app/components/plugins/marketplace/list/card-wrapper.tsx
@@ -1,12 +1,12 @@
'use client'
import type { Plugin } from '@/app/components/plugins/types'
import { useLocale, useTranslation } from '#i18n'
+import { Button } from '@langgenius/dify-ui/button'
import { RiArrowRightUpLine } from '@remixicon/react'
import { useBoolean } from 'ahooks'
import { useTheme } from 'next-themes'
import * as React from 'react'
import { useMemo } from 'react'
-import { Button } from '@/app/components/base/ui/button'
import Card from '@/app/components/plugins/card'
import CardMoreInfo from '@/app/components/plugins/card/card-more-info'
import { useTags } from '@/app/components/plugins/hooks'
diff --git a/web/app/components/plugins/marketplace/sort-dropdown/__tests__/index.spec.tsx b/web/app/components/plugins/marketplace/sort-dropdown/__tests__/index.spec.tsx
index 990bb321de..5bf5b6bb99 100644
--- a/web/app/components/plugins/marketplace/sort-dropdown/__tests__/index.spec.tsx
+++ b/web/app/components/plugins/marketplace/sort-dropdown/__tests__/index.spec.tsx
@@ -31,7 +31,7 @@ vi.mock('../../atoms', () => ({
useMarketplaceSort: () => [mockSort, mockHandleSortChange],
}))
-vi.mock('@/app/components/base/ui/dropdown-menu', async () => {
+vi.mock('@langgenius/dify-ui/dropdown-menu', async () => {
const React = await import('react')
const DropdownMenuContext = React.createContext<{ open: boolean, setOpen: (open: boolean) => void } | null>(null)
diff --git a/web/app/components/plugins/marketplace/sort-dropdown/index.tsx b/web/app/components/plugins/marketplace/sort-dropdown/index.tsx
index a47143de02..b8f5467fa1 100644
--- a/web/app/components/plugins/marketplace/sort-dropdown/index.tsx
+++ b/web/app/components/plugins/marketplace/sort-dropdown/index.tsx
@@ -1,12 +1,12 @@
'use client'
import { useTranslation } from '#i18n'
-import { useState } from 'react'
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
-} from '@/app/components/base/ui/dropdown-menu'
+} from '@langgenius/dify-ui/dropdown-menu'
+import { useState } from 'react'
import { useMarketplaceSort } from '../atoms'
const SortDropdown = () => {
diff --git a/web/app/components/plugins/plugin-auth/authorize/__tests__/api-key-modal.spec.tsx b/web/app/components/plugins/plugin-auth/authorize/__tests__/api-key-modal.spec.tsx
index a4ddec8f51..2bfa94d2ed 100644
--- a/web/app/components/plugins/plugin-auth/authorize/__tests__/api-key-modal.spec.tsx
+++ b/web/app/components/plugins/plugin-auth/authorize/__tests__/api-key-modal.spec.tsx
@@ -15,7 +15,7 @@ const mockToast = {
promise: vi.fn(),
}
-vi.mock('@/app/components/base/ui/toast', () => ({
+vi.mock('@langgenius/dify-ui/toast', () => ({
toast: mockToast,
}))
const mockAddPluginCredential = vi.fn().mockResolvedValue({})
diff --git a/web/app/components/plugins/plugin-auth/authorize/__tests__/authorize-components.spec.tsx b/web/app/components/plugins/plugin-auth/authorize/__tests__/authorize-components.spec.tsx
index c495d5501a..cba5c60654 100644
--- a/web/app/components/plugins/plugin-auth/authorize/__tests__/authorize-components.spec.tsx
+++ b/web/app/components/plugins/plugin-auth/authorize/__tests__/authorize-components.spec.tsx
@@ -104,7 +104,7 @@ const mockToast = {
promise: vi.fn(),
}
-vi.mock('@/app/components/base/ui/toast', () => ({
+vi.mock('@langgenius/dify-ui/toast', () => ({
toast: mockToast,
}))
// Factory function for creating test PluginPayload
diff --git a/web/app/components/plugins/plugin-auth/authorize/__tests__/oauth-client-settings.spec.tsx b/web/app/components/plugins/plugin-auth/authorize/__tests__/oauth-client-settings.spec.tsx
index 5f9b6f5695..2c86820202 100644
--- a/web/app/components/plugins/plugin-auth/authorize/__tests__/oauth-client-settings.spec.tsx
+++ b/web/app/components/plugins/plugin-auth/authorize/__tests__/oauth-client-settings.spec.tsx
@@ -14,7 +14,7 @@ const mockToast = {
promise: vi.fn(),
}
-vi.mock('@/app/components/base/ui/toast', () => ({
+vi.mock('@langgenius/dify-ui/toast', () => ({
toast: mockToast,
}))
const mockSetPluginOAuthCustomClient = vi.fn().mockResolvedValue({})
diff --git a/web/app/components/plugins/plugin-auth/authorize/add-api-key-button.tsx b/web/app/components/plugins/plugin-auth/authorize/add-api-key-button.tsx
index 2db1751bdc..648a87dabc 100644
--- a/web/app/components/plugins/plugin-auth/authorize/add-api-key-button.tsx
+++ b/web/app/components/plugins/plugin-auth/authorize/add-api-key-button.tsx
@@ -1,11 +1,11 @@
+import type { ButtonProps } from '@langgenius/dify-ui/button'
import type { PluginPayload } from '../types'
import type { FormSchema } from '@/app/components/base/form/types'
-import type { ButtonProps } from '@/app/components/base/ui/button'
+import { Button } from '@langgenius/dify-ui/button'
import {
memo,
useState,
} from 'react'
-import { Button } from '@/app/components/base/ui/button'
import ApiKeyModal from './api-key-modal'
export type AddApiKeyButtonProps = {
diff --git a/web/app/components/plugins/plugin-auth/authorize/add-oauth-button.tsx b/web/app/components/plugins/plugin-auth/authorize/add-oauth-button.tsx
index 31e87564e7..44b48db7a2 100644
--- a/web/app/components/plugins/plugin-auth/authorize/add-oauth-button.tsx
+++ b/web/app/components/plugins/plugin-auth/authorize/add-oauth-button.tsx
@@ -1,6 +1,7 @@
+import type { ButtonProps } from '@langgenius/dify-ui/button'
import type { PluginPayload } from '../types'
import type { FormSchema } from '@/app/components/base/form/types'
-import type { ButtonProps } from '@/app/components/base/ui/button'
+import { Button } from '@langgenius/dify-ui/button'
import { cn } from '@langgenius/dify-ui/cn'
import {
RiClipboardLine,
@@ -17,7 +18,6 @@ import { useTranslation } from 'react-i18next'
import ActionButton from '@/app/components/base/action-button'
import Badge from '@/app/components/base/badge'
import { FormTypeEnum } from '@/app/components/base/form/types'
-import { Button } from '@/app/components/base/ui/button'
import { useRenderI18nObject } from '@/hooks/use-i18n'
import { openOAuthPopup } from '@/hooks/use-oauth'
import {
diff --git a/web/app/components/plugins/plugin-auth/authorize/api-key-modal.tsx b/web/app/components/plugins/plugin-auth/authorize/api-key-modal.tsx
index 5d8eafbe67..db513ecb6f 100644
--- a/web/app/components/plugins/plugin-auth/authorize/api-key-modal.tsx
+++ b/web/app/components/plugins/plugin-auth/authorize/api-key-modal.tsx
@@ -3,6 +3,7 @@ import type {
FormRefObject,
FormSchema,
} from '@/app/components/base/form/types'
+import { toast } from '@langgenius/dify-ui/toast'
import {
memo,
useCallback,
@@ -16,7 +17,6 @@ import AuthForm from '@/app/components/base/form/form-scenarios/auth'
import { FormTypeEnum } from '@/app/components/base/form/types'
import Loading from '@/app/components/base/loading'
import Modal from '@/app/components/base/modal/modal'
-import { toast } from '@/app/components/base/ui/toast'
import { ReadmeEntrance } from '../../readme-panel/entrance'
import { ReadmeShowType } from '../../readme-panel/store'
import {
diff --git a/web/app/components/plugins/plugin-auth/authorize/oauth-client-settings.tsx b/web/app/components/plugins/plugin-auth/authorize/oauth-client-settings.tsx
index 625fcaa980..f52b76866a 100644
--- a/web/app/components/plugins/plugin-auth/authorize/oauth-client-settings.tsx
+++ b/web/app/components/plugins/plugin-auth/authorize/oauth-client-settings.tsx
@@ -3,6 +3,8 @@ import type {
FormRefObject,
FormSchema,
} from '@/app/components/base/form/types'
+import { Button } from '@langgenius/dify-ui/button'
+import { toast } from '@langgenius/dify-ui/toast'
import {
useForm,
useStore,
@@ -16,8 +18,6 @@ import {
import { useTranslation } from 'react-i18next'
import AuthForm from '@/app/components/base/form/form-scenarios/auth'
import Modal from '@/app/components/base/modal/modal'
-import { Button } from '@/app/components/base/ui/button'
-import { toast } from '@/app/components/base/ui/toast'
import { ReadmeEntrance } from '../../readme-panel/entrance'
import { ReadmeShowType } from '../../readme-panel/store'
import {
diff --git a/web/app/components/plugins/plugin-auth/authorized-in-data-source-node.tsx b/web/app/components/plugins/plugin-auth/authorized-in-data-source-node.tsx
index 548b1fb175..13044d7bbc 100644
--- a/web/app/components/plugins/plugin-auth/authorized-in-data-source-node.tsx
+++ b/web/app/components/plugins/plugin-auth/authorized-in-data-source-node.tsx
@@ -1,10 +1,10 @@
+import { Button } from '@langgenius/dify-ui/button'
import { cn } from '@langgenius/dify-ui/cn'
import { RiEqualizer2Line } from '@remixicon/react'
import {
memo,
} from 'react'
import { useTranslation } from 'react-i18next'
-import { Button } from '@/app/components/base/ui/button'
import Indicator from '@/app/components/header/indicator'
type AuthorizedInDataSourceNodeProps = {
diff --git a/web/app/components/plugins/plugin-auth/authorized-in-node.tsx b/web/app/components/plugins/plugin-auth/authorized-in-node.tsx
index 048bbf3e8f..e4f55e82e3 100644
--- a/web/app/components/plugins/plugin-auth/authorized-in-node.tsx
+++ b/web/app/components/plugins/plugin-auth/authorized-in-node.tsx
@@ -2,6 +2,7 @@ import type {
Credential,
PluginPayload,
} from './types'
+import { Button } from '@langgenius/dify-ui/button'
import { cn } from '@langgenius/dify-ui/cn'
import { RiArrowDownSLine } from '@remixicon/react'
import {
@@ -10,7 +11,6 @@ import {
useState,
} from 'react'
import { useTranslation } from 'react-i18next'
-import { Button } from '@/app/components/base/ui/button'
import Indicator from '@/app/components/header/indicator'
import {
Authorized,
diff --git a/web/app/components/plugins/plugin-auth/authorized/__tests__/index.spec.tsx b/web/app/components/plugins/plugin-auth/authorized/__tests__/index.spec.tsx
index a127067526..01e195b21b 100644
--- a/web/app/components/plugins/plugin-auth/authorized/__tests__/index.spec.tsx
+++ b/web/app/components/plugins/plugin-auth/authorized/__tests__/index.spec.tsx
@@ -57,7 +57,7 @@ const toastMocks = vi.hoisted(() => ({
promise: vi.fn(),
}))
-vi.mock('@/app/components/base/ui/toast', () => ({
+vi.mock('@langgenius/dify-ui/toast', () => ({
toast: Object.assign(toastMocks.call, {
success: vi.fn((message: string, options?: Record) => toastMocks.call({ type: 'success', message, ...options })),
error: vi.fn((message: string, options?: Record) => toastMocks.call({ type: 'error', message, ...options })),
diff --git a/web/app/components/plugins/plugin-auth/authorized/index.tsx b/web/app/components/plugins/plugin-auth/authorized/index.tsx
index 4729909a1f..fed2873b98 100644
--- a/web/app/components/plugins/plugin-auth/authorized/index.tsx
+++ b/web/app/components/plugins/plugin-auth/authorized/index.tsx
@@ -2,7 +2,17 @@ import type { Credential, PluginPayload } from '../types'
import type {
PortalToFollowElemOptions,
} from '@/app/components/base/portal-to-follow-elem'
+import {
+ AlertDialog,
+ AlertDialogActions,
+ AlertDialogCancelButton,
+ AlertDialogConfirmButton,
+ AlertDialogContent,
+ AlertDialogTitle,
+} from '@langgenius/dify-ui/alert-dialog'
+import { Button } from '@langgenius/dify-ui/button'
import { cn } from '@langgenius/dify-ui/cn'
+import { toast } from '@langgenius/dify-ui/toast'
import {
RiArrowDownSLine,
} from '@remixicon/react'
@@ -18,16 +28,6 @@ import {
PortalToFollowElemContent,
PortalToFollowElemTrigger,
} from '@/app/components/base/portal-to-follow-elem'
-import {
- AlertDialog,
- AlertDialogActions,
- AlertDialogCancelButton,
- AlertDialogConfirmButton,
- AlertDialogContent,
- AlertDialogTitle,
-} from '@/app/components/base/ui/alert-dialog'
-import { Button } from '@/app/components/base/ui/button'
-import { toast } from '@/app/components/base/ui/toast'
import Indicator from '@/app/components/header/indicator'
import Authorize from '../authorize'
import ApiKeyModal from '../authorize/api-key-modal'
diff --git a/web/app/components/plugins/plugin-auth/authorized/item.tsx b/web/app/components/plugins/plugin-auth/authorized/item.tsx
index 82e3fc4563..9193238a55 100644
--- a/web/app/components/plugins/plugin-auth/authorized/item.tsx
+++ b/web/app/components/plugins/plugin-auth/authorized/item.tsx
@@ -1,4 +1,5 @@
import type { Credential } from '../types'
+import { Button } from '@langgenius/dify-ui/button'
import { cn } from '@langgenius/dify-ui/cn'
import {
RiCheckLine,
@@ -16,7 +17,6 @@ import ActionButton from '@/app/components/base/action-button'
import Badge from '@/app/components/base/badge'
import Input from '@/app/components/base/input'
import Tooltip from '@/app/components/base/tooltip'
-import { Button } from '@/app/components/base/ui/button'
import Indicator from '@/app/components/header/indicator'
import { CredentialTypeEnum } from '../types'
diff --git a/web/app/components/plugins/plugin-auth/hooks/__tests__/use-plugin-auth-action.spec.ts b/web/app/components/plugins/plugin-auth/hooks/__tests__/use-plugin-auth-action.spec.ts
index ddf8f83019..06af4a88ee 100644
--- a/web/app/components/plugins/plugin-auth/hooks/__tests__/use-plugin-auth-action.spec.ts
+++ b/web/app/components/plugins/plugin-auth/hooks/__tests__/use-plugin-auth-action.spec.ts
@@ -16,7 +16,7 @@ const toastMocks = vi.hoisted(() => ({
promise: vi.fn(),
}))
-vi.mock('@/app/components/base/ui/toast', () => ({
+vi.mock('@langgenius/dify-ui/toast', () => ({
toast: Object.assign(toastMocks.call, {
success: vi.fn((message: string, options?: Record) => toastMocks.call({ type: 'success', message, ...options })),
error: vi.fn((message: string, options?: Record) => toastMocks.call({ type: 'error', message, ...options })),
diff --git a/web/app/components/plugins/plugin-auth/hooks/use-plugin-auth-action.ts b/web/app/components/plugins/plugin-auth/hooks/use-plugin-auth-action.ts
index b4714ff96c..7c63753ee8 100644
--- a/web/app/components/plugins/plugin-auth/hooks/use-plugin-auth-action.ts
+++ b/web/app/components/plugins/plugin-auth/hooks/use-plugin-auth-action.ts
@@ -1,11 +1,11 @@
import type { PluginPayload } from '../types'
+import { toast } from '@langgenius/dify-ui/toast'
import {
useCallback,
useRef,
useState,
} from 'react'
import { useTranslation } from 'react-i18next'
-import { toast } from '@/app/components/base/ui/toast'
import {
useDeletePluginCredentialHook,
useSetPluginDefaultCredentialHook,
diff --git a/web/app/components/plugins/plugin-auth/plugin-auth-in-agent.tsx b/web/app/components/plugins/plugin-auth/plugin-auth-in-agent.tsx
index f54093f0f9..fd698f811c 100644
--- a/web/app/components/plugins/plugin-auth/plugin-auth-in-agent.tsx
+++ b/web/app/components/plugins/plugin-auth/plugin-auth-in-agent.tsx
@@ -2,6 +2,7 @@ import type {
Credential,
PluginPayload,
} from './types'
+import { Button } from '@langgenius/dify-ui/button'
import { cn } from '@langgenius/dify-ui/cn'
import { RiArrowDownSLine } from '@remixicon/react'
import {
@@ -10,7 +11,6 @@ import {
useState,
} from 'react'
import { useTranslation } from 'react-i18next'
-import { Button } from '@/app/components/base/ui/button'
import Indicator from '@/app/components/header/indicator'
import Authorize from './authorize'
import Authorized from './authorized'
diff --git a/web/app/components/plugins/plugin-auth/plugin-auth-in-datasource-node.tsx b/web/app/components/plugins/plugin-auth/plugin-auth-in-datasource-node.tsx
index e3df399a1c..5cdc893ddd 100644
--- a/web/app/components/plugins/plugin-auth/plugin-auth-in-datasource-node.tsx
+++ b/web/app/components/plugins/plugin-auth/plugin-auth-in-datasource-node.tsx
@@ -1,8 +1,8 @@
import type { ReactNode } from 'react'
+import { Button } from '@langgenius/dify-ui/button'
import { RiAddLine } from '@remixicon/react'
import { memo } from 'react'
import { useTranslation } from 'react-i18next'
-import { Button } from '@/app/components/base/ui/button'
type PluginAuthInDataSourceNodeProps = {
children?: ReactNode
diff --git a/web/app/components/plugins/plugin-detail-panel/__tests__/detail-header.spec.tsx b/web/app/components/plugins/plugin-detail-panel/__tests__/detail-header.spec.tsx
index 0eacbf3bd3..63cf25d039 100644
--- a/web/app/components/plugins/plugin-detail-panel/__tests__/detail-header.spec.tsx
+++ b/web/app/components/plugins/plugin-detail-panel/__tests__/detail-header.spec.tsx
@@ -17,7 +17,7 @@ const { mockToast } = vi.hoisted(() => ({
}),
}))
-vi.mock('@/app/components/base/ui/toast', () => ({
+vi.mock('@langgenius/dify-ui/toast', () => ({
toast: mockToast,
}))
diff --git a/web/app/components/plugins/plugin-detail-panel/__tests__/endpoint-card.spec.tsx b/web/app/components/plugins/plugin-detail-panel/__tests__/endpoint-card.spec.tsx
index 15c231cb17..1dab8bdf84 100644
--- a/web/app/components/plugins/plugin-detail-panel/__tests__/endpoint-card.spec.tsx
+++ b/web/app/components/plugins/plugin-detail-panel/__tests__/endpoint-card.spec.tsx
@@ -10,7 +10,7 @@ const mockDeleteEndpoint = vi.fn()
const mockUpdateEndpoint = vi.fn()
const mockToastNotify = vi.fn()
-vi.mock('@/app/components/base/ui/toast', () => ({
+vi.mock('@langgenius/dify-ui/toast', () => ({
toast: Object.assign(
(message: string, options?: { type?: string }) => mockToastNotify({ type: options?.type, message }),
{
diff --git a/web/app/components/plugins/plugin-detail-panel/__tests__/endpoint-modal.spec.tsx b/web/app/components/plugins/plugin-detail-panel/__tests__/endpoint-modal.spec.tsx
index 0be17f07d4..7ccd5065e5 100644
--- a/web/app/components/plugins/plugin-detail-panel/__tests__/endpoint-modal.spec.tsx
+++ b/web/app/components/plugins/plugin-detail-panel/__tests__/endpoint-modal.spec.tsx
@@ -6,7 +6,7 @@ import EndpointModal from '../endpoint-modal'
const mockToastNotify = vi.fn()
-vi.mock('@/app/components/base/ui/toast', () => ({
+vi.mock('@langgenius/dify-ui/toast', () => ({
toast: Object.assign(
(message: string, options?: { type?: string }) => mockToastNotify({ type: options?.type, message }),
{
diff --git a/web/app/components/plugins/plugin-detail-panel/__tests__/operation-dropdown.spec.tsx b/web/app/components/plugins/plugin-detail-panel/__tests__/operation-dropdown.spec.tsx
index fa3d6f2266..db6ff57957 100644
--- a/web/app/components/plugins/plugin-detail-panel/__tests__/operation-dropdown.spec.tsx
+++ b/web/app/components/plugins/plugin-detail-panel/__tests__/operation-dropdown.spec.tsx
@@ -14,7 +14,7 @@ vi.mock('@langgenius/dify-ui/cn', () => ({
cn: (...args: (string | undefined | false | null)[]) => args.filter(Boolean).join(' '),
}))
-vi.mock('@/app/components/base/ui/dropdown-menu', () => ({
+vi.mock('@langgenius/dify-ui/dropdown-menu', () => ({
DropdownMenu: ({ children, open }: { children: ReactNode, open: boolean }) => (
{children}
),
diff --git a/web/app/components/plugins/plugin-detail-panel/datasource-action-list.tsx b/web/app/components/plugins/plugin-detail-panel/datasource-action-list.tsx
index cafdb7b4b9..5bba7b823b 100644
--- a/web/app/components/plugins/plugin-detail-panel/datasource-action-list.tsx
+++ b/web/app/components/plugins/plugin-detail-panel/datasource-action-list.tsx
@@ -1,5 +1,5 @@
// import { useAppContext } from '@/context/app-context'
-// import { Button } from '@/app/components/base/ui/button'
+// import { Button } from '@langgenius/dify-ui/button'
// import Indicator from '@/app/components/header/indicator'
// import ToolItem from '@/app/components/tools/provider/tool-item'
// import ConfigCredential from '@/app/components/tools/setting/build-in/config-credentials'
diff --git a/web/app/components/plugins/plugin-detail-panel/detail-header/__tests__/index.spec.tsx b/web/app/components/plugins/plugin-detail-panel/detail-header/__tests__/index.spec.tsx
index 84a61cd643..24838fea59 100644
--- a/web/app/components/plugins/plugin-detail-panel/detail-header/__tests__/index.spec.tsx
+++ b/web/app/components/plugins/plugin-detail-panel/detail-header/__tests__/index.spec.tsx
@@ -45,7 +45,7 @@ vi.mock('@/app/components/base/action-button', () => ({
),
}))
-vi.mock('@/app/components/base/ui/button', () => ({
+vi.mock('@langgenius/dify-ui/button', () => ({
Button: ({ children, onClick }: { children: React.ReactNode, onClick?: () => void }) => (
),
@@ -57,7 +57,7 @@ vi.mock('@/app/components/base/badge', () => ({
),
}))
-vi.mock('@/app/components/base/ui/tooltip', () => ({
+vi.mock('@langgenius/dify-ui/tooltip', () => ({
Tooltip: ({ children }: { children: React.ReactNode }) => {children}
,
TooltipTrigger: ({ render }: { render: React.ReactNode }) => <>{render}>,
TooltipContent: ({ children }: { children: React.ReactNode }) => {children}
,
diff --git a/web/app/components/plugins/plugin-detail-panel/detail-header/components/header-modals.tsx b/web/app/components/plugins/plugin-detail-panel/detail-header/components/header-modals.tsx
index 3bf24a19b7..c2f6bb8210 100644
--- a/web/app/components/plugins/plugin-detail-panel/detail-header/components/header-modals.tsx
+++ b/web/app/components/plugins/plugin-detail-panel/detail-header/components/header-modals.tsx
@@ -3,7 +3,6 @@
import type { FC } from 'react'
import type { PluginDetail } from '../../../types'
import type { ModalStates, VersionTarget } from '../hooks'
-import { useTranslation } from 'react-i18next'
import {
AlertDialog,
AlertDialogActions,
@@ -12,7 +11,8 @@ import {
AlertDialogContent,
AlertDialogDescription,
AlertDialogTitle,
-} from '@/app/components/base/ui/alert-dialog'
+} from '@langgenius/dify-ui/alert-dialog'
+import { useTranslation } from 'react-i18next'
import PluginInfo from '@/app/components/plugins/plugin-page/plugin-info'
import UpdateFromMarketplace from '@/app/components/plugins/update-plugin/from-market-place'
import { useGetLanguage } from '@/context/i18n'
diff --git a/web/app/components/plugins/plugin-detail-panel/detail-header/hooks/__tests__/use-plugin-operations.spec.ts b/web/app/components/plugins/plugin-detail-panel/detail-header/hooks/__tests__/use-plugin-operations.spec.ts
index b8873f1087..855530bf18 100644
--- a/web/app/components/plugins/plugin-detail-panel/detail-header/hooks/__tests__/use-plugin-operations.spec.ts
+++ b/web/app/components/plugins/plugin-detail-panel/detail-header/hooks/__tests__/use-plugin-operations.spec.ts
@@ -33,7 +33,7 @@ const {
}
})
-vi.mock('@/app/components/base/ui/toast', () => ({
+vi.mock('@langgenius/dify-ui/toast', () => ({
toast: Object.assign(
(message: string, options?: { type?: string }) => mockToastNotify({ type: options?.type, message }),
{
diff --git a/web/app/components/plugins/plugin-detail-panel/detail-header/hooks/use-plugin-operations.ts b/web/app/components/plugins/plugin-detail-panel/detail-header/hooks/use-plugin-operations.ts
index 765c0e8a4e..62c762a35d 100644
--- a/web/app/components/plugins/plugin-detail-panel/detail-header/hooks/use-plugin-operations.ts
+++ b/web/app/components/plugins/plugin-detail-panel/detail-header/hooks/use-plugin-operations.ts
@@ -2,10 +2,10 @@
import type { PluginDetail } from '../../../types'
import type { ModalStates, VersionTarget } from './use-detail-header-state'
+import { toast } from '@langgenius/dify-ui/toast'
import { useCallback } from 'react'
import { useTranslation } from 'react-i18next'
import { trackEvent } from '@/app/components/base/amplitude'
-import { toast } from '@/app/components/base/ui/toast'
import { useModalContext } from '@/context/modal-context'
import { useProviderContext } from '@/context/provider-context'
import { uninstallPlugin } from '@/service/plugins'
diff --git a/web/app/components/plugins/plugin-detail-panel/detail-header/index.tsx b/web/app/components/plugins/plugin-detail-panel/detail-header/index.tsx
index 5a89afa2c9..fb31df59a0 100644
--- a/web/app/components/plugins/plugin-detail-panel/detail-header/index.tsx
+++ b/web/app/components/plugins/plugin-detail-panel/detail-header/index.tsx
@@ -1,13 +1,13 @@
'use client'
import type { PluginDetail } from '../../types'
+import { Button } from '@langgenius/dify-ui/button'
import { cn } from '@langgenius/dify-ui/cn'
+import { Tooltip, TooltipContent, TooltipTrigger } from '@langgenius/dify-ui/tooltip'
import { useMemo } from 'react'
import { useTranslation } from 'react-i18next'
import ActionButton from '@/app/components/base/action-button'
import Badge from '@/app/components/base/badge'
-import { Button } from '@/app/components/base/ui/button'
-import { Tooltip, TooltipContent, TooltipTrigger } from '@/app/components/base/ui/tooltip'
import { AuthCategory, PluginAuth } from '@/app/components/plugins/plugin-auth'
import OperationDropdown from '@/app/components/plugins/plugin-detail-panel/operation-dropdown'
import PluginVersionPicker from '@/app/components/plugins/update-plugin/plugin-version-picker'
@@ -189,7 +189,6 @@ const DetailHeader = ({
{isAutoUpgradeEnabled && !isReadmeView && (
diff --git a/web/app/components/plugins/plugin-detail-panel/endpoint-card.tsx b/web/app/components/plugins/plugin-detail-panel/endpoint-card.tsx
index a8729763ec..e1adc6282d 100644
--- a/web/app/components/plugins/plugin-detail-panel/endpoint-card.tsx
+++ b/web/app/components/plugins/plugin-detail-panel/endpoint-card.tsx
@@ -1,4 +1,14 @@
import type { EndpointListItem, PluginDetail } from '../types'
+import {
+ AlertDialog,
+ AlertDialogActions,
+ AlertDialogCancelButton,
+ AlertDialogConfirmButton,
+ AlertDialogContent,
+ AlertDialogTitle,
+} from '@langgenius/dify-ui/alert-dialog'
+import { Switch } from '@langgenius/dify-ui/switch'
+import { toast } from '@langgenius/dify-ui/toast'
import { RiClipboardLine, RiDeleteBinLine, RiEditLine, RiLoginCircleLine } from '@remixicon/react'
import { useBoolean } from 'ahooks'
import copy from 'copy-to-clipboard'
@@ -7,17 +17,7 @@ import { useEffect, useMemo, useState } from 'react'
import { useTranslation } from 'react-i18next'
import ActionButton from '@/app/components/base/action-button'
import { CopyCheck } from '@/app/components/base/icons/src/vender/line/files'
-import Switch from '@/app/components/base/switch'
import Tooltip from '@/app/components/base/tooltip'
-import {
- AlertDialog,
- AlertDialogActions,
- AlertDialogCancelButton,
- AlertDialogConfirmButton,
- AlertDialogContent,
- AlertDialogTitle,
-} from '@/app/components/base/ui/alert-dialog'
-import { toast } from '@/app/components/base/ui/toast'
import Indicator from '@/app/components/header/indicator'
import { addDefaultValue, toolCredentialToFormSchemas } from '@/app/components/tools/utils/to-form-schema'
import {
diff --git a/web/app/components/plugins/plugin-detail-panel/endpoint-list.tsx b/web/app/components/plugins/plugin-detail-panel/endpoint-list.tsx
index c3c09db786..2d11305f6e 100644
--- a/web/app/components/plugins/plugin-detail-panel/endpoint-list.tsx
+++ b/web/app/components/plugins/plugin-detail-panel/endpoint-list.tsx
@@ -1,5 +1,6 @@
import type { PluginDetail } from '@/app/components/plugins/types'
import { cn } from '@langgenius/dify-ui/cn'
+import { toast } from '@langgenius/dify-ui/toast'
import {
RiAddLine,
RiApps2AddLine,
@@ -11,7 +12,6 @@ import { useMemo } from 'react'
import { useTranslation } from 'react-i18next'
import ActionButton from '@/app/components/base/action-button'
import Tooltip from '@/app/components/base/tooltip'
-import { toast } from '@/app/components/base/ui/toast'
import { toolCredentialToFormSchemas } from '@/app/components/tools/utils/to-form-schema'
import { useDocLink } from '@/context/i18n'
import {
diff --git a/web/app/components/plugins/plugin-detail-panel/endpoint-modal.tsx b/web/app/components/plugins/plugin-detail-panel/endpoint-modal.tsx
index 4c694d4293..0fe0fff6df 100644
--- a/web/app/components/plugins/plugin-detail-panel/endpoint-modal.tsx
+++ b/web/app/components/plugins/plugin-detail-panel/endpoint-modal.tsx
@@ -2,14 +2,14 @@
import type { FC } from 'react'
import type { FormSchema } from '../../base/form/types'
import type { PluginDetail } from '../types'
+import { Button } from '@langgenius/dify-ui/button'
import { cn } from '@langgenius/dify-ui/cn'
+import { toast } from '@langgenius/dify-ui/toast'
import { RiArrowRightUpLine, RiCloseLine } from '@remixicon/react'
import * as React from 'react'
import { useTranslation } from 'react-i18next'
import ActionButton from '@/app/components/base/action-button'
import Drawer from '@/app/components/base/drawer'
-import { Button } from '@/app/components/base/ui/button'
-import { toast } from '@/app/components/base/ui/toast'
import Form from '@/app/components/header/account-setting/model-provider-page/model-modal/Form'
import { useRenderI18nObject } from '@/hooks/use-i18n'
import { ReadmeEntrance } from '../readme-panel/entrance'
diff --git a/web/app/components/plugins/plugin-detail-panel/model-selector/__tests__/index.spec.tsx b/web/app/components/plugins/plugin-detail-panel/model-selector/__tests__/index.spec.tsx
index 107d42ada2..00ed9eb4f1 100644
--- a/web/app/components/plugins/plugin-detail-panel/model-selector/__tests__/index.spec.tsx
+++ b/web/app/components/plugins/plugin-detail-panel/model-selector/__tests__/index.spec.tsx
@@ -9,7 +9,7 @@ import ModelParameterModal from '../index'
// ==================== Mock Setup ====================
const mockToastNotify = vi.fn()
-vi.mock('@/app/components/base/ui/toast', () => ({
+vi.mock('@langgenius/dify-ui/toast', () => ({
toast: Object.assign(
(message: string, options?: { type?: string }) => mockToastNotify({ type: options?.type, message }),
{
diff --git a/web/app/components/plugins/plugin-detail-panel/model-selector/__tests__/tts-params-panel.spec.tsx b/web/app/components/plugins/plugin-detail-panel/model-selector/__tests__/tts-params-panel.spec.tsx
index e423359872..1d000f7c35 100644
--- a/web/app/components/plugins/plugin-detail-panel/model-selector/__tests__/tts-params-panel.spec.tsx
+++ b/web/app/components/plugins/plugin-detail-panel/model-selector/__tests__/tts-params-panel.spec.tsx
@@ -26,8 +26,8 @@ const MockSelectContext = React.createContext<{
onValueChange: () => {},
})
-vi.mock('@/app/components/base/ui/select', async (importOriginal) => {
- const actual = await importOriginal()
+vi.mock('@langgenius/dify-ui/select', async (importOriginal) => {
+ const actual = await importOriginal()
return {
...actual,
diff --git a/web/app/components/plugins/plugin-detail-panel/model-selector/index.tsx b/web/app/components/plugins/plugin-detail-panel/model-selector/index.tsx
index 5f5c0494a9..d0dcab18ea 100644
--- a/web/app/components/plugins/plugin-detail-panel/model-selector/index.tsx
+++ b/web/app/components/plugins/plugin-detail-panel/model-selector/index.tsx
@@ -9,14 +9,14 @@ import type {
} from '@/app/components/header/account-setting/model-provider-page/declarations'
import type { TriggerProps } from '@/app/components/header/account-setting/model-provider-page/model-parameter-modal/trigger'
import { cn } from '@langgenius/dify-ui/cn'
-import { useMemo, useState } from 'react'
-import { useTranslation } from 'react-i18next'
import {
Popover,
PopoverContent,
PopoverTrigger,
-} from '@/app/components/base/ui/popover'
-import { toast } from '@/app/components/base/ui/toast'
+} from '@langgenius/dify-ui/popover'
+import { toast } from '@langgenius/dify-ui/toast'
+import { useMemo, useState } from 'react'
+import { useTranslation } from 'react-i18next'
import { ModelStatusEnum, ModelTypeEnum } from '@/app/components/header/account-setting/model-provider-page/declarations'
import {
useModelList,
diff --git a/web/app/components/plugins/plugin-detail-panel/model-selector/tts-params-panel.tsx b/web/app/components/plugins/plugin-detail-panel/model-selector/tts-params-panel.tsx
index 1ab7ea2f11..7edf2f5d18 100644
--- a/web/app/components/plugins/plugin-detail-panel/model-selector/tts-params-panel.tsx
+++ b/web/app/components/plugins/plugin-detail-panel/model-selector/tts-params-panel.tsx
@@ -1,7 +1,7 @@
+import { Select, SelectContent, SelectItem, SelectItemIndicator, SelectItemText, SelectTrigger, SelectValue } from '@langgenius/dify-ui/select'
import * as React from 'react'
import { useMemo } from 'react'
import { useTranslation } from 'react-i18next'
-import { Select, SelectContent, SelectItem, SelectItemIndicator, SelectItemText, SelectTrigger, SelectValue } from '@/app/components/base/ui/select'
import { languages } from '@/i18n-config/language'
type Props = {
diff --git a/web/app/components/plugins/plugin-detail-panel/operation-dropdown.tsx b/web/app/components/plugins/plugin-detail-panel/operation-dropdown.tsx
index 4b36e4612c..c7a1529a3e 100644
--- a/web/app/components/plugins/plugin-detail-panel/operation-dropdown.tsx
+++ b/web/app/components/plugins/plugin-detail-panel/operation-dropdown.tsx
@@ -1,16 +1,16 @@
'use client'
+import type { Placement } from '@langgenius/dify-ui/dropdown-menu'
import type { FC } from 'react'
-import type { Placement } from '@/app/components/base/ui/placement'
import { cn } from '@langgenius/dify-ui/cn'
-import * as React from 'react'
-import { useTranslation } from 'react-i18next'
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuSeparator,
DropdownMenuTrigger,
-} from '@/app/components/base/ui/dropdown-menu'
+} from '@langgenius/dify-ui/dropdown-menu'
+import * as React from 'react'
+import { useTranslation } from 'react-i18next'
import { useGlobalPublicStore } from '@/context/global-public-context'
import { PluginSource } from '../types'
diff --git a/web/app/components/plugins/plugin-detail-panel/subscription-list/__tests__/delete-confirm.spec.tsx b/web/app/components/plugins/plugin-detail-panel/subscription-list/__tests__/delete-confirm.spec.tsx
index f083c24f42..c2cc608b45 100644
--- a/web/app/components/plugins/plugin-detail-panel/subscription-list/__tests__/delete-confirm.spec.tsx
+++ b/web/app/components/plugins/plugin-detail-panel/subscription-list/__tests__/delete-confirm.spec.tsx
@@ -15,8 +15,8 @@ vi.mock('@/service/use-triggers', () => ({
useDeleteTriggerSubscription: () => ({ mutate: mockDelete, isPending: false }),
}))
-vi.mock('@/app/components/base/ui/toast', async (importOriginal) => {
- const actual = await importOriginal()
+vi.mock('@langgenius/dify-ui/toast', async (importOriginal) => {
+ const actual = await importOriginal()
return {
...actual,
toast: {
diff --git a/web/app/components/plugins/plugin-detail-panel/subscription-list/__tests__/log-viewer.spec.tsx b/web/app/components/plugins/plugin-detail-panel/subscription-list/__tests__/log-viewer.spec.tsx
index 351c1f9d2d..3594c10ce2 100644
--- a/web/app/components/plugins/plugin-detail-panel/subscription-list/__tests__/log-viewer.spec.tsx
+++ b/web/app/components/plugins/plugin-detail-panel/subscription-list/__tests__/log-viewer.spec.tsx
@@ -6,7 +6,7 @@ import LogViewer from '../log-viewer'
const mockToastNotify = vi.fn()
const mockWriteText = vi.fn()
-vi.mock('@/app/components/base/ui/toast', () => ({
+vi.mock('@langgenius/dify-ui/toast', () => ({
toast: Object.assign(
(message: string, options?: { type?: string }) => mockToastNotify({ type: options?.type, message }),
{
diff --git a/web/app/components/plugins/plugin-detail-panel/subscription-list/__tests__/selector-entry.spec.tsx b/web/app/components/plugins/plugin-detail-panel/subscription-list/__tests__/selector-entry.spec.tsx
index 3c4ff83fc8..37d828591f 100644
--- a/web/app/components/plugins/plugin-detail-panel/subscription-list/__tests__/selector-entry.spec.tsx
+++ b/web/app/components/plugins/plugin-detail-panel/subscription-list/__tests__/selector-entry.spec.tsx
@@ -26,7 +26,7 @@ vi.mock('@/service/use-triggers', () => ({
useDeleteTriggerSubscription: () => ({ mutate: vi.fn(), isPending: false }),
}))
-vi.mock('@/app/components/base/ui/toast', () => ({
+vi.mock('@langgenius/dify-ui/toast', () => ({
toast: Object.assign(vi.fn(), {
success: vi.fn(),
error: vi.fn(),
diff --git a/web/app/components/plugins/plugin-detail-panel/subscription-list/__tests__/selector-view.spec.tsx b/web/app/components/plugins/plugin-detail-panel/subscription-list/__tests__/selector-view.spec.tsx
index 44cec53e28..2256e90e9d 100644
--- a/web/app/components/plugins/plugin-detail-panel/subscription-list/__tests__/selector-view.spec.tsx
+++ b/web/app/components/plugins/plugin-detail-panel/subscription-list/__tests__/selector-view.spec.tsx
@@ -25,7 +25,7 @@ vi.mock('@/service/use-triggers', () => ({
useDeleteTriggerSubscription: () => ({ mutate: mockDelete, isPending: false }),
}))
-vi.mock('@/app/components/base/ui/toast', () => ({
+vi.mock('@langgenius/dify-ui/toast', () => ({
toast: Object.assign(vi.fn(), {
success: vi.fn(),
error: vi.fn(),
diff --git a/web/app/components/plugins/plugin-detail-panel/subscription-list/__tests__/subscription-card.spec.tsx b/web/app/components/plugins/plugin-detail-panel/subscription-list/__tests__/subscription-card.spec.tsx
index 19edb65dfb..9a89aab7cf 100644
--- a/web/app/components/plugins/plugin-detail-panel/subscription-list/__tests__/subscription-card.spec.tsx
+++ b/web/app/components/plugins/plugin-detail-panel/subscription-list/__tests__/subscription-card.spec.tsx
@@ -29,7 +29,7 @@ vi.mock('@/service/use-triggers', () => ({
useDeleteTriggerSubscription: () => ({ mutate: vi.fn(), isPending: false }),
}))
-vi.mock('@/app/components/base/ui/toast', () => ({
+vi.mock('@langgenius/dify-ui/toast', () => ({
toast: Object.assign(vi.fn(), {
success: vi.fn(),
error: vi.fn(),
diff --git a/web/app/components/plugins/plugin-detail-panel/subscription-list/create/__tests__/common-modal.spec.tsx b/web/app/components/plugins/plugin-detail-panel/subscription-list/create/__tests__/common-modal.spec.tsx
index 72532ea38d..459b657f3f 100644
--- a/web/app/components/plugins/plugin-detail-panel/subscription-list/create/__tests__/common-modal.spec.tsx
+++ b/web/app/components/plugins/plugin-detail-panel/subscription-list/create/__tests__/common-modal.spec.tsx
@@ -122,7 +122,7 @@ vi.mock('@/utils/urlValidation', () => ({
}))
const mockToastNotify = vi.fn()
-vi.mock('@/app/components/base/ui/toast', () => ({
+vi.mock('@langgenius/dify-ui/toast', () => ({
toast: Object.assign((params: unknown) => mockToastNotify(params), {
success: (message: unknown) => mockToastNotify({ type: 'success', message }),
error: (message: unknown) => mockToastNotify({ type: 'error', message }),
diff --git a/web/app/components/plugins/plugin-detail-panel/subscription-list/create/__tests__/index.spec.tsx b/web/app/components/plugins/plugin-detail-panel/subscription-list/create/__tests__/index.spec.tsx
index 0714fbf554..e1615e7b0a 100644
--- a/web/app/components/plugins/plugin-detail-panel/subscription-list/create/__tests__/index.spec.tsx
+++ b/web/app/components/plugins/plugin-detail-panel/subscription-list/create/__tests__/index.spec.tsx
@@ -1,8 +1,8 @@
import type { SimpleDetail } from '../../../store'
import type { TriggerOAuthConfig, TriggerProviderApiEntity, TriggerSubscription, TriggerSubscriptionBuilder } from '@/app/components/workflow/block-selector/types'
+import { toast } from '@langgenius/dify-ui/toast'
import { fireEvent, render, screen, waitFor } from '@testing-library/react'
import { beforeEach, describe, expect, it, vi } from 'vitest'
-import { toast } from '@/app/components/base/ui/toast'
import { SupportedCreationMethods } from '@/app/components/plugins/types'
import { TriggerCredentialTypeEnum } from '@/app/components/workflow/block-selector/types'
import { CreateButtonType, CreateSubscriptionButton, DEFAULT_METHOD } from '../index'
@@ -34,7 +34,7 @@ vi.mock('@/app/components/base/portal-to-follow-elem', () => ({
},
}))
-vi.mock('@/app/components/base/ui/toast', () => ({
+vi.mock('@langgenius/dify-ui/toast', () => ({
toast: Object.assign(vi.fn(), {
success: vi.fn(),
error: vi.fn(),
diff --git a/web/app/components/plugins/plugin-detail-panel/subscription-list/create/__tests__/oauth-client.spec.tsx b/web/app/components/plugins/plugin-detail-panel/subscription-list/create/__tests__/oauth-client.spec.tsx
index 5c4407b3c5..46b9499027 100644
--- a/web/app/components/plugins/plugin-detail-panel/subscription-list/create/__tests__/oauth-client.spec.tsx
+++ b/web/app/components/plugins/plugin-detail-panel/subscription-list/create/__tests__/oauth-client.spec.tsx
@@ -86,7 +86,7 @@ vi.mock('@/hooks/use-oauth', () => ({
}))
const mockToastNotify = vi.fn()
-vi.mock('@/app/components/base/ui/toast', () => ({
+vi.mock('@langgenius/dify-ui/toast', () => ({
toast: Object.assign(
(message: string, options?: { type?: string }) => mockToastNotify({ type: options?.type, message }),
{
diff --git a/web/app/components/plugins/plugin-detail-panel/subscription-list/create/hooks/__tests__/use-common-modal-state.helpers.spec.ts b/web/app/components/plugins/plugin-detail-panel/subscription-list/create/hooks/__tests__/use-common-modal-state.helpers.spec.ts
index 61482e2912..87db85b56e 100644
--- a/web/app/components/plugins/plugin-detail-panel/subscription-list/create/hooks/__tests__/use-common-modal-state.helpers.spec.ts
+++ b/web/app/components/plugins/plugin-detail-panel/subscription-list/create/hooks/__tests__/use-common-modal-state.helpers.spec.ts
@@ -27,7 +27,7 @@ const {
mockIsPrivateOrLocalAddress: vi.fn(),
}))
-vi.mock('@/app/components/base/ui/toast', () => ({
+vi.mock('@langgenius/dify-ui/toast', () => ({
toast: {
error: mockToastError,
},
diff --git a/web/app/components/plugins/plugin-detail-panel/subscription-list/create/hooks/__tests__/use-common-modal-state.spec.ts b/web/app/components/plugins/plugin-detail-panel/subscription-list/create/hooks/__tests__/use-common-modal-state.spec.ts
index 399d3ba60c..26e4a69baa 100644
--- a/web/app/components/plugins/plugin-detail-panel/subscription-list/create/hooks/__tests__/use-common-modal-state.spec.ts
+++ b/web/app/components/plugins/plugin-detail-panel/subscription-list/create/hooks/__tests__/use-common-modal-state.spec.ts
@@ -87,7 +87,7 @@ vi.mock('@/service/use-triggers', () => ({
}))
const mockToastNotify = vi.fn()
-vi.mock('@/app/components/base/ui/toast', () => ({
+vi.mock('@langgenius/dify-ui/toast', () => ({
toast: {
success: (message: string) => mockToastNotify({ type: 'success', message }),
error: (message: string) => mockToastNotify({ type: 'error', message }),
diff --git a/web/app/components/plugins/plugin-detail-panel/subscription-list/create/hooks/__tests__/use-oauth-client-state.spec.ts b/web/app/components/plugins/plugin-detail-panel/subscription-list/create/hooks/__tests__/use-oauth-client-state.spec.ts
index cebfc947e7..82eddf501d 100644
--- a/web/app/components/plugins/plugin-detail-panel/subscription-list/create/hooks/__tests__/use-oauth-client-state.spec.ts
+++ b/web/app/components/plugins/plugin-detail-panel/subscription-list/create/hooks/__tests__/use-oauth-client-state.spec.ts
@@ -77,7 +77,7 @@ vi.mock('@/hooks/use-oauth', () => ({
}))
const mockToastNotify = vi.fn()
-vi.mock('@/app/components/base/ui/toast', () => ({
+vi.mock('@langgenius/dify-ui/toast', () => ({
toast: Object.assign(
(message: string, options?: { type?: string }) => mockToastNotify({ type: options?.type, message }),
{
diff --git a/web/app/components/plugins/plugin-detail-panel/subscription-list/create/hooks/use-common-modal-state.helpers.ts b/web/app/components/plugins/plugin-detail-panel/subscription-list/create/hooks/use-common-modal-state.helpers.ts
index 8df864c4fa..2754bbf39d 100644
--- a/web/app/components/plugins/plugin-detail-panel/subscription-list/create/hooks/use-common-modal-state.helpers.ts
+++ b/web/app/components/plugins/plugin-detail-panel/subscription-list/create/hooks/use-common-modal-state.helpers.ts
@@ -3,8 +3,8 @@ import type { Dispatch, SetStateAction } from 'react'
import type { FormRefObject } from '@/app/components/base/form/types'
import type { TriggerSubscriptionBuilder } from '@/app/components/workflow/block-selector/types'
import type { BuildTriggerSubscriptionPayload } from '@/service/use-triggers'
+import { toast } from '@langgenius/dify-ui/toast'
import { useEffect, useRef } from 'react'
-import { toast } from '@/app/components/base/ui/toast'
import { SupportedCreationMethods } from '@/app/components/plugins/types'
import { isPrivateOrLocalAddress } from '@/utils/urlValidation'
diff --git a/web/app/components/plugins/plugin-detail-panel/subscription-list/create/hooks/use-common-modal-state.ts b/web/app/components/plugins/plugin-detail-panel/subscription-list/create/hooks/use-common-modal-state.ts
index 29613c6f4f..0f40da2d58 100644
--- a/web/app/components/plugins/plugin-detail-panel/subscription-list/create/hooks/use-common-modal-state.ts
+++ b/web/app/components/plugins/plugin-detail-panel/subscription-list/create/hooks/use-common-modal-state.ts
@@ -3,10 +3,10 @@ import type { SimpleDetail } from '../../../store'
import type { SchemaItem } from '../components/modal-steps'
import type { FormRefObject } from '@/app/components/base/form/types'
import type { TriggerLogEntity, TriggerSubscriptionBuilder } from '@/app/components/workflow/block-selector/types'
+import { toast } from '@langgenius/dify-ui/toast'
import { debounce } from 'es-toolkit/compat'
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
import { useTranslation } from 'react-i18next'
-import { toast } from '@/app/components/base/ui/toast'
import { SupportedCreationMethods } from '@/app/components/plugins/types'
import { TriggerCredentialTypeEnum } from '@/app/components/workflow/block-selector/types'
import {
diff --git a/web/app/components/plugins/plugin-detail-panel/subscription-list/create/hooks/use-oauth-client-state.ts b/web/app/components/plugins/plugin-detail-panel/subscription-list/create/hooks/use-oauth-client-state.ts
index e5a5ded9df..25058e529c 100644
--- a/web/app/components/plugins/plugin-detail-panel/subscription-list/create/hooks/use-oauth-client-state.ts
+++ b/web/app/components/plugins/plugin-detail-panel/subscription-list/create/hooks/use-oauth-client-state.ts
@@ -2,9 +2,9 @@
import type { FormRefObject } from '@/app/components/base/form/types'
import type { TriggerOAuthClientParams, TriggerOAuthConfig, TriggerSubscriptionBuilder } from '@/app/components/workflow/block-selector/types'
import type { ConfigureTriggerOAuthPayload } from '@/service/use-triggers'
+import { toast } from '@langgenius/dify-ui/toast'
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
import { useTranslation } from 'react-i18next'
-import { toast } from '@/app/components/base/ui/toast'
import { openOAuthPopup } from '@/hooks/use-oauth'
import {
useConfigureTriggerOAuth,
diff --git a/web/app/components/plugins/plugin-detail-panel/subscription-list/create/index.tsx b/web/app/components/plugins/plugin-detail-panel/subscription-list/create/index.tsx
index 4861f30934..b007c89cb9 100644
--- a/web/app/components/plugins/plugin-detail-panel/subscription-list/create/index.tsx
+++ b/web/app/components/plugins/plugin-detail-panel/subscription-list/create/index.tsx
@@ -1,6 +1,8 @@
import type { Option } from '@/app/components/base/select/custom'
import type { TriggerSubscriptionBuilder } from '@/app/components/workflow/block-selector/types'
+import { Button } from '@langgenius/dify-ui/button'
import { cn } from '@langgenius/dify-ui/cn'
+import { toast } from '@langgenius/dify-ui/toast'
import { RiAddLine, RiEqualizer2Line } from '@remixicon/react'
import { useBoolean } from 'ahooks'
import { useCallback, useMemo, useState } from 'react'
@@ -9,8 +11,6 @@ import { ActionButton, ActionButtonState } from '@/app/components/base/action-bu
import Badge from '@/app/components/base/badge'
import CustomSelect from '@/app/components/base/select/custom'
import Tooltip from '@/app/components/base/tooltip'
-import { Button } from '@/app/components/base/ui/button'
-import { toast } from '@/app/components/base/ui/toast'
import { openOAuthPopup } from '@/hooks/use-oauth'
import { useInitiateTriggerOAuth, useTriggerOAuthConfig, useTriggerProviderInfo } from '@/service/use-triggers'
import { SupportedCreationMethods } from '../../../types'
diff --git a/web/app/components/plugins/plugin-detail-panel/subscription-list/create/oauth-client.tsx b/web/app/components/plugins/plugin-detail-panel/subscription-list/create/oauth-client.tsx
index c4ae63ab66..450324ae40 100644
--- a/web/app/components/plugins/plugin-detail-panel/subscription-list/create/oauth-client.tsx
+++ b/web/app/components/plugins/plugin-detail-panel/subscription-list/create/oauth-client.tsx
@@ -1,5 +1,7 @@
'use client'
import type { TriggerOAuthConfig, TriggerSubscriptionBuilder } from '@/app/components/workflow/block-selector/types'
+import { Button } from '@langgenius/dify-ui/button'
+import { toast } from '@langgenius/dify-ui/toast'
import {
RiClipboardLine,
RiInformation2Fill,
@@ -7,8 +9,6 @@ import {
import { useTranslation } from 'react-i18next'
import { BaseForm } from '@/app/components/base/form/components/base'
import Modal from '@/app/components/base/modal/modal'
-import { Button } from '@/app/components/base/ui/button'
-import { toast } from '@/app/components/base/ui/toast'
import OptionCard from '@/app/components/workflow/nodes/_base/components/option-card'
import { usePluginStore } from '../../store'
import { ClientTypeEnum, useOAuthClientState } from './hooks/use-oauth-client-state'
diff --git a/web/app/components/plugins/plugin-detail-panel/subscription-list/delete-confirm.tsx b/web/app/components/plugins/plugin-detail-panel/subscription-list/delete-confirm.tsx
index a360a477e3..3599dc6260 100644
--- a/web/app/components/plugins/plugin-detail-panel/subscription-list/delete-confirm.tsx
+++ b/web/app/components/plugins/plugin-detail-panel/subscription-list/delete-confirm.tsx
@@ -1,6 +1,3 @@
-import { useState } from 'react'
-import { useTranslation } from 'react-i18next'
-import Input from '@/app/components/base/input'
import {
AlertDialog,
AlertDialogActions,
@@ -9,8 +6,11 @@ import {
AlertDialogContent,
AlertDialogDescription,
AlertDialogTitle,
-} from '@/app/components/base/ui/alert-dialog'
-import { toast } from '@/app/components/base/ui/toast'
+} from '@langgenius/dify-ui/alert-dialog'
+import { toast } from '@langgenius/dify-ui/toast'
+import { useState } from 'react'
+import { useTranslation } from 'react-i18next'
+import Input from '@/app/components/base/input'
import { useDeleteTriggerSubscription } from '@/service/use-triggers'
import { useSubscriptionList } from './use-subscription-list'
diff --git a/web/app/components/plugins/plugin-detail-panel/subscription-list/edit/__tests__/apikey-edit-modal.spec.tsx b/web/app/components/plugins/plugin-detail-panel/subscription-list/edit/__tests__/apikey-edit-modal.spec.tsx
index b33f0af8e9..6e192e025c 100644
--- a/web/app/components/plugins/plugin-detail-panel/subscription-list/edit/__tests__/apikey-edit-modal.spec.tsx
+++ b/web/app/components/plugins/plugin-detail-panel/subscription-list/edit/__tests__/apikey-edit-modal.spec.tsx
@@ -47,7 +47,7 @@ vi.mock('@/service/use-triggers', () => ({
useTriggerPluginDynamicOptions: () => ({ data: [], isLoading: false }),
}))
-vi.mock('@/app/components/base/ui/toast', () => ({
+vi.mock('@langgenius/dify-ui/toast', () => ({
toast: Object.assign((args: { type: string, message: string }) => mockToast(args), {
success: (message: string) => mockToast({ type: 'success', message }),
error: (message: string) => mockToast({ type: 'error', message }),
diff --git a/web/app/components/plugins/plugin-detail-panel/subscription-list/edit/__tests__/index.spec.tsx b/web/app/components/plugins/plugin-detail-panel/subscription-list/edit/__tests__/index.spec.tsx
index 126d8e366d..6cf58b8972 100644
--- a/web/app/components/plugins/plugin-detail-panel/subscription-list/edit/__tests__/index.spec.tsx
+++ b/web/app/components/plugins/plugin-detail-panel/subscription-list/edit/__tests__/index.spec.tsx
@@ -13,7 +13,7 @@ import { OAuthEditModal } from '../oauth-edit-modal'
// ==================== Mock Setup ====================
const mockToastNotify = vi.fn()
-vi.mock('@/app/components/base/ui/toast', () => ({
+vi.mock('@langgenius/dify-ui/toast', () => ({
toast: Object.assign((message: string, options?: { type?: string }) => mockToastNotify({ type: options?.type, message }), {
success: (message: string) => mockToastNotify({ type: 'success', message }),
error: (message: string) => mockToastNotify({ type: 'error', message }),
diff --git a/web/app/components/plugins/plugin-detail-panel/subscription-list/edit/__tests__/manual-edit-modal.spec.tsx b/web/app/components/plugins/plugin-detail-panel/subscription-list/edit/__tests__/manual-edit-modal.spec.tsx
index 4fa236783b..e5fa43268a 100644
--- a/web/app/components/plugins/plugin-detail-panel/subscription-list/edit/__tests__/manual-edit-modal.spec.tsx
+++ b/web/app/components/plugins/plugin-detail-panel/subscription-list/edit/__tests__/manual-edit-modal.spec.tsx
@@ -30,7 +30,7 @@ vi.mock('@/service/use-triggers', () => ({
useTriggerPluginDynamicOptions: () => ({ data: [], isLoading: false }),
}))
-vi.mock('@/app/components/base/ui/toast', () => ({
+vi.mock('@langgenius/dify-ui/toast', () => ({
toast: Object.assign((args: { type: string, message: string }) => mockToast(args), {
success: (message: string) => mockToast({ type: 'success', message }),
error: (message: string) => mockToast({ type: 'error', message }),
diff --git a/web/app/components/plugins/plugin-detail-panel/subscription-list/edit/__tests__/oauth-edit-modal.spec.tsx b/web/app/components/plugins/plugin-detail-panel/subscription-list/edit/__tests__/oauth-edit-modal.spec.tsx
index 1927ae5a43..c860cd2818 100644
--- a/web/app/components/plugins/plugin-detail-panel/subscription-list/edit/__tests__/oauth-edit-modal.spec.tsx
+++ b/web/app/components/plugins/plugin-detail-panel/subscription-list/edit/__tests__/oauth-edit-modal.spec.tsx
@@ -30,7 +30,7 @@ vi.mock('@/service/use-triggers', () => ({
useTriggerPluginDynamicOptions: () => ({ data: [], isLoading: false }),
}))
-vi.mock('@/app/components/base/ui/toast', () => ({
+vi.mock('@langgenius/dify-ui/toast', () => ({
toast: Object.assign((args: { type: string, message: string }) => mockToast(args), {
success: (message: string) => mockToast({ type: 'success', message }),
error: (message: string) => mockToast({ type: 'error', message }),
diff --git a/web/app/components/plugins/plugin-detail-panel/subscription-list/edit/apikey-edit-modal.tsx b/web/app/components/plugins/plugin-detail-panel/subscription-list/edit/apikey-edit-modal.tsx
index eea9cb7ff0..f191ad41a8 100644
--- a/web/app/components/plugins/plugin-detail-panel/subscription-list/edit/apikey-edit-modal.tsx
+++ b/web/app/components/plugins/plugin-detail-panel/subscription-list/edit/apikey-edit-modal.tsx
@@ -2,6 +2,7 @@
import type { FormRefObject, FormSchema } from '@/app/components/base/form/types'
import type { ParametersSchema, PluginDetail } from '@/app/components/plugins/types'
import type { TriggerSubscription } from '@/app/components/workflow/block-selector/types'
+import { toast } from '@langgenius/dify-ui/toast'
import { isEqual } from 'es-toolkit/predicate'
import { useMemo, useRef, useState } from 'react'
import { useTranslation } from 'react-i18next'
@@ -9,7 +10,6 @@ import { EncryptedBottom } from '@/app/components/base/encrypted-bottom'
import { BaseForm } from '@/app/components/base/form/components/base'
import { FormTypeEnum } from '@/app/components/base/form/types'
import Modal from '@/app/components/base/modal/modal'
-import { toast } from '@/app/components/base/ui/toast'
import { ReadmeEntrance } from '@/app/components/plugins/readme-panel/entrance'
import { useUpdateTriggerSubscription, useVerifyTriggerSubscription } from '@/service/use-triggers'
import { parsePluginErrorMessage } from '@/utils/error-parser'
diff --git a/web/app/components/plugins/plugin-detail-panel/subscription-list/edit/manual-edit-modal.tsx b/web/app/components/plugins/plugin-detail-panel/subscription-list/edit/manual-edit-modal.tsx
index 333a800ffd..3b3fa1082f 100644
--- a/web/app/components/plugins/plugin-detail-panel/subscription-list/edit/manual-edit-modal.tsx
+++ b/web/app/components/plugins/plugin-detail-panel/subscription-list/edit/manual-edit-modal.tsx
@@ -2,13 +2,13 @@
import type { FormRefObject, FormSchema } from '@/app/components/base/form/types'
import type { ParametersSchema, PluginDetail } from '@/app/components/plugins/types'
import type { TriggerSubscription } from '@/app/components/workflow/block-selector/types'
+import { toast } from '@langgenius/dify-ui/toast'
import { isEqual } from 'es-toolkit/predicate'
import { useMemo, useRef } from 'react'
import { useTranslation } from 'react-i18next'
import { BaseForm } from '@/app/components/base/form/components/base'
import { FormTypeEnum } from '@/app/components/base/form/types'
import Modal from '@/app/components/base/modal/modal'
-import { toast } from '@/app/components/base/ui/toast'
import { ReadmeEntrance } from '@/app/components/plugins/readme-panel/entrance'
import { useUpdateTriggerSubscription } from '@/service/use-triggers'
import { ReadmeShowType } from '../../../readme-panel/store'
diff --git a/web/app/components/plugins/plugin-detail-panel/subscription-list/edit/oauth-edit-modal.tsx b/web/app/components/plugins/plugin-detail-panel/subscription-list/edit/oauth-edit-modal.tsx
index 3937d82a0e..355a132bd2 100644
--- a/web/app/components/plugins/plugin-detail-panel/subscription-list/edit/oauth-edit-modal.tsx
+++ b/web/app/components/plugins/plugin-detail-panel/subscription-list/edit/oauth-edit-modal.tsx
@@ -2,13 +2,13 @@
import type { FormRefObject, FormSchema } from '@/app/components/base/form/types'
import type { ParametersSchema, PluginDetail } from '@/app/components/plugins/types'
import type { TriggerSubscription } from '@/app/components/workflow/block-selector/types'
+import { toast } from '@langgenius/dify-ui/toast'
import { isEqual } from 'es-toolkit/predicate'
import { useMemo, useRef } from 'react'
import { useTranslation } from 'react-i18next'
import { BaseForm } from '@/app/components/base/form/components/base'
import { FormTypeEnum } from '@/app/components/base/form/types'
import Modal from '@/app/components/base/modal/modal'
-import { toast } from '@/app/components/base/ui/toast'
import { ReadmeEntrance } from '@/app/components/plugins/readme-panel/entrance'
import { useUpdateTriggerSubscription } from '@/service/use-triggers'
import { ReadmeShowType } from '../../../readme-panel/store'
diff --git a/web/app/components/plugins/plugin-detail-panel/subscription-list/log-viewer.tsx b/web/app/components/plugins/plugin-detail-panel/subscription-list/log-viewer.tsx
index 1960103822..b0b3efe853 100644
--- a/web/app/components/plugins/plugin-detail-panel/subscription-list/log-viewer.tsx
+++ b/web/app/components/plugins/plugin-detail-panel/subscription-list/log-viewer.tsx
@@ -1,6 +1,7 @@
'use client'
import type { TriggerLogEntity } from '@/app/components/workflow/block-selector/types'
import { cn } from '@langgenius/dify-ui/cn'
+import { toast } from '@langgenius/dify-ui/toast'
import {
RiArrowDownSLine,
RiArrowRightSLine,
@@ -12,7 +13,6 @@ import dayjs from 'dayjs'
import * as React from 'react'
import { useState } from 'react'
import { useTranslation } from 'react-i18next'
-import { toast } from '@/app/components/base/ui/toast'
import CodeEditor from '@/app/components/workflow/nodes/_base/components/editor/code-editor'
import { CodeLanguage } from '@/app/components/workflow/nodes/code/types'
diff --git a/web/app/components/plugins/plugin-detail-panel/tool-selector/__tests__/index.spec.tsx b/web/app/components/plugins/plugin-detail-panel/tool-selector/__tests__/index.spec.tsx
index 537e99d733..168e4f1eba 100644
--- a/web/app/components/plugins/plugin-detail-panel/tool-selector/__tests__/index.spec.tsx
+++ b/web/app/components/plugins/plugin-detail-panel/tool-selector/__tests__/index.spec.tsx
@@ -298,7 +298,7 @@ vi.mock('@/app/components/header/account-setting/model-provider-page/model-modal
// Mock Toast - need to track notify calls for assertions
const mockToastNotify = vi.fn()
-vi.mock('@/app/components/base/ui/toast', () => ({
+vi.mock('@langgenius/dify-ui/toast', () => ({
toast: Object.assign((message: string, options?: { type?: string }) => mockToastNotify({ type: options?.type, message }), {
success: (message: string) => mockToastNotify({ type: 'success', message }),
error: (message: string) => mockToastNotify({ type: 'error', message }),
diff --git a/web/app/components/plugins/plugin-detail-panel/tool-selector/components/__tests__/reasoning-config-form.spec.tsx b/web/app/components/plugins/plugin-detail-panel/tool-selector/components/__tests__/reasoning-config-form.spec.tsx
index b4bfd22405..beab35595c 100644
--- a/web/app/components/plugins/plugin-detail-panel/tool-selector/components/__tests__/reasoning-config-form.spec.tsx
+++ b/web/app/components/plugins/plugin-detail-panel/tool-selector/components/__tests__/reasoning-config-form.spec.tsx
@@ -30,8 +30,8 @@ vi.mock('@/app/components/base/select', () => ({
),
}))
-vi.mock('@/app/components/base/switch', () => ({
- default: ({ checked, onCheckedChange }: { checked: boolean, onCheckedChange: (checked: boolean) => void }) => (
+vi.mock('@langgenius/dify-ui/switch', () => ({
+ Switch: ({ checked, onCheckedChange }: { checked: boolean, onCheckedChange: (checked: boolean) => void }) => (
diff --git a/web/app/components/plugins/plugin-detail-panel/tool-selector/components/__tests__/tool-credentials-form.spec.tsx b/web/app/components/plugins/plugin-detail-panel/tool-selector/components/__tests__/tool-credentials-form.spec.tsx
index bd2bc0dd5c..2d50a2d2bf 100644
--- a/web/app/components/plugins/plugin-detail-panel/tool-selector/components/__tests__/tool-credentials-form.spec.tsx
+++ b/web/app/components/plugins/plugin-detail-panel/tool-selector/components/__tests__/tool-credentials-form.spec.tsx
@@ -10,7 +10,7 @@ vi.mock('@langgenius/dify-ui/cn', () => ({
cn: (...args: unknown[]) => args.filter(Boolean).join(' '),
}))
-vi.mock('@/app/components/base/ui/toast', () => ({
+vi.mock('@langgenius/dify-ui/toast', () => ({
toast: Object.assign(vi.fn(), {
success: vi.fn(),
error: vi.fn(),
diff --git a/web/app/components/plugins/plugin-detail-panel/tool-selector/components/reasoning-config-form.tsx b/web/app/components/plugins/plugin-detail-panel/tool-selector/components/reasoning-config-form.tsx
index 4e6be7d81c..1c731d5eca 100644
--- a/web/app/components/plugins/plugin-detail-panel/tool-selector/components/reasoning-config-form.tsx
+++ b/web/app/components/plugins/plugin-detail-panel/tool-selector/components/reasoning-config-form.tsx
@@ -7,6 +7,7 @@ import type {
ValueSelector,
} from '@/app/components/workflow/types'
import { cn } from '@langgenius/dify-ui/cn'
+import { Switch } from '@langgenius/dify-ui/switch'
import {
RiArrowRightUpLine,
RiBracesLine,
@@ -16,7 +17,6 @@ import { useCallback, useState } from 'react'
import { useTranslation } from 'react-i18next'
import Input from '@/app/components/base/input'
import { SimpleSelect } from '@/app/components/base/select'
-import Switch from '@/app/components/base/switch'
import Tooltip from '@/app/components/base/tooltip'
import { FormTypeEnum } from '@/app/components/header/account-setting/model-provider-page/declarations'
import { useLanguage } from '@/app/components/header/account-setting/model-provider-page/hooks'
diff --git a/web/app/components/plugins/plugin-detail-panel/tool-selector/components/tool-credentials-form.tsx b/web/app/components/plugins/plugin-detail-panel/tool-selector/components/tool-credentials-form.tsx
index 90893c88ec..64068166d0 100644
--- a/web/app/components/plugins/plugin-detail-panel/tool-selector/components/tool-credentials-form.tsx
+++ b/web/app/components/plugins/plugin-detail-panel/tool-selector/components/tool-credentials-form.tsx
@@ -2,7 +2,9 @@
import type { FC } from 'react'
import type { Collection } from '@/app/components/tools/types'
import type { ToolCredentialFormSchema } from '@/app/components/tools/utils/to-form-schema'
+import { Button } from '@langgenius/dify-ui/button'
import { cn } from '@langgenius/dify-ui/cn'
+import { toast } from '@langgenius/dify-ui/toast'
import {
RiArrowRightUpLine,
} from '@remixicon/react'
@@ -10,8 +12,6 @@ import * as React from 'react'
import { useEffect, useState } from 'react'
import { useTranslation } from 'react-i18next'
import Loading from '@/app/components/base/loading'
-import { Button } from '@/app/components/base/ui/button'
-import { toast } from '@/app/components/base/ui/toast'
import Form from '@/app/components/header/account-setting/model-provider-page/model-modal/Form'
import { addDefaultValue, toolCredentialToFormSchemas } from '@/app/components/tools/utils/to-form-schema'
import { useRenderI18nObject } from '@/hooks/use-i18n'
diff --git a/web/app/components/plugins/plugin-detail-panel/tool-selector/components/tool-item.tsx b/web/app/components/plugins/plugin-detail-panel/tool-selector/components/tool-item.tsx
index 3ba30790af..889243d507 100644
--- a/web/app/components/plugins/plugin-detail-panel/tool-selector/components/tool-item.tsx
+++ b/web/app/components/plugins/plugin-detail-panel/tool-selector/components/tool-item.tsx
@@ -1,5 +1,7 @@
'use client'
+import { Button } from '@langgenius/dify-ui/button'
import { cn } from '@langgenius/dify-ui/cn'
+import { Switch } from '@langgenius/dify-ui/switch'
import {
RiDeleteBinLine,
RiEqualizer2Line,
@@ -11,10 +13,8 @@ import { useTranslation } from 'react-i18next'
import ActionButton from '@/app/components/base/action-button'
import AppIcon from '@/app/components/base/app-icon'
import { Group } from '@/app/components/base/icons/src/vender/other'
-import Switch from '@/app/components/base/switch'
import Tooltip from '@/app/components/base/tooltip'
import { ToolTipContent } from '@/app/components/base/tooltip/content'
-import { Button } from '@/app/components/base/ui/button'
import Indicator from '@/app/components/header/indicator'
import { InstallPluginButton } from '@/app/components/workflow/nodes/_base/components/install-plugin-button'
import { useMCPToolAvailability } from '@/app/components/workflow/nodes/_base/components/mcp-tool-availability'
diff --git a/web/app/components/plugins/plugin-item/__tests__/action.spec.tsx b/web/app/components/plugins/plugin-item/__tests__/action.spec.tsx
index 0b0d9c7fc8..b0ecc839b3 100644
--- a/web/app/components/plugins/plugin-item/__tests__/action.spec.tsx
+++ b/web/app/components/plugins/plugin-item/__tests__/action.spec.tsx
@@ -26,7 +26,7 @@ const {
mockToastNotify: vi.fn(),
}))
-vi.mock('@/app/components/base/ui/toast', () => ({
+vi.mock('@langgenius/dify-ui/toast', () => ({
toast: Object.assign(
(message: string, options?: { type?: string }) => mockToastNotify({ type: options?.type, message }),
{
diff --git a/web/app/components/plugins/plugin-item/action.tsx b/web/app/components/plugins/plugin-item/action.tsx
index ed401b534f..fe0ba932fc 100644
--- a/web/app/components/plugins/plugin-item/action.tsx
+++ b/web/app/components/plugins/plugin-item/action.tsx
@@ -2,11 +2,6 @@
import type { FC } from 'react'
import type { MetaData } from '../types'
import type { PluginCategoryEnum } from '@/app/components/plugins/types'
-import { RiDeleteBinLine, RiInformation2Line, RiLoopLeftLine } from '@remixicon/react'
-import { useBoolean } from 'ahooks'
-import * as React from 'react'
-import { useCallback } from 'react'
-import { useTranslation } from 'react-i18next'
import {
AlertDialog,
AlertDialogActions,
@@ -14,8 +9,13 @@ import {
AlertDialogConfirmButton,
AlertDialogContent,
AlertDialogTitle,
-} from '@/app/components/base/ui/alert-dialog'
-import { toast } from '@/app/components/base/ui/toast'
+} from '@langgenius/dify-ui/alert-dialog'
+import { toast } from '@langgenius/dify-ui/toast'
+import { RiDeleteBinLine, RiInformation2Line, RiLoopLeftLine } from '@remixicon/react'
+import { useBoolean } from 'ahooks'
+import * as React from 'react'
+import { useCallback } from 'react'
+import { useTranslation } from 'react-i18next'
import { useModalContext } from '@/context/modal-context'
import { uninstallPlugin } from '@/service/plugins'
import { useInvalidateInstalledPluginList } from '@/service/use-plugins'
diff --git a/web/app/components/plugins/plugin-mutation-model/index.tsx b/web/app/components/plugins/plugin-mutation-model/index.tsx
index 4e8478f8ad..96a60fd938 100644
--- a/web/app/components/plugins/plugin-mutation-model/index.tsx
+++ b/web/app/components/plugins/plugin-mutation-model/index.tsx
@@ -1,10 +1,10 @@
import type { UseMutationResult } from '@tanstack/react-query'
import type { FC, ReactNode } from 'react'
import type { Plugin } from '../types'
+import { Button } from '@langgenius/dify-ui/button'
import * as React from 'react'
import { memo } from 'react'
import Modal from '@/app/components/base/modal'
-import { Button } from '@/app/components/base/ui/button'
import Card from '@/app/components/plugins/card'
type Props = {
diff --git a/web/app/components/plugins/plugin-page/__tests__/debug-info.spec.tsx b/web/app/components/plugins/plugin-page/__tests__/debug-info.spec.tsx
index 2af44ecf97..65a36f4009 100644
--- a/web/app/components/plugins/plugin-page/__tests__/debug-info.spec.tsx
+++ b/web/app/components/plugins/plugin-page/__tests__/debug-info.spec.tsx
@@ -15,7 +15,7 @@ vi.mock('@/service/use-plugins', () => ({
useDebugKey: () => mockDebugKey,
}))
-vi.mock('@/app/components/base/ui/button', () => ({
+vi.mock('@langgenius/dify-ui/button', () => ({
Button: ({ children }: { children: React.ReactNode }) => ,
}))
diff --git a/web/app/components/plugins/plugin-page/__tests__/install-plugin-dropdown.spec.tsx b/web/app/components/plugins/plugin-page/__tests__/install-plugin-dropdown.spec.tsx
index 1f249b16c6..2dd884e18b 100644
--- a/web/app/components/plugins/plugin-page/__tests__/install-plugin-dropdown.spec.tsx
+++ b/web/app/components/plugins/plugin-page/__tests__/install-plugin-dropdown.spec.tsx
@@ -35,13 +35,13 @@ vi.mock('@/app/components/base/icons/src/vender/solid/mediaAndDevices', () => ({
MagicBox: () => magic,
}))
-vi.mock('@/app/components/base/ui/button', () => ({
+vi.mock('@langgenius/dify-ui/button', () => ({
Button: ({ children, onClick, className, ...props }: React.ButtonHTMLAttributes) => (
),
}))
-vi.mock('@/app/components/base/ui/dropdown-menu', async () => {
+vi.mock('@langgenius/dify-ui/dropdown-menu', async () => {
const React = await import('react')
const DropdownMenuContext = React.createContext<{ isOpen: boolean, setOpen: (open: boolean) => void } | null>(null)
diff --git a/web/app/components/plugins/plugin-page/__tests__/use-reference-setting.spec.ts b/web/app/components/plugins/plugin-page/__tests__/use-reference-setting.spec.ts
index c3c415bb4b..efb31b5afb 100644
--- a/web/app/components/plugins/plugin-page/__tests__/use-reference-setting.spec.ts
+++ b/web/app/components/plugins/plugin-page/__tests__/use-reference-setting.spec.ts
@@ -1,7 +1,7 @@
+// Import mocks for assertions
+import { toast } from '@langgenius/dify-ui/toast'
import { renderHook, waitFor } from '@testing-library/react'
import { beforeEach, describe, expect, it, vi } from 'vitest'
-// Import mocks for assertions
-import { toast } from '@/app/components/base/ui/toast'
import { useAppContext } from '@/context/app-context'
import { useGlobalPublicStore } from '@/context/global-public-context'
diff --git a/web/app/components/plugins/plugin-page/debug-info.tsx b/web/app/components/plugins/plugin-page/debug-info.tsx
index 4b3590f15c..4e02fdc2be 100644
--- a/web/app/components/plugins/plugin-page/debug-info.tsx
+++ b/web/app/components/plugins/plugin-page/debug-info.tsx
@@ -1,5 +1,6 @@
'use client'
import type { FC } from 'react'
+import { Button } from '@langgenius/dify-ui/button'
import {
RiArrowRightUpLine,
RiBugLine,
@@ -7,7 +8,6 @@ import {
import * as React from 'react'
import { useTranslation } from 'react-i18next'
import Tooltip from '@/app/components/base/tooltip'
-import { Button } from '@/app/components/base/ui/button'
import { useDocLink } from '@/context/i18n'
import { useDebugKey } from '@/service/use-plugins'
import KeyValueItem from '../base/key-value-item'
diff --git a/web/app/components/plugins/plugin-page/empty/index.tsx b/web/app/components/plugins/plugin-page/empty/index.tsx
index 5141519e35..e90918b4ab 100644
--- a/web/app/components/plugins/plugin-page/empty/index.tsx
+++ b/web/app/components/plugins/plugin-page/empty/index.tsx
@@ -1,4 +1,5 @@
'use client'
+import { Button } from '@langgenius/dify-ui/button'
import { noop } from 'es-toolkit/function'
import * as React from 'react'
import { useEffect, useMemo, useRef, useState } from 'react'
@@ -7,7 +8,6 @@ import { Group } from '@/app/components/base/icons/src/vender/other'
import { FileZip } from '@/app/components/base/icons/src/vender/solid/files'
import { Github } from '@/app/components/base/icons/src/vender/solid/general'
import { MagicBox } from '@/app/components/base/icons/src/vender/solid/mediaAndDevices'
-import { Button } from '@/app/components/base/ui/button'
import InstallFromGitHub from '@/app/components/plugins/install-plugin/install-from-github'
import InstallFromLocalPackage from '@/app/components/plugins/install-plugin/install-from-local-package'
import { SUPPORT_INSTALL_LOCAL_FILE_EXTENSIONS } from '@/config'
diff --git a/web/app/components/plugins/plugin-page/index.tsx b/web/app/components/plugins/plugin-page/index.tsx
index 05a6dd26e3..f80d37189a 100644
--- a/web/app/components/plugins/plugin-page/index.tsx
+++ b/web/app/components/plugins/plugin-page/index.tsx
@@ -2,6 +2,7 @@
import type { Dependency, PluginDeclaration, PluginManifestInMarket } from '../types'
import type { PluginPageTab } from './context'
+import { Button } from '@langgenius/dify-ui/button'
import { cn } from '@langgenius/dify-ui/cn'
import {
RiBookOpenLine,
@@ -14,7 +15,6 @@ import { useEffect, useMemo, useState } from 'react'
import { useTranslation } from 'react-i18next'
import TabSlider from '@/app/components/base/tab-slider'
import Tooltip from '@/app/components/base/tooltip'
-import { Button } from '@/app/components/base/ui/button'
import ReferenceSettingModal from '@/app/components/plugins/reference-setting-modal'
import { MARKETPLACE_API_PREFIX, SUPPORT_INSTALL_LOCAL_FILE_EXTENSIONS } from '@/config'
import { useGlobalPublicStore } from '@/context/global-public-context'
diff --git a/web/app/components/plugins/plugin-page/install-plugin-dropdown.tsx b/web/app/components/plugins/plugin-page/install-plugin-dropdown.tsx
index 9b98d16410..dee3dc17bb 100644
--- a/web/app/components/plugins/plugin-page/install-plugin-dropdown.tsx
+++ b/web/app/components/plugins/plugin-page/install-plugin-dropdown.tsx
@@ -1,6 +1,13 @@
'use client'
+import { Button } from '@langgenius/dify-ui/button'
import { cn } from '@langgenius/dify-ui/cn'
+import {
+ DropdownMenu,
+ DropdownMenuContent,
+ DropdownMenuItem,
+ DropdownMenuTrigger,
+} from '@langgenius/dify-ui/dropdown-menu'
import { RiAddLine, RiArrowDownSLine } from '@remixicon/react'
import { noop } from 'es-toolkit/function'
import { useEffect, useRef, useState } from 'react'
@@ -8,13 +15,6 @@ import { useTranslation } from 'react-i18next'
import { FileZip } from '@/app/components/base/icons/src/vender/solid/files'
import { Github } from '@/app/components/base/icons/src/vender/solid/general'
import { MagicBox } from '@/app/components/base/icons/src/vender/solid/mediaAndDevices'
-import { Button } from '@/app/components/base/ui/button'
-import {
- DropdownMenu,
- DropdownMenuContent,
- DropdownMenuItem,
- DropdownMenuTrigger,
-} from '@/app/components/base/ui/dropdown-menu'
import InstallFromGitHub from '@/app/components/plugins/install-plugin/install-from-github'
import InstallFromLocalPackage from '@/app/components/plugins/install-plugin/install-from-local-package'
import { SUPPORT_INSTALL_LOCAL_FILE_EXTENSIONS } from '@/config'
diff --git a/web/app/components/plugins/plugin-page/plugin-tasks/components/error-plugin-item.tsx b/web/app/components/plugins/plugin-page/plugin-tasks/components/error-plugin-item.tsx
index 0129c65ee2..ab17dd202b 100644
--- a/web/app/components/plugins/plugin-page/plugin-tasks/components/error-plugin-item.tsx
+++ b/web/app/components/plugins/plugin-page/plugin-tasks/components/error-plugin-item.tsx
@@ -1,9 +1,9 @@
import type { FC } from 'react'
import type { Plugin, PluginStatus } from '@/app/components/plugins/types'
import type { Locale } from '@/i18n-config'
+import { Button } from '@langgenius/dify-ui/button'
import { useCallback, useState } from 'react'
import { useTranslation } from 'react-i18next'
-import { Button } from '@/app/components/base/ui/button'
import InstallFromMarketplace from '@/app/components/plugins/install-plugin/install-from-marketplace'
import { PluginSource } from '@/app/components/plugins/types'
import { fetchPluginInfoFromMarketPlace } from '@/service/plugins'
diff --git a/web/app/components/plugins/plugin-page/plugin-tasks/components/plugin-task-list.tsx b/web/app/components/plugins/plugin-page/plugin-tasks/components/plugin-task-list.tsx
index e0dcc5b6f8..24fcf85cde 100644
--- a/web/app/components/plugins/plugin-page/plugin-tasks/components/plugin-task-list.tsx
+++ b/web/app/components/plugins/plugin-page/plugin-tasks/components/plugin-task-list.tsx
@@ -1,7 +1,7 @@
import type { FC } from 'react'
import type { PluginStatus } from '@/app/components/plugins/types'
+import { Button } from '@langgenius/dify-ui/button'
import { useTranslation } from 'react-i18next'
-import { Button } from '@/app/components/base/ui/button'
import { useGetLanguage } from '@/context/i18n'
import ErrorPluginItem from './error-plugin-item'
import PluginSection from './plugin-section'
diff --git a/web/app/components/plugins/plugin-page/plugin-tasks/index.tsx b/web/app/components/plugins/plugin-page/plugin-tasks/index.tsx
index 9a39b43bf6..7603cae33d 100644
--- a/web/app/components/plugins/plugin-page/plugin-tasks/index.tsx
+++ b/web/app/components/plugins/plugin-page/plugin-tasks/index.tsx
@@ -1,14 +1,14 @@
+import {
+ DropdownMenu,
+ DropdownMenuContent,
+ DropdownMenuTrigger,
+} from '@langgenius/dify-ui/dropdown-menu'
import {
useCallback,
useMemo,
useState,
} from 'react'
import { useTranslation } from 'react-i18next'
-import {
- DropdownMenu,
- DropdownMenuContent,
- DropdownMenuTrigger,
-} from '@/app/components/base/ui/dropdown-menu'
import useGetIcon from '@/app/components/plugins/install-plugin/base/use-get-icon'
import PluginTaskList from './components/plugin-task-list'
import TaskStatusIndicator from './components/task-status-indicator'
diff --git a/web/app/components/plugins/plugin-page/plugins-panel.tsx b/web/app/components/plugins/plugin-page/plugins-panel.tsx
index 142c9329cc..a824d55dda 100644
--- a/web/app/components/plugins/plugin-page/plugins-panel.tsx
+++ b/web/app/components/plugins/plugin-page/plugins-panel.tsx
@@ -1,11 +1,11 @@
'use client'
import type { PluginDetail } from '../types'
import type { FilterState } from './filter-management'
+import { Button } from '@langgenius/dify-ui/button'
import { useDebounceFn } from 'ahooks'
import { useMemo } from 'react'
import { useTranslation } from 'react-i18next'
import Loading from '@/app/components/base/loading'
-import { Button } from '@/app/components/base/ui/button'
import PluginDetailPanel from '@/app/components/plugins/plugin-detail-panel'
import { useGetLanguage } from '@/context/i18n'
import { renderI18nObject } from '@/i18n-config'
diff --git a/web/app/components/plugins/plugin-page/use-reference-setting.ts b/web/app/components/plugins/plugin-page/use-reference-setting.ts
index 0a0e925207..b688bdc11e 100644
--- a/web/app/components/plugins/plugin-page/use-reference-setting.ts
+++ b/web/app/components/plugins/plugin-page/use-reference-setting.ts
@@ -1,6 +1,6 @@
+import { toast } from '@langgenius/dify-ui/toast'
import { useMemo } from 'react'
import { useTranslation } from 'react-i18next'
-import { toast } from '@/app/components/base/ui/toast'
import { useAppContext } from '@/context/app-context'
import { useGlobalPublicStore } from '@/context/global-public-context'
import { useInvalidateReferenceSettings, useMutationReferenceSettings, useReferenceSettings } from '@/service/use-plugins'
diff --git a/web/app/components/plugins/provider-card.tsx b/web/app/components/plugins/provider-card.tsx
index 7f07699771..06b611b355 100644
--- a/web/app/components/plugins/provider-card.tsx
+++ b/web/app/components/plugins/provider-card.tsx
@@ -1,6 +1,7 @@
'use client'
import type { FC } from 'react'
import type { Plugin } from './types'
+import { Button } from '@langgenius/dify-ui/button'
import { cn } from '@langgenius/dify-ui/cn'
import { RiArrowRightUpLine } from '@remixicon/react'
import { useBoolean } from 'ahooks'
@@ -8,7 +9,6 @@ import { useTheme } from 'next-themes'
import * as React from 'react'
import { useMemo } from 'react'
import { useTranslation } from 'react-i18next'
-import { Button } from '@/app/components/base/ui/button'
import InstallFromMarketplace from '@/app/components/plugins/install-plugin/install-from-marketplace'
import { getPluginLinkInMarketplace } from '@/app/components/plugins/marketplace/utils'
import { useLocale } from '@/context/i18n'
diff --git a/web/app/components/plugins/reference-setting-modal/auto-update-setting/__tests__/plugins-picker.spec.tsx b/web/app/components/plugins/reference-setting-modal/auto-update-setting/__tests__/plugins-picker.spec.tsx
index 299eecce54..99d7a5fdc5 100644
--- a/web/app/components/plugins/reference-setting-modal/auto-update-setting/__tests__/plugins-picker.spec.tsx
+++ b/web/app/components/plugins/reference-setting-modal/auto-update-setting/__tests__/plugins-picker.spec.tsx
@@ -5,7 +5,7 @@ import { AUTO_UPDATE_MODE } from '../types'
const mockToolPicker = vi.fn()
-vi.mock('@/app/components/base/ui/button', () => ({
+vi.mock('@langgenius/dify-ui/button', () => ({
Button: ({
children,
}: {
diff --git a/web/app/components/plugins/reference-setting-modal/auto-update-setting/__tests__/strategy-picker.spec.tsx b/web/app/components/plugins/reference-setting-modal/auto-update-setting/__tests__/strategy-picker.spec.tsx
index e1e72fbde2..5be10ff146 100644
--- a/web/app/components/plugins/reference-setting-modal/auto-update-setting/__tests__/strategy-picker.spec.tsx
+++ b/web/app/components/plugins/reference-setting-modal/auto-update-setting/__tests__/strategy-picker.spec.tsx
@@ -5,7 +5,7 @@ import { AUTO_UPDATE_STRATEGY } from '../types'
let portalOpen = false
-vi.mock('@/app/components/base/ui/button', () => ({
+vi.mock('@langgenius/dify-ui/button', () => ({
Button: ({
children,
}: {
diff --git a/web/app/components/plugins/reference-setting-modal/auto-update-setting/plugins-picker.tsx b/web/app/components/plugins/reference-setting-modal/auto-update-setting/plugins-picker.tsx
index c0dbbea5c6..1bb4caeb3f 100644
--- a/web/app/components/plugins/reference-setting-modal/auto-update-setting/plugins-picker.tsx
+++ b/web/app/components/plugins/reference-setting-modal/auto-update-setting/plugins-picker.tsx
@@ -1,10 +1,10 @@
'use client'
import type { FC } from 'react'
+import { Button } from '@langgenius/dify-ui/button'
import { RiAddLine } from '@remixicon/react'
import { useBoolean } from 'ahooks'
import * as React from 'react'
import { useTranslation } from 'react-i18next'
-import { Button } from '@/app/components/base/ui/button'
import NoPluginSelected from './no-plugin-selected'
import PluginsSelected from './plugins-selected'
import ToolPicker from './tool-picker'
diff --git a/web/app/components/plugins/reference-setting-modal/auto-update-setting/strategy-picker.tsx b/web/app/components/plugins/reference-setting-modal/auto-update-setting/strategy-picker.tsx
index 0f85b95e3c..22bfa6a30b 100644
--- a/web/app/components/plugins/reference-setting-modal/auto-update-setting/strategy-picker.tsx
+++ b/web/app/components/plugins/reference-setting-modal/auto-update-setting/strategy-picker.tsx
@@ -1,3 +1,4 @@
+import { Button } from '@langgenius/dify-ui/button'
import {
RiArrowDownSLine,
RiCheckLine,
@@ -9,7 +10,6 @@ import {
PortalToFollowElemContent,
PortalToFollowElemTrigger,
} from '@/app/components/base/portal-to-follow-elem'
-import { Button } from '@/app/components/base/ui/button'
import { AUTO_UPDATE_STRATEGY } from './types'
const i18nPrefix = 'autoUpdate.strategy'
diff --git a/web/app/components/plugins/reference-setting-modal/index.tsx b/web/app/components/plugins/reference-setting-modal/index.tsx
index bbf19d8c0a..061777e38b 100644
--- a/web/app/components/plugins/reference-setting-modal/index.tsx
+++ b/web/app/components/plugins/reference-setting-modal/index.tsx
@@ -2,11 +2,11 @@
import type { FC } from 'react'
import type { AutoUpdateConfig } from './auto-update-setting/types'
import type { Permissions, ReferenceSetting } from '@/app/components/plugins/types'
+import { Button } from '@langgenius/dify-ui/button'
import * as React from 'react'
import { useCallback, useState } from 'react'
import { useTranslation } from 'react-i18next'
import Modal from '@/app/components/base/modal'
-import { Button } from '@/app/components/base/ui/button'
import { PermissionType } from '@/app/components/plugins/types'
import OptionCard from '@/app/components/workflow/nodes/_base/components/option-card'
import { useGlobalPublicStore } from '@/context/global-public-context'
diff --git a/web/app/components/plugins/update-plugin/__tests__/from-market-place.spec.tsx b/web/app/components/plugins/update-plugin/__tests__/from-market-place.spec.tsx
index 9d9ad77106..3cc3ef78d1 100644
--- a/web/app/components/plugins/update-plugin/__tests__/from-market-place.spec.tsx
+++ b/web/app/components/plugins/update-plugin/__tests__/from-market-place.spec.tsx
@@ -22,7 +22,7 @@ const {
mockToastError: vi.fn(),
}))
-vi.mock('@/app/components/base/ui/dialog', () => ({
+vi.mock('@langgenius/dify-ui/dialog', () => ({
Dialog: ({ children }: { children: React.ReactNode }) => {children}
,
DialogContent: ({ children }: { children: React.ReactNode }) => {children}
,
DialogTitle: ({ children }: { children: React.ReactNode }) => {children}
,
@@ -37,7 +37,7 @@ vi.mock('@/app/components/base/badge/index', () => ({
default: ({ children }: { children: React.ReactNode }) => {children}
,
}))
-vi.mock('@/app/components/base/ui/button', () => ({
+vi.mock('@langgenius/dify-ui/button', () => ({
Button: ({
children,
onClick,
@@ -49,7 +49,7 @@ vi.mock('@/app/components/base/ui/button', () => ({
}) => ,
}))
-vi.mock('@/app/components/base/ui/toast', () => ({
+vi.mock('@langgenius/dify-ui/toast', () => ({
toast: {
error: mockToastError,
},
diff --git a/web/app/components/plugins/update-plugin/__tests__/index.spec.tsx b/web/app/components/plugins/update-plugin/__tests__/index.spec.tsx
index cdd7462342..d0492696ca 100644
--- a/web/app/components/plugins/update-plugin/__tests__/index.spec.tsx
+++ b/web/app/components/plugins/update-plugin/__tests__/index.spec.tsx
@@ -4,11 +4,11 @@ import type {
UpdateFromMarketPlacePayload,
UpdatePluginModalType,
} from '../../types'
+import { toast } from '@langgenius/dify-ui/toast'
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import { fireEvent, render, screen, waitFor } from '@testing-library/react'
import * as React from 'react'
import { beforeEach, describe, expect, it, vi } from 'vitest'
-import { toast } from '@/app/components/base/ui/toast'
import { PluginCategoryEnum, PluginSource, TaskStatus } from '../../types'
import DowngradeWarningModal from '../downgrade-warning'
import FromGitHub from '../from-github'
diff --git a/web/app/components/plugins/update-plugin/downgrade-warning.tsx b/web/app/components/plugins/update-plugin/downgrade-warning.tsx
index da53ebfb12..801c8ede51 100644
--- a/web/app/components/plugins/update-plugin/downgrade-warning.tsx
+++ b/web/app/components/plugins/update-plugin/downgrade-warning.tsx
@@ -1,5 +1,5 @@
+import { Button } from '@langgenius/dify-ui/button'
import { useTranslation } from 'react-i18next'
-import { Button } from '@/app/components/base/ui/button'
const i18nPrefix = 'autoUpdate.pluginDowngradeWarning'
diff --git a/web/app/components/plugins/update-plugin/from-market-place.tsx b/web/app/components/plugins/update-plugin/from-market-place.tsx
index bde5051808..52484e65aa 100644
--- a/web/app/components/plugins/update-plugin/from-market-place.tsx
+++ b/web/app/components/plugins/update-plugin/from-market-place.tsx
@@ -1,19 +1,19 @@
'use client'
import type { FC } from 'react'
import type { UpdateFromMarketPlacePayload } from '../types'
+import { Button } from '@langgenius/dify-ui/button'
import { cn } from '@langgenius/dify-ui/cn'
-import * as React from 'react'
-import { useCallback, useEffect, useMemo, useState } from 'react'
-import { useTranslation } from 'react-i18next'
-import Badge, { BadgeState } from '@/app/components/base/badge/index'
-import { Button } from '@/app/components/base/ui/button'
import {
Dialog,
DialogCloseButton,
DialogContent,
DialogTitle,
-} from '@/app/components/base/ui/dialog'
-import { toast } from '@/app/components/base/ui/toast'
+} from '@langgenius/dify-ui/dialog'
+import { toast } from '@langgenius/dify-ui/toast'
+import * as React from 'react'
+import { useCallback, useEffect, useMemo, useState } from 'react'
+import { useTranslation } from 'react-i18next'
+import Badge, { BadgeState } from '@/app/components/base/badge/index'
import Card from '@/app/components/plugins/card'
import checkTaskStatus from '@/app/components/plugins/install-plugin/base/check-task-status'
import { pluginManifestToCardPluginProps } from '@/app/components/plugins/install-plugin/utils'
diff --git a/web/app/components/plugins/update-plugin/plugin-version-picker.tsx b/web/app/components/plugins/update-plugin/plugin-version-picker.tsx
index d590f0c9eb..a76fd085ba 100644
--- a/web/app/components/plugins/update-plugin/plugin-version-picker.tsx
+++ b/web/app/components/plugins/update-plugin/plugin-version-picker.tsx
@@ -1,16 +1,16 @@
'use client'
+import type { Placement } from '@langgenius/dify-ui/popover'
import type { FC } from 'react'
-import type { Placement } from '@/app/components/base/ui/placement'
import { cn } from '@langgenius/dify-ui/cn'
-import * as React from 'react'
-import { useCallback } from 'react'
-import { useTranslation } from 'react-i18next'
-import Badge from '@/app/components/base/badge'
import {
Popover,
PopoverContent,
PopoverTrigger,
-} from '@/app/components/base/ui/popover'
+} from '@langgenius/dify-ui/popover'
+import * as React from 'react'
+import { useCallback } from 'react'
+import { useTranslation } from 'react-i18next'
+import Badge from '@/app/components/base/badge'
import useTimestamp from '@/hooks/use-timestamp'
import { useVersionListOfPlugin } from '@/service/use-plugins'
import { isEarlierThanVersion } from '@/utils/semver'
diff --git a/web/app/components/rag-pipeline/components/__tests__/conversion.spec.tsx b/web/app/components/rag-pipeline/components/__tests__/conversion.spec.tsx
index 967e697813..cb11e5baf4 100644
--- a/web/app/components/rag-pipeline/components/__tests__/conversion.spec.tsx
+++ b/web/app/components/rag-pipeline/components/__tests__/conversion.spec.tsx
@@ -37,11 +37,11 @@ const { mockToast } = vi.hoisted(() => {
return { mockToast }
})
-vi.mock('@/app/components/base/ui/toast', () => ({
+vi.mock('@langgenius/dify-ui/toast', () => ({
toast: mockToast,
}))
-vi.mock('@/app/components/base/ui/button', () => ({
+vi.mock('@langgenius/dify-ui/button', () => ({
Button: ({ children, onClick, ...props }: Record) => (
),
diff --git a/web/app/components/rag-pipeline/components/__tests__/publish-as-knowledge-pipeline-modal.spec.tsx b/web/app/components/rag-pipeline/components/__tests__/publish-as-knowledge-pipeline-modal.spec.tsx
index f9eb858b30..7a99b7ab90 100644
--- a/web/app/components/rag-pipeline/components/__tests__/publish-as-knowledge-pipeline-modal.spec.tsx
+++ b/web/app/components/rag-pipeline/components/__tests__/publish-as-knowledge-pipeline-modal.spec.tsx
@@ -22,7 +22,7 @@ vi.mock('@/app/components/base/modal', () => ({
isShow ? {children}
: null,
}))
-vi.mock('@/app/components/base/ui/button', () => ({
+vi.mock('@langgenius/dify-ui/button', () => ({
Button: ({ children, onClick, disabled, ...props }: Record) => (
+ )
+ })}
+ {isFetchingNextPage && (
+
+
+
+ )}
+
+
+
+
+
+
+ )}
void
diff --git a/web/app/components/workflow/block-selector/snippets/use-create-snippet.ts b/web/app/components/workflow/block-selector/snippets/use-create-snippet.ts
index cfa2652921..f838b2e458 100644
--- a/web/app/components/workflow/block-selector/snippets/use-create-snippet.ts
+++ b/web/app/components/workflow/block-selector/snippets/use-create-snippet.ts
@@ -1,7 +1,7 @@
import type { CreateSnippetDialogPayload } from '../../create-snippet-dialog'
import { useState } from 'react'
import { useTranslation } from 'react-i18next'
-import { toast } from '@/app/components/base/ui/toast'
+import { toast } from '@langgenius/dify-ui/toast'
import { useRouter } from '@/next/navigation'
import { consoleClient } from '@/service/client'
import { useCreateSnippetMutation } from '@/service/use-snippets'
diff --git a/web/app/components/workflow/block-selector/snippets/use-insert-snippet.ts b/web/app/components/workflow/block-selector/snippets/use-insert-snippet.ts
index c143af9d6d..ae4f8d4cd7 100644
--- a/web/app/components/workflow/block-selector/snippets/use-insert-snippet.ts
+++ b/web/app/components/workflow/block-selector/snippets/use-insert-snippet.ts
@@ -3,7 +3,7 @@ import { useQueryClient } from '@tanstack/react-query'
import { useCallback } from 'react'
import { useTranslation } from 'react-i18next'
import { useStoreApi } from 'reactflow'
-import { toast } from '@/app/components/base/ui/toast'
+import { toast } from '@langgenius/dify-ui/toast'
import { consoleQuery } from '@/service/client'
import { useNodesSyncDraft, useWorkflowHistory, WorkflowHistoryEvent } from '../../hooks'
@@ -25,9 +25,9 @@ const remapSnippetGraph = (currentNodes: Node[], snippetNodes: Node[], snippetEd
const minRootY = rootNodes.length ? Math.min(...rootNodes.map(node => node.position.y)) : 0
const currentMaxX = currentNodes.length
? Math.max(...currentNodes.map((node) => {
- const nodeX = node.positionAbsolute?.x ?? node.position.x
- return nodeX + (node.width ?? 0)
- }))
+ const nodeX = node.positionAbsolute?.x ?? node.position.x
+ return nodeX + (node.width ?? 0)
+ }))
: 0
const currentMinY = currentNodes.length
? Math.min(...currentNodes.map(node => node.positionAbsolute?.y ?? node.position.y))
@@ -53,17 +53,17 @@ const remapSnippetGraph = (currentNodes: Node[], snippetNodes: Node[], snippetEd
parentId: nextParentId,
position: isRootNode
? {
- x: node.position.x + offsetX,
- y: node.position.y + offsetY,
- }
+ x: node.position.x + offsetX,
+ y: node.position.y + offsetY,
+ }
: node.position,
positionAbsolute: node.positionAbsolute
? (isRootNode
- ? {
- x: node.positionAbsolute.x + offsetX,
- y: node.positionAbsolute.y + offsetY,
- }
- : node.positionAbsolute)
+ ? {
+ x: node.positionAbsolute.x + offsetX,
+ y: node.positionAbsolute.y + offsetY,
+ }
+ : node.positionAbsolute)
: undefined,
selected: true,
data: {
@@ -85,9 +85,9 @@ const remapSnippetGraph = (currentNodes: Node[], snippetNodes: Node[], snippetEd
selected: false,
data: edge.data
? {
- ...edge.data,
- _connectedNodeIsSelected: true,
- }
+ ...edge.data,
+ _connectedNodeIsSelected: true,
+ }
: edge.data,
}))
diff --git a/web/app/components/workflow/block-selector/tool-picker.tsx b/web/app/components/workflow/block-selector/tool-picker.tsx
index a509dd9723..7ba93798e2 100644
--- a/web/app/components/workflow/block-selector/tool-picker.tsx
+++ b/web/app/components/workflow/block-selector/tool-picker.tsx
@@ -8,6 +8,7 @@ import type { ToolDefaultValue, ToolValue } from './types'
import type { CustomCollectionBackend } from '@/app/components/tools/types'
import type { BlockEnum, OnSelectBlock } from '@/app/components/workflow/types'
import { cn } from '@langgenius/dify-ui/cn'
+import { toast } from '@langgenius/dify-ui/toast'
import { useBoolean } from 'ahooks'
import * as React from 'react'
import { useMemo, useState } from 'react'
@@ -17,7 +18,6 @@ import {
PortalToFollowElemContent,
PortalToFollowElemTrigger,
} from '@/app/components/base/portal-to-follow-elem'
-import { toast } from '@/app/components/base/ui/toast'
import SearchBox from '@/app/components/plugins/marketplace/search-box'
import EditCustomToolModal from '@/app/components/tools/edit-custom-collection-modal'
import AllTools from '@/app/components/workflow/block-selector/all-tools'
diff --git a/web/app/components/workflow/comment/comment-input.spec.tsx b/web/app/components/workflow/comment/comment-input.spec.tsx
index 41e583aeed..8796b4ec36 100644
--- a/web/app/components/workflow/comment/comment-input.spec.tsx
+++ b/web/app/components/workflow/comment/comment-input.spec.tsx
@@ -35,7 +35,7 @@ vi.mock('@/context/app-context', () => ({
}),
}))
-vi.mock('@/app/components/base/ui/avatar', () => ({
+vi.mock('@langgenius/dify-ui/avatar', () => ({
Avatar: ({ name }: { name: string }) => {name}
,
default: ({ name }: { name: string }) => {name}
,
}))
diff --git a/web/app/components/workflow/comment/comment-input.tsx b/web/app/components/workflow/comment/comment-input.tsx
index 51cef610c4..e6cbc95a82 100644
--- a/web/app/components/workflow/comment/comment-input.tsx
+++ b/web/app/components/workflow/comment/comment-input.tsx
@@ -1,8 +1,8 @@
import type { FC, PointerEvent as ReactPointerEvent } from 'react'
+import { Avatar } from '@langgenius/dify-ui/avatar'
import { cn } from '@langgenius/dify-ui/cn'
import { memo, useCallback, useEffect, useRef, useState } from 'react'
import { useTranslation } from 'react-i18next'
-import { Avatar } from '@/app/components/base/ui/avatar'
import { useAppContext } from '@/context/app-context'
import { MentionInput } from './mention-input'
diff --git a/web/app/components/workflow/comment/mention-input.spec.tsx b/web/app/components/workflow/comment/mention-input.spec.tsx
index 5ff211d1f2..49fc7b6e87 100644
--- a/web/app/components/workflow/comment/mention-input.spec.tsx
+++ b/web/app/components/workflow/comment/mention-input.spec.tsx
@@ -41,7 +41,7 @@ vi.mock('../store', () => ({
}),
}))
-vi.mock('@/app/components/base/ui/avatar', () => ({
+vi.mock('@langgenius/dify-ui/avatar', () => ({
Avatar: ({ name }: { name: string }) => {name}
,
}))
diff --git a/web/app/components/workflow/comment/mention-input.tsx b/web/app/components/workflow/comment/mention-input.tsx
index 287a1c2e55..b6a7caa055 100644
--- a/web/app/components/workflow/comment/mention-input.tsx
+++ b/web/app/components/workflow/comment/mention-input.tsx
@@ -2,6 +2,8 @@
import type { ReactNode } from 'react'
import type { UserProfile } from '@/service/workflow-comment'
+import { Avatar } from '@langgenius/dify-ui/avatar'
+import { Button } from '@langgenius/dify-ui/button'
import { cn } from '@langgenius/dify-ui/cn'
import { RiArrowUpLine, RiAtLine, RiLoader2Line } from '@remixicon/react'
import {
@@ -19,8 +21,6 @@ import { createPortal } from 'react-dom'
import { useTranslation } from 'react-i18next'
import Textarea from 'react-textarea-autosize'
import EnterKey from '@/app/components/base/icons/src/public/common/EnterKey'
-import { Avatar } from '@/app/components/base/ui/avatar'
-import { Button } from '@/app/components/base/ui/button'
import { useParams } from '@/next/navigation'
import { fetchMentionableUsers } from '@/service/workflow-comment'
import { useStore, useWorkflowStore } from '../store'
diff --git a/web/app/components/workflow/comment/thread.spec.tsx b/web/app/components/workflow/comment/thread.spec.tsx
index 40e522d4b7..58bb99cff0 100644
--- a/web/app/components/workflow/comment/thread.spec.tsx
+++ b/web/app/components/workflow/comment/thread.spec.tsx
@@ -68,14 +68,14 @@ vi.mock('@/app/components/base/inline-delete-confirm', () => ({
),
}))
-vi.mock('@/app/components/base/ui/avatar', () => ({
+vi.mock('@langgenius/dify-ui/avatar', () => ({
Avatar: ({ name }: { name: string }) => {name}
,
AvatarRoot: ({ children }: { children: React.ReactNode }) => {children}
,
AvatarImage: ({ alt }: { alt: string }) => {alt}
,
AvatarFallback: ({ children }: { children: React.ReactNode }) => {children}
,
}))
-vi.mock('@/app/components/base/ui/dropdown-menu', () => ({
+vi.mock('@langgenius/dify-ui/dropdown-menu', () => ({
DropdownMenu: ({ children }: { children: React.ReactNode }) => {children}
,
DropdownMenuTrigger: ({ children, ...props }: React.ComponentProps<'button'>) => (
{children}
@@ -83,7 +83,7 @@ vi.mock('@/app/components/base/ui/dropdown-menu', () => ({
DropdownMenuContent: ({ children }: { children: React.ReactNode }) => {children}
,
}))
-vi.mock('@/app/components/base/ui/tooltip', () => ({
+vi.mock('@langgenius/dify-ui/tooltip', () => ({
Tooltip: ({ children }: { children: React.ReactNode }) => <>{children}>,
TooltipTrigger: ({
children,
diff --git a/web/app/components/workflow/comment/thread.tsx b/web/app/components/workflow/comment/thread.tsx
index d0ab65db15..34e0092372 100644
--- a/web/app/components/workflow/comment/thread.tsx
+++ b/web/app/components/workflow/comment/thread.tsx
@@ -2,20 +2,20 @@
import type { FC, ReactNode } from 'react'
import type { WorkflowCommentDetail, WorkflowCommentDetailReply } from '@/service/workflow-comment'
+import { Avatar, AvatarFallback, AvatarImage, AvatarRoot } from '@langgenius/dify-ui/avatar'
import { cn } from '@langgenius/dify-ui/cn'
+import {
+ DropdownMenu,
+ DropdownMenuContent,
+ DropdownMenuTrigger,
+} from '@langgenius/dify-ui/dropdown-menu'
+import { Tooltip, TooltipContent, TooltipTrigger } from '@langgenius/dify-ui/tooltip'
import { RiArrowDownSLine, RiArrowUpSLine, RiCheckboxCircleFill, RiCheckboxCircleLine, RiCloseLine, RiDeleteBinLine, RiMoreFill } from '@remixicon/react'
import { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { useReactFlow, useViewport } from 'reactflow'
import Divider from '@/app/components/base/divider'
import InlineDeleteConfirm from '@/app/components/base/inline-delete-confirm'
-import { Avatar, AvatarFallback, AvatarImage, AvatarRoot } from '@/app/components/base/ui/avatar'
-import {
- DropdownMenu,
- DropdownMenuContent,
- DropdownMenuTrigger,
-} from '@/app/components/base/ui/dropdown-menu'
-import { Tooltip, TooltipContent, TooltipTrigger } from '@/app/components/base/ui/tooltip'
import { getUserColor } from '@/app/components/workflow/collaboration/utils/user-color'
import { useAppContext } from '@/context/app-context'
import { useFormatTimeFromNow } from '@/hooks/use-format-time-from-now'
diff --git a/web/app/components/workflow/create-snippet-dialog.tsx b/web/app/components/workflow/create-snippet-dialog.tsx
index 182fddfcf4..7271ad7ded 100644
--- a/web/app/components/workflow/create-snippet-dialog.tsx
+++ b/web/app/components/workflow/create-snippet-dialog.tsx
@@ -8,10 +8,10 @@ import { useCallback, useState } from 'react'
import { useTranslation } from 'react-i18next'
import AppIcon from '@/app/components/base/app-icon'
import AppIconPicker from '@/app/components/base/app-icon-picker'
-import { Button } from '@/app/components/base/ui/button'
+import { Button } from '@langgenius/dify-ui/button'
import Input from '@/app/components/base/input'
import Textarea from '@/app/components/base/textarea'
-import { Dialog, DialogCloseButton, DialogContent, DialogPortal, DialogTitle } from '@/app/components/base/ui/dialog'
+import { Dialog, DialogCloseButton, DialogContent, DialogPortal, DialogTitle } from '@langgenius/dify-ui/dialog'
import ShortcutsName from './shortcuts-name'
export type CreateSnippetDialogPayload = {
diff --git a/web/app/components/workflow/dsl-export-confirm-modal.tsx b/web/app/components/workflow/dsl-export-confirm-modal.tsx
index b256f8bd9f..6f60e19854 100644
--- a/web/app/components/workflow/dsl-export-confirm-modal.tsx
+++ b/web/app/components/workflow/dsl-export-confirm-modal.tsx
@@ -1,5 +1,6 @@
'use client'
import type { EnvironmentVariable } from '@/app/components/workflow/types'
+import { Button } from '@langgenius/dify-ui/button'
import { cn } from '@langgenius/dify-ui/cn'
import { RiCloseLine, RiLock2Line } from '@remixicon/react'
import { noop } from 'es-toolkit/function'
@@ -9,7 +10,6 @@ import { useTranslation } from 'react-i18next'
import Checkbox from '@/app/components/base/checkbox'
import { Env } from '@/app/components/base/icons/src/vender/line/others'
import Modal from '@/app/components/base/modal'
-import { Button } from '@/app/components/base/ui/button'
export type DSLExportConfirmModalProps = {
envList: EnvironmentVariable[]
diff --git a/web/app/components/workflow/edge-contextmenu.tsx b/web/app/components/workflow/edge-contextmenu.tsx
index 61b208fcbd..2b7f13190a 100644
--- a/web/app/components/workflow/edge-contextmenu.tsx
+++ b/web/app/components/workflow/edge-contextmenu.tsx
@@ -1,14 +1,14 @@
+import {
+ ContextMenu,
+ ContextMenuContent,
+ ContextMenuItem,
+} from '@langgenius/dify-ui/context-menu'
import {
memo,
useMemo,
} from 'react'
import { useTranslation } from 'react-i18next'
import { useEdges } from 'reactflow'
-import {
- ContextMenu,
- ContextMenuContent,
- ContextMenuItem,
-} from '@/app/components/base/ui/context-menu'
import { useEdgesInteractions, usePanelInteractions } from './hooks'
import ShortcutsName from './shortcuts-name'
import { useStore } from './store'
diff --git a/web/app/components/workflow/header/__tests__/header-layouts.spec.tsx b/web/app/components/workflow/header/__tests__/header-layouts.spec.tsx
index 3422f5f78a..4d91180863 100644
--- a/web/app/components/workflow/header/__tests__/header-layouts.spec.tsx
+++ b/web/app/components/workflow/header/__tests__/header-layouts.spec.tsx
@@ -60,7 +60,7 @@ vi.mock('@/service/use-workflow', () => ({
useInvalidAllLastRun: () => mockInvalidAllLastRun,
}))
-vi.mock('@/app/components/base/ui/toast', () => ({
+vi.mock('@langgenius/dify-ui/toast', () => ({
toast: {
success: (message: string) => mockNotify({ type: 'success', message }),
error: (message: string) => mockNotify({ type: 'error', message }),
diff --git a/web/app/components/workflow/header/__tests__/run-mode.spec.tsx b/web/app/components/workflow/header/__tests__/run-mode.spec.tsx
index 7bc5ef1b0c..02b645e079 100644
--- a/web/app/components/workflow/header/__tests__/run-mode.spec.tsx
+++ b/web/app/components/workflow/header/__tests__/run-mode.spec.tsx
@@ -46,7 +46,7 @@ vi.mock('../../hooks/use-dynamic-test-run-options', () => ({
useDynamicTestRunOptions: () => mockDynamicOptions,
}))
-vi.mock('@/app/components/base/ui/toast', () => ({
+vi.mock('@langgenius/dify-ui/toast', () => ({
toast: {
success: (message: string) => mockNotify({ type: 'success', message }),
error: (message: string) => mockNotify({ type: 'error', message }),
diff --git a/web/app/components/workflow/header/__tests__/test-run-menu-helpers.spec.tsx b/web/app/components/workflow/header/__tests__/test-run-menu-helpers.spec.tsx
index 7df9cd091f..09452055db 100644
--- a/web/app/components/workflow/header/__tests__/test-run-menu-helpers.spec.tsx
+++ b/web/app/components/workflow/header/__tests__/test-run-menu-helpers.spec.tsx
@@ -10,7 +10,7 @@ import {
useShortcutMenu,
} from '../test-run-menu-helpers'
-vi.mock('@/app/components/base/ui/dropdown-menu', async () => {
+vi.mock('@langgenius/dify-ui/dropdown-menu', async () => {
const React = await import('react')
const DropdownMenuContext = React.createContext<{ open: boolean, setOpen: (open: boolean) => void } | null>(null)
diff --git a/web/app/components/workflow/header/__tests__/test-run-menu.spec.tsx b/web/app/components/workflow/header/__tests__/test-run-menu.spec.tsx
index 7e4cbe87c6..7252ed89b6 100644
--- a/web/app/components/workflow/header/__tests__/test-run-menu.spec.tsx
+++ b/web/app/components/workflow/header/__tests__/test-run-menu.spec.tsx
@@ -5,7 +5,7 @@ import { act } from 'react'
import * as React from 'react'
import TestRunMenu, { TriggerType } from '../test-run-menu'
-vi.mock('@/app/components/base/ui/dropdown-menu', async () => {
+vi.mock('@langgenius/dify-ui/dropdown-menu', async () => {
const React = await import('react')
const DropdownMenuContext = React.createContext<{ open: boolean, setOpen: (open: boolean) => void } | null>(null)
diff --git a/web/app/components/workflow/header/chat-variable-button.tsx b/web/app/components/workflow/header/chat-variable-button.tsx
index 63ac90aad6..efac64bd1b 100644
--- a/web/app/components/workflow/header/chat-variable-button.tsx
+++ b/web/app/components/workflow/header/chat-variable-button.tsx
@@ -1,7 +1,7 @@
+import { Button } from '@langgenius/dify-ui/button'
import { cn } from '@langgenius/dify-ui/cn'
import { memo } from 'react'
import { BubbleX } from '@/app/components/base/icons/src/vender/line/others'
-import { Button } from '@/app/components/base/ui/button'
import { useStore } from '@/app/components/workflow/store'
import useTheme from '@/hooks/use-theme'
diff --git a/web/app/components/workflow/header/checklist/__tests__/index.spec.tsx b/web/app/components/workflow/header/checklist/__tests__/index.spec.tsx
index 2c83747dc0..25150e2d04 100644
--- a/web/app/components/workflow/header/checklist/__tests__/index.spec.tsx
+++ b/web/app/components/workflow/header/checklist/__tests__/index.spec.tsx
@@ -47,7 +47,7 @@ vi.mock('../../../hooks', () => ({
}),
}))
-vi.mock('@/app/components/base/ui/popover', () => ({
+vi.mock('@langgenius/dify-ui/popover', () => ({
Popover: ({ children, onOpenChange }: PopoverProps) => {
latestOnOpenChange = onOpenChange
return {children}
diff --git a/web/app/components/workflow/header/checklist/__tests__/plugin-group.spec.tsx b/web/app/components/workflow/header/checklist/__tests__/plugin-group.spec.tsx
index 275c8727d1..78341c2174 100644
--- a/web/app/components/workflow/header/checklist/__tests__/plugin-group.spec.tsx
+++ b/web/app/components/workflow/header/checklist/__tests__/plugin-group.spec.tsx
@@ -1,7 +1,7 @@
import type { ChecklistItem } from '../../../hooks/use-checklist'
+import { Popover, PopoverContent } from '@langgenius/dify-ui/popover'
import { fireEvent, render, screen } from '@testing-library/react'
import { beforeEach, describe, expect, it } from 'vitest'
-import { Popover, PopoverContent } from '@/app/components/base/ui/popover'
import { useStore as usePluginDependencyStore } from '../../../plugin-dependency/store'
import { BlockEnum } from '../../../types'
import { ChecklistPluginGroup } from '../plugin-group'
diff --git a/web/app/components/workflow/header/checklist/index.tsx b/web/app/components/workflow/header/checklist/index.tsx
index 83384c5583..9a54175c8c 100644
--- a/web/app/components/workflow/header/checklist/index.tsx
+++ b/web/app/components/workflow/header/checklist/index.tsx
@@ -3,6 +3,12 @@ import type {
CommonEdgeType,
} from '../../types'
import { cn } from '@langgenius/dify-ui/cn'
+import {
+ Popover,
+ PopoverClose,
+ PopoverContent,
+ PopoverTrigger,
+} from '@langgenius/dify-ui/popover'
import {
memo,
useMemo,
@@ -12,12 +18,6 @@ import { useTranslation } from 'react-i18next'
import {
useEdges,
} from 'reactflow'
-import {
- Popover,
- PopoverClose,
- PopoverContent,
- PopoverTrigger,
-} from '@/app/components/base/ui/popover'
import useNodes from '@/app/components/workflow/store/workflow/use-nodes'
import {
useChecklist,
diff --git a/web/app/components/workflow/header/checklist/plugin-group.tsx b/web/app/components/workflow/header/checklist/plugin-group.tsx
index f74b314a10..91bc385595 100644
--- a/web/app/components/workflow/header/checklist/plugin-group.tsx
+++ b/web/app/components/workflow/header/checklist/plugin-group.tsx
@@ -1,10 +1,10 @@
import type { ChecklistItem } from '../../hooks/use-checklist'
import type { BlockEnum } from '../../types'
import type { Dependency } from '@/app/components/plugins/types'
+import { Button } from '@langgenius/dify-ui/button'
+import { PopoverClose } from '@langgenius/dify-ui/popover'
import { memo, useMemo } from 'react'
import { useTranslation } from 'react-i18next'
-import { Button } from '@/app/components/base/ui/button'
-import { PopoverClose } from '@/app/components/base/ui/popover'
import BlockIcon from '../../block-icon'
import { useStore as usePluginDependencyStore } from '../../plugin-dependency/store'
import { ItemIndicator } from './item-indicator'
diff --git a/web/app/components/workflow/header/env-button.tsx b/web/app/components/workflow/header/env-button.tsx
index 98ab6ba537..eec4af84c8 100644
--- a/web/app/components/workflow/header/env-button.tsx
+++ b/web/app/components/workflow/header/env-button.tsx
@@ -1,7 +1,7 @@
+import { Button } from '@langgenius/dify-ui/button'
import { cn } from '@langgenius/dify-ui/cn'
import { memo } from 'react'
import { Env } from '@/app/components/base/icons/src/vender/line/others'
-import { Button } from '@/app/components/base/ui/button'
import { useInputFieldPanel } from '@/app/components/rag-pipeline/hooks'
import { useStore } from '@/app/components/workflow/store'
import useTheme from '@/hooks/use-theme'
diff --git a/web/app/components/workflow/header/global-variable-button.tsx b/web/app/components/workflow/header/global-variable-button.tsx
index 63a4a427b6..f42677a3df 100644
--- a/web/app/components/workflow/header/global-variable-button.tsx
+++ b/web/app/components/workflow/header/global-variable-button.tsx
@@ -1,7 +1,7 @@
+import { Button } from '@langgenius/dify-ui/button'
import { cn } from '@langgenius/dify-ui/cn'
import { memo } from 'react'
import { GlobalVariable } from '@/app/components/base/icons/src/vender/line/others'
-import { Button } from '@/app/components/base/ui/button'
import { useInputFieldPanel } from '@/app/components/rag-pipeline/hooks'
import { useStore } from '@/app/components/workflow/store'
import useTheme from '@/hooks/use-theme'
diff --git a/web/app/components/workflow/header/header-in-restoring.tsx b/web/app/components/workflow/header/header-in-restoring.tsx
index e6ad4123ed..f07d28ff13 100644
--- a/web/app/components/workflow/header/header-in-restoring.tsx
+++ b/web/app/components/workflow/header/header-in-restoring.tsx
@@ -1,12 +1,12 @@
+import { Button } from '@langgenius/dify-ui/button'
import { cn } from '@langgenius/dify-ui/cn'
+import { toast } from '@langgenius/dify-ui/toast'
import { RiHistoryLine } from '@remixicon/react'
import {
useCallback,
} from 'react'
import { useTranslation } from 'react-i18next'
import { useFeaturesStore } from '@/app/components/base/features/hooks'
-import { Button } from '@/app/components/base/ui/button'
-import { toast } from '@/app/components/base/ui/toast'
import { useSelector as useAppContextSelector } from '@/context/app-context'
import useTheme from '@/hooks/use-theme'
import { useInvalidAllLastRun } from '@/service/use-workflow'
diff --git a/web/app/components/workflow/header/header-in-view-history.tsx b/web/app/components/workflow/header/header-in-view-history.tsx
index cd39ce605a..88077002c7 100644
--- a/web/app/components/workflow/header/header-in-view-history.tsx
+++ b/web/app/components/workflow/header/header-in-view-history.tsx
@@ -1,10 +1,10 @@
import type { ViewHistoryProps } from './view-history'
+import { Button } from '@langgenius/dify-ui/button'
import {
useCallback,
} from 'react'
import { useTranslation } from 'react-i18next'
import { ArrowNarrowLeft } from '@/app/components/base/icons/src/vender/line/arrows'
-import { Button } from '@/app/components/base/ui/button'
import Divider from '../../base/divider'
import {
useWorkflowRun,
diff --git a/web/app/components/workflow/header/online-users.tsx b/web/app/components/workflow/header/online-users.tsx
index cce2af0cdc..0f057f0787 100644
--- a/web/app/components/workflow/header/online-users.tsx
+++ b/web/app/components/workflow/header/online-users.tsx
@@ -1,17 +1,17 @@
'use client'
import type { OnlineUser } from '../collaboration/types/collaboration'
import { ChevronDownIcon } from '@heroicons/react/20/solid'
+import { AvatarFallback, AvatarImage, AvatarRoot } from '@langgenius/dify-ui/avatar'
import { cn } from '@langgenius/dify-ui/cn'
-import { useEffect, useState } from 'react'
-import { useTranslation } from 'react-i18next'
-import { useReactFlow } from 'reactflow'
-import { AvatarFallback, AvatarImage, AvatarRoot } from '@/app/components/base/ui/avatar'
import {
Popover,
PopoverContent,
PopoverTrigger,
-} from '@/app/components/base/ui/popover'
-import { Tooltip, TooltipContent, TooltipTrigger } from '@/app/components/base/ui/tooltip'
+} from '@langgenius/dify-ui/popover'
+import { Tooltip, TooltipContent, TooltipTrigger } from '@langgenius/dify-ui/tooltip'
+import { useEffect, useState } from 'react'
+import { useTranslation } from 'react-i18next'
+import { useReactFlow } from 'reactflow'
import { useAppContext } from '@/context/app-context'
import { getAvatar } from '@/service/common'
import { useCollaboration } from '../collaboration/hooks/use-collaboration'
diff --git a/web/app/components/workflow/header/run-mode.tsx b/web/app/components/workflow/header/run-mode.tsx
index d98b4d6b18..923f6f0330 100644
--- a/web/app/components/workflow/header/run-mode.tsx
+++ b/web/app/components/workflow/header/run-mode.tsx
@@ -1,12 +1,12 @@
import type { TestRunMenuRef, TriggerOption } from './test-run-menu'
import { cn } from '@langgenius/dify-ui/cn'
+import { toast } from '@langgenius/dify-ui/toast'
import { RiLoader2Line, RiPlayLargeLine } from '@remixicon/react'
import * as React from 'react'
import { useCallback, useEffect, useRef } from 'react'
import { useTranslation } from 'react-i18next'
import { trackEvent } from '@/app/components/base/amplitude'
import { StopCircle } from '@/app/components/base/icons/src/vender/line/mediaAndDevices'
-import { toast } from '@/app/components/base/ui/toast'
import { useWorkflowRun, useWorkflowRunValidation, useWorkflowStartRun } from '@/app/components/workflow/hooks'
import ShortcutsName from '@/app/components/workflow/shortcuts-name'
import { useStore } from '@/app/components/workflow/store'
diff --git a/web/app/components/workflow/header/test-run-menu-helpers.tsx b/web/app/components/workflow/header/test-run-menu-helpers.tsx
index 4a25cd87a6..9f14190c54 100644
--- a/web/app/components/workflow/header/test-run-menu-helpers.tsx
+++ b/web/app/components/workflow/header/test-run-menu-helpers.tsx
@@ -1,12 +1,12 @@
/* eslint-disable react-refresh/only-export-components */
import type { MouseEvent, MouseEventHandler, ReactElement } from 'react'
import type { TriggerOption } from './test-run-menu'
+import { DropdownMenuItem } from '@langgenius/dify-ui/dropdown-menu'
import {
cloneElement,
isValidElement,
useEffect,
} from 'react'
-import { DropdownMenuItem } from '@/app/components/base/ui/dropdown-menu'
import ShortcutsName from '../shortcuts-name'
export type ShortcutMapping = {
diff --git a/web/app/components/workflow/header/test-run-menu.tsx b/web/app/components/workflow/header/test-run-menu.tsx
index 6540875e6b..ceaf38592f 100644
--- a/web/app/components/workflow/header/test-run-menu.tsx
+++ b/web/app/components/workflow/header/test-run-menu.tsx
@@ -1,7 +1,7 @@
import type { ShortcutMapping } from './test-run-menu-helpers'
+import { DropdownMenu, DropdownMenuContent, DropdownMenuGroup, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuTrigger } from '@langgenius/dify-ui/dropdown-menu'
import { forwardRef, useCallback, useImperativeHandle, useMemo, useState } from 'react'
import { useTranslation } from 'react-i18next'
-import { DropdownMenu, DropdownMenuContent, DropdownMenuGroup, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuTrigger } from '@/app/components/base/ui/dropdown-menu'
import { OptionRow, SingleOptionTrigger, useShortcutMenu } from './test-run-menu-helpers'
export enum TriggerType {
diff --git a/web/app/components/workflow/header/version-history-button.tsx b/web/app/components/workflow/header/version-history-button.tsx
index ef79c4e411..69dc2b4c4c 100644
--- a/web/app/components/workflow/header/version-history-button.tsx
+++ b/web/app/components/workflow/header/version-history-button.tsx
@@ -1,11 +1,11 @@
import type { FC } from 'react'
+import { Button } from '@langgenius/dify-ui/button'
import { cn } from '@langgenius/dify-ui/cn'
import { RiHistoryLine } from '@remixicon/react'
import { useKeyPress } from 'ahooks'
import * as React from 'react'
import { useCallback } from 'react'
import { useTranslation } from 'react-i18next'
-import { Button } from '@/app/components/base/ui/button'
import useTheme from '@/hooks/use-theme'
import Tooltip from '../../base/tooltip'
import ShortcutsName from '../shortcuts-name'
diff --git a/web/app/components/workflow/hooks/__tests__/use-checklist.spec.ts b/web/app/components/workflow/hooks/__tests__/use-checklist.spec.ts
index 891007ff0e..f6d2774611 100644
--- a/web/app/components/workflow/hooks/__tests__/use-checklist.spec.ts
+++ b/web/app/components/workflow/hooks/__tests__/use-checklist.spec.ts
@@ -89,7 +89,7 @@ vi.mock('../index', () => ({
useNodesMetaData: () => ({ nodes: [], nodesMap: mockNodesMap }),
}))
-vi.mock('@/app/components/base/ui/toast', () => ({
+vi.mock('@langgenius/dify-ui/toast', () => ({
toast: {
success: vi.fn(),
error: vi.fn(),
diff --git a/web/app/components/workflow/hooks/__tests__/use-leader-restore.spec.ts b/web/app/components/workflow/hooks/__tests__/use-leader-restore.spec.ts
index 5837f1484d..a050994f4c 100644
--- a/web/app/components/workflow/hooks/__tests__/use-leader-restore.spec.ts
+++ b/web/app/components/workflow/hooks/__tests__/use-leader-restore.spec.ts
@@ -66,7 +66,7 @@ vi.mock('@/app/components/base/features/hooks', () => ({
}),
}))
-vi.mock('@/app/components/base/ui/toast', () => ({
+vi.mock('@langgenius/dify-ui/toast', () => ({
toast: {
info: (...args: unknown[]) => mockToastInfo(...args),
},
diff --git a/web/app/components/workflow/hooks/__tests__/use-nodes-interactions.spec.ts b/web/app/components/workflow/hooks/__tests__/use-nodes-interactions.spec.ts
index 4117c85abd..41d1fb39d9 100644
--- a/web/app/components/workflow/hooks/__tests__/use-nodes-interactions.spec.ts
+++ b/web/app/components/workflow/hooks/__tests__/use-nodes-interactions.spec.ts
@@ -11,6 +11,14 @@ const mockHandleSyncWorkflowDraft = vi.hoisted(() => vi.fn())
const mockSaveStateToHistory = vi.hoisted(() => vi.fn())
const mockUndo = vi.hoisted(() => vi.fn())
const mockRedo = vi.hoisted(() => vi.fn())
+const mockHandleNodeIterationChildrenCopy = vi.hoisted(() => vi.fn(() => ({
+ copyChildren: [],
+ newIdMapping: {},
+})))
+const mockHandleNodeLoopChildrenCopy = vi.hoisted(() => vi.fn(() => ({
+ copyChildren: [],
+ newIdMapping: {},
+})))
const runtimeNodesMetaDataMap = vi.hoisted(() => ({
value: {} as Record,
}))
@@ -72,14 +80,14 @@ vi.mock('../use-inspect-vars-crud', () => ({
vi.mock('../../nodes/iteration/use-interactions', () => ({
useNodeIterationInteractions: () => ({
handleNodeIterationChildDrag: () => ({ restrictPosition: {} }),
- handleNodeIterationChildrenCopy: vi.fn(),
+ handleNodeIterationChildrenCopy: mockHandleNodeIterationChildrenCopy,
}),
}))
vi.mock('../../nodes/loop/use-interactions', () => ({
useNodeLoopInteractions: () => ({
handleNodeLoopChildDrag: () => ({ restrictPosition: {} }),
- handleNodeLoopChildrenCopy: vi.fn(),
+ handleNodeLoopChildrenCopy: mockHandleNodeLoopChildrenCopy,
}),
}))
@@ -673,4 +681,316 @@ describe('useNodesInteractions', () => {
expect(rfState.setNodes).toHaveBeenCalled()
})
+
+ // Paste title handling should preserve original names until the destination canvas conflicts.
+ describe('paste title handling', () => {
+ beforeEach(() => {
+ runtimeNodesMetaDataMap.value = {
+ [BlockEnum.Code]: {
+ defaultValue: {
+ type: BlockEnum.Code,
+ title: 'Code',
+ desc: '',
+ },
+ metaData: {
+ isSingleton: false,
+ },
+ },
+ }
+ })
+
+ it('preserves the original title when the destination canvas has no conflict', async () => {
+ currentNodes = [
+ createNode({
+ id: 'existing-node',
+ data: {
+ type: BlockEnum.Code,
+ title: 'Existing',
+ desc: '',
+ },
+ }),
+ ]
+ currentEdges = []
+ rfState.nodes = currentNodes as unknown as typeof rfState.nodes
+ rfState.edges = currentEdges as unknown as typeof rfState.edges
+
+ const { result, store } = renderWorkflowHook(() => useNodesInteractions(), {
+ historyStore: {
+ nodes: currentNodes,
+ edges: currentEdges,
+ },
+ })
+
+ store.setState({
+ clipboardElements: [
+ createNode({
+ id: 'clipboard-node',
+ data: {
+ type: BlockEnum.Code,
+ title: 'Clipboard',
+ desc: '',
+ },
+ }),
+ ] as never,
+ clipboardEdges: [] as never,
+ mousePosition: {
+ pageX: 60,
+ pageY: 80,
+ } as never,
+ })
+
+ await act(async () => {
+ await result.current.handleNodesPaste()
+ })
+
+ const pastedNodes = rfState.setNodes.mock.calls.at(-1)?.[0] as Node[]
+ const newNode = pastedNodes.find(node => node.id !== 'existing-node')
+
+ expect(newNode?.data.title).toBe('Clipboard')
+ })
+
+ it('renames pasted nodes only when the destination canvas already uses the title', async () => {
+ currentNodes = [
+ createNode({
+ id: 'existing-node',
+ data: {
+ type: BlockEnum.Code,
+ title: 'Clipboard',
+ desc: '',
+ },
+ }),
+ createNode({
+ id: 'existing-node-2',
+ data: {
+ type: BlockEnum.Code,
+ title: 'Clipboard (1)',
+ desc: '',
+ },
+ }),
+ ]
+ currentEdges = []
+ rfState.nodes = currentNodes as unknown as typeof rfState.nodes
+ rfState.edges = currentEdges as unknown as typeof rfState.edges
+
+ const { result, store } = renderWorkflowHook(() => useNodesInteractions(), {
+ historyStore: {
+ nodes: currentNodes,
+ edges: currentEdges,
+ },
+ })
+
+ store.setState({
+ clipboardElements: [
+ createNode({
+ id: 'clipboard-node',
+ data: {
+ type: BlockEnum.Code,
+ title: 'Clipboard',
+ desc: '',
+ },
+ }),
+ ] as never,
+ clipboardEdges: [] as never,
+ mousePosition: {
+ pageX: 60,
+ pageY: 80,
+ } as never,
+ })
+
+ await act(async () => {
+ await result.current.handleNodesPaste()
+ })
+
+ const pastedNodes = rfState.setNodes.mock.calls.at(-1)?.[0] as Node[]
+ const newNode = pastedNodes.find(node => !currentNodes.some(existingNode => existingNode.id === node.id))
+
+ expect(newNode?.data.title).toBe('Clipboard (2)')
+ })
+ })
+
+ // A copied container can still be selected on the source canvas during same-canvas paste.
+ describe('container paste target detection', () => {
+ beforeEach(() => {
+ runtimeNodesMetaDataMap.value = {
+ [BlockEnum.Iteration]: {
+ defaultValue: {
+ type: BlockEnum.Iteration,
+ title: 'Iteration',
+ desc: '',
+ _children: [],
+ },
+ metaData: {
+ isSingleton: false,
+ },
+ },
+ [BlockEnum.Loop]: {
+ defaultValue: {
+ type: BlockEnum.Loop,
+ title: 'Loop',
+ desc: '',
+ _children: [],
+ },
+ metaData: {
+ isSingleton: false,
+ },
+ },
+ }
+ })
+
+ it.each([
+ [BlockEnum.Iteration, 'iteration-source'],
+ [BlockEnum.Loop, 'loop-source'],
+ ])('pastes a copied %s as a top-level node when the source container remains selected', async (containerType, nodeId) => {
+ currentNodes = [
+ createNode({
+ id: nodeId,
+ position: { x: 20, y: 20 },
+ selected: true,
+ data: {
+ type: containerType,
+ title: containerType === BlockEnum.Iteration ? 'Iteration' : 'Loop',
+ desc: '',
+ _children: [],
+ },
+ }),
+ ]
+ currentEdges = []
+ rfState.nodes = currentNodes as unknown as typeof rfState.nodes
+ rfState.edges = currentEdges as unknown as typeof rfState.edges
+
+ const { result, store } = renderWorkflowHook(() => useNodesInteractions(), {
+ historyStore: {
+ nodes: currentNodes,
+ edges: currentEdges,
+ },
+ })
+
+ store.setState({
+ clipboardElements: [
+ createNode({
+ id: nodeId,
+ position: { x: 120, y: 120 },
+ data: {
+ type: containerType,
+ title: containerType === BlockEnum.Iteration ? 'Iteration' : 'Loop',
+ desc: '',
+ _children: [],
+ },
+ }),
+ ] as never,
+ clipboardEdges: [] as never,
+ mousePosition: {
+ pageX: 60,
+ pageY: 80,
+ } as never,
+ })
+
+ await act(async () => {
+ await result.current.handleNodesPaste()
+ })
+
+ const pastedNodes = rfState.setNodes.mock.calls.at(-1)?.[0] as Node[]
+ const newContainer = pastedNodes.find(node => node.id !== nodeId && node.data.type === containerType)
+
+ expect(newContainer).toBeDefined()
+ expect(newContainer?.parentId).toBeUndefined()
+ expect(newContainer?.data.isInIteration).toBeFalsy()
+ expect(newContainer?.data.isInLoop).toBeFalsy()
+ })
+ })
+
+ // Nested container paste restrictions should stay aligned with available block filtering.
+ describe('nested container paste restrictions', () => {
+ const disallowedNestedPasteNodeTypes = [
+ BlockEnum.End,
+ BlockEnum.Iteration,
+ BlockEnum.Loop,
+ BlockEnum.DataSource,
+ BlockEnum.KnowledgeBase,
+ BlockEnum.HumanInput,
+ ]
+
+ const createNodeMeta = (type: BlockEnum) => ({
+ defaultValue: {
+ type,
+ title: `${type} node`,
+ desc: '',
+ },
+ metaData: {
+ isSingleton: false,
+ },
+ })
+
+ const runDisallowedPasteScenario = async (containerType: BlockEnum.Iteration | BlockEnum.Loop, nodeType: BlockEnum) => {
+ runtimeNodesMetaDataMap.value = {
+ [nodeType]: createNodeMeta(nodeType),
+ }
+
+ const containerId = `${containerType}-container`
+ currentNodes = [
+ createNode({
+ id: containerId,
+ position: { x: 20, y: 20 },
+ selected: true,
+ data: {
+ type: containerType,
+ title: containerType === BlockEnum.Iteration ? 'Iteration' : 'Loop',
+ desc: '',
+ _children: [],
+ },
+ }),
+ ]
+ currentEdges = []
+ rfState.nodes = currentNodes as unknown as typeof rfState.nodes
+ rfState.edges = currentEdges as unknown as typeof rfState.edges
+
+ const { result, store } = renderWorkflowHook(() => useNodesInteractions(), {
+ historyStore: {
+ nodes: currentNodes,
+ edges: currentEdges,
+ },
+ })
+
+ store.setState({
+ clipboardElements: [
+ createNode({
+ id: `${nodeType}-clipboard-node`,
+ position: { x: 100, y: 100 },
+ data: {
+ type: nodeType,
+ title: `${nodeType} clipboard node`,
+ desc: '',
+ },
+ }),
+ ] as never,
+ clipboardEdges: [] as never,
+ mousePosition: {
+ pageX: 60,
+ pageY: 80,
+ } as never,
+ })
+
+ await act(async () => {
+ await result.current.handleNodesPaste()
+ })
+
+ const pastedNodes = rfState.setNodes.mock.calls.at(-1)?.[0] as Node[]
+
+ expect(pastedNodes).toHaveLength(1)
+ expect(pastedNodes[0]?.id).toBe(containerId)
+ expect(pastedNodes[0]?.data._children).toEqual([])
+ expect(pastedNodes.some(node => node.data.type === nodeType && node.parentId === containerId)).toBe(false)
+ }
+
+ it.each(disallowedNestedPasteNodeTypes)(
+ 'should not paste %s into an iteration container',
+ async (nodeType) => {
+ await runDisallowedPasteScenario(BlockEnum.Iteration, nodeType)
+ },
+ )
+
+ it('should not paste human-input into a loop container', async () => {
+ await runDisallowedPasteScenario(BlockEnum.Loop, BlockEnum.HumanInput)
+ })
+ })
})
diff --git a/web/app/components/workflow/hooks/__tests__/use-panel-interactions.spec.ts b/web/app/components/workflow/hooks/__tests__/use-panel-interactions.spec.ts
index 83c21fcb6a..e6efea2870 100644
--- a/web/app/components/workflow/hooks/__tests__/use-panel-interactions.spec.ts
+++ b/web/app/components/workflow/hooks/__tests__/use-panel-interactions.spec.ts
@@ -1,11 +1,22 @@
import type * as React from 'react'
+import { waitFor } from '@testing-library/react'
+import { createEdge, createNode } from '../../__tests__/fixtures'
import { renderWorkflowHook } from '../../__tests__/workflow-test-env'
import { usePanelInteractions } from '../use-panel-interactions'
describe('usePanelInteractions', () => {
let container: HTMLDivElement
+ let readTextMock: ReturnType
beforeEach(() => {
+ readTextMock = vi.fn().mockResolvedValue('')
+ Object.defineProperty(navigator, 'clipboard', {
+ configurable: true,
+ value: {
+ readText: readTextMock,
+ },
+ })
+
container = document.createElement('div')
container.id = 'workflow-container'
container.getBoundingClientRect = vi.fn().mockReturnValue({
@@ -65,6 +76,34 @@ describe('usePanelInteractions', () => {
}).toThrow()
})
+ it('handlePaneContextMenu should sync clipboard from navigator clipboard', async () => {
+ const clipboardNode = createNode({ id: 'clipboard-node' })
+ const clipboardEdge = createEdge({
+ id: 'clipboard-edge',
+ source: clipboardNode.id,
+ target: 'target-node',
+ })
+ readTextMock.mockResolvedValue(JSON.stringify({
+ kind: 'dify-workflow-clipboard',
+ version: '0.6.0',
+ nodes: [clipboardNode],
+ edges: [clipboardEdge],
+ }))
+
+ const { result, store } = renderWorkflowHook(() => usePanelInteractions())
+
+ result.current.handlePaneContextMenu({
+ preventDefault: vi.fn(),
+ clientX: 350,
+ clientY: 250,
+ } as unknown as React.MouseEvent)
+
+ await waitFor(() => {
+ expect(store.getState().clipboardElements).toEqual([clipboardNode])
+ expect(store.getState().clipboardEdges).toEqual([clipboardEdge])
+ })
+ })
+
it('handlePaneContextmenuCancel should clear panelMenu', () => {
const { result, store } = renderWorkflowHook(() => usePanelInteractions(), {
initialStoreState: { panelMenu: { top: 10, left: 20 } },
diff --git a/web/app/components/workflow/hooks/__tests__/use-shortcuts.spec.ts b/web/app/components/workflow/hooks/__tests__/use-shortcuts.spec.ts
index b3c63ff519..1769533221 100644
--- a/web/app/components/workflow/hooks/__tests__/use-shortcuts.spec.ts
+++ b/web/app/components/workflow/hooks/__tests__/use-shortcuts.spec.ts
@@ -11,10 +11,18 @@ type KeyPressRegistration = {
}
}
+type ReactFlowNodeMock = {
+ id: string
+ data: {
+ _isBundled?: boolean
+ }
+}
+
const keyPressRegistrations = vi.hoisted(() => [])
const mockZoomTo = vi.hoisted(() => vi.fn())
const mockGetZoom = vi.hoisted(() => vi.fn(() => 1))
const mockFitView = vi.hoisted(() => vi.fn())
+const mockGetNodes = vi.hoisted(() => vi.fn<() => ReactFlowNodeMock[]>(() => []))
const mockHandleNodesDelete = vi.hoisted(() => vi.fn())
const mockHandleEdgeDelete = vi.hoisted(() => vi.fn())
const mockHandleNodesCopy = vi.hoisted(() => vi.fn())
@@ -41,6 +49,7 @@ vi.mock('reactflow', () => ({
zoomTo: mockZoomTo,
getZoom: mockGetZoom,
fitView: mockFitView,
+ getNodes: mockGetNodes,
}),
}))
@@ -84,6 +93,14 @@ const createKeyboardEvent = (target: HTMLElement = document.body) => ({
target,
}) as unknown as KeyboardEvent
+const createSelectionMock = (commonAncestorContainer: Node): Selection => ({
+ isCollapsed: false,
+ rangeCount: 1,
+ getRangeAt: () => ({
+ commonAncestorContainer,
+ } as unknown as Range),
+} as unknown as Selection)
+
const findRegistration = (matcher: (registration: KeyPressRegistration) => boolean) => {
const registration = keyPressRegistrations.find(matcher)
expect(registration).toBeDefined()
@@ -94,6 +111,7 @@ describe('useShortcuts', () => {
beforeEach(() => {
keyPressRegistrations.length = 0
vi.clearAllMocks()
+ mockGetNodes.mockReturnValue([])
})
it('deletes selected nodes and edges only outside editable inputs', () => {
@@ -141,6 +159,35 @@ describe('useShortcuts', () => {
expect(mockHandleSyncWorkflowDraft).toHaveBeenCalledTimes(4)
})
+ it('copies bundled nodes even when an incidental text selection exists outside the workflow canvas', () => {
+ const getSelectionSpy = vi.spyOn(document, 'getSelection')
+ const textContainer = document.createElement('div')
+ const selectedText = document.createElement('span')
+ selectedText.textContent = 'Selected browser text'
+ textContainer.appendChild(selectedText)
+
+ getSelectionSpy.mockReturnValue(createSelectionMock(selectedText))
+ mockGetNodes.mockReturnValue([
+ {
+ id: 'bundled-node',
+ data: {
+ _isBundled: true,
+ },
+ },
+ ])
+
+ renderWorkflowHook(() => useShortcuts())
+
+ const copyShortcut = findRegistration(registration => registration.keyFilter === 'ctrl.c' || registration.keyFilter === 'meta.c')
+ const event = createKeyboardEvent()
+ copyShortcut.handler(event)
+
+ expect(event.preventDefault).toHaveBeenCalled()
+ expect(mockHandleNodesCopy).toHaveBeenCalledTimes(1)
+
+ getSelectionSpy.mockRestore()
+ })
+
it('dims on shift down, undims on shift up, and responds to zen toggle events', () => {
const { unmount } = renderWorkflowHook(() => useShortcuts())
diff --git a/web/app/components/workflow/hooks/use-checklist.ts b/web/app/components/workflow/hooks/use-checklist.ts
index abafcfd8eb..ae9cc0a4a2 100644
--- a/web/app/components/workflow/hooks/use-checklist.ts
+++ b/web/app/components/workflow/hooks/use-checklist.ts
@@ -16,6 +16,7 @@ import type { ModelItem } from '@/app/components/header/account-setting/model-pr
import type { Emoji } from '@/app/components/tools/types'
import type { DataSet } from '@/models/datasets'
import type { I18nKeysWithPrefix } from '@/types/i18n'
+import { toast } from '@langgenius/dify-ui/toast'
import { useQueries, useQueryClient } from '@tanstack/react-query'
import isDeepEqual from 'fast-deep-equal'
import {
@@ -27,7 +28,6 @@ import {
import { useTranslation } from 'react-i18next'
import { useEdges, useStoreApi } from 'reactflow'
import { useStore as useAppStore } from '@/app/components/app/store'
-import { toast } from '@/app/components/base/ui/toast'
import { ModelTypeEnum } from '@/app/components/header/account-setting/model-provider-page/declarations'
import { useModelList } from '@/app/components/header/account-setting/model-provider-page/hooks'
import useNodes from '@/app/components/workflow/store/workflow/use-nodes'
diff --git a/web/app/components/workflow/hooks/use-leader-restore.ts b/web/app/components/workflow/hooks/use-leader-restore.ts
index 048bb932e0..da0036a4ea 100644
--- a/web/app/components/workflow/hooks/use-leader-restore.ts
+++ b/web/app/components/workflow/hooks/use-leader-restore.ts
@@ -1,11 +1,11 @@
import type { RestoreCompleteData, RestoreIntentData, RestoreRequestData } from '../collaboration/types/collaboration'
import type { SyncCallback } from './use-nodes-sync-draft'
+import { toast } from '@langgenius/dify-ui/toast'
import { useCallback, useEffect, useRef } from 'react'
import { useTranslation } from 'react-i18next'
import { useReactFlow } from 'reactflow'
import { useStore as useAppStore } from '@/app/components/app/store'
import { useFeaturesStore } from '@/app/components/base/features/hooks'
-import { toast } from '@/app/components/base/ui/toast'
import { useGlobalPublicStore } from '@/context/global-public-context'
import { collaborationManager } from '../collaboration/core/collaboration-manager'
import { useWorkflowStore } from '../store'
diff --git a/web/app/components/workflow/hooks/use-nodes-interactions.ts b/web/app/components/workflow/hooks/use-nodes-interactions.ts
index 27b13bb42c..a29c88e9cb 100644
--- a/web/app/components/workflow/hooks/use-nodes-interactions.ts
+++ b/web/app/components/workflow/hooks/use-nodes-interactions.ts
@@ -13,6 +13,7 @@ import type { LoopNodeType } from '../nodes/loop/types'
import type { VariableAssignerNodeType } from '../nodes/variable-assigner/types'
import type { Edge, Node, OnNodeAdd } from '../types'
import type { RAGPipelineVariables } from '@/models/pipeline'
+import { toast } from '@langgenius/dify-ui/toast'
import { produce } from 'immer'
import { useCallback, useRef, useState } from 'react'
import { useTranslation } from 'react-i18next'
@@ -21,6 +22,7 @@ import {
getOutgoers,
useReactFlow,
} from 'reactflow'
+import { useGlobalPublicStore } from '@/context/global-public-context'
import { collaborationManager } from '../collaboration/core/collaboration-manager'
import {
CUSTOM_EDGE,
@@ -48,6 +50,12 @@ import {
getNodeCustomTypeByNodeDataType,
getNodesConnectedSourceOrTargetHandleIdsMap,
getTopLeftNodePosition,
+ isClipboardEdgeStructurallyValid,
+ isClipboardNodeStructurallyValid,
+ isClipboardValueCompatibleWithDefault,
+ readWorkflowClipboard,
+ sanitizeClipboardValueByDefault,
+ writeWorkflowClipboard,
} from '../utils'
import { useWorkflowHistoryStore } from '../workflow-history-store'
import { useAutoGenerateWebhookUrl } from './use-auto-generate-webhook-url'
@@ -75,8 +83,62 @@ const ENTRY_NODE_WRAPPER_OFFSET = {
y: 21, // Adjusted based on visual testing feedback
} as const
+const pruneClipboardNodesWithFilteredAncestors = (
+ sourceNodes: Node[],
+ candidateNodes: Node[],
+): Node[] => {
+ const candidateNodeIds = new Set(candidateNodes.map(node => node.id))
+ const filteredRootIds = sourceNodes
+ .filter(node => !candidateNodeIds.has(node.id))
+ .map(node => node.id)
+
+ if (!filteredRootIds.length)
+ return candidateNodes
+
+ const childrenByParent = new Map()
+ sourceNodes.forEach((node) => {
+ if (!node.parentId)
+ return
+
+ const children = childrenByParent.get(node.parentId) ?? []
+ children.push(node.id)
+ childrenByParent.set(node.parentId, children)
+ })
+
+ const filteredNodeIds = new Set(filteredRootIds)
+ const queue = [...filteredRootIds]
+
+ while (queue.length) {
+ const currentNodeId = queue.shift()!
+ const children = childrenByParent.get(currentNodeId) ?? []
+ children.forEach((childId) => {
+ if (filteredNodeIds.has(childId))
+ return
+
+ filteredNodeIds.add(childId)
+ queue.push(childId)
+ })
+ }
+
+ return candidateNodes.filter(node => !filteredNodeIds.has(node.id))
+}
+
+const getUniquePastedNodeTitle = (
+ sourceTitle: string,
+ reservedTitles: Set,
+) => {
+ let titleCandidate = sourceTitle
+
+ while (reservedTitles.has(titleCandidate))
+ titleCandidate = genNewNodeTitleFromOld(titleCandidate)
+
+ reservedTitles.add(titleCandidate)
+ return titleCandidate
+}
+
export const useNodesInteractions = () => {
const { t } = useTranslation()
+ const appDslVersion = useGlobalPublicStore(s => s.systemFeatures.app_dsl_version)
const collaborativeWorkflow = useCollaborativeWorkflow()
const workflowStore = useWorkflowStore()
const reactflow = useReactFlow()
@@ -445,13 +507,11 @@ export const useNodesInteractions = () => {
}
if (
- edges.find(
- edge =>
- edge.source === source
- && edge.sourceHandle === sourceHandle
- && edge.target === target
- && edge.targetHandle === targetHandle,
- )
+ edges.some(edge =>
+ edge.source === source
+ && edge.sourceHandle === sourceHandle
+ && edge.target === target
+ && edge.targetHandle === targetHandle)
) {
return
}
@@ -769,9 +829,7 @@ export const useNodesInteractions = () => {
const newEdges = produce(edges, (draft) => {
return draft.filter(
edge =>
- !connectedEdges.find(
- connectedEdge => connectedEdge.id === edge.id,
- ),
+ !connectedEdges.some(connectedEdge => connectedEdge.id === edge.id),
)
})
setEdges(newEdges)
@@ -849,7 +907,7 @@ export const useNodesInteractions = () => {
const outgoers = getOutgoers(prevNode!, nodes, edges).sort(
(a, b) => a.position.y - b.position.y,
)
- const lastOutgoer = outgoers[outgoers.length - 1]
+ const lastOutgoer = outgoers.at(-1)
newNode.data._connectedTargetHandleIds
= nodeType === BlockEnum.DataSource ? [] : [targetHandle]
@@ -1577,9 +1635,7 @@ export const useNodesInteractions = () => {
setNodes(newNodes)
const remainingEdges = edges.filter(
edge =>
- !connectedEdges.find(
- connectedEdge => connectedEdge.id === edge.id,
- ),
+ !connectedEdges.some(connectedEdge => connectedEdge.id === edge.id),
)
setEdges([...remainingEdges, ...reconnectedEdges])
if (nodeType === BlockEnum.TriggerWebhook) {
@@ -1649,131 +1705,378 @@ export const useNodesInteractions = () => {
[workflowStore, handleNodeSelect],
)
+ const isNodeCopyable = useCallback((node: Node) => {
+ if (
+ node.type === CUSTOM_ITERATION_START_NODE
+ || node.type === CUSTOM_LOOP_START_NODE
+ ) {
+ return false
+ }
+
+ if (
+ node.data.type === BlockEnum.Start
+ || node.data.type === BlockEnum.LoopEnd
+ || node.data.type === BlockEnum.KnowledgeBase
+ || node.data.type === BlockEnum.DataSourceEmpty
+ ) {
+ return false
+ }
+
+ if (node.type === CUSTOM_NOTE_NODE)
+ return true
+
+ const nodeMeta = nodesMetaDataMap?.[node.data.type as BlockEnum]
+ if (!nodeMeta)
+ return false
+
+ const { metaData } = nodeMeta
+ return !metaData.isSingleton
+ }, [nodesMetaDataMap])
+
+ const getNodeDefaultValueForPaste = useCallback((node: Node) => {
+ if (node.type === CUSTOM_NOTE_NODE)
+ return {}
+
+ const nodeMeta = nodesMetaDataMap?.[node.data.type as BlockEnum]
+ return nodeMeta?.defaultValue
+ }, [nodesMetaDataMap])
+
const handleNodesCopy = useCallback(
(nodeId?: string) => {
if (getNodesReadOnly())
return
- const { setClipboardElements } = workflowStore.getState()
-
- const { nodes } = collaborativeWorkflow.getState()
+ const { setClipboardData } = workflowStore.getState()
+ const { nodes, edges } = collaborativeWorkflow.getState()
+ let nodesToCopy: Node[] = []
if (nodeId) {
- // If nodeId is provided, copy that specific node
- const nodeToCopy = nodes.find(
- node =>
- node.id === nodeId
- && node.data.type !== BlockEnum.Start
- && node.type !== CUSTOM_ITERATION_START_NODE
- && node.type !== CUSTOM_LOOP_START_NODE
- && node.data.type !== BlockEnum.LoopEnd
- && node.data.type !== BlockEnum.KnowledgeBase
- && node.data.type !== BlockEnum.DataSourceEmpty,
- )
+ const nodeToCopy = nodes.find(node => node.id === nodeId && isNodeCopyable(node))
if (nodeToCopy)
- setClipboardElements([nodeToCopy])
+ nodesToCopy = [nodeToCopy]
}
else {
- // If no nodeId is provided, fall back to the current behavior
const bundledNodes = nodes.filter((node) => {
if (!node.data._isBundled)
return false
+
+ if (!isNodeCopyable(node))
+ return false
+
if (node.type === CUSTOM_NOTE_NODE)
return true
- const metaData = nodesMetaDataMap?.[node.data.type as BlockEnum]?.metaData
- if (!metaData)
- return false
- if (metaData.isSingleton)
- return false
return !node.data.isInIteration && !node.data.isInLoop
})
if (bundledNodes.length) {
- setClipboardElements(bundledNodes)
- return
+ nodesToCopy = bundledNodes
}
+ else {
+ const selectedNodes = nodes.filter(
+ node => node.data.selected && isNodeCopyable(node),
+ )
- const selectedNode = nodes.find((node) => {
- if (!node.data.selected)
- return false
- if (node.type === CUSTOM_NOTE_NODE)
- return true
- const metaData = nodesMetaDataMap?.[node.data.type as BlockEnum]?.metaData
- if (!metaData)
- return false
- return !metaData.isSingleton
- })
-
- if (selectedNode)
- setClipboardElements([selectedNode])
+ if (selectedNodes.length)
+ nodesToCopy = selectedNodes
+ }
}
+
+ if (!nodesToCopy.length)
+ return
+
+ const copiedNodesMap = new Map(nodesToCopy.map(node => [node.id, node]))
+ const queue = nodesToCopy
+ .filter(node => node.data.type === BlockEnum.Iteration || node.data.type === BlockEnum.Loop)
+ .map(node => node.id)
+
+ while (queue.length) {
+ const parentId = queue.shift()!
+ nodes.forEach((node) => {
+ if (node.parentId !== parentId || copiedNodesMap.has(node.id))
+ return
+
+ copiedNodesMap.set(node.id, node)
+ if (node.data.type === BlockEnum.Iteration || node.data.type === BlockEnum.Loop)
+ queue.push(node.id)
+ })
+ }
+
+ const copiedNodes = [...copiedNodesMap.values()]
+ const copiedNodeIds = new Set(copiedNodes.map(node => node.id))
+ const copiedEdges = edges.filter(
+ edge => copiedNodeIds.has(edge.source) && copiedNodeIds.has(edge.target),
+ )
+
+ const clipboardData = {
+ nodes: copiedNodes,
+ edges: copiedEdges,
+ }
+
+ setClipboardData(clipboardData)
+ void writeWorkflowClipboard(clipboardData, appDslVersion).catch(() => {})
},
- [getNodesReadOnly, collaborativeWorkflow, workflowStore],
+ [getNodesReadOnly, workflowStore, collaborativeWorkflow, isNodeCopyable, appDslVersion],
)
- const handleNodesPaste = useCallback(() => {
+ const handleNodesPaste = useCallback(async () => {
if (getNodesReadOnly())
return
- const { clipboardElements, mousePosition } = workflowStore.getState()
+ const {
+ clipboardElements: storeClipboardElements,
+ clipboardEdges: storeClipboardEdges,
+ mousePosition,
+ setClipboardData,
+ } = workflowStore.getState()
+ const clipboardData = await readWorkflowClipboard(appDslVersion)
+ const hasSystemClipboard = clipboardData.nodes.length > 0
+ const shouldRunCompatibilityCheck = hasSystemClipboard && clipboardData.isVersionMismatch
+
+ const clipboardElements = hasSystemClipboard
+ ? clipboardData.nodes
+ : storeClipboardElements
+ const clipboardEdges = hasSystemClipboard
+ ? clipboardData.edges
+ : storeClipboardEdges
+
+ if (hasSystemClipboard)
+ setClipboardData(clipboardData)
+
+ const validatedClipboardElements = clipboardElements.filter(isClipboardNodeStructurallyValid)
+ const validatedClipboardEdges = clipboardEdges.filter(isClipboardEdgeStructurallyValid)
+
+ if (!validatedClipboardElements.length)
+ return
const { nodes, setNodes, edges, setEdges } = collaborativeWorkflow.getState()
+ const reservedNodeTitles = new Set(
+ nodes
+ .map(node => node.data.title)
+ .filter((title): title is string => typeof title === 'string'),
+ )
const nodesToPaste: Node[] = []
const edgesToPaste: Edge[] = []
- if (clipboardElements.length) {
- const { x, y } = getTopLeftNodePosition(clipboardElements)
- const { screenToFlowPosition } = reactflow
- const currentPosition = screenToFlowPosition({
- x: mousePosition.pageX,
- y: mousePosition.pageY,
- })
- const offsetX = currentPosition.x - x
- const offsetY = currentPosition.y - y
- let idMapping: Record = {}
- const pastedNodesMap: Record = {}
- const parentChildrenToAppend: { parentId: string, childId: string, childType: BlockEnum }[] = []
- clipboardElements.forEach((nodeToPaste, index) => {
- const nodeType = nodeToPaste.data.type
- const nodeDefaultValue = nodeToPaste.type !== CUSTOM_NOTE_NODE
- ? nodesMetaDataMap?.[nodeType]?.defaultValue
- : undefined
+ let compatibleClipboardElements = validatedClipboardElements.filter((node) => {
+ if (node.type === CUSTOM_NOTE_NODE)
+ return true
- const { newNode, newIterationStartNode, newLoopStartNode }
- = generateNewNode({
- type: nodeToPaste.type,
- data: {
- ...(nodeDefaultValue || {}),
- ...nodeToPaste.data,
- selected: false,
- _isBundled: false,
- _connectedSourceHandleIds: [],
- _connectedTargetHandleIds: [],
- _dimmed: false,
- title: genNewNodeTitleFromOld(nodeToPaste.data.title),
- },
- position: {
- x: nodeToPaste.position.x + offsetX,
- y: nodeToPaste.position.y + offsetY,
- },
- extent: nodeToPaste.extent,
- zIndex: nodeToPaste.zIndex,
+ const nodeDefaultValue = getNodeDefaultValueForPaste(node)
+ if (!nodeDefaultValue)
+ return false
+
+ if (
+ shouldRunCompatibilityCheck
+ && !isClipboardValueCompatibleWithDefault(nodeDefaultValue, node.data)
+ ) {
+ return false
+ }
+
+ return true
+ })
+
+ if (shouldRunCompatibilityCheck) {
+ compatibleClipboardElements = pruneClipboardNodesWithFilteredAncestors(
+ validatedClipboardElements,
+ compatibleClipboardElements,
+ )
+ }
+
+ const compatibleClipboardNodeIds = new Set(
+ compatibleClipboardElements.map(node => node.id),
+ )
+ const filteredNodeCount = shouldRunCompatibilityCheck
+ ? validatedClipboardElements.length - compatibleClipboardElements.length
+ : 0
+ const filteredEdgeCount = shouldRunCompatibilityCheck
+ ? validatedClipboardEdges.filter(edge =>
+ !compatibleClipboardNodeIds.has(edge.source)
+ || !compatibleClipboardNodeIds.has(edge.target),
+ ).length
+ : 0
+
+ if (
+ shouldRunCompatibilityCheck
+ && (filteredNodeCount > 0 || filteredEdgeCount > 0)
+ ) {
+ toast.warning(t('common.clipboardVersionCompatibilityWarning', {
+ ns: 'workflow',
+ }))
+ }
+
+ if (!compatibleClipboardElements.length)
+ return
+
+ const rootClipboardNodes = compatibleClipboardElements.filter(
+ node => !node.parentId || !compatibleClipboardNodeIds.has(node.parentId),
+ )
+ const positionReferenceNodes = rootClipboardNodes.length
+ ? rootClipboardNodes
+ : compatibleClipboardElements
+ const { x, y } = getTopLeftNodePosition(positionReferenceNodes)
+ const { screenToFlowPosition } = reactflow
+ const currentPosition = screenToFlowPosition({
+ x: mousePosition.pageX,
+ y: mousePosition.pageY,
+ })
+ const offsetX = currentPosition.x - x
+ const offsetY = currentPosition.y - y
+ let idMapping: Record = {}
+ const pastedNodesMap: Record = {}
+ const parentChildrenToAppend: { parentId: string, childId: string, childType: BlockEnum }[] = []
+ const selectedNodes = nodes.filter(node => node.selected)
+ // Keep this list aligned with availableBlocksFilter(inContainer)
+ // in use-available-blocks.ts.
+ const commonNestedDisallowPasteNodes = [
+ BlockEnum.End,
+ BlockEnum.Iteration,
+ BlockEnum.Loop,
+ BlockEnum.DataSource,
+ BlockEnum.KnowledgeBase,
+ BlockEnum.HumanInput,
+ ]
+ // Same-canvas copy keeps the source container selected, so only treat a
+ // selected container as the paste target when it is not part of the clipboard.
+ const selectedContainerNode = selectedNodes.length === 1
+ && (selectedNodes[0]?.data.type === BlockEnum.Iteration || selectedNodes[0]?.data.type === BlockEnum.Loop)
+ && !compatibleClipboardNodeIds.has(selectedNodes[0].id)
+ ? selectedNodes[0]
+ : undefined
+
+ rootClipboardNodes.forEach((nodeToPaste, index) => {
+ const nodeDefaultValue = getNodeDefaultValueForPaste(nodeToPaste)
+ if (nodeToPaste.type !== CUSTOM_NOTE_NODE && !nodeDefaultValue)
+ return
+
+ if (selectedContainerNode && commonNestedDisallowPasteNodes.includes(nodeToPaste.data.type))
+ return
+
+ const mergedData = shouldRunCompatibilityCheck
+ ? sanitizeClipboardValueByDefault(nodeDefaultValue ?? {}, nodeToPaste.data) as Record
+ : {
+ ...(nodeToPaste.type !== CUSTOM_NOTE_NODE ? nodeDefaultValue : {}),
+ ...nodeToPaste.data,
+ }
+ const sourceTitle = typeof mergedData.title === 'string'
+ ? mergedData.title
+ : typeof nodeToPaste.data.title === 'string'
+ ? nodeToPaste.data.title
+ : 'Node'
+ const sourceDesc = typeof mergedData.desc === 'string'
+ ? mergedData.desc
+ : typeof nodeToPaste.data.desc === 'string'
+ ? nodeToPaste.data.desc
+ : ''
+
+ const { newNode, newIterationStartNode, newLoopStartNode }
+ = generateNewNode({
+ type: nodeToPaste.type,
+ data: {
+ ...mergedData,
+ type: nodeToPaste.data.type,
+ desc: sourceDesc,
+ selected: false,
+ _isBundled: false,
+ _connectedSourceHandleIds: [],
+ _connectedTargetHandleIds: [],
+ _dimmed: false,
+ isInIteration: false,
+ iteration_id: undefined,
+ isInLoop: false,
+ loop_id: undefined,
+ title: getUniquePastedNodeTitle(sourceTitle, reservedNodeTitles),
+ },
+ position: {
+ x: nodeToPaste.position.x + offsetX,
+ y: nodeToPaste.position.y + offsetY,
+ },
+ extent: nodeToPaste.extent,
+ zIndex: nodeToPaste.zIndex,
+ })
+ newNode.id = newNode.id + index
+
+ let newChildren: Node[] = []
+ if (nodeToPaste.data.type === BlockEnum.Iteration) {
+ if (newIterationStartNode) {
+ newIterationStartNode.parentId = newNode.id
+ const iterationNodeData = newNode.data as IterationNodeType
+ iterationNodeData.start_node_id = newIterationStartNode.id
+ }
+
+ const oldIterationStartNodeInClipboard = compatibleClipboardElements.find(
+ n =>
+ n.parentId === nodeToPaste.id
+ && n.type === CUSTOM_ITERATION_START_NODE,
+ )
+ if (oldIterationStartNodeInClipboard && newIterationStartNode)
+ idMapping[oldIterationStartNodeInClipboard.id] = newIterationStartNode.id
+
+ const copiedIterationChildren = compatibleClipboardElements.filter(
+ n =>
+ n.parentId === nodeToPaste.id
+ && n.type !== CUSTOM_ITERATION_START_NODE,
+ )
+ if (copiedIterationChildren.length) {
+ copiedIterationChildren.forEach((child, childIndex) => {
+ const childType = child.data.type
+ const childDefaultValue = getNodeDefaultValueForPaste(child)
+ if (child.type !== CUSTOM_NOTE_NODE && !childDefaultValue)
+ return
+
+ const mergedChildData = shouldRunCompatibilityCheck
+ ? sanitizeClipboardValueByDefault(childDefaultValue ?? {}, child.data) as Record
+ : {
+ ...(child.type !== CUSTOM_NOTE_NODE ? childDefaultValue : {}),
+ ...child.data,
+ }
+ const childSourceTitle = typeof mergedChildData.title === 'string'
+ ? mergedChildData.title
+ : typeof child.data.title === 'string'
+ ? child.data.title
+ : 'Node'
+ const childSourceDesc = typeof mergedChildData.desc === 'string'
+ ? mergedChildData.desc
+ : typeof child.data.desc === 'string'
+ ? child.data.desc
+ : ''
+
+ const { newNode: newChild } = generateNewNode({
+ type: child.type,
+ data: {
+ ...mergedChildData,
+ desc: childSourceDesc,
+ selected: false,
+ _isBundled: false,
+ _connectedSourceHandleIds: [],
+ _connectedTargetHandleIds: [],
+ _dimmed: false,
+ title: getUniquePastedNodeTitle(childSourceTitle, reservedNodeTitles),
+ isInIteration: true,
+ iteration_id: newNode.id,
+ isInLoop: false,
+ loop_id: undefined,
+ type: childType,
+ },
+ position: child.position,
+ positionAbsolute: child.positionAbsolute,
+ parentId: newNode.id,
+ extent: child.extent,
+ zIndex: ITERATION_CHILDREN_Z_INDEX,
+ })
+ newChild.id = `${newNode.id}${newChild.id + childIndex}`
+ idMapping[child.id] = newChild.id
+ newChildren.push(newChild)
})
- newNode.id = newNode.id + index
- // This new node is movable and can be placed anywhere
- let newChildren: Node[] = []
- if (nodeToPaste.data.type === BlockEnum.Iteration) {
- newIterationStartNode!.parentId = newNode.id;
- (newNode.data as IterationNodeType).start_node_id
- = newIterationStartNode!.id
-
+ }
+ else {
const oldIterationStartNode = nodes.find(
n =>
n.parentId === nodeToPaste.id
&& n.type === CUSTOM_ITERATION_START_NODE,
)
- idMapping[oldIterationStartNode!.id] = newIterationStartNode!.id
+ if (oldIterationStartNode && newIterationStartNode)
+ idMapping[oldIterationStartNode.id] = newIterationStartNode.id
const { copyChildren, newIdMapping }
= handleNodeIterationChildrenCopy(
@@ -1783,24 +2086,97 @@ export const useNodesInteractions = () => {
)
newChildren = copyChildren
idMapping = newIdMapping
- newChildren.forEach((child) => {
- newNode.data._children?.push({
- nodeId: child.id,
- nodeType: child.data.type,
- })
- })
- newChildren.push(newIterationStartNode!)
}
- else if (nodeToPaste.data.type === BlockEnum.Loop) {
- newLoopStartNode!.parentId = newNode.id;
- (newNode.data as LoopNodeType).start_node_id = newLoopStartNode!.id
+ newChildren.forEach((child) => {
+ newNode.data._children?.push({
+ nodeId: child.id,
+ nodeType: child.data.type,
+ })
+ })
+ if (newIterationStartNode)
+ newChildren.push(newIterationStartNode)
+ }
+ else if (nodeToPaste.data.type === BlockEnum.Loop) {
+ if (newLoopStartNode) {
+ newLoopStartNode.parentId = newNode.id
+ const loopNodeData = newNode.data as LoopNodeType
+ loopNodeData.start_node_id = newLoopStartNode.id
+ }
+
+ const oldLoopStartNodeInClipboard = compatibleClipboardElements.find(
+ n =>
+ n.parentId === nodeToPaste.id
+ && n.type === CUSTOM_LOOP_START_NODE,
+ )
+ if (oldLoopStartNodeInClipboard && newLoopStartNode)
+ idMapping[oldLoopStartNodeInClipboard.id] = newLoopStartNode.id
+
+ const copiedLoopChildren = compatibleClipboardElements.filter(
+ n =>
+ n.parentId === nodeToPaste.id
+ && n.type !== CUSTOM_LOOP_START_NODE,
+ )
+ if (copiedLoopChildren.length) {
+ copiedLoopChildren.forEach((child, childIndex) => {
+ const childType = child.data.type
+ const childDefaultValue = getNodeDefaultValueForPaste(child)
+ if (child.type !== CUSTOM_NOTE_NODE && !childDefaultValue)
+ return
+
+ const mergedChildData = shouldRunCompatibilityCheck
+ ? sanitizeClipboardValueByDefault(childDefaultValue ?? {}, child.data) as Record
+ : {
+ ...(child.type !== CUSTOM_NOTE_NODE ? childDefaultValue : {}),
+ ...child.data,
+ }
+ const childSourceTitle = typeof mergedChildData.title === 'string'
+ ? mergedChildData.title
+ : typeof child.data.title === 'string'
+ ? child.data.title
+ : 'Node'
+ const childSourceDesc = typeof mergedChildData.desc === 'string'
+ ? mergedChildData.desc
+ : typeof child.data.desc === 'string'
+ ? child.data.desc
+ : ''
+
+ const { newNode: newChild } = generateNewNode({
+ type: child.type,
+ data: {
+ ...mergedChildData,
+ desc: childSourceDesc,
+ selected: false,
+ _isBundled: false,
+ _connectedSourceHandleIds: [],
+ _connectedTargetHandleIds: [],
+ _dimmed: false,
+ title: getUniquePastedNodeTitle(childSourceTitle, reservedNodeTitles),
+ isInIteration: false,
+ iteration_id: undefined,
+ isInLoop: true,
+ loop_id: newNode.id,
+ type: childType,
+ },
+ position: child.position,
+ positionAbsolute: child.positionAbsolute,
+ parentId: newNode.id,
+ extent: child.extent,
+ zIndex: LOOP_CHILDREN_Z_INDEX,
+ })
+ newChild.id = `${newNode.id}${newChild.id + childIndex}`
+ idMapping[child.id] = newChild.id
+ newChildren.push(newChild)
+ })
+ }
+ else {
const oldLoopStartNode = nodes.find(
n =>
n.parentId === nodeToPaste.id
&& n.type === CUSTOM_LOOP_START_NODE,
)
- idMapping[oldLoopStartNode!.id] = newLoopStartNode!.id
+ if (oldLoopStartNode && newLoopStartNode)
+ idMapping[oldLoopStartNode.id] = newLoopStartNode.id
const { copyChildren, newIdMapping }
= handleNodeLoopChildrenCopy(
@@ -1810,148 +2186,124 @@ export const useNodesInteractions = () => {
)
newChildren = copyChildren
idMapping = newIdMapping
- newChildren.forEach((child) => {
- newNode.data._children?.push({
- nodeId: child.id,
- nodeType: child.data.type,
- })
+ }
+
+ newChildren.forEach((child) => {
+ newNode.data._children?.push({
+ nodeId: child.id,
+ nodeType: child.data.type,
})
- newChildren.push(newLoopStartNode!)
- }
- else {
- // Paste a single regular node. Loop/Iteration nodes are handled above.
- const selectedNode = nodes.find(node => node.selected)
- let pastedToNestedBlock = false
-
- if (selectedNode) {
- // Keep this list aligned with availableBlocksFilter(inContainer)
- // in use-available-blocks.ts.
- const commonNestedDisallowPasteNodes = [
- BlockEnum.End,
- BlockEnum.Iteration,
- BlockEnum.Loop,
- BlockEnum.DataSource,
- BlockEnum.KnowledgeBase,
- BlockEnum.HumanInput,
- ]
-
- if (commonNestedDisallowPasteNodes.includes(nodeToPaste.data.type))
- return
-
- // If a Loop/Iteration container is selected, paste into it as a child.
- if (selectedNode.data.type === BlockEnum.Iteration || selectedNode.data.type === BlockEnum.Loop) {
- const isIteration = selectedNode.data.type === BlockEnum.Iteration
-
- newNode.data.isInIteration = isIteration
- newNode.data.iteration_id = isIteration ? selectedNode.id : undefined
- newNode.data.isInLoop = !isIteration
- newNode.data.loop_id = !isIteration ? selectedNode.id : undefined
-
- newNode.parentId = selectedNode.id
- newNode.zIndex = isIteration ? ITERATION_CHILDREN_Z_INDEX : LOOP_CHILDREN_Z_INDEX
- newNode.positionAbsolute = {
- x: newNode.position.x,
- y: newNode.position.y,
- }
- // Rebase position into the selected container coordinate system.
- newNode.position = getNestedNodePosition(newNode, selectedNode)
-
- // Mirror native add behavior by appending parent._children.
- parentChildrenToAppend.push({ parentId: selectedNode.id, childId: newNode.id, childType: newNode.data.type })
-
- pastedToNestedBlock = true
- }
- }
-
- // Clear loop/iteration metadata when pasting outside nested blocks (fixes #29835)
- // This ensures nodes copied from inside Loop/Iteration are properly independent
- // when pasted outside
- if (!pastedToNestedBlock) {
- newNode.data.isInLoop = false
- newNode.data.loop_id = undefined
- newNode.data.isInIteration = false
- newNode.data.iteration_id = undefined
- newNode.parentId = undefined
- }
- }
-
- idMapping[nodeToPaste.id] = newNode.id
- nodesToPaste.push(newNode)
- pastedNodesMap[newNode.id] = newNode
-
- if (newChildren.length) {
- newChildren.forEach((child) => {
- pastedNodesMap[child.id] = child
- })
- nodesToPaste.push(...newChildren)
- }
- })
-
- // Rebuild edges where both endpoints are part of the pasted set.
- edges.forEach((edge) => {
- const sourceId = idMapping[edge.source]
- const targetId = idMapping[edge.target]
-
- if (sourceId && targetId) {
- const sourceNode = pastedNodesMap[sourceId]
- const targetNode = pastedNodesMap[targetId]
- const parentNode = sourceNode?.parentId && sourceNode.parentId === targetNode?.parentId
- ? pastedNodesMap[sourceNode.parentId] ?? nodes.find(n => n.id === sourceNode.parentId)
- : null
- const isInIteration = parentNode?.data.type === BlockEnum.Iteration
- const isInLoop = parentNode?.data.type === BlockEnum.Loop
- const newEdge: Edge = {
- ...edge,
- id: `${sourceId}-${edge.sourceHandle}-${targetId}-${edge.targetHandle}`,
- source: sourceId,
- target: targetId,
- data: {
- ...edge.data,
- isInIteration,
- iteration_id: isInIteration ? parentNode?.id : undefined,
- isInLoop,
- loop_id: isInLoop ? parentNode?.id : undefined,
- _connectedNodeIsSelected: false,
- },
- zIndex: parentNode
- ? isInIteration
- ? ITERATION_CHILDREN_Z_INDEX
- : isInLoop
- ? LOOP_CHILDREN_Z_INDEX
- : 0
- : 0,
- }
- edgesToPaste.push(newEdge)
- }
- })
-
- const newNodes = produce(nodes, (draft: Node[]) => {
- parentChildrenToAppend.forEach(({ parentId, childId, childType }) => {
- const p = draft.find(n => n.id === parentId)
- if (p) {
- p.data._children?.push({ nodeId: childId, nodeType: childType })
- }
})
- draft.push(...nodesToPaste)
- })
+ if (newLoopStartNode)
+ newChildren.push(newLoopStartNode)
+ }
+ else if (selectedContainerNode) {
+ if (selectedContainerNode.data.type === BlockEnum.Iteration || selectedContainerNode.data.type === BlockEnum.Loop) {
+ const isIteration = selectedContainerNode.data.type === BlockEnum.Iteration
- setNodes(newNodes)
- setEdges([...edges, ...edgesToPaste])
- saveStateToHistory(WorkflowHistoryEvent.NodePaste, {
- nodeId: nodesToPaste?.[0]?.id,
+ newNode.data.isInIteration = isIteration
+ newNode.data.iteration_id = isIteration ? selectedContainerNode.id : undefined
+ newNode.data.isInLoop = !isIteration
+ newNode.data.loop_id = !isIteration ? selectedContainerNode.id : undefined
+
+ newNode.parentId = selectedContainerNode.id
+ newNode.zIndex = isIteration ? ITERATION_CHILDREN_Z_INDEX : LOOP_CHILDREN_Z_INDEX
+ newNode.positionAbsolute = {
+ x: newNode.position.x,
+ y: newNode.position.y,
+ }
+ newNode.position = getNestedNodePosition(newNode, selectedContainerNode)
+ parentChildrenToAppend.push({
+ parentId: selectedContainerNode.id,
+ childId: newNode.id,
+ childType: newNode.data.type,
+ })
+ }
+ }
+
+ idMapping[nodeToPaste.id] = newNode.id
+ nodesToPaste.push(newNode)
+ pastedNodesMap[newNode.id] = newNode
+
+ if (newChildren.length) {
+ newChildren.forEach((child) => {
+ pastedNodesMap[child.id] = child
+ })
+ nodesToPaste.push(...newChildren)
+ }
+ })
+
+ const sourceEdges = validatedClipboardEdges
+
+ sourceEdges.forEach((edge) => {
+ const sourceId = idMapping[edge.source]
+ const targetId = idMapping[edge.target]
+
+ if (sourceId && targetId) {
+ const sourceNode = pastedNodesMap[sourceId]
+ const targetNode = pastedNodesMap[targetId]
+ if (!sourceNode || !targetNode)
+ return
+
+ const parentNode = sourceNode.parentId && sourceNode.parentId === targetNode.parentId
+ ? pastedNodesMap[sourceNode.parentId] ?? nodes.find(n => n.id === sourceNode.parentId)
+ : null
+ const isInIteration = parentNode?.data.type === BlockEnum.Iteration
+ const isInLoop = parentNode?.data.type === BlockEnum.Loop
+ const newEdge: Edge = {
+ ...edge,
+ id: `${sourceId}-${edge.sourceHandle}-${targetId}-${edge.targetHandle}`,
+ source: sourceId,
+ target: targetId,
+ data: {
+ ...(edge.data || {}),
+ sourceType: sourceNode.data.type,
+ targetType: targetNode.data.type,
+ isInIteration,
+ iteration_id: isInIteration ? parentNode?.id : undefined,
+ isInLoop,
+ loop_id: isInLoop ? parentNode?.id : undefined,
+ _connectedNodeIsSelected: false,
+ },
+ zIndex: parentNode
+ ? isInIteration
+ ? ITERATION_CHILDREN_Z_INDEX
+ : isInLoop
+ ? LOOP_CHILDREN_Z_INDEX
+ : 0
+ : 0,
+ }
+ edgesToPaste.push(newEdge)
+ }
+ })
+
+ const newNodes = produce(nodes, (draft: Node[]) => {
+ parentChildrenToAppend.forEach(({ parentId, childId, childType }) => {
+ const p = draft.find(n => n.id === parentId)
+ if (p)
+ p.data._children?.push({ nodeId: childId, nodeType: childType })
})
- handleSyncWorkflowDraft()
- }
+ draft.push(...nodesToPaste)
+ })
+
+ setNodes(newNodes)
+ setEdges([...edges, ...edgesToPaste])
+ saveStateToHistory(WorkflowHistoryEvent.NodePaste, {
+ nodeId: nodesToPaste?.[0]?.id,
+ })
+ handleSyncWorkflowDraft()
}, [
getNodesReadOnly,
workflowStore,
collaborativeWorkflow,
reactflow,
+ t,
saveStateToHistory,
handleSyncWorkflowDraft,
handleNodeIterationChildrenCopy,
handleNodeLoopChildrenCopy,
- nodesMetaDataMap,
+ getNodeDefaultValueForPaste,
+ appDslVersion,
])
const handleNodesDuplicate = useCallback(
@@ -2091,9 +2443,7 @@ export const useNodesInteractions = () => {
const newEdges = produce(edges, (draft) => {
return draft.filter(
edge =>
- !connectedEdges.find(
- connectedEdge => connectedEdge.id === edge.id,
- ),
+ !connectedEdges.some(connectedEdge => connectedEdge.id === edge.id),
)
})
setEdges(newEdges)
diff --git a/web/app/components/workflow/hooks/use-panel-interactions.ts b/web/app/components/workflow/hooks/use-panel-interactions.ts
index 469a7abdee..37120068d4 100644
--- a/web/app/components/workflow/hooks/use-panel-interactions.ts
+++ b/web/app/components/workflow/hooks/use-panel-interactions.ts
@@ -1,12 +1,23 @@
import type { MouseEvent } from 'react'
import { useCallback } from 'react'
+import { useGlobalPublicStore } from '@/context/global-public-context'
import { useWorkflowStore } from '../store'
+import { readWorkflowClipboard } from '../utils'
export const usePanelInteractions = () => {
const workflowStore = useWorkflowStore()
+ const appDslVersion = useGlobalPublicStore(s => s.systemFeatures.app_dsl_version)
const handlePaneContextMenu = useCallback((e: MouseEvent) => {
e.preventDefault()
+ // Sync the latest system clipboard into the workflow store before opening
+ // the pane menu because "Paste here" is disabled when no compatible node
+ // copy exists, including cross-app copies written outside this tab.
+ void readWorkflowClipboard(appDslVersion).then(({ nodes, edges }) => {
+ if (nodes.length)
+ workflowStore.getState().setClipboardData({ nodes, edges })
+ })
+
const container = document.querySelector('#workflow-container')
const { x, y } = container!.getBoundingClientRect()
workflowStore.setState({
@@ -18,7 +29,7 @@ export const usePanelInteractions = () => {
left: e.clientX - x,
},
})
- }, [workflowStore])
+ }, [workflowStore, appDslVersion])
const handlePaneContextmenuCancel = useCallback(() => {
workflowStore.setState({
diff --git a/web/app/components/workflow/hooks/use-shortcuts.ts b/web/app/components/workflow/hooks/use-shortcuts.ts
index 8ba7fa4602..e4100908ff 100644
--- a/web/app/components/workflow/hooks/use-shortcuts.ts
+++ b/web/app/components/workflow/hooks/use-shortcuts.ts
@@ -46,6 +46,7 @@ export const useShortcuts = (): void => {
zoomTo,
getZoom,
fitView,
+ getNodes,
} = useReactFlow()
// Zoom out to a minimum of 0.25 for shortcut
@@ -67,9 +68,14 @@ export const useShortcuts = (): void => {
}, [])
const shouldHandleCopy = useCallback(() => {
+ // Box selection can leave incidental DOM text selection behind while the
+ // workflow selection itself lives on node.data._isBundled.
+ if (getNodes().some(node => node.data._isBundled))
+ return true
+
const selection = document.getSelection()
- return !selection || selection.isCollapsed
- }, [])
+ return !selection || selection.isCollapsed || !selection.rangeCount
+ }, [getNodes])
useKeyPress(['delete', 'backspace'], (e) => {
if (shouldHandleShortcut(e)) {
diff --git a/web/app/components/workflow/index.tsx b/web/app/components/workflow/index.tsx
index 12122c5b72..c535196691 100644
--- a/web/app/components/workflow/index.tsx
+++ b/web/app/components/workflow/index.tsx
@@ -14,7 +14,17 @@ import type {
Node,
} from './types'
import type { VarInInspect } from '@/types/workflow'
+import {
+ AlertDialog,
+ AlertDialogActions,
+ AlertDialogCancelButton,
+ AlertDialogConfirmButton,
+ AlertDialogContent,
+ AlertDialogDescription,
+ AlertDialogTitle,
+} from '@langgenius/dify-ui/alert-dialog'
import { cn } from '@langgenius/dify-ui/cn'
+import { toast } from '@langgenius/dify-ui/toast'
import {
useEventListener,
} from 'ahooks'
@@ -41,16 +51,6 @@ import ReactFlow, {
useReactFlow,
useStoreApi,
} from 'reactflow'
-import {
- AlertDialog,
- AlertDialogActions,
- AlertDialogCancelButton,
- AlertDialogConfirmButton,
- AlertDialogContent,
- AlertDialogDescription,
- AlertDialogTitle,
-} from '@/app/components/base/ui/alert-dialog'
-import { toast } from '@/app/components/base/ui/toast'
import { IS_DEV } from '@/config'
import { useEventEmitterContextContext } from '@/context/event-emitter'
import {
diff --git a/web/app/components/workflow/nodes/_base/components/__tests__/file-support.spec.tsx b/web/app/components/workflow/nodes/_base/components/__tests__/file-support.spec.tsx
index b58b045f92..1a3e004c7e 100644
--- a/web/app/components/workflow/nodes/_base/components/__tests__/file-support.spec.tsx
+++ b/web/app/components/workflow/nodes/_base/components/__tests__/file-support.spec.tsx
@@ -19,7 +19,7 @@ vi.mock('@/app/components/base/file-uploader/hooks', () => ({
useFileSizeLimit: vi.fn(),
}))
-vi.mock('@/app/components/base/ui/toast', () => ({
+vi.mock('@langgenius/dify-ui/toast', () => ({
toast: {
success: vi.fn(),
error: vi.fn(),
diff --git a/web/app/components/workflow/nodes/_base/components/add-button.tsx b/web/app/components/workflow/nodes/_base/components/add-button.tsx
index cd32ee0dc6..d64ab5d098 100644
--- a/web/app/components/workflow/nodes/_base/components/add-button.tsx
+++ b/web/app/components/workflow/nodes/_base/components/add-button.tsx
@@ -1,11 +1,11 @@
'use client'
import type { FC } from 'react'
+import { Button } from '@langgenius/dify-ui/button'
import { cn } from '@langgenius/dify-ui/cn'
import {
RiAddLine,
} from '@remixicon/react'
import * as React from 'react'
-import { Button } from '@/app/components/base/ui/button'
type Props = {
className?: string
diff --git a/web/app/components/workflow/nodes/_base/components/agent-strategy.tsx b/web/app/components/workflow/nodes/_base/components/agent-strategy.tsx
index d13488a0b9..d20acd7f19 100644
--- a/web/app/components/workflow/nodes/_base/components/agent-strategy.tsx
+++ b/web/app/components/workflow/nodes/_base/components/agent-strategy.tsx
@@ -4,11 +4,6 @@ import type { NodeOutPutVar } from '../../../types'
import type { ToolVarInputs } from '../../tool/types'
import type { CredentialFormSchema, CredentialFormSchemaNumberInput, CredentialFormSchemaTextInput } from '@/app/components/header/account-setting/model-provider-page/declarations'
import type { PluginMeta } from '@/app/components/plugins/types'
-import { noop } from 'es-toolkit/function'
-import { memo } from 'react'
-import { useTranslation } from 'react-i18next'
-import { Agent } from '@/app/components/base/icons/src/vender/workflow'
-import ListEmpty from '@/app/components/base/list-empty'
import {
NumberField,
NumberFieldControls,
@@ -16,8 +11,13 @@ import {
NumberFieldGroup,
NumberFieldIncrement,
NumberFieldInput,
-} from '@/app/components/base/ui/number-field'
-import { Slider } from '@/app/components/base/ui/slider'
+} from '@langgenius/dify-ui/number-field'
+import { Slider } from '@langgenius/dify-ui/slider'
+import { noop } from 'es-toolkit/function'
+import { memo } from 'react'
+import { useTranslation } from 'react-i18next'
+import { Agent } from '@/app/components/base/icons/src/vender/workflow'
+import ListEmpty from '@/app/components/base/list-empty'
import { FormTypeEnum, ModelTypeEnum } from '@/app/components/header/account-setting/model-provider-page/declarations'
import { useDefaultModel } from '@/app/components/header/account-setting/model-provider-page/hooks'
import Form from '@/app/components/header/account-setting/model-provider-page/model-modal/Form'
diff --git a/web/app/components/workflow/nodes/_base/components/before-run-form/__tests__/index.spec.tsx b/web/app/components/workflow/nodes/_base/components/before-run-form/__tests__/index.spec.tsx
index a8837f6392..39b9fd8888 100644
--- a/web/app/components/workflow/nodes/_base/components/before-run-form/__tests__/index.spec.tsx
+++ b/web/app/components/workflow/nodes/_base/components/before-run-form/__tests__/index.spec.tsx
@@ -1,11 +1,11 @@
import type { Props as FormProps } from '../form'
import type { BeforeRunFormProps } from '../index'
+import { toast } from '@langgenius/dify-ui/toast'
import { fireEvent, render, screen } from '@testing-library/react'
-import { toast } from '@/app/components/base/ui/toast'
import { BlockEnum, InputVarType } from '@/app/components/workflow/types'
import BeforeRunForm from '../index'
-vi.mock('@/app/components/base/ui/toast', () => ({
+vi.mock('@langgenius/dify-ui/toast', () => ({
toast: {
error: vi.fn(),
},
diff --git a/web/app/components/workflow/nodes/_base/components/before-run-form/index.tsx b/web/app/components/workflow/nodes/_base/components/before-run-form/index.tsx
index f42046a2ad..f15c60e117 100644
--- a/web/app/components/workflow/nodes/_base/components/before-run-form/index.tsx
+++ b/web/app/components/workflow/nodes/_base/components/before-run-form/index.tsx
@@ -5,12 +5,12 @@ import type { Emoji } from '@/app/components/tools/types'
import type { SpecialResultPanelProps } from '@/app/components/workflow/run/special-result-panel'
import type { NodeRunningStatus } from '@/app/components/workflow/types'
import type { HumanInputFormData } from '@/types/workflow'
+import { Button } from '@langgenius/dify-ui/button'
import { cn } from '@langgenius/dify-ui/cn'
+import { toast } from '@langgenius/dify-ui/toast'
import * as React from 'react'
import { useEffect, useRef } from 'react'
import { useTranslation } from 'react-i18next'
-import { Button } from '@/app/components/base/ui/button'
-import { toast } from '@/app/components/base/ui/toast'
import Split from '@/app/components/workflow/nodes/_base/components/split'
import SingleRunForm from '@/app/components/workflow/nodes/human-input/components/single-run-form'
import { BlockEnum } from '@/app/components/workflow/types'
diff --git a/web/app/components/workflow/nodes/_base/components/config-vision.tsx b/web/app/components/workflow/nodes/_base/components/config-vision.tsx
index 109650cf51..d21e53368d 100644
--- a/web/app/components/workflow/nodes/_base/components/config-vision.tsx
+++ b/web/app/components/workflow/nodes/_base/components/config-vision.tsx
@@ -1,11 +1,11 @@
'use client'
import type { FC } from 'react'
import type { ValueSelector, Var, VisionSetting } from '@/app/components/workflow/types'
+import { Switch } from '@langgenius/dify-ui/switch'
import { produce } from 'immer'
import * as React from 'react'
import { useCallback } from 'react'
import { useTranslation } from 'react-i18next'
-import Switch from '@/app/components/base/switch'
import Tooltip from '@/app/components/base/tooltip'
import Field from '@/app/components/workflow/nodes/_base/components/field'
import ResolutionPicker from '@/app/components/workflow/nodes/llm/components/resolution-picker'
diff --git a/web/app/components/workflow/nodes/_base/components/error-handle/error-handle-type-selector.tsx b/web/app/components/workflow/nodes/_base/components/error-handle/error-handle-type-selector.tsx
index 92ca3ed335..07b6519e41 100644
--- a/web/app/components/workflow/nodes/_base/components/error-handle/error-handle-type-selector.tsx
+++ b/web/app/components/workflow/nodes/_base/components/error-handle/error-handle-type-selector.tsx
@@ -1,3 +1,4 @@
+import { Button } from '@langgenius/dify-ui/button'
import {
RiArrowDownSLine,
RiCheckLine,
@@ -9,7 +10,6 @@ import {
PortalToFollowElemContent,
PortalToFollowElemTrigger,
} from '@/app/components/base/portal-to-follow-elem'
-import { Button } from '@/app/components/base/ui/button'
import { ErrorHandleTypeEnum } from './types'
type ErrorHandleTypeSelectorProps = {
diff --git a/web/app/components/workflow/nodes/_base/components/input-number-with-slider.tsx b/web/app/components/workflow/nodes/_base/components/input-number-with-slider.tsx
index 4e253060d6..08afb50b80 100644
--- a/web/app/components/workflow/nodes/_base/components/input-number-with-slider.tsx
+++ b/web/app/components/workflow/nodes/_base/components/input-number-with-slider.tsx
@@ -1,8 +1,8 @@
'use client'
import type { FC } from 'react'
+import { Slider } from '@langgenius/dify-ui/slider'
import * as React from 'react'
import { useCallback } from 'react'
-import { Slider } from '@/app/components/base/ui/slider'
export type InputNumberWithSliderProps = {
value: number
diff --git a/web/app/components/workflow/nodes/_base/components/install-plugin-button.tsx b/web/app/components/workflow/nodes/_base/components/install-plugin-button.tsx
index f6e5434a2d..dd2611b395 100644
--- a/web/app/components/workflow/nodes/_base/components/install-plugin-button.tsx
+++ b/web/app/components/workflow/nodes/_base/components/install-plugin-button.tsx
@@ -1,9 +1,9 @@
import type { ComponentProps, MouseEventHandler } from 'react'
+import { Button } from '@langgenius/dify-ui/button'
import { cn } from '@langgenius/dify-ui/cn'
import { RiInstallLine, RiLoader2Line } from '@remixicon/react'
import { useState } from 'react'
import { useTranslation } from 'react-i18next'
-import { Button } from '@/app/components/base/ui/button'
import checkTaskStatus from '@/app/components/plugins/install-plugin/base/check-task-status'
import { TaskStatus } from '@/app/components/plugins/types'
import { useCheckInstalled, useInstallPackageFromMarketPlace } from '@/service/use-plugins'
diff --git a/web/app/components/workflow/nodes/_base/components/layout/__tests__/field-title.spec.tsx b/web/app/components/workflow/nodes/_base/components/layout/__tests__/field-title.spec.tsx
index abd630cccb..c11b5ecfc8 100644
--- a/web/app/components/workflow/nodes/_base/components/layout/__tests__/field-title.spec.tsx
+++ b/web/app/components/workflow/nodes/_base/components/layout/__tests__/field-title.spec.tsx
@@ -1,7 +1,7 @@
import { fireEvent, render, screen } from '@testing-library/react'
import { FieldTitle } from '../field-title'
-vi.mock('@/app/components/base/ui/tooltip', () => ({
+vi.mock('@langgenius/dify-ui/tooltip', () => ({
Tooltip: ({ children }: { children: React.ReactNode }) => {children}
,
TooltipTrigger: ({ render }: { render: React.ReactNode }) => <>{render}>,
TooltipContent: ({ children }: { children: React.ReactNode }) => {children}
,
diff --git a/web/app/components/workflow/nodes/_base/components/layout/field-title.tsx b/web/app/components/workflow/nodes/_base/components/layout/field-title.tsx
index 5a478c25d3..ce080fb6dd 100644
--- a/web/app/components/workflow/nodes/_base/components/layout/field-title.tsx
+++ b/web/app/components/workflow/nodes/_base/components/layout/field-title.tsx
@@ -1,10 +1,10 @@
import type { ReactNode } from 'react'
import { cn } from '@langgenius/dify-ui/cn'
+import { Tooltip, TooltipContent, TooltipTrigger } from '@langgenius/dify-ui/tooltip'
import {
memo,
useState,
} from 'react'
-import { Tooltip, TooltipContent, TooltipTrigger } from '@/app/components/base/ui/tooltip'
export type FieldTitleProps = {
title?: string
@@ -64,7 +64,6 @@ export const FieldTitle = memo(({
tooltip && (
diff --git a/web/app/components/workflow/nodes/_base/components/memory-config.tsx b/web/app/components/workflow/nodes/_base/components/memory-config.tsx
index 15254ad4f4..ed193490a5 100644
--- a/web/app/components/workflow/nodes/_base/components/memory-config.tsx
+++ b/web/app/components/workflow/nodes/_base/components/memory-config.tsx
@@ -2,13 +2,13 @@
import type { FC } from 'react'
import type { Memory } from '../../../types'
import { cn } from '@langgenius/dify-ui/cn'
+import { Slider } from '@langgenius/dify-ui/slider'
+import { Switch } from '@langgenius/dify-ui/switch'
import { produce } from 'immer'
import * as React from 'react'
import { useCallback } from 'react'
import { useTranslation } from 'react-i18next'
import Input from '@/app/components/base/input'
-import Switch from '@/app/components/base/switch'
-import { Slider } from '@/app/components/base/ui/slider'
import Field from '@/app/components/workflow/nodes/_base/components/field'
import { MemoryRole } from '../../../types'
diff --git a/web/app/components/workflow/nodes/_base/components/next-step/__tests__/operator.spec.tsx b/web/app/components/workflow/nodes/_base/components/next-step/__tests__/operator.spec.tsx
index 9afa29642d..62cf571ae0 100644
--- a/web/app/components/workflow/nodes/_base/components/next-step/__tests__/operator.spec.tsx
+++ b/web/app/components/workflow/nodes/_base/components/next-step/__tests__/operator.spec.tsx
@@ -18,7 +18,7 @@ vi.mock('react-i18next', () => ({
}),
}))
-vi.mock('@/app/components/base/ui/dropdown-menu', async () => {
+vi.mock('@langgenius/dify-ui/dropdown-menu', async () => {
const React = await import('react')
const DropdownMenuContext = React.createContext<{ open: boolean, setOpen: (open: boolean) => void } | null>(null)
@@ -49,7 +49,7 @@ vi.mock('@/app/components/base/ui/dropdown-menu', async () => {
}
})
-vi.mock('@/app/components/base/ui/button', () => ({
+vi.mock('@langgenius/dify-ui/button', () => ({
Button: ({ children, className }: { children: ReactNode, className?: string }) => (
{children}
diff --git a/web/app/components/workflow/nodes/_base/components/next-step/item.tsx b/web/app/components/workflow/nodes/_base/components/next-step/item.tsx
index eca48d85fd..aabb3e6ce1 100644
--- a/web/app/components/workflow/nodes/_base/components/next-step/item.tsx
+++ b/web/app/components/workflow/nodes/_base/components/next-step/item.tsx
@@ -1,6 +1,7 @@
import type {
CommonNodeType,
} from '@/app/components/workflow/types'
+import { Button } from '@langgenius/dify-ui/button'
import { cn } from '@langgenius/dify-ui/cn'
import {
memo,
@@ -8,7 +9,6 @@ import {
useState,
} from 'react'
import { useTranslation } from 'react-i18next'
-import { Button } from '@/app/components/base/ui/button'
import BlockIcon from '@/app/components/workflow/block-icon'
import {
useNodesInteractions,
diff --git a/web/app/components/workflow/nodes/_base/components/next-step/operator.tsx b/web/app/components/workflow/nodes/_base/components/next-step/operator.tsx
index c0a4f0b537..2dd45bbe3c 100644
--- a/web/app/components/workflow/nodes/_base/components/next-step/operator.tsx
+++ b/web/app/components/workflow/nodes/_base/components/next-step/operator.tsx
@@ -2,17 +2,17 @@ import type {
CommonNodeType,
OnSelectBlock,
} from '@/app/components/workflow/types'
+import { Button } from '@langgenius/dify-ui/button'
+import {
+ DropdownMenu,
+ DropdownMenuContent,
+ DropdownMenuTrigger,
+} from '@langgenius/dify-ui/dropdown-menu'
import { intersection } from 'es-toolkit/array'
import {
useCallback,
} from 'react'
import { useTranslation } from 'react-i18next'
-import { Button } from '@/app/components/base/ui/button'
-import {
- DropdownMenu,
- DropdownMenuContent,
- DropdownMenuTrigger,
-} from '@/app/components/base/ui/dropdown-menu'
import BlockSelector from '@/app/components/workflow/block-selector'
import {
useAvailableBlocks,
diff --git a/web/app/components/workflow/nodes/_base/components/node-control.tsx b/web/app/components/workflow/nodes/_base/components/node-control.tsx
index 547d0b1daa..439e097bc9 100644
--- a/web/app/components/workflow/nodes/_base/components/node-control.tsx
+++ b/web/app/components/workflow/nodes/_base/components/node-control.tsx
@@ -1,19 +1,16 @@
import type { FC } from 'react'
import type { Node } from '../../../types'
-import {
- memo,
- useCallback,
- useState,
-} from 'react'
-import { useTranslation } from 'react-i18next'
-import {
- Stop,
-} from '@/app/components/base/icons/src/vender/line/mediaAndDevices'
+import { cn } from '@langgenius/dify-ui/cn'
import {
Tooltip,
TooltipContent,
TooltipTrigger,
-} from '@/app/components/base/ui/tooltip'
+} from '@langgenius/dify-ui/tooltip'
+import { memo } from 'react'
+import { useTranslation } from 'react-i18next'
+import {
+ Stop,
+} from '@/app/components/base/icons/src/vender/line/mediaAndDevices'
import { useWorkflowStore } from '@/app/components/workflow/store'
import {
useNodesInteractions,
@@ -31,23 +28,19 @@ const NodeControl: FC = ({
pluginInstallLocked,
}) => {
const { t } = useTranslation()
- const [open, setOpen] = useState(false)
const { handleNodeSelect } = useNodesInteractions()
const workflowStore = useWorkflowStore()
const isSingleRunning = data._singleRunningStatus === NodeRunningStatus.Running
- const handleOpenChange = useCallback((newOpen: boolean) => {
- setOpen(newOpen)
- }, [])
const isChildNode = !!(data.isInIteration || data.isInLoop)
return (
= ({
id={id}
data={data}
offset={0}
- onOpenChange={handleOpenChange}
triggerClassName="w-5! h-5!"
/>
diff --git a/web/app/components/workflow/nodes/_base/components/panel-operator/index.tsx b/web/app/components/workflow/nodes/_base/components/panel-operator/index.tsx
index 7b3469aaba..ee16fd0c06 100644
--- a/web/app/components/workflow/nodes/_base/components/panel-operator/index.tsx
+++ b/web/app/components/workflow/nodes/_base/components/panel-operator/index.tsx
@@ -1,17 +1,17 @@
import type { OffsetOptions } from '@floating-ui/react'
import type { Node } from '@/app/components/workflow/types'
import { cn } from '@langgenius/dify-ui/cn'
+import {
+ DropdownMenu,
+ DropdownMenuContent,
+ DropdownMenuTrigger,
+} from '@langgenius/dify-ui/dropdown-menu'
import {
memo,
useCallback,
useState,
} from 'react'
import { useTranslation } from 'react-i18next'
-import {
- DropdownMenu,
- DropdownMenuContent,
- DropdownMenuTrigger,
-} from '@/app/components/base/ui/dropdown-menu'
import PanelOperatorPopup from './panel-operator-popup'
type PanelOperatorProps = {
@@ -20,7 +20,6 @@ type PanelOperatorProps = {
triggerClassName?: string
offset?: OffsetOptions | number
onOpenChange?: (open: boolean) => void
- inNode?: boolean
showHelpLink?: boolean
}
const PanelOperator = ({
@@ -45,15 +44,14 @@ const PanelOperator = ({
? offset.crossAxis
: 0
- const handleOpenChange = useCallback((newOpen: boolean) => {
- setOpen(newOpen)
-
- if (onOpenChange)
- onOpenChange(newOpen)
+ const handleOpenChange = useCallback((nextOpen: boolean) => {
+ setOpen(nextOpen)
+ onOpenChange?.(nextOpen)
}, [onOpenChange])
return (
@@ -62,7 +60,7 @@ const PanelOperator = ({
aria-label={t('operation.more', { ns: 'common' })}
className={cn(
'nodrag nopan nowheel flex h-6 w-6 cursor-pointer items-center justify-center rounded-md hover:bg-state-base-hover',
- open && 'bg-state-base-hover',
+ 'data-[popup-open]:bg-state-base-hover',
triggerClassName,
)}
>
@@ -77,7 +75,7 @@ const PanelOperator = ({
handleOpenChange(false)}
+ onClosePopup={() => setOpen(false)}
showHelpLink={showHelpLink}
/>
diff --git a/web/app/components/workflow/nodes/_base/components/prompt/editor.tsx b/web/app/components/workflow/nodes/_base/components/prompt/editor.tsx
index bf153b273e..eab359b00b 100644
--- a/web/app/components/workflow/nodes/_base/components/prompt/editor.tsx
+++ b/web/app/components/workflow/nodes/_base/components/prompt/editor.tsx
@@ -7,6 +7,7 @@ import type {
Variable,
} from '../../../../types'
import { cn } from '@langgenius/dify-ui/cn'
+import { Switch } from '@langgenius/dify-ui/switch'
import {
RiDeleteBinLine,
} from '@remixicon/react'
@@ -14,8 +15,8 @@ import { useBoolean } from 'ahooks'
import copy from 'copy-to-clipboard'
import * as React from 'react'
import { useCallback, useRef } from 'react'
-import { useTranslation } from 'react-i18next'
+import { useTranslation } from 'react-i18next'
import ActionButton from '@/app/components/base/action-button'
import {
Copy,
@@ -25,7 +26,6 @@ import { Variable02 } from '@/app/components/base/icons/src/vender/solid/develop
import { Jinja } from '@/app/components/base/icons/src/vender/workflow'
import PromptEditor from '@/app/components/base/prompt-editor'
import { PROMPT_EDITOR_INSERT_QUICKLY } from '@/app/components/base/prompt-editor/plugins/update-block'
-import Switch from '@/app/components/base/switch'
import Tooltip from '@/app/components/base/tooltip'
import { useWorkflowVariableType } from '@/app/components/workflow/hooks'
import CodeEditor from '@/app/components/workflow/nodes/_base/components/editor/code-editor/editor-support-vars'
diff --git a/web/app/components/workflow/nodes/_base/components/remove-effect-var-confirm.tsx b/web/app/components/workflow/nodes/_base/components/remove-effect-var-confirm.tsx
index a3c4e5b0ad..6ae65da1ae 100644
--- a/web/app/components/workflow/nodes/_base/components/remove-effect-var-confirm.tsx
+++ b/web/app/components/workflow/nodes/_base/components/remove-effect-var-confirm.tsx
@@ -1,7 +1,5 @@
'use client'
import type { FC } from 'react'
-import * as React from 'react'
-import { useTranslation } from 'react-i18next'
import {
AlertDialog,
AlertDialogActions,
@@ -10,7 +8,9 @@ import {
AlertDialogContent,
AlertDialogDescription,
AlertDialogTitle,
-} from '@/app/components/base/ui/alert-dialog'
+} from '@langgenius/dify-ui/alert-dialog'
+import * as React from 'react'
+import { useTranslation } from 'react-i18next'
type Props = {
isShow: boolean
diff --git a/web/app/components/workflow/nodes/_base/components/retry/retry-on-panel.tsx b/web/app/components/workflow/nodes/_base/components/retry/retry-on-panel.tsx
index c80c69a757..7a32d05ae1 100644
--- a/web/app/components/workflow/nodes/_base/components/retry/retry-on-panel.tsx
+++ b/web/app/components/workflow/nodes/_base/components/retry/retry-on-panel.tsx
@@ -1,10 +1,10 @@
import type {
Node,
} from '@/app/components/workflow/types'
+import { Slider } from '@langgenius/dify-ui/slider'
+import { Switch } from '@langgenius/dify-ui/switch'
import { useTranslation } from 'react-i18next'
import Input from '@/app/components/base/input'
-import Switch from '@/app/components/base/switch'
-import { Slider } from '@/app/components/base/ui/slider'
import Split from '@/app/components/workflow/nodes/_base/components/split'
import { useRetryConfig } from './hooks'
import s from './style.module.css'
diff --git a/web/app/components/workflow/nodes/_base/components/variable/__tests__/output-var-list.spec.tsx b/web/app/components/workflow/nodes/_base/components/variable/__tests__/output-var-list.spec.tsx
index e83fee6c55..97b6e52ddc 100644
--- a/web/app/components/workflow/nodes/_base/components/variable/__tests__/output-var-list.spec.tsx
+++ b/web/app/components/workflow/nodes/_base/components/variable/__tests__/output-var-list.spec.tsx
@@ -16,7 +16,7 @@ vi.mock('../var-type-picker', () => ({
),
}))
-vi.mock('@/app/components/base/ui/toast', () => ({
+vi.mock('@langgenius/dify-ui/toast', () => ({
toast: { error: vi.fn() },
}))
diff --git a/web/app/components/workflow/nodes/_base/components/variable/output-var-list.tsx b/web/app/components/workflow/nodes/_base/components/variable/output-var-list.tsx
index a80d0b1ee2..a4bbe9a6eb 100644
--- a/web/app/components/workflow/nodes/_base/components/variable/output-var-list.tsx
+++ b/web/app/components/workflow/nodes/_base/components/variable/output-var-list.tsx
@@ -2,13 +2,13 @@
import type { FC } from 'react'
import type { OutputVar } from '../../../code/types'
import type { VarType } from '@/app/components/workflow/types'
+import { toast } from '@langgenius/dify-ui/toast'
import { useDebounceFn } from 'ahooks'
import { produce } from 'immer'
import * as React from 'react'
import { useCallback } from 'react'
import { useTranslation } from 'react-i18next'
import Input from '@/app/components/base/input'
-import { toast } from '@/app/components/base/ui/toast'
import { checkKeys, replaceSpaceWithUnderscoreInVarNameInput } from '@/utils/var'
import RemoveButton from '../remove-button'
import VarTypePicker from './var-type-picker'
diff --git a/web/app/components/workflow/nodes/_base/components/variable/var-list.tsx b/web/app/components/workflow/nodes/_base/components/variable/var-list.tsx
index b8ec03eb93..3b1aafcdf4 100644
--- a/web/app/components/workflow/nodes/_base/components/variable/var-list.tsx
+++ b/web/app/components/workflow/nodes/_base/components/variable/var-list.tsx
@@ -2,6 +2,7 @@
import type { FC } from 'react'
import type { ValueSelector, Var, Variable } from '@/app/components/workflow/types'
import { cn } from '@langgenius/dify-ui/cn'
+import { toast } from '@langgenius/dify-ui/toast'
import { RiDraggable } from '@remixicon/react'
import { useDebounceFn } from 'ahooks'
import { produce } from 'immer'
@@ -11,7 +12,6 @@ import { useTranslation } from 'react-i18next'
import { ReactSortable } from 'react-sortablejs'
import { v4 as uuid4 } from 'uuid'
import Input from '@/app/components/base/input'
-import { toast } from '@/app/components/base/ui/toast'
import { VarType as VarKindType } from '@/app/components/workflow/nodes/tool/types'
import { checkKeys, replaceSpaceWithUnderscoreInVarNameInput } from '@/utils/var'
import RemoveButton from '../remove-button'
diff --git a/web/app/components/workflow/nodes/_base/components/variable/var-reference-picker.trigger.tsx b/web/app/components/workflow/nodes/_base/components/variable/var-reference-picker.trigger.tsx
index e29c582838..8c46118615 100644
--- a/web/app/components/workflow/nodes/_base/components/variable/var-reference-picker.trigger.tsx
+++ b/web/app/components/workflow/nodes/_base/components/variable/var-reference-picker.trigger.tsx
@@ -7,11 +7,11 @@ import type { Tool } from '@/app/components/tools/types'
import type { TriggerWithProvider } from '@/app/components/workflow/block-selector/types'
import type { Node, ToolWithProvider, ValueSelector, Var } from '@/app/components/workflow/types'
import { cn } from '@langgenius/dify-ui/cn'
+import { Tooltip, TooltipContent, TooltipTrigger } from '@langgenius/dify-ui/tooltip'
import { RiArrowDownSLine, RiCloseLine, RiErrorWarningFill, RiLoader4Line, RiMoreLine } from '@remixicon/react'
import Badge from '@/app/components/base/badge'
import { Line3 } from '@/app/components/base/icons/src/public/common'
import { Variable02 } from '@/app/components/base/icons/src/vender/solid/development'
-import { Tooltip, TooltipContent, TooltipTrigger } from '@/app/components/base/ui/tooltip'
import { VarBlockIcon } from '@/app/components/workflow/block-icon'
import TypeSelector from '@/app/components/workflow/nodes/_base/components/selector'
import { VariableIconWithColor } from '@/app/components/workflow/nodes/_base/components/variable/variable-label'
diff --git a/web/app/components/workflow/nodes/_base/components/variable/variable-label/base/variable-label.tsx b/web/app/components/workflow/nodes/_base/components/variable/variable-label/base/variable-label.tsx
index 6ee0d63830..3690cbf508 100644
--- a/web/app/components/workflow/nodes/_base/components/variable/variable-label/base/variable-label.tsx
+++ b/web/app/components/workflow/nodes/_base/components/variable/variable-label/base/variable-label.tsx
@@ -1,9 +1,9 @@
import type { VariablePayload } from '../types'
import { cn } from '@langgenius/dify-ui/cn'
+import { Tooltip, TooltipContent, TooltipTrigger } from '@langgenius/dify-ui/tooltip'
import { capitalize } from 'es-toolkit/string'
import { memo } from 'react'
import { Warning } from '@/app/components/base/icons/src/vender/line/alertsAndFeedback'
-import { Tooltip, TooltipContent, TooltipTrigger } from '@/app/components/base/ui/tooltip'
import { isConversationVar, isENV, isGlobalVar, isRagVariableVar } from '../../utils'
import { useVarColor } from '../hooks'
import VariableIcon from './variable-icon'
diff --git a/web/app/components/workflow/nodes/_base/components/workflow-panel/last-run/no-data.tsx b/web/app/components/workflow/nodes/_base/components/workflow-panel/last-run/no-data.tsx
index 6237cb6f0f..fd67856369 100644
--- a/web/app/components/workflow/nodes/_base/components/workflow-panel/last-run/no-data.tsx
+++ b/web/app/components/workflow/nodes/_base/components/workflow-panel/last-run/no-data.tsx
@@ -1,10 +1,10 @@
'use client'
import type { FC } from 'react'
+import { Button } from '@langgenius/dify-ui/button'
import { RiPlayLine } from '@remixicon/react'
import * as React from 'react'
import { useTranslation } from 'react-i18next'
import { ClockPlay } from '@/app/components/base/icons/src/vender/line/time'
-import { Button } from '@/app/components/base/ui/button'
type Props = {
canSingleRun: boolean
diff --git a/web/app/components/workflow/nodes/_base/components/workflow-panel/last-run/use-last-run.ts b/web/app/components/workflow/nodes/_base/components/workflow-panel/last-run/use-last-run.ts
index b1efe258df..0a7263fafd 100644
--- a/web/app/components/workflow/nodes/_base/components/workflow-panel/last-run/use-last-run.ts
+++ b/web/app/components/workflow/nodes/_base/components/workflow-panel/last-run/use-last-run.ts
@@ -2,8 +2,8 @@ import type { Props as FormProps } from '@/app/components/workflow/nodes/_base/c
import type { Params as OneStepRunParams } from '@/app/components/workflow/nodes/_base/hooks/use-one-step-run'
// import
import type { CommonNodeType, ValueSelector } from '@/app/components/workflow/types'
+import { toast } from '@langgenius/dify-ui/toast'
import { useCallback, useEffect, useState } from 'react'
-import { toast } from '@/app/components/base/ui/toast'
import {
useNodesSyncDraft,
} from '@/app/components/workflow/hooks'
diff --git a/web/app/components/workflow/nodes/_base/hooks/use-one-step-run.ts b/web/app/components/workflow/nodes/_base/hooks/use-one-step-run.ts
index a05c765a8e..a455ea480a 100644
--- a/web/app/components/workflow/nodes/_base/hooks/use-one-step-run.ts
+++ b/web/app/components/workflow/nodes/_base/hooks/use-one-step-run.ts
@@ -1,10 +1,11 @@
import type { CommonNodeType, InputVar, TriggerNodeType, ValueSelector, Var, Variable } from '@/app/components/workflow/types'
import type { FlowType } from '@/types/common'
import type { NodeRunResult, NodeTracing } from '@/types/workflow'
+import { toast } from '@langgenius/dify-ui/toast'
import { unionBy } from 'es-toolkit/compat'
import { noop } from 'es-toolkit/function'
-import { produce } from 'immer'
+import { produce } from 'immer'
import { useCallback, useEffect, useRef, useState } from 'react'
import { useTranslation } from 'react-i18next'
import {
@@ -12,7 +13,6 @@ import {
} from 'reactflow'
import { trackEvent } from '@/app/components/base/amplitude'
import { getInputVars as doGetInputVars } from '@/app/components/base/prompt-editor/constants'
-import { toast } from '@/app/components/base/ui/toast'
import {
useIsChatMode,
useNodeDataUpdate,
diff --git a/web/app/components/workflow/nodes/_base/node-sections.tsx b/web/app/components/workflow/nodes/_base/node-sections.tsx
index 891f17eb50..a365362123 100644
--- a/web/app/components/workflow/nodes/_base/node-sections.tsx
+++ b/web/app/components/workflow/nodes/_base/node-sections.tsx
@@ -2,7 +2,7 @@ import type { TFunction } from 'i18next'
import type { ReactElement } from 'react'
import type { IterationNodeType } from '@/app/components/workflow/nodes/iteration/types'
import type { NodeProps } from '@/app/components/workflow/types'
-import { Tooltip, TooltipContent, TooltipTrigger } from '@/app/components/base/ui/tooltip'
+import { Tooltip, TooltipContent, TooltipTrigger } from '@langgenius/dify-ui/tooltip'
import { BlockEnum, NodeRunningStatus } from '@/app/components/workflow/types'
type HeaderMetaProps = {
diff --git a/web/app/components/workflow/nodes/assigner/components/__tests__/operation-selector.spec.tsx b/web/app/components/workflow/nodes/assigner/components/__tests__/operation-selector.spec.tsx
index 1a12ab129d..79618a6557 100644
--- a/web/app/components/workflow/nodes/assigner/components/__tests__/operation-selector.spec.tsx
+++ b/web/app/components/workflow/nodes/assigner/components/__tests__/operation-selector.spec.tsx
@@ -4,7 +4,7 @@ import { VarType } from '@/app/components/workflow/types'
import { WriteMode } from '../../types'
import OperationSelector from '../operation-selector'
-vi.mock('@/app/components/base/ui/dropdown-menu', async () => {
+vi.mock('@langgenius/dify-ui/dropdown-menu', async () => {
const React = await import('react')
const DropdownMenuContext = React.createContext<{ open: boolean, setOpen: (open: boolean) => void } | null>(null)
diff --git a/web/app/components/workflow/nodes/assigner/components/operation-selector.tsx b/web/app/components/workflow/nodes/assigner/components/operation-selector.tsx
index a22b8f5f3f..69fb618d16 100644
--- a/web/app/components/workflow/nodes/assigner/components/operation-selector.tsx
+++ b/web/app/components/workflow/nodes/assigner/components/operation-selector.tsx
@@ -3,8 +3,6 @@ import type { WriteMode } from '../types'
import type { Item } from '../utils'
import type { VarType } from '@/app/components/workflow/types'
import { cn } from '@langgenius/dify-ui/cn'
-import { useState } from 'react'
-import { useTranslation } from 'react-i18next'
import {
DropdownMenu,
DropdownMenuContent,
@@ -13,7 +11,9 @@ import {
DropdownMenuLabel,
DropdownMenuSeparator,
DropdownMenuTrigger,
-} from '@/app/components/base/ui/dropdown-menu'
+} from '@langgenius/dify-ui/dropdown-menu'
+import { useState } from 'react'
+import { useTranslation } from 'react-i18next'
import { getOperationItems, isOperationItem } from '../utils'
type OperationSelectorProps = {
diff --git a/web/app/components/workflow/nodes/code/panel.tsx b/web/app/components/workflow/nodes/code/panel.tsx
index 9358507173..b531dcf49c 100644
--- a/web/app/components/workflow/nodes/code/panel.tsx
+++ b/web/app/components/workflow/nodes/code/panel.tsx
@@ -1,9 +1,9 @@
import type { FC } from 'react'
import type { CodeNodeType } from './types'
import type { NodePanelProps } from '@/app/components/workflow/types'
+import { Tooltip, TooltipContent, TooltipTrigger } from '@langgenius/dify-ui/tooltip'
import * as React from 'react'
import { useTranslation } from 'react-i18next'
-import { Tooltip, TooltipContent, TooltipTrigger } from '@/app/components/base/ui/tooltip'
import CodeEditor from '@/app/components/workflow/nodes/_base/components/editor/code-editor'
import Field from '@/app/components/workflow/nodes/_base/components/field'
import TypeSelector from '@/app/components/workflow/nodes/_base/components/selector'
diff --git a/web/app/components/workflow/nodes/data-source-empty/index.tsx b/web/app/components/workflow/nodes/data-source-empty/index.tsx
index a3248b3545..d3f9d21225 100644
--- a/web/app/components/workflow/nodes/data-source-empty/index.tsx
+++ b/web/app/components/workflow/nodes/data-source-empty/index.tsx
@@ -1,4 +1,5 @@
import type { NodeProps } from 'reactflow'
+import { Button } from '@langgenius/dify-ui/button'
import { cn } from '@langgenius/dify-ui/cn'
import { RiAddLine } from '@remixicon/react'
import {
@@ -6,7 +7,6 @@ import {
useCallback,
} from 'react'
import { useTranslation } from 'react-i18next'
-import { Button } from '@/app/components/base/ui/button'
import BlockSelector from '@/app/components/workflow/block-selector'
import { useReplaceDataSourceNode } from './hooks'
diff --git a/web/app/components/workflow/nodes/data-source/before-run-form.tsx b/web/app/components/workflow/nodes/data-source/before-run-form.tsx
index 677418048f..fd6d805d0e 100644
--- a/web/app/components/workflow/nodes/data-source/before-run-form.tsx
+++ b/web/app/components/workflow/nodes/data-source/before-run-form.tsx
@@ -1,10 +1,10 @@
'use client'
import type { FC } from 'react'
import type { CustomRunFormProps } from './types'
+import { Button } from '@langgenius/dify-ui/button'
import * as React from 'react'
import { useCallback } from 'react'
import { useTranslation } from 'react-i18next'
-import { Button } from '@/app/components/base/ui/button'
import LocalFile from '@/app/components/datasets/documents/create-from-pipeline/data-source/local-file'
import OnlineDocuments from '@/app/components/datasets/documents/create-from-pipeline/data-source/online-documents'
import OnlineDrive from '@/app/components/datasets/documents/create-from-pipeline/data-source/online-drive'
diff --git a/web/app/components/workflow/nodes/http/components/__tests__/curl-panel.spec.tsx b/web/app/components/workflow/nodes/http/components/__tests__/curl-panel.spec.tsx
index f42e98f605..141f8fd5f9 100644
--- a/web/app/components/workflow/nodes/http/components/__tests__/curl-panel.spec.tsx
+++ b/web/app/components/workflow/nodes/http/components/__tests__/curl-panel.spec.tsx
@@ -1,6 +1,6 @@
+import { toast } from '@langgenius/dify-ui/toast'
import { render, screen } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
-import { toast } from '@/app/components/base/ui/toast'
import { BodyPayloadValueType, BodyType } from '../../types'
import CurlPanel from '../curl-panel'
import * as curlParser from '../curl-parser'
@@ -19,7 +19,7 @@ vi.mock('@/app/components/workflow/hooks', () => ({
}),
}))
-vi.mock('@/app/components/base/ui/toast', () => ({
+vi.mock('@langgenius/dify-ui/toast', () => ({
toast: {
error: mockToastError,
},
diff --git a/web/app/components/workflow/nodes/http/components/authorization/index.tsx b/web/app/components/workflow/nodes/http/components/authorization/index.tsx
index f44cbe6025..b72e52911d 100644
--- a/web/app/components/workflow/nodes/http/components/authorization/index.tsx
+++ b/web/app/components/workflow/nodes/http/components/authorization/index.tsx
@@ -2,6 +2,7 @@
import type { FC } from 'react'
import type { Authorization as AuthorizationPayloadType } from '../../types'
import type { Var } from '@/app/components/workflow/types'
+import { Button } from '@langgenius/dify-ui/button'
import { cn } from '@langgenius/dify-ui/cn'
import { produce } from 'immer'
import * as React from 'react'
@@ -9,7 +10,6 @@ import { useCallback, useState } from 'react'
import { useTranslation } from 'react-i18next'
import BaseInput from '@/app/components/base/input'
import Modal from '@/app/components/base/modal'
-import { Button } from '@/app/components/base/ui/button'
import Input from '@/app/components/workflow/nodes/_base/components/input-support-select-var'
import useAvailableVarList from '@/app/components/workflow/nodes/_base/hooks/use-available-var-list'
import { VarType } from '@/app/components/workflow/types'
diff --git a/web/app/components/workflow/nodes/http/components/curl-panel.tsx b/web/app/components/workflow/nodes/http/components/curl-panel.tsx
index ef1b28095c..8ba5fb36a9 100644
--- a/web/app/components/workflow/nodes/http/components/curl-panel.tsx
+++ b/web/app/components/workflow/nodes/http/components/curl-panel.tsx
@@ -1,13 +1,13 @@
'use client'
import type { FC } from 'react'
import type { HttpNodeType } from '../types'
+import { Button } from '@langgenius/dify-ui/button'
+import { toast } from '@langgenius/dify-ui/toast'
import * as React from 'react'
import { useCallback, useState } from 'react'
import { useTranslation } from 'react-i18next'
import Modal from '@/app/components/base/modal'
import Textarea from '@/app/components/base/textarea'
-import { Button } from '@/app/components/base/ui/button'
-import { toast } from '@/app/components/base/ui/toast'
import { useNodesInteractions } from '@/app/components/workflow/hooks'
import { parseCurl } from './curl-parser'
diff --git a/web/app/components/workflow/nodes/http/components/key-value/key-value-edit/item.tsx b/web/app/components/workflow/nodes/http/components/key-value/key-value-edit/item.tsx
index 36e0fccbff..b349e8d8b2 100644
--- a/web/app/components/workflow/nodes/http/components/key-value/key-value-edit/item.tsx
+++ b/web/app/components/workflow/nodes/http/components/key-value/key-value-edit/item.tsx
@@ -3,10 +3,6 @@ import type { FC } from 'react'
import type { KeyValue } from '../../../types'
import type { ValueSelector, Var } from '@/app/components/workflow/types'
import { cn } from '@langgenius/dify-ui/cn'
-import { produce } from 'immer'
-import * as React from 'react'
-import { useCallback } from 'react'
-import { useTranslation } from 'react-i18next'
import {
Select,
SelectContent,
@@ -15,7 +11,11 @@ import {
SelectItemText,
SelectTrigger,
SelectValue,
-} from '@/app/components/base/ui/select'
+} from '@langgenius/dify-ui/select'
+import { produce } from 'immer'
+import * as React from 'react'
+import { useCallback } from 'react'
+import { useTranslation } from 'react-i18next'
import { VarType } from '@/app/components/workflow/types'
import VarReferencePicker from '../../../../_base/components/variable/var-reference-picker'
import InputItem from './input-item'
diff --git a/web/app/components/workflow/nodes/http/panel.tsx b/web/app/components/workflow/nodes/http/panel.tsx
index 7eb8ef897e..e1d7b57bf3 100644
--- a/web/app/components/workflow/nodes/http/panel.tsx
+++ b/web/app/components/workflow/nodes/http/panel.tsx
@@ -2,11 +2,11 @@ import type { FC } from 'react'
import type { HttpNodeType } from './types'
import type { NodePanelProps } from '@/app/components/workflow/types'
import { cn } from '@langgenius/dify-ui/cn'
+import { Switch } from '@langgenius/dify-ui/switch'
import { memo } from 'react'
import { useTranslation } from 'react-i18next'
import { FileArrow01 } from '@/app/components/base/icons/src/vender/line/files'
import { Settings01 } from '@/app/components/base/icons/src/vender/line/general'
-import Switch from '@/app/components/base/switch'
import Field from '@/app/components/workflow/nodes/_base/components/field'
import OutputVars, { VarItem } from '@/app/components/workflow/nodes/_base/components/output-vars'
import Split from '@/app/components/workflow/nodes/_base/components/split'
diff --git a/web/app/components/workflow/nodes/human-input/__tests__/panel.spec.tsx b/web/app/components/workflow/nodes/human-input/__tests__/panel.spec.tsx
index 937a2da61a..143b05afae 100644
--- a/web/app/components/workflow/nodes/human-input/__tests__/panel.spec.tsx
+++ b/web/app/components/workflow/nodes/human-input/__tests__/panel.spec.tsx
@@ -2,10 +2,10 @@ import type { ReactNode } from 'react'
import type useConfig from '../hooks/use-config'
import type { HumanInputNodeType } from '../types'
import type { NodePanelProps } from '@/app/components/workflow/types'
+import { toast } from '@langgenius/dify-ui/toast'
import { render, screen } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import copy from 'copy-to-clipboard'
-import { toast } from '@/app/components/base/ui/toast'
import { BlockEnum, InputVarType, VarType } from '@/app/components/workflow/types'
import Panel from '../panel'
import { DeliveryMethodType, UserActionButtonType } from '../types'
@@ -24,7 +24,7 @@ vi.mock('copy-to-clipboard', () => ({
default: vi.fn(),
}))
-vi.mock('@/app/components/base/ui/toast', () => ({
+vi.mock('@langgenius/dify-ui/toast', () => ({
toast: {
success: vi.fn(),
},
diff --git a/web/app/components/workflow/nodes/human-input/components/__tests__/button-style-dropdown.spec.tsx b/web/app/components/workflow/nodes/human-input/components/__tests__/button-style-dropdown.spec.tsx
index 544524110a..056ebf4795 100644
--- a/web/app/components/workflow/nodes/human-input/components/__tests__/button-style-dropdown.spec.tsx
+++ b/web/app/components/workflow/nodes/human-input/components/__tests__/button-style-dropdown.spec.tsx
@@ -10,7 +10,7 @@ vi.mock('react-i18next', () => ({
useTranslation: () => mockUseTranslation(),
}))
-vi.mock('@/app/components/base/ui/button', () => ({
+vi.mock('@langgenius/dify-ui/button', () => ({
Button: (props: {
variant?: string
children?: React.ReactNode
diff --git a/web/app/components/workflow/nodes/human-input/components/__tests__/form-content-preview.spec.tsx b/web/app/components/workflow/nodes/human-input/components/__tests__/form-content-preview.spec.tsx
index 4e637724fd..89b2689761 100644
--- a/web/app/components/workflow/nodes/human-input/components/__tests__/form-content-preview.spec.tsx
+++ b/web/app/components/workflow/nodes/human-input/components/__tests__/form-content-preview.spec.tsx
@@ -35,7 +35,7 @@ vi.mock('@/app/components/base/badge', () => ({
default: ({ children }: { children?: ReactNode }) => {children}
,
}))
-vi.mock('@/app/components/base/ui/button', () => ({
+vi.mock('@langgenius/dify-ui/button', () => ({
Button: ({ children, variant }: { children?: ReactNode, variant?: string }) => (
{children}
),
diff --git a/web/app/components/workflow/nodes/human-input/components/__tests__/user-action.spec.tsx b/web/app/components/workflow/nodes/human-input/components/__tests__/user-action.spec.tsx
index 13f8463676..a47a012f49 100644
--- a/web/app/components/workflow/nodes/human-input/components/__tests__/user-action.spec.tsx
+++ b/web/app/components/workflow/nodes/human-input/components/__tests__/user-action.spec.tsx
@@ -27,7 +27,7 @@ vi.mock('@/app/components/base/input', () => ({
),
}))
-vi.mock('@/app/components/base/ui/button', () => ({
+vi.mock('@langgenius/dify-ui/button', () => ({
Button: (props: {
children?: ReactNode
onClick?: () => void
@@ -38,7 +38,7 @@ vi.mock('@/app/components/base/ui/button', () => ({
),
}))
-vi.mock('@/app/components/base/ui/toast', () => ({
+vi.mock('@langgenius/dify-ui/toast', () => ({
__esModule: true,
toast: {
success: (message: string) => mockNotify({ type: 'success', message }),
diff --git a/web/app/components/workflow/nodes/human-input/components/button-style-dropdown.tsx b/web/app/components/workflow/nodes/human-input/components/button-style-dropdown.tsx
index 363e0070c0..44ddbbfa34 100644
--- a/web/app/components/workflow/nodes/human-input/components/button-style-dropdown.tsx
+++ b/web/app/components/workflow/nodes/human-input/components/button-style-dropdown.tsx
@@ -1,4 +1,5 @@
import type { FC } from 'react'
+import { Button } from '@langgenius/dify-ui/button'
import { cn } from '@langgenius/dify-ui/cn'
import {
RiFontSize,
@@ -11,7 +12,6 @@ import {
PortalToFollowElemContent,
PortalToFollowElemTrigger,
} from '@/app/components/base/portal-to-follow-elem'
-import { Button } from '@/app/components/base/ui/button'
import { UserActionButtonType } from '../types'
const i18nPrefix = 'nodes.humanInput'
diff --git a/web/app/components/workflow/nodes/human-input/components/delivery-method/__tests__/email-configure-modal.spec.tsx b/web/app/components/workflow/nodes/human-input/components/delivery-method/__tests__/email-configure-modal.spec.tsx
index cec9ffe69a..c5b5c680dc 100644
--- a/web/app/components/workflow/nodes/human-input/components/delivery-method/__tests__/email-configure-modal.spec.tsx
+++ b/web/app/components/workflow/nodes/human-input/components/delivery-method/__tests__/email-configure-modal.spec.tsx
@@ -5,7 +5,7 @@ import EmailConfigureModal from '../email-configure-modal'
const mockToastError = vi.hoisted(() => vi.fn())
const mockUseAppContextSelector = vi.hoisted(() => vi.fn())
-vi.mock('@/app/components/base/ui/toast', () => ({
+vi.mock('@langgenius/dify-ui/toast', () => ({
toast: {
error: (message: string) => mockToastError(message),
},
diff --git a/web/app/components/workflow/nodes/human-input/components/delivery-method/email-configure-modal.tsx b/web/app/components/workflow/nodes/human-input/components/delivery-method/email-configure-modal.tsx
index 1d77248037..de38564d95 100644
--- a/web/app/components/workflow/nodes/human-input/components/delivery-method/email-configure-modal.tsx
+++ b/web/app/components/workflow/nodes/human-input/components/delivery-method/email-configure-modal.tsx
@@ -3,6 +3,9 @@ import type {
Node,
NodeOutPutVar,
} from '@/app/components/workflow/types'
+import { Button } from '@langgenius/dify-ui/button'
+import { Switch } from '@langgenius/dify-ui/switch'
+import { toast } from '@langgenius/dify-ui/toast'
import { RiBugLine, RiCloseLine } from '@remixicon/react'
import { noop } from 'es-toolkit/compat'
import { memo, useCallback, useState } from 'react'
@@ -10,9 +13,6 @@ import { Trans, useTranslation } from 'react-i18next'
import Divider from '@/app/components/base/divider'
import Input from '@/app/components/base/input'
import Modal from '@/app/components/base/modal'
-import Switch from '@/app/components/base/switch'
-import { Button } from '@/app/components/base/ui/button'
-import { toast } from '@/app/components/base/ui/toast'
import { useSelector as useAppContextWithSelector } from '@/context/app-context'
import MailBodyInput from './mail-body-input'
import Recipient from './recipient'
diff --git a/web/app/components/workflow/nodes/human-input/components/delivery-method/method-item.tsx b/web/app/components/workflow/nodes/human-input/components/delivery-method/method-item.tsx
index eae2e293e1..9cd63e96dd 100644
--- a/web/app/components/workflow/nodes/human-input/components/delivery-method/method-item.tsx
+++ b/web/app/components/workflow/nodes/human-input/components/delivery-method/method-item.tsx
@@ -4,7 +4,9 @@ import type {
Node,
NodeOutPutVar,
} from '@/app/components/workflow/types'
+import { Button } from '@langgenius/dify-ui/button'
import { cn } from '@langgenius/dify-ui/cn'
+import { Switch } from '@langgenius/dify-ui/switch'
import {
RiDeleteBinLine,
RiEqualizer2Line,
@@ -16,9 +18,7 @@ import { useCallback, useMemo, useState } from 'react'
import { useTranslation } from 'react-i18next'
import ActionButton, { ActionButtonState } from '@/app/components/base/action-button'
import Badge from '@/app/components/base/badge/index'
-import Switch from '@/app/components/base/switch'
import Tooltip from '@/app/components/base/tooltip'
-import { Button } from '@/app/components/base/ui/button'
import Indicator from '@/app/components/header/indicator'
import { useSelector as useAppContextWithSelector } from '@/context/app-context'
import { DeliveryMethodType } from '../../types'
diff --git a/web/app/components/workflow/nodes/human-input/components/delivery-method/recipient/__tests__/index.spec.tsx b/web/app/components/workflow/nodes/human-input/components/delivery-method/recipient/__tests__/index.spec.tsx
index 7fe768781b..cd95a8e9e3 100644
--- a/web/app/components/workflow/nodes/human-input/components/delivery-method/recipient/__tests__/index.spec.tsx
+++ b/web/app/components/workflow/nodes/human-input/components/delivery-method/recipient/__tests__/index.spec.tsx
@@ -17,9 +17,8 @@ vi.mock('@/service/use-common', () => ({
useMembers: () => mockUseMembers(),
}))
-vi.mock('@/app/components/base/switch', () => ({
- __esModule: true,
- default: (props: {
+vi.mock('@langgenius/dify-ui/switch', () => ({
+ Switch: (props: {
checked: boolean
onCheckedChange: (value: boolean) => void
}) => (
diff --git a/web/app/components/workflow/nodes/human-input/components/delivery-method/recipient/email-item.tsx b/web/app/components/workflow/nodes/human-input/components/delivery-method/recipient/email-item.tsx
index 8d370266b0..604736b2a9 100644
--- a/web/app/components/workflow/nodes/human-input/components/delivery-method/recipient/email-item.tsx
+++ b/web/app/components/workflow/nodes/human-input/components/delivery-method/recipient/email-item.tsx
@@ -1,10 +1,10 @@
import type { Recipient as RecipientItem } from '../../../types'
import type { Member } from '@/models/common'
+import { Avatar } from '@langgenius/dify-ui/avatar'
import { cn } from '@langgenius/dify-ui/cn'
import { RiCloseCircleFill, RiErrorWarningFill } from '@remixicon/react'
import * as React from 'react'
import { useTranslation } from 'react-i18next'
-import { Avatar } from '@/app/components/base/ui/avatar'
type Props = {
email: string
diff --git a/web/app/components/workflow/nodes/human-input/components/delivery-method/recipient/index.tsx b/web/app/components/workflow/nodes/human-input/components/delivery-method/recipient/index.tsx
index 36483d33d6..42bd6df171 100644
--- a/web/app/components/workflow/nodes/human-input/components/delivery-method/recipient/index.tsx
+++ b/web/app/components/workflow/nodes/human-input/components/delivery-method/recipient/index.tsx
@@ -1,10 +1,10 @@
import type { RecipientData, Recipient as RecipientItem } from '../../../types'
import { cn } from '@langgenius/dify-ui/cn'
+import { Switch } from '@langgenius/dify-ui/switch'
import { RiGroupLine } from '@remixicon/react'
import { produce } from 'immer'
import { memo } from 'react'
import { useTranslation } from 'react-i18next'
-import Switch from '@/app/components/base/switch'
import { useAppContext } from '@/context/app-context'
import { useMembers } from '@/service/use-common'
import EmailInput from './email-input'
diff --git a/web/app/components/workflow/nodes/human-input/components/delivery-method/recipient/member-list.tsx b/web/app/components/workflow/nodes/human-input/components/delivery-method/recipient/member-list.tsx
index c80c22639e..0e262e839e 100644
--- a/web/app/components/workflow/nodes/human-input/components/delivery-method/recipient/member-list.tsx
+++ b/web/app/components/workflow/nodes/human-input/components/delivery-method/recipient/member-list.tsx
@@ -2,11 +2,11 @@
import type { FC } from 'react'
import type { Recipient } from '@/app/components/workflow/nodes/human-input/types'
import type { Member } from '@/models/common'
+import { Avatar } from '@langgenius/dify-ui/avatar'
import { cn } from '@langgenius/dify-ui/cn'
import { useMemo } from 'react'
import { useTranslation } from 'react-i18next'
import Input from '@/app/components/base/input'
-import { Avatar } from '@/app/components/base/ui/avatar'
const i18nPrefix = 'nodes.humanInput'
diff --git a/web/app/components/workflow/nodes/human-input/components/delivery-method/recipient/member-selector.tsx b/web/app/components/workflow/nodes/human-input/components/delivery-method/recipient/member-selector.tsx
index 827e02c8bb..c2df6afb15 100644
--- a/web/app/components/workflow/nodes/human-input/components/delivery-method/recipient/member-selector.tsx
+++ b/web/app/components/workflow/nodes/human-input/components/delivery-method/recipient/member-selector.tsx
@@ -2,6 +2,7 @@
import type { FC } from 'react'
import type { Recipient } from '@/app/components/workflow/nodes/human-input/types'
import type { Member } from '@/models/common'
+import { Button } from '@langgenius/dify-ui/button'
import { cn } from '@langgenius/dify-ui/cn'
import {
RiContactsBookLine,
@@ -9,7 +10,6 @@ import {
import { useState } from 'react'
import { useTranslation } from 'react-i18next'
import { PortalToFollowElem, PortalToFollowElemContent, PortalToFollowElemTrigger } from '@/app/components/base/portal-to-follow-elem'
-import { Button } from '@/app/components/base/ui/button'
import MemberList from './member-list'
const i18nPrefix = 'nodes.humanInput'
diff --git a/web/app/components/workflow/nodes/human-input/components/delivery-method/test-email-sender.tsx b/web/app/components/workflow/nodes/human-input/components/delivery-method/test-email-sender.tsx
index cef6d1380d..9ad52fa13e 100644
--- a/web/app/components/workflow/nodes/human-input/components/delivery-method/test-email-sender.tsx
+++ b/web/app/components/workflow/nodes/human-input/components/delivery-method/test-email-sender.tsx
@@ -4,6 +4,7 @@ import type {
NodeOutPutVar,
ValueSelector,
} from '@/app/components/workflow/types'
+import { Button } from '@langgenius/dify-ui/button'
import { cn } from '@langgenius/dify-ui/cn'
import { RiArrowRightSFill, RiCloseLine } from '@remixicon/react'
import { noop, unionBy } from 'es-toolkit/compat'
@@ -13,7 +14,6 @@ import { useStore as useAppStore } from '@/app/components/app/store'
import Divider from '@/app/components/base/divider'
import Modal from '@/app/components/base/modal'
import { getInputVars as doGetInputVars } from '@/app/components/base/prompt-editor/constants'
-import { Button } from '@/app/components/base/ui/button'
import FormItem from '@/app/components/workflow/nodes/_base/components/before-run-form/form-item'
import {
getNodeInfoById,
diff --git a/web/app/components/workflow/nodes/human-input/components/delivery-method/upgrade-modal.tsx b/web/app/components/workflow/nodes/human-input/components/delivery-method/upgrade-modal.tsx
index c7b46732d0..060ec2428c 100644
--- a/web/app/components/workflow/nodes/human-input/components/delivery-method/upgrade-modal.tsx
+++ b/web/app/components/workflow/nodes/human-input/components/delivery-method/upgrade-modal.tsx
@@ -1,3 +1,4 @@
+import { Button } from '@langgenius/dify-ui/button'
import { cn } from '@langgenius/dify-ui/cn'
import {
RiMailSendFill,
@@ -7,7 +8,6 @@ import { useTranslation } from 'react-i18next'
import { SparklesSoft } from '@/app/components/base/icons/src/public/common'
import Modal from '@/app/components/base/modal'
import PremiumBadge from '@/app/components/base/premium-badge'
-import { Button } from '@/app/components/base/ui/button'
import { useModalContextSelector } from '@/context/modal-context'
type UpgradeModalProps = {
diff --git a/web/app/components/workflow/nodes/human-input/components/form-content-preview.tsx b/web/app/components/workflow/nodes/human-input/components/form-content-preview.tsx
index 1364941edf..73e8850789 100644
--- a/web/app/components/workflow/nodes/human-input/components/form-content-preview.tsx
+++ b/web/app/components/workflow/nodes/human-input/components/form-content-preview.tsx
@@ -1,14 +1,14 @@
'use client'
+import type { ButtonProps } from '@langgenius/dify-ui/button'
import type { FC } from 'react'
import type { FormInputItem, UserAction } from '../types'
-import type { ButtonProps } from '@/app/components/base/ui/button'
+import { Button } from '@langgenius/dify-ui/button'
import * as React from 'react'
import { useTranslation } from 'react-i18next'
import ActionButton from '@/app/components/base/action-button'
import Badge from '@/app/components/base/badge'
import { getButtonStyle } from '@/app/components/base/chat/chat/answer/human-input-content/utils'
import { Markdown } from '@/app/components/base/markdown'
-import { Button } from '@/app/components/base/ui/button'
import { useStore } from '@/app/components/workflow/store'
import useNodes from '@/app/components/workflow/store/workflow/use-nodes'
import { Note, rehypeNotes, rehypeVariable, Variable } from './variable-in-markdown'
diff --git a/web/app/components/workflow/nodes/human-input/components/single-run-form.tsx b/web/app/components/workflow/nodes/human-input/components/single-run-form.tsx
index dc735830c6..6226b6c06f 100644
--- a/web/app/components/workflow/nodes/human-input/components/single-run-form.tsx
+++ b/web/app/components/workflow/nodes/human-input/components/single-run-form.tsx
@@ -1,15 +1,15 @@
'use client'
-import type { ButtonProps } from '@/app/components/base/ui/button'
+import type { ButtonProps } from '@langgenius/dify-ui/button'
import type { UserAction } from '@/app/components/workflow/nodes/human-input/types'
import type { HumanInputFormData } from '@/types/workflow'
+import { Button } from '@langgenius/dify-ui/button'
import { RiArrowLeftLine } from '@remixicon/react'
-import * as React from 'react'
+import * as React from 'react'
import { useState } from 'react'
import { useTranslation } from 'react-i18next'
import ContentItem from '@/app/components/base/chat/chat/answer/human-input-content/content-item'
import { getButtonStyle, initializeInputs, splitByOutputVar } from '@/app/components/base/chat/chat/answer/human-input-content/utils'
-import { Button } from '@/app/components/base/ui/button'
type Props = {
nodeName: string
diff --git a/web/app/components/workflow/nodes/human-input/components/user-action.tsx b/web/app/components/workflow/nodes/human-input/components/user-action.tsx
index 6ef5609a64..a83ea4f8f2 100644
--- a/web/app/components/workflow/nodes/human-input/components/user-action.tsx
+++ b/web/app/components/workflow/nodes/human-input/components/user-action.tsx
@@ -1,13 +1,13 @@
import type { FC } from 'react'
import type { UserAction } from '../types'
+import { Button } from '@langgenius/dify-ui/button'
+import { toast } from '@langgenius/dify-ui/toast'
import {
RiDeleteBinLine,
} from '@remixicon/react'
import * as React from 'react'
import { useTranslation } from 'react-i18next'
import Input from '@/app/components/base/input'
-import { Button } from '@/app/components/base/ui/button'
-import { toast } from '@/app/components/base/ui/toast'
import ButtonStyleDropdown from './button-style-dropdown'
const i18nPrefix = 'nodes.humanInput'
diff --git a/web/app/components/workflow/nodes/human-input/panel.tsx b/web/app/components/workflow/nodes/human-input/panel.tsx
index 846b135fcf..b7b65e7de8 100644
--- a/web/app/components/workflow/nodes/human-input/panel.tsx
+++ b/web/app/components/workflow/nodes/human-input/panel.tsx
@@ -1,7 +1,9 @@
import type { FC } from 'react'
import type { HumanInputNodeType } from './types'
import type { NodePanelProps, Var } from '@/app/components/workflow/types'
+import { Button } from '@langgenius/dify-ui/button'
import { cn } from '@langgenius/dify-ui/cn'
+import { toast } from '@langgenius/dify-ui/toast'
import {
RiAddLine,
RiClipboardLine,
@@ -17,8 +19,6 @@ import { useTranslation } from 'react-i18next'
import ActionButton from '@/app/components/base/action-button'
import Divider from '@/app/components/base/divider'
import Tooltip from '@/app/components/base/tooltip'
-import { Button } from '@/app/components/base/ui/button'
-import { toast } from '@/app/components/base/ui/toast'
import OutputVars, { VarItem } from '@/app/components/workflow/nodes/_base/components/output-vars'
import Split from '@/app/components/workflow/nodes/_base/components/split'
import useAvailableVarList from '@/app/components/workflow/nodes/_base/hooks/use-available-var-list'
diff --git a/web/app/components/workflow/nodes/if-else/components/condition-add.tsx b/web/app/components/workflow/nodes/if-else/components/condition-add.tsx
index 3b6d729078..85a45ac5c7 100644
--- a/web/app/components/workflow/nodes/if-else/components/condition-add.tsx
+++ b/web/app/components/workflow/nodes/if-else/components/condition-add.tsx
@@ -4,6 +4,7 @@ import type {
ValueSelector,
Var,
} from '@/app/components/workflow/types'
+import { Button } from '@langgenius/dify-ui/button'
import { RiAddLine } from '@remixicon/react'
import {
useCallback,
@@ -15,7 +16,6 @@ import {
PortalToFollowElemContent,
PortalToFollowElemTrigger,
} from '@/app/components/base/portal-to-follow-elem'
-import { Button } from '@/app/components/base/ui/button'
import VarReferenceVars from '@/app/components/workflow/nodes/_base/components/variable/var-reference-vars'
type ConditionAddProps = {
diff --git a/web/app/components/workflow/nodes/if-else/components/condition-list/condition-operator.tsx b/web/app/components/workflow/nodes/if-else/components/condition-list/condition-operator.tsx
index 998cc48205..83dc97f088 100644
--- a/web/app/components/workflow/nodes/if-else/components/condition-list/condition-operator.tsx
+++ b/web/app/components/workflow/nodes/if-else/components/condition-list/condition-operator.tsx
@@ -1,5 +1,6 @@
import type { ComparisonOperator } from '../../types'
import type { VarType } from '@/app/components/workflow/types'
+import { Button } from '@langgenius/dify-ui/button'
import { cn } from '@langgenius/dify-ui/cn'
import { RiArrowDownSLine } from '@remixicon/react'
import {
@@ -12,7 +13,6 @@ import {
PortalToFollowElemContent,
PortalToFollowElemTrigger,
} from '@/app/components/base/portal-to-follow-elem'
-import { Button } from '@/app/components/base/ui/button'
import { getOperators, isComparisonOperatorNeedTranslate } from '../../utils'
const i18nPrefix = 'nodes.ifElse'
diff --git a/web/app/components/workflow/nodes/if-else/components/condition-number-input.tsx b/web/app/components/workflow/nodes/if-else/components/condition-number-input.tsx
index 7bcb4137b5..954f713a78 100644
--- a/web/app/components/workflow/nodes/if-else/components/condition-number-input.tsx
+++ b/web/app/components/workflow/nodes/if-else/components/condition-number-input.tsx
@@ -2,6 +2,7 @@ import type {
NodeOutPutVar,
ValueSelector,
} from '@/app/components/workflow/types'
+import { Button } from '@langgenius/dify-ui/button'
import { cn } from '@langgenius/dify-ui/cn'
import { RiArrowDownSLine } from '@remixicon/react'
import { useBoolean } from 'ahooks'
@@ -18,7 +19,6 @@ import {
PortalToFollowElemContent,
PortalToFollowElemTrigger,
} from '@/app/components/base/portal-to-follow-elem'
-import { Button } from '@/app/components/base/ui/button'
import VarReferenceVars from '@/app/components/workflow/nodes/_base/components/variable/var-reference-vars'
import { VarType } from '@/app/components/workflow/types'
import { variableTransformer } from '@/app/components/workflow/utils'
diff --git a/web/app/components/workflow/nodes/if-else/components/condition-wrap.tsx b/web/app/components/workflow/nodes/if-else/components/condition-wrap.tsx
index 1744ee6490..92d70b37b1 100644
--- a/web/app/components/workflow/nodes/if-else/components/condition-wrap.tsx
+++ b/web/app/components/workflow/nodes/if-else/components/condition-wrap.tsx
@@ -2,6 +2,7 @@
import type { FC } from 'react'
import type { Node, NodeOutPutVar, Var } from '../../../types'
import type { CaseItem, HandleAddCondition, HandleAddSubVariableCondition, HandleRemoveCondition, handleRemoveSubVariableCondition, HandleToggleConditionLogicalOperator, HandleToggleSubVariableConditionLogicalOperator, HandleUpdateCondition, HandleUpdateSubVariableCondition } from '../types'
+import { Button } from '@langgenius/dify-ui/button'
import { cn } from '@langgenius/dify-ui/cn'
import {
RiAddLine,
@@ -14,7 +15,6 @@ import { useCallback, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { ReactSortable } from 'react-sortablejs'
import { PortalSelect as Select } from '@/app/components/base/select'
-import { Button } from '@/app/components/base/ui/button'
import { VarType } from '../../../types'
import { SUB_VARIABLES } from '../../constants'
import { useGetAvailableVars } from '../../variable-assigner/hooks'
diff --git a/web/app/components/workflow/nodes/if-else/panel.tsx b/web/app/components/workflow/nodes/if-else/panel.tsx
index e8dae6fad9..221b00d1f7 100644
--- a/web/app/components/workflow/nodes/if-else/panel.tsx
+++ b/web/app/components/workflow/nodes/if-else/panel.tsx
@@ -1,6 +1,7 @@
import type { FC } from 'react'
import type { IfElseNodeType } from './types'
import type { NodePanelProps } from '@/app/components/workflow/types'
+import { Button } from '@langgenius/dify-ui/button'
import {
RiAddLine,
} from '@remixicon/react'
@@ -8,7 +9,6 @@ import {
memo,
} from 'react'
import { useTranslation } from 'react-i18next'
-import { Button } from '@/app/components/base/ui/button'
import Field from '@/app/components/workflow/nodes/_base/components/field'
import ConditionWrap from './components/condition-wrap'
import useConfig from './use-config'
diff --git a/web/app/components/workflow/nodes/iteration/__tests__/integration.spec.tsx b/web/app/components/workflow/nodes/iteration/__tests__/integration.spec.tsx
index dc7538144e..26b938bf6e 100644
--- a/web/app/components/workflow/nodes/iteration/__tests__/integration.spec.tsx
+++ b/web/app/components/workflow/nodes/iteration/__tests__/integration.spec.tsx
@@ -1,9 +1,9 @@
import type { ReactNode } from 'react'
import type { IterationNodeType } from '../types'
import type { PanelProps } from '@/types/workflow'
+import { toast } from '@langgenius/dify-ui/toast'
import { fireEvent, render, screen } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
-import { toast } from '@/app/components/base/ui/toast'
import { ErrorHandleMode } from '@/app/components/workflow/types'
import { BlockEnum, VarType } from '../../../types'
import AddBlock from '../add-block'
@@ -15,7 +15,7 @@ const mockHandleNodeAdd = vi.fn()
const mockHandleNodeIterationRerender = vi.fn()
let mockNodesReadOnly = false
-vi.mock('@/app/components/base/ui/toast', () => ({
+vi.mock('@langgenius/dify-ui/toast', () => ({
toast: {
success: vi.fn(),
error: vi.fn(),
diff --git a/web/app/components/workflow/nodes/iteration/node.tsx b/web/app/components/workflow/nodes/iteration/node.tsx
index ad9cb3b0fe..a3aee372d7 100644
--- a/web/app/components/workflow/nodes/iteration/node.tsx
+++ b/web/app/components/workflow/nodes/iteration/node.tsx
@@ -2,6 +2,7 @@ import type { FC } from 'react'
import type { IterationNodeType } from './types'
import type { NodeProps } from '@/app/components/workflow/types'
import { cn } from '@langgenius/dify-ui/cn'
+import { toast } from '@langgenius/dify-ui/toast'
import {
memo,
useEffect,
@@ -13,7 +14,6 @@ import {
useNodesInitialized,
useViewport,
} from 'reactflow'
-import { toast } from '@/app/components/base/ui/toast'
import { IterationStartNodeDumb } from '../iteration-start'
import AddBlock from './add-block'
import { useNodeIterationInteractions } from './use-interactions'
diff --git a/web/app/components/workflow/nodes/iteration/panel.tsx b/web/app/components/workflow/nodes/iteration/panel.tsx
index 7128c2d4b4..c9ed2de363 100644
--- a/web/app/components/workflow/nodes/iteration/panel.tsx
+++ b/web/app/components/workflow/nodes/iteration/panel.tsx
@@ -1,12 +1,12 @@
import type { FC } from 'react'
import type { IterationNodeType } from './types'
import type { NodePanelProps } from '@/app/components/workflow/types'
+import { Slider } from '@langgenius/dify-ui/slider'
+import { Switch } from '@langgenius/dify-ui/switch'
import * as React from 'react'
import { useTranslation } from 'react-i18next'
import Input from '@/app/components/base/input'
import Select from '@/app/components/base/select'
-import Switch from '@/app/components/base/switch'
-import { Slider } from '@/app/components/base/ui/slider'
import Field from '@/app/components/workflow/nodes/_base/components/field'
import { ErrorHandleMode } from '@/app/components/workflow/types'
import { MAX_PARALLEL_LIMIT } from '@/config'
diff --git a/web/app/components/workflow/nodes/knowledge-base/components/chunk-structure/index.tsx b/web/app/components/workflow/nodes/knowledge-base/components/chunk-structure/index.tsx
index a7bba6879e..0920e8a8a7 100644
--- a/web/app/components/workflow/nodes/knowledge-base/components/chunk-structure/index.tsx
+++ b/web/app/components/workflow/nodes/knowledge-base/components/chunk-structure/index.tsx
@@ -1,7 +1,7 @@
import type { ChunkStructureEnum } from '../../types'
+import { Button } from '@langgenius/dify-ui/button'
import { memo } from 'react'
import { useTranslation } from 'react-i18next'
-import { Button } from '@/app/components/base/ui/button'
import { Field } from '@/app/components/workflow/nodes/_base/components/layout'
import OptionCard from '../option-card'
import { useChunkStructure } from './hooks'
diff --git a/web/app/components/workflow/nodes/knowledge-base/components/chunk-structure/selector.tsx b/web/app/components/workflow/nodes/knowledge-base/components/chunk-structure/selector.tsx
index 5018bbc784..7ab8de508f 100644
--- a/web/app/components/workflow/nodes/knowledge-base/components/chunk-structure/selector.tsx
+++ b/web/app/components/workflow/nodes/knowledge-base/components/chunk-structure/selector.tsx
@@ -1,6 +1,7 @@
import type { ReactNode } from 'react'
import type { ChunkStructureEnum } from '../../types'
import type { Option } from './type'
+import { Button } from '@langgenius/dify-ui/button'
import { useCallback, useState } from 'react'
import { useTranslation } from 'react-i18next'
import {
@@ -8,7 +9,6 @@ import {
PortalToFollowElemContent,
PortalToFollowElemTrigger,
} from '@/app/components/base/portal-to-follow-elem'
-import { Button } from '@/app/components/base/ui/button'
import OptionCard from '../option-card'
type SelectorProps = {
diff --git a/web/app/components/workflow/nodes/knowledge-base/components/index-method.tsx b/web/app/components/workflow/nodes/knowledge-base/components/index-method.tsx
index 12cc0f1eb5..70ff0e47eb 100644
--- a/web/app/components/workflow/nodes/knowledge-base/components/index-method.tsx
+++ b/web/app/components/workflow/nodes/knowledge-base/components/index-method.tsx
@@ -1,4 +1,5 @@
import { cn } from '@langgenius/dify-ui/cn'
+import { Slider } from '@langgenius/dify-ui/slider'
import { RiQuestionLine } from '@remixicon/react'
import {
memo,
@@ -11,7 +12,6 @@ import {
} from '@/app/components/base/icons/src/vender/knowledge'
import Input from '@/app/components/base/input'
import Tooltip from '@/app/components/base/tooltip'
-import { Slider } from '@/app/components/base/ui/slider'
import { Field } from '@/app/components/workflow/nodes/_base/components/layout'
import {
ChunkStructureEnum,
diff --git a/web/app/components/workflow/nodes/knowledge-base/components/retrieval-setting/search-method-option.tsx b/web/app/components/workflow/nodes/knowledge-base/components/retrieval-setting/search-method-option.tsx
index fe1d863944..a1f601cce9 100644
--- a/web/app/components/workflow/nodes/knowledge-base/components/retrieval-setting/search-method-option.tsx
+++ b/web/app/components/workflow/nodes/knowledge-base/components/retrieval-setting/search-method-option.tsx
@@ -8,6 +8,7 @@ import type {
Option,
} from './type'
import { cn } from '@langgenius/dify-ui/cn'
+import { Switch } from '@langgenius/dify-ui/switch'
import {
memo,
useCallback,
@@ -16,7 +17,6 @@ import {
import { useTranslation } from 'react-i18next'
import WeightedScoreComponent from '@/app/components/app/configuration/dataset-config/params-config/weighted-score'
import { AlertTriangle } from '@/app/components/base/icons/src/vender/solid/alertsAndFeedback'
-import Switch from '@/app/components/base/switch'
import Tooltip from '@/app/components/base/tooltip'
import { DEFAULT_WEIGHTED_SCORE } from '@/models/datasets'
import {
diff --git a/web/app/components/workflow/nodes/knowledge-base/components/retrieval-setting/top-k-and-score-threshold.tsx b/web/app/components/workflow/nodes/knowledge-base/components/retrieval-setting/top-k-and-score-threshold.tsx
index 5136d4897f..814b3cea6d 100644
--- a/web/app/components/workflow/nodes/knowledge-base/components/retrieval-setting/top-k-and-score-threshold.tsx
+++ b/web/app/components/workflow/nodes/knowledge-base/components/retrieval-setting/top-k-and-score-threshold.tsx
@@ -1,7 +1,3 @@
-import { memo, useCallback } from 'react'
-import { useTranslation } from 'react-i18next'
-import Switch from '@/app/components/base/switch'
-import Tooltip from '@/app/components/base/tooltip'
import {
NumberField,
NumberFieldControls,
@@ -9,7 +5,11 @@ import {
NumberFieldGroup,
NumberFieldIncrement,
NumberFieldInput,
-} from '@/app/components/base/ui/number-field'
+} from '@langgenius/dify-ui/number-field'
+import { Switch } from '@langgenius/dify-ui/switch'
+import { memo, useCallback } from 'react'
+import { useTranslation } from 'react-i18next'
+import Tooltip from '@/app/components/base/tooltip'
import { env } from '@/env'
export type TopKAndScoreThresholdProps = {
diff --git a/web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/add-condition.tsx b/web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/add-condition.tsx
index e94caf7b30..ea7870431a 100644
--- a/web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/add-condition.tsx
+++ b/web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/add-condition.tsx
@@ -1,5 +1,6 @@
import type { MetadataShape } from '@/app/components/workflow/nodes/knowledge-retrieval/types'
import type { MetadataInDoc } from '@/models/datasets'
+import { Button } from '@langgenius/dify-ui/button'
import {
RiAddLine,
} from '@remixicon/react'
@@ -15,7 +16,6 @@ import {
PortalToFollowElemContent,
PortalToFollowElemTrigger,
} from '@/app/components/base/portal-to-follow-elem'
-import { Button } from '@/app/components/base/ui/button'
import MetadataIcon from './metadata-icon'
const AddCondition = ({
diff --git a/web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/condition-list/condition-operator.tsx b/web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/condition-list/condition-operator.tsx
index d6828a5392..f14743ce9f 100644
--- a/web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/condition-list/condition-operator.tsx
+++ b/web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/condition-list/condition-operator.tsx
@@ -2,6 +2,7 @@ import type {
ComparisonOperator,
MetadataFilteringVariableType,
} from '@/app/components/workflow/nodes/knowledge-retrieval/types'
+import { Button } from '@langgenius/dify-ui/button'
import { cn } from '@langgenius/dify-ui/cn'
import { RiArrowDownSLine } from '@remixicon/react'
import {
@@ -14,7 +15,6 @@ import {
PortalToFollowElemContent,
PortalToFollowElemTrigger,
} from '@/app/components/base/portal-to-follow-elem'
-import { Button } from '@/app/components/base/ui/button'
import {
getOperators,
isComparisonOperatorNeedTranslate,
diff --git a/web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/condition-list/condition-value-method.tsx b/web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/condition-list/condition-value-method.tsx
index b69bd05cdd..12d7c9cc69 100644
--- a/web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/condition-list/condition-value-method.tsx
+++ b/web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/condition-list/condition-value-method.tsx
@@ -1,3 +1,4 @@
+import { Button } from '@langgenius/dify-ui/button'
import { cn } from '@langgenius/dify-ui/cn'
import { RiArrowDownSLine } from '@remixicon/react'
import { capitalize } from 'es-toolkit/string'
@@ -7,7 +8,6 @@ import {
PortalToFollowElemContent,
PortalToFollowElemTrigger,
} from '@/app/components/base/portal-to-follow-elem'
-import { Button } from '@/app/components/base/ui/button'
export type ConditionValueMethodProps = {
valueMethod?: string
diff --git a/web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/metadata-filter/metadata-filter-selector.tsx b/web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/metadata-filter/metadata-filter-selector.tsx
index 67939a6448..396fd069e7 100644
--- a/web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/metadata-filter/metadata-filter-selector.tsx
+++ b/web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/metadata-filter/metadata-filter-selector.tsx
@@ -1,3 +1,4 @@
+import { Button } from '@langgenius/dify-ui/button'
import {
RiArrowDownSLine,
RiCheckLine,
@@ -9,7 +10,6 @@ import {
PortalToFollowElemContent,
PortalToFollowElemTrigger,
} from '@/app/components/base/portal-to-follow-elem'
-import { Button } from '@/app/components/base/ui/button'
import { MetadataFilteringModeEnum } from '@/app/components/workflow/nodes/knowledge-retrieval/types'
type MetadataFilterSelectorProps = {
diff --git a/web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/metadata-trigger.tsx b/web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/metadata-trigger.tsx
index 1553da1e1f..dd530ae679 100644
--- a/web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/metadata-trigger.tsx
+++ b/web/app/components/workflow/nodes/knowledge-retrieval/components/metadata/metadata-trigger.tsx
@@ -1,4 +1,5 @@
import type { MetadataShape } from '@/app/components/workflow/nodes/knowledge-retrieval/types'
+import { Button } from '@langgenius/dify-ui/button'
import { RiFilter3Line } from '@remixicon/react'
import {
useEffect,
@@ -10,7 +11,6 @@ import {
PortalToFollowElemContent,
PortalToFollowElemTrigger,
} from '@/app/components/base/portal-to-follow-elem'
-import { Button } from '@/app/components/base/ui/button'
import MetadataPanel from './metadata-panel'
const MetadataTrigger = ({
diff --git a/web/app/components/workflow/nodes/knowledge-retrieval/components/retrieval-config.tsx b/web/app/components/workflow/nodes/knowledge-retrieval/components/retrieval-config.tsx
index 3f8198a822..da71682b35 100644
--- a/web/app/components/workflow/nodes/knowledge-retrieval/components/retrieval-config.tsx
+++ b/web/app/components/workflow/nodes/knowledge-retrieval/components/retrieval-config.tsx
@@ -5,6 +5,7 @@ import type { MultipleRetrievalConfig, SingleRetrievalConfig } from '../types'
import type { ModelParameterModalProps } from '@/app/components/header/account-setting/model-provider-page/model-parameter-modal'
import type { DataSet } from '@/models/datasets'
import type { DatasetConfigs } from '@/models/debug'
+import { Button } from '@langgenius/dify-ui/button'
import { cn } from '@langgenius/dify-ui/cn'
import { RiEqualizer2Line } from '@remixicon/react'
import * as React from 'react'
@@ -16,7 +17,6 @@ import {
PortalToFollowElemContent,
PortalToFollowElemTrigger,
} from '@/app/components/base/portal-to-follow-elem'
-import { Button } from '@/app/components/base/ui/button'
import { DATASET_DEFAULT } from '@/config'
import { RETRIEVE_TYPE } from '@/types/app'
diff --git a/web/app/components/workflow/nodes/list-operator/__tests__/panel.spec.tsx b/web/app/components/workflow/nodes/list-operator/__tests__/panel.spec.tsx
index 9e5147bc92..945ca70104 100644
--- a/web/app/components/workflow/nodes/list-operator/__tests__/panel.spec.tsx
+++ b/web/app/components/workflow/nodes/list-operator/__tests__/panel.spec.tsx
@@ -22,9 +22,8 @@ vi.mock('../use-config', () => ({
default: (...args: unknown[]) => mockUseConfig(...args),
}))
-vi.mock('@/app/components/base/switch', () => ({
- __esModule: true,
- default: (props: {
+vi.mock('@langgenius/dify-ui/switch', () => ({
+ Switch: (props: {
checked?: boolean
disabled?: boolean
onCheckedChange: (value: boolean) => void
diff --git a/web/app/components/workflow/nodes/list-operator/components/__tests__/limit-config.spec.tsx b/web/app/components/workflow/nodes/list-operator/components/__tests__/limit-config.spec.tsx
index fbc3356b32..1314b4600b 100644
--- a/web/app/components/workflow/nodes/list-operator/components/__tests__/limit-config.spec.tsx
+++ b/web/app/components/workflow/nodes/list-operator/components/__tests__/limit-config.spec.tsx
@@ -19,8 +19,8 @@ type MockSliderProps = {
const mockSwitch = vi.fn<(props: MockSwitchProps) => void>()
const mockSlider = vi.fn<(props: MockSliderProps) => void>()
-vi.mock('@/app/components/base/switch', () => ({
- default: (props: MockSwitchProps) => {
+vi.mock('@langgenius/dify-ui/switch', () => ({
+ Switch: (props: MockSwitchProps) => {
mockSwitch(props)
return (
({
default: ({ message }: { message: string }) => {message}
,
}))
-vi.mock('@/app/components/base/ui/popover', async () => {
+vi.mock('@langgenius/dify-ui/popover', async () => {
const ReactModule = await vi.importActual('react')
const PopoverContext = ReactModule.createContext<{
diff --git a/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/json-importer.tsx b/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/json-importer.tsx
index 673fd23ef2..da8c89f755 100644
--- a/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/json-importer.tsx
+++ b/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/json-importer.tsx
@@ -1,11 +1,11 @@
import type { FC } from 'react'
+import { Button } from '@langgenius/dify-ui/button'
import { cn } from '@langgenius/dify-ui/cn'
+import { Popover, PopoverContent, PopoverTrigger } from '@langgenius/dify-ui/popover'
import { RiCloseLine } from '@remixicon/react'
import * as React from 'react'
import { useCallback, useEffect, useRef, useState } from 'react'
import { useTranslation } from 'react-i18next'
-import { Button } from '@/app/components/base/ui/button'
-import { Popover, PopoverContent, PopoverTrigger } from '@/app/components/base/ui/popover'
import { JSON_SCHEMA_MAX_DEPTH } from '@/config'
import { checkJsonDepth } from '../../utils'
import CodeEditor from './code-editor'
diff --git a/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/json-schema-config.tsx b/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/json-schema-config.tsx
index e09f6a59fc..96c508b48f 100644
--- a/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/json-schema-config.tsx
+++ b/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/json-schema-config.tsx
@@ -1,11 +1,11 @@
import type { FC } from 'react'
import type { SchemaRoot } from '../../types'
+import { Button } from '@langgenius/dify-ui/button'
import { cn } from '@langgenius/dify-ui/cn'
+import { toast } from '@langgenius/dify-ui/toast'
import { useCallback, useState } from 'react'
import { useTranslation } from 'react-i18next'
import Divider from '@/app/components/base/divider'
-import { Button } from '@/app/components/base/ui/button'
-import { toast } from '@/app/components/base/ui/toast'
import { JSON_SCHEMA_MAX_DEPTH } from '@/config'
import { SegmentedControl } from '../../../../../base/segmented-control'
import { Type } from '../../types'
diff --git a/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/json-schema-generator/generated-result.tsx b/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/json-schema-generator/generated-result.tsx
index 4f7b2ceb0c..2023d093a7 100644
--- a/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/json-schema-generator/generated-result.tsx
+++ b/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/json-schema-generator/generated-result.tsx
@@ -1,11 +1,11 @@
import type { FC } from 'react'
import type { SchemaRoot } from '../../../types'
+import { Button } from '@langgenius/dify-ui/button'
import { RiArrowLeftLine, RiCloseLine, RiSparklingLine } from '@remixicon/react'
import * as React from 'react'
import { useCallback, useMemo, useState } from 'react'
import { useTranslation } from 'react-i18next'
import Loading from '@/app/components/base/loading'
-import { Button } from '@/app/components/base/ui/button'
import { getValidationErrorMessage, validateSchemaAgainstDraft7 } from '../../../utils'
import CodeEditor from '../code-editor'
import ErrorMessage from '../error-message'
diff --git a/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/json-schema-generator/index.tsx b/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/json-schema-generator/index.tsx
index 7b908bafd6..c949b89adb 100644
--- a/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/json-schema-generator/index.tsx
+++ b/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/json-schema-generator/index.tsx
@@ -3,6 +3,7 @@ import type { SchemaRoot } from '../../../types'
import type { FormValue } from '@/app/components/header/account-setting/model-provider-page/declarations'
import type { CompletionParams, Model } from '@/types/app'
import { cn } from '@langgenius/dify-ui/cn'
+import { toast } from '@langgenius/dify-ui/toast'
import * as React from 'react'
import { useCallback, useEffect, useState } from 'react'
import {
@@ -10,7 +11,6 @@ import {
PortalToFollowElemContent,
PortalToFollowElemTrigger,
} from '@/app/components/base/portal-to-follow-elem'
-import { toast } from '@/app/components/base/ui/toast'
import { ModelTypeEnum } from '@/app/components/header/account-setting/model-provider-page/declarations'
import { useModelListAndDefaultModelAndCurrentProviderAndModel } from '@/app/components/header/account-setting/model-provider-page/hooks'
import useTheme from '@/hooks/use-theme'
diff --git a/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/json-schema-generator/prompt-editor.tsx b/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/json-schema-generator/prompt-editor.tsx
index 1a7bce0361..524d7079df 100644
--- a/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/json-schema-generator/prompt-editor.tsx
+++ b/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/json-schema-generator/prompt-editor.tsx
@@ -1,13 +1,13 @@
import type { FC } from 'react'
import type { FormValue } from '@/app/components/header/account-setting/model-provider-page/declarations'
import type { Model } from '@/types/app'
+import { Button } from '@langgenius/dify-ui/button'
import { RiCloseLine, RiSparklingFill } from '@remixicon/react'
import * as React from 'react'
import { useCallback } from 'react'
import { useTranslation } from 'react-i18next'
import Textarea from '@/app/components/base/textarea'
import Tooltip from '@/app/components/base/tooltip'
-import { Button } from '@/app/components/base/ui/button'
import ModelParameterModal from '@/app/components/header/account-setting/model-provider-page/model-parameter-modal'
type ModelInfo = {
diff --git a/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/visual-editor/add-field.tsx b/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/visual-editor/add-field.tsx
index d25ee83f80..8c57509f84 100644
--- a/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/visual-editor/add-field.tsx
+++ b/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/visual-editor/add-field.tsx
@@ -1,8 +1,8 @@
+import { Button } from '@langgenius/dify-ui/button'
import { RiAddCircleFill } from '@remixicon/react'
import * as React from 'react'
import { useCallback } from 'react'
import { useTranslation } from 'react-i18next'
-import { Button } from '@/app/components/base/ui/button'
import { useMittContext } from './context'
import { useVisualEditorStore } from './store'
diff --git a/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/visual-editor/edit-card/advanced-actions.tsx b/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/visual-editor/edit-card/advanced-actions.tsx
index 3601895942..9a72df3d31 100644
--- a/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/visual-editor/edit-card/advanced-actions.tsx
+++ b/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/visual-editor/edit-card/advanced-actions.tsx
@@ -1,8 +1,8 @@
import type { FC } from 'react'
+import { Button } from '@langgenius/dify-ui/button'
import { useKeyPress } from 'ahooks'
import * as React from 'react'
import { useTranslation } from 'react-i18next'
-import { Button } from '@/app/components/base/ui/button'
import ShortcutsName from '@/app/components/workflow/shortcuts-name'
import { getKeyboardKeyCodeBySystem } from '@/app/components/workflow/utils'
diff --git a/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/visual-editor/edit-card/required-switch.tsx b/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/visual-editor/edit-card/required-switch.tsx
index 9d5a5c1fed..674ef320f2 100644
--- a/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/visual-editor/edit-card/required-switch.tsx
+++ b/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/visual-editor/edit-card/required-switch.tsx
@@ -1,7 +1,7 @@
import type { FC } from 'react'
+import { Switch } from '@langgenius/dify-ui/switch'
import * as React from 'react'
import { useTranslation } from 'react-i18next'
-import Switch from '@/app/components/base/switch'
type RequiredSwitchProps = {
defaultValue: boolean
diff --git a/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/visual-editor/hooks.ts b/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/visual-editor/hooks.ts
index cf7ef1571d..0c327b6e03 100644
--- a/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/visual-editor/hooks.ts
+++ b/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/visual-editor/hooks.ts
@@ -1,10 +1,10 @@
import type { VisualEditorProps } from '.'
import type { Field } from '../../../types'
import type { EditData } from './edit-card'
+import { toast } from '@langgenius/dify-ui/toast'
import { noop } from 'es-toolkit/function'
import { produce } from 'immer'
import { useTranslation } from 'react-i18next'
-import { toast } from '@/app/components/base/ui/toast'
import { ArrayType, Type } from '../../../types'
import { findPropertyWithPath } from '../../../utils'
import { useMittContext } from './context'
diff --git a/web/app/components/workflow/nodes/llm/components/panel-memory-section.tsx b/web/app/components/workflow/nodes/llm/components/panel-memory-section.tsx
index 568575139a..59b38b41db 100644
--- a/web/app/components/workflow/nodes/llm/components/panel-memory-section.tsx
+++ b/web/app/components/workflow/nodes/llm/components/panel-memory-section.tsx
@@ -1,9 +1,9 @@
import type { FC } from 'react'
import type { LLMNodeType } from '../types'
import type { Memory, Node, NodeOutPutVar } from '@/app/components/workflow/types'
+import { Tooltip, TooltipContent, TooltipTrigger } from '@langgenius/dify-ui/tooltip'
import * as React from 'react'
import { useTranslation } from 'react-i18next'
-import { Tooltip, TooltipContent, TooltipTrigger } from '@/app/components/base/ui/tooltip'
import MemoryConfig from '@/app/components/workflow/nodes/_base/components/memory-config'
import Editor from '@/app/components/workflow/nodes/_base/components/prompt/editor'
@@ -52,7 +52,6 @@ const PanelMemorySection: FC = ({
{t('nodes.common.memories.title', { ns: 'workflow' })}
@@ -75,7 +74,6 @@ const PanelMemorySection: FC = ({
user
diff --git a/web/app/components/workflow/nodes/llm/components/panel-output-section.tsx b/web/app/components/workflow/nodes/llm/components/panel-output-section.tsx
index a70f035728..bd333b6b1f 100644
--- a/web/app/components/workflow/nodes/llm/components/panel-output-section.tsx
+++ b/web/app/components/workflow/nodes/llm/components/panel-output-section.tsx
@@ -1,10 +1,10 @@
import type { FC } from 'react'
import type { LLMNodeType, StructuredOutput } from '../types'
+import { Switch } from '@langgenius/dify-ui/switch'
+import { Tooltip, TooltipContent, TooltipTrigger } from '@langgenius/dify-ui/tooltip'
import { RiAlertFill, RiQuestionLine } from '@remixicon/react'
import * as React from 'react'
import { useTranslation } from 'react-i18next'
-import Switch from '@/app/components/base/switch'
-import { Tooltip, TooltipContent, TooltipTrigger } from '@/app/components/base/ui/tooltip'
import OutputVars, { VarItem } from '@/app/components/workflow/nodes/_base/components/output-vars'
import Split from '@/app/components/workflow/nodes/_base/components/split'
import StructureOutput from './structure-output'
@@ -43,7 +43,6 @@ const PanelOutputSection: FC = ({
{(!isModelSupportStructuredOutput && !!inputs.structured_output_enabled) && (
@@ -61,7 +60,6 @@ const PanelOutputSection: FC = ({
{t('structOutput.structured', { ns: 'app' })}
diff --git a/web/app/components/workflow/nodes/llm/components/reasoning-format-config.tsx b/web/app/components/workflow/nodes/llm/components/reasoning-format-config.tsx
index c9177e2d64..1c390ecb83 100644
--- a/web/app/components/workflow/nodes/llm/components/reasoning-format-config.tsx
+++ b/web/app/components/workflow/nodes/llm/components/reasoning-format-config.tsx
@@ -1,7 +1,7 @@
import type { FC } from 'react'
+import { Switch } from '@langgenius/dify-ui/switch'
import * as React from 'react'
import { useTranslation } from 'react-i18next'
-import Switch from '@/app/components/base/switch'
import Field from '@/app/components/workflow/nodes/_base/components/field'
type ReasoningFormatConfigProps = {
diff --git a/web/app/components/workflow/nodes/llm/components/structure-output.tsx b/web/app/components/workflow/nodes/llm/components/structure-output.tsx
index 884c638110..d8b3e132bb 100644
--- a/web/app/components/workflow/nodes/llm/components/structure-output.tsx
+++ b/web/app/components/workflow/nodes/llm/components/structure-output.tsx
@@ -1,13 +1,13 @@
'use client'
import type { FC } from 'react'
import type { SchemaRoot, StructuredOutput } from '../types'
+import { Button } from '@langgenius/dify-ui/button'
import { cn } from '@langgenius/dify-ui/cn'
import { RiEditLine } from '@remixicon/react'
import { useBoolean } from 'ahooks'
import * as React from 'react'
import { useCallback } from 'react'
import { useTranslation } from 'react-i18next'
-import { Button } from '@/app/components/base/ui/button'
import ShowPanel from '@/app/components/workflow/nodes/_base/components/variable/object-child-tree-panel/show'
import { Type } from '../types'
import JsonSchemaConfigModal from './json-schema-config-modal'
diff --git a/web/app/components/workflow/nodes/llm/panel.tsx b/web/app/components/workflow/nodes/llm/panel.tsx
index f465366ee9..922a18e46e 100644
--- a/web/app/components/workflow/nodes/llm/panel.tsx
+++ b/web/app/components/workflow/nodes/llm/panel.tsx
@@ -1,10 +1,10 @@
import type { FC } from 'react'
import type { LLMNodeType } from './types'
import type { NodePanelProps } from '@/app/components/workflow/types'
+import { toast } from '@langgenius/dify-ui/toast'
import * as React from 'react'
import { useCallback } from 'react'
import { useTranslation } from 'react-i18next'
-import { toast } from '@/app/components/base/ui/toast'
import ModelParameterModal from '@/app/components/header/account-setting/model-provider-page/model-parameter-modal'
import Field from '@/app/components/workflow/nodes/_base/components/field'
import Split from '@/app/components/workflow/nodes/_base/components/split'
diff --git a/web/app/components/workflow/nodes/loop/__tests__/integration.spec.tsx b/web/app/components/workflow/nodes/loop/__tests__/integration.spec.tsx
index 99ce377b99..0af05e9674 100644
--- a/web/app/components/workflow/nodes/loop/__tests__/integration.spec.tsx
+++ b/web/app/components/workflow/nodes/loop/__tests__/integration.spec.tsx
@@ -181,7 +181,7 @@ vi.mock('@/app/components/workflow/nodes/_base/components/editor/code-editor', (
),
}))
-vi.mock('@/app/components/base/ui/toast', () => ({
+vi.mock('@langgenius/dify-ui/toast', () => ({
toast: {
success: (message: string) => mockToastNotify({ type: 'success', message }),
error: (message: string) => mockToastNotify({ type: 'error', message }),
diff --git a/web/app/components/workflow/nodes/loop/components/condition-add.tsx b/web/app/components/workflow/nodes/loop/components/condition-add.tsx
index 1d9be045d2..36a09dd434 100644
--- a/web/app/components/workflow/nodes/loop/components/condition-add.tsx
+++ b/web/app/components/workflow/nodes/loop/components/condition-add.tsx
@@ -4,6 +4,7 @@ import type {
ValueSelector,
Var,
} from '@/app/components/workflow/types'
+import { Button } from '@langgenius/dify-ui/button'
import { RiAddLine } from '@remixicon/react'
import {
useCallback,
@@ -15,7 +16,6 @@ import {
PortalToFollowElemContent,
PortalToFollowElemTrigger,
} from '@/app/components/base/portal-to-follow-elem'
-import { Button } from '@/app/components/base/ui/button'
import VarReferenceVars from '@/app/components/workflow/nodes/_base/components/variable/var-reference-vars'
type ConditionAddProps = {
diff --git a/web/app/components/workflow/nodes/loop/components/condition-list/condition-operator.tsx b/web/app/components/workflow/nodes/loop/components/condition-list/condition-operator.tsx
index 9380282b54..916326c416 100644
--- a/web/app/components/workflow/nodes/loop/components/condition-list/condition-operator.tsx
+++ b/web/app/components/workflow/nodes/loop/components/condition-list/condition-operator.tsx
@@ -1,5 +1,6 @@
import type { ComparisonOperator } from '../../types'
import type { VarType } from '@/app/components/workflow/types'
+import { Button } from '@langgenius/dify-ui/button'
import { cn } from '@langgenius/dify-ui/cn'
import { RiArrowDownSLine } from '@remixicon/react'
import {
@@ -12,7 +13,6 @@ import {
PortalToFollowElemContent,
PortalToFollowElemTrigger,
} from '@/app/components/base/portal-to-follow-elem'
-import { Button } from '@/app/components/base/ui/button'
import { getOperators, isComparisonOperatorNeedTranslate } from '../../utils'
const i18nPrefix = 'nodes.ifElse'
diff --git a/web/app/components/workflow/nodes/loop/components/condition-number-input.tsx b/web/app/components/workflow/nodes/loop/components/condition-number-input.tsx
index 7bcb4137b5..954f713a78 100644
--- a/web/app/components/workflow/nodes/loop/components/condition-number-input.tsx
+++ b/web/app/components/workflow/nodes/loop/components/condition-number-input.tsx
@@ -2,6 +2,7 @@ import type {
NodeOutPutVar,
ValueSelector,
} from '@/app/components/workflow/types'
+import { Button } from '@langgenius/dify-ui/button'
import { cn } from '@langgenius/dify-ui/cn'
import { RiArrowDownSLine } from '@remixicon/react'
import { useBoolean } from 'ahooks'
@@ -18,7 +19,6 @@ import {
PortalToFollowElemContent,
PortalToFollowElemTrigger,
} from '@/app/components/base/portal-to-follow-elem'
-import { Button } from '@/app/components/base/ui/button'
import VarReferenceVars from '@/app/components/workflow/nodes/_base/components/variable/var-reference-vars'
import { VarType } from '@/app/components/workflow/types'
import { variableTransformer } from '@/app/components/workflow/utils'
diff --git a/web/app/components/workflow/nodes/loop/components/condition-wrap.tsx b/web/app/components/workflow/nodes/loop/components/condition-wrap.tsx
index 50e3fd248e..f73386bfa8 100644
--- a/web/app/components/workflow/nodes/loop/components/condition-wrap.tsx
+++ b/web/app/components/workflow/nodes/loop/components/condition-wrap.tsx
@@ -2,6 +2,7 @@
import type { FC } from 'react'
import type { Node, NodeOutPutVar, Var } from '../../../types'
import type { Condition, HandleAddCondition, HandleAddSubVariableCondition, HandleRemoveCondition, handleRemoveSubVariableCondition, HandleToggleConditionLogicalOperator, HandleToggleSubVariableConditionLogicalOperator, HandleUpdateCondition, HandleUpdateSubVariableCondition, LogicalOperator } from '../types'
+import { Button } from '@langgenius/dify-ui/button'
import { cn } from '@langgenius/dify-ui/cn'
import {
RiAddLine,
@@ -10,7 +11,6 @@ import * as React from 'react'
import { useCallback } from 'react'
import { useTranslation } from 'react-i18next'
import { PortalSelect as Select } from '@/app/components/base/select'
-import { Button } from '@/app/components/base/ui/button'
import { VarType } from '../../../types'
import { useGetAvailableVars } from '../../variable-assigner/hooks'
import { SUB_VARIABLES } from './../default'
diff --git a/web/app/components/workflow/nodes/loop/components/loop-variables/item.tsx b/web/app/components/workflow/nodes/loop/components/loop-variables/item.tsx
index 7bd7e09c1b..b0668cca60 100644
--- a/web/app/components/workflow/nodes/loop/components/loop-variables/item.tsx
+++ b/web/app/components/workflow/nodes/loop/components/loop-variables/item.tsx
@@ -2,12 +2,12 @@ import type {
LoopVariable,
LoopVariablesComponentShape,
} from '@/app/components/workflow/nodes/loop/types'
+import { toast } from '@langgenius/dify-ui/toast'
import { RiDeleteBinLine } from '@remixicon/react'
import { useCallback } from 'react'
import { useTranslation } from 'react-i18next'
import ActionButton from '@/app/components/base/action-button'
import Input from '@/app/components/base/input'
-import { toast } from '@/app/components/base/ui/toast'
import { ValueType, VarType } from '@/app/components/workflow/types'
import { checkKeys, replaceSpaceWithUnderscoreInVarNameInput } from '@/utils/var'
import FormItem from './form-item'
diff --git a/web/app/components/workflow/nodes/parameter-extractor/__tests__/integration.spec.tsx b/web/app/components/workflow/nodes/parameter-extractor/__tests__/integration.spec.tsx
index 490f981268..f63d9a0d50 100644
--- a/web/app/components/workflow/nodes/parameter-extractor/__tests__/integration.spec.tsx
+++ b/web/app/components/workflow/nodes/parameter-extractor/__tests__/integration.spec.tsx
@@ -4,9 +4,9 @@ import type { Param, ParameterExtractorNodeType } from '../types'
import type { ToolParameter } from '@/app/components/tools/types'
import type { ToolDefaultValue } from '@/app/components/workflow/block-selector/types'
import type { PanelProps } from '@/types/workflow'
+import { toast } from '@langgenius/dify-ui/toast'
import { fireEvent, render, screen } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
-import { toast } from '@/app/components/base/ui/toast'
import {
useTextGenerationCurrentProviderAndModelAndModelList,
} from '@/app/components/header/account-setting/model-provider-page/hooks'
@@ -39,7 +39,7 @@ let mockWorkflowTools: MockToolCollection[] = []
let mockSelectedToolInfo: ToolDefaultValue | undefined
let mockBlockSelectorOpen = false
-vi.mock('@/app/components/base/ui/toast', () => ({
+vi.mock('@langgenius/dify-ui/toast', () => ({
toast: {
success: vi.fn(),
error: vi.fn(),
diff --git a/web/app/components/workflow/nodes/parameter-extractor/components/extract-parameter/__tests__/update.spec.tsx b/web/app/components/workflow/nodes/parameter-extractor/components/extract-parameter/__tests__/update.spec.tsx
index 7e6c7f22dc..c73b990b68 100644
--- a/web/app/components/workflow/nodes/parameter-extractor/components/extract-parameter/__tests__/update.spec.tsx
+++ b/web/app/components/workflow/nodes/parameter-extractor/components/extract-parameter/__tests__/update.spec.tsx
@@ -1,11 +1,11 @@
import type { Param } from '../../../types'
+import { toast } from '@langgenius/dify-ui/toast'
import { fireEvent, render, screen, waitFor, within } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
-import { toast } from '@/app/components/base/ui/toast'
import { ParamType } from '../../../types'
import Update from '../update'
-vi.mock('@/app/components/base/ui/toast', () => ({
+vi.mock('@langgenius/dify-ui/toast', () => ({
toast: {
success: vi.fn(),
error: vi.fn(),
diff --git a/web/app/components/workflow/nodes/parameter-extractor/components/extract-parameter/update.tsx b/web/app/components/workflow/nodes/parameter-extractor/components/extract-parameter/update.tsx
index d3a3a39b60..9be35242c5 100644
--- a/web/app/components/workflow/nodes/parameter-extractor/components/extract-parameter/update.tsx
+++ b/web/app/components/workflow/nodes/parameter-extractor/components/extract-parameter/update.tsx
@@ -2,6 +2,9 @@
import type { FC } from 'react'
import type { Param } from '../../types'
import type { MoreInfo } from '@/app/components/workflow/types'
+import { Button } from '@langgenius/dify-ui/button'
+import { Switch } from '@langgenius/dify-ui/switch'
+import { toast } from '@langgenius/dify-ui/toast'
import { useBoolean } from 'ahooks'
import * as React from 'react'
import { useCallback, useState } from 'react'
@@ -11,10 +14,7 @@ import ConfigSelect from '@/app/components/app/configuration/config-var/config-s
import Input from '@/app/components/base/input'
import Modal from '@/app/components/base/modal'
import Select from '@/app/components/base/select'
-import Switch from '@/app/components/base/switch'
import Textarea from '@/app/components/base/textarea'
-import { Button } from '@/app/components/base/ui/button'
-import { toast } from '@/app/components/base/ui/toast'
import { ChangeType } from '@/app/components/workflow/types'
import { checkKeys } from '@/utils/var'
import { ParamType } from '../../types'
diff --git a/web/app/components/workflow/nodes/start/__tests__/use-config.spec.ts b/web/app/components/workflow/nodes/start/__tests__/use-config.spec.ts
index 16d37a6a8f..847d58afe4 100644
--- a/web/app/components/workflow/nodes/start/__tests__/use-config.spec.ts
+++ b/web/app/components/workflow/nodes/start/__tests__/use-config.spec.ts
@@ -32,7 +32,7 @@ vi.mock('@/app/components/workflow/hooks/use-inspect-vars-crud', () => ({
default: (...args: unknown[]) => mockUseInspectVarsCrud(...args),
}))
-vi.mock('@/app/components/base/ui/toast', () => ({
+vi.mock('@langgenius/dify-ui/toast', () => ({
__esModule: true,
toast: {
error: (message: string) => mockNotify({ type: 'error', message }),
diff --git a/web/app/components/workflow/nodes/start/components/var-list.tsx b/web/app/components/workflow/nodes/start/components/var-list.tsx
index d4f6511cdb..3cb1b82edf 100644
--- a/web/app/components/workflow/nodes/start/components/var-list.tsx
+++ b/web/app/components/workflow/nodes/start/components/var-list.tsx
@@ -2,13 +2,13 @@
import type { FC } from 'react'
import type { InputVar, MoreInfo } from '@/app/components/workflow/types'
import { cn } from '@langgenius/dify-ui/cn'
+import { toast } from '@langgenius/dify-ui/toast'
import { RiDraggable } from '@remixicon/react'
import { produce } from 'immer'
import * as React from 'react'
import { useCallback, useMemo } from 'react'
import { useTranslation } from 'react-i18next'
import { ReactSortable } from 'react-sortablejs'
-import { toast } from '@/app/components/base/ui/toast'
import { ChangeType } from '@/app/components/workflow/types'
import { hasDuplicateStr } from '@/utils/var'
import VarItem from './var-item'
diff --git a/web/app/components/workflow/nodes/start/use-config.ts b/web/app/components/workflow/nodes/start/use-config.ts
index e954253561..4ae67bb8d3 100644
--- a/web/app/components/workflow/nodes/start/use-config.ts
+++ b/web/app/components/workflow/nodes/start/use-config.ts
@@ -1,10 +1,10 @@
import type { StartNodeType } from './types'
import type { InputVar, MoreInfo, ValueSelector } from '@/app/components/workflow/types'
+import { toast } from '@langgenius/dify-ui/toast'
import { useBoolean } from 'ahooks'
import { produce } from 'immer'
import { useCallback, useState } from 'react'
import { useTranslation } from 'react-i18next'
-import { toast } from '@/app/components/base/ui/toast'
import {
useIsChatMode,
useNodesReadOnly,
diff --git a/web/app/components/workflow/nodes/tool/components/tool-form/item.tsx b/web/app/components/workflow/nodes/tool/components/tool-form/item.tsx
index 7ccf137899..6cd13984f0 100644
--- a/web/app/components/workflow/nodes/tool/components/tool-form/item.tsx
+++ b/web/app/components/workflow/nodes/tool/components/tool-form/item.tsx
@@ -4,12 +4,12 @@ import type { ToolVarInputs } from '../../types'
import type { CredentialFormSchema } from '@/app/components/header/account-setting/model-provider-page/declarations'
import type { Tool } from '@/app/components/tools/types'
import type { ToolWithProvider } from '@/app/components/workflow/types'
+import { Button } from '@langgenius/dify-ui/button'
import {
RiBracesLine,
} from '@remixicon/react'
import { useBoolean } from 'ahooks'
import Tooltip from '@/app/components/base/tooltip'
-import { Button } from '@/app/components/base/ui/button'
import { FormTypeEnum } from '@/app/components/header/account-setting/model-provider-page/declarations'
import { useLanguage } from '@/app/components/header/account-setting/model-provider-page/hooks'
import { SchemaModal } from '@/app/components/plugins/plugin-detail-panel/tool-selector/components'
diff --git a/web/app/components/workflow/nodes/tool/hooks/use-config.ts b/web/app/components/workflow/nodes/tool/hooks/use-config.ts
index 5e3f928dcb..dda11df74a 100644
--- a/web/app/components/workflow/nodes/tool/hooks/use-config.ts
+++ b/web/app/components/workflow/nodes/tool/hooks/use-config.ts
@@ -1,11 +1,11 @@
import type { ToolNodeType, ToolVarInputs } from '../types'
import type { InputVar } from '@/app/components/workflow/types'
+import { toast } from '@langgenius/dify-ui/toast'
import { useBoolean } from 'ahooks'
import { capitalize } from 'es-toolkit/string'
import { produce } from 'immer'
import { useCallback, useEffect, useMemo, useState } from 'react'
import { useTranslation } from 'react-i18next'
-import { toast } from '@/app/components/base/ui/toast'
import { useLanguage } from '@/app/components/header/account-setting/model-provider-page/hooks'
import { CollectionType } from '@/app/components/tools/types'
import {
diff --git a/web/app/components/workflow/nodes/trigger-plugin/components/trigger-form/item.tsx b/web/app/components/workflow/nodes/trigger-plugin/components/trigger-form/item.tsx
index 68810fb068..44eb9d44c6 100644
--- a/web/app/components/workflow/nodes/trigger-plugin/components/trigger-form/item.tsx
+++ b/web/app/components/workflow/nodes/trigger-plugin/components/trigger-form/item.tsx
@@ -4,12 +4,12 @@ import type { CredentialFormSchema } from '@/app/components/header/account-setti
import type { Event } from '@/app/components/tools/types'
import type { TriggerWithProvider } from '@/app/components/workflow/block-selector/types'
import type { PluginTriggerVarInputs } from '@/app/components/workflow/nodes/trigger-plugin/types'
+import { Button } from '@langgenius/dify-ui/button'
import {
RiBracesLine,
} from '@remixicon/react'
import { useBoolean } from 'ahooks'
import Tooltip from '@/app/components/base/tooltip'
-import { Button } from '@/app/components/base/ui/button'
import { FormTypeEnum } from '@/app/components/header/account-setting/model-provider-page/declarations'
import { useLanguage } from '@/app/components/header/account-setting/model-provider-page/hooks'
import { SchemaModal } from '@/app/components/plugins/plugin-detail-panel/tool-selector/components'
diff --git a/web/app/components/workflow/nodes/trigger-schedule/components/on-minute-selector.tsx b/web/app/components/workflow/nodes/trigger-schedule/components/on-minute-selector.tsx
index 61c943992c..7c0713019e 100644
--- a/web/app/components/workflow/nodes/trigger-schedule/components/on-minute-selector.tsx
+++ b/web/app/components/workflow/nodes/trigger-schedule/components/on-minute-selector.tsx
@@ -1,6 +1,6 @@
+import { Slider } from '@langgenius/dify-ui/slider'
import * as React from 'react'
import { useTranslation } from 'react-i18next'
-import { Slider } from '@/app/components/base/ui/slider'
type OnMinuteSelectorProps = {
value?: number
diff --git a/web/app/components/workflow/nodes/trigger-webhook/__tests__/use-config.spec.tsx b/web/app/components/workflow/nodes/trigger-webhook/__tests__/use-config.spec.tsx
index 92a0457598..8f47304734 100644
--- a/web/app/components/workflow/nodes/trigger-webhook/__tests__/use-config.spec.tsx
+++ b/web/app/components/workflow/nodes/trigger-webhook/__tests__/use-config.spec.tsx
@@ -1,7 +1,7 @@
import type { WebhookTriggerNodeType } from '../types'
+import { toast } from '@langgenius/dify-ui/toast'
import { renderHook } from '@testing-library/react'
import { useStore as useAppStore } from '@/app/components/app/store'
-import { toast } from '@/app/components/base/ui/toast'
import { BlockEnum, VarType } from '@/app/components/workflow/types'
import { fetchWebhookUrl } from '@/service/apps'
import { createNodeCrudModuleMock } from '../../__tests__/use-config-test-utils'
@@ -18,7 +18,7 @@ vi.mock('react-i18next', () => ({
}),
}))
-vi.mock('@/app/components/base/ui/toast', () => ({
+vi.mock('@langgenius/dify-ui/toast', () => ({
__esModule: true,
toast: {
error: vi.fn(),
diff --git a/web/app/components/workflow/nodes/trigger-webhook/panel.tsx b/web/app/components/workflow/nodes/trigger-webhook/panel.tsx
index 4f276d3535..d7f46edfcf 100644
--- a/web/app/components/workflow/nodes/trigger-webhook/panel.tsx
+++ b/web/app/components/workflow/nodes/trigger-webhook/panel.tsx
@@ -1,14 +1,6 @@
import type { FC } from 'react'
import type { HttpMethod, WebhookTriggerNodeType } from './types'
import type { NodePanelProps } from '@/app/components/workflow/types'
-import copy from 'copy-to-clipboard'
-
-import * as React from 'react'
-import { useEffect, useRef, useState } from 'react'
-import { useTranslation } from 'react-i18next'
-import InputWithCopy from '@/app/components/base/input-with-copy'
-import { SimpleSelect } from '@/app/components/base/select'
-import Tooltip from '@/app/components/base/tooltip'
import {
NumberField,
NumberFieldControls,
@@ -16,8 +8,16 @@ import {
NumberFieldGroup,
NumberFieldIncrement,
NumberFieldInput,
-} from '@/app/components/base/ui/number-field'
-import { toast } from '@/app/components/base/ui/toast'
+} from '@langgenius/dify-ui/number-field'
+
+import { toast } from '@langgenius/dify-ui/toast'
+import copy from 'copy-to-clipboard'
+import * as React from 'react'
+import { useEffect, useRef, useState } from 'react'
+import { useTranslation } from 'react-i18next'
+import InputWithCopy from '@/app/components/base/input-with-copy'
+import { SimpleSelect } from '@/app/components/base/select'
+import Tooltip from '@/app/components/base/tooltip'
import Field from '@/app/components/workflow/nodes/_base/components/field'
import OutputVars from '@/app/components/workflow/nodes/_base/components/output-vars'
import Split from '@/app/components/workflow/nodes/_base/components/split'
diff --git a/web/app/components/workflow/nodes/trigger-webhook/use-config.ts b/web/app/components/workflow/nodes/trigger-webhook/use-config.ts
index 7924a35ba0..d01a5ee463 100644
--- a/web/app/components/workflow/nodes/trigger-webhook/use-config.ts
+++ b/web/app/components/workflow/nodes/trigger-webhook/use-config.ts
@@ -1,8 +1,8 @@
import type { HttpMethod, WebhookHeader, WebhookParameter, WebhookTriggerNodeType } from './types'
+import { toast } from '@langgenius/dify-ui/toast'
import { useCallback } from 'react'
import { useTranslation } from 'react-i18next'
import { useStore as useAppStore } from '@/app/components/app/store'
-import { toast } from '@/app/components/base/ui/toast'
import { useNodesReadOnly, useWorkflow } from '@/app/components/workflow/hooks'
import useNodeCrud from '@/app/components/workflow/nodes/_base/hooks/use-node-crud'
import { fetchWebhookUrl } from '@/service/apps'
diff --git a/web/app/components/workflow/nodes/variable-assigner/__tests__/integration.spec.tsx b/web/app/components/workflow/nodes/variable-assigner/__tests__/integration.spec.tsx
index d9176480dc..11ec8bdbcd 100644
--- a/web/app/components/workflow/nodes/variable-assigner/__tests__/integration.spec.tsx
+++ b/web/app/components/workflow/nodes/variable-assigner/__tests__/integration.spec.tsx
@@ -1,9 +1,9 @@
/* eslint-disable ts/no-explicit-any, style/jsx-one-expression-per-line */
import type { VariableAssignerNodeType } from '../types'
import type { PanelProps } from '@/types/workflow'
+import { toast } from '@langgenius/dify-ui/toast'
import { fireEvent, render, screen } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
-import { toast } from '@/app/components/base/ui/toast'
import { renderWorkflowFlowComponent } from '@/app/components/workflow/__tests__/workflow-test-env'
import { BlockEnum, VarType } from '@/app/components/workflow/types'
import AddVariable from '../components/add-variable'
@@ -19,7 +19,7 @@ const mockHandleGroupItemMouseEnter = vi.fn()
const mockHandleGroupItemMouseLeave = vi.fn()
const mockGetAvailableVars = vi.fn()
-vi.mock('@/app/components/base/ui/toast', () => ({
+vi.mock('@langgenius/dify-ui/toast', () => ({
toast: {
success: vi.fn(),
error: vi.fn(),
diff --git a/web/app/components/workflow/nodes/variable-assigner/components/__tests__/var-group-item.spec.tsx b/web/app/components/workflow/nodes/variable-assigner/components/__tests__/var-group-item.spec.tsx
index 20650f0ddd..2a922b9c79 100644
--- a/web/app/components/workflow/nodes/variable-assigner/components/__tests__/var-group-item.spec.tsx
+++ b/web/app/components/workflow/nodes/variable-assigner/components/__tests__/var-group-item.spec.tsx
@@ -1,12 +1,12 @@
import type { ComponentProps } from 'react'
+import { toast } from '@langgenius/dify-ui/toast'
import { fireEvent, screen } from '@testing-library/react'
-import { toast } from '@/app/components/base/ui/toast'
import { createNode, resetFixtureCounters } from '@/app/components/workflow/__tests__/fixtures'
import { renderWorkflowFlowComponent } from '@/app/components/workflow/__tests__/workflow-test-env'
import { BlockEnum, VarType } from '@/app/components/workflow/types'
import VarGroupItem from '../var-group-item'
-vi.mock('@/app/components/base/ui/toast', () => ({
+vi.mock('@langgenius/dify-ui/toast', () => ({
toast: {
success: vi.fn(),
error: vi.fn(),
diff --git a/web/app/components/workflow/nodes/variable-assigner/components/add-variable/index.tsx b/web/app/components/workflow/nodes/variable-assigner/components/add-variable/index.tsx
index 53c3c36e04..078f0c12b5 100644
--- a/web/app/components/workflow/nodes/variable-assigner/components/add-variable/index.tsx
+++ b/web/app/components/workflow/nodes/variable-assigner/components/add-variable/index.tsx
@@ -5,13 +5,13 @@ import type {
Var,
} from '@/app/components/workflow/types'
import { cn } from '@langgenius/dify-ui/cn'
+import { Popover, PopoverContent, PopoverTrigger } from '@langgenius/dify-ui/popover'
import {
memo,
useCallback,
useState,
} from 'react'
import { Plus02 } from '@/app/components/base/icons/src/vender/line/general'
-import { Popover, PopoverContent, PopoverTrigger } from '@/app/components/base/ui/popover'
import AddVariablePopup from '@/app/components/workflow/nodes/_base/components/add-variable-popup'
import { useVariableAssigner } from '../../hooks'
diff --git a/web/app/components/workflow/nodes/variable-assigner/components/var-group-item.tsx b/web/app/components/workflow/nodes/variable-assigner/components/var-group-item.tsx
index 738e986e9c..bc1e428b6a 100644
--- a/web/app/components/workflow/nodes/variable-assigner/components/var-group-item.tsx
+++ b/web/app/components/workflow/nodes/variable-assigner/components/var-group-item.tsx
@@ -2,6 +2,7 @@
import type { ChangeEvent, FC } from 'react'
import type { VarGroupItem as VarGroupItemType } from '../types'
import type { NodeOutPutVar, ValueSelector, Var } from '@/app/components/workflow/types'
+import { toast } from '@langgenius/dify-ui/toast'
import {
RiDeleteBinLine,
} from '@remixicon/react'
@@ -11,7 +12,6 @@ import * as React from 'react'
import { useCallback } from 'react'
import { useTranslation } from 'react-i18next'
import { Folder } from '@/app/components/base/icons/src/vender/line/files'
-import { toast } from '@/app/components/base/ui/toast'
import Field from '@/app/components/workflow/nodes/_base/components/field'
import { VarType as VarKindType } from '@/app/components/workflow/nodes/tool/types'
import { VarType } from '@/app/components/workflow/types'
diff --git a/web/app/components/workflow/nodes/variable-assigner/panel.tsx b/web/app/components/workflow/nodes/variable-assigner/panel.tsx
index 922db3dcc3..93eaa9f9f4 100644
--- a/web/app/components/workflow/nodes/variable-assigner/panel.tsx
+++ b/web/app/components/workflow/nodes/variable-assigner/panel.tsx
@@ -2,9 +2,9 @@ import type { FC } from 'react'
import type { VariableAssignerNodeType } from './types'
import type { NodePanelProps } from '@/app/components/workflow/types'
import { cn } from '@langgenius/dify-ui/cn'
+import { Switch } from '@langgenius/dify-ui/switch'
import * as React from 'react'
import { useTranslation } from 'react-i18next'
-import Switch from '@/app/components/base/switch'
import AddButton from '@/app/components/workflow/nodes/_base/components/add-button'
import OutputVars, { VarItem } from '@/app/components/workflow/nodes/_base/components/output-vars'
import Split from '@/app/components/workflow/nodes/_base/components/split'
diff --git a/web/app/components/workflow/note-node/note-editor/plugins/link-editor-plugin/__tests__/hooks.spec.tsx b/web/app/components/workflow/note-node/note-editor/plugins/link-editor-plugin/__tests__/hooks.spec.tsx
index c3233b49d7..4272050fac 100644
--- a/web/app/components/workflow/note-node/note-editor/plugins/link-editor-plugin/__tests__/hooks.spec.tsx
+++ b/web/app/components/workflow/note-node/note-editor/plugins/link-editor-plugin/__tests__/hooks.spec.tsx
@@ -58,7 +58,7 @@ vi.mock('lexical', () => ({
COMMAND_PRIORITY_LOW: 1,
}))
-vi.mock('@/app/components/base/ui/toast', () => ({
+vi.mock('@langgenius/dify-ui/toast', () => ({
toast: {
error: mockToastError,
},
diff --git a/web/app/components/workflow/note-node/note-editor/plugins/link-editor-plugin/component.tsx b/web/app/components/workflow/note-node/note-editor/plugins/link-editor-plugin/component.tsx
index c67b658de6..754905cd46 100644
--- a/web/app/components/workflow/note-node/note-editor/plugins/link-editor-plugin/component.tsx
+++ b/web/app/components/workflow/note-node/note-editor/plugins/link-editor-plugin/component.tsx
@@ -5,6 +5,7 @@ import {
shift,
useFloating,
} from '@floating-ui/react'
+import { Button } from '@langgenius/dify-ui/button'
import { cn } from '@langgenius/dify-ui/cn'
import {
RiEditLine,
@@ -19,7 +20,6 @@ import {
useState,
} from 'react'
import { useTranslation } from 'react-i18next'
-import { Button } from '@/app/components/base/ui/button'
import { useStore } from '../../store'
import { useLink } from './hooks'
diff --git a/web/app/components/workflow/note-node/note-editor/plugins/link-editor-plugin/hooks.ts b/web/app/components/workflow/note-node/note-editor/plugins/link-editor-plugin/hooks.ts
index 75a5a36fd0..6debfa5f8b 100644
--- a/web/app/components/workflow/note-node/note-editor/plugins/link-editor-plugin/hooks.ts
+++ b/web/app/components/workflow/note-node/note-editor/plugins/link-editor-plugin/hooks.ts
@@ -1,3 +1,4 @@
+import { toast } from '@langgenius/dify-ui/toast'
import { TOGGLE_LINK_COMMAND } from '@lexical/link'
import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext'
import { mergeRegister } from '@lexical/utils'
@@ -5,7 +6,6 @@ import { escape } from 'es-toolkit/string'
import { CLICK_COMMAND, COMMAND_PRIORITY_LOW } from 'lexical'
import { useCallback, useEffect } from 'react'
import { useTranslation } from 'react-i18next'
-import { toast } from '@/app/components/base/ui/toast'
import { useNoteEditorStore } from '../../store'
import { urlRegExp } from '../../utils'
diff --git a/web/app/components/workflow/note-node/note-editor/toolbar/__tests__/operator.spec.tsx b/web/app/components/workflow/note-node/note-editor/toolbar/__tests__/operator.spec.tsx
index c60898b29e..a7959d1252 100644
--- a/web/app/components/workflow/note-node/note-editor/toolbar/__tests__/operator.spec.tsx
+++ b/web/app/components/workflow/note-node/note-editor/toolbar/__tests__/operator.spec.tsx
@@ -25,7 +25,7 @@ type DropdownTriggerProps = {
'aria-label'?: string
}
-vi.mock('@/app/components/base/ui/dropdown-menu', async () => {
+vi.mock('@langgenius/dify-ui/dropdown-menu', async () => {
const React = await import('react')
const DropdownMenuContext = React.createContext<{ open: boolean, setOpen: (open: boolean) => void } | null>(null)
diff --git a/web/app/components/workflow/note-node/note-editor/toolbar/operator.tsx b/web/app/components/workflow/note-node/note-editor/toolbar/operator.tsx
index b17af7fec7..bfbfe317c7 100644
--- a/web/app/components/workflow/note-node/note-editor/toolbar/operator.tsx
+++ b/web/app/components/workflow/note-node/note-editor/toolbar/operator.tsx
@@ -1,17 +1,17 @@
import { cn } from '@langgenius/dify-ui/cn'
-import {
- memo,
- useState,
-} from 'react'
-import { useTranslation } from 'react-i18next'
-import Switch from '@/app/components/base/switch'
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuSeparator,
DropdownMenuTrigger,
-} from '@/app/components/base/ui/dropdown-menu'
+} from '@langgenius/dify-ui/dropdown-menu'
+import { Switch } from '@langgenius/dify-ui/switch'
+import {
+ memo,
+ useState,
+} from 'react'
+import { useTranslation } from 'react-i18next'
import ShortcutsName from '@/app/components/workflow/shortcuts-name'
export type OperatorProps = {
diff --git a/web/app/components/workflow/operator/__tests__/more-actions.spec.tsx b/web/app/components/workflow/operator/__tests__/more-actions.spec.tsx
index b984ca18d6..df5dea301d 100644
--- a/web/app/components/workflow/operator/__tests__/more-actions.spec.tsx
+++ b/web/app/components/workflow/operator/__tests__/more-actions.spec.tsx
@@ -29,7 +29,7 @@ vi.mock('react-i18next', () => ({
}),
}))
-vi.mock('@/app/components/base/ui/dropdown-menu', async () => {
+vi.mock('@langgenius/dify-ui/dropdown-menu', async () => {
const React = await import('react')
const DropdownMenuContext = React.createContext<{ open: boolean, setOpen: (open: boolean) => void } | null>(null)
diff --git a/web/app/components/workflow/operator/more-actions.tsx b/web/app/components/workflow/operator/more-actions.tsx
index 66dbed1a91..c7ea22e1ca 100644
--- a/web/app/components/workflow/operator/more-actions.tsx
+++ b/web/app/components/workflow/operator/more-actions.tsx
@@ -1,5 +1,12 @@
import type { FC } from 'react'
import { cn } from '@langgenius/dify-ui/cn'
+import {
+ DropdownMenu,
+ DropdownMenuContent,
+ DropdownMenuItem,
+ DropdownMenuSeparator,
+ DropdownMenuTrigger,
+} from '@langgenius/dify-ui/dropdown-menu'
import { RiExportLine, RiMoreFill } from '@remixicon/react'
import { toJpeg, toPng, toSvg } from 'html-to-image'
import {
@@ -13,13 +20,6 @@ import { getNodesBounds, useReactFlow } from 'reactflow'
import { useShallow } from 'zustand/react/shallow'
import { useStore as useAppStore } from '@/app/components/app/store'
import ImagePreview from '@/app/components/base/image-uploader/image-preview'
-import {
- DropdownMenu,
- DropdownMenuContent,
- DropdownMenuItem,
- DropdownMenuSeparator,
- DropdownMenuTrigger,
-} from '@/app/components/base/ui/dropdown-menu'
import { useStore } from '@/app/components/workflow/store'
import { downloadUrl } from '@/utils/download'
import { useNodesReadOnly } from '../hooks'
diff --git a/web/app/components/workflow/operator/zoom-in-out.tsx b/web/app/components/workflow/operator/zoom-in-out.tsx
index 14118b36c9..78b07af347 100644
--- a/web/app/components/workflow/operator/zoom-in-out.tsx
+++ b/web/app/components/workflow/operator/zoom-in-out.tsx
@@ -1,5 +1,12 @@
import type { FC } from 'react'
import { cn } from '@langgenius/dify-ui/cn'
+import {
+ DropdownMenu,
+ DropdownMenuContent,
+ DropdownMenuItem,
+ DropdownMenuSeparator,
+ DropdownMenuTrigger,
+} from '@langgenius/dify-ui/dropdown-menu'
import {
Fragment,
memo,
@@ -10,13 +17,6 @@ import {
useReactFlow,
useViewport,
} from 'reactflow'
-import {
- DropdownMenu,
- DropdownMenuContent,
- DropdownMenuItem,
- DropdownMenuSeparator,
- DropdownMenuTrigger,
-} from '@/app/components/base/ui/dropdown-menu'
import { useGlobalPublicStore } from '@/context/global-public-context'
import {
useNodesSyncDraft,
diff --git a/web/app/components/workflow/panel/__tests__/inputs-panel.spec.tsx b/web/app/components/workflow/panel/__tests__/inputs-panel.spec.tsx
index c8ad5f3dde..41174d97a1 100644
--- a/web/app/components/workflow/panel/__tests__/inputs-panel.spec.tsx
+++ b/web/app/components/workflow/panel/__tests__/inputs-panel.spec.tsx
@@ -17,7 +17,7 @@ vi.mock('next/navigation', () => ({
useParams: () => ({}),
}))
-vi.mock('@/app/components/base/ui/toast', () => ({
+vi.mock('@langgenius/dify-ui/toast', () => ({
toast: {
success: (message: string) => mockNotify({ type: 'success', message }),
error: (message: string) => mockNotify({ type: 'error', message }),
diff --git a/web/app/components/workflow/panel/__tests__/workflow-preview.spec.tsx b/web/app/components/workflow/panel/__tests__/workflow-preview.spec.tsx
index e3c85bd2ad..9b5f89ebd0 100644
--- a/web/app/components/workflow/panel/__tests__/workflow-preview.spec.tsx
+++ b/web/app/components/workflow/panel/__tests__/workflow-preview.spec.tsx
@@ -1,9 +1,9 @@
import type { Shape } from '../../store/workflow'
import type { HumanInputFilledFormData, HumanInputFormData } from '@/types/workflow'
+import { toast } from '@langgenius/dify-ui/toast'
import { fireEvent, screen, waitFor } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import copy from 'copy-to-clipboard'
-import { toast } from '@/app/components/base/ui/toast'
import { createNodeTracing, createWorkflowRunningData } from '@/app/components/workflow/__tests__/fixtures'
import { renderWorkflowComponent } from '@/app/components/workflow/__tests__/workflow-test-env'
import { WorkflowRunningStatus } from '@/app/components/workflow/types'
@@ -16,7 +16,7 @@ vi.mock('copy-to-clipboard', () => ({
default: vi.fn(),
}))
-vi.mock('@/app/components/base/ui/toast', () => ({
+vi.mock('@langgenius/dify-ui/toast', () => ({
toast: {
success: vi.fn(),
},
diff --git a/web/app/components/workflow/panel/chat-variable-panel/components/__tests__/object-value-item.spec.tsx b/web/app/components/workflow/panel/chat-variable-panel/components/__tests__/object-value-item.spec.tsx
index 9ed1bed1f4..e2e7c2d562 100644
--- a/web/app/components/workflow/panel/chat-variable-panel/components/__tests__/object-value-item.spec.tsx
+++ b/web/app/components/workflow/panel/chat-variable-panel/components/__tests__/object-value-item.spec.tsx
@@ -4,7 +4,7 @@ import ObjectValueItem, { DEFAULT_OBJECT_VALUE } from '../object-value-item'
const toastError = vi.fn()
-vi.mock('@/app/components/base/ui/toast', () => ({
+vi.mock('@langgenius/dify-ui/toast', () => ({
toast: {
error: (...args: unknown[]) => toastError(...args),
},
diff --git a/web/app/components/workflow/panel/chat-variable-panel/components/__tests__/variable-modal.spec.tsx b/web/app/components/workflow/panel/chat-variable-panel/components/__tests__/variable-modal.spec.tsx
index 67437b574a..cf9400636e 100644
--- a/web/app/components/workflow/panel/chat-variable-panel/components/__tests__/variable-modal.spec.tsx
+++ b/web/app/components/workflow/panel/chat-variable-panel/components/__tests__/variable-modal.spec.tsx
@@ -1,7 +1,7 @@
+import { toast } from '@langgenius/dify-ui/toast'
import { fireEvent, screen, waitFor } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import * as React from 'react'
-import { toast } from '@/app/components/base/ui/toast'
import { renderWorkflowComponent } from '@/app/components/workflow/__tests__/workflow-test-env'
import { ChatVarType } from '../../type'
import VariableModal from '../variable-modal'
@@ -10,7 +10,7 @@ vi.mock('uuid', () => ({
v4: () => 'generated-id',
}))
-vi.mock('@/app/components/base/ui/toast', () => ({
+vi.mock('@langgenius/dify-ui/toast', () => ({
toast: {
error: vi.fn(),
info: vi.fn(),
diff --git a/web/app/components/workflow/panel/chat-variable-panel/components/array-bool-list.tsx b/web/app/components/workflow/panel/chat-variable-panel/components/array-bool-list.tsx
index 8a896b0913..7a1f41028b 100644
--- a/web/app/components/workflow/panel/chat-variable-panel/components/array-bool-list.tsx
+++ b/web/app/components/workflow/panel/chat-variable-panel/components/array-bool-list.tsx
@@ -1,12 +1,12 @@
'use client'
import type { FC } from 'react'
+import { Button } from '@langgenius/dify-ui/button'
import { cn } from '@langgenius/dify-ui/cn'
import { RiAddLine } from '@remixicon/react'
import { produce } from 'immer'
import * as React from 'react'
import { useCallback } from 'react'
import { useTranslation } from 'react-i18next'
-import { Button } from '@/app/components/base/ui/button'
import RemoveButton from '@/app/components/workflow/nodes/_base/components/remove-button'
import BoolValue from './bool-value'
diff --git a/web/app/components/workflow/panel/chat-variable-panel/components/array-value-list.tsx b/web/app/components/workflow/panel/chat-variable-panel/components/array-value-list.tsx
index 8dc21edd09..f37cc4715d 100644
--- a/web/app/components/workflow/panel/chat-variable-panel/components/array-value-list.tsx
+++ b/web/app/components/workflow/panel/chat-variable-panel/components/array-value-list.tsx
@@ -1,12 +1,12 @@
'use client'
import type { FC } from 'react'
+import { Button } from '@langgenius/dify-ui/button'
import { RiAddLine } from '@remixicon/react'
import { produce } from 'immer'
import * as React from 'react'
import { useCallback } from 'react'
import { useTranslation } from 'react-i18next'
import Input from '@/app/components/base/input'
-import { Button } from '@/app/components/base/ui/button'
import RemoveButton from '@/app/components/workflow/nodes/_base/components/remove-button'
type Props = {
diff --git a/web/app/components/workflow/panel/chat-variable-panel/components/object-value-item.tsx b/web/app/components/workflow/panel/chat-variable-panel/components/object-value-item.tsx
index cd8e8468a5..8ef7125f62 100644
--- a/web/app/components/workflow/panel/chat-variable-panel/components/object-value-item.tsx
+++ b/web/app/components/workflow/panel/chat-variable-panel/components/object-value-item.tsx
@@ -1,10 +1,10 @@
'use client'
import type { FC } from 'react'
+import { toast } from '@langgenius/dify-ui/toast'
import { produce } from 'immer'
import * as React from 'react'
import { useCallback, useState } from 'react'
import { useTranslation } from 'react-i18next'
-import { toast } from '@/app/components/base/ui/toast'
import RemoveButton from '@/app/components/workflow/nodes/_base/components/remove-button'
import VariableTypeSelector from '@/app/components/workflow/panel/chat-variable-panel/components/variable-type-select'
import { ChatVarType } from '@/app/components/workflow/panel/chat-variable-panel/type'
diff --git a/web/app/components/workflow/panel/chat-variable-panel/components/variable-modal-trigger.tsx b/web/app/components/workflow/panel/chat-variable-panel/components/variable-modal-trigger.tsx
index 35845cd005..9c97583423 100644
--- a/web/app/components/workflow/panel/chat-variable-panel/components/variable-modal-trigger.tsx
+++ b/web/app/components/workflow/panel/chat-variable-panel/components/variable-modal-trigger.tsx
@@ -1,10 +1,10 @@
'use client'
import type { ConversationVariable } from '@/app/components/workflow/types'
+import { Button } from '@langgenius/dify-ui/button'
+import { Popover, PopoverContent, PopoverTrigger } from '@langgenius/dify-ui/popover'
import { RiAddLine } from '@remixicon/react'
import * as React from 'react'
import { useTranslation } from 'react-i18next'
-import { Button } from '@/app/components/base/ui/button'
-import { Popover, PopoverContent, PopoverTrigger } from '@/app/components/base/ui/popover'
import VariableModal from '@/app/components/workflow/panel/chat-variable-panel/components/variable-modal'
type Props = {
diff --git a/web/app/components/workflow/panel/chat-variable-panel/components/variable-modal.sections.tsx b/web/app/components/workflow/panel/chat-variable-panel/components/variable-modal.sections.tsx
index e1ebe1306d..894ca2f05d 100644
--- a/web/app/components/workflow/panel/chat-variable-panel/components/variable-modal.sections.tsx
+++ b/web/app/components/workflow/panel/chat-variable-panel/components/variable-modal.sections.tsx
@@ -1,8 +1,8 @@
import type { ReactNode } from 'react'
import type { ObjectValueItem } from './variable-modal.helpers'
+import { Button } from '@langgenius/dify-ui/button'
import { RiDraftLine, RiInputField } from '@remixicon/react'
import Input from '@/app/components/base/input'
-import { Button } from '@/app/components/base/ui/button'
import CodeEditor from '@/app/components/workflow/nodes/_base/components/editor/code-editor'
import { CodeLanguage } from '@/app/components/workflow/nodes/code/types'
import { ChatVarType } from '../type'
diff --git a/web/app/components/workflow/panel/chat-variable-panel/components/variable-modal.tsx b/web/app/components/workflow/panel/chat-variable-panel/components/variable-modal.tsx
index 70191b035d..dac7d16efd 100644
--- a/web/app/components/workflow/panel/chat-variable-panel/components/variable-modal.tsx
+++ b/web/app/components/workflow/panel/chat-variable-panel/components/variable-modal.tsx
@@ -1,11 +1,11 @@
import type { ToastPayload } from './variable-modal.helpers'
import type { ConversationVariable } from '@/app/components/workflow/types'
+import { Button } from '@langgenius/dify-ui/button'
import { cn } from '@langgenius/dify-ui/cn'
+import { toast } from '@langgenius/dify-ui/toast'
import { RiCloseLine } from '@remixicon/react'
import * as React from 'react'
import { useTranslation } from 'react-i18next'
-import { Button } from '@/app/components/base/ui/button'
-import { toast } from '@/app/components/base/ui/toast'
import { ChatVarType } from '@/app/components/workflow/panel/chat-variable-panel/type'
import { useWorkflowStore } from '@/app/components/workflow/store'
import { replaceSpaceWithUnderscoreInVarNameInput } from '@/utils/var'
diff --git a/web/app/components/workflow/panel/comments-panel/__tests__/index.spec.tsx b/web/app/components/workflow/panel/comments-panel/__tests__/index.spec.tsx
index 5abacd6035..49ae2a11a8 100644
--- a/web/app/components/workflow/panel/comments-panel/__tests__/index.spec.tsx
+++ b/web/app/components/workflow/panel/comments-panel/__tests__/index.spec.tsx
@@ -109,8 +109,8 @@ vi.mock('@/app/components/base/user-avatar-list', () => ({
UserAvatarList: () => ,
}))
-vi.mock('@/app/components/base/switch', () => ({
- default: ({ checked, onCheckedChange }: { checked: boolean, onCheckedChange: (value: boolean) => void }) => (
+vi.mock('@langgenius/dify-ui/switch', () => ({
+ Switch: ({ checked, onCheckedChange }: { checked: boolean, onCheckedChange: (value: boolean) => void }) => (
onCheckedChange(!checked)}>
toggle
diff --git a/web/app/components/workflow/panel/comments-panel/index.tsx b/web/app/components/workflow/panel/comments-panel/index.tsx
index 21b3c3c4e6..05480abb2b 100644
--- a/web/app/components/workflow/panel/comments-panel/index.tsx
+++ b/web/app/components/workflow/panel/comments-panel/index.tsx
@@ -1,10 +1,10 @@
import type { WorkflowCommentList } from '@/service/workflow-comment'
import { cn } from '@langgenius/dify-ui/cn'
+import { Switch } from '@langgenius/dify-ui/switch'
import { RiCheckboxCircleFill, RiCheckboxCircleLine, RiCheckLine, RiCloseLine, RiFilter3Line } from '@remixicon/react'
import { memo, useCallback, useMemo, useState } from 'react'
import { useTranslation } from 'react-i18next'
import Divider from '@/app/components/base/divider'
-import Switch from '@/app/components/base/switch'
import { UserAvatarList } from '@/app/components/base/user-avatar-list'
import { collaborationManager } from '@/app/components/workflow/collaboration/core/collaboration-manager'
import { useWorkflowComment } from '@/app/components/workflow/hooks/use-workflow-comment'
diff --git a/web/app/components/workflow/panel/debug-and-preview/__tests__/hooks.spec.ts b/web/app/components/workflow/panel/debug-and-preview/__tests__/hooks.spec.ts
index 425dd90e87..9a2b8a8df7 100644
--- a/web/app/components/workflow/panel/debug-and-preview/__tests__/hooks.spec.ts
+++ b/web/app/components/workflow/panel/debug-and-preview/__tests__/hooks.spec.ts
@@ -26,7 +26,7 @@ vi.mock('@/service/workflow', () => ({
submitHumanInputForm: (...args: unknown[]) => mockSubmitHumanInputForm(...args),
}))
-vi.mock('@/app/components/base/ui/toast', () => ({
+vi.mock('@langgenius/dify-ui/toast', () => ({
toast: {
success: vi.fn(),
error: vi.fn(),
diff --git a/web/app/components/workflow/panel/debug-and-preview/__tests__/hooks/handle-resume.spec.ts b/web/app/components/workflow/panel/debug-and-preview/__tests__/hooks/handle-resume.spec.ts
index 04c19f50e9..5a608dcc5b 100644
--- a/web/app/components/workflow/panel/debug-and-preview/__tests__/hooks/handle-resume.spec.ts
+++ b/web/app/components/workflow/panel/debug-and-preview/__tests__/hooks/handle-resume.spec.ts
@@ -27,7 +27,7 @@ vi.mock('@/service/workflow', () => ({
submitHumanInputForm: (...args: any[]) => mockSubmitHumanInputForm(...args),
}))
-vi.mock('@/app/components/base/ui/toast', () => ({
+vi.mock('@langgenius/dify-ui/toast', () => ({
toast: {
success: vi.fn(),
error: vi.fn(),
diff --git a/web/app/components/workflow/panel/debug-and-preview/__tests__/hooks/handle-send.spec.ts b/web/app/components/workflow/panel/debug-and-preview/__tests__/hooks/handle-send.spec.ts
index 0f6ae5d713..fb045c4723 100644
--- a/web/app/components/workflow/panel/debug-and-preview/__tests__/hooks/handle-send.spec.ts
+++ b/web/app/components/workflow/panel/debug-and-preview/__tests__/hooks/handle-send.spec.ts
@@ -26,7 +26,7 @@ vi.mock('@/service/workflow', () => ({
submitHumanInputForm: (...args: any[]) => mockSubmitHumanInputForm(...args),
}))
-vi.mock('@/app/components/base/ui/toast', () => ({
+vi.mock('@langgenius/dify-ui/toast', () => ({
toast: {
success: vi.fn(),
error: vi.fn(),
diff --git a/web/app/components/workflow/panel/debug-and-preview/__tests__/hooks/handle-stop-restart.spec.ts b/web/app/components/workflow/panel/debug-and-preview/__tests__/hooks/handle-stop-restart.spec.ts
index d0b1908606..2868471bd3 100644
--- a/web/app/components/workflow/panel/debug-and-preview/__tests__/hooks/handle-stop-restart.spec.ts
+++ b/web/app/components/workflow/panel/debug-and-preview/__tests__/hooks/handle-stop-restart.spec.ts
@@ -27,7 +27,7 @@ vi.mock('@/service/workflow', () => ({
submitHumanInputForm: (...args: any[]) => mockSubmitHumanInputForm(...args),
}))
-vi.mock('@/app/components/base/ui/toast', () => ({
+vi.mock('@langgenius/dify-ui/toast', () => ({
toast: {
success: vi.fn(),
error: vi.fn(),
diff --git a/web/app/components/workflow/panel/debug-and-preview/__tests__/hooks/misc.spec.ts b/web/app/components/workflow/panel/debug-and-preview/__tests__/hooks/misc.spec.ts
index 549fe08e05..cd2eaa0e00 100644
--- a/web/app/components/workflow/panel/debug-and-preview/__tests__/hooks/misc.spec.ts
+++ b/web/app/components/workflow/panel/debug-and-preview/__tests__/hooks/misc.spec.ts
@@ -27,7 +27,7 @@ vi.mock('@/service/workflow', () => ({
submitHumanInputForm: (...args: any[]) => mockSubmitHumanInputForm(...args),
}))
-vi.mock('@/app/components/base/ui/toast', () => ({
+vi.mock('@langgenius/dify-ui/toast', () => ({
toast: {
success: vi.fn(),
error: vi.fn(),
diff --git a/web/app/components/workflow/panel/debug-and-preview/__tests__/hooks/opening-statement.spec.ts b/web/app/components/workflow/panel/debug-and-preview/__tests__/hooks/opening-statement.spec.ts
index 57db5502b5..cb45029340 100644
--- a/web/app/components/workflow/panel/debug-and-preview/__tests__/hooks/opening-statement.spec.ts
+++ b/web/app/components/workflow/panel/debug-and-preview/__tests__/hooks/opening-statement.spec.ts
@@ -27,7 +27,7 @@ vi.mock('@/service/workflow', () => ({
submitHumanInputForm: (...args: any[]) => mockSubmitHumanInputForm(...args),
}))
-vi.mock('@/app/components/base/ui/toast', () => ({
+vi.mock('@langgenius/dify-ui/toast', () => ({
toast: {
success: vi.fn(),
error: vi.fn(),
diff --git a/web/app/components/workflow/panel/debug-and-preview/__tests__/hooks/sse-callbacks.spec.ts b/web/app/components/workflow/panel/debug-and-preview/__tests__/hooks/sse-callbacks.spec.ts
index 03857ce3d6..f8f3a02c5c 100644
--- a/web/app/components/workflow/panel/debug-and-preview/__tests__/hooks/sse-callbacks.spec.ts
+++ b/web/app/components/workflow/panel/debug-and-preview/__tests__/hooks/sse-callbacks.spec.ts
@@ -26,7 +26,7 @@ vi.mock('@/service/workflow', () => ({
submitHumanInputForm: (...args: any[]) => mockSubmitHumanInputForm(...args),
}))
-vi.mock('@/app/components/base/ui/toast', () => ({
+vi.mock('@langgenius/dify-ui/toast', () => ({
toast: {
success: vi.fn(),
error: vi.fn(),
diff --git a/web/app/components/workflow/panel/debug-and-preview/hooks.ts b/web/app/components/workflow/panel/debug-and-preview/hooks.ts
index 6eef69bfdb..a94e4c1edd 100644
--- a/web/app/components/workflow/panel/debug-and-preview/hooks.ts
+++ b/web/app/components/workflow/panel/debug-and-preview/hooks.ts
@@ -6,6 +6,7 @@ import type {
} from '@/app/components/base/chat/types'
import type { FileEntity } from '@/app/components/base/file-uploader/types'
import type { IOtherOptions } from '@/service/base'
+import { toast } from '@langgenius/dify-ui/toast'
import { uniqBy } from 'es-toolkit/compat'
import { produce, setAutoFreeze } from 'immer'
import {
@@ -26,7 +27,6 @@ import {
getProcessedFiles,
getProcessedFilesFromResponse,
} from '@/app/components/base/file-uploader/utils'
-import { toast } from '@/app/components/base/ui/toast'
import {
CUSTOM_NODE,
} from '@/app/components/workflow/constants'
diff --git a/web/app/components/workflow/panel/env-panel/__tests__/variable-modal.spec.tsx b/web/app/components/workflow/panel/env-panel/__tests__/variable-modal.spec.tsx
index 7e97ef62dc..258784b692 100644
--- a/web/app/components/workflow/panel/env-panel/__tests__/variable-modal.spec.tsx
+++ b/web/app/components/workflow/panel/env-panel/__tests__/variable-modal.spec.tsx
@@ -1,14 +1,14 @@
import type { ReactElement } from 'react'
import type { Shape } from '@/app/components/workflow/store/workflow'
import type { EnvironmentVariable } from '@/app/components/workflow/types'
+import { toast } from '@langgenius/dify-ui/toast'
import { fireEvent, render, screen } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
-import { toast } from '@/app/components/base/ui/toast'
import { WorkflowContext } from '@/app/components/workflow/context'
import { createWorkflowStore } from '@/app/components/workflow/store/workflow'
import VariableModal from '../variable-modal'
-vi.mock('@/app/components/base/ui/toast', () => ({
+vi.mock('@langgenius/dify-ui/toast', () => ({
toast: {
success: vi.fn(),
error: vi.fn(),
diff --git a/web/app/components/workflow/panel/env-panel/variable-modal.tsx b/web/app/components/workflow/panel/env-panel/variable-modal.tsx
index e824dd9f08..267c014e1d 100644
--- a/web/app/components/workflow/panel/env-panel/variable-modal.tsx
+++ b/web/app/components/workflow/panel/env-panel/variable-modal.tsx
@@ -1,5 +1,7 @@
import type { EnvironmentVariable } from '@/app/components/workflow/types'
+import { Button } from '@langgenius/dify-ui/button'
import { cn } from '@langgenius/dify-ui/cn'
+import { toast } from '@langgenius/dify-ui/toast'
import { RiCloseLine } from '@remixicon/react'
import * as React from 'react'
import { useEffect } from 'react'
@@ -7,8 +9,6 @@ import { useTranslation } from 'react-i18next'
import { v4 as uuid4 } from 'uuid'
import Input from '@/app/components/base/input'
import Tooltip from '@/app/components/base/tooltip'
-import { Button } from '@/app/components/base/ui/button'
-import { toast } from '@/app/components/base/ui/toast'
import { useWorkflowStore } from '@/app/components/workflow/store'
import { checkKeys, replaceSpaceWithUnderscoreInVarNameInput } from '@/utils/var'
diff --git a/web/app/components/workflow/panel/env-panel/variable-trigger.tsx b/web/app/components/workflow/panel/env-panel/variable-trigger.tsx
index e075238e48..378fc1c1b5 100644
--- a/web/app/components/workflow/panel/env-panel/variable-trigger.tsx
+++ b/web/app/components/workflow/panel/env-panel/variable-trigger.tsx
@@ -1,10 +1,10 @@
'use client'
import type { EnvironmentVariable } from '@/app/components/workflow/types'
+import { Button } from '@langgenius/dify-ui/button'
+import { Popover, PopoverContent, PopoverTrigger } from '@langgenius/dify-ui/popover'
import { RiAddLine } from '@remixicon/react'
import * as React from 'react'
import { useTranslation } from 'react-i18next'
-import { Button } from '@/app/components/base/ui/button'
-import { Popover, PopoverContent, PopoverTrigger } from '@/app/components/base/ui/popover'
import VariableModal from '@/app/components/workflow/panel/env-panel/variable-modal'
type Props = {
diff --git a/web/app/components/workflow/panel/inputs-panel.tsx b/web/app/components/workflow/panel/inputs-panel.tsx
index 4e6255a2c0..10004236dd 100644
--- a/web/app/components/workflow/panel/inputs-panel.tsx
+++ b/web/app/components/workflow/panel/inputs-panel.tsx
@@ -1,4 +1,5 @@
import type { StartNodeType } from '../nodes/start/types'
+import { Button } from '@langgenius/dify-ui/button'
import {
memo,
useCallback,
@@ -10,7 +11,6 @@ import { useCheckInputsForms } from '@/app/components/base/chat/chat/check-input
import {
getProcessedInputs,
} from '@/app/components/base/chat/chat/utils'
-import { Button } from '@/app/components/base/ui/button'
import { TransferMethod } from '../../base/text-generation/types'
import { useWorkflowRun } from '../hooks'
import { useHooksStore } from '../hooks-store'
diff --git a/web/app/components/workflow/panel/version-history-panel/context-menu/__tests__/menu-item.spec.tsx b/web/app/components/workflow/panel/version-history-panel/context-menu/__tests__/menu-item.spec.tsx
index 844e189067..7dfc362a90 100644
--- a/web/app/components/workflow/panel/version-history-panel/context-menu/__tests__/menu-item.spec.tsx
+++ b/web/app/components/workflow/panel/version-history-panel/context-menu/__tests__/menu-item.spec.tsx
@@ -1,6 +1,6 @@
+import { DropdownMenu, DropdownMenuContent } from '@langgenius/dify-ui/dropdown-menu'
import { render, screen } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
-import { DropdownMenu, DropdownMenuContent } from '@/app/components/base/ui/dropdown-menu'
import { VersionHistoryContextMenuOptions } from '../../../../types'
import MenuItem from '../menu-item'
diff --git a/web/app/components/workflow/panel/version-history-panel/context-menu/index.tsx b/web/app/components/workflow/panel/version-history-panel/context-menu/index.tsx
index f063902753..1b90166f65 100644
--- a/web/app/components/workflow/panel/version-history-panel/context-menu/index.tsx
+++ b/web/app/components/workflow/panel/version-history-panel/context-menu/index.tsx
@@ -1,13 +1,13 @@
import type { FC } from 'react'
-import { RiMoreFill } from '@remixicon/react'
-import * as React from 'react'
-import { Button } from '@/app/components/base/ui/button'
+import { Button } from '@langgenius/dify-ui/button'
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuSeparator,
DropdownMenuTrigger,
-} from '@/app/components/base/ui/dropdown-menu'
+} from '@langgenius/dify-ui/dropdown-menu'
+import { RiMoreFill } from '@remixicon/react'
+import * as React from 'react'
import { VersionHistoryContextMenuOptions } from '../../../types'
import MenuItem from './menu-item'
import useContextMenu from './use-context-menu'
diff --git a/web/app/components/workflow/panel/version-history-panel/context-menu/menu-item.tsx b/web/app/components/workflow/panel/version-history-panel/context-menu/menu-item.tsx
index b1d9ef6ec5..0c0096ab25 100644
--- a/web/app/components/workflow/panel/version-history-panel/context-menu/menu-item.tsx
+++ b/web/app/components/workflow/panel/version-history-panel/context-menu/menu-item.tsx
@@ -1,8 +1,8 @@
import type { FC } from 'react'
import type { VersionHistoryContextMenuOptions } from '../../../types'
import { cn } from '@langgenius/dify-ui/cn'
+import { DropdownMenuItem } from '@langgenius/dify-ui/dropdown-menu'
import * as React from 'react'
-import { DropdownMenuItem } from '@/app/components/base/ui/dropdown-menu'
type MenuItemProps = {
item: {
diff --git a/web/app/components/workflow/panel/version-history-panel/delete-confirm-modal.tsx b/web/app/components/workflow/panel/version-history-panel/delete-confirm-modal.tsx
index 4779be8984..1f7948c41b 100644
--- a/web/app/components/workflow/panel/version-history-panel/delete-confirm-modal.tsx
+++ b/web/app/components/workflow/panel/version-history-panel/delete-confirm-modal.tsx
@@ -1,9 +1,9 @@
import type { FC } from 'react'
import type { VersionHistory } from '@/types/workflow'
+import { Button } from '@langgenius/dify-ui/button'
import * as React from 'react'
import { useTranslation } from 'react-i18next'
import Modal from '@/app/components/base/modal'
-import { Button } from '@/app/components/base/ui/button'
type DeleteConfirmModalProps = {
isOpen: boolean
diff --git a/web/app/components/workflow/panel/version-history-panel/empty.tsx b/web/app/components/workflow/panel/version-history-panel/empty.tsx
index 2c8f6655e1..21751234d7 100644
--- a/web/app/components/workflow/panel/version-history-panel/empty.tsx
+++ b/web/app/components/workflow/panel/version-history-panel/empty.tsx
@@ -1,8 +1,8 @@
import type { FC } from 'react'
+import { Button } from '@langgenius/dify-ui/button'
import { RiHistoryLine } from '@remixicon/react'
import * as React from 'react'
import { useTranslation } from 'react-i18next'
-import { Button } from '@/app/components/base/ui/button'
type EmptyProps = {
onResetFilter: () => void
diff --git a/web/app/components/workflow/panel/version-history-panel/filter/filter-switch.tsx b/web/app/components/workflow/panel/version-history-panel/filter/filter-switch.tsx
index 1df519d0f3..587dc43bdf 100644
--- a/web/app/components/workflow/panel/version-history-panel/filter/filter-switch.tsx
+++ b/web/app/components/workflow/panel/version-history-panel/filter/filter-switch.tsx
@@ -1,7 +1,7 @@
import type { FC } from 'react'
+import { Switch } from '@langgenius/dify-ui/switch'
import * as React from 'react'
import { useTranslation } from 'react-i18next'
-import Switch from '@/app/components/base/switch'
type FilterSwitchProps = {
enabled: boolean
diff --git a/web/app/components/workflow/panel/version-history-panel/index.tsx b/web/app/components/workflow/panel/version-history-panel/index.tsx
index 10dd0e794a..eb1f5c962e 100644
--- a/web/app/components/workflow/panel/version-history-panel/index.tsx
+++ b/web/app/components/workflow/panel/version-history-panel/index.tsx
@@ -1,5 +1,6 @@
'use client'
import type { VersionHistory } from '@/types/workflow'
+import { toast } from '@langgenius/dify-ui/toast'
import { RiArrowDownDoubleLine, RiCloseLine, RiLoader2Line } from '@remixicon/react'
import copy from 'copy-to-clipboard'
import * as React from 'react'
@@ -7,7 +8,6 @@ import { useCallback, useState } from 'react'
import { useTranslation } from 'react-i18next'
import VersionInfoModal from '@/app/components/app/app-publisher/version-info-modal'
import Divider from '@/app/components/base/divider'
-import { toast } from '@/app/components/base/ui/toast'
import { useSelector as useAppContextSelector } from '@/context/app-context'
import { useDeleteWorkflow, useInvalidAllLastRun, useResetWorkflowVersionHistory, useRestoreWorkflow, useUpdateWorkflow, useWorkflowVersionHistory } from '@/service/use-workflow'
import { useDSL, useWorkflowRefreshDraft, useWorkflowRun } from '../../hooks'
diff --git a/web/app/components/workflow/panel/version-history-panel/restore-confirm-modal.tsx b/web/app/components/workflow/panel/version-history-panel/restore-confirm-modal.tsx
index 695e6fe35c..9fc2c25742 100644
--- a/web/app/components/workflow/panel/version-history-panel/restore-confirm-modal.tsx
+++ b/web/app/components/workflow/panel/version-history-panel/restore-confirm-modal.tsx
@@ -1,9 +1,9 @@
import type { FC } from 'react'
import type { VersionHistory } from '@/types/workflow'
+import { Button } from '@langgenius/dify-ui/button'
import * as React from 'react'
import { useTranslation } from 'react-i18next'
import Modal from '@/app/components/base/modal'
-import { Button } from '@/app/components/base/ui/button'
type RestoreConfirmModalProps = {
isOpen: boolean
diff --git a/web/app/components/workflow/panel/workflow-preview.tsx b/web/app/components/workflow/panel/workflow-preview.tsx
index e28c494b20..89ae09c374 100644
--- a/web/app/components/workflow/panel/workflow-preview.tsx
+++ b/web/app/components/workflow/panel/workflow-preview.tsx
@@ -1,4 +1,6 @@
+import { Button } from '@langgenius/dify-ui/button'
import { cn } from '@langgenius/dify-ui/cn'
+import { toast } from '@langgenius/dify-ui/toast'
import copy from 'copy-to-clipboard'
import {
memo,
@@ -8,8 +10,6 @@ import {
} from 'react'
import { useTranslation } from 'react-i18next'
import Loading from '@/app/components/base/loading'
-import { Button } from '@/app/components/base/ui/button'
-import { toast } from '@/app/components/base/ui/toast'
import { submitHumanInputForm } from '@/service/workflow'
import {
useWorkflowInteractions,
diff --git a/web/app/components/workflow/run/__tests__/index.spec.tsx b/web/app/components/workflow/run/__tests__/index.spec.tsx
index 87937dfb4f..39f26df97e 100644
--- a/web/app/components/workflow/run/__tests__/index.spec.tsx
+++ b/web/app/components/workflow/run/__tests__/index.spec.tsx
@@ -22,7 +22,7 @@ vi.mock('@/service/log', () => ({
fetchTracingList: (...args: unknown[]) => mockFetchTracingList(...args),
}))
-vi.mock('@/app/components/base/ui/toast', () => ({
+vi.mock('@langgenius/dify-ui/toast', () => ({
toast: {
error: (...args: unknown[]) => mockToastError(...args),
},
diff --git a/web/app/components/workflow/run/agent-log/__tests__/agent-log-nav-more.spec.tsx b/web/app/components/workflow/run/agent-log/__tests__/agent-log-nav-more.spec.tsx
index 35b32a5ce6..e283740613 100644
--- a/web/app/components/workflow/run/agent-log/__tests__/agent-log-nav-more.spec.tsx
+++ b/web/app/components/workflow/run/agent-log/__tests__/agent-log-nav-more.spec.tsx
@@ -3,7 +3,7 @@ import { render, screen } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import AgentLogNavMore from '../agent-log-nav-more'
-vi.mock('@/app/components/base/ui/dropdown-menu', async () => {
+vi.mock('@langgenius/dify-ui/dropdown-menu', async () => {
const React = await import('react')
const DropdownMenuContext = React.createContext<{ open: boolean, setOpen: (open: boolean) => void } | null>(null)
diff --git a/web/app/components/workflow/run/agent-log/agent-log-item.tsx b/web/app/components/workflow/run/agent-log/agent-log-item.tsx
index e1714c5f35..67da9c7aa9 100644
--- a/web/app/components/workflow/run/agent-log/agent-log-item.tsx
+++ b/web/app/components/workflow/run/agent-log/agent-log-item.tsx
@@ -1,4 +1,5 @@
import type { AgentLogItemWithChildren } from '@/types/workflow'
+import { Button } from '@langgenius/dify-ui/button'
import { cn } from '@langgenius/dify-ui/cn'
import {
RiArrowRightSLine,
@@ -8,7 +9,6 @@ import {
useMemo,
useState,
} from 'react'
-import { Button } from '@/app/components/base/ui/button'
import useGetIcon from '@/app/components/plugins/install-plugin/base/use-get-icon'
import BlockIcon from '@/app/components/workflow/block-icon'
import CodeEditor from '@/app/components/workflow/nodes/_base/components/editor/code-editor'
diff --git a/web/app/components/workflow/run/agent-log/agent-log-nav-more.tsx b/web/app/components/workflow/run/agent-log/agent-log-nav-more.tsx
index 77f3c778a6..5c8d100012 100644
--- a/web/app/components/workflow/run/agent-log/agent-log-nav-more.tsx
+++ b/web/app/components/workflow/run/agent-log/agent-log-nav-more.tsx
@@ -1,13 +1,13 @@
import type { AgentLogItemWithChildren } from '@/types/workflow'
-import { RiMoreLine } from '@remixicon/react'
-import { useState } from 'react'
-import { Button } from '@/app/components/base/ui/button'
+import { Button } from '@langgenius/dify-ui/button'
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
-} from '@/app/components/base/ui/dropdown-menu'
+} from '@langgenius/dify-ui/dropdown-menu'
+import { RiMoreLine } from '@remixicon/react'
+import { useState } from 'react'
type AgentLogNavMoreProps = {
options: AgentLogItemWithChildren[]
diff --git a/web/app/components/workflow/run/agent-log/agent-log-nav.tsx b/web/app/components/workflow/run/agent-log/agent-log-nav.tsx
index 2548256cb1..7996595186 100644
--- a/web/app/components/workflow/run/agent-log/agent-log-nav.tsx
+++ b/web/app/components/workflow/run/agent-log/agent-log-nav.tsx
@@ -1,7 +1,7 @@
import type { AgentLogItemWithChildren } from '@/types/workflow'
+import { Button } from '@langgenius/dify-ui/button'
import { RiArrowLeftLine } from '@remixicon/react'
import { useTranslation } from 'react-i18next'
-import { Button } from '@/app/components/base/ui/button'
import AgentLogNavMore from './agent-log-nav-more'
type AgentLogNavProps = {
diff --git a/web/app/components/workflow/run/index.tsx b/web/app/components/workflow/run/index.tsx
index bd71b662f5..d066690120 100644
--- a/web/app/components/workflow/run/index.tsx
+++ b/web/app/components/workflow/run/index.tsx
@@ -3,10 +3,10 @@ import type { FC } from 'react'
import type { WorkflowRunDetailResponse } from '@/models/log'
import type { NodeTracing } from '@/types/workflow'
import { cn } from '@langgenius/dify-ui/cn'
+import { toast } from '@langgenius/dify-ui/toast'
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
import { useTranslation } from 'react-i18next'
import Loading from '@/app/components/base/loading'
-import { toast } from '@/app/components/base/ui/toast'
import { WorkflowRunningStatus } from '@/app/components/workflow/types'
import { fetchRunDetail, fetchTracingList } from '@/service/log'
import { useStore } from '../store'
diff --git a/web/app/components/workflow/run/iteration-log/iteration-log-trigger.tsx b/web/app/components/workflow/run/iteration-log/iteration-log-trigger.tsx
index 77b9425501..8fb857f344 100644
--- a/web/app/components/workflow/run/iteration-log/iteration-log-trigger.tsx
+++ b/web/app/components/workflow/run/iteration-log/iteration-log-trigger.tsx
@@ -2,10 +2,10 @@ import type {
IterationDurationMap,
NodeTracing,
} from '@/types/workflow'
+import { Button } from '@langgenius/dify-ui/button'
import { RiArrowRightSLine } from '@remixicon/react'
import { useTranslation } from 'react-i18next'
import { Iteration } from '@/app/components/base/icons/src/vender/workflow'
-import { Button } from '@/app/components/base/ui/button'
import { NodeRunningStatus } from '@/app/components/workflow/types'
type IterationLogTriggerProps = {
diff --git a/web/app/components/workflow/run/loop-log/loop-log-trigger.tsx b/web/app/components/workflow/run/loop-log/loop-log-trigger.tsx
index 42593e564f..277db8cef4 100644
--- a/web/app/components/workflow/run/loop-log/loop-log-trigger.tsx
+++ b/web/app/components/workflow/run/loop-log/loop-log-trigger.tsx
@@ -3,10 +3,10 @@ import type {
LoopVariableMap,
NodeTracing,
} from '@/types/workflow'
+import { Button } from '@langgenius/dify-ui/button'
import { RiArrowRightSLine } from '@remixicon/react'
import { useTranslation } from 'react-i18next'
import { Loop } from '@/app/components/base/icons/src/vender/workflow'
-import { Button } from '@/app/components/base/ui/button'
type LoopLogTriggerProps = {
nodeInfo: NodeTracing
diff --git a/web/app/components/workflow/run/retry-log/retry-log-trigger.tsx b/web/app/components/workflow/run/retry-log/retry-log-trigger.tsx
index 592b7775f1..84c7808df1 100644
--- a/web/app/components/workflow/run/retry-log/retry-log-trigger.tsx
+++ b/web/app/components/workflow/run/retry-log/retry-log-trigger.tsx
@@ -1,10 +1,10 @@
import type { NodeTracing } from '@/types/workflow'
+import { Button } from '@langgenius/dify-ui/button'
import {
RiArrowRightSLine,
RiRestartFill,
} from '@remixicon/react'
import { useTranslation } from 'react-i18next'
-import { Button } from '@/app/components/base/ui/button'
type RetryLogTriggerProps = {
nodeInfo: NodeTracing
diff --git a/web/app/components/workflow/selection-contextmenu.tsx b/web/app/components/workflow/selection-contextmenu.tsx
index 13e599bf48..e89f8ff937 100644
--- a/web/app/components/workflow/selection-contextmenu.tsx
+++ b/web/app/components/workflow/selection-contextmenu.tsx
@@ -2,6 +2,14 @@ import type { CreateSnippetDialogPayload } from './create-snippet-dialog'
import type { Edge, Node } from './types'
import type { SnippetCanvasData } from '@/models/snippet'
import { cn } from '@langgenius/dify-ui/cn'
+import {
+ ContextMenu,
+ ContextMenuContent,
+ ContextMenuGroup,
+ ContextMenuItem,
+ ContextMenuLabel,
+ ContextMenuSeparator,
+} from '@langgenius/dify-ui/context-menu'
import { produce } from 'immer'
import {
memo,
@@ -12,13 +20,7 @@ import {
} from 'react'
import { useTranslation } from 'react-i18next'
import { useStore as useReactFlowStore, useStoreApi } from 'reactflow'
-import {
- ContextMenu,
- ContextMenuContent,
- ContextMenuItem,
- ContextMenuSeparator,
-} from '@/app/components/base/ui/context-menu'
-import { toast } from '@/app/components/base/ui/toast'
+import { toast } from '@langgenius/dify-ui/toast'
import { useCollaborativeWorkflow } from '@/app/components/workflow/hooks/use-collaborative-workflow'
import { useSnippetAndEvaluationPlanAccess } from '@/hooks/use-snippet-and-evaluation-plan-access'
import { useRouter } from '@/next/navigation'
@@ -271,11 +273,11 @@ const getSelectedSnippetGraph = (
position: nextPosition,
positionAbsolute: node.positionAbsolute
? (isRootNode
- ? {
- x: node.positionAbsolute.x - minRootX,
- y: node.positionAbsolute.y - minRootY,
- }
- : node.positionAbsolute)
+ ? {
+ x: node.positionAbsolute.x - minRootX,
+ y: node.positionAbsolute.y - minRootY,
+ }
+ : node.positionAbsolute)
: undefined,
selected: false,
data: {
diff --git a/web/app/components/workflow/store/__tests__/workflow-store.spec.ts b/web/app/components/workflow/store/__tests__/workflow-store.spec.ts
index 6edc3dc316..6cebc4e23c 100644
--- a/web/app/components/workflow/store/__tests__/workflow-store.spec.ts
+++ b/web/app/components/workflow/store/__tests__/workflow-store.spec.ts
@@ -1,6 +1,7 @@
import type { Shape, SliceFromInjection } from '../workflow'
import { renderHook } from '@testing-library/react'
import { BlockEnum } from '@/app/components/workflow/types'
+import { createEdge, createNode } from '../../__tests__/fixtures'
import { createTestWorkflowStore, renderWorkflowHook } from '../../__tests__/workflow-test-env'
import { createWorkflowStore, useStore, useWorkflowStore } from '../workflow'
@@ -51,6 +52,7 @@ describe('createWorkflowStore', () => {
['listeningTriggerNodeIds', 'setListeningTriggerNodeIds', ['n1', 'n2']],
['listeningTriggerIsAll', 'setListeningTriggerIsAll', true],
['clipboardElements', 'setClipboardElements', []],
+ ['clipboardEdges', 'setClipboardEdges', []],
['selection', 'setSelection', { x1: 0, y1: 0, x2: 100, y2: 100 }],
['bundleNodeSize', 'setBundleNodeSize', { width: 200, height: 100 }],
['mousePosition', 'setMousePosition', { pageX: 10, pageY: 20, elementX: 5, elementY: 15 }],
@@ -68,6 +70,17 @@ describe('createWorkflowStore', () => {
expect(store.getState().controlMode).toBe('pointer')
expect(localStorage.setItem).toHaveBeenCalledWith('workflow-operation-mode', 'pointer')
})
+
+ it('should update clipboard nodes and edges with setClipboardData', () => {
+ const store = createStore()
+ const nodes = [createNode({ id: 'n-1' })]
+ const edges = [createEdge({ id: 'e-1', source: 'n-1', target: 'n-2' })]
+
+ store.getState().setClipboardData({ nodes, edges })
+
+ expect(store.getState().clipboardElements).toEqual(nodes)
+ expect(store.getState().clipboardEdges).toEqual(edges)
+ })
})
describe('Node Slice Setters', () => {
diff --git a/web/app/components/workflow/store/workflow/workflow-slice.ts b/web/app/components/workflow/store/workflow/workflow-slice.ts
index c3b41f06a2..58e3debc63 100644
--- a/web/app/components/workflow/store/workflow/workflow-slice.ts
+++ b/web/app/components/workflow/store/workflow/workflow-slice.ts
@@ -1,5 +1,6 @@
import type { StateCreator } from 'zustand'
import type {
+ Edge,
Node,
TriggerNodeType,
WorkflowRunningData,
@@ -34,7 +35,10 @@ export type WorkflowSliceShape = {
listeningTriggerIsAll: boolean
setListeningTriggerIsAll: (isAll: boolean) => void
clipboardElements: Node[]
+ clipboardEdges: Edge[]
setClipboardElements: (clipboardElements: Node[]) => void
+ setClipboardEdges: (clipboardEdges: Edge[]) => void
+ setClipboardData: (clipboardData: { nodes: Node[], edges: Edge[] }) => void
selection: null | { x1: number, y1: number, x2: number, y2: number }
setSelection: (selection: WorkflowSliceShape['selection']) => void
bundleNodeSize: { width: number, height: number } | null
@@ -75,7 +79,15 @@ export const createWorkflowSlice: StateCreator = set => ({
listeningTriggerIsAll: false,
setListeningTriggerIsAll: isAll => set(() => ({ listeningTriggerIsAll: isAll })),
clipboardElements: [],
+ clipboardEdges: [],
setClipboardElements: clipboardElements => set(() => ({ clipboardElements })),
+ setClipboardEdges: clipboardEdges => set(() => ({ clipboardEdges })),
+ setClipboardData: ({ nodes, edges }) => {
+ set(() => ({
+ clipboardElements: nodes,
+ clipboardEdges: edges,
+ }))
+ },
selection: null,
setSelection: selection => set(() => ({ selection })),
bundleNodeSize: null,
diff --git a/web/app/components/workflow/types.ts b/web/app/components/workflow/types.ts
index efd21e099c..99ad3af49e 100644
--- a/web/app/components/workflow/types.ts
+++ b/web/app/components/workflow/types.ts
@@ -214,7 +214,7 @@ export type InputVar = {
}
variable: string
max_length?: number
- default?: string | number
+ default?: string | number | boolean
required: boolean
hint?: string
options?: string[]
diff --git a/web/app/components/workflow/update-dsl-modal.tsx b/web/app/components/workflow/update-dsl-modal.tsx
index 04d687ce73..cfa9c995eb 100644
--- a/web/app/components/workflow/update-dsl-modal.tsx
+++ b/web/app/components/workflow/update-dsl-modal.tsx
@@ -1,6 +1,8 @@
'use client'
import type { MouseEventHandler } from 'react'
+import { Button } from '@langgenius/dify-ui/button'
+import { toast } from '@langgenius/dify-ui/toast'
import {
RiAlertFill,
RiCloseLine,
@@ -16,8 +18,6 @@ import { useTranslation } from 'react-i18next'
import Uploader from '@/app/components/app/create-from-dsl-modal/uploader'
import { useStore as useAppStore } from '@/app/components/app/store'
import Modal from '@/app/components/base/modal'
-import { Button } from '@/app/components/base/ui/button'
-import { toast } from '@/app/components/base/ui/toast'
import { usePluginDependencies } from '@/app/components/workflow/plugin-dependency/hooks'
import { useEventEmitterContextContext } from '@/context/event-emitter'
import {
diff --git a/web/app/components/workflow/utils/__tests__/clipboard.spec.ts b/web/app/components/workflow/utils/__tests__/clipboard.spec.ts
new file mode 100644
index 0000000000..ccb3f426d4
--- /dev/null
+++ b/web/app/components/workflow/utils/__tests__/clipboard.spec.ts
@@ -0,0 +1,101 @@
+import { createEdge, createNode } from '../../__tests__/fixtures'
+import {
+ parseWorkflowClipboardText,
+ readWorkflowClipboard,
+ stringifyWorkflowClipboardData,
+ writeWorkflowClipboard,
+} from '../clipboard'
+
+describe('workflow clipboard storage', () => {
+ const currentVersion = '0.6.0'
+ const readTextMock = vi.fn<() => Promise>()
+ const writeTextMock = vi.fn<(text: string) => Promise>()
+
+ beforeEach(() => {
+ readTextMock.mockReset()
+ writeTextMock.mockReset()
+ Object.defineProperty(navigator, 'clipboard', {
+ configurable: true,
+ value: {
+ readText: readTextMock,
+ writeText: writeTextMock,
+ },
+ })
+ })
+
+ it('should return empty clipboard data when clipboard text is empty', async () => {
+ readTextMock.mockResolvedValue('')
+
+ await expect(readWorkflowClipboard(currentVersion)).resolves.toEqual({
+ nodes: [],
+ edges: [],
+ isVersionMismatch: false,
+ })
+ })
+
+ it('should write and read clipboard data', async () => {
+ const nodes = [createNode({ id: 'node-1' })]
+ const edges = [createEdge({ id: 'edge-1', source: 'node-1', target: 'node-2' })]
+
+ const serialized = stringifyWorkflowClipboardData({ nodes, edges }, currentVersion)
+ readTextMock.mockResolvedValue(serialized)
+
+ await writeWorkflowClipboard({ nodes, edges }, currentVersion)
+ expect(writeTextMock).toHaveBeenCalledWith(serialized)
+ await expect(readWorkflowClipboard(currentVersion)).resolves.toEqual({
+ nodes,
+ edges,
+ sourceVersion: currentVersion,
+ isVersionMismatch: false,
+ })
+ })
+
+ it('should allow reading clipboard data with different version', async () => {
+ const nodes = [createNode({ id: 'node-1' })]
+ const edges = [createEdge({ id: 'edge-1', source: 'node-1', target: 'node-2' })]
+ readTextMock.mockResolvedValue(JSON.stringify({
+ kind: 'dify-workflow-clipboard',
+ version: '0.5.0',
+ nodes,
+ edges,
+ }))
+
+ await expect(readWorkflowClipboard(currentVersion)).resolves.toEqual({
+ nodes,
+ edges,
+ sourceVersion: '0.5.0',
+ isVersionMismatch: true,
+ })
+ })
+
+ it('should return empty clipboard data for invalid JSON', () => {
+ expect(parseWorkflowClipboardText('{invalid-json', currentVersion)).toEqual({
+ nodes: [],
+ edges: [],
+ isVersionMismatch: false,
+ })
+ })
+
+ it('should return empty clipboard data for invalid structure', () => {
+ expect(parseWorkflowClipboardText(JSON.stringify({
+ kind: 'unknown',
+ version: 1,
+ nodes: [],
+ edges: [],
+ }), currentVersion)).toEqual({
+ nodes: [],
+ edges: [],
+ isVersionMismatch: false,
+ })
+ })
+
+ it('should return empty clipboard data when clipboard read fails', async () => {
+ readTextMock.mockRejectedValue(new Error('clipboard denied'))
+
+ await expect(readWorkflowClipboard(currentVersion)).resolves.toEqual({
+ nodes: [],
+ edges: [],
+ isVersionMismatch: false,
+ })
+ })
+})
diff --git a/web/app/components/workflow/utils/clipboard.ts b/web/app/components/workflow/utils/clipboard.ts
new file mode 100644
index 0000000000..246d3351f3
--- /dev/null
+++ b/web/app/components/workflow/utils/clipboard.ts
@@ -0,0 +1,205 @@
+import type { Edge, Node } from '../types'
+
+const WORKFLOW_CLIPBOARD_KIND = 'dify-workflow-clipboard'
+
+type WorkflowClipboardPayload = {
+ kind: string
+ version: string
+ nodes: Node[]
+ edges: Edge[]
+}
+
+type WorkflowClipboardData = {
+ nodes: Node[]
+ edges: Edge[]
+}
+
+type WorkflowClipboardReadResult = WorkflowClipboardData & {
+ sourceVersion?: string
+ isVersionMismatch: boolean
+}
+
+const emptyClipboardData: WorkflowClipboardData = {
+ nodes: [],
+ edges: [],
+}
+
+const emptyClipboardReadResult: WorkflowClipboardReadResult = {
+ ...emptyClipboardData,
+ isVersionMismatch: false,
+}
+
+const isNodeArray = (value: unknown): value is Node[] => Array.isArray(value)
+const isEdgeArray = (value: unknown): value is Edge[] => Array.isArray(value)
+const isPlainObject = (value: unknown): value is Record =>
+ value !== null && typeof value === 'object' && !Array.isArray(value)
+
+export const sanitizeClipboardValueByDefault = (defaultValue: unknown, incomingValue: unknown): unknown => {
+ if (defaultValue === undefined)
+ return incomingValue
+
+ if (Array.isArray(defaultValue))
+ return Array.isArray(incomingValue) ? incomingValue : [...defaultValue]
+
+ if (isPlainObject(defaultValue)) {
+ if (!isPlainObject(incomingValue)) {
+ return Object.fromEntries(
+ Object.entries(defaultValue).map(([key, value]) => [
+ key,
+ sanitizeClipboardValueByDefault(value, undefined),
+ ]),
+ )
+ }
+
+ const merged: Record = {}
+ const keys = new Set([
+ ...Object.keys(defaultValue),
+ ...Object.keys(incomingValue),
+ ])
+
+ keys.forEach((key) => {
+ const hasDefault = Object.hasOwn(defaultValue, key)
+ const hasIncoming = Object.hasOwn(incomingValue, key)
+ if (hasDefault && hasIncoming) {
+ merged[key] = sanitizeClipboardValueByDefault(
+ defaultValue[key],
+ incomingValue[key],
+ )
+ return
+ }
+
+ if (hasIncoming) {
+ merged[key] = incomingValue[key]
+ return
+ }
+
+ merged[key] = sanitizeClipboardValueByDefault(defaultValue[key], undefined)
+ })
+
+ return merged
+ }
+
+ if (typeof defaultValue === 'number')
+ return typeof incomingValue === 'number' && Number.isFinite(incomingValue) ? incomingValue : defaultValue
+
+ return typeof incomingValue === typeof defaultValue ? incomingValue : defaultValue
+}
+
+export const isClipboardValueCompatibleWithDefault = (defaultValue: unknown, incomingValue: unknown): boolean => {
+ if (incomingValue === undefined)
+ return true
+
+ if (defaultValue === undefined)
+ return true
+
+ if (Array.isArray(defaultValue))
+ return Array.isArray(incomingValue)
+
+ if (isPlainObject(defaultValue)) {
+ if (!isPlainObject(incomingValue))
+ return false
+
+ return Object.entries(defaultValue).every(([key, value]) => {
+ return isClipboardValueCompatibleWithDefault(
+ value,
+ incomingValue[key],
+ )
+ })
+ }
+
+ if (typeof defaultValue === 'number')
+ return typeof incomingValue === 'number' && Number.isFinite(incomingValue)
+
+ return typeof incomingValue === typeof defaultValue
+}
+
+export const isClipboardNodeStructurallyValid = (value: unknown): value is Node => {
+ if (!isPlainObject(value))
+ return false
+
+ if (typeof value.id !== 'string' || typeof value.type !== 'string')
+ return false
+
+ if (!isPlainObject(value.data) || !isPlainObject(value.position))
+ return false
+
+ return Number.isFinite(value.position.x) && Number.isFinite(value.position.y)
+}
+
+export const isClipboardEdgeStructurallyValid = (value: unknown): value is Edge => {
+ if (!isPlainObject(value))
+ return false
+
+ return typeof value.id === 'string'
+ && typeof value.source === 'string'
+ && typeof value.target === 'string'
+}
+
+export const parseWorkflowClipboardText = (
+ text: string,
+ currentClipboardVersion: string,
+): WorkflowClipboardReadResult => {
+ if (!text)
+ return emptyClipboardReadResult
+
+ try {
+ const parsed = JSON.parse(text) as Partial
+ if (
+ parsed.kind !== WORKFLOW_CLIPBOARD_KIND
+ || typeof parsed.version !== 'string'
+ || !isNodeArray(parsed.nodes)
+ || !isEdgeArray(parsed.edges)
+ ) {
+ return emptyClipboardReadResult
+ }
+
+ const sourceVersion = parsed.version
+
+ const validatedNodes = parsed.nodes.filter(isClipboardNodeStructurallyValid)
+ const validatedEdges = parsed.edges.filter(isClipboardEdgeStructurallyValid)
+
+ return {
+ nodes: validatedNodes,
+ edges: validatedEdges,
+ sourceVersion,
+ isVersionMismatch: sourceVersion !== currentClipboardVersion,
+ }
+ }
+ catch {
+ return emptyClipboardReadResult
+ }
+}
+
+export const stringifyWorkflowClipboardData = (
+ payload: WorkflowClipboardData,
+ currentClipboardVersion: string,
+): string => {
+ const data: WorkflowClipboardPayload = {
+ kind: WORKFLOW_CLIPBOARD_KIND,
+ version: currentClipboardVersion,
+ nodes: payload.nodes,
+ edges: payload.edges,
+ }
+
+ return JSON.stringify(data)
+}
+
+export const writeWorkflowClipboard = async (
+ payload: WorkflowClipboardData,
+ currentClipboardVersion: string,
+): Promise => {
+ const text = stringifyWorkflowClipboardData(payload, currentClipboardVersion)
+ await navigator.clipboard.writeText(text)
+}
+
+export const readWorkflowClipboard = async (
+ currentClipboardVersion: string,
+): Promise => {
+ try {
+ const text = await navigator.clipboard.readText()
+ return parseWorkflowClipboardText(text, currentClipboardVersion)
+ }
+ catch {
+ return emptyClipboardReadResult
+ }
+}
diff --git a/web/app/components/workflow/utils/index.ts b/web/app/components/workflow/utils/index.ts
index 641deec695..5098549528 100644
--- a/web/app/components/workflow/utils/index.ts
+++ b/web/app/components/workflow/utils/index.ts
@@ -1,3 +1,4 @@
+export * from './clipboard'
export * from './common'
export * from './data-source'
export * from './edge'
diff --git a/web/app/components/workflow/variable-inspect/__tests__/value-content-sections.spec.tsx b/web/app/components/workflow/variable-inspect/__tests__/value-content-sections.spec.tsx
index 2ebe527104..bba17a7c32 100644
--- a/web/app/components/workflow/variable-inspect/__tests__/value-content-sections.spec.tsx
+++ b/web/app/components/workflow/variable-inspect/__tests__/value-content-sections.spec.tsx
@@ -18,7 +18,7 @@ const toastMocks = vi.hoisted(() => ({
promise: vi.fn(),
}))
-vi.mock('@/app/components/base/ui/toast', () => ({
+vi.mock('@langgenius/dify-ui/toast', () => ({
toast: Object.assign(toastMocks.call, {
success: vi.fn(),
error: vi.fn(),
diff --git a/web/app/components/workflow/variable-inspect/group.tsx b/web/app/components/workflow/variable-inspect/group.tsx
index 7d94226786..24786e4b1f 100644
--- a/web/app/components/workflow/variable-inspect/group.tsx
+++ b/web/app/components/workflow/variable-inspect/group.tsx
@@ -10,7 +10,7 @@ import {
} from '@remixicon/react'
import { useState } from 'react'
import { useTranslation } from 'react-i18next'
-// import { Button } from '@/app/components/base/ui/button'
+// import { Button } from '@langgenius/dify-ui/button'
import ActionButton from '@/app/components/base/action-button'
import Tooltip from '@/app/components/base/tooltip'
import BlockIcon from '@/app/components/workflow/block-icon'
diff --git a/web/app/components/workflow/variable-inspect/left.tsx b/web/app/components/workflow/variable-inspect/left.tsx
index f3450cea13..6d590eb281 100644
--- a/web/app/components/workflow/variable-inspect/left.tsx
+++ b/web/app/components/workflow/variable-inspect/left.tsx
@@ -1,10 +1,10 @@
import type { currentVarType } from './panel'
import type { VarInInspect } from '@/types/workflow'
+import { Button } from '@langgenius/dify-ui/button'
import { cn } from '@langgenius/dify-ui/cn'
// import { useState } from 'react'
import { useTranslation } from 'react-i18next'
-import { Button } from '@/app/components/base/ui/button'
import { VarInInspectType } from '@/types/workflow'
import useCurrentVars from '../hooks/use-inspect-vars-crud'
import { useNodesInteractions } from '../hooks/use-nodes-interactions'
diff --git a/web/app/components/workflow/variable-inspect/listening.tsx b/web/app/components/workflow/variable-inspect/listening.tsx
index 4559665ffa..3994355d58 100644
--- a/web/app/components/workflow/variable-inspect/listening.tsx
+++ b/web/app/components/workflow/variable-inspect/listening.tsx
@@ -3,13 +3,13 @@ import type { FC } from 'react'
import type { Node } from 'reactflow'
import type { ScheduleTriggerNodeType } from '@/app/components/workflow/nodes/trigger-schedule/types'
import type { WebhookTriggerNodeType } from '@/app/components/workflow/nodes/trigger-webhook/types'
+import { Button } from '@langgenius/dify-ui/button'
import copy from 'copy-to-clipboard'
import { useEffect, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { useStoreApi } from 'reactflow'
import { StopCircle } from '@/app/components/base/icons/src/vender/line/mediaAndDevices'
import Tooltip from '@/app/components/base/tooltip'
-import { Button } from '@/app/components/base/ui/button'
import BlockIcon from '@/app/components/workflow/block-icon'
import { useGetToolIcon } from '@/app/components/workflow/hooks/use-tool-icon'
import { getNextExecutionTime } from '@/app/components/workflow/nodes/trigger-schedule/utils/execution-time-calculator'
diff --git a/web/app/components/workflow/workflow-preview/components/zoom-in-out.tsx b/web/app/components/workflow/workflow-preview/components/zoom-in-out.tsx
index 3f714c5ca9..9ea32caec3 100644
--- a/web/app/components/workflow/workflow-preview/components/zoom-in-out.tsx
+++ b/web/app/components/workflow/workflow-preview/components/zoom-in-out.tsx
@@ -1,5 +1,12 @@
import type { FC } from 'react'
import { cn } from '@langgenius/dify-ui/cn'
+import {
+ DropdownMenu,
+ DropdownMenuContent,
+ DropdownMenuItem,
+ DropdownMenuSeparator,
+ DropdownMenuTrigger,
+} from '@langgenius/dify-ui/dropdown-menu'
import {
Fragment,
memo,
@@ -10,13 +17,6 @@ import {
useReactFlow,
useViewport,
} from 'reactflow'
-import {
- DropdownMenu,
- DropdownMenuContent,
- DropdownMenuItem,
- DropdownMenuSeparator,
- DropdownMenuTrigger,
-} from '@/app/components/base/ui/dropdown-menu'
import TipPopup from '@/app/components/workflow/operator/tip-popup'
import ShortcutsName from '@/app/components/workflow/shortcuts-name'
diff --git a/web/app/education-apply/education-apply-page.tsx b/web/app/education-apply/education-apply-page.tsx
index 7fda139aa9..93259c1ccb 100644
--- a/web/app/education-apply/education-apply-page.tsx
+++ b/web/app/education-apply/education-apply-page.tsx
@@ -1,5 +1,7 @@
'use client'
+import { Button } from '@langgenius/dify-ui/button'
+import { toast } from '@langgenius/dify-ui/toast'
import { RiExternalLinkLine } from '@remixicon/react'
import { noop } from 'es-toolkit/function'
import {
@@ -7,8 +9,6 @@ import {
} from 'react'
import { useTranslation } from 'react-i18next'
import Checkbox from '@/app/components/base/checkbox'
-import { Button } from '@/app/components/base/ui/button'
-import { toast } from '@/app/components/base/ui/toast'
import { EDUCATION_VERIFYING_LOCALSTORAGE_ITEM } from '@/app/education-apply/constants'
import { useDocLink } from '@/context/i18n'
import { useProviderContext } from '@/context/provider-context'
diff --git a/web/app/education-apply/expire-notice-modal.tsx b/web/app/education-apply/expire-notice-modal.tsx
index ec88bba1b5..e54afaecde 100644
--- a/web/app/education-apply/expire-notice-modal.tsx
+++ b/web/app/education-apply/expire-notice-modal.tsx
@@ -1,9 +1,9 @@
'use client'
+import { Button } from '@langgenius/dify-ui/button'
import { RiExternalLinkLine } from '@remixicon/react'
import * as React from 'react'
import { useTranslation } from 'react-i18next'
import Modal from '@/app/components/base/modal'
-import { Button } from '@/app/components/base/ui/button'
import { useDocLink } from '@/context/i18n'
import { useModalContextSelector } from '@/context/modal-context'
import useTimestamp from '@/hooks/use-timestamp'
diff --git a/web/app/education-apply/user-info.tsx b/web/app/education-apply/user-info.tsx
index 32d56b2780..be9b319038 100644
--- a/web/app/education-apply/user-info.tsx
+++ b/web/app/education-apply/user-info.tsx
@@ -1,7 +1,7 @@
+import { Avatar } from '@langgenius/dify-ui/avatar'
+import { Button } from '@langgenius/dify-ui/button'
import { useTranslation } from 'react-i18next'
import { Triangle } from '@/app/components/base/icons/src/public/education'
-import { Avatar } from '@/app/components/base/ui/avatar'
-import { Button } from '@/app/components/base/ui/button'
import { useAppContext } from '@/context/app-context'
import { useRouter } from '@/next/navigation'
import { useLogout } from '@/service/use-common'
diff --git a/web/app/education-apply/verify-state-modal.tsx b/web/app/education-apply/verify-state-modal.tsx
index c4d836f178..bb7439352e 100644
--- a/web/app/education-apply/verify-state-modal.tsx
+++ b/web/app/education-apply/verify-state-modal.tsx
@@ -1,3 +1,4 @@
+import { Button } from '@langgenius/dify-ui/button'
import {
RiExternalLinkLine,
} from '@remixicon/react'
@@ -5,7 +6,6 @@ import * as React from 'react'
import { useEffect, useRef, useState } from 'react'
import { createPortal } from 'react-dom'
import { useTranslation } from 'react-i18next'
-import { Button } from '@/app/components/base/ui/button'
import { useDocLink } from '@/context/i18n'
type IConfirm = {
diff --git a/web/app/forgot-password/ChangePasswordForm.tsx b/web/app/forgot-password/ChangePasswordForm.tsx
index bf262181a9..deb520c436 100644
--- a/web/app/forgot-password/ChangePasswordForm.tsx
+++ b/web/app/forgot-password/ChangePasswordForm.tsx
@@ -1,11 +1,11 @@
'use client'
import { CheckCircleIcon } from '@heroicons/react/24/solid'
+import { Button } from '@langgenius/dify-ui/button'
import { cn } from '@langgenius/dify-ui/cn'
+import { toast } from '@langgenius/dify-ui/toast'
import { useCallback, useState } from 'react'
import { useTranslation } from 'react-i18next'
import Loading from '@/app/components/base/loading'
-import { Button } from '@/app/components/base/ui/button'
-import { toast } from '@/app/components/base/ui/toast'
import { validPassword } from '@/config'
import { useSearchParams } from '@/next/navigation'
import { changePasswordWithToken } from '@/service/common'
diff --git a/web/app/forgot-password/ForgotPasswordForm.tsx b/web/app/forgot-password/ForgotPasswordForm.tsx
index 7ce5e7d8dd..b4fba60182 100644
--- a/web/app/forgot-password/ForgotPasswordForm.tsx
+++ b/web/app/forgot-password/ForgotPasswordForm.tsx
@@ -1,15 +1,15 @@
'use client'
import type { InitValidateStatusResponse } from '@/models/common'
+import { Button } from '@langgenius/dify-ui/button'
+
import { useStore } from '@tanstack/react-form'
import * as React from 'react'
-
import { useEffect, useState } from 'react'
import { useTranslation } from 'react-i18next'
import * as z from 'zod'
import { formContext, useAppForm } from '@/app/components/base/form'
import { zodSubmitValidator } from '@/app/components/base/form/utils/zod-submit-validator'
-import { Button } from '@/app/components/base/ui/button'
import { useRouter } from '@/next/navigation'
import {
fetchInitValidateStatus,
diff --git a/web/app/init/InitPasswordPopup.tsx b/web/app/init/InitPasswordPopup.tsx
index b0c74978fa..6b25fa371a 100644
--- a/web/app/init/InitPasswordPopup.tsx
+++ b/web/app/init/InitPasswordPopup.tsx
@@ -1,9 +1,9 @@
'use client'
import type { InitValidateStatusResponse } from '@/models/common'
+import { Button } from '@langgenius/dify-ui/button'
+import { toast } from '@langgenius/dify-ui/toast'
import { useEffect, useState } from 'react'
import { useTranslation } from 'react-i18next'
-import { Button } from '@/app/components/base/ui/button'
-import { toast } from '@/app/components/base/ui/toast'
import useDocumentTitle from '@/hooks/use-document-title'
import { useRouter } from '@/next/navigation'
import { fetchInitValidateStatus, initValidate } from '@/service/common'
diff --git a/web/app/install/installForm.tsx b/web/app/install/installForm.tsx
index c068548561..d9b6e0c7ad 100644
--- a/web/app/install/installForm.tsx
+++ b/web/app/install/installForm.tsx
@@ -1,8 +1,9 @@
'use client'
import type { InitValidateStatusResponse, SetupStatusResponse } from '@/models/common'
+import { Button } from '@langgenius/dify-ui/button'
import { cn } from '@langgenius/dify-ui/cn'
-import { useStore } from '@tanstack/react-form'
+import { useStore } from '@tanstack/react-form'
import * as React from 'react'
import { useEffect } from 'react'
import { useTranslation } from 'react-i18next'
@@ -10,7 +11,6 @@ import * as z from 'zod'
import { formContext, useAppForm } from '@/app/components/base/form'
import { zodSubmitValidator } from '@/app/components/base/form/utils/zod-submit-validator'
import Input from '@/app/components/base/input'
-import { Button } from '@/app/components/base/ui/button'
import { validPassword } from '@/config'
import { LICENSE_LINK } from '@/constants/link'
diff --git a/web/app/layout.tsx b/web/app/layout.tsx
index 63e506bd45..be0c854f02 100644
--- a/web/app/layout.tsx
+++ b/web/app/layout.tsx
@@ -1,4 +1,6 @@
import type { Viewport } from '@/next'
+import { ToastHost } from '@langgenius/dify-ui/toast'
+import { TooltipProvider } from '@langgenius/dify-ui/tooltip'
import { Provider as JotaiProvider } from 'jotai/react'
import { ThemeProvider } from 'next-themes'
import { NuqsAdapter } from 'nuqs/adapters/next/app'
@@ -6,8 +8,6 @@ import GlobalPublicStoreProvider from '@/context/global-public-context'
import { TanstackQueryInitializer } from '@/context/query-client'
import { getDatasetMap } from '@/env'
import { getLocaleOnServer } from '@/i18n-config/server'
-import { ToastHost } from './components/base/ui/toast'
-import { TooltipProvider } from './components/base/ui/tooltip'
import PartnerStackCookieRecorder from './components/billing/partner-stack/cookie-recorder'
import CreateAppAttributionBootstrap from './components/create-app-attribution-bootstrap'
import { AgentationLoader } from './components/devtools/agentation-loader'
diff --git a/web/app/reset-password/check-code/page.tsx b/web/app/reset-password/check-code/page.tsx
index 98ea828aae..b11935481e 100644
--- a/web/app/reset-password/check-code/page.tsx
+++ b/web/app/reset-password/check-code/page.tsx
@@ -1,10 +1,10 @@
'use client'
+import { Button } from '@langgenius/dify-ui/button'
+import { toast } from '@langgenius/dify-ui/toast'
import { RiArrowLeftLine, RiMailSendFill } from '@remixicon/react'
import { useState } from 'react'
import { useTranslation } from 'react-i18next'
import Input from '@/app/components/base/input'
-import { Button } from '@/app/components/base/ui/button'
-import { toast } from '@/app/components/base/ui/toast'
import Countdown from '@/app/components/signin/countdown'
import { useLocale } from '@/context/i18n'
import { useRouter, useSearchParams } from '@/next/navigation'
diff --git a/web/app/reset-password/page.tsx b/web/app/reset-password/page.tsx
index d58fd7e9ff..63b950bd11 100644
--- a/web/app/reset-password/page.tsx
+++ b/web/app/reset-password/page.tsx
@@ -1,11 +1,11 @@
'use client'
+import { Button } from '@langgenius/dify-ui/button'
+import { toast } from '@langgenius/dify-ui/toast'
import { RiArrowLeftLine, RiLockPasswordLine } from '@remixicon/react'
import { noop } from 'es-toolkit/function'
import { useState } from 'react'
import { useTranslation } from 'react-i18next'
import Input from '@/app/components/base/input'
-import { Button } from '@/app/components/base/ui/button'
-import { toast } from '@/app/components/base/ui/toast'
import { emailRegex } from '@/config'
import { useLocale } from '@/context/i18n'
import useDocumentTitle from '@/hooks/use-document-title'
diff --git a/web/app/reset-password/set-password/page.tsx b/web/app/reset-password/set-password/page.tsx
index 7277b6d30c..4a8cb2af9b 100644
--- a/web/app/reset-password/set-password/page.tsx
+++ b/web/app/reset-password/set-password/page.tsx
@@ -1,12 +1,12 @@
'use client'
+import { Button } from '@langgenius/dify-ui/button'
import { cn } from '@langgenius/dify-ui/cn'
+import { toast } from '@langgenius/dify-ui/toast'
import { RiCheckboxCircleFill } from '@remixicon/react'
import { useCountDown } from 'ahooks'
import { useCallback, useState } from 'react'
import { useTranslation } from 'react-i18next'
import Input from '@/app/components/base/input'
-import { Button } from '@/app/components/base/ui/button'
-import { toast } from '@/app/components/base/ui/toast'
import { validPassword } from '@/config'
import { useRouter, useSearchParams } from '@/next/navigation'
import { changePasswordWithToken } from '@/service/common'
diff --git a/web/app/signin/check-code/page.tsx b/web/app/signin/check-code/page.tsx
index b86c984f31..fb52e0b5b7 100644
--- a/web/app/signin/check-code/page.tsx
+++ b/web/app/signin/check-code/page.tsx
@@ -1,12 +1,12 @@
'use client'
import type { FormEvent } from 'react'
+import { Button } from '@langgenius/dify-ui/button'
+import { toast } from '@langgenius/dify-ui/toast'
import { RiArrowLeftLine, RiMailSendFill } from '@remixicon/react'
import { useEffect, useRef, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { trackEvent } from '@/app/components/base/amplitude'
import Input from '@/app/components/base/input'
-import { Button } from '@/app/components/base/ui/button'
-import { toast } from '@/app/components/base/ui/toast'
import Countdown from '@/app/components/signin/countdown'
import { useLocale } from '@/context/i18n'
diff --git a/web/app/signin/components/mail-and-code-auth.tsx b/web/app/signin/components/mail-and-code-auth.tsx
index 5c5431c42a..41cf50854e 100644
--- a/web/app/signin/components/mail-and-code-auth.tsx
+++ b/web/app/signin/components/mail-and-code-auth.tsx
@@ -1,9 +1,9 @@
import type { FormEvent } from 'react'
+import { Button } from '@langgenius/dify-ui/button'
+import { toast } from '@langgenius/dify-ui/toast'
import { useState } from 'react'
import { useTranslation } from 'react-i18next'
import Input from '@/app/components/base/input'
-import { Button } from '@/app/components/base/ui/button'
-import { toast } from '@/app/components/base/ui/toast'
import { COUNT_DOWN_KEY, COUNT_DOWN_TIME_MS } from '@/app/components/signin/countdown'
import { emailRegex } from '@/config'
import { useLocale } from '@/context/i18n'
diff --git a/web/app/signin/components/mail-and-password-auth.tsx b/web/app/signin/components/mail-and-password-auth.tsx
index ca93b9b391..6feaf11426 100644
--- a/web/app/signin/components/mail-and-password-auth.tsx
+++ b/web/app/signin/components/mail-and-password-auth.tsx
@@ -1,11 +1,11 @@
import type { ResponseError } from '@/service/fetch'
+import { Button } from '@langgenius/dify-ui/button'
+import { toast } from '@langgenius/dify-ui/toast'
import { noop } from 'es-toolkit/function'
import { useState } from 'react'
import { useTranslation } from 'react-i18next'
import { trackEvent } from '@/app/components/base/amplitude'
import Input from '@/app/components/base/input'
-import { Button } from '@/app/components/base/ui/button'
-import { toast } from '@/app/components/base/ui/toast'
import { emailRegex } from '@/config'
import { useLocale } from '@/context/i18n'
import Link from '@/next/link'
diff --git a/web/app/signin/components/social-auth.tsx b/web/app/signin/components/social-auth.tsx
index 6eb9d62703..09fa528d1f 100644
--- a/web/app/signin/components/social-auth.tsx
+++ b/web/app/signin/components/social-auth.tsx
@@ -1,6 +1,6 @@
+import { Button } from '@langgenius/dify-ui/button'
import { cn } from '@langgenius/dify-ui/cn'
import { useTranslation } from 'react-i18next'
-import { Button } from '@/app/components/base/ui/button'
import { API_PREFIX } from '@/config'
import { useSearchParams } from '@/next/navigation'
import { getPurifyHref } from '@/utils'
diff --git a/web/app/signin/components/sso-auth.tsx b/web/app/signin/components/sso-auth.tsx
index 5ead876741..32612e4edd 100644
--- a/web/app/signin/components/sso-auth.tsx
+++ b/web/app/signin/components/sso-auth.tsx
@@ -1,10 +1,10 @@
'use client'
import type { FC } from 'react'
+import { Button } from '@langgenius/dify-ui/button'
+import { toast } from '@langgenius/dify-ui/toast'
import { useState } from 'react'
import { useTranslation } from 'react-i18next'
import { Lock01 } from '@/app/components/base/icons/src/vender/solid/security'
-import { Button } from '@/app/components/base/ui/button'
-import { toast } from '@/app/components/base/ui/toast'
import { useRouter, useSearchParams } from '@/next/navigation'
import { getUserOAuth2SSOUrl, getUserOIDCSSOUrl, getUserSAMLSSOUrl } from '@/service/sso'
import { SSOProtocol } from '@/types/feature'
diff --git a/web/app/signin/invite-settings/page.tsx b/web/app/signin/invite-settings/page.tsx
index 91973f07f9..fe3dac7153 100644
--- a/web/app/signin/invite-settings/page.tsx
+++ b/web/app/signin/invite-settings/page.tsx
@@ -1,5 +1,7 @@
'use client'
import type { Locale } from '@/i18n-config'
+import { Button } from '@langgenius/dify-ui/button'
+import { toast } from '@langgenius/dify-ui/toast'
import { RiAccountCircleLine } from '@remixicon/react'
import { noop } from 'es-toolkit/function'
import { useCallback, useState } from 'react'
@@ -7,8 +9,6 @@ import { useTranslation } from 'react-i18next'
import Input from '@/app/components/base/input'
import Loading from '@/app/components/base/loading'
import { SimpleSelect } from '@/app/components/base/select'
-import { Button } from '@/app/components/base/ui/button'
-import { toast } from '@/app/components/base/ui/toast'
import { LICENSE_LINK } from '@/constants/link'
import { useGlobalPublicStore } from '@/context/global-public-context'
import { setLocaleOnClient } from '@/i18n-config'
diff --git a/web/app/signin/normal-form.tsx b/web/app/signin/normal-form.tsx
index 4fd0eabf66..2ead90f068 100644
--- a/web/app/signin/normal-form.tsx
+++ b/web/app/signin/normal-form.tsx
@@ -1,9 +1,9 @@
import { cn } from '@langgenius/dify-ui/cn'
+import { toast } from '@langgenius/dify-ui/toast'
import { RiContractLine, RiDoorLockLine, RiErrorWarningFill } from '@remixicon/react'
import * as React from 'react'
import { useCallback, useEffect, useState } from 'react'
import { useTranslation } from 'react-i18next'
-import { toast } from '@/app/components/base/ui/toast'
import { IS_CE_EDITION } from '@/config'
import { useGlobalPublicStore } from '@/context/global-public-context'
import Link from '@/next/link'
diff --git a/web/app/signin/one-more-step.tsx b/web/app/signin/one-more-step.tsx
index 6368533e2a..1f615d5e5a 100644
--- a/web/app/signin/one-more-step.tsx
+++ b/web/app/signin/one-more-step.tsx
@@ -1,11 +1,11 @@
'use client'
import type { Reducer } from 'react'
+import { Button } from '@langgenius/dify-ui/button'
+import { toast } from '@langgenius/dify-ui/toast'
import { useReducer } from 'react'
import { useTranslation } from 'react-i18next'
import { SimpleSelect } from '@/app/components/base/select'
import Tooltip from '@/app/components/base/tooltip'
-import { Button } from '@/app/components/base/ui/button'
-import { toast } from '@/app/components/base/ui/toast'
import { LICENSE_LINK } from '@/constants/link'
import { languages, LanguagesSupported } from '@/i18n-config/language'
import Link from '@/next/link'
diff --git a/web/app/signup/check-code/page.tsx b/web/app/signup/check-code/page.tsx
index a71d5289fe..a6a8cadc13 100644
--- a/web/app/signup/check-code/page.tsx
+++ b/web/app/signup/check-code/page.tsx
@@ -1,11 +1,11 @@
'use client'
import type { MailSendResponse, MailValidityResponse } from '@/service/use-common'
+import { Button } from '@langgenius/dify-ui/button'
+import { toast } from '@langgenius/dify-ui/toast'
import { RiArrowLeftLine, RiMailSendFill } from '@remixicon/react'
import { useState } from 'react'
import { useTranslation } from 'react-i18next'
import Input from '@/app/components/base/input'
-import { Button } from '@/app/components/base/ui/button'
-import { toast } from '@/app/components/base/ui/toast'
import Countdown from '@/app/components/signin/countdown'
import { useLocale } from '@/context/i18n'
import { useRouter, useSearchParams } from '@/next/navigation'
diff --git a/web/app/signup/components/input-mail.tsx b/web/app/signup/components/input-mail.tsx
index ae7fbb986d..f4c5214c11 100644
--- a/web/app/signup/components/input-mail.tsx
+++ b/web/app/signup/components/input-mail.tsx
@@ -1,10 +1,10 @@
'use client'
import type { MailSendResponse } from '@/service/use-common'
+import { Button } from '@langgenius/dify-ui/button'
+import { toast } from '@langgenius/dify-ui/toast'
import { useCallback, useState } from 'react'
import { useTranslation } from 'react-i18next'
import Input from '@/app/components/base/input'
-import { Button } from '@/app/components/base/ui/button'
-import { toast } from '@/app/components/base/ui/toast'
import Split from '@/app/signin/split'
import { emailRegex } from '@/config'
import { useGlobalPublicStore } from '@/context/global-public-context'
diff --git a/web/app/signup/set-password/page.tsx b/web/app/signup/set-password/page.tsx
index ffecd0bc0c..a8eb883078 100644
--- a/web/app/signup/set-password/page.tsx
+++ b/web/app/signup/set-password/page.tsx
@@ -1,13 +1,13 @@
'use client'
import type { MailRegisterResponse } from '@/service/use-common'
+import { Button } from '@langgenius/dify-ui/button'
import { cn } from '@langgenius/dify-ui/cn'
+import { toast } from '@langgenius/dify-ui/toast'
import Cookies from 'js-cookie'
import { useCallback, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { trackEvent } from '@/app/components/base/amplitude'
import Input from '@/app/components/base/input'
-import { Button } from '@/app/components/base/ui/button'
-import { toast } from '@/app/components/base/ui/toast'
import { validPassword } from '@/config'
import { useRouter, useSearchParams } from '@/next/navigation'
import { useMailRegister } from '@/service/use-common'
diff --git a/web/context/provider-context-provider.tsx b/web/context/provider-context-provider.tsx
index 078a954e31..0af0f24b9a 100644
--- a/web/context/provider-context-provider.tsx
+++ b/web/context/provider-context-provider.tsx
@@ -1,11 +1,11 @@
'use client'
import type { ReactNode } from 'react'
+import { toast } from '@langgenius/dify-ui/toast'
import { useQueryClient } from '@tanstack/react-query'
import dayjs from 'dayjs'
import { useEffect, useState } from 'react'
import { useTranslation } from 'react-i18next'
-import { toast } from '@/app/components/base/ui/toast'
import { setZendeskConversationFields } from '@/app/components/base/zendesk/utils'
import { defaultPlan } from '@/app/components/billing/config'
import { parseCurrentPlan } from '@/app/components/billing/utils'
diff --git a/web/docs/overlay-migration.md b/web/docs/overlay-migration.md
index 29ef814fcd..0016f34e12 100644
--- a/web/docs/overlay-migration.md
+++ b/web/docs/overlay-migration.md
@@ -11,14 +11,14 @@ This document tracks the migration away from legacy overlay APIs.
- `@/app/components/base/select` (including `custom` / `pure`)
- `@/app/components/base/dialog`
- Replacement primitives:
- - `@/app/components/base/ui/tooltip`
- - `@/app/components/base/ui/dropdown-menu`
- - `@/app/components/base/ui/context-menu`
- - `@/app/components/base/ui/popover`
- - `@/app/components/base/ui/dialog`
- - `@/app/components/base/ui/alert-dialog`
- - `@/app/components/base/ui/select`
- - `@/app/components/base/ui/toast`
+ - `@langgenius/dify-ui/tooltip`
+ - `@langgenius/dify-ui/dropdown-menu`
+ - `@langgenius/dify-ui/context-menu`
+ - `@langgenius/dify-ui/popover`
+ - `@langgenius/dify-ui/dialog`
+ - `@langgenius/dify-ui/alert-dialog`
+ - `@langgenius/dify-ui/select`
+ - `@langgenius/dify-ui/toast`
- Tracking issue:
## ESLint policy
diff --git a/web/eslint.constants.mjs b/web/eslint.constants.mjs
index 57a14fc22d..46690035fb 100644
--- a/web/eslint.constants.mjs
+++ b/web/eslint.constants.mjs
@@ -33,7 +33,7 @@ export const OVERLAY_RESTRICTED_IMPORT_PATTERNS = [
'**/base/tooltip',
'**/base/tooltip/index',
],
- message: 'Deprecated: use @/app/components/base/ui/tooltip instead. See issue #32767.',
+ message: 'Deprecated: use @langgenius/dify-ui/tooltip instead. See issue #32767.',
},
{
group: [
@@ -41,7 +41,7 @@ export const OVERLAY_RESTRICTED_IMPORT_PATTERNS = [
'**/base/modal/index',
'**/base/modal/modal',
],
- message: 'Deprecated: use @/app/components/base/ui/dialog instead. See issue #32767.',
+ message: 'Deprecated: use @langgenius/dify-ui/dialog instead. See issue #32767.',
},
{
group: [
@@ -50,14 +50,14 @@ export const OVERLAY_RESTRICTED_IMPORT_PATTERNS = [
'**/base/select/custom',
'**/base/select/pure',
],
- message: 'Deprecated: use @/app/components/base/ui/select instead. See issue #32767.',
+ message: 'Deprecated: use @langgenius/dify-ui/select instead. See issue #32767.',
},
{
group: [
'**/base/dialog',
'**/base/dialog/index',
],
- message: 'Deprecated: use @/app/components/base/ui/dialog instead. See issue #32767.',
+ message: 'Deprecated: use @langgenius/dify-ui/dialog instead. See issue #32767.',
},
]
diff --git a/web/hooks/use-import-dsl.ts b/web/hooks/use-import-dsl.ts
index 6be67c22ac..1ece2a27f0 100644
--- a/web/hooks/use-import-dsl.ts
+++ b/web/hooks/use-import-dsl.ts
@@ -3,13 +3,13 @@ import type {
DSLImportResponse,
} from '@/models/app'
import type { AppIconType } from '@/types/app'
+import { toast } from '@langgenius/dify-ui/toast'
import {
useCallback,
useRef,
useState,
} from 'react'
import { useTranslation } from 'react-i18next'
-import { toast } from '@/app/components/base/ui/toast'
import { usePluginDependencies } from '@/app/components/workflow/plugin-dependency/hooks'
import { NEED_REFRESH_APP_LIST_KEY } from '@/config'
import { useSelector } from '@/context/app-context'
diff --git a/web/hooks/use-pay.tsx b/web/hooks/use-pay.tsx
index 237913b433..593a33a1d2 100644
--- a/web/hooks/use-pay.tsx
+++ b/web/hooks/use-pay.tsx
@@ -1,7 +1,5 @@
'use client'
-import { useCallback, useEffect, useState } from 'react'
-import { useTranslation } from 'react-i18next'
import {
AlertDialog,
AlertDialogActions,
@@ -9,7 +7,9 @@ import {
AlertDialogContent,
AlertDialogDescription,
AlertDialogTitle,
-} from '@/app/components/base/ui/alert-dialog'
+} from '@langgenius/dify-ui/alert-dialog'
+import { useCallback, useEffect, useState } from 'react'
+import { useTranslation } from 'react-i18next'
import { useRouter, useSearchParams } from '@/next/navigation'
import { useNotionBinding } from '@/service/use-common'
diff --git a/web/i18n/ar-TN/workflow.json b/web/i18n/ar-TN/workflow.json
index ba0a937774..719d8fc9ff 100644
--- a/web/i18n/ar-TN/workflow.json
+++ b/web/i18n/ar-TN/workflow.json
@@ -151,6 +151,7 @@
"common.branch": "فرع",
"common.chooseDSL": "اختر ملف DSL",
"common.chooseStartNodeToRun": "اختر عقدة البداية للتشغيل",
+ "common.clipboardVersionCompatibilityWarning": "تم نسخ هذا المحتوى من إصدار مختلف من تطبيق Dify. قد تكون بعض الأجزاء غير متوافقة.",
"common.commentMode": "وضع التعليقات",
"common.configure": "تكوين",
"common.configureRequired": "التكوين مطلوب",
diff --git a/web/i18n/de-DE/workflow.json b/web/i18n/de-DE/workflow.json
index 3394652b9b..cba6e93300 100644
--- a/web/i18n/de-DE/workflow.json
+++ b/web/i18n/de-DE/workflow.json
@@ -151,6 +151,7 @@
"common.branch": "ZWEIG",
"common.chooseDSL": "Wählen Sie eine DSL(yml)-Datei",
"common.chooseStartNodeToRun": "Wählen Sie den Startknoten zum Ausführen",
+ "common.clipboardVersionCompatibilityWarning": "Dieser Inhalt wurde aus einer anderen Dify-App-Version kopiert. Einige Teile sind möglicherweise nicht kompatibel.",
"common.commentMode": "Kommentar-Modus",
"common.configure": "Konfigurieren",
"common.configureRequired": "Konfiguration erforderlich",
diff --git a/web/i18n/en-US/app-debug.json b/web/i18n/en-US/app-debug.json
index 9c64405ecc..d08a712924 100644
--- a/web/i18n/en-US/app-debug.json
+++ b/web/i18n/en-US/app-debug.json
@@ -325,6 +325,8 @@
"variableConfig.file.image.name": "Image",
"variableConfig.file.supportFileTypes": "Support File Types",
"variableConfig.file.video.name": "Video",
+ "variableConfig.hidden": "Hidden",
+ "variableConfig.hiddenDescription": "Hidden fields are not shown in the Web App start form. They can be populated via URL query parameters. For more details, view the documentation.",
"variableConfig.hide": "Hide",
"variableConfig.inputPlaceholder": "Please input",
"variableConfig.json": "JSON Code",
diff --git a/web/i18n/en-US/app-overview.json b/web/i18n/en-US/app-overview.json
index 81256a769c..cc11378ada 100644
--- a/web/i18n/en-US/app-overview.json
+++ b/web/i18n/en-US/app-overview.json
@@ -56,6 +56,8 @@
"overview.appInfo.embedded.copy": "Copy",
"overview.appInfo.embedded.entry": "Embedded",
"overview.appInfo.embedded.explanation": "Choose the way to embed chat app to your website",
+ "overview.appInfo.embedded.hiddenInputs.description": "These values will be applied to the iframe URL or embedded script inputs.",
+ "overview.appInfo.embedded.hiddenInputs.title": "Config hidden inputs",
"overview.appInfo.embedded.iframe": "To add the chat app any where on your website, add this iframe to your html code.",
"overview.appInfo.embedded.scripts": "To add a chat app to the bottom right of your website add this code to your html.",
"overview.appInfo.embedded.title": "Embed on website",
@@ -104,6 +106,8 @@
"overview.appInfo.settings.workflow.subTitle": "Workflow Details",
"overview.appInfo.settings.workflow.title": "Workflow",
"overview.appInfo.title": "Web App",
+ "overview.appInfo.workflowLaunchHiddenInputs.description": "Please complete the hidden fields before opening the web app.",
+ "overview.appInfo.workflowLaunchHiddenInputs.title": "Hidden fields",
"overview.disableTooltip.triggerMode": "The {{feature}} feature is not supported in Trigger Node mode.",
"overview.status.disable": "Disabled",
"overview.status.running": "In Service",
diff --git a/web/i18n/en-US/workflow.json b/web/i18n/en-US/workflow.json
index 86fe288b82..b47e15a8f0 100644
--- a/web/i18n/en-US/workflow.json
+++ b/web/i18n/en-US/workflow.json
@@ -151,6 +151,7 @@
"common.branch": "BRANCH",
"common.chooseDSL": "Choose DSL file",
"common.chooseStartNodeToRun": "Choose the start node to run",
+ "common.clipboardVersionCompatibilityWarning": "This content was copied from a different Dify app version. Some parts may be incompatible.",
"common.commentMode": "Comment Mode",
"common.configure": "Configure",
"common.configureRequired": "Configure Required",
diff --git a/web/i18n/es-ES/workflow.json b/web/i18n/es-ES/workflow.json
index 203d31bc57..773eea2546 100644
--- a/web/i18n/es-ES/workflow.json
+++ b/web/i18n/es-ES/workflow.json
@@ -151,6 +151,7 @@
"common.branch": "RAMA",
"common.chooseDSL": "Elegir archivo DSL (yml)",
"common.chooseStartNodeToRun": "Elige el nodo de inicio para ejecutar",
+ "common.clipboardVersionCompatibilityWarning": "Este contenido se copió desde una versión diferente de la aplicación Dify. Es posible que algunas partes no sean compatibles.",
"common.commentMode": "Modo de comentarios",
"common.configure": "Configurar",
"common.configureRequired": "Configuración requerida",
diff --git a/web/i18n/fa-IR/workflow.json b/web/i18n/fa-IR/workflow.json
index 6b502a0318..bdb75988f4 100644
--- a/web/i18n/fa-IR/workflow.json
+++ b/web/i18n/fa-IR/workflow.json
@@ -151,6 +151,7 @@
"common.branch": "شاخه",
"common.chooseDSL": "انتخاب فایل DSL (yml)",
"common.chooseStartNodeToRun": "گره شروع را برای اجرا انتخاب کنید",
+ "common.clipboardVersionCompatibilityWarning": "این محتوا از نسخه دیگری از برنامه Dify کپی شده است. ممکن است برخی بخشها ناسازگار باشند.",
"common.commentMode": "حالت نظرات",
"common.configure": "پیکربندی",
"common.configureRequired": "پیکربندی الزامی است",
diff --git a/web/i18n/fr-FR/workflow.json b/web/i18n/fr-FR/workflow.json
index 106665d2bf..c548f0443f 100644
--- a/web/i18n/fr-FR/workflow.json
+++ b/web/i18n/fr-FR/workflow.json
@@ -151,6 +151,7 @@
"common.branch": "BRANCHE",
"common.chooseDSL": "Choisir le fichier DSL(yml)",
"common.chooseStartNodeToRun": "Choisissez le nœud de départ pour lancer",
+ "common.clipboardVersionCompatibilityWarning": "Ce contenu a été copié depuis une version différente de l'application Dify. Certaines parties peuvent être incompatibles.",
"common.commentMode": "Mode commentaires",
"common.configure": "Configurer",
"common.configureRequired": "Configuration requise",
diff --git a/web/i18n/hi-IN/workflow.json b/web/i18n/hi-IN/workflow.json
index 777711ef22..b8ae69a116 100644
--- a/web/i18n/hi-IN/workflow.json
+++ b/web/i18n/hi-IN/workflow.json
@@ -151,6 +151,7 @@
"common.branch": "शाखा",
"common.chooseDSL": "डीएसएल (वाईएमएल) फ़ाइल चुनें",
"common.chooseStartNodeToRun": "चलाने के लिए प्रारंभ नोड चुनें",
+ "common.clipboardVersionCompatibilityWarning": "यह सामग्री Dify ऐप के किसी अलग संस्करण से कॉपी की गई है। इसके कुछ हिस्से असंगत हो सकते हैं।",
"common.commentMode": "टिप्पणी मोड",
"common.configure": "कॉन्फ़िगर करें",
"common.configureRequired": "कॉन्फ़िगरेशन आवश्यक",
diff --git a/web/i18n/id-ID/workflow.json b/web/i18n/id-ID/workflow.json
index 6150fc6505..8b391d9dcd 100644
--- a/web/i18n/id-ID/workflow.json
+++ b/web/i18n/id-ID/workflow.json
@@ -151,6 +151,7 @@
"common.branch": "CABANG",
"common.chooseDSL": "Pilih file DSL",
"common.chooseStartNodeToRun": "Pilih node awal untuk dijalankan",
+ "common.clipboardVersionCompatibilityWarning": "Konten ini disalin dari versi aplikasi Dify yang berbeda. Beberapa bagian mungkin tidak kompatibel.",
"common.commentMode": "Mode komentar",
"common.configure": "Konfigurasikan",
"common.configureRequired": "Konfigurasi yang Diperlukan",
diff --git a/web/i18n/it-IT/workflow.json b/web/i18n/it-IT/workflow.json
index 533e6a1b7d..8f5f34db02 100644
--- a/web/i18n/it-IT/workflow.json
+++ b/web/i18n/it-IT/workflow.json
@@ -151,6 +151,7 @@
"common.branch": "RAMO",
"common.chooseDSL": "Scegli file DSL(yml)",
"common.chooseStartNodeToRun": "Scegli il nodo di partenza da eseguire",
+ "common.clipboardVersionCompatibilityWarning": "Questo contenuto è stato copiato da una versione diversa dell'app Dify. Alcune parti potrebbero non essere compatibili.",
"common.commentMode": "Modalità commenti",
"common.configure": "Configura",
"common.configureRequired": "Configurazione Richiesta",
diff --git a/web/i18n/ja-JP/workflow.json b/web/i18n/ja-JP/workflow.json
index 07aaa99132..9131bb2c50 100644
--- a/web/i18n/ja-JP/workflow.json
+++ b/web/i18n/ja-JP/workflow.json
@@ -151,6 +151,7 @@
"common.branch": "ブランチ",
"common.chooseDSL": "DSL(yml) ファイルを選択",
"common.chooseStartNodeToRun": "実行する開始ノードを選択",
+ "common.clipboardVersionCompatibilityWarning": "このコンテンツは別の Dify アプリバージョンからコピーされました。一部が互換性のない可能性があります。",
"common.commentMode": "コメントモード",
"common.configure": "設定",
"common.configureRequired": "設定が必要",
diff --git a/web/i18n/ko-KR/workflow.json b/web/i18n/ko-KR/workflow.json
index fcdf681ab1..66caf27cbf 100644
--- a/web/i18n/ko-KR/workflow.json
+++ b/web/i18n/ko-KR/workflow.json
@@ -151,6 +151,7 @@
"common.branch": "브랜치",
"common.chooseDSL": "DSL(yml) 파일 선택",
"common.chooseStartNodeToRun": "실행할 시작 노드를 선택하세요",
+ "common.clipboardVersionCompatibilityWarning": "이 콘텐츠는 다른 Dify 앱 버전에서 복사되었습니다. 일부 항목은 호환되지 않을 수 있습니다.",
"common.commentMode": "댓글 모드",
"common.configure": "구성",
"common.configureRequired": "구성 필요",
diff --git a/web/i18n/nl-NL/workflow.json b/web/i18n/nl-NL/workflow.json
index ce1438df6b..fba6e37ae7 100644
--- a/web/i18n/nl-NL/workflow.json
+++ b/web/i18n/nl-NL/workflow.json
@@ -151,6 +151,7 @@
"common.branch": "BRANCH",
"common.chooseDSL": "Choose DSL file",
"common.chooseStartNodeToRun": "Choose the start node to run",
+ "common.clipboardVersionCompatibilityWarning": "Deze inhoud is gekopieerd vanuit een andere Dify-appversie. Sommige onderdelen zijn mogelijk niet compatibel.",
"common.commentMode": "Opmerkingsmodus",
"common.configure": "Configure",
"common.configureRequired": "Configure Required",
diff --git a/web/i18n/pl-PL/workflow.json b/web/i18n/pl-PL/workflow.json
index 23f3f2cebd..3306c65a4b 100644
--- a/web/i18n/pl-PL/workflow.json
+++ b/web/i18n/pl-PL/workflow.json
@@ -151,6 +151,7 @@
"common.branch": "GAŁĄŹ",
"common.chooseDSL": "Wybierz plik DSL(yml)",
"common.chooseStartNodeToRun": "Wybierz węzeł początkowy, aby uruchomić",
+ "common.clipboardVersionCompatibilityWarning": "Ta zawartość została skopiowana z innej wersji aplikacji Dify. Niektóre elementy mogą być niekompatybilne.",
"common.commentMode": "Tryb komentarzy",
"common.configure": "Skonfiguruj",
"common.configureRequired": "Wymagana konfiguracja",
diff --git a/web/i18n/pt-BR/workflow.json b/web/i18n/pt-BR/workflow.json
index 963bb0c176..0d3b069728 100644
--- a/web/i18n/pt-BR/workflow.json
+++ b/web/i18n/pt-BR/workflow.json
@@ -151,6 +151,7 @@
"common.branch": "RAMIFICAÇÃO",
"common.chooseDSL": "Escolha o arquivo DSL(yml)",
"common.chooseStartNodeToRun": "Escolha o nó inicial para executar",
+ "common.clipboardVersionCompatibilityWarning": "Este conteúdo foi copiado de uma versão diferente do aplicativo Dify. Algumas partes podem ser incompatíveis.",
"common.commentMode": "Modo de comentários",
"common.configure": "Configurar",
"common.configureRequired": "Configuração necessária",
diff --git a/web/i18n/ro-RO/workflow.json b/web/i18n/ro-RO/workflow.json
index 2550c6afd2..6a5d938bc7 100644
--- a/web/i18n/ro-RO/workflow.json
+++ b/web/i18n/ro-RO/workflow.json
@@ -151,6 +151,7 @@
"common.branch": "RAMURĂ",
"common.chooseDSL": "Alegeți fișierul DSL(yml)",
"common.chooseStartNodeToRun": "Alegeți nodul de start pentru a rula",
+ "common.clipboardVersionCompatibilityWarning": "Acest conținut a fost copiat dintr-o altă versiune a aplicației Dify. Unele părți pot fi incompatibile.",
"common.commentMode": "Mod comentarii",
"common.configure": "Configurează",
"common.configureRequired": "Configurare necesară",
diff --git a/web/i18n/ru-RU/workflow.json b/web/i18n/ru-RU/workflow.json
index a13a7afe28..9751952b18 100644
--- a/web/i18n/ru-RU/workflow.json
+++ b/web/i18n/ru-RU/workflow.json
@@ -151,6 +151,7 @@
"common.branch": "ВЕТКА",
"common.chooseDSL": "Выберите файл DSL(yml)",
"common.chooseStartNodeToRun": "Выберите начальный узел для запуска",
+ "common.clipboardVersionCompatibilityWarning": "Этот контент был скопирован из другой версии приложения Dify. Некоторые части могут быть несовместимы.",
"common.commentMode": "Режим комментариев",
"common.configure": "Настроить",
"common.configureRequired": "Требуется настройка",
diff --git a/web/i18n/sl-SI/workflow.json b/web/i18n/sl-SI/workflow.json
index deb71836b7..895586d721 100644
--- a/web/i18n/sl-SI/workflow.json
+++ b/web/i18n/sl-SI/workflow.json
@@ -151,6 +151,7 @@
"common.branch": "VEJA",
"common.chooseDSL": "Izberi DSL datoteko",
"common.chooseStartNodeToRun": "Izberite začetno vozlišče za zagon",
+ "common.clipboardVersionCompatibilityWarning": "Ta vsebina je bila kopirana iz druge različice aplikacije Dify. Nekateri deli morda niso združljivi.",
"common.commentMode": "Način komentarjev",
"common.configure": "Konfiguriraj",
"common.configureRequired": "Konfigurirajte zahteve",
diff --git a/web/i18n/th-TH/workflow.json b/web/i18n/th-TH/workflow.json
index 75c82b883d..655e39372b 100644
--- a/web/i18n/th-TH/workflow.json
+++ b/web/i18n/th-TH/workflow.json
@@ -151,6 +151,7 @@
"common.branch": "กิ่ง",
"common.chooseDSL": "เลือกไฟล์ DSL",
"common.chooseStartNodeToRun": "เลือกโหนดเริ่มต้นเพื่อรัน",
+ "common.clipboardVersionCompatibilityWarning": "เนื้อหานี้ถูกคัดลอกจากแอป Dify คนละเวอร์ชัน บางส่วนอาจไม่เข้ากัน",
"common.commentMode": "โหมดความคิดเห็น",
"common.configure": "กําหนดค่า",
"common.configureRequired": "กําหนดค่าที่จําเป็น",
diff --git a/web/i18n/tr-TR/workflow.json b/web/i18n/tr-TR/workflow.json
index 3943a3c067..d4e460bf03 100644
--- a/web/i18n/tr-TR/workflow.json
+++ b/web/i18n/tr-TR/workflow.json
@@ -151,6 +151,7 @@
"common.branch": "DAL",
"common.chooseDSL": "DSL(yml) dosyasını seçin",
"common.chooseStartNodeToRun": "Çalıştırmak için başlangıç düğümünü seçin",
+ "common.clipboardVersionCompatibilityWarning": "Bu içerik farklı bir Dify uygulama sürümünden kopyalandı. Bazı bölümler uyumsuz olabilir.",
"common.commentMode": "Yorum modu",
"common.configure": "Yapılandır",
"common.configureRequired": "Yapılandırma Gerekli",
diff --git a/web/i18n/uk-UA/workflow.json b/web/i18n/uk-UA/workflow.json
index 088df91e32..062c958cac 100644
--- a/web/i18n/uk-UA/workflow.json
+++ b/web/i18n/uk-UA/workflow.json
@@ -151,6 +151,7 @@
"common.branch": "ГІЛКА",
"common.chooseDSL": "Виберіть файл DSL(yml)",
"common.chooseStartNodeToRun": "Виберіть початковий вузол для запуску",
+ "common.clipboardVersionCompatibilityWarning": "Цей вміст скопійовано з іншої версії застосунку Dify. Деякі частини можуть бути несумісними.",
"common.commentMode": "Режим коментарів",
"common.configure": "Налаштувати",
"common.configureRequired": "Потрібна конфігурація",
diff --git a/web/i18n/vi-VN/workflow.json b/web/i18n/vi-VN/workflow.json
index be1dc9cb9c..875b85ebcb 100644
--- a/web/i18n/vi-VN/workflow.json
+++ b/web/i18n/vi-VN/workflow.json
@@ -151,6 +151,7 @@
"common.branch": "NHÁNH",
"common.chooseDSL": "Chọn tệp DSL(yml)",
"common.chooseStartNodeToRun": "Chọn nút bắt đầu để chạy",
+ "common.clipboardVersionCompatibilityWarning": "Nội dung này được sao chép từ một phiên bản ứng dụng Dify khác. Một số phần có thể không tương thích.",
"common.commentMode": "Chế độ bình luận",
"common.configure": "Cấu hình",
"common.configureRequired": "Yêu cầu cấu hình",
diff --git a/web/i18n/zh-Hans/app-debug.json b/web/i18n/zh-Hans/app-debug.json
index fb4f231852..fe7779630d 100644
--- a/web/i18n/zh-Hans/app-debug.json
+++ b/web/i18n/zh-Hans/app-debug.json
@@ -325,6 +325,8 @@
"variableConfig.file.image.name": "图片",
"variableConfig.file.supportFileTypes": "支持的文件类型",
"variableConfig.file.video.name": "视频",
+ "variableConfig.hidden": "Hidden",
+ "variableConfig.hiddenDescription": "隐藏字段不会显示在 Web App 的开始表单中,可通过 URL 查询参数传值。更多信息查看文档。",
"variableConfig.hide": "隐藏",
"variableConfig.inputPlaceholder": "请输入",
"variableConfig.json": "JSON",
diff --git a/web/i18n/zh-Hans/app-overview.json b/web/i18n/zh-Hans/app-overview.json
index 03a26cb32c..a6a31df9c7 100644
--- a/web/i18n/zh-Hans/app-overview.json
+++ b/web/i18n/zh-Hans/app-overview.json
@@ -56,6 +56,8 @@
"overview.appInfo.embedded.copy": "复制",
"overview.appInfo.embedded.entry": "嵌入",
"overview.appInfo.embedded.explanation": "选择一种方式将聊天应用嵌入到你的网站中",
+ "overview.appInfo.embedded.hiddenInputs.description": "这些值会应用到 iframe URL 或嵌入脚本的 inputs 中。",
+ "overview.appInfo.embedded.hiddenInputs.title": "配置隐藏字段",
"overview.appInfo.embedded.iframe": "将以下 iframe 嵌入到你的网站中的目标位置",
"overview.appInfo.embedded.scripts": "将以下代码嵌入到你的网站中",
"overview.appInfo.embedded.title": "嵌入到网站中",
@@ -104,6 +106,8 @@
"overview.appInfo.settings.workflow.subTitle": "工作流详情",
"overview.appInfo.settings.workflow.title": "工作流",
"overview.appInfo.title": "Web App",
+ "overview.appInfo.workflowLaunchHiddenInputs.description": "打开 web app 前,请先填写隐藏字段。",
+ "overview.appInfo.workflowLaunchHiddenInputs.title": "隐藏字段",
"overview.disableTooltip.triggerMode": "触发节点模式下不支持{{feature}}功能。",
"overview.status.disable": "已停用",
"overview.status.running": "运行中",
diff --git a/web/i18n/zh-Hans/workflow.json b/web/i18n/zh-Hans/workflow.json
index dab8d45e11..4849e35281 100644
--- a/web/i18n/zh-Hans/workflow.json
+++ b/web/i18n/zh-Hans/workflow.json
@@ -151,6 +151,7 @@
"common.branch": "分支",
"common.chooseDSL": "选择 DSL(yml) 文件",
"common.chooseStartNodeToRun": "选择启动节点进行运行",
+ "common.clipboardVersionCompatibilityWarning": "此内容复制自不同版本的 Dify 应用,部分内容可能不兼容。",
"common.commentMode": "评论模式",
"common.configure": "配置",
"common.configureRequired": "需要进行配置",
diff --git a/web/i18n/zh-Hant/workflow.json b/web/i18n/zh-Hant/workflow.json
index daf8baf8a8..29d0d76c75 100644
--- a/web/i18n/zh-Hant/workflow.json
+++ b/web/i18n/zh-Hant/workflow.json
@@ -151,6 +151,7 @@
"common.branch": "分支",
"common.chooseDSL": "選擇 DSL(yml)檔",
"common.chooseStartNodeToRun": "選擇要執行的起始節點",
+ "common.clipboardVersionCompatibilityWarning": "此內容複製自不同版本的 Dify 應用,部分內容可能不相容。",
"common.commentMode": "評論模式",
"common.configure": "配置",
"common.configureRequired": "需要進行配置",
diff --git a/web/models/debug.ts b/web/models/debug.ts
index 9aab3334cb..a0867e2b63 100644
--- a/web/models/debug.ts
+++ b/web/models/debug.ts
@@ -52,7 +52,7 @@ export type PromptVariable = {
key: string
name: string
type: string // "string" | "number" | "select",
- default?: string | number
+ default?: string | number | boolean
required?: boolean
options?: string[]
max_length?: number
diff --git a/web/service/base.ts b/web/service/base.ts
index 2063b5bc37..64d13ef59a 100644
--- a/web/service/base.ts
+++ b/web/service/base.ts
@@ -27,8 +27,8 @@ import type {
WorkflowPausedResponse,
WorkflowStartedResponse,
} from '@/types/workflow'
+import { toast } from '@langgenius/dify-ui/toast'
import Cookies from 'js-cookie'
-import { toast } from '@/app/components/base/ui/toast'
import { API_PREFIX, CSRF_COOKIE_NAME, CSRF_HEADER_NAME, IS_CE_EDITION, PASSPORT_HEADER_NAME, PUBLIC_API_PREFIX, WEB_APP_SHARE_CODE_HEADER_NAME } from '@/config'
import { asyncRunSafe } from '@/utils'
import { basePath } from '@/utils/var'
diff --git a/web/service/fetch.spec.ts b/web/service/fetch.spec.ts
index 0c01d32438..632f898e88 100644
--- a/web/service/fetch.spec.ts
+++ b/web/service/fetch.spec.ts
@@ -1,7 +1,7 @@
import { beforeEach, describe, expect, it, vi } from 'vitest'
import { base } from './fetch'
-vi.mock('@/app/components/base/ui/toast', () => ({
+vi.mock('@langgenius/dify-ui/toast', () => ({
toast: {
add: vi.fn(),
},
diff --git a/web/service/fetch.ts b/web/service/fetch.ts
index 82870a8d2e..34bd07160a 100644
--- a/web/service/fetch.ts
+++ b/web/service/fetch.ts
@@ -1,8 +1,8 @@
import type { AfterResponseHook, BeforeRequestHook, Hooks } from 'ky'
import type { IOtherOptions } from './base'
+import { toast } from '@langgenius/dify-ui/toast'
import Cookies from 'js-cookie'
import ky, { HTTPError } from 'ky'
-import { toast } from '@/app/components/base/ui/toast'
import { API_PREFIX, APP_VERSION, CSRF_COOKIE_NAME, CSRF_HEADER_NAME, IS_MARKETPLACE, MARKETPLACE_API_PREFIX, PASSPORT_HEADER_NAME, PUBLIC_API_PREFIX, WEB_APP_SHARE_CODE_HEADER_NAME } from '@/config'
import { getWebAppAccessToken, getWebAppPassport } from './webapp-auth'
diff --git a/web/tailwind.config.ts b/web/tailwind.config.ts
index 32b889e707..21c0455177 100644
--- a/web/tailwind.config.ts
+++ b/web/tailwind.config.ts
@@ -6,9 +6,11 @@ const config: Config = {
'./app/**/*.{js,ts,jsx,tsx}',
'./components/**/*.{js,ts,jsx,tsx}',
'./context/**/*.{js,ts,jsx,tsx}',
+ '../packages/dify-ui/src/**/*.{ts,tsx}',
'./node_modules/streamdown/dist/*.js',
'./node_modules/@streamdown/math/dist/*.js',
'!./**/*.{spec,test}.{js,ts,jsx,tsx}',
+ '!../packages/dify-ui/src/**/*.{spec,test}.{ts,tsx}',
],
...commonConfig,
}
diff --git a/web/types/feature.ts b/web/types/feature.ts
index 5abfb1e1a0..635221f2be 100644
--- a/web/types/feature.ts
+++ b/web/types/feature.ts
@@ -28,6 +28,7 @@ type License = {
}
export type SystemFeatures = {
+ app_dsl_version: string
trial_models: ModelProviderQuotaGetPaid[]
plugin_installation_permission: {
plugin_installation_scope: InstallationScope
@@ -68,6 +69,7 @@ export type SystemFeatures = {
}
export const defaultSystemFeatures: SystemFeatures = {
+ app_dsl_version: '',
trial_models: [],
plugin_installation_permission: {
plugin_installation_scope: InstallationScope.ALL,