Repository: excalidraw/excalidraw-blog Branch: master Commit: 083a4277a924 Files: 43 Total size: 175.7 KB Directory structure: gitextract_g20e0di6/ ├── .github/ │ ├── dependabot.yml │ └── workflows/ │ └── test.yml ├── .gitignore ├── .prettierignore ├── .vscode/ │ └── settings.json ├── .yarnrc ├── LICENSE ├── README.md ├── content/ │ └── blog/ │ ├── browser-fs-access/ │ │ └── index.md │ ├── building-excalidraw-p2p-collaboration-feature/ │ │ └── index.md │ ├── deprecating-excalidraw-electron/ │ │ └── index.md │ ├── enabling-translations/ │ │ └── index.md │ ├── end-to-end-encryption/ │ │ └── index.md │ ├── excalidraw-and-fugu/ │ │ └── index.md │ ├── introducing-excalidraw-plus/ │ │ └── index.md │ ├── one-year-of-excalidraw/ │ │ └── index.md │ ├── open-colors/ │ │ └── index.md │ ├── redesigning-editor-api/ │ │ └── index.md │ ├── reflections-on-excalidraw/ │ │ └── index.md │ ├── rethinking-virtual-whiteboard/ │ │ └── index.md │ ├── tell-your-story-with-charts/ │ │ └── index.md │ ├── webex-meetings-integration/ │ │ └── index.md │ ├── year-three/ │ │ └── index.md │ └── year-two/ │ └── index.md ├── gatsby-browser.js ├── gatsby-config.js ├── gatsby-node.js ├── package.json ├── src/ │ ├── blog.css │ ├── code.css │ ├── components/ │ │ ├── Toggle.css │ │ ├── Toggle.js │ │ ├── excalidraw.js │ │ ├── layout.js │ │ ├── layoutStyles.css │ │ └── seo.js │ ├── pages/ │ │ ├── 404.js │ │ └── index.js │ ├── styles.css │ ├── templates/ │ │ └── blog-post.js │ └── utils/ │ └── typography.js ├── static/ │ └── robots.txt └── vercel.json ================================================ FILE CONTENTS ================================================ ================================================ FILE: .github/dependabot.yml ================================================ version: 2 updates: - package-ecosystem: npm directory: / schedule: interval: weekly day: sunday time: "01:00" ================================================ FILE: .github/workflows/test.yml ================================================ name: Test formatting on: push: branches: - master pull_request: jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v1 - name: Install and test run: | yarn yarn test ================================================ FILE: .gitignore ================================================ .cache .DS_Store .yarn-integrity *.log node_modules npm-debug.log* package-lock.json public yarn-debug.log* yarn-error.log yarn-error.log* ================================================ FILE: .prettierignore ================================================ .cache public ================================================ FILE: .vscode/settings.json ================================================ { "cSpell.enabled": true } ================================================ FILE: .yarnrc ================================================ --add.exact true ================================================ FILE: LICENSE ================================================ The MIT License (MIT) Copyright (c) 2020 Excalidraw Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================ # Excalidraw Blog > For news and updates visit: https://blog.excalidraw.com. ## Develop We are using [Gatsby](https://www.gatsbyjs.com/) and in order to run it locally, execute the following from the root: ``` yarn yarn start ``` Visit [`localhost:8000`](http://localhost:8000) to test it. ## Writing a Blog Post - Create a new folder inside the `content/blog/` - The folder name should follow `kebab-case` - Use the slug of the title of the post to name it - Create `index.md` inside that folder - Create `og.png` inside that folder for the open graph image - Add a frontmatter - `title`: Use Title Case for Titles - `date`: Date in ISO format. Example: `2020-03-12` - `note`: Optional. Displayed next to the date when reading a post. - `image`: Filename of the open graph image. Example: `og.png` - The `note` field is usually used to link to the original post when reposting ([example](https://blog.excalidraw.com/reflections-on-excalidraw/)) - Add somewhere the `` to declare your `excerpt` (it's used on the front page) ================================================ FILE: content/blog/browser-fs-access/index.md ================================================ --- title: Reading and writing files and directories with the browser-fs-access library date: 2020-12-09 note: 'This post appeared first on web.dev.' author: tomayac link: https://twitter.com/tomayac image: chrome-save-as.png --- Browsers have been able to deal with files and directories for a long time. The [File API](https://w3c.github.io/FileAPI/) provides features for representing file objects in web applications, as well as programmatically selecting them and accessing their data. The moment you look closer, though, all that glitters is not gold. ## The traditional way of dealing with files
If you know how it used to work the old way, you can jump down straight to the new way.### Opening files As a developer, you can open and read files via the [``](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/file) element. In its simplest form, opening a file can look something like the code sample below. The `input` object gives you a [`FileList`](https://developer.mozilla.org/en-US/docs/Web/API/FileList), which in the case below consists of just one [`File`](https://developer.mozilla.org/en-US/docs/Web/API/File). A `File` is a specific kind of [`Blob`](https://developer.mozilla.org/en-US/docs/Web/API/Blob), and can be used in any context that a Blob can. ```js const openFile = async () => { return new Promise((resolve) => { const input = document.createElement("input"); input.type = "file"; input.addEventListener("change", () => { resolve(input.files[0]); }); input.click(); }); }; ``` ### Opening directories For opening folders (or directories), you can set the [``](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#attr-webkitdirectory) attribute. Apart from that, everything else works the same as above. Despite its vendor-prefixed name, `webkitdirectory` is not only usable in Chromium and WebKit browsers, but also in the legacy EdgeHTML-based Edge as well as in Firefox. ### Saving (rather: downloading) files For saving a file, traditionally, you are limited to _downloading_ a file, which works thanks to the [``](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a#attr-download:~:text=download) attribute. Given a Blob, you can set the anchor's `href` attribute to a `blob:` URL that you can get from the [`URL.createObjectURL()`](https://developer.mozilla.org/en-US/docs/Web/API/URL/createObjectURL) method.
To prevent memory leaks, always revoke the URL after the download.```js const saveFile = async (blob) => { const a = document.createElement("a"); a.download = "my-file.txt"; a.href = URL.createObjectURL(blob); a.addEventListener("click", (e) => { setTimeout(() => URL.revokeObjectURL(a.href), 30 * 1000); }); a.click(); }; ``` ### The problem A massive downside of the _download_ approach is that there is no way to make a classic open→edit→save flow happen, that is, there is no way to _overwrite_ the original file. Instead, you end up with a new _copy_ of the original file in the operating system's default Downloads folder whenever you "save".
For a more thorough introduction to the File System Access API, see the article The File System Access API: simplifying access to local files.### Opening files With the [File System Access API](https://wicg.github.io/file-system-access/), opening a file is a matter of one call to the `window.showOpenFilePicker()` method. This call returns a file handle, from which you can get the actual `File` via the `getFile()` method. ```js const openFile = async () => { try { // Always returns an array. const [handle] = await window.showOpenFilePicker(); return handle.getFile(); } catch (err) { console.error(err.name, err.message); } }; ``` ### Opening directories Open a directory by calling `window.showDirectoryPicker()` that makes directories selectable in the file dialog box. ### Saving files Saving files is similarly straightforward. From a file handle, you create a writable stream via `createWritable()`, then you write the Blob data by calling the stream's `write()` method, and finally you close the stream by calling its `close()` method. ```js const saveFile = async (blob) => { try { const handle = await window.showSaveFilePicker({ types: [ { accept: { // Omitted }, }, ], }); const writable = await handle.createWritable(); await writable.write(blob); await writable.close(); return handle; } catch (err) { console.error(err.name, err.message); } }; ``` ## Introducing browser-fs-access As perfectly fine as the File System Access API is, it's [not yet widely available](https://caniuse.com/native-filesystem-api).