markdown.py 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899
  1. """Markdown component."""
  2. import textwrap
  3. from typing import List, Union
  4. from reflex.components.component import Component
  5. from reflex.utils import types
  6. from reflex.vars import BaseVar, ImportVar, Var
  7. class Markdown(Component):
  8. """A markdown component."""
  9. library = "react-markdown"
  10. tag = "ReactMarkdown"
  11. is_default = True
  12. @classmethod
  13. def create(cls, *children, **props) -> Component:
  14. """Create a markdown component.
  15. Args:
  16. children: The children of the component.
  17. props: The properties of the component.
  18. Returns:
  19. The markdown component.
  20. """
  21. assert len(children) == 1 and types._isinstance(
  22. children[0], Union[str, Var]
  23. ), "Markdown component must have exactly one child containing the markdown source."
  24. # Get the markdown source.
  25. src = children[0]
  26. if isinstance(src, str):
  27. src = textwrap.dedent(src)
  28. return super().create(src, **props)
  29. def _get_imports(self):
  30. imports = super()._get_imports()
  31. imports["@chakra-ui/react"] = {
  32. ImportVar(tag="Heading"),
  33. ImportVar(tag="Code"),
  34. ImportVar(tag="Text"),
  35. ImportVar(tag="Link"),
  36. ImportVar(tag="UnorderedList"),
  37. ImportVar(tag="OrderedList"),
  38. ImportVar(tag="ListItem"),
  39. }
  40. imports["react-syntax-highlighter"] = {ImportVar(tag="Prism", is_default=True)}
  41. imports["remark-math"] = {ImportVar(tag="remarkMath", is_default=True)}
  42. imports["remark-gfm"] = {ImportVar(tag="remarkGfm", is_default=True)}
  43. imports["rehype-katex"] = {ImportVar(tag="rehypeKatex", is_default=True)}
  44. imports["rehype-raw"] = {ImportVar(tag="rehypeRaw", is_default=True)}
  45. imports[""] = {ImportVar(tag="katex/dist/katex.min.css")}
  46. return imports
  47. def _render(self):
  48. return (
  49. super()
  50. ._render()
  51. .add_props(
  52. components={
  53. "h1": "{({node, ...props}) => <Heading size='2xl' paddingY='0.5em' {...props} />}",
  54. "h2": "{({node, ...props}) => <Heading size='xl' paddingY='0.5em' {...props} />}",
  55. "h3": "{({node, ...props}) => <Heading size='lg' paddingY='0.5em' {...props} />}",
  56. "h4": "{({node, ...props}) => <Heading size='sm' paddingY='0.5em' {...props} />}",
  57. "h5": "{({node, ...props}) => <Heading size='xs' paddingY='0.5em' {...props} />}",
  58. "ul": "{UnorderedList}",
  59. "ol": "{OrderedList}",
  60. "li": "{ListItem}",
  61. "p": "{({node, ...props}) => <Text paddingY='0.5em' {...props} />}",
  62. "a": "{Link}",
  63. "code": """{({node, inline, className, children, ...props}) =>
  64. {
  65. const match = (className || '').match(/language-(?<lang>.*)/);
  66. return !inline ? (
  67. <Prism
  68. children={String(children).replace(/\n$/, '')}
  69. language={match ? match[1] : ''}
  70. {...props}
  71. />
  72. ) : (
  73. <Code {...props}>
  74. {children}
  75. </Code>
  76. );
  77. }}""".replace(
  78. "\n", " "
  79. ),
  80. },
  81. remark_plugins=BaseVar(name="[remarkMath, remarkGfm]", type_=List[str]),
  82. rehype_plugins=BaseVar(
  83. name="[rehypeKatex, rehypeRaw]", type_=List[str]
  84. ),
  85. )
  86. )