|  | @@ -39,7 +39,6 @@ import {
 | 
	
		
			
				|  |  |    getElementContainingPosition,
 | 
	
		
			
				|  |  |    getNormalizedZoom,
 | 
	
		
			
				|  |  |    getSelectedElements,
 | 
	
		
			
				|  |  | -  globalSceneState,
 | 
	
		
			
				|  |  |    isSomeElementSelected,
 | 
	
		
			
				|  |  |    calculateScrollCenter,
 | 
	
		
			
				|  |  |  } from "../scene";
 | 
	
	
		
			
				|  | @@ -137,7 +136,6 @@ import { generateCollaborationLink, getCollaborationLinkData } from "../data";
 | 
	
		
			
				|  |  |  import { mutateElement, newElementWith } from "../element/mutateElement";
 | 
	
		
			
				|  |  |  import { invalidateShapeForElement } from "../renderer/renderElement";
 | 
	
		
			
				|  |  |  import { unstable_batchedUpdates } from "react-dom";
 | 
	
		
			
				|  |  | -import { SceneStateCallbackRemover } from "../scene/globalScene";
 | 
	
		
			
				|  |  |  import { isLinearElement } from "../element/typeChecks";
 | 
	
		
			
				|  |  |  import { actionFinalize, actionDeleteSelected } from "../actions";
 | 
	
		
			
				|  |  |  import {
 | 
	
	
		
			
				|  | @@ -155,6 +153,7 @@ import {
 | 
	
		
			
				|  |  |    getSelectedGroupIdForElement,
 | 
	
		
			
				|  |  |  } from "../groups";
 | 
	
		
			
				|  |  |  import { Library } from "../data/library";
 | 
	
		
			
				|  |  | +import Scene from "../scene/Scene";
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  /**
 | 
	
		
			
				|  |  |   * @param func handler taking at most single parameter (event).
 | 
	
	
		
			
				|  | @@ -243,7 +242,6 @@ class App extends React.Component<ExcalidrawProps, AppState> {
 | 
	
		
			
				|  |  |    portal: Portal = new Portal(this);
 | 
	
		
			
				|  |  |    lastBroadcastedOrReceivedSceneVersion: number = -1;
 | 
	
		
			
				|  |  |    broadcastedElementVersions: Map<string, number> = new Map();
 | 
	
		
			
				|  |  | -  removeSceneCallback: SceneStateCallbackRemover | null = null;
 | 
	
		
			
				|  |  |    unmounted: boolean = false;
 | 
	
		
			
				|  |  |    actionManager: ActionManager;
 | 
	
		
			
				|  |  |    private excalidrawRef: any;
 | 
	
	
		
			
				|  | @@ -252,6 +250,7 @@ class App extends React.Component<ExcalidrawProps, AppState> {
 | 
	
		
			
				|  |  |      width: window.innerWidth,
 | 
	
		
			
				|  |  |      height: window.innerHeight,
 | 
	
		
			
				|  |  |    };
 | 
	
		
			
				|  |  | +  private scene: Scene;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |    constructor(props: ExcalidrawProps) {
 | 
	
		
			
				|  |  |      super(props);
 | 
	
	
		
			
				|  | @@ -266,11 +265,12 @@ class App extends React.Component<ExcalidrawProps, AppState> {
 | 
	
		
			
				|  |  |        ...this.getCanvasOffsets(),
 | 
	
		
			
				|  |  |      };
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +    this.scene = new Scene();
 | 
	
		
			
				|  |  |      this.excalidrawRef = React.createRef();
 | 
	
		
			
				|  |  |      this.actionManager = new ActionManager(
 | 
	
		
			
				|  |  |        this.syncActionResult,
 | 
	
		
			
				|  |  |        () => this.state,
 | 
	
		
			
				|  |  | -      () => globalSceneState.getElementsIncludingDeleted(),
 | 
	
		
			
				|  |  | +      () => this.scene.getElementsIncludingDeleted(),
 | 
	
		
			
				|  |  |      );
 | 
	
		
			
				|  |  |      this.actionManager.registerAll(actions);
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -308,7 +308,7 @@ class App extends React.Component<ExcalidrawProps, AppState> {
 | 
	
		
			
				|  |  |            appState={this.state}
 | 
	
		
			
				|  |  |            setAppState={this.setAppState}
 | 
	
		
			
				|  |  |            actionManager={this.actionManager}
 | 
	
		
			
				|  |  | -          elements={globalSceneState.getElements()}
 | 
	
		
			
				|  |  | +          elements={this.scene.getElements()}
 | 
	
		
			
				|  |  |            onRoomCreate={this.openPortal}
 | 
	
		
			
				|  |  |            onRoomDestroy={this.closePortal}
 | 
	
		
			
				|  |  |            onUsernameChange={(username) => {
 | 
	
	
		
			
				|  | @@ -368,7 +368,7 @@ class App extends React.Component<ExcalidrawProps, AppState> {
 | 
	
		
			
				|  |  |              editingElement = element;
 | 
	
		
			
				|  |  |            }
 | 
	
		
			
				|  |  |          });
 | 
	
		
			
				|  |  | -        globalSceneState.replaceAllElements(actionResult.elements);
 | 
	
		
			
				|  |  | +        this.scene.replaceAllElements(actionResult.elements);
 | 
	
		
			
				|  |  |          if (actionResult.commitToHistory) {
 | 
	
		
			
				|  |  |            history.resumeRecording();
 | 
	
		
			
				|  |  |          }
 | 
	
	
		
			
				|  | @@ -394,7 +394,7 @@ class App extends React.Component<ExcalidrawProps, AppState> {
 | 
	
		
			
				|  |  |              if (actionResult.syncHistory) {
 | 
	
		
			
				|  |  |                history.setCurrentState(
 | 
	
		
			
				|  |  |                  this.state,
 | 
	
		
			
				|  |  | -                globalSceneState.getElementsIncludingDeleted(),
 | 
	
		
			
				|  |  | +                this.scene.getElementsIncludingDeleted(),
 | 
	
		
			
				|  |  |                );
 | 
	
		
			
				|  |  |              }
 | 
	
		
			
				|  |  |            },
 | 
	
	
		
			
				|  | @@ -421,7 +421,7 @@ class App extends React.Component<ExcalidrawProps, AppState> {
 | 
	
		
			
				|  |  |    };
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |    private onFontLoaded = () => {
 | 
	
		
			
				|  |  | -    globalSceneState.getElementsIncludingDeleted().forEach((element) => {
 | 
	
		
			
				|  |  | +    this.scene.getElementsIncludingDeleted().forEach((element) => {
 | 
	
		
			
				|  |  |        if (isTextElement(element)) {
 | 
	
		
			
				|  |  |          invalidateShapeForElement(element);
 | 
	
		
			
				|  |  |        }
 | 
	
	
		
			
				|  | @@ -562,9 +562,7 @@ class App extends React.Component<ExcalidrawProps, AppState> {
 | 
	
		
			
				|  |  |        });
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    this.removeSceneCallback = globalSceneState.addCallback(
 | 
	
		
			
				|  |  | -      this.onSceneUpdated,
 | 
	
		
			
				|  |  | -    );
 | 
	
		
			
				|  |  | +    this.scene.addCallback(this.onSceneUpdated);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      this.addEventListeners();
 | 
	
		
			
				|  |  |      this.setState(this.getCanvasOffsets(), () => {
 | 
	
	
		
			
				|  | @@ -574,14 +572,13 @@ class App extends React.Component<ExcalidrawProps, AppState> {
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |    public componentWillUnmount() {
 | 
	
		
			
				|  |  |      this.unmounted = true;
 | 
	
		
			
				|  |  | -    this.removeSceneCallback!();
 | 
	
		
			
				|  |  |      this.removeEventListeners();
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | +    this.scene.destroy();
 | 
	
		
			
				|  |  |      clearTimeout(touchTimeout);
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |    private onResize = withBatchedUpdates(() => {
 | 
	
		
			
				|  |  | -    globalSceneState
 | 
	
		
			
				|  |  | +    this.scene
 | 
	
		
			
				|  |  |        .getElementsIncludingDeleted()
 | 
	
		
			
				|  |  |        .forEach((element) => invalidateShapeForElement(element));
 | 
	
		
			
				|  |  |      this.setState({});
 | 
	
	
		
			
				|  | @@ -682,10 +679,7 @@ class App extends React.Component<ExcalidrawProps, AppState> {
 | 
	
		
			
				|  |  |          );
 | 
	
		
			
				|  |  |        } catch {}
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  | -    if (
 | 
	
		
			
				|  |  | -      this.state.isCollaborating &&
 | 
	
		
			
				|  |  | -      globalSceneState.getElements().length > 0
 | 
	
		
			
				|  |  | -    ) {
 | 
	
		
			
				|  |  | +    if (this.state.isCollaborating && this.scene.getElements().length > 0) {
 | 
	
		
			
				|  |  |        event.preventDefault();
 | 
	
		
			
				|  |  |        // NOTE: modern browsers no longer allow showing a custom message here
 | 
	
		
			
				|  |  |        event.returnValue = "";
 | 
	
	
		
			
				|  | @@ -753,7 +747,7 @@ class App extends React.Component<ExcalidrawProps, AppState> {
 | 
	
		
			
				|  |  |        );
 | 
	
		
			
				|  |  |        cursorButton[socketID] = user.button;
 | 
	
		
			
				|  |  |      });
 | 
	
		
			
				|  |  | -    const elements = globalSceneState.getElements();
 | 
	
		
			
				|  |  | +    const elements = this.scene.getElements();
 | 
	
		
			
				|  |  |      const { atLeastOneVisibleElement, scrollBars } = renderScene(
 | 
	
		
			
				|  |  |        elements.filter((element) => {
 | 
	
		
			
				|  |  |          // don't render text element that's being currently edited (it's
 | 
	
	
		
			
				|  | @@ -798,14 +792,14 @@ class App extends React.Component<ExcalidrawProps, AppState> {
 | 
	
		
			
				|  |  |      this.saveDebounced();
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      if (
 | 
	
		
			
				|  |  | -      getDrawingVersion(globalSceneState.getElementsIncludingDeleted()) >
 | 
	
		
			
				|  |  | +      getDrawingVersion(this.scene.getElementsIncludingDeleted()) >
 | 
	
		
			
				|  |  |        this.lastBroadcastedOrReceivedSceneVersion
 | 
	
		
			
				|  |  |      ) {
 | 
	
		
			
				|  |  |        this.broadcastScene(SCENE.UPDATE, /* syncAll */ false);
 | 
	
		
			
				|  |  |        this.queueBroadcastAllElements();
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    history.record(this.state, globalSceneState.getElementsIncludingDeleted());
 | 
	
		
			
				|  |  | +    history.record(this.state, this.scene.getElementsIncludingDeleted());
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |    // Copy/paste
 | 
	
	
		
			
				|  | @@ -828,11 +822,11 @@ class App extends React.Component<ExcalidrawProps, AppState> {
 | 
	
		
			
				|  |  |    });
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |    private copyAll = () => {
 | 
	
		
			
				|  |  | -    copyToAppClipboard(globalSceneState.getElements(), this.state);
 | 
	
		
			
				|  |  | +    copyToAppClipboard(this.scene.getElements(), this.state);
 | 
	
		
			
				|  |  |    };
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |    private copyToClipboardAsPng = () => {
 | 
	
		
			
				|  |  | -    const elements = globalSceneState.getElements();
 | 
	
		
			
				|  |  | +    const elements = this.scene.getElements();
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      const selectedElements = getSelectedElements(elements, this.state);
 | 
	
		
			
				|  |  |      exportCanvas(
 | 
	
	
		
			
				|  | @@ -846,14 +840,12 @@ class App extends React.Component<ExcalidrawProps, AppState> {
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |    private copyToClipboardAsSvg = () => {
 | 
	
		
			
				|  |  |      const selectedElements = getSelectedElements(
 | 
	
		
			
				|  |  | -      globalSceneState.getElements(),
 | 
	
		
			
				|  |  | +      this.scene.getElements(),
 | 
	
		
			
				|  |  |        this.state,
 | 
	
		
			
				|  |  |      );
 | 
	
		
			
				|  |  |      exportCanvas(
 | 
	
		
			
				|  |  |        "clipboard-svg",
 | 
	
		
			
				|  |  | -      selectedElements.length
 | 
	
		
			
				|  |  | -        ? selectedElements
 | 
	
		
			
				|  |  | -        : globalSceneState.getElements(),
 | 
	
		
			
				|  |  | +      selectedElements.length ? selectedElements : this.scene.getElements(),
 | 
	
		
			
				|  |  |        this.state,
 | 
	
		
			
				|  |  |        this.canvas!,
 | 
	
		
			
				|  |  |        this.state,
 | 
	
	
		
			
				|  | @@ -958,15 +950,15 @@ class App extends React.Component<ExcalidrawProps, AppState> {
 | 
	
		
			
				|  |  |      const dy = y - elementsCenterY;
 | 
	
		
			
				|  |  |      const groupIdMap = new Map();
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    const newElements = clipboardElements.map((element) =>
 | 
	
		
			
				|  |  | -      duplicateElement(this.state.editingGroupId, groupIdMap, element, {
 | 
	
		
			
				|  |  | +    const newElements = clipboardElements.map((element) => {
 | 
	
		
			
				|  |  | +      return duplicateElement(this.state.editingGroupId, groupIdMap, element, {
 | 
	
		
			
				|  |  |          x: element.x + dx - minX,
 | 
	
		
			
				|  |  |          y: element.y + dy - minY,
 | 
	
		
			
				|  |  | -      }),
 | 
	
		
			
				|  |  | -    );
 | 
	
		
			
				|  |  | +      });
 | 
	
		
			
				|  |  | +    });
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    globalSceneState.replaceAllElements([
 | 
	
		
			
				|  |  | -      ...globalSceneState.getElementsIncludingDeleted(),
 | 
	
		
			
				|  |  | +    this.scene.replaceAllElements([
 | 
	
		
			
				|  |  | +      ...this.scene.getElementsIncludingDeleted(),
 | 
	
		
			
				|  |  |        ...newElements,
 | 
	
		
			
				|  |  |      ]);
 | 
	
		
			
				|  |  |      history.resumeRecording();
 | 
	
	
		
			
				|  | @@ -1004,8 +996,8 @@ class App extends React.Component<ExcalidrawProps, AppState> {
 | 
	
		
			
				|  |  |        verticalAlign: DEFAULT_VERTICAL_ALIGN,
 | 
	
		
			
				|  |  |      });
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    globalSceneState.replaceAllElements([
 | 
	
		
			
				|  |  | -      ...globalSceneState.getElementsIncludingDeleted(),
 | 
	
		
			
				|  |  | +    this.scene.replaceAllElements([
 | 
	
		
			
				|  |  | +      ...this.scene.getElementsIncludingDeleted(),
 | 
	
		
			
				|  |  |        element,
 | 
	
		
			
				|  |  |      ]);
 | 
	
		
			
				|  |  |      this.setState({ selectedElementIds: { [element.id]: true } });
 | 
	
	
		
			
				|  | @@ -1116,15 +1108,15 @@ class App extends React.Component<ExcalidrawProps, AppState> {
 | 
	
		
			
				|  |  |          // elements with more staler versions than ours, ignore them
 | 
	
		
			
				|  |  |          // and keep ours.
 | 
	
		
			
				|  |  |          if (
 | 
	
		
			
				|  |  | -          globalSceneState.getElementsIncludingDeleted() == null ||
 | 
	
		
			
				|  |  | -          globalSceneState.getElementsIncludingDeleted().length === 0
 | 
	
		
			
				|  |  | +          this.scene.getElementsIncludingDeleted() == null ||
 | 
	
		
			
				|  |  | +          this.scene.getElementsIncludingDeleted().length === 0
 | 
	
		
			
				|  |  |          ) {
 | 
	
		
			
				|  |  | -          globalSceneState.replaceAllElements(remoteElements);
 | 
	
		
			
				|  |  | +          this.scene.replaceAllElements(remoteElements);
 | 
	
		
			
				|  |  |          } else {
 | 
	
		
			
				|  |  |            // create a map of ids so we don't have to iterate
 | 
	
		
			
				|  |  |            // over the array more than once.
 | 
	
		
			
				|  |  |            const localElementMap = getElementMap(
 | 
	
		
			
				|  |  | -            globalSceneState.getElementsIncludingDeleted(),
 | 
	
		
			
				|  |  | +            this.scene.getElementsIncludingDeleted(),
 | 
	
		
			
				|  |  |            );
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |            // Reconcile
 | 
	
	
		
			
				|  | @@ -1183,7 +1175,7 @@ class App extends React.Component<ExcalidrawProps, AppState> {
 | 
	
		
			
				|  |  |              newElements,
 | 
	
		
			
				|  |  |            );
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -          globalSceneState.replaceAllElements(newElements);
 | 
	
		
			
				|  |  | +          this.scene.replaceAllElements(newElements);
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |          // We haven't yet implemented multiplayer undo functionality, so we clear the undo stack
 | 
	
	
		
			
				|  | @@ -1317,7 +1309,7 @@ class App extends React.Component<ExcalidrawProps, AppState> {
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      let syncableElements = getSyncableElements(
 | 
	
		
			
				|  |  | -      globalSceneState.getElementsIncludingDeleted(),
 | 
	
		
			
				|  |  | +      this.scene.getElementsIncludingDeleted(),
 | 
	
		
			
				|  |  |      );
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      if (!syncAll) {
 | 
	
	
		
			
				|  | @@ -1340,7 +1332,7 @@ class App extends React.Component<ExcalidrawProps, AppState> {
 | 
	
		
			
				|  |  |      };
 | 
	
		
			
				|  |  |      this.lastBroadcastedOrReceivedSceneVersion = Math.max(
 | 
	
		
			
				|  |  |        this.lastBroadcastedOrReceivedSceneVersion,
 | 
	
		
			
				|  |  | -      getDrawingVersion(globalSceneState.getElementsIncludingDeleted()),
 | 
	
		
			
				|  |  | +      getDrawingVersion(this.scene.getElementsIncludingDeleted()),
 | 
	
		
			
				|  |  |      );
 | 
	
		
			
				|  |  |      for (const syncableElement of syncableElements) {
 | 
	
		
			
				|  |  |        this.broadcastedElementVersions.set(
 | 
	
	
		
			
				|  | @@ -1427,8 +1419,8 @@ class App extends React.Component<ExcalidrawProps, AppState> {
 | 
	
		
			
				|  |  |          (event.shiftKey
 | 
	
		
			
				|  |  |            ? ELEMENT_SHIFT_TRANSLATE_AMOUNT
 | 
	
		
			
				|  |  |            : ELEMENT_TRANSLATE_AMOUNT);
 | 
	
		
			
				|  |  | -      globalSceneState.replaceAllElements(
 | 
	
		
			
				|  |  | -        globalSceneState.getElementsIncludingDeleted().map((el) => {
 | 
	
		
			
				|  |  | +      this.scene.replaceAllElements(
 | 
	
		
			
				|  |  | +        this.scene.getElementsIncludingDeleted().map((el) => {
 | 
	
		
			
				|  |  |            if (this.state.selectedElementIds[el.id]) {
 | 
	
		
			
				|  |  |              const update: { x?: number; y?: number } = {};
 | 
	
		
			
				|  |  |              if (event.key === KEYS.ARROW_LEFT) {
 | 
	
	
		
			
				|  | @@ -1448,7 +1440,7 @@ class App extends React.Component<ExcalidrawProps, AppState> {
 | 
	
		
			
				|  |  |        event.preventDefault();
 | 
	
		
			
				|  |  |      } else if (event.key === KEYS.ENTER) {
 | 
	
		
			
				|  |  |        const selectedElements = getSelectedElements(
 | 
	
		
			
				|  |  | -        globalSceneState.getElements(),
 | 
	
		
			
				|  |  | +        this.scene.getElements(),
 | 
	
		
			
				|  |  |          this.state,
 | 
	
		
			
				|  |  |        );
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -1462,7 +1454,10 @@ class App extends React.Component<ExcalidrawProps, AppState> {
 | 
	
		
			
				|  |  |          ) {
 | 
	
		
			
				|  |  |            history.resumeRecording();
 | 
	
		
			
				|  |  |            this.setState({
 | 
	
		
			
				|  |  | -            editingLinearElement: new LinearElementEditor(selectedElements[0]),
 | 
	
		
			
				|  |  | +            editingLinearElement: new LinearElementEditor(
 | 
	
		
			
				|  |  | +              selectedElements[0],
 | 
	
		
			
				|  |  | +              this.scene,
 | 
	
		
			
				|  |  | +            ),
 | 
	
		
			
				|  |  |            });
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |        } else if (
 | 
	
	
		
			
				|  | @@ -1558,7 +1553,7 @@ class App extends React.Component<ExcalidrawProps, AppState> {
 | 
	
		
			
				|  |  |    });
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |    private setElements = (elements: readonly ExcalidrawElement[]) => {
 | 
	
		
			
				|  |  | -    globalSceneState.replaceAllElements(elements);
 | 
	
		
			
				|  |  | +    this.scene.replaceAllElements(elements);
 | 
	
		
			
				|  |  |    };
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |    private handleTextWysiwyg(
 | 
	
	
		
			
				|  | @@ -1570,8 +1565,8 @@ class App extends React.Component<ExcalidrawProps, AppState> {
 | 
	
		
			
				|  |  |      },
 | 
	
		
			
				|  |  |    ) {
 | 
	
		
			
				|  |  |      const updateElement = (text: string, isDeleted = false) => {
 | 
	
		
			
				|  |  | -      globalSceneState.replaceAllElements([
 | 
	
		
			
				|  |  | -        ...globalSceneState.getElementsIncludingDeleted().map((_element) => {
 | 
	
		
			
				|  |  | +      this.scene.replaceAllElements([
 | 
	
		
			
				|  |  | +        ...this.scene.getElementsIncludingDeleted().map((_element) => {
 | 
	
		
			
				|  |  |            if (_element.id === element.id && isTextElement(_element)) {
 | 
	
		
			
				|  |  |              return updateTextElement(_element, {
 | 
	
		
			
				|  |  |                text,
 | 
	
	
		
			
				|  | @@ -1624,6 +1619,7 @@ class App extends React.Component<ExcalidrawProps, AppState> {
 | 
	
		
			
				|  |  |            setCursorForShape(this.state.elementType);
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |        }),
 | 
	
		
			
				|  |  | +      element,
 | 
	
		
			
				|  |  |      });
 | 
	
		
			
				|  |  |      // deselect all other elements when inserting text
 | 
	
		
			
				|  |  |      this.setState({
 | 
	
	
		
			
				|  | @@ -1642,7 +1638,7 @@ class App extends React.Component<ExcalidrawProps, AppState> {
 | 
	
		
			
				|  |  |      y: number,
 | 
	
		
			
				|  |  |    ): NonDeleted<ExcalidrawTextElement> | null {
 | 
	
		
			
				|  |  |      const element = getElementAtPosition(
 | 
	
		
			
				|  |  | -      globalSceneState.getElements(),
 | 
	
		
			
				|  |  | +      this.scene.getElements(),
 | 
	
		
			
				|  |  |        this.state,
 | 
	
		
			
				|  |  |        x,
 | 
	
		
			
				|  |  |        y,
 | 
	
	
		
			
				|  | @@ -1715,8 +1711,8 @@ class App extends React.Component<ExcalidrawProps, AppState> {
 | 
	
		
			
				|  |  |          mutateElement(element, { verticalAlign: DEFAULT_VERTICAL_ALIGN });
 | 
	
		
			
				|  |  |        }
 | 
	
		
			
				|  |  |      } else {
 | 
	
		
			
				|  |  | -      globalSceneState.replaceAllElements([
 | 
	
		
			
				|  |  | -        ...globalSceneState.getElementsIncludingDeleted(),
 | 
	
		
			
				|  |  | +      this.scene.replaceAllElements([
 | 
	
		
			
				|  |  | +        ...this.scene.getElementsIncludingDeleted(),
 | 
	
		
			
				|  |  |          element,
 | 
	
		
			
				|  |  |        ]);
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -1752,7 +1748,7 @@ class App extends React.Component<ExcalidrawProps, AppState> {
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      const selectedElements = getSelectedElements(
 | 
	
		
			
				|  |  | -      globalSceneState.getElements(),
 | 
	
		
			
				|  |  | +      this.scene.getElements(),
 | 
	
		
			
				|  |  |        this.state,
 | 
	
		
			
				|  |  |      );
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -1763,7 +1759,10 @@ class App extends React.Component<ExcalidrawProps, AppState> {
 | 
	
		
			
				|  |  |        ) {
 | 
	
		
			
				|  |  |          history.resumeRecording();
 | 
	
		
			
				|  |  |          this.setState({
 | 
	
		
			
				|  |  | -          editingLinearElement: new LinearElementEditor(selectedElements[0]),
 | 
	
		
			
				|  |  | +          editingLinearElement: new LinearElementEditor(
 | 
	
		
			
				|  |  | +            selectedElements[0],
 | 
	
		
			
				|  |  | +            this.scene,
 | 
	
		
			
				|  |  | +          ),
 | 
	
		
			
				|  |  |          });
 | 
	
		
			
				|  |  |        }
 | 
	
		
			
				|  |  |        return;
 | 
	
	
		
			
				|  | @@ -1781,7 +1780,7 @@ class App extends React.Component<ExcalidrawProps, AppState> {
 | 
	
		
			
				|  |  |      const selectedGroupIds = getSelectedGroupIds(this.state);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      if (selectedGroupIds.length > 0) {
 | 
	
		
			
				|  |  | -      const elements = globalSceneState.getElements();
 | 
	
		
			
				|  |  | +      const elements = this.scene.getElements();
 | 
	
		
			
				|  |  |        const hitElement = getElementAtPosition(
 | 
	
		
			
				|  |  |          elements,
 | 
	
		
			
				|  |  |          this.state,
 | 
	
	
		
			
				|  | @@ -1803,7 +1802,7 @@ class App extends React.Component<ExcalidrawProps, AppState> {
 | 
	
		
			
				|  |  |                selectedElementIds: { [hitElement!.id]: true },
 | 
	
		
			
				|  |  |                selectedGroupIds: {},
 | 
	
		
			
				|  |  |              },
 | 
	
		
			
				|  |  | -            globalSceneState.getElements(),
 | 
	
		
			
				|  |  | +            this.scene.getElements(),
 | 
	
		
			
				|  |  |            ),
 | 
	
		
			
				|  |  |          );
 | 
	
		
			
				|  |  |          return;
 | 
	
	
		
			
				|  | @@ -1960,7 +1959,7 @@ class App extends React.Component<ExcalidrawProps, AppState> {
 | 
	
		
			
				|  |  |        return;
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    const elements = globalSceneState.getElements();
 | 
	
		
			
				|  |  | +    const elements = this.scene.getElements();
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      const selectedElements = getSelectedElements(elements, this.state);
 | 
	
		
			
				|  |  |      if (
 | 
	
	
		
			
				|  | @@ -2262,7 +2261,7 @@ class App extends React.Component<ExcalidrawProps, AppState> {
 | 
	
		
			
				|  |  |        window.devicePixelRatio,
 | 
	
		
			
				|  |  |      );
 | 
	
		
			
				|  |  |      const selectedElements = getSelectedElements(
 | 
	
		
			
				|  |  | -      globalSceneState.getElements(),
 | 
	
		
			
				|  |  | +      this.scene.getElements(),
 | 
	
		
			
				|  |  |        this.state,
 | 
	
		
			
				|  |  |      );
 | 
	
		
			
				|  |  |      const [minX, minY, maxX, maxY] = getCommonBounds(selectedElements);
 | 
	
	
		
			
				|  | @@ -2377,7 +2376,7 @@ class App extends React.Component<ExcalidrawProps, AppState> {
 | 
	
		
			
				|  |  |      pointerDownState: PointerDownState,
 | 
	
		
			
				|  |  |    ): boolean => {
 | 
	
		
			
				|  |  |      if (this.state.elementType === "selection") {
 | 
	
		
			
				|  |  | -      const elements = globalSceneState.getElements();
 | 
	
		
			
				|  |  | +      const elements = this.scene.getElements();
 | 
	
		
			
				|  |  |        const selectedElements = getSelectedElements(elements, this.state);
 | 
	
		
			
				|  |  |        if (selectedElements.length === 1 && !this.state.editingLinearElement) {
 | 
	
		
			
				|  |  |          const elementWithResizeHandler = getElementWithResizeHandler(
 | 
	
	
		
			
				|  | @@ -2491,12 +2490,12 @@ class App extends React.Component<ExcalidrawProps, AppState> {
 | 
	
		
			
				|  |  |                      [hitElement!.id]: true,
 | 
	
		
			
				|  |  |                    },
 | 
	
		
			
				|  |  |                  },
 | 
	
		
			
				|  |  | -                globalSceneState.getElements(),
 | 
	
		
			
				|  |  | +                this.scene.getElements(),
 | 
	
		
			
				|  |  |                );
 | 
	
		
			
				|  |  |              });
 | 
	
		
			
				|  |  |              // TODO: this is strange...
 | 
	
		
			
				|  |  | -            globalSceneState.replaceAllElements(
 | 
	
		
			
				|  |  | -              globalSceneState.getElementsIncludingDeleted(),
 | 
	
		
			
				|  |  | +            this.scene.replaceAllElements(
 | 
	
		
			
				|  |  | +              this.scene.getElementsIncludingDeleted(),
 | 
	
		
			
				|  |  |              );
 | 
	
		
			
				|  |  |              pointerDownState.hit.wasAddedToSelection = true;
 | 
	
		
			
				|  |  |            }
 | 
	
	
		
			
				|  | @@ -2610,8 +2609,8 @@ class App extends React.Component<ExcalidrawProps, AppState> {
 | 
	
		
			
				|  |  |        mutateElement(element, {
 | 
	
		
			
				|  |  |          points: [...element.points, [0, 0]],
 | 
	
		
			
				|  |  |        });
 | 
	
		
			
				|  |  | -      globalSceneState.replaceAllElements([
 | 
	
		
			
				|  |  | -        ...globalSceneState.getElementsIncludingDeleted(),
 | 
	
		
			
				|  |  | +      this.scene.replaceAllElements([
 | 
	
		
			
				|  |  | +        ...this.scene.getElementsIncludingDeleted(),
 | 
	
		
			
				|  |  |          element,
 | 
	
		
			
				|  |  |        ]);
 | 
	
		
			
				|  |  |        this.setState({
 | 
	
	
		
			
				|  | @@ -2649,8 +2648,8 @@ class App extends React.Component<ExcalidrawProps, AppState> {
 | 
	
		
			
				|  |  |          draggingElement: element,
 | 
	
		
			
				|  |  |        });
 | 
	
		
			
				|  |  |      } else {
 | 
	
		
			
				|  |  | -      globalSceneState.replaceAllElements([
 | 
	
		
			
				|  |  | -        ...globalSceneState.getElementsIncludingDeleted(),
 | 
	
		
			
				|  |  | +      this.scene.replaceAllElements([
 | 
	
		
			
				|  |  | +        ...this.scene.getElementsIncludingDeleted(),
 | 
	
		
			
				|  |  |          element,
 | 
	
		
			
				|  |  |        ]);
 | 
	
		
			
				|  |  |        this.setState({
 | 
	
	
		
			
				|  | @@ -2672,7 +2671,7 @@ class App extends React.Component<ExcalidrawProps, AppState> {
 | 
	
		
			
				|  |  |        if (pointerDownState.drag.offset === null) {
 | 
	
		
			
				|  |  |          pointerDownState.drag.offset = tupleToCoors(
 | 
	
		
			
				|  |  |            getDragOffsetXY(
 | 
	
		
			
				|  |  | -            getSelectedElements(globalSceneState.getElements(), this.state),
 | 
	
		
			
				|  |  | +            getSelectedElements(this.scene.getElements(), this.state),
 | 
	
		
			
				|  |  |              pointerDownState.origin.x,
 | 
	
		
			
				|  |  |              pointerDownState.origin.y,
 | 
	
		
			
				|  |  |            ),
 | 
	
	
		
			
				|  | @@ -2735,7 +2734,7 @@ class App extends React.Component<ExcalidrawProps, AppState> {
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |        if (pointerDownState.resize.isResizing) {
 | 
	
		
			
				|  |  |          const selectedElements = getSelectedElements(
 | 
	
		
			
				|  |  | -          globalSceneState.getElements(),
 | 
	
		
			
				|  |  | +          this.scene.getElements(),
 | 
	
		
			
				|  |  |            this.state,
 | 
	
		
			
				|  |  |          );
 | 
	
		
			
				|  |  |          const resizeHandle = pointerDownState.resize.handle;
 | 
	
	
		
			
				|  | @@ -2796,7 +2795,7 @@ class App extends React.Component<ExcalidrawProps, AppState> {
 | 
	
		
			
				|  |  |          // if elements should be deselected on pointerup
 | 
	
		
			
				|  |  |          pointerDownState.drag.hasOccurred = true;
 | 
	
		
			
				|  |  |          const selectedElements = getSelectedElements(
 | 
	
		
			
				|  |  | -          globalSceneState.getElements(),
 | 
	
		
			
				|  |  | +          this.scene.getElements(),
 | 
	
		
			
				|  |  |            this.state,
 | 
	
		
			
				|  |  |          );
 | 
	
		
			
				|  |  |          if (selectedElements.length > 0) {
 | 
	
	
		
			
				|  | @@ -2818,7 +2817,7 @@ class App extends React.Component<ExcalidrawProps, AppState> {
 | 
	
		
			
				|  |  |              const nextElements = [];
 | 
	
		
			
				|  |  |              const elementsToAppend = [];
 | 
	
		
			
				|  |  |              const groupIdMap = new Map();
 | 
	
		
			
				|  |  | -            for (const element of globalSceneState.getElementsIncludingDeleted()) {
 | 
	
		
			
				|  |  | +            for (const element of this.scene.getElementsIncludingDeleted()) {
 | 
	
		
			
				|  |  |                if (
 | 
	
		
			
				|  |  |                  this.state.selectedElementIds[element.id] ||
 | 
	
		
			
				|  |  |                  // case: the state.selectedElementIds might not have been
 | 
	
	
		
			
				|  | @@ -2846,7 +2845,7 @@ class App extends React.Component<ExcalidrawProps, AppState> {
 | 
	
		
			
				|  |  |                  nextElements.push(element);
 | 
	
		
			
				|  |  |                }
 | 
	
		
			
				|  |  |              }
 | 
	
		
			
				|  |  | -            globalSceneState.replaceAllElements([
 | 
	
		
			
				|  |  | +            this.scene.replaceAllElements([
 | 
	
		
			
				|  |  |                ...nextElements,
 | 
	
		
			
				|  |  |                ...elementsToAppend,
 | 
	
		
			
				|  |  |              ]);
 | 
	
	
		
			
				|  | @@ -2925,7 +2924,7 @@ class App extends React.Component<ExcalidrawProps, AppState> {
 | 
	
		
			
				|  |  |        }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |        if (this.state.elementType === "selection") {
 | 
	
		
			
				|  |  | -        const elements = globalSceneState.getElements();
 | 
	
		
			
				|  |  | +        const elements = this.scene.getElements();
 | 
	
		
			
				|  |  |          if (!event.shiftKey && isSomeElementSelected(elements, this.state)) {
 | 
	
		
			
				|  |  |            this.setState({
 | 
	
		
			
				|  |  |              selectedElementIds: {},
 | 
	
	
		
			
				|  | @@ -2949,7 +2948,7 @@ class App extends React.Component<ExcalidrawProps, AppState> {
 | 
	
		
			
				|  |  |                  }, {} as any),
 | 
	
		
			
				|  |  |                },
 | 
	
		
			
				|  |  |              },
 | 
	
		
			
				|  |  | -            globalSceneState.getElements(),
 | 
	
		
			
				|  |  | +            this.scene.getElements(),
 | 
	
		
			
				|  |  |            ),
 | 
	
		
			
				|  |  |          );
 | 
	
		
			
				|  |  |        }
 | 
	
	
		
			
				|  | @@ -3065,8 +3064,8 @@ class App extends React.Component<ExcalidrawProps, AppState> {
 | 
	
		
			
				|  |  |          isInvisiblySmallElement(draggingElement)
 | 
	
		
			
				|  |  |        ) {
 | 
	
		
			
				|  |  |          // remove invisible element which was added in onPointerDown
 | 
	
		
			
				|  |  | -        globalSceneState.replaceAllElements(
 | 
	
		
			
				|  |  | -          globalSceneState.getElementsIncludingDeleted().slice(0, -1),
 | 
	
		
			
				|  |  | +        this.scene.replaceAllElements(
 | 
	
		
			
				|  |  | +          this.scene.getElementsIncludingDeleted().slice(0, -1),
 | 
	
		
			
				|  |  |          );
 | 
	
		
			
				|  |  |          this.setState({
 | 
	
		
			
				|  |  |            draggingElement: null,
 | 
	
	
		
			
				|  | @@ -3086,8 +3085,8 @@ class App extends React.Component<ExcalidrawProps, AppState> {
 | 
	
		
			
				|  |  |        }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |        if (resizingElement && isInvisiblySmallElement(resizingElement)) {
 | 
	
		
			
				|  |  | -        globalSceneState.replaceAllElements(
 | 
	
		
			
				|  |  | -          globalSceneState
 | 
	
		
			
				|  |  | +        this.scene.replaceAllElements(
 | 
	
		
			
				|  |  | +          this.scene
 | 
	
		
			
				|  |  |              .getElementsIncludingDeleted()
 | 
	
		
			
				|  |  |              .filter((el) => el.id !== resizingElement.id),
 | 
	
		
			
				|  |  |          );
 | 
	
	
		
			
				|  | @@ -3143,7 +3142,7 @@ class App extends React.Component<ExcalidrawProps, AppState> {
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |        if (
 | 
	
		
			
				|  |  |          elementType !== "selection" ||
 | 
	
		
			
				|  |  | -        isSomeElementSelected(globalSceneState.getElements(), this.state)
 | 
	
		
			
				|  |  | +        isSomeElementSelected(this.scene.getElements(), this.state)
 | 
	
		
			
				|  |  |        ) {
 | 
	
		
			
				|  |  |          history.resumeRecording();
 | 
	
		
			
				|  |  |        }
 | 
	
	
		
			
				|  | @@ -3283,7 +3282,7 @@ class App extends React.Component<ExcalidrawProps, AppState> {
 | 
	
		
			
				|  |  |        window.devicePixelRatio,
 | 
	
		
			
				|  |  |      );
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    const elements = globalSceneState.getElements();
 | 
	
		
			
				|  |  | +    const elements = this.scene.getElements();
 | 
	
		
			
				|  |  |      const element = getElementAtPosition(
 | 
	
		
			
				|  |  |        elements,
 | 
	
		
			
				|  |  |        this.state,
 | 
	
	
		
			
				|  | @@ -3409,7 +3408,7 @@ class App extends React.Component<ExcalidrawProps, AppState> {
 | 
	
		
			
				|  |  |      scale: number,
 | 
	
		
			
				|  |  |    ) {
 | 
	
		
			
				|  |  |      const elementClickedInside = getElementContainingPosition(
 | 
	
		
			
				|  |  | -      globalSceneState
 | 
	
		
			
				|  |  | +      this.scene
 | 
	
		
			
				|  |  |          .getElementsIncludingDeleted()
 | 
	
		
			
				|  |  |          .filter((element) => !isTextElement(element)),
 | 
	
		
			
				|  |  |        x,
 | 
	
	
		
			
				|  | @@ -3467,10 +3466,7 @@ class App extends React.Component<ExcalidrawProps, AppState> {
 | 
	
		
			
				|  |  |    }, 300);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |    private saveDebounced = debounce(() => {
 | 
	
		
			
				|  |  | -    saveToLocalStorage(
 | 
	
		
			
				|  |  | -      globalSceneState.getElementsIncludingDeleted(),
 | 
	
		
			
				|  |  | -      this.state,
 | 
	
		
			
				|  |  | -    );
 | 
	
		
			
				|  |  | +    saveToLocalStorage(this.scene.getElementsIncludingDeleted(), this.state);
 | 
	
		
			
				|  |  |    }, 300);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |    private getCanvasOffsets() {
 | 
	
	
		
			
				|  | @@ -3515,10 +3511,10 @@ if (
 | 
	
		
			
				|  |  |    Object.defineProperties(window.h, {
 | 
	
		
			
				|  |  |      elements: {
 | 
	
		
			
				|  |  |        get() {
 | 
	
		
			
				|  |  | -        return globalSceneState.getElementsIncludingDeleted();
 | 
	
		
			
				|  |  | +        return this.app.scene.getElementsIncludingDeleted();
 | 
	
		
			
				|  |  |        },
 | 
	
		
			
				|  |  |        set(elements: ExcalidrawElement[]) {
 | 
	
		
			
				|  |  | -        return globalSceneState.replaceAllElements(elements);
 | 
	
		
			
				|  |  | +        return this.app.scene.replaceAllElements(elements);
 | 
	
		
			
				|  |  |        },
 | 
	
		
			
				|  |  |      },
 | 
	
		
			
				|  |  |      history: {
 |