diff --git a/ElectronClient/app/package-lock.json b/ElectronClient/app/package-lock.json index 0f93fee35c..5e2a11fa24 100644 --- a/ElectronClient/app/package-lock.json +++ b/ElectronClient/app/package-lock.json @@ -2852,6 +2852,24 @@ "uc.micro": "1.0.3" } }, + "markdown-it-katex": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/markdown-it-katex/-/markdown-it-katex-2.0.3.tgz", + "integrity": "sha1-17hqGuoLnWSW+rTnkZoY/e9YnDk=", + "requires": { + "katex": "0.6.0" + }, + "dependencies": { + "katex": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/katex/-/katex-0.6.0.tgz", + "integrity": "sha1-EkGOCRIcBckgQbazuftrqyE8tvM=", + "requires": { + "match-at": "0.1.1" + } + } + } + }, "match-at": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/match-at/-/match-at-0.1.1.tgz", diff --git a/ElectronClient/app/package.json b/ElectronClient/app/package.json index 72d41515a9..1ea37e4b24 100644 --- a/ElectronClient/app/package.json +++ b/ElectronClient/app/package.json @@ -70,6 +70,7 @@ "levenshtein": "^1.0.5", "lodash": "^4.17.4", "markdown-it": "^8.4.0", + "markdown-it-katex": "^2.0.3", "md5": "^2.2.1", "mime": "^2.0.3", "moment": "^2.19.1", diff --git a/README.md b/README.md index 48e2d88638..6350a5b57d 100644 --- a/README.md +++ b/README.md @@ -150,19 +150,19 @@ Joplin uses and renders [Github-flavoured Markdown](https://github.com/adam-p/ma ## Math notation -Math expressions can be added using the [Katex notation](https://khan.github.io/KaTeX/). To add an inline equation, wrap the expression in `` `{.katex}EXPRESSION` ``, eg. `` `{.katex}\sqrt{3x-1}+(1+x)^2` ``. To create an expression block, wrap it as follow: +Math expressions can be added using the [Katex notation](https://khan.github.io/KaTeX/). To add an inline equation, wrap the expression in `$EXPRESSION$`, eg. `$\sqrt{3x-1}+(1+x)^2$`. To create an expression block, wrap it as follow: - ```katex + $$ EXPRESSION - ``` + $$ For example: - ```katex + $$ f(x) = \int_{-\infty}^\infty \hat f(\xi)\,e^{2 \pi i \xi x} \,d\xi - ``` + $$ Here is an example with the Markdown and rendered result side by side: diff --git a/ReactNativeClient/lib/MdToHtml.js b/ReactNativeClient/lib/MdToHtml.js index dc21551fb6..9c4f11bf0f 100644 --- a/ReactNativeClient/lib/MdToHtml.js +++ b/ReactNativeClient/lib/MdToHtml.js @@ -157,7 +157,7 @@ class MdToHtml { } } - customCodeHandler_(language) { + rendererPlugin_(language) { if (!language) return null; const handlers = {}; @@ -196,9 +196,10 @@ class MdToHtml { const isCodeBlock = tag === 'code' && t.block; const isInlineCode = t.type === 'code_inline'; const codeBlockLanguage = t && t.info ? t.info : null; - let codeBlockHandler = null; + let rendererPlugin = null; + let rendererPluginOptions = { tagType: 'inline' }; - if (isCodeBlock) codeBlockHandler = this.customCodeHandler_(codeBlockLanguage); + if (isCodeBlock) rendererPlugin = this.rendererPlugin_(codeBlockLanguage); if (previousToken && previousToken.tag === 'li' && tag === 'p') { // Markdown-it render list items as
Text
');
}
} else if (isInlineCode) {
const result = this.parseInlineCodeLanguage_(tokenContent);
if (result) {
- codeBlockHandler = this.customCodeHandler_(result.language);
+ rendererPlugin = this.rendererPlugin_(result.language);
tokenContent = result.newContent;
}
- if (!codeBlockHandler) {
+ if (!rendererPlugin) {
output.push('');
}
}
- if (codeBlockHandler) {
- codeBlockHandler.loadAssets().catch((error) => {
- console.warn('MdToHtml: Error loading assets for ' + codeBlockHandler.name() + ': ', error.message);
+ if (t.type === 'math_inline' || t.type === 'math_block') {
+ rendererPlugin = this.rendererPlugin_('katex');
+ rendererPluginOptions = { tagType: t.type === 'math_block' ? 'block' : 'inline' };
+ }
+
+ if (rendererPlugin) {
+ rendererPlugin.loadAssets().catch((error) => {
+ console.warn('MdToHtml: Error loading assets for ' + rendererPlugin.name() + ': ', error.message);
});
}
@@ -270,8 +276,10 @@ class MdToHtml {
output = output.concat(parsedChildren);
} else {
if (tokenContent) {
- if ((isCodeBlock || isInlineCode) && codeBlockHandler) {
- output = codeBlockHandler.processContent(output, tokenContent, isCodeBlock ? 'block' : 'inline');
+ if ((isCodeBlock || isInlineCode) && rendererPlugin) {
+ output = rendererPlugin.processContent(output, tokenContent, isCodeBlock ? 'block' : 'inline');
+ } else if (rendererPlugin) {
+ output = rendererPlugin.processContent(output, tokenContent, rendererPluginOptions.tagType);
} else {
output.push(htmlentities(tokenContent));
}
@@ -286,15 +294,15 @@ class MdToHtml {
} else if (tag && t.type.indexOf('inline') >= 0) {
closeTag = openTag;
} else if (isCodeBlock) {
- if (!codeBlockHandler) closeTag = openTag;
+ if (!rendererPlugin) closeTag = openTag;
}
if (isCodeBlock) {
- if (!codeBlockHandler) {
+ if (!rendererPlugin) {
output.push('
');
}
} else if (isInlineCode) {
- if (!codeBlockHandler) {
+ if (!rendererPlugin) {
output.push('
');
}
}
@@ -307,9 +315,9 @@ class MdToHtml {
}
}
- if (codeBlockHandler) {
- const extraCss = codeBlockHandler.extraCss();
- const name = codeBlockHandler.name();
+ if (rendererPlugin) {
+ const extraCss = rendererPlugin.extraCss();
+ const name = rendererPlugin.name();
if (extraCss && !(name in extraCssBlocks)) {
extraCssBlocks[name] = extraCss;
}
@@ -345,6 +353,13 @@ class MdToHtml {
linkify: true,
});
+ // This is currently used only so that the $expression$ and $$\nexpression\n$$ blocks are translated
+ // to math_inline and math_block blocks. These blocks are then processed directly with the Katex
+ // library. It is better this way as then it is possible to conditionally load the CSS required by
+ // Katex and use an up-to-date version of Katex (as of 2018, the plugin is still using 0.6, which is
+ // buggy instead of 0.9).
+ md.use(require('markdown-it-katex'));
+
// Hack to make checkboxes clickable. Ideally, checkboxes should be parsed properly in
// renderTokens_(), but for now this hack works. Marking it with HORRIBLE_HACK so
// that it can be removed and replaced later on.
diff --git a/ReactNativeClient/package-lock.json b/ReactNativeClient/package-lock.json
index 97605d2af4..14dac9fe1e 100644
--- a/ReactNativeClient/package-lock.json
+++ b/ReactNativeClient/package-lock.json
@@ -3666,6 +3666,24 @@
"uc.micro": "1.0.3"
}
},
+ "markdown-it-katex": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/markdown-it-katex/-/markdown-it-katex-2.0.3.tgz",
+ "integrity": "sha1-17hqGuoLnWSW+rTnkZoY/e9YnDk=",
+ "requires": {
+ "katex": "0.6.0"
+ },
+ "dependencies": {
+ "katex": {
+ "version": "0.6.0",
+ "resolved": "https://registry.npmjs.org/katex/-/katex-0.6.0.tgz",
+ "integrity": "sha1-EkGOCRIcBckgQbazuftrqyE8tvM=",
+ "requires": {
+ "match-at": "0.1.1"
+ }
+ }
+ }
+ },
"match-at": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/match-at/-/match-at-0.1.1.tgz",
diff --git a/ReactNativeClient/package.json b/ReactNativeClient/package.json
index f73d96f931..690fd9bbbb 100644
--- a/ReactNativeClient/package.json
+++ b/ReactNativeClient/package.json
@@ -17,6 +17,7 @@
"html-entities": "^1.2.1",
"katex": "^0.9.0-beta1",
"markdown-it": "^8.4.0",
+ "markdown-it-katex": "^2.0.3",
"md5": "^2.2.1",
"moment": "^2.18.1",
"prop-types": "^15.6.0",
diff --git a/docs/images/Katex.png b/docs/images/Katex.png
index bc761595d0..95b029e8a4 100644
Binary files a/docs/images/Katex.png and b/docs/images/Katex.png differ
diff --git a/docs/index.html b/docs/index.html
index e431440d09..dddf60618a 100644
--- a/docs/index.html
+++ b/docs/index.html
@@ -331,16 +331,16 @@ sudo ln -s ~/.joplin-bin/bin/joplin /usr/bin/joplin
Joplin uses and renders Github-flavoured Markdown with a few variations and additions. In particular:
Math expressions can be added using the Katex notation. To add an inline equation, wrap the expression in `{.katex}EXPRESSION`
, eg. `{.katex}\sqrt{3x-1}+(1+x)^2`
. To create an expression block, wrap it as follow:
```katex
+Math expressions can be added using the Katex notation. To add an inline equation, wrap the expression in $EXPRESSION$
, eg. $\sqrt{3x-1}+(1+x)^2$
. To create an expression block, wrap it as follow:
+$$
EXPRESSION
-```
+$$
For example:
-```katex
+$$
f(x) = \int_{-\infty}^\infty
\hat f(\xi)\,e^{2 \pi i \xi x}
\,d\xi
-```
+$$
Here is an example with the Markdown and rendered result side by side:
Checkboxes
@@ -378,14 +378,14 @@ f(x) = \int_{-\infty}^\infty
Croatian
hr_HR
-Hrvoje Mandić trbuhom@net.hr
+Hrvoje Mandić trbuhom@net.hr
72%
Deutsch
de_DE
-Tobias Strobel git@strobeltobias.de
+Tobias Strobel git@strobeltobias.de
92%
@@ -441,14 +441,14 @@ f(x) = \int_{-\infty}^\infty
Русский
ru_RU
-Artyom Karlov artyom.karlov@gmail.com
+Artyom Karlov artyom.karlov@gmail.com
96%
中文 (简体)
zh_CN
-RCJacH RCJacH@outlook.com
+RCJacH RCJacH@outlook.com
76%