mirror of
				https://github.com/zadam/trilium.git
				synced 2025-10-31 02:16:05 +01:00 
			
		
		
		
	refactor(deps): use webpack import for canvas
This commit is contained in:
		| @@ -73,7 +73,6 @@ const copy = async () => { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     const nodeModulesFolder = [ |     const nodeModulesFolder = [ | ||||||
|         "node_modules/@excalidraw/excalidraw/dist/", |  | ||||||
|         "node_modules/katex/dist/", |         "node_modules/katex/dist/", | ||||||
|         "node_modules/dayjs/", |         "node_modules/dayjs/", | ||||||
|         "node_modules/boxicons/css/", |         "node_modules/boxicons/css/", | ||||||
|   | |||||||
| @@ -49,7 +49,6 @@ cp "$script_dir/../build/electron-main.js" "$DIR" | |||||||
| if [[ -d "$DIR"/node_modules ]]; then | if [[ -d "$DIR"/node_modules ]]; then | ||||||
|     # cleanup of useless files in dependencies |     # cleanup of useless files in dependencies | ||||||
|     for d in 'image-q/demo' \ |     for d in 'image-q/demo' \ | ||||||
|         '@excalidraw/excalidraw/dist/excalidraw-assets-dev' '@excalidraw/excalidraw/dist/excalidraw.development.js' '@excalidraw/excalidraw/dist/excalidraw-with-preact.development.js' \ |  | ||||||
|         'mermaid/dist/mermaid.js' \ |         'mermaid/dist/mermaid.js' \ | ||||||
|         'boxicons/svg' 'boxicons/node_modules/react'/* \ |         'boxicons/svg' 'boxicons/node_modules/react'/* \ | ||||||
|         '@jimp/plugin-print/fonts' 'jimp/browser' 'jimp/fonts'; do |         '@jimp/plugin-print/fonts' 'jimp/browser' 'jimp/fonts'; do | ||||||
|   | |||||||
							
								
								
									
										33
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										33
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							| @@ -16,6 +16,7 @@ | |||||||
|         "@mermaid-js/layout-elk": "0.1.7", |         "@mermaid-js/layout-elk": "0.1.7", | ||||||
|         "@mind-elixir/node-menu": "1.0.3", |         "@mind-elixir/node-menu": "1.0.3", | ||||||
|         "@triliumnext/express-partial-content": "1.0.1", |         "@triliumnext/express-partial-content": "1.0.1", | ||||||
|  |         "@types/react-dom": "18.3.1", | ||||||
|         "archiver": "7.0.1", |         "archiver": "7.0.1", | ||||||
|         "async-mutex": "0.5.0", |         "async-mutex": "0.5.0", | ||||||
|         "autocomplete.js": "0.38.1", |         "autocomplete.js": "0.38.1", | ||||||
| @@ -133,6 +134,7 @@ | |||||||
|         "@types/mime-types": "2.1.4", |         "@types/mime-types": "2.1.4", | ||||||
|         "@types/multer": "1.4.12", |         "@types/multer": "1.4.12", | ||||||
|         "@types/node": "22.10.7", |         "@types/node": "22.10.7", | ||||||
|  |         "@types/react": "18.3.1", | ||||||
|         "@types/safe-compare": "1.1.2", |         "@types/safe-compare": "1.1.2", | ||||||
|         "@types/sanitize-html": "2.13.0", |         "@types/sanitize-html": "2.13.0", | ||||||
|         "@types/sax": "1.2.7", |         "@types/sax": "1.2.7", | ||||||
| @@ -3912,6 +3914,12 @@ | |||||||
|         "undici-types": "~6.20.0" |         "undici-types": "~6.20.0" | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|  |     "node_modules/@types/prop-types": { | ||||||
|  |       "version": "15.7.14", | ||||||
|  |       "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.14.tgz", | ||||||
|  |       "integrity": "sha512-gNMvNH49DJ7OJYv+KAKn0Xp45p8PLl6zo2YnvDIbTd4J6MER2BmWN49TG7n9LvkyihINxeKW8+3bfS2yDC9dzQ==", | ||||||
|  |       "license": "MIT" | ||||||
|  |     }, | ||||||
|     "node_modules/@types/qs": { |     "node_modules/@types/qs": { | ||||||
|       "version": "6.9.17", |       "version": "6.9.17", | ||||||
|       "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.17.tgz", |       "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.17.tgz", | ||||||
| @@ -3926,6 +3934,25 @@ | |||||||
|       "dev": true, |       "dev": true, | ||||||
|       "license": "MIT" |       "license": "MIT" | ||||||
|     }, |     }, | ||||||
|  |     "node_modules/@types/react": { | ||||||
|  |       "version": "18.3.1", | ||||||
|  |       "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.1.tgz", | ||||||
|  |       "integrity": "sha512-V0kuGBX3+prX+DQ/7r2qsv1NsdfnCLnTgnRJ1pYnxykBhGMz+qj+box5lq7XsO5mtZsBqpjwwTu/7wszPfMBcw==", | ||||||
|  |       "license": "MIT", | ||||||
|  |       "dependencies": { | ||||||
|  |         "@types/prop-types": "*", | ||||||
|  |         "csstype": "^3.0.2" | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|  |     "node_modules/@types/react-dom": { | ||||||
|  |       "version": "18.3.1", | ||||||
|  |       "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.1.tgz", | ||||||
|  |       "integrity": "sha512-qW1Mfv8taImTthu4KoXgDfLuk4bydU6Q/TkADnDWWHwi4NX4BR+LWfTp2sVmTqRrsHvyDDTelgelxJ+SsejKKQ==", | ||||||
|  |       "license": "MIT", | ||||||
|  |       "dependencies": { | ||||||
|  |         "@types/react": "*" | ||||||
|  |       } | ||||||
|  |     }, | ||||||
|     "node_modules/@types/readdir-glob": { |     "node_modules/@types/readdir-glob": { | ||||||
|       "version": "1.1.5", |       "version": "1.1.5", | ||||||
|       "resolved": "https://registry.npmjs.org/@types/readdir-glob/-/readdir-glob-1.1.5.tgz", |       "resolved": "https://registry.npmjs.org/@types/readdir-glob/-/readdir-glob-1.1.5.tgz", | ||||||
| @@ -6652,6 +6679,12 @@ | |||||||
|         "node": ">=18" |         "node": ">=18" | ||||||
|       } |       } | ||||||
|     }, |     }, | ||||||
|  |     "node_modules/csstype": { | ||||||
|  |       "version": "3.1.3", | ||||||
|  |       "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", | ||||||
|  |       "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", | ||||||
|  |       "license": "MIT" | ||||||
|  |     }, | ||||||
|     "node_modules/cytoscape": { |     "node_modules/cytoscape": { | ||||||
|       "version": "3.30.4", |       "version": "3.30.4", | ||||||
|       "resolved": "https://registry.npmjs.org/cytoscape/-/cytoscape-3.30.4.tgz", |       "resolved": "https://registry.npmjs.org/cytoscape/-/cytoscape-3.30.4.tgz", | ||||||
|   | |||||||
| @@ -61,6 +61,7 @@ | |||||||
|     "@mermaid-js/layout-elk": "0.1.7", |     "@mermaid-js/layout-elk": "0.1.7", | ||||||
|     "@mind-elixir/node-menu": "1.0.3", |     "@mind-elixir/node-menu": "1.0.3", | ||||||
|     "@triliumnext/express-partial-content": "1.0.1", |     "@triliumnext/express-partial-content": "1.0.1", | ||||||
|  |     "@types/react-dom": "18.3.1", | ||||||
|     "archiver": "7.0.1", |     "archiver": "7.0.1", | ||||||
|     "async-mutex": "0.5.0", |     "async-mutex": "0.5.0", | ||||||
|     "autocomplete.js": "0.38.1", |     "autocomplete.js": "0.38.1", | ||||||
| @@ -175,6 +176,7 @@ | |||||||
|     "@types/mime-types": "2.1.4", |     "@types/mime-types": "2.1.4", | ||||||
|     "@types/multer": "1.4.12", |     "@types/multer": "1.4.12", | ||||||
|     "@types/node": "22.10.7", |     "@types/node": "22.10.7", | ||||||
|  |     "@types/react": "18.3.1", | ||||||
|     "@types/safe-compare": "1.1.2", |     "@types/safe-compare": "1.1.2", | ||||||
|     "@types/sanitize-html": "2.13.0", |     "@types/sanitize-html": "2.13.0", | ||||||
|     "@types/sax": "1.2.7", |     "@types/sax": "1.2.7", | ||||||
|   | |||||||
| @@ -72,10 +72,6 @@ const MERMAID: Library = { | |||||||
|     js: ["node_modules/mermaid/dist/mermaid.min.js"] |     js: ["node_modules/mermaid/dist/mermaid.min.js"] | ||||||
| }; | }; | ||||||
|  |  | ||||||
| const EXCALIDRAW: Library = { |  | ||||||
|     js: ["node_modules/react/umd/react.production.min.js", "node_modules/react-dom/umd/react-dom.production.min.js", "node_modules/@excalidraw/excalidraw/dist/excalidraw.production.min.js"] |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| const MARKJS: Library = { | const MARKJS: Library = { | ||||||
|     js: ["node_modules/mark.js/dist/jquery.mark.es6.min.js"] |     js: ["node_modules/mark.js/dist/jquery.mark.es6.min.js"] | ||||||
| }; | }; | ||||||
| @@ -198,7 +194,6 @@ export default { | |||||||
|     KATEX, |     KATEX, | ||||||
|     WHEEL_ZOOM, |     WHEEL_ZOOM, | ||||||
|     MERMAID, |     MERMAID, | ||||||
|     EXCALIDRAW, |  | ||||||
|     MARKJS, |     MARKJS, | ||||||
|     I18NEXT, |     I18NEXT, | ||||||
|     HIGHLIGHT_JS |     HIGHLIGHT_JS | ||||||
|   | |||||||
| @@ -234,7 +234,7 @@ function goToLink(evt: MouseEvent | JQuery.ClickEvent) { | |||||||
|     return goToLinkExt(evt, hrefLink, $link); |     return goToLinkExt(evt, hrefLink, $link); | ||||||
| } | } | ||||||
|  |  | ||||||
| function goToLinkExt(evt: MouseEvent | JQuery.ClickEvent, hrefLink: string | undefined, $link: JQuery<HTMLElement> | null) { | function goToLinkExt(evt: MouseEvent | JQuery.ClickEvent | React.PointerEvent<HTMLCanvasElement>, hrefLink: string | undefined, $link: JQuery<HTMLElement> | null) { | ||||||
|     if (hrefLink?.startsWith("data:")) { |     if (hrefLink?.startsWith("data:")) { | ||||||
|         return true; |         return true; | ||||||
|     } |     } | ||||||
| @@ -249,13 +249,10 @@ function goToLinkExt(evt: MouseEvent | JQuery.ClickEvent, hrefLink: string | und | |||||||
|     const { notePath, viewScope } = parseNavigationStateFromUrl(hrefLink); |     const { notePath, viewScope } = parseNavigationStateFromUrl(hrefLink); | ||||||
|  |  | ||||||
|     const ctrlKey = utils.isCtrlKey(evt); |     const ctrlKey = utils.isCtrlKey(evt); | ||||||
|     const isLeftClick = evt.which === 1; |     const isLeftClick = ("which" in evt && evt.which === 1); | ||||||
|     const isMiddleClick = evt.which === 2; |     const isMiddleClick = ("which" in evt && evt.which === 2); | ||||||
|     const openInNewTab = (isLeftClick && ctrlKey) || isMiddleClick; |     const openInNewTab = (isLeftClick && ctrlKey) || isMiddleClick; | ||||||
|  |  | ||||||
|     const leftClick = evt.which === 1; |  | ||||||
|     const middleClick = evt.which === 2; |  | ||||||
|  |  | ||||||
|     if (notePath) { |     if (notePath) { | ||||||
|         if (openInNewTab) { |         if (openInNewTab) { | ||||||
|             appContext.tabManager.openTabWithNoteWithHoisting(notePath, { viewScope }); |             appContext.tabManager.openTabWithNoteWithHoisting(notePath, { viewScope }); | ||||||
| @@ -276,7 +273,7 @@ function goToLinkExt(evt: MouseEvent | JQuery.ClickEvent, hrefLink: string | und | |||||||
|         const withinEditLink = $link?.hasClass("ck-link-actions__preview"); |         const withinEditLink = $link?.hasClass("ck-link-actions__preview"); | ||||||
|         const outsideOfCKEditor = !$link || $link.closest("[contenteditable]").length === 0; |         const outsideOfCKEditor = !$link || $link.closest("[contenteditable]").length === 0; | ||||||
|  |  | ||||||
|         if (openInNewTab || (withinEditLink && (leftClick || middleClick)) || (outsideOfCKEditor && (leftClick || middleClick))) { |         if (openInNewTab || (withinEditLink && (isLeftClick || isMiddleClick)) || (outsideOfCKEditor && (isLeftClick || isMiddleClick))) { | ||||||
|             if (hrefLink.toLowerCase().startsWith("http") || hrefLink.startsWith("api/")) { |             if (hrefLink.toLowerCase().startsWith("http") || hrefLink.startsWith("api/")) { | ||||||
|                 window.open(hrefLink, "_blank"); |                 window.open(hrefLink, "_blank"); | ||||||
|             } else if ((hrefLink.toLowerCase().startsWith("file:") || hrefLink.toLowerCase().startsWith("geo:")) && utils.isElectron()) { |             } else if ((hrefLink.toLowerCase().startsWith("file:") || hrefLink.toLowerCase().startsWith("geo:")) && utils.isElectron()) { | ||||||
|   | |||||||
| @@ -97,7 +97,7 @@ function isMac() { | |||||||
|     return navigator.platform.indexOf("Mac") > -1; |     return navigator.platform.indexOf("Mac") > -1; | ||||||
| } | } | ||||||
|  |  | ||||||
| function isCtrlKey(evt: KeyboardEvent | MouseEvent | JQuery.ClickEvent | JQuery.ContextMenuEvent | JQuery.TriggeredEvent) { | function isCtrlKey(evt: KeyboardEvent | MouseEvent | JQuery.ClickEvent | JQuery.ContextMenuEvent | JQuery.TriggeredEvent | React.PointerEvent<HTMLCanvasElement>) { | ||||||
|     return (!isMac() && evt.ctrlKey) || (isMac() && evt.metaKey); |     return (!isMac() && evt.ctrlKey) || (isMac() && evt.metaKey); | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										66
									
								
								src/public/app/types.d.ts
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										66
									
								
								src/public/app/types.d.ts
									
									
									
									
										vendored
									
									
								
							| @@ -54,72 +54,6 @@ declare global { | |||||||
|  |  | ||||||
|         process?: ElectronProcess; |         process?: ElectronProcess; | ||||||
|         glob?: CustomGlobals; |         glob?: CustomGlobals; | ||||||
|         React: { |  | ||||||
|             createElement(any, any?, any?); |  | ||||||
|             Fragment: any; |  | ||||||
|             useState({ |  | ||||||
|                 width: undefined, |  | ||||||
|                 height: undefined |  | ||||||
|             }); |  | ||||||
|             useRef(ref: null); |  | ||||||
|             useEffect(cb: () => void, args: unknown[]); |  | ||||||
|             useCallback(cb: (el, ev) => void, args: unknown[]); |  | ||||||
|         }; |  | ||||||
|         ReactDOM: { |  | ||||||
|             unmountComponentAtNode(el: HTMLElement); |  | ||||||
|             createRoot(el: HTMLElement); |  | ||||||
|         } |  | ||||||
|         ExcalidrawLib: { |  | ||||||
|             getSceneVersion(el: unknown[]): number; |  | ||||||
|             exportToSvg(opts: { |  | ||||||
|                 elements: ExcalidrawElement[], |  | ||||||
|                 appState: ExcalidrawAppState, |  | ||||||
|                 exportPadding: number, |  | ||||||
|                 metadata: string, |  | ||||||
|                 files: ExcalidrawElement[] |  | ||||||
|             }): Promise<HTMLElement>; |  | ||||||
|             updateScene, |  | ||||||
|             Excalidraw: unknown |  | ||||||
|         } |  | ||||||
|         EXCALIDRAW_ASSET_PATH: string; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     interface ExcalidrawApi { |  | ||||||
|         getSceneElements(): ExcalidrawElement[]; |  | ||||||
|         getAppState(): ExcalidrawAppState; |  | ||||||
|         getFiles(): ExcalidrawElement[]; |  | ||||||
|         updateScene(scene: ExcalidrawScene); |  | ||||||
|         updateLibrary(opts: { libraryItems?: ExcalidrawLibrary[], merge: boolean }): Promise<ExcalidrawLibrary[]>; |  | ||||||
|         addFiles(files: ExcalidrawElement[]); |  | ||||||
|         history: { |  | ||||||
|             clear(); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     interface ExcalidrawElement { |  | ||||||
|         fileId: number; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     interface ExcalidrawLibrary { |  | ||||||
|         id: string; |  | ||||||
|         name: string; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     interface ExcalidrawScene { |  | ||||||
|         elements: unknown[]; |  | ||||||
|         appState: ExcalidrawAppState; |  | ||||||
|         collaborators: unknown[]; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     interface ExcalidrawAppState { |  | ||||||
|         scrollX?: number; |  | ||||||
|         scrollY?: number; |  | ||||||
|         zoom?: number; |  | ||||||
|         theme?: string; |  | ||||||
|         width?: number; |  | ||||||
|         height?: number; |  | ||||||
|         offsetLeft?: number; |  | ||||||
|         offsetTop?: number; |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     interface AutoCompleteConfig { |     interface AutoCompleteConfig { | ||||||
|   | |||||||
| @@ -1,9 +1,14 @@ | |||||||
| import libraryLoader from "../../services/library_loader.js"; |  | ||||||
| import TypeWidget from "./type_widget.js"; | import TypeWidget from "./type_widget.js"; | ||||||
| import utils from "../../services/utils.js"; | import utils from "../../services/utils.js"; | ||||||
| import linkService from "../../services/link.js"; | import linkService from "../../services/link.js"; | ||||||
| import server from "../../services/server.js"; | import server from "../../services/server.js"; | ||||||
| import type FNote from "../../entities/fnote.js"; | import type FNote from "../../entities/fnote.js"; | ||||||
|  | import type { default as ExcalidrawLib } from "@excalidraw/excalidraw"; | ||||||
|  | import type { ExcalidrawElement, Theme } from "@excalidraw/excalidraw/types/element/types.js"; | ||||||
|  | import type { AppState, BinaryFileData, ExcalidrawImperativeAPI, ExcalidrawProps, LibraryItem, SceneData } from "@excalidraw/excalidraw/types/types.js"; | ||||||
|  | import type { JSX } from "react"; | ||||||
|  | import type React from "react"; | ||||||
|  |  | ||||||
| const TPL = ` | const TPL = ` | ||||||
|     <div class="canvas-widget note-detail-canvas note-detail-printable note-detail"> |     <div class="canvas-widget note-detail-canvas note-detail-printable note-detail"> | ||||||
|         <style> |         <style> | ||||||
| @@ -54,8 +59,8 @@ const TPL = ` | |||||||
|  |  | ||||||
| interface CanvasContent { | interface CanvasContent { | ||||||
|     elements: ExcalidrawElement[], |     elements: ExcalidrawElement[], | ||||||
|     files: ExcalidrawElement[], |     files: BinaryFileData[], | ||||||
|     appState: ExcalidrawAppState |     appState: Partial<AppState> | ||||||
| } | } | ||||||
|  |  | ||||||
| interface AttachmentMetadata { | interface AttachmentMetadata { | ||||||
| @@ -114,13 +119,12 @@ export default class ExcalidrawTypeWidget extends TypeWidget { | |||||||
|     private currentNoteId: string; |     private currentNoteId: string; | ||||||
|     private currentSceneVersion: number; |     private currentSceneVersion: number; | ||||||
|     private libraryChanged: boolean; |     private libraryChanged: boolean; | ||||||
|     private librarycache: ExcalidrawLibrary[]; |     private librarycache: LibraryItem[]; | ||||||
|     private attachmentMetadata: AttachmentMetadata[]; |     private attachmentMetadata: AttachmentMetadata[]; | ||||||
|     private themeStyle!: string; |     private themeStyle!: Theme; | ||||||
|     private excalidrawApi!: ExcalidrawApi; |     private excalidrawLib!: typeof ExcalidrawLib; | ||||||
|     private excalidrawWrapperRef!: { |     private excalidrawApi!: ExcalidrawImperativeAPI; | ||||||
|         current: HTMLElement |     private excalidrawWrapperRef!: React.RefObject<HTMLElement | null>; | ||||||
|     }; |  | ||||||
|  |  | ||||||
|     private $render!: JQuery<HTMLElement>; |     private $render!: JQuery<HTMLElement>; | ||||||
|     private reactHandlers!: JQuery<HTMLElement>; |     private reactHandlers!: JQuery<HTMLElement>; | ||||||
| @@ -133,7 +137,8 @@ export default class ExcalidrawTypeWidget extends TypeWidget { | |||||||
|         this.SCENE_VERSION_ERROR = -2; // -2 indicates error |         this.SCENE_VERSION_ERROR = -2; // -2 indicates error | ||||||
|  |  | ||||||
|         // ensure that assets are loaded from trilium |         // ensure that assets are loaded from trilium | ||||||
|         window.EXCALIDRAW_ASSET_PATH = `${window.location.origin}/node_modules/@excalidraw/excalidraw/dist/`; |         // TODO: | ||||||
|  |         (window as any).EXCALIDRAW_ASSET_PATH = `${window.location.origin}/node_modules/@excalidraw/excalidraw/dist/`; | ||||||
|  |  | ||||||
|         // temporary vars |         // temporary vars | ||||||
|         this.currentNoteId = ""; |         this.currentNoteId = ""; | ||||||
| @@ -169,22 +174,37 @@ export default class ExcalidrawTypeWidget extends TypeWidget { | |||||||
|         this.$widget.toggleClass("full-height", true); |         this.$widget.toggleClass("full-height", true); | ||||||
|         this.$render = this.$widget.find(".canvas-render"); |         this.$render = this.$widget.find(".canvas-render"); | ||||||
|         const documentStyle = window.getComputedStyle(document.documentElement); |         const documentStyle = window.getComputedStyle(document.documentElement); | ||||||
|         this.themeStyle = documentStyle.getPropertyValue("--theme-style")?.trim(); |         this.themeStyle = documentStyle.getPropertyValue("--theme-style")?.trim() as Theme; | ||||||
|  |  | ||||||
|         libraryLoader.requireLibrary(libraryLoader.EXCALIDRAW).then(() => { |         this.#init(); | ||||||
|             const React = window.React; |  | ||||||
|             const ReactDOM = window.ReactDOM; |         return this.$widget; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     async #init() { | ||||||
|         const renderElement = this.$render.get(0); |         const renderElement = this.$render.get(0); | ||||||
|         if (!renderElement) { |         if (!renderElement) { | ||||||
|             throw new Error("Unable to find element to render."); |             throw new Error("Unable to find element to render."); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|             ReactDOM.unmountComponentAtNode(renderElement); |         // See https://github.com/excalidraw/excalidraw/issues/7899. | ||||||
|             const root = ReactDOM.createRoot(renderElement); |         if (!window.process) { | ||||||
|             root.render(React.createElement(() => this.createExcalidrawReactApp())); |             (window.process as any) = {}; | ||||||
|         }); |         } | ||||||
|  |         if (!window.process.env) { | ||||||
|  |             window.process.env = {}; | ||||||
|  |         } | ||||||
|  |         (window.process.env as any).PREACT = false; | ||||||
|  |  | ||||||
|         return this.$widget; |         const excalidraw = (await import("@excalidraw/excalidraw")); | ||||||
|  |         this.excalidrawLib = excalidraw; | ||||||
|  |  | ||||||
|  |         const { unmountComponentAtNode } = await import("react-dom"); | ||||||
|  |         const { createRoot } = await import("react-dom/client"); | ||||||
|  |         const React = (await import("react")).default; | ||||||
|  |         unmountComponentAtNode(renderElement); | ||||||
|  |         const root = createRoot(renderElement); | ||||||
|  |         root.render(React.createElement(() => this.createExcalidrawReactApp(React, excalidraw.Excalidraw))); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
| @@ -215,15 +235,15 @@ export default class ExcalidrawTypeWidget extends TypeWidget { | |||||||
|          * newly instantiated? |          * newly instantiated? | ||||||
|          */ |          */ | ||||||
|         if (!blob?.content?.trim()) { |         if (!blob?.content?.trim()) { | ||||||
|             const sceneData = { |             const sceneData: SceneData = { | ||||||
|                 elements: [], |                 elements: [], | ||||||
|                 appState: { |                 appState: { | ||||||
|                     theme: this.themeStyle |                     theme: this.themeStyle | ||||||
|                 }, |                 } | ||||||
|                 collaborators: [] |  | ||||||
|             }; |             }; | ||||||
|  |  | ||||||
|             this.excalidrawApi.updateScene(sceneData); |             // TODO: Props mismatch. | ||||||
|  |             this.excalidrawApi.updateScene(sceneData as any); | ||||||
|         } else if (blob.content) { |         } else if (blob.content) { | ||||||
|             let content: CanvasContent; |             let content: CanvasContent; | ||||||
|  |  | ||||||
| @@ -240,26 +260,28 @@ export default class ExcalidrawTypeWidget extends TypeWidget { | |||||||
|                 }; |                 }; | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             const { elements, files, appState = {} } = content; |             const { elements, files } = content; | ||||||
|  |             const appState: Partial<AppState> = content.appState ?? {}; | ||||||
|  |  | ||||||
|             appState.theme = this.themeStyle; |             appState.theme = this.themeStyle; | ||||||
|  |  | ||||||
|  |             if (this.excalidrawWrapperRef.current) { | ||||||
|                 const boundingClientRect = this.excalidrawWrapperRef.current.getBoundingClientRect(); |                 const boundingClientRect = this.excalidrawWrapperRef.current.getBoundingClientRect(); | ||||||
|                 appState.width = boundingClientRect.width; |                 appState.width = boundingClientRect.width; | ||||||
|                 appState.height = boundingClientRect.height; |                 appState.height = boundingClientRect.height; | ||||||
|                 appState.offsetLeft = boundingClientRect.left; |                 appState.offsetLeft = boundingClientRect.left; | ||||||
|                 appState.offsetTop = boundingClientRect.top; |                 appState.offsetTop = boundingClientRect.top; | ||||||
|  |             } | ||||||
|  |  | ||||||
|             const sceneData: ExcalidrawScene = { |             const sceneData: SceneData = { | ||||||
|                 elements, |                 elements, | ||||||
|                 appState, |                 appState | ||||||
|                 collaborators: [] |  | ||||||
|             }; |             }; | ||||||
|  |  | ||||||
|             // files are expected in an array when loading. they are stored as a key-index object |             // files are expected in an array when loading. they are stored as a key-index object | ||||||
|             // see example for loading here: |             // see example for loading here: | ||||||
|             // https://github.com/excalidraw/excalidraw/blob/c5a7723185f6ca05e0ceb0b0d45c4e3fbcb81b2a/src/packages/excalidraw/example/App.js#L68 |             // https://github.com/excalidraw/excalidraw/blob/c5a7723185f6ca05e0ceb0b0d45c4e3fbcb81b2a/src/packages/excalidraw/example/App.js#L68 | ||||||
|             const fileArray = []; |             const fileArray: BinaryFileData[] = []; | ||||||
|             for (const fileId in files) { |             for (const fileId in files) { | ||||||
|                 const file = files[fileId]; |                 const file = files[fileId]; | ||||||
|                 // TODO: dataURL is replaceable with a trilium image url |                 // TODO: dataURL is replaceable with a trilium image url | ||||||
| @@ -288,7 +310,7 @@ export default class ExcalidrawTypeWidget extends TypeWidget { | |||||||
|                 } |                 } | ||||||
|  |  | ||||||
|                 // Extract libraryItems from the blobs |                 // Extract libraryItems from the blobs | ||||||
|                 const libraryItems = results.map((result) => result?.blob?.getJsonContentSafely()).filter((item) => !!item) as ExcalidrawLibrary[]; |                 const libraryItems = results.map((result) => result?.blob?.getJsonContentSafely()).filter((item) => !!item) as LibraryItem[]; | ||||||
|  |  | ||||||
|                 // Extract metadata for each attachment |                 // Extract metadata for each attachment | ||||||
|                 const metadata = results.map((result) => result.metadata); |                 const metadata = results.map((result) => result.metadata); | ||||||
| @@ -302,7 +324,8 @@ export default class ExcalidrawTypeWidget extends TypeWidget { | |||||||
|             }); |             }); | ||||||
|  |  | ||||||
|             // Update the scene |             // Update the scene | ||||||
|             this.excalidrawApi.updateScene(sceneData); |             // TODO: Fix type of sceneData | ||||||
|  |             this.excalidrawApi.updateScene(sceneData as any); | ||||||
|             this.excalidrawApi.addFiles(fileArray); |             this.excalidrawApi.addFiles(fileArray); | ||||||
|             this.excalidrawApi.history.clear(); |             this.excalidrawApi.history.clear(); | ||||||
|         } |         } | ||||||
| @@ -328,18 +351,17 @@ export default class ExcalidrawTypeWidget extends TypeWidget { | |||||||
|         const files = this.excalidrawApi.getFiles(); |         const files = this.excalidrawApi.getFiles(); | ||||||
|  |  | ||||||
|         // parallel svg export to combat bitrot and enable rendering image for note inclusion, preview, and share |         // parallel svg export to combat bitrot and enable rendering image for note inclusion, preview, and share | ||||||
|         const svg = await window.ExcalidrawLib.exportToSvg({ |         const svg = await this.excalidrawLib.exportToSvg({ | ||||||
|             elements, |             elements, | ||||||
|             appState, |             appState, | ||||||
|             exportPadding: 5, // 5 px padding |             exportPadding: 5, // 5 px padding | ||||||
|             metadata: "trilium-export", |  | ||||||
|             files |             files | ||||||
|         }); |         }); | ||||||
|         const svgString = svg.outerHTML; |         const svgString = svg.outerHTML; | ||||||
|  |  | ||||||
|         const activeFiles: Record<string, ExcalidrawElement> = {}; |         const activeFiles: Record<string, BinaryFileData> = {}; | ||||||
|         elements.forEach((element) => { |         elements.forEach((element) => { | ||||||
|             if (element.fileId) { |             if ("fileId" in element && element.fileId) { | ||||||
|                 activeFiles[element.fileId] = files[element.fileId]; |                 activeFiles[element.fileId] = files[element.fileId]; | ||||||
|             } |             } | ||||||
|         }); |         }); | ||||||
| @@ -362,7 +384,12 @@ export default class ExcalidrawTypeWidget extends TypeWidget { | |||||||
|             // this.libraryChanged is unset in dataSaved() |             // this.libraryChanged is unset in dataSaved() | ||||||
|  |  | ||||||
|             // there's no separate method to get library items, so have to abuse this one |             // there's no separate method to get library items, so have to abuse this one | ||||||
|             const libraryItems = await this.excalidrawApi.updateLibrary({ merge: true }); |             const libraryItems = await this.excalidrawApi.updateLibrary({ | ||||||
|  |                 libraryItems(currentLibraryItems) { | ||||||
|  |                     return []; | ||||||
|  |                 }, | ||||||
|  |                 merge: true | ||||||
|  |             }); | ||||||
|  |  | ||||||
|             // excalidraw saves the library as a own state. the items are saved to libraryItems. then we compare the library right now with a libraryitemcache. The cache is filled when we first load the Library into the note. |             // excalidraw saves the library as a own state. the items are saved to libraryItems. then we compare the library right now with a libraryitemcache. The cache is filled when we first load the Library into the note. | ||||||
|             //We need the cache to delete old attachments later in the server. |             //We need the cache to delete old attachments later in the server. | ||||||
| @@ -445,33 +472,35 @@ export default class ExcalidrawTypeWidget extends TypeWidget { | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     createExcalidrawReactApp() { |     createExcalidrawReactApp(react: typeof React, excalidrawComponent: React.MemoExoticComponent<(props: ExcalidrawProps) => JSX.Element>) { | ||||||
|         const React = window.React; |         const excalidrawWrapperRef = react.useRef<HTMLElement>(null); | ||||||
|         const { Excalidraw } = window.ExcalidrawLib; |  | ||||||
|         const excalidrawWrapperRef = React.useRef(null); |  | ||||||
|         this.excalidrawWrapperRef = excalidrawWrapperRef; |         this.excalidrawWrapperRef = excalidrawWrapperRef; | ||||||
|         const [dimensions, setDimensions] = React.useState({ |         const [dimensions, setDimensions] = react.useState<{ width?: number, height?: number}>({ | ||||||
|             width: undefined, |             width: undefined, | ||||||
|             height: undefined |             height: undefined | ||||||
|         }); |         }); | ||||||
|  |  | ||||||
|         React.useEffect(() => { |         react.useEffect(() => { | ||||||
|  |             if (excalidrawWrapperRef.current) { | ||||||
|                 const dimensions = { |                 const dimensions = { | ||||||
|                     width: excalidrawWrapperRef.current.getBoundingClientRect().width, |                     width: excalidrawWrapperRef.current.getBoundingClientRect().width, | ||||||
|                     height: excalidrawWrapperRef.current.getBoundingClientRect().height |                     height: excalidrawWrapperRef.current.getBoundingClientRect().height | ||||||
|                 }; |                 }; | ||||||
|                 setDimensions(dimensions); |                 setDimensions(dimensions); | ||||||
|  |             } | ||||||
|  |  | ||||||
|             const onResize = () => { |             const onResize = () => { | ||||||
|                 if (this.note?.type !== "canvas") { |                 if (this.note?.type !== "canvas") { | ||||||
|                     return; |                     return; | ||||||
|                 } |                 } | ||||||
|  |  | ||||||
|  |                 if (excalidrawWrapperRef.current) { | ||||||
|                     const dimensions = { |                     const dimensions = { | ||||||
|                         width: excalidrawWrapperRef.current.getBoundingClientRect().width, |                         width: excalidrawWrapperRef.current.getBoundingClientRect().width, | ||||||
|                         height: excalidrawWrapperRef.current.getBoundingClientRect().height |                         height: excalidrawWrapperRef.current.getBoundingClientRect().height | ||||||
|                     }; |                     }; | ||||||
|                     setDimensions(dimensions); |                     setDimensions(dimensions); | ||||||
|  |                 } | ||||||
|             }; |             }; | ||||||
|  |  | ||||||
|             window.addEventListener("resize", onResize); |             window.addEventListener("resize", onResize); | ||||||
| @@ -479,8 +508,11 @@ export default class ExcalidrawTypeWidget extends TypeWidget { | |||||||
|             return () => window.removeEventListener("resize", onResize); |             return () => window.removeEventListener("resize", onResize); | ||||||
|         }, [excalidrawWrapperRef]); |         }, [excalidrawWrapperRef]); | ||||||
|  |  | ||||||
|         const onLinkOpen = React.useCallback((element, event) => { |         const onLinkOpen = react.useCallback<NonNullable<ExcalidrawProps["onLinkOpen"]>>((element, event) => { | ||||||
|             let link = element.link; |             let link = element.link; | ||||||
|  |             if (!link) { | ||||||
|  |                 return false; | ||||||
|  |             } | ||||||
|  |  | ||||||
|             if (link.startsWith("root/")) { |             if (link.startsWith("root/")) { | ||||||
|                 link = "#" + link; |                 link = "#" + link; | ||||||
| @@ -493,25 +525,24 @@ export default class ExcalidrawTypeWidget extends TypeWidget { | |||||||
|             return linkService.goToLinkExt(nativeEvent, link, null); |             return linkService.goToLinkExt(nativeEvent, link, null); | ||||||
|         }, []); |         }, []); | ||||||
|  |  | ||||||
|         return React.createElement( |         return react.createElement( | ||||||
|             React.Fragment, |             react.Fragment, | ||||||
|             null, |             null, | ||||||
|             React.createElement( |             react.createElement( | ||||||
|                 "div", |                 "div", | ||||||
|                 { |                 { | ||||||
|                     className: "excalidraw-wrapper", |                     className: "excalidraw-wrapper", | ||||||
|                     ref: excalidrawWrapperRef |                     ref: excalidrawWrapperRef | ||||||
|                 }, |                 }, | ||||||
|                 React.createElement(Excalidraw, { |                 react.createElement(excalidrawComponent, { | ||||||
|                     // this makes sure that 1) manual theme switch button is hidden 2) theme stays as it should after opening menu |                     // this makes sure that 1) manual theme switch button is hidden 2) theme stays as it should after opening menu | ||||||
|                     theme: this.themeStyle, |                     theme: this.themeStyle, | ||||||
|                     excalidrawAPI: (api: ExcalidrawApi) => { |                     excalidrawAPI: (api: ExcalidrawImperativeAPI) => { | ||||||
|                         this.excalidrawApi = api; |                         this.excalidrawApi = api; | ||||||
|                     }, |                     }, | ||||||
|                     width: dimensions.width, |  | ||||||
|                     height: dimensions.height, |  | ||||||
|                     onPaste: (data: unknown, event: unknown) => { |                     onPaste: (data: unknown, event: unknown) => { | ||||||
|                         console.log("Verbose: excalidraw internal paste. No trilium action implemented.", data, event); |                         console.log("Verbose: excalidraw internal paste. No trilium action implemented.", data, event); | ||||||
|  |                         return false; | ||||||
|                     }, |                     }, | ||||||
|                     onLibraryChange: () => { |                     onLibraryChange: () => { | ||||||
|                         this.libraryChanged = true; |                         this.libraryChanged = true; | ||||||
| @@ -528,9 +559,11 @@ export default class ExcalidrawTypeWidget extends TypeWidget { | |||||||
|                     autoFocus: false, |                     autoFocus: false, | ||||||
|                     onLinkOpen, |                     onLinkOpen, | ||||||
|                     UIOptions: { |                     UIOptions: { | ||||||
|  |                         canvasActions: { | ||||||
|                             saveToActiveFile: false, |                             saveToActiveFile: false, | ||||||
|                             saveAsImage: false |                             saveAsImage: false | ||||||
|                         } |                         } | ||||||
|  |                     } | ||||||
|                 }) |                 }) | ||||||
|             ) |             ) | ||||||
|         ); |         ); | ||||||
| @@ -555,7 +588,7 @@ export default class ExcalidrawTypeWidget extends TypeWidget { | |||||||
|     getSceneVersion() { |     getSceneVersion() { | ||||||
|         if (this.excalidrawApi) { |         if (this.excalidrawApi) { | ||||||
|             const elements = this.excalidrawApi.getSceneElements(); |             const elements = this.excalidrawApi.getSceneElements(); | ||||||
|             return window.ExcalidrawLib.getSceneVersion(elements); |             return this.excalidrawLib.getSceneVersion(elements); | ||||||
|         } else { |         } else { | ||||||
|             return this.SCENE_VERSION_ERROR; |             return this.SCENE_VERSION_ERROR; | ||||||
|         } |         } | ||||||
|   | |||||||
| @@ -44,13 +44,6 @@ async function register(app: express.Application) { | |||||||
|     app.use(`/assets/vX/stylesheets`, express.static(path.join(srcRoot, "public/stylesheets"))); |     app.use(`/assets/vX/stylesheets`, express.static(path.join(srcRoot, "public/stylesheets"))); | ||||||
|     app.use(`/${assetPath}/libraries`, persistentCacheStatic(path.join(srcRoot, "..", "libraries"))); |     app.use(`/${assetPath}/libraries`, persistentCacheStatic(path.join(srcRoot, "..", "libraries"))); | ||||||
|     app.use(`/assets/vX/libraries`, express.static(path.join(srcRoot, "..", "libraries"))); |     app.use(`/assets/vX/libraries`, express.static(path.join(srcRoot, "..", "libraries"))); | ||||||
|  |  | ||||||
|     // excalidraw-view mode in shared notes |  | ||||||
|     app.use(`/${assetPath}/node_modules/react/umd/react.production.min.js`, persistentCacheStatic(path.join(srcRoot, "..", "node_modules/react/umd/react.production.min.js"))); |  | ||||||
|     app.use(`/${assetPath}/node_modules/react/umd/react.development.js`, persistentCacheStatic(path.join(srcRoot, "..", "node_modules/react/umd/react.development.js"))); |  | ||||||
|     app.use(`/${assetPath}/node_modules/react-dom/umd/react-dom.production.min.js`, persistentCacheStatic(path.join(srcRoot, "..", "node_modules/react-dom/umd/react-dom.production.min.js"))); |  | ||||||
|     app.use(`/${assetPath}/node_modules/react-dom/umd/react-dom.development.js`, persistentCacheStatic(path.join(srcRoot, "..", "node_modules/react-dom/umd/react-dom.development.js"))); |  | ||||||
|     // expose the whole dist folder since complete assets are needed in edit and share |  | ||||||
|     app.use(`/node_modules/@excalidraw/excalidraw/dist/`, express.static(path.join(srcRoot, "..", "node_modules/@excalidraw/excalidraw/dist/"))); |     app.use(`/node_modules/@excalidraw/excalidraw/dist/`, express.static(path.join(srcRoot, "..", "node_modules/@excalidraw/excalidraw/dist/"))); | ||||||
|     app.use(`/${assetPath}/node_modules/@excalidraw/excalidraw/dist/`, persistentCacheStatic(path.join(srcRoot, "..", "node_modules/@excalidraw/excalidraw/dist/"))); |     app.use(`/${assetPath}/node_modules/@excalidraw/excalidraw/dist/`, persistentCacheStatic(path.join(srcRoot, "..", "node_modules/@excalidraw/excalidraw/dist/"))); | ||||||
|  |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user