import { RuleOptions } from '../../MdToHtml'; import { RendererTheme } from '../../types'; import type * as MarkdownIt from 'markdown-it'; import type Token = require('markdown-it/lib/token'); import type Renderer = require('markdown-it/lib/renderer'); export default { assets: function(theme: RendererTheme) { return [ { name: 'mermaid.min.js', }, { name: 'mermaid_render.js', }, { inline: true, // Note: Mermaid is buggy when rendering below a certain width (500px?) // 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 text: '.mermaid { width: 640px; }', mime: 'text/css', }, { inline: true, // Override the default pre styles. Using the default `white-space: pre` // can cause math expressions to be too tall and break some diagrams. text: 'pre.mermaid[data-processed=true] { white-space: unset; }', mime: 'text/css', }, { inline: true, // Export button in mermaid graph should be shown only on hovering the mermaid graph // ref: https://github.com/laurent22/joplin/issues/6101 text: ` .mermaid-export-graph { opacity: 0; height: 0; z-index: 1; position: relative; } .joplin-editable:hover .mermaid-export-graph, .joplin-editable .mermaid-export-graph:has(:focus-visible) { opacity: 1; } .mermaid-export-graph > button:hover { background-color: ${theme.backgroundColorHover3} !important; } `.trim(), mime: 'text/css', }, ].map(e => { return { source: 'mermaid', ...e, }; }); }, plugin: function(markdownIt: MarkdownIt, ruleOptions: RuleOptions) { const defaultRender: Renderer.RenderRule = markdownIt.renderer.rules.fence || function(tokens, idx, options, env, self): string { // eslint-disable-next-line @typescript-eslint/no-explicit-any -- this fallback passes extra `env, self` args to renderToken which the type signature does not declare; preserved for parity with other rule fallbacks return (self.renderToken as any)(tokens, idx, options, env, self); }; const exportButtonMarkup = isDesktop(ruleOptions.platformName) ? exportGraphButton(ruleOptions) : ''; markdownIt.renderer.rules.fence = function(tokens: Token[], idx: number, options: MarkdownIt.Options, env: unknown, self: Renderer) { const token = tokens[idx]; if (token.info !== 'mermaid') return defaultRender(tokens, idx, options, env, self); ruleOptions.context.pluginWasUsed.mermaid = true; const contentHtml = markdownIt.utils.escapeHtml((token.content as string).trimEnd()); 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 // in a `pre` tag, otherwise in WYSIWYG mode TinyMCE removes // all the white space from it, which causes mermaid to fail. // See PR #4670 https://github.com/laurent22/joplin/pull/4670 return `
${exportButtonMarkup}
${contentHtml}
`; }; }, }; const exportGraphButton = (ruleOptions: RuleOptions) => { const theme = ruleOptions.theme; const style = ` display: block; margin-left: auto; border-radius: ${theme.buttonStyle.borderRadius}px; font-size: ${theme.fontSize}px; color: ${theme.color}; background: ${theme.buttonStyle.backgroundColor}; border: ${theme.buttonStyle.border}; `.trim(); // OnClick is handled in the renderer script return `
`; }; const downloadIcon = () => { // https://www.svgrepo.com/svg/505363/download return ''; }; const isDesktop = (platformName?: string) => { if (!platformName) { return false; } return ['darwin', 'linux', 'freebsd', 'win32'].includes(platformName); };