diff --git a/packages/app-desktop/gui/NoteEditor/utils/useMarkupToHtml.ts b/packages/app-desktop/gui/NoteEditor/utils/useMarkupToHtml.ts index 3017382a90..703c5e5037 100644 --- a/packages/app-desktop/gui/NoteEditor/utils/useMarkupToHtml.ts +++ b/packages/app-desktop/gui/NoteEditor/utils/useMarkupToHtml.ts @@ -3,6 +3,7 @@ import { useCallback, useMemo } from 'react'; import { ResourceInfos } from './types'; import markupLanguageUtils from '../../../utils/markupLanguageUtils'; import Setting from '@joplin/lib/models/Setting'; +import shim from '@joplin/lib/shim'; const { themeStyle } = require('@joplin/lib/theme'); import Note from '@joplin/lib/models/Note'; @@ -23,6 +24,7 @@ export interface MarkupToHtmlOptions { useCustomPdfViewer?: boolean; noteId?: string; vendorDir?: string; + platformName?: string; } export default function useMarkupToHtml(deps: HookDependencies) { @@ -40,6 +42,7 @@ export default function useMarkupToHtml(deps: HookDependencies) { options = { replaceResourceInternalToExternalLinks: false, resourceInfos: {}, + platformName: shim.platformName(), ...options, }; diff --git a/packages/renderer/MdToHtml.ts b/packages/renderer/MdToHtml.ts index b5acd0ef22..327f286c45 100644 --- a/packages/renderer/MdToHtml.ts +++ b/packages/renderer/MdToHtml.ts @@ -179,6 +179,8 @@ export interface RuleOptions { noteId?: string; vendorDir?: string; itemIdToUrl?: ItemIdToUrlHandler; + + platformName?: string; } export default class MdToHtml { diff --git a/packages/renderer/MdToHtml/rules/mermaid.ts b/packages/renderer/MdToHtml/rules/mermaid.ts index 59222030be..40af1cff15 100644 --- a/packages/renderer/MdToHtml/rules/mermaid.ts +++ b/packages/renderer/MdToHtml/rules/mermaid.ts @@ -1,6 +1,8 @@ +import { RuleOptions } from '../../MdToHtml'; + export default { - assets: function() { + assets: function(theme: any) { return [ { name: 'mermaid.min.js' }, { name: 'mermaid_render.js' }, @@ -12,14 +14,27 @@ export default { text: '.mermaid { background-color: white; width: 640px; }', 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 { visibility: hidden; } + .joplin-editable:hover .mermaid-export-graph { visibility: visible; } + .mermaid-export-graph:hover { background-color: ${theme.backgroundColorHover3} !important; } + `.trim(), + mime: 'text/css', + }, ]; }, - plugin: function(markdownIt: any) { + plugin: function(markdownIt: any, ruleOptions: RuleOptions) { const defaultRender: Function = markdownIt.renderer.rules.fence || function(tokens: any[], idx: number, options: any, env: any, self: any) { return self.renderToken(tokens, idx, options, env, self); }; + const exportButtonMarkup = isDesktop(ruleOptions.platformName) ? exportGraphButton(ruleOptions) : ''; + markdownIt.renderer.rules.fence = function(tokens: any[], idx: number, options: {}, env: any, self: any) { const token = tokens[idx]; if (token.info !== 'mermaid') return defaultRender(tokens, idx, options, env, self); @@ -31,9 +46,49 @@ export default { return `
${contentHtml}
+ ${exportButtonMarkup}
${contentHtml}
`; }; }, }; + +const exportGraphButton = (ruleOptions: RuleOptions) => { + const theme = ruleOptions.theme; + // Clicking on export button manually triggers a right click context menu event + const onClickHandler = ` + const target = arguments[0].target; + const button = target.closest("button.mermaid-export-graph"); + if (!button) return false; + const $mermaid_elem = button.nextElementSibling; + const rightClickEvent = new PointerEvent("contextmenu", {bubbles: true}); + rightClickEvent.target = $mermaid_elem; + $mermaid_elem.dispatchEvent(rightClickEvent); + return false; + `.trim(); + 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(); + + 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); +};