feat: support LaTeX with react-katex (#2209)

feat: support latex

Co-authored-by: liaoxingyi <liaoxingyi@douban.com>
This commit is contained in:
Kada Liao 2023-09-12 23:53:46 +08:00 committed by GitHub
parent 416e07cb1f
commit 9a0ada6756
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 161 additions and 16 deletions

View File

@ -11,6 +11,7 @@
"@bufbuild/protobuf": "^1.3.1",
"@emotion/react": "^11.11.1",
"@emotion/styled": "^11.11.0",
"@matejmazur/react-katex": "^3.1.3",
"@mui/joy": "5.0.0-beta.2",
"@reduxjs/toolkit": "^1.9.5",
"axios": "^0.27.2",
@ -19,6 +20,7 @@
"highlight.js": "^11.8.0",
"i18next": "^21.10.0",
"i18next-browser-languagedetector": "^7.1.0",
"katex": "^0.16.8",
"lodash-es": "^4.17.21",
"lucide-react": "^0.263.1",
"qrcode.react": "^3.1.0",
@ -37,6 +39,7 @@
},
"devDependencies": {
"@trivago/prettier-plugin-sort-imports": "^3.4.0",
"@types/katex": "^0.16.2",
"@types/lodash-es": "^4.17.9",
"@types/node": "^18.17.15",
"@types/qs": "^6.9.8",

View File

@ -14,6 +14,9 @@ dependencies:
'@emotion/styled':
specifier: ^11.11.0
version: 11.11.0(@emotion/react@11.11.1)(@types/react@18.2.21)(react@18.2.0)
'@matejmazur/react-katex':
specifier: ^3.1.3
version: registry.npmmirror.com/@matejmazur/react-katex@3.1.3(katex@0.16.8)(react@18.2.0)
'@mui/joy':
specifier: 5.0.0-beta.2
version: 5.0.0-beta.2(@emotion/react@11.11.1)(@emotion/styled@11.11.0)(@types/react@18.2.21)(react-dom@18.2.0)(react@18.2.0)
@ -38,6 +41,9 @@ dependencies:
i18next-browser-languagedetector:
specifier: ^7.1.0
version: 7.1.0
katex:
specifier: ^0.16.8
version: registry.npmmirror.com/katex@0.16.8
lodash-es:
specifier: ^4.17.21
version: 4.17.21
@ -88,6 +94,9 @@ devDependencies:
'@trivago/prettier-plugin-sort-imports':
specifier: ^3.4.0
version: 3.4.0(prettier@2.6.2)
'@types/katex':
specifier: ^0.16.2
version: registry.npmmirror.com/@types/katex@0.16.2
'@types/lodash-es':
specifier: ^4.17.9
version: 4.17.9
@ -216,7 +225,7 @@ packages:
dependencies:
'@babel/types': 7.17.0
jsesc: 2.5.2
source-map: 0.5.7
source-map: registry.npmmirror.com/source-map@0.5.7
dev: true
/@babel/generator@7.22.15:
@ -1730,11 +1739,6 @@ packages:
resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==}
dev: true
/commander@4.1.1:
resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==}
engines: {node: '>= 6'}
dev: false
/concat-map@0.0.1:
resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
@ -1784,7 +1788,7 @@ packages:
engines: {node: '>=8.0.0'}
dependencies:
mdn-data: 2.0.14
source-map: 0.6.1
source-map: registry.npmmirror.com/source-map@0.6.1
dev: false
/cssesc@3.0.0:
@ -3669,18 +3673,15 @@ packages:
source-map: 0.6.1
dev: true
/source-map@0.5.6:
resolution: {integrity: sha512-MjZkVp0NHr5+TPihLcadqnlVoGIoWo4IBHptutGh9wI3ttUYvCG26HkSuDi+K6lsZ25syXJXcctwgyVCt//xqA==}
engines: {node: '>=0.10.0'}
dev: false
/source-map@0.5.7:
resolution: {integrity: sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==}
engines: {node: '>=0.10.0'}
dev: false
/source-map@0.6.1:
resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==}
engines: {node: '>=0.10.0'}
dev: true
/sourcemap-codec@1.4.8:
resolution: {integrity: sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==}
@ -3700,7 +3701,7 @@ packages:
/stacktrace-gps@3.1.2:
resolution: {integrity: sha512-GcUgbO4Jsqqg6RxfyTHFiPxdPqF+3LFmQhm7MgCuYQOYuWyqxo5pwRPz5d/u6/WYJdEnWfK4r+jGbyD8TSggXQ==}
dependencies:
source-map: 0.5.6
source-map: registry.npmmirror.com/source-map@0.5.6
stackframe: 1.3.4
dev: false
@ -3776,7 +3777,7 @@ packages:
hasBin: true
dependencies:
'@jridgewell/gen-mapping': 0.3.3
commander: 4.1.1
commander: registry.npmmirror.com/commander@4.1.1
glob: 7.1.6
lines-and-columns: 1.2.4
mz: 2.7.0
@ -4150,3 +4151,67 @@ packages:
react: 18.2.0
use-sync-external-store: 1.2.0(react@18.2.0)
dev: false
registry.npmmirror.com/@matejmazur/react-katex@3.1.3(katex@0.16.8)(react@18.2.0):
resolution: {integrity: sha512-rBp7mJ9An7ktNoU653BWOYdO4FoR4YNwofHZi+vaytX/nWbIlmHVIF+X8VFOn6c3WYmrLT5FFBjKqCZ1sjR5uQ==, registry: https://registry.npmjs.org/, tarball: https://registry.npmmirror.com/@matejmazur/react-katex/-/react-katex-3.1.3.tgz}
id: registry.npmmirror.com/@matejmazur/react-katex/3.1.3
name: '@matejmazur/react-katex'
version: 3.1.3
engines: {node: '>=12', yarn: '>=1.1'}
peerDependencies:
katex: '>=0.9'
react: '>=16'
dependencies:
katex: registry.npmmirror.com/katex@0.16.8
react: 18.2.0
dev: false
registry.npmmirror.com/@types/katex@0.16.2:
resolution: {integrity: sha512-dHsSjSlU/EWEEbeNADr3FtZZOAXPkFPUO457QCnoNqcZQXNqNEu/svQd0Nritvd3wNff4vvC/f4e6xgX3Llt8A==, registry: https://registry.npmjs.org/, tarball: https://registry.npmmirror.com/@types/katex/-/katex-0.16.2.tgz}
name: '@types/katex'
version: 0.16.2
dev: true
registry.npmmirror.com/commander@4.1.1:
resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==, registry: https://registry.npmjs.org/, tarball: https://registry.npmmirror.com/commander/-/commander-4.1.1.tgz}
name: commander
version: 4.1.1
engines: {node: '>= 6'}
dev: false
registry.npmmirror.com/commander@8.3.0:
resolution: {integrity: sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==, registry: https://registry.npmjs.org/, tarball: https://registry.npmmirror.com/commander/-/commander-8.3.0.tgz}
name: commander
version: 8.3.0
engines: {node: '>= 12'}
dev: false
registry.npmmirror.com/katex@0.16.8:
resolution: {integrity: sha512-ftuDnJbcbOckGY11OO+zg3OofESlbR5DRl2cmN8HeWeeFIV7wTXvAOx8kEjZjobhA+9wh2fbKeO6cdcA9Mnovg==, registry: https://registry.npmjs.org/, tarball: https://registry.npmmirror.com/katex/-/katex-0.16.8.tgz}
name: katex
version: 0.16.8
hasBin: true
dependencies:
commander: registry.npmmirror.com/commander@8.3.0
dev: false
registry.npmmirror.com/source-map@0.5.6:
resolution: {integrity: sha512-MjZkVp0NHr5+TPihLcadqnlVoGIoWo4IBHptutGh9wI3ttUYvCG26HkSuDi+K6lsZ25syXJXcctwgyVCt//xqA==, registry: https://registry.npmjs.org/, tarball: https://registry.npmmirror.com/source-map/-/source-map-0.5.6.tgz}
name: source-map
version: 0.5.6
engines: {node: '>=0.10.0'}
dev: false
registry.npmmirror.com/source-map@0.5.7:
resolution: {integrity: sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==, registry: https://registry.npmjs.org/, tarball: https://registry.npmmirror.com/source-map/-/source-map-0.5.7.tgz}
name: source-map
version: 0.5.7
engines: {node: '>=0.10.0'}
dev: true
registry.npmmirror.com/source-map@0.6.1:
resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==, registry: https://registry.npmjs.org/, tarball: https://registry.npmmirror.com/source-map/-/source-map-0.6.1.tgz}
name: source-map
version: 0.6.1
engines: {node: '>=0.10.0'}
dev: false

View File

@ -0,0 +1,35 @@
import TeX from "@matejmazur/react-katex";
import "katex/dist/katex.min.css";
import { matcher } from "../matcher";
const BLOCK_LATEX_REG = new RegExp(
"\\$\\$(\\s*[^\\$\\s][^\\$]*?)\\$\\$|\\\\\\[(.+?)\\\\\\]|\\\\begin{equation}([\\s\\S]+?)\\\\end{equation}"
);
const blockRenderer = (rawStr: string) => {
const matchResult = matcher(rawStr, BLOCK_LATEX_REG);
if (!matchResult) {
return <>{rawStr}</>;
}
let latexCode = "";
if (matchResult[1]) {
// $$
latexCode = matchResult[1];
} else if (matchResult[2]) {
// \[ and \]
latexCode = matchResult[2];
} else if (matchResult[3]) {
// \begin{equation} and \end{equation}
latexCode = matchResult[3];
}
return <TeX block={true}>{latexCode}</TeX>;
};
export default {
name: "blockLatex",
regexp: BLOCK_LATEX_REG,
renderer: blockRenderer,
};

View File

@ -0,0 +1,28 @@
import TeX from "@matejmazur/react-katex";
import "katex/dist/katex.min.css";
export const LATEX_INLINE_REG = /\$(.+?)\$|\\\(([^\\]+)\\\)/g;
const inlineRenderer = (rawStr: string) => {
const matchResult = LATEX_INLINE_REG.exec(rawStr);
if (!matchResult) {
return rawStr;
}
let latexCode = "";
if (matchResult[1]) {
// $
latexCode = matchResult[1];
} else if (matchResult[2]) {
// \( and \)
latexCode = matchResult[2];
}
return <TeX>{latexCode}</TeX>;
};
export default {
name: "inlineLatex",
regexp: LATEX_INLINE_REG,
renderer: inlineRenderer,
};

View File

@ -1,3 +1,4 @@
import BlockLatex from "./BlockLatex";
import Blockquote from "./Blockquote";
import Bold from "./Bold";
import BoldEmphasis from "./BoldEmphasis";
@ -9,6 +10,7 @@ import Heading from "./Heading";
import HorizontalRules from "./HorizontalRules";
import Image from "./Image";
import InlineCode from "./InlineCode";
import InlineLatex from "./InlineLatex";
import Link from "./Link";
import OrderedList from "./OrderedList";
import Paragraph from "./Paragraph";
@ -25,6 +27,7 @@ export { PLAIN_LINK_REG } from "./PlainLink";
// The order determines the order of execution.
export const blockElementParserList = [
BlockLatex,
Br,
CodeBlock,
Blockquote,
@ -36,5 +39,16 @@ export const blockElementParserList = [
HorizontalRules,
Paragraph,
];
export const inlineElementParserList = [Image, BoldEmphasis, Bold, Emphasis, Link, InlineCode, PlainLink, Strikethrough, Tag, PlainText];
export const inlineElementParserList = [
InlineLatex,
Image,
BoldEmphasis,
Bold,
Emphasis,
Link,
InlineCode,
PlainLink,
Strikethrough,
Tag,
PlainText,
];