mirror of
https://github.com/infiniflow/ragflow.git
synced 2026-07-19 06:01:05 +08:00
Feat/web markdown UI updates (#14214)
### What problem does this PR solve? LLM/chat and search UIs render Markdown in several places (document preview, floating chat widget, next-search, etc.). Plugin lists and behavior were duplicated or inconsistent, and single newlines in model output were not always rendered as visible line breaks, which hurts readability for chat-style content. This PR centralizes shared **remark/rehype** configuration (including **`remark-breaks`** for newline handling) and wires the main Markdown surfaces to use it, so behavior is consistent and easier to maintain. ### Type of change - [x] Refactoring --------- Co-authored-by: Yingfeng Zhang <yingfeng.zhang@gmail.com>
This commit is contained in:
30
web/package-lock.json
generated
30
web/package-lock.json
generated
@@ -100,6 +100,7 @@
|
||||
"recharts": "^2.12.4",
|
||||
"rehype-katex": "^7.0.1",
|
||||
"rehype-raw": "^7.0.0",
|
||||
"remark-breaks": "^4.0.0",
|
||||
"remark-gfm": "^4.0.0",
|
||||
"remark-math": "^6.0.0",
|
||||
"sonner": "^1.7.4",
|
||||
@@ -18439,6 +18440,20 @@
|
||||
"url": "https://opencollective.com/unified"
|
||||
}
|
||||
},
|
||||
"node_modules/mdast-util-newline-to-break": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmmirror.com/mdast-util-newline-to-break/-/mdast-util-newline-to-break-2.0.0.tgz",
|
||||
"integrity": "sha512-MbgeFca0hLYIEx/2zGsszCSEJJ1JSCdiY5xQxRcLDDGa8EPvlLPupJ4DSajbMPAnC0je8jfb9TiUATnxxrHUog==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/mdast": "^4.0.0",
|
||||
"mdast-util-find-and-replace": "^3.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/unified"
|
||||
}
|
||||
},
|
||||
"node_modules/mdast-util-phrasing": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmmirror.com/mdast-util-phrasing/-/mdast-util-phrasing-4.1.0.tgz",
|
||||
@@ -22539,6 +22554,21 @@
|
||||
"url": "https://opencollective.com/unified"
|
||||
}
|
||||
},
|
||||
"node_modules/remark-breaks": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmmirror.com/remark-breaks/-/remark-breaks-4.0.0.tgz",
|
||||
"integrity": "sha512-IjEjJOkH4FuJvHZVIW0QCDWxcG96kCq7An/KVH2NfJe6rKZU2AsHeB3OEjPNRxi4QC34Xdx7I2KGYn6IpT7gxQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/mdast": "^4.0.0",
|
||||
"mdast-util-newline-to-break": "^2.0.0",
|
||||
"unified": "^11.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/unified"
|
||||
}
|
||||
},
|
||||
"node_modules/remark-gfm": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmmirror.com/remark-gfm/-/remark-gfm-4.0.1.tgz",
|
||||
|
||||
@@ -122,6 +122,7 @@
|
||||
"recharts": "^2.12.4",
|
||||
"rehype-katex": "^7.0.1",
|
||||
"rehype-raw": "^7.0.0",
|
||||
"remark-breaks": "^4.0.0",
|
||||
"remark-gfm": "^4.0.0",
|
||||
"remark-math": "^6.0.0",
|
||||
"sonner": "^1.7.4",
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { Authorization } from '@/constants/authorization';
|
||||
import { MarkdownRemarkPluginsLite } from '@/constants/markdown-remark-plugins';
|
||||
import { cn } from '@/lib/utils';
|
||||
import FileError from '@/pages/document-viewer/file-error';
|
||||
import { getAuthorization } from '@/utils/authorization-util';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import ReactMarkdown from 'react-markdown';
|
||||
import remarkGfm from 'remark-gfm';
|
||||
|
||||
interface MdProps {
|
||||
// filePath: string;
|
||||
@@ -34,7 +34,9 @@ export const Md: React.FC<MdProps> = ({ url, className }) => {
|
||||
style={{ padding: 4, overflow: 'scroll' }}
|
||||
className={cn(className, 'markdown-body h-[calc(100vh - 200px)]')}
|
||||
>
|
||||
<ReactMarkdown remarkPlugins={[remarkGfm]}>{content}</ReactMarkdown>
|
||||
<ReactMarkdown remarkPlugins={MarkdownRemarkPluginsLite}>
|
||||
{content}
|
||||
</ReactMarkdown>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -36,8 +36,7 @@ import {
|
||||
} from 'react-syntax-highlighter/dist/esm/styles/prism';
|
||||
import rehypeKatex from 'rehype-katex';
|
||||
import rehypeRaw from 'rehype-raw';
|
||||
import remarkGfm from 'remark-gfm';
|
||||
import remarkMath from 'remark-math';
|
||||
import { MarkdownRemarkPlugins } from '@/constants/markdown-remark-plugins';
|
||||
import { visitParents } from 'unist-util-visit-parents';
|
||||
import styles from './floating-chat-widget-markdown.module.less';
|
||||
import { useIsDarkTheme } from './theme-provider';
|
||||
@@ -292,7 +291,7 @@ const FloatingChatWidgetMarkdown = ({
|
||||
<div className="floating-chat-widget" dir={dir}>
|
||||
<Markdown
|
||||
rehypePlugins={[rehypeWrapReference, rehypeKatex, rehypeRaw]}
|
||||
remarkPlugins={[remarkGfm, remarkMath]}
|
||||
remarkPlugins={MarkdownRemarkPlugins}
|
||||
className="text-sm leading-relaxed space-y-2 prose-sm max-w-full"
|
||||
components={
|
||||
{
|
||||
|
||||
@@ -7,8 +7,7 @@ import {
|
||||
} from 'react-syntax-highlighter/dist/esm/styles/prism';
|
||||
import rehypeKatex from 'rehype-katex';
|
||||
import rehypeRaw from 'rehype-raw';
|
||||
import remarkGfm from 'remark-gfm';
|
||||
import remarkMath from 'remark-math';
|
||||
import { MarkdownRemarkPlugins } from '@/constants/markdown-remark-plugins';
|
||||
|
||||
import 'katex/dist/katex.min.css'; // `rehype-katex` does not import the CSS for you
|
||||
|
||||
@@ -33,7 +32,7 @@ const HighLightMarkdown = ({
|
||||
return (
|
||||
<div dir={dir} className={classNames(styles.text)}>
|
||||
<Markdown
|
||||
remarkPlugins={[remarkGfm, remarkMath]}
|
||||
remarkPlugins={MarkdownRemarkPlugins}
|
||||
rehypePlugins={[rehypeRaw, rehypeKatex]}
|
||||
components={
|
||||
{
|
||||
|
||||
@@ -10,8 +10,7 @@ import Markdown from 'react-markdown';
|
||||
import SyntaxHighlighter from 'react-syntax-highlighter';
|
||||
import rehypeKatex from 'rehype-katex';
|
||||
import rehypeRaw from 'rehype-raw';
|
||||
import remarkGfm from 'remark-gfm';
|
||||
import remarkMath from 'remark-math';
|
||||
import { MarkdownRemarkPlugins } from '@/constants/markdown-remark-plugins';
|
||||
import { visitParents } from 'unist-util-visit-parents';
|
||||
|
||||
import { useTranslation } from 'react-i18next';
|
||||
@@ -262,7 +261,7 @@ const MarkdownContent = ({
|
||||
<div dir={dir} className={styles.markdownContentWrapper}>
|
||||
<Markdown
|
||||
rehypePlugins={[rehypeWrapReference, rehypeKatex, rehypeRaw]}
|
||||
remarkPlugins={[remarkGfm, remarkMath]}
|
||||
remarkPlugins={MarkdownRemarkPlugins}
|
||||
components={
|
||||
{
|
||||
p: ({ children, ...props }: any) => <p {...props}>{children}</p>,
|
||||
|
||||
@@ -10,8 +10,7 @@ import Markdown from 'react-markdown';
|
||||
import SyntaxHighlighter from 'react-syntax-highlighter';
|
||||
import rehypeKatex from 'rehype-katex';
|
||||
import rehypeRaw from 'rehype-raw';
|
||||
import remarkGfm from 'remark-gfm';
|
||||
import remarkMath from 'remark-math';
|
||||
import { MarkdownRemarkPlugins } from '@/constants/markdown-remark-plugins';
|
||||
import { visitParents } from 'unist-util-visit-parents';
|
||||
|
||||
import { useTranslation } from 'react-i18next';
|
||||
@@ -355,7 +354,7 @@ function MarkdownContent({
|
||||
<div dir={dir} className={styles.markdownContentWrapper}>
|
||||
<Markdown
|
||||
rehypePlugins={[rehypeWrapReference, rehypeKatex, rehypeRaw]}
|
||||
remarkPlugins={[remarkGfm, remarkMath]}
|
||||
remarkPlugins={MarkdownRemarkPlugins}
|
||||
components={
|
||||
{
|
||||
p: ({ children, ...props }: any) => <p {...props}>{children}</p>,
|
||||
|
||||
17
web/src/constants/markdown-remark-plugins.ts
Normal file
17
web/src/constants/markdown-remark-plugins.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import remarkBreaks from 'remark-breaks';
|
||||
import remarkGfm from 'remark-gfm';
|
||||
import remarkMath from 'remark-math';
|
||||
|
||||
/**
|
||||
* GFM + line breaks only (no TeX). For surfaces that do not wire rehype-katex
|
||||
* (e.g. uploaded document preview).
|
||||
*/
|
||||
export const MarkdownRemarkPluginsLite = [remarkGfm, remarkBreaks];
|
||||
|
||||
/**
|
||||
* Shared Markdown pipeline for assistant-style content:
|
||||
* - remark-gfm: GFM tables, task lists, strikethrough, autolinks, etc.
|
||||
* - remark-math: TeX ($...$ / $$...$$); pair with rehype-katex on render.
|
||||
* - remark-breaks: treat single newlines as hard breaks (common in LLM chat).
|
||||
*/
|
||||
export const MarkdownRemarkPlugins = [remarkGfm, remarkMath, remarkBreaks];
|
||||
@@ -8,8 +8,7 @@ import Markdown from 'react-markdown';
|
||||
import SyntaxHighlighter from 'react-syntax-highlighter';
|
||||
import rehypeKatex from 'rehype-katex';
|
||||
import rehypeRaw from 'rehype-raw';
|
||||
import remarkGfm from 'remark-gfm';
|
||||
import remarkMath from 'remark-math';
|
||||
import { MarkdownRemarkPlugins } from '@/constants/markdown-remark-plugins';
|
||||
import { visitParents } from 'unist-util-visit-parents';
|
||||
|
||||
import { useTranslation } from 'react-i18next';
|
||||
@@ -250,7 +249,7 @@ const MarkdownContent = ({
|
||||
>
|
||||
<Markdown
|
||||
rehypePlugins={[rehypeWrapReference, rehypeKatex, rehypeRaw]}
|
||||
remarkPlugins={[remarkGfm, remarkMath]}
|
||||
remarkPlugins={MarkdownRemarkPlugins}
|
||||
components={
|
||||
{
|
||||
p: ({ children, ...props }: any) => <p {...props}>{children}</p>,
|
||||
|
||||
Reference in New Issue
Block a user