feat(web): add copy action to error toasts (#42480)

Co-authored-by: yyh <yuanyouhuilyz@gmail.com>
This commit is contained in:
Joelandyyh authored and GitHub committed 2026-09-18 07:02:22 +00:00
1 parent 41bc173636
commit a60167b390
597 files changed
+1942 -689

No files matched your search

@@ -1,6 +1,45 @@
import type { ToastManager, ToastViewportProps } from '../index'
import { userEvent } from 'vite-plus/test/browser'
import { render } from 'vitest-browser-react'
import { createToast, createToastManager, toast, ToastHost } from '../index'
import {
createToast,
createToastManager,
ToastCard,
ToastPortal,
ToastProvider,
ToastViewport,
useToastManager,
} from '../index'
function ToastCards() {
const { toasts } = useToastManager<Record<string, never>>()
return toasts.map((item) => <ToastCard key={item.id} toast={item} />)
}
function ExampleToastHost({
manager,
timeout,
limit,
offset,
}: {
manager: ToastManager
timeout?: number
limit?: number
offset?: ToastViewportProps['offset']
}) {
return (
<ToastProvider toastManager={manager} timeout={timeout} limit={limit}>
<ToastPortal>
<ToastViewport offset={offset}>
<ToastCards />
</ToastViewport>
</ToastPortal>
</ToastProvider>
)
}
const manager = createToastManager()
const toast = createToast(manager)
const asHTMLElement = (element: HTMLElement | SVGElement) => element as HTMLElement
@@ -22,7 +61,7 @@ describe('@langgenius/dify-ui/toast', () => {
})
it('should render a success toast when called through the typed shortcut', async () => {
const screen = await render(<ToastHost />)
const screen = await render(<ExampleToastHost manager={manager} />)
toast.success('Saved', {
description: 'Your changes are available now.',
@@ -35,7 +74,7 @@ describe('@langgenius/dify-ui/toast', () => {
})
it('should keep multiple toast roots mounted in a collapsed stack', async () => {
const screen = await render(<ToastHost />)
const screen = await render(<ExampleToastHost manager={manager} />)
toast('First toast')
await expect.element(screen.getByText('First toast')).toBeInTheDocument()
@@ -63,7 +102,7 @@ describe('@langgenius/dify-ui/toast', () => {
>
Underlying action
</button>
<ToastHost timeout={0} />
<ExampleToastHost manager={manager} timeout={0} />
</>,
)
@@ -130,7 +169,7 @@ describe('@langgenius/dify-ui/toast', () => {
height: 20,
}}
/>
<ToastHost timeout={0} offset={{ top: 120 }} />
<ExampleToastHost manager={manager} timeout={0} offset={{ top: 120 }} />
</>,
)
@@ -190,7 +229,7 @@ describe('@langgenius/dify-ui/toast', () => {
height: 20,
}}
/>
<ToastHost timeout={0} />
<ExampleToastHost manager={manager} timeout={0} />
</>,
)
@@ -220,7 +259,7 @@ describe('@langgenius/dify-ui/toast', () => {
})
it('should render a neutral toast when called directly', async () => {
const screen = await render(<ToastHost />)
const screen = await render(<ExampleToastHost manager={manager} />)
toast('Neutral toast')
@@ -232,8 +271,8 @@ describe('@langgenius/dify-ui/toast', () => {
const localToast = createToast(localManager)
const screen = await render(
<>
<ToastHost />
<ToastHost manager={localManager} />
<ExampleToastHost manager={manager} />
<ExampleToastHost manager={localManager} />
</>,
)
@@ -254,7 +293,7 @@ describe('@langgenius/dify-ui/toast', () => {
it('should apply custom positioning to the viewport', async () => {
const localManager = createToastManager()
const localToast = createToast(localManager)
const screen = await render(<ToastHost manager={localManager} offset={{ top: 80 }} />)
const screen = await render(<ExampleToastHost manager={localManager} offset={{ top: 80 }} />)
localToast('Positioned viewport')
@@ -268,7 +307,7 @@ describe('@langgenius/dify-ui/toast', () => {
})
it('should mark overflow toasts as limited when the stack exceeds the configured limit', async () => {
const screen = await render(<ToastHost limit={1} />)
const screen = await render(<ExampleToastHost manager={manager} limit={1} />)
toast('First toast')
toast('Second toast')
@@ -278,7 +317,7 @@ describe('@langgenius/dify-ui/toast', () => {
})
it('should dismiss a toast when dismiss(id) is called', async () => {
const screen = await render(<ToastHost />)
const screen = await render(<ExampleToastHost manager={manager} />)
const toastId = toast('Closable', {
description: 'This toast can be removed.',
@@ -295,7 +334,7 @@ describe('@langgenius/dify-ui/toast', () => {
it('should close a toast when the dismiss button is clicked', async () => {
const onClose = vi.fn()
const screen = await render(<ToastHost />)
const screen = await render(<ExampleToastHost manager={manager} />)
toast('Dismiss me', {
description: 'Manual dismissal path.',
@@ -352,7 +391,7 @@ describe('@langgenius/dify-ui/toast', () => {
>
Underlying action
</button>
<ToastHost />
<ExampleToastHost manager={manager} />
</>,
)
@@ -397,7 +436,7 @@ describe('@langgenius/dify-ui/toast', () => {
})
it('should pass the host timeout to added toasts', async () => {
const screen = await render(<ToastHost timeout={1000} />)
const screen = await render(<ExampleToastHost manager={manager} timeout={1000} />)
toast('Auto dismiss')
await expect.element(screen.getByText('Auto dismiss')).toBeInTheDocument()
@@ -413,7 +452,7 @@ describe('@langgenius/dify-ui/toast', () => {
})
it('should keep a toast persistent when its timeout is zero', async () => {
const screen = await render(<ToastHost timeout={1000} />)
const screen = await render(<ExampleToastHost manager={manager} timeout={1000} />)
toast('Persistent', {
timeout: 0,
@@ -426,7 +465,7 @@ describe('@langgenius/dify-ui/toast', () => {
})
it('should update an existing toast', async () => {
const screen = await render(<ToastHost />)
const screen = await render(<ExampleToastHost manager={manager} />)
const toastId = toast.info('Loading', {
description: 'Preparing your data…',
@@ -445,7 +484,7 @@ describe('@langgenius/dify-ui/toast', () => {
})
it('should upsert an existing toast when add is called with the same id', async () => {
const screen = await render(<ToastHost />)
const screen = await render(<ExampleToastHost manager={manager} />)
toast('Draft saving', {
id: 'draft-save-status',
@@ -468,7 +507,7 @@ describe('@langgenius/dify-ui/toast', () => {
it('should render and invoke toast action props', async () => {
const onAction = vi.fn()
const screen = await render(<ToastHost />)
const screen = await render(<ExampleToastHost manager={manager} />)
toast('Action toast', {
actionProps: {
@@ -484,7 +523,7 @@ describe('@langgenius/dify-ui/toast', () => {
})
it('should transition a promise toast from loading to success', async () => {
const screen = await render(<ToastHost />)
const screen = await render(<ExampleToastHost manager={manager} />)
let resolvePromise: ((value: string) => void) | undefined
const promise = new Promise<string>((resolve) => {
+41 -2
View File
@@ -1,9 +1,48 @@
import type { Meta, StoryObj } from '@storybook/react-vite'
import type { ToastManager, ToastViewportProps } from '.'
import * as React from 'react'
import { expect, within } from 'storybook/test'
import { toast, ToastHost } from '.'
import {
createToast,
createToastManager,
ToastCard,
ToastPortal,
ToastProvider,
ToastViewport,
useToastManager,
} from '.'
import { Button } from '../button'
function ToastCards() {
const { toasts } = useToastManager<Record<string, never>>()
return toasts.map((item) => <ToastCard key={item.id} toast={item} />)
}
function ExampleToastHost({
manager,
timeout,
limit,
offset,
}: {
manager: ToastManager
timeout?: number
limit?: number
offset?: ToastViewportProps['offset']
}) {
return (
<ToastProvider toastManager={manager} timeout={timeout} limit={limit}>
<ToastPortal>
<ToastViewport offset={offset}>
<ToastCards />
</ToastViewport>
</ToastPortal>
</ToastProvider>
)
}
const manager = createToastManager()
const toast = createToast(manager)
const longToastTitle =
'operation error S3: PutObject, exceeded maximum number of attempts, 3, StatusCode: 0, RequestID: , HostID: , request send failed'
const longToastDescription =
@@ -329,7 +368,7 @@ const UpdateExamples = () => {
const ToastDocsDemo = () => {
return (
<React.Fragment>
<ToastHost />
<ExampleToastHost manager={manager} />
<div className="min-h-screen bg-background-default-subtle px-6 py-12">
<div className="mx-auto flex w-full max-w-6xl flex-col gap-8">
<div className="space-y-3">
+36 -27
View File
@@ -79,13 +79,23 @@ type ToastPromiseOptions<Value> = {
error: ToastPromiseResultOption<unknown>
}
type ToastHostProps = {
timeout?: number
limit?: number
manager?: ToastManager
offset?: ToastHostOffset
type ToastCardProps = {
toast: ToastObject<ToastData>
/** Additional content composed inside the card. */
children?: React.ReactNode
}
type ToastViewportProps = {
offset?: ToastHostOffset
children: React.ReactNode
}
const ToastProvider = BaseToast.Provider
type ToastProviderProps = BaseToast.Provider.Props
const ToastPortal = BaseToast.Portal
type ToastPortalProps = BaseToast.Portal.Props
const useToastManager = BaseToast.useToastManager
type ToastHostOffset = Pick<React.CSSProperties, 'top' | 'right'>
type ToastDismiss = (toastId?: string) => void
@@ -161,9 +171,6 @@ function createToastManager(): ToastManager {
return BaseToast.createToastManager<ToastData>()
}
const defaultToastManager = createToastManager()
const toast = createToast(defaultToastManager)
function ToastIcon({ type }: { type?: ToastRenderType }) {
return type ? (
<span aria-hidden="true" className={cn('h-5 w-5', TOAST_TONE_STYLES[type].iconClassName)} />
@@ -175,7 +182,7 @@ function getToneGradientClasses(type?: ToastRenderType) {
return 'from-background-default-subtle to-background-gradient-mask-transparent'
}
function ToastCard({ toast: toastItem }: { toast: ToastObject<ToastData> }) {
function ToastCard({ toast: toastItem, children }: ToastCardProps) {
const toastType = getToastRenderType(toastItem.type)
return (
@@ -183,7 +190,7 @@ function ToastCard({ toast: toastItem }: { toast: ToastObject<ToastData> }) {
toast={toastItem}
swipeDirection={['up', 'right']}
className={cn(
'pointer-events-auto absolute top-0 right-0 w-full origin-top cursor-default rounded-xl select-none focus-visible:ring-2 focus-visible:ring-state-accent-solid focus-visible:outline-hidden',
'group/toast pointer-events-auto absolute top-0 right-0 w-full origin-top cursor-default rounded-xl select-none focus-visible:ring-2 focus-visible:ring-state-accent-solid focus-visible:outline-hidden',
'[--toast-current-height:var(--toast-frontmost-height,var(--toast-height))] [--toast-expanded-offset-y:calc(var(--toast-offset-y)+var(--toast-swipe-movement-y)+(var(--toast-index)*var(--toast-gap)))] [--toast-gap:8px] [--toast-peek:5px] [--toast-scale:calc(1-(var(--toast-index)*0.0225))] [--toast-shrink:calc(1-var(--toast-scale))]',
'z-[calc(100-var(--toast-index))] h-(--toast-current-height)',
'[transition:transform_500ms_cubic-bezier(0.22,1,0.36,1),opacity_500ms,height_150ms] motion-reduce:transition-none',
@@ -232,6 +239,7 @@ function ToastCard({ toast: toastItem }: { toast: ToastObject<ToastData> }) {
</div>
)}
</div>
{children}
<div className="flex shrink-0 items-center justify-center rounded-md p-0.5">
<BaseToast.Close
aria-label={toastCloseLabel}
@@ -249,32 +257,33 @@ function ToastCard({ toast: toastItem }: { toast: ToastObject<ToastData> }) {
)
}
function ToastViewport({ offset }: { offset?: ToastHostOffset }) {
const { toasts } = BaseToast.useToastManager<ToastData>()
function ToastViewport({ offset, children }: ToastViewportProps) {
return (
<BaseToast.Viewport
aria-label={toastViewportLabel}
className="group/toast-viewport pointer-events-none fixed top-4 right-4 z-60 w-90 max-w-[calc(100vw-2rem)] overflow-visible sm:right-8"
style={offset}
>
{toasts.map((toastItem) => (
<ToastCard key={toastItem.id} toast={toastItem} />
))}
{children}
</BaseToast.Viewport>
)
}
function ToastHost({ timeout, limit, manager = defaultToastManager, offset }: ToastHostProps) {
return (
<BaseToast.Provider toastManager={manager} timeout={timeout} limit={limit}>
<BaseToast.Portal>
<ToastViewport offset={offset} />
</BaseToast.Portal>
</BaseToast.Provider>
)
export {
createToast,
createToastManager,
ToastCard,
ToastPortal,
ToastProvider,
ToastViewport,
useToastManager,
}
export { createToast, createToastManager, toast, ToastHost }
export type { ToastHostProps }
export type {
ToastApi,
ToastCardProps,
ToastManager,
ToastPortalProps,
ToastProviderProps,
ToastViewportProps,
}
+1
View File
@@ -4,6 +4,7 @@ const config: StorybookConfig = {
stories: [
'../app/components/**/*.stories.@(js|jsx|mjs|ts|tsx)',
'../features/**/*.stories.@(js|jsx|mjs|ts|tsx)',
'../app/notifications/**/*.stories.@(js|jsx|mjs|ts|tsx)',
],
addons: [
// Not working with Storybook Vite framework
+2 -2
View File
@@ -1,8 +1,8 @@
import type { Decorator, Preview } from '@storybook/react'
import type { Resource } from 'i18next'
import { ToastHost } from '@langgenius/dify-ui/toast'
import { withThemeByDataAttribute } from '@storybook/addon-themes'
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import { AppToastHost } from '@/app/notifications/host'
import { I18nClientProvider as I18N } from '../app/components/provider/i18n'
import commonEnUS from '../i18n/locales/en-US/common.json'
import '../app/styles/markdown.css'
@@ -38,7 +38,7 @@ export const decorators: Decorator[] = [
<QueryClientProvider client={queryClient}>
<I18N locale="en-US" resource={storyResources}>
<>
<ToastHost />
<AppToastHost />
<Story />
</>
</I18N>
@@ -9,12 +9,13 @@
*/
import type { GetBillingSubscriptionData } from '@dify/contracts/api/console/billing/types.gen'
import type { CloudPlan } from '@dify/contracts/api/console/features/types.gen'
import { toast, ToastHost } from '@langgenius/dify-ui/toast'
import { cleanup, screen, waitFor } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import * as React from 'react'
import { ALL_PLANS } from '@/app/components/billing/config'
import { CloudPlanItem } from '@/app/components/billing/pricing/plans/cloud-plan-item'
import { toast } from '@/app/notifications'
import { AppToastHost } from '@/app/notifications/host'
import { createConsoleQueryWrapper } from '@/test/console/query-data'
import { render } from '@/test/console/render'
@@ -86,7 +87,7 @@ const renderCloudPlanItem = ({
const { wrapper } = createConsoleQueryWrapper()
return render(
<>
<ToastHost timeout={0} />
<AppToastHost timeout={0} />
<CloudPlanItem
plan={plan}
billingInterval={billingInterval}
@@ -41,7 +41,7 @@ vi.mock('@/service/apps', () => ({
updateTracingStatus: vi.fn(),
}))
vi.mock('@langgenius/dify-ui/toast', () => ({
vi.mock('@/app/notifications', () => ({
toast: vi.fn(),
}))
@@ -10,9 +10,9 @@ import type {
TencentConfig,
WeaveConfig,
} from '../type'
import { toast } from '@langgenius/dify-ui/toast'
import { render, screen, waitFor } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import { toast } from '@/app/notifications'
import { addTracingConfig, removeTracingConfig, updateTracingConfig } from '@/service/apps'
import ConfigBtn from '../config-button'
import ProviderConfigModal from '../provider-config-modal'
@@ -24,7 +24,7 @@ vi.mock('@/service/apps', () => ({
updateTracingConfig: vi.fn(),
}))
vi.mock('@langgenius/dify-ui/toast', () => ({
vi.mock('@/app/notifications', () => ({
toast: vi.fn(),
}))
@@ -16,7 +16,6 @@ import type {
import type { TracingStatus } from '@/models/app'
import { cn } from '@langgenius/dify-ui/cn'
import { StatusDot } from '@langgenius/dify-ui/status-dot'
import { toast } from '@langgenius/dify-ui/toast'
import { useSuspenseQuery } from '@tanstack/react-query'
import { useBoolean } from 'ahooks'
import { useAtomValue } from 'jotai'
@@ -38,6 +37,7 @@ import {
WeaveIcon,
} from '@/app/components/base/icons/src/public/tracing'
import { LoadingPlaceholder } from '@/app/components/base/loading-placeholder'
import { toast } from '@/app/notifications'
import { workspacePermissionKeysAtom } from '@/context/permission-state'
import { userProfileQueryOptions } from '@/features/account-profile/client'
import { usePathname } from '@/next/navigation'
@@ -23,7 +23,6 @@ import {
} from '@langgenius/dify-ui/alert-dialog'
import { Button } from '@langgenius/dify-ui/button'
import { Dialog, DialogContent } from '@langgenius/dify-ui/dialog'
import { toast } from '@langgenius/dify-ui/toast'
import { useBoolean } from 'ahooks'
import * as React from 'react'
import { useCallback, useState } from 'react'
@@ -31,6 +30,7 @@ import { useTranslation } from 'react-i18next'
import Divider from '@/app/components/base/divider'
import { LinkExternal02 } from '@/app/components/base/icons/src/vender/line/general'
import { Lock01 } from '@/app/components/base/icons/src/vender/solid/security'
import { toast } from '@/app/notifications'
import { addTracingConfig, removeTracingConfig, updateTracingConfig } from '@/service/apps'
import { docURL } from './config'
import Field from './field'
@@ -1,12 +1,12 @@
'use client'
import { Button } from '@langgenius/dify-ui/button'
import { Input } from '@langgenius/dify-ui/input'
import { toast } from '@langgenius/dify-ui/toast'
import { RiArrowLeftLine, RiMailSendFill } from '@remixicon/react'
import { useState } from 'react'
import { useTranslation } from 'react-i18next'
import { useLocale } from '#i18n'
import Countdown from '@/app/components/signin/countdown'
import { toast } from '@/app/notifications'
import useDocumentTitle from '@/hooks/use-document-title'
import { useRouter, useSearchParams } from '@/next/navigation'
import { sendWebAppResetPasswordCode, verifyWebAppResetPasswordCode } from '@/service/common'
@@ -1,13 +1,13 @@
'use client'
import { Button } from '@langgenius/dify-ui/button'
import { Input } from '@langgenius/dify-ui/input'
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 { useLocale } from '#i18n'
import { COUNT_DOWN_TIME_MS, useSetCountdownLeftTime } from '@/app/components/signin/storage'
import { toast } from '@/app/notifications'
import { emailRegex } from '@/config'
import useDocumentTitle from '@/hooks/use-document-title'
import Link from '@/next/link'
@@ -5,11 +5,11 @@ import { Field, FieldDescription, FieldLabel } from '@langgenius/dify-ui/field'
import { Form } from '@langgenius/dify-ui/form'
import { IconButton } from '@langgenius/dify-ui/icon-button'
import { InputGroup, InputGroupAddon, InputGroupInput } from '@langgenius/dify-ui/input-group'
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 { toast } from '@/app/notifications'
import { validPassword } from '@/config'
import useDocumentTitle from '@/hooks/use-document-title'
import Link from '@/next/link'
@@ -2,7 +2,6 @@
import type { FormEvent } from 'react'
import { Button } from '@langgenius/dify-ui/button'
import { Input } from '@langgenius/dify-ui/input'
import { toast } from '@langgenius/dify-ui/toast'
import { useEffect, useRef, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { useLocale } from '#i18n'
@@ -11,6 +10,7 @@ import {
resolveWebAppLoginRedirect,
} from '@/app/(shareLayout)/webapp-signin/login-redirect'
import Countdown from '@/app/components/signin/countdown'
import { toast } from '@/app/notifications'
import { useWebAppStore } from '@/context/web-app-context'
import { useRouter, useSearchParams } from '@/next/navigation'
import { sendWebAppEMailLoginCode, webAppEmailLoginWithCode } from '@/service/common'
@@ -1,12 +1,12 @@
'use client'
import { zSsoProtocol } from '@dify/contracts/api/console/system-features/zod.gen'
import { toast } from '@langgenius/dify-ui/toast'
import { useSuspenseQuery } from '@tanstack/react-query'
import * as React from 'react'
import { useCallback, useEffect } from 'react'
import { resolveWebAppLoginRedirect } from '@/app/(shareLayout)/webapp-signin/login-redirect'
import AppUnavailable from '@/app/components/base/app-unavailable'
import { LoadingPlaceholder } from '@/app/components/base/loading-placeholder'
import { toast } from '@/app/notifications'
import { systemFeaturesQueryOptions } from '@/features/system-features/client'
import { useRouter, useSearchParams } from '@/next/navigation'
import { fetchWebOAuth2SSOUrl, fetchWebOIDCSSOUrl, fetchWebSAMLSSOUrl } from '@/service/share'
@@ -1,11 +1,11 @@
import { Button } from '@langgenius/dify-ui/button'
import { Input } from '@langgenius/dify-ui/input'
import { toast } from '@langgenius/dify-ui/toast'
import { useEffect, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { useLocale } from '#i18n'
import { resolveWebAppLoginRedirect } from '@/app/(shareLayout)/webapp-signin/login-redirect'
import { COUNT_DOWN_TIME_MS, useSetCountdownLeftTime } from '@/app/components/signin/storage'
import { toast } from '@/app/notifications'
import { emailRegex } from '@/config'
import { useRouter, useSearchParams } from '@/next/navigation'
import { sendWebAppEMailLoginCode } from '@/service/common'
@@ -5,13 +5,13 @@ import { Form } from '@langgenius/dify-ui/form'
import { IconButton } from '@langgenius/dify-ui/icon-button'
import { Input } from '@langgenius/dify-ui/input'
import { InputGroup, InputGroupAddon, InputGroupInput } from '@langgenius/dify-ui/input-group'
import { toast } from '@langgenius/dify-ui/toast'
import { useEffect, useState } from 'react'
import { useTranslation } from 'react-i18next'
import {
navigateAfterWebAppLogin,
resolveWebAppLoginRedirect,
} from '@/app/(shareLayout)/webapp-signin/login-redirect'
import { toast } from '@/app/notifications'
import { emailRegex } from '@/config'
import { useWebAppStore } from '@/context/web-app-context'
import Link from '@/next/link'
@@ -2,11 +2,11 @@
import type { SsoProtocol } from '@dify/contracts/api/console/system-features/types.gen'
import { zSsoProtocol } from '@dify/contracts/api/console/system-features/zod.gen'
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 { resolveWebAppLoginRedirect } from '@/app/(shareLayout)/webapp-signin/login-redirect'
import { Lock01 } from '@/app/components/base/icons/src/vender/solid/security'
import { toast } from '@/app/notifications'
import { useRouter, useSearchParams } from '@/next/navigation'
import {
fetchMembersOAuth2SSOUrl,
@@ -7,13 +7,13 @@ import type { ImageFile } from '@/types/app'
import { Avatar } from '@langgenius/dify-ui/avatar'
import { Button } from '@langgenius/dify-ui/button'
import { Dialog, DialogContent } from '@langgenius/dify-ui/dialog'
import { toast } from '@langgenius/dify-ui/toast'
import * as React from 'react'
import { useCallback, useState } from 'react'
import { useTranslation } from 'react-i18next'
import ImageInput from '@/app/components/base/app-icon-picker/ImageInput'
import Divider from '@/app/components/base/divider'
import { useLocalFileUploader } from '@/app/components/base/image-uploader/hooks'
import { toast } from '@/app/notifications'
import { DISABLE_UPLOAD_IMAGE_AS_ICON } from '@/config'
import { updateUserProfile } from '@/service/common'
import { createAvatarImageFile, createCroppedAvatarImage } from './avatar-image'
@@ -4,11 +4,11 @@ import { Dialog, DialogContent, DialogTitle } from '@langgenius/dify-ui/dialog'
import { Field, FieldError, FieldLabel } from '@langgenius/dify-ui/field'
import { IconButton } from '@langgenius/dify-ui/icon-button'
import { Input } from '@langgenius/dify-ui/input'
import { toast } from '@langgenius/dify-ui/toast'
import { RiCloseLine } from '@remixicon/react'
import { useDebounceFn } from 'ahooks'
import { useCallback, useEffect, useRef, useState } from 'react'
import { Trans, useTranslation } from 'react-i18next'
import { toast } from '@/app/notifications'
import { useRouter } from '@/next/navigation'
import { checkEmailExisted, resetEmail, sendVerifyCode, verifyEmail } from '@/service/common'
import { useLogout } from '@/service/use-common'
@@ -8,13 +8,13 @@ import { Field, FieldLabel } from '@langgenius/dify-ui/field'
import { IconButton } from '@langgenius/dify-ui/icon-button'
import { Input } from '@langgenius/dify-ui/input'
import { InputGroup, InputGroupAddon, InputGroupInput } from '@langgenius/dify-ui/input-group'
import { toast } from '@langgenius/dify-ui/toast'
import { useQuery, useQueryClient, useSuspenseQuery } from '@tanstack/react-query'
import { useId, useState } from 'react'
import { useTranslation } from 'react-i18next'
import AppIcon from '@/app/components/base/app-icon'
import PremiumBadge from '@/app/components/base/premium-badge'
import Collapse from '@/app/components/header/account-setting/collapse'
import { toast } from '@/app/notifications'
import { validPassword } from '@/config'
import { userProfileQueryOptions } from '@/features/account-profile/client'
import { systemFeaturesQueryOptions } from '@/features/system-features/client'
@@ -28,7 +28,7 @@ vi.mock('@/service/use-common', () => ({
useLogout: () => ({ mutateAsync: mockLogout }),
}))
vi.mock('@langgenius/dify-ui/toast', () => ({
vi.mock('@/app/notifications', () => ({
toast: { info: vi.fn() },
}))
@@ -4,10 +4,10 @@ import { Dialog, DialogContent, DialogTitle } from '@langgenius/dify-ui/dialog'
import { Field, FieldLabel } from '@langgenius/dify-ui/field'
import { Form } from '@langgenius/dify-ui/form'
import { Textarea } from '@langgenius/dify-ui/textarea'
import { toast } from '@langgenius/dify-ui/toast'
import { useSuspenseQuery } from '@tanstack/react-query'
import { useCallback } from 'react'
import { useTranslation } from 'react-i18next'
import { toast } from '@/app/notifications'
import { userProfileQueryOptions } from '@/features/account-profile/client'
import { useRouter } from '@/next/navigation'
import { useLogout } from '@/service/use-common'
+1 -1
View File
@@ -3,7 +3,6 @@
import { Avatar } from '@langgenius/dify-ui/avatar'
import { Button, buttonVariants } from '@langgenius/dify-ui/button'
import { cn } from '@langgenius/dify-ui/cn'
import { toast } from '@langgenius/dify-ui/toast'
import {
RiAccountCircleLine,
RiGlobalLine,
@@ -16,6 +15,7 @@ import * as React from 'react'
import { useTranslation } from 'react-i18next'
import { LoadingPlaceholder } from '@/app/components/base/loading-placeholder'
import { useLanguage } from '@/app/components/header/account-setting/model-provider-page/hooks'
import { toast } from '@/app/notifications'
import { isLegacyBase401, userProfileQueryOptions } from '@/features/account-profile/client'
import useDocumentTitle from '@/hooks/use-document-title'
import Link from '@/next/link'
@@ -1,10 +1,10 @@
import { toast } from '@langgenius/dify-ui/toast'
import Cookies from 'js-cookie'
import { useEffect, useRef, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { flushRegistrationSuccess } from '@/app/components/base/amplitude/registration-tracking'
import { flushEvents } from '@/app/components/base/amplitude/utils'
import { reportOAuthRegistrationIfNewUser } from '@/app/components/oauth-registration-attribution'
import { toast } from '@/app/notifications'
import { useRouter } from '@/next/navigation'
export function buildReturnUrl(pathname: string, search: string) {
@@ -66,7 +66,7 @@ vi.mock('@/app/components/app/use-export-app-dsl', () => ({
}),
}))
vi.mock('@langgenius/dify-ui/toast', () => ({
vi.mock('@/app/notifications', () => ({
toast: Object.assign(toastMocks.api, {
success: vi.fn((message, options) => toastMocks.call({ type: 'success', message, ...options })),
error: vi.fn((message, options) => toastMocks.call({ type: 'error', message, ...options })),
@@ -6,12 +6,12 @@ import type { Dispatch, SetStateAction } from 'react'
import type { DuplicateAppModalProps } from '@/app/components/app/duplicate-modal'
import type { CreateAppModalProps } from '@/app/components/explore/create-app-modal'
import type { App } from '@/types/app'
import { toast } from '@langgenius/dify-ui/toast'
import { useMutation, useQueryClient, useSuspenseQuery } from '@tanstack/react-query'
import { useCallback, useEffect, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { useStore as useAppStore } from '@/app/components/app/store'
import { useExportAppDsl, useExportWorkflowAppDsl } from '@/app/components/app/use-export-app-dsl'
import { toast } from '@/app/notifications'
import { systemFeaturesQueryOptions } from '@/features/system-features/client'
import { useRouter } from '@/next/navigation'
import {
@@ -129,7 +129,7 @@ vi.mock('@/service/datasets', () => ({
deleteDataset: (...args: unknown[]) => mockDeleteDataset(...args),
}))
vi.mock('@langgenius/dify-ui/toast', () => ({
vi.mock('@/app/notifications', () => ({
toast: {
error: (...args: unknown[]) => mockToast(...args),
},
@@ -17,12 +17,12 @@ import {
DropdownMenuTrigger,
} from '@langgenius/dify-ui/dropdown-menu'
import { IconButton } from '@langgenius/dify-ui/icon-button'
import { toast } from '@langgenius/dify-ui/toast'
import { useSuspenseQuery } from '@tanstack/react-query'
import { useAtomValue } from 'jotai'
import * as React from 'react'
import { useCallback, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { toast } from '@/app/notifications'
import { useDatasetDetailContextWithSelector } from '@/context/dataset-detail'
import { workspacePermissionKeysAtom } from '@/context/permission-state'
import { userProfileQueryOptions } from '@/features/account-profile/client'
@@ -37,7 +37,7 @@ vi.mock('@/utils/download', () => ({
downloadBlob: (args: { data: Blob; fileName: string }) => mockDownloadBlob(args),
}))
vi.mock('@langgenius/dify-ui/toast', () => ({
vi.mock('@/app/notifications', () => ({
toast: {
success: (...args: unknown[]) => mockToastSuccess(...args),
error: (...args: unknown[]) => mockToastError(...args),
@@ -18,7 +18,6 @@ import {
DropdownMenuTrigger,
} from '@langgenius/dify-ui/dropdown-menu'
import { IconButton } from '@langgenius/dify-ui/icon-button'
import { toast } from '@langgenius/dify-ui/toast'
import { useAtomValue } from 'jotai'
import * as React from 'react'
import { useTranslation } from 'react-i18next'
@@ -27,6 +26,7 @@ import {
canCreateAndModifySnippets,
canManageSnippets,
} from '@/app/components/snippets/utils/permission'
import { toast } from '@/app/notifications'
import { workspacePermissionKeysAtom } from '@/context/permission-state'
import { useRouter } from '@/next/navigation'
import {
@@ -34,7 +34,7 @@ vi.mock('@/utils/download', () => ({
downloadBlob: mocks.downloadBlob,
}))
vi.mock('@langgenius/dify-ui/toast', () => ({
vi.mock('@/app/notifications', () => ({
toast: {
error: mocks.toastError,
promise: mocks.toastPromise,
@@ -64,7 +64,7 @@ vi.mock('@/service/access-control', () => ({
}),
}))
vi.mock('@langgenius/dify-ui/toast', () => ({
vi.mock('@/app/notifications', () => ({
toast: {
error: vi.fn(),
success: vi.fn(),
@@ -1,8 +1,8 @@
import type { ReactElement } from 'react'
import { toast } from '@langgenius/dify-ui/toast'
import { QueryClientProvider } from '@tanstack/react-query'
import { screen, waitFor, within } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import { toast } from '@/app/notifications'
import { render } from '@/test/console/render'
import { createTestQueryClient } from '@/test/query-client'
import { EnvironmentServiceApiCard } from '../deployed-environment-access-points/environment-service-api-card'
@@ -105,7 +105,7 @@ vi.mock('@/app/components/app/access-point/shared/api-secret-key-button', () =>
},
}))
vi.mock('@langgenius/dify-ui/toast', () => ({
vi.mock('@/app/notifications', () => ({
toast: {
error: vi.fn(),
success: vi.fn(),
@@ -1,9 +1,9 @@
import type { AccessPointAppInfo, PublishedWorkflow } from '../shared/utils'
import { toast } from '@langgenius/dify-ui/toast'
import { QueryClientProvider } from '@tanstack/react-query'
import { screen, waitFor } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import { BlockEnum } from '@/app/components/workflow/types'
import { toast } from '@/app/notifications'
import { render } from '@/test/console/render'
import { createTestQueryClient } from '@/test/query-client'
import { AppModeEnum } from '@/types/app'
@@ -20,7 +20,7 @@ const mocks = vi.hoisted(() => ({
updateServer: vi.fn(),
}))
vi.mock('@langgenius/dify-ui/toast', () => ({
vi.mock('@/app/notifications', () => ({
toast: {
error: vi.fn(),
success: vi.fn(),
@@ -1,9 +1,9 @@
import type { AccessPointAppInfo } from '../shared/utils'
import { toast } from '@langgenius/dify-ui/toast'
import { QueryClientProvider } from '@tanstack/react-query'
import { screen, waitFor } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import { useStore as useAppStore } from '@/app/components/app/store'
import { toast } from '@/app/notifications'
import { render } from '@/test/console/render'
import { createTestQueryClient } from '@/test/query-client'
import { AppModeEnum } from '@/types/app'
@@ -14,7 +14,7 @@ const mocks = vi.hoisted(() => ({
apiEnable: vi.fn(),
}))
vi.mock('@langgenius/dify-ui/toast', () => ({
vi.mock('@/app/notifications', () => ({
toast: {
error: vi.fn(),
success: vi.fn(),
@@ -1,9 +1,9 @@
import type { AccessPointAppInfo } from '../shared/utils'
import type { AppTrigger } from '@/service/use-tools'
import { toast } from '@langgenius/dify-ui/toast'
import { QueryClientProvider } from '@tanstack/react-query'
import { screen, waitFor } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import { toast } from '@/app/notifications'
import { render } from '@/test/console/render'
import { createTestQueryClient } from '@/test/query-client'
import { AppModeEnum } from '@/types/app'
@@ -19,7 +19,7 @@ const mocks = vi.hoisted(() => ({
updateTriggerStatus: vi.fn(),
}))
vi.mock('@langgenius/dify-ui/toast', () => ({
vi.mock('@/app/notifications', () => ({
toast: {
error: vi.fn(),
success: vi.fn(),
@@ -11,7 +11,7 @@ const mocks = vi.hoisted(() => ({
updateAppSiteConfig: vi.fn().mockResolvedValue({}),
}))
vi.mock('@langgenius/dify-ui/toast', () => ({ toast: mocks.toast }))
vi.mock('@/app/notifications', () => ({ toast: mocks.toast }))
vi.mock('@/app/components/app/store', () => ({
useStore: (selector: (state: { setAppDetail: typeof mocks.setAppDetail }) => unknown) =>
@@ -1,11 +1,11 @@
import type { AccessPointAppInfo, PublishedWorkflow } from '../shared/utils'
import type { InputVar, Node } from '@/app/components/workflow/types'
import { toast } from '@langgenius/dify-ui/toast'
import { QueryClientProvider } from '@tanstack/react-query'
import { screen, waitFor, within } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import { useStore as useAppStore } from '@/app/components/app/store'
import { BlockEnum, InputVarType } from '@/app/components/workflow/types'
import { toast } from '@/app/notifications'
import { AccessMode } from '@/models/access-control'
import { render } from '@/test/console/render'
import { createTestQueryClient } from '@/test/query-client'
@@ -18,7 +18,7 @@ const mocks = vi.hoisted(() => ({
resetSiteAccessToken: vi.fn().mockResolvedValue({}),
}))
vi.mock('@langgenius/dify-ui/toast', () => ({
vi.mock('@/app/notifications', () => ({
toast: {
error: vi.fn(),
success: vi.fn(),
@@ -11,7 +11,6 @@ import {
AlertDialogTitle,
} from '@langgenius/dify-ui/alert-dialog'
import { Button } from '@langgenius/dify-ui/button'
import { toast } from '@langgenius/dify-ui/toast'
import { useMutation } from '@tanstack/react-query'
import { useMemo, useState } from 'react'
import { useTranslation } from 'react-i18next'
@@ -19,6 +18,7 @@ import { AccessPointCard } from '@/app/components/base/access-point/card'
import { AccessPointUrl } from '@/app/components/base/access-point/url'
import MCPServerModal from '@/app/components/tools/mcp/mcp-server-modal'
import { BlockEnum } from '@/app/components/workflow/types'
import { toast } from '@/app/notifications'
import { consoleQuery } from '@/service/console'
import {
useInvalidateMCPServerDetail,
@@ -2,11 +2,11 @@
import type { AccessPointAppInfo } from '../shared/utils'
import type { AccessPointAvailability } from '@/app/components/base/access-point/status'
import { toast } from '@langgenius/dify-ui/toast'
import { useMutation } from '@tanstack/react-query'
import { useTranslation } from 'react-i18next'
import { useStore as useAppStore } from '@/app/components/app/store'
import { getAccessPointStatus } from '@/app/components/base/access-point/status'
import { toast } from '@/app/notifications'
import { consoleQuery } from '@/service/console'
import { ServiceApiCardView } from '../shared/service-api-card-view'
import { getBuiltInAccessUrls } from '../shared/utils'
@@ -5,7 +5,6 @@ import type { TriggerWithProvider } from '@/app/components/workflow/block-select
import type { AppTrigger } from '@/service/use-tools'
import { StatusDot } from '@langgenius/dify-ui/status-dot'
import { Switch } from '@langgenius/dify-ui/switch'
import { toast } from '@langgenius/dify-ui/toast'
import { useMutation } from '@tanstack/react-query'
import { useEffect, useMemo } from 'react'
import { useTranslation } from 'react-i18next'
@@ -13,6 +12,7 @@ import { AccessPointCard, AccessPointEmptyContent } from '@/app/components/base/
import BlockIcon from '@/app/components/workflow/block-icon'
import { useTriggerStatusStore } from '@/app/components/workflow/store/trigger-status'
import { BlockEnum } from '@/app/components/workflow/types'
import { toast } from '@/app/notifications'
import { useDocLink } from '@/context/i18n'
import Link from '@/next/link'
import { consoleQuery } from '@/service/console'
@@ -14,7 +14,6 @@ import {
AlertDialogTitle,
} from '@langgenius/dify-ui/alert-dialog'
import { Button } from '@langgenius/dify-ui/button'
import { toast } from '@langgenius/dify-ui/toast'
import { useMutation } from '@tanstack/react-query'
import { useState } from 'react'
import { useTranslation } from 'react-i18next'
@@ -28,6 +27,7 @@ import { AccessPointCard } from '@/app/components/base/access-point/card'
import { getAccessPointStatus } from '@/app/components/base/access-point/status'
import { AccessPointUrl } from '@/app/components/base/access-point/url'
import AppIcon from '@/app/components/base/app-icon'
import { toast } from '@/app/notifications'
import { AccessMode } from '@/models/access-control'
import { useAppWhiteListSubjects } from '@/service/access-control/use-app-access-control'
import { consoleQuery } from '@/service/console'
@@ -6,11 +6,11 @@ import type {
AccessControlSubjectsStatus,
} from '@/app/components/app/app-access-control/specific-groups-or-members'
import type { AccessControlAccount, AccessControlGroup } from '@/models/access-control'
import { toast } from '@langgenius/dify-ui/toast'
import { useMutation, useQuery, useQueryClient, useSuspenseQuery } from '@tanstack/react-query'
import { useState } from 'react'
import { useTranslation } from 'react-i18next'
import { AccessControlForm } from '@/app/components/app/app-access-control/access-control-form'
import { toast } from '@/app/notifications'
import { systemFeaturesQueryOptions } from '@/features/system-features/client'
import { AccessMode, SubjectType } from '@/models/access-control'
import { consoleQuery } from '@/service/console'
@@ -1,9 +1,9 @@
'use client'
import { toast } from '@langgenius/dify-ui/toast'
import { useMutation, useQuery } from '@tanstack/react-query'
import { useTranslation } from 'react-i18next'
import { useStore as useAppStore } from '@/app/components/app/store'
import { toast } from '@/app/notifications'
import { consoleQuery } from '@/service/console'
import { ServiceApiCardView } from '../shared/service-api-card-view'
@@ -11,7 +11,6 @@ import {
AlertDialogTitle,
} from '@langgenius/dify-ui/alert-dialog'
import { Button } from '@langgenius/dify-ui/button'
import { toast } from '@langgenius/dify-ui/toast'
import { useMutation, useQuery, useQueryClient, useSuspenseQuery } from '@tanstack/react-query'
import { useState } from 'react'
import { useTranslation } from 'react-i18next'
@@ -21,6 +20,7 @@ import { useStore as useAppStore } from '@/app/components/app/store'
import { AccessPointCard } from '@/app/components/base/access-point/card'
import { AccessPointUrl } from '@/app/components/base/access-point/url'
import AppIcon from '@/app/components/base/app-icon'
import { toast } from '@/app/notifications'
import { systemFeaturesQueryOptions } from '@/features/system-features/client'
import { AccessMode, isAccessMode } from '@/models/access-control'
import { consoleQuery } from '@/service/console'
@@ -3,11 +3,11 @@
import type { ConfigParams } from '@/app/components/app/overview/settings'
import type { App } from '@/types/app'
import type { I18nKeysByPrefix } from '@/types/i18n'
import { toast } from '@langgenius/dify-ui/toast'
import { useQueryClient } from '@tanstack/react-query'
import { useCallback } from 'react'
import { useTranslation } from 'react-i18next'
import { useStore as useAppStore } from '@/app/components/app/store'
import { toast } from '@/app/notifications'
import { fetchAppDetail, updateAppSiteConfig } from '@/service/apps'
import { consoleQuery } from '@/service/console'
import { asyncRunSafe } from '@/utils'
@@ -2,9 +2,9 @@ import type { ReactElement } from 'react'
import type { Mock } from 'vite-plus/test'
import type { AnnotationItem } from '../type'
import type { App } from '@/types/app'
import { toast } from '@langgenius/dify-ui/toast'
import { act, fireEvent, screen, waitFor } from '@testing-library/react'
import * as React from 'react'
import { toast } from '@/app/notifications'
import {
addAnnotation,
delAnnotation,
@@ -5,7 +5,7 @@ import { renderWithConsoleQuery } from '@/test/console/query-data'
import AddAnnotationModal from '../index'
const mockToastNotify = vi.fn()
vi.mock('@langgenius/dify-ui/toast', () => ({
vi.mock('@/app/notifications', () => ({
default: {
notify: vi.fn((args) => mockToastNotify(args)),
},
@@ -13,13 +13,13 @@ import {
DrawerTitle,
DrawerViewport,
} from '@langgenius/dify-ui/drawer'
import { toast } from '@langgenius/dify-ui/toast'
import { useQuery } from '@tanstack/react-query'
import { useAtomValue } from 'jotai'
import * as React from 'react'
import { useState } from 'react'
import { useTranslation } from 'react-i18next'
import AnnotationFull from '@/app/components/billing/annotation-full'
import { toast } from '@/app/notifications'
import { deploymentEditionAtom } from '@/features/system-features/state'
import { consoleQuery } from '@/service/console'
import EditItem, { EditItemType } from './edit-item'
@@ -10,7 +10,7 @@ const toastMocks = vi.hoisted(() => ({
promise: vi.fn(),
}))
vi.mock('@langgenius/dify-ui/toast', () => ({
vi.mock('@/app/notifications', () => ({
toast: {
success: (message: string, options?: Record<string, unknown>) =>
toastMocks.notify({ type: 'success', message, ...options }),
@@ -42,7 +42,7 @@ vi.mock('@/app/components/billing/annotation-full', () => ({
}))
const mockNotify = vi.fn()
vi.mock('@langgenius/dify-ui/toast', () => ({
vi.mock('@/app/notifications', () => ({
default: {
notify: (args: unknown) => mockNotify(args),
},
@@ -2,12 +2,12 @@
import type { FC } from 'react'
import { Button } from '@langgenius/dify-ui/button'
import { cn } from '@langgenius/dify-ui/cn'
import { toast } from '@langgenius/dify-ui/toast'
import { RiDeleteBinLine } from '@remixicon/react'
import * as React from 'react'
import { useEffect, useRef, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { Csv as CSVIcon } from '@/app/components/base/icons/src/public/files'
import { toast } from '@/app/notifications'
export type Props = Readonly<{
file: File | undefined
@@ -2,7 +2,6 @@
import type { FC } from 'react'
import { Button } from '@langgenius/dify-ui/button'
import { Dialog, DialogContent } from '@langgenius/dify-ui/dialog'
import { toast } from '@langgenius/dify-ui/toast'
import { RiCloseLine } from '@remixicon/react'
import { useQuery } from '@tanstack/react-query'
import { useAtomValue } from 'jotai'
@@ -10,6 +9,7 @@ import * as React from 'react'
import { useEffect, useState } from 'react'
import { useTranslation } from 'react-i18next'
import AnnotationFull from '@/app/components/billing/annotation-full'
import { toast } from '@/app/notifications'
import { deploymentEditionAtom } from '@/features/system-features/state'
import { annotationBatchImport, checkAnnotationBatchImportProgress } from '@/service/annotation'
import { consoleQuery } from '@/service/console'
@@ -1,6 +1,6 @@
import { toast } from '@langgenius/dify-ui/toast'
import { render, screen, waitFor } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import { toast } from '@/app/notifications'
import EditAnnotationModal from '../index'
const { mockEditAnnotation } = vi.hoisted(() => ({
@@ -18,11 +18,11 @@ import {
DrawerTitle,
DrawerViewport,
} from '@langgenius/dify-ui/drawer'
import { toast } from '@langgenius/dify-ui/toast'
import * as React from 'react'
import { useState } from 'react'
import { useTranslation } from 'react-i18next'
import { MessageCheckRemove } from '@/app/components/base/icons/src/vender/line/communication'
import { toast } from '@/app/notifications'
import useTimestamp from '@/hooks/use-timestamp'
import { editAnnotation } from '@/service/annotation'
import EditItem, { EditItemType } from './edit-item'
+1 -1
View File
@@ -8,7 +8,6 @@ import { cn } from '@langgenius/dify-ui/cn'
import { IconButton } from '@langgenius/dify-ui/icon-button'
import { Pagination } from '@langgenius/dify-ui/pagination'
import { Switch } from '@langgenius/dify-ui/switch'
import { toast } from '@langgenius/dify-ui/toast'
import { RiEqualizer2Line } from '@remixicon/react'
import { useQuery } from '@tanstack/react-query'
import { useDebounce } from 'ahooks'
@@ -20,6 +19,7 @@ import ConfigParamModal from '@/app/components/base/features/new-feature-panel/a
import { MessageFast } from '@/app/components/base/icons/src/vender/solid/communication'
import { LoadingPlaceholder } from '@/app/components/base/loading-placeholder'
import AnnotationFullModal from '@/app/components/billing/annotation-full/modal'
import { toast } from '@/app/notifications'
import { APP_PAGE_LIMIT } from '@/config'
import { useDocLink } from '@/context/i18n'
import { deploymentEditionAtom } from '@/features/system-features/state'
@@ -1,8 +1,8 @@
import type { ReactElement } from 'react'
import type { App } from '@/types/app'
import { toast } from '@langgenius/dify-ui/toast'
import { screen, waitFor } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import { toast } from '@/app/notifications'
import { AccessMode } from '@/models/access-control'
import { renderWithConsoleQuery } from '@/test/console/query-data'
import AccessControl from '../index'
@@ -6,10 +6,10 @@ import type {
AccessControlSubjectsStatus,
} from './specific-groups-or-members'
import type { Subject } from '@/models/access-control'
import { toast } from '@langgenius/dify-ui/toast'
import { useMutation, useSuspenseQuery } from '@tanstack/react-query'
import { useState } from 'react'
import { useTranslation } from 'react-i18next'
import { toast } from '@/app/notifications'
import { systemFeaturesQueryOptions } from '@/features/system-features/client'
import { AccessMode, isAccessMode, SubjectType } from '@/models/access-control'
import { useAppWhiteListSubjects } from '@/service/access-control'
@@ -160,7 +160,7 @@ vi.mock('@/context/permission-state', async () => {
}))
})
vi.mock('@langgenius/dify-ui/toast', () => ({
vi.mock('@/app/notifications', () => ({
toast: {
error: (...args: unknown[]) => mockToastError(...args),
success: (...args: unknown[]) => mockToastSuccess(...args),
@@ -1,8 +1,8 @@
import { toast } from '@langgenius/dify-ui/toast'
import { fireEvent, render, screen } from '@testing-library/react'
import { toast } from '@/app/notifications'
import VersionInfoModal from '../version-info-modal'
vi.mock('@langgenius/dify-ui/toast', () => ({
vi.mock('@/app/notifications', () => ({
toast: {
error: vi.fn(),
},
@@ -1,6 +1,6 @@
import { toast } from '@langgenius/dify-ui/toast'
import { useState } from 'react'
import { useTranslation } from 'react-i18next'
import { toast } from '@/app/notifications'
import { publishToCreatorsPlatform } from '@/service/apps'
export function useMarketplacePublish(appId?: string) {
@@ -1,8 +1,8 @@
import type { WorkflowResponse } from '@dify/contracts/api/console/apps/types.gen'
import type { AppModeEnum } from '@/types/app'
import { toast } from '@langgenius/dify-ui/toast'
import { useState } from 'react'
import { useTranslation } from 'react-i18next'
import { toast } from '@/app/notifications'
import { useInvalidateAppWorkflow, useUpdateWorkflow } from '@/service/use-workflow'
type VersionInfoUpdate = {
@@ -5,9 +5,9 @@ import { Dialog, DialogContent } from '@langgenius/dify-ui/dialog'
import { Field, FieldLabel } from '@langgenius/dify-ui/field'
import { Input } from '@langgenius/dify-ui/input'
import { Textarea } from '@langgenius/dify-ui/textarea'
import { toast } from '@langgenius/dify-ui/toast'
import { useCallback, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { toast } from '@/app/notifications'
type VersionInfoModalProps = {
isOpen: boolean
@@ -1,5 +1,5 @@
import { ToastHost } from '@langgenius/dify-ui/toast'
import { act, render, screen } from '@testing-library/react'
import { AppToastHost } from '@/app/notifications/host'
import Configuration from '../index'
import { toast } from '../toast'
@@ -23,7 +23,7 @@ describe('Configuration', () => {
it('should render configuration notifications in its offset viewport', async () => {
render(
<>
<ToastHost />
<AppToastHost />
<Configuration />
</>,
)
@@ -1,6 +1,6 @@
'use client'
import { ToastHost } from '@langgenius/dify-ui/toast'
import * as React from 'react'
import { AppToastHost } from '@/app/notifications/host'
import ConfigurationView from './configuration-view'
import { useConfiguration } from './hooks/use-configuration'
import { appConfigurationToastManager } from './toast'
@@ -9,7 +9,7 @@ const Configuration = () => {
const viewModel = useConfiguration()
return (
<>
<ToastHost manager={appConfigurationToastManager} offset={{ top: 60 }} />
<AppToastHost manager={appConfigurationToastManager} offset={{ top: 60 }} />
<ConfigurationView {...viewModel} />
</>
)
@@ -2,7 +2,5 @@
import { createToast, createToastManager } from '@langgenius/dify-ui/toast'
const appConfigurationToastManager = createToastManager()
const toast = createToast(appConfigurationToastManager)
export { appConfigurationToastManager, toast }
export const appConfigurationToastManager = createToastManager()
export const toast = createToast(appConfigurationToastManager)
@@ -122,7 +122,7 @@ vi.mock('@/app/components/explore/create-app-modal', () => ({
</div>
) : null,
}))
vi.mock('@langgenius/dify-ui/toast', () => ({
vi.mock('@/app/notifications', () => ({
toast: {
success: (...args: unknown[]) => mockToastSuccess(...args),
error: (...args: unknown[]) => mockToastError(...args),
@@ -5,7 +5,6 @@ import type { App } from '@/models/explore'
import { cn } from '@langgenius/dify-ui/cn'
import { IconButton } from '@langgenius/dify-ui/icon-button'
import { InputGroup, InputGroupAddon, InputGroupInput } from '@langgenius/dify-ui/input-group'
import { toast } from '@langgenius/dify-ui/toast'
import { useMutation, useSuspenseQuery } from '@tanstack/react-query'
import { useDebouncedValue } from 'foxact/use-debounced-value'
import { useAtomValue } from 'jotai'
@@ -17,6 +16,7 @@ import Divider from '@/app/components/base/divider'
import { LoadingPlaceholder } from '@/app/components/base/loading-placeholder'
import CreateAppModal from '@/app/components/explore/create-app-modal'
import { usePluginDependencies } from '@/app/components/workflow/plugin-dependency/hooks'
import { toast } from '@/app/notifications'
import { workspacePermissionKeysAtom } from '@/context/permission-state'
import { userProfileQueryOptions } from '@/features/account-profile/client'
import { systemFeaturesQueryOptions } from '@/features/system-features/client'
@@ -73,7 +73,7 @@ const toastMocks = vi.hoisted(() => ({
mockToastSuccess: vi.fn(),
mockToastError: vi.fn(),
}))
vi.mock('@langgenius/dify-ui/toast', () => ({
vi.mock('@/app/notifications', () => ({
toast: {
success: toastMocks.mockToastSuccess,
error: toastMocks.mockToastError,
@@ -8,7 +8,6 @@ import { cn } from '@langgenius/dify-ui/cn'
import { Input } from '@langgenius/dify-ui/input'
import { Kbd, KbdGroup } from '@langgenius/dify-ui/kbd'
import { Textarea } from '@langgenius/dify-ui/textarea'
import { toast } from '@langgenius/dify-ui/toast'
import { formatForDisplay, useHotkey } from '@tanstack/react-hotkeys'
import { useMutation, useQuery, useSuspenseQuery } from '@tanstack/react-query'
import { useDebounceFn } from 'ahooks'
@@ -18,6 +17,7 @@ import { useTranslation } from 'react-i18next'
import AppIcon from '@/app/components/base/app-icon'
import Divider from '@/app/components/base/divider'
import AppsFull from '@/app/components/billing/apps-full-in-dialog'
import { toast } from '@/app/notifications'
import { workspacePermissionKeysAtom } from '@/context/permission-state'
import { userProfileQueryOptions } from '@/features/account-profile/client'
import { systemFeaturesQueryOptions } from '@/features/system-features/client'
@@ -132,7 +132,7 @@ vi.mock('@/utils/imported-app-redirection', () => ({
mockResolveImportedAppRedirectionTarget(target),
}))
vi.mock('@langgenius/dify-ui/toast', () => ({
vi.mock('@/app/notifications', () => ({
toast: Object.assign((...args: unknown[]) => toastMocks.call(...args), {
success: (...args: unknown[]) => toastMocks.success(...args),
error: (...args: unknown[]) => toastMocks.error(...args),
@@ -1,10 +1,10 @@
import { toast } from '@langgenius/dify-ui/toast'
import { fireEvent, render, screen } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import { useState } from 'react'
import { toast } from '@/app/notifications'
import { Uploader } from '../uploader'
vi.mock('@langgenius/dify-ui/toast', () => ({
vi.mock('@/app/notifications', () => ({
toast: {
error: vi.fn(),
},
@@ -17,7 +17,6 @@ import { IconButton } from '@langgenius/dify-ui/icon-button'
import { Input } from '@langgenius/dify-ui/input'
import { Kbd, KbdGroup } from '@langgenius/dify-ui/kbd'
import { Tabs, TabsList, TabsPanel, TabsTab } from '@langgenius/dify-ui/tabs'
import { toast } from '@langgenius/dify-ui/toast'
import { formatForDisplay, useHotkey } from '@tanstack/react-hotkeys'
import { useMutation, useQuery, useSuspenseQuery } from '@tanstack/react-query'
import { useAtomValue } from 'jotai'
@@ -25,6 +24,7 @@ import { useRef, useState } from 'react'
import { useTranslation } from 'react-i18next'
import AppsFull from '@/app/components/billing/apps-full-in-dialog'
import { usePluginDependencies } from '@/app/components/workflow/plugin-dependency/hooks'
import { toast } from '@/app/notifications'
import { workspacePermissionKeysAtom } from '@/context/permission-state'
import { userProfileQueryOptions } from '@/features/account-profile/client'
import { systemFeaturesQueryOptions } from '@/features/system-features/client'
@@ -3,9 +3,9 @@ import type { ChangeEvent, DragEvent, MouseEvent, RefObject } from 'react'
import { Button } from '@langgenius/dify-ui/button'
import { cn } from '@langgenius/dify-ui/cn'
import { IconButton } from '@langgenius/dify-ui/icon-button'
import { toast } from '@langgenius/dify-ui/toast'
import { useId, useLayoutEffect, useRef, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { toast } from '@/app/notifications'
import { formatFileSize } from '@/utils/format'
type Props = Readonly<{
@@ -19,9 +19,9 @@ import {
PluginCategory,
RuntimeState,
} from '@dify/contracts/enterprise-app-deploy/types.gen'
import { toast } from '@langgenius/dify-ui/toast'
import { act, screen, waitFor, within } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import { toast } from '@/app/notifications'
import { consoleQuery } from '@/service/console'
import {
appWorkflowQueryOptions,
@@ -852,7 +852,7 @@ vi.mock('#i18n', async (importOriginal) => ({
useLocale: () => 'en-US',
}))
vi.mock('@langgenius/dify-ui/toast', () => ({
vi.mock('@/app/notifications', () => ({
toast: {
error: vi.fn(),
},
@@ -12,10 +12,10 @@ import {
DropdownMenuTrigger,
} from '@langgenius/dify-ui/dropdown-menu'
import { IconButton } from '@langgenius/dify-ui/icon-button'
import { toast } from '@langgenius/dify-ui/toast'
import { Fragment, useCallback, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { getWorkflowVersionName } from '@/app/components/workflow/utils/version'
import { toast } from '@/app/notifications'
import { getEnvironmentDeploymentActions } from '../utils/environment-deployment'
import { UndeployConfirmDialog } from './undeploy-confirm-dialog'
@@ -1,10 +1,10 @@
'use client'
import type { EnvironmentDeployment } from '@dify/contracts/enterprise-app-deploy/types.gen'
import { toast } from '@langgenius/dify-ui/toast'
import { useMutation } from '@tanstack/react-query'
import { useCallback } from 'react'
import { useTranslation } from 'react-i18next'
import { toast } from '@/app/notifications'
import { consoleQuery } from '@/service/console'
export function useUndeployWorkflow(appId: string) {
@@ -3,8 +3,8 @@
import type { FormEventHandler } from 'react'
import type { DeploymentConfigurationQueryState } from './use-deployment-configuration-queries'
import type { DeploymentConfigurationValuesController } from './use-deployment-configuration-values'
import { toast } from '@langgenius/dify-ui/toast'
import { useTranslation } from 'react-i18next'
import { toast } from '@/app/notifications'
import { useDeployWorkflow } from '../../hooks/use-deploy-workflow'
import {
credentialProviderName,
@@ -14,7 +14,7 @@ const { mockAppQuota, toastErrorMock } = vi.hoisted(() => ({
toastErrorMock: vi.fn(),
}))
vi.mock('@langgenius/dify-ui/toast', () => ({
vi.mock('@/app/notifications', () => ({
toast: {
error: (...args: unknown[]) => toastErrorMock(...args),
},
@@ -5,7 +5,6 @@ import { Dialog, DialogContent, DialogTitle } from '@langgenius/dify-ui/dialog'
import { Field, FieldLabel } from '@langgenius/dify-ui/field'
import { IconButton } from '@langgenius/dify-ui/icon-button'
import { Input } from '@langgenius/dify-ui/input'
import { toast } from '@langgenius/dify-ui/toast'
import { useQuery } from '@tanstack/react-query'
import { useAtomValue } from 'jotai'
import * as React from 'react'
@@ -13,6 +12,7 @@ import { useState } from 'react'
import { useTranslation } from 'react-i18next'
import AppIcon from '@/app/components/base/app-icon'
import AppsFull from '@/app/components/billing/apps-full-in-dialog'
import { toast } from '@/app/notifications'
import { deploymentEditionAtom } from '@/features/system-features/state'
import { consoleQuery } from '@/service/console'
import AppIconPicker from '../../base/app-icon-picker'
+1 -1
View File
@@ -30,7 +30,6 @@ import {
} from '@langgenius/dify-ui/drawer'
import { IconButton } from '@langgenius/dify-ui/icon-button'
import { StatusDot } from '@langgenius/dify-ui/status-dot'
import { toast } from '@langgenius/dify-ui/toast'
import { Tooltip, TooltipContent, TooltipTrigger } from '@langgenius/dify-ui/tooltip'
import { RiCloseLine, RiEditFill } from '@remixicon/react'
import { useQuery } from '@tanstack/react-query'
@@ -52,6 +51,7 @@ import CopyIcon from '@/app/components/base/copy-icon'
import { LoadingPlaceholder } from '@/app/components/base/loading-placeholder'
import MessageLogModal from '@/app/components/base/message-log-modal'
import { WorkflowContextProvider } from '@/app/components/workflow/context'
import { toast } from '@/app/notifications'
import { userProfileQueryOptions } from '@/features/account-profile/client'
import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints'
import useTimestamp from '@/hooks/use-timestamp'
@@ -44,7 +44,7 @@ const toastMocks = vi.hoisted(() => ({
promise: vi.fn(),
}))
vi.mock('@langgenius/dify-ui/toast', () => ({
vi.mock('@/app/notifications', () => ({
toast: Object.assign(toastMocks.call, {
success: vi.fn((message: string, options?: Record<string, unknown>) =>
toastMocks.call({ type: 'success', message, ...options }),
@@ -27,7 +27,6 @@ import {
} from '@langgenius/dify-ui/select'
import { Switch } from '@langgenius/dify-ui/switch'
import { Textarea } from '@langgenius/dify-ui/textarea'
import { toast } from '@langgenius/dify-ui/toast'
import { Tooltip, TooltipContent, TooltipTrigger } from '@langgenius/dify-ui/tooltip'
import { useQuery } from '@tanstack/react-query'
import { useAtomValue } from 'jotai'
@@ -43,6 +42,7 @@ import {
pricingQueryParamName,
pricingQueryParser,
} from '@/app/components/billing/pricing/query-params'
import { toast } from '@/app/notifications'
import { deploymentEditionAtom } from '@/features/system-features/state'
import { languages } from '@/i18n/language'
import Link from '@/next/link'
@@ -102,7 +102,7 @@ const toastMocks = vi.hoisted(() => ({
promise: vi.fn(),
}))
vi.mock('@langgenius/dify-ui/toast', () => ({
vi.mock('@/app/notifications', () => ({
toast: {
success: (message: string, options?: Record<string, unknown>) =>
toastMocks.notify({ type: 'success', message, ...options }),
@@ -16,13 +16,13 @@ import { Checkbox } from '@langgenius/dify-ui/checkbox'
import { cn } from '@langgenius/dify-ui/cn'
import { Dialog, DialogContent } from '@langgenius/dify-ui/dialog'
import { Input } from '@langgenius/dify-ui/input'
import { toast } from '@langgenius/dify-ui/toast'
import { useMutation, useQuery, useSuspenseQuery } from '@tanstack/react-query'
import { useId, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { useStore as useAppStore } from '@/app/components/app/store'
import AppIcon from '@/app/components/base/app-icon'
import AppsFull from '@/app/components/billing/apps-full-in-dialog'
import { toast } from '@/app/notifications'
import { systemFeaturesQueryOptions } from '@/features/system-features/client'
import { useRouter } from '@/next/navigation'
import { consoleQuery } from '@/service/console'
@@ -14,7 +14,7 @@ vi.mock('copy-to-clipboard', () => ({
default: (...args: unknown[]) => mockCopy(...args),
}))
vi.mock('@langgenius/dify-ui/toast', () => ({
vi.mock('@/app/notifications', () => ({
toast: {
success: (...args: unknown[]) => mockSuccess(...args),
},
@@ -56,7 +56,7 @@ vi.mock('@/app/components/base/markdown', () => ({
Markdown: ({ content }: { content: string }) => <div>{`markdown:${content}`}</div>,
}))
vi.mock('@langgenius/dify-ui/toast', () => ({
vi.mock('@/app/notifications', () => ({
toast: {
warning: (...args: unknown[]) => mockToastWarning(...args),
success: vi.fn(),
@@ -4,11 +4,11 @@ import type { FeedbackType } from '@/app/components/base/chat/chat/type'
import type { WorkflowProcess } from '@/app/components/base/chat/types'
import type { AppSourceType } from '@/service/share'
import { IconButton } from '@langgenius/dify-ui/icon-button'
import { toast } from '@langgenius/dify-ui/toast'
import { Toggle } from '@langgenius/dify-ui/toggle'
import copy from 'copy-to-clipboard'
import { useTranslation } from 'react-i18next'
import NewAudioButton from '@/app/components/base/new-audio-button'
import { toast } from '@/app/notifications'
import { AppSourceType as AppSourceTypeEnum } from '@/service/share'
import { getCopyContent, MAX_GENERATION_DEPTH } from './utils'
@@ -5,7 +5,6 @@ import type { FeedbackType } from '@/app/components/base/chat/chat/type'
import type { WorkflowProcess } from '@/app/components/base/chat/types'
import type { SiteInfo } from '@/models/share'
import { cn } from '@langgenius/dify-ui/cn'
import { toast } from '@langgenius/dify-ui/toast'
import { RiPlayList2Line, RiSparklingFill } from '@remixicon/react'
import { useBoolean } from 'ahooks'
import * as React from 'react'
@@ -15,6 +14,7 @@ import { useStore as useAppStore } from '@/app/components/app/store'
import { useChatContext } from '@/app/components/base/chat/chat/context'
import { LoadingPlaceholder } from '@/app/components/base/loading-placeholder'
import { Markdown } from '@/app/components/base/markdown'
import { toast } from '@/app/notifications'
import { useParams } from '@/next/navigation'
import { fetchTextGenerationMessage } from '@/service/debug'
import {
@@ -1,9 +1,9 @@
import type { ISavedItemsProps } from '../index'
import { toast } from '@langgenius/dify-ui/toast'
import { fireEvent, render, screen } from '@testing-library/react'
import copy from 'copy-to-clipboard'
import * as React from 'react'
import { beforeEach, describe, expect, it, vi } from 'vite-plus/test'
import { toast } from '@/app/notifications'
import SavedItems from '../index'
vi.mock('copy-to-clipboard', () => ({
@@ -3,13 +3,13 @@ import type { FC } from 'react'
import type { SavedMessage } from '@/models/debug'
import { cn } from '@langgenius/dify-ui/cn'
import { IconButton } from '@langgenius/dify-ui/icon-button'
import { toast } from '@langgenius/dify-ui/toast'
import { RiClipboardLine, RiDeleteBinLine } from '@remixicon/react'
import copy from 'copy-to-clipboard'
import * as React from 'react'
import { useTranslation } from 'react-i18next'
import { Markdown } from '@/app/components/base/markdown'
import NewAudioButton from '@/app/components/base/new-audio-button'
import { toast } from '@/app/notifications'
import NoData from './no-data'
export type ISavedItemsProps = {
+1 -1
View File
@@ -1,10 +1,10 @@
'use client'
import type { EnvironmentVariableItemResponse } from '@dify/contracts/api/console/apps/types.gen'
import { toast } from '@langgenius/dify-ui/toast'
import { useMutation } from '@tanstack/react-query'
import { useCallback } from 'react'
import { useTranslation } from 'react-i18next'
import { toast } from '@/app/notifications'
import { consoleClient } from '@/service/console'
import { downloadBlob } from '@/utils/download'
@@ -141,7 +141,7 @@ const toastMocks = vi.hoisted(() => {
}
})
vi.mock('@langgenius/dify-ui/toast', () => ({
vi.mock('@/app/notifications', () => ({
toast: toastMocks.api,
}))
+1 -1
View File
@@ -4,7 +4,6 @@ import type { AppPartial } from '@dify/contracts/api/console/apps/types.gen'
import type { WorkflowOnlineUser } from '@/models/app'
import { zIconType } from '@dify/contracts/api/console/apps/zod.gen'
import { cn } from '@langgenius/dify-ui/cn'
import { toast } from '@langgenius/dify-ui/toast'
import { useSuspenseQuery } from '@tanstack/react-query'
import { useAtomValue } from 'jotai'
import { memo, useCallback, useId, useMemo } from 'react'
@@ -12,6 +11,7 @@ import { useTranslation } from 'react-i18next'
import { AppTypeIcon } from '@/app/components/app/type-selector'
import AppIcon from '@/app/components/base/app-icon'
import { UserAvatarList } from '@/app/components/base/user-avatar-list'
import { toast } from '@/app/notifications'
import { workspacePermissionKeysAtom } from '@/context/permission-state'
import { userProfileQueryOptions } from '@/features/account-profile/client'
import { systemFeaturesQueryOptions } from '@/features/system-features/client'
@@ -36,7 +36,6 @@ import {
import { Field, FieldLabel } from '@langgenius/dify-ui/field'
import { IconButton } from '@langgenius/dify-ui/icon-button'
import { InputGroup, InputGroupAddon, InputGroupInput } from '@langgenius/dify-ui/input-group'
import { toast } from '@langgenius/dify-ui/toast'
import { Toggle } from '@langgenius/dify-ui/toggle'
import { Tooltip, TooltipContent, TooltipTrigger } from '@langgenius/dify-ui/tooltip'
import { useMutation, useSuspenseQuery } from '@tanstack/react-query'
@@ -50,6 +49,7 @@ import {
getStepByStepTourDropdownMenuContentProps,
useStepByStepTourControlledDropdown,
} from '@/app/components/step-by-step-tour/dropdown-menu'
import { toast } from '@/app/notifications'
import { workspacePermissionKeysAtom } from '@/context/permission-state'
import { userProfileQueryOptions } from '@/features/account-profile/client'
import { systemFeaturesQueryOptions } from '@/features/system-features/client'
@@ -2,11 +2,11 @@
import { Button } from '@langgenius/dify-ui/button'
import { Dialog, DialogClose, DialogContent, DialogTitle } from '@langgenius/dify-ui/dialog'
import { toast } from '@langgenius/dify-ui/toast'
import { RiCloseLine } from '@remixicon/react'
import { useCallback, useMemo, useRef, useState } from 'react'
import { useTranslation } from 'react-i18next'
import AppIcon from '@/app/components/base/app-icon'
import { toast } from '@/app/notifications'
import { MARKETPLACE_API_PREFIX } from '@/config'
import {
fetchMarketplaceTemplateDSL,
+1 -1
View File
@@ -3,13 +3,13 @@
import type { AppPartial } from '@dify/contracts/api/console/apps/types.gen'
import { zIconType } from '@dify/contracts/api/console/apps/zod.gen'
import { cn } from '@langgenius/dify-ui/cn'
import { toast } from '@langgenius/dify-ui/toast'
import { useSuspenseQuery } from '@tanstack/react-query'
import { useAtomValue } from 'jotai'
import { memo, useCallback, useId, useMemo } from 'react'
import { useTranslation } from 'react-i18next'
import { AppTypeIcon } from '@/app/components/app/type-selector'
import AppIcon from '@/app/components/base/app-icon'
import { toast } from '@/app/notifications'
import { workspacePermissionKeysAtom } from '@/context/permission-state'
import { userProfileQueryOptions } from '@/features/account-profile/client'
import { systemFeaturesQueryOptions } from '@/features/system-features/client'
@@ -23,7 +23,7 @@ vi.mock('@/service/log', () => ({
fetchAgentLogDetail: vi.fn(),
}))
vi.mock('@langgenius/dify-ui/toast', () => ({
vi.mock('@/app/notifications', () => ({
toast: mockToast,
}))
@@ -21,7 +21,7 @@ vi.mock('@/service/log', () => ({
fetchAgentLogDetail: vi.fn(),
}))
vi.mock('@langgenius/dify-ui/toast', () => ({
vi.mock('@/app/notifications', () => ({
toast: mockToast,
}))
Loaded 100 of 597 files, more files were not shown because too many files have changed in this diff. Show more