mirror of
				https://github.com/TriliumNext/Notes.git
				synced 2025-11-04 15:11:31 +08:00 
			
		
		
		
	refactor(deps): use webpack import for canvas
This commit is contained in:
		
							parent
							
								
									ab65913e52
								
							
						
					
					
						commit
						7d3f506efb
					
				@ -73,7 +73,6 @@ const copy = async () => {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const nodeModulesFolder = [
 | 
			
		||||
        "node_modules/@excalidraw/excalidraw/dist/",
 | 
			
		||||
        "node_modules/katex/dist/",
 | 
			
		||||
        "node_modules/dayjs/",
 | 
			
		||||
        "node_modules/boxicons/css/",
 | 
			
		||||
 | 
			
		||||
@ -49,7 +49,6 @@ cp "$script_dir/../build/electron-main.js" "$DIR"
 | 
			
		||||
if [[ -d "$DIR"/node_modules ]]; then
 | 
			
		||||
    # cleanup of useless files in dependencies
 | 
			
		||||
    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' \
 | 
			
		||||
        'boxicons/svg' 'boxicons/node_modules/react'/* \
 | 
			
		||||
        '@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",
 | 
			
		||||
        "@mind-elixir/node-menu": "1.0.3",
 | 
			
		||||
        "@triliumnext/express-partial-content": "1.0.1",
 | 
			
		||||
        "@types/react-dom": "18.3.1",
 | 
			
		||||
        "archiver": "7.0.1",
 | 
			
		||||
        "async-mutex": "0.5.0",
 | 
			
		||||
        "autocomplete.js": "0.38.1",
 | 
			
		||||
@ -133,6 +134,7 @@
 | 
			
		||||
        "@types/mime-types": "2.1.4",
 | 
			
		||||
        "@types/multer": "1.4.12",
 | 
			
		||||
        "@types/node": "22.10.7",
 | 
			
		||||
        "@types/react": "18.3.1",
 | 
			
		||||
        "@types/safe-compare": "1.1.2",
 | 
			
		||||
        "@types/sanitize-html": "2.13.0",
 | 
			
		||||
        "@types/sax": "1.2.7",
 | 
			
		||||
@ -3912,6 +3914,12 @@
 | 
			
		||||
        "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": {
 | 
			
		||||
      "version": "6.9.17",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.17.tgz",
 | 
			
		||||
@ -3926,6 +3934,25 @@
 | 
			
		||||
      "dev": true,
 | 
			
		||||
      "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": {
 | 
			
		||||
      "version": "1.1.5",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/@types/readdir-glob/-/readdir-glob-1.1.5.tgz",
 | 
			
		||||
@ -6652,6 +6679,12 @@
 | 
			
		||||
        "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": {
 | 
			
		||||
      "version": "3.30.4",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/cytoscape/-/cytoscape-3.30.4.tgz",
 | 
			
		||||
 | 
			
		||||
@ -61,6 +61,7 @@
 | 
			
		||||
    "@mermaid-js/layout-elk": "0.1.7",
 | 
			
		||||
    "@mind-elixir/node-menu": "1.0.3",
 | 
			
		||||
    "@triliumnext/express-partial-content": "1.0.1",
 | 
			
		||||
    "@types/react-dom": "18.3.1",
 | 
			
		||||
    "archiver": "7.0.1",
 | 
			
		||||
    "async-mutex": "0.5.0",
 | 
			
		||||
    "autocomplete.js": "0.38.1",
 | 
			
		||||
@ -175,6 +176,7 @@
 | 
			
		||||
    "@types/mime-types": "2.1.4",
 | 
			
		||||
    "@types/multer": "1.4.12",
 | 
			
		||||
    "@types/node": "22.10.7",
 | 
			
		||||
    "@types/react": "18.3.1",
 | 
			
		||||
    "@types/safe-compare": "1.1.2",
 | 
			
		||||
    "@types/sanitize-html": "2.13.0",
 | 
			
		||||
    "@types/sax": "1.2.7",
 | 
			
		||||
 | 
			
		||||
@ -72,10 +72,6 @@ const MERMAID: Library = {
 | 
			
		||||
    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 = {
 | 
			
		||||
    js: ["node_modules/mark.js/dist/jquery.mark.es6.min.js"]
 | 
			
		||||
};
 | 
			
		||||
@ -198,7 +194,6 @@ export default {
 | 
			
		||||
    KATEX,
 | 
			
		||||
    WHEEL_ZOOM,
 | 
			
		||||
    MERMAID,
 | 
			
		||||
    EXCALIDRAW,
 | 
			
		||||
    MARKJS,
 | 
			
		||||
    I18NEXT,
 | 
			
		||||
    HIGHLIGHT_JS
 | 
			
		||||
 | 
			
		||||
@ -234,7 +234,7 @@ function goToLink(evt: MouseEvent | JQuery.ClickEvent) {
 | 
			
		||||
    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:")) {
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
@ -249,13 +249,10 @@ function goToLinkExt(evt: MouseEvent | JQuery.ClickEvent, hrefLink: string | und
 | 
			
		||||
    const { notePath, viewScope } = parseNavigationStateFromUrl(hrefLink);
 | 
			
		||||
 | 
			
		||||
    const ctrlKey = utils.isCtrlKey(evt);
 | 
			
		||||
    const isLeftClick = evt.which === 1;
 | 
			
		||||
    const isMiddleClick = evt.which === 2;
 | 
			
		||||
    const isLeftClick = ("which" in evt && evt.which === 1);
 | 
			
		||||
    const isMiddleClick = ("which" in evt && evt.which === 2);
 | 
			
		||||
    const openInNewTab = (isLeftClick && ctrlKey) || isMiddleClick;
 | 
			
		||||
 | 
			
		||||
    const leftClick = evt.which === 1;
 | 
			
		||||
    const middleClick = evt.which === 2;
 | 
			
		||||
 | 
			
		||||
    if (notePath) {
 | 
			
		||||
        if (openInNewTab) {
 | 
			
		||||
            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 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/")) {
 | 
			
		||||
                window.open(hrefLink, "_blank");
 | 
			
		||||
            } else if ((hrefLink.toLowerCase().startsWith("file:") || hrefLink.toLowerCase().startsWith("geo:")) && utils.isElectron()) {
 | 
			
		||||
 | 
			
		||||
@ -97,7 +97,7 @@ function isMac() {
 | 
			
		||||
    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);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										66
									
								
								src/public/app/types.d.ts
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										66
									
								
								src/public/app/types.d.ts
									
									
									
									
										vendored
									
									
								
							@ -54,72 +54,6 @@ declare global {
 | 
			
		||||
 | 
			
		||||
        process?: ElectronProcess;
 | 
			
		||||
        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 {
 | 
			
		||||
 | 
			
		||||
@ -1,9 +1,14 @@
 | 
			
		||||
import libraryLoader from "../../services/library_loader.js";
 | 
			
		||||
import TypeWidget from "./type_widget.js";
 | 
			
		||||
import utils from "../../services/utils.js";
 | 
			
		||||
import linkService from "../../services/link.js";
 | 
			
		||||
import server from "../../services/server.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 = `
 | 
			
		||||
    <div class="canvas-widget note-detail-canvas note-detail-printable note-detail">
 | 
			
		||||
        <style>
 | 
			
		||||
@ -54,8 +59,8 @@ const TPL = `
 | 
			
		||||
 | 
			
		||||
interface CanvasContent {
 | 
			
		||||
    elements: ExcalidrawElement[],
 | 
			
		||||
    files: ExcalidrawElement[],
 | 
			
		||||
    appState: ExcalidrawAppState
 | 
			
		||||
    files: BinaryFileData[],
 | 
			
		||||
    appState: Partial<AppState>
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
interface AttachmentMetadata {
 | 
			
		||||
@ -114,13 +119,12 @@ export default class ExcalidrawTypeWidget extends TypeWidget {
 | 
			
		||||
    private currentNoteId: string;
 | 
			
		||||
    private currentSceneVersion: number;
 | 
			
		||||
    private libraryChanged: boolean;
 | 
			
		||||
    private librarycache: ExcalidrawLibrary[];
 | 
			
		||||
    private librarycache: LibraryItem[];
 | 
			
		||||
    private attachmentMetadata: AttachmentMetadata[];
 | 
			
		||||
    private themeStyle!: string;
 | 
			
		||||
    private excalidrawApi!: ExcalidrawApi;
 | 
			
		||||
    private excalidrawWrapperRef!: {
 | 
			
		||||
        current: HTMLElement
 | 
			
		||||
    };
 | 
			
		||||
    private themeStyle!: Theme;
 | 
			
		||||
    private excalidrawLib!: typeof ExcalidrawLib;
 | 
			
		||||
    private excalidrawApi!: ExcalidrawImperativeAPI;
 | 
			
		||||
    private excalidrawWrapperRef!: React.RefObject<HTMLElement | null>;
 | 
			
		||||
 | 
			
		||||
    private $render!: JQuery<HTMLElement>;
 | 
			
		||||
    private reactHandlers!: JQuery<HTMLElement>;
 | 
			
		||||
@ -133,7 +137,8 @@ export default class ExcalidrawTypeWidget extends TypeWidget {
 | 
			
		||||
        this.SCENE_VERSION_ERROR = -2; // -2 indicates error
 | 
			
		||||
 | 
			
		||||
        // 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
 | 
			
		||||
        this.currentNoteId = "";
 | 
			
		||||
@ -169,24 +174,39 @@ export default class ExcalidrawTypeWidget extends TypeWidget {
 | 
			
		||||
        this.$widget.toggleClass("full-height", true);
 | 
			
		||||
        this.$render = this.$widget.find(".canvas-render");
 | 
			
		||||
        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(() => {
 | 
			
		||||
            const React = window.React;
 | 
			
		||||
            const ReactDOM = window.ReactDOM;
 | 
			
		||||
            const renderElement = this.$render.get(0);
 | 
			
		||||
            if (!renderElement) {
 | 
			
		||||
                throw new Error("Unable to find element to render.");
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            ReactDOM.unmountComponentAtNode(renderElement);
 | 
			
		||||
            const root = ReactDOM.createRoot(renderElement);
 | 
			
		||||
            root.render(React.createElement(() => this.createExcalidrawReactApp()));
 | 
			
		||||
        });
 | 
			
		||||
        this.#init();
 | 
			
		||||
 | 
			
		||||
        return this.$widget;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async #init() {
 | 
			
		||||
        const renderElement = this.$render.get(0);
 | 
			
		||||
        if (!renderElement) {
 | 
			
		||||
            throw new Error("Unable to find element to render.");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // See https://github.com/excalidraw/excalidraw/issues/7899.
 | 
			
		||||
        if (!window.process) {
 | 
			
		||||
            (window.process as any) = {};
 | 
			
		||||
        }
 | 
			
		||||
        if (!window.process.env) {
 | 
			
		||||
            window.process.env = {};
 | 
			
		||||
        }
 | 
			
		||||
        (window.process.env as any).PREACT = false;
 | 
			
		||||
 | 
			
		||||
        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)));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * called to populate the widget container with the note content
 | 
			
		||||
     */
 | 
			
		||||
@ -215,15 +235,15 @@ export default class ExcalidrawTypeWidget extends TypeWidget {
 | 
			
		||||
         * newly instantiated?
 | 
			
		||||
         */
 | 
			
		||||
        if (!blob?.content?.trim()) {
 | 
			
		||||
            const sceneData = {
 | 
			
		||||
            const sceneData: SceneData = {
 | 
			
		||||
                elements: [],
 | 
			
		||||
                appState: {
 | 
			
		||||
                    theme: this.themeStyle
 | 
			
		||||
                },
 | 
			
		||||
                collaborators: []
 | 
			
		||||
                }
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            this.excalidrawApi.updateScene(sceneData);
 | 
			
		||||
            // TODO: Props mismatch.
 | 
			
		||||
            this.excalidrawApi.updateScene(sceneData as any);
 | 
			
		||||
        } else if (blob.content) {
 | 
			
		||||
            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;
 | 
			
		||||
 | 
			
		||||
            const boundingClientRect = this.excalidrawWrapperRef.current.getBoundingClientRect();
 | 
			
		||||
            appState.width = boundingClientRect.width;
 | 
			
		||||
            appState.height = boundingClientRect.height;
 | 
			
		||||
            appState.offsetLeft = boundingClientRect.left;
 | 
			
		||||
            appState.offsetTop = boundingClientRect.top;
 | 
			
		||||
            if (this.excalidrawWrapperRef.current) {
 | 
			
		||||
                const boundingClientRect = this.excalidrawWrapperRef.current.getBoundingClientRect();
 | 
			
		||||
                appState.width = boundingClientRect.width;
 | 
			
		||||
                appState.height = boundingClientRect.height;
 | 
			
		||||
                appState.offsetLeft = boundingClientRect.left;
 | 
			
		||||
                appState.offsetTop = boundingClientRect.top;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            const sceneData: ExcalidrawScene = {
 | 
			
		||||
            const sceneData: SceneData = {
 | 
			
		||||
                elements,
 | 
			
		||||
                appState,
 | 
			
		||||
                collaborators: []
 | 
			
		||||
                appState
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            // files are expected in an array when loading. they are stored as a key-index object
 | 
			
		||||
            // see example for loading here:
 | 
			
		||||
            // https://github.com/excalidraw/excalidraw/blob/c5a7723185f6ca05e0ceb0b0d45c4e3fbcb81b2a/src/packages/excalidraw/example/App.js#L68
 | 
			
		||||
            const fileArray = [];
 | 
			
		||||
            const fileArray: BinaryFileData[] = [];
 | 
			
		||||
            for (const fileId in files) {
 | 
			
		||||
                const file = files[fileId];
 | 
			
		||||
                // TODO: dataURL is replaceable with a trilium image url
 | 
			
		||||
@ -288,7 +310,7 @@ export default class ExcalidrawTypeWidget extends TypeWidget {
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                // 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
 | 
			
		||||
                const metadata = results.map((result) => result.metadata);
 | 
			
		||||
@ -302,7 +324,8 @@ export default class ExcalidrawTypeWidget extends TypeWidget {
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            // Update the scene
 | 
			
		||||
            this.excalidrawApi.updateScene(sceneData);
 | 
			
		||||
            // TODO: Fix type of sceneData
 | 
			
		||||
            this.excalidrawApi.updateScene(sceneData as any);
 | 
			
		||||
            this.excalidrawApi.addFiles(fileArray);
 | 
			
		||||
            this.excalidrawApi.history.clear();
 | 
			
		||||
        }
 | 
			
		||||
@ -328,18 +351,17 @@ export default class ExcalidrawTypeWidget extends TypeWidget {
 | 
			
		||||
        const files = this.excalidrawApi.getFiles();
 | 
			
		||||
 | 
			
		||||
        // 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,
 | 
			
		||||
            appState,
 | 
			
		||||
            exportPadding: 5, // 5 px padding
 | 
			
		||||
            metadata: "trilium-export",
 | 
			
		||||
            files
 | 
			
		||||
        });
 | 
			
		||||
        const svgString = svg.outerHTML;
 | 
			
		||||
 | 
			
		||||
        const activeFiles: Record<string, ExcalidrawElement> = {};
 | 
			
		||||
        const activeFiles: Record<string, BinaryFileData> = {};
 | 
			
		||||
        elements.forEach((element) => {
 | 
			
		||||
            if (element.fileId) {
 | 
			
		||||
            if ("fileId" in element && element.fileId) {
 | 
			
		||||
                activeFiles[element.fileId] = files[element.fileId];
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
@ -362,7 +384,12 @@ export default class ExcalidrawTypeWidget extends TypeWidget {
 | 
			
		||||
            // this.libraryChanged is unset in dataSaved()
 | 
			
		||||
 | 
			
		||||
            // 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.
 | 
			
		||||
            //We need the cache to delete old attachments later in the server.
 | 
			
		||||
@ -445,33 +472,35 @@ export default class ExcalidrawTypeWidget extends TypeWidget {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    createExcalidrawReactApp() {
 | 
			
		||||
        const React = window.React;
 | 
			
		||||
        const { Excalidraw } = window.ExcalidrawLib;
 | 
			
		||||
        const excalidrawWrapperRef = React.useRef(null);
 | 
			
		||||
    createExcalidrawReactApp(react: typeof React, excalidrawComponent: React.MemoExoticComponent<(props: ExcalidrawProps) => JSX.Element>) {
 | 
			
		||||
        const excalidrawWrapperRef = react.useRef<HTMLElement>(null);
 | 
			
		||||
        this.excalidrawWrapperRef = excalidrawWrapperRef;
 | 
			
		||||
        const [dimensions, setDimensions] = React.useState({
 | 
			
		||||
        const [dimensions, setDimensions] = react.useState<{ width?: number, height?: number}>({
 | 
			
		||||
            width: undefined,
 | 
			
		||||
            height: undefined
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        React.useEffect(() => {
 | 
			
		||||
            const dimensions = {
 | 
			
		||||
                width: excalidrawWrapperRef.current.getBoundingClientRect().width,
 | 
			
		||||
                height: excalidrawWrapperRef.current.getBoundingClientRect().height
 | 
			
		||||
            };
 | 
			
		||||
            setDimensions(dimensions);
 | 
			
		||||
        react.useEffect(() => {
 | 
			
		||||
            if (excalidrawWrapperRef.current) {
 | 
			
		||||
                const dimensions = {
 | 
			
		||||
                    width: excalidrawWrapperRef.current.getBoundingClientRect().width,
 | 
			
		||||
                    height: excalidrawWrapperRef.current.getBoundingClientRect().height
 | 
			
		||||
                };
 | 
			
		||||
                setDimensions(dimensions);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            const onResize = () => {
 | 
			
		||||
                if (this.note?.type !== "canvas") {
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                const dimensions = {
 | 
			
		||||
                    width: excalidrawWrapperRef.current.getBoundingClientRect().width,
 | 
			
		||||
                    height: excalidrawWrapperRef.current.getBoundingClientRect().height
 | 
			
		||||
                };
 | 
			
		||||
                setDimensions(dimensions);
 | 
			
		||||
                if (excalidrawWrapperRef.current) {
 | 
			
		||||
                    const dimensions = {
 | 
			
		||||
                        width: excalidrawWrapperRef.current.getBoundingClientRect().width,
 | 
			
		||||
                        height: excalidrawWrapperRef.current.getBoundingClientRect().height
 | 
			
		||||
                    };
 | 
			
		||||
                    setDimensions(dimensions);
 | 
			
		||||
                }
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            window.addEventListener("resize", onResize);
 | 
			
		||||
@ -479,8 +508,11 @@ export default class ExcalidrawTypeWidget extends TypeWidget {
 | 
			
		||||
            return () => window.removeEventListener("resize", onResize);
 | 
			
		||||
        }, [excalidrawWrapperRef]);
 | 
			
		||||
 | 
			
		||||
        const onLinkOpen = React.useCallback((element, event) => {
 | 
			
		||||
        const onLinkOpen = react.useCallback<NonNullable<ExcalidrawProps["onLinkOpen"]>>((element, event) => {
 | 
			
		||||
            let link = element.link;
 | 
			
		||||
            if (!link) {
 | 
			
		||||
                return false;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (link.startsWith("root/")) {
 | 
			
		||||
                link = "#" + link;
 | 
			
		||||
@ -493,25 +525,24 @@ export default class ExcalidrawTypeWidget extends TypeWidget {
 | 
			
		||||
            return linkService.goToLinkExt(nativeEvent, link, null);
 | 
			
		||||
        }, []);
 | 
			
		||||
 | 
			
		||||
        return React.createElement(
 | 
			
		||||
            React.Fragment,
 | 
			
		||||
        return react.createElement(
 | 
			
		||||
            react.Fragment,
 | 
			
		||||
            null,
 | 
			
		||||
            React.createElement(
 | 
			
		||||
            react.createElement(
 | 
			
		||||
                "div",
 | 
			
		||||
                {
 | 
			
		||||
                    className: "excalidraw-wrapper",
 | 
			
		||||
                    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
 | 
			
		||||
                    theme: this.themeStyle,
 | 
			
		||||
                    excalidrawAPI: (api: ExcalidrawApi) => {
 | 
			
		||||
                    excalidrawAPI: (api: ExcalidrawImperativeAPI) => {
 | 
			
		||||
                        this.excalidrawApi = api;
 | 
			
		||||
                    },
 | 
			
		||||
                    width: dimensions.width,
 | 
			
		||||
                    height: dimensions.height,
 | 
			
		||||
                    onPaste: (data: unknown, event: unknown) => {
 | 
			
		||||
                        console.log("Verbose: excalidraw internal paste. No trilium action implemented.", data, event);
 | 
			
		||||
                        return false;
 | 
			
		||||
                    },
 | 
			
		||||
                    onLibraryChange: () => {
 | 
			
		||||
                        this.libraryChanged = true;
 | 
			
		||||
@ -528,8 +559,10 @@ export default class ExcalidrawTypeWidget extends TypeWidget {
 | 
			
		||||
                    autoFocus: false,
 | 
			
		||||
                    onLinkOpen,
 | 
			
		||||
                    UIOptions: {
 | 
			
		||||
                        saveToActiveFile: false,
 | 
			
		||||
                        saveAsImage: false
 | 
			
		||||
                        canvasActions: {
 | 
			
		||||
                            saveToActiveFile: false,
 | 
			
		||||
                            saveAsImage: false
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                })
 | 
			
		||||
            )
 | 
			
		||||
@ -555,7 +588,7 @@ export default class ExcalidrawTypeWidget extends TypeWidget {
 | 
			
		||||
    getSceneVersion() {
 | 
			
		||||
        if (this.excalidrawApi) {
 | 
			
		||||
            const elements = this.excalidrawApi.getSceneElements();
 | 
			
		||||
            return window.ExcalidrawLib.getSceneVersion(elements);
 | 
			
		||||
            return this.excalidrawLib.getSceneVersion(elements);
 | 
			
		||||
        } else {
 | 
			
		||||
            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(`/${assetPath}/libraries`, persistentCacheStatic(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(`/${assetPath}/node_modules/@excalidraw/excalidraw/dist/`, persistentCacheStatic(path.join(srcRoot, "..", "node_modules/@excalidraw/excalidraw/dist/")));
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user