missing path2d support for freedawings, remove node-side rendering, allow async getContent()

* ## Excalidraw and SVG
 * 2022-04-16 - @thfrei
 *
 * Known issues:
 *  - excalidraw-to-svg (node.js) does not render any hand drawn (freedraw) paths. There is an issue with
 *    Path2D object not present in node-canvas library used by jsdom. (See Trilium PR for samples and other issues
 *    in respective library. Link will be added later). Related links:
 *     - https://github.com/Automattic/node-canvas/pull/2013
 *     - https://github.com/google/canvas-5-polyfill
 *     - https://github.com/Automattic/node-canvas/issues/1116
 *     - https://www.npmjs.com/package/path2d-polyfill
 *  - excalidraw-to-svg (node.js) takes quite some time to load an image (1-2s)
 *  - excalidraw-utils (browser) does render freedraw, however NOT freedraw with background
 *
 * Due to this issues, we opt to use **only excalidraw in the frontend**. Upon saving, we will also get the SVG
 * output from the live excalidraw instance. We will save this **SVG side by side the native excalidraw format
 * in the trilium note**.
 *
 * Pro: we will combat bit-rot. Showing the SVG will be very fast, since it is already rendered.
 * Con: The note will get bigger (maybe +30%?), we will generate more bandwith.
 *      (However, using trilium desktop instance, does not care too much about bandwidth. Size increase is probably
 *       acceptable, as a trade off.)
This commit is contained in:
Tom 2022-04-19 00:21:20 +02:00
parent c295fdb142
commit 9771b441ad
10 changed files with 84 additions and 420 deletions

336
package-lock.json generated
View file

