diff --git a/apps/client/src/widgets/type_widgets/canvas.ts b/apps/client/src/widgets/type_widgets/canvas.ts
index 3f3eabe50..7ea8dac19 100644
--- a/apps/client/src/widgets/type_widgets/canvas.ts
+++ b/apps/client/src/widgets/type_widgets/canvas.ts
@@ -4,6 +4,7 @@ import server from "../../services/server.js";
import type FNote from "../../entities/fnote.js";
import options from "../../services/options.js";
import { ExcalidrawImperativeAPI } from "@excalidraw/excalidraw/types";
+import type Canvas from "./canvas_el.js";
const TPL = /*html*/`
@@ -101,11 +102,8 @@ interface AttachmentMetadata {
*/
export default class ExcalidrawTypeWidget extends TypeWidget {
- private readonly SCENE_VERSION_INITIAL: number;
- private readonly SCENE_VERSION_ERROR: number;
-
private currentNoteId: string;
- private currentSceneVersion: number;
+
private libraryChanged: boolean;
private librarycache: LibraryItem[];
private attachmentMetadata: AttachmentMetadata[];
@@ -116,21 +114,17 @@ export default class ExcalidrawTypeWidget extends TypeWidget {
private $render!: JQuery;
private reactHandlers!: JQuery;
+ private canvasInstance!: Canvas;
constructor() {
super();
- // constants
- this.SCENE_VERSION_INITIAL = -1; // -1 indicates that it is fresh. excalidraw scene version is always >0
- this.SCENE_VERSION_ERROR = -2; // -2 indicates error
-
// currently required by excalidraw, in order to allows self-hosting fonts locally.
// this avoids making excalidraw load the fonts from an external CDN.
(window as any).EXCALIDRAW_ASSET_PATH = `${window.location.pathname}/node_modules/@excalidraw/excalidraw/dist/prod`;
// temporary vars
this.currentNoteId = "";
- this.currentSceneVersion = this.SCENE_VERSION_INITIAL;
// will be overwritten
this.$render;
@@ -184,12 +178,11 @@ export default class ExcalidrawTypeWidget extends TypeWidget {
}
(window.process.env as any).PREACT = false;
- const renderCanvas = (await import("./canvas_el.js")).default;
- renderCanvas(renderElement, {
- excalidrawAPI: (api: ExcalidrawImperativeAPI) => {
- this.excalidrawApi = api;
- },
+ const Canvas = (await import("./canvas_el.js")).default;
+ this.canvasInstance = new Canvas({
+
});
+ this.canvasInstance.renderCanvas(renderElement);
}
/**
@@ -200,7 +193,7 @@ export default class ExcalidrawTypeWidget extends TypeWidget {
const noteChanged = this.currentNoteId !== note.noteId;
if (noteChanged) {
// reset the scene to omit unnecessary onchange handler
- this.currentSceneVersion = this.SCENE_VERSION_INITIAL;
+ this.canvasInstance?.resetSceneVersion();
}
this.currentNoteId = note.noteId;
@@ -208,7 +201,7 @@ export default class ExcalidrawTypeWidget extends TypeWidget {
const blob = await note.getBlob();
// before we load content into excalidraw, make sure excalidraw has loaded
- while (!this.excalidrawApi) {
+ while (!this.canvasInstance?.excalidrawApi) {
console.log("excalidrawApi not yet loaded, sleep 200ms...");
await utils.sleep(200);
}
@@ -228,7 +221,7 @@ export default class ExcalidrawTypeWidget extends TypeWidget {
};
// TODO: Props mismatch.
- this.excalidrawApi.updateScene(sceneData as any);
+ this.canvasInstance.excalidrawApi.updateScene(sceneData as any);
} else if (blob.content) {
let content: CanvasContent;
@@ -301,7 +294,7 @@ export default class ExcalidrawTypeWidget extends TypeWidget {
const metadata = results.map((result) => result.metadata);
// Update the library and save to independent variables
- this.excalidrawApi.updateLibrary({ libraryItems, merge: false });
+ this.canvasInstance.excalidrawApi.updateLibrary({ libraryItems, merge: false });
// save state of library to compare it to the new state later.
this.librarycache = libraryItems;
@@ -310,14 +303,14 @@ export default class ExcalidrawTypeWidget extends TypeWidget {
// Update the scene
// TODO: Fix type of sceneData
- this.excalidrawApi.updateScene(sceneData as any);
- this.excalidrawApi.addFiles(fileArray);
- this.excalidrawApi.history.clear();
+ this.canvasInstance.excalidrawApi.updateScene(sceneData as any);
+ this.canvasInstance.excalidrawApi.addFiles(fileArray);
+ this.canvasInstance.excalidrawApi.history.clear();
}
// set initial scene version
- if (this.currentSceneVersion === this.SCENE_VERSION_INITIAL) {
- this.currentSceneVersion = this.getSceneVersion();
+ if (this.canvasInstance.isInitialScene()) {
+ this.canvasInstance.updateSceneVersion();
}
}
@@ -326,14 +319,14 @@ export default class ExcalidrawTypeWidget extends TypeWidget {
* this is automatically called after this.saveData();
*/
async getData() {
- const elements = this.excalidrawApi.getSceneElements();
- const appState = this.excalidrawApi.getAppState();
+ const elements = this.canvasInstance.excalidrawApi.getSceneElements();
+ const appState = this.canvasInstance.excalidrawApi.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 a new excalidraw version?
*/
- const files = this.excalidrawApi.getFiles();
+ const files = this.canvasInstance.excalidrawApi.getFiles();
// parallel svg export to combat bitrot and enable rendering image for note inclusion, preview, and share
const svg = await this.excalidrawLib.exportToSvg({
@@ -370,7 +363,7 @@ 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({
+ const libraryItems = await this.canvasInstance.excalidrawApi.updateLibrary({
libraryItems() {
return [];
},
@@ -444,53 +437,20 @@ export default class ExcalidrawTypeWidget extends TypeWidget {
}
// changeHandler is called upon any tiny change in excalidraw. button clicked, hover, etc.
// make sure only when a new element is added, we actually save something.
- const isNewSceneVersion = this.isNewSceneVersion();
+ const isNewSceneVersion = this.canvasInstance.isNewSceneVersion();
/**
* FIXME: however, we might want to make an exception, if viewport changed, since viewport
* is desired to save? (add) and appState background, and some things
*/
// upon updateScene, onchange is called, even though "nothing really changed" that is worth saving
- const isNotInitialScene = this.currentSceneVersion !== this.SCENE_VERSION_INITIAL;
-
+ const isNotInitialScene = !this.canvasInstance.isInitialScene();
const shouldSave = isNewSceneVersion && isNotInitialScene;
if (shouldSave) {
- this.updateSceneVersion();
+ this.canvasInstance.updateSceneVersion();
this.saveData();
}
}
- /**
- * needed to ensure, that multipleOnChangeHandler calls do not trigger a save.
- * we compare the scene version as suggested in:
- * https://github.com/excalidraw/excalidraw/issues/3014#issuecomment-778115329
- *
- * info: sceneVersions are not incrementing. it seems to be a pseudo-random number
- */
- isNewSceneVersion() {
- if (options.is("databaseReadonly")) {
- return false;
- }
-
- const sceneVersion = this.getSceneVersion();
-
- return (
- this.currentSceneVersion === this.SCENE_VERSION_INITIAL || // initial scene version update
- this.currentSceneVersion !== sceneVersion
- ); // ensure scene changed
- }
-
- getSceneVersion() {
- if (this.excalidrawApi) {
- const elements = this.excalidrawApi.getSceneElements();
- return this.excalidrawLib.getSceneVersion(elements);
- } else {
- return this.SCENE_VERSION_ERROR;
- }
- }
-
- updateSceneVersion() {
- this.currentSceneVersion = this.getSceneVersion();
- }
}
diff --git a/apps/client/src/widgets/type_widgets/canvas_el.ts b/apps/client/src/widgets/type_widgets/canvas_el.ts
index 9ab0269e0..5ad275d4e 100644
--- a/apps/client/src/widgets/type_widgets/canvas_el.ts
+++ b/apps/client/src/widgets/type_widgets/canvas_el.ts
@@ -1,10 +1,66 @@
import "@excalidraw/excalidraw/index.css";
-import { Excalidraw } from "@excalidraw/excalidraw";
+import { Excalidraw, getSceneVersion, exportToSvg } from "@excalidraw/excalidraw";
import { createElement, createRef, Fragment, render } from "preact/compat";
-import { ExcalidrawProps } from "@excalidraw/excalidraw/types";
+import { ExcalidrawImperativeAPI, ExcalidrawProps } from "@excalidraw/excalidraw/types";
+
+/** -1 indicates that it is fresh. excalidraw scene version is always >0 */
+const SCENE_VERSION_INITIAL = -1;
+/** -2 indicates error */
+const SCENE_VERSION_ERROR = -2;
+
+export default class Canvas {
+
+ private currentSceneVersion: number;
+ private opts: ExcalidrawProps;
+ excalidrawApi!: ExcalidrawImperativeAPI;
+
+ constructor(opts: ExcalidrawProps) {
+ this.opts = opts;
+ this.currentSceneVersion = SCENE_VERSION_INITIAL;
+ }
+
+ renderCanvas(targetEl: HTMLElement) {
+ render(createCanvasElement({
+ ...this.opts,
+ excalidrawAPI: (api: ExcalidrawImperativeAPI) => {
+ this.excalidrawApi = api;
+ },
+ }), targetEl);
+ }
+
+ /**
+ * needed to ensure, that multipleOnChangeHandler calls do not trigger a save.
+ * we compare the scene version as suggested in:
+ * https://github.com/excalidraw/excalidraw/issues/3014#issuecomment-778115329
+ *
+ * info: sceneVersions are not incrementing. it seems to be a pseudo-random number
+ */
+ isNewSceneVersion() {
+ const sceneVersion = this.getSceneVersion();
+
+ return (
+ this.currentSceneVersion === SCENE_VERSION_INITIAL || // initial scene version update
+ this.currentSceneVersion !== sceneVersion
+ ); // ensure scene changed
+ }
+
+ getSceneVersion() {
+ const elements = this.excalidrawApi.getSceneElements();
+ return getSceneVersion(elements);
+ }
+
+ updateSceneVersion() {
+ this.currentSceneVersion = this.getSceneVersion();
+ }
+
+ resetSceneVersion() {
+ this.currentSceneVersion = SCENE_VERSION_INITIAL;
+ }
+
+ isInitialScene() {
+ return this.currentSceneVersion === SCENE_VERSION_INITIAL;
+ }
-export default function renderCanvas(targetEl: HTMLElement, opts: ExcalidrawProps) {
- render(createCanvasElement(opts), targetEl);
}
function createCanvasElement(opts: ExcalidrawProps) {