From 6ec259a0e71174651bae95d4628138bf6fd68742 Mon Sep 17 00:00:00 2001 From: kj_sh604 Date: Sun, 15 Mar 2026 16:19:35 -0400 Subject: refactor: packages/ --- packages/excalidraw/data/filesystem.ts | 104 +++++++++++++++++++++++++++++++++ 1 file changed, 104 insertions(+) create mode 100644 packages/excalidraw/data/filesystem.ts (limited to 'packages/excalidraw/data/filesystem.ts') diff --git a/packages/excalidraw/data/filesystem.ts b/packages/excalidraw/data/filesystem.ts new file mode 100644 index 0000000..186d587 --- /dev/null +++ b/packages/excalidraw/data/filesystem.ts @@ -0,0 +1,104 @@ +import type { FileSystemHandle } from "browser-fs-access"; +import { + fileOpen as _fileOpen, + fileSave as _fileSave, + supported as nativeFileSystemSupported, +} from "browser-fs-access"; +import { EVENT, MIME_TYPES } from "../constants"; +import { AbortError } from "../errors"; +import { debounce } from "../utils"; + +type FILE_EXTENSION = Exclude; + +const INPUT_CHANGE_INTERVAL_MS = 500; + +export const fileOpen = (opts: { + extensions?: FILE_EXTENSION[]; + description: string; + multiple?: M; +}): Promise => { + // an unsafe TS hack, alas not much we can do AFAIK + type RetType = M extends false | undefined ? File : File[]; + + const mimeTypes = opts.extensions?.reduce((mimeTypes, type) => { + mimeTypes.push(MIME_TYPES[type]); + + return mimeTypes; + }, [] as string[]); + + const extensions = opts.extensions?.reduce((acc, ext) => { + if (ext === "jpg") { + return acc.concat(".jpg", ".jpeg"); + } + return acc.concat(`.${ext}`); + }, [] as string[]); + + return _fileOpen({ + description: opts.description, + extensions, + mimeTypes, + multiple: opts.multiple ?? false, + legacySetup: (resolve, reject, input) => { + const scheduleRejection = debounce(reject, INPUT_CHANGE_INTERVAL_MS); + const focusHandler = () => { + checkForFile(); + document.addEventListener(EVENT.KEYUP, scheduleRejection); + document.addEventListener(EVENT.POINTER_UP, scheduleRejection); + scheduleRejection(); + }; + const checkForFile = () => { + // this hack might not work when expecting multiple files + if (input.files?.length) { + const ret = opts.multiple ? [...input.files] : input.files[0]; + resolve(ret as RetType); + } + }; + requestAnimationFrame(() => { + window.addEventListener(EVENT.FOCUS, focusHandler); + }); + const interval = window.setInterval(() => { + checkForFile(); + }, INPUT_CHANGE_INTERVAL_MS); + return (rejectPromise) => { + clearInterval(interval); + scheduleRejection.cancel(); + window.removeEventListener(EVENT.FOCUS, focusHandler); + document.removeEventListener(EVENT.KEYUP, scheduleRejection); + document.removeEventListener(EVENT.POINTER_UP, scheduleRejection); + if (rejectPromise) { + // so that something is shown in console if we need to debug this + console.warn("Opening the file was canceled (legacy-fs)."); + rejectPromise(new AbortError()); + } + }; + }, + }) as Promise; +}; + +export const fileSave = ( + blob: Blob | Promise, + opts: { + /** supply without the extension */ + name: string; + /** file extension */ + extension: FILE_EXTENSION; + mimeTypes?: string[]; + description: string; + /** existing FileSystemHandle */ + fileHandle?: FileSystemHandle | null; + }, +) => { + return _fileSave( + blob, + { + fileName: `${opts.name}.${opts.extension}`, + description: opts.description, + extensions: [`.${opts.extension}`], + mimeTypes: opts.mimeTypes, + }, + opts.fileHandle, + ); +}; + +export { nativeFileSystemSupported }; +export type { FileSystemHandle }; -- cgit v1.2.3