@ -11,7 +11,6 @@
"dependencies": {
"@electron/remote": "2.0.8",
"@excalidraw/excalidraw": "0.11.0",
"@excalidraw/utils": "0.1.2",
"archiver": "5.3.1",
"async-mutex": "0.3.2",
"axios": "0.26.1",
@ -27,7 +26,6 @@
"electron-dl": "3.3.1",
"electron-find": "1.0.7",
"electron-window-state": "5.0.3",
"excalidraw-to-svg": "3.0.0",
"express": "4.17.2",
"express-partial-content": "1.0.2",
"express-rate-limit": "6.3.0",
@ -2910,16 +2908,6 @@
"node": ">=0.8"
}
},
"node_modules/astral-regex": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz",
"integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==",
"dev": true,
"optional": true,
"engines": {
"node": ">=8"
}
},
"node_modules/async": {
"version": "2.6.3",
"resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz",
@ -3997,71 +3985,6 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/cli-truncate": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-2.1.0.tgz",
"integrity": "sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==",
"dev": true,
"optional": true,
"dependencies": {
"slice-ansi": "^3.0.0",
"string-width": "^4.2.0"
},
"engines": {
"node": ">=8"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/cli-truncate/node_modules/ansi-regex": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
"dev": true,
"optional": true,
"engines": {
"node": ">=8"
}
},
"node_modules/cli-truncate/node_modules/is-fullwidth-code-point": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
"dev": true,
"optional": true,
"engines": {
"node": ">=8"
}
},
"node_modules/cli-truncate/node_modules/string-width": {
"version": "4.2.3",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
"dev": true,
"optional": true,
"dependencies": {
"emoji-regex": "^8.0.0",
"is-fullwidth-code-point": "^3.0.0",
"strip-ansi": "^6.0.1"
},
"engines": {
"node": ">=8"
}
},
"node_modules/cli-truncate/node_modules/strip-ansi": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
"dev": true,
"optional": true,
"dependencies": {
"ansi-regex": "^5.0.1"
},
"engines": {
"node": ">=8"
}
},
"node_modules/cliui": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz",
@ -4966,33 +4889,6 @@
"node": ">=0.10.0"
}
},
"node_modules/dmg-license": {
"version": "1.0.11",
"resolved": "https://registry.npmjs.org/dmg-license/-/dmg-license-1.0.11.tgz",
"integrity": "sha512-ZdzmqwKmECOWJpqefloC5OJy1+WZBBse5+MR88z9g9Zn4VY+WYUkAyojmhzJckH5YbbZGcYIuGAkY5/Ys5OM2Q==",
"deprecated": "Disk image license agreements are deprecated by Apple and will probably be removed in a future macOS release. Discussion at: https://github.com/argv-minus-one/dmg-license/issues/11",
"dev": true,
"optional": true,
"os": [
"darwin"
],
"dependencies": {
"@types/plist": "^3.0.1",
"@types/verror": "^1.10.3",
"ajv": "^6.10.0",
"crc": "^3.8.0",
"iconv-corefoundation": "^1.1.7",
"plist": "^3.0.4",
"smart-buffer": "^4.0.2",
"verror": "^1.10.0"
},
"bin": {
"dmg-license": "bin/dmg-license.js"
},
"engines": {
"node": ">=8"
}
},
"node_modules/dom-serializer": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.3.2.tgz",
@ -7216,19 +7112,6 @@
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
"integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8="
},
"node_modules/fsevents": {
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
"hasInstallScript": true,
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
}
},
"node_modules/function-bind": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
@ -7759,23 +7642,6 @@
"ms": "^2.0.0"
}
},
"node_modules/iconv-corefoundation": {
"version": "1.1.7",
"resolved": "https://registry.npmjs.org/iconv-corefoundation/-/iconv-corefoundation-1.1.7.tgz",
"integrity": "sha512-T10qvkw0zz4wnm560lOEg0PovVqUXuOFhhHAkixw8/sycy7TJt7v/RrkEKEQnAw2viPSJu6iAkErxnzR0g8PpQ==",
"dev": true,
"optional": true,
"os": [
"darwin"
],
"dependencies": {
"cli-truncate": "^2.1.0",
"node-addon-api": "^1.6.3"
},
"engines": {
"node": "^8.11.2 || >=10"
}
},
"node_modules/iconv-lite": {
"version": "0.4.24",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
@ -12984,67 +12850,6 @@
"node": ">=8"
}
},
"node_modules/slice-ansi": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-3.0.0.tgz",
"integrity": "sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==",
"dev": true,
"optional": true,
"dependencies": {
"ansi-styles": "^4.0.0",
"astral-regex": "^2.0.0",
"is-fullwidth-code-point": "^3.0.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/slice-ansi/node_modules/ansi-styles": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
"dev": true,
"optional": true,
"dependencies": {
"color-convert": "^2.0.1"
},
"engines": {
"node": ">=8"
},
"funding": {
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
}
},
"node_modules/slice-ansi/node_modules/color-convert": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
"dev": true,
"optional": true,
"dependencies": {
"color-name": "~1.1.4"
},
"engines": {
"node": ">=7.0.0"
}
},
"node_modules/slice-ansi/node_modules/color-name": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
"dev": true,
"optional": true
},
"node_modules/slice-ansi/node_modules/is-fullwidth-code-point": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
"dev": true,
"optional": true,
"engines": {
"node": ">=8"
}
},
"node_modules/smart-buffer": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.1.0.tgz",
@ -17143,13 +16948,6 @@
"resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
"integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU="
},
"astral-regex": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz",
"integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==",
"dev": true,
"optional": true
},
"async": {
"version": "2.6.3",
"resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz",
@ -17979,55 +17777,6 @@
"integrity": "sha512-x/5fWmGMnbKQAaNwN+UZlV79qBLM9JFnJuJ03gIi5whrob0xV0ofNVHy9DhwGdsMJQc2OKv0oGmLzvaqvAVv+g==",
"dev": true
},
"cli-truncate": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-2.1.0.tgz",
"integrity": "sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==",
"dev": true,
"optional": true,
"requires": {
"slice-ansi": "^3.0.0",
"string-width": "^4.2.0"
},
"dependencies": {
"ansi-regex": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
"dev": true,
"optional": true
},
"is-fullwidth-code-point": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
"dev": true,
"optional": true
},
"string-width": {
"version": "4.2.3",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
"dev": true,
"optional": true,
"requires": {
"emoji-regex": "^8.0.0",
"is-fullwidth-code-point": "^3.0.0",
"strip-ansi": "^6.0.1"
}
},
"strip-ansi": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
"dev": true,
"optional": true,
"requires": {
"ansi-regex": "^5.0.1"
}
}
}
},
"cliui": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz",
@ -18733,23 +18482,6 @@
}
}
},
"dmg-license": {
"version": "1.0.11",
"resolved": "https://registry.npmjs.org/dmg-license/-/dmg-license-1.0.11.tgz",
"integrity": "sha512-ZdzmqwKmECOWJpqefloC5OJy1+WZBBse5+MR88z9g9Zn4VY+WYUkAyojmhzJckH5YbbZGcYIuGAkY5/Ys5OM2Q==",
"dev": true,
"optional": true,
"requires": {
"@types/plist": "^3.0.1",
"@types/verror": "^1.10.3",
"ajv": "^6.10.0",
"crc": "^3.8.0",
"iconv-corefoundation": "^1.1.7",
"plist": "^3.0.4",
"smart-buffer": "^4.0.2",
"verror": "^1.10.0"
}
},
"dom-serializer": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.3.2.tgz",
@ -19868,8 +19600,7 @@
"dev": true
},
"excalidraw-to-svg": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/excalidraw-to-svg/-/excalidraw-to-svg-3.0.0.tgz",
"version": "https://registry.npmjs.org/excalidraw-to-svg/-/excalidraw-to-svg-3.0.0.tgz",
"integrity": "sha512-207lOpzfLIkDKxP7Tnwmk2zUVmDKW/mOyQi1XHh+7taO2OMD5Ydvo0rbTHp4DnTb4Q8B2Tp+hkbC2BaUuvi7Lg==",
"requires": {
"@excalidraw/utils": "^0.1.2",
@ -20404,12 +20135,6 @@
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
"integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8="
},
"fsevents": {
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
"optional": true
},
"function-bind": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
@ -20840,17 +20565,6 @@
"ms": "^2.0.0"
}
},
"iconv-corefoundation": {
"version": "1.1.7",
"resolved": "https://registry.npmjs.org/iconv-corefoundation/-/iconv-corefoundation-1.1.7.tgz",
"integrity": "sha512-T10qvkw0zz4wnm560lOEg0PovVqUXuOFhhHAkixw8/sycy7TJt7v/RrkEKEQnAw2viPSJu6iAkErxnzR0g8PpQ==",
"dev": true,
"optional": true,
"requires": {
"cli-truncate": "^2.1.0",
"node-addon-api": "^1.6.3"
}
},
"iconv-lite": {
"version": "0.4.24",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
@ -24814,54 +24528,6 @@
"resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
"integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q=="
},
"slice-ansi": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-3.0.0.tgz",
"integrity": "sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==",
"dev": true,
"optional": true,
"requires": {
"ansi-styles": "^4.0.0",
"astral-regex": "^2.0.0",
"is-fullwidth-code-point": "^3.0.0"
},
"dependencies": {
"ansi-styles": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
"dev": true,
"optional": true,
"requires": {
"color-convert": "^2.0.1"
}
},
"color-convert": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
"dev": true,
"optional": true,
"requires": {
"color-name": "~1.1.4"
}
},
"color-name": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
"dev": true,
"optional": true
},
"is-fullwidth-code-point": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
"dev": true,
"optional": true
}
}
},
"smart-buffer": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.1.0.tgz",

