core: pre-render math & code blocks before HTML export

This commit is contained in:
Muhammad Ali
2023-03-09 16:45:32 +05:00
committed by Abdullah Atta
parent 69dee3be78
commit 75e62a3fac
6 changed files with 182 additions and 55 deletions

View File

@@ -136,3 +136,28 @@ export async function exportNotes(
}
});
}
function renderMath(content: string) {
const katex = require("katex");
require("katex/contrib/mhchem");
const div = document.createElement("div");
div.innerHTML = content;
const mathBlocks = div.querySelectorAll(".math-block.math-node");
const mathInlines = div.querySelectorAll(".math-inline.math-node");
for (const mathBlock of mathBlocks) {
const text = mathBlock.textContent;
mathBlock.innerHTML = katex.renderToString(text, {
displayMode: true,
output: "mathml"
});
}
for (const mathInline of mathInlines) {
const text = mathInline.textContent;
mathInline.innerHTML = katex.renderToString(text, { output: "mathml" });
}
return div.innerHTML;
}

View File

@@ -120,7 +120,7 @@ export default class Note {
case "html":
templateData.content = rawHTML || content.toHTML();
return template
? HTMLBuilder.buildHTML(templateData)
? await HTMLBuilder.buildHTML(templateData)
: templateData.content;
case "txt":
templateData.content = rawHTML || content.toTXT();

View File

@@ -18,9 +18,11 @@
"entities": "^4.3.1",
"html-to-text": "^9.0.5",
"htmlparser2": "^8.0.1",
"katex": "^0.16.8",
"linkedom": "^0.14.17",
"liqe": "^1.13.0",
"mime-db": "1.52.0",
"prismjs": "^1.29.0",
"qclone": "^1.2.0",
"spark-md5": "^3.0.2"
},
@@ -32,6 +34,8 @@
"@notesnook/crypto": "file:../crypto",
"@types/html-to-text": "^9.0.0",
"@types/jest": "^28.1.6",
"@types/katex": "^0.16.1",
"@types/prismjs": "^1.26.0",
"@types/showdown": "^2.0.0",
"abortcontroller-polyfill": "^1.7.3",
"analyze-es6-modules": "^0.6.2",
@@ -2743,6 +2747,12 @@
"pretty-format": "^28.0.0"
}
},
"node_modules/@types/katex": {
"version": "0.16.1",
"resolved": "https://registry.npmjs.org/@types/katex/-/katex-0.16.1.tgz",
"integrity": "sha512-cwglq2A63Yk082CQk0t8LIoDhZAVgJqkumLyk3grpg3K8sevaDW//Qsspmxj9Sf+97biqt79CfAlPrvizHlP0w==",
"dev": true
},
"node_modules/@types/node": {
"version": "18.11.9",
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.9.tgz",
@@ -2755,6 +2765,12 @@
"integrity": "sha512-ri0UmynRRvZiiUJdiz38MmIblKK+oH30MztdBVR95dv/Ubw6neWSb8u1XpRb72L4qsZOhz+L+z9JD40SJmfWow==",
"dev": true
},
"node_modules/@types/prismjs": {
"version": "1.26.0",
"resolved": "https://registry.npmjs.org/@types/prismjs/-/prismjs-1.26.0.tgz",
"integrity": "sha512-ZTaqn/qSqUuAq1YwvOFQfVW1AR/oQJlLSZVustdjwI+GZ8kr0MSHBj0tsXPW1EqHubx50gtBEjbPGsdZwQwCjQ==",
"dev": true
},
"node_modules/@types/showdown": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/@types/showdown/-/showdown-2.0.0.tgz",
@@ -7943,6 +7959,29 @@
"node": ">=6"
}
},
"node_modules/katex": {
"version": "0.16.8",
"resolved": "https://registry.npmjs.org/katex/-/katex-0.16.8.tgz",
"integrity": "sha512-ftuDnJbcbOckGY11OO+zg3OofESlbR5DRl2cmN8HeWeeFIV7wTXvAOx8kEjZjobhA+9wh2fbKeO6cdcA9Mnovg==",
"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/kleur": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz",
@@ -8587,6 +8626,14 @@
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
}
},
"node_modules/prismjs": {
"version": "1.29.0",
"resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.29.0.tgz",
"integrity": "sha512-Kx/1w86q/epKcmte75LNrEoT+lX8pBpavuAbvJWRXar7Hz8jrtF+e3vY751p0R8H9HdArwaCTNDDzHg/ScJK1Q==",
"engines": {
"node": ">=6"
}
},
"node_modules/private": {
"version": "0.1.8",
"resolved": "https://registry.npmjs.org/private/-/private-0.1.8.tgz",
@@ -11630,6 +11677,12 @@
"pretty-format": "^28.0.0"
}
},
"@types/katex": {
"version": "0.16.1",
"resolved": "https://registry.npmjs.org/@types/katex/-/katex-0.16.1.tgz",
"integrity": "sha512-cwglq2A63Yk082CQk0t8LIoDhZAVgJqkumLyk3grpg3K8sevaDW//Qsspmxj9Sf+97biqt79CfAlPrvizHlP0w==",
"dev": true
},
"@types/node": {
"version": "18.11.9",
"resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.9.tgz",
@@ -11642,6 +11695,12 @@
"integrity": "sha512-ri0UmynRRvZiiUJdiz38MmIblKK+oH30MztdBVR95dv/Ubw6neWSb8u1XpRb72L4qsZOhz+L+z9JD40SJmfWow==",
"dev": true
},
"@types/prismjs": {
"version": "1.26.0",
"resolved": "https://registry.npmjs.org/@types/prismjs/-/prismjs-1.26.0.tgz",
"integrity": "sha512-ZTaqn/qSqUuAq1YwvOFQfVW1AR/oQJlLSZVustdjwI+GZ8kr0MSHBj0tsXPW1EqHubx50gtBEjbPGsdZwQwCjQ==",
"dev": true
},
"@types/showdown": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/@types/showdown/-/showdown-2.0.0.tgz",
@@ -15296,7 +15355,8 @@
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz",
"integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==",
"dev": true
"dev": true,
"requires": {}
},
"jest-regex-util": {
"version": "28.0.2",
@@ -15970,7 +16030,8 @@
"version": "8.11.0",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz",
"integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==",
"dev": true
"dev": true,
"requires": {}
}
}
},
@@ -15992,6 +16053,21 @@
"integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==",
"dev": true
},
"katex": {
"version": "0.16.8",
"resolved": "https://registry.npmjs.org/katex/-/katex-0.16.8.tgz",
"integrity": "sha512-ftuDnJbcbOckGY11OO+zg3OofESlbR5DRl2cmN8HeWeeFIV7wTXvAOx8kEjZjobhA+9wh2fbKeO6cdcA9Mnovg==",
"requires": {
"commander": "^8.3.0"
},
"dependencies": {
"commander": {
"version": "8.3.0",
"resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz",
"integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww=="
}
}
},
"kleur": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz",
@@ -16483,6 +16559,11 @@
}
}
},
"prismjs": {
"version": "1.29.0",
"resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.29.0.tgz",
"integrity": "sha512-Kx/1w86q/epKcmte75LNrEoT+lX8pBpavuAbvJWRXar7Hz8jrtF+e3vY751p0R8H9HdArwaCTNDDzHg/ScJK1Q=="
},
"private": {
"version": "0.1.8",
"resolved": "https://registry.npmjs.org/private/-/private-0.1.8.tgz",
@@ -17230,7 +17311,8 @@
"ws": {
"version": "7.5.9",
"resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz",
"integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q=="
"integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==",
"requires": {}
},
"xml-name-validator": {
"version": "4.0.0",

View File

@@ -15,6 +15,8 @@
"@notesnook/crypto": "file:../crypto",
"@types/html-to-text": "^9.0.0",
"@types/jest": "^28.1.6",
"@types/katex": "^0.16.1",
"@types/prismjs": "^1.26.0",
"@types/showdown": "^2.0.0",
"abortcontroller-polyfill": "^1.7.3",
"analyze-es6-modules": "^0.6.2",
@@ -45,9 +47,11 @@
"entities": "^4.3.1",
"html-to-text": "^9.0.5",
"htmlparser2": "^8.0.1",
"katex": "^0.16.8",
"linkedom": "^0.14.17",
"liqe": "^1.13.0",
"mime-db": "1.52.0",
"prismjs": "^1.29.0",
"qclone": "^1.2.0",
"spark-md5": "^3.0.2"
},

View File

@@ -17,10 +17,69 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { parseHTML } from "../../html-parser";
import HTMLTemplate from "./template";
function buildHTML(templateData) {
return HTMLTemplate(templateData);
const LANGUAGE_REGEX = /(?:^|\s)lang(?:uage)?-([\w-]+)(?=\s|$)/i;
async function buildHTML(templateData) {
return HTMLTemplate(await preprocessHTML(templateData));
}
async function preprocessHTML(templateData) {
const { content } = templateData;
const doc = parseHTML(
content.replaceAll(/<p(.+?)><\/p>/gm, "<p$1><br/></p>")
);
const mathBlocks = doc.querySelectorAll(".math-block.math-node");
const mathInlines = doc.querySelectorAll(".math-inline.math-node");
if (mathBlocks.length || mathInlines.length) {
const { default: katex } = require("katex");
require("katex/contrib/mhchem");
for (const mathBlock of mathBlocks) {
const text = mathBlock.textContent;
mathBlock.innerHTML = katex.renderToString(text, {
displayMode: true,
output: "mathml"
});
}
for (const mathInline of mathInlines) {
const text = mathInline.textContent;
mathInline.innerHTML = katex.renderToString(text, { output: "mathml" });
}
templateData.hasMathBlocks = true;
}
const codeblocks = doc.querySelectorAll("pre > code");
if (codeblocks.length) {
const { default: prismjs } = require("prismjs");
prismjs.register = () => {};
for (const codeblock of codeblocks) {
const language = LANGUAGE_REGEX.exec(
codeblock.parentElement.className
)?.[1];
if (!language) continue;
const {
default: grammar
} = require(`../../../../editor/languages/${language}.js`);
grammar(prismjs);
codeblock.innerHTML = prismjs.highlight(
codeblock.textContent,
prismjs.languages[language],
language
);
}
templateData.hasCodeblock = true;
}
templateData.content = doc.body.innerHTML;
return templateData;
}
export default { buildHTML };

View File

@@ -30,6 +30,8 @@ const template = (data) => `<!DOCTYPE html>
<meta name="created-on" content="${data.createdOn}" />
<meta name="last-edited-on" content="${data.editedOn}" />
${data.tags ? `<meta name="tags" content="${data.tags}" />` : ""}
<link rel="stylesheet" href="https://app.notesnook.com/assets/index-b3954e4fc2fc.css">
<style>
img {
max-width: 100% !important;
@@ -40,8 +42,7 @@ const template = (data) => `<!DOCTYPE html>
body {
background-color: transparent !important;
color: #202124;
font-family: "Open Sans", "Noto Sans", Frutiger, Calibri, Myriad, Arial,
Ubuntu, Helvetica, -apple-system, BlinkMacSystemFont, sans-serif;
font-family: "Open Sans", "Noto Sans", Frutiger, Calibri, Myriad, Arial, Ubuntu, Helvetica, -apple-system, BlinkMacSystemFont, sans-serif;
}
h1,
@@ -123,22 +124,19 @@ const template = (data) => `<!DOCTYPE html>
border: 1px solid #e5e5e5;
border-radius: 5px;
padding: 3px 5px 0px 5px;
font-family: ui-monospace, SFMono-Regular, SF Mono, Consolas,
Liberation Mono, Menlo, monospace !important;
font-family: Hack, Consolas, "Andale Mono", "Lucida Console", "Liberation Mono", "Courier New", Courier, monospace !important;
font-size: 10pt !important;
}
.ProseMirror code > span {
font-family: ui-monospace, SFMono-Regular, SF Mono, Consolas,
Liberation Mono, Menlo, monospace !important;
font-family: Hack, Consolas, "Andale Mono", "Lucida Console", "Liberation Mono", "Courier New", Courier, monospace !important;
}
pre {
padding: 10px;
background-color: #e5e5e5;
border-radius: 5px;
font-family: ui-monospace, SFMono-Regular, SF Mono, Consolas,
Liberation Mono, Menlo, monospace !important;
font-family: Hack, Consolas, "Andale Mono", "Lucida Console", "Liberation Mono", "Courier New", Courier, monospace !important;
margin-bottom: 16px !important;
}
@@ -172,47 +170,6 @@ const template = (data) => `<!DOCTYPE html>
table p {
margin: 0;
}
code[class*="language-"],
pre[class*="language-"]
{
color: #F8F8F2;
background: #282A36;
text-shadow: none;
font-family: PT Mono, Consolas, Monaco, "Andale Mono", "Ubuntu Mono",
monospace;
text-align: left;
white-space: pre-wrap;
word-spacing: normal;
word-break: break-word;
word-wrap: normal;
line-height: 1.5;
-moz-tab-size: 4;
-o-tab-size: 4;
tab-size: 4;
-webkit-hyphens: none;
-moz-hyphens: none;
-ms-hyphens: none;
hyphens: none;
}
pre[class*="language-"]
{
background: #282A36;
border-radius: 0.5em;
padding: 1em;
margin: 0.5em 0;
overflow: auto;
height: auto;
}
:not(pre) > code[class*="language-"],
pre[class*="language-"]
{
background: #282A36;
}
</style>
</head>
<body>