Compare commits

..

36 Commits

Author SHA1 Message Date
Zsolt Viczian
f0ef04ed3e 1.0.7 - tweak to styles 2021-04-29 07:26:58 +02:00
zsviczian
8760f72a13 Update README.md 2021-04-28 22:59:17 +02:00
zsviczian
09602e142c Update README.md 2021-04-28 22:47:05 +02:00
zsviczian
c7500e9ee7 Update README.md 2021-04-28 22:46:27 +02:00
zsviczian
92d3363b5b Update README.md 2021-04-28 22:04:43 +02:00
zsviczian
30682e1b40 Update README.md 2021-04-28 22:04:29 +02:00
zsviczian
d89431bbde Update README.md 2021-04-28 21:51:57 +02:00
Zsolt Viczian
1c707db3a7 1.0.6 2021-04-28 21:38:22 +02:00
zsviczian
a56fda222d Update .gitignore 2021-04-28 16:08:21 +02:00
zsviczian
9fcbe5b7d7 Delete data.json 2021-04-28 16:05:25 +02:00
zsviczian
3c6dbcc8bb Delete data-ZsoltServer.json 2021-04-28 16:05:09 +02:00
zsviczian
25e2f3d8bb Update README.md 2021-04-28 15:58:45 +02:00
zsviczian
1c35e86118 Update README.md 2021-04-28 15:57:34 +02:00
zsviczian
61b716d8f6 Update manifest.json 2021-04-28 15:46:27 +02:00
zsviczian
2a0404fe18 Update versions.json 2021-04-28 15:46:04 +02:00
Zsolt Viczian
4f4a80b317 revert readme commit 2021-04-28 15:43:59 +02:00
Zsolt Viczian
0259dc579f Revert "left align images in readme.md"
This reverts commit fe84c607a6.
2021-04-28 15:43:30 +02:00
zsviczian
fe84c607a6 left align images in readme.md 2021-04-28 15:36:33 +02:00
zsviczian
b8178ac07c Update README.md 2021-04-28 13:52:35 +02:00
Zsolt Viczian
a65c6afed2 correct manifest and versions 2021-04-28 06:25:20 +02:00
Zsolt Viczian
6e207350d6 1.0.6-test 2021-04-27 23:10:29 +02:00
Zsolt Viczian
d64c00f2dd added sync SVP with Excalidraw file 2021-04-26 23:02:22 +02:00
Zsolt Viczian
25a998fc01 1.0.5 2021-04-26 06:32:40 +02:00
Zsolt Viczian
a212136323 removed unused comments and empty lines 2021-04-25 17:07:47 +02:00
Zsolt Viczian
e1a92695d5 1.0.5 2021-04-25 16:51:36 +02:00
Zsolt Viczian
370e35182b Merge branch 'master' of https://github.com/zsviczian/obsidian-excalidraw-plugin 2021-04-25 09:34:34 +02:00
Zsolt Viczian
634bbc2165 Settings to include Excalidraw/Template.excalidraw 2021-04-25 09:34:06 +02:00
zsviczian
a2dd13049e Update README.md 2021-04-25 09:04:35 +02:00
Zsolt Viczian
a64c6e5335 cleand up unused imports 2021-04-24 22:56:36 +02:00
Zsolt Viczian
d57a28c36b 1.0.5-test2 2021-04-24 22:43:43 +02:00
Zsolt Viczian
2d32b4b71a resolved chart pos, and theme on load drawing 2021-04-24 22:18:23 +02:00
Zsolt Viczian
5be455d368 save stencil library to data.json 2021-04-24 10:50:00 +02:00
Zsolt Viczian
c4acf24bca test release with excalidraw 0.7.0-fixtext library 2021-04-24 06:40:27 +02:00
Zsolt Viczian
a13c8e0127 working with libraryItems in ex0.7.0-autoprefix1 2021-04-23 06:43:00 +02:00
Zsolt Viczian
b048dd0ee7 added rollup-plugin-visualizer 2021-04-22 11:25:21 +02:00
Zsolt Viczian
f6a832b2bc updated with Excalidraw 0.7.0-libs2 2021-04-22 10:25:48 +02:00
19 changed files with 769 additions and 253 deletions

3
.babelrc Normal file
View File

@@ -0,0 +1,3 @@
{
"presets": ["@babel/preset-env", "@babel/preset-react"]
}

5
.gitignore vendored
View File

@@ -8,4 +8,7 @@ package-lock.json
# build
main.js
*.js.map
*.js.map
stats.html
hot-reload.bat
data.json

View File

