From 634937edc24ac184d4cd0c5882cb214035b34e3f Mon Sep 17 00:00:00 2001 From: Hiren Kangad Date: Mon, 9 Mar 2026 23:21:17 +0530 Subject: [PATCH] Port history.ts to v2 scene format with diff-based restore Replace Fabric.js Canvas dependency with SceneManager. UndoManager now serializes via sceneManager.serialize() and restores via loadScene() with diff-based reconciliation (no flicker, no CORS workarounds). --- frontend/src/canvas/history.ts | 24 ++++++------------------ 1 file changed, 6 insertions(+), 18 deletions(-) diff --git a/frontend/src/canvas/history.ts b/frontend/src/canvas/history.ts index f9c65f6..beb6948 100644 --- a/frontend/src/canvas/history.ts +++ b/frontend/src/canvas/history.ts @@ -1,14 +1,14 @@ -import { Canvas } from 'fabric'; +import type { SceneManager } from './SceneManager'; export class UndoManager { private stack: string[] = []; private pointer: number = -1; private maxEntries: number = 50; private locked: boolean = false; - private canvas: Canvas; + private sceneManager: SceneManager; - constructor(canvas: Canvas) { - this.canvas = canvas; + constructor(sceneManager: SceneManager) { + this.sceneManager = sceneManager; // Save initial state this.saveState(); } @@ -20,7 +20,7 @@ export class UndoManager { saveState(): void { if (this.locked) return; - const json = JSON.stringify((this.canvas as any).toJSON(['id', 'crossOrigin'])); + const json = JSON.stringify(this.sceneManager.serialize()); // If we're not at the end, discard forward history if (this.pointer < this.stack.length - 1) { @@ -70,19 +70,7 @@ export class UndoManager { this.locked = true; const parsed = JSON.parse(state); - // Ensure images have crossOrigin to prevent canvas tainting - if (parsed?.objects) { - for (const obj of parsed.objects) { - if (obj.type === 'image') obj.crossOrigin = 'anonymous'; - if (obj.type === 'group' && obj.objects) { - for (const child of obj.objects) { - if (child.type === 'image') child.crossOrigin = 'anonymous'; - } - } - } - } - this.canvas.loadFromJSON(parsed).then(() => { - this.canvas.requestRenderAll(); + this.sceneManager.loadScene(parsed, false).then(() => { this.locked = false; }); }