View file

@ -25,7 +25,6 @@
},
"dependencies": {
"@excalidraw/excalidraw": "0.11.0",
"@excalidraw/utils": "0.1.2",
"archiver": "5.3.1",
"async-mutex": "0.3.2",
"axios": "0.26.1",
@ -42,7 +41,6 @@
"electron-find": "1.0.7",
"electron-window-state": "5.0.3",
"@electron/remote": "2.0.8",
"excalidraw-to-svg": "3.0.0",
"express": "4.17.2",
"express-partial-content": "1.0.2",
"express-rate-limit": "6.3.0",

View file

@ -176,33 +176,19 @@ async function setContentPane() {
* can the revisions called without being on the note type before?
* if so: load excalidraw
*/
// FIXME: Does it make sense to use EXCALIDRAW_UTILS that are 1.5 MB
// whereas excalidraw (650kB) +react(12kB)+reactdom(118kB)
/**
* FIXME: We load a font called Virgil.wof2, which originates from excalidraw.com
* REMOVE external dependency!!!! This is defined in the svg in defs.style
*/
await libraryLoader.requireLibrary(libraryLoader.EXCALIDRAW_UTILS);
const {exportToSvg} = window.ExcalidrawUtils
*/
/**
* FIXME: If svg is not present, probably use live excalidraw?
*/
const content = fullNoteRevision.content;
try {
const data = JSON.parse(content)
const excData = {
type: "excalidraw",
version: 2,
source: "trilium",
elements: data.elements,
appState: data.appState,
files: data.files,
}
const svg = await exportToSvg(excData);
$content
.html(
$('<div>')
.html(svg)
);
const svg = data.svg || "no svg present."
$content.html($('<div>').html(svg));
} catch(err) {
console.error("error parsing fullNoteRevision.content as JSON", fullNoteRevision.content, err);
$content.html($("<div>").text("Error parsing content. Please check console.error() for more details."));

View file

@ -67,15 +67,6 @@ const EXCALIDRAW = {
// ]
};
const EXCALIDRAW_UTILS = {
/**
* FIXME: excalidraw-utils does not render pen-background. maybe own built required?
*/
js: [
"node_modules/@excalidraw/utils/dist/excalidraw-utils.min.js", //v0.1.2
]
};
async function requireLibrary(library) {
if (library.css) {
library.css.map(cssUrl => requireCss(cssUrl));
@ -127,6 +118,5 @@ export default {
WHEEL_ZOOM,
FORCE_GRAPH,
MERMAID,
EXCALIDRAW,
EXCALIDRAW_UTILS
EXCALIDRAW
}

View file

@ -150,15 +150,7 @@ async function getRenderedContent(note, options = {}) {
try {
const data = JSON.parse(content)
const excData = {
type: "excalidraw",
version: 2,
source: "trilium",
elements: data.elements,
appState: data.appState,
files: data.files,
}
const svg = await exportToSvg(excData);
const svg = data.svg || "no svg present."
$renderedContent.append($('<div>').html(svg));
} catch(err) {
console.error("error parsing content as JSON", content, err);

View file

@ -68,7 +68,7 @@ export default class NoteDetailWidget extends NoteContextAwareWidget {
const {noteId} = note;
const dto = note.dto;
dto.content = this.getTypeWidget().getContent();
dto.content = await this.getTypeWidget().getContent();
// for read only notes
if (dto.content === undefined) {

View file

@ -6,8 +6,8 @@ import froca from "../../services/froca.js";
import debounce from "./canvas-note-utils/lodash.debounce.js";
import uniqueId from "./canvas-note-utils/lodash.uniqueId.js";
// NoteContextAwareWidget does not handle loading/refreshing of note context
import NoteContextAwareWidget from "../note_context_aware_widget.js";
// NoteContextAwareWidget does not handle loading/refreshing of note context
// import NoteContextAwareWidget from "../note_context_aware_widget.js";
const TPL = `
<div class="canvas-note-widget note-detail-canvas-note note-detail-printable note-detail">
@ -53,6 +53,33 @@ VM18070:2 error trying to resing image file on insertion ChunkLoadError: Loading
*/
/**
* Discussion?: add complete @excalidraw/excalidraw, utils, react, react-dom as library? maybe also node_modules?
* Result: as of know, most dependencies are manually. however no special preference is given.
* Result2: since excalidraw to svg rendering in node is not really working, we can easily do a manual dependency
* management of excalidraw and react.
*/
/**
* ## Excalidraw and SVG
* 2022-04-16 - @thfrei
*
* Known issues:
* - excalidraw-to-svg (node.js) does not render any hand drawn (freedraw) paths. There is an issue with
* Path2D object not present in node-canvas library used by jsdom. (See Trilium PR for samples and other issues
* in respective library. Link will be added later). Related links:
* - https://github.com/Automattic/node-canvas/pull/2013
* - https://github.com/google/canvas-5-polyfill
* - https://github.com/Automattic/node-canvas/issues/1116
* - https://www.npmjs.com/package/path2d-polyfill
* - excalidraw-to-svg (node.js) takes quite some time to load an image (1-2s)
* - excalidraw-utils (browser) does render freedraw, however NOT freedraw with background
*
* Due to this issues, we opt to use **only excalidraw in the frontend**. Upon saving, we will also get the SVG
* output from the live excalidraw instance. We will save this **SVG side by side the native excalidraw format
* in the trilium note**.
*
* Pro: we will combat bit-rot. Showing the SVG will be very fast, since it is already rendered.
* Con: The note will get bigger (maybe +30%?), we will generate more bandwith.
* (However, using trilium desktop instance, does not care too much about bandwidth. Size increase is probably
* acceptable, as a trade off.)
*/
export default class ExcalidrawTypeWidget extends TypeWidget {
constructor() {
@ -236,17 +263,43 @@ export default class ExcalidrawTypeWidget extends TypeWidget {
* gets data from widget container that will be sent via spacedUpdate.scheduleUpdate();
* this is automatically called after this.saveData();
*/
getContent() {
const time = new Date();
async getContent() {
const elements = this.excalidrawRef.current.getSceneElements();
const appState = this.excalidrawRef.current.getAppState();
/**
* A file is not deleted, even though removed from canvas. therefore we only keep
* files that are referenced by an element. Maybe this will change with new excalidraw version?
*/
const files = this.excalidrawRef.current.getFiles();
/**
* parallel svg export to combat bitrot and enable rendering image for note inclusion,
* preview and share.
*/
const svg = await window.Excalidraw.exportToSvg({
elements,
appState,
exportPadding: 5, // 5 px padding
metadata: 'trilium-export',
files
});
/**
* Trials for png
*/
// const png = await window.Excalidraw.exportToBlob(await window.Excalidraw.exportToCanvas({
// elements,
// appState,
// files
// }))
// function blobToBase64(blob) {
// return new Promise((resolve, _) => {
// const reader = new FileReader();
// reader.onloadend = () => resolve(reader.result);
// reader.readAsDataURL(blob);
// });
// }
const activeFiles = {};
elements.forEach((element) => {
@ -260,9 +313,9 @@ export default class ExcalidrawTypeWidget extends TypeWidget {
elements,
appState,
files: activeFiles,
time,
svg: svg.outerHTML,
// png: await blobToBase64(png),
};
// this.log('getContent()', content, activeFiles);
return JSON.stringify(content);
}

View file

@ -37,6 +37,9 @@ export default class TypeWidget extends NoteContextAwareWidget {
return this.$widget.is(":visible");
}
/**
* FIXME: add async here to indicate promise?
*/
getContent() {}
focus() {}

View file

@ -1,6 +1,5 @@
"use strict";
const excalidrawToSvg = require("excalidraw-to-svg");
const imageService = require('../../services/image');
const becca = require('../../becca/becca');
const RESOURCE_DIR = require('../../services/resource_dir').RESOURCE_DIR;
@ -28,22 +27,12 @@ function returnImage(req, res) {
// render the svg in node.js using excalidraw and jsdom
const content = image.getContent();
try {
const data = JSON.parse(content)
const excalidrawData = {
type: "excalidraw",
version: 2,
source: "trilium",
elements: data.elements,
appState: data.appState,
files: data.files,
}
excalidrawToSvg(excalidrawData)
.then(svg => {
const svgHtml = svg.outerHTML;
res.set('Content-Type', "image/svg+xml");
res.set("Cache-Control", "no-cache, no-store, must-revalidate");
res.send(svgHtml);
});
const data = JSON.parse(content);
const svg = data.svg || '<svg />'
res.set('Content-Type', "image/svg+xml");
res.set("Cache-Control", "no-cache, no-store, must-revalidate");
res.send(svg);
} catch(err) {
res.status(500).send("there was an error parsing excalidraw to svg");
}

View file

@ -1,5 +1,3 @@
const excalidrawToSvg = require("excalidraw-to-svg");
const shaca = require("./shaca/shaca");
const shacaLoader = require("./shaca/shaca_loader");
const shareRoot = require("./share_root");
@ -124,21 +122,10 @@ function register(router) {
const content = image.getContent();
try {
const data = JSON.parse(content)
const excalidrawData = {
type: "excalidraw",
version: 2,
source: "trilium",
elements: data.elements,
appState: data.appState,
files: data.files,
}
excalidrawToSvg(excalidrawData)
.then(svg => {
const svgHtml = svg.outerHTML;
res.set('Content-Type', "image/svg+xml");
res.set("Cache-Control", "no-cache, no-store, must-revalidate");
res.send(svgHtml);
});
const svg = data.svg || '<svg />'
res.set('Content-Type', "image/svg+xml");
res.set("Cache-Control", "no-cache, no-store, must-revalidate");
res.send(svg);
} catch(err) {
res.status(500).send("there was an error parsing excalidraw to svg");
}