diff --git a/.pylintrc b/.pylintrc index be4cbe086b..b00f3ed384 100644 --- a/.pylintrc +++ b/.pylintrc @@ -412,7 +412,7 @@ max-attributes=20 max-bool-expr=5 # Maximum number of branch for function / method body -max-branches=12 +max-branches=15 # Maximum number of locals for function / method body max-locals=15 diff --git a/.pylintrc39 b/.pylintrc39 index 05d1d677b9..e3927edeab 100644 --- a/.pylintrc39 +++ b/.pylintrc39 @@ -513,7 +513,7 @@ max-attributes=20 max-bool-expr=5 # Maximum number of branch for function / method body. -max-branches=12 +max-branches=15 # Maximum number of locals for function / method body. max-locals=15 diff --git a/@plotly/dash-generator-test-component-typescript/package-lock.json b/@plotly/dash-generator-test-component-typescript/package-lock.json index bf28ff7842..28cf26e996 100644 --- a/@plotly/dash-generator-test-component-typescript/package-lock.json +++ b/@plotly/dash-generator-test-component-typescript/package-lock.json @@ -3364,9 +3364,9 @@ "dev": true }, "node_modules/c8": { - "version": "7.11.3", - "resolved": "https://registry.npmjs.org/c8/-/c8-7.11.3.tgz", - "integrity": "sha512-6YBmsaNmqRm9OS3ZbIiL2EZgi1+Xc4O24jL3vMYGE6idixYuGdy76rIfIdltSKDj9DpLNrcXSonUTR1miBD0wA==", + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/c8/-/c8-7.13.0.tgz", + "integrity": "sha512-/NL4hQTv1gBL6J6ei80zu3IiTrmePDKXKXOTLpHvcIWZTVYQlDhVWjjWvkhICylE8EwwnMVzDZugCvdx0/DIIA==", "dev": true, "dependencies": { "@bcoe/v8-coverage": "^0.2.3", @@ -8174,9 +8174,9 @@ } }, "node_modules/tslib": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", - "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", "dev": true }, "node_modules/type-detect": { @@ -11146,9 +11146,9 @@ "dev": true }, "c8": { - "version": "7.11.3", - "resolved": "https://registry.npmjs.org/c8/-/c8-7.11.3.tgz", - "integrity": "sha512-6YBmsaNmqRm9OS3ZbIiL2EZgi1+Xc4O24jL3vMYGE6idixYuGdy76rIfIdltSKDj9DpLNrcXSonUTR1miBD0wA==", + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/c8/-/c8-7.13.0.tgz", + "integrity": "sha512-/NL4hQTv1gBL6J6ei80zu3IiTrmePDKXKXOTLpHvcIWZTVYQlDhVWjjWvkhICylE8EwwnMVzDZugCvdx0/DIIA==", "dev": true, "requires": { "@bcoe/v8-coverage": "^0.2.3", @@ -14690,9 +14690,9 @@ } }, "tslib": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", - "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", "dev": true }, "type-detect": { diff --git a/@plotly/dash-generator-test-component-typescript/src/components/MemoTypeScriptComponent.tsx b/@plotly/dash-generator-test-component-typescript/src/components/MemoTypeScriptComponent.tsx index d8074b48c9..a5baf40707 100644 --- a/@plotly/dash-generator-test-component-typescript/src/components/MemoTypeScriptComponent.tsx +++ b/@plotly/dash-generator-test-component-typescript/src/components/MemoTypeScriptComponent.tsx @@ -14,4 +14,4 @@ const MemoTypeScriptComponent = (props: TypescriptComponentProps) => (
{props.required_string}
); -export default React.memo(MemoTypeScriptComponent); +export default React.memo(MemoTypeScriptComponent, () => true); diff --git a/@plotly/dash-generator-test-component-typescript/src/components/StandardComponent.react.js b/@plotly/dash-generator-test-component-typescript/src/components/StandardComponent.react.js index 5396f04712..e676dc2d06 100644 --- a/@plotly/dash-generator-test-component-typescript/src/components/StandardComponent.react.js +++ b/@plotly/dash-generator-test-component-typescript/src/components/StandardComponent.react.js @@ -17,4 +17,4 @@ StandardComponent.propTypes = { children: PropTypes.node, } -export default StandardComponent +export default React.memo(StandardComponent, (prevProps,nextProps) => true) diff --git a/@plotly/dash-jupyterlab/.prettierrc b/@plotly/dash-jupyterlab/.prettierrc new file mode 100644 index 0000000000..92cde390ac --- /dev/null +++ b/@plotly/dash-jupyterlab/.prettierrc @@ -0,0 +1,3 @@ +{ + "singleQuote": true +} \ No newline at end of file diff --git a/@plotly/dash-jupyterlab/lib/index.d.ts b/@plotly/dash-jupyterlab/lib/index.d.ts new file mode 100644 index 0000000000..0b7e1f8c96 --- /dev/null +++ b/@plotly/dash-jupyterlab/lib/index.d.ts @@ -0,0 +1,7 @@ +import { JupyterFrontEndPlugin } from '@jupyterlab/application'; +import '../style/index.css'; +/** + * Initialization data for the jupyterlab-dash extension. + */ +declare const extension: JupyterFrontEndPlugin; +export default extension; diff --git a/@plotly/dash-jupyterlab/lib/index.js b/@plotly/dash-jupyterlab/lib/index.js new file mode 100644 index 0000000000..109b9ee851 --- /dev/null +++ b/@plotly/dash-jupyterlab/lib/index.js @@ -0,0 +1,115 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const application_1 = require("@jupyterlab/application"); +const coreutils_1 = require("@jupyterlab/coreutils"); +const notebook_1 = require("@jupyterlab/notebook"); +const console_1 = require("@jupyterlab/console"); +const widgets_1 = require("@lumino/widgets"); +require("../style/index.css"); +class DashIFrameWidget extends widgets_1.Widget { + /** + * Construct a new DashIFrameWidget. + */ + constructor(port, url) { + super(); + this.id = port; + this.title.label = `Dash (port: ${port})`; + this.title.closable = true; + this.addClass('jp-dashWidget'); + // Add jp-IFrame class to keep drag events from being lost to the iframe + // See https://github.com/phosphorjs/phosphor/issues/305 + // See https://github.com/jupyterlab/jupyterlab/blob/master/packages/apputils/style/iframe.css#L17-L35 + this.addClass('jp-IFrame'); + const serviceUrl = url; + const iframeElement = document.createElement('iframe'); + iframeElement.setAttribute('baseURI', serviceUrl); + this.iframe = iframeElement; + this.iframe.src = serviceUrl; + this.iframe.id = 'iframe-' + this.id; + this.node.appendChild(this.iframe); + } + /** + * Handle update requests for the widget. + */ + onUpdateRequest(msg) { + this.iframe.src += ''; + } +} +function activate(app, restorer, notebooks, consoles) { + // Declare a widget variable + let widgets = new Map(); + // Watch notebook creation + notebooks.widgetAdded.connect((sender, nbPanel) => { + // const session = nbPanel.session; + const sessionContext = nbPanel.sessionContext; + sessionContext.ready.then(() => { + const session = sessionContext.session; + let kernel = session.kernel; + registerCommTarget(kernel, widgets, app); + }); + }); + // Watch console creation + consoles.widgetAdded.connect((sender, consolePanel) => { + const sessionContext = consolePanel.sessionContext; + sessionContext.ready.then(() => { + const session = sessionContext.session; + let kernel = session.kernel; + registerCommTarget(kernel, widgets, app); + }); + }); +} +function registerCommTarget(kernel, widgets, app) { + kernel.registerCommTarget('dash', (comm, msg) => { + comm.onMsg = (msg) => { + let msgData = msg.content.data; + if (msgData.type === 'show') { + let widget; + if (!widgets.has(msgData.port)) { + // Create a new widget + widget = new DashIFrameWidget(msgData.port, msgData.url); + widget.update(); + widgets.set(msgData.port, widget); + // Add instance tracker stuff + } + else { + widget = widgets.get(msgData.port); + } + if (!widget.isAttached) { + // Attach the widget to the main work area + // if it's not there + app.shell.add(widget, 'main'); + widget.update(); + } + else { + // Refresh the widget + widget.update(); + } + // Activate the widget + app.shell.activateById(widget.id); + } + else if (msgData.type === 'base_url_request') { + // Build server url and base subpath. + const baseUrl = coreutils_1.PageConfig.getBaseUrl(); + const baseSubpath = coreutils_1.PageConfig.getOption('baseUrl'); + const n = baseUrl.lastIndexOf(baseSubpath); + const serverUrl = baseUrl.slice(0, n); + comm.send({ + type: 'base_url_response', + server_url: serverUrl, + base_subpath: baseSubpath, + frontend: "jupyterlab", + }); + } + }; + }); +} +/** + * Initialization data for the jupyterlab-dash extension. + */ +const extension = { + id: 'jupyterlab_dash', + autoStart: true, + requires: [application_1.ILayoutRestorer, notebook_1.INotebookTracker, console_1.IConsoleTracker], + activate: activate +}; +exports.default = extension; diff --git a/@plotly/dash-jupyterlab/package.json b/@plotly/dash-jupyterlab/package.json new file mode 100644 index 0000000000..d81245ad2a --- /dev/null +++ b/@plotly/dash-jupyterlab/package.json @@ -0,0 +1,48 @@ +{ + "name": "@plotly/dash-jupyterlab", + "version": "0.4.3", + "description": "A JupyterLab extension for rendering Plotly Dash apps", + "keywords": [ + "jupyter", + "jupyterlab", + "jupyterlab-extension" + ], + "homepage": "https://github.com/plotly/dash", + "bugs": { + "url": "https://github.com/plotly/dash/issues" + }, + "license": "MIT", + "author": "Plotly", + "files": [ + "lib/**/*.{d.ts,eot,gif,html,jpg,js,js.map,json,png,svg,woff2,ttf}", + "style/**/*.{css,eot,gif,html,jpg,json,png,svg,woff2,ttf}" + ], + "main": "lib/index.js", + "types": "lib/index.d.ts", + "repository": { + "type": "git", + "url": "git+https://github.com/plotly/dash.git" + }, + "scripts": { + "build": "tsc", + "build:pack": "jlpm run prepare && jlpm pack --filename ../../dash/labextension/dist/dash-jupyterlab.tgz && jlpm run build:copy", + "build:copy": "cp package.json ../../dash/labextension/dist/package.json", + "clean": "rimraf lib", + "prepare": "mkdir -p ../../dash/labextension/dist && jlpm run clean && jlpm run build", + "prettier": "prettier --write '{!(package),src/**,!(lib)/**}{.js,.jsx,.ts,.tsx,.css,.json,.md}'", + "watch": "tsc -w" + }, + "dependencies": { + "@jupyterlab/application": "^2.0.0 || ^3.0.0", + "@jupyterlab/notebook": "^2.0.0 || ^3.0.0", + "@jupyterlab/console": "^2.0.0 || ^3.0.0" + }, + "devDependencies": { + "prettier": "2.0.5", + "rimraf": "3.0.2", + "typescript": "3.9.3" + }, + "jupyterlab": { + "extension": true + } +} diff --git a/@plotly/dash-jupyterlab/src/index.ts b/@plotly/dash-jupyterlab/src/index.ts new file mode 100644 index 0000000000..5dc58fd2db --- /dev/null +++ b/@plotly/dash-jupyterlab/src/index.ts @@ -0,0 +1,163 @@ +import { + ILayoutRestorer, + JupyterFrontEnd, + JupyterFrontEndPlugin +} from '@jupyterlab/application'; + +import { PageConfig } from '@jupyterlab/coreutils'; + +import { INotebookTracker, NotebookPanel } from '@jupyterlab/notebook'; + +import { KernelMessage, Kernel } from '@jupyterlab/services'; + +import { IConsoleTracker } from '@jupyterlab/console'; + +import { Message } from '@lumino/messaging'; + +import { Widget } from '@lumino/widgets'; + +import '../style/index.css'; + +class DashIFrameWidget extends Widget { + /** + * Construct a new DashIFrameWidget. + */ + constructor(port: string, url: string) { + super(); + + this.id = port; + this.title.label = `Dash (port: ${port})`; + this.title.closable = true; + this.addClass('jp-dashWidget'); + + // Add jp-IFrame class to keep drag events from being lost to the iframe + // See https://github.com/phosphorjs/phosphor/issues/305 + // See https://github.com/jupyterlab/jupyterlab/blob/master/packages/apputils/style/iframe.css#L17-L35 + this.addClass('jp-IFrame'); + + const serviceUrl = url; + const iframeElement = document.createElement('iframe'); + iframeElement.setAttribute('baseURI', serviceUrl); + this.iframe = iframeElement; + this.iframe.src = serviceUrl; + this.iframe.id = 'iframe-' + this.id; + + this.node.appendChild(this.iframe); + } + + /** + * The image element associated with the widget. + */ + readonly iframe: HTMLIFrameElement; + + /** + * Handle update requests for the widget. + */ + onUpdateRequest(msg: Message): void { + this.iframe.src += ''; + } +} + +interface DashMessageData { + type: string; + port: string; + url: string; +} + +function activate( + app: JupyterFrontEnd, + restorer: ILayoutRestorer, + notebooks: INotebookTracker, + consoles: IConsoleTracker +) { + + // Declare a widget variable + let widgets = new Map(); + + // Watch notebook creation + notebooks.widgetAdded.connect((sender, nbPanel: NotebookPanel) => { + // const session = nbPanel.session; + const sessionContext = nbPanel.sessionContext; + sessionContext.ready.then(() => { + const session = sessionContext.session; + let kernel = session.kernel; + registerCommTarget(kernel, widgets, app); + }) + }); + + // Watch console creation + consoles.widgetAdded.connect((sender, consolePanel) => { + const sessionContext = consolePanel.sessionContext; + sessionContext.ready.then(() => { + const session = sessionContext.session; + let kernel = session.kernel; + registerCommTarget(kernel, widgets, app); + }) + }); +} + +function registerCommTarget( + kernel: Kernel.IKernelConnection, + widgets: Map, + app: JupyterFrontEnd +) { + kernel.registerCommTarget( + 'dash', + (comm: Kernel.IComm, msg: KernelMessage.ICommOpenMsg) => { + comm.onMsg = (msg: KernelMessage.ICommMsgMsg) => { + let msgData = (msg.content.data as unknown) as DashMessageData; + if (msgData.type === 'show') { + let widget: DashIFrameWidget; + if (!widgets.has(msgData.port)) { + // Create a new widget + widget = new DashIFrameWidget(msgData.port, msgData.url); + widget.update(); + widgets.set(msgData.port, widget); + + // Add instance tracker stuff + } else { + widget = widgets.get(msgData.port); + } + + if (!widget.isAttached) { + // Attach the widget to the main work area + // if it's not there + app.shell.add(widget, 'main'); + widget.update(); + } else { + // Refresh the widget + widget.update(); + } + + // Activate the widget + app.shell.activateById(widget.id); + } else if (msgData.type === 'base_url_request') { + + // Build server url and base subpath. + const baseUrl = PageConfig.getBaseUrl(); + const baseSubpath = PageConfig.getOption('baseUrl'); + const n = baseUrl.lastIndexOf(baseSubpath) + const serverUrl = baseUrl.slice(0, n) + comm.send({ + type: 'base_url_response', + server_url: serverUrl, + base_subpath: baseSubpath, + frontend: "jupyterlab", + }); + } + }; + } + ); +} + +/** + * Initialization data for the jupyterlab-dash extension. + */ +const extension: JupyterFrontEndPlugin = { + id: 'jupyterlab_dash', + autoStart: true, + requires: [ILayoutRestorer, INotebookTracker, IConsoleTracker], + activate: activate +}; + +export default extension; diff --git a/@plotly/dash-jupyterlab/style/index.css b/@plotly/dash-jupyterlab/style/index.css new file mode 100644 index 0000000000..898111f52e --- /dev/null +++ b/@plotly/dash-jupyterlab/style/index.css @@ -0,0 +1,6 @@ +.jp-dashWidget iframe { + height: 100%; + width: 100%; + padding: 20px; + box-sizing: border-box; +} diff --git a/@plotly/dash-jupyterlab/tsconfig.json b/@plotly/dash-jupyterlab/tsconfig.json new file mode 100644 index 0000000000..6a6738ea31 --- /dev/null +++ b/@plotly/dash-jupyterlab/tsconfig.json @@ -0,0 +1,18 @@ +{ + "compilerOptions": { + "declaration": true, + "lib": ["es2015", "dom"], + "module": "commonjs", + "moduleResolution": "node", + "noEmitOnError": true, + "noUnusedLocals": true, + "outDir": "lib", + "rootDir": "src", + "strict": true, + "strictNullChecks": false, + "target": "es2015", + "types": [], + "esModuleInterop": true + }, + "include": ["src/*"] +} diff --git a/@plotly/dash-jupyterlab/yarn.lock b/@plotly/dash-jupyterlab/yarn.lock new file mode 100644 index 0000000000..ffed6f6902 --- /dev/null +++ b/@plotly/dash-jupyterlab/yarn.lock @@ -0,0 +1,1310 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@babel/runtime@^7.1.2": + version "7.9.2" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.9.2.tgz#d90df0583a3a252f09aaa619665367bae518db06" + integrity sha512-NE2DtOdufG7R5vnfQUTehdTfNycfUANEtCa9PssN9O/xmTzP4E08UI797ixaei6hBEVL9BI/PsdJS5x7mWoB9Q== + dependencies: + regenerator-runtime "^0.13.4" + +"@blueprintjs/core@^3.36.0", "@blueprintjs/core@^3.38.0": + version "3.38.0" + resolved "https://registry.yarnpkg.com/@blueprintjs/core/-/core-3.38.0.tgz#9b8eb086e22b424153fa0e1483c4c8abd9e6c8e0" + integrity sha512-U4yf6B+SbCspNDuvSU2/pl+a80hr9iQgyf2cJ2cOz5It+RV8M+dg9BgB1WaZgqdWgxx/4+A425X5zt2Jw9xalw== + dependencies: + "@blueprintjs/icons" "^3.24.0" + "@types/dom4" "^2.0.1" + classnames "^2.2" + dom4 "^2.1.5" + normalize.css "^8.0.1" + popper.js "^1.16.1" + react-lifecycles-compat "^3.0.4" + react-popper "^1.3.7" + react-transition-group "^2.9.0" + resize-observer-polyfill "^1.5.1" + tslib "~1.13.0" + +"@blueprintjs/icons@^3.24.0": + version "3.24.0" + resolved "https://registry.yarnpkg.com/@blueprintjs/icons/-/icons-3.24.0.tgz#aa7e6042e40806d22f85da8d62990ff0296adcf2" + integrity sha512-OvDDI5EUueS1Y3t594iS8LAGoHhLhYjC2GuN/01a85n+ASLSp0jf0/+uix2JeCOj41iTdRRCINbWuRwVQNNGPw== + dependencies: + classnames "^2.2" + tslib "~1.13.0" + +"@blueprintjs/select@^3.15.0": + version "3.15.2" + resolved "https://registry.yarnpkg.com/@blueprintjs/select/-/select-3.15.2.tgz#89237fb129076277be98535a4e3c0e053ad4f83a" + integrity sha512-3uY89fSxH4zrvZObDx1SEr/K3KizjgY7Z/005/acFkSN+2GFSD9GU5rmwEQfBDdHk5VjqHEWykD5c8YySH1nDA== + dependencies: + "@blueprintjs/core" "^3.38.0" + classnames "^2.2" + tslib "~1.13.0" + +"@fortawesome/fontawesome-free@^5.12.0": + version "5.13.0" + resolved "https://registry.yarnpkg.com/@fortawesome/fontawesome-free/-/fontawesome-free-5.13.0.tgz#fcb113d1aca4b471b709e8c9c168674fbd6e06d9" + integrity sha512-xKOeQEl5O47GPZYIMToj6uuA2syyFlq9EMSl2ui0uytjY9xbe8XS0pexNWmxrdcCyNGyDmLyYw5FtKsalBUeOg== + +"@jupyterlab/application@^2.0.0 || ^3.0.0": + version "3.0.3" + resolved "https://registry.yarnpkg.com/@jupyterlab/application/-/application-3.0.3.tgz#9fc6fe9873842d5a16b205ff9a8bedf76b4456e5" + integrity sha512-aJDG5A/Rc1bWnKtoMM77k6hmjZrf0jRioYYFSbZrJ63k0KG27lCYR7EK1XNucRPMw2qz3IolfYJFPf+quYW9Iw== + dependencies: + "@fortawesome/fontawesome-free" "^5.12.0" + "@jupyterlab/apputils" "^3.0.2" + "@jupyterlab/coreutils" "^5.0.1" + "@jupyterlab/docregistry" "^3.0.3" + "@jupyterlab/rendermime" "^3.0.3" + "@jupyterlab/rendermime-interfaces" "^3.0.2" + "@jupyterlab/services" "^6.0.2" + "@jupyterlab/statedb" "^3.0.1" + "@jupyterlab/translation" "^3.0.2" + "@jupyterlab/ui-components" "^3.0.2" + "@lumino/algorithm" "^1.3.3" + "@lumino/application" "^1.13.1" + "@lumino/commands" "^1.12.0" + "@lumino/coreutils" "^1.5.3" + "@lumino/disposable" "^1.4.3" + "@lumino/messaging" "^1.4.3" + "@lumino/polling" "^1.3.3" + "@lumino/properties" "^1.2.3" + "@lumino/signaling" "^1.4.3" + "@lumino/widgets" "^1.16.1" + +"@jupyterlab/apputils@^3.0.2": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@jupyterlab/apputils/-/apputils-3.0.2.tgz#e45e6b65c9d9dea0d0cbc8074a809ecc960e8bbe" + integrity sha512-nTXwpRUPSVR3yevQyGZH6kKsl41Df3nkKCjp8nAqwAPjcwv+nuxLIwnBQP1BFZ+JfIKn18aoZ8ULoH79qasKNg== + dependencies: + "@jupyterlab/coreutils" "^5.0.1" + "@jupyterlab/services" "^6.0.2" + "@jupyterlab/settingregistry" "^3.0.1" + "@jupyterlab/statedb" "^3.0.1" + "@jupyterlab/translation" "^3.0.2" + "@jupyterlab/ui-components" "^3.0.2" + "@lumino/algorithm" "^1.3.3" + "@lumino/commands" "^1.12.0" + "@lumino/coreutils" "^1.5.3" + "@lumino/disposable" "^1.4.3" + "@lumino/domutils" "^1.2.3" + "@lumino/messaging" "^1.4.3" + "@lumino/properties" "^1.2.3" + "@lumino/signaling" "^1.4.3" + "@lumino/virtualdom" "^1.8.0" + "@lumino/widgets" "^1.16.1" + "@types/react" "^17.0.0" + buffer "^5.6.0" + react "^17.0.1" + react-dom "^17.0.1" + sanitize-html "~1.27.4" + url "^0.11.0" + +"@jupyterlab/attachments@^3.0.3": + version "3.0.3" + resolved "https://registry.yarnpkg.com/@jupyterlab/attachments/-/attachments-3.0.3.tgz#63294c9bc05fa17bec0144a6ce5e4c575b251fb9" + integrity sha512-U6d6NHXcujCefnQuSY/X0cwPpxRDzMzQbHci5uixdWpgt3wJbmTm5tVahgEUZdsTj2ne0gcEsUOeQBeDy6KjBg== + dependencies: + "@jupyterlab/nbformat" "^3.0.1" + "@jupyterlab/observables" "^4.0.1" + "@jupyterlab/rendermime" "^3.0.3" + "@jupyterlab/rendermime-interfaces" "^3.0.2" + "@lumino/disposable" "^1.4.3" + "@lumino/signaling" "^1.4.3" + +"@jupyterlab/cells@^3.0.3": + version "3.0.3" + resolved "https://registry.yarnpkg.com/@jupyterlab/cells/-/cells-3.0.3.tgz#ad9508ce4438f18a92d1af1cc1ffeca0b89f632c" + integrity sha512-NufBnb7y3BiuXZTckj9U6ZOe0gBRMAaSpLXnC04XqtHHrJziZDc0PuiZHEZS7umMdoB04HKui/7/G9y6zjpN3A== + dependencies: + "@jupyterlab/apputils" "^3.0.2" + "@jupyterlab/attachments" "^3.0.3" + "@jupyterlab/codeeditor" "^3.0.2" + "@jupyterlab/codemirror" "^3.0.2" + "@jupyterlab/coreutils" "^5.0.1" + "@jupyterlab/filebrowser" "^3.0.3" + "@jupyterlab/nbformat" "^3.0.1" + "@jupyterlab/observables" "^4.0.1" + "@jupyterlab/outputarea" "^3.0.3" + "@jupyterlab/rendermime" "^3.0.3" + "@jupyterlab/services" "^6.0.2" + "@jupyterlab/ui-components" "^3.0.2" + "@lumino/algorithm" "^1.3.3" + "@lumino/coreutils" "^1.5.3" + "@lumino/dragdrop" "^1.7.1" + "@lumino/messaging" "^1.4.3" + "@lumino/signaling" "^1.4.3" + "@lumino/virtualdom" "^1.8.0" + "@lumino/widgets" "^1.16.1" + react "^17.0.1" + +"@jupyterlab/codeeditor@^3.0.2": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@jupyterlab/codeeditor/-/codeeditor-3.0.2.tgz#45d88eb229f1a57177dc9865f92dcf9cb7f70263" + integrity sha512-0pF/B1G+8l87QRcBCOFWieXGVyuJnTYO2Vp8R/jBKV0hqHljAgao40o1mInt1xGGSkTOcAydETbXEt1J4OCWuA== + dependencies: + "@jupyterlab/coreutils" "^5.0.1" + "@jupyterlab/nbformat" "^3.0.1" + "@jupyterlab/observables" "^4.0.1" + "@jupyterlab/translation" "^3.0.2" + "@jupyterlab/ui-components" "^3.0.2" + "@lumino/coreutils" "^1.5.3" + "@lumino/disposable" "^1.4.3" + "@lumino/dragdrop" "^1.7.1" + "@lumino/messaging" "^1.4.3" + "@lumino/signaling" "^1.4.3" + "@lumino/widgets" "^1.16.1" + +"@jupyterlab/codemirror@^3.0.2": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@jupyterlab/codemirror/-/codemirror-3.0.2.tgz#931bfb8c68aa3daa08cee5f5335c177878b86298" + integrity sha512-i6IhCHDTcTCrNK58I4OjNX9S4erkKu285c47hiJTHxO03YFHOjLtl+i0Ly76R8Qc1+3rpyQxztXve0ZA/K+apQ== + dependencies: + "@jupyterlab/apputils" "^3.0.2" + "@jupyterlab/codeeditor" "^3.0.2" + "@jupyterlab/coreutils" "^5.0.1" + "@jupyterlab/nbformat" "^3.0.1" + "@jupyterlab/observables" "^4.0.1" + "@jupyterlab/statusbar" "^3.0.2" + "@jupyterlab/translation" "^3.0.2" + "@lumino/algorithm" "^1.3.3" + "@lumino/commands" "^1.12.0" + "@lumino/coreutils" "^1.5.3" + "@lumino/disposable" "^1.4.3" + "@lumino/polling" "^1.3.3" + "@lumino/signaling" "^1.4.3" + "@lumino/widgets" "^1.16.1" + codemirror "~5.57.0" + react "^17.0.1" + +"@jupyterlab/console@^2.0.0 || ^3.0.0": + version "3.0.3" + resolved "https://registry.yarnpkg.com/@jupyterlab/console/-/console-3.0.3.tgz#f041af90d798258de8e6cd1199173e5e0195268e" + integrity sha512-Pk5eLlsDHppEGRXmvbhUdAByFizU8vxue3CNmmLW1dqBulCoCLMRVdBv+MlbMKhrrKQwvRshQ5u1zz8OMDSBkQ== + dependencies: + "@jupyterlab/apputils" "^3.0.2" + "@jupyterlab/cells" "^3.0.3" + "@jupyterlab/codeeditor" "^3.0.2" + "@jupyterlab/coreutils" "^5.0.1" + "@jupyterlab/nbformat" "^3.0.1" + "@jupyterlab/observables" "^4.0.1" + "@jupyterlab/rendermime" "^3.0.3" + "@jupyterlab/services" "^6.0.2" + "@jupyterlab/translation" "^3.0.2" + "@jupyterlab/ui-components" "^3.0.2" + "@lumino/algorithm" "^1.3.3" + "@lumino/coreutils" "^1.5.3" + "@lumino/disposable" "^1.4.3" + "@lumino/dragdrop" "^1.7.1" + "@lumino/messaging" "^1.4.3" + "@lumino/signaling" "^1.4.3" + "@lumino/widgets" "^1.16.1" + +"@jupyterlab/coreutils@^5.0.1": + version "5.0.1" + resolved "https://registry.yarnpkg.com/@jupyterlab/coreutils/-/coreutils-5.0.1.tgz#f50305be93d2e0f1e60546f816d103229a92ba24" + integrity sha512-eYjEtZAgoRx0UJY/wfdm7Og+Z1dkcmEjTL5J6MfTROe164F6OCodEFbKECIyVfGt+hoAOiqs3Jg9cPqMw+DA3Q== + dependencies: + "@lumino/coreutils" "^1.5.3" + "@lumino/disposable" "^1.4.3" + "@lumino/signaling" "^1.4.3" + minimist "~1.2.0" + moment "^2.24.0" + path-browserify "^1.0.0" + url-parse "~1.4.7" + +"@jupyterlab/docmanager@^3.0.3": + version "3.0.3" + resolved "https://registry.yarnpkg.com/@jupyterlab/docmanager/-/docmanager-3.0.3.tgz#41e89a8857500501f4e15679fd8fb0379c4db1dd" + integrity sha512-6zId9EWd/X/wlJ7Bhppsdn/5BVuAvMWFSGmv0FtjVPXbPoGN2jZviPwlux4o8cUFS6uj8s0U9eaFfwM5juh9qA== + dependencies: + "@jupyterlab/apputils" "^3.0.2" + "@jupyterlab/coreutils" "^5.0.1" + "@jupyterlab/docregistry" "^3.0.3" + "@jupyterlab/services" "^6.0.2" + "@jupyterlab/statusbar" "^3.0.2" + "@jupyterlab/translation" "^3.0.2" + "@lumino/algorithm" "^1.3.3" + "@lumino/coreutils" "^1.5.3" + "@lumino/disposable" "^1.4.3" + "@lumino/messaging" "^1.4.3" + "@lumino/properties" "^1.2.3" + "@lumino/signaling" "^1.4.3" + "@lumino/widgets" "^1.16.1" + react "^17.0.1" + +"@jupyterlab/docregistry@^3.0.3": + version "3.0.3" + resolved "https://registry.yarnpkg.com/@jupyterlab/docregistry/-/docregistry-3.0.3.tgz#7a99ae8ef7f815e4fe07cf457d83c4ee3ca6589f" + integrity sha512-9qs8suB5fWpzfR4uUemR2lbEvOH9ROuSWNGZxuO8+mN6XEnI+PLbGDO2WuctGxXsWgkg/6foOxIl6xoceCo9vg== + dependencies: + "@jupyterlab/apputils" "^3.0.2" + "@jupyterlab/codeeditor" "^3.0.2" + "@jupyterlab/codemirror" "^3.0.2" + "@jupyterlab/coreutils" "^5.0.1" + "@jupyterlab/observables" "^4.0.1" + "@jupyterlab/rendermime" "^3.0.3" + "@jupyterlab/rendermime-interfaces" "^3.0.2" + "@jupyterlab/services" "^6.0.2" + "@jupyterlab/translation" "^3.0.2" + "@jupyterlab/ui-components" "^3.0.2" + "@lumino/algorithm" "^1.3.3" + "@lumino/coreutils" "^1.5.3" + "@lumino/disposable" "^1.4.3" + "@lumino/messaging" "^1.4.3" + "@lumino/signaling" "^1.4.3" + "@lumino/widgets" "^1.16.1" + +"@jupyterlab/filebrowser@^3.0.3": + version "3.0.3" + resolved "https://registry.yarnpkg.com/@jupyterlab/filebrowser/-/filebrowser-3.0.3.tgz#ca00522719542324136fae3bc83645c8e0004989" + integrity sha512-XlOqvk7XinLadd23W45jI9QqRSpPetxwItyatvMqosR+jtSAuUJBBcFpYfu/TiSItBzKwZ8JuDJYlTJ399O7Wg== + dependencies: + "@jupyterlab/apputils" "^3.0.2" + "@jupyterlab/coreutils" "^5.0.1" + "@jupyterlab/docmanager" "^3.0.3" + "@jupyterlab/docregistry" "^3.0.3" + "@jupyterlab/services" "^6.0.2" + "@jupyterlab/statedb" "^3.0.1" + "@jupyterlab/statusbar" "^3.0.2" + "@jupyterlab/translation" "^3.0.2" + "@jupyterlab/ui-components" "^3.0.2" + "@lumino/algorithm" "^1.3.3" + "@lumino/coreutils" "^1.5.3" + "@lumino/disposable" "^1.4.3" + "@lumino/domutils" "^1.2.3" + "@lumino/dragdrop" "^1.7.1" + "@lumino/messaging" "^1.4.3" + "@lumino/polling" "^1.3.3" + "@lumino/signaling" "^1.4.3" + "@lumino/virtualdom" "^1.8.0" + "@lumino/widgets" "^1.16.1" + react "^17.0.1" + +"@jupyterlab/nbformat@^3.0.1": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@jupyterlab/nbformat/-/nbformat-3.0.1.tgz#56c18449fb6100dd85f22ee73e252ab27eb17dcd" + integrity sha512-cojRKJPoCPei8XiHqGx9IwjMI5llOumrSSYzyBR5qm/NYGOG1mgFwNW2aTf+MoUrZwhpQo8oW27p/W2k2zQAbQ== + dependencies: + "@lumino/coreutils" "^1.5.3" + +"@jupyterlab/notebook@^2.0.0 || ^3.0.0": + version "3.0.3" + resolved "https://registry.yarnpkg.com/@jupyterlab/notebook/-/notebook-3.0.3.tgz#c6ad8091650cee226830c5392b6dd7a62186c04d" + integrity sha512-ennnJaO+fW4Bk6ibotHIYSkb3M3G8NEr+BvrSfIQizlagcuquvk+INO5m2fVL1G623dRaR+2WqNE0UkxK0dBJw== + dependencies: + "@jupyterlab/apputils" "^3.0.2" + "@jupyterlab/cells" "^3.0.3" + "@jupyterlab/codeeditor" "^3.0.2" + "@jupyterlab/coreutils" "^5.0.1" + "@jupyterlab/docregistry" "^3.0.3" + "@jupyterlab/nbformat" "^3.0.1" + "@jupyterlab/observables" "^4.0.1" + "@jupyterlab/rendermime" "^3.0.3" + "@jupyterlab/services" "^6.0.2" + "@jupyterlab/statusbar" "^3.0.2" + "@jupyterlab/translation" "^3.0.2" + "@jupyterlab/ui-components" "^3.0.2" + "@lumino/algorithm" "^1.3.3" + "@lumino/coreutils" "^1.5.3" + "@lumino/domutils" "^1.2.3" + "@lumino/dragdrop" "^1.7.1" + "@lumino/messaging" "^1.4.3" + "@lumino/properties" "^1.2.3" + "@lumino/signaling" "^1.4.3" + "@lumino/virtualdom" "^1.8.0" + "@lumino/widgets" "^1.16.1" + react "^17.0.1" + +"@jupyterlab/observables@^4.0.1": + version "4.0.1" + resolved "https://registry.yarnpkg.com/@jupyterlab/observables/-/observables-4.0.1.tgz#d767fc0831a2ae32ef772492fffc77dd0ffaabe9" + integrity sha512-f+y7w/eBQfw2AWe9cwa6wj5sqKxJMJYlzjUuSlXfSehKp7SR9s+L3d+JOBmfbBF5WHvaF9XA4cGkZM/HZt8ARQ== + dependencies: + "@lumino/algorithm" "^1.3.3" + "@lumino/coreutils" "^1.5.3" + "@lumino/disposable" "^1.4.3" + "@lumino/messaging" "^1.4.3" + "@lumino/signaling" "^1.4.3" + +"@jupyterlab/outputarea@^3.0.3": + version "3.0.3" + resolved "https://registry.yarnpkg.com/@jupyterlab/outputarea/-/outputarea-3.0.3.tgz#4ca2f3857db3611c7ab208a5fef28b35c5b5f53a" + integrity sha512-NBB0aJE0IVPNAEiLj8shLKAX9oAsl1oreKcfd/cfYYQFflKHpWTxlzA25NNv7xcfVqT78i704FemW/k/9kPozA== + dependencies: + "@jupyterlab/apputils" "^3.0.2" + "@jupyterlab/nbformat" "^3.0.1" + "@jupyterlab/observables" "^4.0.1" + "@jupyterlab/rendermime" "^3.0.3" + "@jupyterlab/rendermime-interfaces" "^3.0.2" + "@jupyterlab/services" "^6.0.2" + "@lumino/algorithm" "^1.3.3" + "@lumino/coreutils" "^1.5.3" + "@lumino/disposable" "^1.4.3" + "@lumino/messaging" "^1.4.3" + "@lumino/properties" "^1.2.3" + "@lumino/signaling" "^1.4.3" + "@lumino/widgets" "^1.16.1" + resize-observer-polyfill "^1.5.1" + +"@jupyterlab/rendermime-interfaces@^3.0.2": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@jupyterlab/rendermime-interfaces/-/rendermime-interfaces-3.0.2.tgz#0af6022045a12286ceb6cd732c2a84e322d26d5f" + integrity sha512-BxGXIrGziW+9fvzDF57F4kebJz9p1SWH7Id6VNI21JzCZxJkz7wZpbeUfHzpUiXIh+qogEXEESKKuQg9CzNyqw== + dependencies: + "@jupyterlab/translation" "^3.0.2" + "@lumino/coreutils" "^1.5.3" + "@lumino/widgets" "^1.16.1" + +"@jupyterlab/rendermime@^3.0.3": + version "3.0.3" + resolved "https://registry.yarnpkg.com/@jupyterlab/rendermime/-/rendermime-3.0.3.tgz#f4a213e7d42e253dc02dcb536b00843690adaf92" + integrity sha512-s72CajNpj0UEe4rJVnm5pVbf/jZXc3c5YuXF39BTxpGtsg0uqSysPpYtc1/VjsBoS3R5Bc73P3NIEvOzl6G5cA== + dependencies: + "@jupyterlab/apputils" "^3.0.2" + "@jupyterlab/codemirror" "^3.0.2" + "@jupyterlab/coreutils" "^5.0.1" + "@jupyterlab/nbformat" "^3.0.1" + "@jupyterlab/observables" "^4.0.1" + "@jupyterlab/rendermime-interfaces" "^3.0.2" + "@jupyterlab/services" "^6.0.2" + "@jupyterlab/translation" "^3.0.2" + "@lumino/algorithm" "^1.3.3" + "@lumino/coreutils" "^1.5.3" + "@lumino/messaging" "^1.4.3" + "@lumino/signaling" "^1.4.3" + "@lumino/widgets" "^1.16.1" + lodash.escape "^4.0.1" + marked "^1.1.1" + +"@jupyterlab/services@^6.0.2": + version "6.0.2" + resolved "https://registry.yarnpkg.com/@jupyterlab/services/-/services-6.0.2.tgz#ca9f5f8b6c69013d9d52a15e034eb8b7935fdc5e" + integrity sha512-J0wghVeqMlG71VXkOhlh3ZcyG787wzz9hSFHMGu/1ATlIDtn9z8dbdCAn92rd12jD1nDX1L2KcyjH5pLT+CKeA== + dependencies: + "@jupyterlab/coreutils" "^5.0.1" + "@jupyterlab/nbformat" "^3.0.1" + "@jupyterlab/observables" "^4.0.1" + "@jupyterlab/settingregistry" "^3.0.1" + "@jupyterlab/statedb" "^3.0.1" + "@lumino/algorithm" "^1.3.3" + "@lumino/coreutils" "^1.5.3" + "@lumino/disposable" "^1.4.3" + "@lumino/polling" "^1.3.3" + "@lumino/signaling" "^1.4.3" + node-fetch "^2.6.0" + ws "^7.2.0" + +"@jupyterlab/settingregistry@^3.0.1": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@jupyterlab/settingregistry/-/settingregistry-3.0.1.tgz#8462a3a142f31641e45ed45cd5b1712fcd5fa99e" + integrity sha512-Em/gDygAojndYo7qR4jG0EKCInVQEhTRAnTE/sOYXhwD74Ib3ppFl67N8CvJSr8+jSFhDrLyyVaevI1o6fep9w== + dependencies: + "@jupyterlab/statedb" "^3.0.1" + "@lumino/commands" "^1.12.0" + "@lumino/coreutils" "^1.5.3" + "@lumino/disposable" "^1.4.3" + "@lumino/signaling" "^1.4.3" + ajv "^6.12.3" + json5 "^2.1.1" + +"@jupyterlab/statedb@^3.0.1": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@jupyterlab/statedb/-/statedb-3.0.1.tgz#8b6c757e20a8072c5276019a07c65725a9829aa9" + integrity sha512-+00qBVlboCz945o2CFXrCUJ9oyKcAviA7+dlnE92N5UWBelp8dulMAgWNIW8Uew5O0mvbb1dVAcN9bnbLphGfw== + dependencies: + "@lumino/commands" "^1.12.0" + "@lumino/coreutils" "^1.5.3" + "@lumino/disposable" "^1.4.3" + "@lumino/properties" "^1.2.3" + "@lumino/signaling" "^1.4.3" + +"@jupyterlab/statusbar@^3.0.2": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@jupyterlab/statusbar/-/statusbar-3.0.2.tgz#135ea22d62b79de3e29657abd9569297bbe39203" + integrity sha512-71sx7GRR4FGJExBjBB8TMD63h5wv09aRLO+cQj3xMygG47EX9TaW2Q8zAq4ZQM400UNAh/NGVmr7P7EBdH8jhg== + dependencies: + "@jupyterlab/apputils" "^3.0.2" + "@jupyterlab/codeeditor" "^3.0.2" + "@jupyterlab/coreutils" "^5.0.1" + "@jupyterlab/services" "^6.0.2" + "@jupyterlab/translation" "^3.0.2" + "@jupyterlab/ui-components" "^3.0.2" + "@lumino/algorithm" "^1.3.3" + "@lumino/coreutils" "^1.5.3" + "@lumino/disposable" "^1.4.3" + "@lumino/messaging" "^1.4.3" + "@lumino/signaling" "^1.4.3" + "@lumino/widgets" "^1.16.1" + csstype "~3.0.3" + react "^17.0.1" + typestyle "^2.0.4" + +"@jupyterlab/translation@^3.0.2": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@jupyterlab/translation/-/translation-3.0.2.tgz#478c1eb70c0f730cf1061a92443e5371a823295c" + integrity sha512-OAA3+aD5pxXroB2z3M0xXWRJn246aooAXiPV7lQFFN1IR2Aovb+sBB61TQr+sjqDpHfVA9qADC65RIsr3XOmoA== + dependencies: + "@jupyterlab/coreutils" "^5.0.1" + "@jupyterlab/services" "^6.0.2" + "@jupyterlab/statedb" "^3.0.1" + "@lumino/coreutils" "^1.5.3" + +"@jupyterlab/ui-components@^3.0.2": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@jupyterlab/ui-components/-/ui-components-3.0.2.tgz#f6b87e352446a143d5d76c69d3c7796787605e65" + integrity sha512-QfTMwAMDfHNtJZ0+zCeW8EXPlcPyGKooiMCmZXd1onFIxSpg/mdwV231uiakwARt3on+XBT8JrMgoSf03ywV6w== + dependencies: + "@blueprintjs/core" "^3.36.0" + "@blueprintjs/select" "^3.15.0" + "@jupyterlab/coreutils" "^5.0.1" + "@lumino/coreutils" "^1.5.3" + "@lumino/signaling" "^1.4.3" + "@lumino/virtualdom" "^1.8.0" + "@lumino/widgets" "^1.16.1" + react "^17.0.1" + react-dom "^17.0.1" + typestyle "^2.0.4" + +"@lumino/algorithm@^1.3.3": + version "1.3.3" + resolved "https://registry.yarnpkg.com/@lumino/algorithm/-/algorithm-1.3.3.tgz#fdf4daa407a1ce6f233e173add6a2dda0c99eef4" + integrity sha512-I2BkssbOSLq3rDjgAC3fzf/zAIwkRUnAh60MO0lYcaFdSGyI15w4K3gwZHGIO0p9cKEiNHLXKEODGmOjMLOQ3g== + +"@lumino/application@^1.13.1": + version "1.15.0" + resolved "https://registry.yarnpkg.com/@lumino/application/-/application-1.15.0.tgz#8b7e5142bc6d38d1300164507e0a214a55ea6d05" + integrity sha512-W/12+UFk1oBdJ793L3NegQvt0T355k64fPmDVzPhM48TW8vM/0tDc+zBx1KShn52eRMTrVBDc2cG5t6C5/XF1w== + dependencies: + "@lumino/commands" "^1.12.0" + "@lumino/coreutils" "^1.5.3" + "@lumino/widgets" "^1.18.0" + +"@lumino/collections@^1.3.3": + version "1.3.3" + resolved "https://registry.yarnpkg.com/@lumino/collections/-/collections-1.3.3.tgz#fa95c826b93ee6e24b3c4b07c8f595312525f8cc" + integrity sha512-vN3GSV5INkgM6tMLd+WqTgaPnQNTY7L/aFUtTOC8TJQm+vg1eSmR4fNXsoGHM3uA85ctSJThvdZr5triu1Iajg== + dependencies: + "@lumino/algorithm" "^1.3.3" + +"@lumino/commands@^1.12.0": + version "1.12.0" + resolved "https://registry.yarnpkg.com/@lumino/commands/-/commands-1.12.0.tgz#63a744d034d8bc524455e47f06c0ac5f2eb6ec38" + integrity sha512-5TFlhDzZk1X8rCBjhh0HH3j6CcJ03mx2Pd/1rGa7MB5R+3+yYYk+gTlfHRqsxdehNRmiISaHRSrMnW8bynW7ZQ== + dependencies: + "@lumino/algorithm" "^1.3.3" + "@lumino/coreutils" "^1.5.3" + "@lumino/disposable" "^1.4.3" + "@lumino/domutils" "^1.2.3" + "@lumino/keyboard" "^1.2.3" + "@lumino/signaling" "^1.4.3" + "@lumino/virtualdom" "^1.8.0" + +"@lumino/coreutils@^1.5.3": + version "1.5.3" + resolved "https://registry.yarnpkg.com/@lumino/coreutils/-/coreutils-1.5.3.tgz#89dd7b7f381642a1bf568910c5b62c7bde705d71" + integrity sha512-G72jJ6sgOwAUuilz+cri7LpHIJxllK+qz+YZUC3fyyWHK7oRlZemcc43jZAVE+tagTdMxKYSQWNIVzM5lI8sWw== + +"@lumino/disposable@^1.4.3": + version "1.4.3" + resolved "https://registry.yarnpkg.com/@lumino/disposable/-/disposable-1.4.3.tgz#0a69b15cc5a1e506f93bb390ac44aae338da3c36" + integrity sha512-zKQ9N2AEGcYpG6PJkeMWQXvoXU9w1ocji78z+fboM/SmSgtOIVGeQt3fZeldymf0XrlOPpNXs1ZFg54yWUMnXA== + dependencies: + "@lumino/algorithm" "^1.3.3" + "@lumino/signaling" "^1.4.3" + +"@lumino/domutils@^1.2.3": + version "1.2.3" + resolved "https://registry.yarnpkg.com/@lumino/domutils/-/domutils-1.2.3.tgz#7e8e549a97624bfdbd4dd95ae4d1e30b87799822" + integrity sha512-SEi8WZSy+DWMkL5CfAY78MHbi3x83AVmRFxjs9+A6qsFPde+Hr1I4DNtLsSDmfAWsobHHgBnjyNp2ZkQEq0IEA== + +"@lumino/dragdrop@^1.7.1": + version "1.7.1" + resolved "https://registry.yarnpkg.com/@lumino/dragdrop/-/dragdrop-1.7.1.tgz#1466206d43a64dadca383e0b9a87cc8a14c8c59b" + integrity sha512-IeSSOTmpqBSWz+EVsbGVeHe/KIaHaUsQXZ4BJCEbCKgNGHbqMfUOtlneiKq7rEhZGF4wYs7gWWjNhMVZbUGO9Q== + dependencies: + "@lumino/coreutils" "^1.5.3" + "@lumino/disposable" "^1.4.3" + +"@lumino/keyboard@^1.2.3": + version "1.2.3" + resolved "https://registry.yarnpkg.com/@lumino/keyboard/-/keyboard-1.2.3.tgz#594c73233636d85ed035b1a37a095acf956cfe8c" + integrity sha512-ibS0sz0VABeuJXx2JVSz36sUBMUOcQNCNPybVhwzN/GkJFs0dnDKluMu+3Px0tkB2y33bGPZU/RLZY1Xj/faEA== + +"@lumino/messaging@^1.4.3": + version "1.4.3" + resolved "https://registry.yarnpkg.com/@lumino/messaging/-/messaging-1.4.3.tgz#75a1901f53086c7c0e978a63cb784eae5cc59f3f" + integrity sha512-wa2Pj2KOuLNLS2n0wVBzUVFGbvjL1FLbuCOAUEYfN6xXVleqqtGGzd08uTF7ebu01KCO3VQ38+dkvoaM/C2qPw== + dependencies: + "@lumino/algorithm" "^1.3.3" + "@lumino/collections" "^1.3.3" + +"@lumino/polling@^1.3.3": + version "1.3.3" + resolved "https://registry.yarnpkg.com/@lumino/polling/-/polling-1.3.3.tgz#6336638cb9ba2f4f4c3ef2529c7f260abbd25148" + integrity sha512-uMRi6sPRnKW8m38WUY3qox1jxwzpvceafUbDJATCwyrZ48+YoY5Fxfmd9dqwioHS1aq9np5c6L35a9ZGuS0Maw== + dependencies: + "@lumino/coreutils" "^1.5.3" + "@lumino/disposable" "^1.4.3" + "@lumino/signaling" "^1.4.3" + +"@lumino/properties@^1.2.3": + version "1.2.3" + resolved "https://registry.yarnpkg.com/@lumino/properties/-/properties-1.2.3.tgz#10675e554e4a9dcc4022de01875fd51f33e2c785" + integrity sha512-dbS9V/L+RpQoRjxHMAGh1JYoXaLA6F7xkVbg/vmYXqdXZ7DguO5C3Qteu9tNp7Z7Q31TqFWUCrniTI9UJiJCoQ== + +"@lumino/signaling@^1.4.3": + version "1.4.3" + resolved "https://registry.yarnpkg.com/@lumino/signaling/-/signaling-1.4.3.tgz#d29f7f542fdcd70b91ca275d3ca793ae21cebf6a" + integrity sha512-6clc8SMcH0tyKXIX31xw6sxjxJl5hj4YRd1DTHTS62cegQ0FkO8JjJeuv+Nc1pgTg6nEAf65aSOHpUdsFHDAvQ== + dependencies: + "@lumino/algorithm" "^1.3.3" + +"@lumino/virtualdom@^1.8.0": + version "1.8.0" + resolved "https://registry.yarnpkg.com/@lumino/virtualdom/-/virtualdom-1.8.0.tgz#42ea5778e3870e4961ea36697b28aab997c75fa6" + integrity sha512-X/1b8b7TxB9tb4+xQiS8oArcA/AK7NBZrsg2dzu/gHa3JC45R8nzQ+0tObD8Nd0gF/e9w9Ps9M62rLfefcbbKw== + dependencies: + "@lumino/algorithm" "^1.3.3" + +"@lumino/widgets@^1.16.1", "@lumino/widgets@^1.18.0": + version "1.18.0" + resolved "https://registry.yarnpkg.com/@lumino/widgets/-/widgets-1.18.0.tgz#fa8ce727126a1e91b9f3ba78e08425115046e3ac" + integrity sha512-8i10njkGjctuXrbaoV2dRI2rVUaL7eA5djtHj36pX9cALwciEPHNecF6hoZXmQ4ODv6LTwhr87Uz8LT4Aan77A== + dependencies: + "@lumino/algorithm" "^1.3.3" + "@lumino/commands" "^1.12.0" + "@lumino/coreutils" "^1.5.3" + "@lumino/disposable" "^1.4.3" + "@lumino/domutils" "^1.2.3" + "@lumino/dragdrop" "^1.7.1" + "@lumino/keyboard" "^1.2.3" + "@lumino/messaging" "^1.4.3" + "@lumino/properties" "^1.2.3" + "@lumino/signaling" "^1.4.3" + "@lumino/virtualdom" "^1.8.0" + +"@types/dom4@^2.0.1": + version "2.0.1" + resolved "https://registry.yarnpkg.com/@types/dom4/-/dom4-2.0.1.tgz#506d5781b9bcab81bd9a878b198aec7dee2a6033" + integrity sha512-kSkVAvWmMZiCYtvqjqQEwOmvKwcH+V4uiv3qPQ8pAh1Xl39xggGEo8gHUqV4waYGHezdFw0rKBR8Jt0CrQSDZA== + +"@types/prop-types@*": + version "15.7.3" + resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.3.tgz#2ab0d5da2e5815f94b0b9d4b95d1e5f243ab2ca7" + integrity sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw== + +"@types/react@^17.0.0": + version "17.0.0" + resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.0.tgz#5af3eb7fad2807092f0046a1302b7823e27919b8" + integrity sha512-aj/L7RIMsRlWML3YB6KZiXB3fV2t41+5RBGYF8z+tAKU43Px8C3cYUZsDvf1/+Bm4FK21QWBrDutu8ZJ/70qOw== + dependencies: + "@types/prop-types" "*" + csstype "^3.0.2" + +ajv@^6.12.3: + version "6.12.6" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" + integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== + dependencies: + fast-deep-equal "^3.1.1" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.4.1" + uri-js "^4.2.2" + +ansi-styles@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" + integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== + dependencies: + color-convert "^1.9.0" + +balanced-match@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" + integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c= + +base64-js@^1.3.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" + integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== + +brace-expansion@^1.1.7: + version "1.1.11" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" + integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + +buffer@^5.6.0: + version "5.7.1" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0" + integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ== + dependencies: + base64-js "^1.3.1" + ieee754 "^1.1.13" + +chalk@^2.4.2: + version "2.4.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" + integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== + dependencies: + ansi-styles "^3.2.1" + escape-string-regexp "^1.0.5" + supports-color "^5.3.0" + +classnames@^2.2: + version "2.2.6" + resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.2.6.tgz#43935bffdd291f326dad0a205309b38d00f650ce" + integrity sha512-JR/iSQOSt+LQIWwrwEzJ9uk0xfN3mTVYMwt1Ir5mUcSN6pU+V4zQFFaJsclJbPuAUQH+yfWef6tm7l1quW3C8Q== + +codemirror@~5.57.0: + version "5.57.0" + resolved "https://registry.yarnpkg.com/codemirror/-/codemirror-5.57.0.tgz#d26365b72f909f5d2dbb6b1209349ca1daeb2d50" + integrity sha512-WGc6UL7Hqt+8a6ZAsj/f1ApQl3NPvHY/UQSzG6fB6l4BjExgVdhFaxd7mRTw1UCiYe/6q86zHP+kfvBQcZGvUg== + +color-convert@^1.9.0: + version "1.9.3" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" + integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== + dependencies: + color-name "1.1.3" + +color-name@1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" + integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= + +create-react-context@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/create-react-context/-/create-react-context-0.3.0.tgz#546dede9dc422def0d3fc2fe03afe0bc0f4f7d8c" + integrity sha512-dNldIoSuNSvlTJ7slIKC/ZFGKexBMBrrcc+TTe1NdmROnaASuLPvqpwj9v4XS4uXZ8+YPu0sNmShX2rXI5LNsw== + dependencies: + gud "^1.0.0" + warning "^4.0.3" + +csstype@2.6.9: + version "2.6.9" + resolved "https://registry.yarnpkg.com/csstype/-/csstype-2.6.9.tgz#05141d0cd557a56b8891394c1911c40c8a98d098" + integrity sha512-xz39Sb4+OaTsULgUERcCk+TJj8ylkL4aSVDQiX/ksxbELSqwkgt4d4RD7fovIdgJGSuNYqwZEiVjYY5l0ask+Q== + +csstype@^3.0.2, csstype@~3.0.3: + version "3.0.6" + resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.0.6.tgz#865d0b5833d7d8d40f4e5b8a6d76aea3de4725ef" + integrity sha512-+ZAmfyWMT7TiIlzdqJgjMb7S4f1beorDbWbsocyK4RaiqA5RTX3K14bnBWmmA9QEM0gRdsjyyrEmcyga8Zsxmw== + +deep-equal@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.1.1.tgz#b5c98c942ceffaf7cb051e24e1434a25a2e6076a" + integrity sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g== + dependencies: + is-arguments "^1.0.4" + is-date-object "^1.0.1" + is-regex "^1.0.4" + object-is "^1.0.1" + object-keys "^1.1.1" + regexp.prototype.flags "^1.2.0" + +define-properties@^1.1.2, define-properties@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1" + integrity sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ== + dependencies: + object-keys "^1.0.12" + +dom-helpers@^3.4.0: + version "3.4.0" + resolved "https://registry.yarnpkg.com/dom-helpers/-/dom-helpers-3.4.0.tgz#e9b369700f959f62ecde5a6babde4bccd9169af8" + integrity sha512-LnuPJ+dwqKDIyotW1VzmOZ5TONUN7CwkCR5hrgawTUbkBGYdeoNLZo6nNfGkCrjtE1nXXaj7iMMpDa8/d9WoIA== + dependencies: + "@babel/runtime" "^7.1.2" + +dom-serializer@^1.0.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-1.2.0.tgz#3433d9136aeb3c627981daa385fc7f32d27c48f1" + integrity sha512-n6kZFH/KlCrqs/1GHMOd5i2fd/beQHuehKdWvNNffbGHTr/almdhuVvTVFb3V7fglz+nC50fFusu3lY33h12pA== + dependencies: + domelementtype "^2.0.1" + domhandler "^4.0.0" + entities "^2.0.0" + +dom4@^2.1.5: + version "2.1.5" + resolved "https://registry.yarnpkg.com/dom4/-/dom4-2.1.5.tgz#f98a94eb67b340f0fa5b42b0ee9c38cda035428e" + integrity sha512-gJbnVGq5zaBUY0lUh0LUEVGYrtN75Ks8ZwpwOYvnVFrKy/qzXK4R/1WuLIFExWj/tBxbRAkTzZUGJHXmqsBNjQ== + +domelementtype@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.0.1.tgz#1f8bdfe91f5a78063274e803b4bdcedf6e94f94d" + integrity sha512-5HOHUDsYZWV8FGWN0Njbr/Rn7f/eWSQi1v7+HsUVwXgn8nWWlL64zKDkS0n8ZmQ3mlWOMuXOnR+7Nx/5tMO5AQ== + +domelementtype@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.1.0.tgz#a851c080a6d1c3d94344aed151d99f669edf585e" + integrity sha512-LsTgx/L5VpD+Q8lmsXSHW2WpA+eBlZ9HPf3erD1IoPF00/3JKHZ3BknUVA2QGDNu69ZNmyFmCWBSO45XjYKC5w== + +domhandler@^3.0.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-3.3.0.tgz#6db7ea46e4617eb15cf875df68b2b8524ce0037a" + integrity sha512-J1C5rIANUbuYK+FuFL98650rihynUOEzRLxW+90bKZRWB6A1X1Tf82GxR1qAWLyfNPRvjqfip3Q5tdYlmAa9lA== + dependencies: + domelementtype "^2.0.1" + +domhandler@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-4.0.0.tgz#01ea7821de996d85f69029e81fa873c21833098e" + integrity sha512-KPTbnGQ1JeEMQyO1iYXoagsI6so/C96HZiFyByU3T6iAzpXn8EGEvct6unm1ZGoed8ByO2oirxgwxBmqKF9haA== + dependencies: + domelementtype "^2.1.0" + +domutils@^2.0.0: + version "2.4.4" + resolved "https://registry.yarnpkg.com/domutils/-/domutils-2.4.4.tgz#282739c4b150d022d34699797369aad8d19bbbd3" + integrity sha512-jBC0vOsECI4OMdD0GC9mGn7NXPLb+Qt6KW1YDQzeQYRUFKmNG8lh7mO5HiELfr+lLQE7loDVI4QcAxV80HS+RA== + dependencies: + dom-serializer "^1.0.1" + domelementtype "^2.0.1" + domhandler "^4.0.0" + +entities@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/entities/-/entities-2.0.0.tgz#68d6084cab1b079767540d80e56a39b423e4abf4" + integrity sha512-D9f7V0JSRwIxlRI2mjMqufDrRDnx8p+eEOz7aUM9SuvF8gsBzra0/6tbjl1m8eQHrZlYj6PxqE00hZ1SAIKPLw== + +es-abstract@^1.17.0-next.1, es-abstract@^1.17.5: + version "1.17.5" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.17.5.tgz#d8c9d1d66c8981fb9200e2251d799eee92774ae9" + integrity sha512-BR9auzDbySxOcfog0tLECW8l28eRGpDpU3Dm3Hp4q/N+VtLTmyj4EUN088XZWQDW/hzj6sYRDXeOFsaAODKvpg== + dependencies: + es-to-primitive "^1.2.1" + function-bind "^1.1.1" + has "^1.0.3" + has-symbols "^1.0.1" + is-callable "^1.1.5" + is-regex "^1.0.5" + object-inspect "^1.7.0" + object-keys "^1.1.1" + object.assign "^4.1.0" + string.prototype.trimleft "^2.1.1" + string.prototype.trimright "^2.1.1" + +es-to-primitive@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a" + integrity sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA== + dependencies: + is-callable "^1.1.4" + is-date-object "^1.0.1" + is-symbol "^1.0.2" + +escape-string-regexp@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" + integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= + +fast-deep-equal@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz#545145077c501491e33b15ec408c294376e94ae4" + integrity sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA== + +fast-json-stable-stringify@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" + integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== + +free-style@3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/free-style/-/free-style-3.1.0.tgz#4e2996029534e6b1731611d843437b9e2f473f08" + integrity sha512-vJujYSIyT30iDoaoeigNAxX4yB1RUrh+N2ZMhIElMr3BvCuGXOw7XNJMEEJkDUeamK2Rnb/IKFGKRKlTWIGRWA== + +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= + +function-bind@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" + integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== + +glob@^7.1.3: + version "7.1.6" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" + integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + +gud@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/gud/-/gud-1.0.0.tgz#a489581b17e6a70beca9abe3ae57de7a499852c0" + integrity sha512-zGEOVKFM5sVPPrYs7J5/hYEw2Pof8KCyOwyhG8sAF26mCAeUFAcYPu1mwB7hhpIP29zOIBaDqwuHdLp0jvZXjw== + +has-flag@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" + integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= + +has-symbols@^1.0.0, has-symbols@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.1.tgz#9f5214758a44196c406d9bd76cebf81ec2dd31e8" + integrity sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg== + +has@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" + integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== + dependencies: + function-bind "^1.1.1" + +htmlparser2@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-4.1.0.tgz#9a4ef161f2e4625ebf7dfbe6c0a2f52d18a59e78" + integrity sha512-4zDq1a1zhE4gQso/c5LP1OtrhYTncXNSpvJYtWJBtXAETPlMfi3IFNjGuQbYLuVY4ZR0QMqRVvo4Pdy9KLyP8Q== + dependencies: + domelementtype "^2.0.1" + domhandler "^3.0.0" + domutils "^2.0.0" + entities "^2.0.0" + +ieee754@^1.1.13: + version "1.2.1" + resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" + integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@2: + version "2.0.4" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + +is-arguments@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.0.4.tgz#3faf966c7cba0ff437fb31f6250082fcf0448cf3" + integrity sha512-xPh0Rmt8NE65sNzvyUmWgI1tz3mKq74lGA0mL8LYZcoIzKOzDh6HmrYm3d18k60nHerC8A9Km8kYu87zfSFnLA== + +is-callable@^1.1.4, is-callable@^1.1.5: + version "1.1.5" + resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.1.5.tgz#f7e46b596890456db74e7f6e976cb3273d06faab" + integrity sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q== + +is-date-object@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.2.tgz#bda736f2cd8fd06d32844e7743bfa7494c3bfd7e" + integrity sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g== + +is-regex@^1.0.4, is-regex@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.0.5.tgz#39d589a358bf18967f726967120b8fc1aed74eae" + integrity sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ== + dependencies: + has "^1.0.3" + +is-symbol@^1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.3.tgz#38e1014b9e6329be0de9d24a414fd7441ec61937" + integrity sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ== + dependencies: + has-symbols "^1.0.1" + +"js-tokens@^3.0.0 || ^4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" + integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== + +json-schema-traverse@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" + integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== + +json5@^2.1.1: + version "2.1.3" + resolved "https://registry.yarnpkg.com/json5/-/json5-2.1.3.tgz#c9b0f7fa9233bfe5807fe66fcf3a5617ed597d43" + integrity sha512-KXPvOm8K9IJKFM0bmdn8QXh7udDh1g/giieX0NLCaMnb4hEiVFqnop2ImTXCc5e0/oHz3LTqmHGtExn5hfMkOA== + dependencies: + minimist "^1.2.5" + +lodash.escape@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/lodash.escape/-/lodash.escape-4.0.1.tgz#c9044690c21e04294beaa517712fded1fa88de98" + integrity sha1-yQRGkMIeBClL6qUXcS/e0fqI3pg= + +lodash@^4.17.15: + version "4.17.20" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52" + integrity sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA== + +loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" + integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== + dependencies: + js-tokens "^3.0.0 || ^4.0.0" + +marked@^1.1.1: + version "1.2.7" + resolved "https://registry.yarnpkg.com/marked/-/marked-1.2.7.tgz#6e14b595581d2319cdcf033a24caaf41455a01fb" + integrity sha512-No11hFYcXr/zkBvL6qFmAp1z6BKY3zqLMHny/JN/ey+al7qwCM2+CMBL9BOgqMxZU36fz4cCWfn2poWIf7QRXA== + +minimatch@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" + integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== + dependencies: + brace-expansion "^1.1.7" + +minimist@^1.2.5, minimist@~1.2.0: + version "1.2.5" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" + integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== + +moment@^2.24.0: + version "2.24.0" + resolved "https://registry.yarnpkg.com/moment/-/moment-2.24.0.tgz#0d055d53f5052aa653c9f6eb68bb5d12bf5c2b5b" + integrity sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg== + +node-fetch@^2.6.0: + version "2.6.0" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.0.tgz#e633456386d4aa55863f676a7ab0daa8fdecb0fd" + integrity sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA== + +normalize.css@^8.0.1: + version "8.0.1" + resolved "https://registry.yarnpkg.com/normalize.css/-/normalize.css-8.0.1.tgz#9b98a208738b9cc2634caacbc42d131c97487bf3" + integrity sha512-qizSNPO93t1YUuUhP22btGOo3chcvDFqFaj2TRybP0DMxkHOCTYwp3n34fel4a31ORXy4m1Xq0Gyqpb5m33qIg== + +object-assign@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= + +object-inspect@^1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.7.0.tgz#f4f6bd181ad77f006b5ece60bd0b6f398ff74a67" + integrity sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw== + +object-is@^1.0.1: + version "1.1.2" + resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.1.2.tgz#c5d2e87ff9e119f78b7a088441519e2eec1573b6" + integrity sha512-5lHCz+0uufF6wZ7CRFWJN3hp8Jqblpgve06U5CMQ3f//6iDjPr2PEo9MWCjEssDsa+UZEL4PkFpr+BMop6aKzQ== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.17.5" + +object-keys@^1.0.11, object-keys@^1.0.12, object-keys@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" + integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== + +object.assign@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.0.tgz#968bf1100d7956bb3ca086f006f846b3bc4008da" + integrity sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w== + dependencies: + define-properties "^1.1.2" + function-bind "^1.1.1" + has-symbols "^1.0.0" + object-keys "^1.0.11" + +once@^1.3.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= + dependencies: + wrappy "1" + +parse-srcset@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/parse-srcset/-/parse-srcset-1.0.2.tgz#f2bd221f6cc970a938d88556abc589caaaa2bde1" + integrity sha1-8r0iH2zJcKk42IVWq8WJyqqiveE= + +path-browserify@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-1.0.1.tgz#d98454a9c3753d5790860f16f68867b9e46be1fd" + integrity sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g== + +path-is-absolute@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= + +popper.js@^1.14.4, popper.js@^1.16.1: + version "1.16.1" + resolved "https://registry.yarnpkg.com/popper.js/-/popper.js-1.16.1.tgz#2a223cb3dc7b6213d740e40372be40de43e65b1b" + integrity sha512-Wb4p1J4zyFTbM+u6WuO4XstYx4Ky9Cewe4DWrel7B0w6VVICvPwdOpotjzcf6eD8TsckVnIMNONQyPIUFOUbCQ== + +postcss@^7.0.27: + version "7.0.35" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.35.tgz#d2be00b998f7f211d8a276974079f2e92b970e24" + integrity sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg== + dependencies: + chalk "^2.4.2" + source-map "^0.6.1" + supports-color "^6.1.0" + +prettier@2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.0.5.tgz#d6d56282455243f2f92cc1716692c08aa31522d4" + integrity sha512-7PtVymN48hGcO4fGjybyBSIWDsLU4H4XlvOHfq91pz9kkGlonzwTfYkaIEwiRg/dAJF9YlbsduBAgtYLi+8cFg== + +prop-types@^15.6.1, prop-types@^15.6.2: + version "15.7.2" + resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5" + integrity sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ== + dependencies: + loose-envify "^1.4.0" + object-assign "^4.1.1" + react-is "^16.8.1" + +punycode@1.3.2: + version "1.3.2" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d" + integrity sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0= + +punycode@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" + integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== + +querystring@0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620" + integrity sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA= + +querystringify@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.1.1.tgz#60e5a5fd64a7f8bfa4d2ab2ed6fdf4c85bad154e" + integrity sha512-w7fLxIRCRT7U8Qu53jQnJyPkYZIaR4n5151KMfcJlO/A9397Wxb1amJvROTK6TOnp7PfoAmg/qXiNHI+08jRfA== + +react-dom@^17.0.1: + version "17.0.1" + resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-17.0.1.tgz#1de2560474ec9f0e334285662ede52dbc5426fc6" + integrity sha512-6eV150oJZ9U2t9svnsspTMrWNyHc6chX0KzDeAOXftRa8bNeOKTTfCJ7KorIwenkHd2xqVTBTCZd79yk/lx/Ug== + dependencies: + loose-envify "^1.1.0" + object-assign "^4.1.1" + scheduler "^0.20.1" + +react-is@^16.8.1: + version "16.13.1" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" + integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== + +react-lifecycles-compat@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362" + integrity sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA== + +react-popper@^1.3.7: + version "1.3.7" + resolved "https://registry.yarnpkg.com/react-popper/-/react-popper-1.3.7.tgz#f6a3471362ef1f0d10a4963673789de1baca2324" + integrity sha512-nmqYTx7QVjCm3WUZLeuOomna138R1luC4EqkW3hxJUrAe+3eNz3oFCLYdnPwILfn0mX1Ew2c3wctrjlUMYYUww== + dependencies: + "@babel/runtime" "^7.1.2" + create-react-context "^0.3.0" + deep-equal "^1.1.1" + popper.js "^1.14.4" + prop-types "^15.6.1" + typed-styles "^0.0.7" + warning "^4.0.2" + +react-transition-group@^2.9.0: + version "2.9.0" + resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-2.9.0.tgz#df9cdb025796211151a436c69a8f3b97b5b07c8d" + integrity sha512-+HzNTCHpeQyl4MJ/bdE0u6XRMe9+XG/+aL4mCxVN4DnPBQ0/5bfHWPDuOZUzYdMj94daZaZdCCc1Dzt9R/xSSg== + dependencies: + dom-helpers "^3.4.0" + loose-envify "^1.4.0" + prop-types "^15.6.2" + react-lifecycles-compat "^3.0.4" + +react@^17.0.1: + version "17.0.1" + resolved "https://registry.yarnpkg.com/react/-/react-17.0.1.tgz#6e0600416bd57574e3f86d92edba3d9008726127" + integrity sha512-lG9c9UuMHdcAexXtigOZLX8exLWkW0Ku29qPRU8uhF2R9BN96dLCt0psvzPLlHc5OWkgymP3qwTRgbnw5BKx3w== + dependencies: + loose-envify "^1.1.0" + object-assign "^4.1.1" + +regenerator-runtime@^0.13.4: + version "0.13.5" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.5.tgz#d878a1d094b4306d10b9096484b33ebd55e26697" + integrity sha512-ZS5w8CpKFinUzOwW3c83oPeVXoNsrLsaCoLtJvAClH135j/R77RuymhiSErhm2lKcwSCIpmvIWSbDkIfAqKQlA== + +regexp.prototype.flags@^1.2.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.3.0.tgz#7aba89b3c13a64509dabcf3ca8d9fbb9bdf5cb75" + integrity sha512-2+Q0C5g951OlYlJz6yu5/M33IcsESLlLfsyIaLJaG4FA2r4yP8MvVMJUUP/fVBkSpbbbZlS5gynbEWLipiiXiQ== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.17.0-next.1" + +requires-port@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" + integrity sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8= + +resize-observer-polyfill@^1.5.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz#0e9020dd3d21024458d4ebd27e23e40269810464" + integrity sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg== + +rimraf@3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" + integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== + dependencies: + glob "^7.1.3" + +sanitize-html@~1.27.4: + version "1.27.5" + resolved "https://registry.yarnpkg.com/sanitize-html/-/sanitize-html-1.27.5.tgz#6c8149462adb23e360e1bb71cc0bae7f08c823c7" + integrity sha512-M4M5iXDAUEcZKLXkmk90zSYWEtk5NH3JmojQxKxV371fnMh+x9t1rqdmXaGoyEHw3z/X/8vnFhKjGL5xFGOJ3A== + dependencies: + htmlparser2 "^4.1.0" + lodash "^4.17.15" + parse-srcset "^1.0.2" + postcss "^7.0.27" + +scheduler@^0.20.1: + version "0.20.1" + resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.20.1.tgz#da0b907e24026b01181ecbc75efdc7f27b5a000c" + integrity sha512-LKTe+2xNJBNxu/QhHvDR14wUXHRQbVY5ZOYpOGWRzhydZUqrLb2JBvLPY7cAqFmqrWuDED0Mjk7013SZiOz6Bw== + dependencies: + loose-envify "^1.1.0" + object-assign "^4.1.1" + +source-map@^0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" + integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== + +string.prototype.trimend@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.1.tgz#85812a6b847ac002270f5808146064c995fb6913" + integrity sha512-LRPxFUaTtpqYsTeNKaFOw3R4bxIzWOnbQ837QfBylo8jIxtcbK/A/sMV7Q+OAV/vWo+7s25pOE10KYSjaSO06g== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.17.5" + +string.prototype.trimleft@^2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/string.prototype.trimleft/-/string.prototype.trimleft-2.1.2.tgz#4408aa2e5d6ddd0c9a80739b087fbc067c03b3cc" + integrity sha512-gCA0tza1JBvqr3bfAIFJGqfdRTyPae82+KTnm3coDXkZN9wnuW3HjGgN386D7hfv5CHQYCI022/rJPVlqXyHSw== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.17.5" + string.prototype.trimstart "^1.0.0" + +string.prototype.trimright@^2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/string.prototype.trimright/-/string.prototype.trimright-2.1.2.tgz#c76f1cef30f21bbad8afeb8db1511496cfb0f2a3" + integrity sha512-ZNRQ7sY3KroTaYjRS6EbNiiHrOkjihL9aQE/8gfQ4DtAC/aEBRHFJa44OmoWxGGqXuJlfKkZW4WcXErGr+9ZFg== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.17.5" + string.prototype.trimend "^1.0.0" + +string.prototype.trimstart@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.1.tgz#14af6d9f34b053f7cfc89b72f8f2ee14b9039a54" + integrity sha512-XxZn+QpvrBI1FOcg6dIpxUPgWCPuNXvMD72aaRaUQv1eD4e/Qy8i/hFTe0BUmD60p/QA6bh1avmuPTfNjqVWRw== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.17.5" + +supports-color@^5.3.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" + integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== + dependencies: + has-flag "^3.0.0" + +supports-color@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-6.1.0.tgz#0764abc69c63d5ac842dd4867e8d025e880df8f3" + integrity sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ== + dependencies: + has-flag "^3.0.0" + +tslib@~1.13.0: + version "1.13.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.13.0.tgz#c881e13cc7015894ed914862d276436fa9a47043" + integrity sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q== + +typed-styles@^0.0.7: + version "0.0.7" + resolved "https://registry.yarnpkg.com/typed-styles/-/typed-styles-0.0.7.tgz#93392a008794c4595119ff62dde6809dbc40a3d9" + integrity sha512-pzP0PWoZUhsECYjABgCGQlRGL1n7tOHsgwYv3oIiEpJwGhFTuty/YNeduxQYzXXa3Ge5BdT6sHYIQYpl4uJ+5Q== + +typescript@3.9.3: + version "3.9.3" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.3.tgz#d3ac8883a97c26139e42df5e93eeece33d610b8a" + integrity sha512-D/wqnB2xzNFIcoBG9FG8cXRDjiqSTbG2wd8DMZeQyJlP1vfTkIxH4GKveWaEBYySKIg+USu+E+EDIR47SqnaMQ== + +typestyle@^2.0.4: + version "2.1.0" + resolved "https://registry.yarnpkg.com/typestyle/-/typestyle-2.1.0.tgz#7c5cc567de72cd8bfb686813150b92791aaa7636" + integrity sha512-6uCYPdG4xWLeEcl9O0GtNFnNGhami+irKiLsXSuvWHC/aTS7wdj49WeikWAKN+xHN3b1hm+9v0svwwgSBhCsNA== + dependencies: + csstype "2.6.9" + free-style "3.1.0" + +uri-js@^4.2.2: + version "4.2.2" + resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.2.2.tgz#94c540e1ff772956e2299507c010aea6c8838eb0" + integrity sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ== + dependencies: + punycode "^2.1.0" + +url-parse@~1.4.7: + version "1.4.7" + resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.4.7.tgz#a8a83535e8c00a316e403a5db4ac1b9b853ae278" + integrity sha512-d3uaVyzDB9tQoSXFvuSUNFibTd9zxd2bkVrDRvF5TmvWWQwqE4lgYJ5m+x1DbecWkw+LK4RNl2CU1hHuOKPVlg== + dependencies: + querystringify "^2.1.1" + requires-port "^1.0.0" + +url@^0.11.0: + version "0.11.0" + resolved "https://registry.yarnpkg.com/url/-/url-0.11.0.tgz#3838e97cfc60521eb73c525a8e55bfdd9e2e28f1" + integrity sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE= + dependencies: + punycode "1.3.2" + querystring "0.2.0" + +warning@^4.0.2, warning@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/warning/-/warning-4.0.3.tgz#16e9e077eb8a86d6af7d64aa1e05fd85b4678ca3" + integrity sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w== + dependencies: + loose-envify "^1.0.0" + +wrappy@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= + +ws@^7.2.0: + version "7.2.3" + resolved "https://registry.yarnpkg.com/ws/-/ws-7.2.3.tgz#a5411e1fb04d5ed0efee76d26d5c46d830c39b46" + integrity sha512-HTDl9G9hbkNDk98naoR/cHDws7+EyYMOdL1BmjsZXRUjf7d+MficC4B7HLUPlSiho0vg+CWKrGIt/VJBd1xunQ== diff --git a/CHANGELOG.md b/CHANGELOG.md index 24832c71cf..71307a3240 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,12 @@ This project adheres to [Semantic Versioning](https://semver.org/). ## [UNRELEASED] +## Added + +- [#2530](https://github.com/plotly/dash/pull/2530) Merge JupyterDash repository with Dash. + - Add `jupyter_mode` argument to `app.run`, defaults to `inline` and configurable via `jupyter_dash.default_mode`. + - Add prefixed arguments from `JupyterDash` to `app.run`: `jupyter_width`, `jupyter_height`, `jupyter_server_url`. + ## Fixed - [#2555](https://github.com/plotly/dash/pull/2555) Fix browser back button when removing one of multiple location components from layout, fix [#1312](https://github.com/plotly/dash/issues/1312) diff --git a/MANIFEST.in b/MANIFEST.in index 0389382d65..960033c813 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -10,3 +10,5 @@ include dash/html/* include dash/dash_table/* include dash/dash-renderer/build/*.js include dash/dash-renderer/build/*.map +include dash/labextension/dist/dash-jupyterlab.tgz +include dash/labextension/package.json diff --git a/dash/__init__.py b/dash/__init__.py index 871da672d0..f46d6f77e1 100644 --- a/dash/__init__.py +++ b/dash/__init__.py @@ -39,5 +39,17 @@ page_container, ) from ._patch import Patch # noqa: F401,E402 +from ._jupyter import jupyter_dash # noqa: F401,E402 ctx = callback_context + + +def _jupyter_nbextension_paths(): + return [ + { + "section": "notebook", + "src": "nbextension", + "dest": "dash", + "require": "dash/main", + } + ] diff --git a/dash/_jupyter.py b/dash/_jupyter.py new file mode 100644 index 0000000000..44a582b6b2 --- /dev/null +++ b/dash/_jupyter.py @@ -0,0 +1,495 @@ +import asyncio +import io +import inspect +import logging +import os +import queue +import uuid +import re +import sys +import threading +import time +import traceback + +from typing_extensions import Literal + +from werkzeug.serving import make_server + + +try: + from IPython import get_ipython + from IPython.display import IFrame, display, Javascript + from IPython.core.display import HTML + from IPython.core.ultratb import FormattedTB + from retrying import retry + from ansi2html import Ansi2HTMLConverter + from ipykernel.comm import Comm + import nest_asyncio + + import requests + + _dash_comm = Comm(target_name="dash") + _dep_installed = True +except ImportError: + _dep_installed = False + _dash_comm = None + get_ipython = lambda: None + +JupyterDisplayMode = Literal["inline", "external", "jupyterlab", "tab", "_none"] + + +def _get_skip(error: Exception): + tb = traceback.format_exception(type(error), error, error.__traceback__) + skip = 0 + for i, line in enumerate(tb): + if "%% callback invoked %%" in line: + skip = i + 1 + break + return skip + + +def _custom_formatargvalues( + args, + varargs, + varkw, + locals, # pylint: disable=W0622 + formatarg=str, + formatvarargs=lambda name: "*" + name, + formatvarkw=lambda name: "**" + name, + formatvalue=lambda value: "=" + repr(value), +): + + """Copied from inspect.formatargvalues, modified to place function + arguments on separate lines""" + + # pylint: disable=W0622 + def convert(name, locals=locals, formatarg=formatarg, formatvalue=formatvalue): + return formatarg(name) + formatvalue(locals[name]) + + specs = [] + + # pylint: disable=C0200 + for i in range(len(args)): + specs.append(convert(args[i])) + if varargs: + specs.append(formatvarargs(varargs) + formatvalue(locals[varargs])) + if varkw: + specs.append(formatvarkw(varkw) + formatvalue(locals[varkw])) + + result = "(" + ", ".join(specs) + ")" + + if len(result) < 40: + return result + # Put each arg on a separate line + return "(\n " + ",\n ".join(specs) + "\n)" + + +_jupyter_config = {} + +_caller = {} + + +def _send_jupyter_config_comm_request(): + # If running in an ipython kernel, + # request that the front end extension send us the notebook server base URL + if get_ipython() is not None: + if _dash_comm.kernel is not None: + _caller["parent"] = _dash_comm.kernel.get_parent() + _dash_comm.send({"type": "base_url_request"}) + + +def _jupyter_comm_response_received(): + return bool(_jupyter_config) + + +def _request_jupyter_config(timeout=2): + # Heavily inspired by implementation of CaptureExecution in the + if _dash_comm.kernel is None: + # Not in jupyter setting + return + + _send_jupyter_config_comm_request() + + # Get shell and kernel + shell = get_ipython() + kernel = shell.kernel + + # Start capturing shell events to replay later + captured_events = [] + + def capture_event(stream, ident, parent): + captured_events.append((stream, ident, parent)) + + kernel.shell_handlers["execute_request"] = capture_event + + # increment execution count to avoid collision error + shell.execution_count += 1 + + # Allow kernel to execute comms until we receive the jupyter configuration comm + # response + t0 = time.time() + while True: + if (time.time() - t0) > timeout: + # give up + raise EnvironmentError( + "Unable to communicate with the jupyter_dash notebook or JupyterLab \n" + "extension required to infer Jupyter configuration." + ) + if _jupyter_comm_response_received(): + break + + if asyncio.iscoroutinefunction(kernel.do_one_iteration): + loop = asyncio.get_event_loop() + nest_asyncio.apply(loop) + loop.run_until_complete(kernel.do_one_iteration()) + else: + kernel.do_one_iteration() + + # Stop capturing events, revert the kernel shell handler to the default + # execute_request behavior + kernel.shell_handlers["execute_request"] = kernel.execute_request + + # Replay captured events + # need to flush before replaying so messages show up in current cell not + # replay cells + sys.stdout.flush() + sys.stderr.flush() + + for stream, ident, parent in captured_events: + # Using kernel.set_parent is the key to getting the output of the replayed + # events to show up in the cells that were captured instead of the current cell + kernel.set_parent(ident, parent) + kernel.execute_request(stream, ident, parent) + + +class JupyterDash: + """ + Interact with dash apps inside jupyter notebooks. + """ + + default_mode: JupyterDisplayMode = "inline" + alive_token = str(uuid.uuid4()) + inline_exceptions: bool = True + + _servers = {} + + def infer_jupyter_proxy_config(self): + """ + Infer the current Jupyter server configuration. This will detect + the proper request_pathname_prefix and server_url values to use when + displaying Dash apps.Dash requests will be routed through the proxy. + + Requirements: + + In the classic notebook, this method requires the `dash` nbextension + which should be installed automatically with the installation of the + jupyter-dash Python package. You can see what notebook extensions are installed + by running the following command: + $ jupyter nbextension list + + In JupyterLab, this method requires the `@plotly/dash-jupyterlab` labextension. This + extension should be installed automatically with the installation of the + jupyter-dash Python package, but JupyterLab must be allowed to rebuild before + the extension is activated (JupyterLab should automatically detect the + extension and produce a popup dialog asking for permission to rebuild). You can + see what JupyterLab extensions are installed by running the following command: + $ jupyter labextension list + """ + if not self.in_ipython or self.in_colab: + # No op when not running in a Jupyter context or when in Colab + return + # Assume classic notebook or JupyterLab + _request_jupyter_config() + + def __init__(self): + self.in_ipython = get_ipython() is not None + self.in_colab = "google.colab" in sys.modules + + if _dep_installed and self.in_ipython and _dash_comm: + + @_dash_comm.on_msg + def _receive_message(msg): + prev_parent = _caller.get("parent") + if prev_parent and prev_parent != _dash_comm.kernel.get_parent(): + _dash_comm.kernel.set_parent( + [prev_parent["header"]["session"]], prev_parent + ) + del _caller["parent"] + + msg_data = msg.get("content").get("data") + msg_type = msg_data.get("type", None) + if msg_type == "base_url_response": + _jupyter_config.update(msg_data) + + # pylint: disable=too-many-locals, too-many-branches, too-many-statements + def run_app( + self, + app, + mode: JupyterDisplayMode = None, + width="100%", + height=650, + host="127.0.0.1", + port=8050, + server_url=None, + ): + """ + :type app: dash.Dash + :param mode: How to display the app on the notebook. One Of: + ``"external"``: The URL of the app will be displayed in the notebook + output cell. Clicking this URL will open the app in the default + web browser. + ``"inline"``: The app will be displayed inline in the notebook output cell + in an iframe. + ``"jupyterlab"``: The app will be displayed in a dedicate tab in the + JupyterLab interface. Requires JupyterLab and the `jupyterlab-dash` + extension. + :param width: Width of app when displayed using mode="inline" + :param height: Height of app when displayed using mode="inline" + :param host: Host of the server + :param port: Port used by the server + :param server_url: Use if a custom url is required to display the app. + """ + # Validate / infer display mode + if self.in_colab: + valid_display_values = ["inline", "external"] + else: + valid_display_values = ["jupyterlab", "inline", "external", "tab", "_none"] + + if mode is None: + mode = self.default_mode + elif not isinstance(mode, str): + raise ValueError( + f"The mode argument must be a string\n" + f" Received value of type {type(mode)}: {repr(mode)}" + ) + else: + mode = mode.lower() + if mode not in valid_display_values: + raise ValueError( + f"Invalid display argument {mode}\n" + f" Valid arguments: {valid_display_values}" + ) + + # Terminate any existing server using this port + old_server = self._servers.get((host, port)) + if old_server: + old_server.shutdown() + del self._servers[(host, port)] + + # Configure pathname prefix + if "base_subpath" in _jupyter_config: + requests_pathname_prefix = ( + _jupyter_config["base_subpath"].rstrip("/") + "/proxy/{port}/" + ) + else: + requests_pathname_prefix = app.config.get("requests_pathname_prefix", None) + + if requests_pathname_prefix is not None: + requests_pathname_prefix = requests_pathname_prefix.format(port=port) + else: + requests_pathname_prefix = "/" + + # FIXME Move config initialization to main dash __init__ + # low-level setter to circumvent Dash's config locking + # normally it's unsafe to alter requests_pathname_prefix this late, but + # Jupyter needs some unusual behavior. + dict.__setitem__( + app.config, "requests_pathname_prefix", requests_pathname_prefix + ) + + # # Compute server_url url + if server_url is None: + if "server_url" in _jupyter_config: + server_url = _jupyter_config["server_url"].rstrip("/") + else: + domain_base = os.environ.get("DASH_DOMAIN_BASE", None) + if domain_base: + # Dash Enterprise sets DASH_DOMAIN_BASE environment variable + server_url = "https://" + domain_base + else: + server_url = f"http://{host}:{port}" + else: + server_url = server_url.rstrip("/") + + # server_url = "http://{host}:{port}".format(host=host, port=port) + + dashboard_url = f"{server_url}{requests_pathname_prefix}" + + # prevent partial import of orjson when it's installed and mode=jupyterlab + # TODO: why do we need this? Why only in this mode? Importing here in + # all modes anyway, in case there's a way it can pop up in another mode + try: + # pylint: disable=C0415,W0611 + import orjson # noqa: F401 + except ImportError: + pass + + err_q = queue.Queue() + + server = make_server(host, port, app.server, threaded=True, processes=0) + logging.getLogger("werkzeug").setLevel(logging.ERROR) + + @retry( + stop_max_attempt_number=15, + wait_exponential_multiplier=100, + wait_exponential_max=1000, + ) + def run(): + try: + server.serve_forever() + except SystemExit: + pass + except Exception as error: + err_q.put(error) + raise error + + thread = threading.Thread(target=run) + thread.daemon = True + thread.start() + + self._servers[(host, port)] = server + + # Wait for server to start up + alive_url = f"http://{host}:{port}/_alive_{JupyterDash.alive_token}" + + def _get_error(): + try: + err = err_q.get_nowait() + if err: + raise err + except queue.Empty: + pass + + # Wait for app to respond to _alive endpoint + @retry( + stop_max_attempt_number=15, + wait_exponential_multiplier=10, + wait_exponential_max=1000, + ) + def wait_for_app(): + _get_error() + try: + req = requests.get(alive_url) + res = req.content.decode() + if req.status_code != 200: + raise Exception(res) + + if res != "Alive": + url = f"http://{host}:{port}" + raise OSError( + f"Address '{url}' already in use.\n" + " Try passing a different port to run_server." + ) + except requests.ConnectionError as err: + _get_error() + raise err + + try: + wait_for_app() + + if self.in_colab: + JupyterDash._display_in_colab(dashboard_url, port, mode, width, height) + else: + JupyterDash._display_in_jupyter( + dashboard_url, port, mode, width, height + ) + except Exception as final_error: # pylint: disable=broad-except + msg = str(final_error) + if msg.startswith("", '' + ) + + # Remove explicit background color so Dash dev-tools can set background + # color + html_str = re.sub("background-color:[^;]+;", "", html_str) + + return html_str, 500 + + @property + def active(self): + return _dep_installed and (self.in_ipython or self.in_colab) + + +jupyter_dash = JupyterDash() diff --git a/dash/dash.py b/dash/dash.py index 8fee670b08..e3415e6294 100644 --- a/dash/dash.py +++ b/dash/dash.py @@ -68,6 +68,7 @@ _path_to_page, _import_layouts_from_pages, ) +from ._jupyter import jupyter_dash, JupyterDisplayMode # Add explicit mapping for map files mimetypes.add_type("application/json", ".map", True) @@ -575,19 +576,7 @@ def _handle_error(_): # add a handler for components suites errors to return 404 self.server.errorhandler(InvalidResourceError)(self._invalid_resources_handler) - self._add_url( - "_dash-component-suites//", - self.serve_component_suites, - ) - self._add_url("_dash-layout", self.serve_layout) - self._add_url("_dash-dependencies", self.dependencies) - self._add_url("_dash-update-component", self.dispatch, ["POST"]) - self._add_url("_reload-hash", self.serve_reload_hash) - self._add_url("_favicon.ico", self._serve_default_favicon) - self._add_url("", self.index) - - # catch-all for front-end routes, used by dcc.Location - self._add_url("", self.index) + self._setup_routes() _get_app.APP = self self.enable_pages() @@ -603,6 +592,26 @@ def _add_url(self, name, view_func, methods=("GET",)): # e.g. for adding authentication with flask_login self.routes.append(full_name) + def _setup_routes(self): + self._add_url( + "_dash-component-suites//", + self.serve_component_suites, + ) + self._add_url("_dash-layout", self.serve_layout) + self._add_url("_dash-dependencies", self.dependencies) + self._add_url("_dash-update-component", self.dispatch, ["POST"]) + self._add_url("_reload-hash", self.serve_reload_hash) + self._add_url("_favicon.ico", self._serve_default_favicon) + self._add_url("", self.index) + + if jupyter_dash.active: + self._add_url( + "_alive_" + jupyter_dash.alive_token, jupyter_dash.serve_alive + ) + + # catch-all for front-end routes, used by dcc.Location + self._add_url("", self.index) + @property def layout(self): return self._layout @@ -1674,18 +1683,23 @@ def enable_dev_tools( _reload.watch_thread.daemon = True _reload.watch_thread.start() - if debug and dev_tools.prune_errors: - - secret = gen_salt(20) + if debug: - @self.server.errorhandler(Exception) - def _wrap_errors(error): - # find the callback invocation, if the error is from a callback - # and skip the traceback up to that point - # if the error didn't come from inside a callback, we won't - # skip anything. - tb = _get_traceback(secret, error) - return tb, 500 + if jupyter_dash.active: + jupyter_dash.configure_callback_exception_handling( + self, dev_tools.prune_errors + ) + elif dev_tools.prune_errors: + secret = gen_salt(20) + + @self.server.errorhandler(Exception) + def _wrap_errors(error): + # find the callback invocation, if the error is from a callback + # and skip the traceback up to that point + # if the error didn't come from inside a callback, we won't + # skip anything. + tb = _get_traceback(secret, error) + return tb, 500 if debug and dev_tools.ui: @@ -1792,6 +1806,10 @@ def run( port=os.getenv("PORT", "8050"), proxy=os.getenv("DASH_PROXY", None), debug=None, + jupyter_mode: JupyterDisplayMode = None, + jupyter_width="100%", + jupyter_height=650, + jupyter_server_url=None, dev_tools_ui=None, dev_tools_props_check=None, dev_tools_serve_dev_bundles=None, @@ -1878,6 +1896,20 @@ def run( env: ``DASH_PRUNE_ERRORS`` :type dev_tools_prune_errors: bool + :param jupyter_mode: How to display the application when running + inside a jupyter notebook. + + :param jupyter_width: Determine the width of the output cell + when displaying inline in jupyter notebooks. + :type jupyter_width: str + + :param jupyter_height: Height of app when displayed using + jupyter_mode="inline" + :type jupyter_height: int + + :param jupyter_server_url: Custom server url to display + the app in jupyter notebook. + :param flask_run_options: Given to `Flask.run` :return: @@ -1940,7 +1972,8 @@ def verify_url_part(served_part, url_part, part_name): else: display_url = (protocol, host, f":{port}", path) - self.logger.info("Dash is running on %s://%s%s%s\n", *display_url) + if not jupyter_dash or not jupyter_dash.in_ipython: + self.logger.info("Dash is running on %s://%s%s%s\n", *display_url) if self.config.extra_hot_reload_paths: extra_files = flask_run_options["extra_files"] = [] @@ -1952,7 +1985,18 @@ def verify_url_part(served_part, url_part, part_name): elif os.path.isfile(path): extra_files.append(path) - self.server.run(host=host, port=port, debug=debug, **flask_run_options) + if jupyter_dash.active: + jupyter_dash.run_app( + self, + mode=jupyter_mode, + width=jupyter_width, + height=jupyter_height, + host=host, + port=port, + server_url=jupyter_server_url, + ) + else: + self.server.run(host=host, port=port, debug=debug, **flask_run_options) def enable_pages(self): if not self.use_pages: diff --git a/dash/labextension/package.json b/dash/labextension/package.json new file mode 100644 index 0000000000..2a7a80a8c8 --- /dev/null +++ b/dash/labextension/package.json @@ -0,0 +1,46 @@ +{ + "name": "@plotly/dash-jupyterlab", + "version": "0.4.3", + "description": "A JupyterLab extensions for rendering Plotly Dash apps", + "keywords": [ + "jupyter", + "jupyterlab", + "jupyterlab-extension" + ], + "homepage": "https://github.com/plotly/dash", + "bugs": { + "url": "https://github.com/plotly/dash/issues" + }, + "license": "MIT", + "author": "Plotly", + "files": [ + "lib/**/*.{d.ts,eot,gif,html,jpg,js,js.map,json,png,svg,woff2,ttf}", + "style/**/*.{css,eot,gif,html,jpg,json,png,svg,woff2,ttf}" + ], + "main": "lib/index.js", + "types": "lib/index.d.ts", + "repository": { + "type": "git", + "url": "git+https://github.com/plotly/dash.git" + }, + "scripts": { + "build": "tsc", + "clean": "rimraf lib", + "prepare": "jlpm run clean && jlpm run build", + "prettier": "prettier --write '{!(package),src/**,!(lib)/**}{.js,.jsx,.ts,.tsx,.css,.json,.md}'", + "watch": "tsc -w" + }, + "dependencies": { + "@jupyterlab/application": "^2.0.0 || ^3.0.0", + "@jupyterlab/notebook": "^2.0.0 || ^3.0.0", + "@jupyterlab/console": "^2.0.0 || ^3.0.0" + }, + "devDependencies": { + "prettier": "2.0.5", + "rimraf": "3.0.2", + "typescript": "3.9.3" + }, + "jupyterlab": { + "extension": true + } +} diff --git a/dash/nbextension/README.md b/dash/nbextension/README.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/dash/nbextension/__init__.py b/dash/nbextension/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/dash/nbextension/dash.json b/dash/nbextension/dash.json new file mode 100644 index 0000000000..6e28ddcf08 --- /dev/null +++ b/dash/nbextension/dash.json @@ -0,0 +1,5 @@ +{ + "load_extensions": { + "dash/main": true + } +} diff --git a/dash/nbextension/description.yaml b/dash/nbextension/description.yaml new file mode 100644 index 0000000000..909e9d7a73 --- /dev/null +++ b/dash/nbextension/description.yaml @@ -0,0 +1,9 @@ +Type: Jupyter Notebook Extension +Compatibility: 3.x, 4.x, 5.x, 6.x +Name: Jupyter Dash +Main: main.js +Link: README.md +Description: | + Jupyer Dash support +Parameters: +- none diff --git a/dash/nbextension/main.js b/dash/nbextension/main.js new file mode 100644 index 0000000000..436e371bc5 --- /dev/null +++ b/dash/nbextension/main.js @@ -0,0 +1,42 @@ +// file my_extension/main.js + +define([ + 'base/js/namespace', + 'base/js/utils', +], function(Jupyter, utils){ + + function load_ipython_extension(){ + var notebookUrl = window.location.href + var baseUrl = utils.get_body_data('baseUrl'); + var baseNotebooks = baseUrl + "notebooks" + var n = notebookUrl.search(baseNotebooks) + var jupyterServerUrl = notebookUrl.slice(0, n) + + var register_comm = function() { + Jupyter.notebook.kernel.comm_manager.register_target('dash', + function (comm, msg) { + // Register handlers for later messages: + comm.on_msg(function (msg) { + console.log("Dash message", msg); + var msgData = msg.content.data; + if (msgData.type === 'base_url_request') { + comm.send({ + type: 'base_url_response', + server_url: jupyterServerUrl, + base_subpath: baseUrl, + frontend: "notebook" + }); + } else if (msgData.type === 'show') { + + } + }); + }); + }; + + Jupyter.notebook.events.on('kernel_ready.Kernel', register_comm) + } + + return { + load_ipython_extension: load_ipython_extension + }; +}); diff --git a/package.json b/package.json index b1966c10cc..f5644f3601 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,7 @@ "private::build.components": "python dash/development/update_components.py 'all'", "private::cibuild.renderer": "cd dash/dash-renderer && renderer build", "private::build.renderer": "cd dash/dash-renderer && renderer build", + "private::build.jupyterlab": "cd \\@plotly/dash-jupyterlab && jlpm install && jlpm build:pack", "private::lint.black": "node -e \"if ((process.env.PYVERSION || 'python39') !== 'python36'){process.exit(1)} \" || black dash tests --exclude metadata_test.py --check", "private::lint.flake8": "flake8 --exclude=metadata_test.py dash tests", "private::lint.pylint-dash": "PYLINTRC=${PYLINTRC:=.pylintrc39} && pylint dash setup.py --rcfile=$PYLINTRC", @@ -33,7 +34,7 @@ "private::test.integration-dash-import": "cd tests/integration/dash && python dash_import_test.py", "cibuild": "run-s private::cibuild.*", "build": "run-s private::build.*", - "build.sequential": "npm run private::build.renderer && npm run private::build.components -- --concurrency 1", + "build.sequential": "npm run private::build.jupyterlab && npm run private::build.renderer && npm run private::build.components -- --concurrency 1", "format": "run-s private::format.*", "initialize": "run-s private::initialize.*", "prepare": "husky install", diff --git a/requires-ci.txt b/requires-ci.txt index 117008fb71..3bf72c85e7 100644 --- a/requires-ci.txt +++ b/requires-ci.txt @@ -24,3 +24,4 @@ pytest-sugar==0.9.6 xlrd>=2.0.1;python_version>="3.8" xlrd<2;python_version<"3.8" pytest-rerunfailures +jupyterlab<4.0.0 diff --git a/requires-install.txt b/requires-install.txt index 8730b6d6ea..f92ceb759b 100644 --- a/requires-install.txt +++ b/requires-install.txt @@ -6,3 +6,8 @@ dash_core_components==2.0.0 dash_table==5.0.0 importlib-metadata==4.8.3;python_version<"3.7" contextvars==2.4;python_version<"3.7" +typing_extensions>=4.1.1 +requests +retrying +ansi2html +nest-asyncio diff --git a/setup.py b/setup.py index 2063caca8a..9a99e22641 100644 --- a/setup.py +++ b/setup.py @@ -33,7 +33,7 @@ def read_req_file(req_type): "testing": read_req_file("testing"), "celery": read_req_file("celery"), "diskcache": read_req_file("diskcache"), - "compress": read_req_file("compress"), + "compress": read_req_file("compress") }, entry_points={ "console_scripts": [ @@ -74,4 +74,18 @@ def read_req_file(req_type): "Topic :: Software Development :: Libraries :: Application Frameworks", "Topic :: Software Development :: Widget Sets", ], + data_files=[ + # like `jupyter nbextension install --sys-prefix` + ("share/jupyter/nbextensions/dash", [ + "dash/nbextension/main.js", + ]), + # like `jupyter nbextension enable --sys-prefix` + ("etc/jupyter/nbconfig/notebook.d", [ + "dash/nbextension/dash.json" + ]), + # Place jupyterlab extension in extension directory + ("share/jupyter/lab/extensions", [ + "dash/labextension/dist/dash-jupyterlab.tgz" + ]), + ], )