diff --git a/web/app/components/base/markdown-blocks/code-block.tsx b/web/app/components/base/markdown-blocks/code-block.tsx index 48de8bf4ab..6814659a00 100644 --- a/web/app/components/base/markdown-blocks/code-block.tsx +++ b/web/app/components/base/markdown-blocks/code-block.tsx @@ -8,12 +8,14 @@ import { import ActionButton from '@/app/components/base/action-button' import CopyIcon from '@/app/components/base/copy-icon' import SVGBtn from '@/app/components/base/svg' -import Flowchart from '@/app/components/base/mermaid' import { Theme } from '@/types/app' import useTheme from '@/hooks/use-theme' import SVGRenderer from '../svg-gallery' // Assumes svg-gallery.tsx is in /base directory import MarkdownMusic from '@/app/components/base/markdown-blocks/music' import ErrorBoundary from '@/app/components/base/markdown/error-boundary' +import dynamic from 'next/dynamic' + +const Flowchart = dynamic(() => import('@/app/components/base/mermaid'), { ssr: false }) // Available language https://github.com/react-syntax-highlighter/react-syntax-highlighter/blob/master/AVAILABLE_LANGUAGES_HLJS.MD const capitalizationLanguageNameMap: Record = { diff --git a/web/app/components/base/markdown/index.tsx b/web/app/components/base/markdown/index.tsx index bab5ac8eba..19f39d8aaa 100644 --- a/web/app/components/base/markdown/index.tsx +++ b/web/app/components/base/markdown/index.tsx @@ -1,25 +1,11 @@ -import ReactMarkdown from 'react-markdown' +import dynamic from 'next/dynamic' import 'katex/dist/katex.min.css' -import RemarkMath from 'remark-math' -import RemarkBreaks from 'remark-breaks' -import RehypeKatex from 'rehype-katex' -import RemarkGfm from 'remark-gfm' -import RehypeRaw from 'rehype-raw' import { flow } from 'lodash-es' import cn from '@/utils/classnames' -import { customUrlTransform, preprocessLaTeX, preprocessThinkTag } from './markdown-utils' -import { - AudioBlock, - CodeBlock, - Img, - Link, - MarkdownButton, - MarkdownForm, - Paragraph, - ScriptBlock, - ThinkBlock, - VideoBlock, -} from '@/app/components/base/markdown-blocks' +import { preprocessLaTeX, preprocessThinkTag } from './markdown-utils' +import type { ReactMarkdownWrapperProps } from './react-markdown-wrapper' + +const ReactMarkdown = dynamic(() => import('./react-markdown-wrapper').then(mod => mod.ReactMarkdownWrapper), { ssr: false }) /** * @fileoverview Main Markdown rendering component. @@ -31,9 +17,7 @@ import { export type MarkdownProps = { content: string className?: string - customDisallowedElements?: string[] - customComponents?: Record> -} +} & Pick export const Markdown = (props: MarkdownProps) => { const { customComponents = {} } = props @@ -44,53 +28,7 @@ export const Markdown = (props: MarkdownProps) => { return (
- { - return (tree: any) => { - const iterate = (node: any) => { - if (node.type === 'element' && node.properties?.ref) - delete node.properties.ref - - if (node.type === 'element' && !/^[a-z][a-z0-9]*$/i.test(node.tagName)) { - node.type = 'text' - node.value = `<${node.tagName}` - } - - if (node.children) - node.children.forEach(iterate) - } - tree.children.forEach(iterate) - } - }, - ]} - urlTransform={customUrlTransform} - disallowedElements={['iframe', 'head', 'html', 'meta', 'link', 'style', 'body', ...(props.customDisallowedElements || [])]} - components={{ - code: CodeBlock, - img: Img, - video: VideoBlock, - audio: AudioBlock, - a: Link, - p: Paragraph, - button: MarkdownButton, - form: MarkdownForm, - script: ScriptBlock as any, - details: ThinkBlock, - ...customComponents, - }} - > - {/* Markdown detect has problem. */} - {latexContent} - +
) } diff --git a/web/app/components/base/markdown/react-markdown-wrapper.tsx b/web/app/components/base/markdown/react-markdown-wrapper.tsx new file mode 100644 index 0000000000..054b5f66cb --- /dev/null +++ b/web/app/components/base/markdown/react-markdown-wrapper.tsx @@ -0,0 +1,82 @@ +import ReactMarkdown from 'react-markdown' +import RemarkMath from 'remark-math' +import RemarkBreaks from 'remark-breaks' +import RehypeKatex from 'rehype-katex' +import RemarkGfm from 'remark-gfm' +import RehypeRaw from 'rehype-raw' +import AudioBlock from '@/app/components/base/markdown-blocks/audio-block' +import Img from '@/app/components/base/markdown-blocks/img' +import Link from '@/app/components/base/markdown-blocks/link' +import MarkdownButton from '@/app/components/base/markdown-blocks/button' +import MarkdownForm from '@/app/components/base/markdown-blocks/form' +import Paragraph from '@/app/components/base/markdown-blocks/paragraph' +import ScriptBlock from '@/app/components/base/markdown-blocks/script-block' +import ThinkBlock from '@/app/components/base/markdown-blocks/think-block' +import VideoBlock from '@/app/components/base/markdown-blocks/video-block' +import { customUrlTransform } from './markdown-utils' + +import type { FC } from 'react' + +import dynamic from 'next/dynamic' + +const CodeBlock = dynamic(() => import('@/app/components/base/markdown-blocks/code-block'), { ssr: false }) + +export type ReactMarkdownWrapperProps = { + latexContent: any + customDisallowedElements?: string[] + customComponents?: Record> +} + +export const ReactMarkdownWrapper: FC = (props) => { + const { customComponents, latexContent } = props + + return ( + { + return (tree: any) => { + const iterate = (node: any) => { + if (node.type === 'element' && node.properties?.ref) + delete node.properties.ref + + if (node.type === 'element' && !/^[a-z][a-z0-9]*$/i.test(node.tagName)) { + node.type = 'text' + node.value = `<${node.tagName}` + } + + if (node.children) + node.children.forEach(iterate) + } + tree.children.forEach(iterate) + } + }, + ]} + urlTransform={customUrlTransform} + disallowedElements={['iframe', 'head', 'html', 'meta', 'link', 'style', 'body', ...(props.customDisallowedElements || [])]} + components={{ + code: CodeBlock, + img: Img, + video: VideoBlock, + audio: AudioBlock, + a: Link, + p: Paragraph, + button: MarkdownButton, + form: MarkdownForm, + script: ScriptBlock as any, + details: ThinkBlock, + ...customComponents, + }} + > + {/* Markdown detect has problem. */} + {latexContent} + + ) +} diff --git a/web/app/components/datasets/documents/create-from-pipeline/data-source/local-file/index.tsx b/web/app/components/datasets/documents/create-from-pipeline/data-source/local-file/index.tsx index 144decada5..0692680005 100644 --- a/web/app/components/datasets/documents/create-from-pipeline/data-source/local-file/index.tsx +++ b/web/app/components/datasets/documents/create-from-pipeline/data-source/local-file/index.tsx @@ -7,7 +7,6 @@ import DocumentFileIcon from '@/app/components/datasets/common/document-file-ico import cn from '@/utils/classnames' import type { CustomFile as File, FileItem } from '@/models/datasets' import { ToastContext } from '@/app/components/base/toast' -import SimplePieChart from '@/app/components/base/simple-pie-chart' import { upload } from '@/service/base' import I18n from '@/context/i18n' import { LanguagesSupported } from '@/i18n-config/language' @@ -17,6 +16,9 @@ import useTheme from '@/hooks/use-theme' import { useFileUploadConfig } from '@/service/use-common' import { useDataSourceStore, useDataSourceStoreWithSelector } from '../store' import produce from 'immer' +import dynamic from 'next/dynamic' + +const SimplePieChart = dynamic(() => import('@/app/components/base/simple-pie-chart'), { ssr: false }) const FILES_NUMBER_LIMIT = 20 diff --git a/web/app/components/workflow-app/index.tsx b/web/app/components/workflow-app/index.tsx index c18a86f981..05654f4e74 100644 --- a/web/app/components/workflow-app/index.tsx +++ b/web/app/components/workflow-app/index.tsx @@ -8,7 +8,7 @@ import { } from '@/app/components/workflow/types' import { useWorkflowInit, -} from './hooks' +} from './hooks/use-workflow-init' import { initialEdges, initialNodes, diff --git a/web/app/components/workflow/hooks/use-workflow-history.ts b/web/app/components/workflow/hooks/use-workflow-history.ts index b7338dc4f8..a9b2f0f699 100644 --- a/web/app/components/workflow/hooks/use-workflow-history.ts +++ b/web/app/components/workflow/hooks/use-workflow-history.ts @@ -16,23 +16,25 @@ import type { WorkflowHistoryEventMeta } from '../workflow-history-store' * - InputChange events in Node Panels do not trigger state changes. * - Resizing UI elements does not trigger state changes. */ -export enum WorkflowHistoryEvent { - NodeTitleChange = 'NodeTitleChange', - NodeDescriptionChange = 'NodeDescriptionChange', - NodeDragStop = 'NodeDragStop', - NodeChange = 'NodeChange', - NodeConnect = 'NodeConnect', - NodePaste = 'NodePaste', - NodeDelete = 'NodeDelete', - EdgeDelete = 'EdgeDelete', - EdgeDeleteByDeleteBranch = 'EdgeDeleteByDeleteBranch', - NodeAdd = 'NodeAdd', - NodeResize = 'NodeResize', - NoteAdd = 'NoteAdd', - NoteChange = 'NoteChange', - NoteDelete = 'NoteDelete', - LayoutOrganize = 'LayoutOrganize', -} +export const WorkflowHistoryEvent = { + NodeTitleChange: 'NodeTitleChange', + NodeDescriptionChange: 'NodeDescriptionChange', + NodeDragStop: 'NodeDragStop', + NodeChange: 'NodeChange', + NodeConnect: 'NodeConnect', + NodePaste: 'NodePaste', + NodeDelete: 'NodeDelete', + EdgeDelete: 'EdgeDelete', + EdgeDeleteByDeleteBranch: 'EdgeDeleteByDeleteBranch', + NodeAdd: 'NodeAdd', + NodeResize: 'NodeResize', + NoteAdd: 'NoteAdd', + NoteChange: 'NoteChange', + NoteDelete: 'NoteDelete', + LayoutOrganize: 'LayoutOrganize', +} as const + +export type WorkflowHistoryEventT = keyof typeof WorkflowHistoryEvent export const useWorkflowHistory = () => { const store = useStoreApi() @@ -65,7 +67,7 @@ export const useWorkflowHistory = () => { // Some events may be triggered multiple times in a short period of time. // We debounce the history state update to avoid creating multiple history states // with minimal changes. - const saveStateToHistoryRef = useRef(debounce((event: WorkflowHistoryEvent, meta?: WorkflowHistoryEventMeta) => { + const saveStateToHistoryRef = useRef(debounce((event: WorkflowHistoryEventT, meta?: WorkflowHistoryEventMeta) => { workflowHistoryStore.setState({ workflowHistoryEvent: event, workflowHistoryEventMeta: meta, @@ -74,7 +76,7 @@ export const useWorkflowHistory = () => { }) }, 500)) - const saveStateToHistory = useCallback((event: WorkflowHistoryEvent, meta?: WorkflowHistoryEventMeta) => { + const saveStateToHistory = useCallback((event: WorkflowHistoryEventT, meta?: WorkflowHistoryEventMeta) => { switch (event) { case WorkflowHistoryEvent.NoteChange: // Hint: Note change does not trigger when note text changes, @@ -105,7 +107,7 @@ export const useWorkflowHistory = () => { } }, []) - const getHistoryLabel = useCallback((event: WorkflowHistoryEvent) => { + const getHistoryLabel = useCallback((event: WorkflowHistoryEventT) => { switch (event) { case WorkflowHistoryEvent.NodeTitleChange: return t('workflow.changeHistory.nodeTitleChange') diff --git a/web/app/components/workflow/workflow-history-store.tsx b/web/app/components/workflow/workflow-history-store.tsx index c250708177..96e87f4fd4 100644 --- a/web/app/components/workflow/workflow-history-store.tsx +++ b/web/app/components/workflow/workflow-history-store.tsx @@ -3,7 +3,7 @@ import { type StoreApi, create } from 'zustand' import { type TemporalState, temporal } from 'zundo' import isDeepEqual from 'fast-deep-equal' import type { Edge, Node } from './types' -import type { WorkflowHistoryEvent } from './hooks' +import type { WorkflowHistoryEventT } from './hooks' import { noop } from 'lodash-es' export const WorkflowHistoryStoreContext = createContext({ store: null, shortcutsEnabled: true, setShortcutsEnabled: noop }) @@ -98,7 +98,7 @@ function createStore({ export type WorkflowHistoryStore = { nodes: Node[] edges: Edge[] - workflowHistoryEvent: WorkflowHistoryEvent | undefined + workflowHistoryEvent: WorkflowHistoryEventT | undefined workflowHistoryEventMeta?: WorkflowHistoryEventMeta }