((resolve, reject) => {
+ let canvas: HTMLCanvasElement;
+ let img: HTMLImageElement;
+
+ const cleanUpAndReject = (e: Error) => {
+ if (canvas) canvas.remove();
+ if (img) img.remove();
+ return reject(e);
+ };
+
+ try {
+ img = document.createElement('img');
+ if (!img) throw new Error('Failed to create img element');
+ } catch (e) {
+ return cleanUpAndReject(e);
+ }
+
+ img.onload = function() {
+ try {
+ canvas = document.createElement('canvas');
+ if (!canvas) throw new Error('Failed to create canvas element');
+ canvas.width = img.width;
+ canvas.height = img.height;
+ const ctx = canvas.getContext('2d');
+ if (!ctx) throw new Error('Failed to get context');
+ ctx.drawImage(img, 0, 0, img.width, img.height, 0, 0, img.width, img.height);
+ const pngUri = canvas.toDataURL('image/png');
+ if (!pngUri) throw new Error('Failed to generate png uri');
+ const pngBase64 = pngUri.split(',')[1];
+ const byteString = atob(pngBase64);
+ // write the bytes of the string to a typed array
+ const buff = new Uint8Array(byteString.length);
+ for (let i = 0; i < byteString.length; i++) {
+ buff[i] = byteString.charCodeAt(i);
+ }
+ canvas.remove();
+ img.remove();
+ resolve(buff);
+ } catch (err) {
+ cleanUpAndReject(err);
+ }
+ };
+ img.onerror = function(e) {
+ cleanUpAndReject(new Error(e.toString()));
+ };
+ img.src = svg;
+ });
+};
diff --git a/packages/app-desktop/gui/NoteEditor/utils/useMessageHandler.ts b/packages/app-desktop/gui/NoteEditor/utils/useMessageHandler.ts
index cdc94e5ad2..a26123e518 100644
--- a/packages/app-desktop/gui/NoteEditor/utils/useMessageHandler.ts
+++ b/packages/app-desktop/gui/NoteEditor/utils/useMessageHandler.ts
@@ -35,6 +35,8 @@ export default function useMessageHandler(scrollWhenReady: any, setScrollWhenRea
const menu = await contextMenu({
itemType: arg0 && arg0.type,
resourceId: arg0.resourceId,
+ filename: arg0.filename,
+ mime: arg0.mime,
textToCopy: arg0.textToCopy,
linkToCopy: arg0.linkToCopy || null,
htmlToCopy: '',
diff --git a/packages/app-desktop/gui/note-viewer/index.html b/packages/app-desktop/gui/note-viewer/index.html
index 66cded6760..6559dc9d75 100644
--- a/packages/app-desktop/gui/note-viewer/index.html
+++ b/packages/app-desktop/gui/note-viewer/index.html
@@ -586,9 +586,24 @@
}));
document.addEventListener('contextmenu', webviewLib.logEnabledEventHandler(event => {
- let element = event.target;
-
// To handle right clicks on resource icons
+ let element = event.target;
+
+ // Mermaid svgs are wrapped inside a with class "mermaid"
+ let mermaidElement = element.closest(".mermaid")?.children[0];
+ if (mermaidElement) {
+ const svgString = new XMLSerializer().serializeToString(mermaidElement);
+ if (!!svgString) {
+ ipcProxySendToHost('contextMenu', {
+ type: 'image',
+ textToCopy: svgString,
+ mime: 'image/svg+xml',
+ filename: mermaidElement.id + '.svg',
+ });
+ }
+ return;
+ }
+
if (element && !element.getAttribute('data-resource-id')) element = element.parentElement;
if (element && element.getAttribute('data-resource-id')) {
diff --git a/packages/app-desktop/loadResources.testEnv.ts b/packages/app-desktop/loadResources.testEnv.ts
new file mode 100644
index 0000000000..9fc4853ae5
--- /dev/null
+++ b/packages/app-desktop/loadResources.testEnv.ts
@@ -0,0 +1,20 @@
+/**
+ * A Jest custom test Environment to load the resources for the tests.
+ * Use this test envirenment when you work with resources like images, files.
+ * See gui/NoteEditor/utils/contextMenu.test.ts for an example.
+ */
+
+const JSDOMEnvironment = require('jest-environment-jsdom');
+import type { EnvironmentContext } from '@jest/environment';
+import type { Config } from '@jest/types';
+
+
+export default class CustomEnvironment extends JSDOMEnvironment {
+ constructor(config: Config.ProjectConfig, context?: EnvironmentContext) {
+ // Resources is set to 'usable' to enable fetching of resources like images and fonts while testing
+ // Which does not happen by default in jest
+ // https://stackoverflow.com/a/49482563
+ config.testEnvironmentOptions.resources = 'usable';
+ super(config, context);
+ }
+}
diff --git a/packages/app-desktop/package.json b/packages/app-desktop/package.json
index 6fa9856636..0151818590 100644
--- a/packages/app-desktop/package.json
+++ b/packages/app-desktop/package.json
@@ -116,6 +116,7 @@
"app-builder-bin": "^1.9.11",
"babel-cli": "^6.26.0",
"babel-preset-react": "^6.24.1",
+ "canvas": "^2.9.0",
"electron": "14.1.0",
"electron-builder": "^22.11.7",
"electron-notarize": "^1.0.0",
diff --git a/yarn.lock b/yarn.lock
index cf7dabb1b3..8d67c2a642 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2910,6 +2910,7 @@ __metadata:
async-mutex: ^0.1.3
babel-cli: ^6.26.0
babel-preset-react: ^6.24.1
+ canvas: ^2.9.0
codemirror: ^5.56.0
color: ^3.1.2
compare-versions: ^3.2.1
@@ -4339,6 +4340,25 @@ __metadata:
languageName: node
linkType: hard
+"@mapbox/node-pre-gyp@npm:^1.0.0":
+ version: 1.0.8
+ resolution: "@mapbox/node-pre-gyp@npm:1.0.8"
+ dependencies:
+ detect-libc: ^1.0.3
+ https-proxy-agent: ^5.0.0
+ make-dir: ^3.1.0
+ node-fetch: ^2.6.5
+ nopt: ^5.0.0
+ npmlog: ^5.0.1
+ rimraf: ^3.0.2
+ semver: ^7.3.5
+ tar: ^6.1.11
+ bin:
+ node-pre-gyp: bin/node-pre-gyp
+ checksum: 29a38f39575107fa1665edf14defcfdf62e12bb38e9c27f7457ba42be84060125015171d12b8de3065155a465992f1854a363e2985f071fcbea9ff0701362b05
+ languageName: node
+ linkType: hard
+
"@mrmlnc/readdir-enhanced@npm:^2.2.1":
version: 2.2.1
resolution: "@mrmlnc/readdir-enhanced@npm:2.2.1"
@@ -8915,6 +8935,18 @@ __metadata:
languageName: node
linkType: hard
+"canvas@npm:^2.9.0":
+ version: 2.9.0
+ resolution: "canvas@npm:2.9.0"
+ dependencies:
+ "@mapbox/node-pre-gyp": ^1.0.0
+ nan: ^2.15.0
+ node-gyp: latest
+ simple-get: ^3.0.3
+ checksum: 376ccd47340a46c04d5cabafd6feb1b7ae82c92dc3ae6db68c9cbac17ec1c43d2bf6aab60019690e9d49bf40b41bee3e4e0a8901f39b53e993789698e77e2699
+ languageName: node
+ linkType: hard
+
"capital-case@npm:^1.0.4":
version: 1.0.4
resolution: "capital-case@npm:1.0.4"
@@ -14886,6 +14918,23 @@ __metadata:
languageName: node
linkType: hard
+"gauge@npm:^3.0.0":
+ version: 3.0.2
+ resolution: "gauge@npm:3.0.2"
+ dependencies:
+ aproba: ^1.0.3 || ^2.0.0
+ color-support: ^1.1.2
+ console-control-strings: ^1.0.0
+ has-unicode: ^2.0.1
+ object-assign: ^4.1.1
+ signal-exit: ^3.0.0
+ string-width: ^4.2.3
+ strip-ansi: ^6.0.1
+ wide-align: ^1.1.2
+ checksum: 81296c00c7410cdd48f997800155fbead4f32e4f82109be0719c63edc8560e6579946cc8abd04205297640691ec26d21b578837fd13a4e96288ab4b40b1dc3e9
+ languageName: node
+ linkType: hard
+
"gauge@npm:^4.0.0":
version: 4.0.0
resolution: "gauge@npm:4.0.0"
@@ -20230,7 +20279,7 @@ __metadata:
languageName: node
linkType: hard
-"make-dir@npm:^3.0.0":
+"make-dir@npm:^3.0.0, make-dir@npm:^3.1.0":
version: 3.1.0
resolution: "make-dir@npm:3.1.0"
dependencies:
@@ -21751,7 +21800,7 @@ __metadata:
languageName: node
linkType: hard
-"nan@npm:^2.12.1":
+"nan@npm:^2.12.1, nan@npm:^2.15.0":
version: 2.15.0
resolution: "nan@npm:2.15.0"
dependencies:
@@ -22019,6 +22068,20 @@ __metadata:
languageName: node
linkType: hard
+"node-fetch@npm:^2.6.5":
+ version: 2.6.7
+ resolution: "node-fetch@npm:2.6.7"
+ dependencies:
+ whatwg-url: ^5.0.0
+ peerDependencies:
+ encoding: ^0.1.0
+ peerDependenciesMeta:
+ encoding:
+ optional: true
+ checksum: 8d816ffd1ee22cab8301c7756ef04f3437f18dace86a1dae22cf81db8ef29c0bf6655f3215cb0cdb22b420b6fe141e64b26905e7f33f9377a7fa59135ea3e10b
+ languageName: node
+ linkType: hard
+
"node-gyp-build@npm:^4.2.1":
version: 4.3.0
resolution: "node-gyp-build@npm:4.3.0"
@@ -22473,6 +22536,18 @@ __metadata:
languageName: node
linkType: hard
+"npmlog@npm:^5.0.1":
+ version: 5.0.1
+ resolution: "npmlog@npm:5.0.1"
+ dependencies:
+ are-we-there-yet: ^2.0.0
+ console-control-strings: ^1.1.0
+ gauge: ^3.0.0
+ set-blocking: ^2.0.0
+ checksum: 516b2663028761f062d13e8beb3f00069c5664925871a9b57989642ebe09f23ab02145bf3ab88da7866c4e112cafff72401f61a672c7c8a20edc585a7016ef5f
+ languageName: node
+ linkType: hard
+
"npmlog@npm:^6.0.0":
version: 6.0.0
resolution: "npmlog@npm:6.0.0"
@@ -28646,7 +28721,7 @@ __metadata:
languageName: node
linkType: hard
-"tar@npm:^6.0.2, tar@npm:^6.0.5, tar@npm:^6.1.2":
+"tar@npm:^6.0.2, tar@npm:^6.0.5, tar@npm:^6.1.11, tar@npm:^6.1.2":
version: 6.1.11
resolution: "tar@npm:6.1.11"
dependencies: