You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

index.tsx 2.8KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788
  1. import ReactMarkdown from 'react-markdown'
  2. import 'katex/dist/katex.min.css'
  3. import RemarkMath from 'remark-math'
  4. import RemarkBreaks from 'remark-breaks'
  5. import RehypeKatex from 'rehype-katex'
  6. import RemarkGfm from 'remark-gfm'
  7. import RehypeRaw from 'rehype-raw'
  8. import { flow } from 'lodash-es'
  9. import cn from '@/utils/classnames'
  10. import { customUrlTransform, preprocessLaTeX, preprocessThinkTag } from './markdown-utils'
  11. import {
  12. AudioBlock,
  13. CodeBlock,
  14. Img,
  15. Link,
  16. MarkdownButton,
  17. MarkdownForm,
  18. Paragraph,
  19. ScriptBlock,
  20. ThinkBlock,
  21. VideoBlock,
  22. } from '@/app/components/base/markdown-blocks'
  23. /**
  24. * @fileoverview Main Markdown rendering component.
  25. * This file was refactored to extract individual block renderers and utility functions
  26. * into separate modules for better organization and maintainability as of [Date of refactor].
  27. * Further refactoring candidates (custom block components not fitting general categories)
  28. * are noted in their respective files if applicable.
  29. */
  30. export function Markdown(props: { content: string; className?: string; customDisallowedElements?: string[] }) {
  31. const latexContent = flow([
  32. preprocessThinkTag,
  33. preprocessLaTeX,
  34. ])(props.content)
  35. return (
  36. <div className={cn('markdown-body', '!text-text-primary', props.className)}>
  37. <ReactMarkdown
  38. remarkPlugins={[
  39. RemarkGfm,
  40. [RemarkMath, { singleDollarTextMath: false }],
  41. RemarkBreaks,
  42. ]}
  43. rehypePlugins={[
  44. RehypeKatex,
  45. RehypeRaw as any,
  46. // The Rehype plug-in is used to remove the ref attribute of an element
  47. () => {
  48. return (tree: any) => {
  49. const iterate = (node: any) => {
  50. if (node.type === 'element' && node.properties?.ref)
  51. delete node.properties.ref
  52. if (node.type === 'element' && !/^[a-z][a-z0-9]*$/i.test(node.tagName)) {
  53. node.type = 'text'
  54. node.value = `<${node.tagName}`
  55. }
  56. if (node.children)
  57. node.children.forEach(iterate)
  58. }
  59. tree.children.forEach(iterate)
  60. }
  61. },
  62. ]}
  63. urlTransform={customUrlTransform}
  64. disallowedElements={['iframe', 'head', 'html', 'meta', 'link', 'style', 'body', ...(props.customDisallowedElements || [])]}
  65. components={{
  66. code: CodeBlock,
  67. img: Img,
  68. video: VideoBlock,
  69. audio: AudioBlock,
  70. a: Link,
  71. p: Paragraph,
  72. button: MarkdownButton,
  73. form: MarkdownForm,
  74. script: ScriptBlock as any,
  75. details: ThinkBlock,
  76. }}
  77. >
  78. {/* Markdown detect has problem. */}
  79. {latexContent}
  80. </ReactMarkdown>
  81. </div>
  82. )
  83. }