diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 14480bf3..db22eb90 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -19,6 +19,7 @@ "file-saver": "^2.0.5", "html-midi-player": "^1.5.0", "i18next": "^22.4.15", + "katex": "^0.16.9", "lodash-es": "^4.17.21", "mobx": "^6.9.0", "mobx-react-lite": "^3.4.3", @@ -34,9 +35,11 @@ "react-router-dom": "^6.11.1", "react-toastify": "^9.1.3", "rehype-highlight": "^6.0.0", + "rehype-katex": "^6.0.3", "rehype-raw": "^6.1.1", "remark-breaks": "^3.0.3", "remark-gfm": "^3.0.1", + "remark-math": "^5.1.1", "usehooks-ts": "^2.9.1", "uuid": "^9.0.0" }, @@ -2565,6 +2568,11 @@ "hoist-non-react-statics": "^3.3.0" } }, + "node_modules/@types/katex": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/@types/katex/-/katex-0.14.0.tgz", + "integrity": "sha512-+2FW2CcT0K3P+JMR8YG846bmDwplKUTsWgT2ENwdQ1UdVfRk3GQrh6Mi4sTopy30gI8Uau5CEqHTDZ6YvWIUPA==" + }, "node_modules/@types/lodash": { "version": "4.14.202", "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.202.tgz", @@ -3492,6 +3500,17 @@ "resolved": "https://registry.npmmirror.com/emoji-regex/-/emoji-regex-8.0.0.tgz", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, "node_modules/esbuild": { "version": "0.17.19", "resolved": "https://registry.npmmirror.com/esbuild/-/esbuild-0.17.19.tgz", @@ -3888,6 +3907,61 @@ "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==", "optional": true }, + "node_modules/hast-util-from-dom": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/hast-util-from-dom/-/hast-util-from-dom-4.2.0.tgz", + "integrity": "sha512-t1RJW/OpJbCAJQeKi3Qrj1cAOLA0+av/iPFori112+0X7R3wng+jxLA+kXec8K4szqPRGI8vPxbbpEYvvpwaeQ==", + "dependencies": { + "hastscript": "^7.0.0", + "web-namespaces": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-from-html": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/hast-util-from-html/-/hast-util-from-html-1.0.2.tgz", + "integrity": "sha512-LhrTA2gfCbLOGJq2u/asp4kwuG0y6NhWTXiPKP+n0qNukKy7hc10whqqCFfyvIA1Q5U5d0sp9HhNim9gglEH4A==", + "dependencies": { + "@types/hast": "^2.0.0", + "hast-util-from-parse5": "^7.0.0", + "parse5": "^7.0.0", + "vfile": "^5.0.0", + "vfile-message": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-from-html-isomorphic": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/hast-util-from-html-isomorphic/-/hast-util-from-html-isomorphic-1.0.0.tgz", + "integrity": "sha512-Yu480AKeOEN/+l5LA674a+7BmIvtDj24GvOt7MtQWuhzUwlaaRWdEPXAh3Qm5vhuthpAipFb2vTetKXWOjmTvw==", + "dependencies": { + "@types/hast": "^2.0.0", + "hast-util-from-dom": "^4.0.0", + "hast-util-from-html": "^1.0.0", + "unist-util-remove-position": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-from-html/node_modules/parse5": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz", + "integrity": "sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==", + "dependencies": { + "entities": "^4.4.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, "node_modules/hast-util-from-parse5": { "version": "7.1.2", "resolved": "https://registry.npmmirror.com/hast-util-from-parse5/-/hast-util-from-parse5-7.1.2.tgz", @@ -4214,6 +4288,29 @@ "node": ">=6" } }, + "node_modules/katex": { + "version": "0.16.9", + "resolved": "https://registry.npmjs.org/katex/-/katex-0.16.9.tgz", + "integrity": "sha512-fsSYjWS0EEOwvy81j3vRA8TEAhQhKiqO+FQaKWp0m39qwOzHVBgAUBIXWj1pB+O2W3fIpNa6Y9KSKCVbfPhyAQ==", + "funding": [ + "https://opencollective.com/katex", + "https://github.com/sponsors/katex" + ], + "dependencies": { + "commander": "^8.3.0" + }, + "bin": { + "katex": "cli.js" + } + }, + "node_modules/katex/node_modules/commander": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", + "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", + "engines": { + "node": ">= 12" + } + }, "node_modules/keyborg": { "version": "2.0.0", "resolved": "https://registry.npmmirror.com/keyborg/-/keyborg-2.0.0.tgz", @@ -4469,6 +4566,20 @@ "mdast-util-to-markdown": "^1.3.0" } }, + "node_modules/mdast-util-math": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/mdast-util-math/-/mdast-util-math-2.0.2.tgz", + "integrity": "sha512-8gmkKVp9v6+Tgjtq6SYx9kGPpTf6FVYRa53/DLh479aldR9AyP48qeVOgNZ5X7QUK7nOy4yw7vg6mbiGcs9jWQ==", + "dependencies": { + "@types/mdast": "^3.0.0", + "longest-streak": "^3.0.0", + "mdast-util-to-markdown": "^1.3.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/mdast-util-newline-to-break": { "version": "1.0.0", "resolved": "https://registry.npmmirror.com/mdast-util-newline-to-break/-/mdast-util-newline-to-break-1.0.0.tgz", @@ -4672,6 +4783,29 @@ "uvu": "^0.5.0" } }, + "node_modules/micromark-extension-math": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/micromark-extension-math/-/micromark-extension-math-2.1.2.tgz", + "integrity": "sha512-es0CcOV89VNS9wFmyn+wyFTKweXGW4CEvdaAca6SWRWPyYCbBisnjaHLjWO4Nszuiud84jCpkHsqAJoa768Pvg==", + "dependencies": { + "@types/katex": "^0.16.0", + "katex": "^0.16.0", + "micromark-factory-space": "^1.0.0", + "micromark-util-character": "^1.0.0", + "micromark-util-symbol": "^1.0.0", + "micromark-util-types": "^1.0.0", + "uvu": "^0.5.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/micromark-extension-math/node_modules/@types/katex": { + "version": "0.16.7", + "resolved": "https://registry.npmjs.org/@types/katex/-/katex-0.16.7.tgz", + "integrity": "sha512-HMwFiRujE5PjrgwHQ25+bsLJgowjGjm5Z8FVSf0N6PwgJrwxH0QxzHYDcKsTfV3wva0vzrpqMTJS2jXPr5BMEQ==" + }, "node_modules/micromark-factory-destination": { "version": "1.0.0", "resolved": "https://registry.npmmirror.com/micromark-factory-destination/-/micromark-factory-destination-1.0.0.tgz", @@ -5697,6 +5831,23 @@ "unist-util-visit": "^4.0.0" } }, + "node_modules/rehype-katex": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/rehype-katex/-/rehype-katex-6.0.3.tgz", + "integrity": "sha512-ByZlRwRUcWegNbF70CVRm2h/7xy7jQ3R9LaY4VVSvjnoVWwWVhNL60DiZsBpC5tSzYQOCvDbzncIpIjPZWodZA==", + "dependencies": { + "@types/hast": "^2.0.0", + "@types/katex": "^0.14.0", + "hast-util-from-html-isomorphic": "^1.0.0", + "hast-util-to-text": "^3.1.0", + "katex": "^0.16.0", + "unist-util-visit": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/rehype-raw": { "version": "6.1.1", "resolved": "https://registry.npmmirror.com/rehype-raw/-/rehype-raw-6.1.1.tgz", @@ -5728,6 +5879,21 @@ "unified": "^10.0.0" } }, + "node_modules/remark-math": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/remark-math/-/remark-math-5.1.1.tgz", + "integrity": "sha512-cE5T2R/xLVtfFI4cCePtiRn+e6jKMtFDR3P8V3qpv8wpKjwvHoBA4eJzvX+nVrnlNy0911bdGmuspCSwetfYHw==", + "dependencies": { + "@types/mdast": "^3.0.0", + "mdast-util-math": "^2.0.0", + "micromark-extension-math": "^2.0.0", + "unified": "^10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/remark-parse": { "version": "10.0.2", "resolved": "https://registry.npmmirror.com/remark-parse/-/remark-parse-10.0.2.tgz", @@ -6590,6 +6756,19 @@ "@types/unist": "^2.0.0" } }, + "node_modules/unist-util-remove-position": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/unist-util-remove-position/-/unist-util-remove-position-4.0.2.tgz", + "integrity": "sha512-TkBb0HABNmxzAcfLf4qsIbFbaPDvMO6wa3b3j4VcEzFVaw1LBKwnW4/sRJ/atSLSzoIg41JWEdnE7N6DIhGDGQ==", + "dependencies": { + "@types/unist": "^2.0.0", + "unist-util-visit": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/unist-util-stringify-position": { "version": "3.0.3", "resolved": "https://registry.npmmirror.com/unist-util-stringify-position/-/unist-util-stringify-position-3.0.3.tgz", diff --git a/frontend/package.json b/frontend/package.json index 009c9ff7..f0806374 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -20,6 +20,7 @@ "file-saver": "^2.0.5", "html-midi-player": "^1.5.0", "i18next": "^22.4.15", + "katex": "^0.16.9", "lodash-es": "^4.17.21", "mobx": "^6.9.0", "mobx-react-lite": "^3.4.3", @@ -35,9 +36,11 @@ "react-router-dom": "^6.11.1", "react-toastify": "^9.1.3", "rehype-highlight": "^6.0.0", + "rehype-katex": "^6.0.3", "rehype-raw": "^6.1.1", "remark-breaks": "^3.0.3", "remark-gfm": "^3.0.1", + "remark-math": "^5.1.1", "usehooks-ts": "^2.9.1", "uuid": "^9.0.0" }, diff --git a/frontend/src/components/MarkdownRender.tsx b/frontend/src/components/MarkdownRender.tsx index bfdee294..27977a77 100644 --- a/frontend/src/components/MarkdownRender.tsx +++ b/frontend/src/components/MarkdownRender.tsx @@ -1,6 +1,9 @@ +import 'katex/dist/katex.min.css'; import ReactMarkdown from 'react-markdown'; import rehypeRaw from 'rehype-raw'; import rehypeHighlight from 'rehype-highlight'; +import rehypeKatex from 'rehype-katex'; +import remarkMath from 'remark-math'; import remarkGfm from 'remark-gfm'; import remarkBreaks from 'remark-breaks'; import { FC } from 'react'; @@ -90,8 +93,9 @@ const MarkdownRender: FC = (props 'cite' ]} unwrapDisallowed={true} - remarkPlugins={[remarkGfm, remarkBreaks]} + remarkPlugins={[remarkMath, remarkGfm, remarkBreaks]} rehypePlugins={[ + rehypeKatex, rehypeRaw, [ rehypeHighlight, diff --git a/frontend/vite.config.ts b/frontend/vite.config.ts index 040ed0bb..24b2b541 100644 --- a/frontend/vite.config.ts +++ b/frontend/vite.config.ts @@ -23,7 +23,7 @@ const embedded = [ 'react-beautiful-dnd', 'react-draggable', '@magenta/music', 'html-midi-player', - 'react-markdown', 'rehype-highlight', 'rehype-raw', 'remark-breaks', 'remark-gfm' + 'react-markdown', 'rehype-highlight', 'rehype-raw', 'remark-breaks', 'remark-gfm', 'remark-math', 'rehype-katex', 'katex' ]; function renderChunks(deps: Record) {