1
0
mirror of https://github.com/laurent22/joplin.git synced 2024-12-21 09:38:01 +02:00

Desktop,Mobile: Resolves #3201: Render mermaid diagrams in dark mode when Joplin is in dark mode (#9631)

This commit is contained in:
Henry Heino 2024-01-04 05:45:06 -08:00 committed by GitHub
parent d02058d337
commit 4e09b6f2a4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 82 additions and 18 deletions

View File

@ -1,5 +1,5 @@
module.exports = { module.exports = {
hash:"6cf170562f87803f3d6b9455bc66ccc9", files: { hash:"d4cb80ea030d8b01caeb56299491f244", files: {
'highlight.js/atom-one-dark-reasonable.css': { data: require('./highlight.js/atom-one-dark-reasonable.css.base64.js'), mime: 'text/css', encoding: 'base64' }, 'highlight.js/atom-one-dark-reasonable.css': { data: require('./highlight.js/atom-one-dark-reasonable.css.base64.js'), mime: 'text/css', encoding: 'base64' },
'highlight.js/atom-one-light.css': { data: require('./highlight.js/atom-one-light.css.base64.js'), mime: 'text/css', encoding: 'base64' }, 'highlight.js/atom-one-light.css': { data: require('./highlight.js/atom-one-light.css.base64.js'), mime: 'text/css', encoding: 'base64' },
'katex/fonts/KaTeX_AMS-Regular.woff2': { data: require('./katex/fonts/KaTeX_AMS-Regular.woff2.base64.js'), mime: 'application/octet-stream', encoding: 'base64' }, 'katex/fonts/KaTeX_AMS-Regular.woff2': { data: require('./katex/fonts/KaTeX_AMS-Regular.woff2.base64.js'), mime: 'application/octet-stream', encoding: 'base64' },

View File

@ -1 +1 @@
module.exports = `LyogZ2xvYmFsIG1lcm1haWQgKi8KCmZ1bmN0aW9uIG1lcm1haWRSZWFkeSgpIHsKCS8vIFRoZSBNZXJtYWlkIGluaXRpYWxpemF0aW9uIGNvZGUgcmVuZGVycyB0aGUgTWVybWFpZCBjb2RlIHdpdGhpbiBhbnkgZWxlbWVudCB3aXRoIGNsYXNzICJtZXJtYWlkIiBvcgoJLy8gSUQgIm1lcm1haWQiLiBIb3dldmVyIGluIHNvbWUgY2FzZXMgc29tZSBlbGVtZW50cyBtaWdodCBoYXZlIHRoaXMgSUQgYnV0IG5vdCBiZSBNZXJtYWlkIGNvZGUuCgkvLyBGb3IgZXhhbXBsZSwgTWFya2Rvd24gY29kZSBsaWtlIHRoaXM6CgkvLwoJLy8gICAgICMgTWVybWFpZAoJLy8KCS8vIFdpbGwgZ2VuZXJhdGUgdGhpcyBIVE1MOgoJLy8KCS8vICAgICA8aDEgaWQ9Im1lcm1haWQiPk1lcm1haWQ8L2gxPgoJLy8KCS8vIEFuZCB0aGF0J3MgZ29pbmcgdG8gbWFrZSB0aGUgbGliIHNldCB0aGUgYG1lcm1haWRgIG9iamVjdCB0byB0aGUgSDEgZWxlbWVudC4KCS8vIFNvIGJlbG93LCB3ZSBkb3VibGUtY2hlY2sgdGhhdCB3aGF0IHdlIGhhdmUgcmVhbGx5IGlzIGFuIGluc3RhbmNlIG9mIHRoZSBsaWJyYXJ5LgoJcmV0dXJuIHR5cGVvZiBtZXJtYWlkICE9PSAndW5kZWZpbmVkJyAmJiBtZXJtYWlkICE9PSBudWxsICYmIHR5cGVvZiBtZXJtYWlkID09PSAnb2JqZWN0JyAmJiAhIW1lcm1haWQuaW5pdDsKfQoKZnVuY3Rpb24gbWVybWFpZEluaXQoKSB7CgkvLyBNZXJtYWlkJ3Mgd29uZGVyZnVsIEFQSSBoYXMgdHdvIGluaXQgbWV0aG9kczogaW5pdCgpIGFuZCBpbml0aWFsaXplKCkuCgkvLyBpbml0KCkgaXMgZGVwcmVjdGF0ZWQgYnV0IHdvcmtzLCBhbmQgaW5pdGlhbGl6ZSgpIGlzIHJlY29tbWVuZGVkIGJ1dCBkb2Vzbid0CgkvLyB3b3JrLCBzbyBsZXQncyB1c2UgaW5pdCgpIGZvciBub3cuCglpZiAobWVybWFpZFJlYWR5KCkpIHsKCQl0cnkgewoJCQltZXJtYWlkLmluaXQoKTsKCQl9IGNhdGNoIChlcnJvcikgewoJCQljb25zb2xlLmVycm9yKCdNZXJtYWlkIGVycm9yJywgZXJyb3IpOwoJCX0KCgkJLy8gUmVzZXR0aW5nIGVsZW1lbnRzIHNpemUgLSBzZWUgbWVybWFpZC50cwoJCWNvbnN0IGVsZW1lbnRzID0gZG9jdW1lbnQuZ2V0RWxlbWVudHNCeUNsYXNzTmFtZSgnbWVybWFpZCcpOwoJCWZvciAoY29uc3QgZWxlbWVudCBvZiBlbGVtZW50cykgewoJCQllbGVtZW50LnN0eWxlLndpZHRoID0gJzEwMCUnOwoJCX0KCX0KfQoKZG9jdW1lbnQuYWRkRXZlbnRMaXN0ZW5lcignam9wbGluLW5vdGVEaWRVcGRhdGUnLCAoKSA9PiB7CgltZXJtYWlkSW5pdCgpOwp9KTsKCmNvbnN0IGluaXRJSURfID0gc2V0SW50ZXJ2YWwoKCkgPT4gewoJY29uc3QgaXNSZWFkeSA9IG1lcm1haWRSZWFkeSgpOwoJaWYgKGlzUmVhZHkpIHsKCQljbGVhckludGVydmFsKGluaXRJSURfKTsKCQltZXJtYWlkSW5pdCgpOwoJfQp9LCAxMDApOwo=`; module.exports = `LyogZ2xvYmFsIG1lcm1haWQgKi8KCmZ1bmN0aW9uIG1lcm1haWRSZWFkeSgpIHsKCS8vIFRoZSBNZXJtYWlkIGluaXRpYWxpemF0aW9uIGNvZGUgcmVuZGVycyB0aGUgTWVybWFpZCBjb2RlIHdpdGhpbiBhbnkgZWxlbWVudCB3aXRoIGNsYXNzICJtZXJtYWlkIiBvcgoJLy8gSUQgIm1lcm1haWQiLiBIb3dldmVyIGluIHNvbWUgY2FzZXMgc29tZSBlbGVtZW50cyBtaWdodCBoYXZlIHRoaXMgSUQgYnV0IG5vdCBiZSBNZXJtYWlkIGNvZGUuCgkvLyBGb3IgZXhhbXBsZSwgTWFya2Rvd24gY29kZSBsaWtlIHRoaXM6CgkvLwoJLy8gICAgICMgTWVybWFpZAoJLy8KCS8vIFdpbGwgZ2VuZXJhdGUgdGhpcyBIVE1MOgoJLy8KCS8vICAgICA8aDEgaWQ9Im1lcm1haWQiPk1lcm1haWQ8L2gxPgoJLy8KCS8vIEFuZCB0aGF0J3MgZ29pbmcgdG8gbWFrZSB0aGUgbGliIHNldCB0aGUgYG1lcm1haWRgIG9iamVjdCB0byB0aGUgSDEgZWxlbWVudC4KCS8vIFNvIGJlbG93LCB3ZSBkb3VibGUtY2hlY2sgdGhhdCB3aGF0IHdlIGhhdmUgcmVhbGx5IGlzIGFuIGluc3RhbmNlIG9mIHRoZSBsaWJyYXJ5LgoJcmV0dXJuIHR5cGVvZiBtZXJtYWlkICE9PSAndW5kZWZpbmVkJyAmJiBtZXJtYWlkICE9PSBudWxsICYmIHR5cGVvZiBtZXJtYWlkID09PSAnb2JqZWN0JyAmJiAhIW1lcm1haWQuaW5pdGlhbGl6ZTsKfQoKY29uc3QgaXNEYXJrTW9kZSA9ICgpID0+IHsKCS8vIElmIGFueSBtZXJtYWlkIGVsZW1lbnRzIGFyZSBtYXJrZWQgYXMgcmVxdWlyaW5nIGRhcmsgbW9kZSwgcmVuZGVyICphbGwqCgkvLyBtZXJtYWlkIGVsZW1lbnRzIGluIGRhcmsgbW9kZS4KCXJldHVybiAhIWRvY3VtZW50LnF1ZXJ5U2VsZWN0b3IoJy5tZXJtYWlkLmpvcGxpbi0tbWVybWFpZC11c2UtZGFyay10aGVtZScpOwp9OwoKZnVuY3Rpb24gbWVybWFpZEluaXQoKSB7CglpZiAobWVybWFpZFJlYWR5KCkpIHsKCQljb25zdCBtZXJtYWlkVGFyZ2V0Tm9kZXMgPSBkb2N1bWVudC5nZXRFbGVtZW50c0J5Q2xhc3NOYW1lKCdtZXJtYWlkJyk7CgoJCXRyeSB7CgkJCWNvbnN0IGRhcmtNb2RlID0gaXNEYXJrTW9kZSgpOwoJCQltZXJtYWlkLmluaXRpYWxpemUoewoJCQkJLy8gV2UgY2FsbCBtZXJtYWlkLnJ1biBvdXJzZWx2ZXMgd2hlbmV2ZXIgdGhlIG5vdGUgdXBkYXRlcy4gRG9uJ3QgYXV0by1zdGFydAoJCQkJc3RhcnRPbkxvYWQ6IGZhbHNlLAoKCQkJCWRhcmtNb2RlLAoJCQkJdGhlbWU6IGRhcmtNb2RlID8gJ2RhcmsnIDogJ2RlZmF1bHQnLAoJCQl9KTsKCQkJbWVybWFpZC5ydW4oewoJCQkJbm9kZXM6IG1lcm1haWRUYXJnZXROb2RlcywKCQkJfSk7CgkJfSBjYXRjaCAoZXJyb3IpIHsKCQkJY29uc29sZS5lcnJvcignTWVybWFpZCBlcnJvcicsIGVycm9yKTsKCQl9CgoJCS8vIFJlc2V0dGluZyBlbGVtZW50cyBzaXplIC0gc2VlIG1lcm1haWQudHMKCQlmb3IgKGNvbnN0IGVsZW1lbnQgb2YgbWVybWFpZFRhcmdldE5vZGVzKSB7CgkJCWVsZW1lbnQuc3R5bGUud2lkdGggPSAnMTAwJSc7CgkJfQoJfQp9Cgpkb2N1bWVudC5hZGRFdmVudExpc3RlbmVyKCdqb3BsaW4tbm90ZURpZFVwZGF0ZScsICgpID0+IHsKCW1lcm1haWRJbml0KCk7Cn0pOwoKY29uc3QgaW5pdElJRF8gPSBzZXRJbnRlcnZhbCgoKSA9PiB7Cgljb25zdCBpc1JlYWR5ID0gbWVybWFpZFJlYWR5KCk7CglpZiAoaXNSZWFkeSkgewoJCWNsZWFySW50ZXJ2YWwoaW5pdElJRF8pOwoJCW1lcm1haWRJbml0KCk7Cgl9Cn0sIDEwMCk7Cgpkb2N1bWVudC5hZGRFdmVudExpc3RlbmVyKCdET01Db250ZW50TG9hZGVkJywgKCkgPT4gewoJLy8gSW4gc29tZSBlbnZpcm9ubWVudHMsIHdlIGNhbiBsb2FkIE1lcm1haWQgaW1tZWRpYXRlbHkgKGUuZy4gbW9iaWxlKS4KCS8vIElmIHdlIGRvbid0IGxvYWQgaXQgaW1tZWRpYXRlbHkgaW4gdGhlc2UgZW52aXJvbm1lbnRzLCBNZXJtYWlkIHNlZW1zCgkvLyB0byBpbml0aWFsaXplIGFuZCBhdXRvLXJ1biwgYnV0IHdpdGhvdXQgb3VyIGNvbmZpZ3VyYXRpb24gY2hhbmdlcy4KCWlmIChtZXJtYWlkUmVhZHkoKSkgewoJCW1lcm1haWRJbml0KCk7Cgl9IGVsc2UgewoJCWNsZWFySW50ZXJ2YWwoaW5pdElJRF8pOwoJfQp9KTsK`;

View File

@ -11,7 +11,7 @@ export default {
// Note: Mermaid is buggy when rendering below a certain width (500px?) // Note: Mermaid is buggy when rendering below a certain width (500px?)
// so set an arbitrarily high width here for the container. Once the // so set an arbitrarily high width here for the container. Once the
// diagram is rendered it will be reset to 100% in mermaid_render.js // diagram is rendered it will be reset to 100% in mermaid_render.js
text: '.mermaid { background-color: white; width: 640px; }', text: '.mermaid { width: 640px; }',
mime: 'text/css', mime: 'text/css',
}, },
{ {
@ -40,6 +40,15 @@ export default {
const token = tokens[idx]; const token = tokens[idx];
if (token.info !== 'mermaid') return defaultRender(tokens, idx, options, env, self); if (token.info !== 'mermaid') return defaultRender(tokens, idx, options, env, self);
const contentHtml = markdownIt.utils.escapeHtml(token.content); const contentHtml = markdownIt.utils.escapeHtml(token.content);
const cssClasses = ['mermaid'];
if (ruleOptions.theme.appearance === 'dark') {
// This class applies globally -- if any elements have this class, all mermaid
// elements will be rendered in dark mode.
// (See mermaid_render.js for details).
cssClasses.push('joplin--mermaid-use-dark-theme');
}
// Note: The mermaid script (`contentHtml`) needs to be wrapped // Note: The mermaid script (`contentHtml`) needs to be wrapped
// in a `pre` tag, otherwise in WYSIWYG mode TinyMCE removes // in a `pre` tag, otherwise in WYSIWYG mode TinyMCE removes
// all the white space from it, which causes mermaid to fail. // all the white space from it, which causes mermaid to fail.
@ -48,7 +57,7 @@ export default {
<div class="joplin-editable"> <div class="joplin-editable">
<pre class="joplin-source" data-joplin-language="mermaid" data-joplin-source-open="\`\`\`mermaid&#10;" data-joplin-source-close="&#10;\`\`\`&#10;">${contentHtml}</pre> <pre class="joplin-source" data-joplin-language="mermaid" data-joplin-source-open="\`\`\`mermaid&#10;" data-joplin-source-close="&#10;\`\`\`&#10;">${contentHtml}</pre>
${exportButtonMarkup} ${exportButtonMarkup}
<pre class="mermaid">${contentHtml}</pre> <pre class="${cssClasses.join(' ')}">${contentHtml}</pre>
</div> </div>
`; `;
}; };

View File

@ -13,23 +13,37 @@ function mermaidReady() {
// //
// And that's going to make the lib set the `mermaid` object to the H1 element. // And that's going to make the lib set the `mermaid` object to the H1 element.
// So below, we double-check that what we have really is an instance of the library. // So below, we double-check that what we have really is an instance of the library.
return typeof mermaid !== 'undefined' && mermaid !== null && typeof mermaid === 'object' && !!mermaid.init; return typeof mermaid !== 'undefined' && mermaid !== null && typeof mermaid === 'object' && !!mermaid.initialize;
} }
const isDarkMode = () => {
// If any mermaid elements are marked as requiring dark mode, render *all*
// mermaid elements in dark mode.
return !!document.querySelector('.mermaid.joplin--mermaid-use-dark-theme');
};
function mermaidInit() { function mermaidInit() {
// Mermaid's wonderful API has two init methods: init() and initialize().
// init() is deprectated but works, and initialize() is recommended but doesn't
// work, so let's use init() for now.
if (mermaidReady()) { if (mermaidReady()) {
const mermaidTargetNodes = document.getElementsByClassName('mermaid');
try { try {
mermaid.init(); const darkMode = isDarkMode();
mermaid.initialize({
// We call mermaid.run ourselves whenever the note updates. Don't auto-start
startOnLoad: false,
darkMode,
theme: darkMode ? 'dark' : 'default',
});
mermaid.run({
nodes: mermaidTargetNodes,
});
} catch (error) { } catch (error) {
console.error('Mermaid error', error); console.error('Mermaid error', error);
} }
// Resetting elements size - see mermaid.ts // Resetting elements size - see mermaid.ts
const elements = document.getElementsByClassName('mermaid'); for (const element of mermaidTargetNodes) {
for (const element of elements) {
element.style.width = '100%'; element.style.width = '100%';
} }
} }
@ -46,3 +60,14 @@ const initIID_ = setInterval(() => {
mermaidInit(); mermaidInit();
} }
}, 100); }, 100);
document.addEventListener('DOMContentLoaded', () => {
// In some environments, we can load Mermaid immediately (e.g. mobile).
// If we don't load it immediately in these environments, Mermaid seems
// to initialize and auto-run, but without our configuration changes.
if (mermaidReady()) {
mermaidInit();
} else {
clearInterval(initIID_);
}
});

View File

@ -13,23 +13,37 @@ function mermaidReady() {
// //
// And that's going to make the lib set the `mermaid` object to the H1 element. // And that's going to make the lib set the `mermaid` object to the H1 element.
// So below, we double-check that what we have really is an instance of the library. // So below, we double-check that what we have really is an instance of the library.
return typeof mermaid !== 'undefined' && mermaid !== null && typeof mermaid === 'object' && !!mermaid.init; return typeof mermaid !== 'undefined' && mermaid !== null && typeof mermaid === 'object' && !!mermaid.initialize;
} }
const isDarkMode = () => {
// If any mermaid elements are marked as requiring dark mode, render *all*
// mermaid elements in dark mode.
return !!document.querySelector('.mermaid.joplin--mermaid-use-dark-theme');
};
function mermaidInit() { function mermaidInit() {
// Mermaid's wonderful API has two init methods: init() and initialize().
// init() is deprectated but works, and initialize() is recommended but doesn't
// work, so let's use init() for now.
if (mermaidReady()) { if (mermaidReady()) {
const mermaidTargetNodes = document.getElementsByClassName('mermaid');
try { try {
mermaid.init(); const darkMode = isDarkMode();
mermaid.initialize({
// We call mermaid.run ourselves whenever the note updates. Don't auto-start
startOnLoad: false,
darkMode,
theme: darkMode ? 'dark' : 'default',
});
mermaid.run({
nodes: mermaidTargetNodes,
});
} catch (error) { } catch (error) {
console.error('Mermaid error', error); console.error('Mermaid error', error);
} }
// Resetting elements size - see mermaid.ts // Resetting elements size - see mermaid.ts
const elements = document.getElementsByClassName('mermaid'); for (const element of mermaidTargetNodes) {
for (const element of elements) {
element.style.width = '100%'; element.style.width = '100%';
} }
} }
@ -46,3 +60,14 @@ const initIID_ = setInterval(() => {
mermaidInit(); mermaidInit();
} }
}, 100); }, 100);
document.addEventListener('DOMContentLoaded', () => {
// In some environments, we can load Mermaid immediately (e.g. mobile).
// If we don't load it immediately in these environments, Mermaid seems
// to initialize and auto-run, but without our configuration changes.
if (mermaidReady()) {
mermaidInit();
} else {
clearInterval(initIID_);
}
});

View File

@ -29,6 +29,11 @@
border-bottom: none; border-bottom: none;
} }
/* Overrides Bulma's style for <pre>s to prevent Mermaid elements from having a grey background */
.mermaid {
background-color: white;
}
/* Need to set overflow so that long tables for example are scrollable. We put /* Need to set overflow so that long tables for example are scrollable. We put
it here and not in noteStyle because the desktop app handles scrolling it here and not in noteStyle because the desktop app handles scrolling
differently at the viewer level. differently at the viewer level.