Browse Source

fileHandle refactor & fixes (#2252)

David Luzar 4 years ago
parent
commit
1484c5a63b

+ 3 - 3
package-lock.json

@@ -4686,9 +4686,9 @@
       "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8="
     },
     "browser-nativefs": {
-      "version": "0.10.3",
-      "resolved": "https://registry.npmjs.org/browser-nativefs/-/browser-nativefs-0.10.3.tgz",
-      "integrity": "sha512-WGcoR1aR+bxLlilaJ9fIzVOgSC4MaV+6phCTDGXV1sm+RElFuQFnJXR4BebBf2wEnjCsmGmTDNDTBE5KvVm2UQ=="
+      "version": "0.11.0",
+      "resolved": "https://registry.npmjs.org/browser-nativefs/-/browser-nativefs-0.11.0.tgz",
+      "integrity": "sha512-JsCiw8DeZ5gB59i81e0O0pFSDK124hRoaYR7teNnuvIFVZRXa7vYJStwZxZAJS1tlmVdu5uuC6eIARskyG/IuQ=="
     },
     "browser-process-hrtime": {
       "version": "1.0.0",

+ 1 - 1
package.json

@@ -28,7 +28,7 @@
     "@types/react": "16.9.52",
     "@types/react-dom": "16.9.8",
     "@types/socket.io-client": "1.4.34",
-    "browser-nativefs": "0.10.3",
+    "browser-nativefs": "0.11.0",
     "firebase": "7.23.0",
     "i18next-browser-languagedetector": "6.0.1",
     "lodash.throttle": "4.1.1",

+ 0 - 2
src/actions/actionCanvas.tsx

@@ -59,8 +59,6 @@ export const actionClearCanvas = register({
       showAriaLabel={useIsMobile()}
       onClick={() => {
         if (window.confirm(t("alerts.clearReset"))) {
-          // TODO: Make this part of `AppState`.
-          (window as any).handle = null;
           updateData(null);
         }
       }}

+ 23 - 11
src/actions/actionExport.tsx

@@ -85,12 +85,16 @@ export const actionChangeShouldAddWatermark = register({
 
 export const actionSaveScene = register({
   name: "saveScene",
-  perform: (elements, appState, value) => {
-    // TODO: Make this part of `AppState`.
-    saveAsJSON(elements, appState, (window as any).handle)
-      .catch(muteFSAbortError)
-      .catch((error) => console.error(error));
-    return { commitToHistory: false };
+  perform: async (elements, appState, value) => {
+    try {
+      const { fileHandle } = await saveAsJSON(elements, appState);
+      return { commitToHistory: false, appState: { ...appState, fileHandle } };
+    } catch (error) {
+      if (error?.name !== "AbortError") {
+        console.error(error);
+      }
+      return { commitToHistory: false };
+    }
   },
   keyTest: (event) => {
     return event.key === "s" && event[KEYS.CTRL_OR_CMD] && !event.shiftKey;
@@ -109,11 +113,19 @@ export const actionSaveScene = register({
 
 export const actionSaveAsScene = register({
   name: "saveAsScene",
-  perform: (elements, appState, value) => {
-    saveAsJSON(elements, appState, null)
-      .catch(muteFSAbortError)
-      .catch((error) => console.error(error));
-    return { commitToHistory: false };
+  perform: async (elements, appState, value) => {
+    try {
+      const { fileHandle } = await saveAsJSON(elements, {
+        ...appState,
+        fileHandle: null,
+      });
+      return { commitToHistory: false, appState: { ...appState, fileHandle } };
+    } catch (error) {
+      if (error?.name !== "AbortError") {
+        console.error(error);
+      }
+      return { commitToHistory: false };
+    }
   },
   keyTest: (event) => {
     return event.key === "s" && event.shiftKey && event[KEYS.CTRL_OR_CMD];

+ 11 - 2
src/actions/manager.tsx

@@ -5,6 +5,7 @@ import {
   UpdaterFn,
   ActionFilterFn,
   ActionName,
+  ActionResult,
 } from "./types";
 import { ExcalidrawElement } from "../element/types";
 import { AppState } from "../types";
@@ -13,7 +14,7 @@ import { t } from "../i18n";
 export class ActionManager implements ActionsManagerInterface {
   actions = {} as ActionsManagerInterface["actions"];
 
-  updater: UpdaterFn;
+  updater: (actionResult: ActionResult | Promise<ActionResult>) => void;
 
   getAppState: () => Readonly<AppState>;
 
@@ -24,7 +25,15 @@ export class ActionManager implements ActionsManagerInterface {
     getAppState: () => AppState,
     getElementsIncludingDeleted: () => readonly ExcalidrawElement[],
   ) {
-    this.updater = updater;
+    this.updater = (actionResult) => {
+      if (actionResult && "then" in actionResult) {
+        actionResult.then((actionResult) => {
+          return updater(actionResult);
+        });
+      } else {
+        return updater(actionResult);
+      }
+    };
     this.getAppState = getAppState;
     this.getElementsIncludingDeleted = getElementsIncludingDeleted;
   }

+ 2 - 2
src/actions/types.ts

@@ -16,9 +16,9 @@ type ActionFn = (
   elements: readonly ExcalidrawElement[],
   appState: Readonly<AppState>,
   formData: any,
-) => ActionResult;
+) => ActionResult | Promise<ActionResult>;
 
-export type UpdaterFn = (res: ActionResult, commitToHistory?: boolean) => void;
+export type UpdaterFn = (res: ActionResult) => void;
 export type ActionFilterFn = (action: Action) => void;
 
 export type ActionName =

+ 2 - 0
src/appState.ts

@@ -69,6 +69,7 @@ export const getDefaultAppState = (): Omit<
     width: window.innerWidth,
     height: window.innerHeight,
     isLibraryOpen: false,
+    fileHandle: null,
   };
 };
 
@@ -145,6 +146,7 @@ const APP_STATE_STORAGE_CONF = (<
   zoom: { browser: true, export: false },
   offsetTop: { browser: false, export: false },
   offsetLeft: { browser: false, export: false },
+  fileHandle: { browser: false, export: false },
 });
 
 const _clearAppStateForStorage = <ExportType extends "export" | "browser">(

+ 1 - 1
src/clipboard.ts

@@ -160,7 +160,7 @@ export const parseClipboard = async (
 export const copyCanvasToClipboardAsPng = async (canvas: HTMLCanvasElement) =>
   new Promise((resolve, reject) => {
     try {
-      canvas.toBlob(async (blob: any) => {
+      canvas.toBlob(async (blob) => {
         try {
           await navigator.clipboard.write([
             new window.ClipboardItem({ "image/png": blob }),

+ 2 - 2
src/components/App.tsx

@@ -558,7 +558,7 @@ class App extends React.Component<ExcalidrawProps, AppState> {
             return;
           }
           const fileHandle = launchParams.files[0];
-          const blob = await fileHandle.getFile();
+          const blob: Blob = await fileHandle.getFile();
           blob.handle = fileHandle;
           loadFromBlob(blob, this.state)
             .then(({ elements, appState }) =>
@@ -3845,7 +3845,7 @@ class App extends React.Component<ExcalidrawProps, AppState> {
           // but can be safely ignored on older releases.
           const item = event.dataTransfer.items[0];
           // TODO: Make this part of `AppState`.
-          (window as any).handle = await (item as any).getAsFileSystemHandle();
+          (file as any).handle = await (item as any).getAsFileSystemHandle();
         } catch (error) {
           console.warn(error.name, error.message);
         }

+ 20 - 6
src/data/blob.ts

@@ -4,6 +4,7 @@ import { t } from "../i18n";
 import { AppState } from "../types";
 import { LibraryData, ImportedDataState } from "./types";
 import { calculateScrollCenter } from "../scene";
+import { MIME_TYPES } from "../constants";
 
 export const parseFileContents = async (blob: Blob | File) => {
   let contents: string;
@@ -53,16 +54,22 @@ export const parseFileContents = async (blob: Blob | File) => {
   return contents;
 };
 
+const getMimeType = (blob: Blob): string => {
+  if (blob.type) {
+    return blob.type;
+  }
+  const name = blob.name || "";
+  if (/\.(excalidraw|json)$/.test(name)) {
+    return "application/json";
+  }
+  return "";
+};
+
 export const loadFromBlob = async (
-  blob: any,
+  blob: Blob,
   /** @see restore.localAppState */
   localAppState: AppState | null,
 ) => {
-  if (blob.handle) {
-    // TODO: Make this part of `AppState`.
-    (window as any).handle = blob.handle;
-  }
-
   const contents = await parseFileContents(blob);
   try {
     const data: ImportedDataState = JSON.parse(contents);
@@ -74,6 +81,13 @@ export const loadFromBlob = async (
         elements: data.elements,
         appState: {
           appearance: localAppState?.appearance,
+          fileHandle:
+            blob.handle &&
+            ["application/json", MIME_TYPES.excalidraw].includes(
+              getMimeType(blob),
+            )
+              ? blob.handle
+              : null,
           ...cleanAppStateForExport(data.appState || {}),
           ...(localAppState
             ? calculateScrollCenter(data.elements || [], localAppState, null)

+ 0 - 3
src/data/index.ts

@@ -66,9 +66,6 @@ export type SocketUpdateDataIncoming =
       type: "INVALID_RESPONSE";
     };
 
-// TODO: Make this part of `AppState`.
-(window as any).handle = null;
-
 const byteToHex = (byte: number): string => `0${byte.toString(16)}`.slice(-2);
 
 const generateRandomID = async () => {

+ 6 - 6
src/data/json.ts

@@ -27,23 +27,23 @@ export const serializeAsJSON = (
 export const saveAsJSON = async (
   elements: readonly ExcalidrawElement[],
   appState: AppState,
-  fileHandle: any,
 ) => {
   const serialized = serializeAsJSON(elements, appState);
   const blob = new Blob([serialized], {
     type: "application/json",
   });
-  const name = `${appState.name}.excalidraw`;
-  // TODO: Make this part of `AppState`.
-  (window as any).handle = await fileSave(
+
+  const fileHandle = await fileSave(
     blob,
     {
-      fileName: name,
+      fileName: appState.name,
       description: "Excalidraw file",
       extensions: [".excalidraw"],
     },
-    fileHandle || null,
+    appState.fileHandle,
   );
+
+  return { fileHandle };
 };
 
 export const loadFromJSON = async (localAppState: AppState) => {

+ 1 - 1
src/data/library.ts

@@ -4,7 +4,7 @@ import { loadLibrary, saveLibrary } from "./localStorage";
 
 export class Library {
   /** imports library (currently merges, removing duplicates) */
-  static async importLibrary(blob: any) {
+  static async importLibrary(blob: Blob) {
     const libraryFile = await loadLibraryFromBlob(blob);
     if (!libraryFile || !libraryFile.library) {
       return;

+ 7 - 0
src/global.d.ts

@@ -76,3 +76,10 @@ type CallableType<T extends (...args: any[]) => any> = (
 type ForwardRef<T, P = any> = Parameters<
   CallableType<React.ForwardRefRenderFunction<T, P>>
 >[1];
+
+// --------------------------------------------------------------------------—
+
+interface Blob {
+  handle?: import("browser-nativefs").FileSystemHandle;
+  name?: string;
+}

+ 0 - 1
src/scene/browser-native.d.ts

@@ -1 +0,0 @@
-declare module "browser-nativefs";

+ 83 - 0
src/tests/__snapshots__/regressionTests.test.tsx.snap

@@ -28,6 +28,7 @@ Object {
   "errorMessage": null,
   "exportBackground": true,
   "exportEmbedScene": false,
+  "fileHandle": null,
   "gridSize": null,
   "height": 768,
   "isBindingEnabled": true,
@@ -475,6 +476,7 @@ Object {
   "errorMessage": null,
   "exportBackground": true,
   "exportEmbedScene": false,
+  "fileHandle": null,
   "gridSize": null,
   "height": 768,
   "isBindingEnabled": true,
@@ -928,6 +930,7 @@ Object {
   "errorMessage": null,
   "exportBackground": true,
   "exportEmbedScene": false,
+  "fileHandle": null,
   "gridSize": null,
   "height": 768,
   "isBindingEnabled": false,
@@ -1690,6 +1693,7 @@ Object {
   "errorMessage": null,
   "exportBackground": true,
   "exportEmbedScene": false,
+  "fileHandle": null,
   "gridSize": null,
   "height": 768,
   "isBindingEnabled": true,
@@ -1880,6 +1884,7 @@ Object {
   "errorMessage": null,
   "exportBackground": true,
   "exportEmbedScene": false,
+  "fileHandle": null,
   "gridSize": null,
   "height": 768,
   "isBindingEnabled": true,
@@ -2324,6 +2329,7 @@ Object {
   "errorMessage": null,
   "exportBackground": true,
   "exportEmbedScene": false,
+  "fileHandle": null,
   "gridSize": null,
   "height": 768,
   "isBindingEnabled": true,
@@ -2563,6 +2569,7 @@ Object {
   "errorMessage": null,
   "exportBackground": true,
   "exportEmbedScene": false,
+  "fileHandle": null,
   "gridSize": null,
   "height": 768,
   "isBindingEnabled": true,
@@ -2713,6 +2720,7 @@ Object {
   "errorMessage": null,
   "exportBackground": true,
   "exportEmbedScene": false,
+  "fileHandle": null,
   "gridSize": null,
   "height": 768,
   "isBindingEnabled": true,
@@ -3176,6 +3184,7 @@ Object {
   "errorMessage": null,
   "exportBackground": true,
   "exportEmbedScene": false,
+  "fileHandle": null,
   "gridSize": null,
   "height": 768,
   "isBindingEnabled": true,
@@ -3470,6 +3479,7 @@ Object {
   "errorMessage": null,
   "exportBackground": true,
   "exportEmbedScene": false,
+  "fileHandle": null,
   "gridSize": null,
   "height": 768,
   "isBindingEnabled": true,
@@ -3660,6 +3670,7 @@ Object {
   "errorMessage": null,
   "exportBackground": true,
   "exportEmbedScene": false,
+  "fileHandle": null,
   "gridSize": null,
   "height": 768,
   "isBindingEnabled": true,
@@ -3890,6 +3901,7 @@ Object {
   "errorMessage": null,
   "exportBackground": true,
   "exportEmbedScene": false,
+  "fileHandle": null,
   "gridSize": null,
   "height": 768,
   "isBindingEnabled": true,
@@ -4128,6 +4140,7 @@ Object {
   "errorMessage": null,
   "exportBackground": true,
   "exportEmbedScene": false,
+  "fileHandle": null,
   "gridSize": null,
   "height": 768,
   "isBindingEnabled": true,
@@ -4497,6 +4510,7 @@ Object {
   "errorMessage": null,
   "exportBackground": true,
   "exportEmbedScene": false,
+  "fileHandle": null,
   "gridSize": null,
   "height": 768,
   "isBindingEnabled": true,
@@ -4778,6 +4792,7 @@ Object {
   "errorMessage": null,
   "exportBackground": true,
   "exportEmbedScene": false,
+  "fileHandle": null,
   "gridSize": null,
   "height": 768,
   "isBindingEnabled": true,
@@ -5071,6 +5086,7 @@ Object {
   "errorMessage": null,
   "exportBackground": true,
   "exportEmbedScene": false,
+  "fileHandle": null,
   "gridSize": null,
   "height": 768,
   "isBindingEnabled": true,
@@ -5265,6 +5281,7 @@ Object {
   "errorMessage": null,
   "exportBackground": true,
   "exportEmbedScene": false,
+  "fileHandle": null,
   "gridSize": null,
   "height": 768,
   "isBindingEnabled": true,
@@ -5415,6 +5432,7 @@ Object {
   "errorMessage": null,
   "exportBackground": true,
   "exportEmbedScene": false,
+  "fileHandle": null,
   "gridSize": null,
   "height": 768,
   "isBindingEnabled": true,
@@ -5854,6 +5872,7 @@ Object {
   "errorMessage": null,
   "exportBackground": true,
   "exportEmbedScene": false,
+  "fileHandle": null,
   "gridSize": null,
   "height": 768,
   "isBindingEnabled": true,
@@ -6158,6 +6177,7 @@ Object {
   "errorMessage": null,
   "exportBackground": true,
   "exportEmbedScene": false,
+  "fileHandle": null,
   "gridSize": null,
   "height": 768,
   "isBindingEnabled": true,
@@ -8124,6 +8144,7 @@ Object {
   "errorMessage": null,
   "exportBackground": true,
   "exportEmbedScene": false,
+  "fileHandle": null,
   "gridSize": null,
   "height": 768,
   "isBindingEnabled": true,
@@ -8472,6 +8493,7 @@ Object {
   "errorMessage": null,
   "exportBackground": true,
   "exportEmbedScene": false,
+  "fileHandle": null,
   "gridSize": null,
   "height": 768,
   "isBindingEnabled": true,
@@ -8713,6 +8735,7 @@ Object {
   "errorMessage": null,
   "exportBackground": true,
   "exportEmbedScene": false,
+  "fileHandle": null,
   "gridSize": null,
   "height": 768,
   "isBindingEnabled": true,
@@ -8952,6 +8975,7 @@ Object {
   "errorMessage": null,
   "exportBackground": true,
   "exportEmbedScene": false,
+  "fileHandle": null,
   "gridSize": null,
   "height": 768,
   "isBindingEnabled": true,
@@ -9253,6 +9277,7 @@ Object {
   "errorMessage": null,
   "exportBackground": true,
   "exportEmbedScene": false,
+  "fileHandle": null,
   "gridSize": null,
   "height": 768,
   "isBindingEnabled": true,
@@ -9403,6 +9428,7 @@ Object {
   "errorMessage": null,
   "exportBackground": true,
   "exportEmbedScene": false,
+  "fileHandle": null,
   "gridSize": null,
   "height": 768,
   "isBindingEnabled": true,
@@ -9553,6 +9579,7 @@ Object {
   "errorMessage": null,
   "exportBackground": true,
   "exportEmbedScene": false,
+  "fileHandle": null,
   "gridSize": null,
   "height": 768,
   "isBindingEnabled": true,
@@ -9703,6 +9730,7 @@ Object {
   "errorMessage": null,
   "exportBackground": true,
   "exportEmbedScene": false,
+  "fileHandle": null,
   "gridSize": null,
   "height": 768,
   "isBindingEnabled": true,
@@ -9879,6 +9907,7 @@ Object {
   "errorMessage": null,
   "exportBackground": true,
   "exportEmbedScene": false,
+  "fileHandle": null,
   "gridSize": null,
   "height": 768,
   "isBindingEnabled": true,
@@ -10055,6 +10084,7 @@ Object {
   "errorMessage": null,
   "exportBackground": true,
   "exportEmbedScene": false,
+  "fileHandle": null,
   "gridSize": null,
   "height": 768,
   "isBindingEnabled": true,
@@ -10231,6 +10261,7 @@ Object {
   "errorMessage": null,
   "exportBackground": true,
   "exportEmbedScene": false,
+  "fileHandle": null,
   "gridSize": null,
   "height": 768,
   "isBindingEnabled": true,
@@ -10407,6 +10438,7 @@ Object {
   "errorMessage": null,
   "exportBackground": true,
   "exportEmbedScene": false,
+  "fileHandle": null,
   "gridSize": null,
   "height": 768,
   "isBindingEnabled": true,
@@ -10557,6 +10589,7 @@ Object {
   "errorMessage": null,
   "exportBackground": true,
   "exportEmbedScene": false,
+  "fileHandle": null,
   "gridSize": null,
   "height": 768,
   "isBindingEnabled": true,
@@ -10707,6 +10740,7 @@ Object {
   "errorMessage": null,
   "exportBackground": true,
   "exportEmbedScene": false,
+  "fileHandle": null,
   "gridSize": null,
   "height": 768,
   "isBindingEnabled": true,
@@ -10883,6 +10917,7 @@ Object {
   "errorMessage": null,
   "exportBackground": true,
   "exportEmbedScene": false,
+  "fileHandle": null,
   "gridSize": null,
   "height": 768,
   "isBindingEnabled": true,
@@ -11033,6 +11068,7 @@ Object {
   "errorMessage": null,
   "exportBackground": true,
   "exportEmbedScene": false,
+  "fileHandle": null,
   "gridSize": null,
   "height": 768,
   "isBindingEnabled": true,
@@ -11209,6 +11245,7 @@ Object {
   "errorMessage": null,
   "exportBackground": true,
   "exportEmbedScene": false,
+  "fileHandle": null,
   "gridSize": null,
   "height": 768,
   "isBindingEnabled": true,
@@ -11911,6 +11948,7 @@ Object {
   "errorMessage": null,
   "exportBackground": true,
   "exportEmbedScene": false,
+  "fileHandle": null,
   "gridSize": null,
   "height": 768,
   "isBindingEnabled": true,
@@ -12150,6 +12188,7 @@ Object {
   "errorMessage": null,
   "exportBackground": true,
   "exportEmbedScene": false,
+  "fileHandle": null,
   "gridSize": null,
   "height": 768,
   "isBindingEnabled": true,
@@ -12238,6 +12277,7 @@ Object {
   "errorMessage": null,
   "exportBackground": true,
   "exportEmbedScene": false,
+  "fileHandle": null,
   "gridSize": null,
   "height": 768,
   "isBindingEnabled": true,
@@ -12324,6 +12364,7 @@ Object {
   "errorMessage": null,
   "exportBackground": true,
   "exportEmbedScene": false,
+  "fileHandle": null,
   "gridSize": null,
   "height": 768,
   "isBindingEnabled": true,
@@ -13202,6 +13243,7 @@ Object {
   "errorMessage": null,
   "exportBackground": true,
   "exportEmbedScene": false,
+  "fileHandle": null,
   "gridSize": null,
   "height": 768,
   "isBindingEnabled": true,
@@ -13639,6 +13681,7 @@ Object {
   "errorMessage": null,
   "exportBackground": true,
   "exportEmbedScene": false,
+  "fileHandle": null,
   "gridSize": null,
   "height": 768,
   "isBindingEnabled": true,
@@ -13989,6 +14032,7 @@ Object {
   "errorMessage": null,
   "exportBackground": true,
   "exportEmbedScene": false,
+  "fileHandle": null,
   "gridSize": null,
   "height": 768,
   "isBindingEnabled": true,
@@ -14256,6 +14300,7 @@ Object {
   "errorMessage": null,
   "exportBackground": true,
   "exportEmbedScene": false,
+  "fileHandle": null,
   "gridSize": null,
   "height": 768,
   "isBindingEnabled": true,
@@ -14444,6 +14489,7 @@ Object {
   "errorMessage": null,
   "exportBackground": true,
   "exportEmbedScene": false,
+  "fileHandle": null,
   "gridSize": null,
   "height": 768,
   "isBindingEnabled": true,
@@ -15269,6 +15315,7 @@ Object {
   "errorMessage": null,
   "exportBackground": true,
   "exportEmbedScene": false,
+  "fileHandle": null,
   "gridSize": null,
   "height": 768,
   "isBindingEnabled": true,
@@ -15991,6 +16038,7 @@ Object {
   "errorMessage": null,
   "exportBackground": true,
   "exportEmbedScene": false,
+  "fileHandle": null,
   "gridSize": null,
   "height": 768,
   "isBindingEnabled": true,
@@ -16614,6 +16662,7 @@ Object {
   "errorMessage": null,
   "exportBackground": true,
   "exportEmbedScene": false,
+  "fileHandle": null,
   "gridSize": null,
   "height": 768,
   "isBindingEnabled": true,
@@ -17142,6 +17191,7 @@ Object {
   "errorMessage": null,
   "exportBackground": true,
   "exportEmbedScene": false,
+  "fileHandle": null,
   "gridSize": null,
   "height": 768,
   "isBindingEnabled": true,
@@ -17624,6 +17674,7 @@ Object {
   "errorMessage": null,
   "exportBackground": true,
   "exportEmbedScene": false,
+  "fileHandle": null,
   "gridSize": null,
   "height": 768,
   "isBindingEnabled": true,
@@ -18017,6 +18068,7 @@ Object {
   "errorMessage": null,
   "exportBackground": true,
   "exportEmbedScene": false,
+  "fileHandle": null,
   "gridSize": null,
   "height": 768,
   "isBindingEnabled": true,
@@ -18325,6 +18377,7 @@ Object {
   "errorMessage": null,
   "exportBackground": true,
   "exportEmbedScene": false,
+  "fileHandle": null,
   "gridSize": null,
   "height": 768,
   "isBindingEnabled": true,
@@ -18552,6 +18605,7 @@ Object {
   "errorMessage": null,
   "exportBackground": true,
   "exportEmbedScene": false,
+  "fileHandle": null,
   "gridSize": null,
   "height": 768,
   "isBindingEnabled": true,
@@ -19430,6 +19484,7 @@ Object {
   "errorMessage": null,
   "exportBackground": true,
   "exportEmbedScene": false,
+  "fileHandle": null,
   "gridSize": null,
   "height": 768,
   "isBindingEnabled": true,
@@ -20203,6 +20258,7 @@ Object {
   "errorMessage": null,
   "exportBackground": true,
   "exportEmbedScene": false,
+  "fileHandle": null,
   "gridSize": null,
   "height": 768,
   "isBindingEnabled": true,
@@ -20875,6 +20931,7 @@ Object {
   "errorMessage": null,
   "exportBackground": true,
   "exportEmbedScene": false,
+  "fileHandle": null,
   "gridSize": null,
   "height": 768,
   "isBindingEnabled": true,
@@ -21450,6 +21507,7 @@ Object {
   "errorMessage": null,
   "exportBackground": true,
   "exportEmbedScene": false,
+  "fileHandle": null,
   "gridSize": null,
   "height": 768,
   "isBindingEnabled": true,
@@ -21600,6 +21658,7 @@ Object {
   "errorMessage": null,
   "exportBackground": true,
   "exportEmbedScene": false,
+  "fileHandle": null,
   "gridSize": null,
   "height": 768,
   "isBindingEnabled": true,
@@ -21894,6 +21953,7 @@ Object {
   "errorMessage": null,
   "exportBackground": true,
   "exportEmbedScene": false,
+  "fileHandle": null,
   "gridSize": null,
   "height": 768,
   "isBindingEnabled": true,
@@ -22188,6 +22248,7 @@ Object {
   "errorMessage": null,
   "exportBackground": true,
   "exportEmbedScene": false,
+  "fileHandle": null,
   "gridSize": null,
   "height": 768,
   "isBindingEnabled": true,
@@ -22338,6 +22399,7 @@ Object {
   "errorMessage": null,
   "exportBackground": true,
   "exportEmbedScene": false,
+  "fileHandle": null,
   "gridSize": null,
   "height": 768,
   "isBindingEnabled": true,
@@ -22520,6 +22582,7 @@ Object {
   "errorMessage": null,
   "exportBackground": true,
   "exportEmbedScene": false,
+  "fileHandle": null,
   "gridSize": null,
   "height": 768,
   "isBindingEnabled": true,
@@ -22755,6 +22818,7 @@ Object {
   "errorMessage": null,
   "exportBackground": true,
   "exportEmbedScene": false,
+  "fileHandle": null,
   "gridSize": null,
   "height": 768,
   "isBindingEnabled": true,
@@ -23065,6 +23129,7 @@ Object {
   "errorMessage": null,
   "exportBackground": true,
   "exportEmbedScene": false,
+  "fileHandle": null,
   "gridSize": null,
   "height": 768,
   "isBindingEnabled": true,
@@ -23890,6 +23955,7 @@ Object {
   "errorMessage": null,
   "exportBackground": true,
   "exportEmbedScene": false,
+  "fileHandle": null,
   "gridSize": null,
   "height": 768,
   "isBindingEnabled": true,
@@ -24184,6 +24250,7 @@ Object {
   "errorMessage": null,
   "exportBackground": true,
   "exportEmbedScene": false,
+  "fileHandle": null,
   "gridSize": null,
   "height": 768,
   "isBindingEnabled": true,
@@ -24478,6 +24545,7 @@ Object {
   "errorMessage": null,
   "exportBackground": true,
   "exportEmbedScene": false,
+  "fileHandle": null,
   "gridSize": null,
   "height": 768,
   "isBindingEnabled": true,
@@ -24843,6 +24911,7 @@ Object {
   "errorMessage": null,
   "exportBackground": true,
   "exportEmbedScene": false,
+  "fileHandle": null,
   "gridSize": null,
   "height": 768,
   "isBindingEnabled": true,
@@ -24996,6 +25065,7 @@ Object {
   "errorMessage": null,
   "exportBackground": true,
   "exportEmbedScene": false,
+  "fileHandle": null,
   "gridSize": null,
   "height": 768,
   "isBindingEnabled": true,
@@ -25303,6 +25373,7 @@ Object {
   "errorMessage": null,
   "exportBackground": true,
   "exportEmbedScene": false,
+  "fileHandle": null,
   "gridSize": null,
   "height": 768,
   "isBindingEnabled": true,
@@ -25544,6 +25615,7 @@ Object {
   "errorMessage": null,
   "exportBackground": true,
   "exportEmbedScene": false,
+  "fileHandle": null,
   "gridSize": null,
   "height": 768,
   "isBindingEnabled": true,
@@ -25857,6 +25929,7 @@ Object {
   "errorMessage": null,
   "exportBackground": true,
   "exportEmbedScene": false,
+  "fileHandle": null,
   "gridSize": null,
   "height": 768,
   "isBindingEnabled": true,
@@ -25943,6 +26016,7 @@ Object {
   "errorMessage": null,
   "exportBackground": true,
   "exportEmbedScene": false,
+  "fileHandle": null,
   "gridSize": null,
   "height": 768,
   "isBindingEnabled": true,
@@ -26093,6 +26167,7 @@ Object {
   "errorMessage": null,
   "exportBackground": true,
   "exportEmbedScene": false,
+  "fileHandle": null,
   "gridSize": null,
   "height": 768,
   "isBindingEnabled": true,
@@ -26900,6 +26975,7 @@ Object {
   "errorMessage": null,
   "exportBackground": true,
   "exportEmbedScene": false,
+  "fileHandle": null,
   "gridSize": null,
   "height": 768,
   "isBindingEnabled": true,
@@ -26986,6 +27062,7 @@ Object {
   "errorMessage": null,
   "exportBackground": true,
   "exportEmbedScene": false,
+  "fileHandle": null,
   "gridSize": null,
   "height": 768,
   "isBindingEnabled": true,
@@ -27724,6 +27801,7 @@ Object {
   "errorMessage": null,
   "exportBackground": true,
   "exportEmbedScene": false,
+  "fileHandle": null,
   "gridSize": null,
   "height": 768,
   "isBindingEnabled": true,
@@ -28115,6 +28193,7 @@ Object {
   "errorMessage": null,
   "exportBackground": true,
   "exportEmbedScene": false,
+  "fileHandle": null,
   "gridSize": null,
   "height": 768,
   "isBindingEnabled": true,
@@ -28374,6 +28453,7 @@ Object {
   "errorMessage": null,
   "exportBackground": true,
   "exportEmbedScene": false,
+  "fileHandle": null,
   "gridSize": null,
   "height": 768,
   "isBindingEnabled": true,
@@ -28462,6 +28542,7 @@ Object {
   "errorMessage": null,
   "exportBackground": true,
   "exportEmbedScene": false,
+  "fileHandle": null,
   "gridSize": null,
   "height": 768,
   "isBindingEnabled": true,
@@ -28940,6 +29021,7 @@ Object {
   "errorMessage": null,
   "exportBackground": true,
   "exportEmbedScene": false,
+  "fileHandle": null,
   "gridSize": null,
   "height": 768,
   "isBindingEnabled": true,
@@ -29026,6 +29108,7 @@ Object {
   "errorMessage": null,
   "exportBackground": true,
   "exportEmbedScene": false,
+  "fileHandle": null,
   "gridSize": null,
   "height": 768,
   "isBindingEnabled": true,

+ 1 - 0
src/types.ts

@@ -96,6 +96,7 @@ export type AppState = {
   offsetLeft: number;
 
   isLibraryOpen: boolean;
+  fileHandle: import("browser-nativefs").FileSystemHandle | null;
 };
 
 export type PointerCoords = Readonly<{