From f585e6918abca13142397a1f6312532f2767533a Mon Sep 17 00:00:00 2001 From: Nicholas Bollweg Date: Sun, 1 Nov 2020 10:37:22 -0500 Subject: [PATCH 01/31] start binder --- .binder/environment.yml | 10 ++++++++++ .binder/postBuild | 4 ++++ 2 files changed, 14 insertions(+) create mode 100644 .binder/environment.yml create mode 100755 .binder/postBuild diff --git a/.binder/environment.yml b/.binder/environment.yml new file mode 100644 index 0000000..5ba3655 --- /dev/null +++ b/.binder/environment.yml @@ -0,0 +1,10 @@ +name: jupyter-markup + +channels: + - conda-forge + - nodefaults + +dependencies: + - jupyterlab >=2,<3 + - nodejs >=14,<15 + - python >=3.7,<3.8 diff --git a/.binder/postBuild b/.binder/postBuild new file mode 100755 index 0000000..d47ccd0 --- /dev/null +++ b/.binder/postBuild @@ -0,0 +1,4 @@ +#!/usr/bin/env bash +jlpm --ignore-optional +jupyter labextension link . --no-build --debug +jupyter lab build --debug --minimize=True --dev-build=False From 343e72bf8966d32059ae6911ca46dc1eeee8cc71 Mon Sep 17 00:00:00 2001 From: Nicholas Bollweg Date: Sun, 1 Nov 2020 11:02:24 -0500 Subject: [PATCH 02/31] add serverextension for wasm serving --- .binder/environment.yml | 2 +- .binder/postBuild | 7 +++ LICENSE.txt | 27 ++++++++++++ MANIFEST.in | 1 + etc/jupyterlab-markup-serverextension.json | 7 +++ package.json | 3 +- setup.cfg | 40 ++++++++++++++++++ setup.py | 1 + src_py/jupyterlab_markup/__init__.py | 26 ++++++++++++ .../__pycache__/__init__.cpython-37.pyc | Bin 0 -> 801 bytes 10 files changed, 112 insertions(+), 2 deletions(-) create mode 100644 LICENSE.txt create mode 100644 MANIFEST.in create mode 100644 etc/jupyterlab-markup-serverextension.json create mode 100644 setup.cfg create mode 100644 setup.py create mode 100644 src_py/jupyterlab_markup/__init__.py create mode 100644 src_py/jupyterlab_markup/__pycache__/__init__.cpython-37.pyc diff --git a/.binder/environment.yml b/.binder/environment.yml index 5ba3655..bc504b2 100644 --- a/.binder/environment.yml +++ b/.binder/environment.yml @@ -1,4 +1,4 @@ -name: jupyter-markup +name: jupyterlab-markup channels: - conda-forge diff --git a/.binder/postBuild b/.binder/postBuild index d47ccd0..5dd9a6d 100755 --- a/.binder/postBuild +++ b/.binder/postBuild @@ -1,4 +1,11 @@ #!/usr/bin/env bash +set -eux + +python -m pip install -e . --no-deps --ignore-installed +jupyter serverextension enable jupyterlab_markup --sys-prefix +jupyter serverextension list + jlpm --ignore-optional jupyter labextension link . --no-build --debug jupyter lab build --debug --minimize=True --dev-build=False +jupyter labextension list diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..ad1306d --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,27 @@ +Copyright (c) 2020 Angus Hollands +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/MANIFEST.in b/MANIFEST.in new file mode 100644 index 0000000..46fb23d --- /dev/null +++ b/MANIFEST.in @@ -0,0 +1 @@ +include LICENSE package.json diff --git a/etc/jupyterlab-markup-serverextension.json b/etc/jupyterlab-markup-serverextension.json new file mode 100644 index 0000000..bf2418f --- /dev/null +++ b/etc/jupyterlab-markup-serverextension.json @@ -0,0 +1,7 @@ +{ + "NotebookApp": { + "nbserver_extensions": { + "jupyterlab_markup": true + } + } +} diff --git a/package.json b/package.json index a3a4a32..d8d69f0 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,8 @@ "author": "Angus Hollands", "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}" + "style/**/*.{css,eot,gif,html,jpg,json,png,svg,woff2,ttf}", + "LICENSE.txt" ], "main": "lib/index.js", "types": "lib/index.d.ts", diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 0000000..fad60e2 --- /dev/null +++ b/setup.cfg @@ -0,0 +1,40 @@ +[metadata] +name = jupyterlab_markup +version = 0.2.1 +description = Additional markdown rendering support in markdown. +long_description = file: README.md +long_description_content_type = text/markdown +url = https://github.com/agoose77/jupyterlab-markup +author = Angus Hollands +license = BSD-3-Clause +keywords = + Interactive + Interpreter + Web +classifiers = + Framework :: Jupyter + Intended Audience :: Developers + Intended Audience :: Information Technology + License :: OSI Approved :: BSD License + Programming Language :: Python + Programming Language :: Python :: 3.6 + Programming Language :: Python :: 3.7 + Programming Language :: Python :: 3.8 + Programming Language :: Python :: 3 :: Only + +[options] +install_requires = + notebook +package_dir = + = src_py +packages = find: +include_package_data = True +zip_safe = False + +[options.data_files] +etc/jupyter/jupyter_notebook_config.d = + etc/jupyterlab-markup-serverextension.json + +[options.packages.find] +where = + py_src diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..aefdf20 --- /dev/null +++ b/setup.py @@ -0,0 +1 @@ +__import__("setuptools").setup() diff --git a/src_py/jupyterlab_markup/__init__.py b/src_py/jupyterlab_markup/__init__.py new file mode 100644 index 0000000..db8abe8 --- /dev/null +++ b/src_py/jupyterlab_markup/__init__.py @@ -0,0 +1,26 @@ +import mimetypes + +__version__ = "0.2.1" + + +# https://www.iana.org/assignments/provisional-standard-media-types/provisional-standard-media-types.xhtml +WASM_EXT = ".wasm" +WASM_MIME = "application/wasm" + + +def load_jupyter_server_extension(app): + """ ensure tornado serves `.wasm` files with + """ + _guess = mimetypes.guess_type + + def guess_type(name, strict=True): + if name.endswith(WASM_EXT): + return (WASM_MIME, None) + return _guess(name, strict) + + mimetypes.guess_type = guess_type + app.log.warning( + "[jupyterlab_markup] mimetypes patched to guess: %s => %s", + WASM_EXT, + WASM_MIME + ) diff --git a/src_py/jupyterlab_markup/__pycache__/__init__.cpython-37.pyc b/src_py/jupyterlab_markup/__pycache__/__init__.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..236cb518642ea3c0fa4f9a75f12dd068a04d50c2 GIT binary patch literal 801 zcmaJfgc5Z&=toK!6pTtV6k2OqF0Cxl7_NIh^!E{OyRpsn$4QU`x&chit4oTvnd z|A5*fKZ(6^;xBMw))Z0+iLvH+XZCsC?0BE-?)C`UH}i#kb_n@ygFl8bd4#Sg0w){_ zG9lcV0Cy)2_a-j)d2mK19)}T$IzNy_hHL+Bd@nv&`|&bW<$6DDnj+6qo!3=jGZ|PK znyv1myXY!L&}0su2oFAxDR76oXWe(O0ZkZe$lN)1xu-qtU${WHZ}rwO6_r{@L3J&w zl-E=Vc`6h=Zl#auG%qk(=K7>Z(Z-pTv}WH{_6EATiLfE(M8gHh>)PRD>Ws)o%x&O3 zt|Aylt_elOl`Un$XOBk%cJzE~y6tN4bZ`_o#;ej&m_TWnXF38Cu-QT=^_Bd5nVi(6 zNS0!jG_szHOsizRXjWRtB7J?QOy!$J!~RVrN@lECUD;s2xd~%=m21Z0W@UP<4AZMd z?Azl62n2c%!up`7Q_e1>SUVia#9NJ1!@m(97Il^usvmDFtna_rc6O=uik5jPY<)$W zRA(oG Date: Sun, 1 Nov 2020 11:03:20 -0500 Subject: [PATCH 03/31] ignoring more files, specify python version --- .gitignore | 9 ++++++--- setup.cfg | 1 + .../__pycache__/__init__.cpython-37.pyc | Bin 801 -> 0 bytes 3 files changed, 7 insertions(+), 3 deletions(-) delete mode 100644 src_py/jupyterlab_markup/__pycache__/__init__.cpython-37.pyc diff --git a/.gitignore b/.gitignore index be49ab1..5f77a3d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,9 @@ +__pycache__ +.ipynb_checkpoints *.bundle.* -lib/ -node_modules/ *.egg-info/ -.ipynb_checkpoints +build/ demo/ +dist/ +lib/ +node_modules/ diff --git a/setup.cfg b/setup.cfg index fad60e2..b95c98f 100644 --- a/setup.cfg +++ b/setup.cfg @@ -7,6 +7,7 @@ long_description_content_type = text/markdown url = https://github.com/agoose77/jupyterlab-markup author = Angus Hollands license = BSD-3-Clause +python_requires = >=3.6 keywords = Interactive Interpreter diff --git a/src_py/jupyterlab_markup/__pycache__/__init__.cpython-37.pyc b/src_py/jupyterlab_markup/__pycache__/__init__.cpython-37.pyc deleted file mode 100644 index 236cb518642ea3c0fa4f9a75f12dd068a04d50c2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 801 zcmaJfgc5Z&=toK!6pTtV6k2OqF0Cxl7_NIh^!E{OyRpsn$4QU`x&chit4oTvnd z|A5*fKZ(6^;xBMw))Z0+iLvH+XZCsC?0BE-?)C`UH}i#kb_n@ygFl8bd4#Sg0w){_ zG9lcV0Cy)2_a-j)d2mK19)}T$IzNy_hHL+Bd@nv&`|&bW<$6DDnj+6qo!3=jGZ|PK znyv1myXY!L&}0su2oFAxDR76oXWe(O0ZkZe$lN)1xu-qtU${WHZ}rwO6_r{@L3J&w zl-E=Vc`6h=Zl#auG%qk(=K7>Z(Z-pTv}WH{_6EATiLfE(M8gHh>)PRD>Ws)o%x&O3 zt|Aylt_elOl`Un$XOBk%cJzE~y6tN4bZ`_o#;ej&m_TWnXF38Cu-QT=^_Bd5nVi(6 zNS0!jG_szHOsizRXjWRtB7J?QOy!$J!~RVrN@lECUD;s2xd~%=m21Z0W@UP<4AZMd z?Azl62n2c%!up`7Q_e1>SUVia#9NJ1!@m(97Il^usvmDFtna_rc6O=uik5jPY<)$W zRA(oG Date: Sun, 1 Nov 2020 15:48:17 -0500 Subject: [PATCH 04/31] rework with manager, expose plugin machinery, lazy load more --- .binder/postBuild | 4 +- .gitignore | 2 + .prettierignore | 5 ++ .prettierrc | 3 + MANIFEST.in | 2 +- README.md | 19 ++--- package.json | 9 ++- src/factories.ts | 15 ---- src/index.ts | 78 +++++++++++++++++--- src/manager.ts | 79 ++++++++++++++++++++ src/markdown-it-deflist.d.ts | 12 +-- src/markdown-it-footnote.d.ts | 12 +-- src/renderers.ts | 125 +++++++++++++++---------------- src/tokens.ts | 26 +++++++ src/widgets.ts | 135 ++++++++++++---------------------- tsconfig.json | 7 +- yarn.lock | 5 ++ 17 files changed, 330 insertions(+), 208 deletions(-) create mode 100644 .prettierignore create mode 100644 .prettierrc delete mode 100644 src/factories.ts create mode 100644 src/manager.ts create mode 100644 src/tokens.ts diff --git a/.binder/postBuild b/.binder/postBuild index 5dd9a6d..1f78f51 100755 --- a/.binder/postBuild +++ b/.binder/postBuild @@ -6,6 +6,6 @@ jupyter serverextension enable jupyterlab_markup --sys-prefix jupyter serverextension list jlpm --ignore-optional -jupyter labextension link . --no-build --debug -jupyter lab build --debug --minimize=True --dev-build=False +jupyter labextension install . --no-build --debug +jupyter lab build --debug --minimize=False --dev-build=False jupyter labextension list diff --git a/.gitignore b/.gitignore index 5f77a3d..993bcf5 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,5 @@ demo/ dist/ lib/ node_modules/ +untitled* +Untitled* diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..8023bd8 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,5 @@ +__pycache__/ +*.egg-info/ +build/ +lib/ +node_modules/ diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..8db60ca --- /dev/null +++ b/.prettierrc @@ -0,0 +1,3 @@ +{ + "singleQuote": true +} diff --git a/MANIFEST.in b/MANIFEST.in index 46fb23d..93f01f6 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1 +1 @@ -include LICENSE package.json +include LICENSE.txt diff --git a/README.md b/README.md index becaeee..e09a222 100644 --- a/README.md +++ b/README.md @@ -2,28 +2,28 @@ Adds additional rendering support to markdown in JupyterLab by using [markdown-it](https://github.com/markdown-it/markdown-it), and the following plugins: -* [markdown-it-diagrams](https://github.com/agoose77/markdown-it-diagrams) -* [markdown-it-footnote](https://github.com/markdown-it/markdown-it-footnote) -* [markdown-it-deflist](https://github.com/markdown-it/markdown-it-deflist) - +- [markdown-it-diagrams](https://github.com/agoose77/markdown-it-diagrams) +- [markdown-it-footnote](https://github.com/markdown-it/markdown-it-footnote) +- [markdown-it-deflist](https://github.com/markdown-it/markdown-it-deflist) ![Full example rendering vs markup.](https://i.imgur.com/OL9oGcq.png) ![svgbob rendering](https://i.imgur.com/RbDioU8.gif) ![svgbob rendering](https://i.imgur.com/IQSasVZ.gif) - ## Prerequisites -* JupyterLab +- JupyterLab ## Installation Ensure mimetype exists for wasm: + ```bash echo "application/wasm wasm" | sudo tee -a /etc/mime.types ``` Install extension: + ```bash jupyter labextension install @agoose77/jupyterlab-markup ``` @@ -47,14 +47,15 @@ npm run build jupyter lab build ``` -``` mermaid +```mermaid graph TD; A-->B; A-->C; B-->D; C-->D; ``` -``` bob + +```bob .---. /-o-/-- .-/ / /-> @@ -62,4 +63,4 @@ graph TD; '-. \ \ / ' -``` \ No newline at end of file +``` diff --git a/package.json b/package.json index d8d69f0..910bc2a 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,7 @@ "license": "BSD-3-Clause", "author": "Angus Hollands", "files": [ - "lib/**/*.{d.ts,eot,gif,html,jpg,js,js.map,json,png,svg,woff2,ttf}", + "{src,lib}/**/*.{ts,eot,gif,html,jpg,js,js.map,json,png,svg,woff2,ttf}", "style/**/*.{css,eot,gif,html,jpg,json,png,svg,woff2,ttf}", "LICENSE.txt" ], @@ -27,8 +27,8 @@ "scripts": { "build": "tsc", "clean": "rimraf lib", - "prepare": "npm run clean && npm run build", - "watch": "tsc -w" + "watch": "tsc -w", + "lint": "prettier --list-different --write \"**/*{.ts,.tsx,.js,.jsx,.css,.json,.md,.yml}\"" }, "dependencies": { "@jupyterlab/application": "^2.0.2", @@ -42,7 +42,8 @@ "markdown-it-container": "^2.0.0", "markdown-it-deflist": "^2.0.3", "markdown-it-diagrams": "^0.1.2", - "markdown-it-footnote": "^3.0.2" + "markdown-it-footnote": "^3.0.2", + "prettier": "^2.1.2" }, "devDependencies": { "@types/codemirror": "^0.0.74", diff --git a/src/factories.ts b/src/factories.ts deleted file mode 100644 index 63d611e..0000000 --- a/src/factories.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { IRenderMime } from '@jupyterlab/rendermime-interfaces'; -import {RenderedMarkdown} from './widgets' - - -export const MIME_TYPE = 'text/markdown'; - -/** - * A mime renderer factory for Markdown. - */ -export const markupRendererFactory: IRenderMime.IRendererFactory = { - safe: true, - mimeTypes: [MIME_TYPE], - defaultRank: 60, - createRenderer: options => new RenderedMarkdown(options) -}; diff --git a/src/index.ts b/src/index.ts index 829cf17..724e777 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,17 +1,71 @@ -import {IRenderMimeRegistry} from '@jupyterlab/rendermime'; -import {markupRendererFactory} from "./factories"; -import {JupyterFrontEnd, JupyterFrontEndPlugin} from '@jupyterlab/application'; +import { + IRenderMimeRegistry, + markdownRendererFactory, +} from '@jupyterlab/rendermime'; +import { + JupyterFrontEnd, + JupyterFrontEndPlugin, +} from '@jupyterlab/application'; +import { IMarkdownIt, PLUGIN_ID } from './tokens'; +import { MarkdownItManager } from './manager'; +import { RenderedMarkdown } from './widgets'; -const extension: JupyterFrontEndPlugin = { - id: '@agoose77/jupyterlab-markup', - autoStart: true, - requires: [IRenderMimeRegistry], - activate: (app: JupyterFrontEnd, registry: IRenderMimeRegistry) => { - console.log('JupyterLab extension @agoose77/jupyterlab-markup is activated!'); - registry.addFactory(markupRendererFactory); - } +const core: JupyterFrontEndPlugin = { + id: PLUGIN_ID, + autoStart: true, + requires: [IRenderMimeRegistry], + provides: IMarkdownIt, + activate: (app: JupyterFrontEnd, registry: IRenderMimeRegistry) => { + const manager = new MarkdownItManager(); + RenderedMarkdown.markdownItManager = manager; + markdownRendererFactory.createRenderer = (options) => + new RenderedMarkdown(options); + return manager; + }, }; +const diagrams: JupyterFrontEndPlugin = { + id: `${PLUGIN_ID}:diagrams`, + autoStart: true, + requires: [IMarkdownIt], + activate: (app: JupyterFrontEnd, markdownIt: IMarkdownIt) => { + markdownIt.addPluginProvider('markdown-it-diagrams', async () => { + const { diagramPlugin, awaitRenderAvailable } = await import( + /* webpackChunkName: "markdown-it-diagrams" */ 'markdown-it-diagrams' + ); + await awaitRenderAvailable(); + return diagramPlugin; + }); + }, +}; + +const footnote: JupyterFrontEndPlugin = { + id: `${PLUGIN_ID}:footnote`, + autoStart: true, + requires: [IMarkdownIt], + activate: (app: JupyterFrontEnd, markdownIt: IMarkdownIt) => { + markdownIt.addPluginProvider('markdown-it-footnote', async () => { + const footnotePlugin = await import( + /* webpackChunkName: "markdown-it-footnote" */ 'markdown-it-footnote' + ); + return footnotePlugin.default; + }); + }, +}; + +const deflist: JupyterFrontEndPlugin = { + id: `${PLUGIN_ID}:deflist`, + autoStart: true, + requires: [IMarkdownIt], + activate: (app: JupyterFrontEnd, markdownIt: IMarkdownIt) => { + markdownIt.addPluginProvider('markdown-it-deflist', async () => { + const deflistPlugin = await import( + /* webpackChunkName: "markdown-it-deflist" */ 'markdown-it-deflist' + ); + return deflistPlugin.default; + }); + }, +}; -export default extension; +export default [core, diagrams, footnote, deflist]; diff --git a/src/manager.ts b/src/manager.ts new file mode 100644 index 0000000..4e2d955 --- /dev/null +++ b/src/manager.ts @@ -0,0 +1,79 @@ +import * as MarkdownIt from 'markdown-it'; +import { IMarkdownIt } from './tokens'; + +import { CodeMirrorEditor, Mode } from '@jupyterlab/codemirror'; + +export class MarkdownItManager implements IMarkdownIt { + private _pluginProviders: Map< + string, + IMarkdownIt.IPluginProvider + > = new Map(); + + /** + * add a provider for a plugin which can be resolve lazily + */ + addPluginProvider(name: string, provider: IMarkdownIt.IPluginProvider): void { + this._pluginProviders.set(name, provider); + } + + /** + * remove a provider by name + */ + removePluginProvider(name: string) { + this._pluginProviders.delete(name); + } + + async getMarkdownIt(options: MarkdownIt.Options = {}): Promise { + let md = new MarkdownIt({ + ...this.baseMarkdownItOptions, + ...options, + }); + + for (const [name, provider] of this._pluginProviders.entries()) { + try { + const plugin = await provider(); + md = md.use(plugin); + } catch (err) { + console.warn(`Failed to load/use markdown-it plugin ${name}`, err); + } + } + + return md; + } + + get baseMarkdownItOptions() { + return { + html: true, + linkify: true, + typographer: true, + langPrefix: `cm-s-${CodeMirrorEditor.defaultConfig.theme} language-`, + highlight: this.highlightCode, + }; + } + + highlightCode = (str: string, lang: string) => { + if (!lang) { + return ''; // use external default escaping + } + try { + let spec = Mode.findBest(lang); + console.log(spec); + if (!spec) { + console.log(`No CodeMirror mode: ${lang}`); + return; + } + + let el = document.createElement('div'); + try { + Mode.run(str, spec.mime, el); + return el.innerHTML; + } catch (err) { + console.log(`Failed to highlight ${lang} code`, err); + } + } catch (err) { + console.log(`No CodeMirror mode: ${lang}`); + console.log(`Require CodeMirror mode error: ${err}`); + } + return ''; + }; +} diff --git a/src/markdown-it-deflist.d.ts b/src/markdown-it-deflist.d.ts index 6fbcf19..5b648fa 100644 --- a/src/markdown-it-deflist.d.ts +++ b/src/markdown-it-deflist.d.ts @@ -1,10 +1,10 @@ declare module 'markdown-it-deflist' { - import MarkdownIt = require('markdown-it'); + import MarkdownIt = require('markdown-it'); - namespace markdownItDeflist { - function deflist_plugin(md: MarkdownIt): void; - } + namespace markdownItDeflist { + function deflist_plugin(md: MarkdownIt): void; + } - const MarkdownItDeflist: typeof markdownItDeflist.deflist_plugin; - export = MarkdownItDeflist; + const MarkdownItDeflist: typeof markdownItDeflist.deflist_plugin; + export = MarkdownItDeflist; } diff --git a/src/markdown-it-footnote.d.ts b/src/markdown-it-footnote.d.ts index bdf3601..26692e9 100644 --- a/src/markdown-it-footnote.d.ts +++ b/src/markdown-it-footnote.d.ts @@ -1,10 +1,10 @@ declare module 'markdown-it-footnote' { - import MarkdownIt = require('markdown-it'); + import MarkdownIt = require('markdown-it'); - namespace markdownItFootnote { - function footnote_plugin(md: MarkdownIt): void; - } + namespace markdownItFootnote { + function footnote_plugin(md: MarkdownIt): void; + } - const MarkdownItFootnote: typeof markdownItFootnote.footnote_plugin; - export = MarkdownItFootnote; + const MarkdownItFootnote: typeof markdownItFootnote.footnote_plugin; + export = MarkdownItFootnote; } diff --git a/src/renderers.ts b/src/renderers.ts index a25913e..7b82404 100644 --- a/src/renderers.ts +++ b/src/renderers.ts @@ -1,8 +1,7 @@ -import {removeMath, renderHTML, replaceMath} from "@jupyterlab/rendermime" -import {IRenderMime} from '@jupyterlab/rendermime-interfaces'; -import {ISanitizer} from '@jupyterlab/apputils'; -import * as MarkdownIt from "markdown-it" -import {awaitRenderAvailable} from "markdown-it-diagrams" +import { removeMath, renderHTML, replaceMath } from '@jupyterlab/rendermime'; +import { IRenderMime } from '@jupyterlab/rendermime-interfaces'; +import { ISanitizer } from '@jupyterlab/apputils'; +import * as MarkdownIt from 'markdown-it'; /** * Render Markdown into a host node. @@ -12,85 +11,83 @@ import {awaitRenderAvailable} from "markdown-it-diagrams" * @returns A promise which resolves when rendering is complete. */ export async function renderMarkdown( - options: renderMarkdown.IRenderOptions + options: renderMarkdown.IRenderOptions ): Promise { - let {host, source, md, ...others} = options; + let { host, source, md, ...others } = options; - // Clear the content if there is no source. - if (!source) { - host.textContent = ''; - return; - } + // Clear the content if there is no source. + if (!source) { + host.textContent = ''; + return; + } - // Separate math from normal markdown text. - let parts = removeMath(source); + // Separate math from normal markdown text. + let parts = removeMath(source); - // Wait for WASM import for svgbob! - await awaitRenderAvailable(); - let html = md.render(parts['text']); + let html = md.render(parts['text']); - // Replace math. - html = replaceMath(html, parts['math']); + // Replace math. + html = replaceMath(html, parts['math']); - // Render HTML. - await renderHTML({ - host, - source: html, - ...others - }); + // Render HTML. + await renderHTML({ + host, + source: html, + ...others, + }); } /** * The namespace for the `renderMarkdown` function statics. */ export namespace renderMarkdown { + /** + * The options for the `renderMarkdown` function. + */ + export interface IRenderOptions { /** - * The options for the `renderMarkdown` function. + * The host node for the rendered Markdown. */ - export interface IRenderOptions { - /** - * The host node for the rendered Markdown. - */ - host: HTMLElement; + host: HTMLElement; - /** - * The Markdown source to render. - */ - source: string; + /** + * The Markdown source to render. + */ + source: string; - /** - * Whether the source is trusted. - */ - trusted: boolean; + /** + * Whether the source is trusted. + */ + trusted: boolean; - /** - * The html sanitizer for untrusted source. - */ - sanitizer: ISanitizer; + /** + * The html sanitizer for untrusted source. + */ + sanitizer: ISanitizer; - /** - * An optional url resolver. - */ - resolver: IRenderMime.IResolver | null; + /** + * An optional url resolver. + */ + resolver: IRenderMime.IResolver | null; - /** - * An optional link handler. - */ - linkHandler: IRenderMime.ILinkHandler | null; + /** + * An optional link handler. + */ + linkHandler: IRenderMime.ILinkHandler | null; - /** - * Whether the node should be typeset. - */ - shouldTypeset: boolean; + /** + * Whether the node should be typeset. + */ + shouldTypeset: boolean; - /** - * MarkdownIt renderer - */ - md: MarkdownIt; + /** + * MarkdownIt renderer + */ + md: MarkdownIt; - /** - * The LaTeX typesetter for the application. - */ - latexTypesetter: IRenderMime.ILatexTypesetter | null; - } + /** + * The LaTeX typesetter for the application. + */ + latexTypesetter: IRenderMime.ILatexTypesetter | null; + } } diff --git a/src/tokens.ts b/src/tokens.ts new file mode 100644 index 0000000..0d8092c --- /dev/null +++ b/src/tokens.ts @@ -0,0 +1,26 @@ +import { Token } from '@lumino/coreutils'; +import MarkdownIt from 'markdown-it'; + +export const PLUGIN_ID = '@agoose77/jupyterlab-markup'; + +/* tslint:disable */ +/** + * The MarkdownIt manager token. + */ +export const IMarkdownIt = new Token(PLUGIN_ID); +/* tslint:enable */ + +export interface IMarkdownIt { + addPluginProvider(name: string, provider: IMarkdownIt.IPluginProvider): void; + removePluginProvider(name: string): void; + getMarkdownIt(options?: MarkdownIt.Options): Promise; +} + +export namespace IMarkdownIt { + export interface IPlugin { + (md: MarkdownIt, ...params: any[]): void; + } + export interface IPluginProvider { + (): Promise; + } +} diff --git a/src/widgets.ts b/src/widgets.ts index 48a81b4..c8b8ca6 100644 --- a/src/widgets.ts +++ b/src/widgets.ts @@ -1,94 +1,57 @@ -import {RenderedHTMLCommon} from "@jupyterlab/rendermime" -import {IRenderMime} from '@jupyterlab/rendermime-interfaces'; -import {Message} from '@lumino/messaging'; -import * as renderers from "./renderers"; -import {CodeMirrorEditor, Mode} from '@jupyterlab/codemirror'; -import {diagramPlugin} from "markdown-it-diagrams"; -import * as MarkdownIt from "markdown-it"; -import * as MarkdownItFootnote from "markdown-it-footnote" -import * as MarkdownItDeflist from "markdown-it-deflist" - - -function highlightCode(str: string, lang: string) { - if (!lang) { - return ''; // use external default escaping - } - try { - let spec = Mode.findBest(lang); - console.log(spec); - if (!spec) { - console.log(`No CodeMirror mode: ${lang}`); - return; - } - - let el = document.createElement('div'); - try { - Mode.run(str, spec.mime, el); - return el.innerHTML; - } catch (err) { - console.log(`Failed to highlight ${lang} code`, err); - } - - } catch (err) { - console.log(`No CodeMirror mode: ${lang}`); - console.log(`Require CodeMirror mode error: ${err}`); - } - return ''; -} +import { RenderedHTMLCommon } from '@jupyterlab/rendermime'; +import { IRenderMime } from '@jupyterlab/rendermime-interfaces'; +import { Message } from '@lumino/messaging'; +import * as renderers from './renderers'; +import * as MarkdownIt from 'markdown-it'; +import { IMarkdownIt } from './tokens'; /** * A mime renderer for displaying Markdown with embedded latex. */ export class RenderedMarkdown extends RenderedHTMLCommon { - /** - * Construct a new rendered markdown widget. - * - * @param options - The options for initializing the widget. - */ - md: MarkdownIt; - - constructor(options: IRenderMime.IRendererOptions) { - super(options); - this.addClass('jp-RenderedMarkdown'); - this.md = new MarkdownIt({ - html: true, - linkify: true, - typographer: true, - langPrefix: `cm-s-${CodeMirrorEditor.defaultConfig.theme} language-`, - highlight: highlightCode - }) - .use(diagramPlugin) - .use(MarkdownItFootnote) - .use(MarkdownItDeflist); + /** + * Construct a new rendered markdown widget. + * + * @param options - The options for initializing the widget. + */ + md: MarkdownIt; + static markdownItManager: IMarkdownIt; + + constructor(options: IRenderMime.IRendererOptions) { + super(options); + this.addClass('jp-RenderedMarkdown'); + } + + /** + * Render a mime model. + * + * @param model - The mime model to render. + * + * @returns A promise which resolves when rendering is complete. + */ + async render(model: IRenderMime.IMimeModel): Promise { + if (this.md == null) { + this.md = await RenderedMarkdown.markdownItManager.getMarkdownIt(); } - - /** - * Render a mime model. - * - * @param model - The mime model to render. - * - * @returns A promise which resolves when rendering is complete. - */ - render(model: IRenderMime.IMimeModel): Promise { - return renderers.renderMarkdown({ - host: this.node, - source: String(model.data[this.mimeType]), - trusted: model.trusted, - resolver: this.resolver, - sanitizer: this.sanitizer, - linkHandler: this.linkHandler, - shouldTypeset: this.isAttached, - md: this.md, - latexTypesetter: this.latexTypesetter - }); - } - - /** - * A message handler invoked on an `'after-attach'` message. - */ - onAfterAttach(msg: Message): void { - if (this.latexTypesetter) { - this.latexTypesetter.typeset(this.node); - } + return await renderers.renderMarkdown({ + host: this.node, + source: String(model.data[this.mimeType]), + trusted: model.trusted, + resolver: this.resolver, + sanitizer: this.sanitizer, + linkHandler: this.linkHandler, + shouldTypeset: this.isAttached, + md: this.md, + latexTypesetter: this.latexTypesetter, + }); + } + + /** + * A message handler invoked on an `'after-attach'` message. + */ + onAfterAttach(msg: Message): void { + if (this.latexTypesetter) { + this.latexTypesetter.typeset(this.node); } + } } diff --git a/tsconfig.json b/tsconfig.json index 95377a1..1f2dcda 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -2,7 +2,7 @@ "compilerOptions": { "declaration": true, "lib": ["es2015", "dom"], - "module": "commonjs", + "module": "esnext", "moduleResolution": "node", "noEmitOnError": true, "noUnusedLocals": true, @@ -10,9 +10,10 @@ "rootDir": "src", "strict": true, "strictNullChecks": false, - "target": "es2015", + "target": "es2017", "types": [], - "allowSyntheticDefaultImports": true + "allowSyntheticDefaultImports": true, + "sourceMap": true }, "include": ["src/*"] } diff --git a/yarn.lock b/yarn.lock index 8bf2be3..a5f25e3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1479,6 +1479,11 @@ postcss@^7.0.5: source-map "^0.6.1" supports-color "^6.1.0" +prettier@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.1.2.tgz#3050700dae2e4c8b67c4c3f666cdb8af405e1ce5" + integrity sha512-16c7K+x4qVlJg9rEbXl7HEGmQyZlG4R9AgP+oHKRMsMsuk8s+ATStlf1NpDqyBI1HpVyfjLOeMhH2LvuNvV5Vg== + 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" From 1ddc7f28040bb186510c2f5587329a0c131edcdf Mon Sep 17 00:00:00 2001 From: Nicholas Bollweg Date: Sun, 1 Nov 2020 16:04:14 -0500 Subject: [PATCH 05/31] contributing docs --- CONTRIBUTING.md | 60 +++++++++++++++++++++++++++++++++++++++++ README.md | 35 ++++++++++++------------ package.json | 2 +- src/index.ts | 72 ++----------------------------------------------- src/plugin.ts | 71 ++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 151 insertions(+), 89 deletions(-) create mode 100644 CONTRIBUTING.md create mode 100644 src/plugin.ts diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..e9cd31c --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,60 @@ +## Development + +For a development install (requires npm version 4 or later), do the following in the repository directory: + +```bash +python -m pip install -e . +jupyter serverextension enable jupyterlab_markup +jlpm --ignore-optional +jlpm build +jupyter labextension install . +``` + +To rebuild the package and the JupyterLab app: + +```bash +jlpm build +jupyter lab build +``` + +For live development + +```bash +jlpm watch # will continue running +jupyter lab watch # in another terminal, will continue running +``` + +## Extending + +Additional [`markdown-it` plugins][plugins] +can be added as small labextensions. After getting started with the [official cookiecutter][], +your `plugin.ts` might look something like: + +```ts +import { + JupyterFrontEnd, + JupyterFrontEndPlugin, +} from '@jupyterlab/application'; + +import { IMarkdownIt } from '@agoose77/jupyterlab-markup'; + +const plugin: JupyterFrontEndPlugin = { + id: `@your/package:deflist`, + autoStart: true, + requires: [IMarkdownIt], + activate: (app: JupyterFrontEnd, markdownIt: IMarkdownIt) => { + markdownIt.addPluginProvider('markdown-it-deflist', async () => { + // _please_ lazy load to avoid bloating the main vendor bundle! + const deflistPlugin = await import( + /* webpackChunkName: "markdown-it-deflist" */ 'markdown-it-deflist' + ); + return deflistPlugin.default; + }); + }, +}; + +export default [plugin]; +``` + +[official cookiecutter]: https://github.com/jupyterlab/extension-cookiecutter-ts +[plugins]: https://www.npmjs.com/search?q=keywords:markdown-it-plugin diff --git a/README.md b/README.md index e09a222..83a6cf8 100644 --- a/README.md +++ b/README.md @@ -12,41 +12,38 @@ Adds additional rendering support to markdown in JupyterLab by using [markdown-i ## Prerequisites -- JupyterLab +- JupyterLab 2 ## Installation -Ensure mimetype exists for wasm: - -```bash -echo "application/wasm wasm" | sudo tee -a /etc/mime.types -``` - Install extension: ```bash +pip install jupyterlab_markup jupyter labextension install @agoose77/jupyterlab-markup ``` -You will need to reinstall the extension if you do these steps out of order. - -## Development - -For a development install (requires npm version 4 or later), do the following in the repository directory: +Ensure the extensions are enabled: ```bash -npm install -npm run build -jupyter labextension link . +jupyter labextension list # should contain @goose/jupyterlab-markup +jupyter serverextension list # should contain jupyterlab_markup ``` -To rebuild the package and the JupyterLab app: +If the serverextension is missing, try... ```bash -npm run build -jupyter lab build +jupyter serverextension enable jupyterlab_markup ``` +## Contributing + +Please see the [contributor guide](./CONTRIBUTING.md)! + +## Examples + +### mermaid + ```mermaid graph TD; A-->B; @@ -55,6 +52,8 @@ graph TD; C-->D; ``` +### svgbob + ```bob .---. /-o-/-- diff --git a/package.json b/package.json index 910bc2a..0edd6a6 100644 --- a/package.json +++ b/package.json @@ -53,6 +53,6 @@ "typescript": "~3.7.3" }, "jupyterlab": { - "extension": true + "extension": "lib/plugin.js" } } diff --git a/src/index.ts b/src/index.ts index 724e777..74714d0 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,71 +1,3 @@ -import { - IRenderMimeRegistry, - markdownRendererFactory, -} from '@jupyterlab/rendermime'; -import { - JupyterFrontEnd, - JupyterFrontEndPlugin, -} from '@jupyterlab/application'; +import { IMarkdownIt } from './tokens'; -import { IMarkdownIt, PLUGIN_ID } from './tokens'; -import { MarkdownItManager } from './manager'; -import { RenderedMarkdown } from './widgets'; - -const core: JupyterFrontEndPlugin = { - id: PLUGIN_ID, - autoStart: true, - requires: [IRenderMimeRegistry], - provides: IMarkdownIt, - activate: (app: JupyterFrontEnd, registry: IRenderMimeRegistry) => { - const manager = new MarkdownItManager(); - RenderedMarkdown.markdownItManager = manager; - markdownRendererFactory.createRenderer = (options) => - new RenderedMarkdown(options); - return manager; - }, -}; - -const diagrams: JupyterFrontEndPlugin = { - id: `${PLUGIN_ID}:diagrams`, - autoStart: true, - requires: [IMarkdownIt], - activate: (app: JupyterFrontEnd, markdownIt: IMarkdownIt) => { - markdownIt.addPluginProvider('markdown-it-diagrams', async () => { - const { diagramPlugin, awaitRenderAvailable } = await import( - /* webpackChunkName: "markdown-it-diagrams" */ 'markdown-it-diagrams' - ); - await awaitRenderAvailable(); - return diagramPlugin; - }); - }, -}; - -const footnote: JupyterFrontEndPlugin = { - id: `${PLUGIN_ID}:footnote`, - autoStart: true, - requires: [IMarkdownIt], - activate: (app: JupyterFrontEnd, markdownIt: IMarkdownIt) => { - markdownIt.addPluginProvider('markdown-it-footnote', async () => { - const footnotePlugin = await import( - /* webpackChunkName: "markdown-it-footnote" */ 'markdown-it-footnote' - ); - return footnotePlugin.default; - }); - }, -}; - -const deflist: JupyterFrontEndPlugin = { - id: `${PLUGIN_ID}:deflist`, - autoStart: true, - requires: [IMarkdownIt], - activate: (app: JupyterFrontEnd, markdownIt: IMarkdownIt) => { - markdownIt.addPluginProvider('markdown-it-deflist', async () => { - const deflistPlugin = await import( - /* webpackChunkName: "markdown-it-deflist" */ 'markdown-it-deflist' - ); - return deflistPlugin.default; - }); - }, -}; - -export default [core, diagrams, footnote, deflist]; +export { IMarkdownIt }; diff --git a/src/plugin.ts b/src/plugin.ts new file mode 100644 index 0000000..724e777 --- /dev/null +++ b/src/plugin.ts @@ -0,0 +1,71 @@ +import { + IRenderMimeRegistry, + markdownRendererFactory, +} from '@jupyterlab/rendermime'; +import { + JupyterFrontEnd, + JupyterFrontEndPlugin, +} from '@jupyterlab/application'; + +import { IMarkdownIt, PLUGIN_ID } from './tokens'; +import { MarkdownItManager } from './manager'; +import { RenderedMarkdown } from './widgets'; + +const core: JupyterFrontEndPlugin = { + id: PLUGIN_ID, + autoStart: true, + requires: [IRenderMimeRegistry], + provides: IMarkdownIt, + activate: (app: JupyterFrontEnd, registry: IRenderMimeRegistry) => { + const manager = new MarkdownItManager(); + RenderedMarkdown.markdownItManager = manager; + markdownRendererFactory.createRenderer = (options) => + new RenderedMarkdown(options); + return manager; + }, +}; + +const diagrams: JupyterFrontEndPlugin = { + id: `${PLUGIN_ID}:diagrams`, + autoStart: true, + requires: [IMarkdownIt], + activate: (app: JupyterFrontEnd, markdownIt: IMarkdownIt) => { + markdownIt.addPluginProvider('markdown-it-diagrams', async () => { + const { diagramPlugin, awaitRenderAvailable } = await import( + /* webpackChunkName: "markdown-it-diagrams" */ 'markdown-it-diagrams' + ); + await awaitRenderAvailable(); + return diagramPlugin; + }); + }, +}; + +const footnote: JupyterFrontEndPlugin = { + id: `${PLUGIN_ID}:footnote`, + autoStart: true, + requires: [IMarkdownIt], + activate: (app: JupyterFrontEnd, markdownIt: IMarkdownIt) => { + markdownIt.addPluginProvider('markdown-it-footnote', async () => { + const footnotePlugin = await import( + /* webpackChunkName: "markdown-it-footnote" */ 'markdown-it-footnote' + ); + return footnotePlugin.default; + }); + }, +}; + +const deflist: JupyterFrontEndPlugin = { + id: `${PLUGIN_ID}:deflist`, + autoStart: true, + requires: [IMarkdownIt], + activate: (app: JupyterFrontEnd, markdownIt: IMarkdownIt) => { + markdownIt.addPluginProvider('markdown-it-deflist', async () => { + const deflistPlugin = await import( + /* webpackChunkName: "markdown-it-deflist" */ 'markdown-it-deflist' + ); + return deflistPlugin.default; + }); + }, +}; + +export default [core, diagrams, footnote, deflist]; From 3c137d44e95d9c1a220d76387760444c018b74ab Mon Sep 17 00:00:00 2001 From: Nicholas Bollweg Date: Sun, 1 Nov 2020 16:31:53 -0500 Subject: [PATCH 06/31] rework API, add bootstrap --- .binder/postBuild | 6 ++--- CONTRIBUTING.md | 20 ++++++++-------- package.json | 11 +++++---- src/manager.ts | 6 ++--- src/plugin.ts | 60 ++++++++++++++++++++++++----------------------- src/tokens.ts | 7 +++--- 6 files changed, 56 insertions(+), 54 deletions(-) diff --git a/.binder/postBuild b/.binder/postBuild index 1f78f51..787c749 100755 --- a/.binder/postBuild +++ b/.binder/postBuild @@ -3,9 +3,7 @@ set -eux python -m pip install -e . --no-deps --ignore-installed jupyter serverextension enable jupyterlab_markup --sys-prefix -jupyter serverextension list +jlpm bootstrap -jlpm --ignore-optional -jupyter labextension install . --no-build --debug -jupyter lab build --debug --minimize=False --dev-build=False jupyter labextension list +jupyter serverextension list diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index e9cd31c..9754d18 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -5,9 +5,7 @@ For a development install (requires npm version 4 or later), do the following in ```bash python -m pip install -e . jupyter serverextension enable jupyterlab_markup -jlpm --ignore-optional -jlpm build -jupyter labextension install . +jlpm bootstrap ``` To rebuild the package and the JupyterLab app: @@ -39,16 +37,18 @@ import { import { IMarkdownIt } from '@agoose77/jupyterlab-markup'; const plugin: JupyterFrontEndPlugin = { - id: `@your/package:deflist`, + id: `${PLUGIN_ID}:deflist`, autoStart: true, requires: [IMarkdownIt], activate: (app: JupyterFrontEnd, markdownIt: IMarkdownIt) => { - markdownIt.addPluginProvider('markdown-it-deflist', async () => { - // _please_ lazy load to avoid bloating the main vendor bundle! - const deflistPlugin = await import( - /* webpackChunkName: "markdown-it-deflist" */ 'markdown-it-deflist' - ); - return deflistPlugin.default; + markdownIt.addPluginProvider({ + id: 'markdown-it-deflist', + plugin: async () => { + const deflistPlugin = await import( + /* webpackChunkName: "markdown-it-deflist" */ 'markdown-it-deflist' + ); + return deflistPlugin.default; + }, }); }, }; diff --git a/package.json b/package.json index 0edd6a6..20bb490 100644 --- a/package.json +++ b/package.json @@ -25,10 +25,11 @@ "url": "https://github.com/agoose77/jupyterlab-markup.git" }, "scripts": { + "bootstrap": "jlpm --ignore-optional && jlpm lint && jlpm build && jupyter labextension install . --debug", "build": "tsc", "clean": "rimraf lib", - "watch": "tsc -w", - "lint": "prettier --list-different --write \"**/*{.ts,.tsx,.js,.jsx,.css,.json,.md,.yml}\"" + "lint": "prettier --list-different --write \"**/*{.ts,.tsx,.js,.jsx,.css,.json,.md,.yml}\"", + "watch": "tsc -w" }, "dependencies": { "@jupyterlab/application": "^2.0.2", @@ -42,15 +43,15 @@ "markdown-it-container": "^2.0.0", "markdown-it-deflist": "^2.0.3", "markdown-it-diagrams": "^0.1.2", - "markdown-it-footnote": "^3.0.2", - "prettier": "^2.1.2" + "markdown-it-footnote": "^3.0.2" }, "devDependencies": { "@types/codemirror": "^0.0.74", "@types/markdown-it": "^0.0.9", "@types/markdown-it-container": "^2.0.2", "rimraf": "~3.0.0", - "typescript": "~3.7.3" + "typescript": "~3.7.3", + "prettier": "^2.1.2" }, "jupyterlab": { "extension": "lib/plugin.js" diff --git a/src/manager.ts b/src/manager.ts index 4e2d955..93a76d3 100644 --- a/src/manager.ts +++ b/src/manager.ts @@ -12,8 +12,8 @@ export class MarkdownItManager implements IMarkdownIt { /** * add a provider for a plugin which can be resolve lazily */ - addPluginProvider(name: string, provider: IMarkdownIt.IPluginProvider): void { - this._pluginProviders.set(name, provider); + addPluginProvider(provider: IMarkdownIt.IPluginProvider): void { + this._pluginProviders.set(provider.id, provider); } /** @@ -31,7 +31,7 @@ export class MarkdownItManager implements IMarkdownIt { for (const [name, provider] of this._pluginProviders.entries()) { try { - const plugin = await provider(); + const plugin = await provider.plugin(); md = md.use(plugin); } catch (err) { console.warn(`Failed to load/use markdown-it plugin ${name}`, err); diff --git a/src/plugin.ts b/src/plugin.ts index 724e777..88b1366 100644 --- a/src/plugin.ts +++ b/src/plugin.ts @@ -1,11 +1,5 @@ -import { - IRenderMimeRegistry, - markdownRendererFactory, -} from '@jupyterlab/rendermime'; -import { - JupyterFrontEnd, - JupyterFrontEndPlugin, -} from '@jupyterlab/application'; +import { markdownRendererFactory } from '@jupyterlab/rendermime'; +import { JupyterFrontEndPlugin } from '@jupyterlab/application'; import { IMarkdownIt, PLUGIN_ID } from './tokens'; import { MarkdownItManager } from './manager'; @@ -14,9 +8,8 @@ import { RenderedMarkdown } from './widgets'; const core: JupyterFrontEndPlugin = { id: PLUGIN_ID, autoStart: true, - requires: [IRenderMimeRegistry], provides: IMarkdownIt, - activate: (app: JupyterFrontEnd, registry: IRenderMimeRegistry) => { + activate: (app) => { const manager = new MarkdownItManager(); RenderedMarkdown.markdownItManager = manager; markdownRendererFactory.createRenderer = (options) => @@ -29,13 +22,16 @@ const diagrams: JupyterFrontEndPlugin = { id: `${PLUGIN_ID}:diagrams`, autoStart: true, requires: [IMarkdownIt], - activate: (app: JupyterFrontEnd, markdownIt: IMarkdownIt) => { - markdownIt.addPluginProvider('markdown-it-diagrams', async () => { - const { diagramPlugin, awaitRenderAvailable } = await import( - /* webpackChunkName: "markdown-it-diagrams" */ 'markdown-it-diagrams' - ); - await awaitRenderAvailable(); - return diagramPlugin; + activate: (app, markdownIt: IMarkdownIt) => { + markdownIt.addPluginProvider({ + id: 'markdown-it-diagrams', + plugin: async () => { + const { diagramPlugin, awaitRenderAvailable } = await import( + /* webpackChunkName: "markdown-it-diagrams" */ 'markdown-it-diagrams' + ); + await awaitRenderAvailable(); + return diagramPlugin; + }, }); }, }; @@ -44,12 +40,15 @@ const footnote: JupyterFrontEndPlugin = { id: `${PLUGIN_ID}:footnote`, autoStart: true, requires: [IMarkdownIt], - activate: (app: JupyterFrontEnd, markdownIt: IMarkdownIt) => { - markdownIt.addPluginProvider('markdown-it-footnote', async () => { - const footnotePlugin = await import( - /* webpackChunkName: "markdown-it-footnote" */ 'markdown-it-footnote' - ); - return footnotePlugin.default; + activate: (app, markdownIt: IMarkdownIt) => { + markdownIt.addPluginProvider({ + id: 'markdown-it-footnote', + plugin: async () => { + const footnotePlugin = await import( + /* webpackChunkName: "markdown-it-footnote" */ 'markdown-it-footnote' + ); + return footnotePlugin.default; + }, }); }, }; @@ -58,12 +57,15 @@ const deflist: JupyterFrontEndPlugin = { id: `${PLUGIN_ID}:deflist`, autoStart: true, requires: [IMarkdownIt], - activate: (app: JupyterFrontEnd, markdownIt: IMarkdownIt) => { - markdownIt.addPluginProvider('markdown-it-deflist', async () => { - const deflistPlugin = await import( - /* webpackChunkName: "markdown-it-deflist" */ 'markdown-it-deflist' - ); - return deflistPlugin.default; + activate: (app, markdownIt: IMarkdownIt) => { + markdownIt.addPluginProvider({ + id: 'markdown-it-deflist', + plugin: async () => { + const deflistPlugin = await import( + /* webpackChunkName: "markdown-it-deflist" */ 'markdown-it-deflist' + ); + return deflistPlugin.default; + }, }); }, }; diff --git a/src/tokens.ts b/src/tokens.ts index 0d8092c..6122909 100644 --- a/src/tokens.ts +++ b/src/tokens.ts @@ -11,8 +11,8 @@ export const IMarkdownIt = new Token(PLUGIN_ID); /* tslint:enable */ export interface IMarkdownIt { - addPluginProvider(name: string, provider: IMarkdownIt.IPluginProvider): void; - removePluginProvider(name: string): void; + addPluginProvider(provider: IMarkdownIt.IPluginProvider): void; + removePluginProvider(id: string): void; getMarkdownIt(options?: MarkdownIt.Options): Promise; } @@ -21,6 +21,7 @@ export namespace IMarkdownIt { (md: MarkdownIt, ...params: any[]): void; } export interface IPluginProvider { - (): Promise; + id: string; + plugin: () => Promise; } } From 9c006f1f8dadf2e4d7d4b5b0a0e93fa23ca747c4 Mon Sep 17 00:00:00 2001 From: Nicholas Bollweg Date: Sun, 1 Nov 2020 17:13:00 -0500 Subject: [PATCH 07/31] add link replacer, options to API, typings in folder --- README.md | 1 + package.json | 7 ++--- src/manager.ts | 28 ++++++++++++++++++-- src/plugin.ts | 29 ++++++++++++++++++++- src/tokens.ts | 19 ++++++++++++-- src/{ => typings}/markdown-it-deflist.d.ts | 0 src/{ => typings}/markdown-it-footnote.d.ts | 0 src/typings/markdown-it-replace-link.d.ts | 10 +++++++ src/widgets.ts | 2 +- tsconfig.json | 2 +- yarn.lock | 5 ++++ 11 files changed, 93 insertions(+), 10 deletions(-) rename src/{ => typings}/markdown-it-deflist.d.ts (100%) rename src/{ => typings}/markdown-it-footnote.d.ts (100%) create mode 100644 src/typings/markdown-it-replace-link.d.ts diff --git a/README.md b/README.md index 83a6cf8..5beb1ec 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,7 @@ Adds additional rendering support to markdown in JupyterLab by using [markdown-i - [markdown-it-diagrams](https://github.com/agoose77/markdown-it-diagrams) - [markdown-it-footnote](https://github.com/markdown-it/markdown-it-footnote) - [markdown-it-deflist](https://github.com/markdown-it/markdown-it-deflist) +- [markdown-it-replace-link](https://github.com/martinheidegger/markdown-it-replace-link) ![Full example rendering vs markup.](https://i.imgur.com/OL9oGcq.png) ![svgbob rendering](https://i.imgur.com/RbDioU8.gif) diff --git a/package.json b/package.json index 20bb490..ed26a5f 100644 --- a/package.json +++ b/package.json @@ -43,15 +43,16 @@ "markdown-it-container": "^2.0.0", "markdown-it-deflist": "^2.0.3", "markdown-it-diagrams": "^0.1.2", - "markdown-it-footnote": "^3.0.2" + "markdown-it-footnote": "^3.0.2", + "markdown-it-replace-link": "^1.1.0" }, "devDependencies": { "@types/codemirror": "^0.0.74", "@types/markdown-it": "^0.0.9", "@types/markdown-it-container": "^2.0.2", + "prettier": "^2.1.2", "rimraf": "~3.0.0", - "typescript": "~3.7.3", - "prettier": "^2.1.2" + "typescript": "~3.7.3" }, "jupyterlab": { "extension": "lib/plugin.js" diff --git a/src/manager.ts b/src/manager.ts index 93a76d3..cd322ba 100644 --- a/src/manager.ts +++ b/src/manager.ts @@ -3,6 +3,8 @@ import { IMarkdownIt } from './tokens'; import { CodeMirrorEditor, Mode } from '@jupyterlab/codemirror'; +import { RenderedMarkdown } from './widgets'; + export class MarkdownItManager implements IMarkdownIt { private _pluginProviders: Map< string, @@ -23,9 +25,12 @@ export class MarkdownItManager implements IMarkdownIt { this._pluginProviders.delete(name); } - async getMarkdownIt(options: MarkdownIt.Options = {}): Promise { + async getMarkdownIt( + widget: RenderedMarkdown, + options: MarkdownIt.Options = {} + ): Promise { let md = new MarkdownIt({ - ...this.baseMarkdownItOptions, + ...this.getOptions(widget), ...options, }); @@ -41,6 +46,25 @@ export class MarkdownItManager implements IMarkdownIt { return md; } + async getOptions(widget: RenderedMarkdown) { + let allOptions = this.baseMarkdownItOptions; + + for (const [name, plugin] of this._pluginProviders.entries()) { + if (plugin.options == null) { + continue; + } + try { + allOptions = { ...allOptions, ...(await plugin.options(widget)) }; + } catch (err) { + console.warn( + `Failed to get options from markdown-it plugin ${name}`, + err + ); + } + } + return allOptions; + } + get baseMarkdownItOptions() { return { html: true, diff --git a/src/plugin.ts b/src/plugin.ts index 88b1366..ba0c6e9 100644 --- a/src/plugin.ts +++ b/src/plugin.ts @@ -70,4 +70,31 @@ const deflist: JupyterFrontEndPlugin = { }, }; -export default [core, diagrams, footnote, deflist]; +const replacelink: JupyterFrontEndPlugin = { + id: `${PLUGIN_ID}:markdown-it-replace-link`, + autoStart: true, + requires: [IMarkdownIt], + activate: (app, markdownIt: IMarkdownIt) => { + markdownIt.addPluginProvider({ + id: 'markdown-it-replace-link', + options: async (widget) => { + const { isLocal, resolveUrl } = widget.resolver; + return { + replaceLink: function (link: string, env: any) { + return isLocal(link) ? resolveUrl(link) : link; + }, + }; + }, + plugin: async () => { + const replaceLinkPlugin = await import( + /* webpackChunkName: "markdown-it-replace-link" */ 'markdown-it-replace-link' + ); + return replaceLinkPlugin.default; + }, + }); + }, +}; + +// markdown-it-replace-link + +export default [core, diagrams, footnote, deflist, replacelink]; diff --git a/src/tokens.ts b/src/tokens.ts index 6122909..4f668d8 100644 --- a/src/tokens.ts +++ b/src/tokens.ts @@ -1,5 +1,6 @@ import { Token } from '@lumino/coreutils'; import MarkdownIt from 'markdown-it'; +import { RenderedMarkdown } from './widgets'; export const PLUGIN_ID = '@agoose77/jupyterlab-markup'; @@ -13,15 +14,29 @@ export const IMarkdownIt = new Token(PLUGIN_ID); export interface IMarkdownIt { addPluginProvider(provider: IMarkdownIt.IPluginProvider): void; removePluginProvider(id: string): void; - getMarkdownIt(options?: MarkdownIt.Options): Promise; + getMarkdownIt( + widget: RenderedMarkdown, + options?: MarkdownIt.Options + ): Promise; } export namespace IMarkdownIt { export interface IPlugin { (md: MarkdownIt, ...params: any[]): void; } + export type IPluginOptions = object; export interface IPluginProvider { + /** + * A unique identifier for the plugin, usually the name of the upstream package + */ id: string; - plugin: () => Promise; + /** + * A lazy provider of the plugin function + */ + plugin(): Promise; + /** + * Additional options to pass to the MarkdownIt constructor + */ + options?(widget: RenderedMarkdown): Promise; } } diff --git a/src/markdown-it-deflist.d.ts b/src/typings/markdown-it-deflist.d.ts similarity index 100% rename from src/markdown-it-deflist.d.ts rename to src/typings/markdown-it-deflist.d.ts diff --git a/src/markdown-it-footnote.d.ts b/src/typings/markdown-it-footnote.d.ts similarity index 100% rename from src/markdown-it-footnote.d.ts rename to src/typings/markdown-it-footnote.d.ts diff --git a/src/typings/markdown-it-replace-link.d.ts b/src/typings/markdown-it-replace-link.d.ts new file mode 100644 index 0000000..8b74c4a --- /dev/null +++ b/src/typings/markdown-it-replace-link.d.ts @@ -0,0 +1,10 @@ +declare module 'markdown-it-replace-link' { + import MarkdownIt = require('markdown-it'); + + namespace markdownItDeflist { + function replacelink_plugin(md: MarkdownIt, opts: any): void; + } + + const MarkdownItReplaceLink: typeof markdownItDeflist.replacelink_plugin; + export = MarkdownItReplaceLink; +} diff --git a/src/widgets.ts b/src/widgets.ts index c8b8ca6..38e31b4 100644 --- a/src/widgets.ts +++ b/src/widgets.ts @@ -31,7 +31,7 @@ export class RenderedMarkdown extends RenderedHTMLCommon { */ async render(model: IRenderMime.IMimeModel): Promise { if (this.md == null) { - this.md = await RenderedMarkdown.markdownItManager.getMarkdownIt(); + this.md = await RenderedMarkdown.markdownItManager.getMarkdownIt(this); } return await renderers.renderMarkdown({ host: this.node, diff --git a/tsconfig.json b/tsconfig.json index 1f2dcda..9eff32a 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -15,5 +15,5 @@ "allowSyntheticDefaultImports": true, "sourceMap": true }, - "include": ["src/*"] + "include": ["src/**/*"] } diff --git a/yarn.lock b/yarn.lock index a5f25e3..62bfae3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1306,6 +1306,11 @@ markdown-it-footnote@^3.0.2: resolved "https://registry.yarnpkg.com/markdown-it-footnote/-/markdown-it-footnote-3.0.2.tgz#1575ee7a093648d4e096aa33386b058d92ac8bc1" integrity sha512-JVW6fCmZWjvMdDQSbOT3nnOQtd9iAXmw7hTSh26+v42BnvXeVyGMDBm5b/EZocMed2MbCAHiTX632vY0FyGB8A== +markdown-it-replace-link@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/markdown-it-replace-link/-/markdown-it-replace-link-1.1.0.tgz#cab2343eb27928db1c836e10cd518aaf60a0bd50" + integrity sha512-P+4D/Z16utgQVjMumA5W3lMbxWYk4iwg1Fk59wShFm7MRgzbjJm++tvI18LdYwiG8CKNA1TFWvHcbtehvMuViA== + markdown-it@^10.0.0: version "10.0.0" resolved "https://registry.yarnpkg.com/markdown-it/-/markdown-it-10.0.0.tgz#abfc64f141b1722d663402044e43927f1f50a8dc" From 0cf57cb8fb67042ca579217db1039e4ea7ec6de3 Mon Sep 17 00:00:00 2001 From: Nicholas Bollweg Date: Sun, 1 Nov 2020 17:35:27 -0500 Subject: [PATCH 08/31] more docs --- index.ipynb | 124 +++++++++++++++++++++++++++++++++++++++++++++++++ src/manager.ts | 20 +++++--- src/plugin.ts | 17 ++++++- src/tokens.ts | 9 ++++ src/widgets.ts | 4 ++ 5 files changed, 166 insertions(+), 8 deletions(-) create mode 100644 index.ipynb diff --git a/index.ipynb b/index.ipynb new file mode 100644 index 0000000..0843041 --- /dev/null +++ b/index.ipynb @@ -0,0 +1,124 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Table (of contents) with Local Links\n", + "\n", + "| plugin... | provides... |\n", + "|----------------------------|-----------------------------------------|\n", + "| `markdown-it-diagrams` | [diagrams](#Diagrams) |\n", + "| `markdown-it-deflist` | [definition lists](#Definition%20Lists) |\n", + "| `markdown-it-footnote` | [footnotes](#Footnotes) |\n", + "| `markdown-it-replace-link` | link replacement |" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Footnotes" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Here is a footnote reference,[^1] and another.[^longnote]\n", + "\n", + "[^1]: Here is the footnote.\n", + "\n", + "[^longnote]: Here's one with multiple blocks.\n", + "\n", + " Subsequent paragraphs are indented to show that they\n", + "belong to the previous footnote." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Definition Lists\n", + "\n", + "\n", + "Term 1\n", + " ~ Definition 1\n", + "\n", + "Term 2\n", + " ~ Definition 2a\n", + " ~ Definition 2b" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Diagrams" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Mermaid" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "```mermaid \n", + "graph TD;\n", + " A-->B;\n", + " A-->C;\n", + " B-->D;\n", + " C-->D;\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### svgbob" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "```bob \n", + " .---.\n", + " /-o-/--\n", + " .-/ / /->\n", + "( * \\/\n", + " '-. \\\n", + " \\ /\n", + " '\n", + "```" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.8" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/src/manager.ts b/src/manager.ts index cd322ba..1c0c5f1 100644 --- a/src/manager.ts +++ b/src/manager.ts @@ -12,19 +12,22 @@ export class MarkdownItManager implements IMarkdownIt { > = new Map(); /** - * add a provider for a plugin which can be resolve lazily + * Add a provider for a plugin which can be resolved lazily */ addPluginProvider(provider: IMarkdownIt.IPluginProvider): void { this._pluginProviders.set(provider.id, provider); } /** - * remove a provider by name + * Remove a provider by name */ removePluginProvider(name: string) { this._pluginProviders.delete(name); } + /** + * Get a MarkdownIt instance + */ async getMarkdownIt( widget: RenderedMarkdown, options: MarkdownIt.Options = {} @@ -65,6 +68,9 @@ export class MarkdownItManager implements IMarkdownIt { return allOptions; } + /** + * Default MarkdownIt options, may be overridden by plugins + */ get baseMarkdownItOptions() { return { html: true, @@ -75,15 +81,17 @@ export class MarkdownItManager implements IMarkdownIt { }; } + /** + * Use CodeMirror to highlight code blocks, may be overriden by plugins + */ highlightCode = (str: string, lang: string) => { if (!lang) { return ''; // use external default escaping } try { let spec = Mode.findBest(lang); - console.log(spec); if (!spec) { - console.log(`No CodeMirror mode: ${lang}`); + console.warn(`No CodeMirror mode: ${lang}`); return; } @@ -92,11 +100,11 @@ export class MarkdownItManager implements IMarkdownIt { Mode.run(str, spec.mime, el); return el.innerHTML; } catch (err) { - console.log(`Failed to highlight ${lang} code`, err); + console.warn(`Failed to highlight ${lang} code`, err); } } catch (err) { console.log(`No CodeMirror mode: ${lang}`); - console.log(`Require CodeMirror mode error: ${err}`); + console.warn(`Require CodeMirror mode error: ${err}`); } return ''; }; diff --git a/src/plugin.ts b/src/plugin.ts index ba0c6e9..51a3d02 100644 --- a/src/plugin.ts +++ b/src/plugin.ts @@ -5,6 +5,9 @@ import { IMarkdownIt, PLUGIN_ID } from './tokens'; import { MarkdownItManager } from './manager'; import { RenderedMarkdown } from './widgets'; +/** + * The main plugin which overloads default markdown rendering by `marked` + */ const core: JupyterFrontEndPlugin = { id: PLUGIN_ID, autoStart: true, @@ -18,6 +21,9 @@ const core: JupyterFrontEndPlugin = { }, }; +/** + * Provides text-based diagrams in code blocks + */ const diagrams: JupyterFrontEndPlugin = { id: `${PLUGIN_ID}:diagrams`, autoStart: true, @@ -36,6 +42,9 @@ const diagrams: JupyterFrontEndPlugin = { }, }; +/** + * Provides footnotes + */ const footnote: JupyterFrontEndPlugin = { id: `${PLUGIN_ID}:footnote`, autoStart: true, @@ -53,6 +62,9 @@ const footnote: JupyterFrontEndPlugin = { }, }; +/** + * Provides definition lists + */ const deflist: JupyterFrontEndPlugin = { id: `${PLUGIN_ID}:deflist`, autoStart: true, @@ -70,6 +82,9 @@ const deflist: JupyterFrontEndPlugin = { }, }; +/** + * Replaces local links to the Jupyter files URL + */ const replacelink: JupyterFrontEndPlugin = { id: `${PLUGIN_ID}:markdown-it-replace-link`, autoStart: true, @@ -95,6 +110,4 @@ const replacelink: JupyterFrontEndPlugin = { }, }; -// markdown-it-replace-link - export default [core, diagrams, footnote, deflist, replacelink]; diff --git a/src/tokens.ts b/src/tokens.ts index 4f668d8..76d3446 100644 --- a/src/tokens.ts +++ b/src/tokens.ts @@ -2,6 +2,9 @@ import { Token } from '@lumino/coreutils'; import MarkdownIt from 'markdown-it'; import { RenderedMarkdown } from './widgets'; +/** + * The plugin ID stem for all lab extensions + */ export const PLUGIN_ID = '@agoose77/jupyterlab-markup'; /* tslint:disable */ @@ -11,6 +14,9 @@ export const PLUGIN_ID = '@agoose77/jupyterlab-markup'; export const IMarkdownIt = new Token(PLUGIN_ID); /* tslint:enable */ +/** + * A manager for adding MarkdownIt plugins + */ export interface IMarkdownIt { addPluginProvider(provider: IMarkdownIt.IPluginProvider): void; removePluginProvider(id: string): void; @@ -20,6 +26,9 @@ export interface IMarkdownIt { ): Promise; } +/** + * A namespace for plugin-related types and interfaces + */ export namespace IMarkdownIt { export interface IPlugin { (md: MarkdownIt, ...params: any[]): void; diff --git a/src/widgets.ts b/src/widgets.ts index 38e31b4..ab7a74f 100644 --- a/src/widgets.ts +++ b/src/widgets.ts @@ -15,6 +15,10 @@ export class RenderedMarkdown extends RenderedHTMLCommon { * @param options - The options for initializing the widget. */ md: MarkdownIt; + + /** + * A static manager set by the core plugin for getting MarkdownIt instances + */ static markdownItManager: IMarkdownIt; constructor(options: IRenderMime.IRendererOptions) { From 6091df875a7886ff682f0c38f60fcad6d9f401ba Mon Sep 17 00:00:00 2001 From: Nicholas Bollweg Date: Sun, 1 Nov 2020 18:24:41 -0500 Subject: [PATCH 09/31] add anchor links, clean up bootstrap build --- CONTRIBUTING.md | 8 ++++---- README.md | 1 + package.json | 5 ++++- src/manager.ts | 4 ++-- src/plugin.ts | 30 ++++++++++++++++++++++++----- src/tokens.ts | 4 ++-- src/typings/markdown-it-anchor.d.ts | 10 ++++++++++ yarn.lock | 5 +++++ 8 files changed, 53 insertions(+), 14 deletions(-) create mode 100644 src/typings/markdown-it-anchor.d.ts diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 9754d18..fab006e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -36,8 +36,8 @@ import { import { IMarkdownIt } from '@agoose77/jupyterlab-markup'; -const plugin: JupyterFrontEndPlugin = { - id: `${PLUGIN_ID}:deflist`, +const deflist: JupyterFrontEndPlugin = { + id: `${PLUGIN_ID}:markdown-it-deflist`, autoStart: true, requires: [IMarkdownIt], activate: (app: JupyterFrontEnd, markdownIt: IMarkdownIt) => { @@ -47,13 +47,13 @@ const plugin: JupyterFrontEndPlugin = { const deflistPlugin = await import( /* webpackChunkName: "markdown-it-deflist" */ 'markdown-it-deflist' ); - return deflistPlugin.default; + return [deflistPlugin.default, {}]; }, }); }, }; -export default [plugin]; +export default [deflist]; ``` [official cookiecutter]: https://github.com/jupyterlab/extension-cookiecutter-ts diff --git a/README.md b/README.md index 5beb1ec..f0e49ed 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,7 @@ Adds additional rendering support to markdown in JupyterLab by using [markdown-i - [markdown-it-footnote](https://github.com/markdown-it/markdown-it-footnote) - [markdown-it-deflist](https://github.com/markdown-it/markdown-it-deflist) - [markdown-it-replace-link](https://github.com/martinheidegger/markdown-it-replace-link) +- [markdown-it-anchor](https://github.com/valeriangalliat/markdown-it-anchor) ![Full example rendering vs markup.](https://i.imgur.com/OL9oGcq.png) ![svgbob rendering](https://i.imgur.com/RbDioU8.gif) diff --git a/package.json b/package.json index ed26a5f..f84e264 100644 --- a/package.json +++ b/package.json @@ -25,7 +25,9 @@ "url": "https://github.com/agoose77/jupyterlab-markup.git" }, "scripts": { - "bootstrap": "jlpm --ignore-optional && jlpm lint && jlpm build && jupyter labextension install . --debug", + "bootstrap": "jlpm --ignore-optional && jlpm lint && jlpm build && jlpm ext:install && jlpm lab:build", + "ext:install": "jupyter labextension install . --debug --no-build", + "lab:build": "jupyter lab build --debug --dev-build=False --minimize=False", "build": "tsc", "clean": "rimraf lib", "lint": "prettier --list-different --write \"**/*{.ts,.tsx,.js,.jsx,.css,.json,.md,.yml}\"", @@ -40,6 +42,7 @@ "@jupyterlab/rendermime-interfaces": "^2.0.1", "@lumino/messaging": "^1.3.3", "markdown-it": "^10.0.0", + "markdown-it-anchor": "^6.0.0", "markdown-it-container": "^2.0.0", "markdown-it-deflist": "^2.0.3", "markdown-it-diagrams": "^0.1.2", diff --git a/src/manager.ts b/src/manager.ts index 1c0c5f1..455fa29 100644 --- a/src/manager.ts +++ b/src/manager.ts @@ -39,8 +39,8 @@ export class MarkdownItManager implements IMarkdownIt { for (const [name, provider] of this._pluginProviders.entries()) { try { - const plugin = await provider.plugin(); - md = md.use(plugin); + const [plugin, options] = await provider.plugin(); + md = md.use(plugin, options); } catch (err) { console.warn(`Failed to load/use markdown-it plugin ${name}`, err); } diff --git a/src/plugin.ts b/src/plugin.ts index 51a3d02..77db8a1 100644 --- a/src/plugin.ts +++ b/src/plugin.ts @@ -36,7 +36,7 @@ const diagrams: JupyterFrontEndPlugin = { /* webpackChunkName: "markdown-it-diagrams" */ 'markdown-it-diagrams' ); await awaitRenderAvailable(); - return diagramPlugin; + return [diagramPlugin, {}]; }, }); }, @@ -56,7 +56,7 @@ const footnote: JupyterFrontEndPlugin = { const footnotePlugin = await import( /* webpackChunkName: "markdown-it-footnote" */ 'markdown-it-footnote' ); - return footnotePlugin.default; + return [footnotePlugin.default, {}]; }, }); }, @@ -76,7 +76,7 @@ const deflist: JupyterFrontEndPlugin = { const deflistPlugin = await import( /* webpackChunkName: "markdown-it-deflist" */ 'markdown-it-deflist' ); - return deflistPlugin.default; + return [deflistPlugin.default, {}]; }, }); }, @@ -104,10 +104,30 @@ const replacelink: JupyterFrontEndPlugin = { const replaceLinkPlugin = await import( /* webpackChunkName: "markdown-it-replace-link" */ 'markdown-it-replace-link' ); - return replaceLinkPlugin.default; + return [replaceLinkPlugin.default, {}]; }, }); }, }; -export default [core, diagrams, footnote, deflist, replacelink]; +/** + * Adds anchors to headers + */ +const anchor: JupyterFrontEndPlugin = { + id: `${PLUGIN_ID}:markdown-it-anchor`, + autoStart: true, + requires: [IMarkdownIt], + activate: (app, markdownIt: IMarkdownIt) => { + markdownIt.addPluginProvider({ + id: 'markdown-it-anchor', + plugin: async () => { + const replaceLinkPlugin = await import( + /* webpackChunkName: "markdown-it-anchor" */ 'markdown-it-anchor' + ); + return [replaceLinkPlugin.default, {}]; + }, + }); + }, +}; + +export default [core, anchor, diagrams, footnote, deflist, replacelink]; diff --git a/src/tokens.ts b/src/tokens.ts index 76d3446..8bbdc44 100644 --- a/src/tokens.ts +++ b/src/tokens.ts @@ -40,9 +40,9 @@ export namespace IMarkdownIt { */ id: string; /** - * A lazy provider of the plugin function + * A lazy provider of the plugin function and plugin options */ - plugin(): Promise; + plugin(): Promise<[IPlugin, any]>; /** * Additional options to pass to the MarkdownIt constructor */ diff --git a/src/typings/markdown-it-anchor.d.ts b/src/typings/markdown-it-anchor.d.ts new file mode 100644 index 0000000..90b642b --- /dev/null +++ b/src/typings/markdown-it-anchor.d.ts @@ -0,0 +1,10 @@ +declare module 'markdown-it-anchor' { + import MarkdownIt = require('markdown-it'); + + namespace markdownItFootnote { + function anchor_plugin(md: MarkdownIt): void; + } + + const MarkdownItAnchor: typeof markdownItFootnote.anchor_plugin; + export = MarkdownItAnchor; +} diff --git a/yarn.lock b/yarn.lock index 62bfae3..ace42bd 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1282,6 +1282,11 @@ lower-case@^1.1.1: resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-1.1.4.tgz#9a2cabd1b9e8e0ae993a4bf7d5875c39c42e8eac" integrity sha1-miyr0bno4K6ZOkv31YdcOcQujqw= +markdown-it-anchor@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/markdown-it-anchor/-/markdown-it-anchor-6.0.0.tgz#2ec2554fa4d065f2d1ca2422a50c14c10cf67c2a" + integrity sha512-WOcIGhG1M1W94VV5cmSZAMSKi2vqCxpLAqQZ0wSO9RzQ9Rbls7ecjRVXp5DIPoXrNy9bjv9K7M0nYqNk60ctxQ== + markdown-it-container@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/markdown-it-container/-/markdown-it-container-2.0.0.tgz#0019b43fd02eefece2f1960a2895fba81a404695" From 2bbe34a1fa9746602f015fc477a175f1719d9cb0 Mon Sep 17 00:00:00 2001 From: Nicholas Bollweg Date: Sun, 1 Nov 2020 18:53:27 -0500 Subject: [PATCH 10/31] try more building --- .binder/postBuild | 2 ++ CONTRIBUTING.md | 1 + index.ipynb | 7 ++++--- package.json | 10 ++++------ src/plugin.ts | 7 ++++++- tsconfig.json | 9 ++++++--- yarn.lock | 8 ++++---- 7 files changed, 27 insertions(+), 17 deletions(-) diff --git a/.binder/postBuild b/.binder/postBuild index 787c749..d01321b 100755 --- a/.binder/postBuild +++ b/.binder/postBuild @@ -4,6 +4,8 @@ set -eux python -m pip install -e . --no-deps --ignore-installed jupyter serverextension enable jupyterlab_markup --sys-prefix jlpm bootstrap +jupyter labextension install $(npm pack) --no-build --debug +jupyter lab build --debug --dev-build=False --minimize=False jupyter labextension list jupyter serverextension list diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index fab006e..3c0f67f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -6,6 +6,7 @@ For a development install (requires npm version 4 or later), do the following in python -m pip install -e . jupyter serverextension enable jupyterlab_markup jlpm bootstrap +jupyter labextension install . ``` To rebuild the package and the JupyterLab app: diff --git a/index.ipynb b/index.ipynb index 0843041..3ec2fda 100644 --- a/index.ipynb +++ b/index.ipynb @@ -8,9 +8,10 @@ "\n", "| plugin... | provides... |\n", "|----------------------------|-----------------------------------------|\n", - "| `markdown-it-diagrams` | [diagrams](#Diagrams) |\n", - "| `markdown-it-deflist` | [definition lists](#Definition%20Lists) |\n", - "| `markdown-it-footnote` | [footnotes](#Footnotes) |\n", + "| `markdown-it-anchor` | header anchors |\n", + "| `markdown-it-diagrams` | [diagrams](#diagrams) |\n", + "| `markdown-it-deflist` | [definition lists](#definition-lists) |\n", + "| `markdown-it-footnote` | [footnotes](#footnotes) |\n", "| `markdown-it-replace-link` | link replacement |" ] }, diff --git a/package.json b/package.json index f84e264..0c68f1c 100644 --- a/package.json +++ b/package.json @@ -25,13 +25,11 @@ "url": "https://github.com/agoose77/jupyterlab-markup.git" }, "scripts": { - "bootstrap": "jlpm --ignore-optional && jlpm lint && jlpm build && jlpm ext:install && jlpm lab:build", - "ext:install": "jupyter labextension install . --debug --no-build", - "lab:build": "jupyter lab build --debug --dev-build=False --minimize=False", - "build": "tsc", + "bootstrap": "jlpm --ignore-optional && jlpm lint && jlpm build", + "build": "tsc -b", "clean": "rimraf lib", "lint": "prettier --list-different --write \"**/*{.ts,.tsx,.js,.jsx,.css,.json,.md,.yml}\"", - "watch": "tsc -w" + "watch": "jlpm build -w --preserveWatchOutput" }, "dependencies": { "@jupyterlab/application": "^2.0.2", @@ -55,7 +53,7 @@ "@types/markdown-it-container": "^2.0.2", "prettier": "^2.1.2", "rimraf": "~3.0.0", - "typescript": "~3.7.3" + "typescript": "^4.0.5" }, "jupyterlab": { "extension": "lib/plugin.js" diff --git a/src/plugin.ts b/src/plugin.ts index 77db8a1..524e39e 100644 --- a/src/plugin.ts +++ b/src/plugin.ts @@ -124,7 +124,12 @@ const anchor: JupyterFrontEndPlugin = { const replaceLinkPlugin = await import( /* webpackChunkName: "markdown-it-anchor" */ 'markdown-it-anchor' ); - return [replaceLinkPlugin.default, {}]; + return [ + replaceLinkPlugin.default, + { + permalink: true, + }, + ]; }, }); }, diff --git a/tsconfig.json b/tsconfig.json index 9eff32a..411ca64 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,5 +1,7 @@ { "compilerOptions": { + "allowSyntheticDefaultImports": true, + "composite": true, "declaration": true, "lib": ["es2015", "dom"], "module": "esnext", @@ -7,13 +9,14 @@ "noEmitOnError": true, "noUnusedLocals": true, "outDir": "lib", + "preserveWatchOutput": true, "rootDir": "src", + "sourceMap": true, "strict": true, "strictNullChecks": false, "target": "es2017", - "types": [], - "allowSyntheticDefaultImports": true, - "sourceMap": true + "tsBuildInfoFile": "lib/.tsbuildinfo", + "types": [] }, "include": ["src/**/*"] } diff --git a/yarn.lock b/yarn.lock index ace42bd..0d51160 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1771,10 +1771,10 @@ typed-styles@^0.0.7: resolved "https://registry.yarnpkg.com/typed-styles/-/typed-styles-0.0.7.tgz#93392a008794c4595119ff62dde6809dbc40a3d9" integrity sha512-pzP0PWoZUhsECYjABgCGQlRGL1n7tOHsgwYv3oIiEpJwGhFTuty/YNeduxQYzXXa3Ge5BdT6sHYIQYpl4uJ+5Q== -typescript@~3.7.3: - version "3.7.5" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.7.5.tgz#0692e21f65fd4108b9330238aac11dd2e177a1ae" - integrity sha512-/P5lkRXkWHNAbcJIiHPfRoKqyd7bsyCma1hZNUGfn20qm64T6ZBlrzprymeu918H+mB/0rIg2gGK/BXkhhYgBw== +typescript@^4.0.5: + version "4.0.5" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.0.5.tgz#ae9dddfd1069f1cb5beb3ef3b2170dd7c1332389" + integrity sha512-ywmr/VrTVCmNTJ6iV2LwIrfG1P+lv6luD8sUJs+2eI9NLGigaN+nUQc13iHqisq7bra9lnmUSYqbJvegraBOPQ== typestyle@^2.0.4: version "2.0.4" From e2dd064be1ca49d8a779d856f133966770cbe39c Mon Sep 17 00:00:00 2001 From: Nicholas Bollweg Date: Sun, 1 Nov 2020 19:14:42 -0500 Subject: [PATCH 11/31] add legacy header link class --- src/plugin.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/plugin.ts b/src/plugin.ts index 524e39e..810c90a 100644 --- a/src/plugin.ts +++ b/src/plugin.ts @@ -128,6 +128,7 @@ const anchor: JupyterFrontEndPlugin = { replaceLinkPlugin.default, { permalink: true, + permalinkClass: 'jp-InternalAnchorLink', }, ]; }, From 8700c0c74b9080e137eae97aaa51fa3b8aba1bb7 Mon Sep 17 00:00:00 2001 From: Nicholas Bollweg Date: Sun, 1 Nov 2020 19:33:03 -0500 Subject: [PATCH 12/31] handle plugins with multiple options --- CONTRIBUTING.md | 2 +- index.ipynb | 4 +++- src/manager.ts | 4 ++-- src/plugin.ts | 8 ++++---- src/tokens.ts | 2 +- 5 files changed, 11 insertions(+), 9 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 3c0f67f..9e18249 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -48,7 +48,7 @@ const deflist: JupyterFrontEndPlugin = { const deflistPlugin = await import( /* webpackChunkName: "markdown-it-deflist" */ 'markdown-it-deflist' ); - return [deflistPlugin.default, {}]; + return [deflistPlugin.default]; }, }); }, diff --git a/index.ipynb b/index.ipynb index 3ec2fda..e0b78e9 100644 --- a/index.ipynb +++ b/index.ipynb @@ -12,7 +12,9 @@ "| `markdown-it-diagrams` | [diagrams](#diagrams) |\n", "| `markdown-it-deflist` | [definition lists](#definition-lists) |\n", "| `markdown-it-footnote` | [footnotes](#footnotes) |\n", - "| `markdown-it-replace-link` | link replacement |" + "| `markdown-it-replace-link` | link replacement |\n", + "\n", + "" ] }, { diff --git a/src/manager.ts b/src/manager.ts index 455fa29..c8af3d9 100644 --- a/src/manager.ts +++ b/src/manager.ts @@ -39,8 +39,8 @@ export class MarkdownItManager implements IMarkdownIt { for (const [name, provider] of this._pluginProviders.entries()) { try { - const [plugin, options] = await provider.plugin(); - md = md.use(plugin, options); + const [plugin, ...options] = await provider.plugin(); + md = md.use(plugin, ...options); } catch (err) { console.warn(`Failed to load/use markdown-it plugin ${name}`, err); } diff --git a/src/plugin.ts b/src/plugin.ts index 810c90a..f2098a2 100644 --- a/src/plugin.ts +++ b/src/plugin.ts @@ -36,7 +36,7 @@ const diagrams: JupyterFrontEndPlugin = { /* webpackChunkName: "markdown-it-diagrams" */ 'markdown-it-diagrams' ); await awaitRenderAvailable(); - return [diagramPlugin, {}]; + return [diagramPlugin]; }, }); }, @@ -56,7 +56,7 @@ const footnote: JupyterFrontEndPlugin = { const footnotePlugin = await import( /* webpackChunkName: "markdown-it-footnote" */ 'markdown-it-footnote' ); - return [footnotePlugin.default, {}]; + return [footnotePlugin.default]; }, }); }, @@ -76,7 +76,7 @@ const deflist: JupyterFrontEndPlugin = { const deflistPlugin = await import( /* webpackChunkName: "markdown-it-deflist" */ 'markdown-it-deflist' ); - return [deflistPlugin.default, {}]; + return [deflistPlugin.default]; }, }); }, @@ -104,7 +104,7 @@ const replacelink: JupyterFrontEndPlugin = { const replaceLinkPlugin = await import( /* webpackChunkName: "markdown-it-replace-link" */ 'markdown-it-replace-link' ); - return [replaceLinkPlugin.default, {}]; + return [replaceLinkPlugin.default]; }, }); }, diff --git a/src/tokens.ts b/src/tokens.ts index 8bbdc44..4bbe798 100644 --- a/src/tokens.ts +++ b/src/tokens.ts @@ -42,7 +42,7 @@ export namespace IMarkdownIt { /** * A lazy provider of the plugin function and plugin options */ - plugin(): Promise<[IPlugin, any]>; + plugin(): Promise<[IPlugin, ...any]>; /** * Additional options to pass to the MarkdownIt constructor */ From 7ca1f71161f8ca9ff8680e546d4edaf581f02df2 Mon Sep 17 00:00:00 2001 From: Nicholas Bollweg Date: Sun, 1 Nov 2020 21:14:08 -0500 Subject: [PATCH 13/31] task lists --- README.md | 12 +++--- index.ipynb | 57 ++++++++++++++----------- package.json | 3 +- src/manager.ts | 10 +++-- src/plugin.ts | 47 +++++++++++++++++--- src/typings/markdown-it-task-lists.d.ts | 10 +++++ yarn.lock | 5 +++ 7 files changed, 104 insertions(+), 40 deletions(-) create mode 100644 src/typings/markdown-it-task-lists.d.ts diff --git a/README.md b/README.md index f0e49ed..03d5663 100644 --- a/README.md +++ b/README.md @@ -2,15 +2,15 @@ Adds additional rendering support to markdown in JupyterLab by using [markdown-it](https://github.com/markdown-it/markdown-it), and the following plugins: +- [markdown-it-anchor](https://github.com/valeriangalliat/markdown-it-anchor) +- [markdown-it-deflist](https://github.com/markdown-it/markdown-it-deflist) - [markdown-it-diagrams](https://github.com/agoose77/markdown-it-diagrams) - [markdown-it-footnote](https://github.com/markdown-it/markdown-it-footnote) -- [markdown-it-deflist](https://github.com/markdown-it/markdown-it-deflist) - [markdown-it-replace-link](https://github.com/martinheidegger/markdown-it-replace-link) -- [markdown-it-anchor](https://github.com/valeriangalliat/markdown-it-anchor) - -![Full example rendering vs markup.](https://i.imgur.com/OL9oGcq.png) -![svgbob rendering](https://i.imgur.com/RbDioU8.gif) -![svgbob rendering](https://i.imgur.com/IQSasVZ.gif) +- [markdown-it-task-lists](https://github.com/revin/markdown-it-task-lists) + ![Full example rendering vs markup.](https://i.imgur.com/OL9oGcq.png) + ![svgbob rendering](https://i.imgur.com/RbDioU8.gif) + ![svgbob rendering](https://i.imgur.com/IQSasVZ.gif) ## Prerequisites diff --git a/index.ipynb b/index.ipynb index e0b78e9..ba2b535 100644 --- a/index.ipynb +++ b/index.ipynb @@ -4,7 +4,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "## Table (of contents) with Local Links\n", + "## Table of Contents\n", "\n", "| plugin... | provides... |\n", "|----------------------------|-----------------------------------------|\n", @@ -13,29 +13,7 @@ "| `markdown-it-deflist` | [definition lists](#definition-lists) |\n", "| `markdown-it-footnote` | [footnotes](#footnotes) |\n", "| `markdown-it-replace-link` | link replacement |\n", - "\n", - "" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Footnotes" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Here is a footnote reference,[^1] and another.[^longnote]\n", - "\n", - "[^1]: Here is the footnote.\n", - "\n", - "[^longnote]: Here's one with multiple blocks.\n", - "\n", - " Subsequent paragraphs are indented to show that they\n", - "belong to the previous footnote." + "| `markdown-it-task-lists` | [task lists](#task-lists) |" ] }, { @@ -101,6 +79,37 @@ " '\n", "```" ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Footnotes" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Here is a footnote reference,[^1] and another.[^longnote]\n", + "\n", + "[^1]: Here is the footnote.\n", + "\n", + "[^longnote]: Here's one with multiple blocks.\n", + "\n", + " Subsequent paragraphs are indented to show that they\n", + "belong to the previous footnote." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Task Lists\n", + "\n", + "- [x] done\n", + "- [ ] not done" + ] } ], "metadata": { diff --git a/package.json b/package.json index 0c68f1c..fa040a7 100644 --- a/package.json +++ b/package.json @@ -45,7 +45,8 @@ "markdown-it-deflist": "^2.0.3", "markdown-it-diagrams": "^0.1.2", "markdown-it-footnote": "^3.0.2", - "markdown-it-replace-link": "^1.1.0" + "markdown-it-replace-link": "^1.1.0", + "markdown-it-task-lists": "^2.1.1" }, "devDependencies": { "@types/codemirror": "^0.0.74", diff --git a/src/manager.ts b/src/manager.ts index c8af3d9..a40abe3 100644 --- a/src/manager.ts +++ b/src/manager.ts @@ -32,10 +32,12 @@ export class MarkdownItManager implements IMarkdownIt { widget: RenderedMarkdown, options: MarkdownIt.Options = {} ): Promise { - let md = new MarkdownIt({ - ...this.getOptions(widget), + const allOptions = { + ...(await this.getOptions(widget)), ...options, - }); + }; + + let md = new MarkdownIt('default', allOptions); for (const [name, provider] of this._pluginProviders.entries()) { try { @@ -103,7 +105,7 @@ export class MarkdownItManager implements IMarkdownIt { console.warn(`Failed to highlight ${lang} code`, err); } } catch (err) { - console.log(`No CodeMirror mode: ${lang}`); + console.warn(`No CodeMirror mode: ${lang}`); console.warn(`Require CodeMirror mode error: ${err}`); } return ''; diff --git a/src/plugin.ts b/src/plugin.ts index f2098a2..ac9a1ec 100644 --- a/src/plugin.ts +++ b/src/plugin.ts @@ -4,6 +4,7 @@ import { JupyterFrontEndPlugin } from '@jupyterlab/application'; import { IMarkdownIt, PLUGIN_ID } from './tokens'; import { MarkdownItManager } from './manager'; import { RenderedMarkdown } from './widgets'; +import { PathExt } from '@jupyterlab/coreutils'; /** * The main plugin which overloads default markdown rendering by `marked` @@ -93,10 +94,17 @@ const replacelink: JupyterFrontEndPlugin = { markdownIt.addPluginProvider({ id: 'markdown-it-replace-link', options: async (widget) => { - const { isLocal, resolveUrl } = widget.resolver; + const { resolver } = widget; return { replaceLink: function (link: string, env: any) { - return isLocal(link) ? resolveUrl(link) : link; + // hacky, but this can't be a promise + if (!resolver.isLocal(link)) { + return link; + } + const cwd = encodeURI( + PathExt.dirname((resolver as any)._parent.path) + ); + return PathExt.resolve(cwd, link); }, }; }, @@ -121,11 +129,11 @@ const anchor: JupyterFrontEndPlugin = { markdownIt.addPluginProvider({ id: 'markdown-it-anchor', plugin: async () => { - const replaceLinkPlugin = await import( + const anchorPlugin = await import( /* webpackChunkName: "markdown-it-anchor" */ 'markdown-it-anchor' ); return [ - replaceLinkPlugin.default, + anchorPlugin.default, { permalink: true, permalinkClass: 'jp-InternalAnchorLink', @@ -136,4 +144,33 @@ const anchor: JupyterFrontEndPlugin = { }, }; -export default [core, anchor, diagrams, footnote, deflist, replacelink]; +/** + * Adds github-flavored task lists + */ +const tasklist: JupyterFrontEndPlugin = { + id: `${PLUGIN_ID}:markdown-it-task-lists`, + autoStart: true, + requires: [IMarkdownIt], + activate: (app, markdownIt: IMarkdownIt) => { + markdownIt.addPluginProvider({ + id: 'markdown-it-task-lists', + plugin: async () => { + const tasklistPlugin = await import( + /* webpackChunkName: "markdown-it-task-lists" */ 'markdown-it-task-lists' + ); + return [tasklistPlugin.default]; + }, + }); + }, +}; + +export default [ + core, + // plugins + anchor, + deflist, + diagrams, + footnote, + replacelink, + tasklist, +]; diff --git a/src/typings/markdown-it-task-lists.d.ts b/src/typings/markdown-it-task-lists.d.ts new file mode 100644 index 0000000..4e77b02 --- /dev/null +++ b/src/typings/markdown-it-task-lists.d.ts @@ -0,0 +1,10 @@ +declare module 'markdown-it-task-lists' { + import MarkdownIt = require('markdown-it'); + + namespace markdownItTaskList { + function tasklist_plugin(md: MarkdownIt, opts: any): void; + } + + const MarkdownItTaskList: typeof markdownItTaskList.tasklist_plugin; + export = MarkdownItTaskList; +} diff --git a/yarn.lock b/yarn.lock index 0d51160..e65cf0a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1316,6 +1316,11 @@ markdown-it-replace-link@^1.1.0: resolved "https://registry.yarnpkg.com/markdown-it-replace-link/-/markdown-it-replace-link-1.1.0.tgz#cab2343eb27928db1c836e10cd518aaf60a0bd50" integrity sha512-P+4D/Z16utgQVjMumA5W3lMbxWYk4iwg1Fk59wShFm7MRgzbjJm++tvI18LdYwiG8CKNA1TFWvHcbtehvMuViA== +markdown-it-task-lists@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/markdown-it-task-lists/-/markdown-it-task-lists-2.1.1.tgz#f68f4d2ac2bad5a2c373ba93081a1a6848417088" + integrity sha512-TxFAc76Jnhb2OUu+n3yz9RMu4CwGfaT788br6HhEDlvWfdeJcLUsxk1Hgw2yJio0OXsxv7pyIPmvECY7bMbluA== + markdown-it@^10.0.0: version "10.0.0" resolved "https://registry.yarnpkg.com/markdown-it/-/markdown-it-10.0.0.tgz#abfc64f141b1722d663402044e43927f1f50a8dc" From 5eee04705a10edd3640984a1f1ba79e228932c42 Mon Sep 17 00:00:00 2001 From: Nicholas Bollweg Date: Mon, 2 Nov 2020 13:28:34 -0500 Subject: [PATCH 14/31] per-plugin options --- package.json | 16 ++++-- schema/core.json | 38 ++++++++++++++ src/manager.ts | 133 +++++++++++++++++++++++++++++++++++++++++++---- src/plugin.ts | 18 ++++--- src/tokens.ts | 7 ++- 5 files changed, 190 insertions(+), 22 deletions(-) create mode 100644 schema/core.json diff --git a/package.json b/package.json index fa040a7..1482f49 100644 --- a/package.json +++ b/package.json @@ -14,8 +14,7 @@ "license": "BSD-3-Clause", "author": "Angus Hollands", "files": [ - "{src,lib}/**/*.{ts,eot,gif,html,jpg,js,js.map,json,png,svg,woff2,ttf}", - "style/**/*.{css,eot,gif,html,jpg,json,png,svg,woff2,ttf}", + "{src,lib,schema}/**/*.{ts,eot,gif,html,jpg,js,map,json,png,svg,woff2,ttf,css}", "LICENSE.txt" ], "main": "lib/index.js", @@ -57,6 +56,17 @@ "typescript": "^4.0.5" }, "jupyterlab": { - "extension": "lib/plugin.js" + "extension": "lib/plugin.js", + "discovery": { + "server": { + "base": { + "name": "jupyterlab_markup" + }, + "managers": [ + "pip" + ] + } + }, + "schemaDir": "schema" } } diff --git a/schema/core.json b/schema/core.json new file mode 100644 index 0000000..7b25c37 --- /dev/null +++ b/schema/core.json @@ -0,0 +1,38 @@ +{ + "jupyter.lab.setting-icon": "ui-components:markdown", + "jupyter.lab.setting-icon-label": "Markdown Extensions", + "title": "Markdown Extensions", + "description": "Markdown extension settings", + "type": "object", + "properties": { + "enabled": { + "title": "Enable markdown-it Markdown renderer", + "type": "boolean", + "default": true, + "description": "Use markdown-it for new renderers. Re-open existing documents to see changes." + }, + "disabled-plugins": { + "title": "Disabled plugins", + "type": "array", + "items": { + "type": "string" + }, + "description": "markdown-it plugins to disable for new renderers. Re-open existing documents to see changes.", + "default": [] + }, + "markdown-it-options": { + "title": "MarkdownIt options", + "type": "object", + "description": "Advanced. JSON-compatible override options to MarkdownIt." + }, + "plugin-options": { + "title": "markdown-it plugin options", + "type": "object", + "description": "Advanced. List of positional JSON-compatible options to pass to the named plugin.", + "default": {}, + "additionalProperties": { + "type": "array" + } + } + } +} diff --git a/src/manager.ts b/src/manager.ts index a40abe3..34a305f 100644 --- a/src/manager.ts +++ b/src/manager.ts @@ -1,16 +1,103 @@ -import * as MarkdownIt from 'markdown-it'; -import { IMarkdownIt } from './tokens'; - +import { IRenderMime, markdownRendererFactory } from '@jupyterlab/rendermime'; +import { ISettingRegistry } from '@jupyterlab/settingregistry'; import { CodeMirrorEditor, Mode } from '@jupyterlab/codemirror'; +import * as MarkdownIt from 'markdown-it'; + import { RenderedMarkdown } from './widgets'; +import { IMarkdownIt } from './tokens'; + +/** + * A handle for the original createRenderer for restoring default behavior + */ +const ORIGINAL_RENDERER = markdownRendererFactory.createRenderer; +/** + * An implementation of a source of markdown renderers with markdown-it and plugins + */ export class MarkdownItManager implements IMarkdownIt { + protected userDisabledPlugins: string[] = []; + /** + * Whether to use the markdown-it renderer: if installed, will use unless configured by user + */ + protected useMarkdownIt = true; + + /** + * MarkdownIt options configured by the user. + */ + protected userMarkdownItOptions: object = {}; + + /** + * Per-plugin options configured by the user. + */ + protected userPluginOptions: IMarkdownIt.IAllPluginOptions = {}; + + /** + * A handle on the settings for this plugin, which will be set... eventually + */ + private _settings: ISettingRegistry.ISettings | null; + + /** + * Providers labeled by an arbitrary key (usually the markdown-it package name) + */ private _pluginProviders: Map< string, IMarkdownIt.IPluginProvider > = new Map(); + constructor() { + markdownRendererFactory.createRenderer = this.createRenderer; + } + + /** + * Create a new renderer, either with markdown-it or the original implementation + */ + protected createRenderer = (options: IRenderMime.IRendererOptions) => { + return this.useMarkdownIt + ? new RenderedMarkdown(options) + : ORIGINAL_RENDERER(options); + }; + + /** + * Update the settings, and handle changes. + */ + set settings(settings) { + if (this._settings) { + this._settings.changed.disconnect(this.onSettingsChanged, this); + } + this._settings = settings; + if (settings != null) { + this._settings.changed.connect(this.onSettingsChanged, this); + this.onSettingsChanged(); + } + } + + /** + * The settings + */ + get settings() { + return this._settings; + } + + /** + * Update caches of settings values for new renderers + */ + protected onSettingsChanged() { + const useMarkdownIt = this.settings?.composite['enabled'] as boolean; + this.useMarkdownIt = useMarkdownIt == null ? true : useMarkdownIt; + + this.userMarkdownItOptions = + (this.settings?.composite['markdown-it-options'] as any) || {}; + + this.userDisabledPlugins = + (this.settings?.composite['disabled-plugins'] as string[]) || []; + + this.userPluginOptions = + (this.settings?.composite[ + 'plugin-options' + ] as IMarkdownIt.IAllPluginOptions) || {}; + } + /** * Add a provider for a plugin which can be resolved lazily */ @@ -35,26 +122,46 @@ export class MarkdownItManager implements IMarkdownIt { const allOptions = { ...(await this.getOptions(widget)), ...options, + ...this.userMarkdownItOptions, }; let md = new MarkdownIt('default', allOptions); - for (const [name, provider] of this._pluginProviders.entries()) { + for (const [id, provider] of this._pluginProviders.entries()) { + if (this.userDisabledPlugins.indexOf(id) !== -1) { + continue; + } try { - const [plugin, ...options] = await provider.plugin(); - md = md.use(plugin, ...options); + const userOptions = this.userPluginOptions[id] || []; + const [plugin, ...pluginOptions] = await provider.plugin(); + let i = 0; + const maxOptions = Math.max(pluginOptions.length, userOptions.length); + let compositeOptions = new Array(maxOptions); + while (i < maxOptions) { + compositeOptions[i] = + userOptions.length < i ? userOptions[i] : pluginOptions[i]; + i++; + } + md = md.use(plugin, ...compositeOptions); } catch (err) { - console.warn(`Failed to load/use markdown-it plugin ${name}`, err); + console.warn(`Failed to load/use markdown-it plugin ${id}`, err); } } return md; } + /** + * Combine core options with base options, plugin provider options, and user settings + */ async getOptions(widget: RenderedMarkdown) { let allOptions = this.baseMarkdownItOptions; - for (const [name, plugin] of this._pluginProviders.entries()) { + for (const [id, plugin] of this._pluginProviders.entries()) { + if (this.userDisabledPlugins.indexOf(id) !== -1) { + continue; + } + if (plugin.options == null) { continue; } @@ -62,7 +169,7 @@ export class MarkdownItManager implements IMarkdownIt { allOptions = { ...allOptions, ...(await plugin.options(widget)) }; } catch (err) { console.warn( - `Failed to get options from markdown-it plugin ${name}`, + `Failed to get options from markdown-it plugin ${id}`, err ); } @@ -71,7 +178,9 @@ export class MarkdownItManager implements IMarkdownIt { } /** - * Default MarkdownIt options, may be overridden by plugins + * Default MarkdownIt options, + * + * NOTE: May be overridden by plugins */ get baseMarkdownItOptions() { return { @@ -84,7 +193,9 @@ export class MarkdownItManager implements IMarkdownIt { } /** - * Use CodeMirror to highlight code blocks, may be overriden by plugins + * Use CodeMirror to highlight code blocks, + * + * NOTE: May be overridden by plugins */ highlightCode = (str: string, lang: string) => { if (!lang) { diff --git a/src/plugin.ts b/src/plugin.ts index ac9a1ec..7b21474 100644 --- a/src/plugin.ts +++ b/src/plugin.ts @@ -1,23 +1,29 @@ -import { markdownRendererFactory } from '@jupyterlab/rendermime'; +import { PathExt } from '@jupyterlab/coreutils'; + import { JupyterFrontEndPlugin } from '@jupyterlab/application'; +import { ISettingRegistry } from '@jupyterlab/settingregistry'; import { IMarkdownIt, PLUGIN_ID } from './tokens'; import { MarkdownItManager } from './manager'; import { RenderedMarkdown } from './widgets'; -import { PathExt } from '@jupyterlab/coreutils'; /** * The main plugin which overloads default markdown rendering by `marked` */ const core: JupyterFrontEndPlugin = { - id: PLUGIN_ID, + id: `${PLUGIN_ID}:core`, autoStart: true, provides: IMarkdownIt, - activate: (app) => { + requires: [ISettingRegistry], + activate: (app, settings: ISettingRegistry) => { const manager = new MarkdownItManager(); + // set the static manager RenderedMarkdown.markdownItManager = manager; - markdownRendererFactory.createRenderer = (options) => - new RenderedMarkdown(options); + // eventually load settings + settings + .load(core.id) + .then((settings) => (manager.settings = settings)) + .catch(console.warn); return manager; }, }; diff --git a/src/tokens.ts b/src/tokens.ts index 4bbe798..5711102 100644 --- a/src/tokens.ts +++ b/src/tokens.ts @@ -33,7 +33,7 @@ export namespace IMarkdownIt { export interface IPlugin { (md: MarkdownIt, ...params: any[]): void; } - export type IPluginOptions = object; + export type TPluginOptions = object; export interface IPluginProvider { /** * A unique identifier for the plugin, usually the name of the upstream package @@ -46,6 +46,9 @@ export namespace IMarkdownIt { /** * Additional options to pass to the MarkdownIt constructor */ - options?(widget: RenderedMarkdown): Promise; + options?(widget: RenderedMarkdown): Promise; + } + export interface IAllPluginOptions { + [key: string]: any[]; } } From 6db1e21b04a561225497bd5ea38a4d0dfbfc101c Mon Sep 17 00:00:00 2001 From: Nicholas Bollweg Date: Mon, 2 Nov 2020 14:27:00 -0500 Subject: [PATCH 15/31] refactor builtins --- schema/core.json | 4 +- src/builtins/anchor.ts | 25 ++++++ src/builtins/deflist.ts | 29 +++++++ src/builtins/diagrams.ts | 43 ++++++++++ src/builtins/footnote.ts | 31 +++++++ src/builtins/index.ts | 18 ++++ src/builtins/replace-link.ts | 33 +++++++ src/builtins/task-lists.ts | 26 ++++++ src/index.ts | 5 +- src/plugin.ts | 161 ++--------------------------------- src/tokens.ts | 22 ++++- src/utils.ts | 20 +++++ 12 files changed, 253 insertions(+), 164 deletions(-) create mode 100644 src/builtins/anchor.ts create mode 100644 src/builtins/deflist.ts create mode 100644 src/builtins/diagrams.ts create mode 100644 src/builtins/footnote.ts create mode 100644 src/builtins/index.ts create mode 100644 src/builtins/replace-link.ts create mode 100644 src/builtins/task-lists.ts create mode 100644 src/utils.ts diff --git a/schema/core.json b/schema/core.json index 7b25c37..c436996 100644 --- a/schema/core.json +++ b/schema/core.json @@ -23,10 +23,10 @@ "markdown-it-options": { "title": "MarkdownIt options", "type": "object", - "description": "Advanced. JSON-compatible override options to MarkdownIt." + "description": "Advanced. JSON-compatible override options to MarkdownIt. https://markdown-it.github.io/markdown-it/#MarkdownIt.new" }, "plugin-options": { - "title": "markdown-it plugin options", + "title": "markdown-it plugin options. See individual plugins for options.", "type": "object", "description": "Advanced. List of positional JSON-compatible options to pass to the named plugin.", "default": {}, diff --git a/src/builtins/anchor.ts b/src/builtins/anchor.ts new file mode 100644 index 0000000..f592d1a --- /dev/null +++ b/src/builtins/anchor.ts @@ -0,0 +1,25 @@ +import { PACKAGE_NS, simpleMarkdownItPlugin } from '..'; + +/** + * Adds anchors to headers + */ +export const anchor = simpleMarkdownItPlugin(PACKAGE_NS, { + id: 'markdown-it-anchor', + title: 'Heading Anchors', + description: 'Create clickable links for header elements', + documentationUrls: { + Plugin: 'https://github.com/valeriangalliat/markdown-it-anchor', + }, + plugin: async () => { + const anchorPlugin = await import( + /* webpackChunkName: "markdown-it-anchor" */ 'markdown-it-anchor' + ); + return [ + anchorPlugin.default, + { + permalink: true, + permalinkClass: 'jp-InternalAnchorLink', + }, + ]; + }, +}); diff --git a/src/builtins/deflist.ts b/src/builtins/deflist.ts new file mode 100644 index 0000000..e1c93c6 --- /dev/null +++ b/src/builtins/deflist.ts @@ -0,0 +1,29 @@ +import { PACKAGE_NS, simpleMarkdownItPlugin } from '..'; + +/** + * Provides definition lists + */ +export const deflist = simpleMarkdownItPlugin(PACKAGE_NS, { + id: 'markdown-it-deflist', + title: 'Definition Lists', + description: 'Create definition lists', + documentationUrls: { + Plugin: 'https://github.com/markdown-it/markdown-it-deflist', + }, + examples: { + Example: ` +Term 1 +~ Definition 1 + +Term 2 +~ Definition 2a +~ Definition 2b + `, + }, + plugin: async () => { + const deflistPlugin = await import( + /* webpackChunkName: "markdown-it-deflist" */ 'markdown-it-deflist' + ); + return [deflistPlugin.default]; + }, +}); diff --git a/src/builtins/diagrams.ts b/src/builtins/diagrams.ts new file mode 100644 index 0000000..abab1e2 --- /dev/null +++ b/src/builtins/diagrams.ts @@ -0,0 +1,43 @@ +import { PACKAGE_NS, simpleMarkdownItPlugin } from '..'; + +/** + * Provides text-based diagrams in code blocks + */ +export const diagrams = simpleMarkdownItPlugin(PACKAGE_NS, { + id: 'markdown-it-diagrams', + title: 'Diagrams', + description: 'Diagrams in code blocks from mermaid and svgbob', + documentationUrls: { + Plugin: 'https://github.com/agoose77/markdown-it-diagrams', + MermaidJS: 'https://mermaid-js.github.io/mermaid', + svgbob: 'https://github.com/ivanceras/svgbob', + }, + examples: { + 'Mermaid Flowchart': ` + \`\`\`mermaid + graph TD; + A-->B; + A-->C; + B-->D; + C-->D; + \`\`\``, + svgbob: ` + \`\`\`bob + .---. + /-o-/-- + .-/ / /-> + ( * \/ + '-. \ + \ / + ' + \`\`\` + `, + }, + plugin: async () => { + const { diagramPlugin, awaitRenderAvailable } = await import( + /* webpackChunkName: "markdown-it-diagrams" */ 'markdown-it-diagrams' + ); + await awaitRenderAvailable(); + return [diagramPlugin]; + }, +}); diff --git a/src/builtins/footnote.ts b/src/builtins/footnote.ts new file mode 100644 index 0000000..c67a957 --- /dev/null +++ b/src/builtins/footnote.ts @@ -0,0 +1,31 @@ +import { PACKAGE_NS, simpleMarkdownItPlugin } from '..'; + +/** + * Provides footnotes + */ +export const footnote = simpleMarkdownItPlugin(PACKAGE_NS, { + id: 'markdown-it-footnote', + title: 'Footnotes', + description: 'Create links notes that appear after the current paragraph', + documentationUrls: { + Plugin: 'https://github.com/markdown-it/markdown-it-footnote', + }, + examples: { + Simple: ` +Here is a footnote reference,[^1] and another.[^longnote] + +[^1]: Here is the footnote. + +[^longnote]: Here's one with multiple blocks. + + Subsequent paragraphs are indented to show that they +belong to the previous footnote. + `, + }, + plugin: async () => { + const footnotePlugin = await import( + /* webpackChunkName: "markdown-it-footnote" */ 'markdown-it-footnote' + ); + return [footnotePlugin.default]; + }, +}); diff --git a/src/builtins/index.ts b/src/builtins/index.ts new file mode 100644 index 0000000..17bd1a6 --- /dev/null +++ b/src/builtins/index.ts @@ -0,0 +1,18 @@ +import { anchor } from './anchor'; +import { deflist } from './deflist'; +import { diagrams } from './diagrams'; +import { footnote } from './footnote'; +import { replaceLink } from './replace-link'; +import { taskLists } from './task-lists'; + +/** + * Builtin plugins provided by this labextension + */ +export const BUILTINS = [ + anchor, + deflist, + diagrams, + footnote, + replaceLink, + taskLists, +]; diff --git a/src/builtins/replace-link.ts b/src/builtins/replace-link.ts new file mode 100644 index 0000000..afd7e4f --- /dev/null +++ b/src/builtins/replace-link.ts @@ -0,0 +1,33 @@ +import { PACKAGE_NS, simpleMarkdownItPlugin } from '..'; +import { PathExt } from '@jupyterlab/coreutils'; + +/** + * Replaces relative links with the Jupyter files URLs + */ +export const replaceLink = simpleMarkdownItPlugin(PACKAGE_NS, { + id: 'markdown-it-replace-link', + title: 'Replace Links', + description: 'Replaces relative links to local Jupyter URLs', + documentationUrls: { + Plugin: 'markdown-it-replace-link', + }, + options: async (widget) => { + const { resolver } = widget; + return { + replaceLink: function (link: string, env: any) { + // hacky, but this can't be a promise + if (!resolver.isLocal(link)) { + return link; + } + const cwd = encodeURI(PathExt.dirname((resolver as any)._parent.path)); + return PathExt.resolve(cwd, link); + }, + }; + }, + plugin: async () => { + const replaceLinkPlugin = await import( + /* webpackChunkName: "markdown-it-replace-link" */ 'markdown-it-replace-link' + ); + return [replaceLinkPlugin.default]; + }, +}); diff --git a/src/builtins/task-lists.ts b/src/builtins/task-lists.ts new file mode 100644 index 0000000..6f2eb8c --- /dev/null +++ b/src/builtins/task-lists.ts @@ -0,0 +1,26 @@ +import { PACKAGE_NS, simpleMarkdownItPlugin } from '..'; + +/** + * Adds github-flavored task lists + */ +export const taskLists = simpleMarkdownItPlugin(PACKAGE_NS, { + id: 'markdown-it-task-lists', + title: 'Task Lists', + description: 'Create checklists from lists', + documentationUrls: { + Plugin: 'https://github.com/revin/markdown-it-task-lists', + }, + examples: { + Example: ` +- [x] done +- [ ] to do +- [ ] ~~not going to do~~ + `, + }, + plugin: async () => { + const tasklistPlugin = await import( + /* webpackChunkName: "markdown-it-task-lists" */ 'markdown-it-task-lists' + ); + return [tasklistPlugin.default]; + }, +}); diff --git a/src/index.ts b/src/index.ts index 74714d0..5953507 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,3 +1,2 @@ -import { IMarkdownIt } from './tokens'; - -export { IMarkdownIt }; +export { IMarkdownIt, PACKAGE_NS } from './tokens'; +export { simpleMarkdownItPlugin } from './utils'; diff --git a/src/plugin.ts b/src/plugin.ts index 7b21474..af03893 100644 --- a/src/plugin.ts +++ b/src/plugin.ts @@ -1,17 +1,17 @@ -import { PathExt } from '@jupyterlab/coreutils'; - import { JupyterFrontEndPlugin } from '@jupyterlab/application'; import { ISettingRegistry } from '@jupyterlab/settingregistry'; -import { IMarkdownIt, PLUGIN_ID } from './tokens'; +import { IMarkdownIt, PACKAGE_NS } from './tokens'; import { MarkdownItManager } from './manager'; import { RenderedMarkdown } from './widgets'; +import { BUILTINS } from './builtins'; + /** * The main plugin which overloads default markdown rendering by `marked` */ const core: JupyterFrontEndPlugin = { - id: `${PLUGIN_ID}:core`, + id: `${PACKAGE_NS}:core`, autoStart: true, provides: IMarkdownIt, requires: [ISettingRegistry], @@ -28,155 +28,4 @@ const core: JupyterFrontEndPlugin = { }, }; -/** - * Provides text-based diagrams in code blocks - */ -const diagrams: JupyterFrontEndPlugin = { - id: `${PLUGIN_ID}:diagrams`, - autoStart: true, - requires: [IMarkdownIt], - activate: (app, markdownIt: IMarkdownIt) => { - markdownIt.addPluginProvider({ - id: 'markdown-it-diagrams', - plugin: async () => { - const { diagramPlugin, awaitRenderAvailable } = await import( - /* webpackChunkName: "markdown-it-diagrams" */ 'markdown-it-diagrams' - ); - await awaitRenderAvailable(); - return [diagramPlugin]; - }, - }); - }, -}; - -/** - * Provides footnotes - */ -const footnote: JupyterFrontEndPlugin = { - id: `${PLUGIN_ID}:footnote`, - autoStart: true, - requires: [IMarkdownIt], - activate: (app, markdownIt: IMarkdownIt) => { - markdownIt.addPluginProvider({ - id: 'markdown-it-footnote', - plugin: async () => { - const footnotePlugin = await import( - /* webpackChunkName: "markdown-it-footnote" */ 'markdown-it-footnote' - ); - return [footnotePlugin.default]; - }, - }); - }, -}; - -/** - * Provides definition lists - */ -const deflist: JupyterFrontEndPlugin = { - id: `${PLUGIN_ID}:deflist`, - autoStart: true, - requires: [IMarkdownIt], - activate: (app, markdownIt: IMarkdownIt) => { - markdownIt.addPluginProvider({ - id: 'markdown-it-deflist', - plugin: async () => { - const deflistPlugin = await import( - /* webpackChunkName: "markdown-it-deflist" */ 'markdown-it-deflist' - ); - return [deflistPlugin.default]; - }, - }); - }, -}; - -/** - * Replaces local links to the Jupyter files URL - */ -const replacelink: JupyterFrontEndPlugin = { - id: `${PLUGIN_ID}:markdown-it-replace-link`, - autoStart: true, - requires: [IMarkdownIt], - activate: (app, markdownIt: IMarkdownIt) => { - markdownIt.addPluginProvider({ - id: 'markdown-it-replace-link', - options: async (widget) => { - const { resolver } = widget; - return { - replaceLink: function (link: string, env: any) { - // hacky, but this can't be a promise - if (!resolver.isLocal(link)) { - return link; - } - const cwd = encodeURI( - PathExt.dirname((resolver as any)._parent.path) - ); - return PathExt.resolve(cwd, link); - }, - }; - }, - plugin: async () => { - const replaceLinkPlugin = await import( - /* webpackChunkName: "markdown-it-replace-link" */ 'markdown-it-replace-link' - ); - return [replaceLinkPlugin.default]; - }, - }); - }, -}; - -/** - * Adds anchors to headers - */ -const anchor: JupyterFrontEndPlugin = { - id: `${PLUGIN_ID}:markdown-it-anchor`, - autoStart: true, - requires: [IMarkdownIt], - activate: (app, markdownIt: IMarkdownIt) => { - markdownIt.addPluginProvider({ - id: 'markdown-it-anchor', - plugin: async () => { - const anchorPlugin = await import( - /* webpackChunkName: "markdown-it-anchor" */ 'markdown-it-anchor' - ); - return [ - anchorPlugin.default, - { - permalink: true, - permalinkClass: 'jp-InternalAnchorLink', - }, - ]; - }, - }); - }, -}; - -/** - * Adds github-flavored task lists - */ -const tasklist: JupyterFrontEndPlugin = { - id: `${PLUGIN_ID}:markdown-it-task-lists`, - autoStart: true, - requires: [IMarkdownIt], - activate: (app, markdownIt: IMarkdownIt) => { - markdownIt.addPluginProvider({ - id: 'markdown-it-task-lists', - plugin: async () => { - const tasklistPlugin = await import( - /* webpackChunkName: "markdown-it-task-lists" */ 'markdown-it-task-lists' - ); - return [tasklistPlugin.default]; - }, - }); - }, -}; - -export default [ - core, - // plugins - anchor, - deflist, - diagrams, - footnote, - replacelink, - tasklist, -]; +export default [core, ...BUILTINS]; diff --git a/src/tokens.ts b/src/tokens.ts index 5711102..499862a 100644 --- a/src/tokens.ts +++ b/src/tokens.ts @@ -3,15 +3,15 @@ import MarkdownIt from 'markdown-it'; import { RenderedMarkdown } from './widgets'; /** - * The plugin ID stem for all lab extensions + * The ID stem for all plugins */ -export const PLUGIN_ID = '@agoose77/jupyterlab-markup'; +export const PACKAGE_NS = '@agoose77/jupyterlab-markup'; /* tslint:disable */ /** * The MarkdownIt manager token. */ -export const IMarkdownIt = new Token(PLUGIN_ID); +export const IMarkdownIt = new Token(PACKAGE_NS); /* tslint:enable */ /** @@ -39,6 +39,22 @@ export namespace IMarkdownIt { * A unique identifier for the plugin, usually the name of the upstream package */ id: string; + /** + * A human-readable name for the plugin + */ + title: string; + /** + * A short description for the plugin + */ + description: string; + /** + * URLs for learning more about the plugin with human-readable keys + */ + documentationUrls: { [key: string]: string }; + /** + * Short usage examples of any new syntax with human-readable keys + */ + examples?: { [key: string]: string }; /** * A lazy provider of the plugin function and plugin options */ diff --git a/src/utils.ts b/src/utils.ts new file mode 100644 index 0000000..8222e4d --- /dev/null +++ b/src/utils.ts @@ -0,0 +1,20 @@ +import { JupyterFrontEndPlugin } from '@jupyterlab/application'; + +import { IMarkdownIt } from './tokens'; + +/** + * Convenience method for building JupyterLab MarkdownIt plugins + */ +export function simpleMarkdownItPlugin( + packageName: string, + provider: IMarkdownIt.IPluginProvider +): JupyterFrontEndPlugin { + return { + id: `${packageName}:${provider.id}`, + autoStart: true, + requires: [IMarkdownIt], + activate: (app, markdownIt: IMarkdownIt) => { + markdownIt.addPluginProvider(provider); + }, + }; +} From b1b11baa31d5acfd5a41eff229ee13bc88b85571 Mon Sep 17 00:00:00 2001 From: Nicholas Bollweg Date: Mon, 2 Nov 2020 14:31:01 -0500 Subject: [PATCH 16/31] slim down CONTRIBUTING --- CONTRIBUTING.md | 31 ++----------------------------- 1 file changed, 2 insertions(+), 29 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 9e18249..29d8e31 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -27,35 +27,8 @@ jupyter lab watch # in another terminal, will continue running Additional [`markdown-it` plugins][plugins] can be added as small labextensions. After getting started with the [official cookiecutter][], -your `plugin.ts` might look something like: - -```ts -import { - JupyterFrontEnd, - JupyterFrontEndPlugin, -} from '@jupyterlab/application'; - -import { IMarkdownIt } from '@agoose77/jupyterlab-markup'; - -const deflist: JupyterFrontEndPlugin = { - id: `${PLUGIN_ID}:markdown-it-deflist`, - autoStart: true, - requires: [IMarkdownIt], - activate: (app: JupyterFrontEnd, markdownIt: IMarkdownIt) => { - markdownIt.addPluginProvider({ - id: 'markdown-it-deflist', - plugin: async () => { - const deflistPlugin = await import( - /* webpackChunkName: "markdown-it-deflist" */ 'markdown-it-deflist' - ); - return [deflistPlugin.default]; - }, - }); - }, -}; - -export default [deflist]; -``` +your `plugin.ts` might look something like the the [builtins](./src/builtins/), +which use the `simpleMarkdownItPlugin` boilerplate [wrapper](./src/utils.ts). [official cookiecutter]: https://github.com/jupyterlab/extension-cookiecutter-ts [plugins]: https://www.npmjs.com/search?q=keywords:markdown-it-plugin From cc9414db1964bb3b3ed8af1ce5ccb3487a9ae0d7 Mon Sep 17 00:00:00 2001 From: Nicholas Bollweg Date: Mon, 2 Nov 2020 14:45:39 -0500 Subject: [PATCH 17/31] add settings boilerplate --- package.json | 2 +- src/plugin.ts | 29 ++++++++++++++++++++++++++--- src/settings.tsx | 32 ++++++++++++++++++++++++++++++++ src/tokens.ts | 8 ++++++++ tsconfig.json | 1 + 5 files changed, 68 insertions(+), 4 deletions(-) create mode 100644 src/settings.tsx diff --git a/package.json b/package.json index 1482f49..9342ab2 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,7 @@ "license": "BSD-3-Clause", "author": "Angus Hollands", "files": [ - "{src,lib,schema}/**/*.{ts,eot,gif,html,jpg,js,map,json,png,svg,woff2,ttf,css}", + "{src,lib,schema}/**/*.{ts,tsx,eot,gif,html,jpg,js,map,json,png,svg,woff2,ttf,css}", "LICENSE.txt" ], "main": "lib/index.js", diff --git a/src/plugin.ts b/src/plugin.ts index af03893..b0e07a1 100644 --- a/src/plugin.ts +++ b/src/plugin.ts @@ -1,9 +1,12 @@ import { JupyterFrontEndPlugin } from '@jupyterlab/application'; import { ISettingRegistry } from '@jupyterlab/settingregistry'; +import { MainAreaWidget, ICommandPalette } from '@jupyterlab/apputils'; +import { markdownIcon } from '@jupyterlab/ui-components'; -import { IMarkdownIt, PACKAGE_NS } from './tokens'; +import { CommandIDs, IMarkdownIt, PACKAGE_NS } from './tokens'; import { MarkdownItManager } from './manager'; import { RenderedMarkdown } from './widgets'; +import { MarkdownItSettings } from './settings'; import { BUILTINS } from './builtins'; @@ -14,8 +17,8 @@ const core: JupyterFrontEndPlugin = { id: `${PACKAGE_NS}:core`, autoStart: true, provides: IMarkdownIt, - requires: [ISettingRegistry], - activate: (app, settings: ISettingRegistry) => { + requires: [ISettingRegistry, ICommandPalette], + activate: (app, settings: ISettingRegistry, palette: ICommandPalette) => { const manager = new MarkdownItManager(); // set the static manager RenderedMarkdown.markdownItManager = manager; @@ -24,6 +27,26 @@ const core: JupyterFrontEndPlugin = { .load(core.id) .then((settings) => (manager.settings = settings)) .catch(console.warn); + + // commands + app.commands.addCommand(CommandIDs.showSettings, { + label: 'Markdown Extensions', + execute: (args) => { + const model = new MarkdownItSettings.Model(); + model.manager = manager; + const content = new MarkdownItSettings(model); + const main = new MainAreaWidget({ content }); + main.title.label = 'Markdown Extensions'; + main.title.icon = markdownIcon; + app.shell.add(main, 'main'); + }, + }); + + palette.addItem({ + command: CommandIDs.showSettings, + category: 'Markdown', + }); + return manager; }, }; diff --git a/src/settings.tsx b/src/settings.tsx new file mode 100644 index 0000000..d8ab874 --- /dev/null +++ b/src/settings.tsx @@ -0,0 +1,32 @@ +import { VDomModel, VDomRenderer } from '@jupyterlab/apputils'; + +import * as React from 'react'; + +import { IMarkdownIt } from './tokens'; + +/** + * A configuration/documentation UI for markdown-it plugins + */ +export class MarkdownItSettings extends VDomRenderer { + protected render() { + return
; + } +} + +export namespace MarkdownItSettings { + /** + * A model for managing markdown-it plugin settings + */ + export class Model extends VDomModel { + _manager: IMarkdownIt; + + get manager() { + return this._manager; + } + + set manager(manager) { + this._manager = manager; + this.stateChanged.emit(void 0); + } + } +} diff --git a/src/tokens.ts b/src/tokens.ts index 499862a..83ad5d9 100644 --- a/src/tokens.ts +++ b/src/tokens.ts @@ -26,6 +26,14 @@ export interface IMarkdownIt { ): Promise; } +/** + * A namespace for lumino command IDs + */ +export namespace CommandIDs { + export const showSettings = 'markdown-it:show-settings'; + export const toggleRenderer = 'markdown-it:toggle-renderer'; +} + /** * A namespace for plugin-related types and interfaces */ diff --git a/tsconfig.json b/tsconfig.json index 411ca64..4d9b330 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -3,6 +3,7 @@ "allowSyntheticDefaultImports": true, "composite": true, "declaration": true, + "jsx": "react", "lib": ["es2015", "dom"], "module": "esnext", "moduleResolution": "node", From bb3a9ab61198317e10053b391d5b757c3d093e26 Mon Sep 17 00:00:00 2001 From: Nicholas Bollweg Date: Mon, 2 Nov 2020 15:23:35 -0500 Subject: [PATCH 18/31] basic style, table --- package.json | 2 +- src/manager.ts | 19 ++++++++++++ src/plugin.ts | 2 ++ src/settings.tsx | 78 ++++++++++++++++++++++++++++++++++++++++++++++-- src/tokens.ts | 2 ++ style/index.css | 11 +++++++ 6 files changed, 110 insertions(+), 4 deletions(-) create mode 100644 style/index.css diff --git a/package.json b/package.json index 9342ab2..b0dd892 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,7 @@ "license": "BSD-3-Clause", "author": "Angus Hollands", "files": [ - "{src,lib,schema}/**/*.{ts,tsx,eot,gif,html,jpg,js,map,json,png,svg,woff2,ttf,css}", + "{src,lib,style,schema}/**/*.{ts,tsx,eot,gif,html,jpg,js,map,json,png,svg,woff2,ttf,css}", "LICENSE.txt" ], "main": "lib/index.js", diff --git a/src/manager.ts b/src/manager.ts index 34a305f..c8e18a9 100644 --- a/src/manager.ts +++ b/src/manager.ts @@ -1,3 +1,5 @@ +import { Signal } from '@lumino/signaling'; + import { IRenderMime, markdownRendererFactory } from '@jupyterlab/rendermime'; import { ISettingRegistry } from '@jupyterlab/settingregistry'; import { CodeMirrorEditor, Mode } from '@jupyterlab/codemirror'; @@ -16,7 +18,13 @@ const ORIGINAL_RENDERER = markdownRendererFactory.createRenderer; * An implementation of a source of markdown renderers with markdown-it and plugins */ export class MarkdownItManager implements IMarkdownIt { + settingsChanged = new Signal(this); + + /** + * A list of plugin ids disabled by user settings + */ protected userDisabledPlugins: string[] = []; + /** * Whether to use the markdown-it renderer: if installed, will use unless configured by user */ @@ -96,6 +104,9 @@ export class MarkdownItManager implements IMarkdownIt { (this.settings?.composite[ 'plugin-options' ] as IMarkdownIt.IAllPluginOptions) || {}; + + // re-brodcast settings changes + this.settingsChanged.emit(void 0); } /** @@ -105,6 +116,14 @@ export class MarkdownItManager implements IMarkdownIt { this._pluginProviders.set(provider.id, provider); } + getPluginProvider(id: string) { + return this._pluginProviders.get(id); + } + + get pluginProviderIds() { + return [...this._pluginProviders.keys()]; + } + /** * Remove a provider by name */ diff --git a/src/plugin.ts b/src/plugin.ts index b0e07a1..5ae191f 100644 --- a/src/plugin.ts +++ b/src/plugin.ts @@ -10,6 +10,8 @@ import { MarkdownItSettings } from './settings'; import { BUILTINS } from './builtins'; +import '../style/index.css'; + /** * The main plugin which overloads default markdown rendering by `marked` */ diff --git a/src/settings.tsx b/src/settings.tsx index d8ab874..862053f 100644 --- a/src/settings.tsx +++ b/src/settings.tsx @@ -2,14 +2,83 @@ import { VDomModel, VDomRenderer } from '@jupyterlab/apputils'; import * as React from 'react'; -import { IMarkdownIt } from './tokens'; +import { MarkdownItManager } from './manager'; + +const SETTINGS_CLASS = 'jp-MarkdownItSettings'; /** * A configuration/documentation UI for markdown-it plugins */ export class MarkdownItSettings extends VDomRenderer { + constructor(model: MarkdownItSettings.Model) { + super(model); + this.addClass(SETTINGS_CLASS); + this.addClass('jp-RenderedHTMLCommon'); + } + /** + * Render the settings form + */ protected render() { - return
; + const manager = this.model?.manager; + if (manager == null) { + return
; + } + + const { pluginProviderIds } = manager; + + return ( +
+ + {this.renderHeader()} + + {pluginProviderIds.map(this.renderPluginProvider, this)} + +
+
+ ); + } + + /** + * Render the table header + */ + protected renderHeader() { + return ( + + ID + Plugin + Description + Enabled + + ); + } + + /** + * Render a single plugin provider + */ + protected renderPluginProvider(id: string) { + const provider = this.model.manager.getPluginProvider(id); + if (!provider) { + return ( + + + {id} + + + ); + } + + return ( + + + {id} + + {provider.title} + {provider.description} + + + + + ); } } @@ -18,7 +87,7 @@ export namespace MarkdownItSettings { * A model for managing markdown-it plugin settings */ export class Model extends VDomModel { - _manager: IMarkdownIt; + _manager: MarkdownItManager; get manager() { return this._manager; @@ -26,6 +95,9 @@ export namespace MarkdownItSettings { set manager(manager) { this._manager = manager; + if (manager) { + manager.settingsChanged.connect(() => this.stateChanged.emit(void 0)); + } this.stateChanged.emit(void 0); } } diff --git a/src/tokens.ts b/src/tokens.ts index 83ad5d9..c744653 100644 --- a/src/tokens.ts +++ b/src/tokens.ts @@ -20,10 +20,12 @@ export const IMarkdownIt = new Token(PACKAGE_NS); export interface IMarkdownIt { addPluginProvider(provider: IMarkdownIt.IPluginProvider): void; removePluginProvider(id: string): void; + getPluginProvider(id: string): IMarkdownIt.IPluginProvider | null; getMarkdownIt( widget: RenderedMarkdown, options?: MarkdownIt.Options ): Promise; + pluginProviderIds: string[]; } /** diff --git a/style/index.css b/style/index.css new file mode 100644 index 0000000..c79697d --- /dev/null +++ b/style/index.css @@ -0,0 +1,11 @@ +.jp-MarkdownItSettings table { + width: 100%; +} + +.jp-MarkdownItSettings.jp-RenderedHTMLCommon thead th { + text-align: center; +} + +.jp-MarkdownItSettings.jp-RenderedHTMLCommon tbody th { + text-align: left; +} From 9d7b9e49484a0225395523eed8818b1fe4b47b55 Mon Sep 17 00:00:00 2001 From: Nicholas Bollweg Date: Mon, 2 Nov 2020 22:44:01 -0500 Subject: [PATCH 19/31] add plugin disabling --- src/plugin.ts | 2 +- src/settings.tsx | 56 +++++++++++++++++++++++++++++++++++++++++++++--- style/index.css | 6 +++++- 3 files changed, 59 insertions(+), 5 deletions(-) diff --git a/src/plugin.ts b/src/plugin.ts index 5ae191f..ac78615 100644 --- a/src/plugin.ts +++ b/src/plugin.ts @@ -32,7 +32,7 @@ const core: JupyterFrontEndPlugin = { // commands app.commands.addCommand(CommandIDs.showSettings, { - label: 'Markdown Extensions', + label: 'Markdown Extensions...', execute: (args) => { const model = new MarkdownItSettings.Model(); model.manager = manager; diff --git a/src/settings.tsx b/src/settings.tsx index 862053f..017fe2b 100644 --- a/src/settings.tsx +++ b/src/settings.tsx @@ -15,6 +15,12 @@ export class MarkdownItSettings extends VDomRenderer { this.addClass(SETTINGS_CLASS); this.addClass('jp-RenderedHTMLCommon'); } + + dispose() { + super.dispose(); + this.model.dispose(); + } + /** * Render the settings form */ @@ -56,7 +62,8 @@ export class MarkdownItSettings extends VDomRenderer { * Render a single plugin provider */ protected renderPluginProvider(id: string) { - const provider = this.model.manager.getPluginProvider(id); + const m = this.model; + const provider = m.manager.getPluginProvider(id); if (!provider) { return ( @@ -75,11 +82,23 @@ export class MarkdownItSettings extends VDomRenderer { {provider.title} {provider.description} - + ); } + + protected onPluginEnabledChanged = ( + evt: React.ChangeEvent + ) => { + const { value, checked } = evt.currentTarget; + this.model.setPluginEnabled(value, checked); + }; } export namespace MarkdownItSettings { @@ -88,6 +107,15 @@ export namespace MarkdownItSettings { */ export class Model extends VDomModel { _manager: MarkdownItManager; + disabledPlugins: string[] = []; + + dispose() { + super.dispose(); + if (this._manager) { + this._manager.settingsChanged.disconnect(this.onSettingsChanged, this); + this._manager = null; + } + } get manager() { return this._manager; @@ -96,9 +124,31 @@ export namespace MarkdownItSettings { set manager(manager) { this._manager = manager; if (manager) { - manager.settingsChanged.connect(() => this.stateChanged.emit(void 0)); + manager.settingsChanged.connect(this.onSettingsChanged, this); } this.stateChanged.emit(void 0); } + + setPluginEnabled(id: string, enabled: boolean) { + let disabledPlugins = this.disabledPlugins.slice(); + const idx = disabledPlugins.indexOf(id); + if (enabled) { + disabledPlugins.splice(idx); + } else { + disabledPlugins.push(id); + } + if (disabledPlugins.length) { + this.manager.settings.set('disabled-plugins', disabledPlugins); + } else { + this.manager.settings.remove('disabled-plugins'); + } + } + + onSettingsChanged() { + this.disabledPlugins = (this.manager.settings.composite[ + 'disabled-plugins' + ] || []) as string[]; + this.stateChanged.emit(void 0); + } } } diff --git a/style/index.css b/style/index.css index c79697d..7a417c2 100644 --- a/style/index.css +++ b/style/index.css @@ -6,6 +6,10 @@ text-align: center; } -.jp-MarkdownItSettings.jp-RenderedHTMLCommon tbody th { +.jp-MarkdownItSettings.jp-RenderedHTMLCommon tbody th, +.jp-MarkdownItSettings.jp-RenderedHTMLCommon tbody td { text-align: left; } +.jp-MarkdownItSettings.jp-RenderedHTMLCommon tbody td:last-child { + text-align: center; +} From cd98b744def38e5fad25305e70d7ff19ca5c3ef5 Mon Sep 17 00:00:00 2001 From: Nicholas Bollweg Date: Tue, 3 Nov 2020 00:33:49 -0500 Subject: [PATCH 20/31] use article/section instead of table --- src/settings.tsx | 191 +++++++++++++++++++++++++++++++++++------------ style/index.css | 71 +++++++++++++++--- 2 files changed, 205 insertions(+), 57 deletions(-) diff --git a/src/settings.tsx b/src/settings.tsx index 017fe2b..51d17c3 100644 --- a/src/settings.tsx +++ b/src/settings.tsx @@ -3,8 +3,13 @@ import { VDomModel, VDomRenderer } from '@jupyterlab/apputils'; import * as React from 'react'; import { MarkdownItManager } from './manager'; +import { IMarkdownIt } from './tokens'; const SETTINGS_CLASS = 'jp-MarkdownItSettings'; +const DOCS_CLASS = 'jp-MarkdownItSettings-Docs'; +const EXAMPLES_CLASS = 'jp-MarkdownItSettings-Examples'; + +const ID_STEM = 'id-jp-mu-'; /** * A configuration/documentation UI for markdown-it plugins @@ -25,71 +30,120 @@ export class MarkdownItSettings extends VDomRenderer { * Render the settings form */ protected render() { - const manager = this.model?.manager; + const m = this.model; + const manager = m?.manager; if (manager == null) { return
; } - const { pluginProviderIds } = manager; + const { providers } = m; return (
- - {this.renderHeader()} - - {pluginProviderIds.map(this.renderPluginProvider, this)} - -
+
+
    +
  • + Global +
  • +
  • + Plugins +
      {providers.map(this.renderPluginNav, this)}
    +
  • +
+
+
+
+

Global

+ +
+ Enable to use the markdown-it Markdown renderer and + extensions for any new renderers. +
+
+

Extensions

+ {providers.map(this.renderPluginProvider, this)} +
); } /** - * Render the table header + * Render a single plugin provider nav link */ - protected renderHeader() { + protected renderPluginNav(provider: IMarkdownIt.IPluginProvider) { return ( - - ID - Plugin - Description - Enabled - +
  • + {provider.title} +
  • ); } /** - * Render a single plugin provider + * Render a single plugin provider section */ - protected renderPluginProvider(id: string) { + protected renderPluginProvider(provider: IMarkdownIt.IPluginProvider) { const m = this.model; - const provider = m.manager.getPluginProvider(id); - if (!provider) { - return ( - - - {id} - - - ); + + const docs = []; + const examples = []; + + for (const label in provider.documentationUrls) { + docs.push(this.renderDoc(label, provider.documentationUrls[label])); + } + + for (const label in provider.examples || {}) { + examples.push(this.renderExample(label, provider.examples[label])); } return ( - - - {id} - - {provider.title} - {provider.description} - - - - +
    +
    +

    + +

    +
      {docs}
    +
    +
    {provider.description}
    +
      {examples}
    +
    + ); + } + + protected renderDoc(label: string, url: string) { + return ( +
  • + + {label} + +
  • + ); + } + + protected renderExample(label: string, code: string) { + return ( +
    +

    + +

    +
    +          {code}
    +        
    +
    ); } @@ -99,6 +153,10 @@ export class MarkdownItSettings extends VDomRenderer { const { value, checked } = evt.currentTarget; this.model.setPluginEnabled(value, checked); }; + + protected onEnabledChanged = (evt: React.ChangeEvent) => { + this.model.enabled = evt.currentTarget.checked; + }; } export namespace MarkdownItSettings { @@ -107,7 +165,9 @@ export namespace MarkdownItSettings { */ export class Model extends VDomModel { _manager: MarkdownItManager; - disabledPlugins: string[] = []; + _disabledPlugins: string[] = []; + _enabled: boolean = true; + _providers: IMarkdownIt.IPluginProvider[] = []; dispose() { super.dispose(); @@ -125,12 +185,28 @@ export namespace MarkdownItSettings { this._manager = manager; if (manager) { manager.settingsChanged.connect(this.onSettingsChanged, this); + this.onSettingsChanged(); } - this.stateChanged.emit(void 0); + } + + get disabledPlugins() { + return this._disabledPlugins; + } + + get enabled() { + return this._enabled; + } + + set enabled(enabled) { + this._manager.settings.set('enabled', enabled); + } + + get providers() { + return this._providers; } setPluginEnabled(id: string, enabled: boolean) { - let disabledPlugins = this.disabledPlugins.slice(); + let disabledPlugins = this._disabledPlugins.slice(); const idx = disabledPlugins.indexOf(id); if (enabled) { disabledPlugins.splice(idx); @@ -145,10 +221,29 @@ export namespace MarkdownItSettings { } onSettingsChanged() { - this.disabledPlugins = (this.manager.settings.composite[ - 'disabled-plugins' - ] || []) as string[]; + const { composite } = this.manager.settings; + + if (composite != null) { + this._disabledPlugins = (composite['disabled-plugins'] || + []) as string[]; + + this._enabled = composite['enabled'] as boolean; + this._providers = this.manager.pluginProviderIds.map( + this.manager.getPluginProvider, + this.manager + ); + this._providers.sort(this.sortByTitle); + console.log(this._providers); + } + this.stateChanged.emit(void 0); } + + sortByTitle( + a: IMarkdownIt.IPluginProvider, + b: IMarkdownIt.IPluginProvider + ) { + return a.title.localeCompare(b.title); + } } } diff --git a/style/index.css b/style/index.css index 7a417c2..c18f57c 100644 --- a/style/index.css +++ b/style/index.css @@ -1,15 +1,68 @@ -.jp-MarkdownItSettings table { - width: 100%; +.jp-MarkdownItSettings { + overflow-y: auto; + display: flex; + flex-direction: row; + padding: 0; + margin: 0; } -.jp-MarkdownItSettings.jp-RenderedHTMLCommon thead th { - text-align: center; +.jp-MarkdownItSettings div { + display: flex; + flex-direction: row; + flex: 1; } -.jp-MarkdownItSettings.jp-RenderedHTMLCommon tbody th, -.jp-MarkdownItSettings.jp-RenderedHTMLCommon tbody td { - text-align: left; +.jp-MarkdownItSettings header { + flex: 0; + min-width: fit-content; + white-space: nowrap; + padding-right: var(--jp-content-font-size3); + overflow-y: auto; + padding: var(--jp-content-font-size3); } -.jp-MarkdownItSettings.jp-RenderedHTMLCommon tbody td:last-child { - text-align: center; + +.jp-MarkdownItSettings header ul { + margin: 0; + padding: 0; + list-style: none; +} + +.jp-MarkdownItSettings article { + flex: 1; + overflow-y: auto; + padding: var(--jp-content-font-size3); +} + +.jp-MarkdownItSettings > div { + display: flex; + flex-direction: row; + border-bottom: solid var(--jp-border-width) var(--jp-border-color2); +} + +.jp-MarkdownItSettings h4 { + flex: 1; + font-size: var(--jp-ui-font-size0); + font-weight: 600; + letter-spacing: 1px; + text-transform: uppercase; +} + +.jp-MarkdownItSettings h4 label { + cursor: pointer; +} + +.jp-MarkdownItSettings-Docs { + flex: 0; + display: flex; + flex-direction: row; + list-style: none; + padding: 0; + margin: 0; +} + +.jp-MarkdownItSettings-Docs li { + flex: 1; + white-space: nowrap; + padding-left: var(--jp-content-font-size3); + list-style: none; } From 32fa80e3df93178483a9866a14786a1fcb553439 Mon Sep 17 00:00:00 2001 From: Nicholas Bollweg Date: Tue, 3 Nov 2020 01:01:28 -0500 Subject: [PATCH 21/31] some more style tweaks --- src/settings.tsx | 20 +++++++++++++++----- style/index.css | 31 +++++++++++++++++++++++-------- 2 files changed, 38 insertions(+), 13 deletions(-) diff --git a/src/settings.tsx b/src/settings.tsx index 51d17c3..2f2c7a9 100644 --- a/src/settings.tsx +++ b/src/settings.tsx @@ -8,8 +8,9 @@ import { IMarkdownIt } from './tokens'; const SETTINGS_CLASS = 'jp-MarkdownItSettings'; const DOCS_CLASS = 'jp-MarkdownItSettings-Docs'; const EXAMPLES_CLASS = 'jp-MarkdownItSettings-Examples'; +const DISABLED_CLASS = 'jp-mu-mod-disabled'; -const ID_STEM = 'id-jp-mu-'; +const ID_STEM = 'id-jp-mu'; /** * A configuration/documentation UI for markdown-it plugins @@ -78,8 +79,12 @@ export class MarkdownItSettings extends VDomRenderer { * Render a single plugin provider nav link */ protected renderPluginNav(provider: IMarkdownIt.IPluginProvider) { + const m = this.model; + const enabled = m.disabledPlugins.indexOf(provider.id) === -1; + const navClass = enabled && m.enabled ? '' : DISABLED_CLASS; + return ( -
  • +
  • {provider.title}
  • ); @@ -90,6 +95,8 @@ export class MarkdownItSettings extends VDomRenderer { */ protected renderPluginProvider(provider: IMarkdownIt.IPluginProvider) { const m = this.model; + const enabled = m.disabledPlugins.indexOf(provider.id) === -1; + const sectionClass = enabled && m.enabled ? '' : DISABLED_CLASS; const docs = []; const examples = []; @@ -103,14 +110,18 @@ export class MarkdownItSettings extends VDomRenderer { } return ( -
    +

    @@ -85,7 +87,7 @@ export class MarkdownItSettings extends VDomRenderer { return (
  • - {provider.title} + {provider.title}
  • ); } @@ -112,7 +114,7 @@ export class MarkdownItSettings extends VDomRenderer { return (
    diff --git a/style/index.css b/style/index.css index d8ff3ab..734e5f8 100644 --- a/style/index.css +++ b/style/index.css @@ -32,6 +32,7 @@ overflow-y: auto; padding: var(--jp-content-font-size2); padding-left: 0; + border-right: solid var(--jp-border-width) var(--jp-border-color2); } .jp-MarkdownItSettings header ul { From cce39bde1faa7b17b9ef7af457d86432bab5b7f2 Mon Sep 17 00:00:00 2001 From: Nicholas Bollweg Date: Tue, 3 Nov 2020 01:14:59 -0500 Subject: [PATCH 23/31] fix rel --- src/settings.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/settings.tsx b/src/settings.tsx index 09dc4a0..48e2e4e 100644 --- a/src/settings.tsx +++ b/src/settings.tsx @@ -140,7 +140,7 @@ export class MarkdownItSettings extends VDomRenderer { protected renderDoc(label: string, url: string) { return (
  • - + {label}
  • From 96bff91576bf95126c0cbf0ac2d96e265ad43da7 Mon Sep 17 00:00:00 2001 From: Nicholas Bollweg Date: Tue, 3 Nov 2020 09:55:32 -0500 Subject: [PATCH 24/31] provide advanced settings escape hatch --- schema/core.json | 2 +- src/plugin.ts | 32 ++++++++++++++++++++++++-------- src/settings.tsx | 17 +++++++++++++++++ 3 files changed, 42 insertions(+), 9 deletions(-) diff --git a/schema/core.json b/schema/core.json index c436996..99d5ac8 100644 --- a/schema/core.json +++ b/schema/core.json @@ -1,5 +1,5 @@ { - "jupyter.lab.setting-icon": "ui-components:markdown", + "jupyter.lab.setting-icon": "jupyterlab-markup:core", "jupyter.lab.setting-icon-label": "Markdown Extensions", "title": "Markdown Extensions", "description": "Markdown extension settings", diff --git a/src/plugin.ts b/src/plugin.ts index ac78615..2d34d7f 100644 --- a/src/plugin.ts +++ b/src/plugin.ts @@ -1,7 +1,7 @@ import { JupyterFrontEndPlugin } from '@jupyterlab/application'; import { ISettingRegistry } from '@jupyterlab/settingregistry'; import { MainAreaWidget, ICommandPalette } from '@jupyterlab/apputils'; -import { markdownIcon } from '@jupyterlab/ui-components'; +import { markdownIcon, LabIcon } from '@jupyterlab/ui-components'; import { CommandIDs, IMarkdownIt, PACKAGE_NS } from './tokens'; import { MarkdownItManager } from './manager'; @@ -12,6 +12,13 @@ import { BUILTINS } from './builtins'; import '../style/index.css'; +const ICON_ID = `jupyterlab-markup:core`; + +const markupIcon = new LabIcon({ + name: ICON_ID, + svgstr: markdownIcon.svgstr.replace('jp-icon-contrast0', 'jp-icon-contrast1'), +}); + /** * The main plugin which overloads default markdown rendering by `marked` */ @@ -30,17 +37,26 @@ const core: JupyterFrontEndPlugin = { .then((settings) => (manager.settings = settings)) .catch(console.warn); + let settingsMain: MainAreaWidget; + // commands app.commands.addCommand(CommandIDs.showSettings, { label: 'Markdown Extensions...', execute: (args) => { - const model = new MarkdownItSettings.Model(); - model.manager = manager; - const content = new MarkdownItSettings(model); - const main = new MainAreaWidget({ content }); - main.title.label = 'Markdown Extensions'; - main.title.icon = markdownIcon; - app.shell.add(main, 'main'); + if (settingsMain == null) { + const model = new MarkdownItSettings.Model(); + model.advancedRequested.connect(() => + app.commands.execute('settingeditor:open') + ); + model.manager = manager; + const content = new MarkdownItSettings(model); + settingsMain = new MainAreaWidget({ content }); + settingsMain.title.label = 'Markdown Extensions'; + settingsMain.title.icon = markupIcon; + settingsMain.disposed.connect(() => (settingsMain = null)); + } + app.shell.add(settingsMain, 'main'); + app.shell.activateById(settingsMain.id); }, }); diff --git a/src/settings.tsx b/src/settings.tsx index 48e2e4e..0fb7700 100644 --- a/src/settings.tsx +++ b/src/settings.tsx @@ -1,3 +1,5 @@ +import { Signal } from '@lumino/signaling'; + import { VDomModel, VDomRenderer } from '@jupyterlab/apputils'; import * as React from 'react'; @@ -35,6 +37,11 @@ export class MarkdownItSettings extends VDomRenderer { protected render() { const m = this.model; const manager = m?.manager; + const advancedLink = ( + + Open in Advanced Settings... + + ); if (manager == null) { return
    ; } @@ -52,6 +59,7 @@ export class MarkdownItSettings extends VDomRenderer { Markdown-it Plugins
      {providers.map(this.renderPluginNav, this)}
    +
  • {advancedLink}
  • @@ -71,6 +79,10 @@ export class MarkdownItSettings extends VDomRenderer {

    Markdown-it Plugins

    +
    + Extensions can be individually enabled or disabled with the + checkboxes below. See {advancedLink} for more fine-grained control. +
    {providers.map(this.renderPluginProvider, this)}
    @@ -170,6 +182,10 @@ export class MarkdownItSettings extends VDomRenderer { protected onEnabledChanged = (evt: React.ChangeEvent) => { this.model.enabled = evt.currentTarget.checked; }; + + onAdvancedClicked = () => { + this.model.advancedRequested.emit(void 0); + }; } export namespace MarkdownItSettings { @@ -177,6 +193,7 @@ export namespace MarkdownItSettings { * A model for managing markdown-it plugin settings */ export class Model extends VDomModel { + advancedRequested = new Signal(this); _manager: MarkdownItManager; _disabledPlugins: string[] = []; _enabled: boolean = true; From dfb18aa3cbeca5cc632780df7303ea744febdb4c Mon Sep 17 00:00:00 2001 From: Nicholas Bollweg Date: Tue, 3 Nov 2020 10:02:13 -0500 Subject: [PATCH 25/31] add default value to markdownit options --- schema/core.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/schema/core.json b/schema/core.json index 99d5ac8..4d3dd38 100644 --- a/schema/core.json +++ b/schema/core.json @@ -23,7 +23,8 @@ "markdown-it-options": { "title": "MarkdownIt options", "type": "object", - "description": "Advanced. JSON-compatible override options to MarkdownIt. https://markdown-it.github.io/markdown-it/#MarkdownIt.new" + "description": "Advanced. JSON-compatible override options to MarkdownIt. https://markdown-it.github.io/markdown-it/#MarkdownIt.new", + "default": {} }, "plugin-options": { "title": "markdown-it plugin options. See individual plugins for options.", From ed3e4c60e4b0c51d35a58cf465fb19dd8dffe9b7 Mon Sep 17 00:00:00 2001 From: Nicholas Bollweg Date: Wed, 4 Nov 2020 10:54:52 -0500 Subject: [PATCH 26/31] remove redundant link replacer, add main menu entries, some docs --- README.md | 28 +- package.json | 2 +- src/builtins/index.ts | 10 +- src/builtins/replace-link.ts | 33 - src/manager.ts | 33 +- src/plugin.ts | 66 +- src/settings.tsx | 6 +- src/tokens.ts | 5 + src/typings/markdown-it-replace-link.d.ts | 10 - style/index.css | 2 + yarn.lock | 1384 ++++++++++++--------- 11 files changed, 887 insertions(+), 692 deletions(-) delete mode 100644 src/builtins/replace-link.ts delete mode 100644 src/typings/markdown-it-replace-link.d.ts diff --git a/README.md b/README.md index 03d5663..3d18f6b 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,6 @@ Adds additional rendering support to markdown in JupyterLab by using [markdown-i - [markdown-it-deflist](https://github.com/markdown-it/markdown-it-deflist) - [markdown-it-diagrams](https://github.com/agoose77/markdown-it-diagrams) - [markdown-it-footnote](https://github.com/markdown-it/markdown-it-footnote) -- [markdown-it-replace-link](https://github.com/martinheidegger/markdown-it-replace-link) - [markdown-it-task-lists](https://github.com/revin/markdown-it-task-lists) ![Full example rendering vs markup.](https://i.imgur.com/OL9oGcq.png) ![svgbob rendering](https://i.imgur.com/RbDioU8.gif) @@ -16,6 +15,13 @@ Adds additional rendering support to markdown in JupyterLab by using [markdown-i - JupyterLab 2 +## Limitations + +Most custom markdown extensions not covered by the default `marked`-based +renderer (e.g. task lists, header anchors) will not work for others who do not +have this extension installed. Markdown is very lenient, so the no data should +be _lost_, but it might look strange. + ## Installation Install extension: @@ -38,6 +44,26 @@ If the serverextension is missing, try... jupyter serverextension enable jupyterlab_markup ``` +## Usage + +After installing the extension (and restarting/reloading JupyterLab), all plugins +will be enabled by default. + +All plugins (and `markdown-it` itself) can be disabled via the _Command Palette_ or +under the [_Settings_ menu](https://jupyterlab.readthedocs.io/en/stable/user/interface.html#menu-bar) +with _Use Markdown Extensions_. This will not affect existing renderers, so open +documents will need to be reopened. + +Individual plugins can be previewed, enabled/disabled from the palette or menu under +_Markdown Extension Settings..._. This view also provides links and examples of +any features added by the extensions. + +### Advanced + +A number of settings can be configured through the JupyterLab +_Advanced Settings Editor_, including plugin and markdownit options. As with the +above limitations, heavy customization might make your documents look strange. + ## Contributing Please see the [contributor guide](./CONTRIBUTING.md)! diff --git a/package.json b/package.json index b0dd892..f1f09a1 100644 --- a/package.json +++ b/package.json @@ -34,6 +34,7 @@ "@jupyterlab/application": "^2.0.2", "@jupyterlab/codemirror": "^2.0.2", "@jupyterlab/fileeditor": "^2.0.2", + "@jupyterlab/mainmenu": "^2.0.2", "@jupyterlab/markdownviewer": "^2.0.2", "@jupyterlab/rendermime": "^2.0.2", "@jupyterlab/rendermime-interfaces": "^2.0.1", @@ -44,7 +45,6 @@ "markdown-it-deflist": "^2.0.3", "markdown-it-diagrams": "^0.1.2", "markdown-it-footnote": "^3.0.2", - "markdown-it-replace-link": "^1.1.0", "markdown-it-task-lists": "^2.1.1" }, "devDependencies": { diff --git a/src/builtins/index.ts b/src/builtins/index.ts index 17bd1a6..c65c37a 100644 --- a/src/builtins/index.ts +++ b/src/builtins/index.ts @@ -2,17 +2,9 @@ import { anchor } from './anchor'; import { deflist } from './deflist'; import { diagrams } from './diagrams'; import { footnote } from './footnote'; -import { replaceLink } from './replace-link'; import { taskLists } from './task-lists'; /** * Builtin plugins provided by this labextension */ -export const BUILTINS = [ - anchor, - deflist, - diagrams, - footnote, - replaceLink, - taskLists, -]; +export const BUILTINS = [anchor, deflist, diagrams, footnote, taskLists]; diff --git a/src/builtins/replace-link.ts b/src/builtins/replace-link.ts deleted file mode 100644 index afd7e4f..0000000 --- a/src/builtins/replace-link.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { PACKAGE_NS, simpleMarkdownItPlugin } from '..'; -import { PathExt } from '@jupyterlab/coreutils'; - -/** - * Replaces relative links with the Jupyter files URLs - */ -export const replaceLink = simpleMarkdownItPlugin(PACKAGE_NS, { - id: 'markdown-it-replace-link', - title: 'Replace Links', - description: 'Replaces relative links to local Jupyter URLs', - documentationUrls: { - Plugin: 'markdown-it-replace-link', - }, - options: async (widget) => { - const { resolver } = widget; - return { - replaceLink: function (link: string, env: any) { - // hacky, but this can't be a promise - if (!resolver.isLocal(link)) { - return link; - } - const cwd = encodeURI(PathExt.dirname((resolver as any)._parent.path)); - return PathExt.resolve(cwd, link); - }, - }; - }, - plugin: async () => { - const replaceLinkPlugin = await import( - /* webpackChunkName: "markdown-it-replace-link" */ 'markdown-it-replace-link' - ); - return [replaceLinkPlugin.default]; - }, -}); diff --git a/src/manager.ts b/src/manager.ts index c8e18a9..cfe7440 100644 --- a/src/manager.ts +++ b/src/manager.ts @@ -57,15 +57,6 @@ export class MarkdownItManager implements IMarkdownIt { markdownRendererFactory.createRenderer = this.createRenderer; } - /** - * Create a new renderer, either with markdown-it or the original implementation - */ - protected createRenderer = (options: IRenderMime.IRendererOptions) => { - return this.useMarkdownIt - ? new RenderedMarkdown(options) - : ORIGINAL_RENDERER(options); - }; - /** * Update the settings, and handle changes. */ @@ -109,6 +100,21 @@ export class MarkdownItManager implements IMarkdownIt { this.settingsChanged.emit(void 0); } + get enabled() { + const enabled = this.settings?.composite; + return !!(enabled == null ? true : enabled); + } + + set enabled(enabled) { + if (this.settings == null) { + console.warn( + `Can't set enabled status of markdown extensions without settings` + ); + return; + } + this.settings.set('enabled', enabled); + } + /** * Add a provider for a plugin which can be resolved lazily */ @@ -131,6 +137,15 @@ export class MarkdownItManager implements IMarkdownIt { this._pluginProviders.delete(name); } + /** + * Create a new renderer, either with markdown-it or the original implementation + */ + protected createRenderer = (options: IRenderMime.IRendererOptions) => { + return this.useMarkdownIt + ? new RenderedMarkdown(options) + : ORIGINAL_RENDERER(options); + }; + /** * Get a MarkdownIt instance */ diff --git a/src/plugin.ts b/src/plugin.ts index 2d34d7f..28f591f 100644 --- a/src/plugin.ts +++ b/src/plugin.ts @@ -3,7 +3,15 @@ import { ISettingRegistry } from '@jupyterlab/settingregistry'; import { MainAreaWidget, ICommandPalette } from '@jupyterlab/apputils'; import { markdownIcon, LabIcon } from '@jupyterlab/ui-components'; -import { CommandIDs, IMarkdownIt, PACKAGE_NS } from './tokens'; +// optional +import { IMainMenu } from '@jupyterlab/mainmenu'; + +import { + CommandIDs, + IMarkdownIt, + PACKAGE_NS, + COMMAND_CATEGORY, +} from './tokens'; import { MarkdownItManager } from './manager'; import { RenderedMarkdown } from './widgets'; import { MarkdownItSettings } from './settings'; @@ -27,7 +35,14 @@ const core: JupyterFrontEndPlugin = { autoStart: true, provides: IMarkdownIt, requires: [ISettingRegistry, ICommandPalette], - activate: (app, settings: ISettingRegistry, palette: ICommandPalette) => { + optional: [IMainMenu], + activate: ( + app, + settings: ISettingRegistry, + palette: ICommandPalette, + menu?: IMainMenu + ) => { + const { commands, shell } = app; const manager = new MarkdownItManager(); // set the static manager RenderedMarkdown.markdownItManager = manager; @@ -40,13 +55,13 @@ const core: JupyterFrontEndPlugin = { let settingsMain: MainAreaWidget; // commands - app.commands.addCommand(CommandIDs.showSettings, { - label: 'Markdown Extensions...', + commands.addCommand(CommandIDs.showSettings, { + label: 'Markdown Extension Settings...', execute: (args) => { if (settingsMain == null) { const model = new MarkdownItSettings.Model(); model.advancedRequested.connect(() => - app.commands.execute('settingeditor:open') + commands.execute('settingeditor:open') ); model.manager = manager; const content = new MarkdownItSettings(model); @@ -55,16 +70,51 @@ const core: JupyterFrontEndPlugin = { settingsMain.title.icon = markupIcon; settingsMain.disposed.connect(() => (settingsMain = null)); } - app.shell.add(settingsMain, 'main'); - app.shell.activateById(settingsMain.id); + shell.add(settingsMain, 'main'); + shell.activateById(settingsMain.id); + }, + }); + + // cached enabled setting + let enabled = true; + + manager.settingsChanged.connect(() => { + const { composite } = manager.settings; + if (composite != null) { + enabled = !!composite.enabled; + } + }); + + commands.addCommand(CommandIDs.toggleRenderer, { + label: (args) => `Use Markdown Extensions`, + caption: 'Reopen documents to see changes', + isToggled: () => enabled, + isEnabled: () => manager.settings != null, + execute: async (args) => { + manager.enabled = !!(args?.enabled == null ? !enabled : args.enabled); }, }); palette.addItem({ command: CommandIDs.showSettings, - category: 'Markdown', + category: COMMAND_CATEGORY, }); + palette.addItem({ + command: CommandIDs.toggleRenderer, + category: COMMAND_CATEGORY, + }); + + if (menu) { + menu.settingsMenu.addGroup( + [ + { command: CommandIDs.toggleRenderer }, + { command: CommandIDs.showSettings }, + ], + 100 + ); + } + return manager; }, }; diff --git a/src/settings.tsx b/src/settings.tsx index 0fb7700..eb798cc 100644 --- a/src/settings.tsx +++ b/src/settings.tsx @@ -27,8 +27,8 @@ export class MarkdownItSettings extends VDomRenderer { } dispose() { + this.model?.dispose(); super.dispose(); - this.model.dispose(); } /** @@ -163,7 +163,9 @@ export class MarkdownItSettings extends VDomRenderer { return (

    - +

               {code}
    diff --git a/src/tokens.ts b/src/tokens.ts
    index c744653..e3b7be5 100644
    --- a/src/tokens.ts
    +++ b/src/tokens.ts
    @@ -7,6 +7,11 @@ import { RenderedMarkdown } from './widgets';
      */
     export const PACKAGE_NS = '@agoose77/jupyterlab-markup';
     
    +/**
    + * The category for markdown extension commands
    + */
    +export const COMMAND_CATEGORY = 'Markdown Extensions';
    +
     /* tslint:disable */
     /**
      * The MarkdownIt manager token.
    diff --git a/src/typings/markdown-it-replace-link.d.ts b/src/typings/markdown-it-replace-link.d.ts
    deleted file mode 100644
    index 8b74c4a..0000000
    --- a/src/typings/markdown-it-replace-link.d.ts
    +++ /dev/null
    @@ -1,10 +0,0 @@
    -declare module 'markdown-it-replace-link' {
    -  import MarkdownIt = require('markdown-it');
    -
    -  namespace markdownItDeflist {
    -    function replacelink_plugin(md: MarkdownIt, opts: any): void;
    -  }
    -
    -  const MarkdownItReplaceLink: typeof markdownItDeflist.replacelink_plugin;
    -  export = MarkdownItReplaceLink;
    -}
    diff --git a/style/index.css b/style/index.css
    index 734e5f8..7d21851 100644
    --- a/style/index.css
    +++ b/style/index.css
    @@ -61,6 +61,7 @@
       text-transform: uppercase;
       margin: 0;
       padding: 0;
    +  white-space: nowrap;
     }
     
     .jp-MarkdownItSettings h4 label {
    @@ -74,6 +75,7 @@
       list-style: none;
       padding: 0 !important;
       margin: 0;
    +  flex-wrap: wrap;
     }
     
     .jp-MarkdownItSettings-Docs li {
    diff --git a/yarn.lock b/yarn.lock
    index e65cf0a..d1d79fe 100644
    --- a/yarn.lock
    +++ b/yarn.lock
    @@ -2,46 +2,137 @@
     # yarn lockfile v1
     
     
    +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.4":
    +  version "7.10.4"
    +  resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.10.4.tgz#168da1a36e90da68ae8d49c0f1b48c7c6249213a"
    +  integrity sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==
    +  dependencies:
    +    "@babel/highlight" "^7.10.4"
    +
    +"@babel/generator@^7.12.5":
    +  version "7.12.5"
    +  resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.12.5.tgz#a2c50de5c8b6d708ab95be5e6053936c1884a4de"
    +  integrity sha512-m16TQQJ8hPt7E+OS/XVQg/7U184MLXtvuGbCdA7na61vha+ImkyyNM/9DDA0unYCVZn3ZOhng+qz48/KBOT96A==
    +  dependencies:
    +    "@babel/types" "^7.12.5"
    +    jsesc "^2.5.1"
    +    source-map "^0.5.0"
    +
    +"@babel/helper-function-name@^7.10.4":
    +  version "7.10.4"
    +  resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.10.4.tgz#d2d3b20c59ad8c47112fa7d2a94bc09d5ef82f1a"
    +  integrity sha512-YdaSyz1n8gY44EmN7x44zBn9zQ1Ry2Y+3GTA+3vH6Mizke1Vw0aWDM66FOYEPw8//qKkmqOckrGgTYa+6sceqQ==
    +  dependencies:
    +    "@babel/helper-get-function-arity" "^7.10.4"
    +    "@babel/template" "^7.10.4"
    +    "@babel/types" "^7.10.4"
    +
    +"@babel/helper-get-function-arity@^7.10.4":
    +  version "7.10.4"
    +  resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.10.4.tgz#98c1cbea0e2332f33f9a4661b8ce1505b2c19ba2"
    +  integrity sha512-EkN3YDB+SRDgiIUnNgcmiD361ti+AVbL3f3Henf6dqqUyr5dMsorno0lJWJuLhDhkI5sYEpgj6y9kB8AOU1I2A==
    +  dependencies:
    +    "@babel/types" "^7.10.4"
    +
    +"@babel/helper-split-export-declaration@^7.11.0":
    +  version "7.11.0"
    +  resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.11.0.tgz#f8a491244acf6a676158ac42072911ba83ad099f"
    +  integrity sha512-74Vejvp6mHkGE+m+k5vHY93FX2cAtrw1zXrZXRlG4l410Nm9PxfEiVTn1PjDPV5SnmieiueY4AFg2xqhNFuuZg==
    +  dependencies:
    +    "@babel/types" "^7.11.0"
    +
    +"@babel/helper-validator-identifier@^7.10.4":
    +  version "7.10.4"
    +  resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz#a78c7a7251e01f616512d31b10adcf52ada5e0d2"
    +  integrity sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==
    +
    +"@babel/highlight@^7.10.4":
    +  version "7.10.4"
    +  resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.10.4.tgz#7d1bdfd65753538fabe6c38596cdb76d9ac60143"
    +  integrity sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==
    +  dependencies:
    +    "@babel/helper-validator-identifier" "^7.10.4"
    +    chalk "^2.0.0"
    +    js-tokens "^4.0.0"
    +
    +"@babel/parser@^7.10.4", "@babel/parser@^7.12.5", "@babel/parser@^7.7.0":
    +  version "7.12.5"
    +  resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.12.5.tgz#b4af32ddd473c0bfa643bd7ff0728b8e71b81ea0"
    +  integrity sha512-FVM6RZQ0mn2KCf1VUED7KepYeUWoVShczewOCfm3nzoBybaih51h+sYVVGthW9M6lPByEPTQf+xm27PBdlpwmQ==
    +
     "@babel/runtime@^7.1.2":
    -  version "7.5.5"
    -  resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.5.5.tgz#74fba56d35efbeca444091c7850ccd494fd2f132"
    -  integrity sha512-28QvEGyQyNkB0/m2B4FU7IEZGK2NUrcMtT6BZEFALTguLk+AUT6ofsHtPk5QyjAdUkpMJ+/Em+quwz4HOt30AQ==
    +  version "7.12.5"
    +  resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.12.5.tgz#410e7e487441e1b360c29be715d870d9b985882e"
    +  integrity sha512-plcc+hbExy3McchJCEQG3knOsuh3HH+Prx1P6cLIkET/0dLuQDEnrT+s27Axgc9bqfsmNUNHfscgMUdBpC9xfg==
    +  dependencies:
    +    regenerator-runtime "^0.13.4"
    +
    +"@babel/template@^7.10.4":
    +  version "7.10.4"
    +  resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.10.4.tgz#3251996c4200ebc71d1a8fc405fba940f36ba278"
    +  integrity sha512-ZCjD27cGJFUB6nmCB1Enki3r+L5kJveX9pq1SvAUKoICy6CZ9yD8xO086YXdYhvNjBdnekm4ZnaP5yC8Cs/1tA==
    +  dependencies:
    +    "@babel/code-frame" "^7.10.4"
    +    "@babel/parser" "^7.10.4"
    +    "@babel/types" "^7.10.4"
    +
    +"@babel/traverse@^7.7.0":
    +  version "7.12.5"
    +  resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.12.5.tgz#78a0c68c8e8a35e4cacfd31db8bb303d5606f095"
    +  integrity sha512-xa15FbQnias7z9a62LwYAA5SZZPkHIXpd42C6uW68o8uTuua96FHZy1y61Va5P/i83FAAcMpW8+A/QayntzuqA==
    +  dependencies:
    +    "@babel/code-frame" "^7.10.4"
    +    "@babel/generator" "^7.12.5"
    +    "@babel/helper-function-name" "^7.10.4"
    +    "@babel/helper-split-export-declaration" "^7.11.0"
    +    "@babel/parser" "^7.12.5"
    +    "@babel/types" "^7.12.5"
    +    debug "^4.1.0"
    +    globals "^11.1.0"
    +    lodash "^4.17.19"
    +
    +"@babel/types@^7.10.4", "@babel/types@^7.11.0", "@babel/types@^7.12.5", "@babel/types@^7.7.0":
    +  version "7.12.6"
    +  resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.12.6.tgz#ae0e55ef1cce1fbc881cd26f8234eb3e657edc96"
    +  integrity sha512-hwyjw6GvjBLiyy3W0YQf0Z5Zf4NpYejUnKFcfcUhZCSffoBBp30w6wP2Wn6pk31jMYZvcOrB/1b7cGXvEoKogA==
       dependencies:
    -    regenerator-runtime "^0.13.2"
    +    "@babel/helper-validator-identifier" "^7.10.4"
    +    lodash "^4.17.19"
    +    to-fast-properties "^2.0.0"
     
    -"@blueprintjs/core@^3.22.2", "@blueprintjs/core@^3.24.0":
    -  version "3.24.0"
    -  resolved "https://registry.yarnpkg.com/@blueprintjs/core/-/core-3.24.0.tgz#593a2b289bb94224f3a924eb1b3065ea3c4ca00a"
    -  integrity sha512-qW29DDPjzYsT27J6n97C0jZ1ifvEEziwNC98UhaKdSE7I8qxbLsb+ft2JOop+pEX4ab67T1lhQKAiQjWCPKZng==
    +"@blueprintjs/core@^3.22.2", "@blueprintjs/core@^3.34.0":
    +  version "3.35.0"
    +  resolved "https://registry.yarnpkg.com/@blueprintjs/core/-/core-3.35.0.tgz#ed48ad7e6692f7dc32e28200a7984e029102ce3f"
    +  integrity sha512-2coEMDX1JJuHvDCt6wZSB6zntDlKvUmi4rqjLeGR+ZOo4TtFB92GSjycMtupka1PURM1A66oQZvnMiBIjuMW6Q==
       dependencies:
    -    "@blueprintjs/icons" "^3.14.0"
    +    "@blueprintjs/icons" "^3.22.0"
         "@types/dom4" "^2.0.1"
         classnames "^2.2"
         dom4 "^2.1.5"
         normalize.css "^8.0.1"
    -    popper.js "^1.15.0"
    +    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.10.0"
    +    tslib "~1.13.0"
     
    -"@blueprintjs/icons@^3.14.0":
    -  version "3.14.0"
    -  resolved "https://registry.yarnpkg.com/@blueprintjs/icons/-/icons-3.14.0.tgz#9f9a51b116907d103e4e2e9b78d53d4ac6f107fd"
    -  integrity sha512-cvQ3CSdy0DqVqcXcPqSxoycJw497TVP5goyE6xCFlVs84477ahxh7Uung6J+CCoDVBuI87h576LtuyjwSxorvQ==
    +"@blueprintjs/icons@^3.22.0":
    +  version "3.22.0"
    +  resolved "https://registry.yarnpkg.com/@blueprintjs/icons/-/icons-3.22.0.tgz#6a7c177e9aa96f0ed10bc93d88f7c6687db336ad"
    +  integrity sha512-clfdwRQlzqs2sDxjwQr4p10Z3bGNTnqpsLgN+4TN1ECf7plEEukhvQh6YK/Lfd5xDhEBEEZ/YQCawZbyAYjfXg==
       dependencies:
         classnames "^2.2"
    -    tslib "~1.10.0"
    +    tslib "~1.13.0"
     
     "@blueprintjs/select@^3.11.2":
    -  version "3.12.0"
    -  resolved "https://registry.yarnpkg.com/@blueprintjs/select/-/select-3.12.0.tgz#cd20b39ecb79c9c117d9a26fd54789ed6d605aec"
    -  integrity sha512-rABlv5M+h7onuoUuNsratyiukPnkdblDm7lt7GT4fbRmJglSsKylNnfHogNDZkMMHqmgmVB05mgzBQ+kcLA1cw==
    +  version "3.14.3"
    +  resolved "https://registry.yarnpkg.com/@blueprintjs/select/-/select-3.14.3.tgz#ca26ba4161b0d2b261198e12abb3e97a02dbcc10"
    +  integrity sha512-7psdf8SiqZUN1oUjtior1Y994+agKAO02o/7VYx93zfwW8dJkn5bTxGQnc0kDMXWWSFevsZMGfiQav78lZOgBw==
       dependencies:
    -    "@blueprintjs/core" "^3.24.0"
    +    "@blueprintjs/core" "^3.34.0"
         classnames "^2.2"
    -    tslib "~1.10.0"
    +    tslib "~1.13.0"
     
     "@braintree/sanitize-url@^3.1.0":
       version "3.1.0"
    @@ -49,45 +140,45 @@
       integrity sha512-GcIY79elgB+azP74j8vqkiXz8xLFfIzbQJdlwOPisgbKT00tviJQuEghOXSMVxJ00HoYJbGswr4kcllUc4xCcg==
     
     "@fortawesome/fontawesome-free@^5.12.0":
    -  version "5.12.1"
    -  resolved "https://registry.yarnpkg.com/@fortawesome/fontawesome-free/-/fontawesome-free-5.12.1.tgz#2a98fea9fbb8a606ddc79a4680034e9d5591c550"
    -  integrity sha512-ZtjIIFplxncqxvogq148C3hBLQE+W3iJ8E4UvJ09zIJUgzwLcROsWwFDErVSXY2Plzao5J9KUYNHKHMEUYDMKw==
    +  version "5.15.1"
    +  resolved "https://registry.yarnpkg.com/@fortawesome/fontawesome-free/-/fontawesome-free-5.15.1.tgz#ccfef6ddbe59f8fe8f694783e1d3eb88902dc5eb"
    +  integrity sha512-OEdH7SyC1suTdhBGW91/zBfR6qaIhThbcN8PUXtXilY4GYnSBbVqOntdHbC1vXwsDnX0Qix2m2+DSU1J51ybOQ==
     
     "@jupyterlab/application@^2.0.2":
    -  version "2.0.2"
    -  resolved "https://registry.yarnpkg.com/@jupyterlab/application/-/application-2.0.2.tgz#393965798c4e04f522f9aa6a9b02b883a35a6634"
    -  integrity sha512-/4KG2jBaUx5s+uuEKpTjJC3kOEQWKmpDNorOLP8PPsdWMl1VlrYJJmnpvIUOLLnJZAycnK7O4z7jBDp6wv4S2Q==
    +  version "2.2.6"
    +  resolved "https://registry.yarnpkg.com/@jupyterlab/application/-/application-2.2.6.tgz#4c505d7196f784c11d533b4539e7b2fbc5d664d6"
    +  integrity sha512-pW/Cu+t3CYii52YncrbcRWSfAFTIlsDy8yMxgYqkG6TSsImca3Rix3o69jTu264Ct2UQzEWP4+6u2IqF5wziuw==
       dependencies:
         "@fortawesome/fontawesome-free" "^5.12.0"
    -    "@jupyterlab/apputils" "^2.0.2"
    -    "@jupyterlab/coreutils" "^4.0.2"
    -    "@jupyterlab/docregistry" "^2.0.2"
    -    "@jupyterlab/rendermime" "^2.0.2"
    -    "@jupyterlab/rendermime-interfaces" "^2.0.1"
    -    "@jupyterlab/services" "^5.0.2"
    -    "@jupyterlab/statedb" "^2.0.1"
    -    "@jupyterlab/ui-components" "^2.0.2"
    +    "@jupyterlab/apputils" "^2.2.6"
    +    "@jupyterlab/coreutils" "^4.2.5"
    +    "@jupyterlab/docregistry" "^2.2.4"
    +    "@jupyterlab/rendermime" "^2.2.4"
    +    "@jupyterlab/rendermime-interfaces" "^2.2.1"
    +    "@jupyterlab/services" "^5.2.5"
    +    "@jupyterlab/statedb" "^2.2.5"
    +    "@jupyterlab/ui-components" "^2.2.4"
         "@lumino/algorithm" "^1.2.3"
         "@lumino/application" "^1.8.4"
         "@lumino/commands" "^1.10.1"
         "@lumino/coreutils" "^1.4.2"
         "@lumino/disposable" "^1.3.5"
         "@lumino/messaging" "^1.3.3"
    -    "@lumino/polling" "^1.0.4"
    +    "@lumino/polling" "^1.1.1"
         "@lumino/properties" "^1.1.6"
         "@lumino/signaling" "^1.3.5"
         "@lumino/widgets" "^1.11.1"
     
    -"@jupyterlab/apputils@^2.0.2":
    -  version "2.0.2"
    -  resolved "https://registry.yarnpkg.com/@jupyterlab/apputils/-/apputils-2.0.2.tgz#23e158e9d2092045570c798e7430895c0ad4709f"
    -  integrity sha512-mJO/h3x+jtKXPJegdOB5LkvOWLjACKElWCWZXGtizHASYXKrCCAYQ3VDkmrdx2zibu+gDqlMFtbPJxMvYt65Vw==
    -  dependencies:
    -    "@jupyterlab/coreutils" "^4.0.2"
    -    "@jupyterlab/services" "^5.0.2"
    -    "@jupyterlab/settingregistry" "^2.0.1"
    -    "@jupyterlab/statedb" "^2.0.1"
    -    "@jupyterlab/ui-components" "^2.0.2"
    +"@jupyterlab/apputils@^2.2.6":
    +  version "2.2.6"
    +  resolved "https://registry.yarnpkg.com/@jupyterlab/apputils/-/apputils-2.2.6.tgz#13cadd7a4765eca0995bf68313ec9c99d4f534a5"
    +  integrity sha512-aWg0c6edfQC6b1sLFskirG+LqH1aGKnVGj7R7HR1loyOLVVq/HpZ9wkPZLCh8Gi1Wy4Tqvm2R46glmuu84W8xA==
    +  dependencies:
    +    "@jupyterlab/coreutils" "^4.2.5"
    +    "@jupyterlab/services" "^5.2.5"
    +    "@jupyterlab/settingregistry" "^2.2.5"
    +    "@jupyterlab/statedb" "^2.2.5"
    +    "@jupyterlab/ui-components" "^2.2.4"
         "@lumino/algorithm" "^1.2.3"
         "@lumino/commands" "^1.10.1"
         "@lumino/coreutils" "^1.4.2"
    @@ -103,15 +194,15 @@
         react-dom "~16.9.0"
         sanitize-html "~1.20.1"
     
    -"@jupyterlab/codeeditor@^2.0.2":
    -  version "2.0.2"
    -  resolved "https://registry.yarnpkg.com/@jupyterlab/codeeditor/-/codeeditor-2.0.2.tgz#6ff469a068595b783bd639120db34b39d116df7f"
    -  integrity sha512-l1SrLJN3QNXQB1WH0YrMjKsM3ZOPAYI05r7hBS0U1Kq5vpb73LQ+8w08s15y/yPcuCgibVhonIEQCyiu1wUA3w==
    +"@jupyterlab/codeeditor@^2.2.5":
    +  version "2.2.5"
    +  resolved "https://registry.yarnpkg.com/@jupyterlab/codeeditor/-/codeeditor-2.2.5.tgz#0d5b639e2f3dad829cafa82f7881d8a6d031784c"
    +  integrity sha512-ktc5e/30aabx0GwFBiiNOimtBoDx+ZFWkHeKg1ylpHASGgdQt6Km3E+TQARzFWbwkShyc7WFJySK1ES/uimDVg==
       dependencies:
    -    "@jupyterlab/coreutils" "^4.0.2"
    -    "@jupyterlab/nbformat" "^2.0.1"
    -    "@jupyterlab/observables" "^3.0.1"
    -    "@jupyterlab/ui-components" "^2.0.2"
    +    "@jupyterlab/coreutils" "^4.2.5"
    +    "@jupyterlab/nbformat" "^2.2.5"
    +    "@jupyterlab/observables" "^3.2.5"
    +    "@jupyterlab/ui-components" "^2.2.4"
         "@lumino/coreutils" "^1.4.2"
         "@lumino/disposable" "^1.3.5"
         "@lumino/dragdrop" "^1.5.1"
    @@ -119,31 +210,31 @@
         "@lumino/signaling" "^1.3.5"
         "@lumino/widgets" "^1.11.1"
     
    -"@jupyterlab/codemirror@^2.0.2":
    -  version "2.0.2"
    -  resolved "https://registry.yarnpkg.com/@jupyterlab/codemirror/-/codemirror-2.0.2.tgz#519fa4d9fe572d430d4bf19adefd3b1a7f627d7a"
    -  integrity sha512-IQm/yiPHJtQgJlQt/qqX0/pChGsQn/2JIe38q6R2Hi6V4DbI8WpyVOBhhKIoUqWwGNLsZgoCna2dnB+R7j0Emw==
    -  dependencies:
    -    "@jupyterlab/apputils" "^2.0.2"
    -    "@jupyterlab/codeeditor" "^2.0.2"
    -    "@jupyterlab/coreutils" "^4.0.2"
    -    "@jupyterlab/nbformat" "^2.0.1"
    -    "@jupyterlab/observables" "^3.0.1"
    -    "@jupyterlab/statusbar" "^2.0.2"
    +"@jupyterlab/codemirror@^2.0.2", "@jupyterlab/codemirror@^2.2.4":
    +  version "2.2.4"
    +  resolved "https://registry.yarnpkg.com/@jupyterlab/codemirror/-/codemirror-2.2.4.tgz#e929e4b7e431fe40f30ec0310d99369ee6977fa8"
    +  integrity sha512-arWSgzZxCyLSOGFQvplxnIRdWXJ+VD2skb9OX/Fa/uqknpeyMvGBX5RkaX+edCMjlRfytkWPKlxvMcPgeMVP/Q==
    +  dependencies:
    +    "@jupyterlab/apputils" "^2.2.6"
    +    "@jupyterlab/codeeditor" "^2.2.5"
    +    "@jupyterlab/coreutils" "^4.2.5"
    +    "@jupyterlab/nbformat" "^2.2.5"
    +    "@jupyterlab/observables" "^3.2.5"
    +    "@jupyterlab/statusbar" "^2.2.4"
         "@lumino/algorithm" "^1.2.3"
         "@lumino/commands" "^1.10.1"
         "@lumino/coreutils" "^1.4.2"
         "@lumino/disposable" "^1.3.5"
    -    "@lumino/polling" "^1.0.4"
    +    "@lumino/polling" "^1.1.1"
         "@lumino/signaling" "^1.3.5"
         "@lumino/widgets" "^1.11.1"
    -    codemirror "~5.49.2"
    +    codemirror "~5.53.2"
         react "~16.9.0"
     
    -"@jupyterlab/coreutils@^4.0.2":
    -  version "4.0.2"
    -  resolved "https://registry.yarnpkg.com/@jupyterlab/coreutils/-/coreutils-4.0.2.tgz#2ee81799249f5f4741157ac42ff50a2ee48a0475"
    -  integrity sha512-v4RXIAeykoPjIymdCxUxPLl8lkn18jroGnDflZjvdMk21TZQQJSIrJ5bjrGByh9scco8yNg46z8m1LPguF3z8A==
    +"@jupyterlab/coreutils@^4.2.5":
    +  version "4.2.5"
    +  resolved "https://registry.yarnpkg.com/@jupyterlab/coreutils/-/coreutils-4.2.5.tgz#332047e13e3fa62be4d875f186942d6bcbd70272"
    +  integrity sha512-dkU9aD10vthsDulq1o5CEgIu0pe84v2Krxvfu3m4EYC+pSJmGHsxc3wmnb8MQocPiMJFB79brm6zJaXiy68uWA==
       dependencies:
         "@lumino/coreutils" "^1.4.2"
         "@lumino/disposable" "^1.3.5"
    @@ -153,20 +244,20 @@
         path-posix "~1.0.0"
         url-parse "~1.4.7"
     
    -"@jupyterlab/docregistry@^2.0.2":
    -  version "2.0.2"
    -  resolved "https://registry.yarnpkg.com/@jupyterlab/docregistry/-/docregistry-2.0.2.tgz#45ee6be76aae72e9df0a1a1bc69effaae401235b"
    -  integrity sha512-kGk1AIzcXkpaNI1pwFbyLYiQuSdJUQ/j2A+G8WYhcY64Zwp1tayr0VvaRuEzwcDHueiBYesaarCxY7VNP+Cf3g==
    -  dependencies:
    -    "@jupyterlab/apputils" "^2.0.2"
    -    "@jupyterlab/codeeditor" "^2.0.2"
    -    "@jupyterlab/codemirror" "^2.0.2"
    -    "@jupyterlab/coreutils" "^4.0.2"
    -    "@jupyterlab/observables" "^3.0.1"
    -    "@jupyterlab/rendermime" "^2.0.2"
    -    "@jupyterlab/rendermime-interfaces" "^2.0.1"
    -    "@jupyterlab/services" "^5.0.2"
    -    "@jupyterlab/ui-components" "^2.0.2"
    +"@jupyterlab/docregistry@^2.2.4":
    +  version "2.2.4"
    +  resolved "https://registry.yarnpkg.com/@jupyterlab/docregistry/-/docregistry-2.2.4.tgz#94c7eebab4f1637e46b46e96ce48324598e2da43"
    +  integrity sha512-xmwptfXKr7QkpnNOaLVB/PIWaoZ8JpRCY/cKwQSXq42wkpY7D4iCJyX0iGsPqapJHYOq6g0BnQm195ETTYs/BQ==
    +  dependencies:
    +    "@jupyterlab/apputils" "^2.2.6"
    +    "@jupyterlab/codeeditor" "^2.2.5"
    +    "@jupyterlab/codemirror" "^2.2.4"
    +    "@jupyterlab/coreutils" "^4.2.5"
    +    "@jupyterlab/observables" "^3.2.5"
    +    "@jupyterlab/rendermime" "^2.2.4"
    +    "@jupyterlab/rendermime-interfaces" "^2.2.1"
    +    "@jupyterlab/services" "^5.2.5"
    +    "@jupyterlab/ui-components" "^2.2.4"
         "@lumino/algorithm" "^1.2.3"
         "@lumino/coreutils" "^1.4.2"
         "@lumino/disposable" "^1.3.5"
    @@ -175,44 +266,58 @@
         "@lumino/widgets" "^1.11.1"
     
     "@jupyterlab/fileeditor@^2.0.2":
    -  version "2.0.2"
    -  resolved "https://registry.yarnpkg.com/@jupyterlab/fileeditor/-/fileeditor-2.0.2.tgz#48b98f08aec07923f445092d54af2b9311b9bfe3"
    -  integrity sha512-Tt9xpw1m26HaDTQCHgeVcbZrzECedyjp+jkCKYR4aDyewWCzMIV7AN39CF+noWu/+6LmNoV2km6XGGNv1LGv9A==
    -  dependencies:
    -    "@jupyterlab/apputils" "^2.0.2"
    -    "@jupyterlab/codeeditor" "^2.0.2"
    -    "@jupyterlab/docregistry" "^2.0.2"
    -    "@jupyterlab/statusbar" "^2.0.2"
    -    "@jupyterlab/ui-components" "^2.0.2"
    +  version "2.2.4"
    +  resolved "https://registry.yarnpkg.com/@jupyterlab/fileeditor/-/fileeditor-2.2.4.tgz#7ac8ab44a8803e32df8f364edcb210fe395054fd"
    +  integrity sha512-S1OEx/wzp+u1TPQJHWNwdf8A2pA5racQEc7eoglBG3f3abOpT7en73W4YABt5XHc4bQ+MStQOW2XGkE5fQXZuQ==
    +  dependencies:
    +    "@jupyterlab/apputils" "^2.2.6"
    +    "@jupyterlab/codeeditor" "^2.2.5"
    +    "@jupyterlab/docregistry" "^2.2.4"
    +    "@jupyterlab/statusbar" "^2.2.4"
    +    "@jupyterlab/ui-components" "^2.2.4"
         "@lumino/coreutils" "^1.4.2"
         "@lumino/messaging" "^1.3.3"
         "@lumino/widgets" "^1.11.1"
         react "~16.9.0"
     
    +"@jupyterlab/mainmenu@^2.0.2":
    +  version "2.2.4"
    +  resolved "https://registry.yarnpkg.com/@jupyterlab/mainmenu/-/mainmenu-2.2.4.tgz#2bef0045986d47223271319d3a419626a3f96ab7"
    +  integrity sha512-5EvpMJ+XTeI8YH3vNldCl/epjk8sEvEvKusuifUc1xXLqhX0YpPRL0ebg4NknX2qgeFag94KfQnnGs+pEoWUbg==
    +  dependencies:
    +    "@jupyterlab/apputils" "^2.2.6"
    +    "@jupyterlab/services" "^5.2.5"
    +    "@jupyterlab/ui-components" "^2.2.4"
    +    "@lumino/algorithm" "^1.2.3"
    +    "@lumino/commands" "^1.10.1"
    +    "@lumino/coreutils" "^1.4.2"
    +    "@lumino/disposable" "^1.3.5"
    +    "@lumino/widgets" "^1.11.1"
    +
     "@jupyterlab/markdownviewer@^2.0.2":
    -  version "2.0.2"
    -  resolved "https://registry.yarnpkg.com/@jupyterlab/markdownviewer/-/markdownviewer-2.0.2.tgz#9a64ad9bc3e283e5a61970b23c47261e37734631"
    -  integrity sha512-idtRB/uaQXbhrIYfzybrIwVj1xfJRkH7uCmjVkRdA+9VBRasZquGZ2KRczPjSdDRUmOZfvjORms+S1qt03kkiw==
    +  version "2.2.6"
    +  resolved "https://registry.yarnpkg.com/@jupyterlab/markdownviewer/-/markdownviewer-2.2.6.tgz#0f1ddf565bab1b9ec724db0c24d69fec0ec5ce9a"
    +  integrity sha512-VhjyPHQNFKHBWTiIq6LsM2tXO5L4KsYXs+39nJtngFdy6wDDSncBg7b9nHas9RvZ8wK744iOQpeh6rXCk2RMPQ==
       dependencies:
    -    "@jupyterlab/apputils" "^2.0.2"
    -    "@jupyterlab/coreutils" "^4.0.2"
    -    "@jupyterlab/docregistry" "^2.0.2"
    -    "@jupyterlab/rendermime" "^2.0.2"
    +    "@jupyterlab/apputils" "^2.2.6"
    +    "@jupyterlab/coreutils" "^4.2.5"
    +    "@jupyterlab/docregistry" "^2.2.4"
    +    "@jupyterlab/rendermime" "^2.2.4"
         "@lumino/coreutils" "^1.4.2"
         "@lumino/messaging" "^1.3.3"
         "@lumino/widgets" "^1.11.1"
     
    -"@jupyterlab/nbformat@^2.0.1":
    -  version "2.0.1"
    -  resolved "https://registry.yarnpkg.com/@jupyterlab/nbformat/-/nbformat-2.0.1.tgz#2af01e20755632f5fb8530942c470e0f31f34df4"
    -  integrity sha512-rlf4A3PKxqDV98IeXf/VtdhqcbnKvBRGL+VNxhMOZe3e+DmjIBilRE+VpHmXovwEanOAkNl0fD5Xk3HAkqxxGQ==
    +"@jupyterlab/nbformat@^2.2.5":
    +  version "2.2.5"
    +  resolved "https://registry.yarnpkg.com/@jupyterlab/nbformat/-/nbformat-2.2.5.tgz#76df45471ba438dc22b3e43ea20fc1c93181d206"
    +  integrity sha512-NXxNDMB0n0GJS634KkqZBAS9tAFkkLubv2YfPkWLOjlYHWPclknQfMLWpjn2VTSdj7C+xk6qqsv4YLziRn5BPA==
       dependencies:
         "@lumino/coreutils" "^1.4.2"
     
    -"@jupyterlab/observables@^3.0.1":
    -  version "3.0.1"
    -  resolved "https://registry.yarnpkg.com/@jupyterlab/observables/-/observables-3.0.1.tgz#001ab3d64d47e97eae52d17a0190845f1586db9c"
    -  integrity sha512-iD7w8JYBRT9UXVS3IvljvoDf0ZiHQEu1Pm+784UTa27Az0i6idyV1iWZ+xIHtV+s7ampVjMGiBbqXD6m4HRNpg==
    +"@jupyterlab/observables@^3.2.5":
    +  version "3.2.5"
    +  resolved "https://registry.yarnpkg.com/@jupyterlab/observables/-/observables-3.2.5.tgz#332acea88e5b9bfc1e7040750f929cad7dbdb9a5"
    +  integrity sha512-21y72DScc4EsfcPpVgm4VLUcUWi2AvHuBOtrjPpNxrvrl3hNqTVNOOtX1lEeqVOzdWEJAJ7jeEe96rTkY5tptQ==
       dependencies:
         "@lumino/algorithm" "^1.2.3"
         "@lumino/coreutils" "^1.4.2"
    @@ -220,26 +325,26 @@
         "@lumino/messaging" "^1.3.3"
         "@lumino/signaling" "^1.3.5"
     
    -"@jupyterlab/rendermime-interfaces@^2.0.1":
    -  version "2.0.1"
    -  resolved "https://registry.yarnpkg.com/@jupyterlab/rendermime-interfaces/-/rendermime-interfaces-2.0.1.tgz#f4dae5690b13ad743dda2aebbfc5e1efd079be58"
    -  integrity sha512-QYJcQNKNmrBXHXC31AvBRYk+QqKB0rZrvVXbhggFXBQiYQX1K/lnFvKjphmhhnpUhLKtgUrex+04cJ9Kek00HA==
    +"@jupyterlab/rendermime-interfaces@^2.0.1", "@jupyterlab/rendermime-interfaces@^2.2.1":
    +  version "2.2.1"
    +  resolved "https://registry.yarnpkg.com/@jupyterlab/rendermime-interfaces/-/rendermime-interfaces-2.2.1.tgz#7794b9fd13151b8b4fdc03b1dd45c59f67c7f5b3"
    +  integrity sha512-tjwt3dFfjguV3AeLKhIaCACzaTr2I6F34c4ArJ95WCCRx2DmrRMYFgppLlBo9rKa7BiYeQWZD/lYWvL+GnQZEA==
       dependencies:
         "@lumino/coreutils" "^1.4.2"
         "@lumino/widgets" "^1.11.1"
     
    -"@jupyterlab/rendermime@^2.0.2":
    -  version "2.0.2"
    -  resolved "https://registry.yarnpkg.com/@jupyterlab/rendermime/-/rendermime-2.0.2.tgz#1e317ed136f4f798efaeae43948306c1dce4171c"
    -  integrity sha512-JUGUteRLrwEHX5kPU1rLAIzChEwEQyxDbSCes63fgO5Hn+JXNKKQexWXeHZOm5l1JBGNiokQCz8Jy6fQHmEMYQ==
    -  dependencies:
    -    "@jupyterlab/apputils" "^2.0.2"
    -    "@jupyterlab/codemirror" "^2.0.2"
    -    "@jupyterlab/coreutils" "^4.0.2"
    -    "@jupyterlab/nbformat" "^2.0.1"
    -    "@jupyterlab/observables" "^3.0.1"
    -    "@jupyterlab/rendermime-interfaces" "^2.0.1"
    -    "@jupyterlab/services" "^5.0.2"
    +"@jupyterlab/rendermime@^2.0.2", "@jupyterlab/rendermime@^2.2.4":
    +  version "2.2.4"
    +  resolved "https://registry.yarnpkg.com/@jupyterlab/rendermime/-/rendermime-2.2.4.tgz#4509450539c9e440a28fd9d5318a59e65cc8fce2"
    +  integrity sha512-XhBsUQdyidYrNY71+TZUGJSg/mf3kM90lSwIw+DgHSJJz/BXO5bSyKP5XnTSeKYoz40Ko1PCyRyCmKpmI51J6A==
    +  dependencies:
    +    "@jupyterlab/apputils" "^2.2.6"
    +    "@jupyterlab/codemirror" "^2.2.4"
    +    "@jupyterlab/coreutils" "^4.2.5"
    +    "@jupyterlab/nbformat" "^2.2.5"
    +    "@jupyterlab/observables" "^3.2.5"
    +    "@jupyterlab/rendermime-interfaces" "^2.2.1"
    +    "@jupyterlab/services" "^5.2.5"
         "@lumino/algorithm" "^1.2.3"
         "@lumino/coreutils" "^1.4.2"
         "@lumino/messaging" "^1.3.3"
    @@ -248,30 +353,30 @@
         lodash.escape "^4.0.1"
         marked "^0.8.0"
     
    -"@jupyterlab/services@^5.0.2":
    -  version "5.0.2"
    -  resolved "https://registry.yarnpkg.com/@jupyterlab/services/-/services-5.0.2.tgz#9e3e398c1cf022f35c406a743b46bc60a6723192"
    -  integrity sha512-gBwXikSRWIrj0XiuYMSXd0TXLZJrE18rTtRwrvne0T/gJPg0+4JbORPkaKfFdbX84oQv8XLOT74xUHSPDhst0A==
    +"@jupyterlab/services@^5.2.5":
    +  version "5.2.5"
    +  resolved "https://registry.yarnpkg.com/@jupyterlab/services/-/services-5.2.5.tgz#528a16091ddbf6c445bb7f85168e9b02bcb386d8"
    +  integrity sha512-vhWt+rbDUe3SRvv1GD1WOjsDNhDz2lg33xdsT/+WObZRqeQ9CgzUF2K8Zah9UaiyGmTM3tpUUCTIQ62hNi5wrA==
       dependencies:
    -    "@jupyterlab/coreutils" "^4.0.2"
    -    "@jupyterlab/nbformat" "^2.0.1"
    -    "@jupyterlab/observables" "^3.0.1"
    -    "@jupyterlab/settingregistry" "^2.0.1"
    -    "@jupyterlab/statedb" "^2.0.1"
    +    "@jupyterlab/coreutils" "^4.2.5"
    +    "@jupyterlab/nbformat" "^2.2.5"
    +    "@jupyterlab/observables" "^3.2.5"
    +    "@jupyterlab/settingregistry" "^2.2.5"
    +    "@jupyterlab/statedb" "^2.2.5"
         "@lumino/algorithm" "^1.2.3"
         "@lumino/coreutils" "^1.4.2"
         "@lumino/disposable" "^1.3.5"
    -    "@lumino/polling" "^1.0.4"
    +    "@lumino/polling" "^1.1.1"
         "@lumino/signaling" "^1.3.5"
         node-fetch "^2.6.0"
         ws "^7.2.0"
     
    -"@jupyterlab/settingregistry@^2.0.1":
    -  version "2.0.1"
    -  resolved "https://registry.yarnpkg.com/@jupyterlab/settingregistry/-/settingregistry-2.0.1.tgz#a4bfd4afe5286c8abb327d76852f0acef6f110d3"
    -  integrity sha512-38c5CFXLLNT1zKk0vS/UoD7TA90YpOrs/I5Zc8wY8GIl31IzTmTgre5H5cFSgvgg/imEbsYVWiUXtvTuQHGDWw==
    +"@jupyterlab/settingregistry@^2.2.5":
    +  version "2.2.5"
    +  resolved "https://registry.yarnpkg.com/@jupyterlab/settingregistry/-/settingregistry-2.2.5.tgz#665d2f5bfb601acd7020e2868f8bba1513d8c9cf"
    +  integrity sha512-LoKa27F1WNmeMT168TYo+MgjsYsVawKCZbmU7OGQS6h6J5dx0xQBQvE38NkhCsjnPYyUv4tYmGIFyHQceCDDaA==
       dependencies:
    -    "@jupyterlab/statedb" "^2.0.1"
    +    "@jupyterlab/statedb" "^2.2.5"
         "@lumino/commands" "^1.10.1"
         "@lumino/coreutils" "^1.4.2"
         "@lumino/disposable" "^1.3.5"
    @@ -279,10 +384,10 @@
         ajv "^6.10.2"
         json5 "^2.1.1"
     
    -"@jupyterlab/statedb@^2.0.1":
    -  version "2.0.1"
    -  resolved "https://registry.yarnpkg.com/@jupyterlab/statedb/-/statedb-2.0.1.tgz#5a93726a96fff7e4b1c9571c771302912bd45ad4"
    -  integrity sha512-M8Z9yc5grOa0dspEFBkB3qetAozPKbXYryOCaB2/MYBalTaZfNivJyVVnxf3xQRKolYavOn9ohONmuQq0OMFWQ==
    +"@jupyterlab/statedb@^2.2.5":
    +  version "2.2.5"
    +  resolved "https://registry.yarnpkg.com/@jupyterlab/statedb/-/statedb-2.2.5.tgz#2df5ff18d7417c342aa9651281860cabeffc8ee9"
    +  integrity sha512-+hW1bQ6+p18SNZvjM7hZMPv7odkLWkAp17qoRPtky3j+CFnZW7m49U0XA8QezjLBiX9QdHFYgoUhIZEmrKcPDg==
       dependencies:
         "@lumino/commands" "^1.10.1"
         "@lumino/coreutils" "^1.4.2"
    @@ -290,35 +395,35 @@
         "@lumino/properties" "^1.1.6"
         "@lumino/signaling" "^1.3.5"
     
    -"@jupyterlab/statusbar@^2.0.2":
    -  version "2.0.2"
    -  resolved "https://registry.yarnpkg.com/@jupyterlab/statusbar/-/statusbar-2.0.2.tgz#35924235e6a5efff72b0d1e2ade6cd36158b55d1"
    -  integrity sha512-CGSaIm62ABWrQzAt6Jr79Px1iJMZw+LqlSVGMUDK7uPpeQ0w2Sk0V2B9bydSqJyDyzb/Ja495CYRqLu6rJn94A==
    -  dependencies:
    -    "@jupyterlab/apputils" "^2.0.2"
    -    "@jupyterlab/codeeditor" "^2.0.2"
    -    "@jupyterlab/coreutils" "^4.0.2"
    -    "@jupyterlab/services" "^5.0.2"
    -    "@jupyterlab/ui-components" "^2.0.2"
    +"@jupyterlab/statusbar@^2.2.4":
    +  version "2.2.4"
    +  resolved "https://registry.yarnpkg.com/@jupyterlab/statusbar/-/statusbar-2.2.4.tgz#e998d93440879985b8e339da03d47271ac054618"
    +  integrity sha512-Wyhd5EZYd5Zs4oAsnG1Dt57wE4jUajkGxwr4SSotXXzccmJOLiZsvmNxPuftcZJHqbisG7EweaZXVlKMnb3SsA==
    +  dependencies:
    +    "@jupyterlab/apputils" "^2.2.6"
    +    "@jupyterlab/codeeditor" "^2.2.5"
    +    "@jupyterlab/coreutils" "^4.2.5"
    +    "@jupyterlab/services" "^5.2.5"
    +    "@jupyterlab/ui-components" "^2.2.4"
         "@lumino/algorithm" "^1.2.3"
         "@lumino/coreutils" "^1.4.2"
         "@lumino/disposable" "^1.3.5"
         "@lumino/messaging" "^1.3.3"
    -    "@lumino/polling" "^1.0.4"
    +    "@lumino/polling" "^1.1.1"
         "@lumino/signaling" "^1.3.5"
         "@lumino/widgets" "^1.11.1"
         csstype "~2.6.9"
         react "~16.9.0"
         typestyle "^2.0.4"
     
    -"@jupyterlab/ui-components@^2.0.2":
    -  version "2.0.2"
    -  resolved "https://registry.yarnpkg.com/@jupyterlab/ui-components/-/ui-components-2.0.2.tgz#84a68bbb4fdca0d83ea2892fcf78a8a373716fa9"
    -  integrity sha512-yQ/M3ZtA/Zo8qsvvcwe17qdeTE7xZ6U5l5M/6OVvxIMKR0qXnqSpv8w7jJAgbRusr29Dj8tHbdrEMFethlfwUg==
    +"@jupyterlab/ui-components@^2.2.4":
    +  version "2.2.4"
    +  resolved "https://registry.yarnpkg.com/@jupyterlab/ui-components/-/ui-components-2.2.4.tgz#693b1fe48d3300de17a7b2b435ea85302cee8c6c"
    +  integrity sha512-8OqmlDIlf3OHrKqTGHLBbzY6gDyTGT91o5LSyLuOBhIF2TvPorBYnxWk883pO7n4rSVhZbafBb0U3Hl3X9uT3Q==
       dependencies:
         "@blueprintjs/core" "^3.22.2"
         "@blueprintjs/select" "^3.11.2"
    -    "@jupyterlab/coreutils" "^4.0.2"
    +    "@jupyterlab/coreutils" "^4.2.5"
         "@lumino/coreutils" "^1.4.2"
         "@lumino/signaling" "^1.3.5"
         "@lumino/virtualdom" "^1.6.1"
    @@ -327,123 +432,123 @@
         react-dom "~16.9.0"
         typestyle "^2.0.4"
     
    -"@lumino/algorithm@^1.2.3":
    -  version "1.2.3"
    -  resolved "https://registry.yarnpkg.com/@lumino/algorithm/-/algorithm-1.2.3.tgz#4ab9883d7e9a5b1845372a752dcaee2a35a770c6"
    -  integrity sha512-XBJ/homcm7o8Y9G6MzYvf0FF7SVqUCzvkIO01G2mZhCOnkZZhZ9c4uNOcE2VjSHNxHv2WU0l7d8rdhyKhmet+A==
    +"@lumino/algorithm@^1.2.3", "@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.8.4":
    -  version "1.8.4"
    -  resolved "https://registry.yarnpkg.com/@lumino/application/-/application-1.8.4.tgz#63a26c4ecf8128bf0123739e37922415016f970a"
    -  integrity sha512-f+CgggJ/9jopHT6db76+BjsiPBHjv6fgReU/vKtRGg8rsDjNRDefoWd9bWGWRuPiGymBY8c/+9Kyq5v0UDs5vg==
    +  version "1.11.1"
    +  resolved "https://registry.yarnpkg.com/@lumino/application/-/application-1.11.1.tgz#51318abb857cd4be12fae118f03f3a93c51f2849"
    +  integrity sha512-hhv3y5NdbmMrcM8cZT8j8EMlFq8CVeEALzrfJNAIuMX1wIeo30yfXCntukDZpyw8loXNHEAUO840Qk5nImMA5g==
       dependencies:
    -    "@lumino/commands" "^1.10.1"
    -    "@lumino/coreutils" "^1.4.2"
    -    "@lumino/widgets" "^1.11.1"
    +    "@lumino/commands" "^1.11.4"
    +    "@lumino/coreutils" "^1.5.3"
    +    "@lumino/widgets" "^1.14.1"
     
    -"@lumino/collections@^1.2.3":
    +"@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.10.1", "@lumino/commands@^1.11.4":
    +  version "1.11.4"
    +  resolved "https://registry.yarnpkg.com/@lumino/commands/-/commands-1.11.4.tgz#05e4166ad9c73e5b84f7db208e3f02d597f1e887"
    +  integrity sha512-yZhcx4K5Be/JOIz8OJjo88zzIMkalQ/1ifhTUq5GPi2pdzwmaY6lZjql8r9PX0SRGhWtWLfJX5DTPiOf42fugQ==
    +  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.7.3"
    +
    +"@lumino/coreutils@^1.4.2", "@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.3.5", "@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.1.7", "@lumino/domutils@^1.2.3":
       version "1.2.3"
    -  resolved "https://registry.yarnpkg.com/@lumino/collections/-/collections-1.2.3.tgz#8cd9578dac3a5ecba68972991fdfd2b94d3339bc"
    -  integrity sha512-lrSTb7kru/w8xww8qWqHHhHO3GkoQeXST2oNkOEbWNEO4wuBIHoKPSOmXpUwu58UykBUfd5hL5wbkeTzyNMONg==
    -  dependencies:
    -    "@lumino/algorithm" "^1.2.3"
    +  resolved "https://registry.yarnpkg.com/@lumino/domutils/-/domutils-1.2.3.tgz#7e8e549a97624bfdbd4dd95ae4d1e30b87799822"
    +  integrity sha512-SEi8WZSy+DWMkL5CfAY78MHbi3x83AVmRFxjs9+A6qsFPde+Hr1I4DNtLsSDmfAWsobHHgBnjyNp2ZkQEq0IEA==
     
    -"@lumino/commands@^1.10.1":
    -  version "1.10.1"
    -  resolved "https://registry.yarnpkg.com/@lumino/commands/-/commands-1.10.1.tgz#149186d23cc48215f9f7f6515321f8871797a444"
    -  integrity sha512-HGtXtqKD1WZJszJ42u2DyM3sgxrLal66IoHSJjbn2ygcEVCKDK73NSzoaQtXFtiissMedzKl8aIRXB3uyeEOlw==
    +"@lumino/dragdrop@^1.5.1", "@lumino/dragdrop@^1.6.4":
    +  version "1.6.4"
    +  resolved "https://registry.yarnpkg.com/@lumino/dragdrop/-/dragdrop-1.6.4.tgz#bc87589b7335f40cf8dc5b2cffa14cfb3a1c56cc"
    +  integrity sha512-t+tQazxg/fyyC7T1wm7mnSfUDNPvAbKHRDWaIbBRVjf6M+B5N8eFwwqMZ63nKdzZPbwX6DJq+D2DNlqIB7gOjg==
       dependencies:
    -    "@lumino/algorithm" "^1.2.3"
    -    "@lumino/coreutils" "^1.4.2"
    -    "@lumino/disposable" "^1.3.5"
    -    "@lumino/domutils" "^1.1.7"
    -    "@lumino/keyboard" "^1.1.6"
    -    "@lumino/signaling" "^1.3.5"
    -    "@lumino/virtualdom" "^1.6.1"
    +    "@lumino/coreutils" "^1.5.3"
    +    "@lumino/disposable" "^1.4.3"
     
    -"@lumino/coreutils@^1.4.2":
    -  version "1.4.2"
    -  resolved "https://registry.yarnpkg.com/@lumino/coreutils/-/coreutils-1.4.2.tgz#44cd3d55bb692e876c792f1ecc0df3daa1de63e9"
    -  integrity sha512-SmQ4YDANe25rZd0bLoW7LVAHmgySjkrJmyNPnPW0GrpBt2u4/6D+EQJ8PCCMNWuJvrljBCdlmgOFsT38qYhfcw==
    -
    -"@lumino/disposable@^1.3.5":
    -  version "1.3.5"
    -  resolved "https://registry.yarnpkg.com/@lumino/disposable/-/disposable-1.3.5.tgz#3562ca063117fd2a0735df170f51e41620fa21d0"
    -  integrity sha512-IWDAd+nysBnwLhEtW7M62PVk84OEex9OEktZsS6V+19n/o8/Rw4ccL0pt0GFby01CsVK0YcELDoDaMUZsMiAmA==
    -  dependencies:
    -    "@lumino/algorithm" "^1.2.3"
    -    "@lumino/signaling" "^1.3.5"
    -
    -"@lumino/domutils@^1.1.7":
    -  version "1.1.7"
    -  resolved "https://registry.yarnpkg.com/@lumino/domutils/-/domutils-1.1.7.tgz#9cc16cba0c1e8f31fcb734879dec050505925b16"
    -  integrity sha512-NPysY8XfpCvLNvDe+z1caIUPxOLXWRPQMUAjOj/EhggRyXadan6Lm/5uO6M9S5gW/v9QUXT4+1Sxe3WXz0nRCA==
    +"@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/dragdrop@^1.5.1":
    -  version "1.5.1"
    -  resolved "https://registry.yarnpkg.com/@lumino/dragdrop/-/dragdrop-1.5.1.tgz#502305183d430693edc112f7c234a3d9f2d89f02"
    -  integrity sha512-MFg/hy6hHdPwBZypBue5mlrBzjoNrtBQzzJW+kbM5ftAOvS99ZRgyMMlMQcbsHd+6yib9NOQ64Hd8P8uZEWTdw==
    +"@lumino/messaging@^1.3.3", "@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/coreutils" "^1.4.2"
    -    "@lumino/disposable" "^1.3.5"
    -
    -"@lumino/keyboard@^1.1.6":
    -  version "1.1.6"
    -  resolved "https://registry.yarnpkg.com/@lumino/keyboard/-/keyboard-1.1.6.tgz#bf222369bbeacf2c7d2dfe5003d52736c5a2fc3d"
    -  integrity sha512-W6pqe0TXRfGOoz1ZK1PRmuGZUWpmdoJArrzwmduUf0t2r06yl56S7w76gxrB7ExTidNPPaOWydGIosPgdgZf5A==
    +    "@lumino/algorithm" "^1.3.3"
    +    "@lumino/collections" "^1.3.3"
     
    -"@lumino/messaging@^1.3.3":
    +"@lumino/polling@^1.1.1":
       version "1.3.3"
    -  resolved "https://registry.yarnpkg.com/@lumino/messaging/-/messaging-1.3.3.tgz#75d3c880b11087da130554eeefa9a19572b24d22"
    -  integrity sha512-J+0m1aywl64I9/dr9fzE9IwC+eq90T5gUi1hCXP1MFnZh4aLUymmRV5zFw1CNh/vYlNnEu72xxEuhfCfuhiy8g==
    +  resolved "https://registry.yarnpkg.com/@lumino/polling/-/polling-1.3.3.tgz#6336638cb9ba2f4f4c3ef2529c7f260abbd25148"
    +  integrity sha512-uMRi6sPRnKW8m38WUY3qox1jxwzpvceafUbDJATCwyrZ48+YoY5Fxfmd9dqwioHS1aq9np5c6L35a9ZGuS0Maw==
       dependencies:
    -    "@lumino/algorithm" "^1.2.3"
    -    "@lumino/collections" "^1.2.3"
    +    "@lumino/coreutils" "^1.5.3"
    +    "@lumino/disposable" "^1.4.3"
    +    "@lumino/signaling" "^1.4.3"
     
    -"@lumino/polling@^1.0.4":
    -  version "1.0.4"
    -  resolved "https://registry.yarnpkg.com/@lumino/polling/-/polling-1.0.4.tgz#85f956933fa63c47edf808c141cdb9a7a1a49f4c"
    -  integrity sha512-9OYIDTohToj6SLrxOr+FbeyPvirBU/r53FgmPxulcDgUVnVk4tqTSLIJAUu3mjJd9hnmZZqpSn9ppyjQAo3qSg==
    -  dependencies:
    -    "@lumino/coreutils" "^1.4.2"
    -    "@lumino/disposable" "^1.3.5"
    -    "@lumino/signaling" "^1.3.5"
    -
    -"@lumino/properties@^1.1.6":
    -  version "1.1.6"
    -  resolved "https://registry.yarnpkg.com/@lumino/properties/-/properties-1.1.6.tgz#367538d63453e99e8c94e5559748a0713d9874ac"
    -  integrity sha512-QnZa1IB7sr4Tawf0OKvwgZAptxDRK7DUAMJ71zijXNXH4FlxyThzOWXef51HHFsISKYa8Rn3rysOwtc62XkmXw==
    -
    -"@lumino/signaling@^1.3.5":
    -  version "1.3.5"
    -  resolved "https://registry.yarnpkg.com/@lumino/signaling/-/signaling-1.3.5.tgz#21d77cf201c429f9824e04c19f0cc04027f963c8"
    -  integrity sha512-6jniKrLrJOXKJmaJyU7hr6PBzE4GJ5Wms5hc/yzNKKDBxGSEPdtNJlW3wTNUuSTTtF/9ItN8A8ZC/G0yIu53Tw==
    -  dependencies:
    -    "@lumino/algorithm" "^1.2.3"
    +"@lumino/properties@^1.1.6", "@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/virtualdom@^1.6.1":
    -  version "1.6.1"
    -  resolved "https://registry.yarnpkg.com/@lumino/virtualdom/-/virtualdom-1.6.1.tgz#7f190091e065e7e4e4814836ed5b293aa8359b2d"
    -  integrity sha512-+KdzSw8TCPwvK6qhZr4xTyp6HymvEb2Da0xPdi4RsVUNhYf2gBI03uidXHx76Vx5OIbEgCn1B+0srxvm2ZbWsQ==
    +"@lumino/signaling@^1.3.5", "@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.2.3"
    +    "@lumino/algorithm" "^1.3.3"
     
    -"@lumino/widgets@^1.11.1":
    -  version "1.11.1"
    -  resolved "https://registry.yarnpkg.com/@lumino/widgets/-/widgets-1.11.1.tgz#2aba526f1dba7cb004786f25b3bc4a58bd8fe14d"
    -  integrity sha512-f4QDe6lVNPcjL8Vb20BiP0gzbT1rx0/1Hc719u5oW9c0Z/xrXMWwNhnb/zYM/kBBVBe3omLmCfJOiNuE0oZl0A==
    -  dependencies:
    -    "@lumino/algorithm" "^1.2.3"
    -    "@lumino/commands" "^1.10.1"
    -    "@lumino/coreutils" "^1.4.2"
    -    "@lumino/disposable" "^1.3.5"
    -    "@lumino/domutils" "^1.1.7"
    -    "@lumino/dragdrop" "^1.5.1"
    -    "@lumino/keyboard" "^1.1.6"
    -    "@lumino/messaging" "^1.3.3"
    -    "@lumino/properties" "^1.1.6"
    -    "@lumino/signaling" "^1.3.5"
    -    "@lumino/virtualdom" "^1.6.1"
    +"@lumino/virtualdom@^1.6.1", "@lumino/virtualdom@^1.7.3":
    +  version "1.7.3"
    +  resolved "https://registry.yarnpkg.com/@lumino/virtualdom/-/virtualdom-1.7.3.tgz#57586b088feeeedd020c0815ea5d3159519bd83e"
    +  integrity sha512-YgQyyo5F7nMfcp5wbpJQyBsztFqAQPO1++sbPCJiF8Mt0Zo5+hN0jWG2tw7IymHdXDNypgnrCiiHQZMUXuzCiA==
    +  dependencies:
    +    "@lumino/algorithm" "^1.3.3"
    +
    +"@lumino/widgets@^1.11.1", "@lumino/widgets@^1.14.1":
    +  version "1.14.1"
    +  resolved "https://registry.yarnpkg.com/@lumino/widgets/-/widgets-1.14.1.tgz#2a6c40c207e78635101dc18e2e43e71a2e31c3e3"
    +  integrity sha512-gdar1+y+0k8nm2LCm/m4qTICYRRRv8L46xhGDe8D0xWsuLVP3OEuYGMmexRuk0ep7G/F5exMY0FvG4va6pqOCQ==
    +  dependencies:
    +    "@lumino/algorithm" "^1.3.3"
    +    "@lumino/commands" "^1.11.4"
    +    "@lumino/coreutils" "^1.5.3"
    +    "@lumino/disposable" "^1.4.3"
    +    "@lumino/domutils" "^1.2.3"
    +    "@lumino/dragdrop" "^1.6.4"
    +    "@lumino/keyboard" "^1.2.3"
    +    "@lumino/messaging" "^1.4.3"
    +    "@lumino/properties" "^1.2.3"
    +    "@lumino/signaling" "^1.4.3"
    +    "@lumino/virtualdom" "^1.7.3"
     
     "@types/codemirror@^0.0.74":
       version "0.0.74"
    @@ -458,9 +563,9 @@
       integrity sha512-kSkVAvWmMZiCYtvqjqQEwOmvKwcH+V4uiv3qPQ8pAh1Xl39xggGEo8gHUqV4waYGHezdFw0rKBR8Jt0CrQSDZA==
     
     "@types/estree@*":
    -  version "0.0.39"
    -  resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.39.tgz#e177e699ee1b8c22d23174caaa7422644389509f"
    -  integrity sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==
    +  version "0.0.45"
    +  resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.45.tgz#e9387572998e5ecdac221950dab3e8c3b16af884"
    +  integrity sha512-jnqIUKDUqJbDIUxm0Uj7bnlMnRm1T/eZ9N+AVMqhPgzrba2GhGG5o/jCTwmdPK709nEZsGoMzXEDUjcXHa3W0g==
     
     "@types/linkify-it@*":
       version "2.1.0"
    @@ -468,18 +573,19 @@
       integrity sha512-Q7DYAOi9O/+cLLhdaSvKdaumWyHbm7HAk/bFwwyTuU0arR5yyCeW5GOoqt4tJTpDRxhpx9Q8kQL6vMpuw9hDSw==
     
     "@types/markdown-it-container@^2.0.2":
    -  version "2.0.2"
    -  resolved "https://registry.yarnpkg.com/@types/markdown-it-container/-/markdown-it-container-2.0.2.tgz#0e624653415a1c2f088a5ae51f7bfff480c03f49"
    -  integrity sha512-T770GL+zJz8Ssh1NpLiOruYhrU96yb8ovPSegLrWY5XIkJc6PVVC7kH/oQaVD0rkePpWMFJK018OgS/pwviOMw==
    +  version "2.0.3"
    +  resolved "https://registry.yarnpkg.com/@types/markdown-it-container/-/markdown-it-container-2.0.3.tgz#436de4c019d7d71b60f759037fd4d03611569eb8"
    +  integrity sha512-ouJluaEGWV7clX7NVMRjkQfS/a11hFXDG1U04l8vrS1P2UgAFPlgMpk1rAPgK0MWU1NhcBYWVW7w/SpgePLs0A==
       dependencies:
         "@types/markdown-it" "*"
     
     "@types/markdown-it@*":
    -  version "0.0.8"
    -  resolved "https://registry.yarnpkg.com/@types/markdown-it/-/markdown-it-0.0.8.tgz#9af8704acde87fec70475369ba0413d50717bd8d"
    -  integrity sha512-ouaTOi5kAdkTPl97u6uDkth9od4pQffPF9STcjYVZKFrEwLYf15s7Z772WxWE3IOcYBJglaT0XqdyNEiEfGgYg==
    +  version "10.0.2"
    +  resolved "https://registry.yarnpkg.com/@types/markdown-it/-/markdown-it-10.0.2.tgz#f93334b9c7821ddb19865dfd91ecf688094c2626"
    +  integrity sha512-FGKiVW1UgeIEAChYAuHcfCd0W4LsMEyrSyTVaZiuJhwR4BwSVUD8JKnzmWAMK2FHNLZSPGUaEkpa/dkZj2uq1w==
       dependencies:
         "@types/linkify-it" "*"
    +    "@types/mdurl" "*"
     
     "@types/markdown-it@^0.0.9":
       version "0.0.9"
    @@ -488,18 +594,23 @@
       dependencies:
         "@types/linkify-it" "*"
     
    +"@types/mdurl@*":
    +  version "1.0.2"
    +  resolved "https://registry.yarnpkg.com/@types/mdurl/-/mdurl-1.0.2.tgz#e2ce9d83a613bacf284c7be7d491945e39e1f8e9"
    +  integrity sha512-eC4U9MlIcu2q0KQmXszyn5Akca/0jrQmwDRgpAMJai7qBWq4amIQhZyNau4VYGtCeALvW1/NtjzJJ567aZxfKA==
    +
     "@types/prop-types@*":
    -  version "15.7.1"
    -  resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.1.tgz#f1a11e7babb0c3cad68100be381d1e064c68f1f6"
    -  integrity sha512-CFzn9idOEpHrgdw8JsoTkaDDyRWk1jrzIV8djzcgpq0y9tG4B4lFT+Nxh52DVpDXV+n4+NPNv7M1Dj5uMp6XFg==
    +  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@~16.9.16":
    -  version "16.9.23"
    -  resolved "https://registry.yarnpkg.com/@types/react/-/react-16.9.23.tgz#1a66c6d468ba11a8943ad958a8cb3e737568271c"
    -  integrity sha512-SsGVT4E7L2wLN3tPYLiF20hmZTPGuzaayVunfgXzUn1x4uHVsKH6QDJQ/TdpHqwsTLd4CwrmQ2vOgxN7gE24gw==
    +  version "16.9.55"
    +  resolved "https://registry.yarnpkg.com/@types/react/-/react-16.9.55.tgz#47078587f5bfe028a23b6b46c7b94ac0d436acff"
    +  integrity sha512-6KLe6lkILeRwyyy7yG9rULKJ0sXplUsl98MGoCfpteXf9sPWFWWMknDcsvubcpaTdBuxtsLF6HDUwdApZL/xIg==
       dependencies:
         "@types/prop-types" "*"
    -    csstype "^2.2.0"
    +    csstype "^3.0.2"
     
     "@types/tern@*":
       version "0.23.3"
    @@ -509,9 +620,9 @@
         "@types/estree" "*"
     
     ajv@^6.10.2:
    -  version "6.12.0"
    -  resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.0.tgz#06d60b96d87b8454a5adaba86e7854da629db4b7"
    -  integrity sha512-D6gFiFA0RRLyUbvijN74DWAjXSFxWKaWP7mldxkVhyhAV3+SWA9HEJPHQ2c9soIeTFJqcSdFDGFgdqs1iUU2Hw==
    +  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"
    @@ -537,6 +648,18 @@ array-uniq@^1.0.2:
       resolved "https://registry.yarnpkg.com/array-uniq/-/array-uniq-1.0.3.tgz#af6ac877a25cc7f74e058894753858dfdb24fdb6"
       integrity sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=
     
    +babel-eslint@^10.1.0:
    +  version "10.1.0"
    +  resolved "https://registry.yarnpkg.com/babel-eslint/-/babel-eslint-10.1.0.tgz#6968e568a910b78fb3779cdd8b6ac2f479943232"
    +  integrity sha512-ifWaTHQ0ce+448CYop8AdrQiBsGrnC+bMgfyKFdi6EsPLTAWG+QfyDeM6OH+FmWnKvEq5NnBMLvlBUPKQZoDSg==
    +  dependencies:
    +    "@babel/code-frame" "^7.0.0"
    +    "@babel/parser" "^7.7.0"
    +    "@babel/traverse" "^7.7.0"
    +    "@babel/types" "^7.7.0"
    +    eslint-visitor-keys "^1.0.0"
    +    resolve "^1.12.0"
    +
     balanced-match@^1.0.0:
       version "1.0.0"
       resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767"
    @@ -555,6 +678,14 @@ buffer-from@^1.0.0:
       resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef"
       integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==
     
    +call-bind@^1.0.0:
    +  version "1.0.0"
    +  resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.0.tgz#24127054bb3f9bdcb4b1fb82418186072f77b8ce"
    +  integrity sha512-AEXsYIyyDY3MCzbwdhzG3Jx1R0J2wetQyUynn6dYHAO+bg8l1k7jwZtRv4ryryFs7EP+NDlikJlVe59jr0cM2w==
    +  dependencies:
    +    function-bind "^1.1.1"
    +    get-intrinsic "^1.0.0"
    +
     camel-case@^3.0.0:
       version "3.0.0"
       resolved "https://registry.yarnpkg.com/camel-case/-/camel-case-3.0.0.tgz#ca3c3688a4e9cf3a4cda777dc4dcbc713249cf73"
    @@ -563,7 +694,7 @@ camel-case@^3.0.0:
         no-case "^2.2.0"
         upper-case "^1.1.1"
     
    -chalk@^2.4.1, chalk@^2.4.2:
    +chalk@^2.0.0, chalk@^2.4.1, 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==
    @@ -578,16 +709,16 @@ classnames@^2.2:
       integrity sha512-JR/iSQOSt+LQIWwrwEzJ9uk0xfN3mTVYMwt1Ir5mUcSN6pU+V4zQFFaJsclJbPuAUQH+yfWef6tm7l1quW3C8Q==
     
     clean-css@^4.1.6, clean-css@^4.2.1:
    -  version "4.2.1"
    -  resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-4.2.1.tgz#2d411ef76b8569b6d0c84068dabe85b0aa5e5c17"
    -  integrity sha512-4ZxI6dy4lrY6FHzfiy1aEOXgu4LIsW2MhwG0VBKdcoGoH/XLFgaHSdLTGr4O8Be6A8r3MOphEiI8Gc1n0ecf3g==
    +  version "4.2.3"
    +  resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-4.2.3.tgz#507b5de7d97b48ee53d84adb0160ff6216380f78"
    +  integrity sha512-VcMWDN54ZN/DS+g58HYL5/n4Zrqe8vHJpGA8KdgUXFU4fuP/aHNw8eld9SyEIyabIMJX/0RaY/fplOo5hYLSFA==
       dependencies:
         source-map "~0.6.0"
     
    -codemirror@~5.49.2:
    -  version "5.49.2"
    -  resolved "https://registry.yarnpkg.com/codemirror/-/codemirror-5.49.2.tgz#c84fdaf11b19803f828b0c67060c7bc6d154ccad"
    -  integrity sha512-dwJ2HRPHm8w51WB5YTF9J7m6Z5dtkqbU9ntMZ1dqXyFB9IpjoUFDj80ahRVEoVanfIp6pfASJbOlbWdEf8FOzQ==
    +codemirror@~5.53.2:
    +  version "5.53.2"
    +  resolved "https://registry.yarnpkg.com/codemirror/-/codemirror-5.53.2.tgz#9799121cf8c50809cca487304e9de3a74d33f428"
    +  integrity sha512-wvSQKS4E+P8Fxn/AQ+tQtJnF1qH5UOlxtugFLpubEZ5jcdH2iXTVinb+Xc/4QjshuOxRm4fUsU2QPF1JJKiyXA==
     
     color-convert@^1.9.0:
       version "1.9.3"
    @@ -601,10 +732,10 @@ color-name@1.1.3:
       resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25"
       integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=
     
    -commander@2, commander@^2.19.0, commander@^2.20.0, commander@~2.20.0:
    -  version "2.20.0"
    -  resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.0.tgz#d58bb2b5c1ee8f87b0d340027e9e94e222c5a422"
    -  integrity sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ==
    +commander@2, commander@^2.19.0, commander@^2.20.0:
    +  version "2.20.3"
    +  resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33"
    +  integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==
     
     concat-map@0.0.1:
       version "0.0.1"
    @@ -619,28 +750,26 @@ create-react-context@^0.3.0:
         gud "^1.0.0"
         warning "^4.0.3"
     
    -crypto-random-string@^3.0.1:
    -  version "3.2.0"
    -  resolved "https://registry.yarnpkg.com/crypto-random-string/-/crypto-random-string-3.2.0.tgz#d513ef0c2ac6ff7cad5769de585d9bf2ad5a2b4d"
    -  integrity sha512-8vPu5bsKaq2uKRy3OL7h1Oo7RayAWB8sYexLKAqvCXVib8SxgbmoF1IN4QMKjBv8uI8mp5gPPMbiRah25GMrVQ==
    -  dependencies:
    -    type-fest "^0.8.1"
    -
     css-b64-images@~0.2.5:
       version "0.2.5"
       resolved "https://registry.yarnpkg.com/css-b64-images/-/css-b64-images-0.2.5.tgz#42005d83204b2b4a5d93b6b1a5644133b5927a02"
       integrity sha1-QgBdgyBLK0pdk7axpWRBM7WSegI=
     
    -csstype@^2.2.0, csstype@^2.4.0:
    -  version "2.6.6"
    -  resolved "https://registry.yarnpkg.com/csstype/-/csstype-2.6.6.tgz#c34f8226a94bbb10c32cc0d714afdf942291fc41"
    -  integrity sha512-RpFbQGUE74iyPgvr46U9t1xoQBM8T4BL8SxrN66Le2xYAPSaDJJKeztV3awugusb3g3G9iL8StmkBBXhcbbXhg==
    -
    -csstype@~2.6.9:
    +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:
    +  version "3.0.4"
    +  resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.0.4.tgz#b156d7be03b84ff425c9a0a4b1e5f4da9c5ca888"
    +  integrity sha512-xc8DUsCLmjvCfoD7LTGE0ou2MIWLx0K9RCZwSHMOdynqRsP4MtUcLeqh1HcQ2dInwDTqn+3CE0/FZh1et+p4jA==
    +
    +csstype@~2.6.9:
    +  version "2.6.13"
    +  resolved "https://registry.yarnpkg.com/csstype/-/csstype-2.6.13.tgz#a6893015b90e84dd6e85d0e3b442a1e84f2dbe0f"
    +  integrity sha512-ul26pfSQTZW8dcOnD2iiJssfXw0gdNVX9IJDH/X3K5DGPfj+fUYe3kB+swUY6BF3oZDxaID3AJt+9/ojSAE05A==
    +
     d3-array@1, d3-array@^1.1.1, d3-array@^1.2.0:
       version "1.2.4"
       resolved "https://registry.yarnpkg.com/d3-array/-/d3-array-1.2.4.tgz#635ce4d5eea759f6f605863dbcfc30edc737f71f"
    @@ -652,9 +781,9 @@ d3-axis@1:
       integrity sha512-ejINPfPSNdGFKEOAtnBtdkpr24c4d4jsei6Lg98mxf424ivoDP2956/5HDpIAtmHo85lqT4pruy+zEgvRUBqaQ==
     
     d3-brush@1:
    -  version "1.0.6"
    -  resolved "https://registry.yarnpkg.com/d3-brush/-/d3-brush-1.0.6.tgz#33691f2032d9db6c5d8cb684ff255a9883629e21"
    -  integrity sha512-lGSiF5SoSqO5/mYGD5FAeGKKS62JdA1EV7HPrU2b5rTX4qEJJtpjaGLJngjnkewQy7UnGstnFd3168wpf5z76w==
    +  version "1.1.6"
    +  resolved "https://registry.yarnpkg.com/d3-brush/-/d3-brush-1.1.6.tgz#b0a22c7372cabec128bdddf9bddc058592f89e9b"
    +  integrity sha512-7RW+w7HfMCPyZLifTz/UnJmI5kdkXtpCbombUSs8xniAyo0vIbrDzDwUJB6eJOgl9u5DQOt2TQlYumxzD1SvYA==
       dependencies:
         d3-dispatch "1"
         d3-drag "1"
    @@ -676,9 +805,9 @@ d3-collection@1:
       integrity sha512-ii0/r5f4sjKNTfh84Di+DpztYwqKhEyUlKoPrzUFfeSkWxjW49xU2QzO9qrPrNkpdI0XJkfzvmTu8V2Zylln6A==
     
     d3-color@1:
    -  version "1.2.8"
    -  resolved "https://registry.yarnpkg.com/d3-color/-/d3-color-1.2.8.tgz#4eaf9b60ef188c893fcf8b28f3546aafebfbd9f4"
    -  integrity sha512-yeANXzP37PHk0DbSTMNPhnJD+Nn4G//O5E825bR6fAfHH43hobSBpgB9G9oWVl9+XgUaQ4yCnsX1H+l8DoaL9A==
    +  version "1.4.1"
    +  resolved "https://registry.yarnpkg.com/d3-color/-/d3-color-1.4.1.tgz#c52002bf8846ada4424d55d97982fef26eb3bc8a"
    +  integrity sha512-p2sTHSLCJI2QKunbGb7ocOh7DgTAn8IrLx21QRc/BSnodXM4sv6aLQlnfpvehFMLZEfBc6g9pH9SWQccFYfJ9Q==
     
     d3-contour@1:
       version "1.3.2"
    @@ -688,36 +817,36 @@ d3-contour@1:
         d3-array "^1.1.1"
     
     d3-dispatch@1:
    -  version "1.0.5"
    -  resolved "https://registry.yarnpkg.com/d3-dispatch/-/d3-dispatch-1.0.5.tgz#e25c10a186517cd6c82dd19ea018f07e01e39015"
    -  integrity sha512-vwKx+lAqB1UuCeklr6Jh1bvC4SZgbSqbkGBLClItFBIYH4vqDJCA7qfoy14lXmJdnBOdxndAMxjCbImJYW7e6g==
    +  version "1.0.6"
    +  resolved "https://registry.yarnpkg.com/d3-dispatch/-/d3-dispatch-1.0.6.tgz#00d37bcee4dd8cd97729dd893a0ac29caaba5d58"
    +  integrity sha512-fVjoElzjhCEy+Hbn8KygnmMS7Or0a9sI2UzGwoB7cCtvI1XpVN9GpoYlnb3xt2YV66oXYb1fLJ8GMvP4hdU1RA==
     
     d3-drag@1:
    -  version "1.2.3"
    -  resolved "https://registry.yarnpkg.com/d3-drag/-/d3-drag-1.2.3.tgz#46e206ad863ec465d88c588098a1df444cd33c64"
    -  integrity sha512-8S3HWCAg+ilzjJsNtWW1Mutl74Nmzhb9yU6igspilaJzeZVFktmY6oO9xOh5TDk+BM2KrNFjttZNoJJmDnkjkg==
    +  version "1.2.5"
    +  resolved "https://registry.yarnpkg.com/d3-drag/-/d3-drag-1.2.5.tgz#2537f451acd39d31406677b7dc77c82f7d988f70"
    +  integrity sha512-rD1ohlkKQwMZYkQlYVCrSFxsWPzI97+W+PaEIBNTMxRuxz9RF0Hi5nJWHGVJ3Om9d2fRTe1yOBINJyy/ahV95w==
       dependencies:
         d3-dispatch "1"
         d3-selection "1"
     
     d3-dsv@1:
    -  version "1.1.1"
    -  resolved "https://registry.yarnpkg.com/d3-dsv/-/d3-dsv-1.1.1.tgz#aaa830ecb76c4b5015572c647cc6441e3c7bb701"
    -  integrity sha512-1EH1oRGSkeDUlDRbhsFytAXU6cAmXFzc52YUe6MRlPClmWb85MP1J5x+YJRzya4ynZWnbELdSAvATFW/MbxaXw==
    +  version "1.2.0"
    +  resolved "https://registry.yarnpkg.com/d3-dsv/-/d3-dsv-1.2.0.tgz#9d5f75c3a5f8abd611f74d3f5847b0d4338b885c"
    +  integrity sha512-9yVlqvZcSOMhCYzniHE7EVUws7Fa1zgw+/EAV2BxJoG3ME19V6BQFBwI855XQDsxyOuG7NibqRMTtiF/Qup46g==
       dependencies:
         commander "2"
         iconv-lite "0.4"
         rw "1"
     
     d3-ease@1:
    -  version "1.0.5"
    -  resolved "https://registry.yarnpkg.com/d3-ease/-/d3-ease-1.0.5.tgz#8ce59276d81241b1b72042d6af2d40e76d936ffb"
    -  integrity sha512-Ct1O//ly5y5lFM9YTdu+ygq7LleSgSE4oj7vUt9tPLHUi8VCV7QoizGpdWRWAwCO9LdYzIrQDg97+hGVdsSGPQ==
    +  version "1.0.7"
    +  resolved "https://registry.yarnpkg.com/d3-ease/-/d3-ease-1.0.7.tgz#9a834890ef8b8ae8c558b2fe55bd57f5993b85e2"
    +  integrity sha512-lx14ZPYkhNx0s/2HX5sLFUI3mbasHjSSpwO/KaaNACweVwxUruKyWVcb293wMv1RqTPZyZ8kSZ2NogUZNcLOFQ==
     
     d3-fetch@1:
    -  version "1.1.2"
    -  resolved "https://registry.yarnpkg.com/d3-fetch/-/d3-fetch-1.1.2.tgz#957c8fbc6d4480599ba191b1b2518bf86b3e1be2"
    -  integrity sha512-S2loaQCV/ZeyTyIF2oP8D1K9Z4QizUzW7cWeAOAS4U88qOt3Ucf6GsmgthuYSdyB2HyEm4CeGvkQxWsmInsIVA==
    +  version "1.2.0"
    +  resolved "https://registry.yarnpkg.com/d3-fetch/-/d3-fetch-1.2.0.tgz#15ce2ecfc41b092b1db50abd2c552c2316cf7fc7"
    +  integrity sha512-yC78NBVcd2zFAyR/HnUiBS7Lf6inSCoWcSxFfw8FYL7ydiqe80SazNwoffcqOfs95XaLo7yebsmQqDKSsXUtvA==
       dependencies:
         d3-dsv "1"
     
    @@ -732,43 +861,43 @@ d3-force@1:
         d3-timer "1"
     
     d3-format@1:
    -  version "1.3.2"
    -  resolved "https://registry.yarnpkg.com/d3-format/-/d3-format-1.3.2.tgz#6a96b5e31bcb98122a30863f7d92365c00603562"
    -  integrity sha512-Z18Dprj96ExragQ0DeGi+SYPQ7pPfRMtUXtsg/ChVIKNBCzjO8XYJvRTC1usblx52lqge56V5ect+frYTQc8WQ==
    +  version "1.4.5"
    +  resolved "https://registry.yarnpkg.com/d3-format/-/d3-format-1.4.5.tgz#374f2ba1320e3717eb74a9356c67daee17a7edb4"
    +  integrity sha512-J0piedu6Z8iB6TbIGfZgDzfXxUFN3qQRMofy2oPdXzQibYGqPB/9iMcxr/TGalU+2RsyDO+U4f33id8tbnSRMQ==
     
     d3-geo@1:
    -  version "1.11.6"
    -  resolved "https://registry.yarnpkg.com/d3-geo/-/d3-geo-1.11.6.tgz#134f2ef035ff75a448075fafdea92702a2e0e0cf"
    -  integrity sha512-z0J8InXR9e9wcgNtmVnPTj0TU8nhYT6lD/ak9may2PdKqXIeHUr8UbFLoCtrPYNsjv6YaLvSDQVl578k6nm7GA==
    +  version "1.12.1"
    +  resolved "https://registry.yarnpkg.com/d3-geo/-/d3-geo-1.12.1.tgz#7fc2ab7414b72e59fbcbd603e80d9adc029b035f"
    +  integrity sha512-XG4d1c/UJSEX9NfU02KwBL6BYPj8YKHxgBEw5om2ZnTRSbIcego6dhHwcxuSR3clxh0EpE38os1DVPOmnYtTPg==
       dependencies:
         d3-array "1"
     
     d3-hierarchy@1:
    -  version "1.1.8"
    -  resolved "https://registry.yarnpkg.com/d3-hierarchy/-/d3-hierarchy-1.1.8.tgz#7a6317bd3ed24e324641b6f1e76e978836b008cc"
    -  integrity sha512-L+GHMSZNwTpiq4rt9GEsNcpLa4M96lXMR8M/nMG9p5hBE0jy6C+3hWtyZMenPQdwla249iJy7Nx0uKt3n+u9+w==
    +  version "1.1.9"
    +  resolved "https://registry.yarnpkg.com/d3-hierarchy/-/d3-hierarchy-1.1.9.tgz#2f6bee24caaea43f8dc37545fa01628559647a83"
    +  integrity sha512-j8tPxlqh1srJHAtxfvOUwKNYJkQuBFdM1+JAUfq6xqH5eAqf93L7oG1NVqDa4CpFZNvnNKtCYEUC8KY9yEn9lQ==
     
     d3-interpolate@1:
    -  version "1.3.2"
    -  resolved "https://registry.yarnpkg.com/d3-interpolate/-/d3-interpolate-1.3.2.tgz#417d3ebdeb4bc4efcc8fd4361c55e4040211fd68"
    -  integrity sha512-NlNKGopqaz9qM1PXh9gBF1KSCVh+jSFErrSlD/4hybwoNX/gt1d8CDbDW+3i+5UOHhjC6s6nMvRxcuoMVNgL2w==
    +  version "1.4.0"
    +  resolved "https://registry.yarnpkg.com/d3-interpolate/-/d3-interpolate-1.4.0.tgz#526e79e2d80daa383f9e0c1c1c7dcc0f0583e987"
    +  integrity sha512-V9znK0zc3jOPV4VD2zZn0sDhZU3WAE2bmlxdIwwQPPzPjvyLkd8B3JUVdS1IDUFDkWZ72c9qnv1GK2ZagTZ8EA==
       dependencies:
         d3-color "1"
     
     d3-path@1:
    -  version "1.0.7"
    -  resolved "https://registry.yarnpkg.com/d3-path/-/d3-path-1.0.7.tgz#8de7cd693a75ac0b5480d3abaccd94793e58aae8"
    -  integrity sha512-q0cW1RpvA5c5ma2rch62mX8AYaiLX0+bdaSM2wxSU9tXjU4DNvkx9qiUvjkuWCj3p22UO/hlPivujqMiR9PDzA==
    +  version "1.0.9"
    +  resolved "https://registry.yarnpkg.com/d3-path/-/d3-path-1.0.9.tgz#48c050bb1fe8c262493a8caf5524e3e9591701cf"
    +  integrity sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg==
     
     d3-polygon@1:
    -  version "1.0.5"
    -  resolved "https://registry.yarnpkg.com/d3-polygon/-/d3-polygon-1.0.5.tgz#9a645a0a64ff6cbf9efda96ee0b4a6909184c363"
    -  integrity sha512-RHhh1ZUJZfhgoqzWWuRhzQJvO7LavchhitSTHGu9oj6uuLFzYZVeBzaWTQ2qSO6bz2w55RMoOCf0MsLCDB6e0w==
    +  version "1.0.6"
    +  resolved "https://registry.yarnpkg.com/d3-polygon/-/d3-polygon-1.0.6.tgz#0bf8cb8180a6dc107f518ddf7975e12abbfbd38e"
    +  integrity sha512-k+RF7WvI08PC8reEoXa/w2nSg5AUMTi+peBD9cmFc+0ixHfbs4QmxxkarVal1IkVkgxVuk9JSHhJURHiyHKAuQ==
     
     d3-quadtree@1:
    -  version "1.0.6"
    -  resolved "https://registry.yarnpkg.com/d3-quadtree/-/d3-quadtree-1.0.6.tgz#d1ab2a95a7f27bbde88582c94166f6ae35f32056"
    -  integrity sha512-NUgeo9G+ENQCQ1LsRr2qJg3MQ4DJvxcDNCiohdJGHt5gRhBW6orIB5m5FJ9kK3HNL8g9F4ERVoBzcEwQBfXWVA==
    +  version "1.0.7"
    +  resolved "https://registry.yarnpkg.com/d3-quadtree/-/d3-quadtree-1.0.7.tgz#ca8b84df7bb53763fe3c2f24bd435137f4e53135"
    +  integrity sha512-RKPAeXnkC59IDGD0Wu5mANy0Q2V28L+fNe65pOCXVdVuTJS3WPKaJlFHer32Rbh9gIo9qMuJXio8ra4+YmIymA==
     
     d3-random@1:
       version "1.1.2"
    @@ -776,9 +905,9 @@ d3-random@1:
       integrity sha512-6AK5BNpIFqP+cx/sreKzNjWbwZQCSUatxq+pPRmFIQaWuoD+NrbVWw7YWpHiXpCQ/NanKdtGDuB+VQcZDaEmYQ==
     
     d3-scale-chromatic@1:
    -  version "1.3.3"
    -  resolved "https://registry.yarnpkg.com/d3-scale-chromatic/-/d3-scale-chromatic-1.3.3.tgz#dad4366f0edcb288f490128979c3c793583ed3c0"
    -  integrity sha512-BWTipif1CimXcYfT02LKjAyItX5gKiwxuPRgr4xM58JwlLocWbjPLI7aMEjkcoOQXMkYsmNsvv3d2yl/OKuHHw==
    +  version "1.5.0"
    +  resolved "https://registry.yarnpkg.com/d3-scale-chromatic/-/d3-scale-chromatic-1.5.0.tgz#54e333fc78212f439b14641fb55801dd81135a98"
    +  integrity sha512-ACcL46DYImpRFMBcpk9HhtIyC7bTBR4fNOPxwVSl0LfulDAwyiHyPOTqcDG1+t5d4P9W7t/2NAuWu59aKko/cg==
       dependencies:
         d3-color "1"
         d3-interpolate "1"
    @@ -796,38 +925,38 @@ d3-scale@2:
         d3-time-format "2"
     
     d3-selection@1, d3-selection@^1.1.0:
    -  version "1.4.0"
    -  resolved "https://registry.yarnpkg.com/d3-selection/-/d3-selection-1.4.0.tgz#ab9ac1e664cf967ebf1b479cc07e28ce9908c474"
    -  integrity sha512-EYVwBxQGEjLCKF2pJ4+yrErskDnz5v403qvAid96cNdCMr8rmCYfY5RGzWz24mdIbxmDf6/4EAH+K9xperD5jg==
    +  version "1.4.2"
    +  resolved "https://registry.yarnpkg.com/d3-selection/-/d3-selection-1.4.2.tgz#dcaa49522c0dbf32d6c1858afc26b6094555bc5c"
    +  integrity sha512-SJ0BqYihzOjDnnlfyeHT0e30k0K1+5sR3d5fNueCNeuhZTnGw4M4o8mqJchSwgKMXCNFo+e2VTChiSJ0vYtXkg==
     
     d3-shape@1:
    -  version "1.3.5"
    -  resolved "https://registry.yarnpkg.com/d3-shape/-/d3-shape-1.3.5.tgz#e81aea5940f59f0a79cfccac012232a8987c6033"
    -  integrity sha512-VKazVR3phgD+MUCldapHD7P9kcrvPcexeX/PkMJmkUov4JM8IxsSg1DvbYoYich9AtdTsa5nNk2++ImPiDiSxg==
    +  version "1.3.7"
    +  resolved "https://registry.yarnpkg.com/d3-shape/-/d3-shape-1.3.7.tgz#df63801be07bc986bc54f63789b4fe502992b5d7"
    +  integrity sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw==
       dependencies:
         d3-path "1"
     
     d3-time-format@2:
    -  version "2.1.3"
    -  resolved "https://registry.yarnpkg.com/d3-time-format/-/d3-time-format-2.1.3.tgz#ae06f8e0126a9d60d6364eac5b1533ae1bac826b"
    -  integrity sha512-6k0a2rZryzGm5Ihx+aFMuO1GgelgIz+7HhB4PH4OEndD5q2zGn1mDfRdNrulspOfR6JXkb2sThhDK41CSK85QA==
    +  version "2.3.0"
    +  resolved "https://registry.yarnpkg.com/d3-time-format/-/d3-time-format-2.3.0.tgz#107bdc028667788a8924ba040faf1fbccd5a7850"
    +  integrity sha512-guv6b2H37s2Uq/GefleCDtbe0XZAuy7Wa49VGkPVPMfLL9qObgBST3lEHJBMUp8S7NdLQAGIvr2KXk8Hc98iKQ==
       dependencies:
         d3-time "1"
     
     d3-time@1:
    -  version "1.0.11"
    -  resolved "https://registry.yarnpkg.com/d3-time/-/d3-time-1.0.11.tgz#1d831a3e25cd189eb256c17770a666368762bbce"
    -  integrity sha512-Z3wpvhPLW4vEScGeIMUckDW7+3hWKOQfAWg/U7PlWBnQmeKQ00gCUsTtWSYulrKNA7ta8hJ+xXc6MHrMuITwEw==
    +  version "1.1.0"
    +  resolved "https://registry.yarnpkg.com/d3-time/-/d3-time-1.1.0.tgz#b1e19d307dae9c900b7e5b25ffc5dcc249a8a0f1"
    +  integrity sha512-Xh0isrZ5rPYYdqhAVk8VLnMEidhz5aP7htAADH6MfzgmmicPkTo8LhkLxci61/lCB7n7UmE3bN0leRt+qvkLxA==
     
     d3-timer@1:
    -  version "1.0.9"
    -  resolved "https://registry.yarnpkg.com/d3-timer/-/d3-timer-1.0.9.tgz#f7bb8c0d597d792ff7131e1c24a36dd471a471ba"
    -  integrity sha512-rT34J5HnQUHhcLvhSB9GjCkN0Ddd5Y8nCwDBG2u6wQEeYxT/Lf51fTFFkldeib/sE/J0clIe0pnCfs6g/lRbyg==
    +  version "1.0.10"
    +  resolved "https://registry.yarnpkg.com/d3-timer/-/d3-timer-1.0.10.tgz#dfe76b8a91748831b13b6d9c793ffbd508dd9de5"
    +  integrity sha512-B1JDm0XDaQC+uvo4DT79H0XmBskgS3l6Ve+1SBCfxgmtIb1AVrPIoqd+nPSv+loMX8szQ0sVUhGngL7D5QPiXw==
     
     d3-transition@1:
    -  version "1.2.0"
    -  resolved "https://registry.yarnpkg.com/d3-transition/-/d3-transition-1.2.0.tgz#f538c0e21b2aa1f05f3e965f8567e81284b3b2b8"
    -  integrity sha512-VJ7cmX/FPIPJYuaL2r1o1EMHLttvoIuZhhuAlRoOxDzogV8iQS6jYulDm3xEU3TqL80IZIhI551/ebmCMrkvhw==
    +  version "1.3.2"
    +  resolved "https://registry.yarnpkg.com/d3-transition/-/d3-transition-1.3.2.tgz#a98ef2151be8d8600543434c1ca80140ae23b398"
    +  integrity sha512-sc0gRU4PFqZ47lPVHloMn9tlPcv8jxgOQg+0zjhfZXMQuvppjG6YuwdMBE0TuqCZjeJkLecku/l9R0JPcRhaDA==
       dependencies:
         d3-color "1"
         d3-dispatch "1"
    @@ -842,9 +971,9 @@ d3-voronoi@1:
       integrity sha512-dArJ32hchFsrQ8uMiTBLq256MpnZjeuBtdHpaDlYuQyjU0CVzCJl/BVW+SkszaAeH95D/8gxqAhgx0ouAWAfRg==
     
     d3-zoom@1:
    -  version "1.7.3"
    -  resolved "https://registry.yarnpkg.com/d3-zoom/-/d3-zoom-1.7.3.tgz#f444effdc9055c38077c4299b4df999eb1d47ccb"
    -  integrity sha512-xEBSwFx5Z9T3/VrwDkMt+mr0HCzv7XjpGURJ8lWmIC8wxe32L39eWHIasEe/e7Ox8MPU4p1hvH8PKN2olLzIBg==
    +  version "1.8.3"
    +  resolved "https://registry.yarnpkg.com/d3-zoom/-/d3-zoom-1.8.3.tgz#b6a3dbe738c7763121cd05b8a7795ffe17f4fc0a"
    +  integrity sha512-VoLXTK4wvy1a0JpH2Il+F2CiOhVu7VRXWF5M/LroMIh3/zBAC3WAt7QoIvPibOavVo20hN6/37vwAsdBejLyKQ==
       dependencies:
         d3-dispatch "1"
         d3-drag "1"
    @@ -852,47 +981,10 @@ d3-zoom@1:
         d3-selection "1"
         d3-transition "1"
     
    -d3@^5.14:
    -  version "5.15.0"
    -  resolved "https://registry.yarnpkg.com/d3/-/d3-5.15.0.tgz#ffd44958e6a3cb8a59a84429c45429b8bca5677a"
    -  integrity sha512-C+E80SL2nLLtmykZ6klwYj5rPqB5nlfN5LdWEAVdWPppqTD8taoJi2PxLZjPeYT8FFRR2yucXq+kBlOnnvZeLg==
    -  dependencies:
    -    d3-array "1"
    -    d3-axis "1"
    -    d3-brush "1"
    -    d3-chord "1"
    -    d3-collection "1"
    -    d3-color "1"
    -    d3-contour "1"
    -    d3-dispatch "1"
    -    d3-drag "1"
    -    d3-dsv "1"
    -    d3-ease "1"
    -    d3-fetch "1"
    -    d3-force "1"
    -    d3-format "1"
    -    d3-geo "1"
    -    d3-hierarchy "1"
    -    d3-interpolate "1"
    -    d3-path "1"
    -    d3-polygon "1"
    -    d3-quadtree "1"
    -    d3-random "1"
    -    d3-scale "2"
    -    d3-scale-chromatic "1"
    -    d3-selection "1"
    -    d3-shape "1"
    -    d3-time "1"
    -    d3-time-format "2"
    -    d3-timer "1"
    -    d3-transition "1"
    -    d3-voronoi "1"
    -    d3-zoom "1"
    -
    -d3@^5.7.0:
    -  version "5.9.7"
    -  resolved "https://registry.yarnpkg.com/d3/-/d3-5.9.7.tgz#f3835648a172b438e89ed57eb6c525bdabdcd7dc"
    -  integrity sha512-jENytrmdXtGPw7HuSK2S4gxRM1eUGjKvWQkQ6ct4yK+DB8SG3VcnVrwesfnsv8rIcxMUg18TafT4Q8mOZUMP4Q==
    +d3@^5.14, d3@^5.7.0:
    +  version "5.16.0"
    +  resolved "https://registry.yarnpkg.com/d3/-/d3-5.16.0.tgz#9c5e8d3b56403c79d4ed42fbd62f6113f199c877"
    +  integrity sha512-4PL5hHaHwX4m7Zr1UapXW23apo6pexCgdetdJ5kTmADpG/7T9Gkxw0M0tf/pjoB63ezCCm0u5UaFYy2aMt0Mcw==
       dependencies:
         d3-array "1"
         d3-axis "1"
    @@ -945,11 +1037,11 @@ dagre@^0.8.4, dagre@^0.8.5:
         lodash "^4.17.15"
     
     debug@^4.1.0:
    -  version "4.1.1"
    -  resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791"
    -  integrity sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==
    +  version "4.2.0"
    +  resolved "https://registry.yarnpkg.com/debug/-/debug-4.2.0.tgz#7f150f93920e94c58f5574c2fd01a3110effe7f1"
    +  integrity sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==
       dependencies:
    -    ms "^2.1.1"
    +    ms "2.1.2"
     
     deep-equal@^1.1.1:
       version "1.1.1"
    @@ -963,7 +1055,7 @@ deep-equal@^1.1.1:
         object-keys "^1.1.1"
         regexp.prototype.flags "^1.2.0"
     
    -define-properties@^1.1.2, define-properties@^1.1.3:
    +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==
    @@ -978,23 +1070,28 @@ dom-helpers@^3.4.0:
         "@babel/runtime" "^7.1.2"
     
     dom-serializer@0:
    -  version "0.1.1"
    -  resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.1.1.tgz#1ec4059e284babed36eec2941d4a970a189ce7c0"
    -  integrity sha512-l0IU0pPzLWSHBcieZbpOKgkIn3ts3vAh7ZuFyXNwJxJXk/c4Gwj9xaTJwIDVQCXawWD0qb3IzMGH5rglQaO0XA==
    +  version "0.2.2"
    +  resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.2.2.tgz#1afb81f533717175d478655debc5e332d9f9bb51"
    +  integrity sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g==
       dependencies:
    -    domelementtype "^1.3.0"
    -    entities "^1.1.1"
    +    domelementtype "^2.0.1"
    +    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@1, domelementtype@^1.3.0, domelementtype@^1.3.1:
    +domelementtype@1, domelementtype@^1.3.1:
       version "1.3.1"
       resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.3.1.tgz#d048c44b37b0d10a7f2a3d5fee3f4333d790481f"
       integrity sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==
     
    +domelementtype@^2.0.1:
    +  version "2.0.2"
    +  resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.0.2.tgz#f3b6e549201e46f588b59463dd77187131fe6971"
    +  integrity sha512-wFwTwCVebUrMgGeAwRL/NhZtHAUyT9n9yg4IMDwf10+6iCMxSkVq9MGCVEH+QZWo1nNidy8kNvwmv4zWHDTqvA==
    +
     domhandler@^2.3.0:
       version "2.4.2"
       resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-2.4.2.tgz#8805097e933d65e85546f726d60f5eb88b44f803"
    @@ -1015,27 +1112,57 @@ entities@^1.1.1:
       resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.2.tgz#bdfa735299664dfafd34529ed4f8522a275fea56"
       integrity sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==
     
    +entities@^2.0.0:
    +  version "2.1.0"
    +  resolved "https://registry.yarnpkg.com/entities/-/entities-2.1.0.tgz#992d3129cf7df6870b96c57858c249a120f8b8b5"
    +  integrity sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w==
    +
     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==
    +  version "2.0.3"
    +  resolved "https://registry.yarnpkg.com/entities/-/entities-2.0.3.tgz#5c487e5742ab93c15abb5da22759b8590ec03b7f"
    +  integrity sha512-MyoZ0jgnLvB2X3Lg5HqpFmn1kybDiIfEQmKzTb5apr51Rb+T3KdmMiqa70T+bhGnyv7bQ6WMj2QMHpGMmlrUYQ==
    +
    +entity-decode@^2.0.2:
    +  version "2.0.2"
    +  resolved "https://registry.yarnpkg.com/entity-decode/-/entity-decode-2.0.2.tgz#e4f807e52c3294246e9347d1f2b02b07fd5f92e7"
    +  integrity sha512-5CCY/3ci4MC1m2jlumNjWd7VBFt4VfFnmSqSNmVcXq4gxM3Vmarxtt+SvmBnzwLS669MWdVuXboNVj1qN2esVg==
    +  dependencies:
    +    he "^1.1.1"
     
     es-abstract@^1.17.0-next.1:
    -  version "1.17.4"
    -  resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.17.4.tgz#e3aedf19706b20e7c2594c35fc0d57605a79e184"
    -  integrity sha512-Ae3um/gb8F0mui/jPL+QiqmglkUsaQf7FwBEHYIFkztkneosu9imhqHpBzQ3h1vit8t5iQ74t6PEVvphBZiuiQ==
    +  version "1.17.7"
    +  resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.17.7.tgz#a4de61b2f66989fc7421676c1cb9787573ace54c"
    +  integrity sha512-VBl/gnfcJ7OercKA9MVaegWsBHFjV492syMudcnQZvt/Dw8ezpcOHYZXa/J96O8vx+g4x65YKhxOwDUh63aS5g==
       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"
    +    is-callable "^1.2.2"
    +    is-regex "^1.1.1"
    +    object-inspect "^1.8.0"
         object-keys "^1.1.1"
    -    object.assign "^4.1.0"
    -    string.prototype.trimleft "^2.1.1"
    -    string.prototype.trimright "^2.1.1"
    +    object.assign "^4.1.1"
    +    string.prototype.trimend "^1.0.1"
    +    string.prototype.trimstart "^1.0.1"
    +
    +es-abstract@^1.18.0-next.1:
    +  version "1.18.0-next.1"
    +  resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.18.0-next.1.tgz#6e3a0a4bda717e5023ab3b8e90bec36108d22c68"
    +  integrity sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA==
    +  dependencies:
    +    es-to-primitive "^1.2.1"
    +    function-bind "^1.1.1"
    +    has "^1.0.3"
    +    has-symbols "^1.0.1"
    +    is-callable "^1.2.2"
    +    is-negative-zero "^2.0.0"
    +    is-regex "^1.1.1"
    +    object-inspect "^1.8.0"
    +    object-keys "^1.1.1"
    +    object.assign "^4.1.1"
    +    string.prototype.trimend "^1.0.1"
    +    string.prototype.trimstart "^1.0.1"
     
     es-to-primitive@^1.2.1:
       version "1.2.1"
    @@ -1051,25 +1178,25 @@ escape-string-regexp@^1.0.5:
       resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
       integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=
     
    -escaper@^2.5.3:
    -  version "2.5.3"
    -  resolved "https://registry.yarnpkg.com/escaper/-/escaper-2.5.3.tgz#8b8fe90ba364054151ab7eff18b4ce43b1e13ab5"
    -  integrity sha512-QGb9sFxBVpbzMggrKTX0ry1oiI4CSDAl9vIL702hzl1jGW8VZs7qfqTRX7WDOjoNDoEVGcEtu1ZOQgReSfT2kQ==
    +eslint-visitor-keys@^1.0.0:
    +  version "1.3.0"
    +  resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz#30ebd1ef7c2fdff01c3a4f151044af25fab0523e"
    +  integrity sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==
     
     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==
    +  version "3.1.3"
    +  resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525"
    +  integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==
     
     fast-json-stable-stringify@^2.0.0:
    -  version "2.0.0"
    -  resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz#d5142c0caee6b1189f87d3a76111064f86c8bbf2"
    -  integrity sha1-1RQsDK7msRifh9OnYREGT4bIu/I=
    +  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@2.6.1:
    -  version "2.6.1"
    -  resolved "https://registry.yarnpkg.com/free-style/-/free-style-2.6.1.tgz#6af512568291195854842cbbaacd95578a6a9a8b"
    -  integrity sha512-uaVA8e57tvhrFKAl6x32SGIrGFBoeTAFtfHDzWxjPhiXQiUxOI6EEdEReRkjNO2H9XcdMJXXEnMHw8Q7iMYLbw==
    +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"
    @@ -1081,10 +1208,19 @@ function-bind@^1.1.1:
       resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d"
       integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==
     
    +get-intrinsic@^1.0.0:
    +  version "1.0.1"
    +  resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.0.1.tgz#94a9768fcbdd0595a1c9273aacf4c89d075631be"
    +  integrity sha512-ZnWP+AmS1VUaLgTRy47+zKtjTxz+0xMpx3I52i+aalBK1QP19ggLF3Db89KJX7kjfOfP2eoa01qc++GwPgufPg==
    +  dependencies:
    +    function-bind "^1.1.1"
    +    has "^1.0.3"
    +    has-symbols "^1.0.1"
    +
     glob@^7.1.3:
    -  version "7.1.4"
    -  resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.4.tgz#aa608a2f6c577ad357e1ae5a5c26d9a8d1969255"
    -  integrity sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==
    +  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"
    @@ -1093,6 +1229,11 @@ glob@^7.1.3:
         once "^1.3.0"
         path-is-absolute "^1.0.0"
     
    +globals@^11.1.0:
    +  version "11.12.0"
    +  resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e"
    +  integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==
    +
     graphlib@^2.1.7, graphlib@^2.1.8:
       version "2.1.8"
       resolved "https://registry.yarnpkg.com/graphlib/-/graphlib-2.1.8.tgz#5761d414737870084c92ec7b5dbcb0592c9d35da"
    @@ -1110,11 +1251,6 @@ has-flag@^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:
    -  version "1.0.0"
    -  resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.0.tgz#ba1a8f1af2a0fc39650f5c850367704122063b44"
    -  integrity sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=
    -
     has-symbols@^1.0.1:
       version "1.0.1"
       resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.1.tgz#9f5214758a44196c406d9bd76cebf81ec2dd31e8"
    @@ -1127,7 +1263,7 @@ has@^1.0.3:
       dependencies:
         function-bind "^1.1.1"
     
    -he@^1.2.0:
    +he@^1.1.1, he@^1.2.0:
       version "1.2.0"
       resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f"
       integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==
    @@ -1182,27 +1318,34 @@ is-arguments@^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-callable@^1.1.4, is-callable@^1.2.2:
    +  version "1.2.2"
    +  resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.2.tgz#c7c6715cd22d4ddb48d3e19970223aceabb080d9"
    +  integrity sha512-dnMqspv5nU3LoewK2N/y7KLtxtakvTuaCsU9FU50/QDmdbHNy/4/JuRtMHqRU22o3q+W89YQndQEeCVwK+3qrA==
    +
    +is-core-module@^2.0.0:
    +  version "2.0.0"
    +  resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.0.0.tgz#58531b70aed1db7c0e8d4eb1a0a2d1ddd64bd12d"
    +  integrity sha512-jq1AH6C8MuteOoBPwkxHafmByhL9j5q4OaPGdbuD+ZtQJVzH+i6E3BJDQcBA09k57i2Hh2yQbEG8yObZ0jdlWw==
    +  dependencies:
    +    has "^1.0.3"
     
     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-negative-zero@^2.0.0:
    +  version "2.0.0"
    +  resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.0.tgz#9553b121b0fac28869da9ed459e20c7543788461"
    +  integrity sha1-lVOxIbD6wohp2p7UWeIMdUN4hGE=
     
    -is-regexp@^1.0.0:
    -  version "1.0.0"
    -  resolved "https://registry.yarnpkg.com/is-regexp/-/is-regexp-1.0.0.tgz#fd2d883545c46bac5a633e7b9a09e87fa2cb5069"
    -  integrity sha1-/S2INUXEa6xaYz57mgnof6LLUGk=
    +is-regex@^1.0.4, is-regex@^1.1.1:
    +  version "1.1.1"
    +  resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.1.tgz#c6f98aacc546f6cec5468a07b7b153ab564a57b9"
    +  integrity sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg==
    +  dependencies:
    +    has-symbols "^1.0.1"
     
     is-symbol@^1.0.2:
       version "1.0.3"
    @@ -1211,22 +1354,32 @@ is-symbol@^1.0.2:
       dependencies:
         has-symbols "^1.0.1"
     
    -"js-tokens@^3.0.0 || ^4.0.0":
    +"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^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==
     
    +jsesc@^2.5.1:
    +  version "2.5.2"
    +  resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4"
    +  integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==
    +
     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.1"
    -  resolved "https://registry.yarnpkg.com/json5/-/json5-2.1.1.tgz#81b6cb04e9ba496f1c7005d07b4368a2638f90b6"
    -  integrity sha512-l+3HXD0GEI3huGq1njuqtzYK8OYJyXMkOLtQ53pjWh89tvWS2h6l+1zMkYWqlb57+SiQodKZyvMEFb2X+KrFhQ==
    +  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.0"
    +    minimist "^1.2.5"
    +
    +khroma@^1.1.0:
    +  version "1.1.0"
    +  resolved "https://registry.yarnpkg.com/khroma/-/khroma-1.1.0.tgz#cc17723eb719c5245ea66d23dd577d5695452db5"
    +  integrity sha512-aTO+YX22tYOLEQJYFiatAj1lc5QZ+H5sHWFRBWNCiKwc5NWNUJZyeSeiHEPeURJ2a1GEVYcmyMUwGjjLe5ec5A==
     
     linkify-it@^2.0.0:
       version "2.2.0"
    @@ -1265,10 +1418,10 @@ lodash.mergewith@^4.6.1:
       resolved "https://registry.yarnpkg.com/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz#617121f89ac55f59047c7aec1ccd6654c6590f55"
       integrity sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==
     
    -lodash@^4.17.11, lodash@^4.17.15:
    -  version "4.17.19"
    -  resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.19.tgz#e48ddedbe30b3321783c5b4301fbd353bc1e4a4b"
    -  integrity sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==
    +lodash@^4.17.15, lodash@^4.17.19:
    +  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"
    @@ -1293,9 +1446,9 @@ markdown-it-container@^2.0.0:
       integrity sha1-ABm0P9Au7+zi8ZYKKJX7qBpARpU=
     
     markdown-it-deflist@^2.0.3:
    -  version "2.0.3"
    -  resolved "https://registry.yarnpkg.com/markdown-it-deflist/-/markdown-it-deflist-2.0.3.tgz#5727db04184d3cb2bc6ee4a9641e3a1091d5fd6f"
    -  integrity sha512-/BNZ8ksW42bflm1qQLnRI09oqU2847Z7MVavrR0MORyKLtiUYOMpwtlAfMSZAQU9UCvaUZMpgVAqoS3vpToJxw==
    +  version "2.1.0"
    +  resolved "https://registry.yarnpkg.com/markdown-it-deflist/-/markdown-it-deflist-2.1.0.tgz#50d7a56b9544cd81252f7623bd785e28a8dcef5c"
    +  integrity sha512-3OuqoRUlSxJiuQYu0cWTLHNhhq2xtoSFqsZK8plANg91+RJQU1ziQ6lA2LzmFAEes18uPBsHZpcX6We5l76Nzg==
     
     markdown-it-diagrams@^0.1.2:
       version "0.1.2"
    @@ -1311,11 +1464,6 @@ markdown-it-footnote@^3.0.2:
       resolved "https://registry.yarnpkg.com/markdown-it-footnote/-/markdown-it-footnote-3.0.2.tgz#1575ee7a093648d4e096aa33386b058d92ac8bc1"
       integrity sha512-JVW6fCmZWjvMdDQSbOT3nnOQtd9iAXmw7hTSh26+v42BnvXeVyGMDBm5b/EZocMed2MbCAHiTX632vY0FyGB8A==
     
    -markdown-it-replace-link@^1.1.0:
    -  version "1.1.0"
    -  resolved "https://registry.yarnpkg.com/markdown-it-replace-link/-/markdown-it-replace-link-1.1.0.tgz#cab2343eb27928db1c836e10cd518aaf60a0bd50"
    -  integrity sha512-P+4D/Z16utgQVjMumA5W3lMbxWYk4iwg1Fk59wShFm7MRgzbjJm++tvI18LdYwiG8CKNA1TFWvHcbtehvMuViA==
    -
     markdown-it-task-lists@^2.1.1:
       version "2.1.1"
       resolved "https://registry.yarnpkg.com/markdown-it-task-lists/-/markdown-it-task-lists-2.1.1.tgz#f68f4d2ac2bad5a2c373ba93081a1a6848417088"
    @@ -1333,9 +1481,9 @@ markdown-it@^10.0.0:
         uc.micro "^1.0.5"
     
     marked@^0.8.0:
    -  version "0.8.0"
    -  resolved "https://registry.yarnpkg.com/marked/-/marked-0.8.0.tgz#ec5c0c9b93878dc52dd54be8d0e524097bd81a99"
    -  integrity sha512-MyUe+T/Pw4TZufHkzAfDj6HarCBWia2y27/bhuYkTaiUnfDYFnCP3KUN+9oM7Wi6JA2rymtVYbQu3spE0GCmxQ==
    +  version "0.8.2"
    +  resolved "https://registry.yarnpkg.com/marked/-/marked-0.8.2.tgz#4faad28d26ede351a7a1aaa5fec67915c869e355"
    +  integrity sha512-EGwzEeCcLniFX51DhTpmTom+dSA/MG/OBUDjnWtHbEnjAH180VzUeAw+oE4+Zv+CoYBWyRlYOTR0N8SO9R1PVw==
     
     mdurl@^1.0.1:
       version "1.0.1"
    @@ -1343,21 +1491,22 @@ mdurl@^1.0.1:
       integrity sha1-/oWy7HWlkDfyrf7BAP1sYBdhFS4=
     
     mermaid@^8.4.8:
    -  version "8.4.8"
    -  resolved "https://registry.yarnpkg.com/mermaid/-/mermaid-8.4.8.tgz#8adcfdbc505d6bca52df167cff690427c9727b60"
    -  integrity sha512-sumTNBFwMX7oMQgogdr3NhgTeQOiwcEsm23rQ4KHGW7tpmvMwER1S+1gjCSSnqlmM/zw7Ga7oesYCYicKboRwQ==
    +  version "8.8.2"
    +  resolved "https://registry.yarnpkg.com/mermaid/-/mermaid-8.8.2.tgz#c68b842ffe0d53e5ed11fb1b7887d8a392b72421"
    +  integrity sha512-Ib9jl5TMwgMKv2+vdfKZ/SIUxKYc4GMauKeV2+3BD/bU47kGbc5zv5taCY5iZR+V4hdweHKE7YOl11VGcWBy/w==
       dependencies:
         "@braintree/sanitize-url" "^3.1.0"
    -    crypto-random-string "^3.0.1"
    +    babel-eslint "^10.1.0"
         d3 "^5.7.0"
         dagre "^0.8.4"
         dagre-d3 "^0.6.4"
    +    entity-decode "^2.0.2"
         graphlib "^2.1.7"
         he "^1.2.0"
    -    lodash "^4.17.11"
    +    khroma "^1.1.0"
         minify "^4.1.1"
         moment-mini "^2.22.1"
    -    scope-css "^1.2.1"
    +    stylis "^3.5.2"
     
     minify@^4.1.1:
       version "4.1.3"
    @@ -1379,22 +1528,22 @@ minimatch@^3.0.4:
       dependencies:
         brace-expansion "^1.1.7"
     
    -minimist@^1.2.0, minimist@~1.2.0:
    -  version "1.2.0"
    -  resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284"
    -  integrity sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=
    +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-mini@^2.22.1:
    -  version "2.22.1"
    -  resolved "https://registry.yarnpkg.com/moment-mini/-/moment-mini-2.22.1.tgz#bc32d73e43a4505070be6b53494b17623183420d"
    -  integrity sha512-OUCkHOz7ehtNMYuZjNciXUfwTuz8vmF1MTbAy59ebf+ZBYZO5/tZKuChVWCX+uDo+4idJBpGltNfV8st+HwsGw==
    +  version "2.24.0"
    +  resolved "https://registry.yarnpkg.com/moment-mini/-/moment-mini-2.24.0.tgz#fa68d98f7fe93ae65bf1262f6abb5fb6983d8d18"
    +  integrity sha512-9ARkWHBs+6YJIvrIp0Ik5tyTTtP9PoV0Ssu2Ocq5y9v8+NOOpWiRshAp8c4rZVWTOe+157on/5G+zj5pwIQFEQ==
     
     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==
    +  version "2.29.1"
    +  resolved "https://registry.yarnpkg.com/moment/-/moment-2.29.1.tgz#b2be769fa31940be9eeea6469c075e35006fa3d3"
    +  integrity sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==
     
    -ms@^2.1.1:
    +ms@2.1.2:
       version "2.1.2"
       resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
       integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
    @@ -1426,30 +1575,33 @@ object-assign@^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-inspect@^1.8.0:
    +  version "1.8.0"
    +  resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.8.0.tgz#df807e5ecf53a609cc6bfe93eac3cc7be5b3a9d0"
    +  integrity sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA==
     
     object-is@^1.0.1:
    -  version "1.0.2"
    -  resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.0.2.tgz#6b80eb84fe451498f65007982f035a5b445edec4"
    -  integrity sha512-Epah+btZd5wrrfjkJZq1AOB9O6OxUQto45hzFd7lXGrpHPGE0W1k+426yrZV+k6NJOzLNNW/nVsmZdIWsAqoOQ==
    +  version "1.1.3"
    +  resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.1.3.tgz#2e3b9e65560137455ee3bd62aec4d90a2ea1cc81"
    +  integrity sha512-teyqLvFWzLkq5B9ki8FVWA902UER2qkxmdA4nLf+wjOLAWgxzCWZNCxpDq9MvE8MmhWNr+I8w3BN49Vx36Y6Xg==
    +  dependencies:
    +    define-properties "^1.1.3"
    +    es-abstract "^1.18.0-next.1"
     
    -object-keys@^1.0.11, object-keys@^1.0.12, object-keys@^1.1.1:
    +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==
    +object.assign@^4.1.1:
    +  version "4.1.2"
    +  resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.2.tgz#0ed54a342eceb37b38ff76eb831a0e788cb63940"
    +  integrity sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==
       dependencies:
    -    define-properties "^1.1.2"
    -    function-bind "^1.1.1"
    -    has-symbols "^1.0.0"
    -    object-keys "^1.0.11"
    +    call-bind "^1.0.0"
    +    define-properties "^1.1.3"
    +    has-symbols "^1.0.1"
    +    object-keys "^1.1.1"
     
     once@^1.3.0:
       version "1.4.0"
    @@ -1470,25 +1622,25 @@ path-is-absolute@^1.0.0:
       resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f"
       integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18=
     
    +path-parse@^1.0.6:
    +  version "1.0.6"
    +  resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c"
    +  integrity sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==
    +
     path-posix@~1.0.0:
       version "1.0.0"
       resolved "https://registry.yarnpkg.com/path-posix/-/path-posix-1.0.0.tgz#06b26113f56beab042545a23bfa88003ccac260f"
       integrity sha1-BrJhE/Vr6rBCVFojv6iAA8ysJg8=
     
    -popper.js@^1.14.4:
    -  version "1.15.0"
    -  resolved "https://registry.yarnpkg.com/popper.js/-/popper.js-1.15.0.tgz#5560b99bbad7647e9faa475c6b8056621f5a4ff2"
    -  integrity sha512-w010cY1oCUmI+9KwwlWki+r5jxKfTFDVoadl7MSrIujHU5MJ5OR6HTDj6Xo8aoR/QsA56x8jKjA59qGH4ELtrA==
    -
    -popper.js@^1.15.0:
    +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.5:
    -  version "7.0.17"
    -  resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.17.tgz#4da1bdff5322d4a0acaab4d87f3e782436bad31f"
    -  integrity sha512-546ZowA+KZ3OasvQZHsbuEpysvwTZNGJv9EfyCQdsIDltPSWHAeTQ5fQy/Npi2ZDtLI3zs7Ps/p6wThErhm9fQ==
    +  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"
    @@ -1514,9 +1666,9 @@ punycode@^2.1.0:
       integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==
     
     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==
    +  version "2.2.0"
    +  resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.2.0.tgz#3345941b4153cb9d082d8eee4cda2016a9aef7f6"
    +  integrity sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==
     
     react-dom@~16.9.0:
       version "16.9.0"
    @@ -1529,9 +1681,9 @@ react-dom@~16.9.0:
         scheduler "^0.15.0"
     
     react-is@^16.8.1:
    -  version "16.8.6"
    -  resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.8.6.tgz#5bbc1e2d29141c9fbdfed456343fe2bc430a6a16"
    -  integrity sha512-aUk3bHfZ2bRSVFFbbeVS4i+lNPZr3/WM5jT2J5omUVV1zzcs1nAaf3l51ctA5FFvCRbhrH0bdAsRRQddFJZPtA==
    +  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"
    @@ -1571,18 +1723,18 @@ react@~16.9.0:
         prop-types "^15.6.2"
     
     readable-stream@^3.1.1:
    -  version "3.4.0"
    -  resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.4.0.tgz#a51c26754658e0a3c21dbf59163bd45ba6f447fc"
    -  integrity sha512-jItXPLmrSR8jmTRmRWJXCnGJsfy85mB3Wd/uINMXA65yrnFo0cPClFIUWzo2najVNSl+mx7/4W8ttlLWJe99pQ==
    +  version "3.6.0"
    +  resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198"
    +  integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==
       dependencies:
         inherits "^2.0.3"
         string_decoder "^1.1.1"
         util-deprecate "^1.0.1"
     
    -regenerator-runtime@^0.13.2:
    -  version "0.13.3"
    -  resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.3.tgz#7cf6a77d8f5c6f60eb73c5fc1955b2ceb01e6bf5"
    -  integrity sha512-naKIZz2GQ8JWh///G7L3X6LaQUAMp2lvb1rvwwsURe/VXwD6VMfr+/1NuNw3ag8v2kY1aQ/go5SNn79O9JU7yw==
    +regenerator-runtime@^0.13.4:
    +  version "0.13.7"
    +  resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz#cac2dacc8a1ea675feaabaeb8ae833898ae46f55"
    +  integrity sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==
     
     regexp.prototype.flags@^1.2.0:
       version "1.3.0"
    @@ -1607,6 +1759,14 @@ resize-observer-polyfill@^1.5.1:
       resolved "https://registry.yarnpkg.com/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz#0e9020dd3d21024458d4ebd27e23e40269810464"
       integrity sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==
     
    +resolve@^1.12.0:
    +  version "1.18.1"
    +  resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.18.1.tgz#018fcb2c5b207d2a6424aee361c5a266da8f4130"
    +  integrity sha512-lDfCPaMKfOJXjy0dPayzPdF1phampNWr3qFCjAu+rw/qbQmr5jWH5xN2hwh9QKfw9E5v4hwV7A+jrCmL8yjjqA==
    +  dependencies:
    +    is-core-module "^2.0.0"
    +    path-parse "^1.0.6"
    +
     rimraf@~3.0.0:
       version "3.0.2"
       resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a"
    @@ -1619,10 +1779,10 @@ rw@1:
       resolved "https://registry.yarnpkg.com/rw/-/rw-1.3.3.tgz#3f862dfa91ab766b14885ef4d01124bfda074fb4"
       integrity sha1-P4Yt+pGrdmsUiF700BEkv9oHT7Q=
     
    -safe-buffer@~5.1.0:
    -  version "5.1.2"
    -  resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
    -  integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==
    +safe-buffer@~5.2.0:
    +  version "5.2.1"
    +  resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6"
    +  integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==
     
     "safer-buffer@>= 2.1.2 < 3":
       version "2.1.2"
    @@ -1653,28 +1813,19 @@ scheduler@^0.15.0:
         loose-envify "^1.1.0"
         object-assign "^4.1.1"
     
    -scope-css@^1.2.1:
    -  version "1.2.1"
    -  resolved "https://registry.yarnpkg.com/scope-css/-/scope-css-1.2.1.tgz#c35768bc900cad030a3e0d663a818c0f6a57f40e"
    -  integrity sha512-UjLRmyEYaDNiOS673xlVkZFlVCtckJR/dKgr434VMm7Lb+AOOqXKdAcY7PpGlJYErjXXJzKN7HWo4uRPiZZG0Q==
    -  dependencies:
    -    escaper "^2.5.3"
    -    slugify "^1.3.1"
    -    strip-css-comments "^3.0.0"
    -
    -slugify@^1.3.1:
    -  version "1.3.4"
    -  resolved "https://registry.yarnpkg.com/slugify/-/slugify-1.3.4.tgz#78d2792d7222b55cd9fc81fa018df99af779efeb"
    -  integrity sha512-KP0ZYk5hJNBS8/eIjGkFDCzGQIoZ1mnfQRYS5WM3273z+fxGWXeN0fkwf2ebEweydv9tioZIHGZKoF21U07/nw==
    -
     source-map-support@~0.5.12:
    -  version "0.5.12"
    -  resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.12.tgz#b4f3b10d51857a5af0138d3ce8003b201613d599"
    -  integrity sha512-4h2Pbvyy15EE02G+JOZpUCmqWJuqrs+sEkzewTm++BPi7Hvn/HwcqLAcNxYAyI0x13CpPPn+kMjl+hplXMHITQ==
    +  version "0.5.19"
    +  resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.19.tgz#a98b62f86dcaf4f67399648c085291ab9e8fed61"
    +  integrity sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==
       dependencies:
         buffer-from "^1.0.0"
         source-map "^0.6.0"
     
    +source-map@^0.5.0:
    +  version "0.5.7"
    +  resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc"
    +  integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=
    +
     source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.0, source-map@~0.6.1:
       version "0.6.1"
       resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
    @@ -1693,35 +1844,33 @@ srcset@^1.0.0:
         array-uniq "^1.0.2"
         number-is-nan "^1.0.0"
     
    -string.prototype.trimleft@^2.1.1:
    -  version "2.1.1"
    -  resolved "https://registry.yarnpkg.com/string.prototype.trimleft/-/string.prototype.trimleft-2.1.1.tgz#9bdb8ac6abd6d602b17a4ed321870d2f8dcefc74"
    -  integrity sha512-iu2AGd3PuP5Rp7x2kEZCrB2Nf41ehzh+goo8TV7z8/XDBbsvc6HQIlUl9RjkZ4oyrW1XM5UwlGl1oVEaDjg6Ag==
    +string.prototype.trimend@^1.0.1:
    +  version "1.0.2"
    +  resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.2.tgz#6ddd9a8796bc714b489a3ae22246a208f37bfa46"
    +  integrity sha512-8oAG/hi14Z4nOVP0z6mdiVZ/wqjDtWSLygMigTzAb+7aPEDTleeFf+WrF+alzecxIRkckkJVn+dTlwzJXORATw==
       dependencies:
         define-properties "^1.1.3"
    -    function-bind "^1.1.1"
    +    es-abstract "^1.18.0-next.1"
     
    -string.prototype.trimright@^2.1.1:
    -  version "2.1.1"
    -  resolved "https://registry.yarnpkg.com/string.prototype.trimright/-/string.prototype.trimright-2.1.1.tgz#440314b15996c866ce8a0341894d45186200c5d9"
    -  integrity sha512-qFvWL3/+QIgZXVmJBfpHmxLB7xsUXz6HsUmP8+5dRaC3Q7oKUv9Vo6aMCRZC1smrtyECFsIT30PqBJ1gTjAs+g==
    +string.prototype.trimstart@^1.0.1:
    +  version "1.0.2"
    +  resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.2.tgz#22d45da81015309cd0cdd79787e8919fc5c613e7"
    +  integrity sha512-7F6CdBTl5zyu30BJFdzSTlSlLPwODC23Od+iLoVH8X6+3fvDPPuBVVj9iaB1GOsSTSIgVfsfm27R2FGrAPznWg==
       dependencies:
         define-properties "^1.1.3"
    -    function-bind "^1.1.1"
    +    es-abstract "^1.18.0-next.1"
     
     string_decoder@^1.1.1:
    -  version "1.2.0"
    -  resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.2.0.tgz#fe86e738b19544afe70469243b2a1ee9240eae8d"
    -  integrity sha512-6YqyX6ZWEYguAxgZzHGL7SsCeGx3V2TtOTqZz1xSTSWnqsbWwbptafNyvf/ACquZUXV3DANr5BDIwNYe1mN42w==
    +  version "1.3.0"
    +  resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e"
    +  integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==
       dependencies:
    -    safe-buffer "~5.1.0"
    +    safe-buffer "~5.2.0"
     
    -strip-css-comments@^3.0.0:
    -  version "3.0.0"
    -  resolved "https://registry.yarnpkg.com/strip-css-comments/-/strip-css-comments-3.0.0.tgz#7a5625eff8a2b226cf8947a11254da96e13dae89"
    -  integrity sha1-elYl7/iisibPiUehElTaluE9rok=
    -  dependencies:
    -    is-regexp "^1.0.0"
    +stylis@^3.5.2:
    +  version "3.5.4"
    +  resolved "https://registry.yarnpkg.com/stylis/-/stylis-3.5.4.tgz#f665f25f5e299cf3d64654ab949a57c768b73fbe"
    +  integrity sha512-8/3pSmthWM7lsPBKv7NXkzn2Uc9W7NotcwGNpJaa3k7WMM1XDCA4MgT5k/8BIexd5ydZdboXtU90XH9Ec4Bv/Q==
     
     supports-color@^5.3.0:
       version "5.5.0"
    @@ -1743,33 +1892,33 @@ svgbob-wasm@^0.2.1:
       integrity sha512-z7GQpTjCU0SDWU8QN9O1UJggFI1AsUeqZjZ106EQ/4g0icM1fEKQ9e0BM5MxSpKiby5LpvCt1CIOdPAzEIiwZw==
     
     terser@^4.0.0:
    -  version "4.1.2"
    -  resolved "https://registry.yarnpkg.com/terser/-/terser-4.1.2.tgz#b2656c8a506f7ce805a3f300a2ff48db022fa391"
    -  integrity sha512-jvNoEQSPXJdssFwqPSgWjsOrb+ELoE+ILpHPKXC83tIxOlh2U75F1KuB2luLD/3a6/7K3Vw5pDn+hvu0C4AzSw==
    +  version "4.8.0"
    +  resolved "https://registry.yarnpkg.com/terser/-/terser-4.8.0.tgz#63056343d7c70bb29f3af665865a46fe03a0df17"
    +  integrity sha512-EAPipTNeWsb/3wLPeup1tVPaXfIaU68xMnVdPafIL1TV05OhASArYyIfFvnvJCNrR2NIOvDVNNTFRa+Re2MWyw==
       dependencies:
         commander "^2.20.0"
         source-map "~0.6.1"
         source-map-support "~0.5.12"
     
    -try-catch@^2.0.0:
    +to-fast-properties@^2.0.0:
       version "2.0.0"
    -  resolved "https://registry.yarnpkg.com/try-catch/-/try-catch-2.0.0.tgz#a491141d597f8b72b46757fe1c47059341a16aed"
    -  integrity sha512-RPXpVjsbtWgymwGq5F/OWDFsjEzdvzwHFaMjWWW6f/p6+uk/N7YSKJHQfIfGqITfj8qH4cBqCLMnhKZBaKk7Kg==
    +  resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e"
    +  integrity sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=
    +
    +try-catch@^2.0.0:
    +  version "2.0.1"
    +  resolved "https://registry.yarnpkg.com/try-catch/-/try-catch-2.0.1.tgz#a35d354187c422f291a0bcfd9eb77e3a4f90c1e5"
    +  integrity sha512-LsOrmObN/2WdM+y2xG+t16vhYrQsnV8wftXIcIOWZhQcBJvKGYuamJGwnU98A7Jxs2oZNkJztXlphEOoA0DWqg==
     
     try-to-catch@^1.0.2:
       version "1.1.1"
       resolved "https://registry.yarnpkg.com/try-to-catch/-/try-to-catch-1.1.1.tgz#770162dd13b9a0e55da04db5b7f888956072038a"
       integrity sha512-ikUlS+/BcImLhNYyIgZcEmq4byc31QpC+46/6Jm5ECWkVFhf8SM2Fp/0pMVXPX6vk45SMCwrP4Taxucne8I0VA==
     
    -tslib@~1.10.0:
    -  version "1.10.0"
    -  resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.10.0.tgz#c3c19f95973fb0a62973fb09d90d961ee43e5c8a"
    -  integrity sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ==
    -
    -type-fest@^0.8.1:
    -  version "0.8.1"
    -  resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d"
    -  integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==
    +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"
    @@ -1782,12 +1931,12 @@ typescript@^4.0.5:
       integrity sha512-ywmr/VrTVCmNTJ6iV2LwIrfG1P+lv6luD8sUJs+2eI9NLGigaN+nUQc13iHqisq7bra9lnmUSYqbJvegraBOPQ==
     
     typestyle@^2.0.4:
    -  version "2.0.4"
    -  resolved "https://registry.yarnpkg.com/typestyle/-/typestyle-2.0.4.tgz#b8da5feaf8a4f9d1f69066f3cc4659098bd08457"
    -  integrity sha512-+57eGqcEjiAc51hB/zXnZFoVuzwuxb9WbPpb1VT2zPJPIo88wGXod7dHa0IJ1Ue+sncHj2WZMZEPJRAqwVraoA==
    +  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.4.0"
    -    free-style "2.6.1"
    +    csstype "2.6.9"
    +    free-style "3.1.0"
     
     uc.micro@^1.0.1, uc.micro@^1.0.5:
       version "1.0.6"
    @@ -1795,12 +1944,9 @@ uc.micro@^1.0.1, uc.micro@^1.0.5:
       integrity sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==
     
     uglify-js@^3.5.1:
    -  version "3.6.0"
    -  resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.6.0.tgz#704681345c53a8b2079fb6cec294b05ead242ff5"
    -  integrity sha512-W+jrUHJr3DXKhrsS7NUVxn3zqMOFn0hL/Ei6v0anCIMoKC93TjcflTagwIHLW7SfMFfiQuktQyFVCFHGUE0+yg==
    -  dependencies:
    -    commander "~2.20.0"
    -    source-map "~0.6.1"
    +  version "3.11.5"
    +  resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.11.5.tgz#d6788bc83cf35ff18ea78a65763e480803409bc6"
    +  integrity sha512-btvv/baMqe7HxP7zJSF7Uc16h1mSfuuSplT0/qdjxseesDU+yYzH33eHBH+eMdeRXwujXspaCTooWHQVVBh09w==
     
     upper-case@^1.1.1:
       version "1.1.3"
    @@ -1808,9 +1954,9 @@ upper-case@^1.1.1:
       integrity sha1-9rRQHC7EzdJrp4vnIilh3ndiFZg=
     
     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==
    +  version "4.4.0"
    +  resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.0.tgz#aa714261de793e8a82347a7bcc9ce74e86f28602"
    +  integrity sha512-B0yRTzYdUCCn9n+F4+Gh4yIDtMQcaJsmYBDsTSG8g/OejKBodLQ2IHfN3bM7jUsRXndopT7OIXWdYqc1fjmV6g==
       dependencies:
         punycode "^2.1.0"
     
    @@ -1840,9 +1986,9 @@ wrappy@1:
       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==
    +  version "7.3.1"
    +  resolved "https://registry.yarnpkg.com/ws/-/ws-7.3.1.tgz#d0547bf67f7ce4f12a72dfe31262c68d7dc551c8"
    +  integrity sha512-D3RuNkynyHmEJIpD2qrgVkc9DQ23OrN/moAwZX4L8DfvszsJxpjQuUq3LMx6HoYji9fbIOBY18XWBsAux1ZZUA==
     
     xtend@^4.0.1:
       version "4.0.2"
    
    From 909c4d6a79f519fa139a148ad050743449c7fec7 Mon Sep 17 00:00:00 2001
    From: Nicholas Bollweg 
    Date: Wed, 4 Nov 2020 13:19:01 -0500
    Subject: [PATCH 27/31] lab-compatible anchor slugs, docs, add toc to binder,
     linting, clean up server ext
    
    ---
     .binder/postBuild                    |  4 +-
     .prettierignore                      |  1 +
     .prettierrc                          |  3 +-
     CONTRIBUTING.md                      | 11 ++--
     README.md                            | 30 +++++-----
     index.ipynb                          | 82 +++++++++++++++++++++++++---
     src/builtins/anchor.ts               |  2 +
     src_py/jupyterlab_markup/__init__.py | 31 ++++-------
     8 files changed, 118 insertions(+), 46 deletions(-)
    
    diff --git a/.binder/postBuild b/.binder/postBuild
    index d01321b..3ff1e26 100755
    --- a/.binder/postBuild
    +++ b/.binder/postBuild
    @@ -4,7 +4,9 @@ set -eux
     python -m pip install -e . --no-deps --ignore-installed
     jupyter serverextension enable jupyterlab_markup --sys-prefix
     jlpm bootstrap
    -jupyter labextension install $(npm pack) --no-build --debug
    +jupyter labextension install --no-build --debug \
    +    $(npm pack) \
    +    @jupyterlab/toc
     jupyter lab build --debug --dev-build=False --minimize=False
     
     jupyter labextension list
    diff --git a/.prettierignore b/.prettierignore
    index 8023bd8..a54af56 100644
    --- a/.prettierignore
    +++ b/.prettierignore
    @@ -1,4 +1,5 @@
     __pycache__/
    +.ipynb_checkpoints/
     *.egg-info/
     build/
     lib/
    diff --git a/.prettierrc b/.prettierrc
    index 8db60ca..b3aaa7f 100644
    --- a/.prettierrc
    +++ b/.prettierrc
    @@ -1,3 +1,4 @@
     {
    -    "singleQuote": true
    +    "singleQuote": true,
    +    "proseWrap": "always"
     }
    diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
    index 29d8e31..967c5b0 100644
    --- a/CONTRIBUTING.md
    +++ b/CONTRIBUTING.md
    @@ -1,6 +1,7 @@
     ## Development
     
    -For a development install (requires npm version 4 or later), do the following in the repository directory:
    +For a development install (requires npm version 4 or later), do the following in
    +the repository directory:
     
     ```bash
     python -m pip install -e .
    @@ -25,10 +26,10 @@ jupyter lab watch    # in another terminal, will continue running
     
     ## Extending
     
    -Additional [`markdown-it` plugins][plugins]
    -can be added as small labextensions. After getting started with the [official cookiecutter][],
    -your `plugin.ts` might look something like the the [builtins](./src/builtins/),
    -which use the `simpleMarkdownItPlugin` boilerplate [wrapper](./src/utils.ts).
    +Additional [`markdown-it` plugins][plugins] can be added as small labextensions.
    +After getting started with the [official cookiecutter][], your `plugin.ts` might
    +look something like the the [builtins](./src/builtins/), which use the
    +`simpleMarkdownItPlugin` boilerplate [wrapper](./src/utils.ts).
     
     [official cookiecutter]: https://github.com/jupyterlab/extension-cookiecutter-ts
     [plugins]: https://www.npmjs.com/search?q=keywords:markdown-it-plugin
    diff --git a/README.md b/README.md
    index 3d18f6b..3c016ec 100644
    --- a/README.md
    +++ b/README.md
    @@ -1,6 +1,8 @@
     # jupyterlab-markup
     
    -Adds additional rendering support to markdown in JupyterLab by using [markdown-it](https://github.com/markdown-it/markdown-it), and the following plugins:
    +Adds additional rendering support to markdown in JupyterLab by using
    +[markdown-it](https://github.com/markdown-it/markdown-it), and the following
    +plugins:
     
     - [markdown-it-anchor](https://github.com/valeriangalliat/markdown-it-anchor)
     - [markdown-it-deflist](https://github.com/markdown-it/markdown-it-deflist)
    @@ -19,8 +21,8 @@ Adds additional rendering support to markdown in JupyterLab by using [markdown-i
     
     Most custom markdown extensions not covered by the default `marked`-based
     renderer (e.g. task lists, header anchors) will not work for others who do not
    -have this extension installed. Markdown is very lenient, so the no data should
    -be _lost_, but it might look strange.
    +have this extension installed. Markdown is very lenient, so no data should be
    +_lost_, but it might look strange.
     
     ## Installation
     
    @@ -46,23 +48,25 @@ jupyter serverextension enable jupyterlab_markup
     
     ## Usage
     
    -After installing the extension (and restarting/reloading JupyterLab), all plugins
    -will be enabled by default.
    +After [installing](#Installation) the extension (and restarting/reloading
    +JupyterLab), all plugins will be enabled by default.
     
    -All plugins (and `markdown-it` itself) can be disabled via the _Command Palette_ or
    -under the [_Settings_ menu](https://jupyterlab.readthedocs.io/en/stable/user/interface.html#menu-bar)
    +All plugins (and `markdown-it` itself) can be disabled via the _Command Palette_
    +or under the
    +[_Settings_ menu](https://jupyterlab.readthedocs.io/en/stable/user/interface.html#menu-bar)
     with _Use Markdown Extensions_. This will not affect existing renderers, so open
     documents will need to be reopened.
     
    -Individual plugins can be previewed, enabled/disabled from the palette or menu under
    -_Markdown Extension Settings..._. This view also provides links and examples of
    -any features added by the extensions.
    +Individual plugins can be previewed, enabled/disabled from the palette or menu
    +under _Markdown Extension Settings..._. This view also provides links and
    +examples of any features added by the extensions.
     
     ### Advanced
     
    -A number of settings can be configured through the JupyterLab
    -_Advanced Settings Editor_, including plugin and markdownit options. As with the
    -above limitations, heavy customization might make your documents look strange.
    +A number of settings can be configured through the JupyterLab _Advanced Settings
    +Editor_, including plugin and `markdown-it` options. As with the above
    +[limitations](#Limitations), heavy customization might make your documents look
    +strange.
     
     ## Contributing
     
    diff --git a/index.ipynb b/index.ipynb
    index ba2b535..f0ab637 100644
    --- a/index.ipynb
    +++ b/index.ipynb
    @@ -1,5 +1,12 @@
     {
      "cells": [
    +  {
    +   "cell_type": "markdown",
    +   "metadata": {},
    +   "source": [
    +    "# `@agoose77/jupyterlab-markup` Examples"
    +   ]
    +  },
       {
        "cell_type": "markdown",
        "metadata": {},
    @@ -9,11 +16,30 @@
         "| plugin...                  | provides...                             |\n",
         "|----------------------------|-----------------------------------------|\n",
         "| `markdown-it-anchor`       | header anchors                          |\n",
    -    "| `markdown-it-diagrams`     | [diagrams](#diagrams)                   |\n",
    -    "| `markdown-it-deflist`      | [definition lists](#definition-lists)   |\n",
    -    "| `markdown-it-footnote`     | [footnotes](#footnotes)                 |\n",
    -    "| `markdown-it-replace-link` | link replacement                        |\n",
    -    "| `markdown-it-task-lists`   | [task lists](#task-lists)               |"
    +    "| `markdown-it-diagrams`     | [diagrams](#Diagrams)                   |\n",
    +    "| `markdown-it-deflist`      | [definition lists](#Definition-Lists)   |\n",
    +    "| `markdown-it-footnote`     | [footnotes](#Footnotes)                 |\n",
    +    "| `markdown-it-task-lists`   | [task lists](#Task-Lists)               |"
    +   ]
    +  },
    +  {
    +   "cell_type": "markdown",
    +   "metadata": {},
    +   "source": [
    +    "## Note\n",
    +    "> These examples will [probably only work](./README.md#Limitations) with this labextension installed.\n",
    +    "\n",
    +    "> \n",
    +    "```bash\n",
    +    "jupyter labextension install @agoose77/jupyterlab-markup\n",
    +    "```\n",
    +    "\n",
    +    "> `svgbob` will probably only work on the latest python 3.7 (or later) without the serverextension.\n",
    +    "\n",
    +    "```bash\n",
    +    "pip install jupyterlab_markup    # and maybe\n",
    +    "jupyter serverextension enable --sys-prefix jupyterlab_markup\n",
    +    "```"
        ]
       },
       {
    @@ -108,7 +134,45 @@
         "## Task Lists\n",
         "\n",
         "- [x] done\n",
    -    "- [ ] not done"
    +    "- [ ] not done\n",
    +    "- [ ] ~~not gonna do it~~"
    +   ]
    +  },
    +  {
    +   "cell_type": "markdown",
    +   "metadata": {},
    +   "source": [
    +    "## Rich Display"
    +   ]
    +  },
    +  {
    +   "cell_type": "code",
    +   "execution_count": 10,
    +   "metadata": {},
    +   "outputs": [
    +    {
    +     "data": {
    +      "text/markdown": [
    +       "\n",
    +       "### It works!\n",
    +       "---\n",
    +       "```mermaid\n",
    +       "graph LR; A-->B; A-->C; B-->D; C-->D;\n",
    +       "```\n"
    +      ]
    +     },
    +     "metadata": {},
    +     "output_type": "display_data"
    +    }
    +   ],
    +   "source": [
    +    "display({\"text/markdown\": \"\"\"\n",
    +    "### It works!\n",
    +    "---\n",
    +    "```mermaid\n",
    +    "graph LR; A-->B; A-->C; B-->D; C-->D;\n",
    +    "```\n",
    +    "\"\"\"}, raw=True)"
        ]
       }
      ],
    @@ -129,7 +193,11 @@
        "nbconvert_exporter": "python",
        "pygments_lexer": "ipython3",
        "version": "3.7.8"
    -  }
    +  },
    +  "toc-autonumbering": true,
    +  "toc-showcode": false,
    +  "toc-showmarkdowntxt": true,
    +  "toc-showtags": false
      },
      "nbformat": 4,
      "nbformat_minor": 4
    diff --git a/src/builtins/anchor.ts b/src/builtins/anchor.ts
    index f592d1a..abaf54d 100644
    --- a/src/builtins/anchor.ts
    +++ b/src/builtins/anchor.ts
    @@ -17,8 +17,10 @@ export const anchor = simpleMarkdownItPlugin(PACKAGE_NS, {
         return [
           anchorPlugin.default,
           {
    +        // match JupyterLab default behavior
             permalink: true,
             permalinkClass: 'jp-InternalAnchorLink',
    +        slugify: (title: string) => title.replace(/ /g, '-'),
           },
         ];
       },
    diff --git a/src_py/jupyterlab_markup/__init__.py b/src_py/jupyterlab_markup/__init__.py
    index db8abe8..d4f2bb6 100644
    --- a/src_py/jupyterlab_markup/__init__.py
    +++ b/src_py/jupyterlab_markup/__init__.py
    @@ -1,26 +1,19 @@
    +""" Server companion of jupyterlab-markup
    +
    +    For now, just ensures .wasm files can be served correctly on older
    +    pythons/notebook server, but will eventually handle:
    +    - labextension assets
    +    - nbconvert processors
    +    - test utilities
    +"""
     import mimetypes
     
     __version__ = "0.2.1"
     
     
    -# https://www.iana.org/assignments/provisional-standard-media-types/provisional-standard-media-types.xhtml
    -WASM_EXT = ".wasm"
    -WASM_MIME = "application/wasm"
    -
    -
     def load_jupyter_server_extension(app):
    -    """ ensure tornado serves `.wasm` files with
    +    """ Ensure tornado serves `.wasm` files with correct MIME
         """
    -    _guess = mimetypes.guess_type
    -
    -    def guess_type(name, strict=True):
    -        if name.endswith(WASM_EXT):
    -            return (WASM_MIME, None)
    -        return _guess(name, strict)
    -
    -    mimetypes.guess_type = guess_type
    -    app.log.warning(
    -        "[jupyterlab_markup] mimetypes patched to guess: %s => %s",
    -        WASM_EXT,
    -        WASM_MIME
    -    )
    +    # python >3.7.n (where in is > 0, or something) already has this
    +    # as will some future releases of notebook and jupyter_server, but...s
    +    mimetypes.add_type("application/wasm", ".wasm")
    
    From d05f3475bd378c8a0ae72588d29f987f8d4ab1ae Mon Sep 17 00:00:00 2001
    From: Nicholas Bollweg 
    Date: Wed, 4 Nov 2020 13:21:52 -0500
    Subject: [PATCH 28/31] add binder links
    
    ---
     README.md   | 2 ++
     index.ipynb | 3 ++-
     2 files changed, 4 insertions(+), 1 deletion(-)
    
    diff --git a/README.md b/README.md
    index 3c016ec..be94db2 100644
    --- a/README.md
    +++ b/README.md
    @@ -1,5 +1,7 @@
     # jupyterlab-markup
     
    +[![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/agoose77/jupyterlab-markup/master?urlpath=lab%2Ftree%2Findex.ipynb)
    +
     Adds additional rendering support to markdown in JupyterLab by using
     [markdown-it](https://github.com/markdown-it/markdown-it), and the following
     plugins:
    diff --git a/index.ipynb b/index.ipynb
    index f0ab637..95033b5 100644
    --- a/index.ipynb
    +++ b/index.ipynb
    @@ -27,7 +27,8 @@
        "metadata": {},
        "source": [
         "## Note\n",
    -    "> These examples will [probably only work](./README.md#Limitations) with this labextension installed.\n",
    +    "> These examples will [probably only work](./README.md#Limitations) with this labextension installed. Try it on\n",
    +    "> [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/agoose77/jupyterlab-markup/master?urlpath=lab%2Ftree%2Findex.ipynb)!\n",
         "\n",
         "> \n",
         "```bash\n",
    
    From d999d7d25a4466dd1d982043922d96689db72422 Mon Sep 17 00:00:00 2001
    From: Nicholas Bollweg 
    Date: Wed, 4 Nov 2020 13:28:00 -0500
    Subject: [PATCH 29/31] more badges
    
    ---
     README.md   | 10 +++++++++-
     index.ipynb |  2 +-
     2 files changed, 10 insertions(+), 2 deletions(-)
    
    diff --git a/README.md b/README.md
    index be94db2..968829b 100644
    --- a/README.md
    +++ b/README.md
    @@ -1,6 +1,14 @@
     # jupyterlab-markup
     
    -[![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/agoose77/jupyterlab-markup/master?urlpath=lab%2Ftree%2Findex.ipynb)
    +[![npm-badge][]][npm] [![pypi-badge][]][pypi] [![binder-badge][]][binder]
    +
    +[binder]:
    +  https://mybinder.org/v2/gh/agoose77/jupyterlab-markup/master?urlpath=lab%2Ftree%2Findex.ipynb
    +[binder-badge]: https://mybinder.org/badge_logo.svg
    +[npm-badge]: https://img.shields.io/npm/v/@agoose77/jupyterlab-markup
    +[npm]: https://www.npmjs.com/package/@agoose77/jupyterlab-markup
    +[pypi-badge]: https://img.shields.io/pypi/v/jupyterlab_markup
    +[pypi]: https://pypi.org/project/jupyterlab_markup
     
     Adds additional rendering support to markdown in JupyterLab by using
     [markdown-it](https://github.com/markdown-it/markdown-it), and the following
    diff --git a/index.ipynb b/index.ipynb
    index 95033b5..14ceae6 100644
    --- a/index.ipynb
    +++ b/index.ipynb
    @@ -4,7 +4,7 @@
        "cell_type": "markdown",
        "metadata": {},
        "source": [
    -    "# `@agoose77/jupyterlab-markup` Examples"
    +    "# `jupyterlab-markup` Examples"
        ]
       },
       {
    
    From d57f3824897a8248a3b1380442e853e34965d881 Mon Sep 17 00:00:00 2001
    From: Nicholas Bollweg 
    Date: Thu, 5 Nov 2020 08:24:34 -0500
    Subject: [PATCH 30/31] unwrap docs links
    
    ---
     style/index.css | 1 -
     1 file changed, 1 deletion(-)
    
    diff --git a/style/index.css b/style/index.css
    index 7d21851..a550eb4 100644
    --- a/style/index.css
    +++ b/style/index.css
    @@ -75,7 +75,6 @@
       list-style: none;
       padding: 0 !important;
       margin: 0;
    -  flex-wrap: wrap;
     }
     
     .jp-MarkdownItSettings-Docs li {
    
    From 2333689d1e5fa6303d8010b7c68e8cedafb585e4 Mon Sep 17 00:00:00 2001
    From: Nicholas Bollweg 
    Date: Fri, 6 Nov 2020 15:53:10 -0500
    Subject: [PATCH 31/31] remove async from enabled check because cache
    
    ---
     src/plugin.ts | 2 +-
     1 file changed, 1 insertion(+), 1 deletion(-)
    
    diff --git a/src/plugin.ts b/src/plugin.ts
    index 28f591f..55d11f6 100644
    --- a/src/plugin.ts
    +++ b/src/plugin.ts
    @@ -90,7 +90,7 @@ const core: JupyterFrontEndPlugin = {
           caption: 'Reopen documents to see changes',
           isToggled: () => enabled,
           isEnabled: () => manager.settings != null,
    -      execute: async (args) => {
    +      execute: (args) => {
             manager.enabled = !!(args?.enabled == null ? !enabled : args.enabled);
           },
         });