| 
					
				 | 
			
			
				@@ -20,6 +20,34 @@ const LOCAL_STORAGE_KEY_STATE = "excalidraw-state"; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 const elements = Array.of<ExcalidrawElement>(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+let skipHistory = false; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+const stateHistory: string[] = []; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+function generateHistoryCurrentEntry() { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  return JSON.stringify( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    elements.map(element => ({ ...element, isSelected: false })) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  ); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+function pushHistoryEntry(newEntry: string) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if ( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    stateHistory.length > 0 && 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    stateHistory[stateHistory.length - 1] === newEntry 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  ) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    // If the last entry is the same as this one, ignore it 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    return; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  stateHistory.push(newEntry); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+function restoreHistoryEntry(entry: string) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  const newElements = JSON.parse(entry); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  elements.splice(0, elements.length); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  newElements.forEach((newElement: ExcalidrawElement) => { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    generateDraw(newElement); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    elements.push(newElement); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  }); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  // When restoring, we shouldn't add an history entry otherwise we'll be stuck with it and can't go back 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  skipHistory = true; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 // https://stackoverflow.com/questions/521295/seeding-the-random-number-generator-in-javascript/47593316#47593316 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 const LCG = (seed: number) => () => 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   ((2 ** 31 - 1) & (seed = Math.imul(48271, seed))) / 2 ** 31; 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -901,6 +929,17 @@ class App extends React.Component<{}, AppState> { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				       event.preventDefault(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     } else if (shapesShortcutKeys.includes(event.key.toLowerCase())) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				       this.setState({ elementType: findElementByKey(event.key) }); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } else if (event.metaKey && event.code === "KeyZ") { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      let lastEntry = stateHistory.pop(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      // If nothing was changed since last, take the previous one 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      if (generateHistoryCurrentEntry() === lastEntry) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        lastEntry = stateHistory.pop(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      if (lastEntry !== undefined) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        restoreHistoryEntry(lastEntry); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      this.forceUpdate(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      event.preventDefault(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   }; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -1301,6 +1340,8 @@ class App extends React.Component<{}, AppState> { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                   }); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                   lastX = x; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                   lastY = y; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                  // We don't want to save history when resizing an element 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                  skipHistory = true; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                   this.forceUpdate(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                   return; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 } 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -1319,6 +1360,8 @@ class App extends React.Component<{}, AppState> { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                   }); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                   lastX = x; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                   lastY = y; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                  // We don't want to save history when dragging an element to initially size it 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                  skipHistory = true; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                   this.forceUpdate(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                   return; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 } 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -1347,6 +1390,8 @@ class App extends React.Component<{}, AppState> { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				               if (this.state.elementType === "selection") { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 setSelection(draggingElement); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				               } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+              // We don't want to save history when moving an element 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+              skipHistory = true; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				               this.forceUpdate(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             }; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -1384,6 +1429,8 @@ class App extends React.Component<{}, AppState> { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             window.addEventListener("mousemove", onMouseMove); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             window.addEventListener("mouseup", onMouseUp); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            // We don't want to save history on mouseDown, only on mouseUp when it's fully configured 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            skipHistory = true; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             this.forceUpdate(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				           }} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         /> 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -1407,6 +1454,10 @@ class App extends React.Component<{}, AppState> { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				       viewBackgroundColor: this.state.viewBackgroundColor 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     }); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     save(this.state); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    if (!skipHistory) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+      pushHistoryEntry(generateHistoryCurrentEntry()); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    skipHistory = false; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				   } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 |