@@ -1,34 +1,81 @@
## Obsidian Excalidraw Plugin
# Obsidian Excalidraw Plugin
The Obsidian-Excalidraw plugin integrates [Excalidraw](https://excalidraw.com/), a feature rich sketching tool, into Obsidian. You can store and edit Excalidraw files in your vault and you can transclude drawings into your documents. For a showcase of Excalidraw features, please read my blog post [here](https://www.zsolt.blog/2021/03/showcasing-excalidraw.html).
![image](https://user-images.githubusercontent.com/14358394/115391516-d9df4880-a1df-11eb-95a9-cad850cdf9fc.png)
**See details of the 1.0.6 release including a short video, futher below**
### Key features
- The plugin adds 3 commands to the command palette. 1) To create a new drawing. 2) To find and edit existing drawings in your vault, and 3) to transclude a drawing into a document.
- You can also use the file explorer in your vault to open Excalidraw files.
- You can set up a default folder for saving new drawings in Settings.
- You can set up a Template by creating a drawing, customizing it the way you like it, and specifying the file as the template in settings.
- The plugin saves drawings to your vault as a file with the .excalidraw file-extension.
- You can set the size of embedded image using the [[image.excalidraw|100]] or [[image.excalidraw|100x100]] format.
![image](https://user-images.githubusercontent.com/14358394/115983515-d06c2c80-a5a1-11eb-8d12-c7df91d18107.png)
### How to?
- Add a library: Click [browse libraries](https://libraries.excalidraw.com/?target=_excalidraw&sort=default) in Excalidraw. Download the preferred library and close browser tab. Click Load library in Excalidraw-Obsidian to load your locally saved library.
## Key features
- The plugin adds the following actions to the command palette:
- To create a new drawing
- To find and edit existing drawings in your vault,
- To embed (transclude) a drawing into a document, and
- To export a drawing as PNG or SVG.
- You can also use the file explorer in your vault to open Excalidraw files.
- Use the ribbon button to create a new drawing, do CTRL+Click to open on a new page.
- Open settings to set up a default folder for new drawings.
- Set up a Template by creating a drawing, customizing it the way you like it, and specifying the file as the template in settings.
- The plugin saves drawings to your vault as a file with the *.excalidraw* file extension.
- You can customize the size and position of the embedded image using the [[image.excalidraw|100]], [[image.excalidraw|100x100]], [[image.excalidraw|100|left]] or [[image.excalidraw|right]], format.
- You can setup Excalidraw to automatically export SVG and/or PNG files for your drawings, and to keep those in sync with your drawing.
### Known issues
- When inserting images from the library, sometimes these images appear out of view. Youll need to select “scroll to view” at the bottom of the screen. This is a bug in Excalidraw. The Excalidraw development team has successfully reproduced the bug and promised to resolve it in the next update of Excalidraw.
- On iPad: As you draw left to right it opens left sidebar. Draw right to left, opens right sidebar. Draw down, opens commands palette. So seems open is emulating the gestures, even when drawing towards the center. I have raised the problem with the Obsidian.md team, and Licat promised to resolve this issue in the next Obsidian release.
## How to?
Part 1: Intro to Obsidian-Excalidraw - Start a new drawing (3:12)
### Excalidraw in Obsidian
https://user-images.githubusercontent.com/14358394/115386872-3fc8d180-a1da-11eb-9366-16d0e064932a.mp4
[![Part 1: Intro to Obsidian-Excalidraw - Start a new drawing](https://user-images.githubusercontent.com/14358394/115983840-05797e80-a5a4-11eb-93cd-bae4b1973f72.jpg)](https://youtu.be/i-hIfY-Ecjg)
### Contributing
Feel free to contribute.
Part 2: Intro to Obsidian-Excalidraw - Basic features (6:06)
[![Part 2: Intro to Obsidian-Excalidraw - Basic features](https://user-images.githubusercontent.com/14358394/115983902-699c4280-a5a4-11eb-973d-2ba1bd7ac2db.jpg)](https://youtu.be/-dk7pvdl-H0)
Part 3: Intro to Obsidian-Excalidraw - Advanced features (3:26)
[![Part 3: Intro to Obsidian-Excalidraw - Advanced features](https://user-images.githubusercontent.com/14358394/115983916-7de03f80-a5a4-11eb-8f36-4ad516ef9e80.jpg)](https://youtu.be/2cKlEwo8WU0)
Part 4: Intro to Obsidian-Excalidraw - Setting up a template (1:45)
[![Part 4: Intro to Obsidian-Excalidraw - Setting up a template](https://user-images.githubusercontent.com/14358394/115983929-92bcd300-a5a4-11eb-9d4f-03e5cb9e3ebf.jpg)](https://youtu.be/oNPYZEpmuJ8)
Part 5: Intro to Obsidian-Excalidraw - Stencil Library (3:16)
[![Part 5: Intro to Obsidian-Excalidraw - Stencil Library](https://user-images.githubusercontent.com/14358394/115983944-a8ca9380-a5a4-11eb-8a69-e74ae00d95be.jpg)](https://youtu.be/rLx-9FvlzgI)
Part 6: Intro to Obsidian-Excalidraw: Embedding drawings (2:08)
[![Part 6: Intro to Obsidian-Excalidraw: Embedding drawings](https://user-images.githubusercontent.com/14358394/115983954-bbdd6380-a5a4-11eb-9243-f0151451afcd.jpg)](https://youtu.be/JQeJ-Hh-xAI)
## 1.0.6 update
[![1.0.6 Update](https://user-images.githubusercontent.com/14358394/116312909-58725200-a7ad-11eb-89b9-c67cb48ffebb.jpg)](https://youtu.be/ipZPbcP2B0M)
### SVG styling when embedding using a code block
- new formatting option for the code block embedding
- Valid values: left, right, center... but really anything after the last |.
Here is the corresponding CSS:
```
.excalidraw-svg-left {
float: left;
}
.excalidraw-svg-right {
float: right;
}
.excalidraw-svg-center {
}
.excalidraw-svg {
}
```
## Known issues
- On mobile (iOS and Android): As you draw left to right it opens left sidebar. Draw right to left, opens right sidebar. Draw down, opens commands palette. So seems open is emulating the gestures, even when drawing towards the center. I understand that the issue will be resolved in the next release of Obsidian mobile.
- I have seen two cases when adding a stencil library did not work. In both cases the end solution was a reinstall of Obsidian. The root cause is not clear, but may be due to the incremental updates of Obsidian from an early version.
## Feedback, questions, ideas, problems
By clicking [here](https://github.com/zsviczian/obsidian-excalidraw-plugin/issues) you can create an issue to report a bug, suggest an improvement for this plugin, ask a question, etc.
### Support
If you want to support me and my work, you can donate me a little something.
## Support
If you want to support me and my work, you can donate me a little something on [https://ko-fi/zsolt](https://ko-fi.com/zsolt).
[<img src="https://user-images.githubusercontent.com/14358394/115450238-f39e8100-a21b-11eb-89d0-fa4b82cdbce8.png" width="200">](https://ko-fi.com/zsolt)
[https://ko-fi/zsolt](https://ko-fi.com/zsolt)
[<img style="float:left" src="https://user-images.githubusercontent.com/14358394/115450238-f39e8100-a21b-11eb-89d0-fa4b82cdbce8.png" width="200">](https://ko-fi.com/zsolt)

View File

@@ -1 +0,0 @@
{"openFile":"Blog/attachements/security through obscurity.excalidraw","settings":{"folder":"excalidraw","templateFilePath":"excalidraw/Template.excalidraw"}}

View File

@@ -1 +0,0 @@
{"folder":"excalidraw","templateFilePath":"excalidraw/Template.excalidra","width":"400","openFile":"excalidraw/new file.excalidraw","settings":{"folder":"excalidraw","templateFilePath":""}}

10
dist/manifest.json vendored
View File

@@ -1,10 +0,0 @@
{
"id": "obsidian-excalidraw-plugin",
"name": "Excalidraw",
"version": "1.0.2",
"minAppVersion": "0.11.13",
"description": "An Obsidian plugin to edit and view Excalidraw drawings",
"author": "Zsolt Viczian",
"authorUrl": "https://zsolt.blog",
"isDesktopOnly": false
}

18
dist/styles.css vendored
View File

@@ -1,18 +0,0 @@
.App {
font-family: sans-serif;
text-align: center;
}
.excalidraw-wrapper {
height: 100%;
margin: 0px;
background-color: white;
}
.context-menu-option__shortcut {
background-color: transparent !important;
}
.block-language-excalidraw {
text-align:center;
}

3
esbuild.config.json Normal file
View File

@@ -0,0 +1,3 @@
{
"minify": true
}

View File

@@ -1,10 +1,10 @@
{
"id": "obsidian-excalidraw-plugin",
"name": "Excalidraw",
"version": "1.0.2",
"version": "1.0.7",
"minAppVersion": "0.11.13",
"description": "An Obsidian plugin to edit and view Excalidraw drawings",
"author": "Zsolt Viczian",
"authorUrl": "https://zsolt.blog",
"isDesktopOnly": false
}
}

View File

@@ -11,12 +11,17 @@
"author": "",
"license": "MIT",
"dependencies": {
"@excalidraw/excalidraw": "0.6.0",
"react": "17.0.0",
"react-dom": "17.0.0",
"@excalidraw/excalidraw": "0.7.0",
"aakansha-excalidraw": "0.7.0-draft",
"react": "17.0.1",
"react-dom": "17.0.1",
"react-scripts": "4.0.1"
},
"devDependencies": {
"@babel/core": "^7.3.3",
"@babel/preset-env": "^7.3.1",
"@babel/preset-react": "^7.0.0",
"@rollup/plugin-babel": "5.3.0",
"@rollup/plugin-commonjs": "^15.1.0",
"@rollup/plugin-node-resolve": "^9.0.0",
"@rollup/plugin-typescript": "^6.0.0",
@@ -29,7 +34,9 @@
"rollup-plugin-copy": "3.4.0",
"rollup-plugin-minify": "1.0.3",
"rollup-plugin-postcss": "^4.0.0",
"rollup-plugin-visualizer": "^5.4.1",
"tslib": "^2.0.3",
"typescript": "^4.0.3"
"typescript": "^4.0.3",
"webpack-bundle-analyzer": "^4.4.1"
}
}

View File

@@ -1,22 +1,19 @@
import typescript from '@rollup/plugin-typescript';
import { nodeResolve } from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
import postcss from 'rollup-plugin-postcss';
import copy from 'rollup-plugin-copy';
//import uglify from 'rollup-plugin-uglify';
//import minify from "rollup-plugin-minify"
//import copy from 'rollup-plugin-copy';
import { env } from "process";
import babel from '@rollup/plugin-babel';
import replace from "@rollup/plugin-replace";
import visualizer from "rollup-plugin-visualizer";
const isProd = (process.env.NODE_ENV === "production");
console.log(process.env.NODE_ENV);
console.log("Is production", isProd);
export default {
input: 'src/main.ts',
output: {
dir: isProd ? './dist' : '.',
dir: '.',
sourcemap: 'inline',
format: 'cjs',
exports: 'default'
@@ -29,15 +26,10 @@ export default {
preventAssignment: true,
"process.env.NODE_ENV": JSON.stringify(env.NODE_ENV),
}),
babel({
exclude: "node_modules/**"
}),
commonjs(),
postcss({
plugins: []
}),
copy({
targets: [
{ src: ['manifest.json', 'styles.css'], dest: './dist' }
], flatten: true
}),
//process.env.NODE_ENV === 'production' && minify(),
visualizer(),
]
};

View File

@@ -1,37 +1,115 @@
import { TextFileView, WorkspaceLeaf } from "obsidian";
import {
TextFileView,
WorkspaceLeaf,
normalizePath,
TFile,
Menu,
} from "obsidian";
import * as React from "react";
import * as ReactDOM from "react-dom";
import Excalidraw, {exportToSvg} from "@excalidraw/excalidraw";
import { ExcalidrawElement } from "@excalidraw/excalidraw/types/element/types";
import { AppState } from "@excalidraw/excalidraw/types/types";
import {VIEW_TYPE_EXCALIDRAW, EXCALIDRAW_FILE_EXTENSION, ICON_NAME} from './constants';
import {
AppState,
LibraryItems
} from "@excalidraw/excalidraw/types/types";
import {
VIEW_TYPE_EXCALIDRAW,
EXCALIDRAW_FILE_EXTENSION,
ICON_NAME,
EXCALIDRAW_LIB_HEADER,
VIRGIL_FONT,
CASCADIA_FONT,
DISK_ICON_NAME,
PNG_ICON_NAME,
SVG_ICON_NAME
} from './constants';
import ExcalidrawPlugin from './main';
export interface ExportSettings {
withBackground: boolean,
withTheme: boolean
}
export default class ExcalidrawView extends TextFileView {
private getScene: any;
private excalidrawRef: React.MutableRefObject<any>;
private justLoaded: boolean;
private plugin: ExcalidrawPlugin;
constructor(leaf: WorkspaceLeaf) {
constructor(leaf: WorkspaceLeaf, plugin: ExcalidrawPlugin) {
super(leaf);
this.getScene = null;
this.excalidrawRef = null;
this.plugin = plugin;
this.justLoaded = false;
}
async onClose() {
this.requestSave();
public async saveSVG(data?: string) {
if(!data) {
if (!this.getScene) return false;
data = this.getScene();
}
const filepath = this.file.path.substring(0,this.file.path.lastIndexOf('.'+EXCALIDRAW_FILE_EXTENSION)) + '.svg';
const file = this.app.vault.getAbstractFileByPath(normalizePath(filepath));
const exportSettings: ExportSettings = {
withBackground: this.plugin.settings.exportWithBackground,
withTheme: this.plugin.settings.exportWithTheme
}
const svg = ExcalidrawView.getSVG(data,exportSettings);
if(!svg) return;
//replace font references with base64 fonts
const includesVirgil = svg.querySelector("text[font-family^='Virgil']") != null;
const includesCascadia = svg.querySelector("text[font-family^='Cascadia']") != null;
const defs = svg.querySelector("defs");
if (defs && (includesCascadia || includesVirgil)) {
defs.innerHTML = "<style>" + (includesVirgil ? VIRGIL_FONT : "") + (includesCascadia ? CASCADIA_FONT : "")+"</style>";
}
const svgString = svg.outerHTML;
if(file && file instanceof TFile) await this.app.vault.modify(file,svgString);
else await this.app.vault.create(filepath,svgString);
}
// clear the view content
clear() {
ReactDOM.unmountComponentAtNode(this.contentEl);
this.getScene = null;
public async savePNG(data?: string) {
if(!data) {
if (!this.getScene) return false;
data = this.getScene();
}
const filepath = this.file.path.substring(0,this.file.path.lastIndexOf('.'+EXCALIDRAW_FILE_EXTENSION)) + '.png';
const file = this.app.vault.getAbstractFileByPath(normalizePath(filepath));
const exportSettings: ExportSettings = {
withBackground: this.plugin.settings.exportWithBackground,
withTheme: this.plugin.settings.exportWithTheme
}
const png = await ExcalidrawView.getPNG(data,exportSettings);
if(!png) return;
if(file && file instanceof TFile) await this.app.vault.modifyBinary(file,await png.arrayBuffer());
else await this.app.vault.createBinary(filepath,await png.arrayBuffer());
}
// get the new file content
getViewData () {
if(this.getScene) return this.getScene();
else return '';
if(this.getScene) {
const scene = this.getScene();
if(this.plugin.settings.autoexportSVG) this.saveSVG(scene);
if(this.plugin.settings.autoexportPNG) this.savePNG(scene);
return scene;
}
else return this.data;
}
async onload() {
this.addAction(DISK_ICON_NAME,"Save drawing",async (ev)=> {
await this.save();
this.plugin.triggerEmbedUpdates();
});
this.addAction(PNG_ICON_NAME,"Export as PNG",async (ev)=>this.savePNG());
this.addAction(SVG_ICON_NAME,"Export as SVG",async (ev)=>this.saveSVG());
}
async onunload() {
if(this.excalidrawRef) await this.save();
}
setViewData (data: string, clear: boolean) {
if (this.app.workspace.layoutReady) {
@@ -41,21 +119,38 @@ export default class ExcalidrawView extends TextFileView {
}
}
private loadDrawing (data:string, clear:boolean) :void {
// clear the view content
clear() {
if(this.excalidrawRef) {
this.excalidrawRef = null;
this.getScene = null;
ReactDOM.unmountComponentAtNode(this.contentEl);
}
}
private async loadDrawing (data:string, clear:boolean) {
if(clear) this.clear();
this.justLoaded = true; //a flag to trigger zoom to fit after the drawing has been loaded
const excalidrawData = JSON.parse(data);
this.instantiateExcalidraw({
elements: excalidrawData.elements,
appState: excalidrawData.appState,
scrollToContent: true,
});
if(this.excalidrawRef) {
this.excalidrawRef.current.updateScene({
elements: excalidrawData.elements,
appState: excalidrawData.appState,
});
} else {
this.instantiateExcalidraw({
elements: excalidrawData.elements,
appState: excalidrawData.appState,
scrollToContent: true,
libraryItems: await this.getLibrary(),
});
}
}
// gets the title of the document
getDisplayText() {
if(this.file) return this.file.basename;
else return "Excalidraw (no file)";
}
// confirms this view can accept csv extension
@@ -73,50 +168,58 @@ export default class ExcalidrawView extends TextFileView {
return ICON_NAME;
}
async getLibrary() {
const data = JSON.parse(this.plugin.settings.library);
return data?.library ? data.library : [];
}
private instantiateExcalidraw(initdata: any) {
ReactDOM.render(React.createElement(() => {
let previousSceneVersion = 0;
const reactElement = React.createElement(() => {
const excalidrawRef = React.useRef(null);
const excalidrawWrapperRef = React.useRef(null);
const [dimensions, setDimensions] = React.useState({
width: undefined,
height: undefined
});
this.excalidrawRef = excalidrawRef;
React.useEffect(() => {
setDimensions({
width: this.contentEl.clientWidth,
height: this.contentEl.clientHeight,
});
const onResize = () => {
try {
setDimensions({
width: this.contentEl.clientWidth,
height: this.contentEl.clientHeight,
});
} catch(err) {console.log ("onResize ",err)}
} catch(err) {console.log ("Excalidraw React-Wrapper, onResize ",err)}
};
window.addEventListener("resize", onResize);
return () => window.removeEventListener("resize", onResize);
}, [excalidrawWrapperRef]);
this.getScene = function() {
this.getScene = () => {
if(!excalidrawRef?.current) {
return null;
}
const el: ExcalidrawElement[] = excalidrawRef.current.getSceneElements();
const st: AppState = excalidrawRef.current.getAppState();
return JSON.stringify({
"type": "excalidraw",
"version": 2,
"source": "https://excalidraw.com",
"elements": el.filter(e => !e.isDeleted),
"elements": el,
"appState": {
"theme": st.theme,
"viewBackgroundColor": st.viewBackgroundColor,
"gridSize": st.gridSize,
"zenModeEnabled": st.zenModeEnabled
}
});
};
return React.createElement(
React.Fragment,
null,
@@ -124,34 +227,51 @@ export default class ExcalidrawView extends TextFileView {
"div",
{
className: "excalidraw-wrapper",
ref: excalidrawWrapperRef
ref: excalidrawWrapperRef,
key: "abc",
},
React.createElement(Excalidraw.default, {
ref: excalidrawRef,
key: "xyz",
width: dimensions.width,
height: dimensions.height,
UIOptions: {
canvasActions: {
loadScene: false,
saveScene: false,
saveAsScene: false
saveAsScene: false,
export: false
},
},
initialData: initdata
initialData: initdata,
onChange: (et:ExcalidrawElement[],st:AppState) => {
if(this.justLoaded) {
this.justLoaded = false;
const e = new KeyboardEvent("keydown", {bubbles : true, cancelable : true, shiftKey : true, code:"Digit1"});
this.contentEl.querySelector("canvas")?.dispatchEvent(e);
}
},
onLibraryChange: (items:LibraryItems) => {
(async () => {
this.plugin.settings.library = EXCALIDRAW_LIB_HEADER+JSON.stringify(items)+'}';
await this.plugin.saveSettings();
})();
}
})
)
);
}),(this as any).contentEl);
});
ReactDOM.render(reactElement,(this as any).contentEl);
}
public static getSVG(data:string):SVGSVGElement {
public static getSVG(data:string, exportSettings:ExportSettings):SVGSVGElement {
try {
const excalidrawData = JSON.parse(data);
return exportToSvg({
elements: excalidrawData.elements,
appState: {
exportBackground: true,
exportWithDarkMode: excalidrawData.appState?.theme=="light" ? false : true,
exportBackground: exportSettings.withBackground,
exportWithDarkMode: exportSettings.withTheme ? (excalidrawData.appState?.theme=="light" ? false : true) : false,
... excalidrawData.appState,},
exportPadding:10,
metadata: "Generated by Excalidraw-Obsidian plugin",
@@ -160,4 +280,22 @@ export default class ExcalidrawView extends TextFileView {
return null;
}
}
public static async getPNG(data:string, exportSettings:ExportSettings) {
try {
const excalidrawData = JSON.parse(data);
return await Excalidraw.exportToBlob({
elements: excalidrawData.elements,
appState: {
exportBackground: exportSettings.withBackground,
exportWithDarkMode: exportSettings.withTheme ? (excalidrawData.appState?.theme=="light" ? false : true) : false,
... excalidrawData.appState,},
mimeType: "image/png",
exportWithDarkMode: "true",
metadata: "Generated by Excalidraw-Obsidian plugin",
});
} catch (error) {
return null;
}
}
}

File diff suppressed because one or more lines are too long

View File

@@ -8,6 +8,7 @@ import {
PluginManifest,
MarkdownView,
normalizePath,
MarkdownPostProcessorContext,
} from 'obsidian';
import {
BLANK_DRAWING,
@@ -16,8 +17,15 @@ import {
ICON_NAME,
EXCALIDRAW_FILE_EXTENSION,
CODEBLOCK_EXCALIDRAW,
DISK_ICON,
DISK_ICON_NAME,
PNG_ICON,
PNG_ICON_NAME,
SVG_ICON,
SVG_ICON_NAME,
RERENDER_EVENT
} from './constants';
import ExcalidrawView from './ExcalidrawView';
import ExcalidrawView, {ExportSettings} from './ExcalidrawView';
import {
ExcalidrawSettings,
DEFAULT_SETTINGS,
@@ -39,107 +47,192 @@ export default class ExcalidrawPlugin extends Plugin {
async onload() {
addIcon(ICON_NAME, EXCALIDRAW_ICON);
addIcon(DISK_ICON_NAME,DISK_ICON);
addIcon(PNG_ICON_NAME,PNG_ICON);
addIcon(SVG_ICON_NAME,SVG_ICON);
this.registerView(
VIEW_TYPE_EXCALIDRAW,
(leaf: WorkspaceLeaf) => new ExcalidrawView(leaf)
(leaf: WorkspaceLeaf) => new ExcalidrawView(leaf, this)
);
this.registerExtensions([EXCALIDRAW_FILE_EXTENSION],VIEW_TYPE_EXCALIDRAW);
this.registerMarkdownCodeBlockProcessor(CODEBLOCK_EXCALIDRAW, async (source,el,ctx) => {
const parseError = (message: string) => {
el.createDiv("excalidraw-error",(el)=> {
el.createEl("p","Please provide a link to an excalidraw file: [[file."+EXCALIDRAW_FILE_EXTENSION+"]]");
el.createEl("p",message);
el.createEl("p",source);
})
}
const filename = source.match(/\[{2}(.*)\]{2}/m);
const filenameWH = source.match(/\[{2}(.*)\|(\d*)x(\d*)\]{2}/m);
const filenameW = source.match(/\[{2}(.*)\|(\d*)\]{2}/m);
let fname:string = '';
let fwidth:string = this.settings.width;
let fheight:string = null;
if (filenameWH) {
fname = filenameWH[1];
fwidth = filenameWH[2];
fheight = filenameWH[3];
} else if (filenameW) {
fname = filenameW[1];
fwidth = filenameW[2];
} else if (filename) {
fname = filename[1];
}
if(fname == '') {
parseError("No link to file found in codeblock.");
return;
}
const file = this.app.vault.getAbstractFileByPath(fname);
if(!(file && file instanceof TFile)) {
parseError("File does not exist. " + fname);
return;
}
if(file.extension != EXCALIDRAW_FILE_EXTENSION) {
parseError("Not an excalidraw file. Must have extension " + EXCALIDRAW_FILE_EXTENSION);
return;
}
const content = await this.app.vault.read(file);
const svg = ExcalidrawView.getSVG(content);
if(!svg) {
parseError("Parse error. Not a valid Excalidraw file.");
return;
}
el.createDiv("excalidraw-svg",(el)=> {
svg.removeAttribute('width');
svg.removeAttribute('height');
svg.style.setProperty('width',fwidth);
if(fheight) svg.style.setProperty('height',fheight);
el.appendChild(svg);
el.addEventListener(RERENDER_EVENT,async (e) => {
e.stopPropagation();
el.empty();
this.codeblockProcessor(source,el,ctx,this);
});
});
this.codeblockProcessor(source,el,ctx,this);
});
await this.loadSettings();
this.addSettingTab(new ExcalidrawSettingTab(this.app, this));
this.openDialog = new OpenFileDialog(this.app, this);
this.addRibbonIcon(ICON_NAME, 'Excalidraw', async () => {
this.openDialog.start(openDialogAction.openFile);
this.addRibbonIcon(ICON_NAME, 'Create a new drawing in Excalidraw', async (e) => {
this.createDrawing(this.getNextDefaultFilename(), e.ctrlKey);
});
this.addCommand({
id: "excalidraw-open",
name: "Open an existing drawing or create new one",
name: "Open an existing drawing - IN A NEW PANE",
callback: () => {
this.openDialog.start(openDialogAction.openFile);
this.openDialog.start(openDialogAction.openFile, true);
},
});
this.addCommand({
id: "excalidraw-open-on-current",
name: "Open an existing drawing - IN THE CURRENT ACTIVE PANE",
callback: () => {
this.openDialog.start(openDialogAction.openFile, false);
},
});
this.addCommand({
id: "excalidraw-insert-transclusion",
name: "Transclude an ."+EXCALIDRAW_FILE_EXTENSION+" file into a markdown document",
callback: () => {
this.openDialog.start(openDialogAction.insertLink);
name: "Transclude (embed) an ."+EXCALIDRAW_FILE_EXTENSION+" drawing",
checkCallback: (checking: boolean) => {
if (checking) {
return this.app.workspace.activeLeaf.view.getViewType() == "markdown";
} else {
this.openDialog.start(openDialogAction.insertLink, false);
return true;
}
},
});
this.addCommand({
id: "excalidraw-autocreate",
name: "Create a new drawing",
name: "Create a new drawing - IN A NEW PANE",
callback: () => {
this.createDrawing(this.getNextDefaultFilename());
this.createDrawing(this.getNextDefaultFilename(), true);
},
});
this.addCommand({
id: "excalidraw-autocreate-on-current",
name: "Create a new drawing - IN THE CURRENT ACTIVE PANE",
callback: () => {
this.createDrawing(this.getNextDefaultFilename(), false);
},
});
this.addCommand({
id: 'export-svg',
name: 'Export SVG. Save it next to the current file',
checkCallback: (checking: boolean) => {
if (checking) {
return this.app.workspace.activeLeaf.view.getViewType() == VIEW_TYPE_EXCALIDRAW;
} else {
const view = this.app.workspace.activeLeaf.view;
if(view.getViewType() == VIEW_TYPE_EXCALIDRAW) {
(this.app.workspace.activeLeaf.view as ExcalidrawView).saveSVG();
return true;
}
else return false;
}
},
});
this.addCommand({
id: 'export-png',
name: 'Export PNG. Save it next to the current file',
checkCallback: (checking: boolean) => {
if (checking) {
return this.app.workspace.activeLeaf.view.getViewType() == VIEW_TYPE_EXCALIDRAW;
} else {
const view = this.app.workspace.activeLeaf.view;
if(view.getViewType() == VIEW_TYPE_EXCALIDRAW) {
(this.app.workspace.activeLeaf.view as ExcalidrawView).savePNG();
return true;
}
else return false;
}
},
});
//watch filename change to rename .svg
this.app.vault.on('rename',async (file,oldPath) => {
if (!(this.settings.keepInSync && file instanceof TFile)) return;
if (file.extension != EXCALIDRAW_FILE_EXTENSION) return;
const oldSVGpath = oldPath.substring(0,oldPath.lastIndexOf('.'+EXCALIDRAW_FILE_EXTENSION)) + '.svg';
const svgFile = this.app.vault.getAbstractFileByPath(normalizePath(oldSVGpath));
if(svgFile && svgFile instanceof TFile) {
const newSVGpath = file.path.substring(0,file.path.lastIndexOf('.'+EXCALIDRAW_FILE_EXTENSION)) + '.svg';
await this.app.vault.rename(svgFile,newSVGpath);
}
});
//watch file delete and delete corresponding .svg
this.app.vault.on('delete',async (file:TFile) => {
if (!(this.settings.keepInSync && file instanceof TFile)) return;
if (file.extension != EXCALIDRAW_FILE_EXTENSION) return;
const svgPath = file.path.substring(0,file.path.lastIndexOf('.'+EXCALIDRAW_FILE_EXTENSION)) + '.svg';
const svgFile = this.app.vault.getAbstractFileByPath(normalizePath(svgPath));
if(svgFile && svgFile instanceof TFile) {
await this.app.vault.delete(svgFile);
}
});
}
private async codeblockProcessor(source: string, el: HTMLElement, ctx: MarkdownPostProcessorContext, plugin: ExcalidrawPlugin) {
const parseError = (message: string) => {
el.createDiv("excalidraw-error",(el)=> {
el.createEl("p","Please provide a link to an excalidraw file: [[file."+EXCALIDRAW_FILE_EXTENSION+"]]");
el.createEl("p",message);
el.createEl("p",source);
})
}
const parts = source.match(/\[{2}([^|]*)\|?(\d*)x?(\d*)\|?(.*)\]{2}/m);
if(!parts) {
parseError("No link to file found in codeblock.");
return;
}
const fname = parts[1];
const fwidth = parts[2]? parts[2] : plugin.settings.width;
const fheight = parts[3];
const style = "excalidraw-svg" + (parts[4] ? "-" + parts[4] : "");
if(!fname) {
parseError("No link to file found in codeblock.");
return;
}
const file = plugin.app.vault.getAbstractFileByPath(fname);
if(!(file && file instanceof TFile)) {
parseError("File does not exist. " + fname);
return;
}
if(file.extension != EXCALIDRAW_FILE_EXTENSION) {
parseError("Not an excalidraw file. Must have extension " + EXCALIDRAW_FILE_EXTENSION);
return;
}
const content = await plugin.app.vault.read(file);
const exportSettings: ExportSettings = {
withBackground: plugin.settings.exportWithBackground,
withTheme: plugin.settings.exportWithTheme
}
const svg = ExcalidrawView.getSVG(content,exportSettings);
if(!svg) {
parseError("Parse error. Not a valid Excalidraw file.");
return;
}
el.createDiv(style,(el)=> {
svg.removeAttribute('width');
svg.removeAttribute('height');
svg.style.setProperty('width',fwidth);
if(fheight) svg.style.setProperty('height',fheight);
svg.addClass(style);
el.appendChild(svg);
});
}
public insertCodeblock(data:string) {
const activeView = this.app.workspace.getActiveViewOfType(MarkdownView);
if(activeView) {
@@ -162,7 +255,15 @@ export default class ExcalidrawPlugin extends Plugin {
await this.saveData(this.settings);
}
public async openDrawing(drawingFile: TFile) {
public triggerEmbedUpdates(){
const e = document.createEvent("Event")
e.initEvent(RERENDER_EVENT,true,false);
document
.querySelectorAll("svg[class^='excalidraw-svg']")
.forEach((el) => el.dispatchEvent(e));
}
public async openDrawing(drawingFile: TFile, onNewPane: boolean) {
const leafs = this.app.workspace.getLeavesOfType(VIEW_TYPE_EXCALIDRAW);
let leaf:WorkspaceLeaf = null;
@@ -176,6 +277,10 @@ export default class ExcalidrawPlugin extends Plugin {
if(!leaf) {
leaf = this.app.workspace.getLeaf();
}
if(onNewPane) {
leaf = this.app.workspace.createLeafBySplit(leaf);
}
leaf.setViewState({
type: VIEW_TYPE_EXCALIDRAW,
@@ -187,7 +292,7 @@ export default class ExcalidrawPlugin extends Plugin {
return this.settings.folder+'/Drawing ' + window.moment().format('YYYY-MM-DD HH.mm.ss')+'.'+EXCALIDRAW_FILE_EXTENSION;
}
public async createDrawing(filename: string) {
public async createDrawing(filename: string, onNewPane: boolean) {
const folder = this.app.vault.getAbstractFileByPath(normalizePath(this.settings.folder));
if (!(folder && folder instanceof TFolder)) {
await this.app.vault.createFolder(this.settings.folder);
@@ -196,9 +301,9 @@ export default class ExcalidrawPlugin extends Plugin {
const file = this.app.vault.getAbstractFileByPath(normalizePath(this.settings.templateFilePath));
if(file && file instanceof TFile) {
const content = await this.app.vault.read(file);
this.openDrawing(await this.app.vault.create(filename,content==''?BLANK_DRAWING:content));
this.openDrawing(await this.app.vault.create(filename,content==''?BLANK_DRAWING:content), onNewPane);
} else {
this.openDrawing(await this.app.vault.create(filename,BLANK_DRAWING));
this.openDrawing(await this.app.vault.create(filename,BLANK_DRAWING), onNewPane);
}
}
}

View File

@@ -1,6 +1,13 @@
import { App, FuzzySuggestModal, TFile, TFolder, normalizePath, Vault, TAbstractFile, Instruction } from "obsidian";
import {
App,
FuzzySuggestModal,
TFile
} from "obsidian";
import ExcalidrawPlugin from './main';
import {EMPTY_MESSAGE,EXCALIDRAW_FILE_EXTENSION} from './constants';
import {
EMPTY_MESSAGE,
EXCALIDRAW_FILE_EXTENSION
} from './constants';
export enum openDialogAction {
openFile,
@@ -11,23 +18,23 @@ export class OpenFileDialog extends FuzzySuggestModal<TFile> {
public app: App;
private plugin: ExcalidrawPlugin;
private action: openDialogAction;
private onNewPane: boolean;
constructor(app: App, plugin: ExcalidrawPlugin) {
super(app);
this.app = app;
this.action = openDialogAction.openFile;
this.plugin = plugin;
this.onNewPane = false;
this.setInstructions([{
command: "Type name of drawing to select.",
purpose: "",
}]);
this.inputEl.onkeyup = (e) => {
if(e.key=="Enter" && this.action == openDialogAction.openFile) {
if (this.containerEl.innerText.includes(EMPTY_MESSAGE)) {
this.plugin.createDrawing(this.plugin.settings.folder+'/'+this.inputEl.value+'.'+EXCALIDRAW_FILE_EXTENSION);
this.plugin.createDrawing(this.plugin.settings.folder+'/'+this.inputEl.value+'.'+EXCALIDRAW_FILE_EXTENSION, this.onNewPane);
this.close();
}
}
@@ -46,7 +53,7 @@ export class OpenFileDialog extends FuzzySuggestModal<TFile> {
onChooseItem(item: TFile, _evt: MouseEvent | KeyboardEvent): void {
switch(this.action) {
case(openDialogAction.openFile):
this.plugin.openDrawing(item);
this.plugin.openDrawing(item, this.onNewPane);
break;
case(openDialogAction.insertLink):
this.plugin.insertCodeblock(item.path);
@@ -54,8 +61,9 @@ export class OpenFileDialog extends FuzzySuggestModal<TFile> {
}
}
start(action:openDialogAction): void {
start(action:openDialogAction, onNewPane: boolean): void {
this.action = action;
this.onNewPane = onNewPane;
switch(action) {
case (openDialogAction.openFile):
this.emptyStateText = EMPTY_MESSAGE;

View File

@@ -1,16 +1,33 @@
import {App, PluginSettingTab, Setting} from 'obsidian';
import {
App,
parseFrontMatterAliases,
PluginSettingTab,
Setting
} from 'obsidian';
import type ExcalidrawPlugin from "./main";
export interface ExcalidrawSettings {
folder: string,
templateFilePath: string,
width: string,
exportWithTheme: boolean,
exportWithBackground: boolean,
autoexportSVG: boolean,
autoexportPNG: boolean,
keepInSync: boolean,
library: string,
}
export const DEFAULT_SETTINGS: ExcalidrawSettings = {
folder: 'excalidraw',
templateFilePath: '',
folder: 'Excalidraw',
templateFilePath: 'Excalidraw/Template.excalidraw',
width: '400',
exportWithTheme: true,
exportWithBackground: true,
autoexportSVG: false,
autoexportPNG: false,
keepInSync: false,
library: `{"type":"excalidrawlib","version":1,"library":[]}`,
}
export class ExcalidrawSettingTab extends PluginSettingTab {
@@ -23,27 +40,26 @@ export class ExcalidrawSettingTab extends PluginSettingTab {
display(): void {
let {containerEl} = this;
this.containerEl.empty();
new Setting(containerEl)
.setName('Excalidraw folder')
.setDesc('Default location for Excalidraw drawings. Leaving this empty means drawings will be saved to the Vault root.')
.addText(text => text
.setPlaceholder('excalidraw')
.setValue(this.plugin.settings.folder)
.onChange(async (value) => {
this.plugin.settings.folder = value;
await this.plugin.saveSettings();
}));
.setName('Excalidraw folder')
.setDesc('Default location for your Excalidraw drawings. Leaving this empty means drawings will be created in the Vault root.')
.addText(text => text
.setPlaceholder('Excalidraw')
.setValue(this.plugin.settings.folder)
.onChange(async (value) => {
this.plugin.settings.folder = value;
await this.plugin.saveSettings();
}));
new Setting(containerEl)
.setName('Excalidraw template file')
.setDesc('Full path to file containing the file you want to use as the template for new Excalidraw drawings. '+
'Note that Excalidraw files will have an extension of ".excalidraw" ' +
'Assuming your template is in the default excalidraw folder, the setting would be: excalidraw/Template.excalidraw')
'Note that Excalidraw files will have the extension ".excalidraw". ' +
'Assuming your template is in the default Excalidraw folder, the setting would be: Excalidraw/Template.excalidraw')
.addText(text => text
.setPlaceholder('excalidraw')
.setPlaceholder('Excalidraw/Template.excalidraw')
.setValue(this.plugin.settings.templateFilePath)
.onChange(async (value) => {
this.plugin.settings.templateFilePath = value;
@@ -51,7 +67,7 @@ export class ExcalidrawSettingTab extends PluginSettingTab {
}));
new Setting(containerEl)
.setName('Default width of embedded image')
.setName('Default width of embedded (transcluded) image')
.setDesc('The default width of an embedded drawing. You can specify a different ' +
'width when embedding an image using the [[drawing.excalidraw|100]] or ' +
'[[drawing.excalidraw|100x100]] format.')
@@ -61,6 +77,68 @@ export class ExcalidrawSettingTab extends PluginSettingTab {
.onChange(async (value) => {
this.plugin.settings.width = value;
await this.plugin.saveSettings();
this.plugin.triggerEmbedUpdates();
}));
this.containerEl.createEl('h1', {text: 'Embedded image settings'});
new Setting(containerEl)
.setName('Export image with background')
.setDesc('If turned off, the exported image will be transparent.')
.addToggle(toggle => toggle
.setValue(this.plugin.settings.exportWithBackground)
.onChange(async (value) => {
this.plugin.settings.exportWithBackground = value;
await this.plugin.saveSettings();
this.plugin.triggerEmbedUpdates();
}));
new Setting(containerEl)
.setName('Export image with theme')
.setDesc('Export the image matching the dark/light theme setting used for your drawing in Excalidraw. If turned off, ' +
'drawings created in drak mode will appear as they would in light mode.')
.addToggle(toggle => toggle
.setValue(this.plugin.settings.exportWithTheme)
.onChange(async (value) => {
this.plugin.settings.exportWithTheme = value;
await this.plugin.saveSettings();
this.plugin.triggerEmbedUpdates();
}));
new Setting(containerEl)
.setName('Auto-export SVG')
.setDesc('Automatically create an SVG export of your drawing matching the title of your "my drawing.excalidraw" file. ' +
'The plugin will save the .SVG file in the same folder as the drawing. '+
'You can use this file ("my drawing.svg") to embed your drawing into documents in a platform independent way. ' +
'While the auto export switch is on, this file will get updated every time you edit the excalidraw drawing with the matching name.')
.addToggle(toggle => toggle
.setValue(this.plugin.settings.autoexportSVG)
.onChange(async (value) => {
this.plugin.settings.autoexportSVG = value;
await this.plugin.saveSettings();
}));
new Setting(containerEl)
.setName('Auto-export PNG')
.setDesc('Same as the auto-export SVG, but for PNG.')
.addToggle(toggle => toggle
.setValue(this.plugin.settings.autoexportPNG)
.onChange(async (value) => {
this.plugin.settings.autoexportPNG = value;
await this.plugin.saveSettings();
}));
new Setting(containerEl)
.setName('Keep the .SVG and/or .PNG filenames in sync with the .excalidraw file')
.setDesc('When turned on, the plugin will automaticaly update the filename of the .SVG and/or .PNG files when the .excalidraw file in the same folder (and same name) is renamed. ' +
'The plugin will also automatically delete the .SVG and/or .PNG files when the .excalidraw file in the same folder (and same name) is deleted. ')
.addToggle(toggle => toggle
.setValue(this.plugin.settings.keepInSync)
.onChange(async (value) => {
this.plugin.settings.keepInSync = value;
await this.plugin.saveSettings();
}));
}
}

View File

@@ -15,4 +15,26 @@
.block-language-excalidraw {
text-align:center;
}
.excalidraw .github-corner {
display: none;
}
svg.excalidraw-svg-right-wrap {
float: right;
margin: 0px 0px 20px 20px;
}
svg.excalidraw-svg-left-wrap {
float: left;
margin: 0px 35px 20px 0px;
}
div.excalidraw-svg-right {
text-align: right;
}
div.excalidraw-svg-left {
text-align: left;
}

View File

@@ -1,5 +1,5 @@
{
"1.0.2": "0.11.13",
"1.0.1": "0.11.13",
"1.0.0": "0.11.13"
}
"1.0.7": "0.11.13",
"1.0.6": "0.11.13",
"1.0.5": "0.11.13"
}

206
yarn.lock
View File

@@ -28,7 +28,7 @@
"resolved" "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.13.15.tgz"
"version" "7.13.15"
"@babel/core@^7.0.0", "@babel/core@^7.0.0-0", "@babel/core@^7.1.0", "@babel/core@^7.12.3", "@babel/core@^7.13.0", "@babel/core@^7.4.0-0", "@babel/core@^7.7.5", "@babel/core@^7.8.4", "@babel/core@^7.9.0", "@babel/core@7 || ^7.0.0-rc.2":
"@babel/core@^7.0.0", "@babel/core@^7.0.0-0", "@babel/core@^7.1.0", "@babel/core@^7.12.3", "@babel/core@^7.13.0", "@babel/core@^7.3.3", "@babel/core@^7.4.0-0", "@babel/core@^7.7.5", "@babel/core@^7.8.4", "@babel/core@^7.9.0", "@babel/core@7 || ^7.0.0-rc.2":
"integrity" "sha512-6GXmNYeNjS2Uz+uls5jalOemgIhnTMeaXo+yBUA72kC2uX/8VW6XyhVIo2L8/q0goKQA3EVKx0KOQpVKSeWadQ=="
"resolved" "https://registry.npmjs.org/@babel/core/-/core-7.13.15.tgz"
"version" "7.13.15"
@@ -176,7 +176,7 @@
dependencies:
"@babel/types" "^7.13.12"
"@babel/helper-module-imports@^7.0.0", "@babel/helper-module-imports@^7.12.1", "@babel/helper-module-imports@^7.12.13", "@babel/helper-module-imports@^7.13.12":
"@babel/helper-module-imports@^7.0.0", "@babel/helper-module-imports@^7.10.4", "@babel/helper-module-imports@^7.12.1", "@babel/helper-module-imports@^7.12.13", "@babel/helper-module-imports@^7.13.12":
"integrity" "sha512-4cVvR2/1B693IuOvSI20xqqa/+bl7lqAMR59R4iu39R9aOX8/JoYY1sFaNvUMyMBGnHdwvJgUrzNLoUZxXypxA=="
"resolved" "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.13.12.tgz"
"version" "7.13.12"
@@ -914,7 +914,7 @@
"@babel/helper-create-regexp-features-plugin" "^7.12.13"
"@babel/helper-plugin-utils" "^7.12.13"
"@babel/preset-env@^7.8.4", "@babel/preset-env@^7.9.5":
"@babel/preset-env@^7.3.1", "@babel/preset-env@^7.8.4", "@babel/preset-env@^7.9.5":
"integrity" "sha512-D4JAPMXcxk69PKe81jRJ21/fP/uYdcTZ3hJDF5QX2HSI9bBxxYw/dumdR6dGumhjxlprHPE4XWoPaqzZUVy2MA=="
"resolved" "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.13.15.tgz"
"version" "7.13.15"
@@ -1072,7 +1072,7 @@
"@babel/types" "^7.4.4"
"esutils" "^2.0.2"
"@babel/preset-react@^7.9.4":
"@babel/preset-react@^7.0.0", "@babel/preset-react@^7.9.4":
"integrity" "sha512-gx+tDLIE06sRjKJkVtpZ/t3mzCDOnPG+ggHZG9lffUbX8+wC739x20YQc9V35Do6ZAxaUc/HhVHIiOzz5MvDmA=="
"resolved" "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.13.13.tgz"
"version" "7.13.13"
@@ -1190,10 +1190,10 @@
"minimatch" "^3.0.4"
"strip-json-comments" "^3.1.1"
"@excalidraw/excalidraw@0.6.0":
"integrity" "sha512-JC+Sg1T3AUJOX2xKp0/pCX7d845fta4nKc3Uw1V5Y2a5bi9AWSybjEPiuYsUxtKbCaKf1o6OaBgj/IXvPPIi4Q=="
"resolved" "https://registry.npmjs.org/@excalidraw/excalidraw/-/excalidraw-0.6.0.tgz"
"version" "0.6.0"
"@excalidraw/excalidraw@0.7.0":
"integrity" "sha512-D1yMXhOjWjCJIXT0puZD2QaGQ28QN2DNrVUQTsOouhPSELwhFoM7Exg/ulSwkLBI2l2KN7m39+2tj6QVijv4Sg=="
"resolved" "https://registry.npmjs.org/@excalidraw/excalidraw/-/excalidraw-0.7.0.tgz"
"version" "0.7.0"
"@hapi/address@2.x.x":
"integrity" "sha512-QD1PhQk+s31P1ixsX0H0Suoupp3VMXzIVMSwobR3F3MSUO2YCV0B7xqLcUw/Bh8yuvd3LhpyqLQWTNcRmp6IdQ=="
@@ -1455,6 +1455,19 @@
"schema-utils" "^2.6.5"
"source-map" "^0.7.3"
"@polka/url@^1.0.0-next.9":
"integrity" "sha512-6RglhutqrGFMO1MNUXp95RBuYIuc8wTnMAV5MUhLmjTOy78ncwOw7RgeQ/HeymkKXRhZd0s2DNrM1rL7unk3MQ=="
"resolved" "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.12.tgz"
"version" "1.0.0-next.12"
"@rollup/plugin-babel@5.3.0":
"integrity" "sha512-9uIC8HZOnVLrLHxayq/PTzw+uS25E14KPUBh5ktF+18Mjo5yK0ToMMx6epY0uEgkjwJw0aBW4x2horYXh8juWw=="
"resolved" "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.0.tgz"
"version" "5.3.0"
dependencies:
"@babel/helper-module-imports" "^7.10.4"
"@rollup/pluginutils" "^3.1.0"
"@rollup/plugin-commonjs@^15.1.0":
"integrity" "sha512-xCQqz4z/o0h2syQ7d9LskIMvBSH4PX5PjYdpSSvgS+pQik3WahkQVNWg3D8XJeYjZoVWnIUQYDghuEMRGrmQYQ=="
"resolved" "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-15.1.0.tgz"
@@ -1646,7 +1659,7 @@
"resolved" "https://registry.npmjs.org/@types/anymatch/-/anymatch-1.3.1.tgz"
"version" "1.3.1"
"@types/babel__core@^7.0.0", "@types/babel__core@^7.1.7":
"@types/babel__core@^7.0.0", "@types/babel__core@^7.1.7", "@types/babel__core@^7.1.9":
"integrity" "sha512-zGZJzzBUVDo/eV6KgbE0f0ZI7dInEYvo12Rb70uNQDshC3SkRMb67ja0GgRHZgAX3Za6rhaWlvbDO8rrGyAb1g=="
"resolved" "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.14.tgz"
"version" "7.1.14"
@@ -2143,6 +2156,11 @@
"resolved" "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz"
"version" "4.2.2"
"aakansha-excalidraw@0.7.0-draft":
"integrity" "sha512-bafyy/qQES3E+uI7YHGuMzIh2kKlTKx+NYYpHwR5QhdAdDsA+Tru56Yglo+8cLCSDncN/KyEb1ffQlYJyBqQwA=="
"resolved" "https://registry.npmjs.org/aakansha-excalidraw/-/aakansha-excalidraw-0.7.0-draft.tgz"
"version" "0.7.0-draft"
"abab@^2.0.3", "abab@^2.0.5":
"integrity" "sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q=="
"resolved" "https://registry.npmjs.org/abab/-/abab-2.0.5.tgz"
@@ -2174,6 +2192,11 @@
"resolved" "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz"
"version" "7.2.0"
"acorn-walk@^8.0.0":
"integrity" "sha512-+bpA9MJsHdZ4bgfDcpk0ozQyhhVct7rzOmO0s1IIr0AGGgKBljss8n2zp11rRP2wid5VGeh04CgeKzgat5/25A=="
"resolved" "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.0.2.tgz"
"version" "8.0.2"
"acorn@^6.0.0 || ^7.0.0 || ^8.0.0", "acorn@^7.1.0", "acorn@^7.1.1", "acorn@^7.4.0":
"integrity" "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A=="
"resolved" "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz"
@@ -2184,6 +2207,11 @@
"resolved" "https://registry.npmjs.org/acorn/-/acorn-6.4.2.tgz"
"version" "6.4.2"
"acorn@^8.0.4":
"integrity" "sha512-xYiIVjNuqtKXMxlRMDc6mZUhXehod4a3gbZ1qRlM7icK4EbxUFNLhWoPblCvFtB2Y9CIqHP3CF/rdxLItaQv8g=="
"resolved" "https://registry.npmjs.org/acorn/-/acorn-8.1.1.tgz"
"version" "8.1.1"
"acorn@^8.1.0":
"integrity" "sha512-xYiIVjNuqtKXMxlRMDc6mZUhXehod4a3gbZ1qRlM7icK4EbxUFNLhWoPblCvFtB2Y9CIqHP3CF/rdxLItaQv8g=="
"resolved" "https://registry.npmjs.org/acorn/-/acorn-8.1.1.tgz"
@@ -3364,6 +3392,15 @@
"strip-ansi" "^6.0.0"
"wrap-ansi" "^6.2.0"
"cliui@^7.0.2":
"integrity" "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ=="
"resolved" "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz"
"version" "7.0.4"
dependencies:
"string-width" "^4.2.0"
"strip-ansi" "^6.0.0"
"wrap-ansi" "^7.0.0"
"clone-deep@^4.0.1":
"integrity" "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ=="
"resolved" "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz"
@@ -3469,6 +3506,11 @@
"resolved" "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz"
"version" "4.1.1"
"commander@^6.2.0":
"integrity" "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA=="
"resolved" "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz"
"version" "6.2.1"
"common-tags@^1.8.0":
"integrity" "sha512-6P6g0uetGpW/sdyUy/iQQCbFF0kWVMSIVSyYz7Zgjcgh8mgw8PQzDNZeyZ5DQ2gM7LBoZPHmnjz8rUthkBG5tw=="
"resolved" "https://registry.npmjs.org/common-tags/-/common-tags-1.8.0.tgz"
@@ -3749,6 +3791,11 @@
"resolved" "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-1.0.0.tgz"
"version" "1.0.0"
"cyclist@^1.0.1":
"integrity" "sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk="
"resolved" "https://registry.npmjs.org/cyclist/-/cyclist-1.0.1.tgz"
"version" "1.0.1"
"css-blank-pseudo@^0.1.4":
"integrity" "sha512-LHz35Hr83dnFeipc7oqFDmsjHdljj3TQtxGGiNWSOsTLIAubSm4TEz8qCaKFpk7idaQ1GfWscF4E6mgpBysA1w=="
"resolved" "https://registry.npmjs.org/css-blank-pseudo/-/css-blank-pseudo-0.1.4.tgz"
@@ -3960,11 +4007,6 @@
"resolved" "https://registry.npmjs.org/csstype/-/csstype-3.0.6.tgz"
"version" "3.0.6"
"cyclist@^1.0.1":
"integrity" "sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk="
"resolved" "https://registry.npmjs.org/cyclist/-/cyclist-1.0.1.tgz"
"version" "1.0.1"
"d@^1.0.1", "d@1":
"integrity" "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA=="
"resolved" "https://registry.npmjs.org/d/-/d-1.0.1.tgz"
@@ -4323,7 +4365,7 @@
"resolved" "https://registry.npmjs.org/dotenv/-/dotenv-8.2.0.tgz"
"version" "8.2.0"
"duplexer@^0.1.1":
"duplexer@^0.1.1", "duplexer@^0.1.2":
"integrity" "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg=="
"resolved" "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz"
"version" "0.1.2"
@@ -5333,7 +5375,7 @@
"resolved" "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz"
"version" "1.0.0-beta.2"
"get-caller-file@^2.0.1":
"get-caller-file@^2.0.1", "get-caller-file@^2.0.5":
"integrity" "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg=="
"resolved" "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz"
"version" "2.0.5"
@@ -5504,6 +5546,13 @@
"resolved" "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz"
"version" "1.3.0"
"gzip-size@^6.0.0":
"integrity" "sha512-ax7ZYomf6jqPTQ4+XCpUGyXKHk5WweS+e05MBO4/y3WJ5RkmPXNKvX+bx1behVILVwr6JSQvZAku021CHPXG3Q=="
"resolved" "https://registry.npmjs.org/gzip-size/-/gzip-size-6.0.0.tgz"
"version" "6.0.0"
dependencies:
"duplexer" "^0.1.2"
"gzip-size@5.1.1":
"integrity" "sha512-FNHi6mmoHvs1mxZAds4PpdCS6QG8B4C1krxJsMutgxl5t3+GlRTzzI3NEkifXx2pVsOvJdOGSmIgDhQ55FwdPA=="
"resolved" "https://registry.npmjs.org/gzip-size/-/gzip-size-5.1.1.tgz"
@@ -7427,6 +7476,11 @@
dependencies:
"mime-db" "1.47.0"
"mime@^2.3.1":
"integrity" "sha512-tqkh47FzKeCPD2PUiPB6pkbMzsCasjxAfC62/Wap5qrUWcb+sFasXUC5I3gYM5iBM8v/Qpn4UK0x+j0iHyFPDg=="
"resolved" "https://registry.npmjs.org/mime/-/mime-2.5.2.tgz"
"version" "2.5.2"
"mime@^2.4.4":
"integrity" "sha512-tqkh47FzKeCPD2PUiPB6pkbMzsCasjxAfC62/Wap5qrUWcb+sFasXUC5I3gYM5iBM8v/Qpn4UK0x+j0iHyFPDg=="
"resolved" "https://registry.npmjs.org/mime/-/mime-2.5.2.tgz"
@@ -7596,10 +7650,10 @@
"dns-packet" "^1.3.1"
"thunky" "^1.0.2"
"nanoid@^3.1.20":
"integrity" "sha512-a1cQNyczgKbLX9jwbS/+d7W8fX/RfgYR7lVWwWOGIPNgK2m0MWvrGF6/m4kk6U3QcFMnZf3RIhL0v2Jgh/0Uxw=="
"resolved" "https://registry.npmjs.org/nanoid/-/nanoid-3.1.20.tgz"
"version" "3.1.20"
"nanoid@^3.1.20", "nanoid@^3.1.22":
"integrity" "sha512-/2ZUaJX2ANuLtTvqTlgqBQNJoQO398KyJgZloL0PZkC0dpysjncRUPsFe3DUPzz/y3h+u7C46np8RMuvF3jsSQ=="
"resolved" "https://registry.npmjs.org/nanoid/-/nanoid-3.1.22.tgz"
"version" "3.1.22"
"nanomatch@^1.2.9":
"integrity" "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA=="
@@ -7932,7 +7986,7 @@
dependencies:
"mimic-fn" "^2.1.0"
"open@^7.0.2":
"open@^7.0.2", "open@^7.4.2":
"integrity" "sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q=="
"resolved" "https://registry.npmjs.org/open/-/open-7.4.2.tgz"
"version" "7.4.2"
@@ -7940,6 +7994,11 @@
"is-docker" "^2.0.0"
"is-wsl" "^2.1.1"
"opener@^1.5.2":
"integrity" "sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A=="
"resolved" "https://registry.npmjs.org/opener/-/opener-1.5.2.tgz"
"version" "1.5.2"
"opn@^5.5.0":
"integrity" "sha512-PqHpggC9bLV0VeWcdKhkpxY+3JTzetLSqTCWL/z/tFIbI6G8JCjondXklT1JinczLz2Xib62sSp0T/gKT4KksA=="
"resolved" "https://registry.npmjs.org/opn/-/opn-5.5.0.tgz"
@@ -9424,14 +9483,14 @@
"strip-ansi" "6.0.0"
"text-table" "0.2.0"
"react-dom@^17.0.1", "react-dom@17.0.0":
"integrity" "sha512-OGnFbxCjI2TMAZYMVxi4hqheJiN8rCEVVrL7XIGzCB6beNc4Am8M47HtkvxODZw9QgjmAPKpLba9FTu4fC1byA=="
"resolved" "https://registry.npmjs.org/react-dom/-/react-dom-17.0.0.tgz"
"version" "17.0.0"
"react-dom@^17.0.1", "react-dom@17.0.1":
"integrity" "sha512-6eV150oJZ9U2t9svnsspTMrWNyHc6chX0KzDeAOXftRa8bNeOKTTfCJ7KorIwenkHd2xqVTBTCZd79yk/lx/Ug=="
"resolved" "https://registry.npmjs.org/react-dom/-/react-dom-17.0.1.tgz"
"version" "17.0.1"
dependencies:
"loose-envify" "^1.1.0"
"object-assign" "^4.1.1"
"scheduler" "^0.20.0"
"scheduler" "^0.20.1"
"react-error-overlay@^6.0.9":
"integrity" "sha512-nQTTcUu+ATDbrSD1BZHr5kgSD4oF8OFjxun8uAaL8RwPBacGBNPf/yAuVVdx17N8XNzRDMrZ9XcKZHCjPW+9ew=="
@@ -9519,10 +9578,10 @@
optionalDependencies:
"fsevents" "^2.1.3"
"react@^17.0.1", "react@17.0.0":
"integrity" "sha512-rG9bqS3LMuetoSUKHN8G3fMNuQOePKDThK6+2yXFWtoeTDLVNh/QCaxT+Jr+rNf4lwNXpx+atdn3Aa0oi8/6eQ=="
"resolved" "https://registry.npmjs.org/react/-/react-17.0.0.tgz"
"version" "17.0.0"
"react@^17.0.1", "react@17.0.1":
"integrity" "sha512-lG9c9UuMHdcAexXtigOZLX8exLWkW0Ku29qPRU8uhF2R9BN96dLCt0psvzPLlHc5OWkgymP3qwTRgbnw5BKx3w=="
"resolved" "https://registry.npmjs.org/react/-/react-17.0.1.tgz"
"version" "17.0.1"
dependencies:
"loose-envify" "^1.1.0"
"object-assign" "^4.1.1"
@@ -10088,6 +10147,16 @@
"serialize-javascript" "^4.0.0"
"terser" "^4.6.2"
"rollup-plugin-visualizer@^5.4.1":
"integrity" "sha512-mwrUIfOamkCw3dCtLvgnn/H0rvNSDA1RAe0sO9uHBpmdf86j/xOX/2yeCrVh2Ia/gCGLG846JB00MW0chq8CHQ=="
"resolved" "https://registry.npmjs.org/rollup-plugin-visualizer/-/rollup-plugin-visualizer-5.4.1.tgz"
"version" "5.4.1"
dependencies:
"nanoid" "^3.1.22"
"open" "^7.4.2"
"source-map" "^0.7.3"
"yargs" "^16.2.0"
"rollup-pluginutils@^2.8.1", "rollup-pluginutils@^2.8.2":
"integrity" "sha512-EEp9NhnUkwY8aif6bxgovPHMoMoNr2FulJziTndpt5H9RdwC47GSGuII9XxpSdzVGM0GWrNPHV6ie1LTNJPaLQ=="
"resolved" "https://registry.npmjs.org/rollup-pluginutils/-/rollup-pluginutils-2.8.2.tgz"
@@ -10095,7 +10164,7 @@
dependencies:
"estree-walker" "^0.6.1"
"rollup@^1.20.0 || ^2.0.0", "rollup@^1.20.0||^2.0.0", "rollup@^2.14.0", "rollup@^2.22.0", "rollup@>=0.60.0 <3", "rollup@>=0.66.0 <3", "rollup@2.45.2":
"rollup@^1.20.0 || ^2.0.0", "rollup@^1.20.0||^2.0.0", "rollup@^2.0.0", "rollup@^2.14.0", "rollup@^2.22.0", "rollup@>=0.60.0 <3", "rollup@>=0.66.0 <3", "rollup@2.45.2":
"integrity" "sha512-kRRU7wXzFHUzBIv0GfoFFIN3m9oteY4uAsKllIpQDId5cfnkWF2J130l+27dzDju0E6MScKiV0ZM5Bw8m4blYQ=="
"resolved" "https://registry.npmjs.org/rollup/-/rollup-2.45.2.tgz"
"version" "2.45.2"
@@ -10205,7 +10274,7 @@
dependencies:
"xmlchars" "^2.2.0"
"scheduler@^0.20.0":
"scheduler@^0.20.1":
"integrity" "sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ=="
"resolved" "https://registry.npmjs.org/scheduler/-/scheduler-0.20.2.tgz"
"version" "0.20.2"
@@ -10458,6 +10527,15 @@
dependencies:
"is-arrayish" "^0.3.1"
"sirv@^1.0.7":
"integrity" "sha512-SR36i3/LSWja7AJNRBz4fF/Xjpn7lQFI30tZ434dIy+bitLYSP+ZEenHg36i23V2SGEz+kqjksg0uOGZ5LPiqg=="
"resolved" "https://registry.npmjs.org/sirv/-/sirv-1.0.11.tgz"
"version" "1.0.11"
dependencies:
"@polka/url" "^1.0.0-next.9"
"mime" "^2.3.1"
"totalist" "^1.0.0"
"sisteransi@^1.0.5":
"integrity" "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg=="
"resolved" "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz"
@@ -11202,6 +11280,11 @@
"resolved" "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz"
"version" "1.0.0"
"totalist@^1.0.0":
"integrity" "sha512-gduQwd1rOdDMGxFG1gEvhV88Oirdo2p+KjoYFU7k2g+i7n6AFFbDQ5kMPUsW0pNbfQsB/cwXvT1i4Bue0s9g5g=="
"resolved" "https://registry.npmjs.org/totalist/-/totalist-1.1.0.tgz"
"version" "1.1.0"
"tough-cookie@^2.3.3":
"integrity" "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g=="
"resolved" "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz"
@@ -11271,11 +11354,6 @@
dependencies:
"tslib" "^1.8.1"
"tty-browserify@0.0.0":
"integrity" "sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY="
"resolved" "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz"
"version" "0.0.0"
"tunnel-agent@^0.6.0":
"integrity" "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0="
"resolved" "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz"
@@ -11367,6 +11445,11 @@
"resolved" "https://registry.npmjs.org/typescript/-/typescript-4.1.5.tgz"
"version" "4.1.5"
"tty-browserify@0.0.0":
"integrity" "sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY="
"resolved" "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz"
"version" "0.0.0"
"uglify-js@^2.7.4":
"integrity" "sha1-KcVzMUgFe7Th913zW3qcty5qWd0="
"resolved" "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz"
@@ -11690,6 +11773,21 @@
"resolved" "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-6.1.0.tgz"
"version" "6.1.0"
"webpack-bundle-analyzer@^4.4.1":
"integrity" "sha512-j5m7WgytCkiVBoOGavzNokBOqxe6Mma13X1asfVYtKWM3wxBiRRu1u1iG0Iol5+qp9WgyhkMmBAcvjEfJ2bdDw=="
"resolved" "https://registry.npmjs.org/webpack-bundle-analyzer/-/webpack-bundle-analyzer-4.4.1.tgz"
"version" "4.4.1"
dependencies:
"acorn" "^8.0.4"
"acorn-walk" "^8.0.0"
"chalk" "^4.1.0"
"commander" "^6.2.0"
"gzip-size" "^6.0.0"
"lodash" "^4.17.20"
"opener" "^1.5.2"
"sirv" "^1.0.7"
"ws" "^7.3.1"
"webpack-dev-middleware@^3.7.2":
"integrity" "sha512-djelc/zGiz9nZj/U7PTBi2ViorGJXEWo/3ltkPbDyxCXhhEXkW0ce99falaok4TPj+AsxLiXJR0EBOb0zh9fKQ=="
"resolved" "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-3.7.3.tgz"
@@ -12059,6 +12157,15 @@
"string-width" "^4.1.0"
"strip-ansi" "^6.0.0"
"wrap-ansi@^7.0.0":
"integrity" "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="
"resolved" "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz"
"version" "7.0.0"
dependencies:
"ansi-styles" "^4.0.0"
"string-width" "^4.1.0"
"strip-ansi" "^6.0.0"
"wrappy@1":
"integrity" "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
"resolved" "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz"
@@ -12081,7 +12188,7 @@
dependencies:
"async-limiter" "~1.0.0"
"ws@^7.4.4":
"ws@^7.3.1", "ws@^7.4.4":
"integrity" "sha512-Qm8k8ojNQIMx7S+Zp8u/uHOx7Qazv3Yv4q68MiWWWOJhiwG5W3x7iqmRtJo8xxrciZUY4vRxUTJCKuRnF28ZZw=="
"resolved" "https://registry.npmjs.org/ws/-/ws-7.4.4.tgz"
"version" "7.4.4"
@@ -12106,6 +12213,11 @@
"resolved" "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz"
"version" "4.0.3"
"y18n@^5.0.5":
"integrity" "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA=="
"resolved" "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz"
"version" "5.0.8"
"yallist@^3.0.2":
"integrity" "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="
"resolved" "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz"
@@ -12147,6 +12259,11 @@
"camelcase" "^5.0.0"
"decamelize" "^1.2.0"
"yargs-parser@^20.2.2":
"integrity" "sha512-FiNkvbeHzB/syOjIUxFDCnhSfzAL8R5vs40MgLFBorXACCOAEaWu0gRZl14vG8MR9AOJIZbmkjhusqBYZ3HTHw=="
"resolved" "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.7.tgz"
"version" "20.2.7"
"yargs@^13.3.2":
"integrity" "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw=="
"resolved" "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz"
@@ -12180,6 +12297,19 @@
"y18n" "^4.0.0"
"yargs-parser" "^18.1.2"
"yargs@^16.2.0":
"integrity" "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw=="
"resolved" "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz"
"version" "16.2.0"
dependencies:
"cliui" "^7.0.2"
"escalade" "^3.1.1"
"get-caller-file" "^2.0.5"
"require-directory" "^2.1.1"
"string-width" "^4.2.0"
"y18n" "^5.0.5"
"yargs-parser" "^20.2.2"
"yargs@~3.10.0":
"integrity" "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E="
"resolved" "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz"