Add support for math formulas (#151)

* Add support for LaTeX equations

* Mention math support in the introductory notebook
This commit is contained in:
Jonatan Kłosko 2021-04-08 15:31:46 +02:00 committed by GitHub
parent 121eec784d
commit d68c271aae
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 78 additions and 1 deletions

View file

@ -124,6 +124,10 @@
@apply mb-0; @apply mb-0;
} }
.markdown .katex-display {
@apply my-8;
}
/* Overrides for user-entered markdown */ /* Overrides for user-entered markdown */
[data-element="cell"] .markdown h1, [data-element="cell"] .markdown h1,

View file

@ -1,5 +1,6 @@
import "../css/app.css"; import "../css/app.css";
import "remixicon/fonts/remixicon.css"; import "remixicon/fonts/remixicon.css";
import "katex/dist/katex.min.css";
import "@fontsource/inter"; import "@fontsource/inter";
import "@fontsource/inter/500.css"; import "@fontsource/inter/500.css";

View file

@ -1,6 +1,7 @@
import marked from "marked"; import marked from "marked";
import morphdom from "morphdom"; import morphdom from "morphdom";
import DOMPurify from "dompurify"; import DOMPurify from "dompurify";
import katex from "katex";
import monaco from "./live_editor/monaco"; import monaco from "./live_editor/monaco";
// Reuse Monaco highlighter for Markdown code blocks // Reuse Monaco highlighter for Markdown code blocks
@ -51,7 +52,16 @@ class Markdown {
// Marked requires a trailing slash in the base URL // Marked requires a trailing slash in the base URL
const opts = { baseUrl: this.baseUrl + "/" }; const opts = { baseUrl: this.baseUrl + "/" };
marked(this.content, opts, (error, html) => { // Render math formulas using KaTeX.
// The resulting <span> tags will pass through
// marked.js and sanitization unchanged.
//
// We render math before anything else, because passing
// TeX through markdown renderer may have undesired
// effects like rendering \\ as \.
const contentWithRenderedMath = this.__renderMathInString(this.content);
marked(contentWithRenderedMath, opts, (error, html) => {
const sanitizedHtml = DOMPurify.sanitize(html); const sanitizedHtml = DOMPurify.sanitize(html);
if (sanitizedHtml) { if (sanitizedHtml) {
@ -66,6 +76,22 @@ class Markdown {
}); });
}); });
} }
// Replaces TeX formulas in string with rendered HTML using KaTeX.
__renderMathInString(string) {
const options = {
throwOnError: false,
errorColor: "inherit",
};
return string
.replace(/\$\$([\s\S]*?)\$\$/g, (match, math) => {
return katex.renderToString(math, { ...options, displayMode: true });
})
.replace(/\$([\s\S]*?)\$/g, (match, math) => {
return katex.renderToString(math, options);
});
}
} }
export default Markdown; export default Markdown;

View file

@ -10,6 +10,7 @@
"@fontsource/jetbrains-mono": "^4.2.2", "@fontsource/jetbrains-mono": "^4.2.2",
"dompurify": "^2.2.6", "dompurify": "^2.2.6",
"hyperlist": "^1.0.0", "hyperlist": "^1.0.0",
"katex": "^0.13.2",
"marked": "^1.2.8", "marked": "^1.2.8",
"monaco-editor": "^0.23.0", "monaco-editor": "^0.23.0",
"morphdom": "^2.6.1", "morphdom": "^2.6.1",
@ -9296,6 +9297,25 @@
"verror": "1.10.0" "verror": "1.10.0"
} }
}, },
"node_modules/katex": {
"version": "0.13.2",
"resolved": "https://registry.npmjs.org/katex/-/katex-0.13.2.tgz",
"integrity": "sha512-u/KhjFDhyPr+70aiBn9SL/9w/QlLagIXBi2NZSbNnBUp2tR8dCjQplyEMkEzniem5gOeSCBjlBUg4VaiWs1JJg==",
"dependencies": {
"commander": "^6.0.0"
},
"bin": {
"katex": "cli.js"
}
},
"node_modules/katex/node_modules/commander": {
"version": "6.2.1",
"resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz",
"integrity": "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==",
"engines": {
"node": ">= 6"
}
},
"node_modules/kind-of": { "node_modules/kind-of": {
"version": "6.0.3", "version": "6.0.3",
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz",
@ -23464,6 +23484,21 @@
"verror": "1.10.0" "verror": "1.10.0"
} }
}, },
"katex": {
"version": "0.13.2",
"resolved": "https://registry.npmjs.org/katex/-/katex-0.13.2.tgz",
"integrity": "sha512-u/KhjFDhyPr+70aiBn9SL/9w/QlLagIXBi2NZSbNnBUp2tR8dCjQplyEMkEzniem5gOeSCBjlBUg4VaiWs1JJg==",
"requires": {
"commander": "^6.0.0"
},
"dependencies": {
"commander": {
"version": "6.2.1",
"resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz",
"integrity": "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA=="
}
}
},
"kind-of": { "kind-of": {
"version": "6.0.3", "version": "6.0.3",
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz",

View file

@ -15,6 +15,7 @@
"@fontsource/jetbrains-mono": "^4.2.2", "@fontsource/jetbrains-mono": "^4.2.2",
"dompurify": "^2.2.6", "dompurify": "^2.2.6",
"hyperlist": "^1.0.0", "hyperlist": "^1.0.0",
"katex": "^0.13.2",
"marked": "^1.2.8", "marked": "^1.2.8",
"monaco-editor": "^0.23.0", "monaco-editor": "^0.23.0",
"morphdom": "^2.6.1", "morphdom": "^2.6.1",

View file

@ -164,6 +164,16 @@ defmodule Livebook.Notebook.Welcome do
per runtime, so if you need to modify the dependencies, you should per runtime, so if you need to modify the dependencies, you should
go to the notebook runtime configuration and **reconnect** the current runtime. go to the notebook runtime configuration and **reconnect** the current runtime.
## Math
Livebook supports both inline formulas like $e^{\\pi i} + 1 = 0$, as well as block formulas:
$$
S(x) = \\frac{1}{1 + e^{-x}} = \\frac{e^{x}}{e^{x} + 1}
$$
You can explore all supported expressions [here](https://katex.org/docs/supported.html).
## Stepping up your workflow ## Stepping up your workflow
Once you start using notebooks more, it's gonna be beneficial Once you start using notebooks more, it's gonna be beneficial