mirror of
https://github.com/langgenius/dify.git
synced 2026-10-04 13:47:55 +08:00
fix(web): reduce chart and syntax highlighting bundles (#42845)
This commit is contained in:
1 parent
200da319f2
commit
e9550e50d0
75 files changed
+322
-136
No files matched your search
+18
-2
@@ -922,7 +922,15 @@ export const lintConfig = {
|
||||
message: 'Do not import next/font. Use the project font styles instead.',
|
||||
},
|
||||
{
|
||||
group: ['next/*', '!next/font', '!next/font/*', '!next/image', '!next/image/*'],
|
||||
// next/dynamic must be imported directly for compiler-generated preload metadata.
|
||||
group: [
|
||||
'next/*',
|
||||
'!next/dynamic',
|
||||
'!next/font',
|
||||
'!next/font/*',
|
||||
'!next/image',
|
||||
'!next/image/*',
|
||||
],
|
||||
message:
|
||||
'Import Next APIs from the corresponding @/next/* module instead of next/*.',
|
||||
},
|
||||
@@ -989,7 +997,15 @@ export const lintConfig = {
|
||||
message: 'Do not import next/font. Use the project font styles instead.',
|
||||
},
|
||||
{
|
||||
group: ['next/*', '!next/font', '!next/font/*', '!next/image', '!next/image/*'],
|
||||
// next/dynamic must be imported directly for compiler-generated preload metadata.
|
||||
group: [
|
||||
'next/*',
|
||||
'!next/dynamic',
|
||||
'!next/font',
|
||||
'!next/font/*',
|
||||
'!next/image',
|
||||
'!next/image/*',
|
||||
],
|
||||
message:
|
||||
'Import Next APIs from the corresponding @/next/* module instead of next/*.',
|
||||
},
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
'use client'
|
||||
|
||||
import dynamic from 'next/dynamic'
|
||||
import { Pricing } from '@/app/components/billing/pricing'
|
||||
import { SettingsModal } from '@/app/components/header/account-setting/settings-modal'
|
||||
import dynamic from '@/next/dynamic'
|
||||
|
||||
const InSiteMessageNotification = dynamic(
|
||||
() => import('@/app/components/app/in-site-message/notification'),
|
||||
|
||||
@@ -5,7 +5,7 @@ import * as React from 'react'
|
||||
import { AppModeEnum } from '@/types/app'
|
||||
import AppInfoModals from '../app-info-modals'
|
||||
|
||||
vi.mock('@/next/dynamic', () => ({
|
||||
vi.mock('next/dynamic', () => ({
|
||||
default: (loader: () => Promise<{ default: React.ComponentType }>) => {
|
||||
const LazyComp = React.lazy(loader)
|
||||
return function DynamicWrapper(props: Record<string, unknown>) {
|
||||
|
||||
@@ -15,11 +15,11 @@ import {
|
||||
import { Button } from '@langgenius/dify-ui/button'
|
||||
import { Field, FieldLabel } from '@langgenius/dify-ui/field'
|
||||
import { InputGroup, InputGroupAddon, InputGroupInput } from '@langgenius/dify-ui/input-group'
|
||||
import dynamic from 'next/dynamic'
|
||||
import * as React from 'react'
|
||||
import { useCallback, useState } from 'react'
|
||||
import { Trans, useTranslation } from 'react-i18next'
|
||||
import { AppExportConfirmContent } from '@/app/components/app/export-confirm-modal'
|
||||
import dynamic from '@/next/dynamic'
|
||||
|
||||
const SwitchAppModal = dynamic(() => import('@/app/components/app/switch-app-modal'), {
|
||||
ssr: false,
|
||||
|
||||
@@ -8,6 +8,7 @@ import type { DeploymentDialogRequest } from './types'
|
||||
import type { DeploymentVersion } from './utils/version'
|
||||
import { useSuspenseQuery } from '@tanstack/react-query'
|
||||
import { useAtomValue } from 'jotai'
|
||||
import dynamic from 'next/dynamic'
|
||||
import { useCallback, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useLocale } from '#i18n'
|
||||
@@ -17,7 +18,6 @@ import { getEnterpriseDocUrl } from '@/context/i18n'
|
||||
import { workspacePermissionKeysAtom } from '@/context/permission-state'
|
||||
import { userProfileQueryOptions } from '@/features/account-profile/client'
|
||||
import { getDocLanguage } from '@/i18n/language'
|
||||
import dynamic from '@/next/dynamic'
|
||||
import { AppModeEnum } from '@/types/app'
|
||||
import { getAppACLCapabilities } from '@/utils/permission'
|
||||
import { BuiltInEnvironmentCard } from './built-in-environment-card'
|
||||
|
||||
+1
-1
@@ -19,10 +19,10 @@ import {
|
||||
SelectItemText,
|
||||
SelectTrigger,
|
||||
} from '@langgenius/dify-ui/select'
|
||||
import dynamic from 'next/dynamic'
|
||||
import { memo, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { isLLMEnvironmentVariableValue } from '@/app/components/workflow/llm-environment-variable'
|
||||
import dynamic from '@/next/dynamic'
|
||||
import { resolveEnvironmentVariableSelection } from './utils/workflow-deployment-input'
|
||||
|
||||
const LLMEnvironmentVariableValueField = dynamic(
|
||||
|
||||
@@ -2,7 +2,7 @@ import { render, screen } from '@testing-library/react'
|
||||
import Chart, { MessagesChart } from '../app-chart'
|
||||
|
||||
const reactEChartsMock = vi.fn()
|
||||
vi.mock('echarts-for-react', () => ({
|
||||
vi.mock('echarts-for-react/esm/core', () => ({
|
||||
default: (props: { option: unknown; opts?: unknown }) => {
|
||||
reactEChartsMock(props)
|
||||
return <div role="img" aria-label="Chart" />
|
||||
|
||||
@@ -6,9 +6,10 @@ import type { FC } from 'react'
|
||||
import type { ChartRow } from './app-chart-utils'
|
||||
import { Infotip, InfotipContent, InfotipTrigger } from '@langgenius/dify-ui/infotip'
|
||||
import { useQuery } from '@tanstack/react-query'
|
||||
import ReactECharts from 'echarts-for-react'
|
||||
import ReactECharts from 'echarts-for-react/esm/core'
|
||||
import * as React from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { echarts } from '@/app/components/base/line-chart/echarts'
|
||||
import { LoadingPlaceholder } from '@/app/components/base/loading-placeholder'
|
||||
import { consoleQuery } from '@/service/console'
|
||||
import {
|
||||
@@ -131,6 +132,7 @@ const Chart: React.FC<IChartProps> = ({
|
||||
</div>
|
||||
<div className="h-60 shrink-0 px-6 pb-4">
|
||||
<ReactECharts
|
||||
echarts={echarts}
|
||||
option={options}
|
||||
opts={ECHARTS_RENDER_OPTIONS}
|
||||
style={{ height: '100%', width: '100%' }}
|
||||
|
||||
@@ -247,7 +247,7 @@ vi.mock('@/utils/time', () => ({
|
||||
}))
|
||||
|
||||
// Mock dynamic imports
|
||||
vi.mock('@/next/dynamic', () => ({
|
||||
vi.mock('next/dynamic', () => ({
|
||||
default: (importFn: () => Promise<unknown>) => {
|
||||
void importFn().catch(() => {})
|
||||
const fnString = importFn.toString()
|
||||
|
||||
@@ -38,7 +38,7 @@ vi.mock('@/next/navigation', () => ({
|
||||
useParams: () => ({}),
|
||||
useSearchParams: () => search.params,
|
||||
}))
|
||||
vi.mock('@/next/dynamic', () => ({
|
||||
vi.mock('next/dynamic', () => ({
|
||||
default: (loader: () => Promise<{ default: React.ComponentType }>) => {
|
||||
const LazyComponent = React.lazy(loader)
|
||||
return function Dynamic(props: Record<string, unknown>) {
|
||||
|
||||
@@ -262,7 +262,7 @@ vi.mock('@/hooks/use-pay', () => ({
|
||||
CheckModal: () => null,
|
||||
}))
|
||||
|
||||
vi.mock('@/next/dynamic', () => ({
|
||||
vi.mock('next/dynamic', () => ({
|
||||
default: (importFn: () => Promise<unknown>) => {
|
||||
const fnString = importFn.toString()
|
||||
|
||||
|
||||
@@ -40,6 +40,7 @@ import { Toggle } from '@langgenius/dify-ui/toggle'
|
||||
import { Tooltip, TooltipContent, TooltipTrigger } from '@langgenius/dify-ui/tooltip'
|
||||
import { useMutation, useSuspenseQuery } from '@tanstack/react-query'
|
||||
import { useAtomValue } from 'jotai'
|
||||
import dynamic from 'next/dynamic'
|
||||
import { useCallback, useMemo, useState } from 'react'
|
||||
import { Trans, useTranslation } from 'react-i18next'
|
||||
import { useExportAppDsl, useExportWorkflowAppDsl } from '@/app/components/app/use-export-app-dsl'
|
||||
@@ -54,7 +55,6 @@ import { userProfileQueryOptions } from '@/features/account-profile/client'
|
||||
import { systemFeaturesQueryOptions } from '@/features/system-features/client'
|
||||
import { useAsyncWindowOpen } from '@/hooks/use-async-window-open'
|
||||
import { AccessMode } from '@/models/access-control'
|
||||
import dynamic from '@/next/dynamic'
|
||||
import { useRouter } from '@/next/navigation'
|
||||
import { useGetUserCanAccessApp } from '@/service/access-control/use-app-access-control'
|
||||
import { consoleClient, consoleQuery } from '@/service/console'
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
import type { AppListUrlQuery } from './query-params'
|
||||
import { zPostAppsBody } from '@dify/contracts/api/console/apps/zod.gen'
|
||||
import dynamic from '@/next/dynamic'
|
||||
import dynamic from 'next/dynamic'
|
||||
|
||||
type AppListCategory = AppListUrlQuery['category']
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
'use client'
|
||||
|
||||
import dynamic from '@/next/dynamic'
|
||||
import dynamic from 'next/dynamic'
|
||||
|
||||
const TagManagementModal = dynamic(
|
||||
() =>
|
||||
|
||||
@@ -4,6 +4,7 @@ import type { CreateAppModalProps } from '../explore/create-app-modal'
|
||||
import type { TrackCreateAppParams } from '@/utils/create-app-tracking'
|
||||
import { noop, useQueryClient } from '@tanstack/react-query'
|
||||
import { useAtomValue } from 'jotai'
|
||||
import dynamic from 'next/dynamic'
|
||||
import { useCallback, useRef, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { EducationExpireNotice } from '@/app/education/expire-notice'
|
||||
@@ -12,7 +13,6 @@ import AppListContext from '@/context/app-list-context'
|
||||
import { workspacePermissionKeysAtom } from '@/context/permission-state'
|
||||
import { useImportDSL } from '@/hooks/use-import-dsl'
|
||||
import { DSLImportMode } from '@/models/app'
|
||||
import dynamic from '@/next/dynamic'
|
||||
import { useRouter, useSearchParams } from '@/next/navigation'
|
||||
import { consoleClient, consoleQuery } from '@/service/console'
|
||||
import { trackCreateApp } from '@/utils/create-app-tracking'
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
'use client'
|
||||
|
||||
import type { IAmplitudeProps } from './AmplitudeProvider'
|
||||
import dynamic from '@/next/dynamic'
|
||||
import dynamic from 'next/dynamic'
|
||||
|
||||
const AmplitudeProvider = dynamic(
|
||||
() => import('./AmplitudeProvider').then((module) => module.AmplitudeProvider),
|
||||
|
||||
@@ -38,7 +38,7 @@ const mockDynamic = vi.hoisted(() =>
|
||||
|
||||
const mockPdfPreview = vi.hoisted(() => vi.fn(() => null))
|
||||
|
||||
vi.mock('@/next/dynamic', () => ({
|
||||
vi.mock('next/dynamic', () => ({
|
||||
default: mockDynamic,
|
||||
}))
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
'use client'
|
||||
|
||||
import dynamic from '@/next/dynamic'
|
||||
import dynamic from 'next/dynamic'
|
||||
|
||||
type DynamicPdfPreviewProps = {
|
||||
url: string
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
import { buildChartOptions as buildOverviewOptions } from '@/app/components/app/overview/app-chart-utils'
|
||||
import { buildChartOptions as buildMonitoringOptions } from '@/features/agent-v2/agent-detail/monitoring/chart-utils'
|
||||
import { echarts } from '../echarts'
|
||||
|
||||
it.each([
|
||||
[
|
||||
'overview',
|
||||
buildOverviewOptions({
|
||||
statistics: [
|
||||
{ date: '2026-01-01', count: 3 },
|
||||
{ date: '2026-01-02', count: 7 },
|
||||
],
|
||||
chartType: 'conversations',
|
||||
yField: 'count',
|
||||
}),
|
||||
],
|
||||
[
|
||||
'monitoring',
|
||||
buildMonitoringOptions({
|
||||
rows: [
|
||||
{ date: '2026-01-01', count: 3 },
|
||||
{ date: '2026-01-02', count: 7 },
|
||||
],
|
||||
chartType: 'conversations',
|
||||
valueKey: 'count',
|
||||
}),
|
||||
],
|
||||
] as const)('renders the %s dataset with the registered line-chart runtime', (_name, option) => {
|
||||
const chart = echarts.init(null, undefined, {
|
||||
renderer: 'svg',
|
||||
ssr: true,
|
||||
width: 400,
|
||||
height: 240,
|
||||
})
|
||||
try {
|
||||
chart.setOption({ ...option, animation: false })
|
||||
const svg = chart.renderToSVGString()
|
||||
expect(svg).toContain('stroke="rgb(6,148,162)"')
|
||||
expect(svg).toContain('Jan')
|
||||
} finally {
|
||||
chart.dispose()
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,19 @@
|
||||
import { LineChart } from 'echarts/charts'
|
||||
import { DatasetComponent, GridComponent, TooltipComponent } from 'echarts/components'
|
||||
import * as echarts from 'echarts/core'
|
||||
import { LegacyGridContainLabel } from 'echarts/features'
|
||||
import { CanvasRenderer, SVGRenderer } from 'echarts/renderers'
|
||||
|
||||
// Overview uses SVG; Agent monitoring retains its canvas renderer.
|
||||
echarts.use([
|
||||
LineChart,
|
||||
DatasetComponent,
|
||||
GridComponent,
|
||||
TooltipComponent,
|
||||
LegacyGridContainLabel,
|
||||
CanvasRenderer,
|
||||
SVGRenderer,
|
||||
])
|
||||
|
||||
// oxlint-disable-next-line no-barrel-files/no-barrel-files -- Consumers need the runtime configured by this module's chart and renderer registrations.
|
||||
export { echarts }
|
||||
@@ -1,7 +1,8 @@
|
||||
import type { JSX } from 'react'
|
||||
import type { BundledTheme } from 'shiki/bundle/web'
|
||||
import type { CodeTheme } from './shiki-highlight'
|
||||
import { IconButton } from '@langgenius/dify-ui/icon-button'
|
||||
import { Toggle } from '@langgenius/dify-ui/toggle'
|
||||
import dynamic from 'next/dynamic'
|
||||
import {
|
||||
lazy,
|
||||
memo,
|
||||
@@ -16,7 +17,6 @@ import {
|
||||
import CopyIcon from '@/app/components/base/copy-icon'
|
||||
import ErrorBoundary from '@/app/components/base/markdown/error-boundary'
|
||||
import useTheme from '@/hooks/use-theme'
|
||||
import dynamic from '@/next/dynamic'
|
||||
import { Theme } from '@/types/app'
|
||||
import SVGRenderer from '../svg-gallery' // Assumes svg-gallery.tsx is in /base directory
|
||||
import { highlightCode } from './shiki-highlight'
|
||||
@@ -79,7 +79,7 @@ const ShikiCodeBlock = memo(
|
||||
}: {
|
||||
code: string
|
||||
language: string
|
||||
theme: BundledTheme
|
||||
theme: CodeTheme
|
||||
initial?: JSX.Element
|
||||
}) => {
|
||||
const [nodes, setNodes] = useState(initial)
|
||||
|
||||
@@ -1,14 +1,28 @@
|
||||
import type { JSX } from 'react'
|
||||
import type { BundledTheme } from 'shiki/bundle/web'
|
||||
import { toJsxRuntime } from 'hast-util-to-jsx-runtime'
|
||||
import { Fragment } from 'react'
|
||||
import { jsx, jsxs } from 'react/jsx-runtime'
|
||||
import { bundledLanguages, getSingletonHighlighter } from 'shiki/bundle/web'
|
||||
import { createBundledHighlighter, createSingletonShorthands } from 'shiki/core'
|
||||
import { createOnigurumaEngine } from 'shiki/engine/oniguruma'
|
||||
import { codeLanguages } from './shiki-languages'
|
||||
|
||||
const createHighlighter = createBundledHighlighter({
|
||||
langs: codeLanguages,
|
||||
themes: {
|
||||
'github-dark': () => import('shiki/themes/github-dark.mjs'),
|
||||
'github-light': () => import('shiki/themes/github-light.mjs'),
|
||||
},
|
||||
engine: () => createOnigurumaEngine(import('shiki/wasm')),
|
||||
})
|
||||
|
||||
const { getSingletonHighlighter } = createSingletonShorthands(createHighlighter)
|
||||
|
||||
export type CodeTheme = 'github-dark' | 'github-light'
|
||||
|
||||
type HighlightCodeOptions = {
|
||||
code: string
|
||||
language: string
|
||||
theme: BundledTheme
|
||||
theme: CodeTheme
|
||||
}
|
||||
|
||||
export const highlightCode = async ({
|
||||
@@ -18,7 +32,7 @@ export const highlightCode = async ({
|
||||
}: HighlightCodeOptions): Promise<JSX.Element> => {
|
||||
const normalizedLanguage = language.trim().toLowerCase()
|
||||
const lang =
|
||||
normalizedLanguage === 'dotenv' || Object.hasOwn(bundledLanguages, normalizedLanguage)
|
||||
normalizedLanguage === 'dotenv' || Object.hasOwn(codeLanguages, normalizedLanguage)
|
||||
? normalizedLanguage
|
||||
: 'text'
|
||||
// README fences may name languages outside the web bundle. Load dotenv on
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
// Keep the existing Shiki web language set and aliases without importing its all-theme bundle.
|
||||
export const codeLanguages = {
|
||||
'angular-html': () => import('shiki/langs/angular-html.mjs'),
|
||||
'angular-ts': () => import('shiki/langs/angular-ts.mjs'),
|
||||
astro: () => import('shiki/langs/astro.mjs'),
|
||||
blade: () => import('shiki/langs/blade.mjs'),
|
||||
c: () => import('shiki/langs/c.mjs'),
|
||||
coffee: () => import('shiki/langs/coffee.mjs'),
|
||||
coffeescript: () => import('shiki/langs/coffee.mjs'),
|
||||
cpp: () => import('shiki/langs/cpp.mjs'),
|
||||
'c++': () => import('shiki/langs/cpp.mjs'),
|
||||
css: () => import('shiki/langs/css.mjs'),
|
||||
csv: () => import('shiki/langs/csv.mjs'),
|
||||
glsl: () => import('shiki/langs/glsl.mjs'),
|
||||
graphql: () => import('shiki/langs/graphql.mjs'),
|
||||
gql: () => import('shiki/langs/graphql.mjs'),
|
||||
haml: () => import('shiki/langs/haml.mjs'),
|
||||
handlebars: () => import('shiki/langs/handlebars.mjs'),
|
||||
hbs: () => import('shiki/langs/handlebars.mjs'),
|
||||
html: () => import('shiki/langs/html.mjs'),
|
||||
'html-derivative': () => import('shiki/langs/html-derivative.mjs'),
|
||||
http: () => import('shiki/langs/http.mjs'),
|
||||
hurl: () => import('shiki/langs/hurl.mjs'),
|
||||
imba: () => import('shiki/langs/imba.mjs'),
|
||||
java: () => import('shiki/langs/java.mjs'),
|
||||
javascript: () => import('shiki/langs/javascript.mjs'),
|
||||
js: () => import('shiki/langs/javascript.mjs'),
|
||||
cjs: () => import('shiki/langs/javascript.mjs'),
|
||||
mjs: () => import('shiki/langs/javascript.mjs'),
|
||||
jinja: () => import('shiki/langs/jinja.mjs'),
|
||||
jison: () => import('shiki/langs/jison.mjs'),
|
||||
json: () => import('shiki/langs/json.mjs'),
|
||||
json5: () => import('shiki/langs/json5.mjs'),
|
||||
jsonc: () => import('shiki/langs/jsonc.mjs'),
|
||||
jsonl: () => import('shiki/langs/jsonl.mjs'),
|
||||
jsx: () => import('shiki/langs/jsx.mjs'),
|
||||
julia: () => import('shiki/langs/julia.mjs'),
|
||||
jl: () => import('shiki/langs/julia.mjs'),
|
||||
less: () => import('shiki/langs/less.mjs'),
|
||||
markdown: () => import('shiki/langs/markdown.mjs'),
|
||||
md: () => import('shiki/langs/markdown.mjs'),
|
||||
marko: () => import('shiki/langs/marko.mjs'),
|
||||
mdc: () => import('shiki/langs/mdc.mjs'),
|
||||
mdx: () => import('shiki/langs/mdx.mjs'),
|
||||
php: () => import('shiki/langs/php.mjs'),
|
||||
postcss: () => import('shiki/langs/postcss.mjs'),
|
||||
pug: () => import('shiki/langs/pug.mjs'),
|
||||
jade: () => import('shiki/langs/pug.mjs'),
|
||||
python: () => import('shiki/langs/python.mjs'),
|
||||
py: () => import('shiki/langs/python.mjs'),
|
||||
r: () => import('shiki/langs/r.mjs'),
|
||||
regexp: () => import('shiki/langs/regexp.mjs'),
|
||||
regex: () => import('shiki/langs/regexp.mjs'),
|
||||
sass: () => import('shiki/langs/sass.mjs'),
|
||||
scss: () => import('shiki/langs/scss.mjs'),
|
||||
shellscript: () => import('shiki/langs/shellscript.mjs'),
|
||||
bash: () => import('shiki/langs/shellscript.mjs'),
|
||||
sh: () => import('shiki/langs/shellscript.mjs'),
|
||||
shell: () => import('shiki/langs/shellscript.mjs'),
|
||||
zsh: () => import('shiki/langs/shellscript.mjs'),
|
||||
smithy: () => import('shiki/langs/smithy.mjs'),
|
||||
sql: () => import('shiki/langs/sql.mjs'),
|
||||
stylus: () => import('shiki/langs/stylus.mjs'),
|
||||
styl: () => import('shiki/langs/stylus.mjs'),
|
||||
svelte: () => import('shiki/langs/svelte.mjs'),
|
||||
'ts-tags': () => import('shiki/langs/ts-tags.mjs'),
|
||||
lit: () => import('shiki/langs/ts-tags.mjs'),
|
||||
tsx: () => import('shiki/langs/tsx.mjs'),
|
||||
typescript: () => import('shiki/langs/typescript.mjs'),
|
||||
ts: () => import('shiki/langs/typescript.mjs'),
|
||||
cts: () => import('shiki/langs/typescript.mjs'),
|
||||
mts: () => import('shiki/langs/typescript.mjs'),
|
||||
vue: () => import('shiki/langs/vue.mjs'),
|
||||
'vue-html': () => import('shiki/langs/vue-html.mjs'),
|
||||
'vue-vine': () => import('shiki/langs/vue-vine.mjs'),
|
||||
wasm: () => import('shiki/langs/wasm.mjs'),
|
||||
wgsl: () => import('shiki/langs/wgsl.mjs'),
|
||||
wit: () => import('shiki/langs/wit.mjs'),
|
||||
xml: () => import('shiki/langs/xml.mjs'),
|
||||
yaml: () => import('shiki/langs/yaml.mjs'),
|
||||
yml: () => import('shiki/langs/yaml.mjs'),
|
||||
}
|
||||
@@ -7,7 +7,7 @@ const { mockReactMarkdownWrapper } = vi.hoisted(() => ({
|
||||
mockReactMarkdownWrapper: vi.fn(),
|
||||
}))
|
||||
|
||||
vi.mock('@/next/dynamic', () => ({
|
||||
vi.mock('next/dynamic', () => ({
|
||||
default: () => {
|
||||
const MockStreamdownWrapper = (props: { latexContent: string }) => {
|
||||
mockReactMarkdownWrapper(props)
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import type { SimplePluginInfo, StreamdownWrapperProps } from './streamdown-wrapper'
|
||||
import { cn } from '@langgenius/dify-ui/cn'
|
||||
import { flow } from 'es-toolkit/compat'
|
||||
import dynamic from 'next/dynamic'
|
||||
import { memo, useMemo } from 'react'
|
||||
import dynamic from '@/next/dynamic'
|
||||
import { preprocessLaTeX, preprocessThinkTag } from './markdown-utils'
|
||||
|
||||
const StreamdownWrapper = dynamic(() => import('./streamdown-wrapper'), { ssr: false })
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import type { ComponentType } from 'react'
|
||||
import type { Components, StreamdownProps } from 'streamdown'
|
||||
import { createMathPlugin } from '@streamdown/math'
|
||||
import dynamic from 'next/dynamic'
|
||||
import { memo, useMemo } from 'react'
|
||||
import RemarkBreaks from 'remark-breaks'
|
||||
import { defaultRehypePlugins, defaultRemarkPlugins, Streamdown } from 'streamdown'
|
||||
@@ -17,7 +18,6 @@ import {
|
||||
VideoBlock,
|
||||
} from '@/app/components/base/markdown-blocks'
|
||||
import { ALLOW_INLINE_STYLES, ENABLE_SINGLE_DOLLAR_LATEX } from '@/config'
|
||||
import dynamic from '@/next/dynamic'
|
||||
import { customUrlTransform } from './markdown-utils'
|
||||
import 'katex/dist/katex.min.css'
|
||||
|
||||
|
||||
@@ -1,6 +1,33 @@
|
||||
@property --simple-pie-chart-percentage {
|
||||
syntax: '<number>';
|
||||
inherits: false;
|
||||
initial-value: 0;
|
||||
}
|
||||
|
||||
.simplePieChart {
|
||||
border-radius: 50%;
|
||||
background:
|
||||
radial-gradient(closest-side, transparent 83%, var(--simple-pie-chart-stroke) 83%),
|
||||
conic-gradient(
|
||||
var(--simple-pie-chart-color) calc(var(--simple-pie-chart-percentage) * 1%),
|
||||
#fff 0
|
||||
);
|
||||
animation: reveal var(--simple-pie-chart-duration) cubic-bezier(0.215, 0.61, 0.355, 1);
|
||||
transition: --simple-pie-chart-percentage 500ms cubic-bezier(0.215, 0.61, 0.355, 1);
|
||||
box-shadow:
|
||||
0 0 5px -3px rgb(from var(--simple-pie-chart-color) r g b / 0.1),
|
||||
0.5px 0.5px 3px 0 rgb(from var(--simple-pie-chart-color) r g b / 0.3);
|
||||
}
|
||||
|
||||
@keyframes reveal {
|
||||
from {
|
||||
--simple-pie-chart-percentage: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.simplePieChart {
|
||||
animation: none;
|
||||
transition: none;
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,6 @@
|
||||
import type { EChartsOption } from 'echarts'
|
||||
import type { CSSProperties } from 'react'
|
||||
import { cn } from '@langgenius/dify-ui/cn'
|
||||
import ReactECharts from 'echarts-for-react'
|
||||
import { memo, useMemo } from 'react'
|
||||
import { memo } from 'react'
|
||||
import style from './index.module.css'
|
||||
|
||||
type SimplePieChartProps = {
|
||||
@@ -22,50 +20,15 @@ const SimplePieChart = ({
|
||||
animationDuration,
|
||||
className,
|
||||
}: SimplePieChartProps) => {
|
||||
const option: EChartsOption = useMemo(
|
||||
() => ({
|
||||
series: [
|
||||
{
|
||||
type: 'pie',
|
||||
radius: ['83%', '100%'],
|
||||
animation: false,
|
||||
data: [{ value: 100, itemStyle: { color: stroke } }],
|
||||
emphasis: {
|
||||
disabled: true,
|
||||
},
|
||||
labelLine: {
|
||||
show: false,
|
||||
},
|
||||
cursor: 'default',
|
||||
},
|
||||
{
|
||||
type: 'pie',
|
||||
radius: '83%',
|
||||
animationDuration: animationDuration ?? 600,
|
||||
data: [
|
||||
{ value: percentage, itemStyle: { color: fill } },
|
||||
{ value: 100 - percentage, itemStyle: { color: '#fff' } },
|
||||
],
|
||||
emphasis: {
|
||||
disabled: true,
|
||||
},
|
||||
labelLine: {
|
||||
show: false,
|
||||
},
|
||||
cursor: 'default',
|
||||
},
|
||||
],
|
||||
}),
|
||||
[stroke, fill, percentage, animationDuration],
|
||||
)
|
||||
|
||||
return (
|
||||
<ReactECharts
|
||||
option={option}
|
||||
<div
|
||||
className={cn(style.simplePieChart, className)}
|
||||
style={
|
||||
{
|
||||
'--simple-pie-chart-color': fill,
|
||||
'--simple-pie-chart-stroke': stroke,
|
||||
'--simple-pie-chart-percentage': Math.min(100, Math.max(0, percentage)),
|
||||
'--simple-pie-chart-duration': `${animationDuration ?? 600}ms`,
|
||||
width: size,
|
||||
height: size,
|
||||
} as CSSProperties
|
||||
|
||||
@@ -64,7 +64,7 @@ vi.mock('@/app/components/datasets/common/document-file-icon', () => ({
|
||||
}))
|
||||
|
||||
// Mock SimplePieChart
|
||||
vi.mock('@/next/dynamic', () => ({
|
||||
vi.mock('next/dynamic', () => ({
|
||||
default: () => {
|
||||
const Component = ({ percentage }: { percentage: number }) => (
|
||||
<div data-testid="pie-chart">{percentage}%</div>
|
||||
|
||||
+1
-1
@@ -18,7 +18,7 @@ vi.mock('@/types/app', () => ({
|
||||
}))
|
||||
|
||||
// Mock SimplePieChart with dynamic import handling
|
||||
vi.mock('@/next/dynamic', () => ({
|
||||
vi.mock('next/dynamic', () => ({
|
||||
default: () => {
|
||||
const DynamicComponent = ({
|
||||
percentage,
|
||||
|
||||
@@ -2,11 +2,11 @@
|
||||
import type { CustomFile as File, FileItem } from '@/models/datasets'
|
||||
import { IconButton } from '@langgenius/dify-ui/icon-button'
|
||||
import { RiDeleteBinLine, RiErrorWarningFill } from '@remixicon/react'
|
||||
import dynamic from 'next/dynamic'
|
||||
import { useId, useMemo } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import DocumentFileIcon from '@/app/components/datasets/common/document-file-icon'
|
||||
import useTheme from '@/hooks/use-theme'
|
||||
import dynamic from '@/next/dynamic'
|
||||
import { Theme } from '@/types/app'
|
||||
import { formatFileSize, getFileExtension } from '@/utils/format'
|
||||
import { PROGRESS_COMPLETE, PROGRESS_ERROR } from '../constants'
|
||||
|
||||
+1
-1
@@ -24,7 +24,7 @@ vi.mock('@/app/components/datasets/common/document-file-icon', () => ({
|
||||
}))
|
||||
|
||||
// Mock SimplePieChart
|
||||
vi.mock('@/next/dynamic', () => ({
|
||||
vi.mock('next/dynamic', () => ({
|
||||
default: () => {
|
||||
const Component = ({ percentage }: { percentage: number }) => (
|
||||
<div data-testid="pie-chart">{percentage}%</div>
|
||||
|
||||
+1
-1
@@ -17,7 +17,7 @@ vi.mock('@/types/app', () => ({
|
||||
}))
|
||||
|
||||
// Mock SimplePieChart with dynamic import handling
|
||||
vi.mock('@/next/dynamic', () => ({
|
||||
vi.mock('next/dynamic', () => ({
|
||||
default: () => {
|
||||
const DynamicComponent = ({
|
||||
percentage,
|
||||
|
||||
+1
-1
@@ -1,11 +1,11 @@
|
||||
import type { CustomFile as File, FileItem } from '@/models/datasets'
|
||||
import { cn } from '@langgenius/dify-ui/cn'
|
||||
import { RiDeleteBinLine, RiErrorWarningFill } from '@remixicon/react'
|
||||
import dynamic from 'next/dynamic'
|
||||
import { useMemo } from 'react'
|
||||
import DocumentFileIcon from '@/app/components/datasets/common/document-file-icon'
|
||||
import { getFileType } from '@/app/components/datasets/common/image-uploader/utils'
|
||||
import useTheme from '@/hooks/use-theme'
|
||||
import dynamic from '@/next/dynamic'
|
||||
import { Theme } from '@/types/app'
|
||||
import { formatFileSize } from '@/utils/format'
|
||||
import { PROGRESS_ERROR } from '../constants'
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
'use client'
|
||||
|
||||
import { IS_DEV } from '@/config'
|
||||
import dynamic from '@/next/dynamic'
|
||||
import dynamic from 'next/dynamic'
|
||||
|
||||
const Agentation = dynamic(() => import('agentation').then((module) => module.Agentation), {
|
||||
ssr: false,
|
||||
})
|
||||
const Agentation =
|
||||
process.env.NODE_ENV === 'development'
|
||||
? dynamic(() => import('agentation').then((module) => module.Agentation), { ssr: false })
|
||||
: null
|
||||
|
||||
const IS_AGENTATION_ENABLED = process.env.NEXT_PUBLIC_ENABLE_AGENTATION !== 'false'
|
||||
|
||||
export function AgentationLoader() {
|
||||
if (!IS_DEV || !IS_AGENTATION_ENABLED) return null
|
||||
if (!Agentation || !IS_AGENTATION_ENABLED) return null
|
||||
|
||||
return <Agentation />
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
import type { AppMode } from '@dify/contracts/api/console/installed-apps/types.gen'
|
||||
import type { AppData } from '@/models/share'
|
||||
import { useQuery, useSuspenseQuery } from '@tanstack/react-query'
|
||||
import dynamic from 'next/dynamic'
|
||||
import * as React from 'react'
|
||||
import { useEffect } from 'react'
|
||||
import { LoadingPlaceholder } from '@/app/components/base/loading-placeholder'
|
||||
@@ -9,7 +10,6 @@ import TextGenerationApp from '@/app/components/share/text-generation'
|
||||
import { useWebAppStore } from '@/context/web-app-context'
|
||||
import { systemFeaturesQueryOptions } from '@/features/system-features/client'
|
||||
import { AccessMode } from '@/models/access-control'
|
||||
import dynamic from '@/next/dynamic'
|
||||
import { useGetUserCanAccessApp } from '@/service/access-control/use-app-access-control'
|
||||
import { consoleQuery } from '@/service/console'
|
||||
import AppUnavailable from '../../base/app-unavailable'
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
'use client'
|
||||
|
||||
import type { SettingsDestination } from './query-params'
|
||||
import dynamic from 'next/dynamic'
|
||||
import { useQueryState } from 'nuqs'
|
||||
import dynamic from '@/next/dynamic'
|
||||
import {
|
||||
isAccountSettingDestination,
|
||||
isIntegrationSettingDestination,
|
||||
|
||||
@@ -21,7 +21,7 @@ const { dynamicComponent } = vi.hoisted(() => {
|
||||
}
|
||||
})
|
||||
|
||||
vi.mock('@/next/dynamic', () => ({
|
||||
vi.mock('next/dynamic', () => ({
|
||||
default: () => dynamicComponent(),
|
||||
}))
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import type { PanelProps } from '@/app/components/workflow/panel'
|
||||
import dynamic from 'next/dynamic'
|
||||
import { memo, useMemo } from 'react'
|
||||
import Panel from '@/app/components/workflow/panel'
|
||||
import { useStore } from '@/app/components/workflow/store'
|
||||
import dynamic from '@/next/dynamic'
|
||||
|
||||
const Record = dynamic(() => import('@/app/components/workflow/panel/record'), {
|
||||
ssr: false,
|
||||
|
||||
@@ -115,7 +115,7 @@ vi.mock('@/next/navigation', () => ({
|
||||
useSearchParams: () => new URLSearchParams(''),
|
||||
}))
|
||||
|
||||
vi.mock('@/next/dynamic', () => ({
|
||||
vi.mock('next/dynamic', () => ({
|
||||
default: () => {
|
||||
return function MockDynamicComponent(props: {
|
||||
show: boolean
|
||||
|
||||
@@ -103,8 +103,8 @@ vi.mock('@/next/navigation', () => ({
|
||||
useSearchParams: () => new URLSearchParams(),
|
||||
}))
|
||||
|
||||
vi.mock('@/next/dynamic', () => ({
|
||||
default: () => () => null,
|
||||
vi.mock('@/features/tag-management/components/tag-management-modal', () => ({
|
||||
TagManagementModal: () => null,
|
||||
}))
|
||||
|
||||
vi.mock('@/hooks/use-document-title', () => ({
|
||||
|
||||
@@ -14,6 +14,7 @@ import { Button } from '@langgenius/dify-ui/button'
|
||||
import { cn } from '@langgenius/dify-ui/cn'
|
||||
import { useDebounce } from 'ahooks'
|
||||
import { useAtomValue } from 'jotai'
|
||||
import dynamic from 'next/dynamic'
|
||||
import { useEffect, useMemo, useRef, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { SearchInput } from '@/app/components/base/search-input'
|
||||
@@ -21,7 +22,6 @@ import { workspacePermissionKeysAtom } from '@/context/permission-state'
|
||||
import { currentWorkspaceLoadingAtom } from '@/context/workspace-state'
|
||||
import { TagFilter } from '@/features/tag-management/components/tag-filter'
|
||||
import useDocumentTitle from '@/hooks/use-document-title'
|
||||
import dynamic from '@/next/dynamic'
|
||||
import Link from '@/next/link'
|
||||
import { useInfiniteSnippetList } from '@/service/use-snippets'
|
||||
import CreatorsFilter from '../apps/creators-filter'
|
||||
|
||||
@@ -2,10 +2,10 @@
|
||||
|
||||
import type { PanelProps } from '@/app/components/workflow/panel'
|
||||
import type { SnippetInputField } from '@/models/snippet'
|
||||
import dynamic from 'next/dynamic'
|
||||
import { memo, useMemo } from 'react'
|
||||
import Panel from '@/app/components/workflow/panel'
|
||||
import { useStore } from '@/app/components/workflow/store'
|
||||
import dynamic from '@/next/dynamic'
|
||||
|
||||
const Record = dynamic(() => import('@/app/components/workflow/panel/record'), {
|
||||
ssr: false,
|
||||
|
||||
@@ -177,7 +177,7 @@ vi.mock('@/app/components/workflow-app/components/workflow-panel', () => ({
|
||||
default: () => <div data-testid="workflow-panel">workflow-panel</div>,
|
||||
}))
|
||||
|
||||
vi.mock('@/next/dynamic', async () => {
|
||||
vi.mock('next/dynamic', async () => {
|
||||
const ReactModule = await import('react')
|
||||
|
||||
return {
|
||||
|
||||
@@ -75,7 +75,7 @@ vi.mock('@/app/components/workflow/panel', () => ({
|
||||
),
|
||||
}))
|
||||
|
||||
vi.mock('@/next/dynamic', () => ({
|
||||
vi.mock('next/dynamic', () => ({
|
||||
default: (loader: () => Promise<{ default: React.ComponentType<Record<string, unknown>> }>) => {
|
||||
const LazyComp = React.lazy(loader)
|
||||
return function DynamicWrapper(props: Record<string, unknown>) {
|
||||
|
||||
@@ -3,6 +3,7 @@ import type {
|
||||
TriggerDefaultValue,
|
||||
} from '@/app/components/workflow/block-selector/types'
|
||||
import type { ExportSecretEnvironmentVariable } from '@/app/components/workflow/export-secret-env-event'
|
||||
import dynamic from 'next/dynamic'
|
||||
import { memo, useCallback, useState } from 'react'
|
||||
import { useStoreApi } from 'reactflow'
|
||||
import { START_INITIAL_POSITION } from '@/app/components/workflow/constants'
|
||||
@@ -17,7 +18,6 @@ import { useStore } from '@/app/components/workflow/store'
|
||||
import { BlockEnum } from '@/app/components/workflow/types'
|
||||
import { generateNewNode } from '@/app/components/workflow/utils'
|
||||
import { useEventEmitterContextContext } from '@/context/event-emitter'
|
||||
import dynamic from '@/next/dynamic'
|
||||
import { useAutoOnboarding } from '../hooks/use-auto-onboarding'
|
||||
import { useAvailableNodesMetaData } from '../hooks/use-available-nodes-meta-data'
|
||||
import WorkflowHeader from './workflow-header'
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import type { PanelProps } from '@/app/components/workflow/panel'
|
||||
import dynamic from 'next/dynamic'
|
||||
import { memo, useMemo } from 'react'
|
||||
import { useShallow } from 'zustand/react/shallow'
|
||||
import { useStore as useAppStore } from '@/app/components/app/store'
|
||||
import Panel from '@/app/components/workflow/panel'
|
||||
import CommentsPanel from '@/app/components/workflow/panel/comments-panel'
|
||||
import { useStore } from '@/app/components/workflow/store'
|
||||
import dynamic from '@/next/dynamic'
|
||||
import { useIsChatMode } from '../hooks/use-is-chat-mode'
|
||||
|
||||
const MessageLogModal = dynamic(() => import('@/app/components/base/message-log-modal'), {
|
||||
|
||||
@@ -157,7 +157,7 @@ vi.mock('@/context/workspace-state', async () => {
|
||||
}))
|
||||
})
|
||||
|
||||
vi.mock('@/next/dynamic', () => ({
|
||||
vi.mock('next/dynamic', () => ({
|
||||
default: () => () => null,
|
||||
}))
|
||||
|
||||
|
||||
@@ -49,7 +49,7 @@ it('selects the built-in local file datasource directly with its file extensions
|
||||
const provider = createDatasourceProvider()
|
||||
render(<DataSources searchText="" onSelect={onSelect} dataSources={[provider]} />)
|
||||
|
||||
await user.click(screen.getByRole('button', { name: 'File Source' }))
|
||||
await user.click(await screen.findByRole('button', { name: 'File Source' }))
|
||||
const action = screen.getByRole('button', { name: 'Local File' })
|
||||
expect(action).toHaveAccessibleDescription('Load local files')
|
||||
action.focus()
|
||||
@@ -94,7 +94,7 @@ it('keeps unauthorized installed providers selectable and falls back from null l
|
||||
const onSelect = vi.fn()
|
||||
render(<DataSources searchText="pages" onSelect={onSelect} dataSources={[provider]} />)
|
||||
|
||||
expect(screen.getByRole('button', { name: 'Drive source' })).toHaveAttribute(
|
||||
expect(await screen.findByRole('button', { name: 'Drive source' })).toHaveAttribute(
|
||||
'aria-expanded',
|
||||
'true',
|
||||
)
|
||||
@@ -140,14 +140,14 @@ it('filters by raw provider or action name and retains each matching provider gr
|
||||
dataSources={[provider, namedProvider('other')]}
|
||||
/>,
|
||||
)
|
||||
expect(screen.getByRole('button', { name: 'Matched action' })).toBeInTheDocument()
|
||||
expect(await screen.findByRole('button', { name: 'Matched action' })).toBeInTheDocument()
|
||||
expect(screen.getByRole('button', { name: 'Sibling action' })).toBeInTheDocument()
|
||||
expect(screen.queryByRole('button', { name: 'other' })).not.toBeInTheDocument()
|
||||
|
||||
rerender(
|
||||
<DataSources searchText="searchable-provider" onSelect={vi.fn()} dataSources={[provider]} />,
|
||||
)
|
||||
expect(screen.getByRole('button', { name: 'Matched action' })).toBeInTheDocument()
|
||||
expect(await screen.findByRole('button', { name: 'Matched action' })).toBeInTheDocument()
|
||||
rerender(<DataSources searchText="" onSelect={vi.fn()} dataSources={[provider]} />)
|
||||
await waitFor(() =>
|
||||
expect(screen.queryByRole('button', { name: 'Matched action' })).not.toBeInTheDocument(),
|
||||
@@ -158,7 +158,7 @@ it('filters by raw provider or action name and retains each matching provider gr
|
||||
)
|
||||
})
|
||||
|
||||
it('keeps flat letter navigation sorted with pinyin initials and nonalphabetic providers last', () => {
|
||||
it('keeps flat letter navigation sorted with pinyin initials and nonalphabetic providers last', async () => {
|
||||
const providers = [
|
||||
'1Source',
|
||||
'中文',
|
||||
@@ -173,9 +173,9 @@ it('keeps flat letter navigation sorted with pinyin initials and nonalphabetic p
|
||||
'Lima',
|
||||
].map(namedProvider)
|
||||
render(<DataSources searchText="" onSelect={vi.fn()} dataSources={providers} />)
|
||||
const letters = screen
|
||||
.getAllByRole('button')
|
||||
.filter((button) => /^[A-Z#]$/.test(button.textContent ?? ''))
|
||||
const letters = (await screen.findAllByRole('button')).filter((button) =>
|
||||
/^[A-Z#]$/.test(button.textContent ?? ''),
|
||||
)
|
||||
expect(letters.map((button) => button.textContent)).toEqual([
|
||||
'A',
|
||||
'E',
|
||||
@@ -216,7 +216,7 @@ it('keeps an expanded datasource open when the language changes its sort letter'
|
||||
const { rerender } = render(
|
||||
<DataSources searchText="" onSelect={vi.fn()} dataSources={[provider]} />,
|
||||
)
|
||||
await user.click(screen.getByRole('button', { name: 'File Source' }))
|
||||
await user.click(await screen.findByRole('button', { name: 'File Source' }))
|
||||
expect(screen.getByRole('button', { name: 'Local File' })).toBeInTheDocument()
|
||||
|
||||
language.value = 'zh_Hans'
|
||||
|
||||
@@ -483,13 +483,13 @@ describe('ToolPicker', () => {
|
||||
expect(trigger).toHaveFocus()
|
||||
})
|
||||
|
||||
it('should link the find-more footer to the marketplace tool category', () => {
|
||||
it('should link the find-more footer to the marketplace tool category', async () => {
|
||||
renderToolPicker({
|
||||
isShow: true,
|
||||
selectedTools: [],
|
||||
})
|
||||
|
||||
const footerLink = screen.getByRole('link', { name: /plugin\.findMoreInMarketplace/i })
|
||||
const footerLink = await screen.findByRole('link', { name: /plugin\.findMoreInMarketplace/i })
|
||||
expect(footerLink.closest('footer')).toBeInTheDocument()
|
||||
expect(footerLink).toHaveAttribute('href', 'https://marketplace.test/plugins/tool')
|
||||
})
|
||||
@@ -507,7 +507,7 @@ describe('ToolPicker', () => {
|
||||
})
|
||||
|
||||
expect(screen.queryByText('Built-in Provider')).not.toBeInTheDocument()
|
||||
expect(screen.getByText('Custom Provider')).toBeInTheDocument()
|
||||
expect(await screen.findByText('Custom Provider')).toBeInTheDocument()
|
||||
expect(screen.getByText('MCP Provider')).toBeInTheDocument()
|
||||
|
||||
await user.type(screen.getByRole('searchbox', { name: 'plugin.searchTools' }), 'weather')
|
||||
@@ -612,7 +612,7 @@ describe('ToolPicker', () => {
|
||||
|
||||
renderToolPicker({ isShow: true, scope, selectedTools: [] })
|
||||
|
||||
expect(screen.getByText(visibleProvider)).toBeInTheDocument()
|
||||
expect(await screen.findByText(visibleProvider)).toBeInTheDocument()
|
||||
expect(screen.queryByText(hiddenProvider)).not.toBeInTheDocument()
|
||||
expect(screen.queryByText('Built-in Provider')).not.toBeInTheDocument()
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ import type { ListRef } from '@/app/components/workflow/block-selector/marketpla
|
||||
import { cn } from '@langgenius/dify-ui/cn'
|
||||
import { useSuspenseQuery } from '@tanstack/react-query'
|
||||
import { useDebounce } from 'ahooks'
|
||||
import dynamic from 'next/dynamic'
|
||||
import { useCallback, useMemo, useRef } from 'react'
|
||||
import { trackEvent } from '@/app/components/base/amplitude'
|
||||
import { useMarketplacePlugins } from '@/app/components/plugins/marketplace/query'
|
||||
@@ -18,7 +19,13 @@ import { renderI18nObject } from '@/i18n/metadata'
|
||||
import { PluginCategoryEnum } from '../../plugins/types'
|
||||
import { BlockEnum } from '../types'
|
||||
import { DEFAULT_FILE_EXTENSIONS_IN_LOCAL_FILE_DATA_SOURCE } from './constants'
|
||||
import { DatasourceList } from './datasource-list'
|
||||
|
||||
const DatasourceList = dynamic(
|
||||
() => import('./datasource-list').then((module) => module.DatasourceList),
|
||||
{
|
||||
loading: () => <div className="h-24 animate-pulse rounded-lg bg-background-section" />,
|
||||
},
|
||||
)
|
||||
|
||||
type DataSourcesProps = {
|
||||
className?: string
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import type { RagPipelineDatasourceProviderResponse } from '@dify/contracts/api/console/rag/types.gen'
|
||||
import type { OnSelectBlock, ToolWithProvider } from '../types'
|
||||
import { useSuspenseQuery } from '@tanstack/react-query'
|
||||
import dynamic from 'next/dynamic'
|
||||
import { useEffect, useMemo } from 'react'
|
||||
import { systemFeaturesQueryOptions } from '@/features/system-features/client'
|
||||
import { useFeaturedToolsRecommendations } from '@/service/use-plugins'
|
||||
@@ -13,7 +14,10 @@ import {
|
||||
} from '@/service/use-tools'
|
||||
import { basePath } from '@/utils/var'
|
||||
import { useWorkflowStore } from '../store'
|
||||
import ToolBrowser from './tool-browser'
|
||||
|
||||
const ToolBrowser = dynamic(() => import('./tool-browser'), {
|
||||
loading: () => <div className="h-24 animate-pulse rounded-lg bg-background-section" />,
|
||||
})
|
||||
|
||||
function normalizeToolList(list: ToolWithProvider[] | undefined, currentBasePath?: string) {
|
||||
if (!list || !currentBasePath) return list
|
||||
|
||||
@@ -8,12 +8,12 @@ import { cn } from '@langgenius/dify-ui/cn'
|
||||
import { Popover, PopoverContent, PopoverTitle, PopoverTrigger } from '@langgenius/dify-ui/popover'
|
||||
import { useSuspenseQuery } from '@tanstack/react-query'
|
||||
import { useBoolean } from 'ahooks'
|
||||
import dynamic from 'next/dynamic'
|
||||
import { useMemo, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import SearchBox from '@/app/components/plugins/marketplace/search-box'
|
||||
import EditCustomToolModal from '@/app/components/tools/edit-custom-collection-modal'
|
||||
import { useCanManageTools } from '@/app/components/tools/hooks/use-tool-permissions'
|
||||
import ToolBrowser from '@/app/components/workflow/block-selector/tool-browser'
|
||||
import { toast } from '@/app/notifications'
|
||||
import { systemFeaturesQueryOptions } from '@/features/system-features/client'
|
||||
import { createCustomCollection } from '@/service/tools'
|
||||
@@ -29,6 +29,10 @@ import {
|
||||
useInvalidateAllWorkflowTools,
|
||||
} from '@/service/use-tools'
|
||||
|
||||
const ToolBrowser = dynamic(() => import('./tool-browser'), {
|
||||
loading: () => <div className="h-24 animate-pulse rounded-lg bg-background-section" />,
|
||||
})
|
||||
|
||||
type Props = Readonly<
|
||||
Pick<PopoverContentProps, 'placement' | 'sideOffset'> & {
|
||||
disabled: boolean
|
||||
|
||||
@@ -31,7 +31,7 @@ vi.mock('../../hooks/use-workflow-mode', () => ({
|
||||
useWorkflowMode: () => mockWorkflowMode,
|
||||
}))
|
||||
|
||||
vi.mock('@/next/dynamic', () => ({
|
||||
vi.mock('next/dynamic', () => ({
|
||||
default: () => {
|
||||
dynamicMockState.calls += 1
|
||||
return dynamicMockState.calls === 1 ? DynamicHeaderHistory : DynamicHeaderRestoring
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { HeaderInNormalProps } from './header-in-normal'
|
||||
import type { HeaderInRestoringProps } from './header-in-restoring'
|
||||
import type { HeaderInHistoryProps } from './header-in-view-history'
|
||||
import dynamic from '@/next/dynamic'
|
||||
import dynamic from 'next/dynamic'
|
||||
import { useWorkflowMode } from '../hooks/use-workflow-mode'
|
||||
import HeaderInNormal from './header-in-normal'
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ import { cn } from '@langgenius/dify-ui/cn'
|
||||
import { Infotip, InfotipContent, InfotipTitle, InfotipTrigger } from '@langgenius/dify-ui/infotip'
|
||||
import { Popover, PopoverContent, PopoverTrigger } from '@langgenius/dify-ui/popover'
|
||||
import { useQuery, useSuspenseQuery } from '@tanstack/react-query'
|
||||
import dynamic from 'next/dynamic'
|
||||
import { memo, useEffect, useMemo, useRef, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { SearchInput } from '@/app/components/base/search-input'
|
||||
@@ -23,12 +24,16 @@ import { consoleQuery } from '@/service/console'
|
||||
import { ViewType } from '../../../block-selector/types'
|
||||
import ViewTypeSelect from '../../../block-selector/view-type-select'
|
||||
import { useStrategyInfo } from '../../agent/use-config'
|
||||
import { AgentStrategyList } from './agent-strategy-list'
|
||||
import { InstallPluginButton } from './install-plugin-button'
|
||||
import { SwitchPluginVersion } from './switch-plugin-version'
|
||||
|
||||
const DEFAULT_TAGS: ListProps['tags'] = []
|
||||
|
||||
const AgentStrategyList = dynamic(
|
||||
() => import('./agent-strategy-list').then((module) => module.AgentStrategyList),
|
||||
{ loading: () => <div className="h-24 animate-pulse rounded-lg bg-background-section" /> },
|
||||
)
|
||||
|
||||
const NotFoundWarn = (props: { title: string; description: ReactNode }) => {
|
||||
const { title, description } = props
|
||||
const { t } = useTranslation(['workflow'])
|
||||
|
||||
@@ -103,7 +103,7 @@ vi.mock('@/app/components/workflow/panel/version-history-panel', () => ({
|
||||
),
|
||||
}))
|
||||
|
||||
vi.mock('@/next/dynamic', async () => {
|
||||
vi.mock('next/dynamic', async () => {
|
||||
const ReactModule = await import('react')
|
||||
|
||||
return {
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import type { FC } from 'react'
|
||||
import type { VersionHistoryPanelProps } from '@/app/components/workflow/panel/version-history-panel'
|
||||
import { cn } from '@langgenius/dify-ui/cn'
|
||||
import dynamic from 'next/dynamic'
|
||||
import { memo, useEffect, useRef } from 'react'
|
||||
import { useStore as useReactflow } from 'reactflow'
|
||||
import { useShallow } from 'zustand/react/shallow'
|
||||
import dynamic from '@/next/dynamic'
|
||||
import { Panel as NodePanel } from '../nodes'
|
||||
import { useStore } from '../store'
|
||||
import EnvPanel from './env-panel'
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
'use client'
|
||||
import dynamic from 'next/dynamic'
|
||||
import * as React from 'react'
|
||||
import dynamic from '@/next/dynamic'
|
||||
import { useWorkflowGeneratorStore } from './store'
|
||||
|
||||
// Lazy-load the modal so the bundle of the common layout stays light;
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
'use client'
|
||||
import { Separator } from '@langgenius/dify-ui/separator'
|
||||
import { useSuspenseQuery } from '@tanstack/react-query'
|
||||
import dynamic from 'next/dynamic'
|
||||
import { useLocale } from '#i18n'
|
||||
import { DifyLogo } from '@/app/components/base/logo/dify-logo'
|
||||
import LocaleMenu from '@/app/signin/_locale-menu'
|
||||
import { systemFeaturesQueryOptions } from '@/features/system-features/client'
|
||||
import { setLocaleOnClient } from '@/i18n/client'
|
||||
import { languages } from '@/i18n/language'
|
||||
import dynamic from '@/next/dynamic'
|
||||
|
||||
const ThemeSelector = dynamic(() => import('@/app/components/base/theme-selector'), {
|
||||
ssr: false,
|
||||
|
||||
@@ -12,7 +12,7 @@ const mockEducationStatus = vi.hoisted(() => ({
|
||||
}))
|
||||
const mockPricingModal = vi.hoisted(() => ({ isOpen: false }))
|
||||
|
||||
vi.mock('@/next/dynamic', () => ({
|
||||
vi.mock('next/dynamic', () => ({
|
||||
default:
|
||||
() =>
|
||||
({ expired, onClose }: { expired: boolean; onClose: () => void }) => (
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
'use client'
|
||||
|
||||
import dynamic from 'next/dynamic'
|
||||
import { useQueryState } from 'nuqs'
|
||||
import {
|
||||
pricingQueryParamName,
|
||||
pricingQueryParser,
|
||||
} from '@/app/components/billing/pricing/query-params'
|
||||
import dynamic from '@/next/dynamic'
|
||||
import { useEducationExpireNotice } from './use-expire-notice'
|
||||
|
||||
const ExpireNoticeModal = dynamic(() => import('./modal'), { ssr: false })
|
||||
|
||||
@@ -7,7 +7,7 @@ vi.mock('@/i18n/client', () => ({
|
||||
setLocaleOnClient: vi.fn(),
|
||||
}))
|
||||
|
||||
vi.mock('@/next/dynamic', () => ({
|
||||
vi.mock('next/dynamic', () => ({
|
||||
default: () => () => null,
|
||||
}))
|
||||
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
'use client'
|
||||
import { Separator } from '@langgenius/dify-ui/separator'
|
||||
import { useSuspenseQuery } from '@tanstack/react-query'
|
||||
import dynamic from 'next/dynamic'
|
||||
import { useLocale } from '#i18n'
|
||||
import { DifyLogo } from '@/app/components/base/logo/dify-logo'
|
||||
import { systemFeaturesQueryOptions } from '@/features/system-features/client'
|
||||
import { setLocaleOnClient } from '@/i18n/client'
|
||||
import { languages } from '@/i18n/language'
|
||||
import dynamic from '@/next/dynamic'
|
||||
import LocaleMenu from './_locale-menu'
|
||||
|
||||
const ThemeSelector = dynamic(() => import('@/app/components/base/theme-selector'), {
|
||||
|
||||
@@ -8,9 +8,9 @@ import type { UpdatePluginPayload } from '@/app/components/plugins/types'
|
||||
import type { InputVar } from '@/app/components/workflow/types'
|
||||
import type { ExternalDataTool } from '@/models/common'
|
||||
import type { ModerationConfig, PromptVariable } from '@/models/debug'
|
||||
import dynamic from 'next/dynamic'
|
||||
import { useCallback, useState } from 'react'
|
||||
import { PluginCategoryEnum } from '@/app/components/plugins/types'
|
||||
import dynamic from '@/next/dynamic'
|
||||
import { useTriggerEventsLimitModal } from './hooks/use-trigger-events-limit-modal'
|
||||
import { ModalContext } from './modal-context'
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ import type { ConfigParams, SettingsAppInfo } from '@/app/components/app/overvie
|
||||
import type { AppIconType } from '@/types/app'
|
||||
import { Button } from '@langgenius/dify-ui/button'
|
||||
import { useMutation, useQueryClient } from '@tanstack/react-query'
|
||||
import dynamic from 'next/dynamic'
|
||||
import { useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import {
|
||||
@@ -20,7 +21,6 @@ import { AccessPointUrl } from '@/app/components/base/access-point/url'
|
||||
import AppIcon from '@/app/components/base/app-icon'
|
||||
import { toast } from '@/app/notifications'
|
||||
import { getAgentACLCapabilities } from '@/features/agent-v2/acl'
|
||||
import dynamic from '@/next/dynamic'
|
||||
import { consoleQuery } from '@/service/console'
|
||||
import { AppModeEnum } from '@/types/app'
|
||||
import { getAgentWebAppUrl } from '../../web-app-access'
|
||||
|
||||
+1
-1
@@ -51,7 +51,7 @@ vi.mock('@/features/system-features/state', async () => {
|
||||
return createSystemFeaturesStateModuleMock(() => mockConsoleState)
|
||||
})
|
||||
|
||||
vi.mock('@/next/dynamic', async () => {
|
||||
vi.mock('next/dynamic', async () => {
|
||||
const { useState } = await import('react')
|
||||
|
||||
return {
|
||||
|
||||
+1
-1
@@ -13,6 +13,7 @@ import type { Inputs } from '@/models/debug'
|
||||
import { Avatar } from '@langgenius/dify-ui/avatar'
|
||||
import { cn } from '@langgenius/dify-ui/cn'
|
||||
import { useQueryClient, useSuspenseQuery } from '@tanstack/react-query'
|
||||
import dynamic from 'next/dynamic'
|
||||
import { useCallback, useImperativeHandle, useLayoutEffect, useRef, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { AgentRosterResponseContent } from '@/app/components/base/chat/chat/answer/agent-roster-response-content'
|
||||
@@ -21,7 +22,6 @@ import { getLastAnswer, isValidGeneratedAnswer } from '@/app/components/base/cha
|
||||
import { toast } from '@/app/notifications'
|
||||
import { useDocLink } from '@/context/i18n'
|
||||
import { userProfileQueryOptions } from '@/features/account-profile/client'
|
||||
import dynamic from '@/next/dynamic'
|
||||
import { consoleClient, consoleQuery } from '@/service/console'
|
||||
import { getAgentSoulInputs, getAgentSoulInputsForm } from './chat-config'
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ const mocks = vi.hoisted(() => ({
|
||||
})),
|
||||
}))
|
||||
|
||||
vi.mock('echarts-for-react', () => ({
|
||||
vi.mock('echarts-for-react/esm/core', () => ({
|
||||
default: ({ option, style }: { option: EChartsOption; style?: React.CSSProperties }) => {
|
||||
mocks.chartOptions.push(option)
|
||||
|
||||
|
||||
@@ -3,9 +3,10 @@
|
||||
import type { AgentMonitoringChartRow, AgentMonitoringChartType } from './chart-utils'
|
||||
import type { I18nKeysWithPrefix } from '@/types/i18n'
|
||||
import { Infotip, InfotipContent, InfotipTrigger } from '@langgenius/dify-ui/infotip'
|
||||
import ReactECharts from 'echarts-for-react'
|
||||
import ReactECharts from 'echarts-for-react/esm/core'
|
||||
import { useId } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { echarts } from '@/app/components/base/line-chart/echarts'
|
||||
import { buildChartOptions, getChartValueField, getTokenSummary } from './chart-utils'
|
||||
|
||||
type AgentMonitoringChartProps = {
|
||||
@@ -84,7 +85,7 @@ export function AgentMonitoringChart({
|
||||
</div>
|
||||
|
||||
<div className="h-60 px-6">
|
||||
<ReactECharts option={options} style={{ height: 240, width: '100%' }} />
|
||||
<ReactECharts echarts={echarts} option={options} style={{ height: 240, width: '100%' }} />
|
||||
</div>
|
||||
</article>
|
||||
)
|
||||
|
||||
@@ -7,6 +7,7 @@ import type { TrackCreateAppParams } from '@/utils/create-app-tracking'
|
||||
import { cn } from '@langgenius/dify-ui/cn'
|
||||
import { noop, useQueryClient, useSuspenseQueries, useSuspenseQuery } from '@tanstack/react-query'
|
||||
import { useAtomValue, useSetAtom } from 'jotai'
|
||||
import dynamic from 'next/dynamic'
|
||||
import { useQueryState } from 'nuqs'
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
@@ -30,7 +31,6 @@ import { workspacePermissionKeysAtom } from '@/context/permission-state'
|
||||
import { systemFeaturesQueryOptions } from '@/features/system-features/client'
|
||||
import { useImportDSL } from '@/hooks/use-import-dsl'
|
||||
import { DSLImportMode } from '@/models/app'
|
||||
import dynamic from '@/next/dynamic'
|
||||
import { consoleQuery } from '@/service/console'
|
||||
import { trackCreateApp } from '@/utils/create-app-tracking'
|
||||
import { hasPermission } from '@/utils/permission'
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
|
||||
import type { RecentAppResponse } from '@dify/contracts/api/console/apps/types.gen'
|
||||
import type { RecommendedAppResponse } from '@dify/contracts/api/console/explore/types.gen'
|
||||
import dynamic from 'next/dynamic'
|
||||
import { STEP_BY_STEP_TOUR_TARGETS } from '@/app/components/step-by-step-tour/target-registry'
|
||||
import dynamic from '@/next/dynamic'
|
||||
import { ContinueWork } from '../continue-work/continue-work'
|
||||
|
||||
const LearnDify = dynamic(() => import('@/app/components/explore/learn-dify'), { ssr: false })
|
||||
|
||||
@@ -11,13 +11,13 @@ import { Button } from '@langgenius/dify-ui/button'
|
||||
import { cn } from '@langgenius/dify-ui/cn'
|
||||
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
|
||||
import { noop } from 'es-toolkit/function'
|
||||
import dynamic from 'next/dynamic'
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { SkeletonRectangle } from '@/app/components/base/skeleton'
|
||||
import CodeEditor from '@/app/components/workflow/nodes/_base/components/editor/code-editor'
|
||||
import { toast } from '@/app/notifications'
|
||||
import { useFormatTimeFromNow } from '@/hooks/use-format-time-from-now'
|
||||
import dynamic from '@/next/dynamic'
|
||||
import { consoleQuery } from '@/service/console'
|
||||
import { downloadBlob } from '@/utils/download'
|
||||
import { fetchSkillFileBlob } from '../client'
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
'use client'
|
||||
|
||||
import dynamic from '@/next/dynamic'
|
||||
import dynamic from 'next/dynamic'
|
||||
|
||||
const TagManagementModal = dynamic(
|
||||
() =>
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
export { default } from 'next/dynamic'
|
||||
Reference in new issue
Block a user